modpow with crt; crt; modAdd; modMultiply

This commit is contained in:
Juanelas 2023-06-28 16:42:09 +02:00
parent 80793b35bb
commit 1375c85bb5
23 changed files with 1112 additions and 338 deletions

View File

@ -86,31 +86,31 @@ jobs:
# VARIABLE1: ${{ secrets.VARIABLE1 }} # VARIABLE1: ${{ secrets.VARIABLE1 }}
# VARIABLE2: ${{ secrets.VARIABLE2 }} # VARIABLE2: ${{ secrets.VARIABLE2 }}
publish: # publish:
needs: [nodetests, browsertests] # needs: [nodetests, browsertests]
runs-on: ubuntu-latest # runs-on: ubuntu-latest
steps: # steps:
- name: Git checkout # - name: Git checkout
uses: actions/checkout@v3 # uses: actions/checkout@v3
- name: Install Node.js, NPM and Yarn # - name: Install Node.js, NPM and Yarn
uses: actions/setup-node@v3 # uses: actions/setup-node@v3
with: # with:
node-version: "18.x" # node-version: "18.x"
registry-url: "https://registry.npmjs.org" # registry-url: "https://registry.npmjs.org"
- name: install # # - name: install
run: npm ci # # run: npm ci
- name: coverage # # - name: coverage
run: npm run coverage # # run: npm run coverage
- name: send report to coveralls.io # # - name: send report to coveralls.io
uses: coverallsapp/github-action@master # # uses: coverallsapp/github-action@master
with: # # with:
github-token: ${{ secrets.GITHUB_TOKEN }} # # github-token: ${{ secrets.GITHUB_TOKEN }}
- name: NPM publish # - name: NPM publish
run: npm publish --access public # run: npm publish --access public
env: # env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} # NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

View File

