2.4. Basic operators

Here you will find the list of operators you can use in circom.

The field F_p

Circom provides boolean, arithmetic and bitwise operators. They have the standard semantics but the arithmetic operators applied to numeric values work modulo p , where

p = 21888242871839275222246405745257275088548364400416034343698204186575808495617

Positive and negative numbers in F_p

In the field F_p we have all numbers from the set {0,...,p-1} and with them we can do additions and multiplications. The existence of additive inverses allow us to define positive and negative elements. Formally, we define the function val(z) such that:

  • val(z) = z-p , if p/2 + 1 =< z < p,

  • val(z) = z, otherwise.

The idea is simple: the first half of numbers {0,...,(p+1)/2} are positive and the second half becomes the set of negative numbers{-(p+1)/2,...,-1}. See the following table for the finite field F_7:

Field element z

val(z)

0

0

1

1

2

2

3

3

4

-3

5

-2

6

-1

👉 The consideration of positive and negative becomes particularly important when using relational operators. For example, in the finite field F_7, the element 5 is greater than 1 as absolute numbers, but if we consider the sign of the elements, then 1 is greater than 5, since 1 > -2.

Conditional expressions

Boolean_condition ? true_value : false_value

var z = (x > y) ? x : y

This conditional expression is not allowed in a nested form, hence can only be used at the top level. See more about conditional expressions in the Control flow section.

Boolean operators

Symbol

Meaning

&&

AND

||

OR

!

NOT

Relational operators

Symbol

Meaning

<

Less than mod p (see below)

>

Greater than mod p (see below)

<=

Less or equal than mod p (see below)

>=

Greater or equal than mod p (see below)

==

Equal mod p

!=

Not equal mod p

The relational operators take into account the sign of the elements in F_p. Accordingly, their precise definition is the following:

  • x < y is defined as val(x mod p) < val(y mod p)

  • x > y is defined as val(x mod p) > val(y mod p)

  • x <= y is defined as val(x mod p) <= val(y mod p)

  • x >= y is defined as val(x mod p) >= val(y mod p)

Arithmetic operations

Symbol

Meaning

+

Addition (both binary and unary) mod p

-

Subtraction (both binary and unary) mod p

*

Product mod p

/

Multiplication by the inverse and then mod p

**

Power mod p

\

Quotient of integer division

%

Reminder of integer division

Bitwise operators

Symbol

Meaning

&

AND mod p

|

OR mod p

~

Complement (254 bits) mod p

^

XOR (254 bits) mod p

>>

Shift right mod p (see below)

<<

Shift left mod p (see below)

Shifts

  • x >> k : shifts k bits to the right x

  • x << k : shifts k bits to the left x

The precise definition of shift operations is the following:

  • For all k with 0 =< k =< p/2 (integer division), we have that

    • x >> k = x\(2**k)

    • x << k = (x*(2**k) & (2**b - 1)) mod p, where b is the number of significant bits of p.

  • For all k with (p\2 + 1) =< k < p we have that

    • x >> k = x << (p-k)

    • x << k = x >> (p-k)

    Note that k is also the negative number k-p.

Expressions can be built using all these operators, provided that the conditional operator only occurs at the top level.

Operators with assignment

Additionally, there are operators that combine arithmetic or bitwise operators with a final assignment:

+= , -= , *= , /= , **=, \=, %= , ^= , |= , &= , >>=, <<=.

We also have the operators ++ and -- for unit increment and decrement, respectively. They are equivalent to the operations +=1 and -=1.

Precedence and association of operators

The precedence and association of the operators is like the ones in Rust, as defined here.