-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmod-calculator.ts
81 lines (70 loc) · 1.7 KB
/
mod-calculator.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import { CalcStrategy } from './calc-strategy';
export class ModCalculator implements CalcStrategy {
add(a: number, b: number, mod: number): number {
return (a + b) % mod;
}
sub(a: number, b: number, mod: number): number {
return (a - b) % mod;
}
mul(a: number, b: number, mod?: number): number {
return (a * b) % mod;
}
div(a: number, b: number, mod?: number): number {
return this.mul(a, this.modInv(b, mod));
}
modInv(a: number, n: number) {
try {
const egcd = this.eGcd(this.toZn(a, n), n);
if (egcd.g !== 1) {
return NaN; // modular inverse does not exist
} else {
return this.toZn(egcd.x, n);
}
} catch (error) {
return NaN;
}
}
/**
* 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).
*/
private eGcd(a: number, b: number): EGCDReturn {
if (a <= 0 || b <= 0) throw new RangeError('a and b MUST be > 0'); // a and b MUST be positive
let x = 0;
let y = 1;
let u = 1;
let v = 0;
while (a !== 0) {
const q = b / a;
const r = b % a;
const m = x - u * q;
const n = y - v * q;
b = a;
a = r;
x = u;
y = v;
u = m;
v = n;
}
return {
g: b,
x: x,
y: y,
};
}
/**
* Finds the smallest positive element that is congruent to a in modulo n
*/
private toZn(a: number, n: number): number {
if (n <= 0) {
return NaN;
}
a = a % n;
return a < 0 ? a + n : a;
}
}
interface EGCDReturn {
g: number;
x: number;
y: number;
}