MATH-1259
New class to replace the current "Incrementor" (now deprecated). Additional functionality: negative initial value and/or increment, "range" utility method.
This commit is contained in:
parent
7b9df59a96
commit
818533e92b
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue