xref: /aosp_15_r20/external/apache-commons-math/src/main/java/org/apache/commons/math3/util/IntegerSequence.java (revision d3fac44428dd0296a04a50c6827e3205b8dbea8a)
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 package org.apache.commons.math3.util;
18 
19 import org.apache.commons.math3.exception.MathUnsupportedOperationException;
20 import org.apache.commons.math3.exception.MaxCountExceededException;
21 import org.apache.commons.math3.exception.NotStrictlyPositiveException;
22 import org.apache.commons.math3.exception.NullArgumentException;
23 import org.apache.commons.math3.exception.ZeroException;
24 
25 import java.util.Iterator;
26 
27 /**
28  * Provides a sequence of integers.
29  *
30  * @since 3.6
31  */
32 public class IntegerSequence {
33     /** Utility class contains only static methods. */
IntegerSequence()34     private IntegerSequence() {}
35 
36     /**
37      * Creates a sequence {@code [start .. end]}. It calls {@link #range(int,int,int) range(start,
38      * end, 1)}.
39      *
40      * @param start First value of the range.
41      * @param end Last value of the range.
42      * @return a range.
43      */
range(int start, int end)44     public static Range range(int start, int end) {
45         return range(start, end, 1);
46     }
47 
48     /**
49      * Creates a sequence \( a_i, i < 0 <= n \) where \( a_i = start + i * step \) and \( n \) is
50      * such that \( a_n <= max \) and \( a_{n+1} > max \).
51      *
52      * @param start First value of the range.
53      * @param max Last value of the range that satisfies the above construction rule.
54      * @param step Increment.
55      * @return a range.
56      */
range(final int start, final int max, final int step)57     public static Range range(final int start, final int max, final int step) {
58         return new Range(start, max, step);
59     }
60 
61     /** Generates a sequence of integers. */
62     public static class Range implements Iterable<Integer> {
63         /** Number of integers contained in this range. */
64         private final int size;
65 
66         /** First value. */
67         private final int start;
68 
69         /** Final value. */
70         private final int max;
71 
72         /** Increment. */
73         private final int step;
74 
75         /**
76          * Creates a sequence \( a_i, i < 0 <= n \) where \( a_i = start + i * step \) and \( n \)
77          * is such that \( a_n <= max \) and \( a_{n+1} > max \).
78          *
79          * @param start First value of the range.
80          * @param max Last value of the range that satisfies the above construction rule.
81          * @param step Increment.
82          */
Range(int start, int max, int step)83         public Range(int start, int max, int step) {
84             this.start = start;
85             this.max = max;
86             this.step = step;
87 
88             final int s = (max - start) / step + 1;
89             this.size = s < 0 ? 0 : s;
90         }
91 
92         /**
93          * Gets the number of elements contained in the range.
94          *
95          * @return the size of the range.
96          */
97         public int size() {
98             return size;
99         }
100 
101         /** {@inheritDoc} */
102         public Iterator<Integer> iterator() {
103             return Incrementor.create()
104                     .withStart(start)
105                     .withMaximalCount(max + (step > 0 ? 1 : -1))
106                     .withIncrement(step);
107         }
108     }
109 
110     /**
111      * Utility that increments a counter until a maximum is reached, at which point, the instance
112      * will by default throw a {@link MaxCountExceededException}. However, the user is able to
113      * override this behaviour by defining a custom {@link MaxCountExceededCallback callback}, in
114      * order to e.g. select which exception must be thrown.
115      */
116     public static class Incrementor implements Iterator<Integer> {
117         /** Default callback. */
118         private static final MaxCountExceededCallback CALLBACK =
119                 new MaxCountExceededCallback() {
120                     /** {@inheritDoc} */
121                     public void trigger(int max) throws MaxCountExceededException {
122                         throw new MaxCountExceededException(max);
123                     }
124                 };
125 
126         /** Initial value the counter. */
127         private final int init;
128 
129         /** Upper limit for the counter. */
130         private final int maximalCount;
131 
132         /** Increment. */
133         private final int increment;
134 
135         /** Function called at counter exhaustion. */
136         private final MaxCountExceededCallback maxCountCallback;
137 
138         /** Current count. */
139         private int count = 0;
140 
141         /**
142          * Defines a method to be called at counter exhaustion. The {@link #trigger(int) trigger}
143          * method should usually throw an exception.
144          */
145         public interface MaxCountExceededCallback {
146             /**
147              * Function called when the maximal count has been reached.
148              *
149              * @param maximalCount Maximal count.
150              * @throws MaxCountExceededException at counter exhaustion
151              */
trigger(int maximalCount)152             void trigger(int maximalCount) throws MaxCountExceededException;
153         }
154 
155         /**
156          * Creates an incrementor. The counter will be exhausted either when {@code max} is reached
157          * or when {@code nTimes} increments have been performed.
158          *
159          * @param start Initial value.
160          * @param max Maximal count.
161          * @param step Increment.
162          * @param cb Function to be called when the maximal count has been reached.
163          * @throws NullArgumentException if {@code cb} is {@code null}.
164          */
Incrementor(int start, int max, int step, MaxCountExceededCallback cb)165         private Incrementor(int start, int max, int step, MaxCountExceededCallback cb)
166                 throws NullArgumentException {
167             if (cb == null) {
168                 throw new NullArgumentException();
169             }
170             this.init = start;
171             this.maximalCount = max;
172             this.increment = step;
173             this.maxCountCallback = cb;
174             this.count = start;
175         }
176 
177         /**
178          * Factory method that creates a default instance. The initial and maximal values are set to
179          * 0. For the new instance to be useful, the maximal count must be set by calling {@link
180          * #withMaximalCount(int) withMaximalCount}.
181          *
182          * @return an new instance.
183          */
create()184         public static Incrementor create() {
185             return new Incrementor(0, 0, 1, CALLBACK);
186         }
187 
188         /**
189          * Creates a new instance with a given initial value. The counter is reset to the initial
190          * value.
191          *
192          * @param start Initial value of the counter.
193          * @return a new instance.
194          */
withStart(int start)195         public Incrementor withStart(int start) {
196             return new Incrementor(start, this.maximalCount, this.increment, this.maxCountCallback);
197         }
198 
199         /**
200          * Creates a new instance with a given maximal count. The counter is reset to the initial
201          * value.
202          *
203          * @param max Maximal count.
204          * @return a new instance.
205          */
withMaximalCount(int max)206         public Incrementor withMaximalCount(int max) {
207             return new Incrementor(this.init, max, this.increment, this.maxCountCallback);
208         }
209 
210         /**
211          * Creates a new instance with a given increment. The counter is reset to the initial value.
212          *
213          * @param step Increment.
214          * @return a new instance.
215          */
withIncrement(int step)216         public Incrementor withIncrement(int step) {
217             if (step == 0) {
218                 throw new ZeroException();
219             }
220             return new Incrementor(this.init, this.maximalCount, step, this.maxCountCallback);
221         }
222 
223         /**
224          * Creates a new instance with a given callback. The counter is reset to the initial value.
225          *
226          * @param cb Callback to be called at counter exhaustion.
227          * @return a new instance.
228          */
withCallback(MaxCountExceededCallback cb)229         public Incrementor withCallback(MaxCountExceededCallback cb) {
230             return new Incrementor(this.init, this.maximalCount, this.increment, cb);
231         }
232 
233         /**
234          * Gets the upper limit of the counter.
235          *
236          * @return the counter upper limit.
237          */
getMaximalCount()238         public int getMaximalCount() {
239             return maximalCount;
240         }
241 
242         /**
243          * Gets the current count.
244          *
245          * @return the current count.
246          */
getCount()247         public int getCount() {
248             return count;
249         }
250 
251         /**
252          * Checks whether incrementing the counter {@code nTimes} is allowed.
253          *
254          * @return {@code false} if calling {@link #increment()} will trigger a {@code
255          *     MaxCountExceededException}, {@code true} otherwise.
256          */
canIncrement()257         public boolean canIncrement() {
258             return canIncrement(1);
259         }
260 
261         /**
262          * Checks whether incrementing the counter several times is allowed.
263          *
264          * @param nTimes Number of increments.
265          * @return {@code false} if calling {@link #increment(int) increment(nTimes)} would call the
266          *     {@link MaxCountExceededCallback callback} {@code true} otherwise.
267          */
canIncrement(int nTimes)268         public boolean canIncrement(int nTimes) {
269             final int finalCount = count + nTimes * increment;
270             return increment < 0 ? finalCount > maximalCount : finalCount < maximalCount;
271         }
272 
273         /**
274          * Performs multiple increments.
275          *
276          * @param nTimes Number of increments.
277          * @throws MaxCountExceededException at counter exhaustion.
278          * @throws NotStrictlyPositiveException if {@code nTimes <= 0}.
279          * @see #increment()
280          */
increment(int nTimes)281         public void increment(int nTimes) throws MaxCountExceededException {
282             if (nTimes <= 0) {
283                 throw new NotStrictlyPositiveException(nTimes);
284             }
285 
286             if (!canIncrement(0)) {
287                 maxCountCallback.trigger(maximalCount);
288             }
289             count += nTimes * increment;
290         }
291 
292         /**
293          * Adds the increment value to the current iteration count. At counter exhaustion, this
294          * method will call the {@link MaxCountExceededCallback#trigger(int) trigger} method of the
295          * callback object passed to the {@link #withCallback(MaxCountExceededCallback)} method. If
296          * not explicitly set, a default callback is used that will throw a {@code
297          * MaxCountExceededException}.
298          *
299          * @throws MaxCountExceededException at counter exhaustion, unless a custom {@link
300          *     MaxCountExceededCallback callback} has been set.
301          * @see #increment(int)
302          */
increment()303         public void increment() throws MaxCountExceededException {
304             increment(1);
305         }
306 
307         /** {@inheritDoc} */
hasNext()308         public boolean hasNext() {
309             return canIncrement(0);
310         }
311 
312         /** {@inheritDoc} */
next()313         public Integer next() {
314             final int value = count;
315             increment();
316             return value;
317         }
318 
319         /**
320          * Not applicable.
321          *
322          * @throws MathUnsupportedOperationException
323          */
remove()324         public void remove() {
325             throw new MathUnsupportedOperationException();
326         }
327     }
328 }
329