Skip to content
Lurk Lab

Blog

Lurk Circuit Specification

From the archive. This article describes the work and implementations at its original publication date.

On this page
  1. Introduction
  2. Lurk
  3. Language overview
  4. Fibonacci example
  5. Circuit overview
  6. High-level description
  7. Gadgets
  8. Auxiliary circuits:
  9. Circuit specification
  10. Backends
  11. Main concepts
  12. Pointers
  13. Expressions
  14. Continuations
  15. Environment
  16. Store implementation
  17. Poseidon
  18. Poseidon circuit
  19. Pointers in the circuit
  20. Error system
  21. High-level algorithms
  22. Functional commitments
  23. Globals
  24. Reduce expression
  25. Reduce symbol
  26. Reduce Cons
  27. Apply continuation
  28. Make thunk
  29. CAR-CDR-NAMED
  30. Extend
  31. Extend rec
  32. Low-level description
  33. R1CS
  34. Circuit components
  35. Final Remarks
  36. References
  37. Footnotes

\global\def\var#1{\footnotesize{\textsf{#1}}} \global\def\gad#1{\footnotesize{\textsf{#1}}} \global\def\fun#1{\footnotesize{\textsf{#1}}} \global\def\cir#1{\footnotesize{\text{#1}}} \global\def\cirtitle#1{\footnotesize{\bold{#1}}~} \global\def\cirio#1{\footnotesize{\bold{#1}}~} \global\def\com#1{\footnotesize{\text{#1}}~}

Disclaimer\bf{Disclaimer}: This spec is a work in progress. It is an ongoing project and will continue to be refined over time. Questions about the circuit can be posed in the Lurk Zulip server.

Introduction

Zero-Knowledge Proofs 1 are cryptographic primitives that allow some entity (the prover) to prove to another party (the verifier) the validity of some statement or relation. Today there are many efficient constructions of NIZK proof systems, with different trade-offs, as well as several implementations of the proving systems.

Every proving system, as described in the zk-Interface 2, can be divided into the backend, which is the portion of the software that contains the implementation of the underlying cryptographic protocol, and the frontend, which provides means to express statements in a convenient language, allowing to prove such statements in zero knowledge by compiling them into a low-level representation.

The backend of a proving system consists of the key generation, proving and verification algorithms. It proves statements where the instance and witness are expressed as variable assignments, and relations are expressed via low-level languages. The most common instance of such a low-level language is R1CS, a generalization of arithmetic circuits, introduced in 3 and used in many other proof systems (see 4, 5, 6, 7 among others).

Lurk is a proving frontend which accepts high-level statements written in the form of a Lisp dialect, and produces a low-level proof representation thereof specifically tailored to work well with a certain class of proving backends. Indeed, Lurk produces proof statements shaped as chained but distinct iterations of a single abstract state machine8 represented in R1CS. As the components of the low-level proof statement only differ in their assignment of inputs and outputs, Lurk’s outputs are particularly well suited to cryptographic backends that support recursion, or more generally proof aggregation. As an implementation, Lurk currently focuses on producing statements in R1CS, and supports two backends: Nova 9 and SnarkPack 10.

The Lurk frontend consists of the following:

  • The specification of a high-level language for expressing statements.
  • A compiler that converts relations expressed in the high-level language into the low-level relations suitable for some backends. This leverages a library of “gadgets” consisting of useful and hand-optimized building blocks for certain primitive statements.
  • Instance reduction: conversion of the instance in a high-level statement to a low-level instance
  • Witness reduction: conversion of the witness to a high-level statement to a lowlevel witness (e.g., assignment to witness variables).

Lurk decomposes relations expressed in its Lisp-like source language into iterations of an abstract state machine (ASM), which implements a deterministic interpreter for this language. The workings of the step function of this state machine is detailed in the reduction section below.

At a high level, Lurk’s ASM manages four constructs:

  • a library of R1CS gadgets, which allow translating primitive blocks of the source language directly into arithmetic sub-circuits,
  • a continuation stack, that allows deferring the evaluation of sub-parts of the current program, or upacking deferred part of the computation in the translation of the currently-selected portion of the AST is finished,
  • a reduction strategy, that allows selecting sub-parts of the input AST for evaluation deterministically,
  • a strict hashing discipline for Lurk programs, which allows the prover to memoize the translation of portions of the input program, while producing cryptographic hashes that bind the prover to a specific source program.

This step function proceeds repeatedly until the input program is fully evaluated.

Lurk

Lurk is a functional programming language based on Scheme and Common Lisp. An important aspect of its design is Continuation Passing Style (CPS)11, where the control flow of programs can be managed through the use of continuations. Continuations are recursive data structures that can be used as part of a representation of generic computations. In particular, for each basic operation in a certain computation, a continuation is used as a pointer to the rest of the computation, i.e. it indicates what must happen after this basic operation is computed. Concretely, it takes the form of a stack of continuations that we will describe in greater detail later on. This technique allows us to divide a program into small parts and build a small circuit for each part. We will summarize the main concepts involved in the design of the language, which should enable the reader to understand how zero-knowledge proofs 7 9 12 are constructed in Lurk.

In this section, we provide a summary of Lurk’s main elements. An expression represents a computation involving literals, variables, operations and procedures. Variables are handled by an environment , which is responsible for binding variables to values. We also use continuations to indicate what must be done to finish the computation.

The system’s I/O is formed by an expression, an environment, and a continuation. Understanding these 3 elements is essential to comprehending Lurk. Each element is represented as a pointer, which is implemented using hash functions. In particular, we use Poseidon 13 to instantiate our pointers.

The environment has a list of bindings, which correspond to a mapping between variables and values at a certain point in time. The mapping of these local bindings is valid only for a specific evaluation of an expression. On the other hand, the global state is represented by the store, which behaves as a memory of the system. The store is global, while the environment is local.

Language overview

  • t, nil: are self-evaluating and represent true and false, respectively.
  • if: has the format (<test> <consequent> <alternate>) and represents a conditional expression. It must receive all 3 parameters, where the <test> is an expression used to select the result; the <consequent> is selected if the <test> evaluat es to non-nil; and <alternate> is selected otherwise. Unlike other programming languages, the <alternate> expression is mandatory.
  • lambda: has the format (lambda <formals> <body>) and represents a procedure. The environment when the lambda expression is evaluated is used as a closure, which is extended with <formals>, a list of variables. The unique expression in the <body> is evaluated and returned as the result of the lambda expression.
  • let: has the format (let <bindings> <body>) and represents an assignment expression, where <bindings> represents a list of pairs in the form (<variable>, <init>); and <body> is a unique expression.
  • letrec: has the same format as <let> expressions, following the same rules, but also allowing recursion.
  • quote: has the format (quote <datum>) or (’ <datum>) and evaluates to <datum>.
  • atom: has the format (atom <e>), and it evaluates to t if <e> is not a list, and evaluates to nil otherwise.
  • cons, strcons car, cdr: The expression (cons <a> <d>) produces a pair whose car is <a> and cdr is <d>. When <a> evaluates to a character and <d> evaluates to a string, we have that strcons <a> <d> produces to a string. In this situation we have that car <e> returns the first character when <e> is a string. Correspondingly, cdr <e> returns a string obtained from removing the first character from <e>.
  • arithmetic operations: has the format (<op> <e1> <e2>), where <op> corresponds to an arithmetic operation (+, -, *, /). <e1> is evaluated before <e2> and the operation is carried out in the finite field that is used in the subjacent zero-knowledge backend.
  • equality: has the format (<op> <e1> <e2>), where <op> can be either = or eq. The equality symbol = is used to compare expressions whose result is a number (finite field elements), while the symbol eq is used to compare pointers.
  • emit: has the format (emit <e>) and is used to return the result of the evaluation of <e> as a public value, which can be used to define the instance of the zero-knowledge statement.
  • begin: has the format (begin <e> ...). The sequence of expressions is evaluated from left to right and the last result is returned.
  • current env: returns the current environment represented as an association list.
  • eval: has the format (eval <exp>) or (eval <exp> <env>). The evaluation of <exp> is used as an expression in the environment obtained from the evaluation of <env>. If no <env> is provided, an empty environment is used.
  • hide: has the format (hide <exp> <secret>). Return the commitment of <exp> using secret <secret>.
  • commit: has the the format (commit <exp>). Compute the commitment of <exp>.
  • secret: has the format (secret <comm>). Return the secret used to generate the commitment <comm>, if known.
  • open: has the format (open <comm>). Return the value used to generate the commitment <comm>, if known.
  • comm: has the format (comm <data>). Change the tag of <data> to comm.
  • num: has the format (num <data>). Change the tag of <data> to num.
  • char: has the format (char <data>). Change the tag of <data> to <char>.
  • u64: has the format (u64 <num>). Coerce <num> to be a 64-bit unsigned integer.

Fibonacci example

Here is an example code snippet that implements the Fibonacci sequence. You can click the \small\blacktriangleright button to display the output.

LurkRecorded example
(letrec ((next (lambda (a b n target) (if (eq n
        target)
             a
                   (next b
                   (+ a b)
                   (+ 1 n)
                        target))))
               (fib (next 0 1 0)))
        (fib 10))
Recorded output
Iterations: 521
Result: 55

Figure 1:Fibonacci example\rm{\small{Figure~1: Fibonacci~example}}

Circuit overview

In this section, we give a short description of Lurk’s circuit. Important concepts are introduced to help the reader better understand the purpose of certain components and how they interface with each other.

High-level description

A Lurk program consists of a sequence of reduction steps, or iterations, which are mapped to frames. A set of frames is grouped into a MultiFrame object. Each frame is represented by a CircuitFrame and a Circuit is a sequence of CircuitFrames where the output of one frame is connected to the input of the next, mimicking the evaluation of Lurk expressions. For instance, in the Fibonacci example above, we have 521 iterations, each one mapped into a frame.

In eval.rs, the function reduce_with_witness() computes reduction steps with their witnesses. We provide an implementation of this computation in the circuit using the functions Reduce-expression()\cir{Reduce-expression}() and Apply-Continuation()\cir{Apply-Continuation}() in the file circuit_frame.rs. Global symbols are pre-computed Lurk symbols that can be easily compared with symbols found during expression evaluation.

To reduce an expression, we distinguish between two cases: atoms, such as symbols, and lists, which are more complicated expressions composed of an operation in the first position and other elements that can be atoms or nested lists. In Reduce-Sym()\cir{Reduce-Sym}(), we reduce symbols using comparisons among symbols that require allocation of variables and pointers in the circuit, Boolean logic, and conditionals. For example, Reduce-Sym()\cir{Reduce-Sym}() is used to find the value of a variable. We update the environment and the store accordingly.

The cons function is a crucial building block in functional languages like Lurk. It concatenates car and cdr, allowing us to break down expressions into smaller pieces. In Reduce-Cons()\cir{Reduce-Cons}(), we handle each possible Lurk expression that is constructed using cons. We allocate auxiliary variables in the circuit for later use and use a CAR-CDR-NAMED()\gad{CAR-CDR-NAMED}() gadget as a building block. We include a clause in a multicase gadget for each situation depending on the type of expression we are handling, and select the desired result based on the head of the expression using car. Finally, we return the result of the multicase.

Another important function is Apply-Continuation()\cir{Apply-Continuation}(). In order to finish a reduction step, we must calculate the output of the frame. Each iteration has a continuation tag that requires a computation of the next expression, environment, continuation, and thunk. Therefore, we have to constrain the system to prove we are computing the correct elements, and we have to allocate pointers to use them later. This task is executed in 2 stages:

  1. Some continuations require the calculation of new pointers, while others don’t. For those that need new pointers, since the implementation of pointers requires a hash computation and because hashes are expensive in the circuit, we use a multicase to select the appropriate hash preimage. Then we can compute the hashes just once. This allows us to avoid computing unnecessary hashes.
  2. We then use another multicase to select the continuation results.

Gadgets

To construct the circuit, we use gadgets and auxiliary functions as building blocks. These gadgets are fragments of emitted code generated by the Lurk compiler, which represent the translation of primitive operations of the Lurk language into the low-level language accepted by cryptographic proof backends. Each gadget incorporates constraints that convey the semantics of the source language in the low-level language of arithmetic circuits. For instance, when we interpret an expression like a+ba + b, the emitted gadget not only computes the addition but also takes into account the max values of aa and bb when interpreted as u64, along with the overflow semantics of their sum - even when the gadget manipulates primitive addition operations on 256-bit numbers.

Gadgets are denoted in all caps, and composition of gadgets is shown using a dark gray color. Full details of the gadgets, such as the number of constraints of each component and their implementation description, will be provided in the Low-level description section.

  • Variable types: In the circuit we allow variable to have the following types:
  1. AllocatedNum: represents a field element in the circuit.
  2. AllocatedPtr: represents a pointer in the circuit, which is given by 2 AllocatedNums, denoted by tag()\var{tag}() and hash()\var{hash}() respectively.
  3. AllocatedContPtr: represents a continuation pointer in the circuit. Silimilarly to AllocatedPtr, it is formed by 2 AllocatedNums.
  4. Boolean: represents a bool in the circuit. It can be used both for allocated values or as constants. In the case it represents an allocated value, this value is enforced to be 0 or 1.

Some gadgets, such as NOT\gad{NOT}, refer to other circuits - in this case the Boolean variable being negated. This reflects that arithmetic circuits have input and output variables, and can therefore be connected to each other. In this case, the circuit for NOT\gad{NOT} will have its input wired to the output of the circuit generating the Boolean variable passed as its argument.

  • Syntax :

    • Let: creates a variable in the circuit.

    • Call: allocate a gadget inside a circuit, used when gadgets have no return values.

    • Return: defines the output of a circuit.

    • We use dot notation to access global variables:

      1. Let symbol-tag=globals.sym-tag\var{symbol-tag} = \var{globals.sym-tag}.
    • We use dot notation to access gadget’s helper methods:

      1. Let m=CASE-CLAUSES()m = \gad{CASE-CLAUSES}().
      2. Call m.ADD-CLAUSE(k,c)m.\gad{ADD-CLAUSE}(k, c).
    • Tuples and vectors: syntactic sugar to manipulate circuit variables.

  • Boolean operations: used to handle bit operations like conjunctions, disjunctions, negations, and bit decomposition.

    1. Let o=AND(i1,i2,)o = \gad{AND}(i_1, i_2, \dots): Receives a variadic number of input variables of type Boolean and returns another Boolean representing the conjunction of the input variables.
    2. Let o=OR(i1,i2,)o = \gad{OR}(i_1, i_2, \dots): Receives a variadic number of input variables of type Boolean and returns another Boolean representing the disjunction of the input variables.
    3. Let negated-b=b.NOT()\var{negated-b} = b.\gad{NOT}(): Every variable bb of type Boolean has an auxiliary method called NOT, which receives no input and returns another Boolean representing the negation of bb.
  • Equality: allows equality tests of allocated variables.

    1. Let is-equal=ALLOC-EQUAL(a,b)\var{is-equal} = \gad{ALLOC-EQUAL}(a, b): The Boolean variable is-equal\var{is-equal} is true if and only if aa is equal to bb.
    2. EQUAL(a,b)\gad{EQUAL}(a, b): enforces aa equal to bb.
    3. Let is-zero=ALLOC-IS-ZERO(a)\var{is-zero} = \gad{ALLOC-IS-ZERO}(a): The Boolean variable is-zero\var{is-zero} is true if and only if aa is zero.
  • Pick: used for ternary operators.

    1. Let a=PICK(cond,a,b)a = \gad{PICK}(\var{cond}, a, b): If cond\var{cond} is true, return aa, otherwise return bb.
  • Implication: used for constraints in the form: if a\small{a} is true, then b\small{b} is true, where a\small{a} and b\small{b} are expressions that evaluate to Boolean values.

    1. Call IMPLIES-EQUAL(cond,a,b)\gad{IMPLIES-EQUAL}(\var{cond}, a, b): If cond\var{cond} is true, then aa is enforced to be equal to bb.
  • Arithmetic operations: used to constrain arithmetic operations (+, , , /)\rm{\footnotesize{(+,~-,~*,~/)}} in the subjacent finite field.

    1. Let c=SUM(a,b)c = \gad{SUM}(a, b): cc is enforced to be a+ba + b.
    2. Let c=SUB(a,b)c = \gad{SUB}(a, b): cc is enforced to be aba - b.
    3. Let c=MUL(a,b)c = \gad{MUL}(a, b): cc is enforced to be a.ba . b.
    4. Let c=DIV(a,b)c = \gad{DIV}(a, b): cc is enforced to be a.b1a . b^{-1}.
  • Pointers: formed by a tag, which allows us to identify the type of the pointer, and a hash that links the pointer to its content, which is given by the hash preimage.

    1. Let a=ALLOC-PTR(pointer)a = \gad{ALLOC-PTR}(\var{pointer}): Allocates a pointer in the circuit.
  • Data: functions can allocate different types of data by using pointers. Later, data can be accessed non-deterministically by providing the witness that corresponds to the hash preimage.

    1. Let hash=CONSTRUCT-CONS(car,cdr)\var{hash} = \gad{CONSTRUCT-CONS}(\var{car}, \var{cdr}): Computes a hash function over car\var{car} and cdr\var{cdr}.
    2. Let hash=CONSTRUCT-FUN(args,body,env)\var{hash} = \gad{CONSTRUCT-FUN}(\var{args}, \var{body}, \var{env}): Computes a hash function over args\var{args}, body\var{body} and env\var{env}.
    3. Let hash=CONSTRUCT-LIST(args[])\var{hash} = \gad{CONSTRUCT-LIST}(\var{args}[]): Computes a hash function over args by using a sequence of cons.
    4. Let hash=CONSTRUCT(comp1,comp2,comp3,comp4)\var{hash} = \gad{CONSTRUCT}(\var{comp1}, \var{comp2}, \var{comp3}, \var{comp4}): computes a hash function over comp1,comp2,comp3,comp4\var{comp1}, \var{comp2}, \var{comp3}, \var{comp4}.
  • Multicase: used to select results based on certain selection tags. It is basically a set of cases that share the same set of selection tags. A multicase whose size is equal to 1 is the same as a regular case.

    1. Let case-clauses=CASE-CLAUSES()\var{case-clauses} = \gad{CASE-CLAUSES}(): a list of clauses for a CASE\gad{CASE} gadget.
    2. Let result=CASE(key,clauses,default)\var{result} = \gad{CASE}(\var{key}, \var{clauses}, \var{default}): the result of using key to select a value from clauses. If no clause is found, return default.
    3. Let multicase-clauses=MULTICASE-CLAUSES()\var{multicase-clauses} = \gad{MULTICASE-CLAUSES}(): A list of clauses for a MULTICASE\gad{MULTICASE} gadget.
    4. Let result=MULTICASE(key,clauses,default)\var{result} = \gad{MULTICASE}(\var{key}, \var{clauses}, \var{default}): the result of using key to select a value from clauses. If no clause is found, return default.

Auxiliary circuits:

  • Comparisons: computes the comparison of allocated variables.
  • Enforce n bits: Enforce a certain allocated number can be represented using nn bits.

Circuit specification

This section describes the Lurk circuit in detail. We present the high-level algorithms first to help readers understand our architectural decisions, become familiar with the notation, and comprehend how components are interconnected. We then provide the low-level algorithms, which explain how we construct R1CS constraints for each building block in this document.

Backends

Currently, we support two backends: Groth16 7 and Nova 9. Both are based on R1CS constraints, enabling us to create a single circuit that works with both backends. However, there is an essential difference between the two. Groth16 requires a trusted setup for each circuit, which we want to avoid since updating the circuit would require another ceremony for the new trusted setup. Conversely, Nova doesn’t need a trusted setup. Moreover, Nova allows recursive composition of proofs, making it a practical and exciting alternative. In particular, it fits well into the Lurk circuit since we can fold Lurk frames using Nova’s folding technique. Regrettably, recursive composition is beyond the scope of this document.

From the application layer perspective, the only difference between these systems is the underlying finite field. Specifically, it means that programs like (1 0)\rm{\footnotesize{(- 1 ~0)}} evaluate to different numbers in each case.

Next we summarize the main characteristics of each system:

  • Groth16 7 is implemented over the BLS12-381 elliptic curve. The subjacent Finite Field for Lurk applications is defined over the following prime: 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001
  • Nova 9 is based on the cycle of elliptic curves named Pallas and Vesta 14. The underlying Finite Field is defined over the following prime: 0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001

Lurk data are constructed using finite field elements. In subsequent sections we show how other data types are represented using these elements.

We denote the finite field by F in order to provide an homogeneous description of Lurk circuits, but it is important to note that, depending on the chosen backend, a different prime is used, which may lead to distinct behavior in some situations (as, for example, when modular reductions are involved).

Main concepts

Here we describe how Lurk evaluation works. Later we will show how the circuit implements the same components.

A Lurk expression is evaluated step-by-step by applying a reduction method. The next step to be applied is called a continuation. In this next example we see how a simple program is evaluated.

> (atom 42)
INFO lurk::eval > Frame: 0 Expr: (ATOM 42)
Env: NIL
Cont: Outermost

INFO lurk::eval > Frame: 1 Expr: 42
Env: NIL
Cont: Unop{ operator: Atom, continuation: Outermost }

INFO lurk::eval > Frame: 2 Expr: T
Env: NIL
Cont: Terminal

[2 iterations] => T

Figure 2:Atom example\rm{\small{Figure~2: Atom~example}}

The evaluation process begins by identifying the atom operation. Once the operation is detected, we can create a corresponding continuation, called unop, which is short for unary operation. In the second step, we evaluate the unique operand, the number 42, which results in itself. Next, we apply the atom operation to 42 and obtain the symbol T.

The continuation for the atom operation requires only one expression as input, which in this case is the number 42. We then need to find a way to return this to the caller, which is Frame 0. After this step, we can return the result of evaluating atom over 42, which is T, and terminate the computation since it is the outermost continuation.

The unary operator follows a specific structure, consisting of the operator and the continuation, which is represented as Lurk data by finite field elements. The continuation is represented by a tag and a pointer. To look up the pointer content, we use dictionaries, which are hash maps used to store data in a content-addressable way. A hash function is calculated over the content to generate an index, which is used to address the data efficiently. It is important to note that we do not use dictionaries in the circuit. Instead, we provide the witness that corresponds to the hash preimage and the result non-deterministically. Therefore, we do not need to look up any table to find the result since the result is already there.

Pointers

A pointer consists of two field elements: a tag and a raw pointer. The tag serves the purpose of distinguishing between different types of pointers, which is important because each tag corresponds to a distinct data structure behind the pointer. The content of the pointer has a structure that varies according to its type, and this type can be determined by the tag. The raw pointer is a dictionary index that can be used to efficiently access the content. Pointers are versatile and can be used to store Lurk expressions, allowing for the expression of recursive expressions. Additionally, pointers can be used to represent the environment, as described in the Environment Section.

Figure 3: Pointer
Figure 3: Pointer

Figure 4: Continuation pointer
Figure 4: Continuation pointer

A continuation pointer, denoted by cont\rm{\footnotesize{cont}}, is also defined as 2 field elements: a continuation tag and a raw pointer. It works in the same way as regular pointers, but it is restricted to continuations. While this differentiation is not necessary, it enhances the design organization by separating components based on their functionality. If we want continuations to be first class, we would have to treat them like regular pointers.

A raw pointer is a 32-bit integer (encoded naturally as a field element) that represents an index in some of the dictionaries managed by the store. This enables efficient recovery of the pointer’s content. If the raw pointer is negative, it encodes an opaque pointer, which implies that the store is not aware of it.

Expressions

Expressions are recursive data structures containing nested operations involving literal values, variables, and operations. It can represent arithmetic expressions like (+ 10 32)\rm{\footnotesize{(+~10~32)}}, or lambda expressions such as (lambda (x) x)\rm{\footnotesize{(lambda~(x)~x)}}. An anonymous function that returns the received input argument. Here is a list of Lurk expressions:

  • nil. The nil symbol is a self-evaluating expression. Since it is used frequently, we have a global pointer to represent it.
  • cons(pointer, pointer). A cons expression receives two input arguments, which are given by generic pointers. Both expressions are concatenated, forming a new list, which is inserted into the store.
  • comm(F, pointer). A comm operation receives as input a secret value, represented as a field element, and a second expression, which represents the content of the commitment. The output is a pointer to a field element, which is the commitment using Poseidon hash.
  • sym(string). A Lurk symbol is given by a string, which may be either a restricted Lurk symbol like cons, car, cdr, if, or let, or a new symbol, defined using let or letrec. It could also be a local symbol, as, for example, in arguments of lambda expressions.
  • fun(pointer, pointer, pointer). Each pointer represents the arguments, the body, and the closed environment, respectively.
  • num(F). A num expression is represented by a field element, and the output is a pointer to it.
  • str(string). A string expression is represented as a list of char, recursively interpreted as a cons(char, string), where the first element is some char, and the rest of the string is another string.
  • thunk(pointer, cont). A thunk expression contains a pointer to an expression that must be evaluated before calling the continuation, which is the second argument. It is an important building block for constructing special continuations like tail, outermost, terminal, and error continuations.
  • opaque(pointer). LurkIt allows for opaque pointers, where the content of the pointer is not known by the store. Such pointers can’t be resolved, and they are useful for manipulation of private data (for example).
  • char(char). A char expression is represented by a char type, which is encoded as a field element naturally.

Figure 5 shows the tree structure of Lurk expressions.

Figure 5: Lurk expression example
Figure 5: Lurk expression example

Continuations

Continuations are data structures containing all the information necessary to continue some computation. Different continuations require different information. Two important continuations are unop and binop, which respectively denote the continuation for unary and binary operations. In order to evaluate the second argument of a binop we require a binop2 continuation.

  • outermost. It is the continuation that points to the initial frame.
  • call0(cont). It is represented by a continuation pointer that contains the function to be called.
  • call(pointer, pointer, cont), where the first argument corresponds to the unevaled argument, the second is the saved environment, and the third is the continuation. It creates a call_2\rm{\footnotesize{call\_2}} continuation that will be able to evaluate the argument and finish the call procedure.
  • call2(pointer, pointer, cont), where the first argument corresponds to the function to be called, the second is the saved environment, and the third is the continuation. It creates a tail continuation, which will evaluate the body of the function.
  • tail(pointer, cont). A tail continuation allows nested tails that may be constructed after evaluation of recursive operations to be compressed down to a single step that returns the expected continuation, returning a thunk.
  • cont-error. This represents the continuation after some error happened. It is equivalent to a terminal continuation, because if Lurk finds an error, then a cont-error is returned as the output of the Lurk program evaluation.
  • lookup(pointer, cont). This continuation is responsible for doing lookups in the environment, returning a thunk.
  • unop(operator, cont). The unary operator continuation doesn’t have an unevaluated argument, therefore it can be executed in a single step, returning a thunk.
  • binop(operator, pointer, pointer, cont). This is the first step of a binary operator, where the unevaluated argument pointer is used to construct a binop2\rm{\footnotesize{binop2}} continuation.
  • binop2(operator, pointer, cont). The second step of the binary operator can finish the work returning a thunk.
  • if(pointer, cont). This continuation is used to select which unevaluated expression will be returned to be evaluated next, according to the conditional expression that is currently being evaluated.
  • let(pointer, pointer, pointer, cont). The first argument represents a variable, the second is the body, the third is the saved environment, while the fourth is the continuation. We have that the continuation of a let expression is to extend the environment with the variable and return the body.
  • letrec(pointer, pointer, pointer, cont). Similarly to let, but allowing recursion.
  • dummy. A dummy continuation is useful for maintaining some invariants in the design, allowing us to control the call of the apply-continuation algorithm.
  • terminal. It is the final continuation of a successful Lurk program evaluation.
  • emit(pointer). This continuation is useful for creating intermediary publicly visible output of Lurk evaluations.

In the below Figures, we show the DAG structure of Lurk continuations. For each specific point in time, this DAG is just a stack of continuations, starting with the outermost continuation (see left part of Figure 6), growing and reducing the stack accordingly as we reduce expressions. As an example, we evaluate (+ ( 4 5) ( 11 2))\rm{\footnotesize{\lparen+~\lparen*~4~5\rparen~\lparen*~11~2\rparen\rparen}}.

Expression DAG
Expression DAG
Continuation DAG
Continuation DAG

Figure 6:Reduction step 1\rm{\small{Figure~6: Reduction~step~1}}



Expression DAG
Expression DAG
Continuation DAG
Continuation DAG

Figure 7:Reduction step 2\rm{\small{Figure~7: Reduction~step~2}}


After we compute one reduction step over the input expression, we find a + operator at the head of the expression, which is one among many possible binary operations supported by Lurk. In order to evaluate the two input arguments of this binary operation, we need to create two continuations, respectively binop and binop2, saving context data to allow the calculation and saving the previous continuation to be able to return the control flow to the expected place.

The first step is to create a binop continuation, as shown in Figure 7, saving the second argument, which is not yet evaluated, such that we evaluate it in the next reduction step.

Because the first argument itself is the binary operation ( 4 5)\rm{\footnotesize{\lparen*~4~5\rparen}} (see Figure 7), we need to repeat the same logic again, creating a new binop continuation in the stack, saving the second expression, as before.

Expression DAG
Expression DAG
Continuation DAG
Continuation DAG

Figure 8:Reduction step 3\rm{\small{Figure~8: Reduction~step~3}}



Expression DAG
Expression DAG
Continuation DAG
Continuation DAG

Figure 9:Reduction step 4\rm{\small{Figure~9: Reduction~step~4}}



Expression DAG
Expression DAG
Continuation DAG
Continuation DAG

Figure 10:Reduction step 5\rm{\small{Figure~10: Reduction~step~5}}


Now we have reached a point where the first argument of the binary operation is a self-evaluated expression: the number 4. We can then proceed by applying the continuation, which is responsible for removing itself from the continuation stack. I.e. the leaf binop continuation is removed and replaced with a binop2 continuation that will be applied when the second argument is evaluated. As a result, a thunk containing the evaluations of both arguments is created, such that the binary operation can finally be applied. We then return the control flow to the saved continuation, which is represented here as the previous continuation in the stack.

Since the result of the binary operation is the number 20, which is a self-evaluated expression, we can apply the continuation of the addition expression, creating a corresponding binop2.

Now we evaluate both input arguments of the first multiplication, producing a thunk containing all the information we need to proceed. For instance, we create a binop2 continuation containing the result 20.

Expression DAG
Expression DAG
Continuation DAG
Continuation DAG

Figure 11:Reduction step 6\rm{\small{Figure~11: Reduction~step~6}}



Expression DAG
Expression DAG
Continuation DAG
Continuation DAG

Figure 12:Reduction step 7\rm{\small{Figure~12: Reduction~step~7}}



Expression DAG
Expression DAG
Continuation DAG
Continuation DAG

Figure 13:Reduction step 8\rm{\small{Figure~13: Reduction~step~8}}


Similarly, we evaluate the second multiplication following the same steps. Next, we evaluate the number 2, which is the first argument, and save the number 11 for the next reduction step.

Next, we evaluate the number 11, the second argument, and create a thunk to finish the calculation.

The thunk returns 42 as the result of the addition of 20 and 22 to the previous continuation, which is the outermost continuation. Therefore we simply replace it by the terminal continuation and finish the execution.

Environment

The environment is a data structure that contains pairs of variables and values, such that the variables of Lurk expressions can be evaluated. This simply means that variables are replaced by their values.

Next we show a LET expression, which allows us to create variables in the environment. Briefly, a LET expression receives as input argument a list of pairs (variable, value), and a body, which is an expression that will be evaluated right after the list of variables is inserted into the environment.

Expression DAG
Expression DAG
Continuation DAG
Continuation DAG

Figure 14:Reduction step 9\rm{\small{Figure~14: Reduction~step~9}}


> (let ((a 1)) a)
 INFO  lurk::eval > Frame: 0
  Expr: (LET ((A 1)) A)
  Env: NIL
Cont: Outermost
 INFO  lurk::eval > Frame: 1
Expr: 1
Env: NIL
Cont: Let{ var: A, body: A, saved_env: NIL, continuation: Outermost }
 INFO  lurk::eval > Frame: 2
Expr: A
Env: ((A . 1))
Cont: Tail{ saved_env: NIL, continuation: Outermost }
 INFO  lurk::eval > Frame: 3
Expr: 1
Env: NIL
Cont: Terminal
[3 iterations] => 1

Figure 15:LET example\rm{\small{Figure~15: LET~example}}

Store implementation

In this section, we explain how the store is implemented in Lurk. Although the store is not essential to constructing the circuit, understanding its implementation is useful because the circuit must have components that behave equivalently. We replace the store implementation with corresponding mechanisms in the circuit to preserve the same properties. It is important to note that the underlying hash functions used in the store and the circuit are completely different, and there is no connection between the hash maps used in the store implementation and the use of Poseidon in the circuit.

The store is a set of pointers that uses hash maps to implement content-addressable storage. These maps allow us to insert pointers in the set in constant time. A distinct set is created for each pointer tag, including continuation pointers. When a new pointer is created, its content is inserted into the set, and an index is generated. This index is used as a raw pointer, which is useful for accessing the content later.

The following are the sets defined in the Lurk store: cons, comm, fun, sym, num, str, thunk, call, call, call2, tail, lookup, unop, binop, binop2, if, let, letrec, and emit. The store provides a convenient interface to create and access its content, such as the get and intern functions for each set.

An opaque pointer has a negative raw pointer, therefore it can’t be used as an index in the store. Hence such pointers do not have known content.

How to implement the store in the circuit:

Instead of using hash maps, the circuit uses non-determinism to construct cryptographic hash relations in the form y = H(x), where before starting the construction of the circuit, the prover already knows both the preimage, x, and the result, y, of the hash function. In the circuit, the prover shows all the intermediate steps necessary to calculate y from x. Hence, Lurk objects are represented as a Merkle-DAG, where arrows are constructed using this one-way hash function. To show that a certain path in the Merkle-DAG is valid, we non-deterministically provide the sequence of hash relations that corresponds to the needed witness (instead of indexing the hash maps, as we did in the store implementation). The prover can still use the hash map to efficiently build the witness, but from the perspective of the verifier, there is no store.

Note In the context of zero-knowledge proofs, non-determinism refers to the use of non-deterministic auxiliary inputs to improve the efficiency of the verification process. While computing the value of a function can be computationally expensive, verifying whether an already computed answer is correct is typically less costly. Provers can use non-deterministic auxiliary inputs to provide additional information, but high-level programs that compute the answer often lack this information. The benefits of non-determinism can be illustrated with the example of deciding whether two n-element lists are sorted copies of each other. For more on non-determinism, see 15

Poseidon

Poseidon 13 is a hash function tailored for zero-knowledge proofs. Its input is a list of finite field elements, with the output given as a single field element. It is designed in a modular way, being appropriate in different scenarios, from Fiat-Shamir implementation to authenticated encryption 16.

Here we use it as a hash function to construct pointers, allowing us to have not only content addressable storage, but also a clean solution for recursion, since pointers can be used to represent expressions that contain other expressions in a natural way.

Figure 16: Allocated pointer
Figure 16: Allocated pointer

Figure 17: Hash preimage
Figure 17: Hash preimage

Poseidon circuit

Neptune 17 is a rust implementation of Poseidon, which allows efficient proof construction. In particular, the total number of constraints is 286 for the 4-ary instantiation, 334 for the 6-ary instantiation, and 385 for the 8-ary instantiation

Pointers in the circuit

