aboutsummaryrefslogtreecommitdiff
path: root/test/utils/mersenne.spec.ts
blob: a5484b5c395bdd3dc2bc974a4dafa5b1ec4a6a67 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import { beforeAll, beforeEach, describe, expect, it } from 'vitest';
import { MersenneTwister19937 } from '../../src/internal/mersenne';
import { randomSeed } from '../../src/internal/seed';
import type { Randomizer } from '../../src/randomizer';
import {
  generateMersenne32Randomizer,
  generateMersenne53Randomizer,
} from '../../src/utils/mersenne';
import { seededRuns } from '../support/seeded-runs';
import { times } from '../support/times';
import {
  MERSENNE_MAX_VALUE,
  TWISTER_32CO_MAX_VALUE,
  TWISTER_53CO_MAX_VALUE,
} from './mersenne-test-utils';

const NON_SEEDED_BASED_RUN = 25;

describe('MersenneTwister19937', () => {
  describe('nextU32()', () => {
    it('should be able to return 0', () => {
      const twister = new MersenneTwister19937(257678572);

      // There is no single value seed that can produce 0 in the first call
      for (let i = 0; i < 5; i++) {
        twister.nextU32();
      }

      const actual = twister.nextU32();
      expect(actual).toBe(0);
    });

    it('should be able to return 2^32-1', () => {
      const twister = new MersenneTwister19937(2855577693);
      const actual = twister.nextU32();
      expect(actual).toBe(2 ** 32 - 1);
    });
  });

  describe('nextF32()', () => {
    it('should be able to return 0', () => {
      const twister = new MersenneTwister19937();
      // shortcut to return minimal value
      // the test above shows that it is possible to return 0
      twister.nextU32 = () => 0;
      const actual = twister.nextF32();
      expect(actual).toBe(0);
    });

    it('should be able to return almost 1', () => {
      const twister = new MersenneTwister19937();
      // shortcut to return maximal value
      // the test above shows that it is possible to return 2^32-1
      twister.nextU32 = () => 2 ** 32 - 1;
      const actual = twister.nextF32();
      expect(actual).toBe(TWISTER_32CO_MAX_VALUE);
    });
  });

  describe('nextF53()', () => {
    it('should be able to return 0', () => {
      const twister = new MersenneTwister19937();
      // shortcut to return minimal value
      // the test above shows that it is possible to return 0
      twister.nextU32 = () => 0;
      const actual = twister.nextF53();
      expect(actual).toBe(0);
    });

    it('should be able to return almost 1', () => {
      const twister = new MersenneTwister19937();
      // shortcut to return maximal value
      // the test above shows that it is possible to return 2^32-1
      twister.nextU32 = () => 2 ** 32 - 1;
      const actual = twister.nextF53();
      expect(actual).toBe(TWISTER_53CO_MAX_VALUE);
    });
  });
});

describe.each([
  ['generateMersenne32Randomizer()', generateMersenne32Randomizer],
  ['generateMersenne53Randomizer()', generateMersenne53Randomizer],
])('%s', (_, factory) => {
  const randomizer: Randomizer = factory();

  it('should return a result matching the interface', () => {
    expect(randomizer).toBeDefined();
    expect(randomizer).toBeTypeOf('object');
    expect(randomizer.next).toBeTypeOf('function');
    expect(randomizer.seed).toBeTypeOf('function');
  });

  describe.each(
    [...seededRuns, ...seededRuns.map((v) => [v, 1, 2])].map((v) => [v])
  )('seed: %j', (seed) => {
    beforeEach(() => {
      randomizer.seed(seed);
    });

    it('should return deterministic value for next()', () => {
      const actual = randomizer.next();

      expect(actual).toMatchSnapshot();
    });
  });

  describe.each(
    times(NON_SEEDED_BASED_RUN).flatMap(() => [
      [randomSeed()],
      [[randomSeed(), randomSeed()]],
    ])
  )('random seeded tests %j', (seed) => {
    beforeAll(() => {
      randomizer.seed(seed);
    });

    describe('next', () => {
      it('should return random number from interval [0, 1)', () => {
        const actual = randomizer.next();

        expect(actual).toBeGreaterThanOrEqual(0);
        expect(actual).toBeLessThanOrEqual(MERSENNE_MAX_VALUE);
        expect(actual).toBeLessThan(1);
      });
    });
  });
});