1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.commons.math3.util; 18 19 import org.apache.commons.math3.exception.MathUnsupportedOperationException; 20 import org.apache.commons.math3.exception.MaxCountExceededException; 21 import org.apache.commons.math3.exception.NotStrictlyPositiveException; 22 import org.apache.commons.math3.exception.NullArgumentException; 23 import org.apache.commons.math3.exception.ZeroException; 24 25 import java.util.Iterator; 26 27 /** 28 * Provides a sequence of integers. 29 * 30 * @since 3.6 31 */ 32 public class IntegerSequence { 33 /** Utility class contains only static methods. */ IntegerSequence()34 private IntegerSequence() {} 35 36 /** 37 * Creates a sequence {@code [start .. end]}. It calls {@link #range(int,int,int) range(start, 38 * end, 1)}. 39 * 40 * @param start First value of the range. 41 * @param end Last value of the range. 42 * @return a range. 43 */ range(int start, int end)44 public static Range range(int start, int end) { 45 return range(start, end, 1); 46 } 47 48 /** 49 * Creates a sequence \( a_i, i < 0 <= n \) where \( a_i = start + i * step \) and \( n \) is 50 * such that \( a_n <= max \) and \( a_{n+1} > max \). 51 * 52 * @param start First value of the range. 53 * @param max Last value of the range that satisfies the above construction rule. 54 * @param step Increment. 55 * @return a range. 56 */ range(final int start, final int max, final int step)57 public static Range range(final int start, final int max, final int step) { 58 return new Range(start, max, step); 59 } 60 61 /** Generates a sequence of integers. */ 62 public static class Range implements Iterable<Integer> { 63 /** Number of integers contained in this range. */ 64 private final int size; 65 66 /** First value. */ 67 private final int start; 68 69 /** Final value. */ 70 private final int max; 71 72 /** Increment. */ 73 private final int step; 74 75 /** 76 * Creates a sequence \( a_i, i < 0 <= n \) where \( a_i = start + i * step \) and \( n \) 77 * is such that \( a_n <= max \) and \( a_{n+1} > max \). 78 * 79 * @param start First value of the range. 80 * @param max Last value of the range that satisfies the above construction rule. 81 * @param step Increment. 82 */ Range(int start, int max, int step)83 public Range(int start, int max, int step) { 84 this.start = start; 85 this.max = max; 86 this.step = step; 87 88 final int s = (max - start) / step + 1; 89 this.size = s < 0 ? 0 : s; 90 } 91 92 /** 93 * Gets the number of elements contained in the range. 94 * 95 * @return the size of the range. 96 */ 97 public int size() { 98 return size; 99 } 100 101 /** {@inheritDoc} */ 102 public Iterator<Integer> iterator() { 103 return Incrementor.create() 104 .withStart(start) 105 .withMaximalCount(max + (step > 0 ? 1 : -1)) 106 .withIncrement(step); 107 } 108 } 109 110 /** 111 * Utility that increments a counter until a maximum is reached, at which point, the instance 112 * will by default throw a {@link MaxCountExceededException}. However, the user is able to 113 * override this behaviour by defining a custom {@link MaxCountExceededCallback callback}, in 114 * order to e.g. select which exception must be thrown. 115 */ 116 public static class Incrementor implements Iterator<Integer> { 117 /** Default callback. */ 118 private static final MaxCountExceededCallback CALLBACK = 119 new MaxCountExceededCallback() { 120 /** {@inheritDoc} */ 121 public void trigger(int max) throws MaxCountExceededException { 122 throw new MaxCountExceededException(max); 123 } 124 }; 125 126 /** Initial value the counter. */ 127 private final int init; 128 129 /** Upper limit for the counter. */ 130 private final int maximalCount; 131 132 /** Increment. */ 133 private final int increment; 134 135 /** Function called at counter exhaustion. */ 136 private final MaxCountExceededCallback maxCountCallback; 137 138 /** Current count. */ 139 private int count = 0; 140 141 /** 142 * Defines a method to be called at counter exhaustion. The {@link #trigger(int) trigger} 143 * method should usually throw an exception. 144 */ 145 public interface MaxCountExceededCallback { 146 /** 147 * Function called when the maximal count has been reached. 148 * 149 * @param maximalCount Maximal count. 150 * @throws MaxCountExceededException at counter exhaustion 151 */ trigger(int maximalCount)152 void trigger(int maximalCount) throws MaxCountExceededException; 153 } 154 155 /** 156 * Creates an incrementor. The counter will be exhausted either when {@code max} is reached 157 * or when {@code nTimes} increments have been performed. 158 * 159 * @param start Initial value. 160 * @param max Maximal count. 161 * @param step Increment. 162 * @param cb Function to be called when the maximal count has been reached. 163 * @throws NullArgumentException if {@code cb} is {@code null}. 164 */ Incrementor(int start, int max, int step, MaxCountExceededCallback cb)165 private Incrementor(int start, int max, int step, MaxCountExceededCallback cb) 166 throws NullArgumentException { 167 if (cb == null) { 168 throw new NullArgumentException(); 169 } 170 this.init = start; 171 this.maximalCount = max; 172 this.increment = step; 173 this.maxCountCallback = cb; 174 this.count = start; 175 } 176 177 /** 178 * Factory method that creates a default instance. The initial and maximal values are set to 179 * 0. For the new instance to be useful, the maximal count must be set by calling {@link 180 * #withMaximalCount(int) withMaximalCount}. 181 * 182 * @return an new instance. 183 */ create()184 public static Incrementor create() { 185 return new Incrementor(0, 0, 1, CALLBACK); 186 } 187 188 /** 189 * Creates a new instance with a given initial value. The counter is reset to the initial 190 * value. 191 * 192 * @param start Initial value of the counter. 193 * @return a new instance. 194 */ withStart(int start)195 public Incrementor withStart(int start) { 196 return new Incrementor(start, this.maximalCount, this.increment, this.maxCountCallback); 197 } 198 199 /** 200 * Creates a new instance with a given maximal count. The counter is reset to the initial 201 * value. 202 * 203 * @param max Maximal count. 204 * @return a new instance. 205 */ withMaximalCount(int max)206 public Incrementor withMaximalCount(int max) { 207 return new Incrementor(this.init, max, this.increment, this.maxCountCallback); 208 } 209 210 /** 211 * Creates a new instance with a given increment. The counter is reset to the initial value. 212 * 213 * @param step Increment. 214 * @return a new instance. 215 */ withIncrement(int step)216 public Incrementor withIncrement(int step) { 217 if (step == 0) { 218 throw new ZeroException(); 219 } 220 return new Incrementor(this.init, this.maximalCount, step, this.maxCountCallback); 221 } 222 223 /** 224 * Creates a new instance with a given callback. The counter is reset to the initial value. 225 * 226 * @param cb Callback to be called at counter exhaustion. 227 * @return a new instance. 228 */ withCallback(MaxCountExceededCallback cb)229 public Incrementor withCallback(MaxCountExceededCallback cb) { 230 return new Incrementor(this.init, this.maximalCount, this.increment, cb); 231 } 232 233 /** 234 * Gets the upper limit of the counter. 235 * 236 * @return the counter upper limit. 237 */ getMaximalCount()238 public int getMaximalCount() { 239 return maximalCount; 240 } 241 242 /** 243 * Gets the current count. 244 * 245 * @return the current count. 246 */ getCount()247 public int getCount() { 248 return count; 249 } 250 251 /** 252 * Checks whether incrementing the counter {@code nTimes} is allowed. 253 * 254 * @return {@code false} if calling {@link #increment()} will trigger a {@code 255 * MaxCountExceededException}, {@code true} otherwise. 256 */ canIncrement()257 public boolean canIncrement() { 258 return canIncrement(1); 259 } 260 261 /** 262 * Checks whether incrementing the counter several times is allowed. 263 * 264 * @param nTimes Number of increments. 265 * @return {@code false} if calling {@link #increment(int) increment(nTimes)} would call the 266 * {@link MaxCountExceededCallback callback} {@code true} otherwise. 267 */ canIncrement(int nTimes)268 public boolean canIncrement(int nTimes) { 269 final int finalCount = count + nTimes * increment; 270 return increment < 0 ? finalCount > maximalCount : finalCount < maximalCount; 271 } 272 273 /** 274 * Performs multiple increments. 275 * 276 * @param nTimes Number of increments. 277 * @throws MaxCountExceededException at counter exhaustion. 278 * @throws NotStrictlyPositiveException if {@code nTimes <= 0}. 279 * @see #increment() 280 */ increment(int nTimes)281 public void increment(int nTimes) throws MaxCountExceededException { 282 if (nTimes <= 0) { 283 throw new NotStrictlyPositiveException(nTimes); 284 } 285 286 if (!canIncrement(0)) { 287 maxCountCallback.trigger(maximalCount); 288 } 289 count += nTimes * increment; 290 } 291 292 /** 293 * Adds the increment value to the current iteration count. At counter exhaustion, this 294 * method will call the {@link MaxCountExceededCallback#trigger(int) trigger} method of the 295 * callback object passed to the {@link #withCallback(MaxCountExceededCallback)} method. If 296 * not explicitly set, a default callback is used that will throw a {@code 297 * MaxCountExceededException}. 298 * 299 * @throws MaxCountExceededException at counter exhaustion, unless a custom {@link 300 * MaxCountExceededCallback callback} has been set. 301 * @see #increment(int) 302 */ increment()303 public void increment() throws MaxCountExceededException { 304 increment(1); 305 } 306 307 /** {@inheritDoc} */ hasNext()308 public boolean hasNext() { 309 return canIncrement(0); 310 } 311 312 /** {@inheritDoc} */ next()313 public Integer next() { 314 final int value = count; 315 increment(); 316 return value; 317 } 318 319 /** 320 * Not applicable. 321 * 322 * @throws MathUnsupportedOperationException 323 */ remove()324 public void remove() { 325 throw new MathUnsupportedOperationException(); 326 } 327 } 328 } 329