I wrote this algorithm in JS with the main goal that it will give an answer for 10^750-10^1000 in 2-3 seconds but it solves 10^150 in 2-3 seconds so I am a little far from my goal, the problem is that I sieved through the algorithm couple of times, but nothing I do seems to speed the algorithm significantly.
I would appreciate if any one could enhance my code or give a new one in JS to reach my goal of solving 10^750-10^1000 in under 3 seconds
I implemented Michael O. Rabin and Jeffrey Shallit algorithm for finding the 4 squares summing up to given number n and Miller-Rabin for primality test, I think that these are the very best and fast, so why is my algorithm is not fast as "theoretically could be" ?!
let primes = [2n, 3n, 5n, 7n, 11n, 13n, 17n, 19n, 23n, 29n, 31n, 37n];
function power(x, y, p) {
let res = 1n;
x %= p;
while (y > 0n) {
if (y % 2n == 1n)
res = (res * x) % p;
x = (x * x) % p;
y /= 2n;
}
return res;
}
function millerTest(a, d, n) {
let x = power(a, d, n);
if (x == 1n || x == n - 1n)
return true;
while (d != n - 1n) {
x = (x * x) % n;
d *= 2n;
if (x == 1n)
return false;
if (x == n - 1n)
return true;
}
return false;
}
function isPrime(n) {
if (n <= 1n || n == 4n) return false;
for (let i = 0; i < primes.length; i++) if (n == primes[i]) return true;
let d = n - 1n;
while (d % 2n == 0)
d /= 2n;
for (let i = 0; i < primes.length; i++)
if (!millerTest(primes[i], d, n))
return false;
return true;
}
function abs(a) {
if (a < 0n) return -a;
return a;
}
function round(a, b) {
if (b == 0n) return undefined;
if (a % b == 0n) return a / b;
let d = a / b;
let x = abs(a - (d - 1n) * b);
let y = abs(a - d * b);
let z = abs(a - (d + 1n) * b);
if (x <= y && x <= z) return d - 1n;
if (z <= y && z <= x) return d + 1n;
return d;
}
function complexIsZero(c) {
if (c[0] == 0n && c[1] == 0n) return true;
return false;
}
function complexNorm(c) {
return c[0] * c[0] + c[1] * c[1];
}
function complexGCD(c1, c2) {
if (complexIsZero(c1)) return c2;
if (complexIsZero(c2)) return c1;
let u = c1[0];
let v = c1[1];
let x = c2[0];
let y = c2[1];
let a = round(u * x + v * y, x * x + y * y);
let b = round(v * x - u * y, x * x + y * y);
while (!complexIsZero(c2)) {
c1 = c2;
c2 = [u - a * x + b * y, v - b * x - a * y];
u = c1[0];
v = c1[1];
x = c2[0];
y = c2[1];
a = round(u * x + v * y, x * x + y * y);
b = round(v * x - u * y, x * x + y * y);
}
return c1;
}
function twoSquares(p) {
if (isPrime(p) && p % 4n == 1n) {
for (let i = 0; i < primes.length; i++)
if (power(primes[i], (p - 1n) / 2n, p) == p - 1n) {
let x = power(primes[i], (p - 1n) / 4n, p);
return complexGCD([x, 1n], [p, 0n]);
}
}
return [0n, 0n];
}
function randomInteger(n) {
let str = n + "";
if (str.length <= 8) return BigInt(Math.floor(Math.random() * parseInt(n)));
let res = "";
for (let i = 0; i < str.length - 1; i++)
res += Math.floor(Math.random() * 10);
return BigInt(res);
}
function log2(n) {
let count = 3;
while (n > 1n) {
n /= 8n;
count++;
}
return count;
}
function isPrimeSimple(n) {
if (n <= 1) return false;
if (n == 2 || n == 3) return true;
for (let i = 2; i * i <= n; i++) if (n % i == 0) return false;
return true;
}
function nextPrime(n) {
n++;
while (!isPrimeSimple(n)) n++;
return n;
}
function primorial(n) {
let M = 1n;
let prime = 2;
let logN = log2(n);
for (let i = 2; i <= logN; i++) {
M *= BigInt(prime);
prime = nextPrime(prime);
}
return M;
}
//Quaternion Class
function QIsZero(q1) {
for (let i = 0; i < q1.length; i++) q1[i] = BigInt(q1[i]);
for (let i = 0; i < q1.length; i++) if (q1[i] != 0n) return false;
return true;
}
function Qadd(q1, q2) {
for (let i = 0; i < q1.length; i++) q1[i] = BigInt(q1[i]);
for (let i = 0; i < q2.length; i++) q2[i] = BigInt(q2[i]);
let a1 = q1[0];
let b1 = q1[1];
let c1 = q1[2];
let d1 = q1[3];
let a2 = q2[0];
let b2 = q2[1];
let c2 = q2[2];
let d2 = q2[3];
return [a1 + a2, b1 + b2, c1 + c2, d1 + d2];
}
function Qsub(q1, q2) {
for (let i = 0; i < q1.length; i++) q1[i] = BigInt(q1[i]);
for (let i = 0; i < q2.length; i++) q2[i] = BigInt(q2[i]);
let a1 = q1[0];
let b1 = q1[1];
let c1 = q1[2];
let d1 = q1[3];
let a2 = q2[0];
let b2 = q2[1];
let c2 = q2[2];
let d2 = q2[3];
return [a1 - a2, b1 - b2, c1 - c2, d1 - d2];
}
function Qconj(q1) {
for (let i = 0; i < q1.length; i++) q1[i] = BigInt(q1[i]);
let a1 = q1[0];
let b1 = q1[1];
let c1 = q1[2];
let d1 = q1[3];
return [a1, -b1, -c1, -d1];
}
function Qmul(q1, q2) {
for (let i = 0; i < q1.length; i++) q1[i] = BigInt(q1[i]);
for (let i = 0; i < q2.length; i++) q2[i] = BigInt(q2[i]);
let a1 = q1[0];
let b1 = q1[1];
let c1 = q1[2];
let d1 = q1[3];
let a2 = q2[0];
let b2 = q2[1];
let c2 = q2[2];
let d2 = q2[3];
let x = a1 * a2 - b1 * b2 - c1 * c2 - d1 * d2;
let y = a1 * b2 + b1 * a2 + c1 * d2 - d1 * c2;
let z = a1 * c2 - b1 * d2 + c1 * a2 + d1 * b2;
let w = a1 * d2 + b1 * c2 - c1 * b2 + d1 * a2;
return [x, y, z, w];
}
function Qnorm(q1) {
for (let i = 0; i < q1.length; i++) q1[i] = BigInt(q1[i]);
let a1 = q1[0];
let b1 = q1[1];
let c1 = q1[2];
let d1 = q1[3];
return a1 * a1 + b1 * b1 + c1 * c1 + d1 * d1;
}
function Qdiv(q, r) {
let q0 = q[0];
let q1 = q[1];
let q2 = q[2];
let q3 = q[3];
let r0 = r[0];
let r1 = r[1];
let r2 = r[2];
let r3 = r[3];
let t0 = r0 * q0 + r1 * q1 + r2 * q2 + r3 * q3;
let t1 = r0 * q1 - r1 * q0 - r2 * q3 + r3 * q2;
let t2 = r0 * q2 + r1 * q3 - r2 * q0 - r3 * q1;
let t3 = r0 * q3 - r1 * q2 + r2 * q1 - r3 * q0;
let norm = Qnorm(r);
let a = round(t0, norm);
let b = round(t1, norm);
let c = round(t2, norm);
let d = round(t3, norm);
return [a, b, c, d];
}
function gcdQuaternion(a, b) {
if (QIsZero(a)) return b;
if (QIsZero(b)) return a;
let c, t;
while (!QIsZero(b)) {
c = Qdiv(a, b);
t = Qsub(a, Qmul(b, c));
a = b;
b = t;
}
return a;
}
function fourSquares(n) {
if (n < 0n) return undefined;
if (n == 0n) return [0n, 0n, 0n, 0n];
let d = 0n;
while (n % 2n == 0) {
n /= 2n;
d += 1n;
}
let e = (-4n) ** (d / 4n);
let f = d % 4n;
let M = primorial(n);
let k = 1n;
let p = M * n * k - 1n;
while (!isPrime(p)) {
k += 2n;
p = M * n * k - 1n;
}
let AB = twoSquares(p);
let A = AB[0];
let B = AB[1];
let Q = gcdQuaternion([A, B, 1n, 0n], [n, 0n, 0n, 0n]);
for (let i = 0; i < Q.length; i++) Q[i] *= e;
if (f == 1n) Q = Qmul([1n, 1n, 0n, 0n], Q);
if (f == 2n) Q = Qmul([0n, 2n, 0n, 0n], Q);
if (f == 3n) Q = Qmul([-2n, 2n, 0n, 0n], Q);
return Q;
}
function randomPrime(len) {
let str = randomInteger(10n ** BigInt(len));
let index = 1;
while (!isPrime(BigInt(str.toString() + index))) index += 2;
return BigInt(str.toString() + index);
}
console.log(fourSquares(10n ** 150n));
RSA is an encryption algorithm based on factoring large integers. In RSA, two large prime numbers and a supplementary value are generated as public key. Anyone can use the public key to encrypt a message, but only those with the prime factors can decode the message. There are three phases in the process:
key generation - The public key and private key are generated. The construction method of the keys
generated should be secret.
encryption - The message can be encrypted via public key
decryption - Only the private key can be used to decrypt the
message
Encryption process is as shown:
m - message:
m^e % n = c
c - encrypted message
Decryption process is as shown:
c^d % n = m
This is the implementation of calculating d:
function modInverse(e, phi) {
var m0 = phi, t, q;
var x0 = 0, x1 = 1;
if (phi == 1)
return 0;
while (e > 1) {
// q is quotient
q = Math.floor(e / phi);
t = phi;
// phi is remainder now, process same as
// Euclid's algo
phi = e % phi, e = t;
t = x0;
x0 = x1 - q * x0;
x1 = t;
}
// Make x1 positive
if (x1 < 0)
x1 += m0;
return x1;
}
modInverse(7, 40) // 23
Key pairs of a public key and a private key also need to be generated. Let’s pick 5 and 11 as the primes:
function modInverse(e, phi) {
var m0 = phi, t, q;
var x0 = 0, x1 = 1;
if (phi == 1)
return 0;
while (e > 1) {
// q is quotient
q = Math.floor(e / phi);
t = phi;
// phi is remainder now, process same as
// Euclid's algo
phi = e % phi, e = t;
t = x0;
x0 = x1 - q * x0;
x1 = t;
}
// Make x1 positive
if (x1 < 0)
x1 += m0;
return x1;
}
function isPrime(n){
var prime_numbers=[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97]
for(let i of prime_numbers){
if(n===i){
return true
}
}
}
function RSAKeyPair(p, q) {
// Need to check that they are primes
if (!(isPrime(p) && isPrime(q)))
return;
// Need to check that they're not the same
if (p == q)
return;
var n = p * q,
phi = (p - 1) * (q - 1),
e = 3,
d = modInverse(e, phi);
// Public key: [e,n], Private key: [d,n]
return [[e, n], [d, n]]
}
RSAKeyPair(5,11) //Public key: [3,55], Private key: [27,55]
Complete: Encryption and Decryption
function modInverse(e, phi) {
var m0 = phi, t, q;
var x0 = 0, x1 = 1;
if (phi == 1) {
return 0;
}
while (e > 1) {
// q is quotient
q = Math.floor(e / phi);
t = phi;
// phi is remainder now, process same as
// Euclid's algo
phi = e % phi // 3 % 40
e = t; // e = 40
t = x0; // t = 0
x0 = x1 - q * x0; // 1-0|13|3 x 0
x1 = t; // 0
}
// Make x1 positive
if (x1 < 0) {
x1 += m0;
}
return x1;
}
function isPrime(n){
var prime_numbers=[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97]
for(let i of prime_numbers){
if(n===i){
return true
}
}
}
function RSAKeyPair(p, q) {
// Need to check that they are primes
if (!(isPrime(p) && isPrime(q))) {
return;
}
// Need to check that they're not the same
if (p==q) {
return;
}
var n = p * q,
phi = (p-1)*(q-1),
e = 3,
d = modInverse(e,phi);
// Public key: [e,n], Private key: [d,n]
return [[e,n], [d,n]]
}
RSAKeyPair(5,11)
for (let i in RSAKeyPair(5,11)){
var encrypted_message;
const encryption=c=>{
var m = 2,e = c[0], n = c[1], Encrypted_Message = m ** e % n
console.log("Encryption: " + Encrypted_Message)
encrypted_message=Encrypted_Message
}
const decryption=c=>{
var d = c[0], n = c[1], Decrypted_Message = encrypted_message ** d % n
console.log("Decryption: " + Decrypted_Message)
}
i=="0"?encryption(RSAKeyPair(5, 11)[0]) : i == "1" ? decryption(RSAKeyPair(5, 11)[1]) : false
}
Run it:
function modInverse(e, phi) {
var m0 = phi, t, q;
var x0 = 0, x1 = 1;
if (phi == 1) {
return 0;
}
while (e > 1) {
// q is quotient
q = Math.floor(e / phi);
t = phi;
// phi is remainder now, process same as
// Euclid's algo
phi = e % phi // 3 % 40
e = t; // e = 40
t = x0; // t = 0
x0 = x1 - q * x0; // 1-0|13|3 x 0
x1 = t; // 0
}
// Make x1 positive
if (x1 < 0) {
x1 += m0;
}
return x1;
}
function isPrime(n){
var prime_numbers=[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97]
for(let i of prime_numbers){
if(n===i){
return true
}
}
}
function RSAKeyPair(p, q) {
// Need to check that they are primes
if (!(isPrime(p) && isPrime(q))) {
return;
}
// Need to check that they're not the same
if (p==q) {
return;
}
var n = p * q,
phi = (p-1)*(q-1),
e = 3,
d = modInverse(e,phi);
// Public key: [e,n], Private key: [d,n]
return [[e,n], [d,n]]
}
RSAKeyPair(5,11)
for (let i in RSAKeyPair(5,11)){
var encrypted_message;
const encryption=c=>{
var m=2,e=c[0],n=c[1],Encrypted_Message=m**e%n
console.log("Encryption: "+Encrypted_Message)
encrypted_message=Encrypted_Message
}
const decryption=c=>{
var d=c[0],n=c[1],Decrypted_Message=encrypted_message**d % n
console.log("Decryption: "+Decrypted_Message)
}
i=="0"?encryption(RSAKeyPair(5,11)[0]):i=="1"?decryption(RSAKeyPair(5,11)[1]):false
}
This encrypts the message 2, and the receiver can decrypt that back to 2. However, when I change the message 2 to 3:
function modInverse(e, phi) {
var m0 = phi, t, q;
var x0 = 0, x1 = 1;
if (phi == 1) {
return 0;
}
while (e > 1) {
// q is quotient
q = Math.floor(e / phi);
t = phi;
// phi is remainder now, process same as
// Euclid's algo
phi = e % phi // 3 % 40
e = t; // e = 40
t = x0; // t = 0
x0 = x1 - q * x0; // 1-0|13|3 x 0
x1 = t; // 0
}
// Make x1 positive
if (x1 < 0) {
x1 += m0;
}
return x1;
}
function isPrime(n) {
var prime_numbers = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
for (let i of prime_numbers) {
if (n === i) {
return true
}
}
}
function RSAKeyPair(p, q) {
// Need to check that they are primes
if (!(isPrime(p) && isPrime(q))) {
return;
}
// Need to check that they're not the same
if (p == q) {
return;
}
var n = p * q,
phi = (p - 1) * (q - 1),
e = 3,
d = modInverse(e, phi);
// Public key: [e,n], Private key: [d,n]
return [[e, n], [d, n]]
}
RSAKeyPair(5, 11)
for (let i in RSAKeyPair(5, 11)) {
var encrypted_message;
const encryption = c => {
var m = 3, e = c[0], n = c[1], Encrypted_Message = m ** e % n
console.log("Encryption: " + Encrypted_Message)
encrypted_message = Encrypted_Message
}
const decryption = c => {
var d = c[0], n = c[1], Decrypted_Message = encrypted_message ** d % n
console.log("Decryption: " + Decrypted_Message)
}
i == "0" ? encryption(RSAKeyPair(5, 11)[0]) : i == "1" ? decryption(RSAKeyPair(5, 11)[1]) : false
}
It gives different result. I expect 3 should be the answer, what is wrong?
The posted example uses p = 5 and q = 11 and determines for the modulus N = 55, the public exponent e = 3 and the private exponent d = 27 (returned by RSAKeyPair(5, 11)). This corresponds to a valid key pair.
Although small values are used, the intermediate results can be quite large.
With the plaintext m = 3 the ciphertext c = me mod 55 = 27 results for the encyrption. The value 33 = 27 is obviously uncritical.
For decryption, however, the decrypted data is m = cd mod 55 = 2727 mod 55. The value 2727 (approx. 4.4 * 1038) is critical, since it is above the maximum (safe) integer possible for JavaScript Number.MAX_SAFE_INTEGER = 253 - 1 = 9,007,199,254,740,991. This generally results in a wrong plaintext during decryption.
The problem can be solved by using BigInt for larger numbers:
var e = 3;
var d = 27;
var N = 55;
// Encryption
var m = 3; // getRandomInt(N) // For arbitrary plaintexts uncomment getRandomInt(N)
var c = m ** e % N;
console.log("Plaintext : " + m);
console.log("Ciphertext : " + c);
// Decryption without BigInt
var dec = c ** d % N;
console.log("Result without BigInt: " + dec); // Wrong
// Decryption with BigInt
var dec = BigInt(c) ** BigInt(d) % BigInt(N);
console.log("Result with BigInt : " + dec); // Correct
function getRandomInt(max) {
return Math.floor(Math.random() * Math.floor(max));
}
Of course this applies in general, i.e. not only to encryption and decryption, but also to the key generation, as soon as the values (including intermediate results) become accordingly large.
Edit: As mentioned in the comment, there are more efficient implementations for modular exponentiation than the direct one (= exponentiate, then taking the result modulo). For this, also existing libraries can be used, e.g. bigint-mod-arith, which applies the right-to-left binary method for modular exponentiation.
I have translated the following C++ code:
#include <iostream>
using namespace std;
#define NDIGITS 100
#define LEN (NDIGITS/4+1)*14
long a[LEN];
long b;
long c = LEN;
long d;
long e = 0;
long f = 10000;
long g;
long h = 0;
int main(void) {
cout<<b<<endl;
for(; (b=c-=14) > 0 ;){
for(; --b > 0 ;){
d *= b;
if( h == 0 )
d += 2000*f;
else
d += a[b]*f;
g=b+b-1;
a[b] = d % g;
d /= g;
}
h = printf("%ld",e+d/f);
d = e = d % f;
}
getchar();
return 0;
}
Into JavaScript:
function mod(n, m) {
return ((m % n) + n) % n;
} // mod function to fix javascript modulo bug
function calculate(NDIGITS){
var LEN = (NDIGITS / 4 + 1) * 14,
out = "",
a = [],
b = 0,
c = LEN,
d = 0,
e = 0,
f = 10000,
g = 0,
h = 0;
for( ; a.length != LEN; a.push(0));
for( ; (b=c-=14) > 0 ; ){
for(; --b > 0 ;){
d *= b;
if(h == 0)
d += 2000*f;
else
d += a[b]*f;
g=b+b-1;
a[b] = mod(d, g);
d /= g;
};
h = 4;
out += e + d / f;
d = e = mod(d, f);
};
return out;
};
calculate(100);
The problem is, the C++ (which is correct) output looks like this:
314159265358979323846264338327952884197169399375105820974944592307816406286208998628034825342117067
But the JavaScript (which is wrong) output looks like this:
3141.59265358979345928.3358757688158002.0385670499462603.1996016540431161.44919092773639662.2465149363658988.6127837844255865.38922090756173.61883094848226189.6324225085448150.3443440509899223.2179589088062808.1943642437717982.8973948575671840.86646781354151140.38694447211833938.5632867441137341.458720505086448.7384444661472807.14448220310268936.5521832735086764.9290682040381301.76585926509928223.4135991546457438.115065010927
Where did I mess up in my coding? Thanks for the help.
JavaScript does floating point division.
Arguments exchanged in modulo calculation function.
Here is code that produces the same result as the C++ code provided for the given sample (100) digits:
function mod(m, n) {
return ((m % n) + n) % n;
} // mod function to fix javascript modulo bug
function calculate(NDIGITS) {
var LEN = (NDIGITS / 4 + 1) * 14,
out = "",
a = [],
b = 0,
c = LEN,
d = 0,
e = 0,
f = 10000,
g = 0,
h = 0;
for (; a.length !== LEN; a.push(0));
for (; (b = c -= 14) > 0;) {
for (; --b > 0;) {
d *= b;
if (h === 0) {
d += 2000 * f;
} else {
d += a[b] * f;
}
g = b + b - 1;
a[b] = mod(d, g);
d = Math.floor(d / g);
}
h = Math.floor(e + d / f);
out += h;
h = h.length;
d = e = mod(d, f);
}
return out;
}
console.log(calculate(100));
I have a Javascript code, which I use to bring Night Mode effect on an HTML page...
The code goes something like this-
javascript: (function () {
function RGBtoHSL(RGBColor) {
with(Math) {
var R, G, B;
var cMax, cMin;
var sum, diff;
var Rdelta, Gdelta, Bdelta;
var H, L, S;
R = RGBColor[0];
G = RGBColor[1];
B = RGBColor[2];
cMax = max(max(R, G), B);
cMin = min(min(R, G), B);
sum = cMax + cMin;
diff = cMax - cMin;
L = sum / 2;
if (cMax == cMin) {
S = 0;
H = 0;
} else {
if (L <= (1 / 2)) S = diff / sum;
else S = diff / (2 - sum);
Rdelta = R / 6 / diff;
Gdelta = G / 6 / diff;
Bdelta = B / 6 / diff;
if (R == cMax) H = Gdelta - Bdelta;
else if (G == cMax) H = (1 / 3) + Bdelta - Rdelta;
else H = (2 / 3) + Rdelta - Gdelta; if (H < 0) H += 1;
if (H > 1) H -= 1;
}
return [H, S, L];
}
}
function getRGBColor(node, prop) {
var rgb = getComputedStyle(node, null).getPropertyValue(prop);
var r, g, b;
if (/rgb\((\d+),\s(\d+),\s(\d+)\)/.exec(rgb)) {
r = parseInt(RegExp.$1, 10);
g = parseInt(RegExp.$2, 10);
b = parseInt(RegExp.$3, 10);
return [r / 255, g / 255, b / 255];
}
return rgb;
}
function hslToCSS(hsl) {
return "hsl(" + Math.round(hsl[0] * 360) + ", " + Math.round(hsl[1] * 100) + "%, " + Math.round(hsl[2] * 100) + "%)";
}
var props = ["color", "background-color", "border-left-color", "border-right-color", "border-top-color", "border-bottom-color"];
var props2 = ["color", "backgroundColor", "borderLeftColor", "borderRightColor", "borderTopColor", "borderBottomColor"];
if (typeof getRGBColor(document.documentElement, "background-color") == "string") document.documentElement.style.backgroundColor = "white";
revl(document.documentElement);
function revl(n) {
var i, x, color, hsl;
if (n.nodeType == Node.ELEMENT_NODE) {
for (i = 0; x = n.childNodes[i]; ++i) revl(x);
for (i = 0; x = props[i]; ++i) {
color = getRGBColor(n, x);
if (typeof (color) != "string") {
hsl = RGBtoHSL(color);
hsl[2] = 1 - hsl[2];
n.style[props2[i]] = hslToCSS(hsl);
}
}
}
}
})()
I have saved this as a Bookmarklet in my Bookmark Bar on Google Chrome, but I want this to be automatically applied to every page I load. What should I do to achieve this?
You should write this as a userscript and run it with something like tampermonkey