Skip to main content

halo2_base/gates/range/
mod.rs

1use crate::{
2    gates::flex_gate::{FlexGateConfig, GateInstructions, MAX_PHASE},
3    halo2_proofs::{
4        circuit::{Layouter, Value},
5        plonk::{
6            Advice, Column, ConstraintSystem, Error, SecondPhase, Selector, TableColumn, ThirdPhase,
7        },
8        poly::Rotation,
9    },
10    utils::{
11        biguint_to_fe, bit_length, decompose_fe_to_u64_limbs, fe_to_biguint, modulus,
12        BigPrimeField, ScalarField,
13    },
14    virtual_region::lookups::LookupAnyManager,
15    AssignedValue, Context,
16    QuantumCell::{self, Constant, Existing, Witness},
17};
18
19use super::flex_gate::{FlexGateConfigParams, GateChip};
20
21use getset::Getters;
22use num_bigint::BigUint;
23use num_integer::Integer;
24use num_traits::{One, Zero};
25use std::{cmp::Ordering, ops::Shl};
26
27fn assert_div_mod_no_wrap<F: BigPrimeField>(b: &BigUint, a_num_bits: usize) {
28    assert!(!b.is_zero());
29    assert!(a_num_bits <= F::CAPACITY as usize);
30
31    let native_modulus = modulus::<F>();
32    assert!(b < &native_modulus);
33
34    let div_bound = BigUint::one().shl(a_num_bits as u32) / b + BigUint::one();
35    let max_div = div_bound - BigUint::one();
36    let max_lhs = b * max_div + (b - BigUint::one());
37    assert!(max_lhs < native_modulus);
38}
39
40/// Configuration for Range Chip
41#[derive(Clone, Debug)]
42pub struct RangeConfig<F: ScalarField> {
43    /// Underlying Gate Configuration
44    pub gate: FlexGateConfig<F>,
45    /// Special advice (witness) Columns used only for lookup tables.
46    ///
47    /// Each phase of a halo2 circuit has a distinct lookup_advice column.
48    ///
49    /// * If `gate` has only 1 advice column, lookups are enabled for that column, in which case `lookup_advice` is empty
50    /// * If `gate` has more than 1 advice column some number of user-specified `lookup_advice` columns are added
51    ///     * In this case, we don't need a selector so `q_lookup` is empty
52    pub lookup_advice: Vec<Vec<Column<Advice>>>,
53    /// Selector values for the lookup table.
54    pub q_lookup: Vec<Option<Selector>>,
55    /// Column for lookup table values.
56    pub lookup: TableColumn,
57    /// Defines the number of bits represented in the lookup table [0,2^<sup>lookup_bits</sup>).
58    lookup_bits: usize,
59}
60
61impl<F: ScalarField> RangeConfig<F> {
62    /// Generates a new [RangeConfig] with the specified parameters.
63    ///
64    /// If `num_columns` is 0, then we assume you do not want to perform any lookups in that phase.
65    ///
66    /// Panics if `lookup_bits` > 28.
67    /// * `meta`: [ConstraintSystem] of the circuit
68    /// * `gate_params`: see [FlexGateConfigParams]
69    /// * `num_lookup_advice`: Number of `lookup_advice` [Column]s in each phase
70    /// * `lookup_bits`: Number of bits represented in the LookUp table [0,2^lookup_bits)
71    pub fn configure(
72        meta: &mut ConstraintSystem<F>,
73        gate_params: FlexGateConfigParams,
74        num_lookup_advice: &[usize],
75        lookup_bits: usize,
76    ) -> Self {
77        assert!(lookup_bits <= F::S as usize);
78        // sanity check: only create lookup table if there are lookup_advice columns
79        assert!(!num_lookup_advice.is_empty(), "You are creating a RangeConfig but don't seem to need a lookup table, please double-check if you're using lookups correctly. Consider setting lookup_bits = None in BaseConfigParams");
80
81        let lookup = meta.lookup_table_column();
82
83        let gate = FlexGateConfig::configure(meta, gate_params.clone());
84
85        // For now, we apply the same range lookup table to each phase
86        let mut q_lookup = Vec::new();
87        let mut lookup_advice = Vec::new();
88        for (phase, &num_columns) in num_lookup_advice.iter().enumerate() {
89            let num_advice = *gate_params.num_advice_per_phase.get(phase).unwrap_or(&0);
90            let mut columns = Vec::new();
91            // If num_columns is set to 0, then we assume you do not want to perform any lookups in that phase.
92            // Disable this optimization in phase > 0 because you might set selectors based a cell from other columns.
93            if phase == 0 && num_advice == 1 && num_columns != 0 {
94                q_lookup.push(Some(meta.complex_selector()));
95            } else {
96                q_lookup.push(None);
97                for _ in 0..num_columns {
98                    let a = match phase {
99                        0 => meta.advice_column(),
100                        1 => meta.advice_column_in(SecondPhase),
101                        2 => meta.advice_column_in(ThirdPhase),
102                        _ => panic!("Currently RangeConfig only supports {MAX_PHASE} phases"),
103                    };
104                    meta.enable_equality(a);
105                    columns.push(a);
106                }
107            }
108            lookup_advice.push(columns);
109        }
110
111        let mut config = Self { lookup_advice, q_lookup, lookup, lookup_bits, gate };
112        config.create_lookup(meta);
113
114        log::debug!("Poisoned rows after RangeConfig::configure {}", meta.minimum_rows());
115        config.gate.max_rows = (1 << gate_params.k) - meta.minimum_rows();
116        assert!(
117            (1 << lookup_bits) <= config.gate.max_rows,
118            "lookup table is too large for the circuit degree plus blinding factors!"
119        );
120
121        config
122    }
123
124    /// Returns the number of bits represented in the lookup table [0,2^<sup>lookup_bits</sup>).
125    pub fn lookup_bits(&self) -> usize {
126        self.lookup_bits
127    }
128
129    /// Instantiates the lookup table of the circuit.
130    /// * `meta`: [ConstraintSystem] of the circuit
131    fn create_lookup(&self, meta: &mut ConstraintSystem<F>) {
132        for (phase, q_l) in self.q_lookup.iter().enumerate() {
133            if let Some(q) = q_l {
134                meta.lookup("lookup", |meta| {
135                    let q = meta.query_selector(*q);
136                    // there should only be 1 advice column in phase `phase`
137                    let a =
138                        meta.query_advice(self.gate.basic_gates[phase][0].value, Rotation::cur());
139                    vec![(q * a, self.lookup)]
140                });
141            }
142        }
143        //if multiple columns
144        for la in self.lookup_advice.iter().flat_map(|advices| advices.iter()) {
145            meta.lookup("lookup wo selector", |meta| {
146                let a = meta.query_advice(*la, Rotation::cur());
147                vec![(a, self.lookup)]
148            });
149        }
150    }
151
152    /// Loads the lookup table into the circuit using the provided `layouter`.
153    /// * `layouter`: layouter for the circuit
154    pub fn load_lookup_table(&self, layouter: &mut impl Layouter<F>) -> Result<(), Error> {
155        layouter.assign_table(
156            || format!("{} bit lookup", self.lookup_bits),
157            |mut table| {
158                for idx in 0..(1u32 << self.lookup_bits) {
159                    table.assign_cell(
160                        || "lookup table",
161                        self.lookup,
162                        idx as usize,
163                        || Value::known(F::from(idx as u64)),
164                    )?;
165                }
166                Ok(())
167            },
168        )?;
169        Ok(())
170    }
171}
172
173/// Trait that implements methods to constrain a field element number `x` is within a range of bits.
174pub trait RangeInstructions<F: ScalarField> {
175    /// The type of Gate used within the instructions.
176    type Gate: GateInstructions<F>;
177
178    /// Returns the type of gate used.
179    fn gate(&self) -> &Self::Gate;
180
181    /// Returns the number of bits the lookup table represents.
182    fn lookup_bits(&self) -> usize;
183
184    /// Checks and constrains that `a` lies in the range [0, 2<sup>range_bits</sup>).
185    ///
186    /// Inputs:
187    /// * `a`: [AssignedValue] value to be range checked
188    /// * `range_bits`: number of bits in the range
189    fn range_check(&self, ctx: &mut Context<F>, a: AssignedValue<F>, range_bits: usize);
190
191    /// Constrains that 'a' is less than 'b'.
192    ///
193    /// Assumes that `a` and `b` have bit length <= num_bits bits.
194    ///
195    /// Note: This may fail silently if a or b have more than num_bits.
196    /// * a: [QuantumCell] value to check
197    /// * b: upper bound expressed as a [QuantumCell]
198    /// * num_bits: number of bits used to represent the values of `a` and `b`
199    fn check_less_than(
200        &self,
201        ctx: &mut Context<F>,
202        a: impl Into<QuantumCell<F>>,
203        b: impl Into<QuantumCell<F>>,
204        num_bits: usize,
205    );
206
207    /// Performs a range check that `a` has at most `ceil(b.bits() / lookup_bits) * lookup_bits` bits and then constrains that `a` is less than `b`.
208    ///
209    /// * a: [AssignedValue] value to check
210    /// * b: upper bound expressed as a [u64] value
211    ///
212    /// ## Assumptions
213    /// * `ceil(b.bits() / lookup_bits) * lookup_bits < F::CAPACITY`
214    fn check_less_than_safe(&self, ctx: &mut Context<F>, a: AssignedValue<F>, b: u64) {
215        let range_bits = bit_length(b).div_ceil(self.lookup_bits()) * self.lookup_bits();
216
217        self.range_check(ctx, a, range_bits);
218        self.check_less_than(ctx, a, Constant(F::from(b)), range_bits)
219    }
220
221    /// Performs a range check that `a` has at most `ceil(b.bits() / lookup_bits) * lookup_bits` bits and then constrains that `a` is less than `b`.
222    ///
223    /// * a: [AssignedValue] value to check
224    /// * b: upper bound expressed as a [BigUint] value
225    ///
226    /// ## Assumptions
227    /// * `ceil(b.bits() / lookup_bits) * lookup_bits < F::CAPACITY`
228    fn check_big_less_than_safe(&self, ctx: &mut Context<F>, a: AssignedValue<F>, b: BigUint)
229    where
230        F: BigPrimeField,
231    {
232        let range_bits = (b.bits() as usize).div_ceil(self.lookup_bits()) * self.lookup_bits();
233
234        self.range_check(ctx, a, range_bits);
235        self.check_less_than(ctx, a, Constant(biguint_to_fe(&b)), range_bits)
236    }
237
238    /// Constrains whether `a` is in `[0, b)`, and returns 1 if `a` < `b`, otherwise 0.
239    ///
240    /// Assumes that`a` and `b` are known to have <= num_bits bits.
241    /// * a: first [QuantumCell] to compare
242    /// * b: second [QuantumCell] to compare
243    /// * num_bits: number of bits to represent the values
244    fn is_less_than(
245        &self,
246        ctx: &mut Context<F>,
247        a: impl Into<QuantumCell<F>>,
248        b: impl Into<QuantumCell<F>>,
249        num_bits: usize,
250    ) -> AssignedValue<F>;
251
252    /// Performs a range check that `a` has at most `ceil(bit_length(b) / lookup_bits) * lookup_bits` and then returns whether `a` is in `[0,b)`.
253    ///
254    /// Returns 1 if `a` < `b`, otherwise 0.
255    ///
256    /// * a: [AssignedValue] value to check
257    /// * b: upper bound as [u64] value
258    fn is_less_than_safe(
259        &self,
260        ctx: &mut Context<F>,
261        a: AssignedValue<F>,
262        b: u64,
263    ) -> AssignedValue<F> {
264        let range_bits = bit_length(b).div_ceil(self.lookup_bits()) * self.lookup_bits();
265
266        self.range_check(ctx, a, range_bits);
267        self.is_less_than(ctx, a, Constant(F::from(b)), range_bits)
268    }
269
270    /// Performs a range check that `a` has at most `ceil(b.bits() / lookup_bits) * lookup_bits` bits and then returns whether `a` is in `[0,b)`.
271    ///
272    /// Returns 1 if `a` < `b`, otherwise 0.
273    ///
274    /// * a: [AssignedValue] value to check
275    /// * b: upper bound as [BigUint] value
276    ///
277    /// For the current implementation using `is_less_than`, we require `(ceil(b.bits() / lookup_bits) + 1) * lookup_bits <= F::CAPACITY`
278    fn is_big_less_than_safe(
279        &self,
280        ctx: &mut Context<F>,
281        a: AssignedValue<F>,
282        b: BigUint,
283    ) -> AssignedValue<F>
284    where
285        F: BigPrimeField,
286    {
287        let range_bits = (b.bits() as usize).div_ceil(self.lookup_bits()) * self.lookup_bits();
288
289        self.range_check(ctx, a, range_bits);
290        self.is_less_than(ctx, a, Constant(biguint_to_fe(&b)), range_bits)
291    }
292
293    /// Constrains and returns `(c, r)` such that `a = b * c + r`.
294    ///
295    /// * a: [QuantumCell] value to divide
296    /// * b: [BigUint] value to divide by
297    /// * a_num_bits: number of bits needed to represent the value of `a`
298    ///
299    /// ## Assumptions
300    /// * `b != 0` and that `a` has <= `a_num_bits` bits.
301    /// * `a_num_bits <= F::CAPACITY = F::NUM_BITS - 1`
302    /// * the bounds on `b`, quotient, and remainder imply `b * quotient + remainder < F::MODULUS`
303    fn div_mod(
304        &self,
305        ctx: &mut Context<F>,
306        a: impl Into<QuantumCell<F>>,
307        b: impl Into<BigUint>,
308        a_num_bits: usize,
309    ) -> (AssignedValue<F>, AssignedValue<F>)
310    where
311        F: BigPrimeField,
312    {
313        let a = a.into();
314        let b = b.into();
315        assert_div_mod_no_wrap::<F>(&b, a_num_bits);
316        let a_val = fe_to_biguint(a.value());
317        let (div, rem) = a_val.div_mod_floor(&b);
318        let [div, rem] = [div, rem].map(|v| biguint_to_fe(&v));
319        ctx.assign_region([Witness(rem), Constant(biguint_to_fe(&b)), Witness(div), a], [0]);
320        let rem = ctx.get(-4);
321        let div = ctx.get(-2);
322        // Constrain that a_num_bits fulfills `div < 2 ** a_num_bits / b`.
323        self.check_big_less_than_safe(
324            ctx,
325            div,
326            BigUint::one().shl(a_num_bits as u32) / &b + BigUint::one(),
327        );
328        // Constrain that remainder is less than divisor (i.e. `r < b`).
329        self.check_big_less_than_safe(ctx, rem, b);
330        (div, rem)
331    }
332
333    /// Constrains and returns `(c, r)` such that `a = b * c + r`.
334    ///
335    /// Assumes:
336    /// that `b != 0`.
337    /// that `a` has <= `a_num_bits` bits.
338    /// that `b` has <= `b_num_bits` bits.
339    ///
340    /// Note:
341    /// Let `X = 2 ** b_num_bits`
342    /// Write `a = a1 * X + a0` and `c = c1 * X + c0`
343    /// If we write `b * c0 + r = d1 * X + d0` then
344    ///     `b * c + r = (b * c1 + d1) * X + d0`
345    /// * a: [QuantumCell] value to divide
346    /// * b: [QuantumCell] value to divide by
347    /// * a_num_bits: number of bits needed to represent the value of `a`
348    /// * b_num_bits: number of bits needed to represent the value of `b`
349    ///
350    /// ## Assumptions
351    /// * `b != 0`
352    /// * `a_num_bits <= F::CAPACITY = F::NUM_BITS - 1`
353    /// * `b_num_bits <= F::CAPACITY = F::NUM_BITS - 1`
354    /// * the native products used by the split division do not wrap in `F`
355    fn div_mod_var(
356        &self,
357        ctx: &mut Context<F>,
358        a: impl Into<QuantumCell<F>>,
359        b: impl Into<QuantumCell<F>>,
360        a_num_bits: usize,
361        b_num_bits: usize,
362    ) -> (AssignedValue<F>, AssignedValue<F>)
363    where
364        F: BigPrimeField,
365    {
366        assert!(a_num_bits <= F::CAPACITY as usize);
367        assert!(b_num_bits > 0 && b_num_bits <= F::CAPACITY as usize);
368
369        let native_modulus = modulus::<F>();
370        let x = BigUint::one().shl(b_num_bits as u32);
371        let max_bcr0 = &x * (&x - BigUint::one());
372        assert!(max_bcr0 < native_modulus.clone());
373        if a_num_bits > b_num_bits {
374            let max_b = &x - BigUint::one();
375            let max_div_hi = BigUint::one().shl((a_num_bits - b_num_bits) as u32) - BigUint::one();
376            let max_bcr0_hi = BigUint::one().shl((a_num_bits - b_num_bits) as u32);
377            let max_bcr_hi = max_b * max_div_hi + max_bcr0_hi;
378            assert!(max_bcr_hi < native_modulus);
379        }
380
381        let a = a.into();
382        let b = b.into();
383        ctx.assign_cell(b);
384        let b = ctx.get(-1);
385        self.range_check(ctx, b, b_num_bits);
386        let a_val = fe_to_biguint(a.value());
387        let b_val = fe_to_biguint(b.value());
388        assert!(!b_val.is_zero());
389        let (div, rem) = a_val.div_mod_floor(&b_val);
390        let (div_hi, div_lo) = div.div_mod_floor(&x);
391
392        let x_fe = self.gate().pow_of_two()[b_num_bits];
393        let [div, div_hi, div_lo, rem] = [div, div_hi, div_lo, rem].map(|v| biguint_to_fe(&v));
394        ctx.assign_region(
395            [Witness(div_lo), Witness(div_hi), Constant(x_fe), Witness(div), Witness(rem)],
396            [0],
397        );
398        let [div_lo, div_hi, div, rem] = [-5, -4, -2, -1].map(|i| ctx.get(i));
399        self.range_check(ctx, div_lo, b_num_bits);
400        if a_num_bits <= b_num_bits {
401            self.gate().assert_is_const(ctx, &div_hi, &F::ZERO);
402        } else {
403            self.range_check(ctx, div_hi, a_num_bits - b_num_bits);
404        }
405
406        let (bcr0_hi, bcr0_lo) = {
407            let bcr0 = self.gate().mul_add(ctx, Existing(b), Existing(div_lo), Existing(rem));
408            self.div_mod(ctx, Existing(bcr0), x.clone(), a_num_bits)
409        };
410        let bcr_hi = self.gate().mul_add(ctx, Existing(b), Existing(div_hi), Existing(bcr0_hi));
411
412        let (a_hi, a_lo) = self.div_mod(ctx, a, x, a_num_bits);
413        ctx.constrain_equal(&bcr_hi, &a_hi);
414        ctx.constrain_equal(&bcr0_lo, &a_lo);
415
416        self.range_check(ctx, rem, b_num_bits);
417        self.check_less_than(ctx, Existing(rem), Existing(b), b_num_bits);
418        (div, rem)
419    }
420
421    /// Constrains and returns the last bit of the value of `a`.
422    ///
423    /// Assume `a` has been range checked already to `limb_bits` bits.
424    /// * a: [AssignedValue] value to get the last bit of
425    /// * limb_bits: number of bits in a limb
426    fn get_last_bit(
427        &self,
428        ctx: &mut Context<F>,
429        a: AssignedValue<F>,
430        limb_bits: usize,
431    ) -> AssignedValue<F> {
432        let a_big = fe_to_biguint(a.value());
433        let bit_v = F::from(a_big.bit(0));
434        let two = F::from(2u64);
435        let h_v = F::from_bytes_le(&(a_big >> 1usize).to_bytes_le());
436
437        ctx.assign_region([Witness(bit_v), Witness(h_v), Constant(two), Existing(a)], [0]);
438        let half = ctx.get(-3);
439        let bit = ctx.get(-4);
440
441        self.range_check(ctx, half, limb_bits - 1);
442        self.gate().assert_bit(ctx, bit);
443        bit
444    }
445}
446
447/// # RangeChip
448/// This chip provides methods that rely on "range checking" that a field element `x` is within a range of bits.
449/// Range checks are done using a lookup table with the numbers [0, 2<sup>lookup_bits</sup>).
450#[derive(Clone, Debug, Getters)]
451pub struct RangeChip<F: ScalarField> {
452    /// Underlying [GateChip] for this chip.
453    pub gate: GateChip<F>,
454    /// Lookup manager for each phase, lazily initiated using the [`SharedCopyConstraintManager`](crate::virtual_region::copy_constraints::SharedCopyConstraintManager) from the [Context]
455    /// that first calls it.
456    ///
457    /// The lookup manager is used to store the cells that need to be looked up in the range check lookup table.
458    #[getset(get = "pub")]
459    lookup_manager: [LookupAnyManager<F, 1>; MAX_PHASE],
460    /// Defines the number of bits represented in the lookup table [0,2<sup>lookup_bits</sup>).
461    lookup_bits: usize,
462    /// [Vec] of powers of `2 ** lookup_bits` represented as [QuantumCell::Constant].
463    /// These are precomputed and cached as a performance optimization for later limb decompositions. We precompute up to the higher power that fits in `F`, which is `2 ** ((F::CAPACITY / lookup_bits) * lookup_bits)`.
464    pub limb_bases: Vec<QuantumCell<F>>,
465}
466
467impl<F: ScalarField> RangeChip<F> {
468    /// Creates a new [RangeChip] with the given strategy and lookup_bits.
469    /// * `lookup_bits`: number of bits represented in the lookup table [0,2<sup>lookup_bits</sup>)
470    /// * `lookup_manager`: a [LookupAnyManager] for each phase.
471    ///
472    /// **IMPORTANT:** It is **critical** that all `LookupAnyManager`s use the same [`SharedCopyConstraintManager`](crate::virtual_region::copy_constraints::SharedCopyConstraintManager)
473    /// as in your primary circuit builder.
474    ///
475    /// It is not advised to call this function directly. Instead you should call `BaseCircuitBuilder::range_chip`.
476    pub fn new(lookup_bits: usize, lookup_manager: [LookupAnyManager<F, 1>; MAX_PHASE]) -> Self {
477        let limb_base = F::from(1u64 << lookup_bits);
478        let mut running_base = limb_base;
479        let num_bases = F::CAPACITY as usize / lookup_bits;
480        let mut limb_bases = Vec::with_capacity(num_bases + 1);
481        limb_bases.extend([Constant(F::ONE), Constant(running_base)]);
482        for _ in 2..=num_bases {
483            running_base *= &limb_base;
484            limb_bases.push(Constant(running_base));
485        }
486        let gate = GateChip::new();
487
488        Self { gate, lookup_bits, lookup_manager, limb_bases }
489    }
490
491    fn add_cell_to_lookup(&self, ctx: &Context<F>, a: AssignedValue<F>) {
492        let phase = ctx.phase();
493        let manager = &self.lookup_manager[phase];
494        manager.add_lookup(ctx.tag(), [a]);
495    }
496
497    /// Checks and constrains that `a` lies in the range [0, 2<sup>range_bits</sup>).
498    ///
499    /// This is done by decomposing `a` into `num_limbs` limbs, where `num_limbs = ceil(range_bits / lookup_bits)`.
500    /// Each limb is constrained to be within the range [0, 2<sup>lookup_bits</sup>).
501    /// The limbs are then combined to form `a` again with the last limb having `rem_bits` number of bits.
502    ///
503    /// Returns the last (highest) limb.
504    ///
505    /// Inputs:
506    /// * `a`: [AssignedValue] value to be range checked
507    /// * `range_bits`: number of bits in the range
508    /// * `lookup_bits`: number of bits in the lookup table
509    ///
510    /// # Assumptions
511    /// * `ceil(range_bits / lookup_bits) * lookup_bits <= F::CAPACITY`
512    fn _range_check(
513        &self,
514        ctx: &mut Context<F>,
515        a: AssignedValue<F>,
516        range_bits: usize,
517    ) -> AssignedValue<F> {
518        if range_bits == 0 {
519            self.gate.assert_is_const(ctx, &a, &F::ZERO);
520            return a;
521        }
522        // the number of limbs
523        let num_limbs = range_bits.div_ceil(self.lookup_bits);
524        // println!("range check {} bits {} len", range_bits, k);
525        let rem_bits = range_bits % self.lookup_bits;
526
527        debug_assert!(self.limb_bases.len() >= num_limbs);
528
529        let last_limb = if num_limbs == 1 {
530            self.add_cell_to_lookup(ctx, a);
531            a
532        } else {
533            let limbs = decompose_fe_to_u64_limbs(a.value(), num_limbs, self.lookup_bits)
534                .into_iter()
535                .map(|x| Witness(F::from(x)));
536            let row_offset = ctx.advice.len() as isize;
537            let acc = self.gate.inner_product(ctx, limbs, self.limb_bases[..num_limbs].to_vec());
538            // the inner product above must equal `a`
539            ctx.constrain_equal(&a, &acc);
540            // we fetch the cells to lookup by getting the indices where `limbs` were assigned in `inner_product`. Because `limb_bases[0]` is 1, the progression of indices is 0,1,4,...,4+3*i
541            self.add_cell_to_lookup(ctx, ctx.get(row_offset));
542            for i in 0..num_limbs - 1 {
543                self.add_cell_to_lookup(ctx, ctx.get(row_offset + 1 + 3 * i as isize));
544            }
545            ctx.get(row_offset + 1 + 3 * (num_limbs - 2) as isize)
546        };
547
548        // additional constraints for the last limb if rem_bits != 0
549        match rem_bits.cmp(&1) {
550            // we want to check x := limbs[num_limbs-1] is boolean
551            // we constrain x*(x-1) = 0 + x * x - x == 0
552            // | 0 | x | x | x |
553            Ordering::Equal => {
554                self.gate.assert_bit(ctx, last_limb);
555            }
556            Ordering::Greater => {
557                let mult_val = self.gate.pow_of_two[self.lookup_bits - rem_bits];
558                let check = self.gate.mul(ctx, last_limb, Constant(mult_val));
559                self.add_cell_to_lookup(ctx, check);
560            }
561            _ => {}
562        }
563        last_limb
564    }
565}
566
567impl<F: ScalarField> RangeInstructions<F> for RangeChip<F> {
568    type Gate = GateChip<F>;
569
570    /// The type of Gate used in this chip.
571    fn gate(&self) -> &Self::Gate {
572        &self.gate
573    }
574
575    /// Returns the number of bits represented in the lookup table [0,2<sup>lookup_bits</sup>).
576    fn lookup_bits(&self) -> usize {
577        self.lookup_bits
578    }
579
580    /// Checks and constrains that `a` lies in the range [0, 2<sup>range_bits</sup>).
581    ///
582    /// This is done by decomposing `a` into `num_limbs` limbs, where `num_limbs = ceil(range_bits / lookup_bits)`.
583    /// Each limb is constrained to be within the range [0, 2<sup>lookup_bits</sup>).
584    /// The limbs are then combined to form `a` again with the last limb having `rem_bits` number of bits.
585    ///
586    /// Inputs:
587    /// * `a`: [AssignedValue] value to be range checked
588    /// * `range_bits`: number of bits in the range
589    ///
590    /// # Assumptions
591    /// * `ceil(range_bits / lookup_bits) * lookup_bits <= F::CAPACITY`
592    fn range_check(&self, ctx: &mut Context<F>, a: AssignedValue<F>, range_bits: usize) {
593        self._range_check(ctx, a, range_bits);
594    }
595
596    /// Constrains that 'a' is less than 'b'.
597    ///
598    /// Assumes that`a` and `b` are known to have <= num_bits bits.
599    ///
600    /// Note: This may fail silently if a or b have more than num_bits
601    /// * a: [QuantumCell] value to check
602    /// * b: upper bound expressed as a [QuantumCell]
603    /// * num_bits: number of bits to represent the values
604    fn check_less_than(
605        &self,
606        ctx: &mut Context<F>,
607        a: impl Into<QuantumCell<F>>,
608        b: impl Into<QuantumCell<F>>,
609        num_bits: usize,
610    ) {
611        assert!(
612            num_bits < F::CAPACITY as usize,
613            "num_bits must leave one native-field bit of headroom for check_less_than"
614        );
615        let a = a.into();
616        let b = b.into();
617        let pow_of_two = self.gate.pow_of_two[num_bits];
618        let check_cell = {
619            let shift_a_val = pow_of_two + a.value();
620            // | a + 2^(num_bits) - b | b | 1 | a + 2^(num_bits) | - 2^(num_bits) | 1 | a |
621            let cells = [
622                Witness(shift_a_val - b.value()),
623                b,
624                Constant(F::ONE),
625                Witness(shift_a_val),
626                Constant(-pow_of_two),
627                Constant(F::ONE),
628                a,
629            ];
630            ctx.assign_region(cells, [0, 3]);
631            ctx.get(-7)
632        };
633
634        self.range_check(ctx, check_cell, num_bits);
635    }
636
637    /// Constrains whether `a` is in `[0, b)`, and returns 1 if `a` < `b`, otherwise 0.
638    ///
639    /// * a: first [QuantumCell] to compare
640    /// * b: second [QuantumCell] to compare
641    /// * num_bits: number of bits to represent the values
642    ///
643    /// # Assumptions
644    /// * `a` and `b` are known to have `<= num_bits` bits.
645    /// * (`ceil(num_bits / lookup_bits) + 1) * lookup_bits <= F::CAPACITY`
646    fn is_less_than(
647        &self,
648        ctx: &mut Context<F>,
649        a: impl Into<QuantumCell<F>>,
650        b: impl Into<QuantumCell<F>>,
651        num_bits: usize,
652    ) -> AssignedValue<F> {
653        let a = a.into();
654        let b = b.into();
655
656        let k = num_bits.div_ceil(self.lookup_bits);
657        let padded_bits = k * self.lookup_bits;
658        assert!(
659            padded_bits + self.lookup_bits <= F::CAPACITY as usize,
660            "num_bits is too large for this is_less_than implementation"
661        );
662        let pow_padded = self.gate.pow_of_two[padded_bits];
663
664        let shift_a_val = pow_padded + a.value();
665        let shifted_val = shift_a_val - b.value();
666        let shifted_cell = {
667            ctx.assign_region(
668                [
669                    Witness(shifted_val),
670                    b,
671                    Constant(F::ONE),
672                    Witness(shift_a_val),
673                    Constant(-pow_padded),
674                    Constant(F::ONE),
675                    a,
676                ],
677                [0, 3],
678            );
679            ctx.get(-7)
680        };
681
682        // check whether a - b + 2^padded_bits < 2^padded_bits ?
683        // since assuming a, b < 2^padded_bits we are guaranteed a - b + 2^padded_bits < 2^{padded_bits + 1}
684        let last_limb = self._range_check(ctx, shifted_cell, padded_bits + self.lookup_bits);
685        // last_limb will have the (k + 1)-th limb of `a - b + 2^{k * limb_bits}`, which is zero iff `a < b`
686        self.gate.is_zero(ctx, last_limb)
687    }
688}