LCOV - code coverage report
Current view: top level - randomjs/engine - MersenneTwister19937.ts (source / functions) Hit Total Coverage
Test: coverage.lcov Lines: 11 124 8.9 %
Date: 2021-03-12 10:43:40 Functions: 0 9 0.0 %

          Line data    Source code
       1             : import { Engine } from "../types.ts";
       2           1 : import { INT32_MAX, INT32_SIZE } from "../utils/constants.ts";
       3           1 : import { createEntropy } from "../utils/createEntropy.ts";
       4           1 : import { imul } from "../utils/imul.ts";
       5           1 : import { Int32Array } from "../utils/Int32Array.ts";
       6             : 
       7           1 : const ARRAY_SIZE = 624;
       8           1 : const ARRAY_MAX = ARRAY_SIZE - 1;
       9           1 : const M = 397;
      10           1 : const ARRAY_SIZE_MINUS_M = ARRAY_SIZE - M;
      11           1 : const A = 0x9908b0df;
      12             : 
      13             : /**
      14             :  * An Engine that is a pseudorandom number generator using the Mersenne
      15             :  * Twister algorithm based on the prime 2**19937 − 1
      16             :  *
      17             :  * See http://en.wikipedia.org/wiki/Mersenne_twister
      18             :  */
      19           1 : export class MersenneTwister19937 implements Engine {
      20             : 
      21             :   /**
      22             :    * Returns a MersenneTwister19937 seeded with an initial int32 value
      23             :    * @param initial the initial seed value
      24             :    */
      25           0 :   public static seed(initial: number): MersenneTwister19937 {
      26           0 :     return new MersenneTwister19937().seed(initial);
      27           0 :   }
      28             : 
      29             :   /**
      30             :    * Returns a MersenneTwister19937 seeded with zero or more int32 values
      31             :    * @param source A series of int32 values
      32             :    */
      33           0 :   public static seedWithArray(source: ArrayLike<number>): MersenneTwister19937 {
      34           0 :     return new MersenneTwister19937().seedWithArray(source);
      35           0 :   }
      36             : 
      37             :   /**
      38             :    * Returns a MersenneTwister19937 seeded with the current time and
      39             :    * a series of natively-generated random values
      40             :    */
      41           0 :   public static autoSeed(): MersenneTwister19937 {
      42           0 :     return MersenneTwister19937.seedWithArray(createEntropy());
      43           0 :   }
      44             : 
      45           0 :   private readonly data = new Int32Array(ARRAY_SIZE);
      46           0 :   private index = 0; // integer within [0, 624]
      47           0 :   private uses = 0;
      48             : 
      49             :   /**
      50             :    * MersenneTwister19937 should not be instantiated directly.
      51             :    * Instead, use the static methods `seed`, `seedWithArray`, or `autoSeed`.
      52             :    */
      53           0 :   private constructor() {}
      54             : 
      55             :   /**
      56             :    * Returns the next int32 value of the sequence
      57             :    */
      58           0 :   public next(): number {
      59           0 :     if ((this.index | 0) >= ARRAY_SIZE) {
      60           0 :       refreshData(this.data);
      61           0 :       this.index = 0;
      62           0 :     }
      63             : 
      64           0 :     const value = this.data[this.index];
      65           0 :     this.index = (this.index + 1) | 0;
      66           0 :     this.uses += 1;
      67           0 :     return temper(value) | 0;
      68           0 :   }
      69             : 
      70             :   /**
      71             :    * Returns the number of times that the Engine has been used.
      72             :    *
      73             :    * This can be provided to an unused MersenneTwister19937 with the same
      74             :    * seed, bringing it to the exact point that was left off.
      75             :    */
      76           0 :   public getUseCount(): number {
      77           0 :     return this.uses;
      78           0 :   }
      79             : 
      80             :   /**
      81             :    * Discards one or more items from the engine
      82             :    * @param count The count of items to discard
      83             :    */
      84           0 :   public discard(count: number): this {
      85           0 :     if (count <= 0) {
      86           0 :       return this;
      87           0 :     }
      88           0 :     this.uses += count;
      89           0 :     if ((this.index | 0) >= ARRAY_SIZE) {
      90           0 :       refreshData(this.data);
      91           0 :       this.index = 0;
      92           0 :     }
      93           0 :     while (count + this.index > ARRAY_SIZE) {
      94           0 :       count -= ARRAY_SIZE - this.index;
      95           0 :       refreshData(this.data);
      96           0 :       this.index = 0;
      97           0 :     }
      98           0 :     this.index = (this.index + count) | 0;
      99           0 :     return this;
     100           0 :   }
     101             : 
     102           0 :   private seed(initial: number): this {
     103           0 :     let previous = 0;
     104           0 :     this.data[0] = previous = initial | 0;
     105             : 
     106           0 :     for (let i = 1; i < ARRAY_SIZE; i = (i + 1) | 0) {
     107           0 :       this.data[i] = previous =
     108           0 :         (imul(previous ^ (previous >>> 30), 0x6c078965) + i) | 0;
     109           0 :     }
     110           0 :     this.index = ARRAY_SIZE;
     111           0 :     this.uses = 0;
     112           0 :     return this;
     113           0 :   }
     114             : 
     115           0 :   private seedWithArray(source: ArrayLike<number>): this {
     116           0 :     this.seed(0x012bd6aa);
     117           0 :     seedWithArray(this.data, source);
     118           0 :     return this;
     119           0 :   }
     120           1 : }
     121             : 
     122           0 : function refreshData(data: Int32Array) {
     123           0 :   let k = 0;
     124           0 :   let tmp = 0;
     125           0 :   for (; (k | 0) < ARRAY_SIZE_MINUS_M; k = (k + 1) | 0) {
     126           0 :     tmp = (data[k] & INT32_SIZE) | (data[(k + 1) | 0] & INT32_MAX);
     127           0 :     data[k] = data[(k + M) | 0] ^ (tmp >>> 1) ^ (tmp & 0x1 ? A : 0);
     128           0 :   }
     129             : 
     130           0 :   for (; (k | 0) < ARRAY_MAX; k = (k + 1) | 0) {
     131           0 :     tmp = (data[k] & INT32_SIZE) | (data[(k + 1) | 0] & INT32_MAX);
     132           0 :     data[k] =
     133           0 :       data[(k - ARRAY_SIZE_MINUS_M) | 0] ^ (tmp >>> 1) ^ (tmp & 0x1 ? A : 0);
     134           0 :   }
     135             : 
     136           0 :   tmp = (data[ARRAY_MAX] & INT32_SIZE) | (data[0] & INT32_MAX);
     137           0 :   data[ARRAY_MAX] = data[M - 1] ^ (tmp >>> 1) ^ (tmp & 0x1 ? A : 0);
     138           0 : }
     139             : 
     140           0 : function temper(value: number) {
     141           0 :   value ^= value >>> 11;
     142           0 :   value ^= (value << 7) & 0x9d2c5680;
     143           0 :   value ^= (value << 15) & 0xefc60000;
     144           0 :   return value ^ (value >>> 18);
     145           0 : }
     146             : 
     147           0 : function seedWithArray(data: Int32Array, source: ArrayLike<number>) {
     148           0 :   let i = 1;
     149           0 :   let j = 0;
     150           0 :   const sourceLength = source.length;
     151           0 :   let k = Math.max(sourceLength, ARRAY_SIZE) | 0;
     152           0 :   let previous = data[0] | 0;
     153           0 :   for (; (k | 0) > 0; --k) {
     154           0 :     data[i] = previous =
     155           0 :       ((data[i] ^ imul(previous ^ (previous >>> 30), 0x0019660d)) +
     156           0 :         (source[j] | 0) +
     157           0 :         (j | 0)) |
     158           0 :       0;
     159           0 :     i = (i + 1) | 0;
     160           0 :     ++j;
     161           0 :     if ((i | 0) > ARRAY_MAX) {
     162           0 :       data[0] = data[ARRAY_MAX];
     163           0 :       i = 1;
     164           0 :     }
     165           0 :     if (j >= sourceLength) {
     166           0 :       j = 0;
     167           0 :     }
     168           0 :   }
     169           0 :   for (k = ARRAY_MAX; (k | 0) > 0; --k) {
     170           0 :     data[i] = previous =
     171           0 :       ((data[i] ^ imul(previous ^ (previous >>> 30), 0x5d588b65)) - i) | 0;
     172           0 :     i = (i + 1) | 0;
     173           0 :     if ((i | 0) > ARRAY_MAX) {
     174           0 :       data[0] = data[ARRAY_MAX];
     175           0 :       i = 1;
     176           0 :     }
     177           0 :   }
     178           0 :   data[0] = INT32_SIZE;
     179           0 : }

Generated by: LCOV version 1.15