aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/modules/number/index.ts39
-rw-r--r--test/modules/__snapshots__/number.spec.ts.snap12
-rw-r--r--test/modules/number.spec.ts137
3 files changed, 175 insertions, 13 deletions
diff --git a/src/modules/number/index.ts b/src/modules/number/index.ts
index f5f5c27f..f1dce04f 100644
--- a/src/modules/number/index.ts
+++ b/src/modules/number/index.ts
@@ -375,14 +375,18 @@ export class NumberModule extends SimpleModuleBase {
* @param options Maximum value or options object.
* @param options.min Lower bound for generated bigint. Defaults to `0n`.
* @param options.max Upper bound for generated bigint. Defaults to `min + 999999999999999n`.
+ * @param options.multipleOf The generated bigint will be a multiple of this parameter. Defaults to `1n`.
*
* @throws When `min` is greater than `max`.
+ * @throws When there are no suitable bigint between `min` and `max`.
+ * @throws When `multipleOf` is not a positive bigint.
*
* @example
* faker.number.bigInt() // 55422n
* faker.number.bigInt(100n) // 52n
* faker.number.bigInt({ min: 1000000n }) // 431433n
* faker.number.bigInt({ max: 100n }) // 42n
+ * faker.number.bigInt({ multipleOf: 7n }) // 35n
* faker.number.bigInt({ min: 10n, max: 100n }) // 36n
*
* @since 8.0.0
@@ -406,6 +410,12 @@ export class NumberModule extends SimpleModuleBase {
* @default min + 999999999999999n
*/
max?: bigint | number | string | boolean;
+ /**
+ * The generated bigint will be a multiple of this parameter.
+ *
+ * @default 1n
+ */
+ multipleOf?: bigint | number | string | boolean;
} = {}
): bigint {
if (
@@ -421,27 +431,38 @@ export class NumberModule extends SimpleModuleBase {
const min = BigInt(options.min ?? 0);
const max = BigInt(options.max ?? min + BigInt(999999999999999));
+ const multipleOf = BigInt(options.multipleOf ?? 1);
- if (max === min) {
- return min;
+ if (max < min) {
+ throw new FakerError(`Max ${max} should be larger than min ${min}.`);
}
- if (max < min) {
- throw new FakerError(`Max ${max} should be larger then min ${min}.`);
+ if (multipleOf <= BigInt(0)) {
+ throw new FakerError(`multipleOf should be greater than 0.`);
+ }
+
+ const effectiveMin = min / multipleOf + (min % multipleOf > 0n ? 1n : 0n); // Math.ceil(min / multipleOf)
+ const effectiveMax = max / multipleOf - (max % multipleOf < 0n ? 1n : 0n); // Math.floor(max / multipleOf)
+
+ if (effectiveMin === effectiveMax) {
+ return effectiveMin * multipleOf;
}
- const delta = max - min;
+ if (effectiveMax < effectiveMin) {
+ throw new FakerError(
+ `No suitable bigint value between ${min} and ${max} found.`
+ );
+ }
+ const delta = effectiveMax - effectiveMin + 1n; // +1 for inclusive max bounds and even distribution
const offset =
BigInt(
this.faker.string.numeric({
length: delta.toString(10).length,
allowLeadingZeros: true,
})
- ) %
- (delta + BigInt(1));
-
- return min + offset;
+ ) % delta;
+ return (effectiveMin + offset) * multipleOf;
}
/**
diff --git a/test/modules/__snapshots__/number.spec.ts.snap b/test/modules/__snapshots__/number.spec.ts.snap
index 516abc9f..521fc056 100644
--- a/test/modules/__snapshots__/number.spec.ts.snap
+++ b/test/modules/__snapshots__/number.spec.ts.snap
@@ -1,6 +1,6 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
-exports[`number > 42 > bigInt > noArgs 1`] = `397511086709821n`;
+exports[`number > 42 > bigInt > noArgs 1`] = `975110867098211n`;
exports[`number > 42 > bigInt > with big options 1`] = `19556777749482489605814694n`;
@@ -8,6 +8,8 @@ exports[`number > 42 > bigInt > with bigint value 1`] = `25n`;
exports[`number > 42 > bigInt > with boolean value 1`] = `1n`;
+exports[`number > 42 > bigInt > with multipleOf 1`] = `147890295638632n`;
+
exports[`number > 42 > bigInt > with number value 1`] = `39n`;
exports[`number > 42 > bigInt > with options 1`] = `19n`;
@@ -64,7 +66,7 @@ exports[`number > 42 > romanNumeral > with only max 1`] = `"LXII"`;
exports[`number > 42 > romanNumeral > with only min 1`] = `"MDI"`;
-exports[`number > 1211 > bigInt > noArgs 1`] = `982966736876848n`;
+exports[`number > 1211 > bigInt > noArgs 1`] = `829667368768488n`;
exports[`number > 1211 > bigInt > with big options 1`] = `25442250580110979794946298n`;
@@ -72,6 +74,8 @@ exports[`number > 1211 > bigInt > with bigint value 1`] = `114n`;
exports[`number > 1211 > bigInt > with boolean value 1`] = `1n`;
+exports[`number > 1211 > bigInt > with multipleOf 1`] = `784113589297853n`;
+
exports[`number > 1211 > bigInt > with number value 1`] = `12n`;
exports[`number > 1211 > bigInt > with options 1`] = `44n`;
@@ -128,7 +132,7 @@ exports[`number > 1211 > romanNumeral > with only max 1`] = `"CLIV"`;
exports[`number > 1211 > romanNumeral > with only min 1`] = `"MMMDCCXIV"`;
-exports[`number > 1337 > bigInt > noArgs 1`] = `212435297136194n`;
+exports[`number > 1337 > bigInt > noArgs 1`] = `124352971361947n`;
exports[`number > 1337 > bigInt > with big options 1`] = `27379244885156992800029992n`;
@@ -136,6 +140,8 @@ exports[`number > 1337 > bigInt > with bigint value 1`] = `88n`;
exports[`number > 1337 > bigInt > with boolean value 1`] = `0n`;
+exports[`number > 1337 > bigInt > with multipleOf 1`] = `682275118016671n`;
+
exports[`number > 1337 > bigInt > with number value 1`] = `21n`;
exports[`number > 1337 > bigInt > with options 1`] = `58n`;
diff --git a/test/modules/number.spec.ts b/test/modules/number.spec.ts
index d2762cf4..278baadb 100644
--- a/test/modules/number.spec.ts
+++ b/test/modules/number.spec.ts
@@ -50,6 +50,7 @@ describe('number', () => {
.it('with boolean value', true)
.it('with bigint value', 123n)
.it('with options', { min: -42, max: 69 })
+ .it('with multipleOf', { multipleOf: 7919n })
.it('with big options', {
min: 6135715171537515454317351n,
max: 32465761264574654845432354n,
@@ -631,7 +632,141 @@ describe('number', () => {
expect(() => {
faker.number.bigInt({ min, max });
}).toThrow(
- new FakerError(`Max ${max} should be larger then min ${min}.`)
+ new FakerError(`Max ${max} should be larger than min ${min}.`)
+ );
+ });
+
+ it('should generate a random bigint with a given multipleOf of 1n', () => {
+ const generateBigInt = faker.number.bigInt({ multipleOf: 1n });
+ expect(generateBigInt).toBeTypeOf('bigint');
+ });
+
+ it('should generate a random bigint with a given multipleOf of 7919n', () => {
+ const generateBigInt = faker.number.bigInt({ multipleOf: 7919n });
+ expect(generateBigInt).toBeTypeOf('bigint');
+ expect(generateBigInt % 7919n).toBe(0n);
+ });
+
+ it('should generate a random bigint with a given max value less than multipleOf', () => {
+ const generatedBigInt = faker.number.bigInt({
+ max: 10n,
+ multipleOf: 20n,
+ });
+ expect(generatedBigInt).toBeTypeOf('bigint');
+ expect(generatedBigInt % 20n).toBe(0n);
+ });
+
+ it('should generate a suitable bigint value between negative min and max', () => {
+ const generateBigInt = faker.number.bigInt({
+ min: -9,
+ max: 0,
+ multipleOf: 5,
+ });
+ expect(generateBigInt).toBeTypeOf('bigint');
+ expect(generateBigInt % 5n).toBe(0n);
+ });
+
+ it('should generate a suitable bigint value between negative min and negative max', () => {
+ const generateBigInt = faker.number.bigInt({
+ min: -9,
+ max: -1,
+ multipleOf: 5,
+ });
+ expect(generateBigInt).toBeTypeOf('bigint');
+ expect(generateBigInt).toBe(-5n);
+ });
+
+ it('should generate a suitable bigint value between negative min and negative max (edge case)', () => {
+ const generateBigInt = faker.number.bigInt({
+ min: -9,
+ max: -1,
+ multipleOf: 9,
+ });
+ expect(generateBigInt).toBeTypeOf('bigint');
+ expect(generateBigInt).toBe(-9n);
+ });
+
+ it('should return inclusive positive min/max value', () => {
+ const positive4 = 4n;
+ const positive5 = 5n;
+ let foundPositive4 = false;
+ let foundPositive5 = false;
+
+ for (let iter = 0; iter < 1000; iter++) {
+ const actual = faker.number.bigInt({
+ min: positive4,
+ max: positive5,
+ });
+
+ if (actual === positive4) {
+ foundPositive4 = true;
+ } else if (actual === positive5) {
+ foundPositive5 = true;
+ }
+
+ expect(actual).toBeTypeOf('bigint');
+ expect(actual).toBeGreaterThanOrEqual(positive4);
+ expect(actual).toBeLessThanOrEqual(positive5);
+
+ if (foundPositive4 && foundPositive5) {
+ break;
+ }
+ }
+
+ expect(foundPositive4).toBeTruthy();
+ expect(foundPositive5).toBeTruthy();
+ });
+
+ it('should return inclusive negative min/max value', () => {
+ const negative4 = -4n;
+ const negative5 = -5n;
+ let foundNegative4 = false;
+ let foundNegative5 = false;
+
+ for (let iter = 0; iter < 1000; iter++) {
+ const actual = faker.number.bigInt({
+ min: negative5,
+ max: negative4,
+ });
+
+ if (actual === negative4) {
+ foundNegative4 = true;
+ } else if (actual === negative5) {
+ foundNegative5 = true;
+ }
+
+ expect(actual).toBeTypeOf('bigint');
+ expect(actual).toBeGreaterThanOrEqual(negative5);
+ expect(actual).toBeLessThanOrEqual(negative4);
+
+ if (foundNegative4 && foundNegative5) {
+ break;
+ }
+ }
+
+ expect(foundNegative4).toBeTruthy();
+ expect(foundNegative5).toBeTruthy();
+ });
+
+ it('should throw for non-positive multipleOf', () => {
+ expect(() => faker.number.bigInt({ multipleOf: 0n })).toThrow(
+ new FakerError('multipleOf should be greater than 0.')
+ );
+ });
+
+ it('should throw if there is no suitable bigint value between min and max', () => {
+ expect(() =>
+ faker.number.bigInt({ min: 6, max: 9, multipleOf: 5 })
+ ).toThrow(
+ new FakerError('No suitable bigint value between 6 and 9 found.')
+ );
+ });
+
+ it('should throw if there is no suitable bigint value between same min and max', () => {
+ expect(() =>
+ faker.number.bigInt({ min: 1, max: 1, multipleOf: 5 })
+ ).toThrow(
+ new FakerError('No suitable bigint value between 1 and 1 found.')
);
});
});