-
Notifications
You must be signed in to change notification settings - Fork 98
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: BigInt exponentiation transpiler error (@dfinity/candid) (#599)
* Fix edge case of log2 function on BigInt bit count * Increase ilog2 input value strictness * Convert (BigInt(2) ** BigInt(n)) to iexp2(n)
- Loading branch information
Showing
3 changed files
with
62 additions
and
3 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
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,30 @@ | ||
import { ilog2, iexp2 } from './bigint-math'; | ||
|
||
test('ilog2', () => { | ||
for (let n = 1; n < 100; n++) { | ||
expect(ilog2(n)).toBe(n > 0 ? Math.floor(Math.log2(n)) : NaN); | ||
} | ||
expect(() => ilog2(0)).toThrow('Input must be positive'); | ||
expect(() => ilog2(-1)).toThrow('Input must be positive'); | ||
expect(() => ilog2(1.5)).toThrow( | ||
'The number 1.5 cannot be converted to a BigInt because it is not an integer', | ||
); | ||
expect(() => (ilog2 as (any) => number)('abc')).toThrow('Cannot convert abc to a BigInt'); | ||
}); | ||
|
||
test('iexp2', () => { | ||
for (let n = 0; n < 10; n++) { | ||
expect(iexp2(n)).toBe(BigInt(2 ** n)); | ||
} | ||
expect(() => ilog2(-1)).toThrow('Input must be positive'); | ||
expect(() => iexp2(1.5)).toThrow( | ||
'The number 1.5 cannot be converted to a BigInt because it is not an integer', | ||
); | ||
expect(() => (ilog2 as (any) => number)('abc')).toThrow('Cannot convert abc to a BigInt'); | ||
}); | ||
|
||
test('ilog2 and iexp2', () => { | ||
for (const p of [0, 1, 3, 55, 10000]) { | ||
expect(ilog2(iexp2(BigInt(p)))).toBe(p); | ||
} | ||
}); |
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,28 @@ | ||
/** | ||
* Equivalent to `Math.log2(n)` with support for `BigInt` values | ||
* | ||
* @param n bigint or integer | ||
* @returns integer | ||
*/ | ||
export function ilog2(n: bigint | number): number { | ||
const nBig = BigInt(n); | ||
if (n <= 0) { | ||
throw new RangeError('Input must be positive'); | ||
} | ||
return nBig.toString(2).length - 1; | ||
} | ||
|
||
/** | ||
* Equivalent to `2 ** n` with support for `BigInt` values | ||
* (necessary for browser preprocessors which replace the `**` operator with `Math.pow`) | ||
* | ||
* @param n bigint or integer | ||
* @returns bigint | ||
*/ | ||
export function iexp2(n: bigint | number): bigint { | ||
const nBig = BigInt(n); | ||
if (n < 0) { | ||
throw new RangeError('Input must be non-negative'); | ||
} | ||
return BigInt(1) << nBig; | ||
} |