aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorST-DDT <[email protected]>2023-10-04 18:13:00 +0200
committerGitHub <[email protected]>2023-10-04 18:13:00 +0200
commit5410239245b4a6fe8c1976f8aa33c970923f9f40 (patch)
tree7640a15dfc6781311082a6a7e7a87412d7abcf98 /src
parent88b2443cab22ade0488643568896b64adef420b5 (diff)
downloadfaker-5410239245b4a6fe8c1976f8aa33c970923f9f40.tar.xz
faker-5410239245b4a6fe8c1976f8aa33c970923f9f40.zip
feat: support custom randomizer (#2284)
Diffstat (limited to 'src')
-rw-r--r--src/faker.ts35
-rw-r--r--src/index.ts1
-rw-r--r--src/internal/mersenne.ts (renamed from src/internal/mersenne/twister.ts)28
-rw-r--r--src/internal/mersenne/mersenne.ts45
-rw-r--r--src/modules/number/index.ts12
-rw-r--r--src/randomizer.ts65
-rw-r--r--src/simple-faker.ts31
7 files changed, 156 insertions, 61 deletions
diff --git a/src/faker.ts b/src/faker.ts
index 43811f05..e6b5889a 100644
--- a/src/faker.ts
+++ b/src/faker.ts
@@ -28,6 +28,7 @@ import { ScienceModule } from './modules/science';
import { SystemModule } from './modules/system';
import { VehicleModule } from './modules/vehicle';
import { WordModule } from './modules/word';
+import type { Randomizer } from './randomizer';
import { SimpleFaker } from './simple-faker';
import { mergeLocales } from './utils/merge-locales';
@@ -123,6 +124,10 @@ export class Faker extends SimpleFaker {
*
* @param options The options to use.
* @param options.locale The locale data to use.
+ * @param options.randomizer The Randomizer to use.
+ * Specify this only if you want to use it to achieve a specific goal,
+ * such as sharing the same random generator with other instances/tools.
+ * Defaults to faker's Mersenne Twister based pseudo random number generator.
*
* @example
* import { Faker, es } from '@faker-js/faker';
@@ -144,6 +149,15 @@ export class Faker extends SimpleFaker {
* @see mergeLocales
*/
locale: LocaleDefinition | LocaleDefinition[];
+
+ /**
+ * The Randomizer to use.
+ * Specify this only if you want to use it to achieve a specific goal,
+ * such as sharing the same random generator with other instances/tools.
+ *
+ * @default generateMersenne32Randomizer()
+ */
+ randomizer?: Randomizer;
});
/**
* Creates a new instance of Faker.
@@ -180,6 +194,10 @@ export class Faker extends SimpleFaker {
* @param options.locale The locale data to use or the name of the main locale.
* @param options.locales The locale data to use.
* @param options.localeFallback The name of the fallback locale to use.
+ * @param options.randomizer The Randomizer to use.
+ * Specify this only if you want to use it to achieve a specific goal,
+ * such as sharing the same random generator with other instances/tools.
+ * Defaults to faker's Mersenne Twister based pseudo random number generator.
*
* @example
* import { Faker, es } from '@faker-js/faker';
@@ -203,6 +221,15 @@ export class Faker extends SimpleFaker {
* @see mergeLocales
*/
locale: LocaleDefinition | LocaleDefinition[];
+
+ /**
+ * The Randomizer to use.
+ * Specify this only if you want to use it to achieve a specific goal,
+ * such as sharing the same random generator with other instances/tools.
+ *
+ * @default generateMersenne32Randomizer()
+ */
+ randomizer?: Randomizer;
}
| {
/**
@@ -231,14 +258,18 @@ export class Faker extends SimpleFaker {
);
constructor(
options:
- | { locale: LocaleDefinition | LocaleDefinition[] }
+ | {
+ locale: LocaleDefinition | LocaleDefinition[];
+ randomizer?: Randomizer;
+ }
| {
locales: Record<string, LocaleDefinition>;
locale?: string;
localeFallback?: string;
+ randomizer?: Randomizer;
}
) {
- super();
+ super({ randomizer: options.randomizer });
const { locales } = options as {
locales: Record<string, LocaleDefinition>;
diff --git a/src/index.ts b/src/index.ts
index 000541b7..8adcd384 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -128,5 +128,6 @@ export type { StringModule } from './modules/string';
export type { SystemModule } from './modules/system';
export type { VehicleModule } from './modules/vehicle';
export type { WordModule } from './modules/word';
+export type { Randomizer } from './randomizer';
export { SimpleFaker, simpleFaker } from './simple-faker';
export { mergeLocales } from './utils/merge-locales';
diff --git a/src/internal/mersenne/twister.ts b/src/internal/mersenne.ts
index d25e3acf..d97ec716 100644
--- a/src/internal/mersenne/twister.ts
+++ b/src/internal/mersenne.ts
@@ -1,3 +1,5 @@
+import type { Randomizer } from '../randomizer';
+
/**
* Copyright (c) 2022-2023 Faker
*
@@ -71,7 +73,7 @@
*
* @internal
*/
-export default class MersenneTwister19937 {
+class MersenneTwister19937 {
private readonly N = 624;
private readonly M = 397;
private readonly MATRIX_A = 0x9908b0df; // constant vector a
@@ -323,3 +325,27 @@ export default class MersenneTwister19937 {
}
// These real versions are due to Isaku Wada, 2002/01/09
}
+
+/**
+ * Generates a MersenneTwister19937 randomizer with 32 bits of precision.
+ *
+ * @internal
+ */
+export function generateMersenne32Randomizer(): Randomizer {
+ const twister = new MersenneTwister19937();
+
+ twister.initGenrand(Math.ceil(Math.random() * Number.MAX_SAFE_INTEGER));
+
+ return {
+ next(): number {
+ return twister.genrandReal2();
+ },
+ 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/internal/mersenne/mersenne.ts b/src/internal/mersenne/mersenne.ts
deleted file mode 100644
index c823af7e..00000000
--- a/src/internal/mersenne/mersenne.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import Twister from './twister';
-
-/**
- * Generate seed based random numbers.
- *
- * @internal
- */
-export interface Mersenne {
- /**
- * Generates a random float between `[0, 1)`.
- * This method is called `next` so that it could be used as an [iterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterator_protocol)
- */
- next(): number;
-
- /**
- * Sets the seed to use.
- *
- * @param seed The seed to use.
- */
- seed(seed: number | number[]): void;
-}
-
-/**
- * Generate seed based random numbers.
- *
- * @internal
- */
-export default function mersenne(): Mersenne {
- const twister = new Twister();
-
- twister.initGenrand(Math.ceil(Math.random() * Number.MAX_SAFE_INTEGER));
-
- return {
- next(): number {
- return twister.genrandReal2();
- },
- 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/number/index.ts b/src/modules/number/index.ts
index a2e64724..9216df7a 100644
--- a/src/modules/number/index.ts
+++ b/src/modules/number/index.ts
@@ -1,7 +1,6 @@
import type { SimpleFaker } from '../..';
import { FakerError } from '../../errors/faker-error';
import { bindThisToMemberFunctions } from '../../internal/bind-this-to-member-functions';
-import type { Mersenne } from '../../internal/mersenne/mersenne';
/**
* Module to generate numbers of any kind.
@@ -83,10 +82,9 @@ export class NumberModule {
throw new FakerError(`Max ${max} should be greater than min ${min}.`);
}
- const mersenne: Mersenne =
- // @ts-expect-error: access private member field
- this.faker._mersenne;
- const real = mersenne.next();
+ // @ts-expect-error: access private member field
+ const randomizer = this.faker._randomizer;
+ const real = randomizer.next();
return Math.floor(real * (effectiveMax + 1 - effectiveMin) + effectiveMin);
}
@@ -160,8 +158,8 @@ export class NumberModule {
}
// @ts-expect-error: access private member field
- const mersenne: Mersenne = this.faker._mersenne;
- const real = mersenne.next();
+ const randomizer = this.faker._randomizer;
+ const real = randomizer.next();
return real * (max - min) + min;
}
diff --git a/src/randomizer.ts b/src/randomizer.ts
new file mode 100644
index 00000000..3264ec1d
--- /dev/null
+++ b/src/randomizer.ts
@@ -0,0 +1,65 @@
+/**
+ * Interface for a random number generator.
+ *
+ * **Note:** Normally there is no need to implement this interface directly,
+ * unless you want to achieve a specific goal with it.
+ *
+ * This interface enables you to use random generators from third party libraries such as [pure-rand](https://github.com/dubzzz/pure-rand).
+ *
+ * Instances are expected to be ready for use before being passed to any Faker constructor,
+ * this includes being `seed()`ed with either a random or fixed value.
+ *
+ * For more information please refer to the [documentation](/api/randomizer).
+ *
+ * @example
+ * import { Faker, Randomizer, SimpleFaker } from '@faker-js/faker';
+ * import { RandomGenerator, xoroshiro128plus } from 'pure-rand';
+ *
+ * function generatePureRandRandomizer(
+ * seed: number | number[] = Date.now() ^ (Math.random() * 0x100000000),
+ * factory: (seed: number) => RandomGenerator = xoroshiro128plus
+ * ): Randomizer {
+ * const self = {
+ * next: () => (self.generator.unsafeNext() >>> 0) / 0x100000000,
+ * seed: (seed: number | number[]) => {
+ * self.generator = factory(typeof seed === 'number' ? seed : seed[0]);
+ * },
+ * } as Randomizer & { generator: RandomGenerator };
+ * self.seed(seed);
+ * return self;
+ * }
+ *
+ * const randomizer = generatePureRandRandomizer();
+ *
+ * const simpleFaker = new SimpleFaker({ randomizer });
+ *
+ * const faker = new Faker({
+ * locale: ...,
+ * randomizer,
+ * });
+ */
+export interface Randomizer {
+ /**
+ * Generates a random float between 0 (inclusive) and 1 (exclusive).
+ *
+ * @example
+ * randomizer.next() // 0.3404027920160495
+ * randomizer.next() // 0.929890375900335
+ * randomizer.next() // 0.5866362918861691
+ */
+ next(): number;
+
+ /**
+ * Sets the seed to use.
+ *
+ * @param seed The seed to use.
+ *
+ * @example
+ * // Random seeds
+ * randomizer.seed(Date.now() ^ (Math.random() * 0x100000000));
+ * // Fixed seeds (for reproducibility)
+ * randomizer.seed(42);
+ * randomizer.seed([42, 13.37]);
+ */
+ seed(seed: number | number[]): void;
+}
diff --git a/src/simple-faker.ts b/src/simple-faker.ts
index c4ecb8df..49fc3f1c 100644
--- a/src/simple-faker.ts
+++ b/src/simple-faker.ts
@@ -1,10 +1,10 @@
-import type { Mersenne } from './internal/mersenne/mersenne';
-import mersenne from './internal/mersenne/mersenne';
+import { generateMersenne32Randomizer } from './internal/mersenne';
import { DatatypeModule } from './modules/datatype';
import { SimpleDateModule } from './modules/date';
import { SimpleHelpersModule } from './modules/helpers';
import { NumberModule } from './modules/number';
import { StringModule } from './modules/string';
+import type { Randomizer } from './randomizer';
/**
* This is a simplified Faker class that doesn't need any localized data to generate its output.
@@ -77,7 +77,7 @@ export class SimpleFaker {
}
/** @internal */
- private readonly _mersenne: Mersenne = mersenne();
+ private readonly _randomizer: Randomizer = generateMersenne32Randomizer();
readonly datatype: DatatypeModule = new DatatypeModule(this);
readonly date: SimpleDateModule = new SimpleDateModule(this);
@@ -89,9 +89,28 @@ export class SimpleFaker {
* Creates a new instance of SimpleFaker.
*
* In nearly any case you should use the prebuilt `simpleFaker` instances instead of the constructor.
+ *
+ * @param options The options to use.
+ * @param options.randomizer The Randomizer to use.
+ * Specify this only if you want to use it to achieve a specific goal,
+ * such as sharing the same random generator with other instances/tools.
+ * Defaults to faker's Mersenne Twister based pseudo random number generator.
*/
- constructor() {
- // This empty constructor just exists for VitePress docs
+ constructor(
+ options: {
+ /**
+ * The Randomizer to use.
+ * Specify this only if you want to use it to achieve a specific goal,
+ * such as sharing the same random generator with other instances/tools.
+ *
+ * @default generateMersenne32Randomizer()
+ */
+ randomizer?: Randomizer;
+ } = {}
+ ) {
+ const { randomizer = generateMersenne32Randomizer() } = options;
+
+ this._randomizer = randomizer;
}
/**
@@ -211,7 +230,7 @@ export class SimpleFaker {
seed(
seed: number | number[] = Math.ceil(Math.random() * Number.MAX_SAFE_INTEGER)
): number | number[] {
- this._mersenne.seed(seed);
+ this._randomizer.seed(seed);
return seed;
}