react-native-ffjavascript
Version:
Finite Field Library in Javascript (react-native)
288 lines (231 loc) • 6.56 kB
JavaScript
import bigInt from "big-integer";
import buildSqrt from "./fsqrt.js";
import {getRandomBytes} from "./random.js";
export default class ZqField {
constructor(p) {
this.type="F1";
this.one = bigInt.one;
this.zero = bigInt.zero;
this.p = bigInt(p);
this.m = 1;
this.negone = this.p.minus(bigInt.one);
this.two = bigInt(2);
this.half = this.p.shiftRight(1);
this.bitLength = this.p.bitLength();
this.mask = bigInt.one.shiftLeft(this.bitLength).minus(bigInt.one);
this.n64 = Math.floor((this.bitLength - 1) / 64)+1;
this.n32 = this.n64*2;
this.n8 = this.n64*8;
this.R = bigInt.one.shiftLeft(this.n64*64);
this.Ri = this.inv(this.R);
const e = this.negone.shiftRight(this.one);
this.nqr = this.two;
let r = this.pow(this.nqr, e);
while (!r.equals(this.negone)) {
this.nqr = this.nqr.add(this.one);
r = this.pow(this.nqr, e);
}
this.s = this.zero;
this.t = this.negone;
while (!this.t.isOdd()) {
this.s = this.s.add(this.one);
this.t = this.t.shiftRight(this.one);
}
this.nqr_to_t = this.pow(this.nqr, this.t);
buildSqrt(this);
}
e(a,b) {
const res = bigInt(a,b);
return this.normalize(res);
}
add(a, b) {
let res = a.add(b);
if (res.geq(this.p)) {
res = res.minus(this.p);
}
return res;
}
sub(a, b) {
if (a.geq(b)) {
return a.minus(b);
} else {
return this.p.minus(b.minus(a));
}
}
neg(a) {
if (a.isZero()) return a;
return this.p.minus(a);
}
mul(a, b) {
return a.times(b).mod(this.p);
}
mulScalar(base, s) {
return base.times(bigInt(s)).mod(this.p);
}
square(a) {
return a.square().mod(this.p);
}
eq(a, b) {
return a.eq(b);
}
neq(a, b) {
return a.neq(b);
}
lt(a, b) {
const aa = a.gt(this.half) ? a.minus(this.p) : a;
const bb = b.gt(this.half) ? b.minus(this.p) : b;
return aa.lt(bb);
}
gt(a, b) {
const aa = a.gt(this.half) ? a.minus(this.p) : a;
const bb = b.gt(this.half) ? b.minus(this.p) : b;
return aa.gt(bb);
}
leq(a, b) {
const aa = a.gt(this.half) ? a.minus(this.p) : a;
const bb = b.gt(this.half) ? b.minus(this.p) : b;
return aa.leq(bb);
}
geq(a, b) {
const aa = a.gt(this.half) ? a.minus(this.p) : a;
const bb = b.gt(this.half) ? b.minus(this.p) : b;
return aa.geq(bb);
}
div(a, b) {
if (b.isZero()) throw new Error("Division by zero");
return a.times(b.modInv(this.p)).mod(this.p);
}
idiv(a, b) {
if (b.isZero()) throw new Error("Division by zero");
return a.divide(b);
}
inv(a) {
if (a.isZero()) throw new Error("Division by zero");
return a.modInv(this.p);
}
mod(a, b) {
return a.mod(b);
}
pow(a, b) {
return a.modPow(b, this.p);
}
exp(a, b) {
return a.modPow(b, this.p);
}
band(a, b) {
return a.and(b).and(this.mask).mod(this.p);
}
bor(a, b) {
return a.or(b).and(this.mask).mod(this.p);
}
bxor(a, b) {
return a.xor(b).and(this.mask).mod(this.p);
}
bnot(a) {
return a.xor(this.mask).mod(this.p);
}
shl(a, b) {
if (b.lt(this.bitLength)) {
return a.shiftLeft(b).and(this.mask).mod(this.p);
} else {
const nb = this.p.minus(b);
if (nb.lt(this.bitLength)) {
return this.shr(a, nb);
} else {
return bigInt.zero;
}
}
}
shr(a, b) {
if (b.lt(this.bitLength)) {
return a.shiftRight(b);
} else {
const nb = this.p.minus(b);
if (nb.lt(this.bitLength)) {
return this.shl(a, nb);
} else {
return bigInt.zero;
}
}
}
land(a, b) {
return (a.isZero() || b.isZero()) ? bigInt.zero : bigInt.one;
}
lor(a, b) {
return (a.isZero() && b.isZero()) ? bigInt.zero : bigInt.one;
}
lnot(a) {
return a.isZero() ? bigInt.one : bigInt.zero;
}
sqrt_old(n) {
if (n.equals(this.zero)) return this.zero;
// Test that have solution
const res = this.pow(n, this.negone.shiftRight(this.one));
if (!res.equals(this.one)) return null;
let m = parseInt(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).shiftRight(this.one) );
while (!t.equals(this.one)) {
let sq = this.square(t);
let i = 1;
while (!sq.equals(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.greater(this.p.shiftRight(this.one))) {
r = this.neg(r);
}
return r;
}
normalize(a) {
a = bigInt(a);
if (a.isNegative()) {
return this.p.minus(a.abs().mod(this.p));
} else {
return a.mod(this.p);
}
}
random() {
let res = bigInt(0);
let n = bigInt(this.p.square());
while (!n.isZero()) {
res = res.shiftLeft(8).add(bigInt(getRandomBytes(1)[0]));
n = n.shiftRight(8);
}
return res.mod(this.p);
}
toString(a, base) {
let vs;
if (!a.lesserOrEquals(this.p.shiftRight(bigInt(1)))) {
const v = this.p.minus(a);
vs = "-"+v.toString(base);
} else {
vs = a.toString(base);
}
return vs;
}
isZero(a) {
return a.isZero();
}
fromRng(rng) {
let v;
do {
v = bigInt(0);
for (let i=0; i<this.n64; i++) {
v = v.add(v, rng.nextU64().shiftLeft(64*i));
}
v = v.and(this.mask);
} while (v.geq(this.p));
v = v.times(this.Ri).mod(this.q);
return v;
}
}