diff options
Diffstat (limited to 'src/main/java/org/apache/commons/math3/util/IntegerSequence.java')
-rw-r--r-- | src/main/java/org/apache/commons/math3/util/IntegerSequence.java | 328 |
1 files changed, 328 insertions, 0 deletions
diff --git a/src/main/java/org/apache/commons/math3/util/IntegerSequence.java b/src/main/java/org/apache/commons/math3/util/IntegerSequence.java new file mode 100644 index 0000000..7859ec3 --- /dev/null +++ b/src/main/java/org/apache/commons/math3/util/IntegerSequence.java @@ -0,0 +1,328 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math3.util; + +import org.apache.commons.math3.exception.MathUnsupportedOperationException; +import org.apache.commons.math3.exception.MaxCountExceededException; +import org.apache.commons.math3.exception.NotStrictlyPositiveException; +import org.apache.commons.math3.exception.NullArgumentException; +import org.apache.commons.math3.exception.ZeroException; + +import java.util.Iterator; + +/** + * Provides a sequence of integers. + * + * @since 3.6 + */ +public class IntegerSequence { + /** Utility class contains only static methods. */ + private IntegerSequence() {} + + /** + * Creates a sequence {@code [start .. end]}. It calls {@link #range(int,int,int) range(start, + * end, 1)}. + * + * @param start First value of the range. + * @param end Last value of the range. + * @return a range. + */ + public static Range range(int start, int end) { + return range(start, end, 1); + } + + /** + * Creates a sequence \( a_i, i < 0 <= n \) where \( a_i = start + i * step \) and \( n \) is + * such that \( a_n <= max \) and \( a_{n+1} > max \). + * + * @param start First value of the range. + * @param max Last value of the range that satisfies the above construction rule. + * @param step Increment. + * @return a range. + */ + public static Range range(final int start, final int max, final int step) { + return new Range(start, max, step); + } + + /** Generates a sequence of integers. */ + public static class Range implements Iterable<Integer> { + /** Number of integers contained in this range. */ + private final int size; + + /** First value. */ + private final int start; + + /** Final value. */ + private final int max; + + /** Increment. */ + private final int step; + + /** + * Creates a sequence \( a_i, i < 0 <= n \) where \( a_i = start + i * step \) and \( n \) + * is such that \( a_n <= max \) and \( a_{n+1} > max \). + * + * @param start First value of the range. + * @param max Last value of the range that satisfies the above construction rule. + * @param step Increment. + */ + public Range(int start, int max, int step) { + this.start = start; + this.max = max; + this.step = step; + + final int s = (max - start) / step + 1; + this.size = s < 0 ? 0 : s; + } + + /** + * Gets the number of elements contained in the range. + * + * @return the size of the range. + */ + public int size() { + return size; + } + + /** {@inheritDoc} */ + public Iterator<Integer> iterator() { + return Incrementor.create() + .withStart(start) + .withMaximalCount(max + (step > 0 ? 1 : -1)) + .withIncrement(step); + } + } + + /** + * Utility that increments a counter until a maximum is reached, at which point, the instance + * will by default throw a {@link MaxCountExceededException}. However, the user is able to + * override this behaviour by defining a custom {@link MaxCountExceededCallback callback}, in + * order to e.g. select which exception must be thrown. + */ + public static class Incrementor implements Iterator<Integer> { + /** Default callback. */ + private static final MaxCountExceededCallback CALLBACK = + new MaxCountExceededCallback() { + /** {@inheritDoc} */ + public void trigger(int max) throws MaxCountExceededException { + throw new MaxCountExceededException(max); + } + }; + + /** Initial value the counter. */ + private final int init; + + /** Upper limit for the counter. */ + private final int maximalCount; + + /** Increment. */ + private final int increment; + + /** Function called at counter exhaustion. */ + private final MaxCountExceededCallback maxCountCallback; + + /** Current count. */ + private int count = 0; + + /** + * Defines a method to be called at counter exhaustion. The {@link #trigger(int) trigger} + * method should usually throw an exception. + */ + public interface MaxCountExceededCallback { + /** + * Function called when the maximal count has been reached. + * + * @param maximalCount Maximal count. + * @throws MaxCountExceededException at counter exhaustion + */ + void trigger(int maximalCount) throws MaxCountExceededException; + } + + /** + * Creates an incrementor. The counter will be exhausted either when {@code max} is reached + * or when {@code nTimes} increments have been performed. + * + * @param start Initial value. + * @param max Maximal count. + * @param step Increment. + * @param cb Function to be called when the maximal count has been reached. + * @throws NullArgumentException if {@code cb} is {@code null}. + */ + private Incrementor(int start, int max, int step, MaxCountExceededCallback cb) + throws NullArgumentException { + if (cb == null) { + throw new NullArgumentException(); + } + this.init = start; + this.maximalCount = max; + this.increment = step; + this.maxCountCallback = cb; + this.count = start; + } + + /** + * Factory method that creates a default instance. The initial and maximal values are set to + * 0. For the new instance to be useful, the maximal count must be set by calling {@link + * #withMaximalCount(int) withMaximalCount}. + * + * @return an new instance. + */ + public static Incrementor create() { + return new Incrementor(0, 0, 1, CALLBACK); + } + + /** + * Creates a new instance with a given initial value. The counter is reset to the initial + * value. + * + * @param start Initial value of the counter. + * @return a new instance. + */ + public Incrementor withStart(int start) { + return new Incrementor(start, this.maximalCount, this.increment, this.maxCountCallback); + } + + /** + * Creates a new instance with a given maximal count. The counter is reset to the initial + * value. + * + * @param max Maximal count. + * @return a new instance. + */ + public Incrementor withMaximalCount(int max) { + return new Incrementor(this.init, max, this.increment, this.maxCountCallback); + } + + /** + * Creates a new instance with a given increment. The counter is reset to the initial value. + * + * @param step Increment. + * @return a new instance. + */ + public Incrementor withIncrement(int step) { + if (step == 0) { + throw new ZeroException(); + } + return new Incrementor(this.init, this.maximalCount, step, this.maxCountCallback); + } + + /** + * Creates a new instance with a given callback. The counter is reset to the initial value. + * + * @param cb Callback to be called at counter exhaustion. + * @return a new instance. + */ + public Incrementor withCallback(MaxCountExceededCallback cb) { + return new Incrementor(this.init, this.maximalCount, this.increment, cb); + } + + /** + * Gets the upper limit of the counter. + * + * @return the counter upper limit. + */ + public int getMaximalCount() { + return maximalCount; + } + + /** + * Gets the current count. + * + * @return the current count. + */ + public int getCount() { + return count; + } + + /** + * Checks whether incrementing the counter {@code nTimes} is allowed. + * + * @return {@code false} if calling {@link #increment()} will trigger a {@code + * MaxCountExceededException}, {@code true} otherwise. + */ + public boolean canIncrement() { + return canIncrement(1); + } + + /** + * Checks whether incrementing the counter several times is allowed. + * + * @param nTimes Number of increments. + * @return {@code false} if calling {@link #increment(int) increment(nTimes)} would call the + * {@link MaxCountExceededCallback callback} {@code true} otherwise. + */ + public boolean canIncrement(int nTimes) { + final int finalCount = count + nTimes * increment; + return increment < 0 ? finalCount > maximalCount : finalCount < maximalCount; + } + + /** + * Performs multiple increments. + * + * @param nTimes Number of increments. + * @throws MaxCountExceededException at counter exhaustion. + * @throws NotStrictlyPositiveException if {@code nTimes <= 0}. + * @see #increment() + */ + public void increment(int nTimes) throws MaxCountExceededException { + if (nTimes <= 0) { + throw new NotStrictlyPositiveException(nTimes); + } + + if (!canIncrement(0)) { + maxCountCallback.trigger(maximalCount); + } + count += nTimes * increment; + } + + /** + * Adds the increment value to the current iteration count. At counter exhaustion, this + * method will call the {@link MaxCountExceededCallback#trigger(int) trigger} method of the + * callback object passed to the {@link #withCallback(MaxCountExceededCallback)} method. If + * not explicitly set, a default callback is used that will throw a {@code + * MaxCountExceededException}. + * + * @throws MaxCountExceededException at counter exhaustion, unless a custom {@link + * MaxCountExceededCallback callback} has been set. + * @see #increment(int) + */ + public void increment() throws MaxCountExceededException { + increment(1); + } + + /** {@inheritDoc} */ + public boolean hasNext() { + return canIncrement(0); + } + + /** {@inheritDoc} */ + public Integer next() { + final int value = count; + increment(); + return value; + } + + /** + * Not applicable. + * + * @throws MathUnsupportedOperationException + */ + public void remove() { + throw new MathUnsupportedOperationException(); + } + } +} |