LCOV - code coverage report
Current view: top level - randomjs/distribution - integer.ts (source / functions) Hit Total Coverage
Test: coverage.lcov Lines: 31 131 23.7 %
Date: 2021-03-12 10:43:40 Functions: 4 10 40.0 %

          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 : }

Generated by: LCOV version 1.15