While pointers are implemented outside the circuit using dictionaries, we need a different solution for zero-knowledge circuits. One solution is to use hash functions as for example occurs in the construction of Merkle Trees. Poseidon hash function is allegedly a good solution for Lurk pointers, since it can be implemented using a small number of R1CS constraints.

As shown previously, each possible expression or continuation can be represented using at most 8 field elements. Therefore, we designed the system to allow the hash preimage to be formed by at most 4 components, as shown in Figure 17

Next, we describe in detail how we construct pointers in the circuit. In summary, a cons operation requires the 4-ary hash, functions require the 6-ary hash, and generic pointers require 8-ary hash.

  • Let a=CONSTRUCT(cont-tag,components)a = \gad{CONSTRUCT}(\var{cont-tag}, \var{components}):
    1. Call hash=POSEIDON(components)\var{hash} = \gad{POSEIDON}(\var{components}) - using the 8-ary hash.
    2. Return ALLOC-PTR(cont-tag,hash)\var{ALLOC-PTR}(\var{cont-tag}, \var{hash}).
  • Let CONSTRUCT-CONS(car,cdr)\gad{CONSTRUCT-CONS}(\var{car}, \var{cdr}):
    1. Let hash=POSEIDON(car,cdr)\var{hash} = \gad{POSEIDON}(\var{car}, \var{cdr}), using the 4-ary hash.
    2. Return ALLOC-PTR(globals.cons-tag,hash)\gad{ALLOC-PTR}(\var{globals.cons-tag}, \var{hash}).
  • Let CONSTRUCT-FUN(arg,body,closed-env)\gad{CONSTRUCT-FUN}(\var{arg}, \var{body}, \var{closed-env}):
    1. Call hash=POSEIDON(arg,body,closed)\var{hash} = \gad{POSEIDON}(\var{arg}, \var{body}, \var{closed}), using the 6-ary hash.
    2. Return ALLOC-PTR(globals.fun-tag,hash)\gad{ALLOC-PTR}(\var{globals.fun-tag}, \var{hash}).
  • Let CONSTRUCT-LIST(elts)\gad{CONSTRUCT-LIST}(\var{elts}):
    1. Let first=elts[0]\var{first} = \var{elts}[0].
    2. Let rest-of-elts=elts[1. . ]\var{rest-of-elts} = \var{elts}[1.~.~].
    3. Call tail=CONSTRUCT-LIST(rest-of-elts)\var{tail} = \gad{CONSTRUCT-LIST}(\var{rest-of-elts}).
    4. Return CONSTRUCT-CONS(first,tail)\gad{CONSTRUCT-CONS}(\var{first}, \var{tail}).
  • Let CONSTRUCT-THUNK(val,cont)\gad{CONSTRUCT-THUNK}(\var{val}, \var{cont}):

Error system

Lurk programs can generate error continuations, which indicate an unrecoverable situation. This error is used to finalize the computation. Therefore, it is possible to construct zero-knowledge proofs that a program generated this error. It is important to remark that the error doesn’t carry detailed information to identify the cause of the error. The error can happen for malformed programs, like unary operations that receive more than one argument, or binary operations that receive fewer than two arguments. It can also happen if someone tries to divide by zero. However, the zero-knowledge proofs do not reveal which kind of error occurred.

As an example, when we try to divide by zero, the final output continuation is Error, which is a global pointer used instead of Terminal, such that a verifier can recognize that this program didn’t finish successfully.

> (/ 42 0)
INFO lurk::eval > Frame: 0 Expr: (/ 42 0)
Env: NIL
Cont: Outermost

INFO lurk::eval > Frame: 1 Expr: 42
Env: NIL
Cont: Binop{ operator: Quotient, unevaled_args: (0), saved_env: NIL, continuation: Outermost }

INFO lurk::eval > Frame: 2 Expr: 0
Env: NIL
Cont: Binop2{ operator: Quotient, evaled_arg: 42, continuation: Outermost }

INFO lurk::eval > Frame: 3 Expr: 0
Env: NIL
Cont: Error

Figure 18:Error example\rm{\small{Figure~18: Error~example}}

High-level algorithms

Now we can start to describe how Lurk programs are translated into R1CS constraints. The strategy is to follow a top-down approach, by first showing some high-level components and saying how they interact. Later we can present the low-level construction of constraints.

Self-evaluated expressions: Some expressions already are in the final stage of evaluation (as, for example, literal expressions like NIL, literal numbers, and characters). The complete list of self-evaluated expressions is given by the following tags: nil, num, fun, char, str, comm.

Unary expressions: As the name suggests, unary expressions correspond to operations that receive only one input argument, which in Lurk are given by expressions that must be evaluated before the unary operation is evaluated.

  • atom. It returns true\tt{\footnotesize{true}} if and only if the input argument is not a list.
  • car. This operation receives as input a list and returns its first element.
  • cdr. Complementary to the previous item, it receives a list and returns everything except for the first element.
  • emit. This operation emits an intermediary output expression, so that it can be externally viewed.
  • commit. Creates a commitment to the received expression.
  • open. It is used to open a commitment.
  • secret. It returns the secret element used to create a given commitment.
  • num. Interprets the finite field element as a number.
  • u64. Interprets the finite field element as a 64-bit unsigned integer.
  • comm. Interprets the finite field element as a commitment.
  • char. Interprets the finite field element as a character.

Below is example of a unary expression evaluation.

> (atom 42)
INFO lurk::eval > Frame: 0 Expr: (ATOM 42)
Env: NIL
Cont: Outermost

INFO lurk::eval > Frame: 1 Expr: 42
Env: NIL
Cont: Unop{ operator: Atom, continuation: Outermost }

INFO lurk::eval > Frame: 2 Expr: T
Env: NIL
Cont: Terminal

Figure 19:Unary operation example\rm{\small{Figure~19: Unary~operation~example}}

As a first step, a unop continuation is created, pointing to the outermost continuation. Since 42 is a self-evaluated expression, we can continue the evaluation of this continuation, by returning T.

Binary expressions:

  • +. Addition of the received elements.
  • -. Subtraction of the received elements.
  • *. Multiplication of the received elements.
  • /. Division of the received elements.
  • %. Modular reduction of received elements.
  • >. Greater than.
  • >=. Greater than or equal.
  • >. Less than.
  • <=. Less than or equal.
  • =. Equality test between numbers.
  • eq. Equality test between pointers.
  • cons. Creates a list formed by the concatenation of the received elements.
  • strcons. Creates a list formed by the concatenation of a char and a string.
  • begin. Allows evaluation of multiple expressions, returning the result of the last one.
  • hide. Uses the first argument as a secret to create a commitment to the second argument.

Here is an example showing how Lurk evaluates an addition:

> (+ 2 40)
INFO lurk::eval > Frame: 0 Expr: (+ 2 40)
Env: NIL
Cont: Outermost

INFO lurk::eval > Frame: 1 Expr: 2
Env: NIL
Cont: Binop{ operator: Sum, unevaled_args: (40), saved_env: NIL, continuation: Outermost }

INFO lurk::eval > Frame: 2 Expr: 40
Env: NIL
Cont: Binop2{ operator: Sum, evaled_arg: 2, continuation: Outermost }

INFO lurk::eval > Frame: 3 Expr: 42
Env: NIL
Cont: Terminal

[3 iterations] => 42

Figure 20:Binary operation example\rm{\small{Figure~20: Binary~operation~example}}

The addition expression is reduced by creating a binop continuation containing the second input argument. The first argument is a self-evaluated expression, after which we can create a binop2 continuation, where the second argument will be evaluated, and the sum of both results is returned as a terminal continuation.

Equality expressions: Equality operators are binary operations, therefore we have binop and binop2 continuations.

  • =. Used to check equality of numbers.
  • eq. Used to check equality of pointers.

Comparison expressions:

  • >. Greater than operation.
  • >=. Greater than or equal operation.
  • <. Less than operation.
  • <=. Less than or equal operation.

Conditional expression:

  • if. Receives 3 expressions as arguments. If the first one evaluates to something different from nil, then the result is given by the evaluation of the second expression. Otherwise, the result is the evaluation of the third one.

Functional commitments

General work related to functional commitments can be found here 18 19 20. Functional commitments having a function-privacy property, specifically, are described in this paper 21. We implement functional commitments as first-class Lurk operations. Specifically, we use cons in order to compute the hash of a function and a secret number. Later, we can prove this function evaluates to determined values. In order to construct functional commitments, we need some building blocks, which Lurk provides natively, and whose high-level description is the following:

  • hide(secret, maybe-payload)
  1. If there aren't exactly 2 input arguments, return error.
  2. Otherwise, compute the commitment using all arguments as input of a 3-ary Poseidon instance.
  3. Allocate a pointer to it.
  4. Return this pointer.
  • commit(payload)
  1. If there isn’t exactly 1 input argument, return error.
  2. Otherwise, compute the commitment, using zero as the value of the secret.
  3. Allocate a pointer to it.
  4. Return this pointer.
  • open(commitment)
  1. If there isn’t exactly 1 input argument, return error.
  2. Otherwise, if the commitment is known, find the corresponding pair (secret, payload).
  3. Allocate a pointer to the payload.
  4. Return this pointer.
  • secret(commitment)
  1. If there isn’t exactly 1 input argument, return error.
  2. Otherwise, if the commitment is known, find the corresponding pair (secret, payload).
  3. Allocate a pointer to the secret.
  4. Return this pointer.
  • comm(value)
  1. Take as input a pointer to the value, and it finds the field element given by value.hash().
  2. Allocate a pointer whose tag is globals.comm-tag and the hash is given value.hash().
  3. Return this pointer.

The commitment scheme is implemented by concatenating a pointer and a secret field element and computing the Poseidon hash function. Since functions are defined using lambda expressions, we obtain functional commitments basically for free.

Globals

We allocate constants in order to represent global data. For instance, we have global pointers, such as the terminal pointer, which can only be used in the last frame to indicate a program finished successfully. We also have an error pointer for programs that didn’t finish correctly. The first frame determines the outermost continuation, which has an outermost continuation pointer. Another important global pointer is the one that points to the symbol nil.

We also have global constants for each different tag in the system. Those constants are useful for comparing runtime data and determining which kind of pointer we are dealing with.

Beyond that, we have constants for Boolean variables. We pair true\tt{\footnotesize{true}} with 1 and false\tt{\footnotesize{false}} with 0. Finally, we have a constant of value 0 for default numbers.

Reduce expression

In this section, we explain the step-by-step reduction of complex expressions.

First, we provide an overview in Algorithm 3.1, which constructs multicase clauses selected based on the expression tag. Each distinct tag has a different reduction method, but the overall process remains the same. The result of the reduction provides a new triple of expression, environment, and continuation. Additionally, it determines whether we need to apply the continuation, resulting in a new IO consisting of the triple expression, environment, and continuation. For certain continuations, we need to create a thunk.

Reducing self-evaluated expressions is straightforward. The expression, environment, and continuation remain unchanged, but we must apply the continuation to the evaluated expression.

If the expression is a thunk, we need to verify hash consistency before applying the continuation.

If the expression is a symbol or a cons, we have two distinct scenarios that require detailed explanations. Therefore, we will dedicate a section to each scenario.


Circuit 3.1  Reduce-Expression\cirtitle{Circuit~3.1}~\cir{Reduce-Expression} \\
INPUT expr,env,cont,not-dummy,witness,allocated-cons-witness,allocated-cont-witness \cirio{INPUT} \var{expr},\var{env},\var{cont},\var{not-dummy},\var{witness}, \var{allocated-cons-witness}, \var{allocated-cont-witness}.
OUTPUT expr,env,cont \cirio{OUTPUT} \var{expr},\var{env},\var{cont}.
  1. Compute and add clauses for self-evaluated expressions. \com{Compute and add clauses for self-evaluated expressions.}
  2. Compute and add thunk clause. \com{Compute and add thunk clause.}
  3. Compute and add sym clause. \com{Compute and add sym clause.}
  4. Compute and add cons clause. \com{Compute and add cons clause.}
  5. Get result from multicase gadget. \com{Get result from multicase gadget.}
  6. Apply continuation. \com{Apply continuation.}
  7. Make thunk. \com{Make thunk.}
  8. Error handling. \com{Error handling.}
  9. Return (expr,env,cont)(\var{expr}, \var{env}, \var{cont}).

Specifically, we do the following:

  1. Let clauses=MULTICASE-CLAUSES()\var{clauses} = \gad{MULTICASE-CLAUSES}().
  2. clauses.ADD-CLAUSE(tag.nil,expr,env,cont,witness,globals)\var{clauses}.\gad{ADD-CLAUSE}(\var{tag.nil},\var{expr},\var{env},\var{cont},\var{witness},\var{globals}).
  3. clauses.ADD-CLAUSE(tag.num,expr,env,cont,witness,globals)\var{clauses}.\gad{ADD-CLAUSE}(\var{tag.num},\var{expr},\var{env},\var{cont},\var{witness},\var{globals}).
  4. clauses.ADD-CLAUSE(tag.fun,expr,env,cont,witness,globals)\var{clauses}.\gad{ADD-CLAUSE}(\var{tag.fun},\var{expr},\var{env},\var{cont},\var{witness},\var{globals}).
  5. clauses.ADD-CLAUSE(tag.char,expr,env,cont,witness,globals)\var{clauses}.\gad{ADD-CLAUSE}(\var{tag.char},\var{expr},\var{env},\var{cont},\var{witness},\var{globals}).
  6. clauses.ADD-CLAUSE(tag.str,expr,env,cont,witness,globals)\var{clauses}.\gad{ADD-CLAUSE}(\var{tag.str},\var{expr},\var{env},\var{cont},\var{witness},\var{globals}).
  7. clauses.ADD-CLAUSE(tag.comm,expr,env,cont,witness,globals)\var{clauses}.\gad{ADD-CLAUSE}(\var{tag.comm},\var{expr},\var{env},\var{cont},\var{witness},\var{globals}).
  8. clauses.ADD-CLAUSE(tag.key,expr,env,cont,witness,globals)\var{clauses}.\gad{ADD-CLAUSE}(\var{tag.key},\var{expr},\var{env},\var{cont},\var{witness},\var{globals}).
  9. clauses.ADD-CLAUSE(tag.u64,expr,env,cont,witness,globals)\var{clauses}.\gad{ADD-CLAUSE}(\var{tag.u64},\var{expr},\var{env},\var{cont},\var{witness},\var{globals}).
  10. Let cont-is-terminal=ALLOC-TAG-EQUAL(cont.tag(),globals.terminal-tag())\var{cont-is-terminal} = \gad{ALLOC-TAG-EQUAL}(\var{cont.tag}(), \var{globals.terminal-tag}()).
  11. Let cont-is-error=ALLOC-TAG-EQUAL(cont.tag(),globals.error-tag())\var{cont-is-error} = \gad{ALLOC-TAG-EQUAL}(\var{cont.tag}(), \var{globals.error-tag}()).
  12. Let expr-is-thunk=ALLOC-TAG-EQUAL(expr.tag(),globals.thunk-tag)\var{expr-is-thunk} = \gad{ALLOC-TAG-EQUAL}(\var{expr.tag}(), \var{globals.thunk-tag}).
  13. Let (expr-thunk-hash,expr-thunk-value,expr-thunk-continuation)=expr.ALLOCATE-THUNK-COMPONENTS-UNCONSTRAINED()(\var{expr-thunk-hash}, \var{expr-thunk-value}, \var{expr-thunk-continuation}) = \var{expr}.\gad{ALLOCATE-THUNK-COMPONENTS-UNCONSTRAINED}().
  14. Call IMPLIES-EQUAL(expr-is-thunk,expr-thunk-hash,expr.hash())\gad{IMPLIES-EQUAL}(\var{expr-is-thunk}, \var{expr-thunk-hash}, \var{expr.hash}()).
  15. Call clauses.ADD-CLAUSE(tag.thunk,expr-thunk-value,env,expr-thunk-continuation,globals.true-num)\var{clauses}.\gad{ADD-CLAUSE}(\var{tag.thunk}, \var{expr-thunk-value}, \var{env}, \var{expr-thunk-continuation}, \var{globals.true-num}).
  16. Let reduce-sym-not-dummy=ALLOC-TAG-EQUAL(expr.tag(),globals.sym-tag)\var{reduce-sym-not-dummy} = \gad{ALLOC-TAG-EQUAL}(\var{expr.tag}(), \var{globals.sym-tag}).
  17. Let cont-is-terminal-or-error=OR(cont-is-terminal,cont-is-error)\var{cont-is-terminal-or-error} = \gad{OR}(\var{cont-is-terminal}, \var{cont-is-error}).
  18. Let cont-is-not-terminal-or-error=cont-is-terminal-or-error.NOT()\var{cont-is-not-terminal-or-error} = \var{cont-is-terminal-or-error}.\gad{NOT}().
  19. Let reduce-sym-not-dummy=AND(reduce-sym-not-dummy,cont-is-not-terminal-or-error)\var{reduce-sym-not-dummy} = \gad{AND}(\var{reduce-sym-not-dummy}, \var{cont-is-not-terminal-or-error}).
  20. Let (result,sym-env,sym-cont,sym-apply-cont)=Reduce-Sym(expr,env,cont,reduce-sym-not-dummy,witness,allocated-cons-witness,allocated-cont-witness)(\var{result}, \var{sym-env}, \var{sym-cont}, \var{sym-apply-cont}) = \cir{Reduce-Sym}(\var{expr}, \var{env}, \var{cont}, \var{reduce-sym-not-dummy}, \var{witness}, \var{allocated-cons-witness}, \var{allocated-cont-witness}).
  21. Call clauses.ADD-CLAUSE(tag.sym,sym-result,sym-env,sym-cont,sym-apply-cont)\var{clauses}.\gad{ADD-CLAUSE}(\var{tag.sym}, \var{sym-result}, \var{sym-env}, \var{sym-cont}, \var{sym-apply-cont}).
  22. Let expr-is-cons=ALLOC-TAG-EQUAL(expr.tag(),globals.cons-tag)\var{expr-is-cons} = \gad{ALLOC-TAG-EQUAL}(\var{expr.tag}(), \var{globals.cons-tag}).
  23. Let reduce-cons-not-dummy=AND(expr-is-cons,cont-is-not-terminal-or-error)\var{reduce-cons-not-dummy} = \gad{AND}(\var{expr-is-cons}, \var{cont-is-not-terminal-or-error}).
  24. Let (cons-result,cons-env,cons-cont,cons-apply-cont)=Reduce-cons(expr,env,cont,reduce-cons-not-dummy,witness,allocated-cons-witness,allocated-cont-witness)(\var{cons-result}, \var{cons-env}, \var{cons-cont}, \var{cons-apply-cont}) = \var{Reduce-cons}(\var{expr}, \var{env}, \var{cont}, \var{reduce-cons-not-dummy}, \var{witness}, \var{allocated-cons-witness}, \var{allocated-cont-witness}).
  25. Call clauses.ADD-CLAUSE(tag.cons,cons-result,cons-env,cons-cont,cons-apply-cont)\var{clauses}.\gad{ADD-CLAUSE}(\var{tag.cons}, \var{cons-result}, \var{cons-env}, \var{cons-cont}, \var{cons-apply-cont}).
  26. Let results=MULTICASE(expr.tag(),clauses)\var{results} = \var{MULTICASE}(\var{expr.tag}(), \var{clauses}).
  27. Let first-result-expr0=results[0])\var{first-result-expr0} = \var{results}[0]).
  28. Let first-result-expr=PICK(cont-is-terminal-or-error,globals.nil-ptr,first-result-expr0)\var{first-result-expr} = \var{PICK}(\var{cont-is-terminal-or-error}, \var{globals.nil-ptr}, \var{first-result-expr0}).
  29. Let first-result-env=results[1]\var{first-result-env} = \var{results}[1].
  30. Let first-result-cont=results[2]\var{first-result-cont} = \var{results}[2].
  31. Let first-result-apply-continuation=results[6]\var{first-result-apply-continuation} = \var{results}[6].
  32. Let apply-continuation-boolean0=ALLOC-IS-ZERO(first-result-apply-continuation).NOT()\var{apply-continuation-boolean0} = \gad{ALLOC-IS-ZERO}(\var{first-result-apply-continuation}).\gad{NOT}().
  33. Let apply-continuation-boolean=AND(apply-continuation-boolean0,cont-is-not-terminal-or-error)\var{apply-continuation-boolean} = \gad{AND}(\var{apply-continuation-boolean0}, \var{cont-is-not-terminal-or-error}).
  34. Let apply-continuation-results=Apply-Continuation(expr,env,cont,witness,globals,flag)\var{apply-continuation-results}=\cir{Apply-Continuation}(\var{expr},\var{env},\var{cont},\var{witness},\var{globals},\var{flag}).
  35. Let apply-continuation-make-thunk=apply-continuation-results[3]\var{apply-continuation-make-thunk} = \var{apply-continuation-results}[3].
  36. Let result-expr0=PICK(apply-continuation-boolean,apply-continuation-results[0],first-result-expr)\var{result-expr0} = \gad{PICK}(\var{apply-continuation-boolean}, \var{apply-continuation-results}[0], \var{first-result-expr}).
  37. Let result-env0=PICK(apply-continuation-boolean,apply-continuation-results[1],first-result-env)\var{result-env0} = \gad{PICK}(\var{apply-continuation-boolean}, \var{apply-continuation-results}[1], \var{first-result-env}).
  38. Let result-cont0=PICK(apply-continuation-boolean,apply-continuation-results[2],first-result-cont)\var{result-cont0} = \gad{PICK}(\var{apply-continuation-boolean}, \var{apply-continuation-results}[2], \var{first-result-cont}).
  39. Let make-thunk-num=PICK(apply-continuation-boolean,apply-continuation-make-thunk,globals.false-num)\var{make-thunk-num} = \gad{PICK}(\var{apply-continuation-boolean}, \var{apply-continuation-make-thunk}, \var{globals.false-num}).
  40. Let make-thunk-boolean=ALLOC-IS-ZERO(make-thunk-num).NOT()\var{make-thunk-boolean} = \gad{ALLOC-IS-ZERO}(\var{make-thunk-num}).\gad{NOT}().
  41. Let thunk-results=Make-Thunk(result-cont0,result-expr0,result-env0,make-thunk-boolean,allocated-cont-witness)\var{thunk-results} = \cir{Make-Thunk}(\var{result-cont0}, \var{result-expr0}, \var{result-env0}, \var{make-thunk-boolean}, \var{allocated-cont-witness}).
  42. Let result-expr-candidate=PICK(make-thunk-boolean,thunk-results[0],result-expr0)\var{result-expr-candidate} = \gad{PICK}(\var{make-thunk-boolean}, \var{thunk-results}[0], \var{result-expr0}).
  43. Let result-env-candidate=PICK(make-thunk-boolean,thunk-results[1],result-env0)\var{result-env-candidate} = \gad{PICK}(\var{make-thunk-boolean}, \var{thunk-results}[1], \var{result-env0}).
  44. Let result-cont-candidate=PICK(make-thunk-boolean,thunk-results[2],result-cont0)\var{result-cont-candidate} = \gad{PICK}(\var{make-thunk-boolean}, \var{thunk-results}[2], \var{result-cont0}).
  45. Let result-expr=PICK(cont-is-terminal-or-error,expr,result-expr-candidate)\var{result-expr} = \gad{PICK}(\var{cont-is-terminal-or-error}, \var{expr}, \var{result-expr-candidate}).
  46. Let result-env=PICK(cont-is-terminal-or-error,env,result-env-candidate)\var{result-env} = \gad{PICK}(\var{cont-is-terminal-or-error}, \var{env}, \var{result-env-candidate}).
  47. Let result-cont=PICK(cont-is-terminal-or-error,cont,result-cont-candidate)\var{result-cont} = \gad{PICK}(\var{cont-is-terminal-or-error}, \var{cont}, \var{result-cont-candidate}).
  48. Return (result-expr,result-env,result-cont)(\var{result-expr}, \var{result-env}, \var{result-cont}).

Reduce symbol

To reduce symbol expressions, we must determine whether the symbol is a self-evaluated symbol or a variable that needs to be looked up in the environment. If it’s the latter, we must determine whether we have a regular or recursive environment. In a regular environment, we compare the given symbol with the first binding in the environment. If it’s the variable we’re looking for, we return the corresponding value in a thunk. Otherwise, we recursively call the lookup method in the remaining bindings in the environment. For a recursive environment, we follow the same strategy, but using closures when the value to be used is a function.

To carry out these steps, we first analyze whether the expression is a self-evaluated symbol, like NIL or T, or a variable name that we must look up in the environment. Distinguishing between these possibilities requires multiple booleans and using car-cdr to split the expression and environment in a way that lets us identify code paths leading to the end of the recursive lookup or error continuations.

Finally, we compute a boolean that identifies the control flow. In other words, we determine whether to apply the continuation or not. This boolean is crucial because we must always run the part of the circuit corresponding to apply_cont(), but we restrict the circuit to use dummy variables when this boolean value is false.


Circuit 3.2  Reduce-Sym\cirtitle{Circuit~3.2}~\cir{Reduce-Sym} \\
INPUT expr,env,cont,not-dummy,witness,allocated-cons-witness,allocated-cont-witness \cirio{INPUT} \var{expr},\var{env},\var{cont},\var{not-dummy},\var{witness}, \var{allocated-cons-witness}, \var{allocated-cont-witness}.
OUTPUT expr,env,cont \cirio{OUTPUT} \var{expr},\var{env},\var{cont}.
  1. Calculate condition terms. \com{Calculate condition terms.}
  2. Calculate output predicate. \com{Calculate output predicate.}
  3. Calculate conditions. \com{Calculate conditions.}
  4. Calculate implications. \com{Calculate implications.}

The algorithm receives as input a triple (expr,env,cont)(\var{expr}, \var{env}, \var{cont}), together with a variable called not-dummy\var{not-dummy}, which is a Boolean whose value is false when the input expression is not a symbol. In this case, Reduce-Sym\cir{Reduce-Sym} constraints are not actually used, which means those constraints will contain only dummy values. The algorithm also receives the witness, the store, and the globals as input.

