From 68b8eebe8a4f0953717872e9d8540216b32b4405 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Hern=C3=A1ndez=20Serrano?= Date: Thu, 25 Apr 2019 17:24:29 +0200 Subject: [PATCH] Better code structure. Better README. Added generation of IIFE file-a --- .gitignore | 68 +++++++ README.hbs | 46 ++--- README.md | 138 ++++++++------ build/build.rollup.js | 59 ++++-- dist/bigint-mod-arith-latest.browser.mod.js | 174 ++++++++--------- ...bigint-mod-arith-latest.browser.mod.min.js | 2 +- dist/bigint-mod-arith-latest.node.js | 176 +++++++++--------- package-lock.json | 34 ++-- package.json | 9 +- src/main.js | 166 ++++++++--------- 10 files changed, 486 insertions(+), 386 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1e2dc1b --- /dev/null +++ b/.gitignore @@ -0,0 +1,68 @@ +# Project specific files +dist/bigint-mod-arith-?.?.?.* + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +# next.js build output +.next + +# Visual Studio Code +.vscode + diff --git a/README.hbs b/README.hbs index 1b0d1c8..a7c2ca1 100644 --- a/README.hbs +++ b/README.hbs @@ -1,62 +1,52 @@ # bigint-mod-arith -Some extra functions to work with modular arithmetics using native JS (stage 3) implementation of BigInt. It can be used -with Node.js (>=10.4.0) and [Web Browsers supporting -BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#Browser_compatibility). +Some extra functions to work with modular arithmetics using native JS (stage 3) implementation of BigInt. It can be used by any [Web Browser or webview supporting BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#Browser_compatibility) and with Node.js (>=10.4.0). -If you are looking for a cryptographically secure random generator and for probale primes (generation and testing), you +If you are looking for a cryptographically-secure random generator and for strong probable primes (generation and testing), you may be interested in [bigint-secrets](https://github.com/juanelas/bigint-secrets) -_The operations supported on BigInts are not constant time. BigInt can be therefore **[unsuitable for use in -cryptography](https://www.chosenplaintext.ca/articles/beginners-guide-constant-time-cryptography.html)**_ - -Many platforms provide native support for cryptography, such as -[webcrypto](https://w3c.github.io/webcrypto/Overview.html) or [node -crypto](https://nodejs.org/dist/latest/docs/api/crypto.html). +_The operations supported on BigInts are not constant time. BigInt can be therefore **[unsuitable for use in cryptography](https://www.chosenplaintext.ca/articles/beginners-guide-constant-time-cryptography.html).** Many platforms provide native support for cryptography, such as [Web Cryptography API](https://w3c.github.io/webcrypto/) or [Node.js Crypto](https://nodejs.org/dist/latest/docs/api/crypto.html)._ ## Installation -bigint-mod-arith is distributed as both an ES6 and a CJS module. - -The ES6 module is built for any [web browser supporting -BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#Browser_compatibility). -The module only uses native javascript implementations and no polyfills had been applied. - -The CJS module is built as a standard node module. +bigint-mod-arith is distributed for [web browsers and/or webviews supporting +BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#Browser_compatibility) +as an ES6 module or an IIFE file; and for Node.js (>=10.4.0), as a CJS module. bigint-mod-arith can be imported to your project with `npm`: ```bash npm install bigint-mod-arith ``` +NPM installation defaults to the ES6 module for browsers and the CJS one for Node.js. -For web browsers, you can also [download the bundle from -GitHub](https://raw.githubusercontent.com/juanelas/bigint-mod-arith/master/dist/bigint-mod-arith-latest.browser.mod.min.js). +For web browsers, you can also directly download the minimised version of the [IIFE file](https://raw.githubusercontent.com/juanelas/bigint-mod-arith/master/dist/bigint-mod-arith-latest.browser.min.js) or the [ES6 module](https://raw.githubusercontent.com/juanelas/bigint-mod-arith/master/dist/bigint-mod-arith-latest.browser.mod.min.js) from GitHub. -## Usage examples +## Usage example With node js: ```javascript const bigintModArith = require('bigint-mod-arith'); -// Stage 3 BigInts with value 666 can be declared as BigInt('666') -// or the shorter no-linter-friendly new syntax 666n - +/* Stage 3 BigInts with value 666 can be declared as BigInt('666') +or the shorter new no-so-linter-friendly syntax 666n. +Notice that you can also pass a number, e.g. BigInt(666), but it is not +recommended since values over 2**53 - 1 won't be safe but no warning will +be raised. +*/ let a = BigInt('5'); let b = BigInt('2'); let n = BigInt('19'); -console.log(bigintModArith.modPow(a, b, n)); // prints 6 +console.log(bigintCryptoUtils.modPow(a, b, n)); // prints 6 -console.log(bigintModArith.modInv(BigInt('2'), BigInt('5'))); // prints 3 +console.log(bigintCryptoUtils.modInv(BigInt('2'), BigInt('5'))); // prints 3 -console.log(bigintModArith.modInv(BigInt('3'), BigInt('5'))); // prints 2 +console.log(bigintCryptoUtils.modInv(BigInt('3'), BigInt('5'))); // prints 2 ``` From a browser, you can just load the module in a html page as: ```html -``` +For web browsers, you can also directly download the minimised version of the [IIFE file](https://raw.githubusercontent.com/juanelas/bigint-mod-arith/master/dist/bigint-mod-arith-latest.browser.min.js) or the [ES6 module](https://raw.githubusercontent.com/juanelas/bigint-mod-arith/master/dist/bigint-mod-arith-latest.browser.mod.min.js) from GitHub. -## Usage examples +## Usage example +With node js: ```javascript const bigintModArith = require('bigint-mod-arith'); -// Stage 3 BigInts with value 666 can be declared as BigInt('666') -// or the shorter no-linter-friendly new syntax 666n - -let a = BigInt('5'); -let b = BigInt('2'); +/* Stage 3 BigInts with value 666 can be declared as BigInt('666') +or the shorter new no-so-linter-friendly syntax 666n. +Notice that you can also pass a number, e.g. BigInt(666), but it is not +recommended since values over 2**53 - 1 won't be safe but no warning will +be raised. +*/ +let a = BigInt('5'); +let b = BigInt('2'); let n = BigInt('19'); - -console.log(bigintModArith.modPow(a, b, n)); // prints 6 - -console.log(bigintModArith.modInv(BigInt('2'), BigInt('5'))); // prints 3 - -console.log(bigintModArith.modInv(BigInt('3'), BigInt('5'))); // prints 2 + +console.log(bigintCryptoUtils.modPow(a, b, n)); // prints 6 + +console.log(bigintCryptoUtils.modInv(BigInt('2'), BigInt('5'))); // prints 3 + +console.log(bigintCryptoUtils.modInv(BigInt('3'), BigInt('5'))); // prints 2 +``` + +From a browser, you can just load the module in a html page as: +```html + ``` # bigint-mod-arith JS Doc @@ -52,25 +68,25 @@ console.log(bigintModArith.modInv(BigInt('3'), BigInt('5'))); // prints 2
abs(a)bigint

