/*
Copyright 2018 0KIMS association.
This file is part of circom (Zero Knowledge Circuit Compiler).
circom 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.
circom 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 circom. If not, see .
*/
/*
// Number
///////////////
N: a
{
t: "N",
v: bigInt(a)
}
// Signal
///////////////
{
t: "S",
sIdx: sIdx
}
// Linear Convination
//////////////////
LC: c1*s1 + c2*s2 + c3*s3
{
t: "LC",
coefs: {
s1: bigInt(c1),
s2: bigInt(c2),
s3: bigInt(c3)
}
}
// Quadratic Expression
//////////////////
QEX: a*b + c WHERE a,b,c are LC
{
t: "QEX"
a: { t: "LC", coefs: {...} },
b: { t: "LC", coefs: {...} },
c: { t: "LC", coefs: {...} }
}
NQ: Non quadratic expression
{
t: "NQ"
}
*/
/*
+ N LC QEX NQ
N N LC QEX NQ
LC LC LC QEX NQ
QEX QEX QEX NQ NQ
NQ NQ NQ NQ NQ
* N LC QEX NQ
N N LC QEX NQ
LC LC QEX NQ NQ
QEX QEX NQ NQ NQ
NQ NQ NQ NQ NQ
*/
const bigInt = require("big-integer");
const utils = require("./utils");
const sONE = 0;
class LCAlgebra {
constructor (aField) {
const self = this;
this.field= aField;
[
["lt",2],
["leq",2],
["eq",2],
["neq",2],
["geq",2],
["gt",2],
["idiv",2],
["mod",2],
["band",2],
["bor",2],
["bxor",2],
["bnot",2],
["land",2],
["lor",2],
["lnot",2],
["shl",2],
["shr",2],
].forEach( (op) => {
self._genNQOp(op[0], op[1]);
});
}
_genNQOp(op, nOps) {
const self=this;
self[op] = function() {
const operands = [];
for (let i=0; i=0) sIdx = ctx.signals[sIdx].e;
}
S = S + "[" + sIdx + "]";
}
}
if (S=="") return "0"; else return S;
} else if (a.t == "QEX") {
return "( "+
this.toString(a.a, ctx)+" ) * ( "+
this.toString(a.b, ctx)+" ) + " +
this.toString(a.c, ctx);
} else {
return "NQ";
}
}
evaluate(ctx, n) {
if (n.t == "N") {
return n.v;
} else if (n.t == "SIGNAL") {
return getSignalValue(ctx, n.sIdx);
} else if (n.t == "LC") {
let v= this.field.zero;
for (let k in n.coefs) {
const s = getSignalValue(ctx, k);
if (s === null) return null;
v = this.field.add(v, this.field.mul( n.coefs[k], s));
}
return v;
} else if (n.type == "QEX") {
const a = this.evaluate(ctx, n.a);
if (a === null) return null;
const b = this.evaluate(ctx, n.b);
if (b === null) return null;
const c = this.evaluate(ctx, n.c);
if (c === null) return null;
return this.field.add(this.field.mul(a,b), c);
} else {
return null;
}
function getSignalValue(ctx, sIdx) {
let s = ctx.signals[sIdx];
while (s.e>=0) s = ctx.signals[s.e];
if (utils.isDefined(s.v)) return s.v;
return null;
}
}
canonize(ctx, a) {
if (a.t == "LC") {
const res = this._clone(a);
for (let k in a.coefs) {
let s = k;
while (ctx.signals[s].e>=0) s= ctx.signals[s].e;
if (utils.isDefined(ctx.signals[s].v)&&(k != sONE)) {
const v = this.field.mul(res.coefs[k], ctx.signals[s].v);
if (!utils.isDefined(res.coefs[sONE])) {
res.coefs[sONE]=v;
} else {
res.coefs[sONE]= this.field.add(res.coefs[sONE], v);
}
delete res.coefs[k];
} else if (s != k) {
if (!utils.isDefined(res.coefs[s])) {
res.coefs[s]=res.coefs[k];
} else {
res.coefs[s]= this.field.add(res.coefs[s], res.coefs[k]);
}
delete res.coefs[k];
}
}
for (let k in res.coefs) {
if (res.coefs[k].isZero()) delete res.coefs[k];
}
return res;
} else if (a.t == "QEX") {
const res = {
t: "QEX",
a: this.canonize(ctx, a.a),
b: this.canonize(ctx, a.b),
c: this.canonize(ctx, a.c)
};
return res;
} else {
return a;
}
}
}
module.exports = LCAlgebra;