UniformLongSampler.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.rng.sampling.distribution;

  18. import org.apache.commons.rng.UniformRandomProvider;

  19. /**
  20.  * Discrete uniform distribution sampler generating values of type {@code long}.
  21.  *
  22.  * <p>Sampling uses {@link UniformRandomProvider#nextLong}.</p>
  23.  *
  24.  * <p>When the range is a power of two the number of calls is 1 per sample.
  25.  * Otherwise a rejection algorithm is used to ensure uniformity. In the worst
  26.  * case scenario where the range spans half the range of a {@code long}
  27.  * (2<sup>63</sup> + 1) the expected number of calls is 2 per sample.</p>
  28.  *
  29.  * @since 1.4
  30.  */
  31. public abstract class UniformLongSampler implements SharedStateLongSampler {
  32.     /** Underlying source of randomness. */
  33.     protected final UniformRandomProvider rng;

  34.     /**
  35.      * Discrete uniform distribution sampler when the sample value is fixed.
  36.      */
  37.     private static final class FixedUniformLongSampler extends UniformLongSampler {
  38.         /** The value. */
  39.         private final long value;

  40.         /**
  41.          * @param value The value.
  42.          */
  43.         FixedUniformLongSampler(long value) {
  44.             // No requirement for the RNG
  45.             super(null);
  46.             this.value = value;
  47.         }

  48.         @Override
  49.         public long sample() {
  50.             return value;
  51.         }

  52.         @Override
  53.         public String toString() {
  54.             // No RNG to include in the string
  55.             return "Uniform deviate [X=" + value + "]";
  56.         }

  57.         @Override
  58.         public UniformLongSampler withUniformRandomProvider(UniformRandomProvider rng) {
  59.             // No requirement for the RNG
  60.             return this;
  61.         }
  62.     }

  63.     /**
  64.      * Discrete uniform distribution sampler when the range is a power of 2 and greater than 1.
  65.      * This sampler assumes the lower bound of the range is 0.
  66.      *
  67.      * <p>Note: This cannot be used when the range is 1 (2^0) as the shift would be 64-bits
  68.      * which is ignored by the shift operator.</p>
  69.      */
  70.     private static final class PowerOf2RangeUniformLongSampler extends UniformLongSampler {
  71.         /** Bit shift to apply to the long sample. */
  72.         private final int shift;

  73.         /**
  74.          * @param rng Generator of uniformly distributed random numbers.
  75.          * @param range Maximum range of the sample (exclusive).
  76.          * Must be a power of 2 greater than 2^0.
  77.          */
  78.         PowerOf2RangeUniformLongSampler(UniformRandomProvider rng,
  79.                                         long range) {
  80.             super(rng);
  81.             this.shift = Long.numberOfLeadingZeros(range) + 1;
  82.         }

  83.         /**
  84.          * @param rng Generator of uniformly distributed random numbers.
  85.          * @param source Source to copy.
  86.          */
  87.         PowerOf2RangeUniformLongSampler(UniformRandomProvider rng,
  88.                                         PowerOf2RangeUniformLongSampler source) {
  89.             super(rng);
  90.             this.shift = source.shift;
  91.         }

  92.         @Override
  93.         public long sample() {
  94.             // Use a bit shift to favour the most significant bits.
  95.             return rng.nextLong() >>> shift;
  96.         }

  97.         @Override
  98.         public UniformLongSampler withUniformRandomProvider(UniformRandomProvider rng) {
  99.             return new PowerOf2RangeUniformLongSampler(rng, this);
  100.         }
  101.     }

  102.     /**
  103.      * Discrete uniform distribution sampler when the range is small
  104.      * enough to fit in a positive long.
  105.      * This sampler assumes the lower bound of the range is 0 and the range is
  106.      * non-zero.
  107.      */
  108.     private static final class SmallRangeUniformLongSampler extends UniformLongSampler {
  109.         /** Maximum range of the sample (exclusive). */
  110.         private final long n;
  111.         /** Limit of the uniform range (inclusive) to sample a positive long.
  112.          * This is the largest positive multiple of {@code n} minus 1:
  113.          * {@code floor(2^63 / n) * n - 1}.
  114.          * The -1 changes the limit to an inclusive bound and allows support
  115.          * for a power of 2 range. */
  116.         private final long limit;

  117.         /**
  118.          * @param rng Generator of uniformly distributed random numbers.
  119.          * @param range Maximum range of the sample (exclusive).
  120.          */
  121.         SmallRangeUniformLongSampler(UniformRandomProvider rng,
  122.                                      long range) {
  123.             super(rng);
  124.             this.n = range;
  125.             // Set the upper limit for the positive long.
  126.             // The sample must be selected from the largest multiple
  127.             // of 'range' that fits within a positive value:
  128.             // limit = floor(2^63 / range) * range
  129.             //       = 2^63 - (2^63 % range)
  130.             // Sample:
  131.             //   X in [0, limit) or X in [0, limit - 1]
  132.             //   return X % range
  133.             // This is a one-off computation cost.
  134.             // The divide will truncate towards zero (do not use Math.floorDiv).
  135.             // Note: This is invalid if range is not strictly positive.
  136.             limit = (Long.MIN_VALUE / range) * -range - 1;
  137.         }

  138.         /**
  139.          * @param rng Generator of uniformly distributed random numbers.
  140.          * @param source Source to copy.
  141.          */
  142.         SmallRangeUniformLongSampler(UniformRandomProvider rng,
  143.                                      SmallRangeUniformLongSampler source) {
  144.             super(rng);
  145.             this.n = source.n;
  146.             this.limit = source.limit;
  147.         }

  148.         @Override
  149.         public long sample() {
  150.             // Note:
  151.             // This will have the same output as the rejection algorithm
  152.             // from o.a.c.rng.core.BaseProvider. The limit for the uniform
  153.             // positive value can be pre-computed. This ensures exactly
  154.             // 1 modulus operation per call.
  155.             for (;;) {
  156.                 // bits in [0, limit]
  157.                 final long bits = rng.nextLong() >>> 1;
  158.                 if (bits <= limit) {
  159.                     return bits % n;
  160.                 }
  161.             }
  162.         }

  163.         @Override
  164.         public UniformLongSampler withUniformRandomProvider(UniformRandomProvider rng) {
  165.             return new SmallRangeUniformLongSampler(rng, this);
  166.         }
  167.     }

  168.     /**
  169.      * Discrete uniform distribution sampler when the range between lower and upper is too large
  170.      * to fit in a positive long.
  171.      */
  172.     private static final class LargeRangeUniformLongSampler extends UniformLongSampler {
  173.         /** Lower bound. */
  174.         private final long lower;
  175.         /** Upper bound. */
  176.         private final long upper;

  177.         /**
  178.          * @param rng Generator of uniformly distributed random numbers.
  179.          * @param lower Lower bound (inclusive) of the distribution.
  180.          * @param upper Upper bound (inclusive) of the distribution.
  181.          */
  182.         LargeRangeUniformLongSampler(UniformRandomProvider rng,
  183.                                      long lower,
  184.                                      long upper) {
  185.             super(rng);
  186.             this.lower = lower;
  187.             this.upper = upper;
  188.         }

  189.         @Override
  190.         public long sample() {
  191.             // Use a simple rejection method.
  192.             // This is used when (upper-lower) >= Long.MAX_VALUE.
  193.             // This will loop on average 2 times in the worst case scenario
  194.             // when (upper-lower) == Long.MAX_VALUE.
  195.             while (true) {
  196.                 final long r = rng.nextLong();
  197.                 if (r >= lower &&
  198.                     r <= upper) {
  199.                     return r;
  200.                 }
  201.             }
  202.         }

  203.         @Override
  204.         public UniformLongSampler withUniformRandomProvider(UniformRandomProvider rng) {
  205.             return new LargeRangeUniformLongSampler(rng, lower, upper);
  206.         }
  207.     }

  208.     /**
  209.      * Adds an offset to an underlying discrete sampler.
  210.      */
  211.     private static final class OffsetUniformLongSampler extends UniformLongSampler {
  212.         /** The offset. */
  213.         private final long offset;
  214.         /** The long sampler. */
  215.         private final UniformLongSampler sampler;

  216.         /**
  217.          * @param offset The offset for the sample.
  218.          * @param sampler The discrete sampler.
  219.          */
  220.         OffsetUniformLongSampler(long offset,
  221.                                  UniformLongSampler sampler) {
  222.             // No requirement for the RNG
  223.             super(null);
  224.             this.offset = offset;
  225.             this.sampler = sampler;
  226.         }

  227.         @Override
  228.         public long sample() {
  229.             return offset + sampler.sample();
  230.         }

  231.         @Override
  232.         public String toString() {
  233.             return sampler.toString();
  234.         }

  235.         @Override
  236.         public UniformLongSampler withUniformRandomProvider(UniformRandomProvider rng) {
  237.             return new OffsetUniformLongSampler(offset, sampler.withUniformRandomProvider(rng));
  238.         }
  239.     }

  240.     /**
  241.      * @param rng Generator of uniformly distributed random numbers.
  242.      */
  243.     UniformLongSampler(UniformRandomProvider rng) {
  244.         this.rng = rng;
  245.     }

  246.     /** {@inheritDoc} */
  247.     @Override
  248.     public String toString() {
  249.         return "Uniform deviate [" + rng.toString() + "]";
  250.     }

  251.     /** {@inheritDoc} */
  252.     // Redeclare the signature to return a UniformLongSampler not a SharedStateLongSampler
  253.     @Override
  254.     public abstract UniformLongSampler withUniformRandomProvider(UniformRandomProvider rng);

  255.     /**
  256.      * Creates a new discrete uniform distribution sampler.
  257.      *
  258.      * @param rng Generator of uniformly distributed random numbers.
  259.      * @param lower Lower bound (inclusive) of the distribution.
  260.      * @param upper Upper bound (inclusive) of the distribution.
  261.      * @return the sampler
  262.      * @throws IllegalArgumentException if {@code lower > upper}.
  263.      */
  264.     public static UniformLongSampler of(UniformRandomProvider rng,
  265.                                         long lower,
  266.                                         long upper) {
  267.         if (lower > upper) {
  268.             throw new IllegalArgumentException(lower  + " > " + upper);
  269.         }

  270.         // Choose the algorithm depending on the range

  271.         // Edge case for no range.
  272.         // This must be done first as the methods to handle lower == 0
  273.         // do not handle upper == 0.
  274.         if (upper == lower) {
  275.             return new FixedUniformLongSampler(lower);
  276.         }

  277.         // Algorithms to ignore the lower bound if it is zero.
  278.         if (lower == 0) {
  279.             return createZeroBoundedSampler(rng, upper);
  280.         }

  281.         final long range = (upper - lower) + 1;
  282.         // Check power of 2 first to handle range == 2^63.
  283.         if (isPowerOf2(range)) {
  284.             return new OffsetUniformLongSampler(lower,
  285.                                                 new PowerOf2RangeUniformLongSampler(rng, range));
  286.         }
  287.         if (range <= 0) {
  288.             // The range is too wide to fit in a positive long (larger
  289.             // than 2^63); use a simple rejection method.
  290.             // Note: if range == 0 then the input is [Long.MIN_VALUE, Long.MAX_VALUE].
  291.             // No specialisation exists for this and it is handled as a large range.
  292.             return new LargeRangeUniformLongSampler(rng, lower, upper);
  293.         }
  294.         // Use a sample from the range added to the lower bound.
  295.         return new OffsetUniformLongSampler(lower,
  296.                                             new SmallRangeUniformLongSampler(rng, range));
  297.     }

  298.     /**
  299.      * Create a new sampler for the range {@code 0} inclusive to {@code upper} inclusive.
  300.      *
  301.      * <p>This can handle any positive {@code upper}.
  302.      *
  303.      * @param rng Generator of uniformly distributed random numbers.
  304.      * @param upper Upper bound (inclusive) of the distribution. Must be positive.
  305.      * @return the sampler
  306.      */
  307.     private static UniformLongSampler createZeroBoundedSampler(UniformRandomProvider rng,
  308.                                                                long upper) {
  309.         // Note: Handle any range up to 2^63 (which is negative as a signed
  310.         // 64-bit long but handled as a power of 2)
  311.         final long range = upper + 1;
  312.         return isPowerOf2(range) ?
  313.             new PowerOf2RangeUniformLongSampler(rng, range) :
  314.             new SmallRangeUniformLongSampler(rng, range);
  315.     }

  316.     /**
  317.      * Checks if the value is a power of 2.
  318.      *
  319.      * <p>This returns {@code true} for the value {@code Long.MIN_VALUE} which can be
  320.      * handled as an unsigned long of 2^63.</p>
  321.      *
  322.      * @param value Value.
  323.      * @return {@code true} if a power of 2
  324.      */
  325.     private static boolean isPowerOf2(final long value) {
  326.         return value != 0 && (value & (value - 1)) == 0;
  327.     }
  328. }