mirror of https://github.com/jlizier/jidt
392 lines
13 KiB
Java
392 lines
13 KiB
Java
/*
|
|
* Java Information Dynamics Toolkit (JIDT)
|
|
* Copyright (C) 2017, Joseph T. Lizier
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/*
|
|
* This class was originally distributed as part of the Apache Commons
|
|
* Math3 library (3.6.1), under the Apache License Version 2.0, which is
|
|
* copied below. This Apache 2 software is now included as a derivative
|
|
* work in the GPLv3 licensed JIDT project, as per:
|
|
* http://www.apache.org/licenses/GPL-compatibility.html
|
|
*
|
|
* The original Apache source code has been modified as follows:
|
|
* -- We have modified package names to sit inside the JIDT structure.
|
|
*/
|
|
|
|
/*
|
|
* 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 infodynamics.utils.commonsmath3.util;
|
|
|
|
import java.util.Iterator;
|
|
import infodynamics.utils.commonsmath3.exception.MaxCountExceededException;
|
|
import infodynamics.utils.commonsmath3.exception.NullArgumentException;
|
|
import infodynamics.utils.commonsmath3.exception.MathUnsupportedOperationException;
|
|
import infodynamics.utils.commonsmath3.exception.NotStrictlyPositiveException;
|
|
import infodynamics.utils.commonsmath3.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 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();
|
|
}
|
|
}
|
|
}
|