Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(number): bigint multipleOf #3402

Open
wants to merge 23 commits into
base: next
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
29fdabc
feat(number): bigInt multipleOf - has option
soc221b Feb 16, 2025
7fc7c9d
refactor: fix typo
soc221b Feb 16, 2025
a3352e0
feat(number): bigInt multipleOf - must be positive
soc221b Feb 16, 2025
d868592
feat(number): bigInt multipleOf - larger than the given max
soc221b Feb 16, 2025
a2b3dd2
feat(number): bigInt multipleOf - update description
soc221b Feb 16, 2025
6100bf5
feat(number): bigInt multipleOf - implement
soc221b Feb 16, 2025
1cb6b03
test: fix test name
soc221b Feb 16, 2025
c28fda7
style: lint
soc221b Feb 16, 2025
82fa428
feat: add param and example
soc221b Feb 16, 2025
2aa0270
feat: add throws
soc221b Feb 16, 2025
8ed0e2a
Merge branch 'next' into feat/bigint-multiple-of
ST-DDT Feb 16, 2025
6b70cab
Merge branch 'next' into feat/bigint-multiple-of
ST-DDT Feb 17, 2025
0db440d
test: it should throw if there is no suitable bigint value between mi…
soc221b Feb 22, 2025
98b18d5
test: should generate a random bigint with a given max value less tha…
soc221b Feb 22, 2025
bce50d2
test: it should generate a suitable bigint value between negative min…
soc221b Feb 22, 2025
c9cfa2b
fix: bigint
soc221b Feb 22, 2025
1f3add8
feat: ensure error messages follow the same format
soc221b Feb 22, 2025
66f7a45
test: it should throw if there is no suitable bigint value between sa…
soc221b Feb 22, 2025
7af0457
style: format
soc221b Feb 22, 2025
fd245e9
fix: it should generate a suitable bigint value between negative min …
soc221b Feb 22, 2025
a900efb
fix: it should returns inclusive bounds
soc221b Feb 22, 2025
659fa09
refactor: use BigInt
soc221b Feb 22, 2025
d5379c2
Merge branch 'next' into feat/bigint-multiple-of
ST-DDT Feb 23, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 39 additions & 7 deletions src/modules/number/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 `multipleOf` is not a positive bigint.
* @throws When `multipleOf` is greater than `max`.
*
* @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
Expand All @@ -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 (
Expand All @@ -421,17 +431,40 @@ 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 === BigInt(0)
? min / multipleOf
: BigInt(0) <= min
? min / multipleOf + BigInt(1)
: min / multipleOf;
const effectiveMax =
max % multipleOf === BigInt(0)
? max / multipleOf
: BigInt(0) <= max
? max / multipleOf
: max / multipleOf - BigInt(1);

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;
const offset =
BigInt(
this.faker.string.numeric({
Expand All @@ -440,8 +473,7 @@ export class NumberModule extends SimpleModuleBase {
})
) %
(delta + BigInt(1));

return min + offset;
return (effectiveMin + offset) * multipleOf;
}

/**
Expand Down
6 changes: 6 additions & 0 deletions test/modules/__snapshots__/number.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -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`;
Expand Down Expand Up @@ -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`;
Expand Down Expand Up @@ -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`;
Expand Down
137 changes: 136 additions & 1 deletion test/modules/number.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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.')
);
});
});
Expand Down