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>
<dd><p>Minimum. min(a,b)==b if a&gt;=b. min(a,b)==a if a&lt;=b</p>
</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>
<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
**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 |
| --- | --- |
@ -318,11 +322,15 @@ Minimum. min(a,b)==b if a>=b. min(a,b)==a if a<=b
<a name="modInv"></a>
### modInv(a, n) ⇒ <code>bigint</code> \| <code>NaN</code>
### modInv(a, n) ⇒ <code>bigint</code>
Modular inverse.
**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 |
| --- | --- | --- |

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} b
*
* @throws {RangeError} a and b MUST be > 0
*
* @returns {egcdReturn} A triple (g, x, y), such that ax + by = g = gcd(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} 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) {
try {
const egcd = eGcd(toZn(a, n), n)
if (egcd.g !== 1n) {
return NaN // modular inverse does not exist
} else {
return toZn(egcd.x, n)
}
} catch (error) {
return NaN
const egcd = eGcd(toZn(a, n), n)
if (egcd.g !== 1n) {
throw new RangeError(`${a.toString()} does not have inverse modulo ${n.toString()}`) // modular inverse does not exist
} else {
return toZn(egcd.x, n)
}
}
@ -178,7 +178,7 @@ function modInv (a, n) {
*/
function modPow (b, e, 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)

View File

@ -41,6 +41,8 @@ function bitLength (a) {
* @param {number|bigint} a
* @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).
*/
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} 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) {
try {
const egcd = eGcd(toZn(a, n), n)
if (egcd.g !== 1n) {
return NaN // modular inverse does not exist
} else {
return toZn(egcd.x, n)
}
} catch (error) {
return NaN
const egcd = eGcd(toZn(a, n), n)
if (egcd.g !== 1n) {
throw new RangeError(`${a.toString()} does not have inverse modulo ${n.toString()}`) // modular inverse does not exist
} else {
return toZn(egcd.x, n)
}
}
@ -180,7 +180,7 @@ function modInv (a, n) {
*/
function modPow (b, e, 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)

View File

@ -39,6 +39,8 @@ export function bitLength (a) {
* @param {number|bigint} a
* @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).
*/
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} 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) {
try {
const egcd = eGcd(toZn(a, n), n)
if (egcd.g !== 1n) {
return NaN // modular inverse does not exist
} else {
return toZn(egcd.x, n)
}
} catch (error) {
return NaN
const egcd = eGcd(toZn(a, n), n)
if (egcd.g !== 1n) {
throw new RangeError(`${a.toString()} does not have inverse modulo ${n.toString()}`) // modular inverse does not exist
} else {
return toZn(egcd.x, n)
}
}
@ -178,7 +178,7 @@ export function modInv (a, n) {
*/
export function modPow (b, e, 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)

View File

@ -21,21 +21,20 @@ const inputs = [
a: BigInt(-2),
n: BigInt(5),
modInv: BigInt(2)
},
}]
const invalidInputs = [
{
a: BigInt(2),
n: BigInt(4),
modInv: NaN
n: BigInt(4)
},
{
a: BigInt(0),
n: BigInt(0),
modInv: NaN
n: BigInt(0)
},
{
a: BigInt(0),
n: BigInt(37),
modInv: NaN
n: BigInt(37)
}
]
@ -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 = [
{
a: BigInt(4),
b: BigInt(-1),
n: BigInt(0),
modPow: NaN
},
{
a: BigInt(4),
b: BigInt(-1),
@ -42,6 +36,13 @@ const inputs = [
b: BigInt(3),
n: BigInt(25),
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 () {
let iterations = 500
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} b
*
* @throws {RangeError} a and b MUST be > 0
*
* @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;
@ -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} 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
*