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.math4.legacy.core;
18
19 import java.util.Iterator;
20 import java.util.NoSuchElementException;
21 import org.apache.commons.math4.legacy.exception.MaxCountExceededException;
22 import org.apache.commons.math4.legacy.exception.NullArgumentException;
23 import org.apache.commons.math4.legacy.exception.MathUnsupportedOperationException;
24 import org.apache.commons.math4.legacy.exception.NotStrictlyPositiveException;
25 import org.apache.commons.math4.legacy.exception.ZeroException;
26
27 /**
28 * Provides a sequence of integers.
29 *
30 * @since 3.6
31 */
32 public final class IntegerSequence {
33 /**
34 * Utility class contains only static methods.
35 */
36 private IntegerSequence() {}
37
38 /**
39 * Creates a sequence {@code [start .. end]}.
40 * It calls {@link #range(int,int,int) range(start, end, 1)}.
41 *
42 * @param start First value of the range.
43 * @param end Last value of the range.
44 * @return a range.
45 */
46 public static Range range(int start,
47 int end) {
48 return range(start, end, 1);
49 }
50
51 /**
52 * Creates a sequence <code>a<sub>i</sub>, i < 0 < n</code>
53 * where <code>a<sub>i</sub> = start + i * step</code>
54 * and {@code n} is such that <code>a<sub>n</sub> <= max</code>
55 * and <code>a<sub>n+1</sub> > max</code>.
56 *
57 * @param start First value of the range.
58 * @param max Last value of the range that satisfies the above
59 * construction rule.
60 * @param step Increment.
61 * @return a range.
62 */
63 public static Range range(final int start,
64 final int max,
65 final int step) {
66 return new Range(start, max, step);
67 }
68
69 /**
70 * Generates a sequence of integers.
71 */
72 public static class Range implements Iterable<Integer> {
73 /** Number of integers contained in this range. */
74 private final int size;
75 /** First value. */
76 private final int start;
77 /** Final value. */
78 private final int max;
79 /** Increment. */
80 private final int step;
81
82 /**
83 * Creates a sequence <code>a<sub>i</sub>, i < 0 < n</code>
84 * where <code>a<sub>i</sub> = start + i * step</code>
85 * and {@code n} is such that <code>a<sub>n</sub> <= max</code>
86 * and <code>a<sub>n+1</sub> > max</code>.
87 *
88 * @param start First value of the range.
89 * @param max Last value of the range that satisfies the above
90 * construction rule.
91 * @param step Increment.
92 */
93 public Range(int start,
94 int max,
95 int step) {
96 this.start = start;
97 this.max = max;
98 this.step = step;
99
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 }