@ -8,10 +8,10 @@ import replace from '@rollup/plugin-replace'
import terser from '@rollup/plugin-terser' import terser from '@rollup/plugin-terser'
import rollupPluginTs from '@rollup/plugin-typescript' import rollupPluginTs from '@rollup/plugin-typescript'
import { existsSync, readFileSync } from 'fs' import { existsSync, readFileSync } from 'fs'
import { dirname, join } from 'path' import { builtinModules } from 'module'
import { join } from 'path'
import dts from 'rollup-plugin-dts' import dts from 'rollup-plugin-dts'
import { compile } from './rollup-plugin-dts.js' import { compile } from './rollup-plugin-dts.js'
import { builtinModules } from 'module'
import * as url from 'url' import * as url from 'url'
const __dirname = url.fileURLToPath(new URL('.', import.meta.url)) const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
@ -45,7 +45,7 @@ if (existsSync(input) !== true) throw new Error('The entry point should be index
const tsPluginOptions = { const tsPluginOptions = {
tsconfig: tsConfigPath, tsconfig: tsConfigPath,
outDir: undefined, outDir: undefined,
include: ['src/ts/**/*', 'build/typings/is-browser.d.ts'], include: ['src/ts/**/*', 'build/typings/**/*.d.ts'],
exclude: ['src/**/*.spec.ts'] exclude: ['src/**/*.spec.ts']
} }
@ -100,7 +100,9 @@ export default [
plugins: [ plugins: [
replace({ replace({
IS_BROWSER: true, IS_BROWSER: true,
environment: 'browser',
_MODULE_TYPE: "'ESM'", _MODULE_TYPE: "'ESM'",
_NPM_PKG_VERSION: `'${process.env.npm_package_version}'` ?? "'0.0.1'",
preventAssignment: true preventAssignment: true
}), }),
rollupPluginTs(tsPluginOptions), rollupPluginTs(tsPluginOptions),
@ -142,7 +144,9 @@ export default [
plugins: [ plugins: [
replace({ replace({
IS_BROWSER: true, IS_BROWSER: true,
environment: 'browser',
_MODULE_TYPE: "'BUNDLE'", _MODULE_TYPE: "'BUNDLE'",
_NPM_PKG_VERSION: `'${process.env.npm_package_version}'` ?? "'0.0.1'",
preventAssignment: true preventAssignment: true
}), }),
rollupPluginTs({ rollupPluginTs({
@ -162,20 +166,17 @@ export default [
...sourcemapOutputOptions, ...sourcemapOutputOptions,
format: 'cjs', format: 'cjs',
exports: 'auto', exports: 'auto',
plugins: [ interop: 'auto',
terser() dynamicImportInCjs: false,
] plugins: [terser()]
} }
], ],
plugins: [ plugins: [
replace({
'await import(': 'require(',
delimiters: ['', ''],
preventAssignment: true
}),
replace({ replace({
IS_BROWSER: false, IS_BROWSER: false,
environment: 'nodejs',
_MODULE_TYPE: "'CJS'", _MODULE_TYPE: "'CJS'",
_NPM_PKG_VERSION: `'${process.env.npm_package_version}'` ?? "'0.0.1'",
preventAssignment: true preventAssignment: true
}), }),
rollupPluginTs(tsPluginOptions), rollupPluginTs(tsPluginOptions),
@ -197,23 +198,24 @@ export default [
file: join(rootDir, pkgJson.exports['.'].node.import.default), file: join(rootDir, pkgJson.exports['.'].node.import.default),
...sourcemapOutputOptions, ...sourcemapOutputOptions,
format: 'es', format: 'es',
plugins: [ plugins: [terser()]
terser()
]
} }
], ],
plugins: [ plugins: [
replace({ replace({
IS_BROWSER: false, IS_BROWSER: false,
environment: 'nodejs',
_MODULE_TYPE: "'ESM'", _MODULE_TYPE: "'ESM'",
__filename: `'${pkgJson.exports['.'].node.import.default}'`, _NPM_PKG_VERSION: `'${process.env.npm_package_version}'` ?? "'0.0.1'",
__dirname: `'${dirname(pkgJson.exports['.'].node.import.default)}'`, __filename: 'fileURLToPath(import.meta.url)',
__dirname: 'fileURLToPath(new URL(\'.\', import.meta.url))',
preventAssignment: true preventAssignment: true
}), }),
rollupPluginTs(tsPluginOptions), rollupPluginTs(tsPluginOptions),
compileDts(tmpDeclarationsDir), compileDts(tmpDeclarationsDir),
inject({ inject({
crypto: ['crypto', 'webcrypto'] crypto: ['crypto', 'webcrypto'],
fileURLToPath: ['url', 'fileURLToPath']
}), }),
commonjs({ extensions: ['.js', '.cjs', '.jsx', '.cjsx'] }), commonjs({ extensions: ['.js', '.cjs', '.jsx', '.cjsx'] }),
json(), json(),

View File

@ -61,7 +61,7 @@ const browserTests = async (
await watchDog.catch(async (reason) => { await watchDog.catch(async (reason) => {
console.error(reason) console.error(reason)
}) })
if (puppeteerOptions.headless === true) { if (puppeteerOptions.headless === 'new') {
await close() await close()
} }
}).catch(async (reason) => { }).catch(async (reason) => {
@ -102,9 +102,9 @@ const opts = {
// puppeteer options // puppeteer options
puppeteerOptions: { puppeteerOptions: {
headless: false, headless: false,
devtools: true devtools: true,
// slowMo: 100, // slowMo: 100,
// timeout: 10000 timeout: 0
}, },
logWarnings: false, // log warnings in Node console (usually not needed) logWarnings: false, // log warnings in Node console (usually not needed)
keepServerRunning: false, // keep server running until manually closed with ctrl-c. In combination with puppeteerOptions.headless (or just connecting any browser to the test page) allows debugging in browser keepServerRunning: false, // keep server running until manually closed with ctrl-c. In combination with puppeteerOptions.headless (or just connecting any browser to the test page) allows debugging in browser
@ -113,7 +113,7 @@ const opts = {
const args = process.argv.slice(2) const args = process.argv.slice(2)
if (args[0] === 'headless') { if (args[0] === 'headless') {
opts.puppeteerOptions.headless = true opts.puppeteerOptions.headless = 'new'
args.shift() args.shift()
} }

View File

@ -51,7 +51,7 @@ const tsBundleOptions = {
tsconfig: path.join(rootDir, 'tsconfig.json'), tsconfig: path.join(rootDir, 'tsconfig.json'),
outDir: undefined, // ignore outDir in tsconfig.json outDir: undefined, // ignore outDir in tsconfig.json
sourceMap: false sourceMap: false
// include: ['build/typings/is-browser.d.ts'] // include: ['src/ts/**/*', 'build/typings/**/*.d.ts']
} }
async function buildTests (testFiles) { async function buildTests (testFiles) {
@ -63,6 +63,7 @@ async function buildTests (testFiles) {
replace({ replace({
IS_BROWSER: true, IS_BROWSER: true,
_MODULE_TYPE: "'ESM'", _MODULE_TYPE: "'ESM'",
_NPM_PKG_VERSION: `'${process.env.npm_package_version}'` ?? "'0.0.1'",
preventAssignment: true preventAssignment: true
}), }),
typescriptPlugin(tsBundleOptions), typescriptPlugin(tsBundleOptions),
@ -202,7 +203,7 @@ function _getEnvVarsReplacements (testsCode) {
} }
} }
if (missingEnvVars.length > 0) { if (missingEnvVars.length > 0) {
console.warn('The folloinwg environment variables are missing in your .env file and will be replaced with "undefined": ' + [...(new Set(missingEnvVars)).values()].join(', ')) console.warn('The following environment variables are missing in your .env file and will be replaced with "undefined": ' + [...(new Set(missingEnvVars)).values()].join(', '))
} }
return replacements return replacements
} }

View File

@ -1,2 +0,0 @@
declare const IS_BROWSER: boolean
declare const _MODULE_TYPE: string

106
dist/bundle.esm.js vendored
View File

@ -45,6 +45,41 @@ function eGcd(a, b) {
}; };
} }
function toZn(a, n) {
if (typeof a === 'number')
a = BigInt(a);
if (typeof n === 'number')
n = BigInt(n);
if (n <= 0n) {
throw new RangeError('n must be > 0');
}
const aZn = a % n;
return (aZn < 0n) ? aZn + n : aZn;
}
function modInv(a, n) {
const egcd = eGcd(toZn(a, n), n);
if (egcd.g !== 1n) {
throw new RangeError(`${a.toString()} does not have inverse modulo ${n.toString()}`);
}
else {
return toZn(egcd.x, n);
}
}
function crt(remainders, modulos, modulo) {
if (remainders.length !== modulos.length) {
throw new RangeError('The remainders and modulos arrays should have the same length');
}
const product = modulo ?? modulos.reduce((acc, val) => acc * val, 1n);
return modulos.reduce((sum, mod, index) => {
const partialProduct = product / mod;
const inverse = modInv(partialProduct, mod);
const toAdd = ((partialProduct * inverse) % product * remainders[index]) % product;
return toZn(sum + toAdd, product);
}, 0n);
}
function gcd(a, b) { function gcd(a, b) {
let aAbs = (typeof a === 'number') ? BigInt(abs(a)) : abs(a); let aAbs = (typeof a === 'number') ? BigInt(abs(a)) : abs(a);
let bAbs = (typeof b === 'number') ? BigInt(abs(b)) : abs(b); let bAbs = (typeof b === 'number') ? BigInt(abs(b)) : abs(b);
@ -93,29 +128,25 @@ function min(a, b) {
return (a >= b) ? b : a; return (a >= b) ? b : a;
} }
function toZn(a, n) { function modAdd(addends, n) {
if (typeof a === 'number') const mod = BigInt(n);
a = BigInt(a); const as = addends.map(a => BigInt(a) % mod);
if (typeof n === 'number') return toZn(as.reduce((sum, a) => sum + a % mod, 0n), mod);
n = BigInt(n);
if (n <= 0n) {
throw new RangeError('n must be > 0');
}
const aZn = a % n;
return (aZn < 0n) ? aZn + n : aZn;
} }
function modInv(a, n) { function modMultiply(factors, n) {
const egcd = eGcd(toZn(a, n), n); const mod = BigInt(n);
if (egcd.g !== 1n) { const as = factors.map(a => BigInt(a) % mod);
throw new RangeError(`${a.toString()} does not have inverse modulo ${n.toString()}`); return toZn(as.reduce((prod, a) => prod * a % mod, 1n), mod);
}
else {
return toZn(egcd.x, n);
}
} }
function modPow(b, e, n) { function phi(primeFactorization) {
return primeFactorization.map(v => (v[0] ** (v[1] - 1n)) * (v[0] - 1n)).reduce((prev, curr) => {
return curr * prev;
}, 1n);
}
function modPow(b, e, n, primeFactorization) {
if (typeof b === 'number') if (typeof b === 'number')
b = BigInt(b); b = BigInt(b);
if (typeof e === 'number') if (typeof e === 'number')
@ -130,7 +161,10 @@ function modPow(b, e, n) {
} }
b = toZn(b, n); b = toZn(b, n);
if (e < 0n) { if (e < 0n) {
return modInv(modPow(b, abs(e), n), n); return modInv(modPow(b, abs(e), n, primeFactorization), n);
}
if (primeFactorization !== undefined) {
return modPowWithFactorization(b, e, n, primePowerArguments(primeFactorization));
} }
let r = 1n; let r = 1n;
while (e > 0) { while (e > 0) {
@ -142,5 +176,35 @@ function modPow(b, e, n) {
} }
return r; return r;
} }
function primePowerArguments(primeFactors) {
const primePowers = {};
primeFactors.forEach((primeFactor) => {
if (typeof primeFactor === 'bigint' || typeof primeFactor === 'number') {
const key = String(primeFactor);
if (primePowers[key] === undefined) {
primePowers[key] = { p: BigInt(primeFactor), k: 1n };
}
else {
primePowers[key].k += 1n;
}
}
else {
const key = String(primeFactor[0]);
if (primePowers[key] === undefined) {
primePowers[key] = { p: BigInt(primeFactor[0]), k: BigInt(primeFactor[1]) };
}
else {
primePowers[key].k += BigInt(primeFactor[1]);
}
}
});
return Object.values(primePowers).map(val => [val.p, val.k]);
}
function modPowWithFactorization(b, e, n, primeFactorization) {
const mods = primeFactorization.map(v => v[0] ** v[1]);
const phis = primeFactorization.map(v => phi([v]));
const remainders = phis.map((phi, i) => modPow(b, e % phi, mods[i]));
return crt(remainders, mods, n);
}
export { abs, bitLength, eGcd, gcd, lcm, max, min, modInv, modPow, toZn }; export { abs, bitLength, crt, eGcd, gcd, lcm, max, min, modAdd, modInv, modMultiply, modPow, phi, toZn };

View File

@ -1 +1 @@
function n(n){return n>=0?n:-n}function t(n){if("number"==typeof n&&(n=BigInt(n)),1n===n)return 1;let t=1;do{t++}while((n>>=1n)>1n);return t}function r(n,t){if("number"==typeof n&&(n=BigInt(n)),"number"==typeof t&&(t=BigInt(t)),n<=0n||t<=0n)throw new RangeError("a and b MUST be > 0");let r=0n,e=1n,o=1n,u=0n;for(;0n!==n;){const i=t/n,f=t%n,g=r-o*i,b=e-u*i;t=n,n=f,r=o,e=u,o=g,u=b}return{g:t,x:r,y:e}}function e(t,r){let e="number"==typeof t?BigInt(n(t)):n(t),o="number"==typeof r?BigInt(n(r)):n(r);if(0n===e)return o;if(0n===o)return e;let u=0n;for(;0n===(1n&(e|o));)e>>=1n,o>>=1n,u++;for(;0n===(1n&e);)e>>=1n;do{for(;0n===(1n&o);)o>>=1n;if(e>o){const n=e;e=o,o=n}o-=e}while(0n!==o);return e<<u}function o(t,r){return"number"==typeof t&&(t=BigInt(t)),"number"==typeof r&&(r=BigInt(r)),0n===t&&0n===r?BigInt(0):n(t/e(t,r)*r)}function u(n,t){return n>=t?n:t}function i(n,t){return n>=t?t:n}function f(n,t){if("number"==typeof n&&(n=BigInt(n)),"number"==typeof t&&(t=BigInt(t)),t<=0n)throw new RangeError("n must be > 0");const r=n%t;return r<0n?r+t:r}function g(n,t){const e=r(f(n,t),t);if(1n!==e.g)throw new RangeError(`${n.toString()} does not have inverse modulo ${t.toString()}`);return f(e.x,t)}function b(t,r,e){if("number"==typeof t&&(t=BigInt(t)),"number"==typeof r&&(r=BigInt(r)),"number"==typeof e&&(e=BigInt(e)),e<=0n)throw new RangeError("n must be > 0");if(1n===e)return 0n;if(t=f(t,e),r<0n)return g(b(t,n(r),e),e);let o=1n;for(;r>0;)r%2n===1n&&(o=o*t%e),r/=2n,t=t**2n%e;return o}export{n as abs,t as bitLength,r as eGcd,e as gcd,o as lcm,u as max,i as min,g as modInv,b as modPow,f as toZn}; function n(n){return n>=0?n:-n}function t(n){if("number"==typeof n&&(n=BigInt(n)),1n===n)return 1;let t=1;do{t++}while((n>>=1n)>1n);return t}function r(n,t){if("number"==typeof n&&(n=BigInt(n)),"number"==typeof t&&(t=BigInt(t)),n<=0n||t<=0n)throw new RangeError("a and b MUST be > 0");let r=0n,e=1n,o=1n,i=0n;for(;0n!==n;){const u=t/n,f=t%n,g=r-o*u,c=e-i*u;t=n,n=f,r=o,e=i,o=g,i=c}return{g:t,x:r,y:e}}function e(n,t){if("number"==typeof n&&(n=BigInt(n)),"number"==typeof t&&(t=BigInt(t)),t<=0n)throw new RangeError("n must be > 0");const r=n%t;return r<0n?r+t:r}function o(n,t){const o=r(e(n,t),t);if(1n!==o.g)throw new RangeError(`${n.toString()} does not have inverse modulo ${t.toString()}`);return e(o.x,t)}function i(n,t,r){if(n.length!==t.length)throw new RangeError("The remainders and modulos arrays should have the same length");const i=r??t.reduce(((n,t)=>n*t),1n);return t.reduce(((t,r,u)=>{const f=i/r;return e(t+f*o(f,r)%i*n[u]%i,i)}),0n)}function u(t,r){let e="number"==typeof t?BigInt(n(t)):n(t),o="number"==typeof r?BigInt(n(r)):n(r);if(0n===e)return o;if(0n===o)return e;let i=0n;for(;0n===(1n&(e|o));)e>>=1n,o>>=1n,i++;for(;0n===(1n&e);)e>>=1n;do{for(;0n===(1n&o);)o>>=1n;if(e>o){const n=e;e=o,o=n}o-=e}while(0n!==o);return e<<i}function f(t,r){return"number"==typeof t&&(t=BigInt(t)),"number"==typeof r&&(r=BigInt(r)),0n===t&&0n===r?BigInt(0):n(t/u(t,r)*r)}function g(n,t){return n>=t?n:t}function c(n,t){return n>=t?t:n}function m(n,t){const r=BigInt(t);return e(n.map((n=>BigInt(n)%r)).reduce(((n,t)=>n+t%r),0n),r)}function p(n,t){const r=BigInt(t);return e(n.map((n=>BigInt(n)%r)).reduce(((n,t)=>n*t%r),1n),r)}function a(n){return n.map((n=>n[0]**(n[1]-1n)*(n[0]-1n))).reduce(((n,t)=>t*n),1n)}function s(t,r,u,f){if("number"==typeof t&&(t=BigInt(t)),"number"==typeof r&&(r=BigInt(r)),"number"==typeof u&&(u=BigInt(u)),u<=0n)throw new RangeError("n must be > 0");if(1n===u)return 0n;if(t=e(t,u),r<0n)return o(s(t,n(r),u,f),u);if(void 0!==f)return function(n,t,r,e){const o=e.map((n=>n[0]**n[1])),u=e.map((n=>a([n]))),f=u.map(((r,e)=>s(n,t%r,o[e])));return i(f,o,r)}(t,r,u,function(n){const t={};return n.forEach((n=>{if("bigint"==typeof n||"number"==typeof n){const r=String(n);void 0===t[r]?t[r]={p:BigInt(n),k:1n}:t[r].k+=1n}else{const r=String(n[0]);void 0===t[r]?t[r]={p:BigInt(n[0]),k:BigInt(n[1])}:t[r].k+=BigInt(n[1])}})),Object.values(t).map((n=>[n.p,n.k]))}(f));let g=1n;for(;r>0;)r%2n===1n&&(g=g*t%u),r/=2n,t=t**2n%u;return g}export{n as abs,t as bitLength,i as crt,r as eGcd,u as gcd,f as lcm,g as max,c as min,m as modAdd,o as modInv,p as modMultiply,s as modPow,a as phi,e as toZn};

2
dist/bundle.iife.js vendored
View File

@ -1 +1 @@
var bigintModArith=function(n){"use strict";function t(n){return n>=0?n:-n}function r(n,t){if("number"==typeof n&&(n=BigInt(n)),"number"==typeof t&&(t=BigInt(t)),n<=0n||t<=0n)throw new RangeError("a and b MUST be > 0");let r=0n,e=1n,o=1n,i=0n;for(;0n!==n;){const u=t/n,f=t%n,g=r-o*u,m=e-i*u;t=n,n=f,r=o,e=i,o=g,i=m}return{g:t,x:r,y:e}}function e(n,r){let e="number"==typeof n?BigInt(t(n)):t(n),o="number"==typeof r?BigInt(t(r)):t(r);if(0n===e)return o;if(0n===o)return e;let i=0n;for(;0n===(1n&(e|o));)e>>=1n,o>>=1n,i++;for(;0n===(1n&e);)e>>=1n;do{for(;0n===(1n&o);)o>>=1n;if(e>o){const n=e;e=o,o=n}o-=e}while(0n!==o);return e<<i}function o(n,t){if("number"==typeof n&&(n=BigInt(n)),"number"==typeof t&&(t=BigInt(t)),t<=0n)throw new RangeError("n must be > 0");const r=n%t;return r<0n?r+t:r}function i(n,t){const e=r(o(n,t),t);if(1n!==e.g)throw new RangeError(`${n.toString()} does not have inverse modulo ${t.toString()}`);return o(e.x,t)}return n.abs=t,n.bitLength=function(n){if("number"==typeof n&&(n=BigInt(n)),1n===n)return 1;let t=1;do{t++}while((n>>=1n)>1n);return t},n.eGcd=r,n.gcd=e,n.lcm=function(n,r){return"number"==typeof n&&(n=BigInt(n)),"number"==typeof r&&(r=BigInt(r)),0n===n&&0n===r?BigInt(0):t(n/e(n,r)*r)},n.max=function(n,t){return n>=t?n:t},n.min=function(n,t){return n>=t?t:n},n.modInv=i,n.modPow=function n(r,e,u){if("number"==typeof r&&(r=BigInt(r)),"number"==typeof e&&(e=BigInt(e)),"number"==typeof u&&(u=BigInt(u)),u<=0n)throw new RangeError("n must be > 0");if(1n===u)return 0n;if(r=o(r,u),e<0n)return i(n(r,t(e),u),u);let f=1n;for(;e>0;)e%2n===1n&&(f=f*r%u),e/=2n,r=r**2n%u;return f},n.toZn=o,n}({}); var bigintModArith=function(n){"use strict";function t(n){return n>=0?n:-n}function r(n,t){if("number"==typeof n&&(n=BigInt(n)),"number"==typeof t&&(t=BigInt(t)),n<=0n||t<=0n)throw new RangeError("a and b MUST be > 0");let r=0n,e=1n,o=1n,i=0n;for(;0n!==n;){const u=t/n,f=t%n,c=r-o*u,g=e-i*u;t=n,n=f,r=o,e=i,o=c,i=g}return{g:t,x:r,y:e}}function e(n,t){if("number"==typeof n&&(n=BigInt(n)),"number"==typeof t&&(t=BigInt(t)),t<=0n)throw new RangeError("n must be > 0");const r=n%t;return r<0n?r+t:r}function o(n,t){const o=r(e(n,t),t);if(1n!==o.g)throw new RangeError(`${n.toString()} does not have inverse modulo ${t.toString()}`);return e(o.x,t)}function i(n,t,r){if(n.length!==t.length)throw new RangeError("The remainders and modulos arrays should have the same length");const i=r??t.reduce(((n,t)=>n*t),1n);return t.reduce(((t,r,u)=>{const f=i/r;return e(t+f*o(f,r)%i*n[u]%i,i)}),0n)}function u(n,r){let e="number"==typeof n?BigInt(t(n)):t(n),o="number"==typeof r?BigInt(t(r)):t(r);if(0n===e)return o;if(0n===o)return e;let i=0n;for(;0n===(1n&(e|o));)e>>=1n,o>>=1n,i++;for(;0n===(1n&e);)e>>=1n;do{for(;0n===(1n&o);)o>>=1n;if(e>o){const n=e;e=o,o=n}o-=e}while(0n!==o);return e<<i}function f(n){return n.map((n=>n[0]**(n[1]-1n)*(n[0]-1n))).reduce(((n,t)=>t*n),1n)}function c(n,r,u,g){if("number"==typeof n&&(n=BigInt(n)),"number"==typeof r&&(r=BigInt(r)),"number"==typeof u&&(u=BigInt(u)),u<=0n)throw new RangeError("n must be > 0");if(1n===u)return 0n;if(n=e(n,u),r<0n)return o(c(n,t(r),u,g),u);if(void 0!==g)return function(n,t,r,e){const o=e.map((n=>n[0]**n[1])),u=e.map((n=>f([n]))),g=u.map(((r,e)=>c(n,t%r,o[e])));return i(g,o,r)}(n,r,u,function(n){const t={};return n.forEach((n=>{if("bigint"==typeof n||"number"==typeof n){const r=String(n);void 0===t[r]?t[r]={p:BigInt(n),k:1n}:t[r].k+=1n}else{const r=String(n[0]);void 0===t[r]?t[r]={p:BigInt(n[0]),k:BigInt(n[1])}:t[r].k+=BigInt(n[1])}})),Object.values(t).map((n=>[n.p,n.k]))}(g));let m=1n;for(;r>0;)r%2n===1n&&(m=m*n%u),r/=2n,n=n**2n%u;return m}return n.abs=t,n.bitLength=function(n){if("number"==typeof n&&(n=BigInt(n)),1n===n)return 1;let t=1;do{t++}while((n>>=1n)>1n);return t},n.crt=i,n.eGcd=r,n.gcd=u,n.lcm=function(n,r){return"number"==typeof n&&(n=BigInt(n)),"number"==typeof r&&(r=BigInt(r)),0n===n&&0n===r?BigInt(0):t(n/u(n,r)*r)},n.max=function(n,t){return n>=t?n:t},n.min=function(n,t){return n>=t?t:n},n.modAdd=function(n,t){const r=BigInt(t);return e(n.map((n=>BigInt(n)%r)).reduce(((n,t)=>n+t%r),0n),r)},n.modInv=o,n.modMultiply=function(n,t){const r=BigInt(t);return e(n.map((n=>BigInt(n)%r)).reduce(((n,t)=>n*t%r),1n),r)},n.modPow=c,n.phi=f,n.toZn=e,n}({});

2
dist/bundle.umd.js vendored
View File

@ -1 +1 @@
!function(n,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((n="undefined"!=typeof globalThis?globalThis:n||self).bigintModArith={})}(this,(function(n){"use strict";function t(n){return n>=0?n:-n}function e(n,t){if("number"==typeof n&&(n=BigInt(n)),"number"==typeof t&&(t=BigInt(t)),n<=0n||t<=0n)throw new RangeError("a and b MUST be > 0");let e=0n,r=1n,o=1n,i=0n;for(;0n!==n;){const f=t/n,u=t%n,g=e-o*f,b=r-i*f;t=n,n=u,e=o,r=i,o=g,i=b}return{g:t,x:e,y:r}}function r(n,e){let r="number"==typeof n?BigInt(t(n)):t(n),o="number"==typeof e?BigInt(t(e)):t(e);if(0n===r)return o;if(0n===o)return r;let i=0n;for(;0n===(1n&(r|o));)r>>=1n,o>>=1n,i++;for(;0n===(1n&r);)r>>=1n;do{for(;0n===(1n&o);)o>>=1n;if(r>o){const n=r;r=o,o=n}o-=r}while(0n!==o);return r<<i}function o(n,t){if("number"==typeof n&&(n=BigInt(n)),"number"==typeof t&&(t=BigInt(t)),t<=0n)throw new RangeError("n must be > 0");const e=n%t;return e<0n?e+t:e}function i(n,t){const r=e(o(n,t),t);if(1n!==r.g)throw new RangeError(`${n.toString()} does not have inverse modulo ${t.toString()}`);return o(r.x,t)}n.abs=t,n.bitLength=function(n){if("number"==typeof n&&(n=BigInt(n)),1n===n)return 1;let t=1;do{t++}while((n>>=1n)>1n);return t},n.eGcd=e,n.gcd=r,n.lcm=function(n,e){return"number"==typeof n&&(n=BigInt(n)),"number"==typeof e&&(e=BigInt(e)),0n===n&&0n===e?BigInt(0):t(n/r(n,e)*e)},n.max=function(n,t){return n>=t?n:t},n.min=function(n,t){return n>=t?t:n},n.modInv=i,n.modPow=function n(e,r,f){if("number"==typeof e&&(e=BigInt(e)),"number"==typeof r&&(r=BigInt(r)),"number"==typeof f&&(f=BigInt(f)),f<=0n)throw new RangeError("n must be > 0");if(1n===f)return 0n;if(e=o(e,f),r<0n)return i(n(e,t(r),f),f);let u=1n;for(;r>0;)r%2n===1n&&(u=u*e%f),r/=2n,e=e**2n%f;return u},n.toZn=o})); !function(n,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((n="undefined"!=typeof globalThis?globalThis:n||self).bigintModArith={})}(this,(function(n){"use strict";function t(n){return n>=0?n:-n}function e(n,t){if("number"==typeof n&&(n=BigInt(n)),"number"==typeof t&&(t=BigInt(t)),n<=0n||t<=0n)throw new RangeError("a and b MUST be > 0");let e=0n,r=1n,o=1n,i=0n;for(;0n!==n;){const u=t/n,f=t%n,c=e-o*u,g=r-i*u;t=n,n=f,e=o,r=i,o=c,i=g}return{g:t,x:e,y:r}}function r(n,t){if("number"==typeof n&&(n=BigInt(n)),"number"==typeof t&&(t=BigInt(t)),t<=0n)throw new RangeError("n must be > 0");const e=n%t;return e<0n?e+t:e}function o(n,t){const o=e(r(n,t),t);if(1n!==o.g)throw new RangeError(`${n.toString()} does not have inverse modulo ${t.toString()}`);return r(o.x,t)}function i(n,t,e){if(n.length!==t.length)throw new RangeError("The remainders and modulos arrays should have the same length");const i=e??t.reduce(((n,t)=>n*t),1n);return t.reduce(((t,e,u)=>{const f=i/e;return r(t+f*o(f,e)%i*n[u]%i,i)}),0n)}function u(n,e){let r="number"==typeof n?BigInt(t(n)):t(n),o="number"==typeof e?BigInt(t(e)):t(e);if(0n===r)return o;if(0n===o)return r;let i=0n;for(;0n===(1n&(r|o));)r>>=1n,o>>=1n,i++;for(;0n===(1n&r);)r>>=1n;do{for(;0n===(1n&o);)o>>=1n;if(r>o){const n=r;r=o,o=n}o-=r}while(0n!==o);return r<<i}function f(n){return n.map((n=>n[0]**(n[1]-1n)*(n[0]-1n))).reduce(((n,t)=>t*n),1n)}function c(n,e,u,g){if("number"==typeof n&&(n=BigInt(n)),"number"==typeof e&&(e=BigInt(e)),"number"==typeof u&&(u=BigInt(u)),u<=0n)throw new RangeError("n must be > 0");if(1n===u)return 0n;if(n=r(n,u),e<0n)return o(c(n,t(e),u,g),u);if(void 0!==g)return function(n,t,e,r){const o=r.map((n=>n[0]**n[1])),u=r.map((n=>f([n]))),g=u.map(((e,r)=>c(n,t%e,o[r])));return i(g,o,e)}(n,e,u,function(n){const t={};return n.forEach((n=>{if("bigint"==typeof n||"number"==typeof n){const e=String(n);void 0===t[e]?t[e]={p:BigInt(n),k:1n}:t[e].k+=1n}else{const e=String(n[0]);void 0===t[e]?t[e]={p:BigInt(n[0]),k:BigInt(n[1])}:t[e].k+=BigInt(n[1])}})),Object.values(t).map((n=>[n.p,n.k]))}(g));let d=1n;for(;e>0;)e%2n===1n&&(d=d*n%u),e/=2n,n=n**2n%u;return d}n.abs=t,n.bitLength=function(n){if("number"==typeof n&&(n=BigInt(n)),1n===n)return 1;let t=1;do{t++}while((n>>=1n)>1n);return t},n.crt=i,n.eGcd=e,n.gcd=u,n.lcm=function(n,e){return"number"==typeof n&&(n=BigInt(n)),"number"==typeof e&&(e=BigInt(e)),0n===n&&0n===e?BigInt(0):t(n/u(n,e)*e)},n.max=function(n,t){return n>=t?n:t},n.min=function(n,t){return n>=t?t:n},n.modAdd=function(n,t){const e=BigInt(t);return r(n.map((n=>BigInt(n)%e)).reduce(((n,t)=>n+t%e),0n),e)},n.modInv=o,n.modMultiply=function(n,t){const e=BigInt(t);return r(n.map((n=>BigInt(n)%e)).reduce(((n,t)=>n*t%e),1n),e)},n.modPow=c,n.phi=f,n.toZn=r}));

File diff suppressed because one or more lines are too long

15
dist/index.d.ts vendored
View File

@ -2,6 +2,8 @@ declare function abs(a: number | bigint): number | bigint;
declare function bitLength(a: number | bigint): number; declare function bitLength(a: number | bigint): number;
declare function crt(remainders: bigint[], modulos: bigint[], modulo?: bigint): bigint;
interface Egcd { interface Egcd {
g: bigint; g: bigint;
x: bigint; x: bigint;
@ -17,10 +19,19 @@ declare function max(a: number | bigint, b: number | bigint): number | bigint;
declare function min(a: number | bigint, b: number | bigint): number | bigint; declare function min(a: number | bigint, b: number | bigint): number | bigint;
declare function modAdd(addends: Array<number | bigint>, n: number | bigint): bigint;
declare function modInv(a: number | bigint, n: number | bigint): bigint; declare function modInv(a: number | bigint, n: number | bigint): bigint;
declare function modPow(b: number | bigint, e: number | bigint, n: number | bigint): bigint; declare function modMultiply(factors: Array<number | bigint>, n: number | bigint): bigint;
type PrimePower = [number | bigint, number | bigint];
type PrimeFactor = number | bigint | PrimePower;
declare function modPow(b: number | bigint, e: number | bigint, n: number | bigint, primeFactorization?: PrimeFactor[]): bigint;
type PrimeFactorization = Array<[bigint, bigint]>;
declare function phi(primeFactorization: PrimeFactorization): bigint;
declare function toZn(a: number | bigint, n: number | bigint): bigint; declare function toZn(a: number | bigint, n: number | bigint): bigint;
export { Egcd, abs, bitLength, eGcd, gcd, lcm, max, min, modInv, modPow, toZn }; export { Egcd, PrimeFactor, PrimeFactorization, PrimePower, abs, bitLength, crt, eGcd, gcd, lcm, max, min, modAdd, modInv, modMultiply, modPow, phi, toZn };

4
dist/index.node.cjs vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -8,19 +8,59 @@ Some common functions for modular arithmetic using native JS implementation of B
- [Egcd](interfaces/Egcd.md) - [Egcd](interfaces/Egcd.md)
### Type Aliases
- [PrimeFactor](API.md#primefactor)
- [PrimeFactorization](API.md#primefactorization)
- [PrimePower](API.md#primepower)
### Functions ### Functions
- [abs](API.md#abs) - [abs](API.md#abs)
- [bitLength](API.md#bitlength) - [bitLength](API.md#bitlength)
- [crt](API.md#crt)
- [eGcd](API.md#egcd) - [eGcd](API.md#egcd)
- [gcd](API.md#gcd) - [gcd](API.md#gcd)
- [lcm](API.md#lcm) - [lcm](API.md#lcm)
- [max](API.md#max) - [max](API.md#max)
- [min](API.md#min) - [min](API.md#min)
- [modAdd](API.md#modadd)
- [modInv](API.md#modinv) - [modInv](API.md#modinv)
- [modMultiply](API.md#modmultiply)
- [modPow](API.md#modpow) - [modPow](API.md#modpow)
- [phi](API.md#phi)
- [toZn](API.md#tozn) - [toZn](API.md#tozn)
## Type Aliases
### PrimeFactor
Ƭ **PrimeFactor**: `number` \| `bigint` \| [`PrimePower`](API.md#primepower)
#### Defined in
[modPow.ts:8](https://github.com/juanelas/bigint-mod-arith/blob/80793b3/src/ts/modPow.ts#L8)
___
### PrimeFactorization
Ƭ **PrimeFactorization**: [`bigint`, `bigint`][]
#### Defined in
phi.ts:1
___
### PrimePower
Ƭ **PrimePower**: [`number` \| `bigint`, `number` \| `bigint`]
#### Defined in
[modPow.ts:7](https://github.com/juanelas/bigint-mod-arith/blob/80793b3/src/ts/modPow.ts#L7)
## Functions ## Functions
### abs ### abs
@ -43,7 +83,7 @@ The absolute value of a
#### Defined in #### Defined in
[abs.ts:8](https://github.com/juanelas/bigint-mod-arith/blob/84ebe88/src/ts/abs.ts#L8) [abs.ts:8](https://github.com/juanelas/bigint-mod-arith/blob/80793b3/src/ts/abs.ts#L8)
___ ___
@ -67,7 +107,37 @@ The bit length
#### Defined in #### Defined in
[bitLength.ts:7](https://github.com/juanelas/bigint-mod-arith/blob/84ebe88/src/ts/bitLength.ts#L7) [bitLength.ts:7](https://github.com/juanelas/bigint-mod-arith/blob/80793b3/src/ts/bitLength.ts#L7)
___
### crt
**crt**(`remainders`, `modulos`, `modulo?`): `bigint`
Chinese remainder theorem states that if one knows the remainders of the Euclidean division of an integer n by several integers, then one can determine uniquely the remainder of the division of n by the product of these integers, under the condition that the divisors are pairwise coprime (no two divisors share a common factor other than 1). Provided that n_i are pairwise coprime, and a_i any integers, this function returns a solution for the following system of equations:
x ≡ a_1 mod n_1
x ≡ a_2 mod n_2
x ≡ a_k mod n_k
#### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `remainders` | `bigint`[] | the array of remainders a_i. For example [17n, 243n, 344n] |
| `modulos` | `bigint`[] | the array of modulos n_i. For example [769n, 2017n, 47701n] |
| `modulo?` | `bigint` | the product of all modulos. Provided here just to save some operations if it is already known |
#### Returns
`bigint`
x
#### Defined in
crt.ts:16
___ ___
@ -97,7 +167,7 @@ A triple (g, x, y), such that ax + by = g = gcd(a, b).
#### Defined in #### Defined in
[egcd.ts:17](https://github.com/juanelas/bigint-mod-arith/blob/84ebe88/src/ts/egcd.ts#L17) [egcd.ts:17](https://github.com/juanelas/bigint-mod-arith/blob/80793b3/src/ts/egcd.ts#L17)
___ ___
@ -122,7 +192,7 @@ The greatest common divisor of a and b
#### Defined in #### Defined in
[gcd.ts:10](https://github.com/juanelas/bigint-mod-arith/blob/84ebe88/src/ts/gcd.ts#L10) [gcd.ts:11](https://github.com/juanelas/bigint-mod-arith/blob/80793b3/src/ts/gcd.ts#L11)
___ ___
@ -147,7 +217,7 @@ The least common multiple of a and b
#### Defined in #### Defined in
[lcm.ts:10](https://github.com/juanelas/bigint-mod-arith/blob/84ebe88/src/ts/lcm.ts#L10) [lcm.ts:11](https://github.com/juanelas/bigint-mod-arith/blob/80793b3/src/ts/lcm.ts#L11)
___ ___
@ -172,7 +242,7 @@ Maximum of numbers a and b
#### Defined in #### Defined in
[max.ts:9](https://github.com/juanelas/bigint-mod-arith/blob/84ebe88/src/ts/max.ts#L9) [max.ts:9](https://github.com/juanelas/bigint-mod-arith/blob/80793b3/src/ts/max.ts#L9)
___ ___
@ -197,7 +267,32 @@ Minimum of numbers a and b
#### Defined in #### Defined in
[min.ts:9](https://github.com/juanelas/bigint-mod-arith/blob/84ebe88/src/ts/min.ts#L9) [min.ts:9](https://github.com/juanelas/bigint-mod-arith/blob/80793b3/src/ts/min.ts#L9)
___
### modAdd
**modAdd**(`addends`, `n`): `bigint`
Modular addition of (a_1 + ... + a_r) mod n
#### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `addends` | (`number` \| `bigint`)[] | an array of the numbers a_i to add. For example [3, 12353251235n, 1243, -12341232545990n] |
| `n` | `number` \| `bigint` | the modulo |
#### Returns
`bigint`
The smallest positive integer that is congruent with (a_1 + ... + a_r) mod n
#### Defined in
modAdd.ts:9
___ ___
@ -226,15 +321,41 @@ The inverse modulo n
#### Defined in #### Defined in
[modInv.ts:13](https://github.com/juanelas/bigint-mod-arith/blob/84ebe88/src/ts/modInv.ts#L13) [modInv.ts:14](https://github.com/juanelas/bigint-mod-arith/blob/80793b3/src/ts/modInv.ts#L14)
___
### modMultiply
**modMultiply**(`factors`, `n`): `bigint`
Modular addition of (a_1 * ... * a_r) mod n
*
#### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `factors` | (`number` \| `bigint`)[] | an array of the numbers a_i to multiply. For example [3, 12353251235n, 1243, -12341232545990n] * |
| `n` | `number` \| `bigint` | the modulo * |
#### Returns
`bigint`
The smallest positive integer that is congruent with (a_1 * ... * a_r) mod n
#### Defined in
modMultiply.ts:9
___ ___
### modPow ### modPow
**modPow**(`b`, `e`, `n`): `bigint` **modPow**(`b`, `e`, `n`, `primeFactorization?`): `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 if the prime factorization is not provided, or the chinese remainder theorem otherwise.
**`Throws`** **`Throws`**
@ -247,6 +368,7 @@ RangeError if n <= 0
| `b` | `number` \| `bigint` | base | | `b` | `number` \| `bigint` | base |
| `e` | `number` \| `bigint` | exponent | | `e` | `number` \| `bigint` | exponent |
| `n` | `number` \| `bigint` | modulo | | `n` | `number` \| `bigint` | modulo |
| `primeFactorization?` | [`PrimeFactor`](API.md#primefactor)[] | an array of the prime factors, for example [5n, 5n, 13n, 27n], or prime powers as [p, k], for instance [[5, 2], [13, 1], [27, 1]]. If the prime factorization is provided the chinese remainder theorem is used to greatly speed up the exponentiation. |
#### Returns #### Returns
@ -256,7 +378,31 @@ b**e mod n
#### Defined in #### Defined in
[modPow.ts:15](https://github.com/juanelas/bigint-mod-arith/blob/84ebe88/src/ts/modPow.ts#L15) [modPow.ts:22](https://github.com/juanelas/bigint-mod-arith/blob/80793b3/src/ts/modPow.ts#L22)
___
### phi
**phi**(`primeFactorization`): `bigint`
A function that computes the Euler's totien function of a number n, whose prime power factorization is known
#### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `primeFactorization` | [`PrimeFactorization`](API.md#primefactorization) | an array of arrays containing the prime power factorization, i.e. for n = (p1**k1)*(p2**k2)*...*(pr**kr), one should provide [[p1, k1], [p2, k2], ... , [pr, kr]] |
#### Returns
`bigint`
phi((p1**k1)*(p2**k2)*...*(pr**kr))
#### Defined in
phi.ts:9
___ ___
@ -289,4 +435,4 @@ A bigint with the smallest positive representation of a modulo n
#### Defined in #### Defined in
[toZn.ts:14](https://github.com/juanelas/bigint-mod-arith/blob/84ebe88/src/ts/toZn.ts#L14) [toZn.ts:14](https://github.com/juanelas/bigint-mod-arith/blob/80793b3/src/ts/toZn.ts#L14)

View File

@ -16,7 +16,7 @@
#### Defined in #### Defined in
[egcd.ts:2](https://github.com/juanelas/bigint-mod-arith/blob/84ebe88/src/ts/egcd.ts#L2) [egcd.ts:2](https://github.com/juanelas/bigint-mod-arith/blob/80793b3/src/ts/egcd.ts#L2)
___ ___
@ -26,7 +26,7 @@ ___
#### Defined in #### Defined in
[egcd.ts:3](https://github.com/juanelas/bigint-mod-arith/blob/84ebe88/src/ts/egcd.ts#L3) [egcd.ts:3](https://github.com/juanelas/bigint-mod-arith/blob/80793b3/src/ts/egcd.ts#L3)
___ ___
@ -36,4 +36,4 @@ ___
#### Defined in #### Defined in
[egcd.ts:4](https://github.com/juanelas/bigint-mod-arith/blob/84ebe88/src/ts/egcd.ts#L4) [egcd.ts:4](https://github.com/juanelas/bigint-mod-arith/blob/80793b3/src/ts/egcd.ts#L4)

863
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -50,10 +50,26 @@
} }
}, },
"./esm-browser-bundle": "./dist/bundle.esm.min.js", "./esm-browser-bundle": "./dist/bundle.esm.min.js",
"./dist/bundle.esm.min.js": "./dist/bundle.esm.min.js",
"./esm-browser-bundle-nomin": "./dist/bundle.esm.js", "./esm-browser-bundle-nomin": "./dist/bundle.esm.js",
"./dist/bundle.esm.js": "./dist/bundle.esm.js",
"./iife-browser-bundle": "./dist/bundle.iife.js", "./iife-browser-bundle": "./dist/bundle.iife.js",
"./dist/bundle.iife.js": "./dist/bundle.iife.js",
"./umd-browser-bundle": "./dist/bundle.umd.js", "./umd-browser-bundle": "./dist/bundle.umd.js",
"./types": "./dist/index.d.ts" "./dist/bundle.umd.js": "./dist/bundle.umd.js",
"./dist/index.node": {
"types": "./dist/index.d.ts",
"default": "./dist/index.node.cjs"
},
"./dist/index.node.esm": {
"types": "./dist/index.d.ts",
"default": "./dist/index.node.esm.js"
},
"./dist/index.browser.esm": {
"types": "./dist/index.d.ts",
"default": "./dist/index.browser.esm.js"
},
"./package.json": "./package.json"
}, },
"imports": { "imports": {
"#pkg": { "#pkg": {
@ -66,7 +82,7 @@
"default": "./dist/index.node.esm.js" "default": "./dist/index.node.esm.js"
}, },
"default": { "default": {
"types": "./dist/esm/index.d.ts", "types": "./dist/index.d.ts",
"default": "./dist/index.browser.esm.js" "default": "./dist/index.browser.esm.js"
} }
} }
@ -135,7 +151,7 @@
} }
}, },
"devDependencies": { "devDependencies": {
"@rollup/plugin-commonjs": "^24.0.1", "@rollup/plugin-commonjs": "^25.0.2",
"@rollup/plugin-inject": "^5.0.3", "@rollup/plugin-inject": "^5.0.3",
"@rollup/plugin-json": "^6.0.0", "@rollup/plugin-json": "^6.0.0",
"@rollup/plugin-multi-entry": "^6.0.0", "@rollup/plugin-multi-entry": "^6.0.0",
@ -145,7 +161,7 @@
"@rollup/plugin-typescript": "^11.1.0", "@rollup/plugin-typescript": "^11.1.0",
"@types/chai": "^4.2.22", "@types/chai": "^4.2.22",
"@types/mocha": "^10.0.0", "@types/mocha": "^10.0.0",
"c8": "^7.12.0", "c8": "^8.0.0",
"chai": "^4.3.3", "chai": "^4.3.3",
"dotenv": "^16.0.3", "dotenv": "^16.0.3",
"glob": "^10.0.0", "glob": "^10.0.0",
@ -154,7 +170,7 @@
"mocha": "^10.0.0", "mocha": "^10.0.0",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"pirates": "^4.0.1", "pirates": "^4.0.1",
"puppeteer": "^19.1.2", "puppeteer": "^20.7.3",
"rimraf": "^5.0.0", "rimraf": "^5.0.0",
"rollup": "^3.20.2", "rollup": "^3.20.2",
"rollup-plugin-dts": "^5.3.0", "rollup-plugin-dts": "^5.3.0",

View File

@ -1,4 +1,5 @@
import { abs } from './abs.js' import { abs } from './abs.js'
/** /**
* Greatest common divisor of two integers based on the iterative binary algorithm. * Greatest common divisor of two integers based on the iterative binary algorithm.
* *

View File

@ -6,11 +6,15 @@
export { abs } from './abs.js' export { abs } from './abs.js'
export { bitLength } from './bitLength.js' export { bitLength } from './bitLength.js'
export { crt } from './crt.js'
export { Egcd, eGcd } from './egcd.js' export { Egcd, eGcd } from './egcd.js'
export { gcd } from './gcd.js' export { gcd } from './gcd.js'
export { lcm } from './lcm.js' export { lcm } from './lcm.js'
export { max } from './max.js' export { max } from './max.js'
export { min } from './min.js' export { min } from './min.js'
export { modAdd } from './modAdd.js'
export { modInv } from './modInv.js' export { modInv } from './modInv.js'
export { modPow } from './modPow.js' export { modMultiply } from './modMultiply.js'
export { PrimeFactor, PrimePower, modPow } from './modPow.js'
export { PrimeFactorization, phi } from './phi.js'
export { toZn } from './toZn.js' export { toZn } from './toZn.js'

View File

@ -1,5 +1,6 @@
import { abs } from './abs.js' import { abs } from './abs.js'
import { gcd } from './gcd.js' import { gcd } from './gcd.js'
/** /**
* The least common multiple computed as abs(a*b)/gcd(a,b) * The least common multiple computed as abs(a*b)/gcd(a,b)
* @param a * @param a

View File

@ -1,5 +1,6 @@
import { eGcd } from './egcd.js' import { eGcd } from './egcd.js'
import { toZn } from './toZn.js' import { toZn } from './toZn.js'
/** /**
* Modular inverse. * Modular inverse.
* *

View File

@ -1,18 +1,25 @@
import { abs } from './abs.js' import { abs } from './abs.js'
import { crt } from './crt.js'
import { modInv } from './modInv.js' import { modInv } from './modInv.js'
import { PrimeFactorization, phi } from './phi.js'
import { toZn } from './toZn.js' import { toZn } from './toZn.js'
export type PrimePower = [number | bigint, number | bigint] // [p, k] is for p**k
export type PrimeFactor = number | bigint | PrimePower
/** /**
* 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 if the prime factorization is not provided, or the chinese remainder theorem otherwise.
* *
* @param b base * @param b base
* @param e exponent * @param e exponent
* @param n modulo * @param n modulo
* @param primeFactorization an array of the prime factors, for example [5n, 5n, 13n, 27n], or prime powers as [p, k], for instance [[5, 2], [13, 1], [27, 1]]. If the prime factorization is provided the chinese remainder theorem is used to greatly speed up the exponentiation.
* *
* @throws {@link RangeError} if n <= 0 * @throws {@link RangeError} if n <= 0
* *
* @returns b**e mod n * @returns b**e mod n
*/ */
export function modPow (b: number | bigint, e: number | bigint, n: number | bigint): bigint { export function modPow (b: number | bigint, e: number | bigint, n: number | bigint, primeFactorization?: PrimeFactor[]): bigint {
if (typeof b === 'number') b = BigInt(b) if (typeof b === 'number') b = BigInt(b)
if (typeof e === 'number') e = BigInt(e) if (typeof e === 'number') e = BigInt(e)
if (typeof n === 'number') n = BigInt(n) if (typeof n === 'number') n = BigInt(n)
@ -26,7 +33,11 @@ export function modPow (b: number | bigint, e: number | bigint, n: number | bigi
b = toZn(b, n) b = toZn(b, n)
if (e < 0n) { if (e < 0n) {
return modInv(modPow(b, abs(e), n), n) return modInv(modPow(b, abs(e), n, primeFactorization), n)
}
if (primeFactorization !== undefined) {
return modPowWithFactorization(b, e, n, primePowerArguments(primeFactorization))
} }
let r = 1n let r = 1n
@ -39,3 +50,39 @@ export function modPow (b: number | bigint, e: number | bigint, n: number | bigi
} }
return r return r
} }
function primePowerArguments (primeFactors: PrimeFactor[]): PrimeFactorization {
interface PrimePowers {
[p: string]: {
p: bigint
k: bigint
}
}
const primePowers: PrimePowers = {}
primeFactors.forEach((primeFactor: PrimeFactor) => {
if (typeof primeFactor === 'bigint' || typeof primeFactor === 'number') {
const key = String(primeFactor)
if (primePowers[key] === undefined) {
primePowers[key] = { p: BigInt(primeFactor), k: 1n }
} else {
primePowers[key].k += 1n
}
} else {
const key = String(primeFactor[0])
if (primePowers[key] === undefined) {
primePowers[key] = { p: BigInt(primeFactor[0]), k: BigInt(primeFactor[1]) }
} else {
primePowers[key].k += BigInt(primeFactor[1])
}
}
})
return Object.values(primePowers).map(val => [val.p, val.k])
}
function modPowWithFactorization (b: bigint, e: bigint, n: bigint, primeFactorization: PrimeFactorization): bigint {
const mods = primeFactorization.map(v => v[0] ** v[1])
const phis = primeFactorization.map(v => phi([v]))
const remainders = phis.map((phi, i) => modPow(b, e % phi, mods[i]))
return crt(remainders, mods, n)
}

View File

@ -24,13 +24,58 @@ describe('modPow', function () {
a: 2, a: 2,
b: BigInt(255), b: BigInt(255),
n: BigInt(64), n: BigInt(64),
modPow: BigInt(0) modPow: BigInt(0),
factorization: [
[2n, 6n] as [bigint, bigint]
]
}, },
{ {
a: BigInt(3), a: BigInt(3),
b: BigInt(3), b: BigInt(3),
n: BigInt(25), n: BigInt(25),
modPow: BigInt(2) modPow: BigInt(2)
},
{
a: 1245124521461246134613464612465425n,
b: 9861532432154028374523497086414586n,
n: 500153323344587826888856848415851710985056973082286918972465210560564286327935715658989361814629267526408365126549790626298674370641919299205366510873756645480988096218726721049169354944272728793562147809267541398194457197043260189n,
modPow: 240451691789770161942445790505417018856065097712648273113407316782091824158551945401793333941917448724323499865320019414055693250255681376010258044942735855039307823617978628203426153507803442305305788129411652227222626528677503357n,
factorization: [
218230499735828681265329912048785421189n,
303663914757588658442322995331731552767n,
330074629227155283615948126790169269393n,
318474781297924758545194431666113118619n,
258866136593300047022978927175555617677n,
277352503791024309522752317490262967817n
]
},
{
a: 14,
b: 7,
n: 75,
modPow: (14n ** 7n) % 75n,
factorization: [5, 5, 3]
},
{
a: 14,
b: 7,
n: 75,
modPow: (14n ** 7n) % 75n,
factorization: [
[5, 2],
[3, 1]
] as Array<[number, number]>
},
{
a: 14,
b: 7,
n: 75,
modPow: (14n ** 7n) % 75n,
factorization: [
[5, 1],
[5, 1],
[3, 1]
] as Array<[number, number]>
} }
] ]
const invalidInputs = [ const invalidInputs = [
@ -49,6 +94,14 @@ describe('modPow', function () {
chai.expect(String(ret)).to.equal(String(input.modPow)) chai.expect(String(ret)).to.equal(String(input.modPow))
}) })
}) })
if (input.factorization !== undefined) {
describe(`modPow(${input.a}, ${input.b}, ${input.n}, ${input.factorization.toString()})`, function () {
it(`should return ${input.modPow}`, function () {
const ret = bma.modPow(input.a, input.b, input.n, input.factorization)
chai.expect(String(ret)).to.equal(String(input.modPow))
})
})
}
} }
for (const input of invalidInputs) { for (const input of invalidInputs) {
describe(`modPow(${input.a}, ${input.b}, ${input.n})`, function () { describe(`modPow(${input.a}, ${input.b}, ${input.n})`, function () {
@ -63,15 +116,37 @@ describe('modPow', function () {
}) })
} }
describe('Time profiling', function () { describe('Time profiling', function () {
let iterations = 500 let iterations = 200
it(`just testing ${iterations} iterations of a big modular exponentiation (1024 bits)`, function () { it(`just testing ${iterations} iterations of a big modular exponentiation (2556 bits) without knowing the factorization`, function () {
const p = BigInt('103920301461718841589267304263845359224454055603847417021399996422142529929535423886894599506329362009085557636432288745748144369296043048325513558512136442971686130986388589421125262751724362880217790112013162815676017250234401214198365302142787009943498370856167174244675719638815809347261773472114842038647') const b = 14456431150320172221616509514915526986361238106327826551303073486525612767865296230128540157601034246266893020552680362383297675100669325175361571346279322237329539671666972989978592763343703141198972640627941866075477300010699834312750158094259287059018038648351844386887485449450696346016139524199689991349708808322654998848805208669420846568444495238595132694793197702398883664520354286998606227508937724093725751362231924270085297872354786607945787781132474291940577910417557559293025781300297297108502666688221522472289490235815670978276613741134646350434101590756987811531081642032408524942596500338402454466381n
const b = BigInt('313632271690673451924314047671460131678794095260951233878123501752357966284491455239133687519908410656818506813151659324961829045286402303082891913186909806785080978448037486178337722667190743610785429936585699831407575170854873682955317589189564880931807976657385223632835801016017549762825562427694700595') const e = 6476828424489334540282736436237684965801863681796103123350476262449294411071194504043425154023126089205310946452545492503146985930350204469300515359582715304152166280054365341048395809786334314857324636363499307693460463510288529968621389225833525926438586030988343742064052508822378379640511991190252363284703683198952353035019752852304188032580325320976126425473699273404534699914473174622613565918567472049036606462965033376292343629368800907251671928637057211397168781968913665986137146419257679003630816671501426427417948524164164352320112674416922816719009895345189205426946805834621054179616261802871233808542n
const e = BigInt('452149997592306202232720864363485824701879487303880767747217308770351197801836846325633986474037061753983278534192061455638289551714281047915315943771002615269860312318606105460307037327329178890486613832051027105330475852552183444938408408863970975090778239473049899109989825645608770309107015209564444316') const p = 14042712215688595888153548037889265603500445396639048834534593945467996508131090693365705725532399000992101105698944349047255173597667719370418259169482368508397450171314103850946590710597528264317201250415269575297721977669910571659769606054734401815045082525419212665804298903457270476082779587884970903510490346851467622773314340966549056833592925777140709340896303359906331395892064520612544700229681757412030739888382792194999476266900467607277786534152843408108882693507381781909441777014175765880808549111456404523289456415574592100070259890096499076724913855490255757168221008761519287520710678016476002003203122666993484494111951001896014648841958164118312595430383512299989338389906619375114947097244226374433271798549164684597345168487916487128962249635970759n
while (iterations > 0) { while (iterations > 0) {
bma.modPow(b, e, p) bma.modPow(b, e, p)
iterations-- iterations--
} }
}) })
let iterations2 = 200
it(`just testing ${iterations2} iterations of a big modular exponentiation (2556 bits) knowing the factorization`, function () {
const b = 14456431150320172221616509514915526986361238106327826551303073486525612767865296230128540157601034246266893020552680362383297675100669325175361571346279322237329539671666972989978592763343703141198972640627941866075477300010699834312750158094259287059018038648351844386887485449450696346016139524199689991349708808322654998848805208669420846568444495238595132694793197702398883664520354286998606227508937724093725751362231924270085297872354786607945787781132474291940577910417557559293025781300297297108502666688221522472289490235815670978276613741134646350434101590756987811531081642032408524942596500338402454466381n
const e = 6476828424489334540282736436237684965801863681796103123350476262449294411071194504043425154023126089205310946452545492503146985930350204469300515359582715304152166280054365341048395809786334314857324636363499307693460463510288529968621389225833525926438586030988343742064052508822378379640511991190252363284703683198952353035019752852304188032580325320976126425473699273404534699914473174622613565918567472049036606462965033376292343629368800907251671928637057211397168781968913665986137146419257679003630816671501426427417948524164164352320112674416922816719009895345189205426946805834621054179616261802871233808542n
const p = 14042712215688595888153548037889265603500445396639048834534593945467996508131090693365705725532399000992101105698944349047255173597667719370418259169482368508397450171314103850946590710597528264317201250415269575297721977669910571659769606054734401815045082525419212665804298903457270476082779587884970903510490346851467622773314340966549056833592925777140709340896303359906331395892064520612544700229681757412030739888382792194999476266900467607277786534152843408108882693507381781909441777014175765880808549111456404523289456415574592100070259890096499076724913855490255757168221008761519287520710678016476002003203122666993484494111951001896014648841958164118312595430383512299989338389906619375114947097244226374433271798549164684597345168487916487128962249635970759n
const factorization = [
64735041287835073339645110378796758902638279101014381826528695387496122442691n,
100608456040189246257906094489725850536673092825529567729728751391736301991369n,
58992718408385655584411200892712190864864350334099743757220725378063820240657n,
96562131825975924912838022675233698320760586729314385931723127363428962983247n,
68620104844493133229164822823175832794894654493599547904820655831878232061531n,
71474468886948151751742173259692288576445414562898468020644046548578552084431n,
96793884673833556249816099345608529931050134280462961194018209385115828684189n,
94085307601549784597508419912586171618182711224796241928810495401174923988119n,
91841918562944515248478087657554520191420619060733607414018266296555587309041n,
92269642318171922312931975258187595106742943586895754768486213552371747180589n
]
while (iterations2 > 0) {
bma.modPow(b, e, p, factorization)
iterations2--
}
})
}) })
}) })