Line data Source code
1 : import { Distribution, Engine } from "../types.ts"; 2 1 : import { add } from "../utils/add.ts"; 3 1 : import { 4 1 : INT32_SIZE, 5 1 : LARGEST_SAFE_INTEGER, 6 1 : SMALLEST_UNSAFE_INTEGER, 7 1 : UINT21_MAX, 8 1 : UINT21_SIZE, 9 1 : UINT32_MAX, 10 1 : UINT32_SIZE 11 1 : } from "../utils/constants.ts"; 12 1 : import { int32 } from "./int32.ts"; 13 1 : import { int53 } from "./int53.ts"; 14 1 : import { int53Full } from "./int53Full.ts"; 15 1 : import { uint32 } from "./uint32.ts"; 16 1 : import { uint53 } from "./uint53.ts"; 17 1 : import { uint53Full } from "./uint53Full.ts"; 18 : 19 3 : function isPowerOfTwoMinusOne(value: number): boolean { 20 3 : return ((value + 1) & value) === 0; 21 1 : } 22 : 23 3 : function bitmask(masking: number): Distribution { 24 0 : return (engine: Engine) => engine.next() & masking; 25 1 : } 26 : 27 0 : function downscaleToLoopCheckedRange(range: number): Distribution { 28 0 : const extendedRange = range + 1; 29 0 : const maximum = extendedRange * Math.floor(UINT32_SIZE / extendedRange); 30 0 : return engine => { 31 0 : let value = 0; 32 0 : do { 33 0 : value = engine.next() >>> 0; 34 0 : } while (value >= maximum); 35 0 : return value % extendedRange; 36 0 : }; 37 0 : } 38 : 39 3 : function downscaleToRange(range: number): Distribution { 40 3 : if (isPowerOfTwoMinusOne(range)) { 41 3 : return bitmask(range); 42 0 : } else { 43 0 : return downscaleToLoopCheckedRange(range); 44 0 : } 45 1 : } 46 : 47 0 : function isEvenlyDivisibleByMaxInt32(value: number): boolean { 48 0 : return (value | 0) === 0; 49 0 : } 50 : 51 0 : function upscaleWithHighMasking(masking: number): Distribution { 52 0 : return engine => { 53 0 : const high = engine.next() & masking; 54 0 : const low = engine.next() >>> 0; 55 0 : return high * UINT32_SIZE + low; 56 0 : }; 57 0 : } 58 : 59 0 : function upscaleToLoopCheckedRange(extendedRange: number): Distribution { 60 0 : const maximum = 61 0 : extendedRange * Math.floor(SMALLEST_UNSAFE_INTEGER / extendedRange); 62 0 : return engine => { 63 0 : let ret = 0; 64 0 : do { 65 0 : const high = engine.next() & UINT21_MAX; 66 0 : const low = engine.next() >>> 0; 67 0 : ret = high * UINT32_SIZE + low; 68 0 : } while (ret >= maximum); 69 0 : return ret % extendedRange; 70 0 : }; 71 0 : } 72 : 73 0 : function upscaleWithinU53(range: number): Distribution { 74 0 : const extendedRange = range + 1; 75 0 : if (isEvenlyDivisibleByMaxInt32(extendedRange)) { 76 0 : const highRange = ((extendedRange / UINT32_SIZE) | 0) - 1; 77 0 : if (isPowerOfTwoMinusOne(highRange)) { 78 0 : return upscaleWithHighMasking(highRange); 79 0 : } 80 0 : } 81 0 : return upscaleToLoopCheckedRange(extendedRange); 82 0 : } 83 : 84 0 : function upscaleWithinI53AndLoopCheck(min: number, max: number): Distribution { 85 0 : return engine => { 86 0 : let ret = 0; 87 0 : do { 88 0 : const high = engine.next() | 0; 89 0 : const low = engine.next() >>> 0; 90 0 : ret = 91 0 : (high & UINT21_MAX) * UINT32_SIZE + 92 0 : low + 93 0 : (high & UINT21_SIZE ? -SMALLEST_UNSAFE_INTEGER : 0); 94 0 : } while (ret < min || ret > max); 95 0 : return ret; 96 0 : }; 97 0 : } 98 : 99 : /** 100 : * Returns a Distribution to return a value within [min, max] 101 : * @param min The minimum integer value, inclusive. No less than -0x20000000000000. 102 : * @param max The maximum integer value, inclusive. No greater than 0x20000000000000. 103 : */ 104 1 : export function integer(min: number, max: number): Distribution { 105 3 : min = Math.floor(min); 106 3 : max = Math.floor(max); 107 0 : if (min < -SMALLEST_UNSAFE_INTEGER || !isFinite(min)) { 108 0 : throw new RangeError( 109 0 : `Expected min to be at least ${-SMALLEST_UNSAFE_INTEGER}` 110 0 : ); 111 0 : } else if (max > SMALLEST_UNSAFE_INTEGER || !isFinite(max)) { 112 0 : throw new RangeError( 113 0 : `Expected max to be at most ${SMALLEST_UNSAFE_INTEGER}` 114 0 : ); 115 0 : } 116 : 117 3 : const range = max - min; 118 0 : if (range <= 0 || !isFinite(range)) { 119 0 : return () => min; 120 0 : } else if (range === UINT32_MAX) { 121 0 : if (min === 0) { 122 0 : return uint32; 123 0 : } else { 124 0 : return add(int32, min + INT32_SIZE); 125 0 : } 126 0 : } else if (range < UINT32_MAX) { 127 3 : return add(downscaleToRange(range), min); 128 0 : } else if (range === LARGEST_SAFE_INTEGER) { 129 0 : return add(uint53, min); 130 0 : } else if (range < LARGEST_SAFE_INTEGER) { 131 0 : return add(upscaleWithinU53(range), min); 132 0 : } else if (max - 1 - min === LARGEST_SAFE_INTEGER) { 133 0 : return add(uint53Full, min); 134 0 : } else if ( 135 0 : min === -SMALLEST_UNSAFE_INTEGER && 136 0 : max === SMALLEST_UNSAFE_INTEGER 137 0 : ) { 138 0 : return int53Full; 139 0 : } else if (min === -SMALLEST_UNSAFE_INTEGER && max === LARGEST_SAFE_INTEGER) { 140 0 : return int53; 141 0 : } else if (min === -LARGEST_SAFE_INTEGER && max === SMALLEST_UNSAFE_INTEGER) { 142 0 : return add(int53, 1); 143 0 : } else if (max === SMALLEST_UNSAFE_INTEGER) { 144 0 : return add(upscaleWithinI53AndLoopCheck(min - 1, max - 1), 1); 145 0 : } else { 146 0 : return upscaleWithinI53AndLoopCheck(min, max); 147 0 : } 148 1 : }