/* Copyright 2018 0kims association This file is part of zksnark javascript library. zksnark javascript library is 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. zksnark javascript library 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 zksnark javascript library. If not, see . */ const fUtils = require("./futils.js"); class F3Field { constructor(F, nonResidue) { this.F = F; this.zero = [this.F.zero, this.F.zero, this.F.zero]; this.one = [this.F.one, this.F.zero, this.F.zero]; this.nonResidue = nonResidue; } _mulByNonResidue(a) { return this.F.mul(this.nonResidue, a); } copy(a) { return [this.F.copy(a[0]), this.F.copy(a[1]), this.F.copy(a[2])]; } add(a, b) { return [ this.F.add(a[0], b[0]), this.F.add(a[1], b[1]), this.F.add(a[2], b[2]) ]; } double(a) { return this.add(a,a); } sub(a, b) { return [ this.F.sub(a[0], b[0]), this.F.sub(a[1], b[1]), this.F.sub(a[2], b[2]) ]; } neg(a) { return this.sub(this.zero, a); } mul(a, b) { const aA = this.F.mul(a[0] , b[0]); const bB = this.F.mul(a[1] , b[1]); const cC = this.F.mul(a[2] , b[2]); return [ this.F.add( aA, this._mulByNonResidue( this.F.sub( this.F.mul( this.F.add(a[1], a[2]), this.F.add(b[1], b[2])), this.F.add(bB, cC)))), // aA + non_residue*((b+c)*(B+C)-bB-cC), this.F.add( this.F.sub( this.F.mul( this.F.add(a[0], a[1]), this.F.add(b[0], b[1])), this.F.add(aA, bB)), this._mulByNonResidue( cC)), // (a+b)*(A+B)-aA-bB+non_residue*cC this.F.add( this.F.sub( this.F.mul( this.F.add(a[0], a[2]), this.F.add(b[0], b[2])), this.F.add(aA, cC)), bB)]; // (a+c)*(A+C)-aA+bB-cC) } inverse(a) { const t0 = this.F.square(a[0]); // t0 = a^2 ; const t1 = this.F.square(a[1]); // t1 = b^2 ; const t2 = this.F.square(a[2]); // t2 = c^2; const t3 = this.F.mul(a[0],a[1]); // t3 = ab const t4 = this.F.mul(a[0],a[2]); // t4 = ac const t5 = this.F.mul(a[1],a[2]); // t5 = bc; // c0 = t0 - non_residue * t5; const c0 = this.F.sub(t0, this._mulByNonResidue(t5)); // c1 = non_residue * t2 - t3; const c1 = this.F.sub(this._mulByNonResidue(t2), t3); const c2 = this.F.sub(t1, t4); // c2 = t1-t4 // t6 = (a * c0 + non_residue * (c * c1 + b * c2)).inverse(); const t6 = this.F.inverse( this.F.add( this.F.mul(a[0], c0), this._mulByNonResidue( this.F.add( this.F.mul(a[2], c1), this.F.mul(a[1], c2))))); return [ this.F.mul(t6, c0), // t6*c0 this.F.mul(t6, c1), // t6*c1 this.F.mul(t6, c2)]; // t6*c2 } div(a, b) { return this.mul(a, this.inverse(b)); } square(a) { const s0 = this.F.square(a[0]); // s0 = a^2 const ab = this.F.mul(a[0], a[1]); // ab = a*b const s1 = this.F.add(ab, ab); // s1 = 2ab; const s2 = this.F.square( this.F.add(this.F.sub(a[0],a[1]), a[2])); // s2 = (a - b + c)^2; const bc = this.F.mul(a[1],a[2]); // bc = b*c const s3 = this.F.add(bc, bc); // s3 = 2*bc const s4 = this.F.square(a[2]); // s4 = c^2 return [ this.F.add( s0, this._mulByNonResidue(s3)), // s0 + non_residue * s3, this.F.add( s1, this._mulByNonResidue(s4)), // s1 + non_residue * s4, this.F.sub( this.F.add( this.F.add(s1, s2) , s3 ), this.F.add(s0, s4))]; // s1 + s2 + s3 - s0 - s4 } isZero(a) { return this.F.isZero(a[0]) && this.F.isZero(a[1]) && this.F.isZero(a[2]); } equals(a, b) { return this.F.equals(a[0], b[0]) && this.F.equals(a[1], b[1]) && this.F.equals(a[2], b[2]); } affine(a) { return [this.F.affine(a[0]), this.F.affine(a[1]), this.F.affine(a[2])]; } mulScalar(base, e) { return fUtils.mulScalar(this, base, e); } exp(base, e) { return fUtils.exp(this, base, e); } toString(a) { const cp = this.affine(a); return `[ ${this.F.toString(cp[0])} , ${this.F.toString(cp[1])}, ${this.F.toString(cp[2])} ]`; } } module.exports = F3Field;