001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.math3.util;
018
019import java.util.Iterator;
020import org.apache.commons.math3.exception.MaxCountExceededException;
021import org.apache.commons.math3.exception.NullArgumentException;
022import org.apache.commons.math3.exception.MathUnsupportedOperationException;
023import org.apache.commons.math3.exception.NotStrictlyPositiveException;
024import org.apache.commons.math3.exception.ZeroException;
025
026/**
027 * Provides a sequence of integers.
028 *
029 * @since 3.6
030 */
031public class IntegerSequence {
032    /**
033     * Utility class contains only static methods.
034     */
035    private IntegerSequence() {}
036
037    /**
038     * Creates a sequence {@code [start .. end]}.
039     * It calls {@link #range(int,int,int) range(start, end, 1)}.
040     *
041     * @param start First value of the range.
042     * @param end Last value of the range.
043     * @return a range.
044     */
045    public static Range range(int start,
046                              int end) {
047        return range(start, end, 1);
048    }
049
050    /**
051     * Creates a sequence \( a_i, i < 0 <= n \)
052     * where \( a_i = start + i * step \)
053     * and \( n \) is such that \( a_n <= max \) and \( a_{n+1} > max \).
054     *
055     * @param start First value of the range.
056     * @param max Last value of the range that satisfies the above
057     * construction rule.
058     * @param step Increment.
059     * @return a range.
060     */
061    public static Range range(final int start,
062                              final int max,
063                              final int step) {
064        return new Range(start, max, step);
065    }
066
067    /**
068     * Generates a sequence of integers.
069     */
070    public static class Range implements Iterable<Integer> {
071        /** Number of integers contained in this range. */
072        private final int size;
073        /** First value. */
074        private final int start;
075        /** Final value. */
076        private final int max;
077        /** Increment. */
078        private final int step;
079
080        /**
081         * Creates a sequence \( a_i, i < 0 <= n \)
082         * where \( a_i = start + i * step \)
083         * and \( n \) is such that \( a_n <= max \) and \( a_{n+1} > max \).
084         *
085         * @param start First value of the range.
086         * @param max Last value of the range that satisfies the above
087         * construction rule.
088         * @param step Increment.
089         */
090        public Range(int start,
091                     int max,
092                     int step) {
093            this.start = start;
094            this.max = max;
095            this.step = step;
096
097            final int s = (max - start) / step + 1;
098            this.size = s < 0 ? 0 : s;
099        }
100
101        /**
102         * Gets the number of elements contained in the range.
103         *
104         * @return the size of the range.
105         */
106        public int size() {
107            return size;
108        }
109
110        /** {@inheritDoc} */
111        public Iterator<Integer> iterator() {
112            return Incrementor.create()
113                .withStart(start)
114                .withMaximalCount(max + (step > 0 ? 1 : -1))
115                .withIncrement(step);
116        }
117    }
118
119    /**
120     * Utility that increments a counter until a maximum is reached, at
121     * which point, the instance will by default throw a
122     * {@link MaxCountExceededException}.
123     * However, the user is able to override this behaviour by defining a
124     * custom {@link MaxCountExceededCallback callback}, in order to e.g.
125     * select which exception must be thrown.
126     */
127    public static class Incrementor implements Iterator<Integer> {
128        /** Default callback. */
129        private static final MaxCountExceededCallback CALLBACK
130            = new MaxCountExceededCallback() {
131                    /** {@inheritDoc} */
132                    public void trigger(int max) throws MaxCountExceededException {
133                        throw new MaxCountExceededException(max);
134                    }
135                };
136
137        /** Initial value the counter. */
138        private final int init;
139        /** Upper limit for the counter. */
140        private final int maximalCount;
141        /** Increment. */
142        private final int increment;
143        /** Function called at counter exhaustion. */
144        private final MaxCountExceededCallback maxCountCallback;
145        /** Current count. */
146        private int count = 0;
147
148        /**
149         * Defines a method to be called at counter exhaustion.
150         * The {@link #trigger(int) trigger} method should usually throw an exception.
151         */
152        public interface MaxCountExceededCallback {
153            /**
154             * Function called when the maximal count has been reached.
155             *
156             * @param maximalCount Maximal count.
157             * @throws MaxCountExceededException at counter exhaustion
158             */
159            void trigger(int maximalCount) throws MaxCountExceededException;
160        }
161
162        /**
163         * Creates an incrementor.
164         * The counter will be exhausted either when {@code max} is reached
165         * or when {@code nTimes} increments have been performed.
166         *
167         * @param start Initial value.
168         * @param max Maximal count.
169         * @param step Increment.
170         * @param cb Function to be called when the maximal count has been reached.
171         * @throws NullArgumentException if {@code cb} is {@code null}.
172         */
173        private Incrementor(int start,
174                            int max,
175                            int step,
176                            MaxCountExceededCallback cb)
177            throws NullArgumentException {
178            if (cb == null) {
179                throw new NullArgumentException();
180            }
181            this.init = start;
182            this.maximalCount = max;
183            this.increment = step;
184            this.maxCountCallback = cb;
185            this.count = start;
186        }
187
188        /**
189         * Factory method that creates a default instance.
190         * The initial and maximal values are set to 0.
191         * For the new instance to be useful, the maximal count must be set
192         * by calling {@link #withMaximalCount(int) withMaximalCount}.
193         *
194         * @return an new instance.
195         */
196        public static Incrementor create() {
197            return new Incrementor(0, 0, 1, CALLBACK);
198        }
199
200        /**
201         * Creates a new instance with a given initial value.
202         * The counter is reset to the initial value.
203         *
204         * @param start Initial value of the counter.
205         * @return a new instance.
206         */
207        public Incrementor withStart(int start) {
208            return new Incrementor(start,
209                                   this.maximalCount,
210                                   this.increment,
211                                   this.maxCountCallback);
212        }
213
214        /**
215         * Creates a new instance with a given maximal count.
216         * The counter is reset to the initial value.
217         *
218         * @param max Maximal count.
219         * @return a new instance.
220         */
221        public Incrementor withMaximalCount(int max) {
222            return new Incrementor(this.init,
223                                   max,
224                                   this.increment,
225                                   this.maxCountCallback);
226        }
227
228        /**
229         * Creates a new instance with a given increment.
230         * The counter is reset to the initial value.
231         *
232         * @param step Increment.
233         * @return a new instance.
234         */
235        public Incrementor withIncrement(int step) {
236            if (step == 0) {
237                throw new ZeroException();
238            }
239            return new Incrementor(this.init,
240                                   this.maximalCount,
241                                   step,
242                                   this.maxCountCallback);
243        }
244
245        /**
246         * Creates a new instance with a given callback.
247         * The counter is reset to the initial value.
248         *
249         * @param cb Callback to be called at counter exhaustion.
250         * @return a new instance.
251         */
252        public Incrementor withCallback(MaxCountExceededCallback cb) {
253            return new Incrementor(this.init,
254                                   this.maximalCount,
255                                   this.increment,
256                                   cb);
257        }
258
259        /**
260         * Gets the upper limit of the counter.
261         *
262         * @return the counter upper limit.
263         */
264        public int getMaximalCount() {
265            return maximalCount;
266        }
267
268        /**
269         * Gets the current count.
270         *
271         * @return the current count.
272         */
273        public int getCount() {
274            return count;
275        }
276
277        /**
278         * Checks whether incrementing the counter {@code nTimes} is allowed.
279         *
280         * @return {@code false} if calling {@link #increment()}
281         * will trigger a {@code MaxCountExceededException},
282         * {@code true} otherwise.
283         */
284        public boolean canIncrement() {
285            return canIncrement(1);
286        }
287
288        /**
289         * Checks whether incrementing the counter several times is allowed.
290         *
291         * @param nTimes Number of increments.
292         * @return {@code false} if calling {@link #increment(int)
293         * increment(nTimes)} would call the {@link MaxCountExceededCallback callback}
294         * {@code true} otherwise.
295         */
296        public boolean canIncrement(int nTimes) {
297            final int finalCount = count + nTimes * increment;
298            return increment < 0 ?
299                finalCount > maximalCount :
300                finalCount < maximalCount;
301        }
302
303        /**
304         * Performs multiple increments.
305         *
306         * @param nTimes Number of increments.
307         * @throws MaxCountExceededException at counter exhaustion.
308         * @throws NotStrictlyPositiveException if {@code nTimes <= 0}.
309         *
310         * @see #increment()
311         */
312        public void increment(int nTimes) throws MaxCountExceededException {
313            if (nTimes <= 0) {
314                throw new NotStrictlyPositiveException(nTimes);
315            }
316
317            if (!canIncrement(0)) {
318                maxCountCallback.trigger(maximalCount);
319            }
320            count += nTimes * increment;
321        }
322
323        /**
324         * Adds the increment value to the current iteration count.
325         * At counter exhaustion, this method will call the
326         * {@link MaxCountExceededCallback#trigger(int) trigger} method of the
327         * callback object passed to the
328         * {@link #withCallback(MaxCountExceededCallback)} method.
329         * If not explicitly set, a default callback is used that will throw
330         * a {@code MaxCountExceededException}.
331         *
332         * @throws MaxCountExceededException at counter exhaustion, unless a
333         * custom {@link MaxCountExceededCallback callback} has been set.
334         *
335         * @see #increment(int)
336         */
337        public void increment() throws MaxCountExceededException {
338            increment(1);
339        }
340
341        /** {@inheritDoc} */
342        public boolean hasNext() {
343            return canIncrement(0);
344        }
345
346        /** {@inheritDoc} */
347        public Integer next() {
348            final int value = count;
349            increment();
350            return value;
351        }
352
353        /**
354         * Not applicable.
355         *
356         * @throws MathUnsupportedOperationException
357         */
358        public void remove() {
359            throw new MathUnsupportedOperationException();
360        }
361    }
362}