/* 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"); const assert = require("assert"); 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; assert(i0) { 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