Use ruffini for calculatig lagrange polinomials
This commit is contained in:
parent
2910b7cf7d
commit
2cf2803019
@ -1,260 +0,0 @@
|
||||
/*
|
||||
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);
|
||||
}
|
||||
|
||||
mulScalar(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.mulScalar(b, a[0]);
|
||||
if (b.length == 1) return this.mulScalar(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;
|
@ -17,8 +17,8 @@ if (typeof(BigInt) != "undefined") {
|
||||
if (aux <= nq) {
|
||||
aux = aux % q;
|
||||
}
|
||||
if (aux.isNegative()) {
|
||||
aux = aux.add(q);
|
||||
if (aux < wBigInt.zero) {
|
||||
aux = aux + q;
|
||||
}
|
||||
} else {
|
||||
if (aux >= q) {
|
||||
|
@ -5,14 +5,14 @@
|
||||
is represented by the array [ p0, p1, p2, p3, ... ]
|
||||
*/
|
||||
|
||||
const bigInt = require("./bigInt");
|
||||
const bigInt = require("./bigint.js");
|
||||
|
||||
class PolFieldZq {
|
||||
class PolField {
|
||||
constructor (F) {
|
||||
this.F = F;
|
||||
|
||||
const q = this.F.q;
|
||||
let rem = q.sub(this.F.one);
|
||||
let rem = q.sub(bigInt(1));
|
||||
let s = 0;
|
||||
while (!rem.isOdd()) {
|
||||
s ++;
|
||||
@ -79,7 +79,7 @@ class PolFieldZq {
|
||||
[b, a] = [a, b];
|
||||
}
|
||||
|
||||
if (b.length < log2(a.length)) {
|
||||
if ((b.length <= 2) || (b.length < log2(a.length))) {
|
||||
return this.mulNormal(a,b);
|
||||
} else {
|
||||
return this.mulFFT(a,b);
|
||||
@ -151,14 +151,14 @@ class PolFieldZq {
|
||||
}
|
||||
|
||||
lagrange(points) {
|
||||
let roots = [this.F.one];
|
||||
for (let i=0; i<points.length; i++) {
|
||||
roots = this.mul(roots, [this.F.neg(points[i][0]), this.F.one]);
|
||||
}
|
||||
|
||||
let sum = [];
|
||||
for (let i=0; i<points.length; i++) {
|
||||
let mpol = [this.F.one];
|
||||
for (let j=0;j<points.length;j++) {
|
||||
if (i!=j) {
|
||||
mpol = this.mul(mpol, [this.F.neg(points[j][0]), this.F.one]);
|
||||
}
|
||||
}
|
||||
let mpol = this.ruffini(roots, points[i][0]);
|
||||
const factor =
|
||||
this.F.mul(
|
||||
this.F.inverse(this.eval(mpol, points[i][0])),
|
||||
@ -226,6 +226,15 @@ class PolFieldZq {
|
||||
return true;
|
||||
}
|
||||
|
||||
ruffini(p, r) {
|
||||
const res = new Array(p.length-1);
|
||||
res[res.length-1] = p[p.length-1];
|
||||
for (let i = res.length-2; 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;
|
||||
@ -333,4 +342,4 @@ 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;
|
||||
module.exports = PolField;
|
||||
|
@ -1,6 +1,6 @@
|
||||
const fUtils = require("./futils.js");
|
||||
|
||||
class RatZqField {
|
||||
class RatField {
|
||||
constructor(F) {
|
||||
this.F = F;
|
||||
this.zero = [F.zero, F.one];
|
||||
@ -105,4 +105,4 @@ class RatZqField {
|
||||
}
|
||||
|
||||
|
||||
module.exports = RatZqField;
|
||||
module.exports = RatField;
|
37
src/setup.js
37
src/setup.js
@ -3,11 +3,13 @@ const bigInt = require("./bigint.js");
|
||||
const BN128 = require("./BN128.js");
|
||||
const PolField = require("./polfield.js");
|
||||
const ZqField = require("./zqfield.js");
|
||||
const RatField = require("./ratfield.js");
|
||||
|
||||
const bn128 = new BN128();
|
||||
const G1 = bn128.G1;
|
||||
const G2 = bn128.G2;
|
||||
const PolF = new PolField(new ZqField(bn128.r));
|
||||
const RatPolF = new PolField(new RatField(new ZqField(bn128.r)));
|
||||
const F = new ZqField(bn128.r);
|
||||
|
||||
module.exports = function setup(circuit) {
|
||||
@ -40,9 +42,9 @@ function calculatePolinomials(setup, circuit) {
|
||||
bPoints[s] = [];
|
||||
cPoints[s] = [];
|
||||
for (let c=0; c<circuit.nConstrains; c++) {
|
||||
aPoints[s].push([bigInt(c), circuit.a(c, s)]);
|
||||
bPoints[s].push([bigInt(c), circuit.b(c, s)]);
|
||||
cPoints[s].push([bigInt(c), circuit.c(c, s)]);
|
||||
aPoints[s].push([[bigInt(c), F.one], [circuit.a(c, s), F.one]]);
|
||||
bPoints[s].push([[bigInt(c), F.one], [circuit.b(c, s), F.one]]);
|
||||
cPoints[s].push([[bigInt(c), F.one], [circuit.c(c, s), F.one]]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,10 +53,15 @@ function calculatePolinomials(setup, circuit) {
|
||||
setup.vk_proof.polsB = [];
|
||||
setup.vk_proof.polsC = [];
|
||||
for (let s=0; s<circuit.nSignals; s++) {
|
||||
console.log(`Caclcualte Pol ${s}/${circuit.nSignals}`);
|
||||
setup.vk_proof.polsA.push(PolF.lagrange( aPoints[s] ));
|
||||
setup.vk_proof.polsB.push(PolF.lagrange( bPoints[s] ));
|
||||
setup.vk_proof.polsC.push(PolF.lagrange( cPoints[s] ));
|
||||
// console.log(`Caclcualte Pol ${s}/${circuit.nSignals}`);
|
||||
const pA = RatPolF.lagrange( aPoints[s] );
|
||||
const pB = RatPolF.lagrange( bPoints[s] );
|
||||
const pC = RatPolF.lagrange( cPoints[s] );
|
||||
|
||||
setup.vk_proof.polsA.push( unrat(pA) );
|
||||
setup.vk_proof.polsB.push( unrat(pB) );
|
||||
setup.vk_proof.polsC.push( unrat(pC) );
|
||||
|
||||
}
|
||||
|
||||
// Calculate Z polinomial
|
||||
@ -96,7 +103,7 @@ function calculateEncriptedValuesAtT(setup, circuit) {
|
||||
for (let s=0; s<circuit.nSignals; s++) {
|
||||
|
||||
// A[i] = G1 * polA(t)
|
||||
const at = PolF.eval(setup.vk_proof.polsA[s], setup.toxic.t);
|
||||
const at = F.affine(PolF.eval(setup.vk_proof.polsA[s], setup.toxic.t));
|
||||
const A = G1.affine(G1.mulScalar(G1.g, at));
|
||||
|
||||
setup.vk_proof.A.push(A);
|
||||
@ -107,7 +114,7 @@ function calculateEncriptedValuesAtT(setup, circuit) {
|
||||
|
||||
|
||||
// B1[i] = G1 * polB(t)
|
||||
const bt = PolF.eval(setup.vk_proof.polsB[s], setup.toxic.t);
|
||||
const bt = F.affine(PolF.eval(setup.vk_proof.polsB[s], setup.toxic.t));
|
||||
const B1 = G1.affine(G1.mulScalar(G1.g, bt));
|
||||
|
||||
// B2[i] = G2 * polB(t)
|
||||
@ -116,13 +123,13 @@ function calculateEncriptedValuesAtT(setup, circuit) {
|
||||
setup.vk_proof.B.push(B2);
|
||||
|
||||
// C[i] = G1 * polC(t)
|
||||
const ct = PolF.eval(setup.vk_proof.polsC[s], setup.toxic.t);
|
||||
const ct = F.affine(PolF.eval(setup.vk_proof.polsC[s], setup.toxic.t));
|
||||
const C = G1.affine(G1.mulScalar( G1.g, ct));
|
||||
setup.vk_proof.C.push (C);
|
||||
|
||||
// K = G1 * (A+B+C)
|
||||
|
||||
const kt = F.add(F.add(at, bt), ct);
|
||||
const kt = F.affine(F.add(F.add(at, bt), ct));
|
||||
const K = G1.affine(G1.mulScalar( G1.g, kt));
|
||||
|
||||
|
||||
@ -169,3 +176,11 @@ function calculateHexps(setup, circuit) {
|
||||
}
|
||||
}
|
||||
|
||||
function unrat(p) {
|
||||
const res = new Array(p.length);
|
||||
for (let i=0; i<p.length; i++) {
|
||||
res[i] = RatPolF.F.toF(p[i]);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -141,5 +141,14 @@ describe("Polinomial field", () => {
|
||||
assert(PF.F.equals(v, points[i][1]));
|
||||
}
|
||||
});
|
||||
it("Should test ruffini", () => {
|
||||
const PF = new PolField(new ZqField(r));
|
||||
const a = [bigInt(1), bigInt(2), bigInt(3), bigInt(4), bigInt(5),bigInt(6), bigInt(7)];
|
||||
|
||||
const b = PF.mul(a, [bigInt(-7), bigInt(1)]);
|
||||
const c = PF.ruffini(b, bigInt(7));
|
||||
|
||||
assert(PF.equals(a, c));
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -2,11 +2,11 @@ const chai = require("chai");
|
||||
|
||||
const bigInt = require("../src/bigint.js");
|
||||
const ZqField = require("../src/zqfield.js");
|
||||
const RatZqField = require("../src/ratzqfield.js");
|
||||
const RatField = require("../src/ratfield.js");
|
||||
|
||||
const q = bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
|
||||
const Z = new ZqField(q);
|
||||
const R = new RatZqField(Z);
|
||||
const R = new RatField(Z);
|
||||
|
||||
const assert = chai.assert;
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
||||
{"nPublic":2,"A":[["0","1","0"],["0","1","0"],["0","1","0"]],"vk_a":[["10910302893854256300335313159192947444388132687074523434871198422054676751347","7128911999163755080071576365381312078199150810378083348330952764137111826309"],["18631850238317279011886579705479432506796416675676074259923090354119752710032","11613125271011748641588689311610150724463404115865535933368983811438235418330"],["1","0"]],"vk_b":["14021005419725675778621050747090735283429719877068464218769467811976154477903","13400327218680122582347155709033494654797979408389561950304209881074672567956","1"],"vk_c":[["3096051994409557504719773510360462097114704303646108617318273214347944304327","21060178932557065464613441158524747948458716806166605021180362439829514495363"],["13711469505507426929293229841903695489923719718740047084134989818704335937579","16689306268247690470861364441748198782838298216373469479699367433528545900642"],["1","0"]],"vk_gb_1":["6323076061844787963801699179984388237278671131058334789970950232032140773376","1289442623160734848462556712373448525459100416297212719395950333399658173112","1"],"vk_gb_2":[["14738403669696391106523308033989669531459082697567002658683173458264070358472","7115351592298265627577071367133297887460251153742186717064165044604072075208"],["5684452168257595880393745672144928966665179424697978770339950828964351397925","16005652883870491274891326398599328665806046577140362628567012925774147115231"],["1","0"]],"vk_g":[["13057967944664071939530697379611124014113572609236714070685841838860689079766","4921285704318374954421284662839198492272478322335189504342768676294475453825"],["16821731243154385058674189668904278548308574642868092417486261078313511500423","14329710484989081757569399730710287771777297111484092478685760712065088557056"],["1","0"]],"vk_z":[["9830782349217318574531840014041507435528173882006141768847850813321843762209","908701408738093737682928637200323566556898851221230193697915803980061312337"],["18420854262130852746249857908832658786725598484918874058527841900342812415992","11905336095877065527602013373361492354917686666454749599264218906638347950295"],["1","0"]]}
|
||||
{"nPublic":2,"A":[["0","1","0"],["0","1","0"],["0","1","0"]],"vk_a":[["1114178395252120380395835212597801981089399905319368502910322038327297636405","5033894282647920143094588130989706038816118666385188297428164028250802121541"],["11204369287866566481790440577452570863155384959741708497053387501206022041196","5804122404011709706164213043421176877293761551268564612957682946191385948083"],["1","0"]],"vk_b":["21502216352809485080540484588657130416201550918596561744135679102208461442666","2868090884426856501439409534931120005724644305621622228858085860168686686429","1"],"vk_c":[["6132826916796173784201732531285327915041466217971374202416165094662772918168","21745292514182636990945632198433440675390812739972912576130340262689749324094"],["771505583294284223449947888751235336787816998120918568114562783124678603668","3393096609476927450002720393920165677184260635403434210470312068614019743097"],["1","0"]],"vk_gb_1":["12719499470261599561309259431682387098359700522807496275922119401706283527128","16621363578670067280062291944220200497396193306084620768371306905372731356597","1"],"vk_gb_2":[["10964959780919004944755595440619126945893196497479441585015855710878271674269","2128708169016642077859536709142486990439502854897055565299822951071887976085"],["2388598870284376494192851636260646163256895248220461084487594797988889825261","20110295053122713144819603341600445626597656441687959152080135372786154275292"],["1","0"]],"vk_g":[["12255811247188042732673820469397127953046292441028573933507149762907917860381","7279942967825808009089038706789890489125264289371774766033392871666671678200"],["7190249513755559992931891810866588807891969433945206422275821065594394539722","21314329220359655301619987728101583897798145529926111725521967279053488865751"],["1","0"]],"vk_z":[["17586236151988045592673833893440158242030817616966567700538296220528189715389","3551310632722212929105681060373481610723584194504089389345599320999381324165"],["9267522905249404702171212401947711344560303757795853174593820034048921438294","6158557086581678490843952132823078409925994557584598523494082907032631181314"],["1","0"]]}
|
Loading…
Reference in New Issue
Block a user