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}