mirror of
https://github.com/arnaucube/circom.git
synced 2026-02-07 03:06:42 +01:00
Construction phase redone
This commit is contained in:
@@ -87,7 +87,7 @@ function instantiateConstant(ctx, value) {
|
|||||||
function createRefs(ctx, ast) {
|
function createRefs(ctx, ast) {
|
||||||
const scopeLabels = [];
|
const scopeLabels = [];
|
||||||
iterateAST(ast, (ast, level) => {
|
iterateAST(ast, (ast, level) => {
|
||||||
while ((scopeLabels.length>0)&&(!scopeLabels[scopeLabels.length-1].startsWith(level))) {
|
while ((scopeLabels.length>0)&&(!level.startsWith(scopeLabels[scopeLabels.length-1]))) {
|
||||||
ctx.scopes.pop();
|
ctx.scopes.pop();
|
||||||
scopeLabels.pop();
|
scopeLabels.pop();
|
||||||
}
|
}
|
||||||
@@ -329,7 +329,7 @@ function genDeclareVariable(ctx, ast) {
|
|||||||
}
|
}
|
||||||
sizes = utils.accSizes(sizes);
|
sizes = utils.accSizes(sizes);
|
||||||
} else {
|
} else {
|
||||||
sizes = null; // If not sizes, the sized are defined in the first assignement.
|
sizes = [1,0];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((!v.sizes)&&(sizes)) {
|
if ((!v.sizes)&&(sizes)) {
|
||||||
|
|||||||
@@ -17,26 +17,18 @@
|
|||||||
along with circom. If not, see <https://www.gnu.org/licenses/>.
|
along with circom. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const fs = require("fs");
|
|
||||||
const path = require("path");
|
|
||||||
const bigInt = require("big-integer");
|
const bigInt = require("big-integer");
|
||||||
const __P__ = new bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
|
const __P__ = new bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
|
||||||
const sONE = 0;
|
const sONE = 0;
|
||||||
const assert = require("assert");
|
|
||||||
const buildC = require("./c_build");
|
const buildC = require("./c_build");
|
||||||
const exec = require("./exec");
|
const constructionPhase = require("./construction_phase");
|
||||||
const lc = require("./lcalgebra");
|
|
||||||
const Ctx = require("./ctx");
|
const Ctx = require("./ctx");
|
||||||
const ZqField = require("./zqfield");
|
const ZqField = require("fflib").ZqField;
|
||||||
const utils = require("./utils");
|
const utils = require("./utils");
|
||||||
const buildR1cs = require("./r1csfile").buildR1cs;
|
const buildR1cs = require("./r1csfile").buildR1cs;
|
||||||
|
|
||||||
module.exports = compile;
|
module.exports = compile;
|
||||||
|
|
||||||
const parser = require("../parser/jaz.js").parser;
|
|
||||||
|
|
||||||
const timeout = ms => new Promise(res => setTimeout(res, ms));
|
|
||||||
|
|
||||||
async function compile(srcFile, options) {
|
async function compile(srcFile, options) {
|
||||||
options.p = options.p || __P__;
|
options.p = options.p || __P__;
|
||||||
if (!options) {
|
if (!options) {
|
||||||
@@ -45,26 +37,15 @@ async function compile(srcFile, options) {
|
|||||||
if (typeof options.reduceConstraints === "undefined") {
|
if (typeof options.reduceConstraints === "undefined") {
|
||||||
options.reduceConstraints = true;
|
options.reduceConstraints = true;
|
||||||
}
|
}
|
||||||
const fullFileName = srcFile;
|
|
||||||
const fullFilePath = path.dirname(fullFileName);
|
|
||||||
|
|
||||||
const src = fs.readFileSync(fullFileName, "utf8");
|
|
||||||
const ast = parser.parse(src);
|
|
||||||
|
|
||||||
assert(ast.type == "BLOCK");
|
|
||||||
|
|
||||||
const ctx = new Ctx();
|
const ctx = new Ctx();
|
||||||
ctx.field = new ZqField(options.p);
|
ctx.field = new ZqField(options.p);
|
||||||
ctx.mainComponent = options.mainComponent || "main";
|
|
||||||
ctx.filePath= fullFilePath;
|
|
||||||
ctx.fileName= fullFileName;
|
|
||||||
ctx.includedFiles = {};
|
|
||||||
ctx.includedFiles[fullFileName] = src.split("\n");
|
|
||||||
|
|
||||||
ctx.verbose= options.verbose || false;
|
ctx.verbose= options.verbose || false;
|
||||||
|
ctx.mainComponent = options.mainComponent || "main";
|
||||||
|
|
||||||
|
|
||||||
exec(ctx, ast);
|
constructionPhase(ctx, srcFile);
|
||||||
|
|
||||||
|
console.log("NConstraints Before: "+ctx.constraints.length);
|
||||||
|
|
||||||
if (ctx.error) {
|
if (ctx.error) {
|
||||||
throw(ctx.error);
|
throw(ctx.error);
|
||||||
@@ -91,6 +72,8 @@ async function compile(srcFile, options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("NConstraints After: "+ctx.constraints.length);
|
||||||
|
|
||||||
generateWitnessNames(ctx);
|
generateWitnessNames(ctx);
|
||||||
|
|
||||||
if (ctx.error) {
|
if (ctx.error) {
|
||||||
@@ -235,8 +218,8 @@ function reduceConstants(ctx) {
|
|||||||
const newConstraints = [];
|
const newConstraints = [];
|
||||||
for (let i=0; i<ctx.constraints.length; i++) {
|
for (let i=0; i<ctx.constraints.length; i++) {
|
||||||
if ((ctx.verbose)&&(i%10000 == 0)) console.log("reducing constants: ", i);
|
if ((ctx.verbose)&&(i%10000 == 0)) console.log("reducing constants: ", i);
|
||||||
const c = lc.canonize(ctx, ctx.constraints[i]);
|
const c = ctx.lc.canonize(ctx, ctx.constraints[i]);
|
||||||
if (!lc.isZero(c)) {
|
if (!ctx.lc.isZero(c)) {
|
||||||
newConstraints.push(c);
|
newConstraints.push(c);
|
||||||
}
|
}
|
||||||
delete ctx.constraints[i];
|
delete ctx.constraints[i];
|
||||||
@@ -265,19 +248,19 @@ function reduceConstrains(ctx) {
|
|||||||
|
|
||||||
// Mov to C if possible.
|
// Mov to C if possible.
|
||||||
if (isConstant(c.a)) {
|
if (isConstant(c.a)) {
|
||||||
const ct = {type: "NUMBER", value: c.a.values[sONE]};
|
const ct = {t: "N", v: c.a.coefs[sONE]};
|
||||||
c.c = lc.add(lc.mul(c.b, ct), c.c);
|
c.c = ctx.lc.add(ctx.lc.mul(c.b, ct), c.c);
|
||||||
c.a = { type: "LINEARCOMBINATION", values: {} };
|
c.a = { t: "LC", coefs: {} };
|
||||||
c.b = { type: "LINEARCOMBINATION", values: {} };
|
c.b = { t: "LC", coefs: {} };
|
||||||
}
|
}
|
||||||
if (isConstant(c.b)) {
|
if (isConstant(c.b)) {
|
||||||
const ct = {type: "NUMBER", value: c.b.values[sONE]};
|
const ct = {t: "N", v: c.b.coefs[sONE]};
|
||||||
c.c = lc.add(lc.mul(c.a, ct), c.c);
|
c.c = ctx.lc.add(ctx.lc.mul(c.a, ct), c.c);
|
||||||
c.a = { type: "LINEARCOMBINATION", values: {} };
|
c.a = { t: "LC", coefs: {} };
|
||||||
c.b = { type: "LINEARCOMBINATION", values: {} };
|
c.b = { t: "LC", coefs: {} };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lc.isZero(c.a) || lc.isZero(c.b)) {
|
if (ctx.lc.isZero(c.a) || ctx.lc.isZero(c.b)) {
|
||||||
const isolatedSignal = getFirstInternalSignal(ctx, c.c);
|
const isolatedSignal = getFirstInternalSignal(ctx, c.c);
|
||||||
if (isolatedSignal) {
|
if (isolatedSignal) {
|
||||||
|
|
||||||
@@ -288,22 +271,22 @@ function reduceConstrains(ctx) {
|
|||||||
|
|
||||||
|
|
||||||
const isolatedSignalEquivalence = {
|
const isolatedSignalEquivalence = {
|
||||||
type: "LINEARCOMBINATION",
|
t: "LC",
|
||||||
values: {}
|
coefs: {}
|
||||||
};
|
};
|
||||||
const invCoef = c.c.values[isolatedSignal].modInv(__P__);
|
const invCoef = c.c.coefs[isolatedSignal].modInv(__P__);
|
||||||
for (const s in c.c.values) {
|
for (const s in c.c.coefs) {
|
||||||
if (s != isolatedSignal) {
|
if (s != isolatedSignal) {
|
||||||
const v = __P__.minus(c.c.values[s]).times(invCoef).mod(__P__);
|
const v = __P__.minus(c.c.coefs[s]).times(invCoef).mod(__P__);
|
||||||
if (!v.isZero()) {
|
if (!v.isZero()) {
|
||||||
isolatedSignalEquivalence.values[s] = v;
|
isolatedSignalEquivalence.coefs[s] = v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let j in lSignal.inConstraints) {
|
for (let j in lSignal.inConstraints) {
|
||||||
if ((j!=i)&&(ctx.constraints[j])) {
|
if ((j!=i)&&(ctx.constraints[j])) {
|
||||||
ctx.constraints[j] = lc.substitute(ctx.constraints[j], isolatedSignal, isolatedSignalEquivalence);
|
ctx.constraints[j] = ctx.lc.substitute(ctx.constraints[j], isolatedSignal, isolatedSignalEquivalence);
|
||||||
linkSignalsConstraint(j);
|
linkSignalsConstraint(j);
|
||||||
if (j<i) {
|
if (j<i) {
|
||||||
nextPossibleConstraints[j] = true;
|
nextPossibleConstraints[j] = true;
|
||||||
@@ -315,7 +298,7 @@ function reduceConstrains(ctx) {
|
|||||||
|
|
||||||
lSignal.c = ctx.stDISCARDED;
|
lSignal.c = ctx.stDISCARDED;
|
||||||
} else {
|
} else {
|
||||||
if (lc.isZero(c.c)) ctx.constraints[i] = null;
|
if (ctx.lc.isZero(c.c)) ctx.constraints[i] = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -341,9 +324,9 @@ function reduceConstrains(ctx) {
|
|||||||
|
|
||||||
function linkSignalsConstraint(cidx) {
|
function linkSignalsConstraint(cidx) {
|
||||||
const ct = ctx.constraints[cidx];
|
const ct = ctx.constraints[cidx];
|
||||||
for (let k in ct.a.values) linkSignal(k, cidx);
|
for (let k in ct.a.coefs) linkSignal(k, cidx);
|
||||||
for (let k in ct.b.values) linkSignal(k, cidx);
|
for (let k in ct.b.coefs) linkSignal(k, cidx);
|
||||||
for (let k in ct.c.values) linkSignal(k, cidx);
|
for (let k in ct.c.coefs) linkSignal(k, cidx);
|
||||||
}
|
}
|
||||||
|
|
||||||
function unindexVariables() {
|
function unindexVariables() {
|
||||||
@@ -378,7 +361,7 @@ function reduceConstrains(ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getFirstInternalSignal(ctx, l) {
|
function getFirstInternalSignal(ctx, l) {
|
||||||
for (let k in l.values) {
|
for (let k in l.coefs) {
|
||||||
const signal = ctx.signals[k];
|
const signal = ctx.signals[k];
|
||||||
if (signal.c == ctx.stINTERNAL) return k;
|
if (signal.c == ctx.stINTERNAL) return k;
|
||||||
}
|
}
|
||||||
@@ -386,10 +369,10 @@ function reduceConstrains(ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isConstant(l) {
|
function isConstant(l) {
|
||||||
for (let k in l.values) {
|
for (let k in l.coefs) {
|
||||||
if ((k != sONE) && (!l.values[k].isZero())) return false;
|
if ((k != sONE) && (!l.coefs[k].isZero())) return false;
|
||||||
}
|
}
|
||||||
if (!l.values[sONE] || l.values[sONE].isZero()) return false;
|
if (!l.coefs[sONE] || l.coefs[sONE].isZero()) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -483,9 +466,9 @@ function buildConstraints(ctx) {
|
|||||||
const res = [];
|
const res = [];
|
||||||
|
|
||||||
function fillLC(dst, src) {
|
function fillLC(dst, src) {
|
||||||
if (src.type != "LINEARCOMBINATION") throw new Error("Constraint is not a LINEARCOMBINATION");
|
if (src.t != "LC") throw new Error("Constraint is not a LINEARCOMBINATION");
|
||||||
for (let s in src.values) {
|
for (let s in src.coefs) {
|
||||||
const v = src.values[s].toString();
|
const v = src.coefs[s].toString();
|
||||||
const id = ctx.signalName2Idx[s];
|
const id = ctx.signalName2Idx[s];
|
||||||
dst[id] = v;
|
dst[id] = v;
|
||||||
}
|
}
|
||||||
@@ -498,7 +481,7 @@ function buildConstraints(ctx) {
|
|||||||
|
|
||||||
fillLC(A, ctx.constraints[i].a);
|
fillLC(A, ctx.constraints[i].a);
|
||||||
fillLC(B, ctx.constraints[i].b);
|
fillLC(B, ctx.constraints[i].b);
|
||||||
fillLC(C, lc.negate(ctx.constraints[i].c));
|
fillLC(C, ctx.lc.negate(ctx.constraints[i].c));
|
||||||
|
|
||||||
res.push([A,B,C]);
|
res.push([A,B,C]);
|
||||||
}
|
}
|
||||||
|
|||||||
1070
src/construction_phase.js
Normal file
1070
src/construction_phase.js
Normal file
File diff suppressed because it is too large
Load Diff
1335
src/exec.js
1335
src/exec.js
File diff suppressed because it is too large
Load Diff
@@ -61,6 +61,12 @@ function iterateAST(ast, fn, _pfx) {
|
|||||||
iterateAST(ast.value, fn, getPfx());
|
iterateAST(ast.value, fn, getPfx());
|
||||||
} else if (ast.type == "ARRAY") {
|
} else if (ast.type == "ARRAY") {
|
||||||
iterate(ast.values);
|
iterate(ast.values);
|
||||||
|
} else if ((ast.type == "TEMPLATEDEF")) {
|
||||||
|
//
|
||||||
|
} else if ((ast.type == "FUNCTIONDEF")) {
|
||||||
|
//
|
||||||
|
} else if ((ast.type == "INCLUDE")) {
|
||||||
|
//
|
||||||
} else {
|
} else {
|
||||||
assert(false, "GEN -> Invalid AST iteration: " + ast.type);
|
assert(false, "GEN -> Invalid AST iteration: " + ast.type);
|
||||||
}
|
}
|
||||||
|
|||||||
750
src/lcalgebra.js
750
src/lcalgebra.js
@@ -18,403 +18,453 @@
|
|||||||
*/
|
*/
|
||||||
/*
|
/*
|
||||||
|
|
||||||
NUMBER: a
|
// Number
|
||||||
|
///////////////
|
||||||
|
N: a
|
||||||
|
|
||||||
{
|
{
|
||||||
type: "NUMBER",
|
t: "N",
|
||||||
value: bigInt(a)
|
v: bigInt(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
LINEARCOMBINATION: c1*s1 + c2*s2 + c3*s3
|
// Signal
|
||||||
|
///////////////
|
||||||
{
|
{
|
||||||
type: "LINEARCOMBINATION",
|
t: "S",
|
||||||
values: {
|
sIdx: sIdx
|
||||||
|
}
|
||||||
|
|
||||||
|
// Linear Convination
|
||||||
|
//////////////////
|
||||||
|
LC: c1*s1 + c2*s2 + c3*s3
|
||||||
|
{
|
||||||
|
t: "LC",
|
||||||
|
coefs: {
|
||||||
s1: bigInt(c1),
|
s1: bigInt(c1),
|
||||||
s2: bigInt(c2),
|
s2: bigInt(c2),
|
||||||
s3: bigInt(c3)
|
s3: bigInt(c3)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Quadratic Expression
|
||||||
QEQ: a*b + c WHERE a,b,c are LINEARCOMBINATION
|
//////////////////
|
||||||
|
QEX: a*b + c WHERE a,b,c are LC
|
||||||
{
|
{
|
||||||
type: "QEQ"
|
t: "QEX"
|
||||||
a: { type: LINEARCOMBINATION, values: {...} },
|
a: { t: "LC", coefs: {...} },
|
||||||
b: { type: LINEARCOMBINATION, values: {...} },
|
b: { t: "LC", coefs: {...} },
|
||||||
c: { type: LINEARCOMBINATION, values: {...} }
|
c: { t: "LC", coefs: {...} }
|
||||||
|
}
|
||||||
|
|
||||||
|
NQ: Non quadratic expression
|
||||||
|
{
|
||||||
|
t: "NQ"
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
+ NUM LC QEQ
|
+ N LC QEX NQ
|
||||||
NUM NUM LC QEQ
|
N N LC QEX NQ
|
||||||
LC LC LC QEQ
|
LC LC LC QEX NQ
|
||||||
QEQ QEQ QEQ ERR
|
QEX QEX QEX NQ NQ
|
||||||
|
NQ NQ NQ NQ NQ
|
||||||
|
|
||||||
* NUM LC QEQ
|
* N LC QEX NQ
|
||||||
NUM NUM LC QEQ
|
N N LC QEX NQ
|
||||||
LC LC QEQ ERR
|
LC LC QEX NQ NQ
|
||||||
QEQ QEQ ERR ERR
|
QEX QEX NQ NQ NQ
|
||||||
|
NQ NQ NQ NQ NQ
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const bigInt = require("big-integer");
|
const bigInt = require("big-integer");
|
||||||
const __P__ = new bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
|
const utils = require("./utils");
|
||||||
const sONE = 0;
|
const sONE = 0;
|
||||||
const utils = require("./utils.js");
|
|
||||||
|
|
||||||
exports.add = add;
|
class LCAlgebra {
|
||||||
exports.mul = mul;
|
constructor (aField) {
|
||||||
exports.evaluate = evaluate;
|
const self = this;
|
||||||
exports.negate = negate;
|
this.field= aField;
|
||||||
exports.sub = sub;
|
[
|
||||||
exports.toQEQ = toQEQ;
|
["lt",2],
|
||||||
exports.isZero = isZero;
|
["leq",2],
|
||||||
exports.toString = toString;
|
["eq",2],
|
||||||
exports.canonize = canonize;
|
["neq",2],
|
||||||
exports.substitute = substitute;
|
["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]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function signal2lc(a) {
|
_genNQOp(op, nOps) {
|
||||||
let lc;
|
const self=this;
|
||||||
if (a.type == "SIGNAL") {
|
self[op] = function() {
|
||||||
lc = {
|
const operands = [];
|
||||||
type: "LINEARCOMBINATION",
|
for (let i=0; i<nOps; i++) {
|
||||||
values: {}
|
if (typeof(arguments[i]) !== "object") throw new Error("Invalid operand type");
|
||||||
|
if (arguments[i].t !== "N") return {t: "NQ"};
|
||||||
|
operands.push(arguments[i].v);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
t: "N",
|
||||||
|
v: self.field[op](...operands)
|
||||||
};
|
};
|
||||||
lc.values[a.sIdx] = bigInt(1);
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
_signal2lc(a) {
|
||||||
|
if (a.t == "S") {
|
||||||
|
const lc = {
|
||||||
|
t: "LC",
|
||||||
|
coefs: {}
|
||||||
|
};
|
||||||
|
lc.coefs[a.sIdx] = bigInt(1);
|
||||||
return lc;
|
return lc;
|
||||||
} else {
|
} else {
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function clone(a) {
|
|
||||||
|
_clone(a) {
|
||||||
const res = {};
|
const res = {};
|
||||||
res.type = a.type;
|
res.t = a.t;
|
||||||
if (a.type == "NUMBER") {
|
if (a.t == "N") {
|
||||||
res.value = bigInt(a.value);
|
res.v = a.v;
|
||||||
} else if (a.type == "LINEARCOMBINATION") {
|
} else if (a.t == "S") {
|
||||||
res.values = {};
|
res.sIdx = a.sIdx;
|
||||||
for (let k in a.values) {
|
} else if (a.t == "LC") {
|
||||||
res.values[k] = bigInt(a.values[k]);
|
res.coefs = {};
|
||||||
|
for (let k in a.coefs) {
|
||||||
|
res.coefs[k] = a.coefs[k];
|
||||||
}
|
}
|
||||||
} else if (a.type == "QEQ") {
|
} else if (a.t == "QEX") {
|
||||||
res.a = clone(a.a);
|
res.a = this._clone(a.a);
|
||||||
res.b = clone(a.b);
|
res.b = this._clone(a.b);
|
||||||
res.c = clone(a.c);
|
res.c = this._clone(a.c);
|
||||||
} else if (a.type == "ERROR") {
|
|
||||||
res.errStr = a.errStr;
|
|
||||||
} else {
|
|
||||||
res.type = "ERROR";
|
|
||||||
res.errStr = "Invilid type when clonning: "+a.type;
|
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
function add(_a, _b) {
|
add(_a,_b) {
|
||||||
const a = signal2lc(_a);
|
const self = this;
|
||||||
const b = signal2lc(_b);
|
const a = self._signal2lc(_a);
|
||||||
if (a.type == "ERROR") return a;
|
const b = self._signal2lc(_b);
|
||||||
if (b.type == "ERROR") return b;
|
if (a.t == "NQ") return a;
|
||||||
if (a.type == "NUMBER") {
|
if (b.t == "NQ") return b;
|
||||||
if (b.type == "NUMBER") {
|
if (a.t == "N") {
|
||||||
return addNumNum(a,b);
|
if (b.t == "N") {
|
||||||
} else if (b.type=="LINEARCOMBINATION") {
|
return add_N_N(a,b);
|
||||||
return addLCNum(b,a);
|
} else if (b.t=="LC") {
|
||||||
} else if (b.type=="QEQ") {
|
return add_LC_N(b,a);
|
||||||
return addQEQNum(b,a);
|
} else if (b.t=="QEX") {
|
||||||
|
return add_QEX_N(b,a);
|
||||||
} else {
|
} else {
|
||||||
return { type: "ERROR", errStr: "LC Add Invalid Type 2: "+b.type };
|
return { type: "NQ" };
|
||||||
}
|
}
|
||||||
} else if (a.type=="LINEARCOMBINATION") {
|
} else if (a.t=="LC") {
|
||||||
if (b.type == "NUMBER") {
|
if (b.t == "N") {
|
||||||
return addLCNum(a,b);
|
return add_LC_N(a,b);
|
||||||
} else if (b.type=="LINEARCOMBINATION") {
|
} else if (b.t=="LC") {
|
||||||
return addLCLC(a,b);
|
return add_LC_LC(a,b);
|
||||||
} else if (b.type=="QEQ") {
|
} else if (b.t=="QEX") {
|
||||||
return addQEQLC(b,a);
|
return add_QEX_LC(b,a);
|
||||||
} else {
|
} else {
|
||||||
return { type: "ERROR", errStr: "LC Add Invalid Type 2: "+b.type };
|
return { t: "NQ" };
|
||||||
}
|
}
|
||||||
} else if (a.type=="QEQ") {
|
} else if (a.t=="QEX") {
|
||||||
if (b.type == "NUMBER") {
|
if (b.t == "N") {
|
||||||
return addQEQNum(a,b);
|
return add_QEX_N(a,b);
|
||||||
} else if (b.type=="LINEARCOMBINATION") {
|
} else if (b.t=="LC") {
|
||||||
return addQEQLC(a,b);
|
return add_QEX_LC(a,b);
|
||||||
} else if (b.type=="QEQ") {
|
} else if (b.t=="QEX") {
|
||||||
return { type: "ERROR", errStr: "QEQ + QEQ" };
|
return { t: "NQ" };
|
||||||
} else {
|
} else {
|
||||||
return { type: "ERROR", errStr: "LC Add Invalid Type 2: "+b.type };
|
return { t: "NQ" };
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return { type: "ERROR", errStr: "LC Add Invalid Type 1: "+a.type };
|
return { t: "NQ" };
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function addNumNum(a,b) {
|
function add_N_N(a,b) {
|
||||||
if (!a.value || !b.value) return { type: "NUMBER" };
|
|
||||||
return {
|
return {
|
||||||
type: "NUMBER",
|
t: "N",
|
||||||
value: a.value.add(b.value).mod(__P__)
|
v: self.field.add(a.v, b.v)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function addLCNum(a,b) {
|
function add_LC_N(a,b) {
|
||||||
let res = clone(a);
|
let res = self._clone(a);
|
||||||
if (!b.value) {
|
if (b.v.isZero()) return res;
|
||||||
return { type: "ERROR", errStr: "LinearCombination + undefined" };
|
if (!utils.isDefined(res.coefs[sONE])) {
|
||||||
}
|
res.coefs[sONE]= b.v;
|
||||||
if (b.value.isZero()) return res;
|
|
||||||
if (!res.values[sONE]) {
|
|
||||||
res.values[sONE]=bigInt(b.value);
|
|
||||||
} else {
|
} else {
|
||||||
res.values[sONE]= res.values[sONE].add(b.value).mod(__P__);
|
res.coefs[sONE]= self.field.add(res.coefs[sONE], b.v);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
function addLCLC(a,b) {
|
function add_LC_LC(a,b) {
|
||||||
let res = clone(a);
|
let res = self._clone(a);
|
||||||
for (let k in b.values) {
|
for (let k in b.coefs) {
|
||||||
if (!res.values[k]) {
|
if (!utils.isDefined(res.coefs[k])) {
|
||||||
res.values[k]=bigInt(b.values[k]);
|
res.coefs[k]=b.coefs[k];
|
||||||
} else {
|
} else {
|
||||||
res.values[k]= res.values[k].add(b.values[k]).mod(__P__);
|
res.coefs[k]= self.field.add(res.coefs[k], b.coefs[k]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
function addQEQNum(a,b) {
|
function add_QEX_N(a,b) {
|
||||||
let res = clone(a);
|
let res = self._clone(a);
|
||||||
res.c = addLCNum(res.c, b);
|
res.c = add_LC_N(res.c, b);
|
||||||
if (res.c.type == "ERROR") return res.c;
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
function addQEQLC(a,b) {
|
function add_QEX_LC(a,b) {
|
||||||
let res = clone(a);
|
let res = self._clone(a);
|
||||||
res.c = addLCLC(res.c, b);
|
res.c = add_LC_LC(res.c, b);
|
||||||
if (res.c.type == "ERROR") return res.c;
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
function mul(_a, _b) {
|
|
||||||
const a = signal2lc(_a);
|
|
||||||
const b = signal2lc(_b);
|
|
||||||
if (a.type == "ERROR") return a;
|
|
||||||
if (b.type == "ERROR") return b;
|
|
||||||
if (a.type == "NUMBER") {
|
|
||||||
if (b.type == "NUMBER") {
|
|
||||||
return mulNumNum(a,b);
|
|
||||||
} else if (b.type=="LINEARCOMBINATION") {
|
|
||||||
return mulLCNum(b,a);
|
|
||||||
} else if (b.type=="QEQ") {
|
|
||||||
return mulQEQNum(b,a);
|
|
||||||
} else {
|
|
||||||
return { type: "ERROR", errStr: "LC Mul Invalid Type 2: "+b.type };
|
|
||||||
}
|
|
||||||
} else if (a.type=="LINEARCOMBINATION") {
|
|
||||||
if (b.type == "NUMBER") {
|
|
||||||
return mulLCNum(a,b);
|
|
||||||
} else if (b.type=="LINEARCOMBINATION") {
|
|
||||||
return mulLCLC(a,b);
|
|
||||||
} else if (b.type=="QEQ") {
|
|
||||||
return { type: "ERROR", errStr: "LC * QEQ" };
|
|
||||||
} else {
|
|
||||||
return { type: "ERROR", errStr: "LC Mul Invalid Type 2: "+b.type };
|
|
||||||
}
|
|
||||||
} else if (a.type=="QEQ") {
|
|
||||||
if (b.type == "NUMBER") {
|
|
||||||
return mulQEQNum(a,b);
|
|
||||||
} else if (b.type=="LINEARCOMBINATION") {
|
|
||||||
return { type: "ERROR", errStr: "QEC * LC" };
|
|
||||||
} else if (b.type=="QEQ") {
|
|
||||||
return { type: "ERROR", errStr: "QEQ * QEQ" };
|
|
||||||
} else {
|
|
||||||
return { type: "ERROR", errStr: "LC Mul Invalid Type 2: "+b.type };
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return { type: "ERROR", errStr: "LC Mul Invalid Type 1: "+a.type };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mul(_a,_b) {
|
||||||
|
const self = this;
|
||||||
|
const a = self._signal2lc(_a);
|
||||||
|
const b = self._signal2lc(_b);
|
||||||
|
if (a.t == "NQ") return a;
|
||||||
|
if (b.t == "NQ") return b;
|
||||||
|
if (a.t == "N") {
|
||||||
|
if (b.t == "N") {
|
||||||
|
return mul_N_N(a,b);
|
||||||
|
} else if (b.t=="LC") {
|
||||||
|
return mul_LC_N(b,a);
|
||||||
|
} else if (b.t=="QEX") {
|
||||||
|
return mul_QEX_N(b,a);
|
||||||
|
} else {
|
||||||
|
return { t: "NQ"};
|
||||||
|
}
|
||||||
|
} else if (a.t=="LC") {
|
||||||
|
if (b.t == "N") {
|
||||||
|
return mul_LC_N(a,b);
|
||||||
|
} else if (b.t=="LC") {
|
||||||
|
return mul_LC_LC(a,b);
|
||||||
|
} else if (b.t=="QEX") {
|
||||||
|
return { t: "NQ" };
|
||||||
|
} else {
|
||||||
|
return { t: "NQ" };
|
||||||
|
}
|
||||||
|
} else if (a.t=="QEX") {
|
||||||
|
if (b.t == "N") {
|
||||||
|
return mul_QEX_N(a,b);
|
||||||
|
} else if (b.t=="LC") {
|
||||||
|
return { t: "NQ" };
|
||||||
|
} else if (b.t=="QEX") {
|
||||||
|
return { t: "NQ" };
|
||||||
|
} else {
|
||||||
|
return { t: "NQ" };
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return { t: "NQ" };
|
||||||
|
}
|
||||||
|
|
||||||
function mulNumNum(a,b) {
|
function mul_N_N(a,b) {
|
||||||
if (!a.value || !b.value) return { type: "NUMBER" };
|
|
||||||
return {
|
return {
|
||||||
type: "NUMBER",
|
t: "N",
|
||||||
value: a.value.times(b.value).mod(__P__)
|
v: self.field.mul(a.v, b.v)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function mulLCNum(a,b) {
|
function mul_LC_N(a,b) {
|
||||||
let res = clone(a);
|
let res = self._clone(a);
|
||||||
if (!b.value) {
|
for (let k in res.coefs) {
|
||||||
return {type: "ERROR", errStr: "LinearCombination * undefined"};
|
res.coefs[k] = self.field.mul(res.coefs[k], b.v);
|
||||||
}
|
|
||||||
for (let k in res.values) {
|
|
||||||
res.values[k] = res.values[k].times(b.value).mod(__P__);
|
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
function mulLCLC(a,b) {
|
function mul_LC_LC(a,b) {
|
||||||
return {
|
return {
|
||||||
type: "QEQ",
|
t: "QEX",
|
||||||
a: clone(a),
|
a: self._clone(a),
|
||||||
b: clone(b),
|
b: self._clone(b),
|
||||||
c: { type: "LINEARCOMBINATION", values: {}}
|
c: { t: "LC", coefs: {}}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function mulQEQNum(a,b) {
|
function mul_QEX_N(a,b) {
|
||||||
let res = {
|
return {
|
||||||
type: "QEQ",
|
t: "QEX",
|
||||||
a: mulLCNum(a.a, b),
|
a: mul_LC_N(a.a, b),
|
||||||
b: clone(a.b),
|
b: self._clone(a.b),
|
||||||
c: mulLCNum(a.c, b)
|
c: mul_LC_N(a.c, b)
|
||||||
};
|
};
|
||||||
if (res.a.type == "ERROR") return res.a;
|
}
|
||||||
if (res.c.type == "ERROR") return res.a;
|
}
|
||||||
|
|
||||||
|
neg(_a) {
|
||||||
|
const a = this._signal2lc(_a);
|
||||||
|
let res = this._clone(a);
|
||||||
|
if (res.t == "N") {
|
||||||
|
res.v = this.field.neg(a.v);
|
||||||
|
} else if (res.t == "LC") {
|
||||||
|
for (let k in res.coefs) {
|
||||||
|
res.coefs[k] = this.field.neg(res.coefs[k]);
|
||||||
|
}
|
||||||
|
} else if (res.t == "QEX") {
|
||||||
|
res.a = this.neg(res.a);
|
||||||
|
res.c = this.neg(res.c);
|
||||||
|
} else {
|
||||||
|
res = {t: "NQ"};
|
||||||
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSignalValue(ctx, sIdx) {
|
sub(a, b) {
|
||||||
const s = ctx.signals[sIdx];
|
return this.add(a, this.neg(b));
|
||||||
if (s.e >= 0) {
|
}
|
||||||
return getSignalValue(ctx, s.e);
|
|
||||||
|
div(a, b) {
|
||||||
|
if (b.t == "N") {
|
||||||
|
if (b.v.isZero()) throw new Error("Division by zero");
|
||||||
|
const inv = {
|
||||||
|
t: "N",
|
||||||
|
v: this.field.inv(b.v)
|
||||||
|
};
|
||||||
|
return this.mul(a, inv);
|
||||||
} else {
|
} else {
|
||||||
|
return {t: "NQ"};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pow(a, b) {
|
||||||
|
if (b.t == "N") {
|
||||||
|
if (b.v.isZero()) {
|
||||||
|
if (this.isZero(a)) {
|
||||||
|
throw new Error("Zero to the Zero");
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
t: "N",
|
||||||
|
v: this.field.one
|
||||||
|
};
|
||||||
|
} else if (b.v.eq(this.field.one)) {
|
||||||
|
return a;
|
||||||
|
} else if (b.v.eq(bigInt(2))) {
|
||||||
|
return this.mul(a,a);
|
||||||
|
} else {
|
||||||
|
if (a.t=="N") {
|
||||||
|
return {
|
||||||
|
t: "N",
|
||||||
|
v: this.field.pow(a.v, b.v)
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {t: "NQ"};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {t: "NQ"};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
substitute(where, signal, equivalence) {
|
||||||
|
if (equivalence.t != "LC") throw new Error("Equivalence must be a Linear Combination");
|
||||||
|
if (where.t == "LC") {
|
||||||
|
if (!utils.isDefined(where.coefs[signal]) || where.coefs[signal].isZero()) return where;
|
||||||
|
const res=this._clone(where);
|
||||||
|
const coef = res.coefs[signal];
|
||||||
|
for (let k in equivalence.coefs) {
|
||||||
|
if (k != signal) {
|
||||||
|
const v = this.field.mul( coef, equivalence.coefs[k] );
|
||||||
|
if (!utils.isDefined(res.coefs[k])) {
|
||||||
|
res.coefs[k]=v;
|
||||||
|
} else {
|
||||||
|
res.coefs[k]= this.field.add(res.coefs[k],v);
|
||||||
|
}
|
||||||
|
if (res.coefs[k].isZero()) delete res.coefs[k];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete res.coefs[signal];
|
||||||
|
return res;
|
||||||
|
} else if (where.t == "QEX") {
|
||||||
const res = {
|
const res = {
|
||||||
type: "NUMBER"
|
t: "QEX",
|
||||||
|
a: this.substitute(where.a, signal, equivalence),
|
||||||
|
b: this.substitute(where.b, signal, equivalence),
|
||||||
|
c: this.substitute(where.c, signal, equivalence)
|
||||||
};
|
};
|
||||||
if (s.v) {
|
|
||||||
res.value = s.v;
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function evaluate(ctx, n) {
|
|
||||||
if (n.type == "NUMBER") {
|
|
||||||
return n;
|
|
||||||
} else if (n.type == "SIGNAL") {
|
|
||||||
return getSignalValue(ctx, n.sIdx);
|
|
||||||
} else if (n.type == "LINEARCOMBINATION") {
|
|
||||||
const v= {
|
|
||||||
type: "NUMBER",
|
|
||||||
value: bigInt(0)
|
|
||||||
};
|
|
||||||
for (let k in n.values) {
|
|
||||||
const s = getSignalValue(ctx, k);
|
|
||||||
if (s.type != "NUMBER") return {type: "ERROR", errStr: "Invalid signal in linear Combination: " + k};
|
|
||||||
if (!s.value) return { type: "NUMBER" };
|
|
||||||
v.value = v.value.add( n.values[k].times(s.value)).mod(__P__);
|
|
||||||
}
|
|
||||||
return v;
|
|
||||||
} else if (n.type == "QEQ") {
|
|
||||||
const a = evaluate(ctx, n.a);
|
|
||||||
if (a.type == "ERROR") return a;
|
|
||||||
if (!a.value) return { type: "NUMBER" };
|
|
||||||
const b = evaluate(ctx, n.b);
|
|
||||||
if (b.type == "ERROR") return b;
|
|
||||||
if (!b.value) return { type: "NUMBER" };
|
|
||||||
const c = evaluate(ctx, n.c);
|
|
||||||
if (c.type == "ERROR") return c;
|
|
||||||
if (!c.value) return { type: "NUMBER" };
|
|
||||||
|
|
||||||
return {
|
|
||||||
type: "NUMBER",
|
|
||||||
value: (a.value.times(b.value).add(c.value)).mod(__P__)
|
|
||||||
};
|
|
||||||
} else if (n.type == "ERROR") {
|
|
||||||
return n;
|
|
||||||
} else {
|
|
||||||
return {type: "ERROR", errStr: "Invalid type in evaluate: "+n.type};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function negate(_a) {
|
|
||||||
const a = signal2lc(_a);
|
|
||||||
let res = clone(a);
|
|
||||||
if (res.type == "NUMBER") {
|
|
||||||
res.value = __P__.minus(a.value).mod(__P__);
|
|
||||||
} else if (res.type == "LINEARCOMBINATION") {
|
|
||||||
for (let k in res.values) {
|
|
||||||
res.values[k] = __P__.minus(res.values[k]).mod(__P__);
|
|
||||||
}
|
|
||||||
} else if (res.type == "QEQ") {
|
|
||||||
res.a = negate(res.a);
|
|
||||||
res.c = negate(res.c);
|
|
||||||
} else if (res.type == "ERROR") {
|
|
||||||
return res;
|
return res;
|
||||||
} else {
|
} else {
|
||||||
res = {type: "ERROR", errStr: "LC Negate invalid Type: "+res.type};
|
return where;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toQEX(a) {
|
||||||
|
if (a.t == "N") {
|
||||||
|
const res = {
|
||||||
|
t: "QEX",
|
||||||
|
a: {t: "LC", coefs: {}},
|
||||||
|
b: {t: "LC", coefs: {}},
|
||||||
|
c: {t: "LC", coefs: {}}
|
||||||
|
};
|
||||||
|
res.c[sONE] = a.v;
|
||||||
return res;
|
return res;
|
||||||
}
|
} else if (a.t == "LC") {
|
||||||
|
|
||||||
function sub(a, b) {
|
|
||||||
return add(a, negate(b));
|
|
||||||
}
|
|
||||||
|
|
||||||
function toQEQ(a) {
|
|
||||||
if (a.type == "NUMBER") {
|
|
||||||
return {
|
return {
|
||||||
type: "QEQ",
|
t: "QEX",
|
||||||
a: {type: "LINEARCOMBINATION", values: {}},
|
a: {t: "LC", coefs: {}},
|
||||||
b: {type: "LINEARCOMBINATION", values: {}},
|
b: {t: "LC", coefs: {}},
|
||||||
c: {type: "LINEARCOMBINATION", values: {sONE: bigInt(a.value)}}
|
c: this._clone(a)
|
||||||
};
|
};
|
||||||
} else if (a.type == "LINEARCOMBINATION") {
|
} else if (a.t == "QEX") {
|
||||||
return {
|
return this._clone(a);
|
||||||
type: "QEQ",
|
|
||||||
a: {type: "LINEARCOMBINATION", values: {}},
|
|
||||||
b: {type: "LINEARCOMBINATION", values: {}},
|
|
||||||
c: clone(a)
|
|
||||||
};
|
|
||||||
} else if (a.type == "QEQ") {
|
|
||||||
return clone(a);
|
|
||||||
} else if (a.type == "ERROR") {
|
|
||||||
return clone(a);
|
|
||||||
} else {
|
} else {
|
||||||
return {type: "ERROR", errStr: "toQEQ invalid Type: "+a.type};
|
throw new Error(`Type ${a.t} can not be converted to QEX`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function isZero(a) {
|
isZero(a) {
|
||||||
if (a.type == "NUMBER") {
|
if (a.t == "N") {
|
||||||
return a.value.isZero();
|
return a.v.isZero();
|
||||||
} else if (a.type == "LINEARCOMBINATION") {
|
} else if (a.t == "LC") {
|
||||||
for (let k in a.values) {
|
for (let k in a.coefs) {
|
||||||
if (!a.values[k].isZero()) return false;
|
if (!a.coefs[k].isZero()) return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else if (a.type == "QEQ") {
|
} else if (a.t == "QEX") {
|
||||||
return (isZero(a.a) || isZero(a.b)) && isZero(a.c);
|
return (this.isZero(a.a) || this.isZero(a.b)) && this.isZero(a.c);
|
||||||
} else if (a.type == "ERROR") {
|
|
||||||
return false;
|
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function toString(a, ctx) {
|
toString(a, ctx) {
|
||||||
if (a.type == "NUMBER") {
|
if (a.t == "N") {
|
||||||
return a.value.toString();
|
return a.v.toString();
|
||||||
} else if (a.type == "LINEARCOMBINATION") {
|
} else if (a.t == "LC") {
|
||||||
let S="";
|
let S="";
|
||||||
for (let k in a.values) {
|
for (let k in a.coefs) {
|
||||||
if (!a.values[k].isZero()) {
|
if (!a.coefs[k].isZero()) {
|
||||||
let c;
|
let c;
|
||||||
if (a.values[k].greater(__P__.divide(2))) {
|
if (a.coefs[k].greater(this.field.p.divide(2))) {
|
||||||
S = S + "-";
|
S = S + "-";
|
||||||
c = __P__.minus(a.values[k]);
|
c = this.field.p.minus(a.coefs[k]);
|
||||||
} else {
|
} else {
|
||||||
if (S!="") S=S+" + ";
|
if (S!="") S=S+" + ";
|
||||||
c = a.values[k];
|
c = a.coefs[k];
|
||||||
}
|
}
|
||||||
if (!c.equals(1)) {
|
if (!c.equals(bigInt.one)) {
|
||||||
S = S + c.toString() + "*";
|
S = S + c.toString() + "*";
|
||||||
}
|
}
|
||||||
let sIdx = k;
|
let sIdx = k;
|
||||||
@@ -425,84 +475,98 @@ function toString(a, ctx) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (S=="") return "0"; else return S;
|
if (S=="") return "0"; else return S;
|
||||||
} else if (a.type == "QEQ") {
|
} else if (a.t == "QEX") {
|
||||||
return "( "+toString(a.a, ctx)+" ) * ( "+toString(a.b, ctx)+" ) + " + toString(a.c, ctx);
|
return "( "+
|
||||||
} else if (a.type == "ERROR") {
|
this.toString(a.a, ctx)+" ) * ( "+
|
||||||
return "ERROR: "+a.errStr;
|
this.toString(a.b, ctx)+" ) + " +
|
||||||
|
this.toString(a.c, ctx);
|
||||||
} else {
|
} else {
|
||||||
return "INVALID";
|
return "NQ";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function canonize(ctx, a) {
|
evaluate(ctx, n) {
|
||||||
if (a.type == "LINEARCOMBINATION") {
|
if (n.t == "N") {
|
||||||
const res = clone(a);
|
return n.v;
|
||||||
for (let k in a.values) {
|
} 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;
|
let s = k;
|
||||||
while (ctx.signals[s].e>=0) s= ctx.signals[s].e;
|
while (ctx.signals[s].e>=0) s= ctx.signals[s].e;
|
||||||
if (utils.isDefined(ctx.signals[s].v)&&(k != sONE)) {
|
if (utils.isDefined(ctx.signals[s].v)&&(k != sONE)) {
|
||||||
const v = res.values[k].times(ctx.signals[s].v).mod(__P__);
|
const v = this.field.mul(res.coefs[k], ctx.signals[s].v);
|
||||||
if (!res.values[sONE]) {
|
if (!utils.isDefined(res.coefs[sONE])) {
|
||||||
res.values[sONE]=v;
|
res.coefs[sONE]=v;
|
||||||
} else {
|
} else {
|
||||||
res.values[sONE]= res.values[sONE].add(v).mod(__P__);
|
res.coefs[sONE]= this.field.add(res.coefs[sONE], v);
|
||||||
}
|
}
|
||||||
delete res.values[k];
|
delete res.coefs[k];
|
||||||
} else if (s != k) {
|
} else if (s != k) {
|
||||||
if (!res.values[s]) {
|
if (!utils.isDefined(res.coefs[s])) {
|
||||||
res.values[s]=bigInt(res.values[k]);
|
res.coefs[s]=res.coefs[k];
|
||||||
} else {
|
} else {
|
||||||
res.values[s]= res.values[s].add(res.values[k]).mod(__P__);
|
res.coefs[s]= this.field.add(res.coefs[s], res.coefs[k]);
|
||||||
}
|
}
|
||||||
delete res.values[k];
|
delete res.coefs[k];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (let k in res.values) {
|
for (let k in res.coefs) {
|
||||||
if (res.values[k].isZero()) delete res.values[k];
|
if (res.coefs[k].isZero()) delete res.coefs[k];
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
} else if (a.type == "QEQ") {
|
} else if (a.t == "QEX") {
|
||||||
const res = {
|
const res = {
|
||||||
type: "QEQ",
|
t: "QEX",
|
||||||
a: canonize(ctx, a.a),
|
a: this.canonize(ctx, a.a),
|
||||||
b: canonize(ctx, a.b),
|
b: this.canonize(ctx, a.b),
|
||||||
c: canonize(ctx, a.c)
|
c: this.canonize(ctx, a.c)
|
||||||
};
|
};
|
||||||
return res;
|
return res;
|
||||||
} else {
|
} else {
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function substitute(where, signal, equivalence) {
|
|
||||||
if (equivalence.type != "LINEARCOMBINATION") throw new Error("Equivalence must be a Linear Combination");
|
|
||||||
if (where.type == "LINEARCOMBINATION") {
|
|
||||||
if (!where.values[signal] || where.values[signal].isZero()) return where;
|
|
||||||
const res=clone(where);
|
|
||||||
const coef = res.values[signal];
|
|
||||||
for (let k in equivalence.values) {
|
|
||||||
if (k != signal) {
|
|
||||||
const v = coef.times(equivalence.values[k]).mod(__P__);
|
|
||||||
if (!res.values[k]) {
|
|
||||||
res.values[k]=v;
|
|
||||||
} else {
|
|
||||||
res.values[k]= res.values[k].add(v).mod(__P__);
|
|
||||||
}
|
|
||||||
if (res.values[k].isZero()) delete res.values[k];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
delete res.values[signal];
|
|
||||||
return res;
|
|
||||||
} else if (where.type == "QEQ") {
|
|
||||||
const res = {
|
|
||||||
type: "QEQ",
|
|
||||||
a: substitute(where.a, signal, equivalence),
|
|
||||||
b: substitute(where.b, signal, equivalence),
|
|
||||||
c: substitute(where.c, signal, equivalence)
|
|
||||||
};
|
|
||||||
return res;
|
|
||||||
} else {
|
|
||||||
return where;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module.exports = LCAlgebra;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
|
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
const assert = require("assert");
|
const assert = require("assert");
|
||||||
const lc = require("./lcalgebra");
|
|
||||||
const bigInt = require("big-integer");
|
const bigInt = require("big-integer");
|
||||||
|
|
||||||
module.exports.buildR1cs = buildR1cs;
|
module.exports.buildR1cs = buildR1cs;
|
||||||
@@ -262,19 +261,19 @@ async function buildR1cs(ctx, fileName) {
|
|||||||
async function writeConstraint(c) {
|
async function writeConstraint(c) {
|
||||||
await writeLC(c.a);
|
await writeLC(c.a);
|
||||||
await writeLC(c.b);
|
await writeLC(c.b);
|
||||||
await writeLC(lc.negate(c.c));
|
await writeLC(ctx.lc.neg(c.c));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function writeLC(lc) {
|
async function writeLC(lc) {
|
||||||
const idxs = Object.keys(lc.values);
|
const idxs = Object.keys(lc.coefs);
|
||||||
await writeU32(idxs.length);
|
await writeU32(idxs.length);
|
||||||
for (let s in lc.values) {
|
for (let s in lc.coefs) {
|
||||||
let lSignal = ctx.signals[s];
|
let lSignal = ctx.signals[s];
|
||||||
|
|
||||||
while (lSignal.e >=0 ) lSignal = ctx.signals[lSignal.e];
|
while (lSignal.e >=0 ) lSignal = ctx.signals[lSignal.e];
|
||||||
|
|
||||||
await writeU32(lSignal.id);
|
await writeU32(lSignal.id);
|
||||||
await writeBigInt(lc.values[s]);
|
await writeBigInt(lc.coefs[s]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -124,3 +124,4 @@ function accSizes2Str(sizes) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
118
src/zqfield.js
118
src/zqfield.js
@@ -1,118 +0,0 @@
|
|||||||
const bigInt = require("big-integer");
|
|
||||||
const assert = require("assert");
|
|
||||||
|
|
||||||
module.exports = class ZqField {
|
|
||||||
constructor(p) {
|
|
||||||
this.one = bigInt.one;
|
|
||||||
this.zero = bigInt.zero;
|
|
||||||
this.p = p;
|
|
||||||
this.bitLength = p.bitLength();
|
|
||||||
this.mask = bigInt.one.shiftLeft(this.bitLength - 1).minus(bigInt.one);
|
|
||||||
}
|
|
||||||
|
|
||||||
add(a, b) {
|
|
||||||
let res = a.add(b);
|
|
||||||
if (res.geq(this.p)) {
|
|
||||||
res = res.minus(this.p);
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub(a, b) {
|
|
||||||
if (a.geq(b)) {
|
|
||||||
return a.minus(b);
|
|
||||||
} else {
|
|
||||||
return this.p.minus(b.minus(a));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
neg(a) {
|
|
||||||
if (a.isZero()) return a;
|
|
||||||
return this.p.minus(a);
|
|
||||||
}
|
|
||||||
|
|
||||||
mul(a, b) {
|
|
||||||
return a.times(b).mod(this.p);
|
|
||||||
}
|
|
||||||
|
|
||||||
lt(a, b) {
|
|
||||||
return a.lt(b) ? bigInt(1) : bigInt(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
eq(a, b) {
|
|
||||||
return a.eq(b) ? bigInt(1) : bigInt(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
gt(a, b) {
|
|
||||||
return a.gt(b) ? bigInt(1) : bigInt(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
leq(a, b) {
|
|
||||||
return a.leq(b) ? bigInt(1) : bigInt(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
geq(a, b) {
|
|
||||||
return a.geq(b) ? bigInt(1) : bigInt(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
neq(a, b) {
|
|
||||||
return a.neq(b) ? bigInt(1) : bigInt(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
div(a, b) {
|
|
||||||
assert(!b.isZero(), "Division by zero");
|
|
||||||
return a.times(b.modInv(this.p)).mod(this.p);
|
|
||||||
}
|
|
||||||
|
|
||||||
idiv(a, b) {
|
|
||||||
assert(!b.isZero(), "Division by zero");
|
|
||||||
return a.divide(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
mod(a, b) {
|
|
||||||
return a.mod(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
pow(a, b) {
|
|
||||||
return a.modPow(b, this.p);
|
|
||||||
}
|
|
||||||
|
|
||||||
band(a, b) {
|
|
||||||
return a.and(b).and(this.mask);
|
|
||||||
}
|
|
||||||
|
|
||||||
bor(a, b) {
|
|
||||||
return a.or(b).and(this.mask);
|
|
||||||
}
|
|
||||||
|
|
||||||
bxor(a, b) {
|
|
||||||
return a.xor(b).and(this.mask);
|
|
||||||
}
|
|
||||||
|
|
||||||
bnot(a) {
|
|
||||||
return a.xor(this.mask).and(this.mask);
|
|
||||||
}
|
|
||||||
|
|
||||||
shl(a, b) {
|
|
||||||
if (b.geq(this.bitLength)) return bigInt.zero;
|
|
||||||
return a.shiftLeft(b).and(this.mask);
|
|
||||||
}
|
|
||||||
|
|
||||||
shr(a, b) {
|
|
||||||
if (b.geq(this.bitLength)) return bigInt.zero;
|
|
||||||
return a.shiftRight(b).and(this.mask);
|
|
||||||
}
|
|
||||||
|
|
||||||
land(a, b) {
|
|
||||||
return (a.isZero() || b.isZero) ? bigInt.zero : bigInt.one;
|
|
||||||
}
|
|
||||||
|
|
||||||
lor(a, b) {
|
|
||||||
return (a.isZero() && b.isZero) ? bigInt.zero : bigInt.one;
|
|
||||||
}
|
|
||||||
|
|
||||||
lnot(a) {
|
|
||||||
return a.isZero() ? bigInt.one : bigInt.zero;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
@@ -44,7 +44,8 @@ async function doTest(circuit, testVectors) {
|
|||||||
|
|
||||||
describe("basic cases", function () {
|
describe("basic cases", function () {
|
||||||
this.timeout(100000);
|
this.timeout(100000);
|
||||||
/* it("inout", async () => {
|
/*
|
||||||
|
it("inout", async () => {
|
||||||
await doTest(
|
await doTest(
|
||||||
"inout.circom",
|
"inout.circom",
|
||||||
[
|
[
|
||||||
@@ -298,6 +299,17 @@ describe("basic cases", function () {
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
|
it("Component array 2d", async () => {
|
||||||
|
await doTest(
|
||||||
|
"componentarray2.circom",
|
||||||
|
[
|
||||||
|
[{in: [1,2]}, {out: [1, 256]}],
|
||||||
|
[{in: [0,3]}, {out: [0, 6561]}],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
/*
|
||||||
it("Constant circuit", async () => {
|
it("Constant circuit", async () => {
|
||||||
await doTest(
|
await doTest(
|
||||||
"constantcircuit.circom",
|
"constantcircuit.circom",
|
||||||
@@ -306,12 +318,11 @@ describe("basic cases", function () {
|
|||||||
[{}, {out: [1,0,1,0, 0,0,0,1, 0,1,1,1, 0,1,0,1, 1,1,1,0, 0,1,1,0, 1,1,0,1, 1,1,0,1]}],
|
[{}, {out: [1,0,1,0, 0,0,0,1, 0,1,1,1, 0,1,0,1, 1,1,1,0, 0,1,1,0, 1,1,0,1, 1,1,0,1]}],
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}); */
|
});
|
||||||
it("Constant internal circuit", async () => {
|
it("Constant internal circuit", async () => {
|
||||||
await doTest(
|
await doTest(
|
||||||
"constantinternalcircuit.circom",
|
"constantinternalcircuit.circom",
|
||||||
[
|
[
|
||||||
// 0xbb67ae85
|
|
||||||
[{in: 1}, {out: 5}],
|
[{in: 1}, {out: 5}],
|
||||||
[{in: 0}, {out: 4}],
|
[{in: 0}, {out: 4}],
|
||||||
[{in: -2}, {out: 2}],
|
[{in: -2}, {out: 2}],
|
||||||
@@ -319,4 +330,14 @@ describe("basic cases", function () {
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
it("include", async () => {
|
||||||
|
await doTest(
|
||||||
|
"include.circom",
|
||||||
|
[
|
||||||
|
[{in: 3}, {out: 6}],
|
||||||
|
[{in: 6}, {out: 15}],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
*/
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,18 +4,15 @@
|
|||||||
function Add3(arr1, arr2, arr3) {
|
function Add3(arr1, arr2, arr3) {
|
||||||
var res[3];
|
var res[3];
|
||||||
|
|
||||||
var i;
|
|
||||||
var j;
|
|
||||||
|
|
||||||
res[0] = arr1;
|
res[0] = arr1;
|
||||||
res[1] = 0;
|
res[1] = 0;
|
||||||
for (i=0; i<2; i += 1) {
|
for (var i=0; i<2; i += 1) {
|
||||||
res[1] = res[1] + arr2[i];
|
res[1] = res[1] + arr2[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
res[2] = 0;
|
res[2] = 0;
|
||||||
for (i=0; i<2; i++) {
|
for (var i=0; i<2; i++) {
|
||||||
for (j=0; j<3; j += 1) {
|
for (var j=0; j<3; j += 1) {
|
||||||
res[2] = res[2] + arr3[i][j];
|
res[2] = res[2] + arr3[i][j];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -27,8 +24,8 @@ template Main() {
|
|||||||
signal input in;
|
signal input in;
|
||||||
signal output out[3];
|
signal output out[3];
|
||||||
|
|
||||||
var c = Add3(1, [2,3], [[4,5,6], [7,8,9]]); // [1, 5, 39];
|
var c[3] = Add3(1, [2,3], [[4,5,6], [7,8,9]]); // [1, 5, 39];
|
||||||
var d = Add3(in, [in+1, in+2], [[in+1, in+2, in+3], [in+1, in+2, in+3]]);
|
var d[3] = Add3(in, [in+1, in+2], [[in+1, in+2, in+3], [in+1, in+2, in+3]]);
|
||||||
|
|
||||||
out[0] <-- d[0] + c[0];
|
out[0] <-- d[0] + c[0];
|
||||||
out[0] === in+c[0];
|
out[0] === in+c[0];
|
||||||
|
|||||||
27
test/circuits/componentarray2.circom
Normal file
27
test/circuits/componentarray2.circom
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
template Square() {
|
||||||
|
signal input in;
|
||||||
|
signal output out;
|
||||||
|
|
||||||
|
out <== in**2;
|
||||||
|
}
|
||||||
|
|
||||||
|
template Main(n, nrounds) {
|
||||||
|
signal input in[n];
|
||||||
|
signal output out[n];
|
||||||
|
|
||||||
|
component squares[n][nrounds];
|
||||||
|
|
||||||
|
for (var i=0; i<n; i++) {
|
||||||
|
for (var r=0; r<nrounds; r++) {
|
||||||
|
squares[i][r] = Square();
|
||||||
|
if (r==0) {
|
||||||
|
squares[i][r].in <== in[i];
|
||||||
|
} else {
|
||||||
|
squares[i][r].in <== squares[i][r-1].out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
squares[i][nrounds-1].out ==> out[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component main = Main(2, 3);
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
template H(x) {
|
template H(x) {
|
||||||
signal output out[32];
|
signal output out[32];
|
||||||
var c = [0x6a09e667,
|
var c[8] = [0x6a09e667,
|
||||||
0xbb67ae85,
|
0xbb67ae85,
|
||||||
0x3c6ef372,
|
0x3c6ef372,
|
||||||
0xa54ff53a,
|
0xa54ff53a,
|
||||||
|
|||||||
16
test/circuits/include.circom
Normal file
16
test/circuits/include.circom
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
include "included.circom";
|
||||||
|
include "included.circom"; // Include twice is fine. The second one is not included
|
||||||
|
|
||||||
|
template Main() {
|
||||||
|
signal input in;
|
||||||
|
signal output out;
|
||||||
|
|
||||||
|
component t1 = T1();
|
||||||
|
|
||||||
|
var a = F1(3);
|
||||||
|
|
||||||
|
in ==> t1.in;
|
||||||
|
t1.out + a ==> out; /// out <-- in**2/3+3
|
||||||
|
}
|
||||||
|
|
||||||
|
component main = Main();
|
||||||
10
test/circuits/included.circom
Normal file
10
test/circuits/included.circom
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
template T1() {
|
||||||
|
signal input in;
|
||||||
|
signal output out;
|
||||||
|
|
||||||
|
out <== in**2/3;
|
||||||
|
}
|
||||||
|
|
||||||
|
function F1(a) {
|
||||||
|
return a**2/3;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user