-
-
Notifications
You must be signed in to change notification settings - Fork 930
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test(mersenne): add tests for value ranges (#2470)
- Loading branch information
Showing
6 changed files
with
190 additions
and
59 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
// Moved to a separate file to avoid importing the tests | ||
|
||
/** | ||
* The maximum value that can be returned by `MersenneTwister19937.genrandReal2()`. | ||
* This is the max possible value with 32 bits of precision that is less than 1. | ||
*/ | ||
export const TWISTER_32CO_MAX_VALUE = 0.9999999997671694; | ||
/** | ||
* The maximum value that can be returned by `MersenneTwister19937.genrandRes53()`. | ||
* This is the max possible value with 53 bits of precision that is less than 1. | ||
*/ | ||
export const TWISTER_53CO_MAX_VALUE = 0.9999999999999999; | ||
// Re-exported because the value might change in the future | ||
/** | ||
* The maximum value that can be returned by `next()`. | ||
*/ | ||
export const MERSENNE_MAX_VALUE = TWISTER_32CO_MAX_VALUE; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; | ||
import { | ||
generateMersenne32Randomizer, | ||
MersenneTwister19937, | ||
} from '../../src/internal/mersenne'; | ||
import type { Randomizer } from '../../src/randomizer'; | ||
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; | ||
|
||
function newTwister( | ||
seed: number = Math.random() * Number.MAX_SAFE_INTEGER | ||
): MersenneTwister19937 { | ||
const twister = new MersenneTwister19937(); | ||
twister.initGenrand(seed); | ||
return twister; | ||
} | ||
|
||
describe('MersenneTwister19937', () => { | ||
describe('genrandInt32()', () => { | ||
it('should be able to return 0', () => { | ||
const twister = newTwister(257678572); | ||
|
||
// There is no single value seed that can produce 0 in the first call | ||
for (let i = 0; i < 5; i++) { | ||
twister.genrandInt32(); | ||
} | ||
|
||
const actual = twister.genrandInt32(); | ||
expect(actual).toBe(0); | ||
}); | ||
|
||
it('should be able to return 2^32-1', () => { | ||
const twister = newTwister(2855577693); | ||
const actual = twister.genrandInt32(); | ||
expect(actual).toBe(2 ** 32 - 1); | ||
}); | ||
}); | ||
|
||
describe('genrandReal2()', () => { | ||
it('should be able to return 0', () => { | ||
const twister = newTwister(); | ||
// shortcut to return minimal value | ||
// the test above shows that it is possible to return 0 | ||
twister.genrandInt32 = () => 0; | ||
const actual = twister.genrandReal2(); | ||
expect(actual).toBe(0); | ||
}); | ||
|
||
it('should be able to return almost 1', () => { | ||
const twister = newTwister(); | ||
// shortcut to return maximal value | ||
// the test above shows that it is possible to return 2^32-1 | ||
twister.genrandInt32 = () => 2 ** 32 - 1; | ||
const actual = twister.genrandReal2(); | ||
expect(actual).toBe(TWISTER_32CO_MAX_VALUE); | ||
}); | ||
}); | ||
|
||
describe('genrandRes53()', () => { | ||
it('should be able to return 0', () => { | ||
const twister = newTwister(); | ||
// shortcut to return minimal value | ||
// the test above shows that it is possible to return 0 | ||
twister.genrandInt32 = () => 0; | ||
const actual = twister.genrandRes53(); | ||
expect(actual).toBe(0); | ||
}); | ||
|
||
it('should be able to return almost 1', () => { | ||
const twister = newTwister(); | ||
// shortcut to return maximal value | ||
// the test above shows that it is possible to return 2^32-1 | ||
twister.genrandInt32 = () => 2 ** 32 - 1; | ||
const actual = twister.genrandRes53(); | ||
expect(actual).toBe(TWISTER_53CO_MAX_VALUE); | ||
}); | ||
}); | ||
}); | ||
|
||
describe('generateMersenne32Randomizer()', () => { | ||
const randomizer: Randomizer = generateMersenne32Randomizer(); | ||
|
||
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(); | ||
}); | ||
}); | ||
|
||
function randomSeed(): number { | ||
return Math.ceil(Math.random() * 1_000_000_000); | ||
} | ||
|
||
// Create and log-back the seed for debug purposes | ||
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); | ||
}); | ||
}); | ||
}); | ||
}); |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters