@ -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)); |
@ -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; i<m; i++) { |
||||
|
res[i] = this.F.add(a[i] || this.F.zero, b[i] || this.F.zero); |
||||
|
} |
||||
|
return this.reduce(res); |
||||
|
} |
||||
|
|
||||
|
double(a) { |
||||
|
return this.add(a,a); |
||||
|
} |
||||
|
|
||||
|
sub(a, b) { |
||||
|
const m = Math.max(a.length, b.length); |
||||
|
const res = new Array(m); |
||||
|
for (let i=0; i<m; i++) { |
||||
|
res[i] = this.F.sub(a[i] || this.F.zero, b[i] || this.F.zero); |
||||
|
} |
||||
|
return this.reduce(res); |
||||
|
} |
||||
|
|
||||
|
mulEscalar(a, b) { |
||||
|
if (this.F.isZero(b)) return []; |
||||
|
const res = new Array(a.length); |
||||
|
for (let i=0; i<a.length; i++) { |
||||
|
res[i] = this.F.mul(a[i], b); |
||||
|
} |
||||
|
return res; |
||||
|
} |
||||
|
|
||||
|
mul(a, b) { |
||||
|
if (a.length == 0) return []; |
||||
|
if (b.length == 0) return []; |
||||
|
if (a.length == 1) return this.mulEscalar(b, a[0]); |
||||
|
if (b.length == 1) return this.mulEscalar(a, b[0]); |
||||
|
|
||||
|
const longestN = Math.max(a.length, b.length); |
||||
|
const bitsResult = log2(longestN-1)+2; |
||||
|
const m = 1 << bitsResult; |
||||
|
const ea = this.extend(a,m); |
||||
|
const eb = this.extend(b,m); |
||||
|
|
||||
|
const ta = this._fft(ea, bitsResult, 0, 1, false); |
||||
|
const tb = this._fft(eb, bitsResult, 0, 1, false); |
||||
|
|
||||
|
const tres = new Array(m); |
||||
|
|
||||
|
for (let i=0; i<m; i++) { |
||||
|
tres[i] = this.F.mul(ta[i], tb[i]); |
||||
|
} |
||||
|
|
||||
|
const res = this._fft(tres, bitsResult, 0, 1, true); |
||||
|
|
||||
|
const twoinvm = this.F.inverse(bigInt(m)); |
||||
|
const resn = new Array(m); |
||||
|
for (let i=0; i<m; i++) { |
||||
|
resn[i] = this.F.mul(res[(m-i)%m], twoinvm); |
||||
|
} |
||||
|
|
||||
|
return this.reduce(this.affine(resn)); |
||||
|
} |
||||
|
|
||||
|
square(a) { |
||||
|
return this.mul(a,a); |
||||
|
} |
||||
|
|
||||
|
scaleX(p, n) { |
||||
|
if (n==0) { |
||||
|
return p; |
||||
|
} else if (n>0) { |
||||
|
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<p.length; i++) { |
||||
|
v = this.F.add(v, this.F.mul(p[i], ix)); |
||||
|
ix = this.F.mul(ix, x); |
||||
|
} |
||||
|
return v; |
||||
|
} |
||||
|
|
||||
|
lagrange(points) { |
||||
|
throw new Error("Not Implementted"); |
||||
|
} |
||||
|
|
||||
|
_fft(pall, bits, offset, step) { |
||||
|
|
||||
|
const n = 1 << bits; |
||||
|
if (n==1) { |
||||
|
return [ pall[offset] ]; |
||||
|
} |
||||
|
|
||||
|
const ndiv2 = n >> 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; i<ndiv2; i++) { |
||||
|
out[i] = this.F.add(p1[i], this.F.mul(m, p2[i])); |
||||
|
out[i+ndiv2] = this.F.sub(p1[i], this.F.mul(m, p2[i])); |
||||
|
m = this.F.mul(m, this.w[bits]); |
||||
|
} |
||||
|
|
||||
|
return out; |
||||
|
} |
||||
|
|
||||
|
extend(p, e) { |
||||
|
if (e == p.length) return p; |
||||
|
const z = new Array(e-p.length).fill(this.F.zero); |
||||
|
|
||||
|
return p.concat(z); |
||||
|
} |
||||
|
|
||||
|
reduce(p) { |
||||
|
if (p.length == 0) return p; |
||||
|
if (! this.F.isZero(p[p.length-1]) ) return p; |
||||
|
let i=p.length-1; |
||||
|
while( i>0 && this.F.isZero(p[i]) ) i--; |
||||
|
return p.slice(0, i+1); |
||||
|
} |
||||
|
|
||||
|
affine(p) { |
||||
|
for (let i=0; i<p.length; i++) { |
||||
|
p[i] = this.F.affine(p[i]); |
||||
|
} |
||||
|
return p; |
||||
|
} |
||||
|
|
||||
|
equals(a, b) { |
||||
|
const pa = this.reduce(this.affine(a)); |
||||
|
const pb = this.reduce(this.affine(b)); |
||||
|
|
||||
|
if (pa.length != pb.length) return false; |
||||
|
for (let i=0; i<pb.length; i++) { |
||||
|
if (!this.F.equals(pa[i], pb[i])) return false; |
||||
|
} |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
_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 +(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; |
@ -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); |
||||
|
|
||||
|
}); |