@toruslabs/ffjavascript
Version:
Finite Field Library in Javascript
368 lines (301 loc) • 7.46 kB
JavaScript
'use strict';
var fft = require('./fft.js');
var fsqrt = require('./fsqrt.js');
var futils = require('./futils.js');
var random = require('./random.js');
var scalar = require('./scalar.js');
/* global BigInt */
class ZqField {
constructor(p) {
this.type = "F1";
this.one = BigInt(1);
this.zero = BigInt(0);
this.p = BigInt(p);
this.m = 1;
this.negone = this.p - this.one;
this.two = BigInt(2);
this.half = this.p >> this.one;
this.bitLength = scalar.bitLength(this.p);
this.mask = (this.one << BigInt(this.bitLength)) - this.one;
this.n64 = Math.floor((this.bitLength - 1) / 64) + 1;
this.n32 = this.n64 * 2;
this.n8 = this.n64 * 8;
this.R = this.e(this.one << BigInt(this.n64 * 64));
this.Ri = this.inv(this.R);
const e = this.negone >> this.one;
this.nqr = this.two;
let r = this.pow(this.nqr, e);
while (!this.eq(r, this.negone)) {
this.nqr = this.nqr + this.one;
r = this.pow(this.nqr, e);
}
this.s = 0;
this.t = this.negone;
while ((this.t & this.one) == this.zero) {
this.s = this.s + 1;
this.t = this.t >> this.one;
}
this.nqr_to_t = this.pow(this.nqr, this.t);
fsqrt(this);
this.FFT = new fft(this, this, this.mul.bind(this));
this.fft = this.FFT.fft.bind(this.FFT);
this.ifft = this.FFT.ifft.bind(this.FFT);
this.w = this.FFT.w;
this.wi = this.FFT.wi;
this.shift = this.square(this.nqr);
this.k = this.exp(this.nqr, 2 ** this.s);
}
e(a, b) {
let res;
if (!b) {
res = BigInt(a);
} else if (b == 16) {
res = BigInt("0x" + a);
}
if (res < 0) {
let nres = -res;
if (nres >= this.p) nres = nres % this.p;
return this.p - nres;
} else {
return res >= this.p ? res % this.p : res;
}
}
add(a, b) {
const res = a + b;
return res >= this.p ? res - this.p : res;
}
sub(a, b) {
return a >= b ? a - b : this.p - b + a;
}
neg(a) {
return a ? this.p - a : a;
}
mul(a, b) {
return (a * b) % this.p;
}
mulScalar(base, s) {
return (base * this.e(s)) % this.p;
}
square(a) {
return (a * a) % this.p;
}
eq(a, b) {
return a == b;
}
neq(a, b) {
return a != b;
}
lt(a, b) {
const aa = a > this.half ? a - this.p : a;
const bb = b > this.half ? b - this.p : b;
return aa < bb;
}
gt(a, b) {
const aa = a > this.half ? a - this.p : a;
const bb = b > this.half ? b - this.p : b;
return aa > bb;
}
leq(a, b) {
const aa = a > this.half ? a - this.p : a;
const bb = b > this.half ? b - this.p : b;
return aa <= bb;
}
geq(a, b) {
const aa = a > this.half ? a - this.p : a;
const bb = b > this.half ? b - this.p : b;
return aa >= bb;
}
div(a, b) {
return this.mul(a, this.inv(b));
}
idiv(a, b) {
if (!b) throw new Error("Division by zero");
return a / b;
}
inv(a) {
if (!a) throw new Error("Division by zero");
let t = this.zero;
let r = this.p;
let newt = this.one;
let newr = a % this.p;
while (newr) {
let q = r / newr;
[t, newt] = [newt, t - q * newt];
[r, newr] = [newr, r - q * newr];
}
if (t < this.zero) t += this.p;
return t;
}
mod(a, b) {
return a % b;
}
pow(b, e) {
return futils.exp(this, b, e);
}
exp(b, e) {
return futils.exp(this, b, e);
}
band(a, b) {
const res = a & b & this.mask;
return res >= this.p ? res - this.p : res;
}
bor(a, b) {
const res = (a | b) & this.mask;
return res >= this.p ? res - this.p : res;
}
bxor(a, b) {
const res = (a ^ b) & this.mask;
return res >= this.p ? res - this.p : res;
}
bnot(a) {
const res = a ^ this.mask;
return res >= this.p ? res - this.p : res;
}
shl(a, b) {
if (Number(b) < this.bitLength) {
const res = (a << b) & this.mask;
return res >= this.p ? res - this.p : res;
} else {
const nb = this.p - b;
if (Number(nb) < this.bitLength) {
return a >> nb;
} else {
return this.zero;
}
}
}
shr(a, b) {
if (Number(b) < this.bitLength) {
return a >> b;
} else {
const nb = this.p - b;
if (Number(nb) < this.bitLength) {
const res = (a << nb) & this.mask;
return res >= this.p ? res - this.p : res;
} else {
return 0;
}
}
}
land(a, b) {
return a && b ? this.one : this.zero;
}
lor(a, b) {
return a || b ? this.one : this.zero;
}
lnot(a) {
return a ? this.zero : this.one;
}
sqrt_old(n) {
if (n == this.zero) return this.zero;
// Test that have solution
const res = this.pow(n, this.negone >> this.one);
if (res != this.one) return null;
let m = this.s;
let c = this.nqr_to_t;
let t = this.pow(n, this.t);
let r = this.pow(n, this.add(this.t, this.one) >> this.one);
while (t != this.one) {
let sq = this.square(t);
let i = 1;
while (sq != this.one) {
i++;
sq = this.square(sq);
}
// b = c ^ m-i-1
let b = c;
for (let j = 0; j < m - i - 1; j++) b = this.square(b);
m = i;
c = this.square(b);
t = this.mul(t, c);
r = this.mul(r, b);
}
if (r > this.p >> this.one) {
r = this.neg(r);
}
return r;
}
normalize(a, b) {
a = BigInt(a, b);
if (a < 0) {
let na = -a;
if (na >= this.p) na = na % this.p;
return this.p - na;
} else {
return a >= this.p ? a % this.p : a;
}
}
random() {
const nBytes = (this.bitLength * 2) / 8;
let res = this.zero;
for (let i = 0; i < nBytes; i++) {
res = (res << BigInt(8)) + BigInt(random.getRandomBytes(1)[0]);
}
return res % this.p;
}
toString(a, base) {
base = base || 10;
let vs;
if (a > this.half && base == 10) {
const v = this.p - a;
vs = "-" + v.toString(base);
} else {
vs = a.toString(base);
}
return vs;
}
isZero(a) {
return a == this.zero;
}
fromRng(rng) {
let v;
do {
v = this.zero;
for (let i = 0; i < this.n64; i++) {
v += rng.nextU64() << BigInt(64 * i);
}
v &= this.mask;
} while (v >= this.p);
v = (v * this.Ri) % this.p; // Convert from montgomery
return v;
}
fft(a) {
return this.FFT.fft(a);
}
ifft(a) {
return this.FFT.ifft(a);
}
// Returns a buffer with Little Endian Representation
toRprLE(buff, o, e) {
scalar.toRprLE(buff, o, e, this.n64 * 8);
}
// Returns a buffer with Big Endian Representation
toRprBE(buff, o, e) {
scalar.toRprBE(buff, o, e, this.n64 * 8);
}
// Returns a buffer with Big Endian Montgomery Representation
toRprBEM(buff, o, e) {
return this.toRprBE(buff, o, this.mul(this.R, e));
}
toRprLEM(buff, o, e) {
return this.toRprLE(buff, o, this.mul(this.R, e));
}
// Pases a buffer with Little Endian Representation
fromRprLE(buff, o) {
return scalar.fromRprLE(buff, o, this.n8);
}
// Pases a buffer with Big Endian Representation
fromRprBE(buff, o) {
return scalar.fromRprBE(buff, o, this.n8);
}
fromRprLEM(buff, o) {
return this.mul(this.fromRprLE(buff, o), this.Ri);
}
fromRprBEM(buff, o) {
return this.mul(this.fromRprBE(buff, o), this.Ri);
}
toObject(a) {
return a;
}
}
module.exports = ZqField;