aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShinigami <[email protected]>2022-10-30 09:03:26 +0100
committerGitHub <[email protected]>2022-10-30 08:03:26 +0000
commit9abfcfb90cc73bfe961d446f5fb950976d5c2ccd (patch)
tree1e76203a6cfd8bb6c2b83734fee4cebf56db5072
parent4da3d5eac3805f609f4e40385aee6a645fd93540 (diff)
downloadfaker-9abfcfb90cc73bfe961d446f5fb950976d5c2ccd.tar.xz
faker-9abfcfb90cc73bfe961d446f5fb950976d5c2ccd.zip
refactor(mersenne): rewrite internal mersenne (#1447)
-rw-r--r--src/faker.ts11
-rw-r--r--src/internal/mersenne/mersenne.ts96
-rw-r--r--src/modules/datatype/index.ts11
-rw-r--r--test/__snapshots__/mersenne.spec.ts.snap48
-rw-r--r--test/mersenne.spec.ts75
5 files changed, 75 insertions, 166 deletions
diff --git a/src/faker.ts b/src/faker.ts
index f8a46511..fd7ed522 100644
--- a/src/faker.ts
+++ b/src/faker.ts
@@ -1,7 +1,8 @@
import type { LocaleDefinition } from './definitions';
import { FakerError } from './errors/faker-error';
import { deprecated } from './internal/deprecated';
-import { MersenneModule } from './internal/mersenne/mersenne';
+import type { Mersenne } from './internal/mersenne/mersenne';
+import mersenne from './internal/mersenne/mersenne';
import type { KnownLocale } from './locales';
import { AnimalModule } from './modules/animal';
import { ColorModule } from './modules/color';
@@ -79,7 +80,7 @@ export class Faker {
readonly definitions: LocaleDefinition = this.initDefinitions();
/** @internal */
- private readonly _mersenne: MersenneModule = new MersenneModule();
+ private readonly _mersenne: Mersenne = mersenne();
readonly random: RandomModule = new RandomModule(this);
@@ -286,11 +287,7 @@ export class Faker {
seed(
seed: number | number[] = Math.ceil(Math.random() * Number.MAX_SAFE_INTEGER)
): number | number[] {
- if (Array.isArray(seed) && seed.length) {
- this._mersenne.seed_array(seed);
- } else if (!Array.isArray(seed) && !isNaN(seed)) {
- this._mersenne.seed(seed);
- }
+ this._mersenne.seed(seed);
return seed;
}
diff --git a/src/internal/mersenne/mersenne.ts b/src/internal/mersenne/mersenne.ts
index 16019e29..e7c0355c 100644
--- a/src/internal/mersenne/mersenne.ts
+++ b/src/internal/mersenne/mersenne.ts
@@ -1,81 +1,49 @@
-import { FakerError } from '../../errors/faker-error';
-import Gen from './twister';
+import Twister from './twister';
/**
- * Module to generate seed based random numbers.
+ * Generate seed based random numbers.
*
* @internal
*/
-export class MersenneModule {
- private gen = new Gen();
-
- constructor() {
- this.gen.initGenrand(Math.ceil(Math.random() * Number.MAX_SAFE_INTEGER));
-
- // Bind `this` so namespaced is working correctly
- for (const name of Object.getOwnPropertyNames(MersenneModule.prototype)) {
- if (name === 'constructor' || typeof this[name] !== 'function') {
- continue;
- }
- this[name] = this[name].bind(this);
- }
- }
-
+export interface Mersenne {
/**
- * Generates a random number between `[min, max)`.
- *
- * @param max The maximum number. Defaults to `32768`.
- * @param min The minimum number. Defaults to `0`.
+ * Generates a random number between `[min, max)`. The result is already floored.
*
- * @example
- * faker.mersenne.rand() // 15515
- * faker.mersenne.rand(1000, 500) // 578
- *
- * @since 5.5.0
+ * @param options The options to generate a random number.
+ * @param options.min The minimum number.
+ * @param options.max The maximum number.
*/
- rand(max = 32768, min = 0): number {
- if (min > max) {
- const temp = min;
- min = max;
- max = temp;
- }
-
- return Math.floor(this.gen.genrandReal2() * (max - min) + min);
- }
+ next(options: { max: number; min: number }): number;
/**
* Sets the seed to use.
*
- * @param S The seed to use.
- * @throws If the seed is not a `number`.
- *
- * @since 5.5.0
+ * @param seed The seed to use.
*/
- seed(S: number): void {
- if (typeof S !== 'number') {
- throw new FakerError(
- `seed(S) must take numeric argument; is ${typeof S}`
- );
- }
+ seed(seed: number | number[]): void;
+}
+
+/**
+ * Generate seed based random numbers.
+ *
+ * @internal
+ */
+export default function mersenne(): Mersenne {
+ const twister = new Twister();
- this.gen.initGenrand(S);
- }
+ twister.initGenrand(Math.ceil(Math.random() * Number.MAX_SAFE_INTEGER));
- /**
- * Sets the seed to use.
- *
- * @param A The seed to use.
- * @throws If the seed is not a `number[]`.
- *
- * @since 5.5.0
- */
- seed_array(A: number[]): void {
- if (typeof A !== 'object') {
- throw new FakerError(
- `seed_array(A) must take array of numbers; is ${typeof A}`
- );
- }
+ return {
+ next({ min, max }): number {
+ return Math.floor(twister.genrandReal2() * (max - min) + min);
+ },
- this.gen.initByArray(A, A.length);
- }
+ seed(seed: number | number[]): void {
+ if (typeof seed === 'number') {
+ twister.initGenrand(seed);
+ } else if (Array.isArray(seed)) {
+ twister.initByArray(seed, seed.length);
+ }
+ },
+ };
}
diff --git a/src/modules/datatype/index.ts b/src/modules/datatype/index.ts
index 41c5c3e4..d4fcbbd6 100644
--- a/src/modules/datatype/index.ts
+++ b/src/modules/datatype/index.ts
@@ -1,7 +1,7 @@
import type { Faker } from '../..';
import { FakerError } from '../../errors/faker-error';
import { deprecated } from '../../internal/deprecated';
-import type { MersenneModule } from '../../internal/mersenne/mersenne';
+import type { Mersenne } from '../../internal/mersenne/mersenne';
/**
* Module to generate various primitive values and data types.
@@ -56,13 +56,14 @@ export class DatatypeModule {
throw new FakerError(`Max ${max} should be greater than min ${min}.`);
}
- const mersenne: MersenneModule =
+ const mersenne: Mersenne =
// @ts-expect-error: access private member field
this.faker._mersenne;
- const randomNumber = Math.floor(
- mersenne.rand(max / precision + 1, min / precision)
- );
+ const randomNumber = mersenne.next({
+ min: min / precision,
+ max: max / precision + 1,
+ });
// Workaround problem in float point arithmetics for e.g. 6681493 / 0.01
return randomNumber / (1 / precision);
diff --git a/test/__snapshots__/mersenne.spec.ts.snap b/test/__snapshots__/mersenne.spec.ts.snap
index 1c221b19..21d3e4cc 100644
--- a/test/__snapshots__/mersenne.spec.ts.snap
+++ b/test/__snapshots__/mersenne.spec.ts.snap
@@ -1,49 +1,37 @@
// Vitest Snapshot v1
-exports[`mersenne twister > seed: [42,1,2] > rand() 1`] = `28056`;
+exports[`mersenne twister > seed: [42,1,2] > should return deterministic values for next({ min: -50, max: 60 }) 1`] = `44`;
-exports[`mersenne twister > seed: [42,1,2] > should return deterministic values for rand(100, 0) 1`] = `85`;
+exports[`mersenne twister > seed: [42,1,2] > should return deterministic values for next({ min: -60, max: 0 }) 1`] = `-9`;
-exports[`mersenne twister > seed: [42,1,2] > should return deterministic values for rand(100, undefined) 1`] = `85`;
+exports[`mersenne twister > seed: [42,1,2] > should return deterministic values for next({ min: 0, max: 100 }) 1`] = `85`;
-exports[`mersenne twister > seed: [42,1,2] > should return deterministic values for rand(undefined, 0) 1`] = `28056`;
+exports[`mersenne twister > seed: [1211,1,2] > should return deterministic values for next({ min: -50, max: 60 }) 1`] = `48`;
-exports[`mersenne twister > seed: [1211,1,2] > rand() 1`] = `29217`;
+exports[`mersenne twister > seed: [1211,1,2] > should return deterministic values for next({ min: -60, max: 0 }) 1`] = `-7`;
-exports[`mersenne twister > seed: [1211,1,2] > should return deterministic values for rand(100, 0) 1`] = `89`;
+exports[`mersenne twister > seed: [1211,1,2] > should return deterministic values for next({ min: 0, max: 100 }) 1`] = `89`;
-exports[`mersenne twister > seed: [1211,1,2] > should return deterministic values for rand(100, undefined) 1`] = `89`;
+exports[`mersenne twister > seed: [1337,1,2] > should return deterministic values for next({ min: -50, max: 60 }) 1`] = `-31`;
-exports[`mersenne twister > seed: [1211,1,2] > should return deterministic values for rand(undefined, 0) 1`] = `29217`;
+exports[`mersenne twister > seed: [1337,1,2] > should return deterministic values for next({ min: -60, max: 0 }) 1`] = `-50`;
-exports[`mersenne twister > seed: [1337,1,2] > rand() 1`] = `5895`;
+exports[`mersenne twister > seed: [1337,1,2] > should return deterministic values for next({ min: 0, max: 100 }) 1`] = `17`;
-exports[`mersenne twister > seed: [1337,1,2] > should return deterministic values for rand(100, 0) 1`] = `17`;
+exports[`mersenne twister > seed: 42 > should return deterministic values for next({ min: -50, max: 60 }) 1`] = `-9`;
-exports[`mersenne twister > seed: [1337,1,2] > should return deterministic values for rand(100, undefined) 1`] = `17`;
+exports[`mersenne twister > seed: 42 > should return deterministic values for next({ min: -60, max: 0 }) 1`] = `-38`;
-exports[`mersenne twister > seed: [1337,1,2] > should return deterministic values for rand(undefined, 0) 1`] = `5895`;
+exports[`mersenne twister > seed: 42 > should return deterministic values for next({ min: 0, max: 100 }) 1`] = `37`;
-exports[`mersenne twister > seed: 42 > rand() 1`] = `12272`;
+exports[`mersenne twister > seed: 1211 > should return deterministic values for next({ min: -50, max: 60 }) 1`] = `52`;
-exports[`mersenne twister > seed: 42 > should return deterministic values for rand(100, 0) 1`] = `37`;
+exports[`mersenne twister > seed: 1211 > should return deterministic values for next({ min: -60, max: 0 }) 1`] = `-5`;
-exports[`mersenne twister > seed: 42 > should return deterministic values for rand(100, undefined) 1`] = `37`;
+exports[`mersenne twister > seed: 1211 > should return deterministic values for next({ min: 0, max: 100 }) 1`] = `92`;
-exports[`mersenne twister > seed: 42 > should return deterministic values for rand(undefined, 0) 1`] = `12272`;
+exports[`mersenne twister > seed: 1337 > should return deterministic values for next({ min: -50, max: 60 }) 1`] = `-22`;
-exports[`mersenne twister > seed: 1211 > rand() 1`] = `30425`;
+exports[`mersenne twister > seed: 1337 > should return deterministic values for next({ min: -60, max: 0 }) 1`] = `-45`;
-exports[`mersenne twister > seed: 1211 > should return deterministic values for rand(100, 0) 1`] = `92`;
-
-exports[`mersenne twister > seed: 1211 > should return deterministic values for rand(100, undefined) 1`] = `92`;
-
-exports[`mersenne twister > seed: 1211 > should return deterministic values for rand(undefined, 0) 1`] = `30425`;
-
-exports[`mersenne twister > seed: 1337 > rand() 1`] = `8586`;
-
-exports[`mersenne twister > seed: 1337 > should return deterministic values for rand(100, 0) 1`] = `26`;
-
-exports[`mersenne twister > seed: 1337 > should return deterministic values for rand(100, undefined) 1`] = `26`;
-
-exports[`mersenne twister > seed: 1337 > should return deterministic values for rand(undefined, 0) 1`] = `8586`;
+exports[`mersenne twister > seed: 1337 > should return deterministic values for next({ min: 0, max: 100 }) 1`] = `26`;
diff --git a/test/mersenne.spec.ts b/test/mersenne.spec.ts
index 6b0a3125..f6585783 100644
--- a/test/mersenne.spec.ts
+++ b/test/mersenne.spec.ts
@@ -1,53 +1,39 @@
import { beforeAll, beforeEach, describe, expect, it } from 'vitest';
-import { FakerError } from '../src/errors/faker-error';
-import { MersenneModule } from '../src/internal/mersenne/mersenne';
+import type { Mersenne } from '../src/internal/mersenne/mersenne';
+import mersenneFn from '../src/internal/mersenne/mersenne';
import { seededRuns } from './support/seededRuns';
const minMaxTestCases = [
- { max: 100, min: 0 },
- { max: undefined, min: 0 },
- { max: 100, min: undefined },
+ { min: 0, max: 100 },
+ { min: -60, max: 0 },
+ { min: -50, max: 60 },
];
-const functionNames = ['rand'];
-
const NON_SEEDED_BASED_RUN = 25;
describe('mersenne twister', () => {
- let mersenne: MersenneModule;
+ let mersenne: Mersenne;
beforeEach(() => {
- mersenne = new MersenneModule();
+ mersenne = mersenneFn();
});
for (const seed of [...seededRuns, [42, 1, 2], [1337, 1, 2], [1211, 1, 2]]) {
describe(`seed: ${JSON.stringify(seed)}`, () => {
beforeEach(() => {
- if (Array.isArray(seed)) {
- mersenne.seed_array(seed);
- } else {
- mersenne.seed(seed);
- }
+ mersenne.seed(seed);
});
- for (const functionName of functionNames) {
- it(`${functionName}()`, () => {
- const actual = mersenne[functionName]();
-
- expect(actual).toMatchSnapshot();
- });
- }
-
for (const { min, max } of minMaxTestCases) {
- it(`should return deterministic values for rand(${max}, ${min})`, () => {
- const actual = mersenne.rand(max, min);
+ it(`should return deterministic values for next({ min: ${min}, max: ${max} })`, () => {
+ const actual = mersenne.next({ min, max });
expect(actual).toMatchSnapshot();
});
}
- it.todo(`should return 0 for rand(1)`, () => {
- const actual = mersenne.rand(1);
+ it.todo(`should return 0 for next({ min: ${0}, max: ${1} })`, () => {
+ const actual = mersenne.next({ min: 0, max: 1 });
expect(actual).toEqual(0);
});
@@ -66,22 +52,13 @@ describe('mersenne twister', () => {
for (const seed of seeds) {
describe(`random seeded tests ${JSON.stringify(seed)}`, () => {
beforeAll(() => {
- if (Array.isArray(seed)) {
- mersenne.seed_array(seed);
- } else {
- mersenne.seed(seed);
- }
+ mersenne.seed(seed);
});
for (let i = 1; i <= NON_SEEDED_BASED_RUN; i++) {
- describe('rand', () => {
- it('should return a random number without given min / max arguments', () => {
- const randomNumber = mersenne.rand();
- expect(randomNumber).toBeTypeOf('number');
- });
-
+ describe('next', () => {
it('should return random number from interval [min, max)', () => {
- const actual = mersenne.rand(0, 2);
+ const actual = mersenne.next({ min: 0, max: 2 });
expect(actual).toBeGreaterThanOrEqual(0);
expect(actual).toBeLessThan(2);
@@ -90,26 +67,4 @@ describe('mersenne twister', () => {
}
});
}
-
- it('should throw an error when attempting to seed() a non-integer', () => {
- expect(() =>
- mersenne.seed(
- // @ts-expect-error: non-integer error
- 'abc'
- )
- ).toThrowError(
- new FakerError('seed(S) must take numeric argument; is string')
- );
- });
-
- it('should throw an error when attempting to seed() a non-integer', () => {
- expect(() =>
- mersenne.seed_array(
- // @ts-expect-error: non-integer error
- 'abc'
- )
- ).toThrowError(
- new FakerError('seed_array(A) must take array of numbers; is string')
- );
- });
});