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