arnaucube
arnaucube/circomPublic Self-hosted repository
circom/src/compiler.js
src/compiler.jsRaw
  1/*
  2    Copyright 2018 0KIMS association.
  3
  4    This file is part of circom (Zero Knowledge Circuit Compiler).
  5
  6    circom is a free software: you can redistribute it and/or modify it
  7    under the terms of the GNU General Public License as published by
  8    the Free Software Foundation, either version 3 of the License, or
  9    (at your option) any later version.
 10
 11    circom is distributed in the hope that it will be useful, but WITHOUT
 12    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 13    or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
 14    License for more details.
 15
 16    You should have received a copy of the GNU General Public License
 17    along with circom. If not, see <https://www.gnu.org/licenses/>.
 18*/
 19
 20const Scalar = require("ffjavascript").Scalar;
 21const sONE = 0;
 22const build = require("./build");
 23const BuilderC = require("../ports/c/builder.js");
 24const BuilderWasm = require("../ports/wasm/builder.js");
 25const constructionPhase = require("./construction_phase");
 26const Ctx = require("./ctx");
 27const utils = require("./utils");
 28const buildR1cs = require("./r1csfile").buildR1cs;
 29const BigArray = require("./bigarray");
 30const buildSyms = require("./buildsyms");
 31const {performance} = require("perf_hooks");
 32
 33module.exports = compile;
 34const measures = {};
 35
 36function ms2String(v) {
 37    v = Math.floor(v);
 38    const ms = v % 1000;
 39    v = Math.floor(v/1000);
 40    const secs = v % 60;
 41    v = Math.floor(v/60);
 42    const mins = v % 60;
 43    v = Math.floor(v/60);
 44    const hours = v % 24;
 45    const days = Math.floor(v/24);
 46    let S = "";
 47    if (days) S = S + days + "D ";
 48    if ((S!="")||(hours)) S = S + hours.toString().padStart(2, "0") + ":";
 49    if ((S!="")||(mins)) S = S + mins.toString().padStart(2, "0") + ":";
 50    if ((S!="")||(secs)) S = S + secs.toString().padStart(2, "0");
 51    S+=".";
 52    S = S + ms.toString().padStart(3, "0");
 53    return S;
 54}
 55
 56async function compile(srcFile, options) {
 57    options.prime = options.prime || Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617");
 58    if (!options) {
 59        options = {};
 60    }
 61    if (typeof options.reduceConstraints === "undefined") {
 62        options.reduceConstraints = true;
 63    }
 64    const ctx = new Ctx(options.prime);
 65    ctx.verbose= options.verbose || false;
 66    ctx.mainComponent = options.mainComponent || "main";
 67    ctx.newThreadTemplates = options.newThreadTemplates;
 68
 69    measures.constructionPhase = -performance.now();
 70    constructionPhase(ctx, srcFile);
 71    measures.constructionPhase += performance.now();
 72
 73    if (ctx.verbose) console.log("NConstraints Before: "+ctx.constraints.length);
 74    if (ctx.verbose) console.log("NSignals Before: "+ctx.signals.length);
 75
 76    if (ctx.error) {
 77        throw(ctx.error);
 78    }
 79
 80    if (ctx.getComponentIdx(ctx.mainComponent)<0) {
 81        throw new Error("A main component must be defined");
 82    }
 83
 84    if (ctx.verbose) console.log("Classify Signals");
 85    measures.classifySignals = -performance.now();
 86    classifySignals(ctx);
 87    measures.classifySignals += performance.now();
 88
 89    if (ctx.verbose) console.log("Reduce Constants");
 90    measures.reduceConstants = -performance.now();
 91    reduceConstants(ctx);
 92    measures.reduceConstants += performance.now();
 93
 94    if (options.reduceConstraints) {
 95
 96        if (ctx.verbose) console.log("Reduce Constraints");
 97        // Repeat while reductions are performed
 98/*
 99        let oldNConstrains = -1;
100        while (ctx.constraints.length != oldNConstrains) {
101            if (ctx.verbose) console.log("Reducing constraints: "+ctx.constraints.length);
102            oldNConstrains = ctx.constraints.length;
103            reduceConstrains(ctx);
104        }
105*/
106        measures.reduceConstraints = -performance.now();
107        await reduceConstrains(ctx);
108        measures.reduceConstraints += performance.now();
109
110    }
111
112    if (ctx.verbose) console.log("NConstraints After: "+ctx.constraints.length);
113
114    measures.generateWitnessNames = -performance.now();
115    generateWitnessNames(ctx);
116    measures.generateWitnessNames += performance.now();
117
118    if (ctx.error) {
119        throw(ctx.error);
120    }
121
122    if (options.cSourceWriteStream) {
123        if (ctx.verbose) console.log("Generating c...");
124        measures.generateC = -performance.now();
125        ctx.builder = new BuilderC(options.prime);
126        build(ctx);
127        const rdStream = ctx.builder.build();
128        rdStream.pipe(options.cSourceWriteStream);
129        measures.generateC += performance.now();
130
131        // await new Promise(fulfill => options.cSourceWriteStream.on("finish", fulfill));
132    }
133
134    if ((options.wasmWriteStream)||(options.watWriteStream)) {
135        if (ctx.verbose) console.log("Generating wasm...");
136        measures.generateWasm = -performance.now();
137        ctx.builder = new BuilderWasm(options.prime);
138        build(ctx);
139        if (options.wasmWriteStream) {
140            const rdStream = ctx.builder.build("wasm");
141            rdStream.pipe(options.wasmWriteStream);
142        }
143        if (options.watWriteStream) {
144            const rdStream = ctx.builder.build("wat");
145            rdStream.pipe(options.watWriteStream);
146        }
147        measures.generateWasm += performance.now();
148
149        // await new Promise(fulfill => options.wasmWriteStream.on("finish", fulfill));
150    }
151
152    // const mainCode = gen(ctx,ast);
153    if (ctx.error) throw(ctx.error);
154
155    if (options.r1csFileName) {
156        measures.generateR1cs = -performance.now();
157        await buildR1cs(ctx, options.r1csFileName);
158        measures.generateR1cs += performance.now();
159    }
160
161    if (options.symWriteStream) {
162        measures.generateSyms = -performance.now();
163        const rdStream = buildSyms(ctx);
164        rdStream.pipe(options.symWriteStream);
165        measures.generateSyms += performance.now();
166
167        await new Promise(fulfill => options.symWriteStream.on("finish", fulfill));
168    }
169
170//    const def = buildCircuitDef(ctx, mainCode);
171
172    if (ctx.verbose) {
173        for (let [mStr, mValue] of Object.entries(measures)) {
174            console.log(mStr + ": " + ms2String(mValue));
175        }
176    }
177}
178
179
180
181function classifySignals(ctx) {
182
183    const ERROR = 0xFFFF;
184
185    function priorize(t1, t2) {
186        if ((t1 == ERROR) || (t2==ERROR)) return ERROR;
187        if (t1 == ctx.stINTERNAL) {
188            return t2;
189        } else if (t2==ctx.stINTERNAL) {
190            return t1;
191        }
192        if ((t1 == ctx.stONE) || (t2 == ctx.stONE)) return ctx.stONE;
193        if ((t1 == ctx.stOUTPUT) || (t2 == ctx.stOUTPUT)) return ctx.stOUTPUT;
194        if ((t1 == ctx.stCONSTANT) || (t2 == ctx.stCONSTANT)) return ctx.stCONSTANT;
195        if ((t1 == ctx.stDISCARDED) || (t2 == ctx.stDISCARDED)) return ctx.stDISCARDED;
196        if (t1!=t2) return ERROR;
197        return t1;
198    }
199
200    // First classify the signals
201    for (let s=0; s<ctx.signals.length; s++) {
202        if ((ctx.verbose)&&(s%100000 == 0)) console.log(`classify signals: ${s}/${ctx.signals.length}`);
203        const signal = ctx.signals[s];
204        let tAll = ctx.stINTERNAL;
205        let lSignal = signal;
206        let end = false;
207        while (!end) {
208            let t = lSignal.c || ctx.stINTERNAL;
209            if (s == 0) {
210                t = ctx.stONE;
211            } else if (lSignal.o & ctx.MAIN) {
212                if (lSignal.o & ctx.IN) {
213                    if (lSignal.o & ctx.PRV) {
214                        t = ctx.stPRVINPUT;
215                    } else {
216                        t = ctx.stPUBINPUT;
217                    }
218                } else if (lSignal.o & ctx.OUT) {
219                    t = ctx.stOUTPUT;
220                }
221            } else if (utils.isDefined(lSignal.v)) {
222                t = ctx.stCONSTANT;
223            }
224            tAll = priorize(t,tAll);
225            if (lSignal.e>=0) {
226                lSignal = ctx.signals[lSignal.e];
227            } else {
228                end=true;
229            }
230        }
231        if (tAll == ERROR) {
232            throw new Error("Incompatible types in signal: " + s);
233        }
234        lSignal.c = tAll;
235    }
236}
237
238
239function generateWitnessNames(ctx) {
240    const totals = {};
241    totals[ctx.stONE] = 0;
242    totals[ctx.stOUTPUT] = 0;
243    totals[ctx.stPUBINPUT] = 0;
244    totals[ctx.stPRVINPUT] = 0;
245    totals[ctx.stINTERNAL] = 0;
246    totals[ctx.stDISCARDED] = 0;
247    totals[ctx.stCONSTANT] = 0;
248    const ids = {};
249
250    // First classify the signals
251    for (let s=0; s<ctx.signals.length; s++) {
252
253        if ((ctx.verbose)&&(s%10000 == 0)) console.log("generate witness (counting): ", s);
254
255        const signal = ctx.signals[s];
256        let lSignal = signal;
257        while (lSignal.e>=0) lSignal = ctx.signals[lSignal.e];
258
259        if (!( lSignal.o & ctx.COUNTED) ) {
260            lSignal.o |= ctx.COUNTED;
261            totals[lSignal.c] ++;
262        }
263    }
264
265    ids[ctx.stONE] = 0;
266    ids[ctx.stOUTPUT] = 1;
267    ids[ctx.stPUBINPUT] = ids[ctx.stOUTPUT] + totals[ctx.stOUTPUT];
268    ids[ctx.stPRVINPUT] = ids[ctx.stPUBINPUT] + totals[ctx.stPUBINPUT];
269    ids[ctx.stINTERNAL] = ids[ctx.stPRVINPUT] + totals[ctx.stPRVINPUT];
270    ids[ctx.stDISCARDED] = ids[ctx.stINTERNAL] + totals[ctx.stINTERNAL];
271    ids[ctx.stCONSTANT] = ids[ctx.stDISCARDED] + totals[ctx.stDISCARDED];
272    const nSignals = ids[ctx.stCONSTANT] + totals[ctx.stCONSTANT];
273
274    for (let s=0; s<ctx.signals.length; s++) {
275
276        if ((ctx.verbose)&&(s%10000 == 0)) console.log("seting id: ", s);
277
278        const signal = ctx.signals[s];
279        let lSignal = signal;
280        while (lSignal.e>=0) {
281            lSignal = ctx.signals[lSignal.e];
282        }
283        if ( typeof(lSignal.id) === "undefined" ) {
284            lSignal.id = ids[lSignal.c] ++;
285        }
286
287        signal.id = lSignal.id;
288    }
289
290    ctx.totals = totals;
291}
292
293function reduceConstants(ctx) {
294    const newConstraints = new BigArray();
295    for (let i=0; i<ctx.constraints.length; i++) {
296        if ((ctx.verbose)&&(i%10000 == 0)) console.log("reducing constants: ", i);
297        const c = ctx.lc.canonize(ctx, ctx.constraints[i]);
298        if (!ctx.lc.isZero(c)) {
299            newConstraints.push(c);
300        }
301        delete ctx.constraints[i];
302    }
303    ctx.constraints = newConstraints;
304}
305
306async function reduceConstrains(ctx) {
307    const sig2constraint = new BigArray();
308    let removedSignals = new BigArray();
309    let nRemoved;
310    let lIdx;
311
312
313    let possibleConstraints = new BigArray(ctx.constraints.length);
314    let nextPossibleConstraints;
315    for (let i=0; i<ctx.constraints.length; i++) {
316        if ((ctx.verbose)&&(i%100000 == 0)) console.log(`indexing constraints: ${i}/${ctx.constraints.length}`);
317
318        const insertedSig = { 0: true};  // Do not insert one.
319        const c = ctx.constraints[i];
320        for (let s in c.a.coefs) {
321            if (!insertedSig[s]) {
322                if (!sig2constraint[s]) sig2constraint[s] = [];
323                sig2constraint[s].push(i);
324                insertedSig[s] = true;
325            }
326        }
327        for (let s in c.b.coefs) {
328            if (!insertedSig[s]) {
329                if (!sig2constraint[s]) sig2constraint[s] = [];
330                sig2constraint[s].push(i);
331                insertedSig[s] = true;
332            }
333        }
334        for (let s in c.c.coefs) {
335            if (!insertedSig[s]) {
336                if (!sig2constraint[s]) sig2constraint[s] = [];
337                sig2constraint[s].push(i);
338                insertedSig[s] = true;
339            }
340        }
341        possibleConstraints[i] = i;
342    }
343
344    while (possibleConstraints.length >0) {
345        nextPossibleConstraints = new BigArray();
346        removedSignals = new BigArray();
347        nRemoved = 0;
348        lIdx = new BigArray();
349        for (let i=0;i<possibleConstraints.length;i++) {
350            if ((ctx.verbose)&&(i%10000 == 0)) {
351                await Promise.resolve();
352                console.log(`reducing constraints: ${i}/${possibleConstraints.length}   reduced: ${nRemoved}`);
353            }
354            const c = ctx.constraints[possibleConstraints[i]];
355            if (!c) continue;
356
357            // Swap a and b if b has more variables.
358            if (Object.keys(c.b).length > Object.keys(c.a).length) {
359                const aux = c.a;
360                c.a=c.b;
361                c.b=aux;
362            }
363
364            // Mov to C if possible.
365            if (isConstant(c.a)) {
366                const ct = {t: "N", v: c.a.coefs[sONE]};
367                c.c = ctx.lc.add(ctx.lc.mul(c.b, ct), c.c);
368                c.a = { t: "LC", coefs: {} };
369                c.b = { t: "LC", coefs: {} };
370            }
371            if (isConstant(c.b)) {
372                const ct = {t: "N", v: c.b.coefs[sONE]};
373                c.c = ctx.lc.add(ctx.lc.mul(c.a, ct), c.c);
374                c.a = { t: "LC", coefs: {} };
375                c.b = { t: "LC", coefs: {} };
376            }
377
378            if (ctx.lc.isZero(c.a) || ctx.lc.isZero(c.b)) {
379                const freeC = substituteRemoved(c.c);
380                const isolatedSignal = getFirstInternalSignal(ctx, freeC);
381                if (isolatedSignal) {
382    //                console.log(isolatedSignal);
383    //                console.log(freeC);
384                    removedSignals[isolatedSignal] = isolateSignal(freeC, isolatedSignal);
385                    if (lIdx[isolatedSignal]) {
386                        lIdx[isolatedSignal].forEach( (s) => {
387                            removedSignals[s] = substitute(removedSignals[s], isolatedSignal, removedSignals[isolatedSignal]);
388                        });
389                    }
390
391                    addTolIdx(removedSignals[isolatedSignal], isolatedSignal);
392                    ctx.constraints[possibleConstraints[i]] = null;
393                    nRemoved ++;
394
395                    sig2constraint[isolatedSignal].forEach( (s) => {
396                        nextPossibleConstraints[s] = true;
397                    });
398                }
399            }
400        }
401
402        nextPossibleConstraints = nextPossibleConstraints.getKeys();
403
404        for (let i=0; i<nextPossibleConstraints.length;i++) {
405            if ((ctx.verbose)&&(i%10000 == 0)) {
406                await Promise.resolve();
407                console.log(`substituting constraints: ${i}/${nextPossibleConstraints.length}`);
408            }
409            const c = ctx.constraints[nextPossibleConstraints[i]];
410            if (c) {
411                const nc = {
412                    a: substituteRemoved(c.a),
413                    b: substituteRemoved(c.b),
414                    c: substituteRemoved(c.c)
415                };
416                if (ctx.lc.isZero(nc)) {
417                    delete ctx.constraints[nextPossibleConstraints[i]];
418                } else {
419                    ctx.constraints[nextPossibleConstraints[i]] = nc;
420                }
421            }
422        }
423
424        const removedSignalsList = removedSignals.getKeys;
425
426        for (let i=0; i<removedSignalsList.length; i++) {
427            if ((ctx.verbose    )&&(i%100000 == 0)) console.log(`removing signals: ${i}/${removedSignalsList.length}`);
428            const s = removedSignalsList[i];
429
430            let lSignal = ctx.signals[s];
431            while (lSignal.e>=0) {
432                lSignal = ctx.signals[lSignal.e];
433            }
434
435            lSignal.c = ctx.stDISCARDED;
436        }
437
438        possibleConstraints = nextPossibleConstraints;
439    }
440
441    let o=0;
442    for (let i=0; i<ctx.constraints.length;i++) {
443        if ((ctx.verbose)&&(i%100000 == 0)) console.log(`reordering constraints: ${i}/${ctx.constraints.length}`);
444        if (ctx.constraints[i]) {
445            if (!ctx.lc.isZero(ctx.constraints[i])) {
446                ctx.constraints[o] = ctx.constraints[i];
447                o++;
448            }
449        }
450    }
451
452    ctx.constraints.length = o;
453
454    function getFirstInternalSignal(ctx, l) {
455        for (let k in l.coefs) {
456            k = Number(k);
457            const signal = ctx.signals[k];
458            if ((signal.c == ctx.stINTERNAL)&&(!ctx.F.isZero(l.coefs[k])) &&(!removedSignals[k])) return k;
459        }
460        return null;
461    }
462
463    function isolateSignal(lc, s) {
464        const eq = {
465            t: "LC",
466            coefs: {}
467        };
468        const invCoef = ctx.F.inv(lc.coefs[s]);
469        for (const k in lc.coefs) {
470            if (k != s) {
471                const v = ctx.F.mul( ctx.F.neg(lc.coefs[k]), invCoef);
472                if (!ctx.F.isZero(v)) {
473                    eq.coefs[k] = v;
474                }
475            }
476        }
477        return eq;
478    }
479
480    function substituteRemoved(lc) {
481        const newLc = ctx.lc._clone(lc);
482        for (let k in lc.coefs) {
483            if (removedSignals[k]) {
484                delete newLc.coefs[k];
485                for (let k2 in removedSignals[k].coefs) {
486                    const newP = ctx.F.mul(removedSignals[k].coefs[k2], lc.coefs[k]);
487                    if (!ctx.F.isZero(newP)) {
488                        if (newLc.coefs[k2]) {
489                            newLc.coefs[k2] = ctx.F.add(newLc.coefs[k2], newP);
490                            if (ctx.F.isZero(newLc.coefs[k2])) delete newLc.coefs[k2];
491                        } else {
492                            newLc.coefs[k2] = newP;
493                        }
494                    }
495                }
496            }
497        }
498        return newLc;
499    }
500
501    function substitute(lc, s, eq) {
502        if (!lc.coefs[s]) return lc;
503        const newLc = ctx.lc._clone(lc);
504        delete newLc.coefs[s];
505        for (let k2 in eq.coefs) {
506            const newP = ctx.F.mul(eq.coefs[k2], lc.coefs[s]);
507            if (!ctx.F.isZero(newP)) {
508                if (newLc.coefs[k2]) {
509                    newLc.coefs[k2] = ctx.F.add(newLc.coefs[k2], newP);
510                    if (ctx.F.isZero(newLc.coefs[k2])) delete newLc.coefs[k2];
511                } else {
512                    newLc.coefs[k2] = newP;
513                }
514            }
515        }
516        return newLc;
517    }
518
519    function isConstant(l) {
520        for (let k in l.coefs) {
521            if ((k != sONE) && (!ctx.F.isZero(l.coefs[k]))) return false;
522        }
523        if (!l.coefs[sONE] || ctx.F.isZero(l.coefs[sONE])) return false;
524        return true;
525    }
526
527    function addTolIdx(lc, newS) {
528        for (let s in lc.coefs) {
529            if (!lIdx[s]) lIdx[s] = [];
530            lIdx[s].push(newS);
531        }
532    }
533
534
535}
536
537
538function reduceConstrains_old(ctx) {
539    indexVariables();
540    let possibleConstraints = ctx.constraints;
541    let ii=0;
542    while (possibleConstraints.length>0) {
543        let nextPossibleConstraints = new BigArray();
544        for (let i=0; i<possibleConstraints.length; i++) {
545            ii++;
546            if ((ctx.verbose)&&(ii%10000 == 0)) console.log("reducing constraints: ", i);
547            if (!ctx.constraints[i]) continue;
548            const c = ctx.constraints[i];
549
550            // Swap a and b if b has more variables.
551            if (Object.keys(c.b).length > Object.keys(c.a).length) {
552                const aux = c.a;
553                c.a=c.b;
554                c.b=aux;
555            }
556
557            // Mov to C if possible.
558            if (isConstant(c.a)) {
559                const ct = {t: "N", v: c.a.coefs[sONE]};
560                c.c = ctx.lc.add(ctx.lc.mul(c.b, ct), c.c);
561                c.a = { t: "LC", coefs: {} };
562                c.b = { t: "LC", coefs: {} };
563            }
564            if (isConstant(c.b)) {
565                const ct = {t: "N", v: c.b.coefs[sONE]};
566                c.c = ctx.lc.add(ctx.lc.mul(c.a, ct), c.c);
567                c.a = { t: "LC", coefs: {} };
568                c.b = { t: "LC", coefs: {} };
569            }
570
571            if (ctx.lc.isZero(c.a) || ctx.lc.isZero(c.b)) {
572                const isolatedSignal = getFirstInternalSignal(ctx, c.c);
573                if (isolatedSignal) {
574
575                    let lSignal = ctx.signals[isolatedSignal];
576                    while (lSignal.e>=0) {
577                        lSignal = ctx.signals[lSignal.e];
578                    }
579
580
581                    const isolatedSignalEquivalence = {
582                        t: "LC",
583                        coefs: {}
584                    };
585                    const invCoef = ctx.F.inv(c.c.coefs[isolatedSignal]);
586                    for (const s in c.c.coefs) {
587                        if (s != isolatedSignal) {
588                            const v = ctx.F.mul( ctx.F.neg(c.c.coefs[s]), invCoef);
589                            if (!ctx.F.isZero(v)) {
590                                isolatedSignalEquivalence.coefs[s] = v;
591                            }
592                        }
593                    }
594
595                    for (let j in lSignal.inConstraints) {
596                        if ((j!=i)&&(ctx.constraints[j])) {
597                            ctx.constraints[j] = ctx.lc.substitute(ctx.constraints[j], isolatedSignal, isolatedSignalEquivalence);
598                            linkSignalsConstraint(j);
599                            if (j<i) {
600                                nextPossibleConstraints.push(j);
601                            }
602                        }
603                    }
604
605                    ctx.constraints[i] = null;
606
607                    lSignal.c = ctx.stDISCARDED;
608                } else {
609                    if (ctx.lc.isZero(c.c)) ctx.constraints[i] = null;
610                }
611            }
612        }
613        possibleConstraints = nextPossibleConstraints;
614    }
615    unindexVariables();
616
617    // Pack the constraints
618    let o = 0;
619    for (let i=0; i<ctx.constraints.length; i++) {
620        if (ctx.constraints[i]) {
621            if (o != i) {
622                ctx.constraints[o] = ctx.constraints[i];
623            }
624            o++;
625        }
626    }
627    ctx.constraints.length = o;
628
629    function indexVariables() {
630        for (let i=0; i<ctx.constraints.length; i++) linkSignalsConstraint(i);
631    }
632
633    function linkSignalsConstraint(cidx) {
634        const ct = ctx.constraints[cidx];
635        for (let k in ct.a.coefs) linkSignal(k, cidx);
636        for (let k in ct.b.coefs) linkSignal(k, cidx);
637        for (let k in ct.c.coefs) linkSignal(k, cidx);
638    }
639
640    function unindexVariables() {
641        for (let s=0; s<ctx.signals.length; s++) {
642            let lSignal = ctx.signals[s];
643            while (lSignal.e>=0) {
644                lSignal = ctx.signals[lSignal.e];
645            }
646            if (lSignal.inConstraints) delete lSignal.inConstraints;
647        }
648    }
649
650/*
651    function unlinkSignal(signalName, cidx) {
652        let lSignal = ctx.signals[signalName];
653        while (lSignal.e>=0) {
654            lSignal = ctx.signals[lSignal.e];
655        }
656        if ((lSignal.inConstraints)&&(lSignal.inConstraints[cidx])) {
657            delete lSignal.inConstraints[cidx];
658        }
659    }
660*/
661
662    function linkSignal(signalName, cidx) {
663        let lSignal = ctx.signals[signalName];
664        while (lSignal.e>=0) {
665            lSignal = ctx.signals[lSignal.e];
666        }
667        if (!lSignal.inConstraints) lSignal.inConstraints = {};
668        lSignal.inConstraints[cidx] = true;
669    }
670
671    function getFirstInternalSignal(ctx, l) {
672        for (let k in l.coefs) {
673            const signal = ctx.signals[k];
674            if (signal.c == ctx.stINTERNAL) return k;
675        }
676        return null;
677    }
678
679    function isConstant(l) {
680        for (let k in l.coefs) {
681            if ((k != sONE) && (!ctx.F.isZero(l.coefs[k]))) return false;
682        }
683        if (!l.coefs[sONE] || ctx.F.isZero(l.coefs[sONE])) return false;
684        return true;
685    }
686
687}
688
689/*
690
691function buildCircuitDef(ctx, mainCode) {
692    const res = {
693        mainCode: mainCode
694    };
695    res.signalName2Idx = ctx.signalName2Idx;
696
697    res.components = [];
698    res.componentName2Idx = {};
699    for (let c in ctx.components) {
700        const idCoponent = res.components.length;
701        res.components.push({
702            name: c,
703            params: ctx.components[c].params,
704            template: ctx.components[c].template,
705            inputSignals: 0
706        });
707        res.componentName2Idx[c] = idCoponent;
708    }
709
710    res.signals = new Array(ctx.signalNames.length);
711    for (let i=0; i<ctx.signalNames.length; i++) {
712        res.signals[i] = {
713            names: ctx.signalNames[i],
714            triggerComponents: []
715        };
716        ctx.signalNames[i].map( (fullName) => {
717            const idComponet = res.componentName2Idx[ctx.signals[fullName].component];
718            if (ctx.signals[fullName].direction == "IN") {
719                res.signals[i].triggerComponents.push(idComponet);
720                res.components[idComponet].inputSignals++;
721            }
722        });
723    }
724
725    res.constraints = buildConstraints(ctx);
726
727    res.templates = ctx.templates;
728
729    res.functions = {};
730    for (let f in ctx.functions) {
731        res.functions[f] = {
732            params: ctx.functionParams[f],
733            func: ctx.functions[f]
734        };
735    }
736
737    res.nPrvInputs = ctx.totals.prvInput;
738    res.nPubInputs = ctx.totals.pubInput;
739    res.nInputs = res.nPrvInputs + res.nPubInputs;
740    res.nOutputs = ctx.totals.output;
741    res.nVars = res.nInputs + res.nOutputs + ctx.totals.one + ctx.totals.internal;
742    res.nConstants = ctx.totals.constant;
743    res.nSignals = res.nVars + res.nConstants;
744
745    return res;
746}
747
748*/
749
750
751/*
752    Build constraints
753
754A constraint like this
755
756      [s1 + 2*s2 + 3*s3] * [ s2 + 5*s4] - [s0        ] = 0
757      [     5*s2 + 6*s3] * [ s2 +     ] - [s0 + 2* s2] = 0
758      [s1 +          s3] * [ s2 + 5*s3] - [s4        ] = 0
759
760is converted to
761
762[
763    [{"1":"1","2":"2","3":"3"} , {"2":"1","4":"5"} , {"0":"1"         }],
764    [{        "2":"5","3":"6"} , {"2":"1"        } , {"0":"1", "2":"2"}],
765    [{"1":"1",        "3":"1"} , {"2":"1","3":"5"} , {"4":"1"         }]
766]
767           ^                            ^                  ^
768           |                            |                  |
769           A                            B                  C
770
771*/
772/*
773function buildConstraints(ctx) {
774    const res = [];
775
776    function fillLC(dst, src) {
777        if (src.t != "LC") throw new Error("Constraint is not a LINEARCOMBINATION");
778        for (let s in src.coefs) {
779            const v = src.coefs[s].toString();
780            const id = ctx.signalName2Idx[s];
781            dst[id] = v;
782        }
783    }
784
785    for (let i=0; i<ctx.constraints.length; i++) {
786        const A = {};
787        const B = {};
788        const C = {};
789
790        fillLC(A, ctx.constraints[i].a);
791        fillLC(B, ctx.constraints[i].b);
792        fillLC(C, ctx.lc.negate(ctx.constraints[i].c));
793
794        res.push([A,B,C]);
795    }
796
797    return res;
798}
799*/
800
801/*
802function buildSyms(ctx, strm) {
803
804    let nSyms;
805
806    addSymbolsComponent(ctx.mainComponent + ".", ctx.getComponentIdx(ctx.mainComponent));
807
808
809    function addSymbolsComponent(prefix, idComponet) {
810        for (let n in  ctx.components[idComponet].names.o) {
811            const entrie = ctx.components[idComponet].names.o[n];
812            addSymbolArray(prefix+n, entrie.type, entrie.sizes, entrie.offset);
813        }
814    }
815
816    function addSymbolArray(prefix, type, sizes, offset) {
817        if (sizes.length==0) {
818            if (type == "S") {
819                let s=offset;
820                while (ctx.signals[s].e >= 0) s = ctx.signals[s].e;
821                let wId = ctx.signals[s].id;
822                if (typeof(wId) == "undefined") wId=-1;
823                strm.write(`${offset},${wId},${prefix}\n`);
824                nSyms ++;
825                if ((ctx.verbose)&&(nSyms%10000 == 0)) console.log("Symbols saved: "+nSyms);
826            } else {
827                addSymbolsComponent(prefix+".", offset);
828            }
829            return 1;
830        } else {
831            let acc = 0;
832            for (let i=0; i<sizes[0]; i++) {
833                acc += addSymbolArray(`${prefix}[${i}]`, type, sizes.slice(1), offset + acc );
834            }
835            return acc;
836        }
837    }
838
839}
840
841*/
842