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