IntegerSequence.java

  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.math4.legacy.core;

  18. import java.util.Iterator;
  19. import java.util.NoSuchElementException;
  20. import org.apache.commons.math4.legacy.exception.MaxCountExceededException;
  21. import org.apache.commons.math4.legacy.exception.NullArgumentException;
  22. import org.apache.commons.math4.legacy.exception.MathUnsupportedOperationException;
  23. import org.apache.commons.math4.legacy.exception.NotStrictlyPositiveException;
  24. import org.apache.commons.math4.legacy.exception.ZeroException;

  25. /**
  26.  * Provides a sequence of integers.
  27.  *
  28.  * @since 3.6
  29.  */
  30. public final class IntegerSequence {
  31.     /**
  32.      * Utility class contains only static methods.
  33.      */
  34.     private IntegerSequence() {}

  35.     /**
  36.      * Creates a sequence {@code [start .. end]}.
  37.      * It calls {@link #range(int,int,int) range(start, end, 1)}.
  38.      *
  39.      * @param start First value of the range.
  40.      * @param end Last value of the range.
  41.      * @return a range.
  42.      */
  43.     public static Range range(int start,
  44.                               int end) {
  45.         return range(start, end, 1);
  46.     }

  47.     /**
  48.      * Creates a sequence <code>a<sub>i</sub>, i &lt; 0 &lt; n</code>
  49.      * where <code>a<sub>i</sub> = start + i * step</code>
  50.      * and {@code n} is such that <code>a<sub>n</sub> &lt;= max</code>
  51.      * and  <code>a<sub>n+1</sub> &gt; max</code>.
  52.      *
  53.      * @param start First value of the range.
  54.      * @param max Last value of the range that satisfies the above
  55.      * construction rule.
  56.      * @param step Increment.
  57.      * @return a range.
  58.      */
  59.     public static Range range(final int start,
  60.                               final int max,
  61.                               final int step) {
  62.         return new Range(start, max, step);
  63.     }

  64.     /**
  65.      * Generates a sequence of integers.
  66.      */
  67.     public static class Range implements Iterable<Integer> {
  68.         /** Number of integers contained in this range. */
  69.         private final int size;
  70.         /** First value. */
  71.         private final int start;
  72.         /** Final value. */
  73.         private final int max;
  74.         /** Increment. */
  75.         private final int step;

  76.         /**
  77.          * Creates a sequence <code>a<sub>i</sub>, i &lt; 0 &lt; n</code>
  78.          * where <code>a<sub>i</sub> = start + i * step</code>
  79.          * and {@code n} is such that <code>a<sub>n</sub> &lt;= max</code>
  80.          * and  <code>a<sub>n+1</sub> &gt; max</code>.
  81.          *
  82.          * @param start First value of the range.
  83.          * @param max Last value of the range that satisfies the above
  84.          * construction rule.
  85.          * @param step Increment.
  86.          */
  87.         public Range(int start,
  88.                      int max,
  89.                      int step) {
  90.             this.start = start;
  91.             this.max = max;
  92.             this.step = step;

  93.             final int s = (max - start) / step + 1;
  94.             this.size = s < 0 ? 0 : s;
  95.         }

  96.         /**
  97.          * Gets the number of elements contained in the range.
  98.          *
  99.          * @return the size of the range.
  100.          */
  101.         public int size() {
  102.             return size;
  103.         }

  104.         /** {@inheritDoc} */
  105.         @Override
  106.         public Iterator<Integer> iterator() {
  107.             return Incrementor.create()
  108.                 .withStart(start)
  109.                 .withMaximalCount(max + (step > 0 ? 1 : -1))
  110.                 .withIncrement(step);
  111.         }
  112.     }

  113.     /**
  114.      * Utility that increments a counter until a maximum is reached, at
  115.      * which point, the instance will by default throw a
  116.      * {@link MaxCountExceededException}.
  117.      * However, the user is able to override this behaviour by defining a
  118.      * custom {@link MaxCountExceededCallback callback}, in order to e.g.
  119.      * select which exception must be thrown.
  120.      */
  121.     public static final class Incrementor implements Iterator<Integer> {
  122.         /** Default callback. */
  123.         private static final MaxCountExceededCallback CALLBACK
  124.             = new MaxCountExceededCallback() {
  125.                     /** {@inheritDoc} */
  126.                     @Override
  127.                     public void trigger(int max) throws MaxCountExceededException {
  128.                         throw new MaxCountExceededException(max);
  129.                     }
  130.                 };

  131.         /** Initial value the counter. */
  132.         private final int init;
  133.         /** Upper limit for the counter. */
  134.         private final int maximalCount;
  135.         /** Increment. */
  136.         private final int increment;
  137.         /** Function called at counter exhaustion. */
  138.         private final MaxCountExceededCallback maxCountCallback;
  139.         /** Current count. */
  140.         private int count;

  141.         /**
  142.          * Defines a method to be called at counter exhaustion.
  143.          * The {@link #trigger(int) trigger} 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.              */
  152.             void trigger(int maximalCount) throws MaxCountExceededException;
  153.         }

  154.         /**
  155.          * Creates an incrementor.
  156.          * 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.          */
  165.         private Incrementor(int start,
  166.                             int max,
  167.                             int step,
  168.                             MaxCountExceededCallback cb)
  169.             throws NullArgumentException {
  170.             if (cb == null) {
  171.                 throw new NullArgumentException();
  172.             }
  173.             this.init = start;
  174.             this.maximalCount = max;
  175.             this.increment = step;
  176.             this.maxCountCallback = cb;
  177.             this.count = start;
  178.         }

  179.         /**
  180.          * Factory method that creates a default instance.
  181.          * The initial and maximal values are set to 0.
  182.          * For the new instance to be useful, the maximal count must be set
  183.          * by calling {@link #withMaximalCount(int) withMaximalCount}.
  184.          *
  185.          * @return an new instance.
  186.          */
  187.         public static Incrementor create() {
  188.             return new Incrementor(0, 0, 1, CALLBACK);
  189.         }

  190.         /**
  191.          * Creates a new instance with a given initial value.
  192.          * The counter is reset to the initial value.
  193.          *
  194.          * @param start Initial value of the counter.
  195.          * @return a new instance.
  196.          */
  197.         public Incrementor withStart(int start) {
  198.             return new Incrementor(start,
  199.                                    this.maximalCount,
  200.                                    this.increment,
  201.                                    this.maxCountCallback);
  202.         }

  203.         /**
  204.          * Creates a new instance with a given maximal count.
  205.          * The counter is reset to the initial value.
  206.          *
  207.          * @param max Maximal count.
  208.          * @return a new instance.
  209.          */
  210.         public Incrementor withMaximalCount(int max) {
  211.             return new Incrementor(this.init,
  212.                                    max,
  213.                                    this.increment,
  214.                                    this.maxCountCallback);
  215.         }

  216.         /**
  217.          * Creates a new instance with a given increment.
  218.          * The counter is reset to the initial value.
  219.          *
  220.          * @param step Increment.
  221.          * @return a new instance.
  222.          */
  223.         public Incrementor withIncrement(int step) {
  224.             if (step == 0) {
  225.                 throw new ZeroException();
  226.             }
  227.             return new Incrementor(this.init,
  228.                                    this.maximalCount,
  229.                                    step,
  230.                                    this.maxCountCallback);
  231.         }

  232.         /**
  233.          * Creates a new instance with a given callback.
  234.          * The counter is reset to the initial value.
  235.          *
  236.          * @param cb Callback to be called at counter exhaustion.
  237.          * @return a new instance.
  238.          */
  239.         public Incrementor withCallback(MaxCountExceededCallback cb) {
  240.             return new Incrementor(this.init,
  241.                                    this.maximalCount,
  242.                                    this.increment,
  243.                                    cb);
  244.         }

  245.         /**
  246.          * Gets the upper limit of the counter.
  247.          *
  248.          * @return the counter upper limit.
  249.          */
  250.         public int getMaximalCount() {
  251.             return maximalCount;
  252.         }

  253.         /**
  254.          * Gets the current count.
  255.          *
  256.          * @return the current count.
  257.          */
  258.         public int getCount() {
  259.             return count;
  260.         }

  261.         /**
  262.          * Checks whether incrementing the counter {@code nTimes} is allowed.
  263.          *
  264.          * @return {@code false} if calling {@link #increment()}
  265.          * will trigger a {@code MaxCountExceededException},
  266.          * {@code true} otherwise.
  267.          */
  268.         public boolean canIncrement() {
  269.             return canIncrement(1);
  270.         }

  271.         /**
  272.          * Checks whether incrementing the counter several times is allowed.
  273.          *
  274.          * @param nTimes Number of increments.
  275.          * @return {@code false} if calling {@link #increment(int)
  276.          * increment(nTimes)} would call the {@link MaxCountExceededCallback callback}
  277.          * {@code true} otherwise.
  278.          */
  279.         public boolean canIncrement(int nTimes) {
  280.             final int finalCount = count + nTimes * increment;
  281.             return increment < 0 ?
  282.                 finalCount > maximalCount :
  283.                 finalCount < maximalCount;
  284.         }

  285.         /**
  286.          * Performs multiple increments.
  287.          *
  288.          * @param nTimes Number of increments.
  289.          * @throws MaxCountExceededException at counter exhaustion.
  290.          * @throws NotStrictlyPositiveException if {@code nTimes <= 0}.
  291.          *
  292.          * @see #increment()
  293.          */
  294.         public void increment(int nTimes) throws MaxCountExceededException {
  295.             if (nTimes <= 0) {
  296.                 throw new NotStrictlyPositiveException(nTimes);
  297.             }

  298.             count += nTimes * increment;

  299.             if (!canIncrement(0)) {
  300.                 maxCountCallback.trigger(maximalCount);
  301.             }
  302.         }

  303.         /**
  304.          * Adds the increment value to the current iteration count.
  305.          * At counter exhaustion, this method will call the
  306.          * {@link MaxCountExceededCallback#trigger(int) trigger} method of the
  307.          * callback object passed to the
  308.          * {@link #withCallback(MaxCountExceededCallback)} method.
  309.          * If not explicitly set, a default callback is used that will throw
  310.          * a {@code MaxCountExceededException}.
  311.          *
  312.          * @throws MaxCountExceededException at counter exhaustion, unless a
  313.          * custom {@link MaxCountExceededCallback callback} has been set.
  314.          *
  315.          * @see #increment(int)
  316.          */
  317.         public void increment() throws MaxCountExceededException {
  318.             increment(1);
  319.         }

  320.         /** {@inheritDoc} */
  321.         @Override
  322.         public boolean hasNext() {
  323.             return canIncrement(0);
  324.         }

  325.         /** {@inheritDoc} */
  326.         @Override
  327.         public Integer next() {
  328.             if (canIncrement(0)) {
  329.                 final int value = count;
  330.                 count += increment;
  331.                 return value;
  332.             } else {
  333.                 // Contract for "Iterator".
  334.                 throw new NoSuchElementException();
  335.             }
  336.         }

  337.         /**
  338.          * Not applicable.
  339.          *
  340.          * @throws MathUnsupportedOperationException always
  341.          */
  342.         @Override
  343.         public void remove() {
  344.             throw new MathUnsupportedOperationException();
  345.         }
  346.     }
  347. }