001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.math4.legacy.core; 018 019import java.util.Iterator; 020import java.util.NoSuchElementException; 021import org.apache.commons.math4.legacy.exception.MaxCountExceededException; 022import org.apache.commons.math4.legacy.exception.NullArgumentException; 023import org.apache.commons.math4.legacy.exception.MathUnsupportedOperationException; 024import org.apache.commons.math4.legacy.exception.NotStrictlyPositiveException; 025import org.apache.commons.math4.legacy.exception.ZeroException; 026 027/** 028 * Provides a sequence of integers. 029 * 030 * @since 3.6 031 */ 032public final class IntegerSequence { 033 /** 034 * Utility class contains only static methods. 035 */ 036 private IntegerSequence() {} 037 038 /** 039 * Creates a sequence {@code [start .. end]}. 040 * It calls {@link #range(int,int,int) range(start, end, 1)}. 041 * 042 * @param start First value of the range. 043 * @param end Last value of the range. 044 * @return a range. 045 */ 046 public static Range range(int start, 047 int end) { 048 return range(start, end, 1); 049 } 050 051 /** 052 * Creates a sequence <code>a<sub>i</sub>, i < 0 < n</code> 053 * where <code>a<sub>i</sub> = start + i * step</code> 054 * and {@code n} is such that <code>a<sub>n</sub> <= max</code> 055 * and <code>a<sub>n+1</sub> > max</code>. 056 * 057 * @param start First value of the range. 058 * @param max Last value of the range that satisfies the above 059 * construction rule. 060 * @param step Increment. 061 * @return a range. 062 */ 063 public static Range range(final int start, 064 final int max, 065 final int step) { 066 return new Range(start, max, step); 067 } 068 069 /** 070 * Generates a sequence of integers. 071 */ 072 public static class Range implements Iterable<Integer> { 073 /** Number of integers contained in this range. */ 074 private final int size; 075 /** First value. */ 076 private final int start; 077 /** Final value. */ 078 private final int max; 079 /** Increment. */ 080 private final int step; 081 082 /** 083 * Creates a sequence <code>a<sub>i</sub>, i < 0 < n</code> 084 * where <code>a<sub>i</sub> = start + i * step</code> 085 * and {@code n} is such that <code>a<sub>n</sub> <= max</code> 086 * and <code>a<sub>n+1</sub> > max</code>. 087 * 088 * @param start First value of the range. 089 * @param max Last value of the range that satisfies the above 090 * construction rule. 091 * @param step Increment. 092 */ 093 public Range(int start, 094 int max, 095 int step) { 096 this.start = start; 097 this.max = max; 098 this.step = step; 099 100 final int s = (max - start) / step + 1; 101 this.size = s < 0 ? 0 : s; 102 } 103 104 /** 105 * Gets the number of elements contained in the range. 106 * 107 * @return the size of the range. 108 */ 109 public int size() { 110 return size; 111 } 112 113 /** {@inheritDoc} */ 114 @Override 115 public Iterator<Integer> iterator() { 116 return Incrementor.create() 117 .withStart(start) 118 .withMaximalCount(max + (step > 0 ? 1 : -1)) 119 .withIncrement(step); 120 } 121 } 122 123 /** 124 * Utility that increments a counter until a maximum is reached, at 125 * which point, the instance will by default throw a 126 * {@link MaxCountExceededException}. 127 * However, the user is able to override this behaviour by defining a 128 * custom {@link MaxCountExceededCallback callback}, in order to e.g. 129 * select which exception must be thrown. 130 */ 131 public static final class Incrementor implements Iterator<Integer> { 132 /** Default callback. */ 133 private static final MaxCountExceededCallback CALLBACK 134 = new MaxCountExceededCallback() { 135 /** {@inheritDoc} */ 136 @Override 137 public void trigger(int max) throws MaxCountExceededException { 138 throw new MaxCountExceededException(max); 139 } 140 }; 141 142 /** Initial value the counter. */ 143 private final int init; 144 /** Upper limit for the counter. */ 145 private final int maximalCount; 146 /** Increment. */ 147 private final int increment; 148 /** Function called at counter exhaustion. */ 149 private final MaxCountExceededCallback maxCountCallback; 150 /** Current count. */ 151 private int count; 152 153 /** 154 * Defines a method to be called at counter exhaustion. 155 * The {@link #trigger(int) trigger} method should usually throw an exception. 156 */ 157 public interface MaxCountExceededCallback { 158 /** 159 * Function called when the maximal count has been reached. 160 * 161 * @param maximalCount Maximal count. 162 * @throws MaxCountExceededException at counter exhaustion 163 */ 164 void trigger(int maximalCount) throws MaxCountExceededException; 165 } 166 167 /** 168 * Creates an incrementor. 169 * The counter will be exhausted either when {@code max} is reached 170 * or when {@code nTimes} increments have been performed. 171 * 172 * @param start Initial value. 173 * @param max Maximal count. 174 * @param step Increment. 175 * @param cb Function to be called when the maximal count has been reached. 176 * @throws NullArgumentException if {@code cb} is {@code null}. 177 */ 178 private Incrementor(int start, 179 int max, 180 int step, 181 MaxCountExceededCallback cb) 182 throws NullArgumentException { 183 if (cb == null) { 184 throw new NullArgumentException(); 185 } 186 this.init = start; 187 this.maximalCount = max; 188 this.increment = step; 189 this.maxCountCallback = cb; 190 this.count = start; 191 } 192 193 /** 194 * Factory method that creates a default instance. 195 * The initial and maximal values are set to 0. 196 * For the new instance to be useful, the maximal count must be set 197 * by calling {@link #withMaximalCount(int) withMaximalCount}. 198 * 199 * @return an new instance. 200 */ 201 public static Incrementor create() { 202 return new Incrementor(0, 0, 1, CALLBACK); 203 } 204 205 /** 206 * Creates a new instance with a given initial value. 207 * The counter is reset to the initial value. 208 * 209 * @param start Initial value of the counter. 210 * @return a new instance. 211 */ 212 public Incrementor withStart(int start) { 213 return new Incrementor(start, 214 this.maximalCount, 215 this.increment, 216 this.maxCountCallback); 217 } 218 219 /** 220 * Creates a new instance with a given maximal count. 221 * The counter is reset to the initial value. 222 * 223 * @param max Maximal count. 224 * @return a new instance. 225 */ 226 public Incrementor withMaximalCount(int max) { 227 return new Incrementor(this.init, 228 max, 229 this.increment, 230 this.maxCountCallback); 231 } 232 233 /** 234 * Creates a new instance with a given increment. 235 * The counter is reset to the initial value. 236 * 237 * @param step Increment. 238 * @return a new instance. 239 */ 240 public Incrementor withIncrement(int step) { 241 if (step == 0) { 242 throw new ZeroException(); 243 } 244 return new Incrementor(this.init, 245 this.maximalCount, 246 step, 247 this.maxCountCallback); 248 } 249 250 /** 251 * Creates a new instance with a given callback. 252 * The counter is reset to the initial value. 253 * 254 * @param cb Callback to be called at counter exhaustion. 255 * @return a new instance. 256 */ 257 public Incrementor withCallback(MaxCountExceededCallback cb) { 258 return new Incrementor(this.init, 259 this.maximalCount, 260 this.increment, 261 cb); 262 } 263 264 /** 265 * Gets the upper limit of the counter. 266 * 267 * @return the counter upper limit. 268 */ 269 public int getMaximalCount() { 270 return maximalCount; 271 } 272 273 /** 274 * Gets the current count. 275 * 276 * @return the current count. 277 */ 278 public int getCount() { 279 return count; 280 } 281 282 /** 283 * Checks whether incrementing the counter {@code nTimes} is allowed. 284 * 285 * @return {@code false} if calling {@link #increment()} 286 * will trigger a {@code MaxCountExceededException}, 287 * {@code true} otherwise. 288 */ 289 public boolean canIncrement() { 290 return canIncrement(1); 291 } 292 293 /** 294 * Checks whether incrementing the counter several times is allowed. 295 * 296 * @param nTimes Number of increments. 297 * @return {@code false} if calling {@link #increment(int) 298 * increment(nTimes)} would call the {@link MaxCountExceededCallback callback} 299 * {@code true} otherwise. 300 */ 301 public boolean canIncrement(int nTimes) { 302 final int finalCount = count + nTimes * increment; 303 return increment < 0 ? 304 finalCount > maximalCount : 305 finalCount < maximalCount; 306 } 307 308 /** 309 * Performs multiple increments. 310 * 311 * @param nTimes Number of increments. 312 * @throws MaxCountExceededException at counter exhaustion. 313 * @throws NotStrictlyPositiveException if {@code nTimes <= 0}. 314 * 315 * @see #increment() 316 */ 317 public void increment(int nTimes) throws MaxCountExceededException { 318 if (nTimes <= 0) { 319 throw new NotStrictlyPositiveException(nTimes); 320 } 321 322 count += nTimes * increment; 323 324 if (!canIncrement(0)) { 325 maxCountCallback.trigger(maximalCount); 326 } 327 } 328 329 /** 330 * Adds the increment value to the current iteration count. 331 * At counter exhaustion, this method will call the 332 * {@link MaxCountExceededCallback#trigger(int) trigger} method of the 333 * callback object passed to the 334 * {@link #withCallback(MaxCountExceededCallback)} method. 335 * If not explicitly set, a default callback is used that will throw 336 * a {@code MaxCountExceededException}. 337 * 338 * @throws MaxCountExceededException at counter exhaustion, unless a 339 * custom {@link MaxCountExceededCallback callback} has been set. 340 * 341 * @see #increment(int) 342 */ 343 public void increment() throws MaxCountExceededException { 344 increment(1); 345 } 346 347 /** {@inheritDoc} */ 348 @Override 349 public boolean hasNext() { 350 return canIncrement(0); 351 } 352 353 /** {@inheritDoc} */ 354 @Override 355 public Integer next() { 356 if (canIncrement(0)) { 357 final int value = count; 358 count += increment; 359 return value; 360 } else { 361 // Contract for "Iterator". 362 throw new NoSuchElementException(); 363 } 364 } 365 366 /** 367 * Not applicable. 368 * 369 * @throws MathUnsupportedOperationException always 370 */ 371 @Override 372 public void remove() { 373 throw new MathUnsupportedOperationException(); 374 } 375 } 376}