The circuit described here mimics the evaluation of expressions whose tag is equal to globals.sym-tag\var{globals.sym-tag}. However, if we follow exactly the same steps as implemented in eval.rs, then some constraints would be unnecessarily repeated. Hence, in order to eliminate those constraints, we need to pay the price of making it a bit harder to guarantee that the circuit implementation corresponds to what is implemented in eval.rs. We clarify here the differences between both worlds, and show why they are equivalent.

  • Circuit Reduce-Symbol( expr: AllocatedPtr, env: AllocatedPtr, cont: AllocatedPtr, not_dummy: Boolean, witness: Witness ):
    1. Calculate condition terms:
      1. Let output-expr=witness.prethunk-output-expr\var{output-expr} = \var{witness}.\var{prethunk-output-expr}.
      2. Let output-env=witness.prethunk-output-env\var{output-env} = \var{witness}.\var{prethunk-output-env}.
      3. Let output-cont=witness.prethunk-output-cont\var{output-cont} = \var{witness}.\var{prethunk-output-cont}.
      4. Let sym-is-nil=expr.IS-NIL()\var{sym-is-nil} = \var{expr}.\gad{IS-NIL}().
      5. Let sym-is-t=ALLOC-EQUAL(expr,globals.t-ptr)\var{sym-is-t} = \gad{ALLOC-EQUAL}(\var{expr}, \var{globals.t-ptr}).
      6. Let sym-is-nil-or-t=OR(sym-is-nil,sym-is-t)\var{sym-is-nil-or-t} = \gad{OR}(\var{sym-is-nil}, \var{sym-is-t}).
      7. Let sym-is-self-evaluating=AND(sym-is-nil-or-t,not-dummy)\var{sym-is-self-evaluating} = \gad{AND}(\var{sym-is-nil-or-t}, \var{not-dummy}).
      8. Let sym-otherwise=AND(sym-is-self-evaluating.NOT(),not-dummy)\var{sym-otherwise} = \gad{AND}(\var{sym-is-self-evaluating}.\gad{NOT}(), \var{not-dummy}).
      9. Let env-is-nil=env.IS-NIL()\var{env-is-nil} = \var{env}.\gad{IS-NIL}()
      10. Let env-not-nil=env-is-nil.NOT()\var{env-not-nil} = \var{env-is-nil}.\gad{NOT}().
      11. Let env-not-dummy=sym-otherwise\var{env-not-dummy} = \var{sym-otherwise}.
      12. Let (binding,smaller-env)=CAR-CDR-NAMED(env,names.env,allocated-cons-witness,env-not-dummy)(\var{binding}, \var{smaller-env}) = \gad{CAR-CDR-NAMED}(\var{env}, \var{names.env}, \var{allocated-cons-witness}, \var{env-not-dummy}).
      13. Let main=AND(sym-otherwise,env-not-nil)\var{main} = \gad{AND}(\var{sym-otherwise}, \var{env-not-nil}).
      14. Let binding-is-nil=binding.IS-NIL()\var{binding-is-nil} = \var{binding}.\gad{IS-NIL}().
      15. Let binding-not-nil=binding-is-nil.NOT()\var{binding-not-nil} = \var{binding-is-nil}.\gad{NOT}().
      16. Let binding-is-cons=IS-CONS(binding)\var{binding-is-cons} = \gad{IS-CONS}(\var{binding}).
      17. Let env-car-not-dummy=AND(main,binding-is-cons)\var{env-car-not-dummy} = \gad{AND}(\var{main}, \var{binding-is-cons}).
      18. Let (var-or-rec-binding,val-or-more-rec-env)=CAR-CDR-NAMED(binding,names.env-car,allocated-cons-witness,env-car-not-dummy)(\var{var-or-rec-binding}, \var{val-or-more-rec-env}) = \gad{CAR-CDR-NAMED}(\var{binding}, \var{names.env-car}, \var{allocated-cons-witness}, \var{env-car-not-dummy}).
      19. Let var-or-rec-binding-is-sym=IS-SYM(var-or-rec-binding)\var{var-or-rec-binding-is-sym} = \gad{IS-SYM}(\var{var-or-rec-binding}).
      20. Let var-or-rec-binding-is-cons=IS-CONS(var-or-rec-binding)\var{var-or-rec-binding-is-cons} = \gad{IS-CONS}(\var{var-or-rec-binding}).
      21. Let var-or-rec-binding-is-sym-or-cons=OR(var-or-rec-binding-is-sym,var-or-rec-binding-is-cons)\var{var-or-rec-binding-is-sym-or-cons} = \gad{OR}(\var{var-or-rec-binding-is-sym}, \var{var-or-rec-binding-is-cons}).
      22. Let with-binding=AND(main,binding-not-nil)\var{with-binding} = \gad{AND}(\var{main}, \var{binding-not-nil}).
      23. Let with-sym-binding=AND(with-binding,var-or-rec-binding-is-sym)\var{with-sym-binding} = \gad{AND}(\var{with-binding}, \var{var-or-rec-binding-is-sym}).
      24. Let with-cons-binding=AND(with-binding,var-or-rec-binding-is-cons)\var{with-cons-binding} = \gad{AND}(\var{with-binding}, \var{var-or-rec-binding-is-cons}).
      25. Let with-other-binding=AND(with-binding,var-or-rec-binding-is-sym-or-cons.NOT())\var{with-other-binding} = \gad{AND}(\var{with-binding}, \var{var-or-rec-binding-is-sym-or-cons}.\gad{NOT}()).
      26. Let v=var-or-rec-bindingv = \var{var-or-rec-binding}.
      27. Let val=val-or-more-rec-env\var{val} = \var{val-or-more-rec-env}.
      28. Let v-is-expr1=ALLOC-EQUAL(expr,v)\var{v-is-expr1} = \gad{ALLOC-EQUAL}(\var{expr}, v).
      29. Let envcaar-not-dummy=with-cons-binding\var{envcaar-not-dummy} = \var{with-cons-binding}.
      30. Let (v2,val2)=CAR-CDR-NAMED(globals,var-or-rec-binding,names.env-caar,allocated-cons-witness,envcaar-not-dummy)(\var{v2}, \var{val2}) = \gad{CAR-CDR-NAMED}(\var{globals}, \var{var-or-rec-binding}, \var{names.env-caar}, \var{allocated-cons-witness}, \var{envcaar-not-dummy}).
      31. Let val2-is-fun=IS-FUN(val2)\var{val2-is-fun} = \gad{IS-FUN}(\var{val2}).
      32. Let v2-is-expr=ALLOC-EQUAL(v2,expr)\var{v2-is-expr} = \gad{ALLOC-EQUAL}(\var{v2}, \var{expr}).
      33. Let v2-is-expr-real=AND(v2-is-expr,envcaar-not-dummy)\var{v2-is-expr-real} = \gad{AND}(\var{v2-is-expr}, \var{envcaar-not-dummy}).
      34. Let extended-env-not-dummy=AND(val2-is-fun,v2-is-expr-real)\var{extended-env-not-dummy} = \gad{AND}(\var{val2-is-fun}, \var{v2-is-expr-real}).
      35. Let (fun-hash,fun-arg,fun-body,fun-closed-env)=ALLOCATE-MAYBE-FUN-UNCONSTRAINED(witness.closure-to-extend())(\var{fun-hash}, \var{fun-arg}, \var{fun-body}, \var{fun-closed-env}) = \gad{ALLOCATE-MAYBE-FUN-UNCONSTRAINED}(\var{witness}.\fun{closure-to-extend}()).
      36. Call IMPLIES-EQUAL(extended-env-not-dummy,fun-hash,val2.hash())\gad{IMPLIES-EQUAL}(\var{extended-env-not-dummy}, \var{fun-hash}, \var{val2.hash}()).
      37. Let rec-env=binding\var{rec-env} = \var{binding}.
      38. Let extended-env=CONSTRUCT-CONS-NAMED(rec-env,fun-closed-env,names.extended-closure-env,allocated-cons-witness,extended-env-not-dummy)\var{extended-env} = \gad{CONSTRUCT-CONS-NAMED}(\var{rec-env}, \var{fun-closed-env}, \var{names.extended-closure-env}, \var{allocated-cons-witness}, \var{extended-env-not-dummy}).
      39. Let extended-fun=CONSTRUCT-FUN(fun-arg,fun-body,extended-env)\var{extended-fun} = \gad{CONSTRUCT-FUN}(\var{fun-arg}, \var{fun-body}, \var{extended-env}).
      40. Let val-to-use=PICK(val2-is-fun,extended-fun,val2)\var{val-to-use} = \gad{PICK}(\var{val2-is-fun}, \var{extended-fun}, \var{val2}).
      41. Let smaller-rec-env=val-or-more-rec-env\var{smaller-rec-env} = \var{val-or-more-rec-env}.
      42. Let smaller-rec-env-is-nil=smaller-rec-env.IS-NIL()\var{smaller-rec-env-is-nil} = \var{smaller-rec-env}.\gad{IS-NIL}().
      43. Let smaller-rec-env-not-nil=smaller-rec-env-is-nil.NOT()\var{smaller-rec-env-not-nil} = \var{smaller-rec-env-is-nil}.\gad{NOT}().
      44. Let v2-not-expr=v2-is-expr.NOT()\var{v2-not-expr} = \var{v2-is-expr}.\gad{NOT}().
      45. Let otherwise-and-v2-not-expr=AND(v2-not-expr,with-cons-binding)\var{otherwise-and-v2-not-expr} = \gad{AND}(\var{v2-not-expr}, \var{with-cons-binding}).
      46. Let smaller-rec-env-not-dummy=AND(smaller-rec-env-not-nil,otherwise-and-v2-not-expr)\var{smaller-rec-env-not-dummy} = \gad{AND}(\var{smaller-rec-env-not-nil}, \var{otherwise-and-v2-not-expr}).
      47. Let rec-extended-env=CONSTRUCT-CONS-NAMED(smaller-rec-env,smaller-env,names.env-to-use,allocated-cons-witness,smaller-rec-env-not-dummy)\var{rec-extended-env} = \gad{CONSTRUCT-CONS-NAMED}(\var{smaller-rec-env}, \var{smaller-env}, \var{names.env-to-use}, \var{allocated-cons-witness}, \var{smaller-rec-env-not-dummy}).
      48. Let env-to-use=PICK(smaller-rec-env-is-nil,smaller-env,rec-extended-env)\var{env-to-use} = \gad{PICK}(\var{smaller-rec-env-is-nil}, \var{smaller-env}, \var{rec-extended-env}).
      49. Let cont-is-lookup=ALLOC-TAG-EQUAL(cont.tag(),globals.lookup-cont-tag\var{cont-is-lookup} = \gad{ALLOC-TAG-EQUAL}(\var{cont.tag}(), \var{globals.lookup-cont-tag}.
      50. Let needed-env-missing=AND(sym-otherwise,env-is-nil)\var{needed-env-missing} = \gad{AND}(\var{sym-otherwise}, \var{env-is-nil}).
      51. Let needed-binding-missing=AND(main,binding-is-nil)\var{needed-binding-missing} = \gad{AND}(\var{main}, \var{binding-is-nil}).
      52. Let with-sym-binding-matched=AND(with-sym-binding,v-is-expr1)\var{with-sym-binding-matched} = \gad{AND}(\var{with-sym-binding}, \var{v-is-expr1}).
      53. Let with-sym-binding-unmatched=AND(with-sym-binding,v-is-expr1.NOT())\var{with-sym-binding-unmatched} = \gad{AND}(\var{with-sym-binding}, \var{v-is-expr1}.\gad{NOT}()).
      54. Let with-sym-binding-unmatched-old-lookup=AND(with-sym-binding-unmatched,cont-is-lookup)\var{with-sym-binding-unmatched-old-lookup} = \gad{AND}(\var{with-sym-binding-unmatched}, \var{cont-is-lookup}).
      55. Let with-sym-binding-unmatched-new-lookup=AND(with-sym-binding-unmatched,cont-is-lookup.NOT())\var{with-sym-binding-unmatched-new-lookup} = \gad{AND}(\var{with-sym-binding-unmatched}, \var{cont-is-lookup}.\gad{NOT}()).
      56. Let with-cons-binding-matched=AND(with-cons-binding,v2-is-expr)\var{with-cons-binding-matched} = \gad{AND}(\var{with-cons-binding}, \var{v2-is-expr}).
      57. Let with-cons-binding-unmatched=AND(with-cons-binding,v2-is-expr.NOT())\var{with-cons-binding-unmatched} = \gad{AND}(\var{with-cons-binding}, \var{v2-is-expr}.\gad{NOT}()).
      58. Let with-cons-binding-unmatched-old-lookup=AND(with-cons-binding-unmatched,cont-is-lookup)\var{with-cons-binding-unmatched-old-lookup} = \gad{AND}(\var{with-cons-binding-unmatched}, \var{cont-is-lookup}).
      59. Let with-cons-binding-unmatched-new-lookup=AND(with-cons-binding-unmatched,cont-is-lookup.NOT())\var{with-cons-binding-unmatched-new-lookup} = \gad{AND}(\var{with-cons-binding-unmatched}, \var{cont-is-lookup}.\gad{NOT}()).
      60. Let lookup-continuation-not-dummy=OR(with-sym-binding-unmatched-new-lookup,with-cons-binding-unmatched-new-lookup)\var{lookup-continuation-not-dummy} = \gad{OR}(\var{with-sym-binding-unmatched-new-lookup}, \var{with-cons-binding-unmatched-new-lookup}).
      61. Let lookup-continuation=CONSTRUCT-NAMED(names.lookup,globals.lookup-cont-tag,(env,cont,default-num-pair,default-num-pair),allocated-cont-witness,lookup-continuation-not-dummy)\var{lookup-continuation} = \gad{CONSTRUCT-NAMED}(\var{names.lookup}, \var{globals.lookup-cont-tag}, (\var{env}, \var{cont}, \var{default-num-pair}, \var{default-num-pair}), \var{allocated-cont-witness}, \var{lookup-continuation-not-dummy}).
    2. Calculate output predicate:
      1. Letoutput-expr-is-expr=EQUAL(output-expr,expr)\var{output-expr-is-expr} = \gad{EQUAL}(\var{output-expr}, \var{expr}).
      2. Let output-env-is-env=EQUAL(output-env,env)\var{output-env-is-env} = \gad{EQUAL}(\var{output-env}, \var{env}).
      3. Let output-cont-is-cont=EQUAL(output-cont,cont)\var{output-cont-is-cont} = \gad{EQUAL}(\var{output-cont}, \var{cont}).
      4. Let output-cont-is-error=EQUAL(output-cont,globals.error-ptr)\var{output-cont-is-error} = \gad{EQUAL}(\var{output-cont}, \var{globals.error-ptr}).
      5. Let output-expr-is-val=EQUAL(output-expr,val)\var{output-expr-is-val} = \var{EQUAL}(\var{output-expr}, \var{val}).
      6. Let output-env-is-smaller-env=EQUAL(output-env,smaller-env)\var{output-env-is-smaller-env} = \gad{EQUAL}(\var{output-env}, \var{smaller-env}).
      7. Let output-cont-is-lookup=EQUAL(output-cont,lookup-continuation)\var{output-cont-is-lookup} = \gad{EQUAL}(\var{output-cont}, \var{lookup-continuation}).
      8. Let output-expr-is-val-to-use=EQUAL(output-expr,val-to-use)\var{output-expr-is-val-to-use} = \gad{EQUAL}(\var{output-expr}, \var{val-to-use}).
      9. Let output-env-is-env-to-use=EQUAL(output-env,env-to-use)\var{output-env-is-env-to-use} = \gad{EQUAL}(\var{output-env}, \var{env-to-use}).
    3. Calculate conditions:
      1. Let output-expr-should-be-expr=OR(needed-env-missing,sym-is-self-evaluating,needed-binding-missing,with-sym-binding-unmatched,with-cons-binding-unmatched)\var{output-expr-should-be-expr} = \gad{OR}(\var{needed-env-missing}, \var{sym-is-self-evaluating}, \var{needed-binding-missing}, \var{with-sym-binding-unmatched}, \var{with-cons-binding-unmatched}).
      2. Let output-expr-should-be-val=with-sym-binding-matched\var{output-expr-should-be-val} = \var{with-sym-binding-matched}.
      3. Let output-expr-should-be-val-to-use=with-cons-binding-matched\var{output-expr-should-be-val-to-use} = \var{with-cons-binding-matched}.
      4. Let output-env-should-be-env=OR(needed-binding-missing,needed-env-missing,sym-is-self-evaluating,with-sym-binding-matched,with-cons-binding-matched)\var{output-env-should-be-env} = \gad{OR}(\var{needed-binding-missing}, \var{needed-env-missing}, \var{sym-is-self-evaluating}, \var{with-sym-binding-matched}, \var{with-cons-binding-matched}).
      5. Let output-env-should-be-smaller-env=with-sym-binding-unmatched\var{output-env-should-be-smaller-env} = \var{with-sym-binding-unmatched}.
      6. Let output-env-should-be-env-to-use=with-cons-binding-unmatched-new-lookup\var{output-env-should-be-env-to-use} = \var{with-cons-binding-unmatched-new-lookup}.
      7. Let output-cont-should-be-cont=OR(sym-is-self-evaluating,with-sym-binding-matched,with-sym-binding-unmatched-old-lookup,with-cons-binding-matched,with-cons-binding-unmatched-old-lookup)\var{output-cont-should-be-cont} = \gad{OR}(\var{sym-is-self-evaluating}, \var{with-sym-binding-matched}, \var{with-sym-binding-unmatched-old-lookup}, \var{with-cons-binding-matched}, \var{with-cons-binding-unmatched-old-lookup}).
      8. Let output-cont-should-be-error=OR(with-other-binding,needed-env-missing,needed-binding-missing)\var{output-cont-should-be-error} = \gad{OR}(\var{with-other-binding}, \var{needed-env-missing}, \var{needed-binding-missing}).
    4. Calculate implications:
      1. Call IMPLIES(output-expr-should-be-expr,output-expr-is-expr)\gad{IMPLIES}(\var{output-expr-should-be-expr}, \var{output-expr-is-expr}).
      2. Call IMPLIES(output-expr-should-be-val,output-expr-is-val)\gad{IMPLIES}(\var{output-expr-should-be-val}, \var{output-expr-is-val}).
      3. Call IMPLIES(output-expr-should-be-val-to-use,output-expr-is-val-to-use)\gad{IMPLIES}(\var{output-expr-should-be-val-to-use}, \var{output-expr-is-val-to-use}).
      4. Call IMPLIES(output-cont-should-be-error,output-expr-is-expr)\gad{IMPLIES}(\var{output-cont-should-be-error}, \var{output-expr-is-expr}).
      5. Call IMPLIES(output-env-should-be-env,output-env-is-env)\gad{IMPLIES}(\var{output-env-should-be-env}, \var{output-env-is-env}).
      6. Call IMPLIES(output-env-should-be-smaller-env,output-env-is-smaller-env)\gad{IMPLIES}(\var{output-env-should-be-smaller-env}, \var{output-env-is-smaller-env}).
      7. Call IMPLIES(output-env-should-be-env-to-use,output-env-is-env-to-use)\gad{IMPLIES}(\var{output-env-should-be-env-to-use}, \var{output-env-is-env-to-use}).
      8. Call IMPLIES(output-cont-should-be-error,output-env-is-env)\gad{IMPLIES}(\var{output-cont-should-be-error}, \var{output-env-is-env}).
      9. Call IMPLIES(output-cont-should-be-cont,output-cont-is-cont)\gad{IMPLIES}(\var{output-cont-should-be-cont}, \var{output-cont-is-cont}).
      10. Call IMPLIES(output-cont-should-be-error,output-cont-is-error)\gad{IMPLIES}(\var{output-cont-should-be-error}, \var{output-cont-is-error}).
      11. Call IMPLIES(lookup-continuation-not-dummy,output-cont-is-lookup)\gad{IMPLIES}(\var{lookup-continuation-not-dummy}, \var{output-cont-is-lookup}).
      12. Let apply-cont-bool=OR(with-cons-binding-matched,with-sym-binding-matched,sym-is-self-evaluating)\var{apply-cont-bool} = \gad{OR}(\var{with-cons-binding-matched}, \var{with-sym-binding-matched}, \var{sym-is-self-evaluating}).
      13. Let apply-cont-num=BOOLEAN-NUM(apply-cont-bool)\var{apply-cont-num} = \gad{BOOLEAN-NUM}(\var{apply-cont-bool}).
      14. Return (output-expr,output-env,output-cont,apply-cont-num)(\var{output-expr}, \var{output-env}, \var{output-cont}, \var{apply-cont-num}).

Reduce Cons

In this section we describe the Reduce-Cons()\cir{Reduce-Cons}() algorithm, which is responsible for taking a cons expression and reducing it to a triple (expr, env, cont) to be evaluated next, and determining if the continuation will be applied or not. In order to avoid calculating unnecessary hashes inside the circuit, we first use a multicase to select the preimage for the next continuation, then we compute – just once – the continuation pointer. Afterward, we use a second multicase to select the final result.


Circuit 3.3  Reduce-cons\cirtitle{Circuit~3.3}~\cir{Reduce-cons} \\
  1. INPUT expr,env,cont,witness\cirio{INPUT} \var{expr}, \var{env}, \var{cont}, \var{witness}.
  2. OUTPUT expr,env,cont\cirio{OUTPUT} \var{expr}, \var{env}, \var{cont}.
  3. Compute preimage clauses. \com{Compute preimage clauses.}
  4. Let preimage=MULTICASE(preimage-clauses)\var{preimage} = \gad{MULTICASE}(\var{preimage-clauses}).
  5. Calculate newer continuation pointer. \com{Calculate newer continuation pointer.}
  6. Compute clauses. \com{Compute clauses.}
  7. result=MULTICASE(clauses)\var{result} = \gad{MULTICASE}(\var{clauses}).
  8. Return result\var{result}.

In Reduce-Sym()\cir{Reduce-Sym}() we dealt with expressions that correspond to just one symbol. On the other hand, Reduce-Cons()\cir{Reduce-Cons}() allows us to reduce more complicated expressions, since a cons expression can represent unary and binary operations. In particular, those operations have a list of parameters that themselves can be symbols or cons expressions.

Here we can describe how to reduce cons expressions:

A cons\tt{\footnotesize{cons}} expression can also be used to represent lambda, let, and letrec operations. The first thing we do with a cons operation is to split its arguments into head – the first element of the expression, which corresponds to the car operation – and the rest of the elements, which correspond to the cdr operation. Next, we describe how we calculate the constraints and how we add one clause to the multicase gadget for each possible head.

  1. Let (head,rest)=CAR-CDR-NAMED(expr,cons-names.expr,allocated-cons-witness,not-dummy)(\var{head}, \var{rest}) = \cir{CAR-CDR-NAMED}(\var{expr}, \var{cons-names.expr}, \var{allocated-cons-witness}, \var{not-dummy}).
  2. Let head-is-lambda0=ALLOC-EQUAL(head.hash(),globals.lambda-sym.hash())\var{head-is-lambda0} = \gad{ALLOC-EQUAL}(\var{head.hash}(), \var{globals.lambda-sym.hash}()).
  3. Let head-is-let=ALLOC-EQUAL(head.hash(),globals.let-sym.hash())\var{head-is-let} = \gad{ALLOC-EQUAL}(\var{head.hash}(), \var{globals.let-sym.hash}()).
  4. Let head-is-letrec=ALLOC-EQUAL(head.hash(),globals.letrec-sym.hash())\var{head-is-letrec} = \gad{ALLOC-EQUAL}(\var{head.hash}(), \var{globals.letrec-sym.hash}()).
  5. Let head-is-eval=ALLOC-EQUAL(head.hash(),globals.eval-sym.hash())\var{head-is-eval} = \gad{ALLOC-EQUAL}(\var{head.hash}(), \var{globals.eval-sym.hash}()).
  6. Let head-is-quote0=ALLOC-EQUAL(head.hash(),globals.quote-sym.hash())\var{head-is-quote0} = \gad{ALLOC-EQUAL}(\var{head.hash}(), \var{globals.quote-sym.hash}()).
  7. Let head-is-cons=ALLOC-EQUAL(head.hash(),globals.cons-sym.hash())\var{head-is-cons} = \gad{ALLOC-EQUAL}(\var{head.hash}(), \var{globals.cons-sym.hash}()).
  8. Let head-is-hide=ALLOC-EQUAL(head.hash(),globals.hide-sym.hash())\var{head-is-hide} = \gad{ALLOC-EQUAL}(\var{head.hash}(), \var{globals.hide-sym.hash}()).
  9. Let head-is-commit=ALLOC-EQUAL(head.hash(),globals.commit-sym.hash())\var{head-is-commit} = \gad{ALLOC-EQUAL}(\var{head.hash}(), \var{globals.commit-sym.hash}()).
  10. Let head-is-open=ALLOC-EQUAL(head.hash(),globals.open-sym.hash())\var{head-is-open} = \gad{ALLOC-EQUAL}(\var{head.hash}(), \var{globals.open-sym.hash}()).
  11. Let head-is-secret=ALLOC-EQUAL(head.hash(),globals.secret-sym.hash())\var{head-is-secret} = \gad{ALLOC-EQUAL}(\var{head.hash}(), \var{globals.secret-sym.hash}()).
  12. Let head-is-num=ALLOC-EQUAL(head.hash(),globals.num-sym.hash())\var{head-is-num} = \gad{ALLOC-EQUAL}(\var{head.hash}(), \var{globals.num-sym.hash}()).
  13. Let head-is-u64=ALLOC-EQUAL(head.hash(),globals.u64-sym.hash())\var{head-is-u64} = \gad{ALLOC-EQUAL}(\var{head.hash}(), \var{globals.u64-sym.hash}()).
  14. Let head-is-comm=ALLOC-EQUAL(head.hash(),globals.comm-sym.hash())\var{head-is-comm} = \gad{ALLOC-EQUAL}(\var{head.hash}(), \var{globals.comm-sym.hash}()).
  15. Let head-is-char=ALLOC-EQUAL(head.hash(),globals.char-sym.hash())\var{head-is-char} = \gad{ALLOC-EQUAL}(\var{head.hash}(), \var{globals.char-sym.hash}()).
  16. Let head-is-begin=ALLOC-EQUAL(head.hash(),globals.begin-sym.hash())\var{head-is-begin} = \gad{ALLOC-EQUAL}(\var{head.hash}(), \var{globals.begin-sym.hash}()).
  17. Let head-is-car=ALLOC-EQUAL(head.hash(),globals.car-sym.hash())\var{head-is-car} = \gad{ALLOC-EQUAL}(\var{head.hash}(), \var{globals.car-sym.hash}()).
  18. Let head-is-cdr=ALLOC-EQUAL(head.hash(),globals.cdr-sym.hash())\var{head-is-cdr} = \gad{ALLOC-EQUAL}(\var{head.hash}(), \var{globals.cdr-sym.hash}()).
  19. Let head-is-atom=ALLOC-EQUAL(head.hash(),globals.atom-sym.hash())\var{head-is-atom} = \gad{ALLOC-EQUAL}(\var{head.hash}(), \var{globals.atom-sym.hash}()).
  20. Let head-is-emit=ALLOC-EQUAL(head.hash(),globals.emit-sym.hash())\var{head-is-emit} = \gad{ALLOC-EQUAL}(\var{head.hash}(), \var{globals.emit-sym.hash}()).
  21. Let head-is-plus=ALLOC-EQUAL(head.hash(),globals.plus-sym.hash())\var{head-is-plus} = \gad{ALLOC-EQUAL}(\var{head.hash}(), \var{globals.plus-sym.hash}()).
  22. Let head-is-minus=ALLOC-EQUAL(head.hash(),globals.minus-sym.hash())\var{head-is-minus} = \gad{ALLOC-EQUAL}(\var{head.hash}(), \var{globals.minus-sym.hash}()).
  23. Let head-is-times=ALLOC-EQUAL(head.hash(),globals.times-sym.hash())\var{head-is-times} = \gad{ALLOC-EQUAL}(\var{head.hash}(), \var{globals.times-sym.hash}()).
  24. Let head-is-div=ALLOC-EQUAL(head.hash(),globals.div-sym.hash())\var{head-is-div} = \gad{ALLOC-EQUAL}(\var{head.hash}(), \var{globals.div-sym.hash}()).
  25. Let head-is-mod=ALLOC-EQUAL(head.hash(),globals.mod-sym.hash())\var{head-is-mod} = \gad{ALLOC-EQUAL}(\var{head.hash}(), \var{globals.mod-sym.hash}()).
  26. Let head-is-numequal=ALLOC-EQUAL(head.hash(),globals.numequal-sym.hash())\var{head-is-numequal} = \gad{ALLOC-EQUAL}(\var{head.hash}(), \var{globals.numequal-sym.hash}()).
  27. Let head-is-eq=ALLOC-EQUAL(head.hash(),globals.eq-sym.hash())\var{head-is-eq} = \gad{ALLOC-EQUAL}(\var{head.hash}(), \var{globals.eq-sym.hash}()).
  28. Let head-is-less=ALLOC-EQUAL(head.hash(),globals.less-sym.hash())\var{head-is-less} = \gad{ALLOC-EQUAL}(\var{head.hash}(), \var{globals.less-sym.hash}()).
  29. Let head-is-less-equal=ALLOC-EQUAL(head.hash(),globals.less-equal-sym.hash())\var{head-is-less-equal} = \gad{ALLOC-EQUAL}(\var{head.hash}(), \var{globals.less-equal-sym.hash}()).
  30. Let head-is-greater=ALLOC-EQUAL(head.hash(),globals.greater-sym.hash())\var{head-is-greater} = \gad{ALLOC-EQUAL}(\var{head.hash}(), \var{globals.greater-sym.hash}()).
  31. Let head-is-greater-equal=ALLOC-EQUAL(head.hash(),globals.greater-equal-sym.hash())\var{head-is-greater-equal} = \gad{ALLOC-EQUAL}(\var{head.hash}(), \var{globals.greater-equal-sym.hash}()).
  32. Let head-is-if0=ALLOC-EQUAL(head.hash(),globals.if-sym.hash())\var{head-is-if0} = \gad{ALLOC-EQUAL}(\var{head.hash}(), \var{globals.if-sym.hash}()).
  33. Let head-is-current-env0=ALLOC-EQUAL(head.hash(),globals.current-env-sym.hash())\var{head-is-current-env0} = \gad{ALLOC-EQUAL}(\var{head.hash}(), \var{globals.current-env-sym.hash}()).
  34. Let head-is-a-sym=IS-SYM(head)\var{head-is-a-sym} = \gad{IS-SYM}(\var{head}).
  35. Let head-is-fun=IS-FUN(head)\var{head-is-fun} = \gad{IS-FUN}(\var{head}).
  36. Let head-is-a-cons=IS-CONS(head)\var{head-is-a-cons} = \gad{IS-CONS}(\var{head}).
  37. Let head-is-binop0=OR(head-is-cons,head-is-strcons,head-is-hide,head-is-begin,head-is-plus,head-is-minus,head-is-times,head-is-div,head-is-mod,head-is-equal,head-is-eq,head-is-less,head-is-less-equal,head-is-greater,head-is-greater-equal,head-is-if,head-is-eval)\var{head-is-binop0} = \gad{OR}(\var{head-is-cons}, \var{head-is-strcons}, \var{head-is-hide}, \var{head-is-begin}, \var{head-is-plus}, \var{head-is-minus}, \var{head-is-times}, \var{head-is-div}, \\ \var{head-is-mod}, \var{head-is-equal}, \var{head-is-eq}, \var{head-is-less}, \var{head-is-less-equal}, \var{head-is-greater}, \var{head-is-greater-equal}, \var{head-is-if}, \var{head-is-eval}).
  38. Let head-is-binop=AND(head-is-binop0,head-is-a-sym)\var{head-is-binop} = \gad{AND}(\var{head-is-binop0}, \var{head-is-a-sym}).
  39. Let head-is-unop0=OR(head-is-car,head-is-cdr,head-is-commit,head-is-num,head-is-u64,head-is-comm,head-is-char,head-is-open,head-is-secret,head-is-atom,head-is-emit,head-is-eval\var{head-is-unop0} = \gad{OR}(\var{head-is-car}, \var{head-is-cdr}, \var{head-is-commit}, \var{head-is-num}, \var{head-is-u64}, \var{head-is-comm}, \var{head-is-char}, \var{head-is-open}, \var{head-is-secret}, \var{head-is-atom}, \var{head-is-emit}, \var{head-is-eval}.
  40. Let head-is-let-or-letrec0=OR(head-is-let,head-is-letrec)\var{head-is-let-or-letrec0} = \gad{OR}(\var{head-is-let}, \var{head-is-letrec}).
  41. Let head-is-let-or-letrec=AND(head-is-let-or-letrec0,head-is-a-sym)\var{head-is-let-or-letrec} = \gad{AND}(\var{head-is-let-or-letrec0}, \var{head-is-a-sym}).
  42. Let head-is-lambda=AND(head-is-lambda0,head-is-a-sym)\var{head-is-lambda} = \gad{AND}(\var{head-is-lambda0}, \var{head-is-a-sym}).
  43. Let head-is-quote=AND(head-is-quote0,head-is-a-sym)\var{head-is-quote} = \gad{AND}(\var{head-is-quote0}, \var{head-is-a-sym}).
  44. Let head-is-current-env=AND(head-is-current-env0,head-is-a-sym)\var{head-is-current-env} = \gad{AND}(\var{head-is-current-env0}, \var{head-is-a-sym}).
  45. Let head-is-if=AND(head-is-if0,head-is-a-sym)\var{head-is-if} = \gad{AND}(\var{head-is-if0}, \var{head-is-a-sym}).
  46. Let head-is-any=OR(head-is-quote,head-is-if,head-is-lambda,head-is-current-env,head-is-let-or-letrec,head-is-unop,head-is-binop)\var{head-is-any} = \gad{OR}(\var{head-is-quote}, \var{head-is-if}, \var{head-is-lambda}, \var{head-is-current-env}, \var{head-is-let-or-letrec}, \var{head-is-unop},\var{head-is-binop}).
  47. Let head-potentially-fun-type=OR(head-is-a-sym,head-is-a-cons,head-is-fun)\var{head-potentially-fun-type} = \gad{OR}(\var{head-is-a-sym}, \var{head-is-a-cons}, \var{head-is-fun}).
  48. Let head-potentially-fun=AND(head-potentially-fun-type,head-is-any.not())\var{head-potentially-fun} = \gad{AND}(\var{head-potentially-fun-type}, \var{head-is-any}.\gad{not}()).
  49. Let rest-is-nil=rest.IS-NIL()\var{rest-is-nil} = \var{rest}.\gad{IS-NIL}().
  50. Let rest-is-cons=IS-CONS(rest)\var{rest-is-cons} = \gad{IS-CONS}(\var{rest}).
  51. Let expr-cdr-not-dummy=AND(not-dummy,rest-is-nil.NOT(),rest-is-cons,head-is-any,head-is-current-env.NOT())\var{expr-cdr-not-dummy} = \gad{AND}(\var{not-dummy}, \var{rest-is-nil}.\gad{NOT}(), \var{rest-is-cons}, \var{head-is-any}, \var{head-is-current-env}.\gad{NOT}()).
  52. Let is-dotted-error=AND(rest-is-nil.NOT(),rest-is-cons.NOT(),expr-cdr-not-dummy.NOT())\var{is-dotted-error} = \gad{AND}(\var{rest-is-nil}.\gad{NOT}(), \var{rest-is-cons}.\gad{NOT}(), \var{expr-cdr-not-dummy}.\gad{NOT}()).
  53. Let (arg1,more)=CAR-CDR-NAMED(rest,cons-names.expr-cdr,allocated-cons-witness,expr-cdr-not-dummy)(\var{arg1}, \var{more}) = \gad{CAR-CDR-NAMED}(\var{rest}, \var{cons-names.expr-cdr}, \var{allocated-cons-witness}, \var{expr-cdr-not-dummy}).
  54. Let more-is-nil=ALLOC-EQUAL(more,globals.nil-ptr)\var{more-is-nil} = \gad{ALLOC-EQUAL}(\var{more}, \var{globals.nil-ptr}).
  55. Let is-binop-missing-arg-error=AND(head-is-binop,more-is-nil,head-is-begin.NOT(),head-is-eval.NOT())\var{is-binop-missing-arg-error} = \gad{AND}(\var{head-is-binop}, \var{more-is-nil}, \var{head-is-begin}.\gad{NOT}(), \var{head-is-eval}.\gad{NOT}()).
  56. Let arg1-is-cons=IS-CONS(arg1)\var{arg1-is-cons} = \gad{IS-CONS}(\var{arg1}).
  57. Let arg1-is-str=IS-STR(arg1)\var{arg1-is-str} = \gad{IS-STR}(\var{arg1}).
  58. Let arg1-is-nil=arg1.IS-NIL()\var{arg1-is-nil} = \var{arg1}.\gad{IS-NIL}().
  59. Let expr-cadr-not-dummy0=OR(arg1-is-cons,arg1-is-nil,arg1-is-str)\var{expr-cadr-not-dummy0} = \gad{OR}(\var{arg1-is-cons}, \var{arg1-is-nil}, \var{arg1-is-str}).
  60. Let expr-cadr-not-dummy=AND(expr-cdr-not-dummy,expr-cadr-not-dummy0,head-is-lambda)\var{expr-cadr-not-dummy} = \gad{AND}(\var{expr-cdr-not-dummy}, \var{expr-cadr-not-dummy0}, \var{head-is-lambda}).
  61. Let (car-args,cdr-args)=CAR-CDR-NAMED(arg1,cons-names.expr-cadr,allocated-cons-witness,expr-cadr-not-dummy)(\var{car-args}, \var{cdr-args}) = \gad{CAR-CDR-NAMED}(\var{arg1}, \var{cons-names.expr-cadr}, \var{allocated-cons-witness}, \var{expr-cadr-not-dummy}).
  62. Let end-is-nil=more.IS-NIL()\var{end-is-nil} = \var{more}.\gad{IS-NIL}().

In many cases, we need to compute a new pointer, which requires the calculation of the hash function. Since this operation is expensive in the circuit, we want to avoid it as much as possible. In order to do that, we use a multicase gadget to select the preimage in those situations. Then we compute the hash only once. Later, a second multicase is used to select the final result.

  1. Let default-num-pair=(globals.default-num,globals.default-num)\var{default-num-pair} = (\var{globals}.\var{default-num}, \var{globals}.\var{default-num}).

Case head is lambda\rm{\small{\bold{Case}~head~\bold{is}~lambda}}.

A lambda expression receives a list of arguments and a body expression as input. We use one-argument lambdas as building blocks. In order to deal with multiple arguments, we recursively use nested one-argument lambdas. If the list of arguments is empty, we use a dummy argument instead. Otherwise, we take the first argument to build the function and construct an inner body that itself is another lambda expression containing the rest of the list of arguments and the same body, and use that function to call apply-continuation.

  1. Let (args,body)=(arg1,more)(\var{args}, \var{body}) = (\var{arg1}, \var{more}).
  2. Let args-is-nil=args.IS-NIL()\var{args-is-nil} = \var{args}.\gad{IS-NIL}().
  3. Let cdr-args-is-nil=cdr-args.IS-NIL()\var{cdr-args-is-nil} = \var{cdr-args}.\gad{IS-NIL}().
  4. Let arg=PICK(args-is-nil,globals.dummy-arg-ptr,car-args)\var{arg} = \gad{PICK}(\var{args-is-nil}, \var{globals.dummy-arg-ptr}, \var{car-args}).
  5. Let arg-is-sym=arg.IS-SYM()\var{arg-is-sym} = \var{arg}.\gad{IS-SYM}().
  6. Let lambda-not-dummy=AND(head-is-lambda,not-dummy,cdr-args-not-nil)\var{lambda-not-dummy} = \gad{AND}(\var{head-is-lambda}, \var{not-dummy}, \var{cdr-args-not-nil}).
  7. Let inner-not-dummy=AND(lambda-not-dummy,cdr-args-is-nil.NOT())\var{inner-not-dummy} = \gad{AND}(\var{lambda-not-dummy}, \var{cdr-args-is-nil}.\gad{NOT}()).
  8. Let inner=CONSTRUCT-CONS-NAMED(cdr-args,body,cons-names.inner-lambda,allocated-cons-witness,inner-not-dummy).\var{inner} = \gad{CONSTRUCT-CONS-NAMED}(\var{cdr-args}, \var{body}, \var{cons-names.inner-lambda}, \var{allocated-cons-witness}, \var{inner-not-dummy}).
  9. Let l=CONSTRUCT-CONS-NAMED(globals.lambda-sym,inner,cons-names.lambda,allocated-cons-witness,inner-not-dummy)l = \gad{CONSTRUCT-CONS-NAMED}(\var{globals.lambda-sym}, \var{inner}, \var{cons-names.lambda}, \var{allocated-cons-witness}, \var{inner-not-dummy}).
  10. Let list=CONSTRUCT-CONS-NAMED(l,globals.nil-ptr,cons-names.inner-body,allocated-cons-witness,inner-not-dummy)\var{list} = \gad{CONSTRUCT-CONS-NAMED}(l, \var{globals.nil-ptr}, \var{cons-names.inner-body}, \var{allocated-cons-witness}, \var{inner-not-dummy}).
  11. Let inner-body=PICK(cdr-args-is-nil,body,list)\var{inner-body} = \gad{PICK}(\var{cdr-args-is-nil}, \var{body}, \var{list}).
  12. Let function=CONSTRUCT-FUN(arg,inner-body,env)\var{function} = \gad{CONSTRUCT-FUN}(\var{arg}, \var{inner-body}, \var{env}).
  13. Let lambda-arg-error=AND(arg-is-sym.NOT(),lambda-not-dummy)\var{lambda-arg-error} = \gad{AND}(\var{arg-is-sym}.\gad{NOT}(), \var{lambda-not-dummy}).
  14. Let lambda-expr=PICK(lambda-arg-error,expr,function)\var{lambda-expr} = \gad{PICK}(\var{lambda-arg-error}, \var{expr}, \var{function}).
  15. Let lambda-cont=PICK(lambda-arg-error,globals.error-ptr-cont,cont)\var{lambda-cont} = \gad{PICK}(\var{lambda-arg-error}, \var{globals.error-ptr-cont}, \var{cont}).
  16. Call clauses.ADD-CLAUSE(constants.lambda,lambda-expr,lambda-env,lambda-cont,globals.true-num)\var{clauses}.\gad{ADD-CLAUSE}(\var{constants.lambda}, \var{lambda-expr}, \var{lambda-env}, \var{lambda-cont}, \var{globals.true-num}).

Case head is quote\rm{\small{\bold{Case}~head~\bold{is}~quote}}.

  1. Let arg1-or-expr=PICK(end-is-nil,arg1,expr)\var{arg1-or-expr} = \gad{PICK}(\var{end-is-nil}, \var{arg1}, \var{expr}).
  2. Let the-cont=PICK(end-is-nil,cont,globals.error-ptr-cont)\var{the-cont} = \gad{PICK}(\var{end-is-nil}, \var{cont}, \var{globals.error-ptr-cont}).
  3. clauses.ADD-CLAUSE(constants.quote,arg1-or-expr,env,the-cont,globals.true-num)\var{clauses}.\gad{ADD-CLAUSE}(\var{constants.quote}, \var{arg1-or-expr}, \var{env}, \var{the-cont}, \var{globals.true-num}).

Case head is let or letrec\rm{\small{\bold{Case}~head~\bold{is}~let~\bold{or}~letrec}}.

Because both let and letrec share common subcircuits, we are going to factor them out in order to reduce the number of constraints.

Initially, we have:

  1. Let (bindings, body) = (arg1, more).
  2. Let let-letrec-not-dummy=AND(not-dummy,head-is-let-or-letrec)\var{let-letrec-not-dummy} = \gad{AND}(\var{not-dummy}, \var{head-is-let-or-letrec}).
  3. Let (bindings,body)=(arg1,more)(\var{bindings}, \var{body}) = (\var{arg1}, \var{more}).
  4. Let (body1,rest-body)=CAR-CDR-NAMED(body,cons-names.expr-cddr,allocated-cons-witness,let-letrec-not-dummy)(\var{body1}, \var{rest-body}) = \gad{CAR-CDR-NAMED}(\var{body}, \var{cons-names.expr-cddr}, \var{allocated-cons-witness}, \var{let-letrec-not-dummy}).
  5. Let bindings-is-nil=binding.IS-NIL()\var{bindings-is-nil} = \var{binding}.\gad{IS-NIL}().
  6. Let bindings-is-cons=ALLOC-EQUAL(bindings.tag(),globals.cons-tag)\var{bindings-is-cons} = \gad{ALLOC-EQUAL}(\var{bindings.tag}(), \var{globals.cons-tag}).
  7. Let body-is-nil=body.IS-NIL()\var{body-is-nil} = \var{body}.\gad{IS-NIL}().
  8. Let rest-body-is-nil=rest-body.IS-NIL()\var{rest-body-is-nil} = \var{rest-body}.\gad{IS-NIL}().

A let or a letrec expression will receive a list of bindings to be added to the environment and a body expression to be evaluated. If the list of bindings is empty, we just return it together with the current environment and continuation. If not, we take the first element of the list, add it to the environment, and recursively use another let or letrec to evaluate the rest of the list.

  1. Let (binding1,rest-bindings)=(car-args,cdr-args)(\var{binding1}, \var{rest-bindings}) = (\var{car-args}, \var{cdr-args}).
  2. Let expr-caadr-not-dummy=AND(rest-body-is-nil,body-is-nil.NOT(),bindings-is-cons,bindings-is-nil.NOT(),let-letrec-not-dummy)\var{expr-caadr-not-dummy} = \gad{AND}(\var{rest-body-is-nil}, \gad{body-is-nil}.\gad{NOT}(), \var{bindings-is-cons}, \var{bindings-is-nil}.\gad{NOT}(), \var{let-letrec-not-dummy}).
  3. Let (var-let-letrec,vals)=CAR-CDR-NAMED(binding1,cons-names.expr-caadr,allocated-cons-witness,expr-caadr-not-dummy)(\var{var-let-letrec}, \var{vals}) = \cir{CAR-CDR-NAMED}(\var{binding1}, \var{cons-names.expr-caadr}, \var{allocated-cons-witness}, \var{expr-caadr-not-dummy}).
  4. Let var-let-letrec-is-sym=var-let-letrec.IS-SYM()\var{var-let-letrec-is-sym} = \var{var-let-letrec}.\gad{IS-SYM}().
  5. Let var-let-letrec-is-nil=var-let-letrec.IS-NIL()\var{var-let-letrec-is-nil} = \var{var-let-letrec}.\gad{IS-NIL}().
  6. Let var-let-letrec-is-list=OR(var-let-letrec-is-sym,var-let-letrec-is-nil)\var{var-let-letrec-is-list} = \gad{OR}(\var{var-let-letrec-is-sym}, \var{var-let-letrec-is-nil}).
  7. Let expr-caaadr-not-dummy=AND(expr-caadr-not-dummy,var-let-letrec-is-list)\var{expr-caaadr-not-dummy} = \gad{AND}(\var{expr-caadr-not-dummy}, \gad{var-let-letrec-is-list}).
  8. let (val,end)=CAR-CDR-NAMED(vals,cons-names.expr-caaadr,allocated-cons-witness,expr-caadr-not-dummy)(\var{val}, \var{end}) = \var{CAR-CDR-NAMED}(\var{vals}, \var{cons-names.expr-caaadr}, \var{allocated-cons-witness}, \var{expr-caadr-not-dummy}).
  9. Let end-is-nil=end.IS-NIL()\var{end-is-nil} = \var{end}.\gad{IS-NIL}().
  10. Let cond-error=OR(rest-body-is-nil.NOT(),end-is-nil.NOT(),body-is-nil,var-let-letrec-is-list.NOT())\var{cond-error} = \gad{OR}(\var{rest-body-is-nil}.\gad{NOT}(), \var{end-is-nil}.\gad{NOT}(), \var{body-is-nil}, \var{var-let-letrec-is-list}.\gad{NOT}()).
  11. Let rest-bindings-is-nil=rest-bindings.IS-NIL()\var{rest-bindings-is-nil} = \var{rest-bindings}.\gad{IS-NIL}().
  12. Let expanded-inner-not-dummy0=AND(rest-bindings-is-nil.NOT(),end-is-nil)\var{expanded-inner-not-dummy0} = \gad{AND}(\var{rest-bindings-is-nil}.\gad{NOT}(), \var{end-is-nil}).
  13. Let expanded-inner-not-dummy=AND(expanded-inner-not-dummy0,let-letrec-not-dummy,body-is-nil.NOT(),rest-body-is-nil)\var{expanded-inner-not-dummy} = \gad{AND}(\var{expanded-inner-not-dummy0}, \var{let-letrec-not-dummy}, \var{body-is-nil}.\gad{NOT}(),\var{rest-body-is-nil}).
  14. Let expanded0=CONSTRUCT-CONS-NAMED(rest-bindings,body,cons-names.expanded-inner,allocated-cons-witness,expanded-inner-not-dummy)\var{expanded0} = \gad{CONSTRUCT-CONS-NAMED}(\var{rest-bindings}, \var{body}, \var{cons-names.expanded-inner}, \var{allocated-cons-witness}, \var{expanded-inner-not-dummy}).
  15. Let expanded1=CONSTRUCT-CONS-NAMED(head,expanded0,cons-names.expanded,allocated-cons-witness,expanded-inner-not-dummy)\var{expanded1} = \gad{CONSTRUCT-CONS-NAMED}(\var{head}, \var{expanded0}, \var{cons-names.expanded}, \var{allocated-cons-witness}, \var{expanded-inner-not-dummy}).
  16. Let expanded=PICK(rest-bindings-is-nil,body1,expanded1)\var{expanded} = \gad{PICK}(\var{rest-bindings-is-nil}, \var{body1}, \var{expanded1}).
  17. Let output-expr=PICK(bindings-is-nil,body1,val)\var{output-expr} = \gad{PICK}(\var{bindings-is-nil}, \var{body1}, \var{val}).
  18. Let the-expr=PICK(cond-error,expr,output-expr)\var{the-expr} = \gad{PICK}(\var{cond-error}, \var{expr}, \var{output-expr}).
  19. Let expanded-let=expanded\var{expanded-let} = \var{expanded}.
  20. Let expanded-letrec=expanded\var{expanded-letrec} = \var{expanded}.
  21. Let let-continuation-components=[var-let-letrec,expanded-let,env,cont]\var{let-continuation-components} = [\var{var-let-letrec}, \var{expanded-let}, \var{env}, \var{cont}].
  22. Call preimage-clauses.ADD-CLAUSE(let-sym.value(),globals.let-cont-tag,let-continuation-components)\var{preimage-clauses}.\gad{ADD-CLAUSE}(\var{let-sym.value}(), \var{globals.let-cont-tag}, \var{let-continuation-components}).
  23. Let letrec-continuation-components[var-let-letrec,expanded-letrec,env,cont]\var{letrec-continuation-components} [\var{var-let-letrec}, \var{expanded-letrec}, \var{env}, \var{cont}].
  24. Call preimage-clauses.ADD-CLAUSE(constants.letrec,globals.letrec-cont-tag,letrec-continuation-components)\var{preimage-clauses}.\gad{ADD-CLAUSE}(\var{constants.letrec}, \var{globals.letrec-cont-tag}, \var{letrec-continuation-components}).

Case head is eval\rm{\small{\bold{Case}~head~\bold{is}~eval}}.

  1. Let the-op=PICK(end-is-nil,globals.unop-cont-tag,globals.binop-cont-tag)\var{the-op} = \gad{PICK}(\var{end-is-nil}, \var{globals.unop-cont-tag}, \var{globals.binop-cont-tag}).
  2. Let op1-or-op2=PICK(end-is-nil,globals.op1-eval-tag,globals.op2-eval-tag)\var{op1-or-op2} = \gad{PICK}(\var{end-is-nil}, \var{globals.op1-eval-tag}, \var{globals.op2-eval-tag}).
  3. Let cont-or-env-tag=PICK(end-is-nil,cont.tag(),env.tag())\var{cont-or-env-tag} = \gad{PICK}(\var{end-is-nil}, \var{cont.tag}(), \var{env.tag}()).
  4. Let cont-or-env-hash=PICK(end-is-nil,cont.hash(),env.hash())\var{cont-or-env-hash} = \gad{PICK}(\var{end-is-nil}, \var{cont.hash}(), \var{env.hash}()).
  5. Let default-or-expr-tag=PICK(end-is-nil,globals.default-num,more.tag())\var{default-or-expr-tag} = \gad{PICK}(\var{end-is-nil}, \var{globals.default-num}, \var{more.tag}()).
  6. Let default-or-expr-hash=PICK(end-is-nil,globals.default-num,more.hash())\var{default-or-expr-hash} = \gad{PICK}(\var{end-is-nil}, \var{globals.default-num}, \var{more.hash}()).
  7. Let default-or-cont-tag=PICK(end-is-nil,globals.default-num,cont.tag())\var{default-or-cont-tag} = \gad{PICK}(\var{end-is-nil}, \var{globals.default-num}, \var{cont.tag}()).
  8. Let default-or-cont-hash=PICK(end-is-nil,globals.default-num,cont.hash())\var{default-or-cont-hash} = \gad{PICK}(\var{end-is-nil}, \var{globals.default-num}, \var{cont.hash}()).
  9. Let eval-continuation-components=[[op1-or-op2,globals.default-num],[env-or-cont-tag,env-or-cont-hash],[default-or-expr-tag,default-or-expr-hash],[default-or-cont-tag,default-or-cont-hash]]\var{eval-continuation-components} = [[\var{op1-or-op2}, \var{globals.default-num}], [\var{env-or-cont-tag}, \var{env-or-cont-hash}], [\var{default-or-expr-tag}, \var{default-or-expr-hash}], [\var{default-or-cont-tag}, \var{default-or-cont-hash}]].
  10. Call preimage-clauses.ADD-CLAUSE(constants.eval,the-op,eval-continuation-components)\var{preimage-clauses}.\gad{ADD-CLAUSE}(\var{constants.eval}, \var{the-op}, \var{eval-continuation-components}).

Case head is cons\rm{\small{\bold{Case}~head~\bold{is}~cons}}.

  1. Let cons-cont-components=((globals.op2-cons-tag,globals.default-num),env,more,cont)\var{cons-cont-components} = ((\var{globals.op2-cons-tag}, \var{globals.default-num}), \var{env}, \var{more}, \var{cont}).
  2. Call preimage-clauses.ADD-CLAUSE(constants.cons,globals.binop-cont-tag,cons-cont-components)\var{preimage-clauses}.\gad{ADD-CLAUSE}(\var{constants.cons}, \var{globals.binop-cont-tag}, \var{cons-cont-components}).

Case head is strcons\rm{\small{\bold{Case}~head~\bold{is}~strcons}}.

  1. Let strcons-cont-components=((globals.op2-strcons-tag,globals.default-num),env,more,cont)\var{strcons-cont-components} = ((\var{globals.op2-strcons-tag}, \var{globals.default-num}), \var{env}, \var{more}, \var{cont}).
  2. Call preimage-clauses.ADD-CLAUSE(constants.strcons,globals.binop-cont-tag,strcons-cont-components)\var{preimage-clauses}.\gad{ADD-CLAUSE}(\var{constants.strcons}, \var{globals.binop-cont-tag}, \var{strcons-cont-components}).

Case head is hide\rm{\small{\bold{Case}~head~\bold{is}~hide}}.

  1. Let hide-cont-components=((globals.op2-hide-tag,globals.default-num),env,more,cont)\var{hide-cont-components} = ((\var{globals.op2-hide-tag}, \var{globals.default-num}), \var{env}, \var{more}, \var{cont}).
  2. Call preimage-clauses.ADD-CLAUSE(constants.hide,globals.binop-cont-tag,hide-cont-components)\var{preimage-clauses}.\gad{ADD-CLAUSE}(\var{constants.hide}, \var{globals.binop-cont-tag}, \var{hide-cont-components}).

Case head is commit\rm{\small{\bold{Case}~head~\bold{is}~commit}}.

  1. Let commit-cont-components=((globals.op1-commit-tag,globals.default-num),(cont.tag(),cont.hash()),default-num-pair,default-num-pair)\var{commit-cont-components} = ((\var{globals}.\var{op1-commit-tag}, \var{globals}.\var{default-num}), (\var{cont}.\fun{tag}(), \var{cont}.\fun{hash}()), \var{default-num-pair}, \var{default-num-pair}).
  2. Call preimage-clauses.ADD-CLAUSE(constants.commit,globals.unop-cont-tag,commit-cont-components)\var{preimage-clauses}.\gad{ADD-CLAUSE}(\var{constants.commit}, \var{globals}.\var{unop-cont-tag}, \var{commit-cont-components}).

Case head is open\rm{\small{\bold{Case}~head~\bold{is}~open}}.

  1. Call results.ADD-CLAUSE(constants.open,arg1-or-expr,env,newer-cont-if-end-not-nil,globals.false)\var{results}.\gad{ADD-CLAUSE}(\var{constants.open}, \var{arg1-or-expr}, \var{env}, \var{newer-cont-if-end-not-nil}, \var{globals.false}).

Case head is u64\rm{\small{\bold{Case}~head~\bold{is}~u64}}.

  1. Let u64-cont-components=((globals.op1-u64-tag,globals.default-num),(cont.tag(),cont.hash()),default-num-pair,default-num-pair)\var{u64-cont-components} = ((\var{globals}.\var{op1-u64-tag}, \var{globals}.\var{default-num}), (\var{cont}.\fun{tag}(), \var{cont}.\fun{hash}()), \var{default-num-pair}, \var{default-num-pair}).
  2. Call preimage-clauses.ADD-CLAUSE(constants.u64,globals.unop-cont-tag,char-cont-components)\var{preimage-clauses}.\gad{ADD-CLAUSE}(\var{constants}.\var{u64}, \var{globals}.\var{unop-cont-tag}, \var{char-cont-components}).

Case head is char\rm{\small{\bold{Case}~head~\bold{is}~char}}.

  1. Let char-cont-components=((globals.op1-char-tag,globals.default-num),(cont.tag(),cont.hash()),default-num-pair,default-num-pair)\var{char-cont-components} = ((\var{globals}.\var{op1-char-tag}, \var{globals}.\var{default-num}), (\var{cont}.\fun{tag}(), \var{cont}.\fun{hash}()), \var{default-num-pair}, \var{default-num-pair}).
  2. Call preimage-clauses.ADD-CLAUSE(constants.char,globals.unop-cont-tag,char-cont-components)\var{preimage-clauses}.\gad{ADD-CLAUSE}(\var{constants}.\var{char}, \var{globals}.\var{unop-cont-tag}, \var{char-cont-components}).

Case head is car\rm{\small{\bold{Case}~head~\bold{is}~car}}.

  1. Let car-cont-components=((globals.op1-char-tag,globals.default-num),(cont.tag(),cont.hash()),default-num-pair,default-num-pair)\var{car-cont-components} = ((\var{globals}.\var{op1-char-tag}, \var{globals}.\var{default-num}), (\var{cont}.\fun{tag}(), \var{cont}.\fun{hash}()), \var{default-num-pair}, \var{default-num-pair}).
  2. Call preimage-clauses.ADD-CLAUSE(constants.car,globals.unop-cont-tag,car-cont-components)\var{preimage-clauses}.\gad{ADD-CLAUSE}(\var{constants}.\var{car}, \var{globals}.\var{unop-cont-tag}, \var{car-cont-components}).

Case head is cdr\rm{\small{\bold{Case}~head~\bold{is}~cdr}}.

  1. Let cdr-cont-components=(globals.op1-car-tag,globals.default-num),(cont.tag(),cont.hash()),default-num-pair,default-num-pair)\var{cdr-cont-components} = (\var{globals}.\var{op1-car-tag}, \var{globals}.\var{default-num}), (\var{cont}.\fun{tag}(), \var{cont}.\fun{hash}()), \var{default-num-pair}, \var{default-num-pair}).
  2. Call preimage-clauses.ADD-CLAUSE(constants.cdr,globals.unop-cont-tag,cdr-cont-components)\var{preimage-clauses}.\gad{ADD-CLAUSE}(\var{constants}.\var{cdr}, \var{globals}.\var{unop-cont-tag}, \var{cdr-cont-components}).

Case head is comm\rm{\small{\bold{Case}~head~\bold{is}~comm}}.

  1. Let comm-cont-components=((globals.op1-comm-tag,globals.default-num),(cont.tag(),cont.hash()),default-num-pair,default-num-pair)\var{comm-cont-components} = ((\var{globals}.\var{op1-comm-tag}, \var{globals}.\var{default-num}), (\var{cont}.\fun{tag}(), \var{cont}.\fun{hash}()), \var{default-num-pair}, \var{default-num-pair}).
  2. Call preimage-clauses.ADD-CLAUSE(constants.comm,globals.unop-cont-tag,comm-cont-components)\var{preimage-clauses}.\gad{ADD-CLAUSE}(\var{constants.comm}, \var{globals}.\var{unop-cont-tag}, \var{comm-cont-components}).

Case head is num\rm{\small{\bold{Case}~head~\bold{is}~num}}.

  1. Let num-cont-components=((globals.op1-num-tag,globals.default-num),(cont.tag(),cont.hash()),default-num-pair,default-num-pair)\var{num-cont-components} = ((\var{globals.op1-num-tag}, \var{globals.default-num}), (\var{cont.tag}(), \var{cont.hash}()), \var{default-num-pair}, \var{default-num-pair}).
  2. Call preimage-clauses.ADD-CLAUSE(constants.num,globals.unop-cont-tag,num-cont-components)\var{preimage-clauses}.\gad{ADD-CLAUSE}(\var{constants.num}, \var{globals.unop-cont-tag}, \var{num-cont-components}).

Case head is atom\rm{\small{\bold{Case}~head~\bold{is}~atom}}.

  1. Let atom-cont-components=((globals.op1-atom-tag,globals.default-num),(cont.tag(),cont.hash()),default-num-pair,default-num-pair)\var{atom-cont-components} = ((\var{globals.op1-atom-tag}, \var{globals.default-num}), (\var{cont.tag}(), \var{cont.hash}()), \var{default-num-pair}, \var{default-num-pair}).
  2. Call preimage-clauses.ADD-CLAUSE(constants.atom,globals.unop-cont-tag,atom-cont-components)\var{preimage-clauses}.\gad{ADD-CLAUSE}(\var{constants.atom}, \var{globals.unop-cont-tag}, \var{atom-cont-components}).

Case head is emit\rm{\small{\bold{Case}~head~\bold{is}~emit}}.

  1. Let emit-cont-components=((globals.op1-emit-tag,globals.default-num),(cont.tag(),cont.hash()),default-num-pair,default-num-pair)\var{emit-cont-components} = ((\var{globals.op1-emit-tag}, \var{globals.default-num}), (\var{cont.tag}(), \var{cont.hash}()), \var{default-num-pair}, \var{default-num-pair}).
  2. Call preimage-clauses.ADD-CLAUSE(constants.emit,globals.unop-cont-tag,emit-cont-components)\var{preimage-clauses}.\gad{ADD-CLAUSE}(\var{constants.emit}, \var{globals.unop-cont-tag}, \var{emit-cont-components}).

Case head is begin\rm{\small{\bold{Case}~head~\bold{is}~begin}}.

  1. Let begin-cont-components=((globals.op2-begin-tag,globals.default-num),env,more,cont)\var{begin-cont-components} = ((\var{globals.op2-begin-tag}, \var{globals.default-num}), \var{env}, \var{more}, \var{cont}).
  2. Call preimage-clauses.ADD-CLAUSE(constants.begin,globals.binop-cont-tag,begin-cont-components)\var{preimage-clauses}.\gad{ADD-CLAUSE}(\var{constants.begin}, \var{globals.binop-cont-tag}, \var{begin-cont-components}).

Case head is +\rm{\small{\bold{Case}~head~\bold{is}~+}}.

  1. Let sum-cont-components=((globals.op2-sum-tag,globals.default-num),env,more,cont)\var{sum-cont-components} = ((\var{globals.op2-sum-tag}, \var{globals.default-num}), \var{env}, \var{more}, \var{cont}).
  2. Call preimage-clauses.ADD-CLAUSE(constants.sum,globals.binop-cont-tag,sum-cont-components)\var{preimage-clauses}.\gad{ADD-CLAUSE}(\var{constants.sum}, \var{globals.binop-cont-tag}, \var{sum-cont-components}).

Case head is –\rm{\small{\bold{Case}~head~\bold{is}~–}}.

  1. Let diff-cont-components=((globals.op2-diff-tag,globals.default-num),env,more,cont)\var{diff-cont-components} = ((\var{globals.op2-diff-tag}, \var{globals.default-num}), \var{env}, \var{more}, \var{cont}).
  2. Call preimage-clauses.ADD-CLAUSE(constants.diff,globals.binop-cont-tag,diff-cont-components)\var{preimage-clauses}.\gad{ADD-CLAUSE}(\var{constants.diff}, \var{globals.binop-cont-tag}, \var{diff-cont-components}).

Case head is /\rm{\small{\bold{Case}~head~\bold{is}~/}}.

  1. Let quotient-cont-components=((globals.op2-diff-tag,globals.default-num),env,more,cont)\var{quotient-cont-components} = ((\var{globals.op2-diff-tag}, \var{globals.default-num}), \var{env}, \var{more}, \var{cont}).
  2. Call preimage-clauses.ADD-CLAUSE(constants.div,globals.binop-cont-tag,quotient-cont-components)\var{preimage-clauses}.\gad{ADD-CLAUSE}(\var{constants.div}, \var{globals.binop-cont-tag}, \var{quotient-cont-components}).

Case head is \rm{\small{\bold{Case}~head~\bold{is}~*}}.

  1. Let product-cont-components=((globals.op2-prod-tag,globals.default-num),env,more,cont)\var{product-cont-components} = ((\var{globals.op2-prod-tag}, \var{globals.default-num}), \var{env}, \var{more}, \var{cont}).
  2. Call preimage-clauses.ADD-CLAUSE(constants.mul,globals.binop-cont-tag,product-cont-components)\var{preimage-clauses}.\gad{ADD-CLAUSE}(\var{constants.mul}, \var{globals.binop-cont-tag}, \var{product-cont-components}).

Case head is=\rm{\small{\bold{Case}~head~\bold{is}=}}.

  1. Let numequal-cont-components=((globals.rel2-numequal-tag,globals.default-num),env,more,cont)\var{numequal-cont-components} = ((\var{globals.rel2-numequal-tag}, \var{globals.default-num}), \var{env}, \var{more}, \var{cont}).
  2. Call preimage-clauses.ADD-CLAUSE(contants.numequal,globals.binop-cont-tag,numequal-cont-components)\var{preimage-clauses}.\gad{ADD-CLAUSE}(\var{contants.numequal}, \var{globals.binop-cont-tag}, \var{numequal-cont-components}).

Case head is eq\rm{\small{\bold{Case}~head~\bold{is}~eq}}.

  1. Let equal-cont-components=((globals.rel2-numequal-tag,globals.default-num),env,more,cont)\var{equal-cont-components} = ((\var{globals.rel2-numequal-tag}, \var{globals.default-num}), \var{env}, \var{more}, \var{cont}).
  2. Call preimage-clauses.ADD-CLAUSE(constant.equal,globals.binop-cont-tag,equal-cont-components)\var{preimage-clauses}.\gad{ADD-CLAUSE}(\var{constant.equal}, \var{globals.binop-cont-tag}, \var{equal-cont-components}).

Case head is if\rm{\small{\bold{Case}~head~\bold{is}~if}}.

  1. Let if-cont-components=(more,cont,default-num-pair,default-num-pair)\var{if-cont-components} = (\var{more}, \var{cont}, \var{default-num-pair}, \var{default-num-pair}).
  2. Call preimage-clauses.ADD-CLAUSE(constants.if,globals.if-cont-tag,if-cont-components)\var{preimage-clauses}.\gad{ADD-CLAUSE}(\var{constants.if}, \var{globals.if-cont-tag}, \var{if-cont-components}).

Case head is currentenv\rm{\small{\bold{Case}~head~\bold{is}~current{-}env}}.

  1. Let current-env-cont=PICK(rest-is-nil,globals.error-ptr-cont,cont)\var{current-env-cont} = \gad{PICK}(\var{rest-is-nil}, \var{globals.error-ptr-cont}, \var{cont}).
  2. Call clauses.ADD-CLAUSE(constants.current-env,env,env,globals.current-env-cont,globals.true)\var{clauses}.\gad{ADD-CLAUSE}(\var{constants.current-env}, \var{env}, \var{env}, \var{globals.current-env-cont}, \var{globals.true}).

Compute the hash preimage components\rm{\footnotesize{Compute~the~hash~preimage~components}}:

  1. Let preimage-result=MULTICASE(preimage-clauses)\var{preimage-result} = \gad{MULTICASE}(\var{preimage-clauses}).
  2. Let newer-cont=CONSTRUCT-FROM-COMPONENTS(preimage-components-result)\var{newer-cont} = \gad{CONSTRUCT-FROM-COMPONENTS}(\var{preimage-components-result}).
  3. Let newer-cont-if-end-is-nil=PICK(end-is-nil,newer-cont,globals.error-ptr-cont)\var{newer-cont-if-end-is-nil} = \gad{PICK}(\var{end-is-nil}, \var{newer-cont}, \var{globals.error-ptr-cont}).
  4. Let newer-cont-if-end-not-nil=PICK(end-is-nil,globals.error-ptr-cont,newer-cont)\var{newer-cont-if-end-not-nil} = \gad{PICK}(\var{end-is-nil}, \var{globals.error-ptr-cont}, \var{newer-cont}).

Case head is let or letrec\rm{\small{\bold{Case}~head~\bold{is}~let~\bold{or}~letrec}}.

  1. Let output-cont=PICK(bindings-is-nil,cont,newer-cont)\var{output-cont} = \gad{PICK}(\var{bindings-is-nil}, \var{cont}, \var{newer-cont}).
  2. Let the-cont-let-letrec=PICK(cond-error,globals.error-ptr-cont,output-cont)\var{the-cont-let-letrec} = \gad{PICK}(\var{cond-error}, \var{globals.error-ptr-cont}, \var{output-cont}).
  3. Call clauses.ADD-CLAUSE(constants.let,the-expr,env,the-cont-let-letrec,globals.false)\var{clauses}.\gad{ADD-CLAUSE}(\var{constants.let}, \var{the-expr}, \var{env}, \var{the-cont-let-letrec}, \var{globals.false}).
  4. Call clauses.ADD-CLAUSE(constants.letrec,the-expr,env,the-cont-let-letrec,globals.false)\var{clauses}.\gad{ADD-CLAUSE}(\var{constants.letrec}, \var{the-expr}, \var{env}, \var{the-cont-let-letrec}, \var{globals.false}).

Case head is cons\rm{\small{\bold{Case}~head~\bold{is}~cons}}.

  1. Call clauses.ADD-CLAUSE(constants.cons,arg1,env,newer-cont-if-end-not-nil,globals.false)\var{clauses}.\gad{ADD-CLAUSE}(\var{constants.cons}, \var{arg1}, \var{env}, \var{newer-cont-if-end-not-nil}, \var{globals.false}).

Case head is hide\rm{\small{\bold{Case}~head~\bold{is}~hide}}.

  1. Call clauses.ADD-CLAUSE(contants.hide,arg1,env,newer-cont-if-end-not-nil,globals.false)\var{clauses}.\gad{ADD-CLAUSE}(\var{contants.hide}, \var{arg1}, \var{env}, \var{newer-cont-if-end-not-nil}, \var{globals.false}).

Case head is begin\rm{\small{\bold{Case}~head~\bold{is}~begin}}.

  1. Let the-cont-begin=PICK(end-is-nil,cont,newer-cont)\var{the-cont-begin} = \gad{PICK}(\var{end-is-nil}, \var{cont}, \var{newer-cont}).
  2. Call clauses.ADD-CLAUSE(contants.begin,arg1,env,the-cont-begin,globals.false)\var{clauses}.\gad{ADD-CLAUSE}(\var{contants.begin}, \var{arg1}, \var{env}, \var{the-cont-begin}, \var{globals.false}).

Case head is car\rm{\small{\bold{Case}~head~\bold{is}~car}}.

  1. Call clauses.ADD-CLAUSE(contants.car,arg1-or-expr,env,newer-cont-if-end-not-nil,globals.false)\var{clauses}.\gad{ADD-CLAUSE}(\var{contants.car}, \var{arg1-or-expr}, \var{env}, \var{newer-cont-if-end-not-nil}, \var{globals.false}).

Case head is cdr\rm{\small{\bold{Case}~head~\bold{is}~cdr}}.

  1. Call clauses.ADD-CLAUSE(contants.cdr,arg1-or-expr,env,newer-cont-if-end-not-nil,globals.false)\var{clauses}.\gad{ADD-CLAUSE}(\var{contants.cdr}, \var{arg1-or-expr}, \var{env}, \var{newer-cont-if-end-not-nil}, \var{globals.false}).

Case head is commit\rm{\small{\bold{Case}~head~\bold{is}~commit}}.

  1. Call clauses.ADD-CLAUSE(constants.commit,arg1-or-expr,env,newer-cont-if-end-not-nil,globals.false)\var{clauses}.\gad{ADD-CLAUSE}(\var{constants.commit}, \var{arg1-or-expr}, \var{env}, \var{newer-cont-if-end-not-nil}, \var{globals.false}).

Case head is secret\rm{\small{\bold{Case}~head~\bold{is}~secret}}.

  1. Call clauses.ADD-CLAUSE(constants.secret,arg1-or-expr,env,newer-cont-if-end-not-nil,globals.false)\var{clauses}.\gad{ADD-CLAUSE}(\var{constants.secret}, \var{arg1-or-expr}, \var{env}, \var{newer-cont-if-end-not-nil}, \var{globals.false}).

Case head is num\rm{\small{\bold{Case}~head~\bold{is}~num}}.

  1. Call clauses.ADD-CLAUSE(constants.num,arg1-or-expr,env,newer-cont-if-end-not-nil,globals.false)\var{clauses}.\gad{ADD-CLAUSE}(\var{constants.num}, \var{arg1-or-expr}, \var{env}, \var{newer-cont-if-end-not-nil}, \var{globals.false}).

Case head is comm\rm{\small{\bold{Case}~head~\bold{is}~comm}}.

  1. Call clauses.ADD-CLAUSE(constants.comm,arg1-or-expr,env,newer-cont-if-end-not-nil,globals.false)\var{clauses}.\gad{ADD-CLAUSE}(\var{constants.comm}, \var{arg1-or-expr}, \var{env}, \var{newer-cont-if-end-not-nil}, \var{globals.false}).

Case head is char\rm{\small{\bold{Case}~head~\bold{is}~char}}.

  1. Call clauses.ADD-CLAUSE(constants.char,arg1-or-expr,env,newer-cont-if-end-not-nil,globals.false)\var{clauses}.\gad{ADD-CLAUSE}(\var{constants.char}, \var{arg1-or-expr}, \var{env}, \var{newer-cont-if-end-not-nil}, \var{globals.false}).

Case head is atom\rm{\small{\bold{Case}~head~\bold{is}~atom}}.

  1. Call clauses.ADD-CLAUSE(constants.atom,arg1-or-expr,env,newer-cont-if-end-not-nil,globals.false)\var{clauses}.\gad{ADD-CLAUSE}(\var{constants.atom}, \var{arg1-or-expr}, \var{env}, \var{newer-cont-if-end-not-nil}, \var{globals.false}).

Case head is emit\rm{\small{\bold{Case}~head~\bold{is}~emit}}.

  1. Call clauses.ADD-CLAUSE(constants.emit,arg1-or-expr,env,newer-cont-if-end-not-nil,globals.false)\var{clauses}.\gad{ADD-CLAUSE}(\var{constants.emit}, \var{arg1-or-expr}, \var{env}, \var{newer-cont-if-end-not-nil}, \var{globals.false}).

Case head is +\rm{\small{\bold{Case}~head~\bold{is}~+}}.

  1. Call clauses.ADD-CLAUSE(constants.sum,arg1,env,newer-cont,globals.false)\var{clauses}.\gad{ADD-CLAUSE}(\var{constants.sum}, \var{arg1}, \var{env}, \var{newer-cont}, \var{globals.false}).

Case head is –\rm{\small{\bold{Case}~head~\bold{is}~–}}.

  1. Call clauses.ADD-CLAUSE(constants.diff,arg1,env,newer-cont,globals.false)\var{clauses}.\gad{ADD-CLAUSE}(\var{constants.diff}, \var{arg1}, \var{env}, \var{newer-cont}, \var{globals.false}).

Case head is \rm{\small{\bold{Case}~head~\bold{is}~*}}.

  1. Call clauses.ADD-CLAUSE(constants.mul,arg1,env,newer-cont,globals.false)\var{clauses}.\gad{ADD-CLAUSE}(\var{constants.mul}, \var{arg1}, \var{env}, \var{newer-cont}, \var{globals.false}).

Case head is /\rm{\small{\bold{Case}~head~\bold{is}~/}}.

  1. Call clauses.ADD-CLAUSE(constants.div,arg1,env,newer-cont,globals.false)\var{clauses}.\gad{ADD-CLAUSE}(\var{constants.div}, \var{arg1}, \var{env}, \var{newer-cont}, \var{globals.false}).

Case head is=\rm{\small{\bold{Case}~head~\bold{is}=}}.

  1. Call clauses.ADD-CLAUSE(constants.numequal,arg1,env,newer-cont,globals.false)\var{clauses}.\gad{ADD-CLAUSE}(\var{constants.numequal}, \var{arg1}, \var{env}, \var{newer-cont}, \var{globals.false}).

Case head is eq\rm{\small{\bold{Case}~head~\bold{is}~eq}}.

  1. Call clauses.ADD-CLAUSE(constants.equal,arg1,env,newer-cont,globals.false)\var{clauses}.\gad{ADD-CLAUSE}(\var{constants.equal}, \var{arg1}, \var{env}, \var{newer-cont}, \var{globals.false}).

Case head is if\rm{\small{\bold{Case}~head~\bold{is}~if}}.

  1. Call clauses.ADD-CLAUSE(constants.if-sym,arg1,env,newer-cont,globals.false)\var{clauses}.\gad{ADD-CLAUSE}(\var{constants.if-sym}, \var{arg1}, \var{env}, \var{newer-cont}, \var{globals.false}).

Case head is (FN.ARGs)\rm{\small{\bold{Case}~head~\bold{is}~(FN.ARGs)}}.

  1. Let fun-form=head\var{fun-form} = \var{head}.
  2. Let more-args-is-nil=more.IS-NIL()\var{more-args-is-nil} = \var{more}.\gad{IS-NIL}().
  3. Let args-is-nil-or-more-is-nil=OR(rest-is-nil,more-args-is-nil)\var{args-is-nil-or-more-is-nil} = \gad{OR}(\var{rest-is-nil}, \var{more-args-is-nil}).
  4. Let fn-not-dummy=AND(args-is-nil-or-more-is-nil.NOT(),head-is-any.NOT(),not-dummy)\var{fn-not-dummy} = \gad{AND}(\var{args-is-nil-or-more-is-nil}.\gad{NOT}(), \var{head-is-any}.\gad{NOT}(), \var{not-dummy}).
  5. Let expanded-inner0=CONSTRUCT-CONS-NAMED(arg1,globals.nil-ptr,cons-names.expanded-inner0,allocated-cons-witness,fn-not-dummy)\var{expanded-inner0} = \gad{CONSTRUCT-CONS-NAMED}(\var{arg1}, \var{globals.nil-ptr}, \var{cons-names.expanded-inner0}, \var{allocated-cons-witness}, \var{fn-not-dummy}).
  6. Let expanded-inner=CONSTRUCT-CONS-NAMED(fun-form,expanded-inner0,cons-names.expanded-inner,allocated-cons-witness,fn-not-dummy)\var{expanded-inner} = \gad{CONSTRUCT-CONS-NAMED}(\var{fun-form}, \var{expanded-inner0}, \var{cons-names.expanded-inner}, \var{allocated-cons-witness}, \var{fn-not-dummy}).
  7. Let expanded=CONSTRUCT-CONS-NAMED(expanded-inner,more,cons-names.fun-expanded,allocated-cons-witness,fn-not-dummy)\var{expanded} = \gad{CONSTRUCT-CONS-NAMED}(\var{expanded-inner}, \var{more}, \var{cons-names.fun-expanded}, \var{allocated-cons-witness}, \var{fn-not-dummy}).
  8. Let res=PICK(args-is-nil-or-more-is-nil,fun-form,expanded)\var{res} = \var{PICK}(\var{args-is-nil-or-more-is-nil}, \var{fun-form}, \var{expanded}).
  9. Let continuation=PICK(args-is-nil-or-more-is-nil,newer-cont,cont)\var{continuation} = \gad{PICK}(\var{args-is-nil-or-more-is-nil}, \var{newer-cont}, \var{cont}).
  10. Let defaults=[res.tag(),res.hash(),env.tag(),env.hash(),continuation.tag(),continuation.hash(),globals.false-num]\var{defaults} = [\var{res.tag}(), \var{res.hash}(), \var{env.tag}(), \var{env.hash}(), \var{continuation.tag}(), \var{continuation.hash}(), \var{globals.false-num}].

Finally, we compute the last multicase and return the result.

  1. Let results=multi-case(head.hash(),clauses,defaults)\var{results} = \gad{multi-case}(\var{head.hash}(), \var{clauses}, \var{defaults}).
  2. Let result-expr=results[0]\var{result-expr} = \var{results}[0].
  3. Let result-env=results[1]\var{result-env} = \var{results}[1].
  4. Let result-cont=results[2]\var{result-cont} = \var{results}[2].
  5. Let result-apply-cont=results[3]\var{result-apply-cont} = \var{results}[3].
  6. Return (result-expr,result-env,result-cont,result-apply-cont)(\var{result-expr}, \var{result-env}, \var{result-cont}, \var{result-apply-cont}).

Apply continuation


Circuit 3.4  Apply-Continuation\cirtitle{Circuit~3.4}~\cir{Apply-Continuation} \\
  1. INPUT result,env,cont,witness\cirio{INPUT} \var{result}, \var{env}, \var{cont}, \var{witness}.
  2. OUTPUT expr,env,cont\cirio{OUTPUT} \var{expr}, \var{env}, \var{cont}.
  3. Do pre-pointer computations, adding clauses to both multicases \com{Do pre-pointer computations, adding clauses to both multicases}.
  4. preimage=MULTICASE(preimage-clauses)\var{preimage} = \gad{MULTICASE}(\var{preimage-clauses}).
  5. Calculate newer continuation pointer \com{Calculate newer continuation pointer}.
  6. Do post-pointer computations, adding clauses to second multicase \com{Do post-pointer computations, adding clauses to second multicase}.
  7. result=MULTICASE(clauses)\var{result} = \gad{MULTICASE}(\var{clauses}).
  8. Return result\var{result}.

Initially, we do:

  1. Call clauses.ADD-CLAUSE(cont-tags.outermost,result,env,globals.terminal-ptr,globals.false-num,globals.false-num)\var{clauses}.\gad{ADD-CLAUSE}(\var{cont-tags.outermost}, \var{result}, \var{env}, \var{globals.terminal-ptr}, \var{globals.false-num}, \var{globals.false-num}).
  2. Call clauses.ADD-CLAUSE(cont-tags.terminal,result,env,globals.terminal-ptr,globals.false-num,globals.false-num)\var{clauses}.\gad{ADD-CLAUSE}(\var{cont-tags.terminal}, \var{result}, \var{env}, \var{globals.terminal-ptr}, \var{globals.false-num}, \var{globals.false-num}).
  3. Call clauses.ADD-CLAUSE(cont-tags.error,result,env,globals.error-cont-ptr,globals.false-num,globals.false-num)\var{clauses}.\gad{ADD-CLAUSE}(\var{cont-tags.error}, \var{result}, \var{env}, \var{globals.error-cont-ptr}, \var{globals.false-num}, \var{globals.false-num}).
  4. Let cont-is-terminal=ALLOC-TAG-EQUAL(cont.tag(),globals.terminal-tag())\var{cont-is-terminal} = \gad{ALLOC-TAG-EQUAL}(\var{cont.tag}(), \var{globals.terminal-tag}()).
  5. Let cont-is-dummy=ALLOC-TAG-EQUAL(cont.tag(),globals.dummy-tag())\var{cont-is-dummy} = \gad{ALLOC-TAG-EQUAL}(\var{cont.tag}(), \var{globals.dummy-tag}()).
  6. Let cont-is-error=ALLOC-TAG-EQUAL(cont.tag(),globals.error-tag())\var{cont-is-error} = \gad{ALLOC-TAG-EQUAL}(\var{cont.tag}(), \var{globals.error-tag}()).
  7. Let cont-is-outermost=ALLOC-TAG-EQUAL(cont.tag(),globals.outermost-tag())\var{cont-is-outermost} = \gad{ALLOC-TAG-EQUAL}(\var{cont.tag}(), \var{globals.outermost-tag}()).
  8. Let cont-is-trivial=OR(cont-is-terminal,cont-is-dummy,cont-is-error,cont-is-outermost)\var{cont-is-trivial} = \gad{OR}(\var{cont-is-terminal}, \var{cont-is-dummy}, \var{cont-is-error}, \var{cont-is-outermost}).
  9. Let apply-continuation-components-not-dummy=AND(cont-is-trivial.NOT(),not-dummy)\var{apply-continuation-components-not-dummy} = \gad{AND}(\var{cont-is-trivial}.\gad{NOT}(), \var{not-dummy}).
  10. Let (continuation-tag,continuation-components)=get-named-components(cont,cons-names.apply-continuation,allocated-cont-witness,apply-continuation-components-not-dummy)(\var{continuation-tag}, \var{continuation-components}) = \fun{get-named-components}(\var{cont}, \var{cons-names.apply-continuation}, \var{allocated-cont-witness}, \var{apply-continuation-components-not-dummy}).
  11. Let continuation=continuation-components[0]\var{continuation} = \var{continuation-components}[0].
  12. call clauses.ADD-CLAUSE(cont-tag.emit,result,env,continuation,globals.true-num,globals.false-num)\var{clauses}.\gad{ADD-CLAUSE}(\var{cont-tag.emit}, \var{result}, \var{env}, \var{continuation}, \var{globals.true-num}, \var{globals.false-num}).
  13. Let preimage-clauses=MULTICASE-CLAUSES()\var{preimage-clauses} = \gad{MULTICASE-CLAUSES}().
  14. Let cont-components=get-cont-components(witness)\var{cont-components} = \fun{get-cont-components}(\var{witness}).
  15. Let default-num-pair=(globals.default-num,globals.default-num)\var{default-num-pair} = (\var{globals.default-num},\var{globals.default-num}).

Case cont is call0\rm{\small{\bold{Case}~ cont~\bold{is}~call0}}.

  1. Let old-components=(cont-components[0],cont-components[1],cont-components[2],cont-components[3])\var{old-components} = (\var{cont-components}[0], \var{cont-components}[1], \var{cont-components}[2], \var{cont-components}[3]).
  2. Call preimage-clauses.ADD-CLAUSE(globals.call0-sym,globals.tail-cont-tag,old-cont-components)\var{preimage-clauses}.\gad{ADD-CLAUSE}(\var{globals.call0-sym}, \var{globals.tail-cont-tag}, \var{old-cont-components}).

Case cont is call\rm{\small{\bold{Case}~ cont~\bold{is}~call}}.

  1. Let call-components=(cont-components[0],result,cont-components[2],default-num-pair)\var{call-components} = (\var{cont-components}[0], \var{result}, \var{cont-components}[2], \var{default-num-pair}).
  2. Call preimage-clauses.ADD-CLAUSE(globals.call-sym,globals.call2-cont-tag,call-components)\var{preimage-clauses}.\gad{ADD-CLAUSE}(\var{globals.call-sym}, \var{globals.call2-cont-tag}, \var{call-components}).

Case cont is call2\rm{\small{\bold{Case}~ cont~\bold{is}~call2}}.

  1. Let all2-components=(saved-env,continuation,default-num-pair,default-num-pair)\var{all2-components} = (\var{saved-env}, \var{continuation}, \var{default-num-pair}, \var{default-num-pair}).
  2. Call preimage-clauses.ADD-CLAUSE(globals.call2-sym,globals.tail-cont-tag,call2-components)\var{preimage-clauses}.\gad{ADD-CLAUSE}(\var{globals.call2-sym}, \var{globals.tail-cont-tag}, \var{call2-components}).

Case cont is let\rm{\small{\bold{Case}~ cont~\bold{is}~let}}.

  1. Let let-components=(cont-components[2],cont-components[3],default-num-pair,default-num-pair)\var{let-components} = (\var{cont-components}[2], \var{cont-components}[3], \var{default-num-pair}, \var{default-num-pair}).
  2. Call preimage-clauses.ADD-CLAUSE(globals.let-sym,globals.tail-cont-tag,let-components)\var{preimage-clauses}.\gad{ADD-CLAUSE}(\var{globals.let-sym}, \var{globals.tail-cont-tag}, \var{let-components}).

Case cont is letrec\rm{\small{\bold{Case}~ cont~\bold{is}~letrec}}.

  1. Let letrec-components=(cont-components[2],cont-components[3],default-num-pair,default-num-pair)\var{letrec-components} = (\var{cont-components}[2], \var{cont-components}[3], \var{default-num-pair}, \var{default-num-pair}).
  2. Call preimage-clauses.ADD-CLAUSE(globals.letrec-sym,globals.tail-cont-tag,letrec-components)\var{preimage-clauses}.\gad{ADD-CLAUSE}(\var{globals.letrec-sym}, \var{globals.tail-cont-tag}, \var{letrec-components}).

At this point we need to add commitment constraints that are going to be useful later.

  1. Let operator=continuation-components[0]\var{operator} = \var{continuation-components}[0].
  2. Let is-op2-hide=ALLOC-TAG-EQUAL(globals.op2-hide-tag,operator.tag())\var{is-op2-hide} = \gad{ALLOC-TAG-EQUAL}(\var{globals.op2-hide-tag}, \var{operator.tag}()).
  3. Let is-op1-open=ALLOC-TAG-EQUAL(globals.op1-open-tag,operator.tag())\var{is-op1-open} = \gad{ALLOC-TAG-EQUAL}(\var{globals.op1-open-tag}, \var{operator.tag}()).
  4. Let is-op1-secret=ALLOC-TAG-EQUAL(globals.op1-secret-tag,operator.tag())\var{is-op1-secret} = \gad{ALLOC-TAG-EQUAL}(\var{globals.op1-secret-tag}, \var{operator.tag}()).
  5. Let is-op1-open-or-secret=OR(is-op1-open,is-op1-secret)\var{is-op1-open-or-secret} = \gad{OR}(\var{is-op1-open}, \var{is-op1-secret}).
  6. Let digest=result.hash()\var{digest} = \var{result.hash}().
  7. Let (secret-res,open-res)=NON-DETERMINISTIC-OPEN-SECRET(expr-tag.comm,digest)(\var{secret-res}, \var{open-res}) = \gad{NON-DETERMINISTIC-OPEN-SECRET}(\var{expr-tag.comm}, \var{digest}).
  8. Let open-expr=ALLOC(open-res)\var{open-expr} = \gad{ALLOC}(\var{open-res}).
  9. Let open-secret=ALLOC(secret-res)\var{open-secret} = \gad{ALLOC}(\var{secret-res}).
  10. Let arg1=continuation-components[1]\var{arg1} = \var{continuation-components}[1].
  11. Let commit-secret=PICK(is-op2-hide,arg1.hash(),globals.default-num)\var{commit-secret} = \gad{PICK}(\var{is-op2-hide}, \var{arg1.hash}(), \var{globals.default-num}),
  12. Let secret=PICK(is-op1-open-or-secret,open-secret,commit-secret)\var{secret} = \gad{PICK}(\var{is-op1-open-or-secret}, \var{open-secret}, \var{commit-secret}).
  13. Let committed=PICK(is-op1-open,open-expr,result)\var{committed} = \gad{PICK}(\var{is-op1-open}, \var{open-expr}, \var{result}).
  14. Let commitment=HIDE(secret,committed)\var{commitment} = \gad{HIDE}(\var{secret}, \var{committed}).
  15. Let commitment-secret=secret\var{commitment-secret} = \var{secret}.
  16. Let committed-expr=committed\var{committed-expr} = \var{committed}.
let (unop_val, unop_continuation) = {

Case cont is unop\rm{\small{\bold{Case}~ cont~\bold{is}~unop}}.

  1. Let op1=continuation-components[0]\var{op1} = \var{continuation-components}[0].
  2. Let unop-continuation=continuation-components[1]\var{unop-continuation} = \var{continuation-components}[1].
  3. Let cont-is-unop=ALLOC-TAG-EQUAL(cont.tag(),globals.unop-cont-tag)\var{cont-is-unop} = \gad{ALLOC-TAG-EQUAL}(\var{cont.tag}(), \var{globals.unop-cont-tag}).
  4. Let unop-op-is-car=ALLOC-TAG-EQUAL(op1.tag(),globals.op1-car-tag)\var{unop-op-is-car} = \gad{ALLOC-TAG-EQUAL}(\var{op1.tag}(), \var{globals.op1-car-tag}).
  5. Let unop-op-is-cdr=ALLOC-TAG-EQUAL(op1.tag(),globals.op1-cdr-tag)\var{unop-op-is-cdr} = \gad{ALLOC-TAG-EQUAL}(\var{op1.tag}(), \var{globals.op1-cdr-tag}).
  6. Let unop-op-is-car-or-cdr=OR(unop-op-is-car,unop-op-is-cdr)\var{unop-op-is-car-or-cdr} = \gad{OR}(\var{unop-op-is-car}, \var{unop-op-is-cdr}).
  7. Let result-is-cons=ALLOC-TAG-EQUAL(result.tag(),globals.cons-tag)\var{result-is-cons} = \gad{ALLOC-TAG-EQUAL}(\var{result.tag}(), \var{globals.cons-tag}).
  8. Let result-is-str=ALLOC-TAG-EQUAL(result.tag(),globals.str-tag)\var{result-is-str} = \gad{ALLOC-TAG-EQUAL}(\var{result.tag}(), \var{globals.str-tag}).
  9. Let result-is-empty-str=ALLOC-EQUAL(result,globals.empty-str-ptr)\var{result-is-empty-str} = \gad{ALLOC-EQUAL}(\var{result}, \var{globals.empty-str-ptr}).
  10. Let result-is-cons-like=OR(result-is-cons,result-is-str)\var{result-is-cons-like} = \gad{OR}(\var{result-is-cons}, \var{result-is-str}).
  11. Let unop-cons-not-dummy=AND(cont-is-unop,unop-op-is-car-or-cdr,result-is-cons-like,result-is-empty-str.NOT())\var{unop-cons-not-dummy} = \gad{AND}(\var{cont-is-unop}, \var{unop-op-is-car-or-cdr}, \var{result-is-cons-like}, \var{result-is-empty-str}.\gad{NOT}()).
  12. Let (allocated-car,allocated-cdr)=CAR-CDR-NAMED(result,cons-names.unop-cons-like,allocated-cons-witness,unop-cons-not-dummy)(\var{allocated-car}, \var{allocated-cdr}) = \gad{CAR-CDR-NAMED}(\var{result}, \var{cons-names.unop-cons-like}, \var{allocated-cons-witness}, \var{unop-cons-not-dummy}).
  13. Let res-car=PICK(result-is-empty-str,globals.nil-ptr,allocated-car)\var{res-car} = \gad{PICK}(\var{result-is-empty-str}, \var{globals.nil-ptr}, \var{allocated-car}).
  14. Let res-cdr=PICK(result-is-empty-str,globals.empty-str-ptr,allocated-cdr)\var{res-cdr} = \gad{PICK}(\var{result-is-empty-str}, \var{globals.empty-str-ptr}, \var{allocated-cdr}).
  15. Let is-atom-ptr=PICK(result-is-cons,globals.nil-ptr,globals.t-ptr)\var{is-atom-ptr} = \gad{PICK}(\var{result-is-cons}, \var{globals.nil-ptr}, \var{globals.t-ptr}).
  16. Let num=TO-NUM(result)\var{num} = \gad{TO-NUM}(\var{result}).
  17. Let comm=TO-COMM(result)\var{comm} = \gad{TO-COMM}(\var{result}).
  18. Let (u32-elem,u64-elem)=to-unsigned-integers(result.hash())(\var{u32-elem}, \var{u64-elem}) = \gad{to-unsigned-integers}(\var{result.hash}()).
  19. Call unop-clauses.ADD-CLAUSE(op1.car-tag,(res-car.tag(),res-car.hash())\var{unop-clauses}.\gad{ADD-CLAUSE}(\var{op1.car-tag}, (\var{res-car.tag}(), \var{res-car.hash}()).
  20. Call unop-clauses.ADD-CLAUSE(op1.cdr-tag,(res-cdr.tag(),res-cdr.hash())\var{unop-clauses}.\gad{ADD-CLAUSE}(\var{op1.cdr-tag}, (\var{res-cdr.tag}(), \var{res-cdr.hash}()).
  21. Call unop-clauses.ADD-CLAUSE(op1.atom-tag,(is-atom-ptr.tag(),is-atom-ptr.hash())\var{unop-clauses}.\gad{ADD-CLAUSE}(\var{op1.atom-tag}, (\var{is-atom-ptr.tag}(), \var{is-atom-ptr.hash}()).
  22. Call unop-clauses.ADD-CLAUSE(op1.emit-tag,(result.tag(),result.hash())\var{unop-clauses}.\gad{ADD-CLAUSE}(\var{op1.emit-tag}, (\var{result.tag}(), \var{result.hash}()).
  23. Call unop-clauses.ADD-CLAUSE(op1.commit-tag,(commitment.tag(),commitment.hash())\var{unop-clauses}.\gad{ADD-CLAUSE}(\var{op1.commit-tag}, (\var{commitment.tag}(), \var{commitment.hash}()).
  24. Call unop-clauses.ADD-CLAUSE(op1.open-tag,(committed-expr.tag(),commitment.hash())\var{unop-clauses}.\gad{ADD-CLAUSE}(\var{op1.open-tag}, (\var{committed-expr.tag}(), \var{commitment.hash}()).
  25. Call unop-clauses.ADD-CLAUSE(op1.secret-tag,(globals.num-tag,commitment-secret())\var{unop-clauses}.\gad{ADD-CLAUSE}(\var{op1.secret-tag}, (\var{globals.num-tag}, \var{commitment-secret}()).
  26. Call unop-clauses.ADD-CLAUSE(op1.num-tag,(globals.num-tag,num.hash())\var{unop-clauses}.\gad{ADD-CLAUSE}(\var{op1.num-tag}, (\var{globals.num-tag}, \var{num.hash}()).
  27. Call unop-clauses.ADD-CLAUSE(op1.u64-tag,(globals.u64-tag,u64-elem())\var{unop-clauses}.\gad{ADD-CLAUSE}(\var{op1.u64-tag}, (\var{globals.u64-tag}, \var{u64-elem}()).
  28. Call unop-clauses.ADD-CLAUSE(op1.comm-tag,(comm.tag(),comm.hash())\var{unop-clauses}.\gad{ADD-CLAUSE}(\var{op1.comm-tag}, (\var{comm.tag}(), \var{comm.hash}()).
  29. Call unop-clauses.ADD-CLAUSE(op1.char-tag,(globals.char-tag,is-atom-ptr.hash())\var{unop-clauses}.\gad{ADD-CLAUSE}(\var{op1.char-tag}, (\var{globals.char-tag}, \var{is-atom-ptr.hash}()).
  30. Call unop-clauses.ADD-CLAUSE(op1.eval-tag,(result.tag(),result.hash())\var{unop-clauses}.\gad{ADD-CLAUSE}(\var{op1.eval-tag}, (\var{result.tag}(), \var{result.hash}()).
  31. Let res=MULTI-CASE(op1.tag(),unop-clauses)\var{res} = \gad{MULTI-CASE}(\var{op1.tag}(), \var{unop-clauses}).
  32. let unop-val=res[0]\var{unop-val} = \var{res}[0].
  33. Let emit-components=(unop-continuation,default-num-pair,default-num-pair,default-num-pair)\var{emit-components} = (\var{unop-continuation}, \var{default-num-pair}, \var{default-num-pair}, \var{default-num-pair}).
  34. Call preimage-clauses.ADD-CLAUSE(globals.unop-tag,globals.emit-cont-tag,emit-components)\var{preimage-clauses}.\gad{ADD-CLAUSE}(\var{globals.unop-tag}, \var{globals.emit-cont-tag}, \var{emit-components}).

Case cont is binop\rm{\small{\bold{Case}~ cont~\bold{is}~binop}}.

  1. Let unop-continuation=cont-components[3]\var{unop-continuation} = \var{cont-components}[3].
  2. Let binop-components=(cont-components[0],result,unop-continaution,default-num-pair)\var{binop-components} = (\var{cont-components}[0], \var{result}, \var{unop-continaution}, \var{default-num-pair}).
  3. Call preimage-clauses.ADD-CLAUSE(globals.binop-tag,globals.binop2-cont-tag,binop-components)\var{preimage-clauses}.\gad{ADD-CLAUSE}(\var{globals.binop-tag}, \var{globals.binop2-cont-tag}, \var{binop-components}).

Compute the hash preimage components\rm{\footnotesize{Compute~the~hash~preimage~components}}:

  1. Let preimage-result=MULTICASE(preimage-clauses)\var{preimage-result} = \gad{MULTICASE}(\var{preimage-clauses}).
  2. Let newer-cont=CONSTRUCT-FROM-COMPONENTS(preimage-result)\var{newer-cont} = \gad{CONSTRUCT-FROM-COMPONENTS}(\var{preimage-result}).

We define result-is-fun\var{result-is-fun}, which is used in Call0, Call and Call2, as follows:

  1. Let result-is-fun=IS-FUN(function)\var{result-is-fun} = \gad{IS-FUN}(\var{function}).

Case cont is call0\rm{\small{\bold{Case}~ cont~\bold{is}~call0}}.

  1. Let continuation=continuation-components[1]\var{continuation} = \var{continuation-components}[1].
  2. Let cont-is-call0=ALLOC-TAG-EQUAL(cont.tag(),globals.call0-cont-tag)\var{cont-is-call0} = \gad{ALLOC-TAG-EQUAL}(\var{cont.tag}(), \var{globals.call0-cont-tag}).
  3. Let call0-not-dummy=AND(cont-is-call0,result-is-fun,not-dummy)\var{call0-not-dummy} = \gad{AND}(\var{cont-is-call0}, \var{result-is-fun}, \var{not-dummy}).
  4. Let (fun-hash,arg-t,body-t,closed-env)=ALLOCATE-MAYBE-FUN-UNCONSTRAINED(result)(\var{fun-hash}, \var{arg-t}, \var{body-t}, \var{closed-env}) = \gad{ALLOCATE-MAYBE-FUN-UNCONSTRAINED}(\var{result}).
  5. Call IMPLIES-EQUAL(call0-not-dummy,fun-hash,result.hash())\gad{IMPLIES-EQUAL}(\var{call0-not-dummy}, \var{fun-hash}, \var{result.hash}()).
  6. Let args-is-dummy=alloc-equal(arg-t,gobals.dummy-arg-ptr)\var{args-is-dummy} = \gad{alloc-equal}(\var{arg-t}, \var{gobals.dummy-arg-ptr}).
  7. Let body-t-is-cons=ALLOC-EQUAL(body-t.tag(),globals.cons-tag)\var{body-t-is-cons} = \gad{ALLOC-EQUAL}(\var{body-t.tag}(), \var{globals.cons-tag}).
  8. Let body-t-is-nil=body-t.IS-NIL()\var{body-t-is-nil} = \var{body-t}.\gad{IS-NIL}().
  9. Let body-t-is-list=OR(body-t-is-cons,body-t-is-list)\var{body-t-is-list} = \gad{OR}(\var{body-t-is-cons}, \var{body-t-is-list}).
  10. Let zero-arg-call-not-dummy=AND(call0-not-dummy,body-t-is-list,args-is-dummy)\var{zero-arg-call-not-dummy} = \gad{AND}(\var{call0-not-dummy}, \var{body-t-is-list}, \var{args-is-dummy}).
  11. Let (body-form,end)=CAR-CDR-NAMED(body-t,cons-names.fun-body,allocated-cons-witness,zero-arg-call-not-dummy)(\var{body-form}, \var{end}) = \gad{CAR-CDR-NAMED}(\var{body-t}, \var{cons-names.fun-body}, \var{allocated-cons-witness}, \var{zero-arg-call-not-dummy}).
  12. Let end-is-nil=end.IS-NIL()\var{end-is-nil} = \var{end}.\gad{IS-NIL}().
  13. Let continuation-is-tail=ALLOC-TAG-EQUAL(continuation.tag(),globals.tail-cont-tag)\var{continuation-is-tail} = \gad{ALLOC-TAG-EQUAL}(\var{continuation.tag}(), \var{globals.tail-cont-tag}).
  14. Let tail-cont=PICK(continuation-is-tail,continuation,newer-cont2)\var{tail-cont} = \gad{PICK}(\var{continuation-is-tail}, \var{continuation}, \var{newer-cont2}).
  15. Let next-expr=PICK(args-is-dummy,body-form,result)\var{next-expr} = \gad{PICK}(\var{args-is-dummy}, \var{body-form}, \var{result}).
  16. Let next-env=PICK(args-is-dummy,closed-env,env)\var{next-env} = \gad{PICK}(\var{args-is-dummy}, \var{closed-env}, \var{env}).
  17. Let next-cont=PICK(args-is-dummy,tail-cont,continuation)\var{next-cont} = \gad{PICK}(\var{args-is-dummy}, \var{tail-cont}, \var{continuation}).
  18. Let body-form-is-nil=body-form.IS-NIL()\var{body-form-is-nil} = \var{body-form}.\gad{IS-NIL}().
  19. Let body-is-well-formed=AND(body-form-is-nil.NOT(),end-is-nil)\var{body-is-well-formed} = \gad{AND}(\var{body-form-is-nil}.\gad{NOT}(), \var{end-is-nil}).
  20. Let result-is-valid-fun=AND(result-is-fun,body-is-well-formed)\var{result-is-valid-fun} = \gad{AND}(\var{result-is-fun}, \var{body-is-well-formed}).
  21. Let result-is-valid-zero-arg-fun=AND(result-is-valid-fun,arg-is-dummy)\var{result-is-valid-zero-arg-fun} = \gad{AND}(\var{result-is-valid-fun}, \var{arg-is-dummy}).
  22. Let the-expr=PICK(result-is-valid-zero-arg-fun,next-expr,result)\var{the-expr} = \gad{PICK}(\var{result-is-valid-zero-arg-fun}, \var{next-expr}, \var{result}).
  23. Let the-env=PICK(result-is-fun,next-env,env)\var{the-env} = \gad{PICK}(\var{result-is-fun}, \var{next-env}, \var{env}).
  24. Let result-not-fun-and-zero-arg-call-is-dummy=AND(zero-arg-call-not-dummy.NOT(),result-is-fun)\var{result-not-fun-and-zero-arg-call-is-dummy} = \gad{AND}(\var{zero-arg-call-not-dummy}.\gad{NOT}(), \var{result-is-fun}).
  25. Let the-cont-not-error=OR(result-is-valid-fun,result-not-fun-and-zero-arg-call-is-dummy)\var{the-cont-not-error} = \gad{OR}(\var{result-is-valid-fun}, \var{result-not-fun-and-zero-arg-call-is-dummy}).
  26. Let the-cont=PICK(the-cont-not-error,next-cont,globals.error-ptr-cont)\var{the-cont} = \gad{PICK}(\var{the-cont-not-error}, \var{next-cont}, \var{globals.error-ptr-cont}).
  27. Let newer-cont2-not-dummy0=AND(continuation-is-tail.NOT(),args-is-dummy,result-is-fun)\var{newer-cont2-not-dummy0} = \gad{AND}(\var{continuation-is-tail}.\gad{NOT}(), \var{args-is-dummy}, \var{result-is-fun}).
  28. Let newer-cont2-not-dummy=BOOLEAN-NUM(newer-cont2-not-dummy0)\var{newer-cont2-not-dummy} = \gad{BOOLEAN-NUM}(\var{newer-cont2-not-dummy0}).
  29. Call results.ADD-CLAUSE(cont-tag.call0,the-expr,the-env,the-cont,globals.false-num,newer-cont2-not-dummy)\var{results}.\gad{ADD-CLAUSE}(\var{cont-tag.call0}, \var{the-expr}, \var{the-env}, \var{the-cont}, \var{globals.false-num}, \var{newer-cont2-not-dummy}).

Case cont is call\rm{\small{\bold{Case}~ cont~\bold{is}~call}}.

  1. Let next-expr=continuation-components[1]\var{next-expr} = \var{continuation-components}[1].
  2. Let next-expr=PICK(result-is-fun,next-expr,result)\var{next-expr} = \gad{PICK}(\var{result-is-fun}, \var{next-expr}, \var{result}).
  3. Let the-cont=PICK(result-is-fun,newer-cont2,globals.error-ptr-cont)\var{the-cont} = \gad{PICK}(\var{result-is-fun}, \var{newer-cont2}, \var{globals.error-ptr-cont}).
  4. Call results.ADD-CLAUSE(cont-tag.call,next-expr,env,the-cont,globals.false-num,newer-cont2-not-dummy)\var{results}.\gad{ADD-CLAUSE}(\var{cont-tag.call}, \var{next-expr}, \var{env}, \var{the-cont}, \var{globals.false-num}, \var{newer-cont2-not-dummy}).

Case cont is call2\rm{\small{\bold{Case}~ cont~\bold{is}~call2}}.

  1. Let fun=cont-components[1]\var{fun} = \var{cont-components}[1].
  2. Let continuation=cont-components[2]\var{continuation} = \var{cont-components}[2].
  3. Let cont-is-call2-precomp=ALLOC-TAG-EQUAL(cont.tag(),globals.call2-cont-tag)\var{cont-is-call2-precomp} = \gad{ALLOC-TAG-EQUAL}(\var{cont.tag}(), \var{globals.call2-cont-tag}).
  4. Let cont-is-call2-and-not-dummy=AND(cont-is-call2-precomp,not-dummy)\var{cont-is-call2-and-not-dummy} = \gad{AND}(\var{cont-is-call2-precomp}, \var{not-dummy}).
  5. Let (hash,arg-t,body-t,closed-env)=ALLOCATE-MAYBE-FUN-UNCONSTRAINED(fun)(\var{hash}, \var{arg-t}, \var{body-t}, \var{closed-env}) = \gad{ALLOCATE-MAYBE-FUN-UNCONSTRAINED}(\var{fun}).
  6. Call IMPLIES-EQUAL(cont-is-call2-and-not-dummy,fun.hash(),hash)\gad{IMPLIES-EQUAL}(\var{cont-is-call2-and-not-dummy}, \var{fun.hash}(), \var{hash}).
  7. Let args-is-dummy=ALLOC-EQUAL(arg-t,globals.dummy-arg-ptr)\var{args-is-dummy} = \gad{ALLOC-EQUAL}(\var{arg-t}, \var{globals.dummy-arg-ptr}).
  8. Let args-is-not-dummy=args-is-dummy.NOT()\var{args-is-not-dummy} = \var{args-is-dummy}.\gad{NOT}().
  9. Let cont-is-call2-and-not-dummy-and-not-dummy-args=AND(cont-is-call2-and-not-dummy,args-is-not-dummy)\var{cont-is-call2-and-not-dummy-and-not-dummy-args} = \gad{AND}(\var{cont-is-call2-and-not-dummy}, \var{args-is-not-dummy}).
  10. Let (body-form,end)=CAR-CDR-NAMED(body-t,cons-names.fun-body,allocated-cons-witness,cont-is-call2-and-not-dummy-and-not-dummy-args)(\var{body-form}, \var{end}) = \gad{CAR-CDR-NAMED}(\var{body-t}, \var{cons-names.fun-body}, \var{allocated-cons-witness}, \var{cont-is-call2-and-not-dummy-and-not-dummy-args}).
  11. Let body-form-is-nil=body-form.IS-NIL()\var{body-form-is-nil} = \var{body-form}.\gad{IS-NIL}().
  12. Let end-is-nil=end.IS-NIL()\var{end-is-nil} = \var{end}.\gad{IS-NIL}().
  13. Let body-is-well-formed=AND(body-form-is-nil.NOT(),end-is-nil)\var{body-is-well-formed} = \gad{AND}(\var{body-form-is-nil}.\gad{NOT}(), \var{end-is-nil}).
  14. Let extend-not-dummy=AND(cont-is-call2-and-not-dummy,args-is-dummy.NOT(),body-is-well-formed)\var{extend-not-dummy} = \gad{AND}(\var{cont-is-call2-and-not-dummy}, \var{args-is-dummy}.\gad{NOT}(), \var{body-is-well-formed}).
  15. Let newer-env=Extend-named(closed-env,arg-t,result,cons-names.closed-env,allocated-cons-witness,extend-not-dummy)\var{newer-env} = \gad{Extend-named}(\var{closed-env}, \var{arg-t}, \var{result}, \var{cons-names.closed-env}, \var{allocated-cons-witness}, \var{extend-not-dummy}).
  16. Let continuation-is-tail=ALLOC-TAG-EQUAL(continuation.tag(),globals.tail-cont-tag)\var{continuation-is-tail} = \gad{ALLOC-TAG-EQUAL}(\var{continuation.tag}(), \var{globals.tail-cont-tag}).
  17. Let tail-cont=PICK(continuation-is-tail,continuation,newer-cont2)\var{tail-cont} = \gad{PICK}(\var{continuation-is-tail}, \var{continuation}, \var{newer-cont2}).
  18. Let cond0=OR(args-is-dummy.not(),result-is-fun)\var{cond0} = \gad{OR}(\var{args-is-dummy}.\gad{not}(), \var{result-is-fun}).
  19. Let cond=AND(cond0,body-is-well-formed)\var{cond} = \gad{AND}(\var{cond0}, \var{body-is-well-formed}).
  20. Let the-cont=PICK(cond,tail-cont,globals.error-ptr-cont)\var{the-cont} = \gad{PICK}(\var{cond}, \var{tail-cont}, \var{globals.error-ptr-cont}).
  21. Let the-env=PICK(cond,newer-env,env)\var{the-env} = \gad{PICK}(\var{cond}, \var{newer-env}, \var{env}).
  22. Let the-expr=PICK(cond,body-form,result)\var{the-expr} = \gad{PICK}(\var{cond}, \var{body-form}, \var{result}).
  23. Let newer-cont2-not-dummy0=AND(continuation-is-tail.NOT(),cond)\var{newer-cont2-not-dummy0} = \gad{AND}(\var{continuation-is-tail}.\gad{NOT}(), \var{cond}).
  24. Let newer-cont2-not-dummy=BOOLEAN-NUM(newer-cont2-not-dummy0)\var{newer-cont2-not-dummy} = \gad{BOOLEAN-NUM}(\var{newer-cont2-not-dummy0}).

Case cont is binop\rm{\small{\bold{Case}~ cont~\bold{is}~binop}}.

  1. Let operator=continuation-components[0]\var{operator} = \var{continuation-components}[0].
  2. Let saved-env=continuation-components[1]\var{saved-env} = \var{continuation-components}[1].
  3. Let unevaled-args=continuation-components[2]\var{unevaled-args} = \var{continuation-components}[2].
  4. Let cont-is-binop=ALLOC-TAG-EQUAL(cont.tag(),globals.binop-cont-tag)\var{cont-is-binop} = \gad{ALLOC-TAG-EQUAL}(\var{cont.tag}(), \var{globals.binop-cont-tag}).
  5. Let binop-not-dummy=AND(cont-is-binop,not-dummy)\var{binop-not-dummy} = \gad{AND}(\var{cont-is-binop}, \var{not-dummy}).
  6. Let (allocated-arg2,allocated-rest)=CAR-CDR-NAMED(unevaled-args,cons-names.unevaled-args,allocated-cons-witness,binop-not-dummy)(\var{allocated-arg2}, \var{allocated-rest}) = \gad{CAR-CDR-NAMED}(\var{unevaled-args}, \var{cons-names.unevaled-args}, \var{allocated-cons-witness}, \var{binop-not-dummy}).
  7. Let op-is-begin=ALLOC-TAG-EQUAL(operator.tag(),globals.op2-begin-tag)\var{op-is-begin} = \gad{ALLOC-TAG-EQUAL}(\var{operator.tag}(), \var{globals.op2-begin-tag}).
  8. Let rest-is-nil=ALLOC-EQUAL(allocated-rest,globals.nil-ptr)\var{rest-is-nil} = \gad{ALLOC-EQUAL}(\var{allocated-rest}, \var{globals.nil-ptr}).
  9. Let rest-not-nil=rest-is-nil.NOT()\var{rest-not-nil} = \var{rest-is-nil}.\gad{NOT}().
  10. Let begin=GET-BEGIN()\var{begin} = \gad{GET-BEGIN}().
  11. Let allocated-begin=ALLOC-PTR(begin)\var{allocated-begin} = \gad{ALLOC-PTR}(\var{begin}).
  12. Let begin-not-dummy=AND(op-is-begin,binop-not-dummy,rest-not-nil)\var{begin-not-dummy} = \gad{AND}(\var{op-is-begin}, \var{binop-not-dummy}, \var{rest-not-nil}).
  13. Let begin-again=CONSTRUCT-CONS-NAMED(allocated-begin,unevaled-args,cons-name.begin,allocated-cons-witness,begin-not-dummy)\var{begin-again} = \gad{CONSTRUCT-CONS-NAMED}(\var{allocated-begin}, \var{unevaled-args}, \var{cons-name.begin}, \var{allocated-cons-witness}, \var{begin-not-dummy}).
  14. Let the-expr-if-begin=PICK(op-is-begin,begin-again,result)\var{the-expr-if-begin} = \gad{PICK}(\var{op-is-begin}, \var{begin-again}, \var{result}).
  15. Let otherwise=op-is-begin.NOT()\var{otherwise} = \var{op-is-begin}.\gad{NOT}().
  16. Let otherwise-and-rest-is-nil=AND(otherwise,rest-is-nil)\var{otherwise-and-rest-is-nil} = \gad{AND}(\var{otherwise}, \var{rest-is-nil}).
  17. Let the-expr=PICK(rest-is-nil,allocated-arg2,the-expr-if-begin)\var{the-expr} = \gad{PICK}(\var{rest-is-nil}, \var{allocated-arg2}, \var{the-expr-if-begin}).
  18. Let the-env=PICK(rest-is-nil,saved-env,env)\var{the-env} = \gad{PICK}(\var{rest-is-nil}, \var{saved-env}, \var{env}).
  19. Let the-cont-otherwise=PICK(otherwise-and-rest-is-nil,newer-cont2,globals.error-ptr-cont)\var{the-cont-otherwise} = \gad{PICK}(\var{otherwise-and-rest-is-nil}, \var{newer-cont2}, \var{globals.error-ptr-cont}).
  20. Let the-cont=PICK(otherwise,the-cont-otherwise,continuation)\var{the-cont} = \gad{PICK}(\var{otherwise}, \var{the-cont-otherwise}, \var{continuation}).
  21. Let newer-cont2-not-dummy=BOOLEAN-NUM(otherwise-and-rest-is-nil)\var{newer-cont2-not-dummy} = \gad{BOOLEAN-NUM}(\var{otherwise-and-rest-is-nil}).

Case cont is binop2\rm{\small{\bold{Case}~ cont~\bold{is}~binop2}}.

  1. Let op2=cont-components[0]\var{op2} = \var{cont-components}[0].
  2. Let arg1=cont-components[1]\var{arg1} = \var{cont-components}[1].
  3. Let continuation=cont-components[2]\var{continuation} = \var{cont-components}[2].
  4. Let arg2=result\var{arg2} = \var{result}.
  5. Let arg1-is-num=IS-NUM(arg1)\var{arg1-is-num} = \gad{IS-NUM}(\var{arg1}).
  6. Let arg2-is-num=IS-NUM(arg2)\var{arg2-is-num} = \gad{IS-NUM}(\var{arg2}).
  7. Let both-args-are-num=AND(arg1-is-num,arg2-is-num)\var{both-args-are-num} = \gad{AND}(\var{arg1-is-num}, \var{arg2-is-num}).
  8. Let arg1-is-u64=IS-U64(arg1)\var{arg1-is-u64} = \gad{IS-U64}(\var{arg1}).
  9. Let arg2-is-u64=IS-U64(arg2)\var{arg2-is-u64} = \gad{IS-U64}(\var{arg2}).
  10. Let both-args-are-u64s=and(arg1-is-u64,arg2-is-u64)\var{both-args-are-u64s} = \gad{and}(\var{arg1-is-u64}, \var{arg2-is-u64}).
  11. Let arg1-is-num-and-arg2-is-u64=AND(arg1-is-num,arg2-is-u64)\var{arg1-is-num-and-arg2-is-u64} = \gad{AND}(\var{arg1-is-num}, \var{arg2-is-u64}).
  12. Let arg1-is-u64-and-arg2-is-num=AND(arg1-is-u64,arg2-is-num)\var{arg1-is-u64-and-arg2-is-num} = \gad{AND}(\var{arg1-is-u64}, \var{arg2-is-num}).
  13. Let args-are-num-or-u64=OR(both-args-are-nums,both-args-are-u64s,arg1-is-num-and-arg2-is-u64,arg1-is-u64-and-arg2-is-num)\var{args-are-num-or-u64} = \gad{OR}(\var{both-args-are-nums}, \var{both-args-are-u64s}, \var{arg1-is-num-and-arg2-is-u64}, \var{arg1-is-u64-and-arg2-is-num}).
  14. Let arg1-u64-to-num=TO-NUM(arg1)\var{arg1-u64-to-num} = \gad{TO-NUM}(\var{arg1}).
  15. Let arg1-final=PICK(arg1-is-u64-and-arg2-is-num,arg1-u64-to-num,arg1)\var{arg1-final} = \gad{PICK}(\var{arg1-is-u64-and-arg2-is-num}, \var{arg1-u64-to-num}, \var{arg1}).
  16. Let arg2-u64-to-num=TO-NUM(arg2)\var{arg2-u64-to-num} = \gad{TO-NUM}(\var{arg2}).
  17. Let arg2-final=PICK(arg1-is-num-and-arg2-is-u64,arg2-u64-to-num,arg2)\var{arg2-final} = \gad{PICK}(\var{arg1-is-num-and-arg2-is-u64}, \var{arg2-u64-to-num}, \var{arg2}).
  18. Let a=arg1.hash()a = \var{arg1.hash}().
  19. Let b=arg2.hash()b = \var{arg2.hash}().
  20. Let args-equal=AND(arg1-final,arg2-final)\var{args-equal} = \gad{AND}(\var{arg1-final}, \var{arg2-final}).
  21. Let args-equal-ptr=PICK(args-equal,globals.t-ptr,globals.nil-ptr)\var{args-equal-ptr} = \gad{PICK}(\var{args-equal}, \var{globals.t-ptr}, \var{globals.nil-ptr}).
  22. Let not-dummy=ALLOC-TAG-EQUAL(cont.tag(),globals.binop2-cont-tag)\var{not-dummy} = \gad{ALLOC-TAG-EQUAL}(\var{cont.tag}(), \var{globals.binop2-cont-tag}).
  23. Let sum=ADD(a,b)\var{sum} = \gad{ADD}(a, b).
  24. Let diff=SUB(a,b)\var{diff} = \gad{SUB}(a, b).
  25. Let product=MUL(a,b)\var{product} = \gad{MUL}(a, b).
  26. Let op2-is-div=ALLOC-TAG-EQUAL(op2.tag(),globals.op2-quotient-tag)\var{op2-is-div} = \gad{ALLOC-TAG-EQUAL}(\var{op2.tag}(), \var{globals.op2-quotient-tag}).
  27. Let op2-is-mod=ALLOC-TAG-EQUAL(op2.tag(),globals.op2-modulo-tag)\var{op2-is-mod} = \gad{ALLOC-TAG-EQUAL}(\var{op2.tag}(), \var{globals.op2-modulo-tag}).
  28. Let op2-is-div-or-mod=OR(op2-is-div,op2-is-mod)\var{op2-is-div-or-mod} = \gad{OR}(\var{op2-is-div}, \var{op2-is-mod}).
  29. Let b-is-zero=ALLOC-IS-ZERO(b)\var{b-is-zero} = \gad{ALLOC-IS-ZERO}(b).
  30. Let divisor=PICK(b-is-zero,1,b)\var{divisor} = \gad{PICK}(\var{b-is-zero}, 1, b) - if b is zero, take a dummy divisor.
  31. Let quotient=DIV(a,b)\var{quotient} = \gad{DIV}(a, b).
  32. Let is-cons=ALLOC-TAG-EQUAL(op2.tag(),globals.op2-cons-tag)\var{is-cons} = \gad{ALLOC-TAG-EQUAL}(\var{op2.tag}(), \var{globals.op2-cons-tag}).
  33. Let is-strcons=ALLOC-TAG-EQUAL(op2.tag(),globals.op2-strcons-tag)\var{is-strcons} = \gad{ALLOC-TAG-EQUAL}(\var{op2.tag}(), \var{globals.op2-strcons-tag}).
  34. Let is-cons-or-strcons=OR(is-cons,is-strcons)\var{is-cons-or-strcons} = \gad{OR}(\var{is-cons}, \var{is-strcons}).
  35. Let arg1-is-char=ALLOC-TAG-EQUAL(arg1.tag(),globals.char-tag)\var{arg1-is-char} = \gad{ALLOC-TAG-EQUAL}(\var{arg1.tag}(), \var{globals.char-tag}).
  36. Let arg2-is-str=ALLOC-TAG-EQUAL(arg2.tag(),globals.str-tag)\var{arg2-is-str} = \gad{ALLOC-TAG-EQUAL}(\var{arg2.tag}(), \var{globals.str-tag}).
  37. Let args-are-char-str=AND(arg1-is-char,arg2-is-str)\var{args-are-char-str} = \gad{AND}(\var{arg1-is-char}, \var{arg2-is-str}).
  38. Let args-not-char-str=args-are-char-str.NOT()\var{args-not-char-str} = \var{args-are-char-str}.\gad{NOT}().
  39. Let invalid-strcons-tag=AND(args-not-char-str,is-strcons)\var{invalid-strcons-tag} = \gad{AND}(\var{args-not-char-str}, \var{is-strcons}).
  40. Let cons-not-dummy=AND(is-cons-or-strcons,not-dummy,invalid-strcons-tag.NOT())\var{cons-not-dummy} = \gad{AND}(\var{is-cons-or-strcons}, \var{not-dummy}, \var{invalid-strcons-tag}.\gad{NOT}()).
  41. Let cons=CONSTRUCT-CONS-NAMED(arg1,arg2,cons-names.the-cons,allocated-cons-witness,cons-not-dummy)\var{cons} = \gad{CONSTRUCT-CONS-NAMED}(\var{arg1}, \var{arg2}, \var{cons-names.the-cons}, \var{allocated-cons-witness}, \var{cons-not-dummy}).
  42. Let op2-clauses=CASE-CLAUSES()\var{op2-clauses} = \gad{CASE-CLAUSES}().
  43. Call op2-clauses.ADD-CLAUSE(globals.sum-tag,sum)\var{op2-clauses}.\gad{ADD-CLAUSE}(\var{globals.sum-tag}, \var{sum}).
  44. Call op2-clauses.ADD-CLAUSE(globals.diff-tag,diff)\var{op2-clauses}.\gad{ADD-CLAUSE}(\var{globals.diff-tag}, \var{diff}).
  45. Call op2-clauses.ADD-CLAUSE(globals.product-tag,product)\var{op2-clauses}.\gad{ADD-CLAUSE}(\var{globals.product-tag}, \var{product}).
  46. Call op2-clauses.ADD-CLAUSE(quotient-tag,quotient)\var{op2-clauses}.\gad{ADD-CLAUSE}(\var{quotient-tag}, \var{quotient}).
  47. Call op2-clauses.ADD-CLAUSE(globals.equal-tag,args-equal-ptr.hash())\var{op2-clauses}.\gad{ADD-CLAUSE}(\var{globals.equal-tag}, \var{args-equal-ptr.hash}()).
  48. Call op2-clauses.ADD-CLAUSE(globals.numequal-tag,args-equal-ptr.hash())\var{op2-clauses}.\gad{ADD-CLAUSE}(\var{globals.numequal-tag}, \var{args-equal-ptr.hash}()).
  49. Call op2-clauses.ADD-CLAUSE(globals.cons-tag,cons.hash())\var{op2-clauses}.\gad{ADD-CLAUSE}(\var{globals.cons-tag}, \var{cons.hash}()).
  50. Call op2-clauses.ADD-CLAUSE(globals.strcons-tag,cons.hash())\var{op2-clauses}.\gad{ADD-CLAUSE}(\var{globals.strcons-tag}, \var{cons.hash}()).
  51. Call op2-clauses.ADD-CLAUSE(hide-tag,commitment.hash())\var{op2-clauses}.\gad{ADD-CLAUSE}(\var{hide-tag}, \var{commitment.hash}()).
  52. Let default=globals.default-num\var{default} = \var{globals.default-num}.
  53. Let val=CASE(op2.tag(),op2-clauses,default)\var{val} = \gad{CASE}(\var{op2.tag}(), \var{op2-clauses}, \var{default}).
  54. Let is-equal=ALLOC-TAG-EQUAL(op2.tag(),globals.op2-equal-tag)\var{is-equal} = \gad{ALLOC-TAG-EQUAL}(\var{op2.tag}(), \var{globals.op2-equal-tag}).
  55. Let is-num-equal=ALLOC-TAG-EQUAL(op2.tag(),globals.op2-num-equal-tag)\var{is-num-equal} = \gad{ALLOC-TAG-EQUAL}(\var{op2.tag}(), \var{globals.op2-num-equal-tag}).
  56. Let is-equal–or-num-equal=OR(is-equal,is-num-equal)\var{is-equal–or-num-equal} = \gad{OR}(\var{is-equal}, \var{is-num-equal}).
  57. Let op2-is-hide=ALLOC-TAG-EQUAL(op2.tag(),globals.op2-hide-tag)\var{op2-is-hide} = \gad{ALLOC-TAG-EQUAL}(\var{op2.tag}(), \var{globals.op2-hide-tag}).
  58. Let commitment-tag-is-comm=IS-COMM(commitment)\var{commitment-tag-is-comm} = \gad{IS-COMM}(\var{commitment}).
  59. Let commitment-tag-is-dummy=ALLOC-IS-ZERO(commitment.tag())\var{commitment-tag-is-dummy} = \gad{ALLOC-IS-ZERO}(\var{commitment.tag}()).
  60. Let commitment-tag-is-correct=OR(commitment-tag-is-comm,commitment-tag-is-dummy)\var{commitment-tag-is-correct} = \gad{OR}(\var{commitment-tag-is-comm}, \var{commitment-tag-is-dummy}).
  61. call ENFORCE-IMPLICATION(op2-is-hide,commitment-tag-is-correct)\gad{ENFORCE-IMPLICATION}(\var{op2-is-hide}, \var{commitment-tag-is-correct}).
  62. Let cons-tag=PICK(is-strcons,globals.str-tag,globals.cons-tag)\var{cons-tag} = \gad{PICK}(\var{is-strcons}, \var{globals.str-tag}, \var{globals.cons-tag}).
  63. Let comm-or-num-tag=PICK(op2-is-hide,globals.comm-tag,globals.num-tag)\var{comm-or-num-tag} = \gad{PICK}(\var{op2-is-hide}, \var{globals.comm-tag}, \var{globals.num-tag}).
  64. Let is-cons-or-hide=OR(is-cons,op2-is-hide)\var{is-cons-or-hide} = \gad{OR}(\var{is-cons}, \var{op2-is-hide}).
  65. Let is-cons-or-strcons-or-hide-or-equal=OR(is-cons-or-hide,is-strcons,is-equal)\var{is-cons-or-strcons-or-hide-or-equal} = \gad{OR}(\var{is-cons-or-hide}, \var{is-strcons}, \var{is-equal}).
  66. Let is-cons-or-strcons-or-hide-or-equal-or-num-equal=OR(is-cons-or-strcons-or-hide-or-equal,is-num-equal)\var{is-cons-or-strcons-or-hide-or-equal-or-num-equal} = \gad{OR}(\var{is-cons-or-strcons-or-hide-or-equal}, \var{is-num-equal}).
  67. Let res-tag0=PICK(is-cons-or-strcons,cons-tag,comm-or-num-tag)\var{res-tag0} = \gad{PICK}(\var{is-cons-or-strcons}, \var{cons-tag}, \var{comm-or-num-tag}).
  68. Let res-tag=PICK(is-equal-or-num-equal,args-equal-ptr.tag(),res-tag0)\var{res-tag} = \gad{PICK}(\var{is-equal-or-num-equal}, \var{args-equal-ptr.tag}(), \var{res-tag0}).
  69. Let res=ALLOC-FROM-PARTS(res-tag,val)\var{res} = \gad{ALLOC-FROM-PARTS}(\var{res-tag}, \var{val}).
  70. Let (is-comparison-tag,comp-val,diff-is-negative)=COMPARISON-HELPER(a,b,diff,p2.tag())(\var{is-comparison-tag}, \var{comp-val}, \var{diff-is-negative}) = \cir{COMPARISON-HELPER}(a, b, \var{diff}, \var{p2.tag}()).
  71. Let field-arithmetic-result=PICK(is-comparison-tag,comp-val,res)\var{field-arithmetic-result} = \gad{PICK}(\var{is-comparison-tag}, \var{comp-val}, \var{res}).
  72. Let field-arithmetic-result-plus-2p64=ADD(field-arithmetic-result.hash(),globals.power2-64-num)\var{field-arithmetic-result-plus-2p64} = \gad{ADD}(\var{field-arithmetic-result.hash}(), \var{globals.power2-64-num}).
  73. Let op2-is-diff=ALLOC-TAG-EQUAL(op2.tag(),globals.op2-diff-tag)\var{op2-is-diff} = \gad{ALLOC-TAG-EQUAL}(\var{op2.tag}(), \var{globals.op2-diff-tag}).
  74. Let diff-is-negative-and-op2-is-diff=AND(diff-is-negative,op2-is-diff)\var{diff-is-negative-and-op2-is-diff} = \gad{AND}(\var{diff-is-negative}, \var{op2-is-diff}).
  75. Let field-arith-and-u64-diff-result=PICK(diff-is-negative-and-op2-is-diff,field-arithmetic-result-plus-2p64,field-arithmetic-result.hash())\var{field-arith-and-u64-diff-result} = \gad{PICK}(\var{diff-is-negative-and-op2-is-diff}, \var{field-arithmetic-result-plus-2p64}, \var{field-arithmetic-result.hash}()).
  76. Let coerce-to-u64=TO-U64(field-arith-and-u64-diff-result)\var{coerce-to-u64} = \gad{TO-U64}(\var{field-arith-and-u64-diff-result}).
  77. Let coerce-to-u64-ptr=from-parts(globals.u64-tag,coerce-to-u64)\var{coerce-to-u64-ptr} = \gad{from-parts}(\var{globals.u64-tag}, \var{coerce-to-u64}).
  78. Let both-args-are-u64s-and-not-comparison=AND(both-args-are-u64s,is-comparison-tag.NOT())\var{both-args-are-u64s-and-not-comparison} = \gad{AND}(\var{both-args-are-u64s}, \var{is-comparison-tag}.\gad{NOT}()).
  79. Let partial-u64-result=PICK(both-args-are-u64s-and-not-comparison,coerce-to-u64-ptr,field-arithmetic-result)\var{partial-u64-result} = \gad{PICK}(\var{both-args-are-u64s-and-not-comparison}, \var{coerce-to-u64-ptr}, \var{field-arithmetic-result}).
  80. Let (alloc-q,alloc-r)=ENFORCE-U64-DIV-MOD(op2-is-mod,arg1,arg2)(\var{alloc-q}, \var{alloc-r}) = \gad{ENFORCE-U64-DIV-MOD}(\var{op2-is-mod}, \var{arg1}, \var{arg2}).
  81. Let alloc-q-ptr=from-parts(globals.u64-tag,alloc-q)\var{alloc-q-ptr} = \gad{from-parts}(\var{globals.u64-tag}, \var{alloc-q}).
  82. Let alloc-r-ptr=from-parts(globals.u64-tag,alloc-r)\var{alloc-r-ptr} = \gad{from-parts}(\var{globals.u64-tag}, \var{alloc-r}).
  83. Let op2-is-div-and-args-are-u64s=AND(op2-is-div,both-args-are-u64s)\var{op2-is-div-and-args-are-u64s} = \gad{AND}(\var{op2-is-div}, \var{both-args-are-u64s}).
  84. Let include-u64-quotient=PICK(op2-is-div-and-args-are-u64s,alloc-q-ptr,partial-u64-result)\var{include-u64-quotient} = \gad{PICK}(\var{op2-is-div-and-args-are-u64s}, \var{alloc-q-ptr}, \var{partial-u64-result}).
  85. Let op2-is-mod-and-args-are-u64s=AND(op2-is-mod,both-args-are-u64s)\var{op2-is-mod-and-args-are-u64s} = \gad{AND}(\var{op2-is-mod}, \var{both-args-are-u64s}).
  86. Let op2-is-mod-and-args-are-not-u64s=AND(op2-is-mod,both-args-are-u64s.NOT())\var{op2-is-mod-and-args-are-not-u64s} = \gad{AND}(\var{op2-is-mod}, \var{both-args-are-u64s}.\gad{NOT}()).
  87. Let arithmetic-result=PICK(op2-is-mod-and-args-are-u64s,alloc-r-ptr,include-u64-quotient)\var{arithmetic-result} = \gad{PICK}(\var{op2-is-mod-and-args-are-u64s}, \var{alloc-r-ptr}, \var{include-u64-quotient}).
  88. Let valid-types=OR(is-cons-or-strcons-or-hide-or-equal,args-are-num-or-u64)\var{valid-types} = \gad{OR}(\var{is-cons-or-strcons-or-hide-or-equal}, \var{args-are-num-or-u64}).
  89. Let real-div-or-more-and-b-is-zero=AND(not-dummy,op2-is-div-or-mod,b-is-zero)\var{real-div-or-more-and-b-is-zero} = \gad{AND}(\var{not-dummy}, \var{op2-is-div-or-mod}, \var{b-is-zero}).
  90. Let valid-types-and-not-div-by-zero=AND(valid-types,real-div-or-more-and-b-is-zero.NOT())\var{valid-types-and-not-div-by-zero} = \gad{AND}(\var{valid-types}, \var{real-div-or-more-and-b-is-zero}.\gad{NOT}()).
  91. Let op2-not-num-or-u64-and-not-cons-or-strcons-or-hide-or-equal-or-num-equal=AND(args-are-num-or-u64.NOT(),is-cons-or-strcons-or-hide-or-equal-or-num-equal.NOT())\var{op2-not-num-or-u64-and-not-cons-or-strcons-or-hide-or-equal-or-num-equal} = \gad{AND}(\var{args-are-num-or-u64}.\gad{NOT}(), \var{is-cons-or-strcons-or-hide-or-equal-or-num-equal}.\gad{NOT}()).
  92. Let invalid-secret-tag-hide=AND(arg1-is-u64,op2-is-hide)\var{invalid-secret-tag-hide} = \gad{AND}(\var{arg1-is-u64}, \var{op2-is-hide}).
  93. Let op2-is-hide-and-arg1-is-not-num=AND(op2-is-hide,arg1-is-num.NOT())\var{op2-is-hide-and-arg1-is-not-num} = \gad{AND}(\var{op2-is-hide}, \var{arg1-is-num}.\gad{NOT}()).
  94. Let any-error=OR(valid-types-and-not-div-by-zero.NOT(),op2-not-num-or-u64-and-not-cons-or-strcons-or-hide-or-equal-or-num-equal,invalid-strcons-tag,op2-is-hide-and-arg1-is-not-num,op2-is-mod-and-args-are-not-u64s,invalid-secret-tag-hide)\var{any-error} = \gad{OR}(\var{valid-types-and-not-div-by-zero}.\gad{NOT}(), \var{op2-not-num-or-u64-and-not-cons-or-strcons-or-hide-or-equal-or-num-equal}, \var{invalid-strcons-tag}, \\ \var{op2-is-hide-and-arg1-is-not-num}, \var{op2-is-mod-and-args-are-not-u64s}, \var{invalid-secret-tag-hide}).
  95. Let op2-is-eval=ALLOC-TAG-EQUAL(op2.tag(),globals.op2-eval-tag)\var{op2-is-eval} = \gad{ALLOC-TAG-EQUAL}(\var{op2.tag}(), \var{globals.op2-eval-tag}).
  96. Let the-cont0=PICK(any-error,globals.error-ptr-cont,continuation)\var{the-cont0} = \gad{PICK}(\var{any-error}, \var{globals.error-ptr-cont}, \var{continuation}).
  97. Let the-cont=PICK(op2-is-eval,continuation,the-cont0)\var{the-cont} = \gad{PICK}(\var{op2-is-eval}, \var{continuation}, \var{the-cont0}).
  98. Let the-expr0=pick(any-error,result,arithmetic-result)\var{the-expr0} = \gad{pick}(\var{any-error}, \var{result}, \var{arithmetic-result}).
  99. Let the-expr=PICK(op2-is-eval,arg1,the-expr0)\var{the-expr} = \gad{PICK}(\var{op2-is-eval}, \var{arg1}, \var{the-expr0}).
  100. Let the-env=PICK(op2-is-eval,arg2,env)\var{the-env} = \gad{PICK}(\var{op2-is-eval}, \var{arg2}, \var{env}).
  101. Let make-thunk-num=BOOLEAN-TO-NUM(op2-is-eval.NOT())\var{make-thunk-num} = \gad{BOOLEAN-TO-NUM}(\var{op2-is-eval}.\gad{NOT}()).
  102. Call results.ADD-CLAUSE(cont-tag.binop2,the-expr,the-env,the-cont,make-thunk-num,globals.false-num)\var{results}.\gad{ADD-CLAUSE}(\var{cont-tag.binop2}, \var{the-expr}, \var{the-env}, \var{the-cont}, \var{make-thunk-num}, \var{globals.false-num}).

Case cont is if\rm{\small{\bold{Case}~ cont~\bold{is}~if}}.

  1. Let unevaled-args=cont-components[0]\var{unevaled-args} = \var{cont-components}[0].
  2. Let continuation=cont-components[1]\var{continuation} = \var{cont-components}[1].
  3. Let condition=result\var{condition} = \var{result}.
  4. Let cont-is-if=ALLOC-TAG-EQUAL(cont.tag(),globals.if-cont-tag)\var{cont-is-if} = \gad{ALLOC-TAG-EQUAL}(\var{cont.tag}(), \var{globals.if-cont-tag}).
  5. Let if-not-dummy=AND(cont-is-if,not-dummy)\var{if-not-dummy} = \gad{AND}(\var{cont-is-if}, \var{not-dummy}).
  6. Let (arg1,more)=CAR-CDR-NAMED(unevaled-args,cons-names.unevaled-args,allocated-cons-witness,if-not-dummy)(\var{arg1}, \var{more}) = \gad{CAR-CDR-NAMED}(\var{unevaled-args}, \var{cons-names.unevaled-args}, \var{allocated-cons-witness}, \var{if-not-dummy}).
  7. Let condition-is-nil=condition.IS-NIL()\var{condition-is-nil} = \var{condition}.\gad{IS-NIL}().
  8. Let (arg2,end)=CAR-CDR-NAMED(more,cons-names.unevaled-args-cdr,allocated-cons-witness,if-not-dummy)(\var{arg2}, \var{end}) = \gad{CAR-CDR-NAMED}(\var{more}, \var{cons-names.unevaled-args-cdr}, \var{allocated-cons-witness}, \var{if-not-dummy}).
  9. Let end-is-nil=end.IS-NIL()\var{end-is-nil} = \var{end}.\gad{IS-NIL}().
  10. Let res=PICK(condition-is-nil,arg2,arg1)\var{res} = \gad{PICK}(\var{condition-is-nil}, \var{arg2}, \var{arg1}).
  11. Let the-expr=PICK(end-is-nil,res,arg1)\var{the-expr} = \gad{PICK}(\var{end-is-nil}, \var{res}, \var{arg1}).
  12. Let the-cont=PICK(end-is-nil,continuation,globals.error-ptr-cont)\var{the-cont} = \gad{PICK}(\var{end-is-nil}, \var{continuation}, \var{globals.error-ptr-cont}).
  13. Call results.ADD-CLAUSE(globals.if-sym,the-expr,env,the-cont,globals.false)\var{results}.\gad{ADD-CLAUSE}(\var{globals.if-sym}, \var{the-expr}, \var{env}, \var{the-cont}, \var{globals.false}).

Case cont is lookup\rm{\small{\bold{Case}~ cont~\bold{is}~lookup}}.

  1. Let saved-env=cont-components[0]\var{saved-env} = \var{cont-components}[0].
  2. Let continuation=cont-components[1]\var{continuation} = \var{cont-components}[1].
  3. Call results.ADD-CLAUSE(globals.lookup-sym,result,saved-env,continuation,globals.true)\var{results}.\gad{ADD-CLAUSE}(\var{globals.lookup-sym}, \var{result}, \var{saved-env}, \var{continuation}, \var{globals.true}).

Case cont is tail\rm{\small{\bold{Case}~ cont~\bold{is}~tail}}.

  1. Let saved-env=cont-components[0]\var{saved-env} = \var{cont-components}[0].
  2. Let continuation=cont-components[1]\var{continuation} = \var{cont-components}[1].
  3. Call results.ADD-CLAUSE(tail,result,saved-env,continuation,globals.true)\var{results}.\gad{ADD-CLAUSE}(\var{tail}, \var{result}, \var{saved-env}, \var{continuation}, \var{globals}.\var{true}).

Case cont is let\rm{\small{\bold{Case}~ cont~\bold{is}~let}}.

  1. Let var=cont-components[0]\var{var} = \var{cont-components}[0].
  2. Let body=cont-components[1]\var{body} = \var{cont-components}[1].
  3. Let let-cont=cont-components[3]\var{let-cont} = \var{cont-components}[3].
  4. Let cont-is-let=ALLOC-TAG-EQUAL(cont.tag(),globals.let-cont-tag)\var{cont-is-let} = \gad{ALLOC-TAG-EQUAL}(\var{cont.tag}(), \var{globals.let-cont-tag}).
  5. Let let-cont-is-let=ALLOC-TAG-EQUAL(let-cont.tag(),globals.let-cont-tag)\var{let-cont-is-let} = \gad{ALLOC-TAG-EQUAL}(\var{let-cont.tag}(), \var{globals.let-cont-tag}).
  6. Let extended-env-not-dummy0=AND(let-cont-is-let,not-dummy)\var{extended-env-not-dummy0} = \gad{AND}(\var{let-cont-is-let}, \var{not-dummy}).
  7. Let extended-env-not-dummy=AND(extended-env-not-dummy0,cont-is-let)\var{extended-env-not-dummy} = \gad{AND}(\var{extended-env-not-dummy0}, \var{cont-is-let}).
  8. Let extended-env=EXTEND-NAMED(env,var,result,cons-names.env,allocated-cons-witness,extended-env-not-dummy)\var{extended-env} = \gad{EXTEND-NAMED}(\var{env}, \var{var}, \var{result}, \var{cons-names.env}, \var{allocated-cons-witness}, \var{extended-env-not-dummy}).
  9. Let continuation-is-tail=ALLOC-TAG-EQUAL(let-cont.tag(),globals.tail-cont-tag)\var{continuation-is-tail} = \gad{ALLOC-TAG-EQUAL}(\var{let-cont}.\fun{tag}(), \var{globals}.\var{tail-cont-tag}).
  10. Let tail-cont=PICK(continuation-is-tail,let-cont,newer-cont)\var{tail-cont} = \gad{PICK}(\var{continuation-is-tail}, \var{let-cont}, \var{newer-cont}).
  11. Call results.ADD-CLAUSE(let,body,extended-env,let-cont,newer-cont2-not-dummy)\var{results}.\gad{ADD-CLAUSE}(\var{let}, \var{body}, \var{extended-env}, \var{let-cont}, \var{newer-cont2-not-dummy}).
  12. Let newer-cont2-not-dummy=BOOLEAN-NUM(continuation-is-tail.NOT())\var{newer-cont2-not-dummy} = \gad{BOOLEAN-NUM}(\var{continuation-is-tail}.\gad{NOT}()).

Case cont is letrec\rm{\small{\bold{Case}~ cont~\bold{is}~letrec}}.

  1. Let var=cont-components[0]\var{var} = \var{cont-components}[0].
  2. Let body=cont-components[1]\var{body} = \var{cont-components}[1].
  3. Let letrec-cont=cont-components[3]\var{letrec-cont} = \var{cont-components}[3].
  4. Let letrec-cont-is-letrec-cont=ALLOC-TAG-EQUAL(letrec-cont.tag(),globals.letrec-cont-tag)\var{letrec-cont-is-letrec-cont} = \gad{ALLOC-TAG-EQUAL}(\var{letrec-cont.tag}(), \var{globals.letrec-cont-tag}).
  5. Let extend-rec-not-dummy=AND(letrec-cont-is-letrec-cont,not-dummy)\var{extend-rec-not-dummy} = \gad{AND}(\var{letrec-cont-is-letrec-cont}, \var{not-dummy}).
  6. Let extended-env=Extend-Rec(env,var,result,allocated-cons-witness,extend-rec-not-dummy)\var{extended-env} = \cir{Extend-Rec}(\var{env}, \var{var}, \var{result}, \var{allocated-cons-witness}, \var{extend-rec-not-dummy}).
  7. Let is-error=ALLOC-EQUAL(extended-env,globals.error-ptr-cont)\var{is-error} = \gad{ALLOC-EQUAL}(\var{extended-env}, \var{globals}.\var{error-ptr-cont}).
  8. Let continuation-is-tail=ALLOC-TAG-EQUAL(letrec-cont,globals.tail-cont-tag)\var{continuation-is-tail} = \gad{ALLOC-TAG-EQUAL}(\var{letrec-cont}, \var{globals}.\var{tail-cont-tag}).
  9. Let tail-cont=PICK(continuation-is-tail,letrec-cont,newer-cont)\var{tail-cont} = \gad{PICK}(\var{continuation-is-tail}, \var{letrec-cont}, \var{newer-cont}).
  10. Let the-cont=PICK(is-error,globals.error-ptr-cont,tail-cont)\var{the-cont} = \gad{PICK}(\var{is-error}, \var{globals}.\var{error-ptr-cont}, \var{tail-cont}).
  11. Let newer-cont2-not-dummy=BOOLEAN-NUM(continuation-is-tail.NOT())\var{newer-cont2-not-dummy} = \gad{BOOLEAN-NUM}(\var{continuation-is-tail}.\gad{NOT}()).
  12. Call results.ADD-CLAUSE(letrec,body,extended-env,the-cont,newer-cont2-not-dummy)\var{results}.\gad{ADD-CLAUSE}(\var{letrec}, \var{body}, \var{extended-env}, \var{the-cont}, \var{newer-cont2-not-dummy}).

Case cont is unop\rm{\small{\bold{Case}~ cont~\bold{is}~unop}}.

  1. Let unop-op1=continuation-components[0]\var{unop-op1} = \var{continuation-components}[0].
  2. Let other-unop-continuation=continuation-components[1]\var{other-unop-continuation} = \var{continuation-components}[1].
  3. Let op1-is-emit=ALLOC-TAG-EQUAL(g.op1-emit-tag,unop-op1.tag())\var{op1-is-emit} = \gad{ALLOC-TAG-EQUAL}(\var{g.op1-emit-tag}, \var{unop-op1.tag}()).
  4. Let op1-is-eval=ALLOC-TAG-EQUAL(g.op1-eval-tag,unop-op1.tag())\var{op1-is-eval} = \gad{ALLOC-TAG-EQUAL}(\var{g.op1-eval-tag}, \var{unop-op1.tag}()).
  5. Let unop-continuation0=PICK(op1-is-emit,newer-cont2,other-unop-continuation)\var{unop-continuation0} = \gad{PICK}(\var{op1-is-emit}, \var{newer-cont2}, \var{other-unop-continuation}).
  6. Let unop-continuation=PICK(op1-is-eval,continuation,unop-continuation0)\var{unop-continuation} = \gad{PICK}(\var{op1-is-eval}, \var{continuation}, \var{unop-continuation0}).
  7. Let result-is-cons=ALLOC-TAG-EQUAL(g.cons-tag,result.tag())\var{result-is-cons} = \gad{ALLOC-TAG-EQUAL}(\var{g.cons-tag}, \var{result.tag}()).
  8. Let result-is-str=ALLOC-TAG-EQUAL(g.str-tag,result.tag())\var{result-is-str} = \gad{ALLOC-TAG-EQUAL}(\var{g.str-tag}, \var{result.tag}()).
  9. Let result-is-nil=result.IS-NIL()\var{result-is-nil} = \var{result}.\gad{IS-NIL}().
  10. Let car-cdr-is-valid=OR(result-is-cons,result-is-str,result-is-nil)\var{car-cdr-is-valid} = \gad{OR}(\var{result-is-cons}, \var{result-is-str}, \var{result-is-nil}).
  11. Let op1-is-car=ALLOC-TAG-EQUAL(g.op1-car-tag,unop-op1.tag())\var{op1-is-car} = \gad{ALLOC-TAG-EQUAL}(\var{g.op1-car-tag}, \var{unop-op1.tag}()).
  12. Let op1-is-cdr=ALLOC-TAG-EQUAL(g.op1-cdr-tag,unop-op1.tag())\var{op1-is-cdr} = \gad{ALLOC-TAG-EQUAL}(\var{g.op1-cdr-tag}, \var{unop-op1.tag}()).
  13. Let op1-is-car-or-cdr=OR(op1-is-car,op1-is-cdr)\var{op1-is-car-or-cdr} = \gad{OR}(\var{op1-is-car}, \var{op1-is-cdr}).
  14. Let car-cdr-is-invalid=AND(op1-is-car-or-cdr,car-cdr-is-valid.NOT())\var{car-cdr-is-invalid} = \gad{AND}(\var{op1-is-car-or-cdr}, \var{car-cdr-is-valid}.\gad{NOT}()).
  15. Let op1-is-comm=ALLOC-TAG-EQUAL(globals.op1-comm-tag,unop-op1.tag())\var{op1-is-comm} = \gad{ALLOC-TAG-EQUAL}(\var{globals.op1-comm-tag}, \var{unop-op1.tag}()).
  16. Let op1-is-num=ALLOC-TAG-EQUAL(globals.op1-num-tag,unop-op1.tag())\var{op1-is-num} = \gad{ALLOC-TAG-EQUAL}(\var{globals.op1-num-tag}, \var{unop-op1.tag}()).
  17. Let op1-is-char=ALLOC-TAG-EQUAL(globals.op1-char-tag,unop-op1.tag())\var{op1-is-char} = \gad{ALLOC-TAG-EQUAL}(\var{globals.op1-char-tag}, \var{unop-op1.tag}()).
  18. Let op1-is-open=ALLOC-TAG-EQUAL(globals.op1-open-tag,unop-op1.tag())\var{op1-is-open} = \gad{ALLOC-TAG-EQUAL}(\var{globals.op1-open-tag}, \var{unop-op1.tag}()).
  19. Let op1-is-secret=ALLOC-TAG-EQUAL(globals.op1-secret-tag,unop-op1.tag())\var{op1-is-secret} = \gad{ALLOC-TAG-EQUAL}(\var{globals.op1-secret-tag}, \var{unop-op1.tag}()).
  20. Let op1-is-u64=ALLOC-TAG-EQUAL(globals.op1-u64-tag,unop-op1.tag())\var{op1-is-u64} = \gad{ALLOC-TAG-EQUAL}(\var{globals.op1-u64-tag}, \var{unop-op1.tag}()).
  21. Let tag-is-char=ALLOC-TAG-EQUAL(globals.char-tag,result.tag())\var{tag-is-char} = \gad{ALLOC-TAG-EQUAL}(\var{globals.char-tag}, \var{result.tag}()).
  22. Let tag-is-num=ALLOC-TAG-EQUAL(globals.num-tag,result.tag())\var{tag-is-num} = \gad{ALLOC-TAG-EQUAL}(\var{globals.num-tag}, \var{result.tag}()).
  23. Let tag-is-comm=ALLOC-TAG-EQUAL(gloabls.comm-tag,result.tag())\var{tag-is-comm} = \gad{ALLOC-TAG-EQUAL}(\var{gloabls.comm-tag}, \var{result.tag}()).
  24. Let tag-is-u64=ALLOC-TAG-EQUAL(globals.u64-tag,result.tag())\var{tag-is-u64} = \gad{ALLOC-TAG-EQUAL}(\var{globals.u64-tag}, \var{result.tag}()).
  25. Let tag-is-num-or-comm=OR(tag-is-num,tag-is-comm)\var{tag-is-num-or-comm} = \gad{OR}(\var{tag-is-num}, \var{tag-is-comm}).
  26. Let tag-is-num-or-char=OR(tag-is-num,tag-is-char)\var{tag-is-num-or-char} = \gad{OR}(\var{tag-is-num}, \var{tag-is-char}).
  27. Let tag-is-num-or-comm-or-char=OR(tag-is-num-or-comm,tag-is-char)\var{tag-is-num-or-comm-or-char} = \gad{OR}(\var{tag-is-num-or-comm}, \var{tag-is-char}).
  28. Let tag-is-num-or-comm-or-char-or-u64=OR(tag-is-num-or-comm-or-char,tag-is-u64)\var{tag-is-num-or-comm-or-char-or-u6}4 = \gad{OR}(\var{tag-is-num-or-comm-or-char}, \var{tag-is-u64}).
  29. Let comm-invalid-tag-error=AND(tag-is-num-or-comm.NOT(),op1-is-comm)\var{comm-invalid-tag-error} = \gad{AND}(\var{tag-is-num-or-comm}.\gad{NOT}(), \var{op1-is-comm}).
  30. Let num-invalid-tag-error=AND(tag-is-num-or-comm-or-char-or-u64.NOT(),op1-is-num)\var{num-invalid-tag-error} = \gad{AND}(\var{tag-is-num-or-comm-or-char-or-u64}.\gad{NOT}(), \var{op1-is-num}).
  31. Let char-invalid-tag-error=AND(tag-is-num-or-char.NOT(),op1-is-char)\var{char-invalid-tag-error} = \gad{AND}(\var{tag-is-num-or-char}.\gad{NOT}(), \var{op1-is-char}).
  32. Let open-invalid-tag-error=AND(tag-is-num-or-comm.NOT(),op1-is-open)\var{open-invalid-tag-error} = \gad{AND}(\var{tag-is-num-or-comm}.\gad{NOT}(), \var{op1-is-open}).
  33. Let secret-invalid-tag-error=AND(tag-is-num-or-comm.NOT(),op1-is-secret)\var{secret-invalid-tag-error} = \gad{AND}(\var{tag-is-num-or-comm}.\gad{NOT}(), \var{op1-is-secret}).
  34. Let u64-invalid-tag-error=AND(op1-is-u64,tag-is-num.NOT())\var{u64-invalid-tag-error} = \gad{AND}(\var{op1-is-u64}, \var{tag-is-num}.\gad{NOT}()).
  35. Let any-error=OR(car-cdr-is-invalid,comm-invalid-tag-error,num-invalid-tag-error,char-invalid-tag-error,open-invalid-tag-error,secret-invalid-tag-error,u64-invalid-tag-error)\var{any-error} = \gad{OR}(\var{car-cdr-is-invalid}, \var{comm-invalid-tag-error}, \var{num-invalid-tag-error}, \var{char-invalid-tag-error}, \var{open-invalid-tag-error}, \var{secret-invalid-tag-error}, \var{u64-invalid-tag-error}).
  36. Let the-expr=PICK(any-error,result,unop-val)\var{the-expr} = \gad{PICK}(\var{any-error}, \var{result}, \var{unop-val}).
  37. Let the-env=PICK(op1-is-eval,globals.nil-ptr,env)\var{the-env} = \gad{PICK}(\var{op1-is-eval}, \var{globals.nil-ptr}, \var{env}).
  38. Let the-cont=PICK(any-error,globals.error-ptr-cont,unop-continuation)\var{the-cont} = \gad{PICK}(\var{any-error}, \var{globals.error-ptr-cont}, \var{unop-continuation}).
  39. Let make-thunk-num=BOOLEAN-TO-NUM(op1-is-eval.NOT())\var{make-thunk-num} = \gad{BOOLEAN-TO-NUM}(\var{op1-is-eval}.\gad{NOT}()).
  40. Let newer-cont2-not-dummy0=AND(op1-is-emit,any-error.NOT())\var{newer-cont2-not-dummy0} = \gad{AND}(\var{op1-is-emit}, \var{any-error}.\gad{NOT}()).
  41. Let newer-cont2-not-dummy=BOOLEAN-NUM(newer-cont2-not-dummy0)\var{newer-cont2-not-dummy} = \gad{BOOLEAN-NUM}(\var{newer-cont2-not-dummy0}).
  42. Call results.ADD-CLAUSE(cont-tag.unop,the-expr,the-env,the-cont,make-thunk-num,newer-cont2-not-dummy)\var{results}.\gad{ADD-CLAUSE}(\var{cont-tag.unop}, \var{the-expr}, \var{the-env}, \var{the-cont}, \var{make-thunk-num}, \var{newer-cont2-not-dummy}).

Make thunk

A thunk is constructed to finish a determined layer of computation. For a certain operation, when all the input expressions are evaluated and the result is obtained in the apply-cont function, we build a thunk containing this result and an appropriate continuation pointer. We use it to terminate a Lurk program, by mapping the outermost continuation into a terminal continuation, or by propagating terminal and error continuations accordingly. Beyond that a thunk is used to return a value to the next stacked continuation, which happens for unop, binop2, lookup, tail and emit.

  • Make-Thunk:\cir{Make-Thunk:} \\

  • INPUT result,env,cont,not-dummy,allocated-cont-witness \cirio{INPUT} \var{result},\var{env},\var{cont},\var{not-dummy}, \var{allocated-cont-witness}.
    OUTPUT expr,env,cont \cirio{OUTPUT} \var{expr},\var{env},\var{cont}.
    1. Let thunk-clauses=MULTICASE-CLAUSES()\var{thunk-clauses} = \gad{MULTICASE-CLAUSES}().
    2. Let cont-is-tail=ALLOC-EQUAL(cont.tag(),globals.tail-cont-tag)\var{cont-is-tail} = \gad{ALLOC-EQUAL}(\var{cont.tag}(), \var{globals.tail-cont-tag}).
    3. Let make-thunk-cont-not-dummy=AND(cont-is-tail,not-dummy)\var{make-thunk-cont-not-dummy} = \gad{AND}(\var{cont-is-tail}, \var{not-dummy}).
    4. Let cont-components=get-named-components(cont,cont-names.make-thunk,allocated-cont-witness,make-thunk-cont-not-dummy)\var{cont-components} = \fun{get-named-components}(\var{cont}, \var{cont-names.make-thunk}, \var{allocated-cont-witness}, \var{make-thunk-cont-not-dummy}).
    5. Let saved-env=cont-components[0]\var{saved-env} = \var{cont-components}[0].
    6. Let continuation=cont-components[1]\var{continuation} = \var{cont-components}[1].
    7. Let result-expr=CONSTRUCT-THUNK(result,continuation)\var{result-expr} = \gad{CONSTRUCT-THUNK}(\var{result}, \var{continuation}).
    8. Call thunk-clauses.ADD-CLAUSE(tail,result-expr,env,globals.dummy-ptr)\var{thunk-clauses}.\gad{ADD-CLAUSE}(\var{tail}, \var{result-expr}, \var{env}, \var{globals}.\var{dummy-ptr}).
    9. Call thunk-clauses.ADD-CLAUSE(outermost,result,env,globals.terminal\var{thunk-clauses}.\gad{ADD-CLAUSE}(\var{outermost}, \var{result}, \var{env}, \var{globals}.\var{terminal}.
    10. Call thunk-clauses.ADD-CLAUSE(terminal,result,env,globals.terminal)\var{thunk-clauses}.\gad{ADD-CLAUSE}(\var{terminal}, \var{result}, \var{env}, \var{globals}.\var{terminal}).
    11. Call thunk-clauses.ADD-CLAUSE(error-cont-tag,result,env,globals.error)\var{thunk-clauses}.\gad{ADD-CLAUSE}(\var{error-cont-tag}, \var{result}, \var{env}, \var{globals}.\var{error}).
    12. Call thunk-clauses.ADD-DEFAULT(thunk,env,globals.dummy-ptr)\var{thunk-clauses}.\gad{ADD-DEFAULT}(\var{thunk}, \var{env}, \var{globals}.\var{dummy-ptr}).
    13. Call thunk-result=MULTICASE(thunk-clauses)\var{thunk-result} = \gad{MULTICASE}(\var{thunk-clauses}).
    14. Return (thunk-result.expr,thunk-result.env,thunk-result.cont)(\var{thunk-result}.\var{expr}, \var{thunk-result}.\var{env}, \var{thunk-result}.\var{cont}).

CAR-CDR-NAMED

This gadget receives as input an allocated pointer called maybe-cons.

  • CAR-CDR-NAMED:\cir{CAR-CDR-NAMED:} \\

  • INPUT maybe-cons,name,allocated-cons-witness,not-dummy \cirio{INPUT} \var{maybe-cons}, \var{name}, \var{allocated-cons-witness}, \var{not-dummy}.
    OUTPUT car,cdr \cirio{OUTPUT} \var{car},\var{cdr}.
    1. Let maybe-cons-is-nil=maybe-cons.IS-NIL()\var{maybe-cons-is-nil} = \var{maybe-cons}.\gad{IS-NIL}().
    2. Let cons-not-dummy=AND(maybe-cons-is-nil.NOT(),not-dummy)\var{cons-not-dummy} = \gad{AND}(\var{maybe-cons-is-nil}.\gad{NOT}(), \var{not-dummy}).
    3. Let (allocated-car,allocated-cdr,allocated-digest)=allocated-cons-witness.get-cons(name,cons-not-dummy.NOT())(\var{allocated-car}, \var{allocated-cdr}, \var{allocated-digest}) = \var{allocated-cons-witness}.\fun{get-cons}(\var{name}, \var{cons-not-dummy}.\gad{NOT}()).
    4. Let real-cons=ALLOC-EQUAL(maybe-cons.hash(),allocated-digest)\var{real-cons} = \gad{ALLOC-EQUAL}(\var{maybe-cons.hash}(), \var{allocated-digest}).
    5. Call IMPLIES(cons-not-dummy,real-cons)\gad{IMPLIES}(\var{cons-not-dummy}, \var{real-cons}).
    6. Let res-car=PICK(maybe-cons-is-nil,globals.nil-ptr,allocated-car)\var{res-car} = \gad{PICK}(\var{maybe-cons-is-nil}, \var{globals.nil-ptr}, \var{allocated-car}).
    7. Let res-cdr=PICK(maybe-cons-is-nil,globals.nil-ptr,allocated-cdr)\var{res-cdr} = \gad{PICK}(\var{maybe-cons-is-nil}, \var{globals.nil-ptr}, \var{allocated-cdr}).
    8. Return (res-car,res-cdr)(\var{res-car}, \var{res-cdr}).

Extend

We need to extend the environment for each new binding in a let expression. To extend the environment with a new binding we do the following:

  • Extend:\cir{Extend:} \\

  • INPUT env,var,val,name,allocated-cons-witness,not-dummy \cirio{INPUT} \var{env}, \var{var}, \var{val}, \var{name}, \var{allocated-cons-witness}, \var{not-dummy}.
    OUTPUT extended-env \cirio{OUTPUT} \var{extended-env}.
    1. Let new-binding=CONSTRUCT-CONS-NAMED(var,val,cons-names.binding,allocated-cons-witness,not-dummy)\var{new-binding} = \gad{CONSTRUCT-CONS-NAMED}(\var{var}, \var{val}, \var{cons-names.binding}, \var{allocated-cons-witness}, \var{not-dummy}).
    2. Return CONSTRUCT-CONS-NAMED(new-binding,env,name,allocated-cons-witness,not-dummy\gad{CONSTRUCT-CONS-NAMED}(\var{new-binding}, \var{env}, \var{name}, \var{allocated-cons-witness}, \var{not-dumm}y.

Extend rec

This circuit is used to extend the recursive environment receives as input the environment env and allocated pointers to a variable var and a value val.

  • Extend-Rec:\cir{Extend-Rec:} \\

  • INPUT env,var,val,allocated-cons-witness,not-dummy \cirio{INPUT} \var{env}, \var{var}, \var{val}, \var{allocated-cons-witness}, \var{not-dummy}.
    OUTPUT extended-env \cirio{OUTPUT} \var{extended-env}.
    1. Let (binding-or-env,rest)=CAR-CDR-NAMED(env,cons-names.env,allocate-cons-witness,not-dummy)(\var{binding-or-env}, \var{rest}) = \cir{CAR-CDR-NAMED}(\var{env}, \var{cons-names.env}, \var{allocate-cons-witness}, \var{not-dummy}).
    2. Let (var-or-binding,dummy-val-or-more-bindings)=CAR-CDR-NAMED(binding-or-env,cons-names.env-car,allocated-cons-witness,not-dummy).(\var{var-or-binding}, \var{dummy-val-or-more-bindings}) = \cir{CAR-CDR-NAMED}(\var{binding-or-env}, \var{cons-names.env-car}, \var{allocated-cons-witness}, \var{not-dummy}).
    3. Let var-or-binding-is-cons=IS-CONS(var-or-binding)\var{var-or-binding-is-cons} = \gad{IS-CONS}(\var{var-or-binding}).
    4. Let cons=CONSTRUCT-CONS-NAMED(var,val,cons-names.new-rec-cadr,allocated-cons-witness,not-dummy)\var{cons} = \gad{CONSTRUCT-CONS-NAMED}(\var{var}, \var{val}, \var{cons-names.new-rec-cadr}, \var{allocated-cons-witness}, \var{not-dummy}).
    5. Let list=CONSTRUCT-CONS-NAMED(cons,globals.nil-ptr,cons-names.NewRec,allocated-cons-witness,not-dummy)\var{list} = \gad{CONSTRUCT-CONS-NAMED}(\var{cons}, \var{globals.nil-ptr}, \var{cons-names.NewRec}, \var{allocated-cons-witness}, \var{not-dummy}).
    6. Let new-env-if-sym-or-nil=CONSTRUCT-CONS-NAMED(list,env,cons-names.extended-rec,allocated-cons-witness,not-dummy)\var{new-env-if-sym-or-nil} = \gad{CONSTRUCT-CONS-NAMED}(\var{list}, \var{env}, \var{cons-names.extended-rec}, \var{allocated-cons-witness}, \var{not-dummy}).
    7. Let cons-branch-not-dummy=AND(var-or-binding-is-cons,not-dummy)\var{cons-branch-not-dummy} = \gad{AND}(\var{var-or-binding-is-cons}, \var{not-dummy}).
    8. Let cons2=CONSTRUCT-CONS-NAMED(cons,binding-or-env,cons-names.new-rec,allocated-cons-witness,cons-branch-not-dummy)\var{cons2} = \gad{CONSTRUCT-CONS-NAMED}(\var{cons}, \var{binding-or-env}, \var{cons-names.new-rec}, \var{allocated-cons-witness}, \var{cons-branch-not-dummy}).
    9. Let cons3=CONSTRUCT-CONS-NAMED(cons2,rest,cons-names.extended-rec,allocated-cons-witness,cons-branch-not-dummy)\var{cons3} = \gad{CONSTRUCT-CONS-NAMED}(\var{cons2}, \var{rest}, \var{cons-names.extended-rec}, \var{allocated-cons-witness}, \var{cons-branch-not-dummy}).
    10. Let is-sym=var-or-binding.IS-SYM()\var{is-sym} = \var{var-or-binding}.\gad{IS-SYM}().
    11. Let is-nil=var-or-binding.IS-NIL()\var{is-nil} = \var{var-or-binding}.\gad{IS-NIL}().
    12. Let is-sym-or-nil=OR(is-sym,is-nil)\var{is-sym-or-nil} = \gad{OR}(\var{is-sym}, \var{is-nil}).
    13. Let is-cons=var-or-binding-is-cons\var{is-cons} = \var{var-or-binding-is-cons}.
    14. Let new-env-if-cons=PICK(is-cons,cons3,globals.error-ptr)\var{new-env-if-cons} = \gad{PICK}(\var{is-cons}, \var{cons3}, \var{globals.error-ptr}).
    15. Let extended-env=PICK(is-sym-or-nil,new-env-if-sym-or-nil,new-env-if-cons\var{extended-env} = \gad{PICK}(\var{is-sym-or-nil}, \var{new-env-if-sym-or-nil}, \var{new-env-if-cons}.
    16. Return extended-env\var{extended-env}.

Low-level description

In this section, we describe how R1CS constraints are constructed for each building block previously used in the construction of the Lurk circuit. Those components are usually called gadgets. Below is a description of how to obtain R1CS constraints for each necessary gadget.

R1CS

We denote the witness by w{\footnotesize{w}}, which corresponds to all the intermediate values of the subjacent program. Matrices A, B, C{\footnotesize{A,~B,~C}} contain the public values of the program, encoding all the constraints that the witness will have to satisfy. Specifically, it constrains the composition of gadgets that implement the reduction step. Hence, it is responsible for the validation of frame transitions.

Each frame has an Input and Output (IO), both constituted by the triple (expr, env, cont)\rm{\footnotesize{(expr,~env,~cont)}}. The input expression is reduced frame by frame, generating a sequence of IOs where the output of a previous frame is equal to the input of the next frame. The IO is part of the witness and therefore corresponds to private data constrained in w{\footnotesize{w}}. We say the w{\footnotesize{w}} satisfies A, B, C{\footnotesize{A,~B,~C}} if the following relation is respected:

(A.w)  (B.w)=(C.w)\footnotesize{(A.w)~\circ~(B.w)=(C.w)}

where \scriptsize{\circ} signifies component multiplication.

Circuit components

In this section we describe each gadget that is necessary for the construction of the Lurk circuit in detail. We start by showing how to construct logic operations like conjunctions and disjunctions, and then we show how to implement pointers using Poseidon. Next, we present gadgets to carry out arithmetic operations, bit decomposition, and comparisons. We finally describe how to implement ternary operators, which are used to construct the case gadget, which can be used to select one among many clauses, given a key element. Multiple case gadgets can be composed into a single multicase gadget, which allows us to select multiple clauses while requiring fewer constraints than if we simply repeated the case gadget multiple times.

In order to describe R1CS constraints, we use a short notation showing the multiplications involving certain linear combinations of private variables. From this, we consider it trivial to derive the formal description presented above.

Boolean operations

Here we show how to compute Boolean operations.

  • a.NOT()a.\gad{NOT}(): given the field element aa that is guaranteed to be either 0 or 1, we don't need to use multiplications in order to calculate the NOT function. Instead, NOT can be computed by using the linear combination (1a)(1 – a).
  • AND(a,b)\gad{AND}(a, b): given two bits, aa and bb, as input, we calculate the AND function using the following multiplication:
    1. Constrain a×b=resulta \times b = \var{result}, where result\var{result} corresponds to the output of the AND function.
  • OR(a,b)\gad{OR}(a, b): given bits aa and bb, the OR function can be calculated using both NOT and AND, using De Morgan’s law. The output is AND(a.NOT(),b.NOT()).NOT().
  • XOR(a,b)\gad{XOR}(a, b): given bits aa and bb, the XOR function can be calculated using the formula 2.a×b=a+bc2.a \times b = a + b – c, where cc is the result. This relation can easily be checked to be valid if and only if c=abc = a \oplus b.
  • ENFORCE-IMPLICATION(a,b)\gad{ENFORCE-IMPLICATION}(a, b): given bits aa and bb, we say that aa implies bb if the following conditions hold:
    1. Call implication=IMPLIES(a,b)\var{implication} = \gad{IMPLIES}(a, b).
    2. Call ENFORCE-TRUE(implication)\gad{ENFORCE-TRUE}(\var{implication}).
  • ENFORCE-EQUAL(a,b)\gad{ENFORCE-EQUAL}(a, b): given bits aa and bb, we enforce equality of bits using the constraint
    1. Constrain (ab)×1=0(a – b) \times 1 = 0.
  • ENFORCE-TRUE(a)\gad{ENFORCE-TRUE}(a): given bit aa, we call ENFORCE-EQUAL(a, 1).
  • ENFORCE-FALSE(a)\gad{ENFORCE-FALSE}(a): given bit aa, we call ENFORCE-EQUAL(a, 0).
  • ENFORCE-BIT(a)\gad{ENFORCE-BIT}(a): given bit aa, we
    1. Constrain (1a)×a=0(1 – a) \times a = 0.
  • IMPLIES(a,b)\gad{IMPLIES}(a, b): equivalent to AND(a,b.NOT()).NOT()\gad{AND}(a, b.\gad{NOT}()).\gad{NOT}().

In Table 1 we show how many constraints and witnesses are required to construct each gadget.


Table 1: Logic gadgets summary
Gadget Constraints Witnesses
NOT 0 0
OR 1 1
AND 1 1
XOR 1 1
ENFORCE-IMPLICATION 2 1
ENFORCE-EQUAL 1 0
ENFORCE-TRUE 1 0
ENFORCE-FALSE 1 0
ENFORCE-BIT 1 0
IMPLIES 1 1

Pointers

In this section we describe the construction of gadgets for pointers. The basic building block is Poseidon gadget, which can be instantiated in different ways. What distinguishes each instantiation is the number of input field elements. As we increase the number of input elements, we also increase the circuit size of the gadget. Since each pointer needs two field elements to represent it, in order to create a gadget for cons operation, we to pass two allocated pointers as input, therefore we need 4 field elements and, consequently, we have to use the 4-ary instantiation of Poseidon gadget. Analogously, we need 6-ary instantiation of Poseidon gadget to construct a pointer for function, since it needs to pass as input 3 allocated pointers, namely the argument, the body and the environment. Finally, we may need to pass 4 allocated pointers to create generic pointers, as for example is required for some continuation pointers. Next we describe different gadgets that we provide to construct different types of pointers.

  • ALLOC-NUM(n)\gad{ALLOC-NUM}(n): given a field element n as input, it allocates a new variable whose value is n. There is no constraint for this gadget. It is only necessary to allocate a new witness in the circuit.
  • ALLOC-PTR(tag,hash)\gad{ALLOC-PTR}(\var{tag}, \var{hash}): given a pair of field elements, tag and hash as input, it proceed as follows:
    1. ALLOC-NUM(tag)\gad{ALLOC-NUM}(\var{tag}).
    2. ALLOC-NUM(hash)\gad{ALLOC-NUM}(\var{hash}).
  • ALLOC-CONSTANT(c)\gad{ALLOC-CONSTANT}(c): given a field element cc as input, it allocates a new pointer whose value is cc. Namely, the constraint is implemented as:
    1. Constrain allocatedoutput×1=c\rm{allocated-output} \times 1 = c.
  • ALLOC-CONSTANT-PTR(c)\gad{ALLOC-CONSTANT-PTR}(c): given a pointer cc to a constant value, it allocates the pointer in the circuit as follows:
    1. Let alloc-tag=ALLOC-CONSTANT(c.tag())\var{alloc-tag} = \gad{ALLOC-CONSTANT}(c.\fun{tag}()).
    2. Let alloc-hash=ALLOC-CONSTANT(c.hash())\var{alloc-hash} = \gad{ALLOC-CONSTANT}(c.\fun{hash}()).
  • ALLOC-CONSTANT-CONT-PTR(c)\gad{ALLOC-CONSTANT-CONT-PTR}(c): given a continuation pointer cc to a constant value, it allocates the pointer in the circuit as follows:
    1. Let alloc-tag=ALLOC-CONSTANT(c.tag())\var{alloc-tag} = \gad{ALLOC-CONSTANT}(c.\fun{tag}()).
    2. Let alloc-hash=ALLOC-CONSTANT(c.hash())\var{alloc-hash} = \gad{ALLOC-CONSTANT}(c.\fun{hash}()).
  • ALLOC-FROM-PARTS(tag,hash)\gad{ALLOC-FROM-PARTS}(\var{tag}, \var{hash}): it receives as input two field elements, as allocated numbers, that can be used to allocate a new pointer in the circuit. No constraints are required for this purpose.
  • ALLOC-EQUAL(a,b)\gad{ALLOC-EQUAL}(a, b): it receives as input two allocated numbers, aa and bb, and as output it gives us a Boolean that indicates if aa is equal to bb or not.
    1. Let diff=SUB(ab)\var{diff} = \gad{SUB}(a – b).
    2. Let result=ALLOC-BIT(a==b)\var{result} = \gad{ALLOC-BIT}(a == b).
    3. Constrain result×diff=0\var{result} \times \var{diff} = 0.
    4. Constrain (diff+result)×q=1(\var{diff} + \var{result}) \times q = 1.
  • ALLOC-TAG-EQUAL(a,b)\gad{ALLOC-TAG-EQUAL}(a, b): it receives as input two numbers, where aa is an allocated number and bb is a constant, and as output it gives us a Boolean that indicates if aa is equal to bb or not. It works in the same way as ALLOC-EQUAL\gad{ALLOC-EQUAL}, but avoids the unnecessary allocation of tag constants as global variables.
    1. Let diff=SUB(ab)\var{diff} = \gad{SUB}(a – b).
    2. Let result=ALLOC-BIT(a==b)\var{result} = \gad{ALLOC-BIT}(a == b).
    3. Constrain result×diff=0\var{result} \times \var{diff} = 0.
    4. Constrain (diff+result)×q=1(\var{diff} + \var{result}) \times q = 1.
  • IS-SYM(a)\gad{IS-SYM(a)}: the same as ALLOC-TAG-EQUAL(a,c)\gad{ALLOC-TAG-EQUAL}(a, c), where cc represents the field element whose value corresponds to the symbol tag.
  • IS-FUN(a)\gad{IS-FUN(a)}: the same as ALLOC-TAG-EQUAL(a,c)\gad{ALLOC-TAG-EQUAL}(a, c), where cc represents the field element whose value corresponds to the function tag.
  • IS-CONS(a)\gad{IS-CONS(a)}: the same as ALLOC-TAG-EQUAL(a,c)\gad{ALLOC-TAG-EQUAL}(a, c), where cc represents the field element whose value corresponds to the cons tag.
  • IS-STR(a)\gad{IS-STR(a)}: the same as ALLOC-TAG-EQUAL(a,c)\gad{ALLOC-TAG-EQUAL}(a, c), where cc represents the field element whose value corresponds to the string tag.
  • IS-NUM(a)\gad{IS-NUM(a)}: the same as ALLOC-TAG-EQUAL(a,c)\gad{ALLOC-TAG-EQUAL}(a, c), where cc represents the field element whose value corresponds to the number tag.
  • IS-U64(a)\gad{IS-U64(a)}: the same as ALLOC-TAG-EQUAL(a,c)\gad{ALLOC-TAG-EQUAL}(a, c), where cc represents the field element whose value corresponds to the u64 tag.
  • IS-CHAR(a)\gad{IS-CHAR(a)}: the same as ALLOC-TAG-EQUAL(a,c)\gad{ALLOC-TAG-EQUAL}(a, c), where cc represents the field element whose value corresponds to the char tag.
  • IS-COMM(a)\gad{IS-COMM(a)}: the same as ALLOC-TAG-EQUAL(a,c)\gad{ALLOC-TAG-EQUAL}(a, c), where cc represents the field element whose value corresponds to the comm tag.
  • IS-THUNK(a)\gad{IS-THUNK(a)}: the same as ALLOC-TAG-EQUAL(a,c)\gad{ALLOC-TAG-EQUAL}(a, c), where cc represents the field element whose value corresponds to the thunk tag.
  • ALLOC-IS-ZERO(a)\gad{ALLOC-IS-ZERO}(a): it receives as input an allocated number aa. The output is given by a Boolean that indicates if the input number is zero or not. It is constructed as follows:
    1. Let is-zero=(a==0)\var{is-zero} = (a == 0).
    2. Let result=ALLOC-BIT(is-zero)\var{result} = \gad{ALLOC-BIT}(\var{is-zero}).
    3. Constrain result×a=0\var{result} \times a = 0.
    4. Constrain (x+result)×q=1(x + \var{result}) \times q = 1.
  • ALLOCATE-DUMMY-COMPONENTS()\gad{ALLOCATE-DUMMY-COMPONENTS}(): it receives no input argument and is responsible for allocating dummy variables and creating a pointer for them. Concretely, we have the following:
    1. Let value=ALLOC-FROM-PARTS(0,0)\var{value} = \gad{ALLOC-FROM-PARTS}(0, 0).
    2. Let cont=ALLOC-FROM-PARTS(0,0)\var{cont} = \gad{ALLOC-FROM-PARTS}(0, 0).
    3. Let dummy-hash=CONSTRUCT-THUNK(value,cont)\var{dummy-hash} = \gad{CONSTRUCT-THUNK}(\var{value}, \var{cont}).
  • ALLOCATE-THUNK-COMPONENTS()\gad{ALLOCATE-THUNK-COMPONENTS}(): it allocate thunk components
  • ALLOCATE-HASH-COMPONENTS\gad{ALLOCATE-HASH-COMPONENTS}
  • CONSTRUCT(components)\gad{CONSTRUCT}(\var{components}): it receives as input 4 allocated pointers, represented as 8 field elements, and calls the 8-ary Poseidon gadget.
  • CONSTRUCT-CONS(components)\gad{CONSTRUCT-CONS}(\var{components}): it receives as input 2 allocated pointers, represented as 4 field elements, and calls the 4-ary Poseidon gadget.
  • CONSTRUCT-THUNK(components)\gad{CONSTRUCT-THUNK}(\var{components}): it receives as input 2 allocated pointers, represented as 4 field elements, and calls the 4-ary Poseidon gadget.
  • CONSTRUCT-FUN(components)\gad{CONSTRUCT-FUN}(\var{components}): it receives as input 3 allocated pointers, represented as 6 field elements, and calls the 6-ary Poseidon gadget.
  • CONSTRUCT-LIST(elements)\gad{CONSTRUCT-LIST}(\var{elements}): it receives as input a list of n allocated pointers and uses cons n1n – 1 times in order to construct a pointer to the output list.

Table 2: Pointer gadgets summary
Gadget Constraints Witnesses
ALLOC-NUM 0 1
ALLOC-PTR 0 2
ALLOC-CONSTANT 1 1
ALLOC-CONSTANT-CONT-PTR 2 2
ALLOC-FROM-PARTS 0 0
ALLOC-EQUAL 4 3
ALLOC-TAG-EQUAL 3 2
IS-SYM 3 2
IS-FUN 3 2
IS-CONS 3 2
IS-STR 3 2
IS-NUM 3 2
IS-U64 3 2
IS-CHAR 3 2
IS-COMM 3 2
IS-THUNK 3 2
ALLOC-IS-ZERO 3 2
ALLOCATE-THUNK-COMPONENTS 289 293
ALLOCATE-MAYBE-DUMMY-COMPONENTS 390 398
ALLOCATE-MAYBE-FUN 339 345
CONSTRUCT 388 388
CONSTRUCT-CONS 286 284
CONSTRUCT-THUNK 286 284
CONSTRUCT-FUN 334 334
CONSTRUCT-COMMITMENT 334 334
CONSTRUCT-LIST 286(n – 1) 284(n – 1)

Functional Commitments

  • SECRET(commitment)\gad{SECRET}(\var{commitment}):
    1. Check if the opening is known. If so, name it (secret,payload)(\var{secret}, \var{payload}).
    2. Let open-commitment=CONSTRUCT-COMMITMENT(secret,payload)\var{open-commitment} = \gad{CONSTRUCT-COMMITMENT}(\var{secret}, \var{payload}).
    3. Let valid-opening=ALLOC-EQUAL(commitment,open-commitment)\var{valid-opening} = \gad{ALLOC-EQUAL}(\var{commitment}, \var{open-commitment}).
    4. Return secret\var{secret}.
  • NUM(value)\gad{NUM}(\var{value}):
    1. Let num-value=value.hash()\var{num-value} = \var{value.hash}().
    2. Let alloc-num-res=ALLOC-FROM-PARTS(globals.num-tag,num-value)\var{alloc-num-res} = \gad{ALLOC-FROM-PARTS}(\var{globals.num-tag}, \var{num-value}).
    3. Return alloc-num-res\var{alloc-num-res}.
  • CHAR(value)\gad{CHAR}(\var{value}):
    1. Let char-value=value.hash()\var{char-value} = \var{value.hash}().
    2. Let alloc-char-res=ALLOC-FROM-PARTS(globals.char-tag,char-value)\var{alloc-char-res} = \gad{ALLOC-FROM-PARTS}(\var{globals.char-tag}, \var{char-value}).
    3. Return alloc-char-res\var{alloc-char-res}.
  • COMM(value)\gad{COMM}(\var{value}):
    1. Let comm-value=value.hash()\var{comm-value} = \var{value.hash}().
    2. Let alloc-comm-res=ALLOC-FROM-PARTS(globals.comm-tag,comm-value)\var{alloc-comm-res} = \gad{ALLOC-FROM-PARTS}(\var{globals.comm-tag}, \var{comm-value}).
    3. Return alloc-comm-res\var{alloc-comm-res}.

Table 3: Functional commitments gadgets summary
Gadget Constraints Witnesses
HIDE 334 334
COMMIT 334 334
OPEN 334 334
SECRET 334 334
NUM 0 0
CHAR 0 0
COMM 0 0

Arithmetic operations

Here we present all the arithmetic operations that are required to implement the Lurk circuit. For each operation, we provide two gadgets. The first receives both input and output terms, constraining them so that it really corresponds to the correct calculation of that operation. The second only receives the input terms, and it is the responsibility of the gadget to allocate the output and calculate it accordingly.

  • SUM(a,b,res)\gad{SUM}(a, b, \var{res}): It receives as input the two operands and the result as allocated numbers.
    1. Constrain: (a+b)×(1)=res(a + b) \times (1) = \var{res}.
  • ADD(a,b)\gad{ADD}(a, b): In this case, no result is provided. We first need to allocate and assign the correct value.
    1. Let res=ALLOC(a+b)\var{res} = \gad{ALLOC}(a + b).
    2. Call SUM(a,b,res)\gad{SUM}(a, b, \var{res}).
  • DIFFERENCE(a,b,res)\gad{DIFFERENCE}(a, b, \var{res}): It receives as input the two operands and the result as allocated numbers.
    1. Constrain: (res+b)×(1)=a(\var{res} + b) \times (1) = a.
  • SUB(a, b): In this case no result is provided. We first need to allocate and assign the correct value.
    1. Let res=ALLOC(ab)\var{res} = \gad{ALLOC}(a – b).
    2. Call DIFFERENCE(a,b,res)\gad{DIFFERENCE}(a, b, \var{res}).
  • PRODUCT(a,b,res)\gad{PRODUCT}(a, b, \var{res}): It receives as input the two operands and the result as allocated numbers.
    1. Constrain: (a)×(b)=res(a) \times (b) = \var{res}.
  • MUL(a,b)\gad{MUL}(a, b): In this case no result is provided. We first need to allocate and assign the correct value.
    1. Let res=ALLOC(a.b)\var{res} = \gad{ALLOC}(a.b).
    2. Call PRODUCT(a,b,res)\gad{PRODUCT}(a, b, \var{res}).
  • DIV(a,b)\gad{DIV}(a, b): For division, we multiply by the inverse.
    1. Let inv=ALLOC(b1)\var{inv} = \gad{ALLOC}(b^{-1})
    2. Let res=MUL(a,inv)\var{res} = \gad{MUL}(a, \var{inv}).

Table 4: Arithmetic gadgets summary
Gadget Constraints Witnesses
SUM 1 0
ADD 1 1
DIFFERENCE 1 0
SUB 1 1
PRODUCT 1 0
MUL 1 1
DIV 1 1

Comparisons

We have that a number is defined to be negative if the parity bit (the least significant bit) is odd after doubling, meaning that the field element (after doubling) is larger than the underlying prime p that defines the field, then a modular reduction must have been carried out, changing the parity that should be even (since we multiplied by 2) to odd. In other words, we define negative numbers to be those field elements that are larger than p/2p/2.

Operations like ﹤, ≤, ﹥, ≥ are implemented using the bit decomposition 3 times. To test if a<ba < b, we calculate the difference diff=(ba)\var{diff} = (b – a) and test if diff is negative by seeing if the parity bit, the least significant bit, of 2diff2\var{diff} is 1. If it is the case, it means 2diff2\var{diff} is larger than pp. Therefore, after computing the modular reduction, the parity bit is changed from 0 to 1. By composing with equality tests and other basic Boolean operations, we also obtain ≤, ﹥, ≥.

  • IS-NEGATIVE:\gad{IS-NEGATIVE:} \\

  • INPUT num \cirio{INPUT} \var{num}.
    OUTPUT num-is-negative \cirio{OUTPUT} \var{num-is-negative}.
    1. Let double-num=ADD(num,num)\var{double-num} = \gad{ADD}(\var{num}, \var{num}).
    2. Let double-num-bits=double-num.to-bits-le-strict()\var{double-num-bits} = \var{double-num}.\cir{to-bits-le-strict}().
    3. Let lsb-2num=double-num-bits[0]\var{lsb-2num} = \var{double-num-bits}[0].
    4. Let num-is-negative=lsb-2num\var{num-is-negative} = \var{lsb-2num}.
    5. Return num-is-negative\var{num-is-negative}.

In order to compare 2 field elements, we first compute the predicate Is-Negative()\cir{Is-Negative}() for aa, bb and (ba)(b - a), which are input parameters. Then we use a multicase to select the desired result according to the operation given by op2\var{op2}.

  • COMPARISON-HELPER:\gad{COMPARISON-HELPER:} \\

  • INPUT a,b,diff,op2 \cirio{INPUT} a, b, \var{diff}, \var{op2}.
    OUTPUT is-comparison-tag,comp-val,diff-is-negative \cirio{OUTPUT} \var{is-comparison-tag}, \var{comp-val}, \var{diff-is-negative}.
    1. Let a-is-negative=Is-Negative(a)\var{a-is-negative} = \gad{Is-Negative}(a).
    2. Let b-is-negative=Is-Negative(b)\var{b-is-negative} = \gad{Is-Negative}(b).
    3. Let diff-is-negative=Is-Negative(diff)\var{diff-is-negative} = \gad{Is-Negative}(\var{diff}).
    4. Let diff-is-zero=ALLOC-IS-ZERO(diff)\var{diff-is-zero} = \gad{ALLOC-IS-ZERO}(\var{diff}).
    5. Let diff-is-not-positive=OR(diff-is-positive,diff-is-zero)\var{diff-is-not-positive} = \gad{OR}(\var{diff-is-positive}, \var{diff-is-zero}).
    6. Let diff-is-positive=AND(diff-is-negative.NOT(),diff-is-zero.NOT())\var{diff-is-positive} = \gad{AND}(\var{diff-is-negative}.\gad{NOT}(), \var{diff-is-zero}.\gad{NOT}()).
    7. Let diff-is-not-negative=diff-is-negative.NOT()\var{diff-is-not-negative} = \var{diff-is-negative}.\gad{NOT}().
    8. Let not-one-negative-and-other-not-negative=XOR(a-is-negative,b-is-negative)\var{not-one-negative-and-other-not-negative} = \gad{XOR}(\var{a-is-negative}, \var{b-is-negative}).
    9. Let a-negative-and-b-not-negative=AND(a-is-negative,b-is-negative.NOT())\var{a-negative-and-b-not-negative} = \gad{AND}(\var{a-is-negative}, \var{b-is-negative}.\gad{NOT}()).
    10. Let alloc-num-diff-is-negative=BOOLEAN-TO-NUM(diff-is-negative)\var{alloc-num-diff-is-negative} = \gad{BOOLEAN-TO-NUM}(\var{diff-is-negative}).
    11. Let alloc-num-diff-is-not-positive=BOOLEAN-TO-NUM(diff-is-not-positive)\var{alloc-num-diff-is-not-positive} = \gad{BOOLEAN-TO-NUM}(\var{diff-is-not-positive}).
    12. Let alloc-num-diff-is-positive=BOOLEAN-TO-NUM(diff-is-positive)\var{alloc-num-diff-is-positive} = \gad{BOOLEAN-TO-NUM}(\var{diff-is-positive}).
    13. Let alloc-num-diff-is-not-negative=BOOLEAN-TO-NUM(diff-is-not-negative)\var{alloc-num-diff-is-not-negative} = \gad{BOOLEAN-TO-NUM}(\var{diff-is-not-negative}).
    14. Let comp-clauses=CASE-CLAUSES()\var{comp-clauses} = \gad{CASE-CLAUSES}().
    15. Let comp-clauses.ADD-CLAUSE(alloc-num-diff-is-negative,globals.true,globals.false)\var{comp-clauses}.\gad{ADD-CLAUSE}(\var{alloc-num-diff-is-negative}, \var{globals.true}, \var{globals.false}).
    16. Let comp-clauses.ADD-CLAUSE(alloc-num-diff-is-not-positive,globals.true,globals.false)\var{comp-clauses}.\gad{ADD-CLAUSE}(\var{alloc-num-diff-is-not-positive}, \var{globals.true}, \var{globals.false}).
    17. Let comp-clauses.ADD-CLAUSE(alloc-num-diff-is-positive,globals.false,globals.true)\var{comp-clauses}.\gad{ADD-CLAUSE}(\var{alloc-num-diff-is-positive}, \var{globals.false}, \var{globals.true}).
    18. Let comp-clauses.ADD-CLAUSE(alloc-num-diff-is-not-negative,globals.false,globals.true)\var{comp-clauses}.\gad{ADD-CLAUSE}(\var{alloc-num-diff-is-not-negative}, \var{globals.false}, \var{globals.true}).
    19. Let comp-result=MULTICASE(op2.tag(),comp-clauses,comp-default)\var{comp-result} = \gad{MULTICASE}(\var{op2.tag}(), \var{comp-clauses}, \var{comp-default}).
    20. Let comp-val-same-sign-num=comp-result[0]\var{comp-val-same-sign-num} = \var{comp-result}[0].
    21. Let comp-val-a-neg-and-b-not-neg-num=comp-result[1]\var{comp-val-a-neg-and-b-not-neg-num} = \var{comp-result[1]}.
    22. Let comp-val-a-not-neg-and-b-neg-num=comp-result[2]\var{comp-val-a-not-neg-and-b-neg-num} = \var{comp-result[2]}.
    23. Let is-comparison-tag=comp-result.is-default.NOT()\var{is-comparison-tag} = \var{comp-result.is-default}.\gad{NOT}().
    24. Let comp-val1=PICK(a-negative-and-b-not-negative,comp-val-a-neg-and-b-not-neg-num,comp-val-a-not-neg-and-b-neg-num)\var{comp-val1} = \gad{PICK}(\var{a-negative-and-b-not-negative}, \var{comp-val-a-neg-and-b-not-neg-num}, \var{comp-val-a-not-neg-and-b-neg-num}).
    25. Let comp-val2=PICK(not-one-negative-and-b-not-negative,comp-val-same-sign-num,comp-val1)\var{comp-val2} = \gad{PICK}(\var{not-one-negative-and-b-not-negative}, \var{comp-val-same-sign-num}, \var{comp-val1}).
    26. Let comp-val-is-zero=ALLOC-IS-ZERO(comp-val2)\var{comp-val-is-zero} = \gad{ALLOC-IS-ZERO}(\var{comp-val2}).
    27. Let comp-val=PICK(comp-val-is-zero,globals.nil-ptr,globals.t-ptr)\var{comp-val} = \gad{PICK}(\var{comp-val-is-zero}, \var{globals.nil-ptr}, \var{globals.t-ptr}).
    28. Return (is-comparison-tag,comp-val,diff-is-negative)(\var{is-comparison-tag}, \var{comp-val}, \var{diff-is-negative}).

Next we define an auxiliary function that is responsible for constraining the coercion from field element to an unsigned integer. To do that, we use big number to calculate the remainder after division by an appropriate power of 2, depending on the size\var{size}. Later, we use the LINEAR()\gad{LINEAR}() gadget to constrain the relation a=b.q+ra = b.q + r, where qq is the power of 2 mentioned above, and 0r<q0 \leq r < q.

  • TO-UNSIGNED-INTEGER-HELPER:\gad{TO-UNSIGNED-INTEGER-HELPER:} \\

  • INPUT field-elem,field-bn,field-elem-bits,size \cirio{INPUT} \var{field-elem}, \var{field-bn}, \var{field-elem-bits}, \var{size}.
    OUTPUT r-num \cirio{OUTPUT} \var{r-num}.
    1. Let power-of-two-bn=pow(2,size) \var{power-of-two-bn} = \fun{pow}(2, \var{size})~- computed as a big number.
    2. Let (q-bn,r-bn)=field-bn.div-rem(power-of-two-bn)(\var{q-bn}, \var{r-bn}) = \var{field-bn.div-rem}(\var{power-of-two-bn}).
    3. Let q-num=ALLOCATE-UNCONSTRAINED-BIGNUM(q-bn)\var{q-num} = \gad{ALLOCATE-UNCONSTRAINED-BIGNUM}(\var{q-bn}).
    4. Let r-num=ALLOCATE-UNCONSTRAINED-BIGNUM(r-bn)\var{r-num} = \gad{ALLOCATE-UNCONSTRAINED-BIGNUM}(\var{r-bn}).
    5. Let pow2-size=field-pow(2,size) \var{pow2-size} = \fun{field-pow}(2, \var{size})~- computed as a field element.
    6. Call LINEAR(q-num,pow2-size,r-num,field-elem)\gad{LINEAR}(\var{q-num}, \var{pow2-size}, \var{r-num}, \var{field-elem}).
    7. Let r-bits=field-elem-bits[0..size]\var{r-bits} = \var{field-elem-bits}[0 \var{..size}].
    8. Call ENFORCE-PACK(r-bits,r-num)\gad{ENFORCE-PACK}(\var{r-bits}, \var{r-num}).

Next we present an auxiliary function that converts from num to unsigned integers by taking the least significant bits. The output is a pair of allocated numbers, where the first one corresponds to the u32 coercion, while the second corresponds to the u64 coercion.

  • TO-UNSIGNED-INTEGERS:\gad{TO-UNSIGNED-INTEGERS:} \\

  • INPUT num \cirio{INPUT} \var{num}.
    OUTPUT r32-num,r64-num \cirio{OUTPUT} \var{r32-num}, \var{r64-num}.
    1. Let field-bn=from-bytes-le(num)\var{field-bn} = \fun{from-bytes-le}(\var{num}).
    2. Let field-elem-bits=num.to-bits-le()\var{field-elem-bits} = \var{num.to-bits-le}().
    3. Let r32-num=TO-UNSIGNED-INTEGER-HELPER(maybe-unsigned,field-bn,field-elem-bits,32)\var{r32-num} = \gad{TO-UNSIGNED-INTEGER-HELPER}(\var{maybe-unsigned}, \var{field-bn}, \var{field-elem-bits}, 32).
    4. Let r64-num=TO-UNSIGNED-INTEGER-HELPER(maybe-unsigned,field-bn,field-elem-bits,64)\var{r64-num} = \gad{TO-UNSIGNED-INTEGER-HELPER}(\var{maybe-unsigned}, \var{field-bn}, \var{field-elem-bits}, 64).
    5. Return (r32-num,r64-num)(\var{r32-num}, \var{r64-num}).

Table 5: Comparisons gadgets summary
Gadget Constraints Witnesses
IS-NEGATIVE 389 388
COMPARISON-HELPER 1215 1208
TO-UNSIGNED-INTEGER-HELPER 2 2
TO-UNSIGNED-INTEGERS 360 259

Coercion

  • TO-U64:\gad{TO-U64:} \\

  • INPUT maybe-u64 \cirio{INPUT} \var{maybe-u64}.
    OUTPUT r64-num \cirio{OUTPUT} \var{r64-num}.
    1. Let field-bn=from-bytes-le(maybe-u64.to-bytes-le)\var{field-bn} = \fun{from-bytes-le}(\var{maybe-u64.to-bytes-le}).
    2. Let field-elem-bits=maybe-u64.to-bits-le()\var{field-elem-bits} = \var{maybe-u64.to-bits-le}().
    3. Let r64-num=TO-UNSIGNED-INTEGER-HELPER(maybe-unsigned,field-bn,field-elem-bits,64)?\var{r64-num} = \gad{TO-UNSIGNED-INTEGER-HELPER}(\var{maybe-unsigned}, \var{field-bn}, \var{field-elem-bits}, 64)?.
    4. Return (r64-num)(\var{r64-num}).

Next we enforce div and mod operation for U64. We need to show that arg1=q.arg2+r\var{arg1} = q.\var{arg2} + r, such that 0r<arg20 \leq r < \var{arg2}.

  • ENFORCE-U64-DIV-MOD:\gad{ENFORCE-U64-DIV-MOD:} \\

  • INPUT cond,arg1,arg2 \cirio{INPUT} \var{cond}, \var{arg1}, \var{arg2}.
    OUTPUT (alloc-q-num,alloc-r-num) \cirio{OUTPUT} (\var{alloc-q-num}, \var{alloc-r-num}).
    1. Let arg1-u64=arg1.to-u64-unchecked()\var{arg1-u64} = \var{arg1.to-u64-unchecked}().
    2. Let arg2-u64=arg2.to-u64-unchecked()\var{arg2-u64} = \var{arg2.to-u64-unchecked}().
    3. Let (q,r)=If arg2-u640 Return (arg1-u64/arg2-u64,arg1-u64(modarg2-u64)) Else Return (0,0) (q, r) = \fun{If}~ \var{arg2-u64} \neq 0 ~\fun{Return}~ {(\var{arg1-u64} / \var{arg2-u64}, \var{arg1-u64} \pmod{\var{arg2-u64}})} ~\fun{Else Return}~ {(0, 0)}~- If denominator is zero, replace by dummies.
    4. Let alloc-r-num=ALLOC(r)\var{alloc-r-num} = \gad{ALLOC}(r).
    5. Let alloc-q-num=ALLOC(q)\var{alloc-q-num} = \gad{ALLOC}(q).
    6. Let alloc-arg1-num=ALLOC(arg1-u64)\var{alloc-arg1-num} = \gad{ALLOC}(\var{arg1-u64}).
    7. Let alloc-arg2-num=ALLOC(arg2-u64)\var{alloc-arg2-num} = \gad{ALLOC}(\var{arg2-u64}).
    8. Let product-u64mod=MUL(alloc-q-num,alloc-arg2-num)\var{product-u64mod} = \gad{MUL}(\var{alloc-q-num}, \var{alloc-arg2-num}).
    9. Let sum-u64mod=ADD(product-u64mod,alloc-r-num)\var{sum-u64mod} = \gad{ADD}(\var{product-u64mod}, \var{alloc-r-num}).
    10. Let u64mod-decomp=ALLOC-EQUAL(sum-u64mod,alloc-arg1-num)\var{u64mod-decomp} = \gad{ALLOC-EQUAL}(\var{sum-u64mod}, \var{alloc-arg1-num}).
    11. Let b-is-zero=alloc-is-zero(arg2.hash())\var{b-is-zero} = \gad{alloc-is-zero}(\var{arg2.hash}()).
    12. Let b-is-not-zero-and-cond=AND(b-is-zero.NOT(),cond)\var{b-is-not-zero-and-cond} = \gad{AND}(\var{b-is-zero}.\gad{NOT}(), \var{cond}).
    13. Call ENFORCE-IMPLICATION(b-is-not-zero-and-cond,u64mod-decomp)\gad{ENFORCE-IMPLICATION}(\var{b-is-not-zero-and-cond}, \var{u64mod-decomp}).
    14. Call enforce-less-than-bound(cond,alloc-r-num,alloc-arg2-num)\gad{enforce-less-than-bound}(\var{cond}, \var{alloc-r-num}, \var{alloc-arg2-num}).
    15. Return (alloc-q-num,alloc-r-num)(\var{alloc-q-num}, \var{alloc-r-num}).

Given that cond\var{cond} is satisfied, next we enforce the num<bound\var{num} < \var{bound}. This is done by proving (boundnum)(\var{bound} - \var{num}) is positive. num\var{num} and bound\var{bound} must be a positive field element. cond\var{cond} is a Boolean condition that enforces the validation if and only if it is true.

  • ENFORCE-LESS-THAN-BOUND:\gad{ENFORCE-LESS-THAN-BOUND:} \\

  • INPUT cond,num,bound \cirio{INPUT} \var{cond}, \var{num}, \var{bound}.
    1. Let diff-bound-num=SUB(bound,num)\var{diff-bound-num} = \gad{SUB}(\var{bound}, \var{num}).
    2. Let diff-bound-num-is-negative=ALLOCATE-IS-NEGATIVE(diff-bound-num)\var{diff-bound-num-is-negative} = \gad{ALLOCATE-IS-NEGATIVE}(\var{diff-bound-num}).
    3. Call ENFORCE-IMPLICATION(cond,diff-bound-num-is-negative.NOT())\gad{ENFORCE-IMPLICATION}(\var{cond}, \var{diff-bound-num-is-negative}.\gad{NOT}()).

Next we convert from bn to num. This allocation is NOT constrained here. In the circuit we use it to prove u64 decomposition, since using bn we have division with remainder, which is used to find the quotient after dividing by 2ˆ64. Therefore we constrain this relation afterwards. In order to do that we use an external library for big number arithmetic, because in finite field we can´t compute the Euclidean division.

  • ALLOCATE-UNCONSTRAINED-BIGNUM:\cir{ALLOCATE-UNCONSTRAINED-BIGNUM:} \\

  • INPUT bn \cirio{INPUT} \var{bn}.
    OUTPUT num \cirio{OUTPUT} \var{num}.
    1. Let bytes-le=bn.to-bytes-le()\var{bytes-le} = \var{bn.to-bytes-le}().
    2. Pad bytes-le\var{bytes-le} with zeros, such that it has length 32.
    3. Let num=ALLOC(bytes-le)\var{num} = \gad{ALLOC}(\var{bytes-le}).
    4. Return num\var{num}.

Table 6: Bit decomposition gadgets summary
Gadget Constraints Witnesses
TO-U64\gad{TO-U64} 258 257
ENFORCE-U64-DIV-MOD\gad{ENFORCE-U64-DIV-MOD} 404 403
ENFORCE-LESS-THAN-BOUND\gad{ENFORCE-LESS-THAN-BOUND} 392 390
ALLOCATE-UNCONSTRAINED-BIGNUM\gad{ALLOCATE-UNCONSTRAINED-BIGNUM} 0 1

Bit decomposition

We use a gadget from the bellperson 22 library, called to-bits-le-strict(a)\fun{to-bits-le-strict}(a), in order to decompose field elements into bit representation.

to-bits-le-strict(a)\fun{to-bits-le-strict}(a) returns a vector of Booleans corresponding to the bits of aa using little endian representation.


Table 7: Bit decomposition gadgets summary
Gadget Constraints Witnesses
BIT-DECOMP-LE\gad{BIT-DECOMP-LE} 388 387

Equality

All operations described in this section are binary operations, a detailed description of which can be found in Apply continuation Section. Here, we explain how each operation is implemented. There is not a gadget construction for each of them, however. Their implementation is part of the binop and binop2 in Apply-Continuation\cir{Apply-Continuation}.

  • Number equality. This is easily implemented using ALLOC-EQUAL\gad{ALLOC-EQUAL}.
  • Equality of expressions (recursive). This is obtained by testing equality of hashes.

Conditionals

We use ternary operators to construct conditionals. The main building block is the gadget that can select a field element, given a boolean condition.

  • PICK-FIELD-ELEMENT(condition,a,b)\gad{PICK-FIELD-ELEMENT}(\var{condition}, a, b) If condition\var{condition} is true\var{true}, ensure res=a\var{res} = a. Otherwise, ensure res=b\var{res} = b.
    1. Let res\var{res} be
      • If condition\var{condition}, then let res=ALLOC(a)\var{res} = \gad{ALLOC}(a).
      • Otherwise, let res=ALLOC(b)\var{res} = \gad{ALLOC}(b).
    2. Constrain: (ba)×condition=(bres)(b – a) \times \var{condition}=(b – \var{res}).

In order to pick a pointer we define the next gadget.

  • PICK(condition,a,b):\gad{PICK}(\var{condition}, a, b):
    1. Let res-tag=PICK-FIELD-ELEMENT(condition,a.tag(),b.tag())\var{res-tag} = \gad{PICK-FIELD-ELEMENT}(\var{condition}, \var{a.tag}(), \var{b.tag}()).
    2. Let res-hash=PICK-FIELD-ELEMENT(condition,a.hash(),b.hash())\var{res-hash} = \gad{PICK-FIELD-ELEMENT}(\var{condition}, \var{a.hash}(), \var{b.hash}()).
    3. Return res=ALLOC-FROM-PARTS(res-tag,res-hash)\var{res} = \gad{ALLOC-FROM-PARTS}(\var{res-tag}, \var{res-hash}).

Table 8: Conditionals gadgets summary
Gadget Constraints Witnesses
PICK-FIELD-ELEMENT\gad{PICK-FIELD-ELEMENT} 1 1
PICK\gad{PICK} 2 2

Multicase

The multicase gadget is particularly important for Lurk, since it is a core part of Reduce-Cons()\cir{Reduce-Cons}() and Apply-Continuation()\cir{Apply-Continuation}(), which are essential components in reduction of expressions. In summary, a multicase is a combination of multiple case gadgets, eliminating common constraints.

A clause is given by a pair of field elements denoted by (key,value)(\var{key}, \var{value}). A case statement is given by a set of clauses where no repeated keys appear. Also, it has a default clause, which is used when no key satisfies the one given as input.

A multicase is a set of cases where the same sequence of keys is used for each case, including their order. This way, we can calculate a selector\var{selector} which will be applied for every case. We constrain the selector only once, avoiding unnecessary circuit growth. The strategy to enforce selection is the following:

  • Selector: allocate one bit per clause.
  • Test that if after adding all selectors we get 1, then exactly one is true, since each element is a bit.
  • Enforce selected key.

For the first case clauses, we calculate all the constraints a case gadget has, as follows:

  • Constrain: acc=i(keyiselected)\var{acc} = \prod_i{(key_i- \var{selected})}

such that acc\var{acc} is zero if and only if some key is selected.

  • is-selected=ALLOC-IS-ZERO(acc)\var{is-selected}= \gad{ALLOC-IS-ZERO}(\var{acc}).

Now, for the next cases, some constraints do not need to be repeated. We can proceed by computing the constraints of the result, by calculating the dot product of the selector and the values.

  • Let sum\var{sum} be initialized with zero.
  • For each clause cc:
    1. sum=sum+PICK(selector,c.value,0)\var{sum} = \var{sum} + \gad{PICK}(\var{selector}, c.\var{value}, 0).

Finally, we need to constrain the default result, which follows:

  • Let res\var{res} be:
    1. If is-selected\var{is-selected} is true, return sum\var{sum}.
    2. Otherwise, return default\var{default}.

The number of constraints for a case gadget is given by 7+4c7 + 4c, where cc corresponds to the number of clauses in it. Moreover, the number of witnesses is given by 11+4c11 + 4c.

For the multicase, we have that the number of constraint is cost-of-case+4(m1)\var{cost-of-case} + 4(m – 1), where mm is the number of cases. Furthermore, the number of witnesses is cost-of-case+5(m1)\var{cost-of-case} + 5(m – 1).

Final Remarks

In this document, we presented Lurk’s circuit specification, demonstrating how Lurk programs are proved in zero-knowledge. The total size of the circuit (as of January 2023) is 12513 constraints and 12140 witnesses. Being able to reduce generic Lurk expressions to such a small frame size allows us to use recursive SNARKs efficiently. In particular, we plan to integrate Nova folding techniques to obtain further performance improvements.

The reader wanting more information about Lurk will find several references below, including the Lurk evaluation specification 23 and the Lurk reduction notes 24.

References

Footnotes

  1. Shafi Goldwasser, Silvio Micali, and Charles Rackoff. The knowledge complexity of interactive proof-systems. In STOC 1985, pages 291–304, 1985

  2. Benarroch, D., Gurkan, K., Kahat, R., Nicolas, A., & Tromer, E. (2019). zkInterface, a standard tool for zero-knowledge interoperability.

  3. Rosario Gennaro, Craig Gentry, Bryan Parno, and Mariana Raykova. Quadratic span programs and succinct NIZKs without PCPs.

  4. Bryan Parno, Jon Howell, Craig Gentry, and Mariana Raykova. Pinocchio: Nearly practical verifiable computation

  5. Eli Ben-Sasson, Alessandro Chiesa, Daniel Genkin, Eran Tromer, and Madars Virza. SNARKs for C: Verifying program executions succinctly and in zero knowledge, Cryptology ePrint Archive, Report 2013/507, 2013.

  6. Eli Ben-Sasson, Alessandro Chiesa, Eran Tromer, and Madars Virza. Succinct non-interactive zero knowledge for a von Neumann architecture. In USENIX Security 2014, pages 781–796, 2014.

  7. Jens Groth. On the size of pairing-based non-interactive arguments. In proc. Eurocrypt ’16, Part II, pages 305–326, 2016. 2 3 4

  8. Yuri Gurevich, Evolving Algebras, IFIP 1994.

  9. Kothapalli, A., Setty, S., Tzialla, I. (2022 ) Nova: Recursive Zero-Knowledge Arguments from Folding Schemes, Cryptology ePrint Archive, Report 2021/370, 2021. https://ia.cr/2021/370. 2 3 4

  10. Gailly, N., Maller, M., Nitulescu, A., SnarkPack: Practical SNARK Aggregation, Cryptology ePrint Archive, Paper 2021/529.

  11. A good source of information on CPS is the book “Essentials of Programming Languages” 25

  12. Sean Bowe, Jack Grigg, and Daira Hopwood. Recursive proof composition without a trusted setup. Cryptology ePrint Archive, Report 2019/1021, 2019. https://ia.cr/2019/1021.

  13. Lorenzo Grassi, Dmitry Khovratovich, Christian Rechberger, Arnab Roy, and Markus Schofnegger. Poseidon: A new hash function for zero-knowledge proof systems. Cryptology ePrint Archive, Report 2019/458, 2019. https://ia.cr/2019/458. 2

  14. Hopwood, Daira, et al. “Zcash protocol specification.” version 2022.3.8, 2016 §5.4.9.6

  15. Virza, Madars, On deploying succinct zero-knowledge proofs, https://dspace.mit.edu/handle/1721.1/113986

  16. SAFE. github, 2022. https://safe-hash.dev

  17. Neptune: reference Poseidon implementation. github, 2021. https://github.com/filecoin-project/neptune/

  18. Benoît Libert, Somindu C. Ramanna, and Moti Yung. Functional commitment schemes: From polynomial commitments to pairing-based accumulators from simple assumptions. Cryptology ePrint Archive, Paper 2016/766, 2016. https://eprint.iacr.org/2016/766.

  19. Helger Lipmaa and Kateryna Pavlyk. Succinct functional commitment for a large class of arithmetic circuits. Cryptology ePrint Archive, Paper 2021/932, 2021. https://eprint.iacr.org/2021/932.

  20. Chris Peikert, Zachary Pepin, and Chad Sharp. Vector and functional commitments from lattices. Cryptology ePrint Archive, Paper 2021/1254, 2021. https://eprint.iacr.org/2021/1254.

  21. Dan Boneh, Wilson Nguyen, and Alex Ozdemir. Efficient functional commitments: How to commit to a private function. Cryptology ePrint Archive, Paper 2021/1342, 2021. https://eprint.iacr.org/2021/1342

  22. Bellperson implementation. github, 2021. https://github.com/filecoin-project/bellperson

  23. Lurk evaluation notes. github, 2022. https://github.com/lurk-lab/lurk-rs/blob/master/notes/eval.md

  24. Lurk reduction notes. github, 2022. https://github.com/lurk-lang/lurk-rs/blob/master/notes/reduction-notes.md

  25. Daniel P. Friedman and Mitchell Wand. Essentials of Programming Languages, 3rd Edition. The MIT Press, 3 edition, 2008.