Throw RangeError instead of returning NaN (which is not a bigint)

This commit is contained in:
Juanra Dikal 2020-05-29 17:33:07 +02:00
parent 9c4eaf2830
commit a2da4b0c4b
9 changed files with 89 additions and 53 deletions

View File

@ -148,7 +148,7 @@ iterations of Miller-Rabin Probabilistic Primality Test (FIPS 186-4 C.3.1)</p>
<dt><a href="#min">min(a, b)</a><code>bigint</code></dt> <dt><a href="#min">min(a, b)</a><code>bigint</code></dt>
<dd><p>Minimum. min(a,b)==b if a&gt;=b. min(a,b)==a if a&lt;=b</p> <dd><p>Minimum. min(a,b)==b if a&gt;=b. min(a,b)==a if a&lt;=b</p>
</dd> </dd>
<dt><a href="#modInv">modInv(a, n)</a><code>bigint</code> | <code>NaN</code></dt> <dt><a href="#modInv">modInv(a, n)</a><code>bigint</code></dt>
<dd><p>Modular inverse.</p> <dd><p>Modular inverse.</p>
</dd> </dd>
<dt><a href="#modPow">modPow(b, e, n)</a><code>bigint</code></dt> <dt><a href="#modPow">modPow(b, e, n)</a><code>bigint</code></dt>
@ -225,6 +225,10 @@ Take positive integers a, b as input, and return a triple (g, x, y), such that a
**Kind**: global function **Kind**: global function
**Returns**: [<code>egcdReturn</code>](#egcdReturn) - A triple (g, x, y), such that ax + by = g = gcd(a, b). **Returns**: [<code>egcdReturn</code>](#egcdReturn) - A triple (g, x, y), such that ax + by = g = gcd(a, b).
**Throws**:
- <code>RangeError</code> a and b MUST be > 0
| Param | Type | | Param | Type |
| --- | --- | | --- | --- |
@ -318,11 +322,15 @@ Minimum. min(a,b)==b if a>=b. min(a,b)==a if a<=b
<a name="modInv"></a> <a name="modInv"></a>
### modInv(a, n) ⇒ <code>bigint</code> \| <code>NaN</code> ### modInv(a, n) ⇒ <code>bigint</code>
Modular inverse. Modular inverse.
**Kind**: global function **Kind**: global function
**Returns**: <code>bigint</code> \| <code>NaN</code> - the inverse modulo n or NaN if it does not exist **Returns**: <code>bigint</code> - the inverse modulo n
**Throws**:
- <code>RangeError</code> a does not have inverse modulo n
| Param | Type | Description | | Param | Type | Description |
| --- | --- | --- | | --- | --- | --- |

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -39,6 +39,8 @@ function bitLength (a) {
* @param {number|bigint} a * @param {number|bigint} a
* @param {number|bigint} b * @param {number|bigint} b
* *
* @throws {RangeError} a and b MUST be > 0
*
* @returns {egcdReturn} A triple (g, x, y), such that ax + by = g = gcd(a, b). * @returns {egcdReturn} A triple (g, x, y), such that ax + by = g = gcd(a, b).
*/ */
function eGcd (a, b) { function eGcd (a, b) {
@ -152,18 +154,16 @@ function min (a, b) {
* @param {number|bigint} a The number to find an inverse for * @param {number|bigint} a The number to find an inverse for
* @param {number|bigint} n The modulo * @param {number|bigint} n The modulo
* *
* @returns {bigint|NaN} the inverse modulo n or NaN if it does not exist * @throws {RangeError} a does not have inverse modulo n
*
* @returns {bigint} the inverse modulo n
*/ */
function modInv (a, n) { function modInv (a, n) {
try { const egcd = eGcd(toZn(a, n), n)
const egcd = eGcd(toZn(a, n), n) if (egcd.g !== 1n) {
if (egcd.g !== 1n) { throw new RangeError(`${a.toString()} does not have inverse modulo ${n.toString()}`) // modular inverse does not exist
return NaN // modular inverse does not exist } else {
} else { return toZn(egcd.x, n)
return toZn(egcd.x, n)
}
} catch (error) {
return NaN
} }
} }
@ -178,7 +178,7 @@ function modInv (a, n) {
*/ */
function modPow (b, e, n) { function modPow (b, e, n) {
n = BigInt(n) n = BigInt(n)
if (n === 0n) { return NaN } else if (n === 1n) { return BigInt(0) } if (n === 0n) { throw new RangeError('n must be > 0') } else if (n === 1n) { return BigInt(0) }
b = toZn(b, n) b = toZn(b, n)

View File

@ -41,6 +41,8 @@ function bitLength (a) {
* @param {number|bigint} a * @param {number|bigint} a
* @param {number|bigint} b * @param {number|bigint} b
* *
* @throws {RangeError} a and b MUST be > 0
*
* @returns {egcdReturn} A triple (g, x, y), such that ax + by = g = gcd(a, b). * @returns {egcdReturn} A triple (g, x, y), such that ax + by = g = gcd(a, b).
*/ */
function eGcd (a, b) { function eGcd (a, b) {
@ -154,18 +156,16 @@ function min (a, b) {
* @param {number|bigint} a The number to find an inverse for * @param {number|bigint} a The number to find an inverse for
* @param {number|bigint} n The modulo * @param {number|bigint} n The modulo
* *
* @returns {bigint|NaN} the inverse modulo n or NaN if it does not exist * @throws {RangeError} a does not have inverse modulo n
*
* @returns {bigint} the inverse modulo n
*/ */
function modInv (a, n) { function modInv (a, n) {
try { const egcd = eGcd(toZn(a, n), n)
const egcd = eGcd(toZn(a, n), n) if (egcd.g !== 1n) {
if (egcd.g !== 1n) { throw new RangeError(`${a.toString()} does not have inverse modulo ${n.toString()}`) // modular inverse does not exist
return NaN // modular inverse does not exist } else {
} else { return toZn(egcd.x, n)
return toZn(egcd.x, n)
}
} catch (error) {
return NaN
} }
} }
@ -180,7 +180,7 @@ function modInv (a, n) {
*/ */
function modPow (b, e, n) { function modPow (b, e, n) {
n = BigInt(n) n = BigInt(n)
if (n === 0n) { return NaN } else if (n === 1n) { return BigInt(0) } if (n === 0n) { throw new RangeError('n must be > 0') } else if (n === 1n) { return BigInt(0) }
b = toZn(b, n) b = toZn(b, n)

View File

@ -39,6 +39,8 @@ export function bitLength (a) {
* @param {number|bigint} a * @param {number|bigint} a
* @param {number|bigint} b * @param {number|bigint} b
* *
* @throws {RangeError} a and b MUST be > 0
*
* @returns {egcdReturn} A triple (g, x, y), such that ax + by = g = gcd(a, b). * @returns {egcdReturn} A triple (g, x, y), such that ax + by = g = gcd(a, b).
*/ */
export function eGcd (a, b) { export function eGcd (a, b) {
@ -152,18 +154,16 @@ export function min (a, b) {
* @param {number|bigint} a The number to find an inverse for * @param {number|bigint} a The number to find an inverse for
* @param {number|bigint} n The modulo * @param {number|bigint} n The modulo
* *
* @returns {bigint|NaN} the inverse modulo n or NaN if it does not exist * @throws {RangeError} a does not have inverse modulo n
*
* @returns {bigint} the inverse modulo n
*/ */
export function modInv (a, n) { export function modInv (a, n) {
try { const egcd = eGcd(toZn(a, n), n)
const egcd = eGcd(toZn(a, n), n) if (egcd.g !== 1n) {
if (egcd.g !== 1n) { throw new RangeError(`${a.toString()} does not have inverse modulo ${n.toString()}`) // modular inverse does not exist
return NaN // modular inverse does not exist } else {
} else { return toZn(egcd.x, n)
return toZn(egcd.x, n)
}
} catch (error) {
return NaN
} }
} }
@ -178,7 +178,7 @@ export function modInv (a, n) {
*/ */
export function modPow (b, e, n) { export function modPow (b, e, n) {
n = BigInt(n) n = BigInt(n)
if (n === 0n) { return NaN } else if (n === 1n) { return BigInt(0) } if (n === 0n) { throw new RangeError('n must be > 0') } else if (n === 1n) { return BigInt(0) }
b = toZn(b, n) b = toZn(b, n)

View File

@ -21,21 +21,20 @@ const inputs = [
a: BigInt(-2), a: BigInt(-2),
n: BigInt(5), n: BigInt(5),
modInv: BigInt(2) modInv: BigInt(2)
}, }]
const invalidInputs = [
{ {
a: BigInt(2), a: BigInt(2),
n: BigInt(4), n: BigInt(4)
modInv: NaN
}, },
{ {
a: BigInt(0), a: BigInt(0),
n: BigInt(0), n: BigInt(0)
modInv: NaN
}, },
{ {
a: BigInt(0), a: BigInt(0),
n: BigInt(37), n: BigInt(37)
modInv: NaN
} }
] ]
@ -49,4 +48,16 @@ describe('modInv', function () {
}) })
}) })
} }
for (const input of invalidInputs) {
describe(`modInv(${input.a}, ${input.n})`, function () {
it('should throw RangeError', function () {
try {
_pkg.modInv(input.a, input.n)
throw new Error('should have failed')
} catch (err) {
chai.expect(err).to.be.instanceOf(RangeError)
}
})
})
}
}) })

View File

@ -7,12 +7,6 @@ const chai = require('chai')
// <-- // <--
const inputs = [ const inputs = [
{
a: BigInt(4),
b: BigInt(-1),
n: BigInt(0),
modPow: NaN
},
{ {
a: BigInt(4), a: BigInt(4),
b: BigInt(-1), b: BigInt(-1),
@ -42,6 +36,13 @@ const inputs = [
b: BigInt(3), b: BigInt(3),
n: BigInt(25), n: BigInt(25),
modPow: BigInt(2) modPow: BigInt(2)
}]
const invalidInputs = [
{
a: BigInt(4),
b: BigInt(-1),
n: BigInt(0)
} }
] ]
@ -55,6 +56,18 @@ describe('modPow', function () {
}) })
}) })
} }
for (const input of invalidInputs) {
describe(`modPow(${input.a}, ${input.b}, ${input.n})`, function () {
it('should throw RangeError', function () {
try {
_pkg.modPow(input.a, input.b, input.n)
throw new Error('should have failed')
} catch (err) {
chai.expect(err).to.be.instanceOf(RangeError)
}
})
})
}
describe('Time profiling', function () { describe('Time profiling', function () {
let iterations = 500 let iterations = 500
it(`just testing ${iterations} iterations of a big modular exponentiation (1024 bits)`, function () { it(`just testing ${iterations} iterations of a big modular exponentiation (1024 bits)`, function () {

8
types/index.d.ts vendored
View File

@ -34,6 +34,8 @@ export function bitLength(a: number | bigint): number;
* @param {number|bigint} a * @param {number|bigint} a
* @param {number|bigint} b * @param {number|bigint} b
* *
* @throws {RangeError} a and b MUST be > 0
*
* @returns {egcdReturn} A triple (g, x, y), such that ax + by = g = gcd(a, b). * @returns {egcdReturn} A triple (g, x, y), such that ax + by = g = gcd(a, b).
*/ */
export function eGcd(a: number | bigint, b: number | bigint): egcdReturn; export function eGcd(a: number | bigint, b: number | bigint): egcdReturn;
@ -91,9 +93,11 @@ export function min(a: number | bigint, b: number | bigint): bigint;
* @param {number|bigint} a The number to find an inverse for * @param {number|bigint} a The number to find an inverse for
* @param {number|bigint} n The modulo * @param {number|bigint} n The modulo
* *
* @returns {bigint|NaN} the inverse modulo n or NaN if it does not exist * @throws {RangeError} a does not have inverse modulo n
*
* @returns {bigint} the inverse modulo n
*/ */
export function modInv(a: number | bigint, n: number | bigint): number | bigint; export function modInv(a: number | bigint, n: number | bigint): bigint;
/** /**
* Modular exponentiation b**e mod n. Currently using the right-to-left binary method * Modular exponentiation b**e mod n. Currently using the right-to-left binary method
* *