You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by er...@apache.org on 2015/08/30 16:26:48 UTC
[math] MATH-1259
Repository: commons-math
Updated Branches:
refs/heads/master 7b9df59a9 -> 818533e92
MATH-1259
New class to replace the current "Incrementor" (now deprecated).
Additional functionality: negative initial value and/or increment,
"range" utility method.
Project: http://git-wip-us.apache.org/repos/asf/commons-math/repo
Commit: http://git-wip-us.apache.org/repos/asf/commons-math/commit/818533e9
Tree: http://git-wip-us.apache.org/repos/asf/commons-math/tree/818533e9
Diff: http://git-wip-us.apache.org/repos/asf/commons-math/diff/818533e9
Branch: refs/heads/master
Commit: 818533e92bfb1c142ae4ce5be18ab92d1ae17025
Parents: 7b9df59
Author: Gilles <er...@apache.org>
Authored: Sun Aug 30 16:23:23 2015 +0200
Committer: Gilles <er...@apache.org>
Committed: Sun Aug 30 16:23:23 2015 +0200
----------------------------------------------------------------------
.../apache/commons/math4/util/Incrementor.java | 3 +
.../commons/math4/util/IntegerSequence.java | 324 +++++++++++++++++++
.../commons/math4/util/IntegerSequenceTest.java | 248 ++++++++++++++
3 files changed, 575 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/commons-math/blob/818533e9/src/main/java/org/apache/commons/math4/util/Incrementor.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/math4/util/Incrementor.java b/src/main/java/org/apache/commons/math4/util/Incrementor.java
index 1f10cac..0898ec5 100644
--- a/src/main/java/org/apache/commons/math4/util/Incrementor.java
+++ b/src/main/java/org/apache/commons/math4/util/Incrementor.java
@@ -28,7 +28,10 @@ import org.apache.commons.math4.exception.NullArgumentException;
* select which exception must be thrown.
*
* @since 3.0
+ *
+ * @deprecated Use {@link IntegerSequence.Incrementor} instead.
*/
+@Deprecated
public class Incrementor {
/**
* Upper limit for the counter.
http://git-wip-us.apache.org/repos/asf/commons-math/blob/818533e9/src/main/java/org/apache/commons/math4/util/IntegerSequence.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/math4/util/IntegerSequence.java b/src/main/java/org/apache/commons/math4/util/IntegerSequence.java
new file mode 100644
index 0000000..82e7c97
--- /dev/null
+++ b/src/main/java/org/apache/commons/math4/util/IntegerSequence.java
@@ -0,0 +1,324 @@
+/*
+ * 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.math4.util;
+
+import java.util.Iterator;
+import org.apache.commons.math4.exception.MaxCountExceededException;
+import org.apache.commons.math4.exception.NullArgumentException;
+import org.apache.commons.math4.exception.MathUnsupportedOperationException;
+import org.apache.commons.math4.exception.NotStrictlyPositiveException;
+import org.apache.commons.math4.exception.ZeroException;
+
+/**
+ * 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 Iterable<Integer> 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 Iterable<Integer> range(final int start,
+ final int max,
+ final int step) {
+ return new Iterable<Integer>() {
+ /** {@inheritDoc} */
+ @Override
+ 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} */
+ @Override
+ 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 nTimes Number of increments.
+ * @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 explictly 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} */
+ @Override
+ public boolean hasNext() {
+ return canIncrement(0);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Integer next() {
+ final int value = count;
+ increment();
+ return value;
+ }
+
+ /**
+ * Not applicable.
+ *
+ * @throws MathUnsupportedOperationException
+ */
+ @Override
+ public void remove() {
+ throw new MathUnsupportedOperationException();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/commons-math/blob/818533e9/src/test/java/org/apache/commons/math4/util/IntegerSequenceTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/math4/util/IntegerSequenceTest.java b/src/test/java/org/apache/commons/math4/util/IntegerSequenceTest.java
new file mode 100644
index 0000000..d1a6764
--- /dev/null
+++ b/src/test/java/org/apache/commons/math4/util/IntegerSequenceTest.java
@@ -0,0 +1,248 @@
+/*
+ * 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.math4.util;
+
+import java.util.List;
+import java.util.ArrayList;
+import org.apache.commons.math4.exception.MaxCountExceededException;
+import org.apache.commons.math4.exception.TooManyEvaluationsException;
+import org.apache.commons.math4.exception.NotStrictlyPositiveException;
+import org.apache.commons.math4.exception.ZeroException;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Tests for {@link IntegerSequence} and {@link IntegerSequence#Incrementor}.
+ */
+public class IntegerSequenceTest {
+ @Test
+ public void testIncreasingRange() {
+ final int start = 1;
+ final int max = 7;
+ final int step = 2;
+
+ final List<Integer> seq = new ArrayList<Integer>();
+ for (Integer i : IntegerSequence.range(start, max, step)) {
+ seq.add(i);
+ }
+
+ Assert.assertEquals(4, seq.size());
+ for (int i = 0; i < seq.size(); i++) {
+ Assert.assertEquals(start + i * step, seq.get(i).intValue());
+ }
+ }
+
+ @Test
+ public void testIncreasingRangeNegativeEnd() {
+ final int start = -10;
+ final int max = -1;
+ final int step = 2;
+
+ final List<Integer> seq = new ArrayList<Integer>();
+ for (Integer i : IntegerSequence.range(start, max, step)) {
+ seq.add(i);
+ }
+
+ Assert.assertEquals(5, seq.size());
+ for (int i = 0; i < seq.size(); i++) {
+ Assert.assertEquals(start + i * step, seq.get(i).intValue());
+ }
+ }
+
+ @Test
+ public void testDecreasingRange() {
+ final int start = 10;
+ final int max = -8;
+ final int step = -3;
+
+ final List<Integer> seq = new ArrayList<Integer>();
+ for (Integer i : IntegerSequence.range(start, max, step)) {
+ seq.add(i);
+ }
+
+ Assert.assertEquals(7, seq.size());
+ for (int i = 0; i < seq.size(); i++) {
+ Assert.assertEquals(start + i * step, seq.get(i).intValue());
+ }
+ }
+
+ @Test
+ public void testSingleElementRange() {
+ final int start = 1;
+ final int max = 1;
+ final int step = -1;
+
+ final List<Integer> seq = new ArrayList<Integer>();
+ for (Integer i : IntegerSequence.range(start, max, step)) {
+ seq.add(i);
+ }
+
+ Assert.assertEquals(1, seq.size());
+ Assert.assertEquals(start, seq.get(0).intValue());
+ }
+
+ @Test
+ public void testBasicRange() {
+ final int start = -2;
+ final int end = 4;
+
+ final List<Integer> seq = new ArrayList<Integer>();
+ for (Integer i : IntegerSequence.range(start, end)) {
+ seq.add(i);
+ }
+
+ for (int i = start; i <= end; i++) {
+ Assert.assertEquals(i, seq.get(i - start).intValue());
+ }
+ }
+
+ @Test
+ public void testEmptyRange() {
+ final int start = 2;
+ final int end = 1;
+
+ final List<Integer> seq = new ArrayList<Integer>();
+ for (Integer i : IntegerSequence.range(start, end)) {
+ seq.add(i);
+ }
+
+ Assert.assertEquals(0, seq.size());
+ }
+
+ @Test
+ public void testEmptyRangeNegativeStart() {
+ final int start = -2;
+ final int max = -1;
+ final int step = -1;
+
+ final List<Integer> seq = new ArrayList<Integer>();
+ for (Integer i : IntegerSequence.range(start, max, step)) {
+ seq.add(i);
+ }
+
+ Assert.assertEquals(0, seq.size());
+ }
+
+ @Test(expected=MaxCountExceededException.class)
+ public void testIncrementorCountExceeded() {
+ final int start = 1;
+ final int max = 7;
+ final int step = 2;
+
+ final IntegerSequence.Incrementor inc =
+ IntegerSequence.Incrementor.create()
+ .withStart(start)
+ .withMaximalCount(max)
+ .withIncrement(step);
+
+ Assert.assertTrue(inc.canIncrement(2));
+ Assert.assertFalse(inc.canIncrement(3));
+
+ while (true) {
+ inc.increment();
+ }
+ }
+
+ @Test
+ public void testCanIncrementZeroTimes() {
+ final int start = 1;
+ final int max = 2;
+ final int step = 1;
+
+ final IntegerSequence.Incrementor inc
+ = IntegerSequence.Incrementor.create()
+ .withStart(start)
+ .withMaximalCount(max)
+ .withIncrement(step);
+
+ Assert.assertTrue(inc.canIncrement(0));
+ }
+
+ @Test(expected=NotStrictlyPositiveException.class)
+ public void testIncrementZeroTimes() {
+ final int start = 1;
+ final int max = 2;
+ final int step = 1;
+
+ final IntegerSequence.Incrementor inc
+ = IntegerSequence.Incrementor.create()
+ .withStart(start)
+ .withMaximalCount(max)
+ .withIncrement(step);
+
+ inc.increment(0);
+ }
+
+ @Test(expected=ZeroException.class)
+ public void testIncrementZeroStep() {
+ final int step = 0;
+
+ final IntegerSequence.Incrementor inc
+ = IntegerSequence.Incrementor.create()
+ .withIncrement(step);
+ }
+
+ @Test
+ public void testIteratorZeroElement() {
+ final int start = 1;
+ final int max = 1;
+ final int step = 1;
+
+ final IntegerSequence.Incrementor inc
+ = IntegerSequence.Incrementor.create()
+ .withStart(start)
+ .withMaximalCount(max)
+ .withIncrement(step);
+
+ Assert.assertFalse(inc.hasNext());
+ try {
+ inc.increment();
+ Assert.fail("exception expected");
+ } catch (MaxCountExceededException e) {
+ // Expected.
+ }
+ }
+
+ @Test(expected=TooManyEvaluationsException.class)
+ public void testIncrementorAlternateException() {
+ final int start = 1;
+ final int max = 2;
+ final int step = 1;
+
+ final IntegerSequence.Incrementor.MaxCountExceededCallback cb
+ = new IntegerSequence.Incrementor.MaxCountExceededCallback() {
+ /** {@inheritDoc} */
+ public void trigger(int max) {
+ throw new TooManyEvaluationsException(max);
+ }
+ };
+
+ final IntegerSequence.Incrementor inc
+ = IntegerSequence.Incrementor.create()
+ .withStart(start)
+ .withMaximalCount(max)
+ .withIncrement(step)
+ .withCallback(cb);
+
+ try {
+ // One call must succeed.
+ inc.increment();
+ } catch (RuntimeException e) {
+ Assert.fail("unexpected exception");
+ }
+
+ // Second call must fail.
+ inc.increment();
+ }
+}