diff --git a/aux/calculate2roots.js b/aux/calculate2roots.js new file mode 100644 index 0000000..e68dc93 --- /dev/null +++ b/aux/calculate2roots.js @@ -0,0 +1,66 @@ + +const bigInt = require("../src/bigint.js"); +const ZqField = require("../src/zqfield.js"); + + +const r = bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617"); +const s = 28; +const nqr_to_t = bigInt("19103219067921713944291392827692070036145651957329286315305642004821462161904"); +const t_minus_1_over_2 = bigInt("40770029410420498293352137776570907027550720424234931066070132305055"); +const root_unity = bigInt("19103219067921713944291392827692070036145651957329286315305642004821462161904"); +const t = bigInt("81540058820840996586704275553141814055101440848469862132140264610111"); + +const F = new ZqField(r); + +function sqrt(a) { + + let v = s; + let z = nqr_to_t; + let w = F.exp(a, t_minus_1_over_2); + let x = F.mul(a, w); + let b = F.mul(x, w); + + + // compute square root with Tonelli--Shanks + // (does not terminate if not a square!) + + while (!F.equals(b, F.one)) + { + let m = 0; + let b2m = b; + while (!F.equals(b2m, F.one)) + { + /* invariant: b2m = b^(2^m) after entering this loop */ + b2m = F.square(b2m); + m += 1; + } + + let j = v-m-1; + w = z; + while (j > 0) + { + w = F.square(w); + --j; + } // w = z^2^(v-m-1) + + z = F.square(w); + b = F.mul(b, z); + x = F.mul(x, w); + v = m; + } + + return x; +} + +const p_minus1= F.sub(r,bigInt(1)); +const gen = bigInt(bigInt(5)); +const twoto28= F.exp(bigInt(2), bigInt(28)); +const rem = F.div(p_minus1, twoto28); +const w28 = F.exp(gen, rem); + +const one = F.exp(w28, twoto28); + + +console.log(F.toString(w28)); +console.log(w28.toString(10)); +console.log(F.toString(one)); diff --git a/file%3a/Users/jbaylina/git/personal/zksnark/src/polfield.js b/file%3a/Users/jbaylina/git/personal/zksnark/src/polfield.js new file mode 100644 index 0000000..2ff835e --- /dev/null +++ b/file%3a/Users/jbaylina/git/personal/zksnark/src/polfield.js @@ -0,0 +1,260 @@ +/* + This library do operations on polinomials where their coefficients are in field F + + The polynomial P(x) = p0 + p1 * x + p2 * x^2 + p3 * x^3, ... + is represented by the array [ p0, p1, p2, p3, ... ] + */ + +const bigInt = require("./bigInt"); +const ZqField = require("./zqfield"); + +class PolFieldZq { + constructor (q) { + this.F = new ZqField(q); + + let rem = q.sub(bigInt(1)); + let s = 0; + while (!rem.isOdd()) { + s ++; + rem = rem.shiftRight(1); + } + + this.w = new Array(s+1); + this.wi = new Array(s+1); + this.w[s] = this.F.exp(bigInt(5), 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--; + } + + } + + add(a, b) { + const m = Math.max(a.length, b.length); + const res = new Array(m); + for (let i=0; i0) { + const z = new Array(n).fill(this.F.zero); + return z.concat(p); + } else { + return p.slice(-n); + } + } + + div(a, b) { + throw new Error("Not Implementted"); + } + + eval(p, x) { + let v = this.F.zero; + let ix = 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= bigInt(1); + for (let i=0; i0 && this.F.isZero(p[i]) ) i--; + return p.slice(0, i+1); + } + + affine(p) { + for (let i=0; i> 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 +(2k-2-scaleV) -(2k-2-scaleV)) /v => + // res = rec * x^(m - (2k-2-scaleV)) => + // res = rec * x^(m - 2k +2 + scaleV) + + const rec = this._reciprocal(this.scaleX(v, scaleV), kbits); + const res = this.scaleX(rec, m - k*2 +2+scaleV); + + return res; + + } + +} + +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 ) ); +} + +module.exports = PolFieldZq; diff --git a/src/bigint.js b/src/bigint.js index 15bcc69..6984e80 100644 --- a/src/bigint.js +++ b/src/bigint.js @@ -3,8 +3,6 @@ const bigInt = require("big-integer"); let wBigInt; -console.log("XXX"); - if (typeof(BigInt) != "undefined") { wBigInt = BigInt; wBigInt.one = wBigInt(1); diff --git a/src/polfield.js b/src/polfield.js index 60e5b9e..9c7a443 100644 --- a/src/polfield.js +++ b/src/polfield.js @@ -5,46 +5,291 @@ is represented by the array [ p0, p1, p2, p3, ... ] */ -class PolField { - constructor (F) { - this.F = F; - } +const bigInt = require("./bigInt"); +const ZqField = require("./zqfield"); + +class PolFieldZq { + constructor (q) { + this.F = new ZqField(q); + + let rem = q.sub(bigInt(1)); + let s = 0; + while (!rem.isOdd()) { + s ++; + rem = rem.shiftRight(1); + } + + this.w = new Array(s+1); + this.wi = new Array(s+1); + this.w[s] = this.F.exp(bigInt(5), 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--; + } - _reduce(a) { - let i = a.length-1; - while ((i>=0) && (this.F.isZero(a[i])) ) i--; - return (i < a.length-1) ? a.slice(0, i+1) : a; } add(a, b) { - const maxGrade = Math.max(a.length, b.length); - const res = new Array(maxGrade); - 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); + } } eval(p, x) { - throw new Error("Not Implementted"); + let v = this.F.zero; + let ix = 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= bigInt(1); + for (let i=0; i0 && this.F.isZero(p[i]) ) i--; + return p.slice(0, i+1); + } + + affine(p) { + for (let i=0; i> 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([bigInt(1)], 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; + } } +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 ) ); +} -module.exports = PolField; +module.exports = PolFieldZq; diff --git a/src/zqfield.js b/src/zqfield.js index 76d0c41..817743e 100644 --- a/src/zqfield.js +++ b/src/zqfield.js @@ -16,6 +16,8 @@ class ZqField { this.equals = bigInt.genEquals(q); this.affine = bigInt.genAffine(q); this.isZero = bigInt.genIsZero(q); + this.two = this.add(this.one, this.one); + this.twoinv = this.inverse(this.two); } copy(a) { diff --git a/test/algebra.js b/test/algebra.js index b9eedff..21fc984 100644 --- a/test/algebra.js +++ b/test/algebra.js @@ -154,7 +154,6 @@ describe("Pairing", () => { const g1b = bn128.G1.mulEscalar(bn128.G1.g, 30); const g2b = bn128.G2.mulEscalar(bn128.G2.g, 25); - const pre1a = bn128.precomputeG1(g1a); const pre2a = bn128.precomputeG2(g2a); const pre1b = bn128.precomputeG1(g1b); diff --git a/test/pols.js b/test/pols.js new file mode 100644 index 0000000..20279e2 --- /dev/null +++ b/test/pols.js @@ -0,0 +1,107 @@ +const chai = require("chai"); + +const bigInt = require("../src/bigint.js"); +const PolField = require("../src/polfield.js"); + +const assert = chai.assert; + +const r = bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617"); + +describe("Polinomial field", () => { + it("Should compute a multiplication", () => { + const PF = new PolField(r); + + const a = [bigInt(1), bigInt(2), bigInt(3)]; + const b = [bigInt(1), bigInt(2), bigInt(3)]; + const res = PF.mul(a,b); + + assert(PF.equals(res, [bigInt(1), bigInt(4), bigInt(10), bigInt(12), bigInt(9)])); + }); + it("Should compute a multiplication 2", () => { + const PF = new PolField(r); + + const a = [bigInt(5), bigInt(1)]; + const b = [bigInt(-5), bigInt(1)]; + const res = PF.mul(a,b); + + assert(PF.equals(res, [bigInt(-25), bigInt(0), bigInt(1)])); + }); + it("Should compute an addition", () => { + const PF = new PolField(r); + + const a = [bigInt(5), bigInt(1)]; + const b = [bigInt(-5), bigInt(1)]; + const res = PF.add(a,b); + + assert(PF.equals(res, [bigInt(0), bigInt(2)])); + }); + it("Should compute a substraction", () => { + const PF = new PolField(r); + + const a = [bigInt(5), bigInt(3), bigInt(4)]; + const b = [bigInt(5), bigInt(1)]; + const res = PF.sub(a,b); + + assert(PF.equals(res, [bigInt(0), bigInt(2), bigInt(4)])); + }); + it("Should compute reciprocal", () => { + const PF = new PolField(r); + + const a = [bigInt(4), bigInt(1), bigInt(-3), bigInt(-1), bigInt(2),bigInt(1), bigInt(-1), bigInt(1)]; + const res = PF._reciprocal(a, 3, 0); + + assert(PF.equals(res, [bigInt(12), bigInt(15), bigInt(3), bigInt(-4), bigInt(-3), bigInt(0), bigInt(1), bigInt(1)])); + }); + it("Should div2", () => { + const PF = new PolField(r); + + // x^6 + const a = [bigInt(0), bigInt(0), bigInt(0), bigInt(0), bigInt(0),bigInt(0), bigInt(1)]; + // x^5 + const b = [bigInt(0), bigInt(0), bigInt(0), bigInt(0), bigInt(0), bigInt(1)]; + + const res = PF._div2(6, b); + assert(PF.equals(res, [bigInt(0), bigInt(1)])); + + const res2 = PF.div(a,b); + assert(PF.equals(res2, [bigInt(0), bigInt(1)])); + }); + it("Should div", () => { + const PF = new PolField(r); + + const a = [bigInt(1), bigInt(2), bigInt(3), bigInt(4), bigInt(5),bigInt(6), bigInt(7)]; + const b = [bigInt(8), bigInt(9), bigInt(10), bigInt(11), bigInt(12), bigInt(13)]; + + const c = PF.mul(a,b); + const d = PF.div(c,b); + + assert(PF.equals(a, d)); + }); + + it("Should div big/small", () => { + const PF = new PolField(r); + + const a = [bigInt(1), bigInt(2), bigInt(3), bigInt(4), bigInt(5),bigInt(6), bigInt(7)]; + const b = [bigInt(8), bigInt(9)]; + + const c = PF.mul(a,b); + const d = PF.div(c,b); + + assert(PF.equals(a, d)); + }); + it("Should div random big", () => { + const PF = new PolField(r); + + const a = []; + const b = []; + for (let i=0; i<1000; i++) a.push(bigInt(Math.floor(Math.random()*100000) -500000)); + for (let i=0; i<300; i++) b.push(bigInt(Math.floor(Math.random()*100000) -500000)); + + const c = PF.mul(a,b); + + const d = PF.div(c,b); + + assert(PF.equals(a, d)); + }).timeout(10000000); + +});