summaryrefslogtreecommitdiff
path: root/src/main/java/org/apache/commons/math3/util/IntegerSequence.java
diff options
context:
space:
mode:
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.java328
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();
+ }
+ }
+}