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 18 package org.apache.commons.rng.simple.internal; 19 20 import org.apache.commons.rng.UniformRandomProvider; 21 import org.apache.commons.rng.core.util.NumberFactory; 22 23 /** 24 * Utility for creating seeds. 25 */ 26 final class SeedUtils { 27 /** The modulus {@code 256 % 9}. */ 28 private static final int MOD_09 = 256 % 9; 29 /** The modulus {@code 256 % 10}. */ 30 private static final int MOD_10 = 256 % 10; 31 /** The modulus {@code 256 % 11}. */ 32 private static final int MOD_11 = 256 % 11; 33 /** The modulus {@code 256 % 12}. */ 34 private static final int MOD_12 = 256 % 12; 35 /** The modulus {@code 256 % 13}. */ 36 private static final int MOD_13 = 256 % 13; 37 /** The modulus {@code 256 % 14}. */ 38 private static final int MOD_14 = 256 % 14; 39 /** The modulus {@code 256 % 15}. */ 40 private static final int MOD_15 = 256 % 15; 41 /** The 16 hex digits in an array. */ 42 private static final byte[] HEX_DIGIT_ARRAY = {0xf, 0xe, 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 43 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0x0}; 44 45 /** 46 * Provider of unsigned 8-bit integers. 47 */ 48 private static class UnsignedByteProvider { 49 /** Mask to extract the lowest 2 bits from an integer. */ 50 private static final int MASK_2_BITS = 0x3; 51 /** Mask to extract the lowest 8 bits from an integer. */ 52 private static final int MASK_8_BITS = 0xff; 53 54 /** Source of randomness. */ 55 private final UniformRandomProvider rng; 56 /** The current 32-bits of randomness. */ 57 private int bits; 58 /** The counter tracking the bits to extract. */ 59 private int counter; 60 61 /** 62 * @param rng Source of randomness. 63 */ 64 UnsignedByteProvider(UniformRandomProvider rng) { 65 this.rng = rng; 66 } 67 68 /** 69 * Return the next unsigned byte. 70 * 71 * @return Value in the range[0,255] 72 */ 73 int nextUnsignedByte() { 74 // Every 4 bytes generate a new 32-bit value 75 final int count = counter & MASK_2_BITS; 76 counter++; 77 if (count == 0) { 78 bits = rng.nextInt(); 79 // Consume the first byte 80 return bits & MASK_8_BITS; 81 } 82 // Consume a remaining byte (occurs 3 times) 83 bits >>>= 8; 84 return bits & MASK_8_BITS; 85 } 86 } 87 88 /** 89 * Class contains only static methods. 90 */ 91 private SeedUtils() {} 92 93 /** 94 * Creates an {@code int} containing a permutation of 8 hex digits chosen from 16. 95 * 96 * @param rng Source of randomness. 97 * @return hex digit permutation. 98 */ 99 static int createIntHexPermutation(UniformRandomProvider rng) { 100 final UnsignedByteProvider provider = new UnsignedByteProvider(rng); 101 return createUpperBitsHexPermutation(provider); 102 } 103 104 /** 105 * Creates a {@code long} containing a permutation of 8 hex digits chosen from 16 in 106 * the upper and lower 32-bits. 107 * 108 * @param rng Source of randomness. 109 * @return hex digit permutation. 110 */ 111 static long createLongHexPermutation(UniformRandomProvider rng) { 112 final UnsignedByteProvider provider = new UnsignedByteProvider(rng); 113 // Extract upper bits and combine with a second sample 114 return NumberFactory.makeLong(createUpperBitsHexPermutation(provider), 115 createUpperBitsHexPermutation(provider)); 116 } 117 118 /** 119 * Creates a {@code int} containing a permutation of 8 hex digits chosen from 16. 120 * 121 * @param provider Source of randomness. 122 * @return hex digit permutation. 123 */ 124 private static int createUpperBitsHexPermutation(UnsignedByteProvider provider) { 125 // Compute a Fisher-Yates shuffle in-place on the 16 hex digits. 126 // Each digit is chosen uniformly from the remaining digits. 127 // The value is swapped with the current digit. 128 129 // The following is an optimised sampling algorithm that generates 130 // a uniform deviate in the range [0,n) from an unsigned byte. 131 // The expected number of samples is 256/n. 132 // This has a bias when n does not divide into 256. So samples must 133 // be rejected at a rate of (256 % n) / 256: 134 // n 256 % n Rejection rate 135 // 9 4 0.015625 136 // 10 6 0.0234375 137 // 11 3 0.01171875 138 // 12 4 0.015625 139 // 13 9 0.03515625 140 // 14 4 0.015625 141 // 15 1 0.00390625 142 // 16 0 0 143 // The probability of no rejections is 1 - (1-p15) * (1-p14) ... = 0.115 144 145 final byte[] digits = HEX_DIGIT_ARRAY.clone(); 146 147 // The first digit has no bias. Get an index using a mask to avoid modulus. 148 int bits = copyToOutput(digits, 0, 15, provider.nextUnsignedByte() & 0xf); 149 150 // All remaining digits have a bias so use the rejection algorithm to find 151 // an appropriate uniform deviate in the range [0,n) 152 bits = copyToOutput(digits, bits, 14, nextUnsignedByteInRange(provider, MOD_15, 15)); 153 bits = copyToOutput(digits, bits, 13, nextUnsignedByteInRange(provider, MOD_14, 14)); 154 bits = copyToOutput(digits, bits, 12, nextUnsignedByteInRange(provider, MOD_13, 13)); 155 bits = copyToOutput(digits, bits, 11, nextUnsignedByteInRange(provider, MOD_12, 12)); 156 bits = copyToOutput(digits, bits, 10, nextUnsignedByteInRange(provider, MOD_11, 11)); 157 bits = copyToOutput(digits, bits, 9, nextUnsignedByteInRange(provider, MOD_10, 10)); 158 bits = copyToOutput(digits, bits, 8, nextUnsignedByteInRange(provider, MOD_09, 9)); 159 160 return bits; 161 } 162 163 164 /** 165 * Get the next unsigned byte in the range {@code [0,n)} rejecting any over-represented 166 * sample value using the pre-computed modulus. 167 * 168 * <p>This algorithm is as per Lemire (2019) adapted for 8-bit arithmetic.</p> 169 * 170 * @param provider Provider of bytes. 171 * @param threshold Modulus threshold {@code 256 % n}. 172 * @param n Upper range (exclusive) 173 * @return Value. 174 * @see <a href="https://arxiv.org/abs/1805.10941"> 175 * Lemire (2019): Fast Random Integer Generation in an Interval</a> 176 */ 177 private static int nextUnsignedByteInRange(UnsignedByteProvider provider, int threshold, int n) { 178 // Rejection method using multiply by a fraction: 179 // n * [0, 2^8 - 1) 180 // ------------ 181 // 2^8 182 // The result is mapped back to an integer and will be in the range [0, n) 183 int m; 184 do { 185 // Compute product of n * [0, 2^8 - 1) 186 m = n * provider.nextUnsignedByte(); 187 188 // Test the sample uniformity. 189 } while ((m & 0xff) < threshold); 190 // Final divide by 2^8 191 return m >>> 8; 192 } 193 194 /** 195 * Copy the lower hex digit to the output bits. Swap the upper hex digit into 196 * the lower position. This is equivalent to a swap step of a Fisher-Yates shuffle on 197 * an array but the output of the shuffle are written to the bits. 198 * 199 * @param digits Digits. 200 * @param bits Bits. 201 * @param upper Upper index. 202 * @param lower Lower index. 203 * @return Updated bits. 204 */ 205 private static int copyToOutput(byte[] digits, int bits, int upper, int lower) { 206 // Move the existing bits up and append the next hex digit. 207 // This is equivalent to swapping lower to upper. 208 final int newbits = bits << 4 | digits[lower] & 0xf; 209 // Swap upper to lower. 210 digits[lower] = digits[upper]; 211 return newbits; 212 } 213 }