Absolute value. abs(a)==a if a>=0. abs(a)==-a if a<0

+
eGcd(a, b)egcdReturn
+

An iterative implementation of the extended euclidean algorithm or extended greatest common divisor algorithm. +Take positive integers a, b as input, and return a triple (g, x, y), such that ax + by = g = gcd(a, b).

+
gcd(a, b)bigint

Greatest-common divisor of two integers based on the iterative binary algorithm.

lcm(a, b)bigint

The least common multiple computed as abs(a*b)/gcd(a,b)

-
toZn(a, n)bigint
-

Finds the smallest positive element that is congruent to a in modulo n

-
-
eGcd(a, b)egcdReturn
-

An iterative implementation of the extended euclidean algorithm or extended greatest common divisor algorithm. -Take positive integers a, b as input, and return a triple (g, x, y), such that ax + by = g = gcd(a, b).

-
modInv(a, n)bigint

Modular inverse.

modPow(a, b, n)bigint

Modular exponentiation a**b mod n

+
toZn(a, n)bigint
+

Finds the smallest positive element that is congruent to a in modulo n

+
## Typedefs @@ -93,6 +109,19 @@ Absolute value. abs(a)==a if a>=0. abs(a)==-a if a<0 | --- | --- | | a | number \| bigint | + + +## eGcd(a, b) ⇒ [egcdReturn](#egcdReturn) +An iterative implementation of the extended euclidean algorithm or extended greatest common divisor algorithm. +Take positive integers a, b as input, and return a triple (g, x, y), such that ax + by = g = gcd(a, b). + +**Kind**: global function + +| Param | Type | +| --- | --- | +| a | number \| bigint | +| b | number \| bigint | + ## gcd(a, b) ⇒ bigint @@ -119,32 +148,6 @@ The least common multiple computed as abs(a*b)/gcd(a,b) | a | number \| bigint | | b | number \| bigint | - - -## toZn(a, n) ⇒ bigint -Finds the smallest positive element that is congruent to a in modulo n - -**Kind**: global function -**Returns**: bigint - The smallest positive representation of a in modulo n - -| Param | Type | Description | -| --- | --- | --- | -| a | number \| bigint | An integer | -| n | number \| bigint | The modulo | - - - -## eGcd(a, b) ⇒ [egcdReturn](#egcdReturn) -An iterative implementation of the extended euclidean algorithm or extended greatest common divisor algorithm. -Take positive integers a, b as input, and return a triple (g, x, y), such that ax + by = g = gcd(a, b). - -**Kind**: global function - -| Param | Type | -| --- | --- | -| a | number \| bigint | -| b | number \| bigint | - ## modInv(a, n) ⇒ bigint @@ -172,6 +175,19 @@ Modular exponentiation a**b mod n | b | number \| bigint | exponent | | n | number \| bigint | modulo | + + +## toZn(a, n) ⇒ bigint +Finds the smallest positive element that is congruent to a in modulo n + +**Kind**: global function +**Returns**: bigint - The smallest positive representation of a in modulo n + +| Param | Type | Description | +| --- | --- | --- | +| a | number \| bigint | An integer | +| n | number \| bigint | The modulo | + ## egcdReturn : Object @@ -187,4 +203,4 @@ A triple (g, x, y), such that ax + by = g = gcd(a, b). | y | bigint | -* * * +* * * \ No newline at end of file diff --git a/build/build.rollup.js b/build/build.rollup.js index 9e46a21..8b7c4bc 100644 --- a/build/build.rollup.js +++ b/build/build.rollup.js @@ -1,48 +1,73 @@ +'use strict'; + const rollup = require('rollup'); -const commonjs = require('rollup-plugin-commonjs'); const minify = require('rollup-plugin-babel-minify'); const fs = require('fs'); const path = require('path'); const pkgJson = require('../package.json'); +const rootDir = path.join(__dirname, '..'); +const srcDir = path.join(rootDir, 'src'); +const dstDir = path.join(rootDir, 'dist'); const buildOptions = [ { // Browser input: { - input: path.join(__dirname, '..', 'src', 'main.js'), - plugins: [ - commonjs() - ], + input: path.join(srcDir, 'main.js') }, output: { - file: path.join(__dirname, '..', 'dist', `${pkgJson.name}-${pkgJson.version}.browser.mod.js`), - format: 'esm' + file: path.join(dstDir, `${pkgJson.name}-${pkgJson.version}.browser.js`), + format: 'iife', + name: camelise(pkgJson.name) } }, { // Browser minified input: { - input: path.join(__dirname, '..', 'src', 'main.js'), + input: path.join(srcDir, 'main.js'), plugins: [ - commonjs(), minify({ 'comments': false }) ], }, output: { - file: path.join(__dirname, '..', 'dist', `${pkgJson.name}-${pkgJson.version}.browser.mod.min.js`), + file: path.join(dstDir, `${pkgJson.name}-${pkgJson.version}.browser.min.js`), + format: 'iife', + name: camelise(pkgJson.name) + } + }, + { // Browser esm + input: { + input: path.join(srcDir, 'main.js') + }, + output: { + file: path.join(dstDir, `${pkgJson.name}-${pkgJson.version}.browser.mod.js`), + format: 'esm' + } + }, + { // Browser esm minified + input: { + input: path.join(srcDir, 'main.js'), + plugins: [ + minify({ + 'comments': false + }) + ], + }, + output: { + file: path.join(dstDir, `${pkgJson.name}-${pkgJson.version}.browser.mod.min.js`), format: 'esm' } }, { // Node input: { - input: path.join(__dirname, '..', 'src', 'main.js'), + input: path.join(srcDir, 'main.js'), }, output: { - file: path.join(__dirname, '..', 'dist', `${pkgJson.name}-${pkgJson.version}.node.js`), + file: path.join(dstDir, `${pkgJson.name}-${pkgJson.version}.node.js`), format: 'cjs' } - }, + } ]; for (const options of buildOptions) { @@ -50,7 +75,6 @@ for (const options of buildOptions) { } - /* --- HELPLER FUNCTIONS --- */ async function build(options) { @@ -69,3 +93,10 @@ async function build(options) { options.output.file.replace(`${pkgJson.name}-${pkgJson.version}.`, `${pkgJson.name}-latest.`) ); } + +function camelise(str) { + return str.replace(/-([a-z])/g, + function (m, w) { + return w.toUpperCase(); + }); +} \ No newline at end of file diff --git a/dist/bigint-mod-arith-latest.browser.mod.js b/dist/bigint-mod-arith-latest.browser.mod.js index 85bb47b..bb83052 100644 --- a/dist/bigint-mod-arith-latest.browser.mod.js +++ b/dist/bigint-mod-arith-latest.browser.mod.js @@ -1,3 +1,7 @@ +const _ZERO = BigInt(0); +const _ONE = BigInt(1); +const _TWO = BigInt(2); + /** * Absolute value. abs(a)==a if a>=0. abs(a)==-a if a<0 * @@ -5,68 +9,10 @@ * * @returns {bigint} the absolute value of a */ -const abs = function (a) { +function abs(a) { a = BigInt(a); - return (a >= BigInt(0)) ? a : -a; -}; - -/** - * Greatest-common divisor of two integers based on the iterative binary algorithm. - * - * @param {number|bigint} a - * @param {number|bigint} b - * - * @returns {bigint} The greatest common divisor of a and b - */ -const gcd = function (a, b) { - a = abs(a); - b = abs(b); - let shift = BigInt(0); - while (!((a | b) & BigInt(1))) { - a >>= BigInt(1); - b >>= BigInt(1); - shift++; - } - while (!(a & BigInt(1))) a >>= BigInt(1); - do { - while (!(b & BigInt(1))) b >>= BigInt(1); - if (a > b) { - let x = a; - a = b; - b = x; - } - b -= a; - } while (b); - - // rescale - return a << shift; -}; - -/** - * The least common multiple computed as abs(a*b)/gcd(a,b) - * @param {number|bigint} a - * @param {number|bigint} b - * - * @returns {bigint} The least common multiple of a and b - */ -const lcm = function (a, b) { - a = BigInt(a); - b = BigInt(b); - return abs(a * b) / gcd(a, b); -}; - -/** - * Finds the smallest positive element that is congruent to a in modulo n - * @param {number|bigint} a An integer - * @param {number|bigint} n The modulo - * - * @returns {bigint} The smallest positive representation of a in modulo n - */ -const toZn = function (a, n) { - n = BigInt(n); - a = BigInt(a) % n; - return (a < 0) ? a + n : a; -}; + return (a >= _ZERO) ? a : -a; +} /** * @typedef {Object} egcdReturn A triple (g, x, y), such that ax + by = g = gcd(a, b). @@ -83,15 +29,15 @@ const toZn = function (a, n) { * * @returns {egcdReturn} */ -const eGcd = function (a, b) { +function eGcd(a, b) { a = BigInt(a); b = BigInt(b); - let x = BigInt(0); - let y = BigInt(1); - let u = BigInt(1); - let v = BigInt(0); + let x = _ZERO; + let y = _ONE; + let u = _ONE; + let v = _ZERO; - while (a !== BigInt(0)) { + while (a !== _ZERO) { let q = b / a; let r = b % a; let m = x - (u * q); @@ -108,7 +54,52 @@ const eGcd = function (a, b) { x: x, y: y }; -}; +} + +/** + * Greatest-common divisor of two integers based on the iterative binary algorithm. + * + * @param {number|bigint} a + * @param {number|bigint} b + * + * @returns {bigint} The greatest common divisor of a and b + */ +function gcd(a, b) { + a = abs(a); + b = abs(b); + let shift = _ZERO; + while (!((a | b) & _ONE)) { + a >>= _ONE; + b >>= _ONE; + shift++; + } + while (!(a & _ONE)) a >>= _ONE; + do { + while (!(b & _ONE)) b >>= _ONE; + if (a > b) { + let x = a; + a = b; + b = x; + } + b -= a; + } while (b); + + // rescale + return a << shift; +} + +/** + * The least common multiple computed as abs(a*b)/gcd(a,b) + * @param {number|bigint} a + * @param {number|bigint} b + * + * @returns {bigint} The least common multiple of a and b + */ +function lcm(a, b) { + a = BigInt(a); + b = BigInt(b); + return abs(a * b) / gcd(a, b); +} /** * Modular inverse. @@ -118,14 +109,14 @@ const eGcd = function (a, b) { * * @returns {bigint} the inverse modulo n */ -const modInv = function (a, n) { +function modInv(a, n) { let egcd = eGcd(a, n); - if (egcd.b !== BigInt(1)) { + if (egcd.b !== _ONE) { return null; // modular inverse does not exist } else { return toZn(egcd.x, n); } -}; +} /** * Modular exponentiation a**b mod n @@ -135,20 +126,20 @@ const modInv = function (a, n) { * * @returns {bigint} a**b mod n */ -const modPow = function (a, b, n) { +function modPow(a, b, n) { // See Knuth, volume 2, section 4.6.3. n = BigInt(n); a = toZn(a, n); b = BigInt(b); - if (b < BigInt(0)) { + if (b < _ZERO) { return modInv(modPow(a, abs(b), n), n); } - let result = BigInt(1); + let result = _ONE; let x = a; while (b > 0) { - var leastSignificantBit = b % BigInt(2); - b = b / BigInt(2); - if (leastSignificantBit == BigInt(1)) { + var leastSignificantBit = b % _TWO; + b = b / _TWO; + if (leastSignificantBit == _ONE) { result = result * x; result = result % n; } @@ -156,20 +147,19 @@ const modPow = function (a, b, n) { x = x % n; } return result; -}; +} -var main = { - abs: abs, - gcd: gcd, - lcm: lcm, - modInv: modInv, - modPow: modPow -}; -var main_1 = main.abs; -var main_2 = main.gcd; -var main_3 = main.lcm; -var main_4 = main.modInv; -var main_5 = main.modPow; +/** + * Finds the smallest positive element that is congruent to a in modulo n + * @param {number|bigint} a An integer + * @param {number|bigint} n The modulo + * + * @returns {bigint} The smallest positive representation of a in modulo n + */ +function toZn(a, n) { + n = BigInt(n); + a = BigInt(a) % n; + return (a < 0) ? a + n : a; +} -export default main; -export { main_1 as abs, main_2 as gcd, main_3 as lcm, main_4 as modInv, main_5 as modPow }; +export { abs, eGcd, gcd, lcm, modInv, modPow, toZn }; diff --git a/dist/bigint-mod-arith-latest.browser.mod.min.js b/dist/bigint-mod-arith-latest.browser.mod.min.js index 952f775..8bdd544 100644 --- a/dist/bigint-mod-arith-latest.browser.mod.min.js +++ b/dist/bigint-mod-arith-latest.browser.mod.min.js @@ -1 +1 @@ -const abs=function(b){return b=BigInt(b),b>=BigInt(0)?b:-b},gcd=function(c,d){c=abs(c),d=abs(d);let e=BigInt(0);for(;!((c|d)&BigInt(1));)c>>=BigInt(1),d>>=BigInt(1),e++;for(;!(c&BigInt(1));)c>>=BigInt(1);do{for(;!(d&BigInt(1));)d>>=BigInt(1);if(c>d){let a=c;c=d,d=a}d-=c}while(d);return c<b?b+c:b},eGcd=function(c,d){c=BigInt(c),d=BigInt(d);let e=BigInt(0),f=BigInt(1),g=BigInt(1),h=BigInt(0);for(;c!==BigInt(0);){let a=d/c,b=d%c,i=e-g*a,j=f-h*a;d=c,c=b,e=g,f=h,g=i,h=j}return{b:d,x:e,y:f}},modInv=function(b,a){let c=eGcd(b,a);return c.b===BigInt(1)?toZn(c.x,a):null},modPow=function(c,d,e){if(e=BigInt(e),c=toZn(c,e),d=BigInt(d),d=_ZERO?b:-b}function eGcd(c,d){c=BigInt(c),d=BigInt(d);let e=_ZERO,f=_ONE,g=_ONE,h=_ZERO;for(;c!==_ZERO;){let a=d/c,b=d%c,i=e-g*a,j=f-h*a;d=c,c=b,e=g,f=h,g=i,h=j}return{b:d,x:e,y:f}}function gcd(c,d){c=abs(c),d=abs(d);let e=_ZERO;for(;!((c|d)&_ONE);)c>>=_ONE,d>>=_ONE,e++;for(;!(c&_ONE);)c>>=_ONE;do{for(;!(d&_ONE);)d>>=_ONE;if(c>d){let a=c;c=d,d=a}d-=c}while(d);return c<b?b+c:b}export{abs,eGcd,gcd,lcm,modInv,modPow,toZn}; diff --git a/dist/bigint-mod-arith-latest.node.js b/dist/bigint-mod-arith-latest.node.js index f144e90..9da0c11 100644 --- a/dist/bigint-mod-arith-latest.node.js +++ b/dist/bigint-mod-arith-latest.node.js @@ -1,5 +1,11 @@ 'use strict'; +Object.defineProperty(exports, '__esModule', { value: true }); + +const _ZERO = BigInt(0); +const _ONE = BigInt(1); +const _TWO = BigInt(2); + /** * Absolute value. abs(a)==a if a>=0. abs(a)==-a if a<0 * @@ -7,68 +13,10 @@ * * @returns {bigint} the absolute value of a */ -const abs = function (a) { +function abs(a) { a = BigInt(a); - return (a >= BigInt(0)) ? a : -a; -}; - -/** - * Greatest-common divisor of two integers based on the iterative binary algorithm. - * - * @param {number|bigint} a - * @param {number|bigint} b - * - * @returns {bigint} The greatest common divisor of a and b - */ -const gcd = function (a, b) { - a = abs(a); - b = abs(b); - let shift = BigInt(0); - while (!((a | b) & BigInt(1))) { - a >>= BigInt(1); - b >>= BigInt(1); - shift++; - } - while (!(a & BigInt(1))) a >>= BigInt(1); - do { - while (!(b & BigInt(1))) b >>= BigInt(1); - if (a > b) { - let x = a; - a = b; - b = x; - } - b -= a; - } while (b); - - // rescale - return a << shift; -}; - -/** - * The least common multiple computed as abs(a*b)/gcd(a,b) - * @param {number|bigint} a - * @param {number|bigint} b - * - * @returns {bigint} The least common multiple of a and b - */ -const lcm = function (a, b) { - a = BigInt(a); - b = BigInt(b); - return abs(a * b) / gcd(a, b); -}; - -/** - * Finds the smallest positive element that is congruent to a in modulo n - * @param {number|bigint} a An integer - * @param {number|bigint} n The modulo - * - * @returns {bigint} The smallest positive representation of a in modulo n - */ -const toZn = function (a, n) { - n = BigInt(n); - a = BigInt(a) % n; - return (a < 0) ? a + n : a; -}; + return (a >= _ZERO) ? a : -a; +} /** * @typedef {Object} egcdReturn A triple (g, x, y), such that ax + by = g = gcd(a, b). @@ -85,15 +33,15 @@ const toZn = function (a, n) { * * @returns {egcdReturn} */ -const eGcd = function (a, b) { +function eGcd(a, b) { a = BigInt(a); b = BigInt(b); - let x = BigInt(0); - let y = BigInt(1); - let u = BigInt(1); - let v = BigInt(0); + let x = _ZERO; + let y = _ONE; + let u = _ONE; + let v = _ZERO; - while (a !== BigInt(0)) { + while (a !== _ZERO) { let q = b / a; let r = b % a; let m = x - (u * q); @@ -110,7 +58,52 @@ const eGcd = function (a, b) { x: x, y: y }; -}; +} + +/** + * Greatest-common divisor of two integers based on the iterative binary algorithm. + * + * @param {number|bigint} a + * @param {number|bigint} b + * + * @returns {bigint} The greatest common divisor of a and b + */ +function gcd(a, b) { + a = abs(a); + b = abs(b); + let shift = _ZERO; + while (!((a | b) & _ONE)) { + a >>= _ONE; + b >>= _ONE; + shift++; + } + while (!(a & _ONE)) a >>= _ONE; + do { + while (!(b & _ONE)) b >>= _ONE; + if (a > b) { + let x = a; + a = b; + b = x; + } + b -= a; + } while (b); + + // rescale + return a << shift; +} + +/** + * The least common multiple computed as abs(a*b)/gcd(a,b) + * @param {number|bigint} a + * @param {number|bigint} b + * + * @returns {bigint} The least common multiple of a and b + */ +function lcm(a, b) { + a = BigInt(a); + b = BigInt(b); + return abs(a * b) / gcd(a, b); +} /** * Modular inverse. @@ -120,14 +113,14 @@ const eGcd = function (a, b) { * * @returns {bigint} the inverse modulo n */ -const modInv = function (a, n) { +function modInv(a, n) { let egcd = eGcd(a, n); - if (egcd.b !== BigInt(1)) { + if (egcd.b !== _ONE) { return null; // modular inverse does not exist } else { return toZn(egcd.x, n); } -}; +} /** * Modular exponentiation a**b mod n @@ -137,20 +130,20 @@ const modInv = function (a, n) { * * @returns {bigint} a**b mod n */ -const modPow = function (a, b, n) { +function modPow(a, b, n) { // See Knuth, volume 2, section 4.6.3. n = BigInt(n); a = toZn(a, n); b = BigInt(b); - if (b < BigInt(0)) { + if (b < _ZERO) { return modInv(modPow(a, abs(b), n), n); } - let result = BigInt(1); + let result = _ONE; let x = a; while (b > 0) { - var leastSignificantBit = b % BigInt(2); - b = b / BigInt(2); - if (leastSignificantBit == BigInt(1)) { + var leastSignificantBit = b % _TWO; + b = b / _TWO; + if (leastSignificantBit == _ONE) { result = result * x; result = result % n; } @@ -158,12 +151,25 @@ const modPow = function (a, b, n) { x = x % n; } return result; -}; +} -module.exports = { - abs: abs, - gcd: gcd, - lcm: lcm, - modInv: modInv, - modPow: modPow -}; +/** + * Finds the smallest positive element that is congruent to a in modulo n + * @param {number|bigint} a An integer + * @param {number|bigint} n The modulo + * + * @returns {bigint} The smallest positive representation of a in modulo n + */ +function toZn(a, n) { + n = BigInt(n); + a = BigInt(a) % n; + return (a < 0) ? a + n : a; +} + +exports.abs = abs; +exports.eGcd = eGcd; +exports.gcd = gcd; +exports.lcm = lcm; +exports.modInv = modInv; +exports.modPow = modPow; +exports.toZn = toZn; diff --git a/package-lock.json b/package-lock.json index 30f75cf..006b93a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -188,9 +188,9 @@ "dev": true }, "@types/node": { - "version": "11.13.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-11.13.0.tgz", - "integrity": "sha512-rx29MMkRdVmzunmiA4lzBYJNnXsW/PhG4kMBy2ATsYaDjGGR75dCFEVVROKpNwlVdcUX3xxlghKQOeDPBJobng==", + "version": "11.13.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-11.13.7.tgz", + "integrity": "sha512-suFHr6hcA9mp8vFrZTgrmqW2ZU3mbWsryQtQlY/QvwTISCw7nw/j+bCQPPohqmskhmqa5wLNuMHTTsc+xf1MQg==", "dev": true }, "acorn": { @@ -774,9 +774,9 @@ } }, "commander": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", - "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", + "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", "dev": true, "optional": true }, @@ -1169,9 +1169,9 @@ "dev": true }, "handlebars": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.1.tgz", - "integrity": "sha512-3Zhi6C0euYZL5sM0Zcy7lInLXKQ+YLcF/olbN010mzGQ4XVm50JeyBnMqofHh696GrciGruC7kCcApPDJvVgwA==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz", + "integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==", "dev": true, "requires": { "neo-async": "^2.6.0", @@ -1891,13 +1891,13 @@ "dev": true }, "rollup": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-1.9.0.tgz", - "integrity": "sha512-cNZx9MLpKFMSaObdVFeu8nXw8gfw6yjuxWjt5mRCJcBZrAJ0NHAYwemKjayvYvhLaNNkf3+kS2DKRKS5J6NRVg==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-1.10.1.tgz", + "integrity": "sha512-pW353tmBE7QP622ITkGxtqF0d5gSRCVPD9xqM+fcPjudeZfoXMFW2sCzsTe2TU/zU1xamIjiS9xuFCPVT9fESw==", "dev": true, "requires": { "@types/estree": "0.0.39", - "@types/node": "^11.13.0", + "@types/node": "^11.13.5", "acorn": "^6.1.1" } }, @@ -2328,13 +2328,13 @@ "dev": true }, "uglify-js": { - "version": "3.4.10", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.10.tgz", - "integrity": "sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw==", + "version": "3.5.8", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.5.8.tgz", + "integrity": "sha512-GFSjB1nZIzoIq70qvDRtWRORHX3vFkAnyK/rDExc0BN7r9+/S+Voz3t/fwJuVfjppAMz+ceR2poE7tkhvnVwQQ==", "dev": true, "optional": true, "requires": { - "commander": "~2.19.0", + "commander": "~2.20.0", "source-map": "~0.6.1" } }, diff --git a/package.json b/package.json index 49ea338..9fcf5cf 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,9 @@ "lcm", "gcd", "egcd", + "modinv", "modular inverse", + "modpow", "modular exponentiation" ], "license": "MIT", @@ -26,13 +28,14 @@ "src": "./src" }, "scripts": { - "docs:build": "jsdoc2md --template=README.hbs --files ./src/main.js > README.md", "build": "node build/build.rollup.js", - "prepublishOnly": "npm run build && npm run docs:build" + "build:docs": "jsdoc2md --template=README.hbs --files ./src/main.js > README.md", + "build:all": "npm run build && npm run build:docs", + "prepublishOnly": "npm run build && npm run build:docs" }, "devDependencies": { "jsdoc-to-markdown": "^4.0.1", - "rollup": "^1.9.0", + "rollup": "^1.10.1", "rollup-plugin-babel-minify": "^8.0.0", "rollup-plugin-commonjs": "^9.3.4" } diff --git a/src/main.js b/src/main.js index 902588e..79b5b40 100644 --- a/src/main.js +++ b/src/main.js @@ -1,5 +1,9 @@ 'use strict'; +const _ZERO = BigInt(0); +const _ONE = BigInt(1); +const _TWO = BigInt(2); + /** * Absolute value. abs(a)==a if a>=0. abs(a)==-a if a<0 * @@ -7,68 +11,10 @@ * * @returns {bigint} the absolute value of a */ -const abs = function (a) { +export function abs(a) { a = BigInt(a); - return (a >= BigInt(0)) ? a : -a; -}; - -/** - * Greatest-common divisor of two integers based on the iterative binary algorithm. - * - * @param {number|bigint} a - * @param {number|bigint} b - * - * @returns {bigint} The greatest common divisor of a and b - */ -const gcd = function (a, b) { - a = abs(a); - b = abs(b); - let shift = BigInt(0); - while (!((a | b) & BigInt(1))) { - a >>= BigInt(1); - b >>= BigInt(1); - shift++; - } - while (!(a & BigInt(1))) a >>= BigInt(1); - do { - while (!(b & BigInt(1))) b >>= BigInt(1); - if (a > b) { - let x = a; - a = b; - b = x; - } - b -= a; - } while (b); - - // rescale - return a << shift; -}; - -/** - * The least common multiple computed as abs(a*b)/gcd(a,b) - * @param {number|bigint} a - * @param {number|bigint} b - * - * @returns {bigint} The least common multiple of a and b - */ -const lcm = function (a, b) { - a = BigInt(a); - b = BigInt(b); - return abs(a * b) / gcd(a, b); -}; - -/** - * Finds the smallest positive element that is congruent to a in modulo n - * @param {number|bigint} a An integer - * @param {number|bigint} n The modulo - * - * @returns {bigint} The smallest positive representation of a in modulo n - */ -const toZn = function (a, n) { - n = BigInt(n); - a = BigInt(a) % n; - return (a < 0) ? a + n : a; -}; + return (a >= _ZERO) ? a : -a; +} /** * @typedef {Object} egcdReturn A triple (g, x, y), such that ax + by = g = gcd(a, b). @@ -85,15 +31,15 @@ const toZn = function (a, n) { * * @returns {egcdReturn} */ -const eGcd = function (a, b) { +export function eGcd(a, b) { a = BigInt(a); b = BigInt(b); - let x = BigInt(0); - let y = BigInt(1); - let u = BigInt(1); - let v = BigInt(0); + let x = _ZERO; + let y = _ONE; + let u = _ONE; + let v = _ZERO; - while (a !== BigInt(0)) { + while (a !== _ZERO) { let q = b / a; let r = b % a; let m = x - (u * q); @@ -110,7 +56,52 @@ const eGcd = function (a, b) { x: x, y: y }; -}; +} + +/** + * Greatest-common divisor of two integers based on the iterative binary algorithm. + * + * @param {number|bigint} a + * @param {number|bigint} b + * + * @returns {bigint} The greatest common divisor of a and b + */ +export function gcd(a, b) { + a = abs(a); + b = abs(b); + let shift = _ZERO; + while (!((a | b) & _ONE)) { + a >>= _ONE; + b >>= _ONE; + shift++; + } + while (!(a & _ONE)) a >>= _ONE; + do { + while (!(b & _ONE)) b >>= _ONE; + if (a > b) { + let x = a; + a = b; + b = x; + } + b -= a; + } while (b); + + // rescale + return a << shift; +} + +/** + * The least common multiple computed as abs(a*b)/gcd(a,b) + * @param {number|bigint} a + * @param {number|bigint} b + * + * @returns {bigint} The least common multiple of a and b + */ +export function lcm(a, b) { + a = BigInt(a); + b = BigInt(b); + return abs(a * b) / gcd(a, b); +} /** * Modular inverse. @@ -120,14 +111,14 @@ const eGcd = function (a, b) { * * @returns {bigint} the inverse modulo n */ -const modInv = function (a, n) { +export function modInv(a, n) { let egcd = eGcd(a, n); - if (egcd.b !== BigInt(1)) { + if (egcd.b !== _ONE) { return null; // modular inverse does not exist } else { return toZn(egcd.x, n); } -}; +} /** * Modular exponentiation a**b mod n @@ -137,20 +128,20 @@ const modInv = function (a, n) { * * @returns {bigint} a**b mod n */ -const modPow = function (a, b, n) { +export function modPow(a, b, n) { // See Knuth, volume 2, section 4.6.3. n = BigInt(n); a = toZn(a, n); b = BigInt(b); - if (b < BigInt(0)) { + if (b < _ZERO) { return modInv(modPow(a, abs(b), n), n); } - let result = BigInt(1); + let result = _ONE; let x = a; while (b > 0) { - var leastSignificantBit = b % BigInt(2); - b = b / BigInt(2); - if (leastSignificantBit == BigInt(1)) { + var leastSignificantBit = b % _TWO; + b = b / _TWO; + if (leastSignificantBit == _ONE) { result = result * x; result = result % n; } @@ -158,12 +149,17 @@ const modPow = function (a, b, n) { x = x % n; } return result; -}; +} -module.exports = { - abs: abs, - gcd: gcd, - lcm: lcm, - modInv: modInv, - modPow: modPow -}; \ No newline at end of file +/** + * Finds the smallest positive element that is congruent to a in modulo n + * @param {number|bigint} a An integer + * @param {number|bigint} n The modulo + * + * @returns {bigint} The smallest positive representation of a in modulo n + */ +export function toZn(a, n) { + n = BigInt(n); + a = BigInt(a) % n; + return (a < 0) ? a + n : a; +}