modpow with crt; crt; modAdd; modMultiply; phi
This commit is contained in:
parent
1375c85bb5
commit
fbe007f359
|
@ -0,0 +1,3 @@
|
||||||
|
declare const IS_BROWSER: boolean
|
||||||
|
declare const _MODULE_TYPE: string
|
||||||
|
declare const _NPM_PKG_VERSION: string
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { modInv } from './modInv.js'
|
||||||
|
import { toZn } from './toZn.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* @param remainders the array of remainders a_i. For example [17n, 243n, 344n]
|
||||||
|
* @param modulos the array of modulos n_i. For example [769n, 2017n, 47701n]
|
||||||
|
* @param modulo the product of all modulos. Provided here just to save some operations if it is already known
|
||||||
|
* @returns x
|
||||||
|
*/
|
||||||
|
export function crt (
|
||||||
|
remainders: bigint[],
|
||||||
|
modulos: bigint[],
|
||||||
|
modulo?: bigint
|
||||||
|
): bigint {
|
||||||
|
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)
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { toZn } from './toZn.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modular addition of (a_1 + ... + a_r) mod n
|
||||||
|
* @param addends an array of the numbers a_i to add. For example [3, 12353251235n, 1243, -12341232545990n]
|
||||||
|
* @param n the modulo
|
||||||
|
* @returns The smallest positive integer that is congruent with (a_1 + ... + a_r) mod n
|
||||||
|
*/
|
||||||
|
export function modAdd (addends: Array<number | bigint>, n: number | bigint): bigint {
|
||||||
|
const mod = BigInt(n)
|
||||||
|
const as = addends.map(a => BigInt(a) % mod)
|
||||||
|
return toZn(as.reduce((sum, a) => sum + a % mod, 0n), mod)
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { toZn } from './toZn.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modular addition of (a_1 * ... * a_r) mod n
|
||||||
|
* @param factors an array of the numbers a_i to multiply. For example [3, 12353251235n, 1243, -12341232545990n]
|
||||||
|
* @param n the modulo
|
||||||
|
* @returns The smallest positive integer that is congruent with (a_1 * ... * a_r) mod n
|
||||||
|
*/
|
||||||
|
export function modMultiply (factors: Array<number | bigint>, n: number | bigint): bigint {
|
||||||
|
const mod = BigInt(n)
|
||||||
|
const as = factors.map(a => BigInt(a) % mod)
|
||||||
|
return toZn(as.reduce((prod, a) => prod * a % mod, 1n), mod)
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
export type PrimeFactorization = Array<[bigint, bigint]>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A function that computes the Euler's totien function of a number n, whose prime power factorization is known
|
||||||
|
*
|
||||||
|
* @param 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 phi((p1**k1)*(p2**k2)*...*(pr**kr))
|
||||||
|
*/
|
||||||
|
export function phi (primeFactorization: PrimeFactorization): bigint {
|
||||||
|
return primeFactorization.map(v => (v[0] ** (v[1] - 1n)) * (v[0] - 1n)).reduce((prev, curr) => {
|
||||||
|
return curr * prev
|
||||||
|
}, 1n)
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
import * as bma from '#pkg'
|
||||||
|
|
||||||
|
describe('crt', function () {
|
||||||
|
const tests = [
|
||||||
|
{
|
||||||
|
input: {
|
||||||
|
remainders: [2n, 3n, 10n],
|
||||||
|
modulos: [5n, 7n, 11n]
|
||||||
|
},
|
||||||
|
output: 87n
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: {
|
||||||
|
remainders: [3n, 3n, 4n],
|
||||||
|
modulos: [7n, 5n, 12n],
|
||||||
|
modulo: 7n * 5n * 12n
|
||||||
|
},
|
||||||
|
output: 388n
|
||||||
|
}
|
||||||
|
]
|
||||||
|
const invalidTests = [
|
||||||
|
{
|
||||||
|
input: {
|
||||||
|
remainders: [2n, 3n, 10n],
|
||||||
|
modulos: [5n, 7n]
|
||||||
|
},
|
||||||
|
output: 87n
|
||||||
|
}
|
||||||
|
]
|
||||||
|
for (const test of tests) {
|
||||||
|
describe(`crt([${test.input.remainders.toString()}], [${test.input.modulos.toString()}])`, function () {
|
||||||
|
it(`should return ${test.output}`, function () {
|
||||||
|
const ret = bma.crt(test.input.remainders, test.input.modulos, test.input.modulo)
|
||||||
|
chai.expect(ret).to.equal(test.output)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for (const test of invalidTests) {
|
||||||
|
describe(`crt([${test.input.remainders.toString()}], [${test.input.modulos.toString()}])`, function () {
|
||||||
|
it('should throw RangeError', function () {
|
||||||
|
try {
|
||||||
|
bma.crt(test.input.remainders, test.input.modulos)
|
||||||
|
throw new Error('should have failed')
|
||||||
|
} catch (err) {
|
||||||
|
chai.expect(err).to.be.instanceOf(RangeError)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
|
@ -0,0 +1,31 @@
|
||||||
|
import * as bma from '#pkg'
|
||||||
|
|
||||||
|
describe('modAdd', function () {
|
||||||
|
const inputs = [
|
||||||
|
{
|
||||||
|
addends: [1n, 14n, 5n],
|
||||||
|
n: 5n,
|
||||||
|
result: 0n
|
||||||
|
},
|
||||||
|
{
|
||||||
|
addends: [98146598146508942650812465n, 971326598235697821592183520352089356n],
|
||||||
|
n: 972136523n,
|
||||||
|
result: 88640188n
|
||||||
|
},
|
||||||
|
{
|
||||||
|
addends: [98146598146508942650812465n, -971326598235697821592183520352089356n],
|
||||||
|
n: 972136523n,
|
||||||
|
result: 133225889n
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
for (const input of inputs) {
|
||||||
|
describe(`modAdd([${input.addends.toString()}], ${input.n})`, function () {
|
||||||
|
it(`should return ${input.result}`, function () {
|
||||||
|
const ret = bma.modAdd(input.addends, input.n)
|
||||||
|
// chai.assert( String(ret) === String(input.modInv) );
|
||||||
|
chai.expect(String(ret)).to.be.equal(String(input.result))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
|
@ -0,0 +1,30 @@
|
||||||
|
import * as bma from '#pkg'
|
||||||
|
|
||||||
|
describe('modMultiply', function () {
|
||||||
|
const inputs = [
|
||||||
|
{
|
||||||
|
factors: [-1n, 1n, 19n],
|
||||||
|
n: 5n,
|
||||||
|
result: 1n
|
||||||
|
},
|
||||||
|
{
|
||||||
|
factors: [98146598146508942650812465n, 971326598235697821592183520352089356n],
|
||||||
|
n: 972136523n,
|
||||||
|
result: 326488233n
|
||||||
|
},
|
||||||
|
{
|
||||||
|
factors: [98146598146508942650812465n, -971326598235697821592183520352089356n],
|
||||||
|
n: 972136523n,
|
||||||
|
result: 645648290n
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
for (const input of inputs) {
|
||||||
|
describe(`modMultiply([${input.factors.toString()}], ${input.n})`, function () {
|
||||||
|
it(`should return ${input.result}`, function () {
|
||||||
|
const ret = bma.modMultiply(input.factors, input.n)
|
||||||
|
chai.expect(String(ret)).to.be.equal(String(input.result))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
|
@ -0,0 +1,22 @@
|
||||||
|
import * as bma from '#pkg'
|
||||||
|
|
||||||
|
describe('phi', function () {
|
||||||
|
const tests = [
|
||||||
|
{
|
||||||
|
input: [[17n, 1n], [19n, 1n]] as bma.PrimeFactorization,
|
||||||
|
output: (17n - 1n) * (19n - 1n)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: [[17n, 4n]] as bma.PrimeFactorization,
|
||||||
|
output: (17n ** 3n) * 16n
|
||||||
|
}
|
||||||
|
]
|
||||||
|
for (const test of tests) {
|
||||||
|
describe(`phi([${test.input.toString()}])`, function () {
|
||||||
|
it(`should return ${test.output}`, function () {
|
||||||
|
const ret = bma.phi(test.input)
|
||||||
|
chai.expect(ret).to.equal(test.output)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
Loading…
Reference in New Issue