halo2_base/gates/flex_gate/
mod.rs

1use crate::{
2    halo2_proofs::{
3        plonk::{
4            Advice, Assigned, Column, ConstraintSystem, FirstPhase, Fixed, SecondPhase, Selector,
5            ThirdPhase,
6        },
7        poly::Rotation,
8    },
9    utils::ScalarField,
10    AssignedValue, Context,
11    QuantumCell::{self, Constant, Existing, Witness, WitnessFraction},
12};
13use itertools::Itertools;
14use serde::{Deserialize, Serialize};
15use std::{
16    iter::{self},
17    marker::PhantomData,
18};
19
20pub mod threads;
21
22/// Vector of thread advice column break points
23pub type ThreadBreakPoints = Vec<usize>;
24/// Vector of vectors tracking the thread break points across different halo2 phases
25pub type MultiPhaseThreadBreakPoints = Vec<ThreadBreakPoints>;
26
27/// The maximum number of phases in halo2.
28pub(super) const MAX_PHASE: usize = 3;
29
30/// # Vertical Gate Strategy:
31/// `q_0 * (a + b * c - d) = 0`
32/// where
33/// * `a = value[0], b = value[1], c = value[2], d = value[3]`
34/// * `q = q_enable[0]`
35/// * `q` is either 0 or 1 so this is just a simple selector
36///
37/// We chose `a + b * c` instead of `a * b + c` to allow "chaining" of gates, i.e., the output of one gate because `a` in the next gate.
38///
39/// A configuration for a basic gate chip describing the selector, and advice column values.
40#[derive(Clone, Debug)]
41pub struct BasicGateConfig<F: ScalarField> {
42    /// [Selector] column that stores selector values that are used to activate gates in the advice column.
43    // `q_enable` will have either length 1 or 2, depending on the strategy
44    pub q_enable: Selector,
45    /// [Column] that stores the advice values of the gate.
46    pub value: Column<Advice>,
47    /// Marker for the field type.
48    _marker: PhantomData<F>,
49}
50
51impl<F: ScalarField> BasicGateConfig<F> {
52    /// Constructor
53    pub fn new(q_enable: Selector, value: Column<Advice>) -> Self {
54        Self { q_enable, value, _marker: PhantomData }
55    }
56
57    /// Instantiates a new [BasicGateConfig].
58    ///
59    /// Assumes `phase` is in the range [0, MAX_PHASE).
60    /// * `meta`: [ConstraintSystem] used for the gate
61    /// * `phase`: The phase to add the gate to
62    pub fn configure(meta: &mut ConstraintSystem<F>, phase: u8) -> Self {
63        let value = match phase {
64            0 => meta.advice_column_in(FirstPhase),
65            1 => meta.advice_column_in(SecondPhase),
66            2 => meta.advice_column_in(ThirdPhase),
67            _ => panic!("Currently BasicGate only supports {MAX_PHASE} phases"),
68        };
69        meta.enable_equality(value);
70
71        let q_enable = meta.selector();
72
73        let config = Self { q_enable, value, _marker: PhantomData };
74        config.create_gate(meta);
75        config
76    }
77
78    /// Wrapper for [ConstraintSystem].create_gate(name, meta) creates a gate form [q * (a + b * c - out)].
79    /// * `meta`: [ConstraintSystem] used for the gate
80    fn create_gate(&self, meta: &mut ConstraintSystem<F>) {
81        meta.create_gate("1 column a + b * c = out", |meta| {
82            let q = meta.query_selector(self.q_enable);
83
84            let a = meta.query_advice(self.value, Rotation::cur());
85            let b = meta.query_advice(self.value, Rotation::next());
86            let c = meta.query_advice(self.value, Rotation(2));
87            let out = meta.query_advice(self.value, Rotation(3));
88
89            vec![q * (a + b * c - out)]
90        })
91    }
92}
93
94/// A Config struct defining the parameters for [FlexGateConfig]
95#[derive(Clone, Default, Debug, Serialize, Deserialize)]
96pub struct FlexGateConfigParams {
97    /// Specifies the number of rows in the circuit to be 2<sup>k</sup>
98    pub k: usize,
99    /// The number of advice columns per phase
100    pub num_advice_per_phase: Vec<usize>,
101    /// The number of fixed columns
102    pub num_fixed: usize,
103}
104
105/// Defines a configuration for a flex gate chip describing the selector, and advice column values for the chip.
106#[derive(Clone, Debug)]
107pub struct FlexGateConfig<F: ScalarField> {
108    /// A [Vec] of [BasicGateConfig] that define gates for each halo2 phase.
109    pub basic_gates: Vec<Vec<BasicGateConfig<F>>>,
110    /// A [Vec] of [Fixed] [Column]s for allocating constant values.
111    pub constants: Vec<Column<Fixed>>,
112    /// Max number of usable rows in the circuit.
113    pub max_rows: usize,
114}
115
116impl<F: ScalarField> FlexGateConfig<F> {
117    /// Generates a new [FlexGateConfig]
118    ///
119    /// * `meta`: [ConstraintSystem] of the circuit
120    /// * `params`: see [FlexGateConfigParams]
121    pub fn configure(meta: &mut ConstraintSystem<F>, params: FlexGateConfigParams) -> Self {
122        // create fixed (constant) columns and enable equality constraints
123        let mut constants = Vec::with_capacity(params.num_fixed);
124        for _i in 0..params.num_fixed {
125            let c = meta.fixed_column();
126            meta.enable_equality(c);
127            // meta.enable_constant(c);
128            constants.push(c);
129        }
130
131        let mut basic_gates = vec![];
132        for (phase, &num_columns) in params.num_advice_per_phase.iter().enumerate() {
133            let config =
134                (0..num_columns).map(|_| BasicGateConfig::configure(meta, phase as u8)).collect();
135            basic_gates.push(config);
136        }
137        log::info!("Poisoned rows after FlexGateConfig::configure {}", meta.minimum_rows());
138        Self {
139            basic_gates,
140            constants,
141            // Warning: this needs to be updated if you create more advice columns after this `FlexGateConfig` is created
142            max_rows: (1 << params.k) - meta.minimum_rows(),
143        }
144    }
145}
146
147/// Trait that defines basic arithmetic operations for a gate.
148pub trait GateInstructions<F: ScalarField> {
149    /// Returns a slice of the [ScalarField] field elements 2^i for i in 0..F::NUM_BITS.
150    fn pow_of_two(&self) -> &[F];
151
152    /// Constrains and returns `a + b * 1 = out`.
153    ///
154    /// Defines a vertical gate of form | a | b | 1 | a + b | where (a + b) = out.
155    /// * `ctx`: [Context] to add the constraints to
156    /// * `a`: [QuantumCell] value
157    /// * `b`: [QuantumCell] value to add to 'a`
158    fn add(
159        &self,
160        ctx: &mut Context<F>,
161        a: impl Into<QuantumCell<F>>,
162        b: impl Into<QuantumCell<F>>,
163    ) -> AssignedValue<F> {
164        let a = a.into();
165        let b = b.into();
166        let out_val = *a.value() + b.value();
167        ctx.assign_region_last([a, b, Constant(F::ONE), Witness(out_val)], [0])
168    }
169
170    /// Constrains and returns `out = a + 1`.
171    ///
172    /// * `ctx`: [Context] to add the constraints to
173    /// * `a`: [QuantumCell] value
174    fn inc(&self, ctx: &mut Context<F>, a: impl Into<QuantumCell<F>>) -> AssignedValue<F> {
175        self.add(ctx, a, Constant(F::ONE))
176    }
177
178    /// Constrains and returns `a + b * (-1) = out`.
179    ///
180    /// Defines a vertical gate of form | a - b | b | 1 | a |, where (a - b) = out.
181    /// * `ctx`: [Context] to add the constraints to
182    /// * `a`: [QuantumCell] value
183    /// * `b`: [QuantumCell] value to subtract from 'a'
184    fn sub(
185        &self,
186        ctx: &mut Context<F>,
187        a: impl Into<QuantumCell<F>>,
188        b: impl Into<QuantumCell<F>>,
189    ) -> AssignedValue<F> {
190        let a = a.into();
191        let b = b.into();
192        let out_val = *a.value() - b.value();
193        // slightly better to not have to compute -F::ONE since F::ONE is cached
194        ctx.assign_region([Witness(out_val), b, Constant(F::ONE), a], [0]);
195        ctx.get(-4)
196    }
197
198    /// Constrains and returns `out = a - 1`.
199    ///
200    /// * `ctx`: [Context] to add the constraints to
201    /// * `a`: [QuantumCell] value
202    fn dec(&self, ctx: &mut Context<F>, a: impl Into<QuantumCell<F>>) -> AssignedValue<F> {
203        self.sub(ctx, a, Constant(F::ONE))
204    }
205
206    /// Constrains and returns  `a - b * c = out`.
207    ///
208    /// Defines a vertical gate of form | a - b * c | b | c | a |, where (a - b * c) = out.
209    /// * `ctx`: [Context] to add the constraints to
210    /// * `a`: [QuantumCell] value to subtract 'b * c' from
211    /// * `b`: [QuantumCell] value
212    /// * `c`: [QuantumCell] value
213    fn sub_mul(
214        &self,
215        ctx: &mut Context<F>,
216        a: impl Into<QuantumCell<F>>,
217        b: impl Into<QuantumCell<F>>,
218        c: impl Into<QuantumCell<F>>,
219    ) -> AssignedValue<F> {
220        let a = a.into();
221        let b = b.into();
222        let c = c.into();
223        let out_val = *a.value() - *b.value() * c.value();
224        ctx.assign_region_last([Witness(out_val), b, c, a], [0]);
225        ctx.get(-4)
226    }
227
228    /// Constrains and returns `a * (-1) = out`.
229    ///
230    /// Defines a vertical gate of form | a | -a | 1 | 0 |, where (-a) = out.
231    /// * `ctx`: the [Context] to add the constraints to
232    /// * `a`: [QuantumCell] value to negate
233    fn neg(&self, ctx: &mut Context<F>, a: impl Into<QuantumCell<F>>) -> AssignedValue<F> {
234        let a = a.into();
235        let out_val = -*a.value();
236        ctx.assign_region([a, Witness(out_val), Constant(F::ONE), Constant(F::ZERO)], [0]);
237        ctx.get(-3)
238    }
239
240    /// Constrains and returns  `0 + a * b = out`.
241    ///
242    /// Defines a vertical gate of form | 0 | a | b | a * b |, where (a * b) = out.
243    /// * `ctx`: [Context] to add the constraints to
244    /// * `a`: [QuantumCell] value
245    /// * `b`: [QuantumCell] value to multiply 'a' by
246    fn mul(
247        &self,
248        ctx: &mut Context<F>,
249        a: impl Into<QuantumCell<F>>,
250        b: impl Into<QuantumCell<F>>,
251    ) -> AssignedValue<F> {
252        let a = a.into();
253        let b = b.into();
254        let out_val = *a.value() * b.value();
255        ctx.assign_region_last([Constant(F::ZERO), a, b, Witness(out_val)], [0])
256    }
257
258    /// Constrains and returns  `a * b + c = out`.
259    ///
260    /// Defines a vertical gate of form | c | a | b | a * b + c |, where (a * b + c) = out.
261    /// * `ctx`: [Context] to add the constraints to
262    /// * `a`: [QuantumCell] value
263    /// * `b`: [QuantumCell] value to multiply 'a' by
264    /// * `c`: [QuantumCell] value to add to 'a * b'
265    fn mul_add(
266        &self,
267        ctx: &mut Context<F>,
268        a: impl Into<QuantumCell<F>>,
269        b: impl Into<QuantumCell<F>>,
270        c: impl Into<QuantumCell<F>>,
271    ) -> AssignedValue<F> {
272        let a = a.into();
273        let b = b.into();
274        let c = c.into();
275        let out_val = *a.value() * b.value() + c.value();
276        ctx.assign_region_last([c, a, b, Witness(out_val)], [0])
277    }
278
279    /// Constrains and returns `(1 - a) * b = b - a * b`.
280    ///
281    /// Defines a vertical gate of form | (1 - a) * b | a | b | b |, where (1 - a) * b = out.
282    /// * `ctx`: [Context] to add the constraints to
283    /// * `a`: [QuantumCell] value
284    /// * `b`: [QuantumCell] value to multiply 'a' by
285    fn mul_not(
286        &self,
287        ctx: &mut Context<F>,
288        a: impl Into<QuantumCell<F>>,
289        b: impl Into<QuantumCell<F>>,
290    ) -> AssignedValue<F> {
291        let a = a.into();
292        let b = b.into();
293        let out_val = (F::ONE - a.value()) * b.value();
294        ctx.assign_region_smart([Witness(out_val), a, b, b], [0], [(2, 3)], []);
295        ctx.get(-4)
296    }
297
298    /// Constrains that x is boolean (e.g. 0 or 1).
299    ///
300    /// Defines a vertical gate of form | 0 | x | x | x |.
301    /// * `ctx`: [Context] to add the constraints to
302    /// * `x`: [QuantumCell] value to constrain
303    fn assert_bit(&self, ctx: &mut Context<F>, x: AssignedValue<F>) {
304        ctx.assign_region([Constant(F::ZERO), Existing(x), Existing(x), Existing(x)], [0]);
305    }
306
307    /// Constrains and returns a / b = out.
308    ///
309    /// Defines a vertical gate of form | 0 | a / b | b | a |, where a / b = out.
310    ///
311    /// Assumes `b != 0`.
312    /// * `ctx`: [Context] to add the constraints to
313    /// * `a`: [QuantumCell] value
314    /// * `b`: [QuantumCell] value to divide 'a' by
315    fn div_unsafe(
316        &self,
317        ctx: &mut Context<F>,
318        a: impl Into<QuantumCell<F>>,
319        b: impl Into<QuantumCell<F>>,
320    ) -> AssignedValue<F> {
321        let a = a.into();
322        let b = b.into();
323        // TODO: if really necessary, make `c` of type `Assigned<F>`
324        // this would require the API using `Assigned<F>` instead of `F` everywhere, so leave as last resort
325        let c = b.value().invert().unwrap() * a.value();
326        ctx.assign_region([Constant(F::ZERO), Witness(c), b, a], [0]);
327        ctx.get(-3)
328    }
329
330    /// Constrains that `a` is equal to `constant` value.
331    /// * `ctx`: [Context] to add the constraints to
332    /// * `a`: [QuantumCell] value
333    /// * `constant`: constant value to constrain `a` to be equal to
334    fn assert_is_const(&self, ctx: &mut Context<F>, a: &AssignedValue<F>, constant: &F) {
335        if !ctx.witness_gen_only {
336            ctx.copy_manager.lock().unwrap().constant_equalities.push((*constant, a.cell.unwrap()));
337        }
338    }
339
340    /// Constrains and returns the inner product of `<a, b>`.
341    ///
342    /// Assumes 'a' and 'b' are the same length.
343    /// * `ctx`: [Context] to add the constraints to
344    /// * `a`: Iterator of [QuantumCell] values
345    /// * `b`: Iterator of [QuantumCell] values to take inner product of `a` by
346    fn inner_product<QA>(
347        &self,
348        ctx: &mut Context<F>,
349        a: impl IntoIterator<Item = QA>,
350        b: impl IntoIterator<Item = QuantumCell<F>>,
351    ) -> AssignedValue<F>
352    where
353        QA: Into<QuantumCell<F>>;
354
355    /// Returns the inner product of `<a, b>` and the last element of `a` after it has been assigned.
356    ///
357    /// **NOT** encouraged for general usage.
358    /// This is a low-level function, where you want to avoid first assigning `a` and then copying the last element into the
359    /// correct cell for this computation.
360    ///
361    /// Assumes 'a' and 'b' are the same length.
362    /// * `ctx`: [Context] of the circuit
363    /// * `a`: Iterator of [QuantumCell]s
364    /// * `b`: Iterator of [QuantumCell]s to take inner product of `a` by
365    fn inner_product_left_last<QA>(
366        &self,
367        ctx: &mut Context<F>,
368        a: impl IntoIterator<Item = QA>,
369        b: impl IntoIterator<Item = QuantumCell<F>>,
370    ) -> (AssignedValue<F>, AssignedValue<F>)
371    where
372        QA: Into<QuantumCell<F>>;
373
374    /// Returns `(<a,b>, a_assigned)`. See `inner_product` for more details.
375    ///
376    /// **NOT** encouraged for general usage.
377    /// This is a low-level function, useful for when you want to simultaneously compute an inner product while assigning
378    /// private witnesses for the first time. This avoids first assigning `a` and then copying into the correct cells
379    /// for this computation. We do not return the assignments of `a` in `inner_product` as an optimization to avoid
380    /// the memory allocation of having to collect the vectors.
381    ///
382    /// Assumes 'a' and 'b' are the same length.
383    fn inner_product_left<QA>(
384        &self,
385        ctx: &mut Context<F>,
386        a: impl IntoIterator<Item = QA>,
387        b: impl IntoIterator<Item = QuantumCell<F>>,
388    ) -> (AssignedValue<F>, Vec<AssignedValue<F>>)
389    where
390        QA: Into<QuantumCell<F>>;
391
392    /// Calculates and constrains the inner product.
393    ///
394    /// Returns the assignment trace where `output[i]` has the running sum `sum_{j=0..=i} a[j] * b[j]`.
395    ///
396    /// Assumes 'a' and 'b' are the same length.
397    /// * `ctx`: [Context] to add the constraints to
398    /// * `a`: Iterator of [QuantumCell] values
399    /// * `b`: Iterator of [QuantumCell] values to calculate the partial sums of the inner product of `a` by.
400    fn inner_product_with_sums<'thread, QA>(
401        &self,
402        ctx: &'thread mut Context<F>,
403        a: impl IntoIterator<Item = QA>,
404        b: impl IntoIterator<Item = QuantumCell<F>>,
405    ) -> Box<dyn Iterator<Item = AssignedValue<F>> + 'thread>
406    where
407        QA: Into<QuantumCell<F>>;
408
409    /// Constrains and returns the sum of [QuantumCell]'s in iterator `a`.
410    /// * `ctx`: [Context] to add the constraints to
411    /// * `a`: Iterator of [QuantumCell] values to sum
412    fn sum<Q>(&self, ctx: &mut Context<F>, a: impl IntoIterator<Item = Q>) -> AssignedValue<F>
413    where
414        Q: Into<QuantumCell<F>>,
415    {
416        let mut a = a.into_iter().peekable();
417        let start = a.next();
418        if start.is_none() {
419            return ctx.load_zero();
420        }
421        let start = start.unwrap().into();
422        if a.peek().is_none() {
423            return ctx.assign_region_last([start], []);
424        }
425        let (len, hi) = a.size_hint();
426        assert_eq!(Some(len), hi);
427
428        let mut sum = *start.value();
429        let cells = iter::once(start).chain(a.flat_map(|a| {
430            let a = a.into();
431            sum += a.value();
432            [a, Constant(F::ONE), Witness(sum)]
433        }));
434        ctx.assign_region_last(cells, (0..len).map(|i| 3 * i as isize))
435    }
436
437    /// Calculates and constrains the sum of the elements of `a`.
438    ///
439    /// Returns the assignment trace where `output[i]` has the running sum `sum_{j=0..=i} a[j]`.
440    /// * `ctx`: [Context] to add the constraints to
441    /// * `a`: Iterator of [QuantumCell] values to sum
442    fn partial_sums<'thread, Q>(
443        &self,
444        ctx: &'thread mut Context<F>,
445        a: impl IntoIterator<Item = Q>,
446    ) -> Box<dyn Iterator<Item = AssignedValue<F>> + 'thread>
447    where
448        Q: Into<QuantumCell<F>>,
449    {
450        let mut a = a.into_iter().peekable();
451        let start = a.next();
452        if start.is_none() {
453            return Box::new(iter::once(ctx.load_zero()));
454        }
455        let start = start.unwrap().into();
456        if a.peek().is_none() {
457            return Box::new(iter::once(ctx.assign_region_last([start], [])));
458        }
459        let (len, hi) = a.size_hint();
460        assert_eq!(Some(len), hi);
461
462        let mut sum = *start.value();
463        let cells = iter::once(start).chain(a.flat_map(|a| {
464            let a = a.into();
465            sum += a.value();
466            [a, Constant(F::ONE), Witness(sum)]
467        }));
468        ctx.assign_region(cells, (0..len).map(|i| 3 * i as isize));
469        Box::new((0..=len).rev().map(|i| ctx.get(-1 - 3 * (i as isize))))
470    }
471
472    /// Calculates and constrains the accumulated product of 'a' and 'b' i.e. `x_i = b_1 * (a_1...a_{i - 1})
473    ///     + b_2 * (a_2...a_{i - 1})
474    ///     + ...
475    ///     + b_i`
476    ///
477    /// Returns the assignment trace where `output[i]` is the running accumulated product x_i.
478    ///
479    /// Assumes 'a' and 'b' are the same length.
480    /// * `ctx`: [Context] to add the constraints to
481    /// * `a`: Iterator of [QuantumCell] values
482    /// * `b`: Iterator of [QuantumCell] values to take the accumulated product of `a` by
483    fn accumulated_product<QA, QB>(
484        &self,
485        ctx: &mut Context<F>,
486        a: impl IntoIterator<Item = QA>,
487        b: impl IntoIterator<Item = QB>,
488    ) -> Vec<AssignedValue<F>>
489    where
490        QA: Into<QuantumCell<F>>,
491        QB: Into<QuantumCell<F>>,
492    {
493        let mut b = b.into_iter();
494        let mut a = a.into_iter();
495        let b_first = b.next();
496        if let Some(b_first) = b_first {
497            let b_first = ctx.assign_region_last([b_first], []);
498            std::iter::successors(Some(b_first), |x| {
499                a.next().zip(b.next()).map(|(a, b)| self.mul_add(ctx, Existing(*x), a, b))
500            })
501            .collect()
502        } else {
503            vec![]
504        }
505    }
506
507    /// Constrains and returns the sum of products of `coeff * (a * b)` defined in `values` plus a variable `var` e.g.
508    /// `x = var + values[0].0 * (values[0].1 * values[0].2) + values[1].0 * (values[1].1 * values[1].2) + ... + values[n].0 * (values[n].1 * values[n].2)`.
509    /// * `ctx`: [Context] to add the constraints to.
510    /// * `values`: Iterator of tuples `(coeff, a, b)` where `coeff` is a field element, `a` and `b` are [QuantumCell]'s.
511    /// * `var`: [QuantumCell] that represents the value of a variable added to the sum.
512    fn sum_products_with_coeff_and_var(
513        &self,
514        ctx: &mut Context<F>,
515        values: impl IntoIterator<Item = (F, QuantumCell<F>, QuantumCell<F>)>,
516        var: QuantumCell<F>,
517    ) -> AssignedValue<F>;
518
519    /// Constrains and returns `a || b`, assuming `a` and `b` are boolean.
520    ///
521    /// Defines a vertical gate of form `| 1 - b | 1 | b | 1 | b | a | 1 - b | out |`, where `out = a + b - a * b`.
522    /// * `ctx`: [Context] to add the constraints to.
523    /// * `a`: [QuantumCell] that contains a boolean value.
524    /// * `b`: [QuantumCell] that contains a boolean value.
525    fn or(
526        &self,
527        ctx: &mut Context<F>,
528        a: impl Into<QuantumCell<F>>,
529        b: impl Into<QuantumCell<F>>,
530    ) -> AssignedValue<F> {
531        let a = a.into();
532        let b = b.into();
533        let not_b_val = F::ONE - b.value();
534        let out_val = *a.value() + b.value() - *a.value() * b.value();
535        let cells = [
536            Witness(not_b_val),
537            Constant(F::ONE),
538            b,
539            Constant(F::ONE),
540            b,
541            a,
542            Witness(not_b_val),
543            Witness(out_val),
544        ];
545        ctx.assign_region_smart(cells, [0, 4], [(0, 6), (2, 4)], []);
546        ctx.last().unwrap()
547    }
548
549    /// Constrains and returns `a & b`, assumeing `a` and `b` are boolean.
550    ///
551    /// Defines a vertical gate of form | 0 | a | b | out |, where out = a * b.
552    /// * `ctx`: [Context] to add the constraints to.
553    /// * `a`: [QuantumCell] that contains a boolean value.
554    /// * `b`: [QuantumCell] that contains a boolean value.
555    fn and(
556        &self,
557        ctx: &mut Context<F>,
558        a: impl Into<QuantumCell<F>>,
559        b: impl Into<QuantumCell<F>>,
560    ) -> AssignedValue<F> {
561        self.mul(ctx, a, b)
562    }
563
564    /// Constrains and returns `!a` assumeing `a` is boolean.
565    ///
566    /// Defines a vertical gate of form | 1 - a | a | 1 | 1 |, where 1 - a = out.
567    /// * `ctx`: [Context] to add the constraints to.
568    /// * `a`: [QuantumCell] that contains a boolean value.
569    fn not(&self, ctx: &mut Context<F>, a: impl Into<QuantumCell<F>>) -> AssignedValue<F> {
570        self.sub(ctx, Constant(F::ONE), a)
571    }
572
573    /// Constrains and returns `sel ? a : b` assuming `sel` is boolean.
574    ///
575    /// Defines a vertical gate of form `| 1 - sel | sel | 1 | a | 1 - sel | sel | 1 | b | out |`, where out = sel * a + (1 - sel) * b.
576    /// * `ctx`: [Context] to add the constraints to.
577    /// * `a`: [QuantumCell] that contains a boolean value.
578    /// * `b`: [QuantumCell] that contains a boolean value.
579    /// * `sel`: [QuantumCell] that contains a boolean value.
580    fn select(
581        &self,
582        ctx: &mut Context<F>,
583        a: impl Into<QuantumCell<F>>,
584        b: impl Into<QuantumCell<F>>,
585        sel: impl Into<QuantumCell<F>>,
586    ) -> AssignedValue<F>;
587
588    /// Constains and returns `a || (b && c)`, assuming `a`, `b` and `c` are boolean.
589    ///
590    /// Defines a vertical gate of form `| 1 - b c | b | c | 1 | a - 1 | 1 - b c | out | a - 1 | 1 | 1 | a |`, where out = a + b * c - a * b * c.
591    /// * `ctx`: [Context] to add the constraints to.
592    /// * `a`: [QuantumCell] that contains a boolean value.
593    /// * `b`: [QuantumCell] that contains a boolean value.
594    /// * `c`: [QuantumCell] that contains a boolean value.
595    fn or_and(
596        &self,
597        ctx: &mut Context<F>,
598        a: impl Into<QuantumCell<F>>,
599        b: impl Into<QuantumCell<F>>,
600        c: impl Into<QuantumCell<F>>,
601    ) -> AssignedValue<F>;
602
603    /// Constrains and returns an indicator vector from a slice of boolean values, where `output[idx] = 1` iff idx = (the number represented by `bits` in binary little endian), otherwise `output[idx] = 0`.
604    /// * `ctx`: [Context] to add the constraints to
605    /// * `bits`: slice of [QuantumCell]'s that contains boolean values
606    ///
607    /// # Assumptions
608    /// * `bits` is non-empty
609    fn bits_to_indicator(
610        &self,
611        ctx: &mut Context<F>,
612        bits: &[AssignedValue<F>],
613    ) -> Vec<AssignedValue<F>> {
614        let k = bits.len();
615        assert!(k > 0, "bits_to_indicator: bits must be non-empty");
616
617        // (inv_last_bit, last_bit) = (1, 0) if bits[k - 1] = 0
618        let (inv_last_bit, last_bit) = {
619            ctx.assign_region(
620                [
621                    Witness(F::ONE - bits[k - 1].value()),
622                    Existing(bits[k - 1]),
623                    Constant(F::ONE),
624                    Constant(F::ONE),
625                ],
626                [0],
627            );
628            (ctx.get(-4), ctx.get(-3))
629        };
630        let mut indicator = Vec::with_capacity(2 * (1 << k) - 2);
631        let mut offset = 0;
632        indicator.push(inv_last_bit);
633        indicator.push(last_bit);
634        for (idx, bit) in bits.iter().rev().enumerate().skip(1) {
635            for old_idx in 0..(1 << idx) {
636                // inv_prod_val = (1 - bit) * indicator[offset + old_idx]
637                let inv_prod_val = (F::ONE - bit.value()) * indicator[offset + old_idx].value();
638                ctx.assign_region(
639                    [
640                        Witness(inv_prod_val),
641                        Existing(indicator[offset + old_idx]),
642                        Existing(*bit),
643                        Existing(indicator[offset + old_idx]),
644                    ],
645                    [0],
646                );
647                indicator.push(ctx.get(-4));
648
649                // prod = bit * indicator[offset + old_idx]
650                let prod = self.mul(ctx, Existing(indicator[offset + old_idx]), Existing(*bit));
651                indicator.push(prod);
652            }
653            offset += 1 << idx;
654        }
655        indicator.split_off((1 << k) - 2)
656    }
657
658    /// Constrains and returns a [Vec] `indicator` of length `len`, where `indicator[i] == 1 if i == idx otherwise 0`, if `idx >= len` then `indicator` is all zeros.
659    ///
660    /// Assumes `len` is greater than 0.
661    /// * `ctx`: [Context] to add the constraints to
662    /// * `idx`: [QuantumCell]  index of the indicator vector to be set to 1
663    /// * `len`: length of the `indicator` vector
664    fn idx_to_indicator(
665        &self,
666        ctx: &mut Context<F>,
667        idx: impl Into<QuantumCell<F>>,
668        len: usize,
669    ) -> Vec<AssignedValue<F>> {
670        let mut idx = idx.into();
671        (0..len)
672            .map(|i| {
673                // need to use assigned idx after i > 0 so equality constraint holds
674                if i == 0 {
675                    // unroll `is_zero` to make sure if `idx == Witness(_)` it is replaced by `Existing(_)` in later iterations
676                    let x = idx.value();
677                    let (is_zero, inv) = if x.is_zero_vartime() {
678                        (F::ONE, Assigned::Trivial(F::ONE))
679                    } else {
680                        (F::ZERO, Assigned::Rational(F::ONE, *x))
681                    };
682                    let cells = [
683                        Witness(is_zero),
684                        idx,
685                        WitnessFraction(inv),
686                        Constant(F::ONE),
687                        Constant(F::ZERO),
688                        idx,
689                        Witness(is_zero),
690                        Constant(F::ZERO),
691                    ];
692                    ctx.assign_region_smart(cells, [0, 4], [(0, 6), (1, 5)], []); // note the two `idx` need to be constrained equal: (1, 5)
693                    idx = Existing(ctx.get(-3)); // replacing `idx` with Existing cell so future loop iterations constrain equality of all `idx`s
694                    ctx.get(-2)
695                } else {
696                    self.is_equal(ctx, idx, Constant(F::from(i as u64)))
697                }
698            })
699            .collect()
700    }
701
702    /// Constrains the inner product of `a` and `indicator` and returns `a[idx]` (e.g. the value of `a` at `idx`).
703    ///
704    /// Assumes that `a` and `indicator` are non-empty iterators of the same length, the values of `indicator` are boolean,
705    /// and that `indicator` has at most one `1` bit.
706    /// * `ctx`: [Context] to add the constraints to
707    /// * `a`: Iterator of [QuantumCell]'s that contains field elements
708    /// * `indicator`: Iterator of [AssignedValue]'s where `indicator[i] == 1` if `i == idx`, otherwise `0`
709    fn select_by_indicator<Q>(
710        &self,
711        ctx: &mut Context<F>,
712        a: impl IntoIterator<Item = Q>,
713        indicator: impl IntoIterator<Item = AssignedValue<F>>,
714    ) -> AssignedValue<F>
715    where
716        Q: Into<QuantumCell<F>>,
717    {
718        let mut sum = F::ZERO;
719        let a = a.into_iter();
720        let (len, hi) = a.size_hint();
721        assert_eq!(Some(len), hi);
722
723        let cells =
724            std::iter::once(Constant(F::ZERO)).chain(a.zip(indicator).flat_map(|(a, ind)| {
725                let a = a.into();
726                sum = if ind.value().is_zero_vartime() { sum } else { *a.value() };
727                [a, Existing(ind), Witness(sum)]
728            }));
729        ctx.assign_region_last(cells, (0..len).map(|i| 3 * i as isize))
730    }
731
732    /// Constrains and returns `cells[idx]` if `idx < cells.len()`, otherwise return 0.
733    ///
734    /// Assumes that `cells` and `idx` are non-empty iterators of the same length.
735    /// * `ctx`: [Context] to add the constraints to
736    /// * `cells`: Iterator of [QuantumCell]s to select from
737    /// * `idx`: [QuantumCell] with value `idx` where `idx` is the index of the cell to be selected
738    fn select_from_idx<Q>(
739        &self,
740        ctx: &mut Context<F>,
741        cells: impl IntoIterator<Item = Q>,
742        idx: impl Into<QuantumCell<F>>,
743    ) -> AssignedValue<F>
744    where
745        Q: Into<QuantumCell<F>>,
746    {
747        let cells = cells.into_iter();
748        let (len, hi) = cells.size_hint();
749        assert_eq!(Some(len), hi);
750
751        let ind = self.idx_to_indicator(ctx, idx, len);
752        self.select_by_indicator(ctx, cells, ind)
753    }
754
755    /// `array2d` is an array of fixed length arrays.
756    /// Assumes:
757    /// * `array2d.len() == indicator.len()`
758    /// * `array2d[i].len() == array2d[j].len()` for all `i,j`.
759    /// * the values of `indicator` are boolean and that `indicator` has at most one `1` bit.
760    /// * the lengths of `array2d` and `indicator` are the same.
761    ///
762    /// Returns the "dot product" of `array2d` with `indicator` as a fixed length (1d) array of length `array2d[0].len()`.
763    fn select_array_by_indicator<AR, AV>(
764        &self,
765        ctx: &mut Context<F>,
766        array2d: &[AR],
767        indicator: &[AssignedValue<F>],
768    ) -> Vec<AssignedValue<F>>
769    where
770        AR: AsRef<[AV]>,
771        AV: AsRef<AssignedValue<F>>,
772    {
773        (0..array2d[0].as_ref().len())
774            .map(|j| {
775                self.select_by_indicator(
776                    ctx,
777                    array2d.iter().map(|array_i| *array_i.as_ref()[j].as_ref()),
778                    indicator.iter().copied(),
779                )
780            })
781            .collect()
782    }
783
784    /// Constrains that a cell is equal to 0 and returns `1` if `a = 0`, otherwise `0`.
785    ///
786    /// Defines a vertical gate of form `| out | a | inv | 1 | 0 | a | out | 0 |`, where out = 1 if a = 0, otherwise out = 0.
787    /// * `ctx`: [Context] to add the constraints to
788    /// * `a`: [QuantumCell] value to be constrained
789    fn is_zero(&self, ctx: &mut Context<F>, a: AssignedValue<F>) -> AssignedValue<F> {
790        let x = a.value();
791        let (is_zero, inv) = if x.is_zero_vartime() {
792            (F::ONE, Assigned::Trivial(F::ONE))
793        } else {
794            (F::ZERO, Assigned::Rational(F::ONE, *x))
795        };
796
797        let cells = [
798            Witness(is_zero),
799            Existing(a),
800            WitnessFraction(inv),
801            Constant(F::ONE),
802            Constant(F::ZERO),
803            Existing(a),
804            Witness(is_zero),
805            Constant(F::ZERO),
806        ];
807        ctx.assign_region_smart(cells, [0, 4], [(0, 6)], []);
808        ctx.get(-2)
809    }
810
811    /// Constrains that the value of two cells are equal: b - a = 0, returns `1` if `a = b`, otherwise `0`.
812    /// * `ctx`: [Context] to add the constraints to
813    /// * `a`: [QuantumCell] value
814    /// * `b`: [QuantumCell] value to compare to `a`
815    fn is_equal(
816        &self,
817        ctx: &mut Context<F>,
818        a: impl Into<QuantumCell<F>>,
819        b: impl Into<QuantumCell<F>>,
820    ) -> AssignedValue<F> {
821        let diff = self.sub(ctx, a, b);
822        self.is_zero(ctx, diff)
823    }
824
825    /// Constrains and returns little-endian bit vector representation of `a`.
826    ///
827    /// Assumes `range_bits >= bit_length(a)`.
828    /// * `a`: [QuantumCell] of the value to convert
829    /// * `range_bits`: range of bits needed to represent `a`
830    fn num_to_bits(
831        &self,
832        ctx: &mut Context<F>,
833        a: AssignedValue<F>,
834        range_bits: usize,
835    ) -> Vec<AssignedValue<F>>;
836
837    /// Constrains and computes `a`<sup>`exp`</sup> where both `a, exp` are witnesses. The exponent is computed in the native field `F`.
838    ///
839    /// Constrains that `exp` has at most `max_bits` bits.
840    fn pow_var(
841        &self,
842        ctx: &mut Context<F>,
843        a: AssignedValue<F>,
844        exp: AssignedValue<F>,
845        max_bits: usize,
846    ) -> AssignedValue<F>;
847
848    /// Performs and constrains Lagrange interpolation on `coords` and evaluates the resulting polynomial at `x`.
849    ///
850    /// Given pairs `coords[i] = (x_i, y_i)`, let `f` be the unique degree `len(coords) - 1` polynomial such that `f(x_i) = y_i` for all `i`.
851    ///
852    /// Returns:
853    /// (f(x), Prod_i(x - x_i))
854    /// * `ctx`: [Context] to add the constraints to
855    /// * `coords`: immutable reference to a slice of tuples of [AssignedValue]s representing the points to interpolate over such that `coords[i] = (x_i, y_i)`
856    /// * `x`: x-coordinate of the point to evaluate `f` at
857    ///
858    /// # Assumptions
859    /// * `coords` is non-empty
860    fn lagrange_and_eval(
861        &self,
862        ctx: &mut Context<F>,
863        coords: &[(AssignedValue<F>, AssignedValue<F>)],
864        x: AssignedValue<F>,
865    ) -> (AssignedValue<F>, AssignedValue<F>) {
866        assert!(!coords.is_empty(), "coords should not be empty");
867        let mut z = self.sub(ctx, Existing(x), Existing(coords[0].0));
868        for coord in coords.iter().skip(1) {
869            let sub = self.sub(ctx, Existing(x), Existing(coord.0));
870            z = self.mul(ctx, Existing(z), Existing(sub));
871        }
872        let mut eval = None;
873        for i in 0..coords.len() {
874            // compute (x - x_i) * Prod_{j != i} (x_i - x_j)
875            let mut denom = self.sub(ctx, Existing(x), Existing(coords[i].0));
876            for j in 0..coords.len() {
877                if i == j {
878                    continue;
879                }
880                let sub = self.sub(ctx, coords[i].0, coords[j].0);
881                denom = self.mul(ctx, denom, sub);
882            }
883            // TODO: batch inversion
884            let is_zero = self.is_zero(ctx, denom);
885            self.assert_is_const(ctx, &is_zero, &F::ZERO);
886
887            // y_i / denom
888            let quot = self.div_unsafe(ctx, coords[i].1, denom);
889            eval = if let Some(eval) = eval {
890                let eval = self.add(ctx, eval, quot);
891                Some(eval)
892            } else {
893                Some(quot)
894            };
895        }
896        let out = self.mul(ctx, eval.unwrap(), z);
897        (out, z)
898    }
899}
900
901/// A chip that implements the [GateInstructions] trait supporting basic arithmetic operations.
902#[derive(Clone, Debug)]
903pub struct GateChip<F: ScalarField> {
904    /// The field elements 2^i for i in 0..F::NUM_BITS.
905    pub pow_of_two: Vec<F>,
906    /// To avoid Montgomery conversion in `F::from` for common small numbers, we keep a cache of field elements.
907    pub field_element_cache: Vec<F>,
908}
909
910impl<F: ScalarField> Default for GateChip<F> {
911    fn default() -> Self {
912        Self::new()
913    }
914}
915
916impl<F: ScalarField> GateChip<F> {
917    /// Returns a new [GateChip] with some precomputed values. This can be called out of circuit and has no extra dependencies.
918    pub fn new() -> Self {
919        let mut pow_of_two = Vec::with_capacity(F::NUM_BITS as usize);
920        let two = F::from(2);
921        pow_of_two.push(F::ONE);
922        pow_of_two.push(two);
923        for _ in 2..F::NUM_BITS {
924            pow_of_two.push(two * pow_of_two.last().unwrap());
925        }
926        let field_element_cache = (0..1024).map(|i| F::from(i)).collect();
927
928        Self { pow_of_two, field_element_cache }
929    }
930
931    /// Calculates and constrains the inner product of `<a, b>`.
932    /// If the first element of `b` is `Constant(F::ONE)`, then an optimization is performed to save 3 cells.
933    ///
934    /// Returns `true` if `b` start with `Constant(F::ONE)`, and `false` otherwise.
935    ///
936    /// Assumes `a` and `b` are the same length.
937    /// * `ctx`: [Context] of the circuit
938    /// * `a`: Iterator of [QuantumCell] values
939    /// * `b`: Iterator of [QuantumCell] values to take inner product of `a` by
940    fn inner_product_simple<QA>(
941        &self,
942        ctx: &mut Context<F>,
943        a: impl IntoIterator<Item = QA>,
944        b: impl IntoIterator<Item = QuantumCell<F>>,
945    ) -> bool
946    where
947        QA: Into<QuantumCell<F>>,
948    {
949        let mut sum;
950        let mut a = a.into_iter();
951        let mut b = b.into_iter().peekable();
952
953        let b_starts_with_one = matches!(b.peek(), Some(Constant(c)) if c == &F::ONE);
954        let cells = if b_starts_with_one {
955            b.next();
956            let start_a = a.next().unwrap().into();
957            sum = *start_a.value();
958            iter::once(start_a)
959        } else {
960            sum = F::ZERO;
961            iter::once(Constant(F::ZERO))
962        }
963        .chain(a.zip(b).flat_map(|(a, b)| {
964            let a = a.into();
965            sum += *a.value() * b.value();
966            [a, b, Witness(sum)]
967        }));
968
969        if ctx.witness_gen_only() {
970            ctx.assign_region(cells, vec![]);
971        } else {
972            let cells = cells.collect::<Vec<_>>();
973            let lo = cells.len();
974            let len = lo / 3;
975            ctx.assign_region(cells, (0..len).map(|i| 3 * i as isize));
976        };
977        b_starts_with_one
978    }
979}
980
981impl<F: ScalarField> GateInstructions<F> for GateChip<F> {
982    /// Returns a slice of the [ScalarField] elements 2<sup>i</sup> for i in 0..F::NUM_BITS.
983    fn pow_of_two(&self) -> &[F] {
984        &self.pow_of_two
985    }
986
987    /// Constrains and returns the inner product of `<a, b>`.
988    /// If the first element of `b` is `Constant(F::ONE)`, then an optimization is performed to save 3 cells.
989    ///
990    /// Assumes 'a' and 'b' are the same length.
991    /// * `ctx`: [Context] to add the constraints to
992    /// * `a`: Iterator of [QuantumCell] values
993    /// * `b`: Iterator of [QuantumCell] values to take inner product of `a` by
994    fn inner_product<QA>(
995        &self,
996        ctx: &mut Context<F>,
997        a: impl IntoIterator<Item = QA>,
998        b: impl IntoIterator<Item = QuantumCell<F>>,
999    ) -> AssignedValue<F>
1000    where
1001        QA: Into<QuantumCell<F>>,
1002    {
1003        self.inner_product_simple(ctx, a, b);
1004        ctx.last().unwrap()
1005    }
1006
1007    /// Returns the inner product of `<a, b>` and the last element of `a` after it has been assigned.
1008    ///
1009    /// **NOT** encouraged for general usage.
1010    /// This is a low-level function, where you want to avoid first assigning `a` and then copying the last element into the
1011    /// correct cell for this computation.
1012    ///
1013    /// Assumes 'a' and 'b' are the same length.
1014    /// * `ctx`: [Context] of the circuit
1015    /// * `a`: Iterator of [QuantumCell]s
1016    /// * `b`: Iterator of [QuantumCell]s to take inner product of `a` by
1017    fn inner_product_left_last<QA>(
1018        &self,
1019        ctx: &mut Context<F>,
1020        a: impl IntoIterator<Item = QA>,
1021        b: impl IntoIterator<Item = QuantumCell<F>>,
1022    ) -> (AssignedValue<F>, AssignedValue<F>)
1023    where
1024        QA: Into<QuantumCell<F>>,
1025    {
1026        let a = a.into_iter();
1027        let (len, hi) = a.size_hint();
1028        assert_eq!(Some(len), hi);
1029        let row_offset = ctx.advice.len();
1030        let b_starts_with_one = self.inner_product_simple(ctx, a, b);
1031        let a_last = if b_starts_with_one {
1032            if len == 1 {
1033                ctx.get(row_offset as isize)
1034            } else {
1035                ctx.get((row_offset + 1 + 3 * (len - 2)) as isize)
1036            }
1037        } else {
1038            ctx.get((row_offset + 1 + 3 * (len - 1)) as isize)
1039        };
1040        (ctx.last().unwrap(), a_last)
1041    }
1042
1043    /// Returns `(<a,b>, a_assigned)`. See `inner_product` for more details.
1044    ///
1045    /// **NOT** encouraged for general usage.
1046    /// This is a low-level function, useful for when you want to simultaneously compute an inner product while assigning
1047    /// private witnesses for the first time. This avoids first assigning `a` and then copying into the correct cells
1048    /// for this computation. We do not return the assignments of `a` in `inner_product` as an optimization to avoid
1049    /// the memory allocation of having to collect the vectors.
1050    ///
1051    /// We do not return `b_assigned` because if `b` starts with `Constant(F::ONE)`, the first element of `b` is not assigned.
1052    ///
1053    /// Assumes 'a' and 'b' are the same length.
1054    fn inner_product_left<QA>(
1055        &self,
1056        ctx: &mut Context<F>,
1057        a: impl IntoIterator<Item = QA>,
1058        b: impl IntoIterator<Item = QuantumCell<F>>,
1059    ) -> (AssignedValue<F>, Vec<AssignedValue<F>>)
1060    where
1061        QA: Into<QuantumCell<F>>,
1062    {
1063        let a = a.into_iter().collect_vec();
1064        let len = a.len();
1065        let row_offset = ctx.advice.len();
1066        let b_starts_with_one = self.inner_product_simple(ctx, a, b);
1067        let a_assigned = (0..len)
1068            .map(|i| {
1069                if b_starts_with_one {
1070                    if i == 0 {
1071                        ctx.get(row_offset as isize)
1072                    } else {
1073                        ctx.get((row_offset + 1 + 3 * (i - 1)) as isize)
1074                    }
1075                } else {
1076                    ctx.get((row_offset + 1 + 3 * i) as isize)
1077                }
1078            })
1079            .collect_vec();
1080        (ctx.last().unwrap(), a_assigned)
1081    }
1082
1083    /// Calculates and constrains the inner product.
1084    ///
1085    /// Returns the assignment trace where `output[i]` has the running sum `sum_{j=0..=i} a[j] * b[j]`.
1086    ///
1087    /// Assumes 'a' and 'b' are the same length.
1088    /// * `ctx`: [Context] to add the constraints to
1089    /// * `a`: Iterator of [QuantumCell] values
1090    /// * `b`: Iterator of [QuantumCell] values to calculate the partial sums of the inner product of `a` by
1091    fn inner_product_with_sums<'thread, QA>(
1092        &self,
1093        ctx: &'thread mut Context<F>,
1094        a: impl IntoIterator<Item = QA>,
1095        b: impl IntoIterator<Item = QuantumCell<F>>,
1096    ) -> Box<dyn Iterator<Item = AssignedValue<F>> + 'thread>
1097    where
1098        QA: Into<QuantumCell<F>>,
1099    {
1100        let row_offset = ctx.advice.len();
1101        let b_starts_with_one = self.inner_product_simple(ctx, a, b);
1102        if b_starts_with_one {
1103            Box::new((row_offset..ctx.advice.len()).step_by(3).map(|i| ctx.get(i as isize)))
1104        } else {
1105            // in this case the first assignment is 0 so we skip it
1106            Box::new((row_offset..ctx.advice.len()).step_by(3).skip(1).map(|i| ctx.get(i as isize)))
1107        }
1108    }
1109
1110    /// Constrains and returns the sum of products of `coeff * (a * b)` defined in `values` plus a variable `var` e.g.
1111    /// `x = var + values[0].0 * (values[0].1 * values[0].2) + values[1].0 * (values[1].1 * values[1].2) + ... + values[n].0 * (values[n].1 * values[n].2)`.
1112    /// * `ctx`: [Context] to add the constraints to
1113    /// * `values`: Iterator of tuples `(coeff, a, b)` where `coeff` is a field element, `a` and `b` are [QuantumCell]'s
1114    /// * `var`: [QuantumCell] that represents the value of a variable added to the sum
1115    fn sum_products_with_coeff_and_var(
1116        &self,
1117        ctx: &mut Context<F>,
1118        values: impl IntoIterator<Item = (F, QuantumCell<F>, QuantumCell<F>)>,
1119        var: QuantumCell<F>,
1120    ) -> AssignedValue<F> {
1121        // Create an iterator starting with `var` and
1122        let (a, b): (Vec<_>, Vec<_>) = std::iter::once((var, Constant(F::ONE)))
1123            .chain(values.into_iter().filter_map(|(c, va, vb)| {
1124                if c == F::ONE {
1125                    Some((va, vb))
1126                } else if c != F::ZERO {
1127                    let prod = self.mul(ctx, va, vb);
1128                    Some((QuantumCell::Existing(prod), Constant(c)))
1129                } else {
1130                    None
1131                }
1132            }))
1133            .unzip();
1134        self.inner_product(ctx, a, b)
1135    }
1136
1137    /// Constrains and returns `sel ? a : b` assuming `sel` is boolean.
1138    ///
1139    /// Defines a vertical gate of form `| 1 - sel | sel | 1 | a | 1 - sel | sel | 1 | b | out |`, where out = sel * a + (1 - sel) * b.
1140    /// * `ctx`: [Context] to add the constraints to
1141    /// * `a`: [QuantumCell] that contains a boolean value
1142    /// * `b`: [QuantumCell] that contains a boolean value
1143    /// * `sel`: [QuantumCell] that contains a boolean value
1144    fn select(
1145        &self,
1146        ctx: &mut Context<F>,
1147        a: impl Into<QuantumCell<F>>,
1148        b: impl Into<QuantumCell<F>>,
1149        sel: impl Into<QuantumCell<F>>,
1150    ) -> AssignedValue<F> {
1151        let a = a.into();
1152        let b = b.into();
1153        let sel = sel.into();
1154        let diff_val = *a.value() - b.value();
1155        let out_val = diff_val * sel.value() + b.value();
1156        // | a - b | 1 | b | a |
1157        // | b | sel | a - b | out |
1158        let cells = [
1159            Witness(diff_val),
1160            Constant(F::ONE),
1161            b,
1162            a,
1163            b,
1164            sel,
1165            Witness(diff_val),
1166            Witness(out_val),
1167        ];
1168        ctx.assign_region_smart(cells, [0, 4], [(0, 6), (2, 4)], []);
1169        ctx.last().unwrap()
1170    }
1171
1172    /// Constains and returns `a || (b && c)`, assuming `a`, `b` and `c` are boolean.
1173    ///
1174    /// Defines a vertical gate of form `| 1 - b c | b | c | 1 | a - 1 | 1 - b c | out | a - 1 | 1 | 1 | a |`, where out = a + b * c - a * b * c.
1175    /// * `ctx`: [Context] to add the constraints to
1176    /// * `a`: [QuantumCell] that contains a boolean value
1177    /// * `b`: [QuantumCell] that contains a boolean value
1178    /// * `c`: [QuantumCell] that contains a boolean value
1179    fn or_and(
1180        &self,
1181        ctx: &mut Context<F>,
1182        a: impl Into<QuantumCell<F>>,
1183        b: impl Into<QuantumCell<F>>,
1184        c: impl Into<QuantumCell<F>>,
1185    ) -> AssignedValue<F> {
1186        let a = a.into();
1187        let b = b.into();
1188        let c = c.into();
1189        let bc_val = *b.value() * c.value();
1190        let not_bc_val = F::ONE - bc_val;
1191        let not_a_val = *a.value() - F::ONE;
1192        let out_val = bc_val + a.value() - bc_val * a.value();
1193        let cells = [
1194            Witness(not_bc_val),
1195            b,
1196            c,
1197            Constant(F::ONE),
1198            Witness(not_a_val),
1199            Witness(not_bc_val),
1200            Witness(out_val),
1201            Witness(not_a_val),
1202            Constant(F::ONE),
1203            Constant(F::ONE),
1204            a,
1205        ];
1206        ctx.assign_region_smart(cells, [0, 3, 7], [(4, 7), (0, 5)], []);
1207        ctx.get(-5)
1208    }
1209
1210    /// Constrains and returns little-endian bit vector representation of `a`.
1211    ///
1212    /// Assumes `range_bits >= number of bits in a`.
1213    /// * `a`: [QuantumCell] of the value to convert
1214    /// * `range_bits`: range of bits needed to represent `a`. Assumes `range_bits > 0`.
1215    fn num_to_bits(
1216        &self,
1217        ctx: &mut Context<F>,
1218        a: AssignedValue<F>,
1219        range_bits: usize,
1220    ) -> Vec<AssignedValue<F>> {
1221        let bits = a.value().to_u64_limbs(range_bits, 1).into_iter().map(|x| Witness(F::from(x)));
1222
1223        let mut bit_cells = Vec::with_capacity(range_bits);
1224        let row_offset = ctx.advice.len();
1225        let acc = self.inner_product(
1226            ctx,
1227            bits,
1228            self.pow_of_two[..range_bits].iter().map(|c| Constant(*c)),
1229        );
1230        ctx.constrain_equal(&a, &acc);
1231        debug_assert!(range_bits > 0);
1232        bit_cells.push(ctx.get(row_offset as isize));
1233        for i in 1..range_bits {
1234            bit_cells.push(ctx.get((row_offset + 1 + 3 * (i - 1)) as isize));
1235        }
1236
1237        for bit_cell in &bit_cells {
1238            self.assert_bit(ctx, *bit_cell);
1239        }
1240        bit_cells
1241    }
1242
1243    /// Constrains and computes `a^exp` where both `a, exp` are witnesses. The exponent is computed in the native field `F`.
1244    ///
1245    /// Constrains that `exp` has at most `max_bits` bits.
1246    fn pow_var(
1247        &self,
1248        ctx: &mut Context<F>,
1249        a: AssignedValue<F>,
1250        exp: AssignedValue<F>,
1251        max_bits: usize,
1252    ) -> AssignedValue<F> {
1253        let exp_bits = self.num_to_bits(ctx, exp, max_bits);
1254        // standard square-and-mul approach
1255        let mut acc = ctx.load_constant(F::ONE);
1256        for (i, bit) in exp_bits.into_iter().rev().enumerate() {
1257            if i > 0 {
1258                // square
1259                acc = self.mul(ctx, acc, acc);
1260            }
1261            let mul = self.mul(ctx, acc, a);
1262            acc = self.select(ctx, mul, acc, bit);
1263        }
1264        acc
1265    }
1266}