UNPKG

@kotevode/ffjavascript

Version:

Finite Field Library in Javascript

149 lines (116 loc) 4.19 kB
/* 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 <https://www.gnu.org/licenses/>. */ /* 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 ]. */ export default class FFT { constructor (G, F, opMulGF) { this.F = F; this.G = G; this.opMulGF = opMulGF; let rem = F.sqrt_t || F.t; let s = F.sqrt_s || F.s; let nqr = F.one; while (F.eq(F.pow(nqr, F.half), F.one)) nqr = F.add(nqr, F.one); this.w = new Array(s+1); this.wi = new Array(s+1); this.w[s] = this.F.pow(nqr, rem); this.wi[s] = this.F.inv(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<n; j++) { rootsi[j] = r; r = this.F.mul(r, this.w[i]); } this.roots.push(rootsi); } */ this._setRoots(Math.min(s, 15)); } _setRoots(n) { for (let i=n; (i>=0) && (!this.roots[i]); i--) { let r = this.F.one; const nroots = 1 << i; const rootsi = new Array(nroots); for (let j=0; j<nroots; j++) { rootsi[j] = r; r = this.F.mul(r, this.w[i]); } this.roots[i] = rootsi; } } fft(p) { if (p.length <= 1) return p; const bits = log2(p.length-1)+1; this._setRoots(bits); const m = 1 << bits; if (p.length != m) { throw new Error("Size must be multiple of 2"); } const res = __fft(this, p, bits, 0, 1); return res; } ifft(p) { if (p.length <= 1) return p; const bits = log2(p.length-1)+1; this._setRoots(bits); const m = 1 << bits; if (p.length != m) { throw new Error("Size must be multiple of 2"); } const res = __fft(this, p, bits, 0, 1); const twoinvm = this.F.inv( this.F.mulScalar(this.F.one, m) ); const resn = new Array(m); for (let i=0; i<m; i++) { resn[i] = this.opMulGF(res[(m-i)%m], twoinvm); } return resn; } } 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.G.add(pall[offset], pall[offset + step]), PF.G.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<ndiv2; i++) { out[i] = PF.G.add(p1[i], PF.opMulGF(p2[i], PF.roots[bits][i])); out[i+ndiv2] = PF.G.sub(p1[i], PF.opMulGF(p2[i], PF.roots[bits][i])); } return out; }