/*
Copyright 2018 0kims association.
This file is part of snarkjs.
snarkjs is a free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as published by the
Free Software Foundation, either version 3 of the License, or (at your option)
any later version.
snarkjs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
snarkjs. If not, see .
*/
/*
This library does operations on polynomials with coefficients in a field F.
A polynomial P(x) = p0 + p1 * x + p2 * x^2 + ... + pn * x^n is represented
by the array [ p0, p1, p2, ... , pn ].
*/
const bigInt = require("./bigint.js");
class PolField {
constructor (F) {
this.F = F;
const q = this.F.q;
let rem = q.sub(bigInt(1));
let s = 0;
while (!rem.isOdd()) {
s ++;
rem = rem.shr(1);
}
const five = this.F.add(this.F.add(this.F.two, this.F.two), this.F.one);
this.w = new Array(s+1);
this.wi = new Array(s+1);
this.w[s] = this.F.exp(five, rem);
this.wi[s] = this.F.inverse(this.w[s]);
let n=s-1;
while (n>=0) {
this.w[n] = this.F.square(this.w[n+1]);
this.wi[n] = this.F.square(this.wi[n+1]);
n--;
}
this.roots = [];
/* for (let i=0; i<16; i++) {
let r = this.F.one;
n = 1 << i;
const rootsi = new Array(n);
for (let j=0; j=0) && (!this.roots[i]); i--) {
let r = this.F.one;
const nroots = 1 << i;
const rootsi = new Array(nroots);
for (let j=0; j a.length) {
[b, a] = [a, b];
}
if ((b.length <= 2) || (b.length < log2(a.length))) {
return this.mulNormal(a,b);
} else {
return this.mulFFT(a,b);
}
}
mulNormal(a, b) {
let res = [];
b = this.affine(b);
for (let i=0; i0) {
const z = new Array(n).fill(this.F.zero);
return z.concat(p);
} else {
if (-n >= p.length) return [];
return p.slice(-n);
}
}
eval2(p, x) {
let v = this.F.zero;
let ix = this.F.one;
for (let i=0; i> 1),
F.mul(
x,
_eval(p, newX, offset+step , step << 1, n >> 1)));
return res;
}
}
lagrange(points) {
let roots = [this.F.one];
for (let i=0; i> 1;
const p1 = this._fft(pall, bits-1, offset, step*2);
const p2 = this._fft(pall, bits-1, offset+step, step*2);
const out = new Array(n);
let m= this.F.one;
for (let i=0; i0 && this.F.isZero(p[i]) ) i--;
return p.slice(0, i+1);
}
affine(p) {
for (let i=0; i=0; i--) {
res[i] = this.F.add(this.F.mul(res[i+1], r), p[i+1]);
}
return res;
}
_next2Power(v) {
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
return v;
}
toString(p) {
const ap = this.affine(p);
let S = "";
for (let i=ap.length-1; i>=0; i--) {
if (!this.F.isZero(p[i])) {
if (S!="") S += " + ";
S = S + p[i].toString(10);
if (i>0) {
S = S + "x";
if (i>1) {
S = S + "^" +i;
}
}
}
}
return S;
}
_reciprocal(p, bits) {
const k = 1 << bits;
if (k==1) {
return [ this.F.inverse(p[0]) ];
}
const np = this.scaleX(p, -k/2);
const q = this._reciprocal(np, bits-1);
const a = this.scaleX(this.double(q), 3*k/2-2);
const b = this.mul( this.square(q), p);
return this.scaleX(this.sub(a,b), -(k-2));
}
// divides x^m / v
_div2(m, v) {
const kbits = log2(v.length-1)+1;
const k = 1 << kbits;
const scaleV = k - v.length;
// rec = x^(k - 2) / v* x^scaleV =>
// rec = x^(k-2-scaleV)/ v
//
// res = x^m/v = x^(m + (2*k-2 - scaleV) - (2*k-2 - scaleV)) /v =>
// res = rec * x^(m - (2*k-2 - scaleV)) =>
// res = rec * x^(m - 2*k + 2 + scaleV)
const rec = this._reciprocal(this.scaleX(v, scaleV), kbits);
const res = this.scaleX(rec, m - 2*k + 2 + scaleV);
return res;
}
div(_u, _v) {
if (_u.length < _v.length) return [];
const kbits = log2(_v.length-1)+1;
const k = 1 << kbits;
const u = this.scaleX(_u, k-_v.length);
const v = this.scaleX(_v, k-_v.length);
const n = v.length-1;
let m = u.length-1;
const s = this._reciprocal(v, kbits);
let t;
if (m>2*n) {
t = this.sub(this.scaleX([this.F.one], 2*n), this.mul(s, v));
}
let q = [];
let rem = u;
let us, ut;
let finish = false;
while (!finish) {
us = this.mul(rem, s);
q = this.add(q, this.scaleX(us, -2*n));
if ( m > 2*n ) {
ut = this.mul(rem, t);
rem = this.scaleX(ut, -2*n);
m = rem.length-1;
} else {
finish = true;
}
}
return q;
}
// returns the ith nth-root of one
oneRoot(n, i) {
let nbits = log2(n-1)+1;
let res = this.F.one;
let r = i;
if(i>=n) {
throw new Error("Given 'i' should be lower than 'n'");
}
else if (1<0) {
if (r & 1 == 1) {
res = this.F.mul(res, this.w[nbits]);
}
r = r >> 1;
nbits --;
}
return res;
}
computeVanishingPolinomial(bits, t) {
const m = 1 << bits;
return this.F.sub(this.F.exp(t, bigInt(m)), this.F.one);
}
evaluateLagrangePolynomials(bits, t) {
const m= 1 << bits;
const tm = this.F.exp(t, bigInt(m));
const u= new Array(m).fill(this.F.zero);
this._setRoots(bits);
const omega = this.w[bits];
if (this.F.equals(tm, this.F.one)) {
for (let i = 0; i < m; i++) {
if (this.F.equals(this.roots[bits][0],t)) { // i.e., t equals omega^i
u[i] = this.F.one;
return u;
}
}
}
const z = this.F.sub(tm, this.F.one);
// let l = this.F.mul(z, this.F.exp(this.F.twoinv, m));
let l = this.F.mul(z, this.F.inverse(bigInt(m)));
for (let i = 0; i < m; i++) {
u[i] = this.F.mul(l, this.F.inverse(this.F.sub(t,this.roots[bits][i])));
l = this.F.mul(l, omega);
}
return u;
}
log2(V) {
return log2(V);
}
}
function log2( V )
{
return( ( ( V & 0xFFFF0000 ) !== 0 ? ( V &= 0xFFFF0000, 16 ) : 0 ) | ( ( V & 0xFF00FF00 ) !== 0 ? ( V &= 0xFF00FF00, 8 ) : 0 ) | ( ( V & 0xF0F0F0F0 ) !== 0 ? ( V &= 0xF0F0F0F0, 4 ) : 0 ) | ( ( V & 0xCCCCCCCC ) !== 0 ? ( V &= 0xCCCCCCCC, 2 ) : 0 ) | ( ( V & 0xAAAAAAAA ) !== 0 ) );
}
function __fft(PF, pall, bits, offset, step) {
const n = 1 << bits;
if (n==1) {
return [ pall[offset] ];
} else if (n==2) {
return [
PF.F.add(pall[offset], pall[offset + step]),
PF.F.sub(pall[offset], pall[offset + step])];
}
const ndiv2 = n >> 1;
const p1 = __fft(PF, pall, bits-1, offset, step*2);
const p2 = __fft(PF, pall, bits-1, offset+step, step*2);
const out = new Array(n);
for (let i=0; i