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