Introducing Achronyme — a language for zero-knowledge proofs. Read the announcement

Operators & Costs

Arithmetic, comparison, and logical operators with their constraint costs.

Every operation in a circuit compiles to zero or more constraints. Understanding costs helps you write efficient circuits — fewer constraints mean faster proof generation and smaller proofs.

Arithmetic Operators

OperatorExampleConstraintsNotes
+a + b0Linear combination
-a - b0Linear combination
* (by constant)a * 30Scalar multiplication of LC
* (two variables)a * b1Multiplication gate
/ (by constant)a / 30Multiply by inverse
/ (two variables)a / b2Inverse + multiplication
^ (exponentiation)x ^ nO(log n)Square-and-multiply
- (negation)-a0Negate LC coefficients
circuit exponent(x2: Public, x3: Public, x4: Public, x: Witness) {
    assert_eq(x ^ 2, x2)
    assert_eq(x ^ 3, x3)
    assert_eq(x ^ 4, x4)
}

Comparison Operators

OperatorExampleConstraintsNotes
==a == b2IsZero gadget
!=a != b2IsZero gadget + negate
<a < b~760253-bit decomposition + range checks
<=a <= b~760253-bit decomposition + range checks
>a > b~760Same as b < a
>=a >= b~760Same as b <= a

Equality checks are cheap (2 constraints). Ordering comparisons are expensive (~760 constraints each) because they require bit decomposition over the BN254 field. Use them sparingly.

circuit compare(x: Witness, y: Witness) {
    let eq = x == y
    let lt = x < y
    assert(lt)
}

Logical Operators

OperatorExampleConstraintsNotes
&&a && b32 boolean enforcements + 1 multiplication
||a || b32 boolean enforcements + 1 gate
!!a0–1Free if operand is already proven boolean
circuit logical(x: Witness, y: Witness) {
    let both = (x < y) && (y > x)
    assert(both)

    let either = (x == y) || (x < y)
    assert(either)

    let not_eq = !(x == y)
    assert(not_eq)
}

Why Some Operations Are Free

Addition and subtraction are free because they operate on linear combinations — weighted sums of variables. The R1CS constraint system represents wires as linear combinations, so adding two LCs just merges their terms without creating a new constraint.

Multiplication by a constant is also free — it scales all coefficients in the LC. Only multiplication of two variable expressions requires a constraint gate (A * B = C).

Optimization: Boolean Propagation

The compiler’s bool_prop pass tracks which variables are already proven to be boolean (0 or 1). When a value produced by ==, <, or another comparison feeds into &&, ||, or !, the redundant boolean enforcement is skipped, saving constraints.

For example, !(x == y) costs 2 constraints for the == and 0 for the !, because the output of == is already known to be boolean.

The pass recognizes these sources as proven-boolean:

  • Constants 0 and 1
  • Comparison results (==, !=, <, <=, >, >=)
  • RangeCheck(x, 1) results (including those from : Bool enforcement)
  • Assert operands and results
  • Variables with Bool type annotation (from declarations)
  • Not/And/Or/Mux of proven-boolean operands

Type annotations extend this optimization: declaring witness flag: Bool marks the variable as proven boolean, saving 1 constraint every time it’s used in a boolean context (e.g., as a mux condition). Annotating : Bool on an untyped value (via let, function params, or return types) emits a one-time RangeCheck enforcement (+1 constraint), after which the value is also tracked as proven-boolean for downstream savings.

Navigation