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();
+    }
+}