1use std::{
50 cell::RefCell,
51 cmp::min,
52 collections::HashMap,
53 hash::{Hash, Hasher},
54 ops::Neg,
55 rc::Rc,
56};
57
58use midnight_proofs::{
59 circuit::{Chip, Layouter, Region, Value},
60 plonk::{Advice, Column, ConstraintSystem, Constraints, Error, Fixed, Instance, Selector},
61 poly::Rotation,
62};
63use num_bigint::BigUint;
64use num_traits::Zero;
65
66#[cfg(any(test, feature = "testing"))]
67use crate::testing_utils::FromScratch;
68use crate::{
69 instructions::{
70 public_input::CommittedInstanceInstructions, ArithInstructions, AssertionInstructions,
71 AssignmentInstructions, BinaryInstructions, CanonicityInstructions,
72 ControlFlowInstructions, ConversionInstructions, EqualityInstructions, FieldInstructions,
73 PublicInputInstructions, UnsafeConversionInstructions, ZeroInstructions,
74 },
75 types::{AssignedNative, InnerValue, Instantiable},
76 utils::ComposableChip,
77 CircuitField,
78};
79
80pub const NB_ARITH_COLS: usize = 5;
84
85pub const NB_ARITH_FIXED_COLS: usize = NB_ARITH_COLS + 4;
87
88const NB_PARALLEL_ADD_COLS: usize = 3;
95
96#[derive(Clone, Debug)]
98pub struct NativeConfig {
99 q_arith: Selector,
100 q_12_minus_34: Selector,
101 q_par_add: Selector,
102 pub(crate) value_cols: [Column<Advice>; NB_ARITH_COLS],
103 coeff_cols: [Column<Fixed>; NB_ARITH_COLS],
104 q_next_col: Column<Fixed>,
105 mul_ab_col: Column<Fixed>,
106 mul_ac_col: Column<Fixed>,
107 constant_col: Column<Fixed>,
108 fixed_values_col: Column<Fixed>,
109 committed_instance_col: Column<Instance>,
110 instance_col: Column<Instance>,
111}
112
113#[derive(Clone, Debug)]
115pub struct NativeChip<F: CircuitField> {
116 config: NativeConfig,
117 cached_fixed: Rc<RefCell<HashMap<BigUint, AssignedNative<F>>>>,
118 committed_instance_offset: Rc<RefCell<usize>>,
119 instance_offset: Rc<RefCell<usize>>,
120}
121
122impl<F: CircuitField> Chip<F> for NativeChip<F> {
123 type Config = NativeConfig;
124 type Loaded = ();
125
126 fn config(&self) -> &Self::Config {
127 &self.config
128 }
129
130 fn loaded(&self) -> &Self::Loaded {
131 &()
132 }
133}
134
135impl<F: CircuitField> ComposableChip<F> for NativeChip<F> {
136 type SharedResources = (
137 [Column<Advice>; NB_ARITH_COLS],
138 [Column<Fixed>; NB_ARITH_FIXED_COLS],
139 [Column<Instance>; 2], );
141
142 type InstructionDeps = ();
143 fn new(config: &NativeConfig, _sub_chips: &()) -> Self {
145 Self {
146 config: config.clone(),
147 cached_fixed: Default::default(),
148 instance_offset: Rc::new(RefCell::new(0)),
149 committed_instance_offset: Rc::new(RefCell::new(0)),
150 }
151 }
152
153 fn configure(
156 meta: &mut ConstraintSystem<F>,
157 shared_res: &Self::SharedResources,
158 ) -> NativeConfig {
159 let value_columns = &shared_res.0;
160 let fixed_columns = &shared_res.1;
161
162 let committed_instance_col = shared_res.2[0];
165 let instance_col = shared_res.2[1];
166
167 let q_arith = meta.selector();
168 let q_12_minus_34 = meta.selector();
169 let q_par_add = meta.selector();
170 let coeff_cols: [Column<Fixed>; NB_ARITH_COLS] = fixed_columns[4..].try_into().unwrap();
171 let q_next_col = fixed_columns[0];
172 let mul_ab_col = fixed_columns[1];
173 let mul_ac_col = fixed_columns[2];
174 let constant_col = fixed_columns[3];
175
176 let fixed_values_col = meta.fixed_column();
177 meta.enable_equality(fixed_values_col);
178
179 for col in value_columns.iter() {
180 meta.enable_equality(*col);
181 }
182 meta.enable_equality(committed_instance_col);
183 meta.enable_equality(instance_col);
184
185 meta.create_gate("arith_gate", |meta| {
186 let values = value_columns
187 .iter()
188 .map(|col| meta.query_advice(*col, Rotation::cur()))
189 .collect::<Vec<_>>();
190
191 let next_value = meta.query_advice(value_columns[0], Rotation::next());
192
193 let coeffs = coeff_cols
194 .iter()
195 .map(|col| meta.query_fixed(*col, Rotation::cur()))
196 .collect::<Vec<_>>();
197
198 let q_next_coeff = meta.query_fixed(q_next_col, Rotation::cur());
199 let mul_ab_coeff = meta.query_fixed(mul_ab_col, Rotation::cur());
200 let mul_ac_coeff = meta.query_fixed(mul_ac_col, Rotation::cur());
201 let constant = meta.query_fixed(constant_col, Rotation::cur());
202
203 let id = values
204 .iter()
205 .zip(coeffs.iter())
206 .fold(constant, |acc, (value, coeff)| acc + coeff * value)
207 + q_next_coeff * next_value
208 + mul_ab_coeff * &values[0] * &values[1]
209 + mul_ac_coeff * &values[0] * &values[2];
210
211 Constraints::with_selector(q_arith, vec![id])
212 });
213
214 meta.create_gate("12_minus_34", |meta| {
215 let v1 = meta.query_advice(value_columns[1], Rotation::cur());
216 let v2 = meta.query_advice(value_columns[2], Rotation::cur());
217 let v3 = meta.query_advice(value_columns[3], Rotation::cur());
218 let v4 = meta.query_advice(value_columns[4], Rotation::cur());
219
220 Constraints::with_selector(q_12_minus_34, vec![v1 + v2 - v3 - v4])
221 });
222
223 meta.create_gate("parallel_add_gate", |meta| {
224 let ids = (value_columns[0..NB_PARALLEL_ADD_COLS].iter())
225 .zip(coeff_cols[0..NB_PARALLEL_ADD_COLS].iter())
226 .map(|(val_col, const_col)| {
227 let val = meta.query_advice(*val_col, Rotation::cur());
228 let res = meta.query_advice(*val_col, Rotation::next());
229 let c = meta.query_fixed(*const_col, Rotation::cur());
230 val + c - res
231 })
232 .collect::<Vec<_>>();
233
234 Constraints::with_selector(q_par_add, ids)
235 });
236
237 NativeConfig {
238 q_arith,
239 q_12_minus_34,
240 q_par_add,
241 value_cols: *value_columns,
242 coeff_cols,
243 q_next_col,
244 mul_ab_col,
245 mul_ac_col,
246 constant_col,
247 fixed_values_col,
248 committed_instance_col,
249 instance_col,
250 }
251 }
252
253 fn load(&self, _layouter: &mut impl Layouter<F>) -> Result<(), Error> {
254 Ok(())
255 }
256}
257
258impl<F: CircuitField> NativeChip<F> {
259 fn custom(
263 &self,
264 region: &mut Region<'_, F>,
265 coeffs: &[F; NB_ARITH_COLS],
266 q_next_coeff: F,
267 mul_coeffs: (F, F),
268 constant: F,
269 offset: usize,
270 ) -> Result<(), Error> {
271 self.config.q_arith.enable(region, offset)?;
272
273 for (i, coeff) in coeffs.iter().enumerate() {
274 region.assign_fixed(
275 || "arith coeff",
276 self.config.coeff_cols[i],
277 offset,
278 || Value::known(*coeff),
279 )?;
280 }
281
282 region.assign_fixed(
283 || "arith q_next",
284 self.config.q_next_col,
285 offset,
286 || Value::known(q_next_coeff),
287 )?;
288 region.assign_fixed(
289 || "arith mul_ab",
290 self.config.mul_ab_col,
291 offset,
292 || Value::known(mul_coeffs.0),
293 )?;
294 region.assign_fixed(
295 || "arith mul_ac",
296 self.config.mul_ac_col,
297 offset,
298 || Value::known(mul_coeffs.1),
299 )?;
300 region.assign_fixed(
301 || "arith const",
302 self.config.constant_col,
303 offset,
304 || Value::known(constant),
305 )?;
306
307 Ok(())
308 }
309
310 fn copy_in_row(
312 &self,
313 region: &mut Region<'_, F>,
314 x: &AssignedNative<F>,
315 column: &Column<Advice>,
316 offset: usize,
317 ) -> Result<(), Error> {
318 let y = region.assign_advice(
319 || "arith copy_in_row",
320 *column,
321 offset,
322 || x.value().copied(),
323 )?;
324 region.constrain_equal(x.cell(), y.cell())?;
325 Ok(())
326 }
327
328 fn add_and_double_mul(
330 &self,
331 layouter: &mut impl Layouter<F>,
332 (a, x): (F, &AssignedNative<F>),
333 (b, y): (F, &AssignedNative<F>),
334 (c, z): (F, &AssignedNative<F>),
335 k: F,
336 (m1, m2): (F, F),
337 ) -> Result<AssignedNative<F>, Error> {
338 let res_value = x
339 .value()
340 .zip(y.value())
341 .zip(z.value())
342 .map(|((x, y), z)| a * x + b * y + c * z + k + m1 * x * y + m2 * x * z);
343 layouter.assign_region(
344 || "add and double mul",
345 |mut region| {
346 self.copy_in_row(&mut region, x, &self.config.value_cols[0], 0)?;
347 self.copy_in_row(&mut region, y, &self.config.value_cols[1], 0)?;
348 self.copy_in_row(&mut region, z, &self.config.value_cols[2], 0)?;
349 let res =
350 region.assign_advice(|| "res", self.config.value_cols[4], 0, || res_value)?;
351 let mut coeffs = [F::ZERO; NB_ARITH_COLS];
352 coeffs[0] = a; coeffs[1] = b; coeffs[2] = c; coeffs[4] = -F::ONE; self.custom(&mut region, &coeffs, F::ZERO, (m1, m2), k, 0)?;
357 Ok(res)
358 },
359 )
360 }
361
362 fn assign_with_shifted_inverse(
368 &self,
369 layouter: &mut impl Layouter<F>,
370 value: Value<F>,
371 shift: F,
372 ) -> Result<(AssignedNative<F>, AssignedNative<F>), Error> {
373 layouter.assign_region(
374 || "assign with shifted inverse",
375 |mut region| {
376 let r_value = value.map(|x| (x - shift).invert().unwrap_or(F::ZERO));
378 let x = region.assign_advice(|| "x", self.config.value_cols[0], 0, || value)?;
379 let r = region.assign_advice(|| "r", self.config.value_cols[1], 0, || r_value)?;
380 let mut coeffs = [F::ZERO; NB_ARITH_COLS];
381 coeffs[1] = -shift; self.custom(&mut region, &coeffs, F::ZERO, (F::ONE, F::ZERO), -F::ONE, 0)?;
383 Ok((x, r))
384 },
385 )
386 }
387
388 pub(crate) fn assign_linear_combination_aux(
416 &self,
417 region: &mut Region<'_, F>,
418 terms: &[(F, Value<F>)],
419 constant: F,
420 result: &Value<F>,
421 cols_used: usize,
422 offset: &mut usize,
423 ) -> Result<(Vec<AssignedNative<F>>, AssignedNative<F>), Error> {
424 assert!(cols_used < NB_ARITH_COLS);
426
427 let chunk_len = min(terms.len(), cols_used);
431
432 let mut coeffs = [F::ZERO; NB_ARITH_COLS];
434
435 let assigned_result = region.assign_advice(
437 || "assign linear combination term",
438 self.config.value_cols[0],
439 *offset,
440 || *result,
441 )?;
442 coeffs[0] = -F::ONE;
443
444 let mut assigned_limbs = terms[0..chunk_len]
446 .iter()
447 .enumerate()
448 .map(|(i, term)| {
449 coeffs[i + 1] = term.0;
450 region.assign_advice(
451 || "assign linear combination term",
452 self.config.value_cols[i + 1],
453 *offset,
454 || term.1,
455 )
456 })
457 .collect::<Result<Vec<_>, _>>()?;
458
459 if terms.len() <= cols_used {
462 self.custom(
463 region,
464 &coeffs,
465 F::ZERO,
466 (F::ZERO, F::ZERO),
467 constant,
468 *offset,
469 )?;
470 Ok((assigned_limbs, assigned_result))
471 }
472 else {
474 let chunk_result =
476 terms[..chunk_len].iter().fold(Value::known(constant), |acc, (coeff, x)| {
477 acc.zip(*x).map(|(acc, val)| acc + *coeff * val)
478 });
479 let next_result = *result - chunk_result;
480
481 self.custom(
492 region,
493 &coeffs,
494 F::ONE,
495 (F::ZERO, F::ZERO),
496 constant,
497 *offset,
498 )?;
499 *offset += 1;
500 let (new_assigned_limbs, _) = self.assign_linear_combination_aux(
501 region,
502 &terms[chunk_len..],
503 F::ZERO,
504 &next_result,
505 cols_used,
506 offset,
507 )?;
508 assigned_limbs.extend_from_slice(&new_assigned_limbs);
509 Ok((assigned_limbs, assigned_result))
510 }
511 }
512
513 pub fn nb_public_inputs(&self) -> usize {
516 *self.instance_offset.borrow()
517 }
518}
519
520impl<F: CircuitField> NativeChip<F> {
521 pub(crate) fn add_constants_in_region(
524 &self,
525 region: &mut Region<'_, F>,
526 variables: &[AssignedNative<F>; NB_PARALLEL_ADD_COLS],
527 constants: &[F; NB_PARALLEL_ADD_COLS],
528 offset: &mut usize,
529 ) -> Result<Vec<AssignedNative<F>>, Error> {
530 self.config.q_par_add.enable(region, *offset)?;
531
532 (variables.iter())
533 .zip(self.config.value_cols)
534 .try_for_each(|(x, col)| self.copy_in_row(region, x, &col, *offset))?;
535
536 constants.iter().zip(self.config.coeff_cols).try_for_each(|(c, col)| {
537 region.assign_fixed(|| "add_consts", col, *offset, || Value::known(*c))?;
538 Ok::<(), Error>(())
539 })?;
540
541 *offset += 1;
542
543 let res_values = variables.iter().zip(constants).map(|(x, c)| x.value().map(|x| *x + *c));
544
545 res_values
546 .zip(self.config.value_cols)
547 .map(|(val, col)| region.assign_advice(|| "add_consts", col, *offset, || val))
548 .collect::<Result<Vec<_>, Error>>()
549 }
550}
551
552impl<F> AssignmentInstructions<F, AssignedNative<F>> for NativeChip<F>
553where
554 F: CircuitField,
555{
556 fn assign(
557 &self,
558 layouter: &mut impl Layouter<F>,
559 value: Value<F>,
560 ) -> Result<AssignedNative<F>, Error> {
561 layouter.assign_region(
562 || "Assign native value",
563 |mut region| {
564 region.assign_advice(|| "assign element", self.config.value_cols[0], 0, || value)
565 },
566 )
567 }
568
569 fn assign_fixed(
570 &self,
571 layouter: &mut impl Layouter<F>,
572 constant: F,
573 ) -> Result<AssignedNative<F>, Error> {
574 let constant_big = constant.to_biguint();
575 if let Some(assigned) = self.cached_fixed.borrow().get(&constant_big) {
576 return Ok(assigned.clone());
577 };
578
579 let x = layouter.assign_region(
580 || "Assign fixed",
581 |mut region| {
582 let mut x = region.assign_fixed(
583 || "fixed",
584 self.config.fixed_values_col,
585 0,
586 || Value::known(constant),
587 )?;
588 x.update_value(constant)?;
591 Ok(x)
592 },
593 )?;
594
595 self.cached_fixed.borrow_mut().insert(constant_big.clone(), x.clone());
597
598 Ok(x)
599 }
600
601 fn assign_many(
603 &self,
604 layouter: &mut impl Layouter<F>,
605 values: &[Value<F>],
606 ) -> Result<Vec<AssignedNative<F>>, Error> {
607 layouter.assign_region(
608 || "assign_many (native)",
609 |mut region| {
610 let mut assigned = vec![];
611 for (i, chunk_values) in values.chunks(NB_ARITH_COLS).enumerate() {
612 for (value, col) in chunk_values.iter().zip(self.config.value_cols.iter()) {
613 let cell = region.assign_advice(|| "assign", *col, i, || *value)?;
614 assigned.push(cell);
615 }
616 }
617 Ok(assigned)
618 },
619 )
620 }
621}
622
623impl<F> PublicInputInstructions<F, AssignedNative<F>> for NativeChip<F>
624where
625 F: CircuitField,
626{
627 fn as_public_input(
628 &self,
629 _layouter: &mut impl Layouter<F>,
630 assigned: &AssignedNative<F>,
631 ) -> Result<Vec<AssignedNative<F>>, Error> {
632 Ok(vec![assigned.clone()])
633 }
634
635 fn constrain_as_public_input(
636 &self,
637 layouter: &mut impl Layouter<F>,
638 assigned: &AssignedNative<F>,
639 ) -> Result<(), Error> {
640 let mut offset = self.instance_offset.borrow_mut();
641 layouter.constrain_instance(assigned.cell(), self.config.instance_col, *offset)?;
642 *offset += 1;
643 Ok(())
644 }
645
646 fn assign_as_public_input(
647 &self,
648 layouter: &mut impl Layouter<F>,
649 value: Value<F>,
650 ) -> Result<AssignedNative<F>, Error> {
651 let assigned = self.assign(layouter, value)?;
653 self.constrain_as_public_input(layouter, &assigned)?;
654 Ok(assigned)
655 }
656}
657
658impl<F> CommittedInstanceInstructions<F, AssignedNative<F>> for NativeChip<F>
659where
660 F: CircuitField,
661{
662 fn constrain_as_committed_public_input(
663 &self,
664 layouter: &mut impl Layouter<F>,
665 assigned: &AssignedNative<F>,
666 ) -> Result<(), Error> {
667 let mut offset = self.committed_instance_offset.borrow_mut();
668 layouter.constrain_instance(
669 assigned.cell(),
670 self.config.committed_instance_col,
671 *offset,
672 )?;
673 *offset += 1;
674 Ok(())
675 }
676}
677
678impl<F> AssignmentInstructions<F, AssignedBit<F>> for NativeChip<F>
679where
680 F: CircuitField,
681{
682 fn assign(
683 &self,
684 layouter: &mut impl Layouter<F>,
685 value: Value<bool>,
686 ) -> Result<AssignedBit<F>, Error> {
687 layouter.assign_region(
688 || "Assign big",
689 |mut region| {
690 let b_value = value.map(|b| if b { F::ONE } else { F::ZERO });
692 let b = region.assign_advice(|| "b", self.config.value_cols[0], 0, || b_value)?;
693 self.copy_in_row(&mut region, &b, &self.config.value_cols[1], 0)?;
694 let mut coeffs = [F::ZERO; NB_ARITH_COLS];
695 coeffs[0] = -F::ONE; self.custom(&mut region, &coeffs, F::ZERO, (F::ONE, F::ZERO), F::ZERO, 0)?;
697 Ok(AssignedBit(b))
698 },
699 )
700 }
701
702 fn assign_fixed(
703 &self,
704 layouter: &mut impl Layouter<F>,
705 bit: bool,
706 ) -> Result<AssignedBit<F>, Error> {
707 let constant = if bit { F::ONE } else { F::ZERO };
708 let x = self.assign_fixed(layouter, constant)?;
709 Ok(AssignedBit(x))
710 }
711}
712
713impl<F> PublicInputInstructions<F, AssignedBit<F>> for NativeChip<F>
714where
715 F: CircuitField,
716{
717 fn as_public_input(
718 &self,
719 _layouter: &mut impl Layouter<F>,
720 assigned: &AssignedBit<F>,
721 ) -> Result<Vec<AssignedNative<F>>, Error> {
722 Ok(vec![assigned.clone().into()])
723 }
724
725 fn constrain_as_public_input(
726 &self,
727 layouter: &mut impl Layouter<F>,
728 assigned: &AssignedBit<F>,
729 ) -> Result<(), Error> {
730 let assigned_as_native: AssignedNative<F> = assigned.clone().into();
731 self.constrain_as_public_input(layouter, &assigned_as_native)
732 }
733
734 fn assign_as_public_input(
735 &self,
736 layouter: &mut impl Layouter<F>,
737 value: Value<bool>,
738 ) -> Result<AssignedBit<F>, Error> {
739 let assigned = self.assign(layouter, value)?;
742 self.constrain_as_public_input(layouter, &assigned)?;
743 Ok(assigned)
744 }
745}
746
747impl<F> AssertionInstructions<F, AssignedNative<F>> for NativeChip<F>
748where
749 F: CircuitField,
750{
751 fn assert_equal(
752 &self,
753 layouter: &mut impl Layouter<F>,
754 x: &AssignedNative<F>,
755 y: &AssignedNative<F>,
756 ) -> Result<(), Error> {
757 layouter.assign_region(
758 || "Assert equal",
759 |mut region| region.constrain_equal(x.cell(), y.cell()),
760 )
761 }
762
763 fn assert_not_equal(
764 &self,
765 layouter: &mut impl Layouter<F>,
766 x: &AssignedNative<F>,
767 y: &AssignedNative<F>,
768 ) -> Result<(), Error> {
769 layouter.assign_region(
770 || "Assert not equal",
771 |mut region| {
772 let r_value = (x.value().copied() - y.value().copied())
775 .map(|v| v.invert().unwrap_or(F::ZERO));
776 region.assign_advice(|| "r", self.config.value_cols[0], 0, || r_value)?;
777 self.copy_in_row(&mut region, x, &self.config.value_cols[1], 0)?;
778 self.copy_in_row(&mut region, y, &self.config.value_cols[2], 0)?;
779 let coeffs = [F::ZERO; NB_ARITH_COLS];
780 self.custom(&mut region, &coeffs, F::ZERO, (F::ONE, -F::ONE), -F::ONE, 0)?;
781 Ok(())
782 },
783 )
784 }
785
786 fn assert_equal_to_fixed(
787 &self,
788 layouter: &mut impl Layouter<F>,
789 x: &AssignedNative<F>,
790 constant: F,
791 ) -> Result<(), Error> {
792 let c = self.assign_fixed(layouter, constant)?;
793 self.assert_equal(layouter, x, &c)
794 }
795
796 fn assert_not_equal_to_fixed(
797 &self,
798 layouter: &mut impl Layouter<F>,
799 x: &AssignedNative<F>,
800 constant: F,
801 ) -> Result<(), Error> {
802 let (y, _) = self.assign_with_shifted_inverse(layouter, x.value().copied(), constant)?;
805 self.assert_equal(layouter, x, &y)
806 }
807}
808
809impl<F> AssertionInstructions<F, AssignedBit<F>> for NativeChip<F>
810where
811 F: CircuitField,
812{
813 fn assert_equal(
814 &self,
815 layouter: &mut impl Layouter<F>,
816 x: &AssignedBit<F>,
817 y: &AssignedBit<F>,
818 ) -> Result<(), Error> {
819 self.assert_equal(layouter, &x.0, &y.0)
820 }
821
822 fn assert_not_equal(
823 &self,
824 layouter: &mut impl Layouter<F>,
825 x: &AssignedBit<F>,
826 y: &AssignedBit<F>,
827 ) -> Result<(), Error> {
828 let diff = self.sub(layouter, &x.0, &y.0)?;
829 self.assert_non_zero(layouter, &diff)
830 }
831
832 fn assert_equal_to_fixed(
833 &self,
834 layouter: &mut impl Layouter<F>,
835 x: &AssignedBit<F>,
836 b: bool,
837 ) -> Result<(), Error> {
838 let constant = if b { F::ONE } else { F::ZERO };
839 self.assert_equal_to_fixed(layouter, &x.0, constant)
840 }
841
842 fn assert_not_equal_to_fixed(
843 &self,
844 layouter: &mut impl Layouter<F>,
845 x: &AssignedBit<F>,
846 constant: bool,
847 ) -> Result<(), Error> {
848 self.assert_equal_to_fixed(layouter, x, !constant)
849 }
850}
851
852impl<F> ZeroInstructions<F, AssignedNative<F>> for NativeChip<F> where F: CircuitField {}
853
854impl<F> ArithInstructions<F, AssignedNative<F>> for NativeChip<F>
855where
856 F: CircuitField,
857{
858 fn linear_combination(
859 &self,
860 layouter: &mut impl Layouter<F>,
861 terms: &[(F, AssignedNative<F>)],
862 constant: F,
863 ) -> Result<AssignedNative<F>, Error> {
864 let zero: AssignedNative<F> = self.assign_fixed(layouter, F::ZERO)?;
865
866 let terms: Vec<_> = terms
867 .iter()
868 .filter(|(c, x)| !F::is_zero_vartime(c) && *x != zero)
869 .cloned()
870 .collect();
871
872 if terms.is_empty() {
873 return self.assign_fixed(layouter, constant);
874 }
875
876 if let [(coeff, var)] = terms.as_slice() {
878 if *coeff == F::ONE && F::is_zero_vartime(&constant) {
879 return Ok(var.clone());
880 }
881 }
882
883 let term_values = terms
886 .iter()
887 .map(|(c, assigned_t)| (*c, assigned_t.value().copied()))
888 .collect::<Vec<_>>();
889
890 let result = terms.iter().fold(Value::known(constant), |acc, (coeff, x)| {
891 acc.zip(x.value()).map(|(acc, val)| acc + *coeff * val)
892 });
893
894 layouter.assign_region(
895 || "Linear combination",
896 |mut region| {
897 let mut offset = 0;
898 let (assigned_limbs, assigned_result) = self.assign_linear_combination_aux(
899 &mut region,
900 term_values.as_slice(),
901 constant,
902 &result,
903 NB_ARITH_COLS - 1,
904 &mut offset,
905 )?;
906
907 assert_eq!(assigned_limbs.len(), terms.len());
908
909 assigned_limbs.iter().zip(terms.iter()).try_for_each(|(new_av, (_, av))| {
911 region.constrain_equal(new_av.cell(), av.cell())
912 })?;
913
914 Ok(assigned_result)
915 },
916 )
917 }
918
919 fn mul(
920 &self,
921 layouter: &mut impl Layouter<F>,
922 x: &AssignedNative<F>,
923 y: &AssignedNative<F>,
924 multiplying_constant: Option<F>,
925 ) -> Result<AssignedNative<F>, Error> {
926 if multiplying_constant == Some(F::ZERO) {
927 return self.assign_fixed(layouter, F::ZERO);
928 }
929
930 let m = multiplying_constant.unwrap_or(F::ONE);
931
932 let one: AssignedNative<F> = self.assign_fixed(layouter, F::ONE)?;
933 if m == F::ONE && x.clone() == one {
934 return Ok(y.clone());
935 }
936 if m == F::ONE && y.clone() == one {
937 return Ok(x.clone());
938 }
939
940 self.add_and_mul(
941 layouter,
942 (F::ZERO, x),
943 (F::ZERO, y),
944 (F::ZERO, x),
945 F::ZERO,
946 m,
947 )
948 }
949
950 fn div(
951 &self,
952 layouter: &mut impl Layouter<F>,
953 x: &AssignedNative<F>,
954 y: &AssignedNative<F>,
955 ) -> Result<AssignedNative<F>, Error> {
956 let y_inv = self.inv(layouter, y)?;
957 self.mul(layouter, x, &y_inv, None)
958 }
959
960 fn inv(
961 &self,
962 layouter: &mut impl Layouter<F>,
963 x: &AssignedNative<F>,
964 ) -> Result<AssignedNative<F>, Error> {
965 let (y, inv) = self.assign_with_shifted_inverse(layouter, x.value().copied(), F::ZERO)?;
966 self.assert_equal(layouter, x, &y)?;
967 Ok(inv)
968 }
969
970 fn inv0(
971 &self,
972 layouter: &mut impl Layouter<F>,
973 x: &AssignedNative<F>,
974 ) -> Result<AssignedNative<F>, Error> {
975 let is_zero = self.is_zero(layouter, x)?;
976 let zero = self.assign_fixed(layouter, F::ZERO)?;
977 let one = self.assign_fixed(layouter, F::ONE)?;
978 let invertible = self.select(layouter, &is_zero, &one, x)?;
979 let inverse = self.inv(layouter, &invertible)?;
980 self.select(layouter, &is_zero, &zero, &inverse)
981 }
982
983 fn add_and_mul(
984 &self,
985 layouter: &mut impl Layouter<F>,
986 (a, x): (F, &AssignedNative<F>),
987 (b, y): (F, &AssignedNative<F>),
988 (c, z): (F, &AssignedNative<F>),
989 k: F,
990 m: F,
991 ) -> Result<AssignedNative<F>, Error> {
992 self.add_and_double_mul(layouter, (a, x), (b, y), (c, z), k, (m, F::ZERO))
993 }
994
995 fn add_constants(
996 &self,
997 layouter: &mut impl Layouter<F>,
998 xs: &[AssignedNative<F>],
999 constants: &[F],
1000 ) -> Result<Vec<AssignedNative<F>>, Error> {
1001 assert_eq!(xs.len(), constants.len());
1002
1003 let pairs = (xs.iter().zip(constants.iter()))
1004 .filter(|&(_, &c)| c != F::ZERO)
1005 .collect::<Vec<_>>();
1006
1007 let mut non_trivial_outputs = Vec::with_capacity(pairs.len());
1008
1009 let mut chunks = pairs.chunks_exact(NB_PARALLEL_ADD_COLS);
1010 for chunk in chunks.by_ref() {
1011 let outputs = layouter.assign_region(
1012 || "add_constants",
1013 |mut region| {
1014 let values = chunk.iter().map(|(x, _)| (*x).clone()).collect::<Vec<_>>();
1015 let consts = chunk.iter().map(|(_, c)| **c).collect::<Vec<_>>();
1016 self.add_constants_in_region(
1017 &mut region,
1018 &values.try_into().unwrap(),
1019 &consts.try_into().unwrap(),
1020 &mut 0,
1021 )
1022 },
1023 )?;
1024 non_trivial_outputs.extend(outputs);
1025 }
1026
1027 for (x, c) in chunks.remainder() {
1029 non_trivial_outputs.push(self.add_constant(layouter, x, **c)?);
1030 }
1031
1032 let mut outputs = Vec::with_capacity(xs.len());
1033 let mut j = 0;
1034 for i in 0..xs.len() {
1035 if constants[i] != F::ZERO {
1036 outputs.push(non_trivial_outputs[j].clone());
1037 j += 1;
1038 } else {
1039 outputs.push(xs[i].clone())
1040 }
1041 }
1042
1043 Ok(outputs)
1044 }
1045}
1046
1047impl<F: CircuitField> Instantiable<F> for AssignedBit<F> {
1048 fn as_public_input(element: &bool) -> Vec<F> {
1049 vec![if *element { F::ONE } else { F::ZERO }]
1050 }
1051
1052 fn from_public_input(fields: &[F]) -> Option<bool> {
1053 match fields {
1054 [f] if *f == F::ZERO => Some(false),
1055 [f] if *f == F::ONE => Some(true),
1056 _ => None,
1057 }
1058 }
1059}
1060
1061#[derive(Clone, Debug, PartialEq, Eq)]
1066#[must_use]
1067pub struct AssignedBit<F: CircuitField>(pub(crate) AssignedNative<F>);
1068
1069impl<F: CircuitField> Hash for AssignedBit<F> {
1070 fn hash<H: Hasher>(&self, state: &mut H) {
1071 self.0.hash(state)
1072 }
1073}
1074
1075impl<F: CircuitField> InnerValue for AssignedBit<F> {
1076 type Element = bool;
1077
1078 fn value(&self) -> Value<bool> {
1079 self.0.value().map(|b| !F::is_zero_vartime(b))
1080 }
1081}
1082
1083impl<F: CircuitField> From<AssignedBit<F>> for AssignedNative<F> {
1084 fn from(bit: AssignedBit<F>) -> Self {
1085 bit.0
1086 }
1087}
1088
1089impl<F> ConversionInstructions<F, AssignedNative<F>, AssignedBit<F>> for NativeChip<F>
1090where
1091 F: CircuitField,
1092{
1093 fn convert_value(&self, x: &F) -> Option<bool> {
1094 let is_zero: bool = F::is_zero(x).into();
1095 Some(!is_zero)
1096 }
1097
1098 fn convert(
1099 &self,
1100 layouter: &mut impl Layouter<F>,
1101 x: &AssignedNative<F>,
1102 ) -> Result<AssignedBit<F>, Error> {
1103 let b_value = x.value().map(|v| !F::is_zero_vartime(v));
1104 let b: AssignedBit<F> = self.assign(layouter, b_value)?;
1105 self.assert_equal(layouter, x, &b.0)?;
1106 Ok(b)
1107 }
1108}
1109
1110impl<F> ConversionInstructions<F, AssignedBit<F>, AssignedNative<F>> for NativeChip<F>
1111where
1112 F: CircuitField,
1113{
1114 fn convert_value(&self, x: &bool) -> Option<F> {
1115 Some(if *x { F::from(1) } else { F::from(0) })
1116 }
1117
1118 fn convert(
1119 &self,
1120 _layouter: &mut impl Layouter<F>,
1121 bit: &AssignedBit<F>,
1122 ) -> Result<AssignedNative<F>, Error> {
1123 Ok(bit.clone().0)
1124 }
1125}
1126
1127impl<F> UnsafeConversionInstructions<F, AssignedNative<F>, AssignedBit<F>> for NativeChip<F>
1128where
1129 F: CircuitField,
1130{
1131 fn convert_unsafe(
1140 &self,
1141 _layouter: &mut impl Layouter<F>,
1142 x: &AssignedNative<F>,
1143 ) -> Result<AssignedBit<F>, Error> {
1144 #[cfg(not(test))]
1145 x.value().map(|&x| {
1146 assert!(
1147 x == F::ZERO || x == F::ONE,
1148 "Trying to convert {:?} to an AssignedBit!",
1149 x
1150 );
1151 });
1152 Ok(AssignedBit(x.clone()))
1153 }
1154}
1155
1156#[cfg(test)]
1157impl<F> UnsafeConversionInstructions<F, AssignedBit<F>, AssignedNative<F>> for NativeChip<F>
1158where
1159 F: CircuitField,
1160{
1161 fn convert_unsafe(
1162 &self,
1163 layouter: &mut impl Layouter<F>,
1164 x: &AssignedBit<F>,
1165 ) -> Result<AssignedNative<F>, Error> {
1166 self.convert(layouter, x)
1167 }
1168}
1169
1170impl<F> BinaryInstructions<F> for NativeChip<F>
1171where
1172 F: CircuitField,
1173{
1174 fn and(
1175 &self,
1176 layouter: &mut impl Layouter<F>,
1177 bits: &[AssignedBit<F>],
1178 ) -> Result<AssignedBit<F>, Error> {
1179 let mut acc = bits.first().unwrap().0.clone();
1180 for b in bits.iter().skip(1) {
1181 acc = self.mul(layouter, &acc, &b.0, None)?;
1182 }
1183 Ok(AssignedBit(acc))
1184 }
1185
1186 fn or(
1187 &self,
1188 layouter: &mut impl Layouter<F>,
1189 bits: &[AssignedBit<F>],
1190 ) -> Result<AssignedBit<F>, Error> {
1191 let mut acc = bits.first().unwrap().0.clone();
1192 for b in bits.iter().skip(1) {
1193 acc = self.add_and_mul(
1195 layouter,
1196 (F::ONE, &acc),
1197 (F::ONE, &b.0),
1198 (F::ZERO, &acc),
1199 F::ZERO,
1200 -F::ONE,
1201 )?;
1202 }
1203 Ok(AssignedBit(acc))
1204 }
1205
1206 fn xor(
1207 &self,
1208 layouter: &mut impl Layouter<F>,
1209 bits: &[AssignedBit<F>],
1210 ) -> Result<AssignedBit<F>, Error> {
1211 let mut acc = bits.first().unwrap().0.clone();
1212 for b in bits.iter().skip(1) {
1213 acc = self.add_and_mul(
1215 layouter,
1216 (F::ONE, &acc),
1217 (F::ONE, &b.0),
1218 (F::ZERO, &acc),
1219 F::ZERO,
1220 -F::from(2),
1221 )?;
1222 }
1223 Ok(AssignedBit(acc))
1224 }
1225
1226 fn not(
1227 &self,
1228 layouter: &mut impl Layouter<F>,
1229 bit: &AssignedBit<F>,
1230 ) -> Result<AssignedBit<F>, Error> {
1231 let neg_bit = self.linear_combination(layouter, &[(-F::ONE, bit.0.clone())], F::ONE)?;
1232 Ok(AssignedBit(neg_bit))
1233 }
1234}
1235
1236impl<F> EqualityInstructions<F, AssignedNative<F>> for NativeChip<F>
1237where
1238 F: CircuitField + From<u64> + Neg<Output = F>,
1239{
1240 fn is_equal(
1241 &self,
1242 layouter: &mut impl Layouter<F>,
1243 x: &AssignedNative<F>,
1244 y: &AssignedNative<F>,
1245 ) -> Result<AssignedBit<F>, Error> {
1246 let value_cols = &self.config.value_cols;
1252 let res_val = x.value().zip(y.value()).map(|(x, y)| F::from((x == y) as u64));
1253 let aux_val = x.value().zip(y.value()).map(|(x, y)| (*x - *y).invert().unwrap_or(F::ONE));
1254
1255 let res = layouter.assign_region(
1257 || "is_equal (i)",
1258 |mut region| {
1259 region.assign_advice(|| "aux", value_cols[0], 0, || aux_val)?;
1260 self.copy_in_row(&mut region, x, &value_cols[1], 0)?;
1261 self.copy_in_row(&mut region, y, &value_cols[2], 0)?;
1262 let res = region.assign_advice(|| "res", value_cols[4], 0, || res_val)?;
1263 let mut coeffs = [F::ZERO; NB_ARITH_COLS];
1264 coeffs[4] = F::ONE; self.custom(&mut region, &coeffs, F::ZERO, (F::ONE, -F::ONE), -F::ONE, 0)?;
1266 Ok(res)
1267 },
1268 )?;
1269
1270 let must_be_zero = self.add_and_double_mul(
1272 layouter,
1273 (F::ZERO, &res),
1274 (F::ZERO, x),
1275 (F::ZERO, y),
1276 F::ZERO,
1277 (F::ONE, -F::ONE),
1278 )?;
1279 self.assert_zero(layouter, &must_be_zero)?;
1280
1281 Ok(AssignedBit(res))
1283 }
1284
1285 fn is_not_equal(
1286 &self,
1287 layouter: &mut impl Layouter<F>,
1288 x: &AssignedNative<F>,
1289 y: &AssignedNative<F>,
1290 ) -> Result<AssignedBit<F>, Error> {
1291 let value_cols = &self.config.value_cols;
1297 let res_val = x.value().zip(y.value()).map(|(x, y)| F::from((x != y) as u64));
1298 let aux_val = x.value().zip(y.value()).map(|(x, y)| (*x - *y).invert().unwrap_or(F::ONE));
1299
1300 let res = layouter.assign_region(
1302 || "is_not_equal (i)",
1303 |mut region| {
1304 region.assign_advice(|| "aux", value_cols[0], 0, || aux_val)?;
1305 self.copy_in_row(&mut region, x, &value_cols[1], 0)?;
1306 self.copy_in_row(&mut region, y, &value_cols[2], 0)?;
1307 let res = region.assign_advice(|| "res", value_cols[4], 0, || res_val)?;
1308 let mut coeffs = [F::ZERO; NB_ARITH_COLS];
1309 coeffs[4] = -F::ONE; self.custom(&mut region, &coeffs, F::ZERO, (F::ONE, -F::ONE), F::ZERO, 0)?;
1311 Ok(res)
1312 },
1313 )?;
1314
1315 let must_be_zero = self.add_and_double_mul(
1317 layouter,
1318 (F::ZERO, &res),
1319 (F::ONE, x),
1320 (-F::ONE, y),
1321 F::ZERO,
1322 (-F::ONE, F::ONE),
1323 )?;
1324 self.assert_zero(layouter, &must_be_zero)?;
1325
1326 Ok(AssignedBit(res))
1328 }
1329
1330 fn is_equal_to_fixed(
1331 &self,
1332 layouter: &mut impl Layouter<F>,
1333 x: &AssignedNative<F>,
1334 c: F,
1335 ) -> Result<AssignedBit<F>, Error> {
1336 let value_cols = &self.config.value_cols;
1342 let res_val = x.value().map(|x| F::from((*x == c) as u64));
1343 let aux_val = x.value().map(|x| (*x - c).invert().unwrap_or(F::ONE));
1344
1345 let res = layouter.assign_region(
1347 || "is_equal (i)",
1348 |mut region| {
1349 region.assign_advice(|| "aux", value_cols[0], 0, || aux_val)?;
1350 self.copy_in_row(&mut region, x, &value_cols[1], 0)?;
1351 let res = region.assign_advice(|| "res", value_cols[4], 0, || res_val)?;
1352 let mut coeffs = [F::ZERO; NB_ARITH_COLS];
1353 coeffs[0] = -c; coeffs[4] = F::ONE; self.custom(&mut region, &coeffs, F::ZERO, (F::ONE, F::ZERO), -F::ONE, 0)?;
1356 Ok(res)
1357 },
1358 )?;
1359
1360 let must_be_zero = self.add_and_mul(
1362 layouter,
1363 (-c, &res),
1364 (F::ZERO, x),
1365 (F::ZERO, x),
1366 F::ZERO,
1367 F::ONE,
1368 )?;
1369 self.assert_zero(layouter, &must_be_zero)?;
1370
1371 Ok(AssignedBit(res))
1373 }
1374
1375 fn is_not_equal_to_fixed(
1376 &self,
1377 layouter: &mut impl Layouter<F>,
1378 x: &AssignedNative<F>,
1379 c: F,
1380 ) -> Result<AssignedBit<F>, Error> {
1381 let value_cols = &self.config.value_cols;
1387 let res_val = x.value().map(|x| F::from((*x != c) as u64));
1388 let aux_val = x.value().map(|x| (*x - c).invert().unwrap_or(F::ONE));
1389
1390 let res = layouter.assign_region(
1392 || "is_not_equal (i)",
1393 |mut region| {
1394 region.assign_advice(|| "aux", value_cols[0], 0, || aux_val)?;
1395 self.copy_in_row(&mut region, x, &value_cols[1], 0)?;
1396 let res = region.assign_advice(|| "res", value_cols[4], 0, || res_val)?;
1397 let mut coeffs = [F::ZERO; NB_ARITH_COLS];
1398 coeffs[0] = -c; coeffs[4] = -F::ONE; self.custom(&mut region, &coeffs, F::ZERO, (F::ONE, F::ZERO), F::ZERO, 0)?;
1401 Ok(res)
1402 },
1403 )?;
1404
1405 let must_be_zero =
1407 self.add_and_mul(layouter, (c, &res), (F::ONE, x), (F::ZERO, x), -c, -F::ONE)?;
1408 self.assert_zero(layouter, &must_be_zero)?;
1409
1410 Ok(AssignedBit(res))
1412 }
1413}
1414
1415impl<F> EqualityInstructions<F, AssignedBit<F>> for NativeChip<F>
1416where
1417 F: CircuitField + From<u64> + Neg<Output = F>,
1418{
1419 fn is_equal(
1420 &self,
1421 layouter: &mut impl Layouter<F>,
1422 a: &AssignedBit<F>,
1423 b: &AssignedBit<F>,
1424 ) -> Result<AssignedBit<F>, Error> {
1425 self.add_and_mul(
1427 layouter,
1428 (-F::ONE, &a.0),
1429 (-F::ONE, &b.0),
1430 (F::ZERO, &a.0),
1431 F::ONE,
1432 F::from(2),
1433 )
1434 .map(AssignedBit)
1435 }
1436
1437 fn is_not_equal(
1438 &self,
1439 layouter: &mut impl Layouter<F>,
1440 a: &AssignedBit<F>,
1441 b: &AssignedBit<F>,
1442 ) -> Result<AssignedBit<F>, Error> {
1443 self.add_and_mul(
1445 layouter,
1446 (F::ONE, &a.0),
1447 (F::ONE, &b.0),
1448 (F::ZERO, &a.0),
1449 F::ZERO,
1450 -F::from(2),
1451 )
1452 .map(AssignedBit)
1453 }
1454
1455 fn is_equal_to_fixed(
1456 &self,
1457 layouter: &mut impl Layouter<F>,
1458 b: &AssignedBit<F>,
1459 constant: bool,
1460 ) -> Result<AssignedBit<F>, Error> {
1461 let assigned_constant = self.assign_fixed(layouter, constant)?;
1462 self.is_equal(layouter, b, &assigned_constant)
1463 }
1464
1465 fn is_not_equal_to_fixed(
1466 &self,
1467 layouter: &mut impl Layouter<F>,
1468 b: &AssignedBit<F>,
1469 constant: bool,
1470 ) -> Result<AssignedBit<F>, Error> {
1471 let assigned_constant = self.assign_fixed(layouter, constant)?;
1472 self.is_not_equal(layouter, b, &assigned_constant)
1473 }
1474}
1475
1476impl<F> ControlFlowInstructions<F, AssignedNative<F>> for NativeChip<F>
1477where
1478 F: CircuitField + From<u64> + Neg<Output = F>,
1479{
1480 fn select(
1481 &self,
1482 layouter: &mut impl Layouter<F>,
1483 cond: &AssignedBit<F>,
1484 x: &AssignedNative<F>,
1485 y: &AssignedNative<F>,
1486 ) -> Result<AssignedNative<F>, Error> {
1487 self.add_and_double_mul(
1491 layouter,
1492 (F::ZERO, &cond.0),
1493 (F::ZERO, x),
1494 (F::ONE, y),
1495 F::ZERO,
1496 (F::ONE, -F::ONE),
1497 )
1498 }
1499
1500 fn cond_swap(
1501 &self,
1502 layouter: &mut impl Layouter<F>,
1503 cond: &AssignedBit<F>,
1504 x: &AssignedNative<F>,
1505 y: &AssignedNative<F>,
1506 ) -> Result<(AssignedNative<F>, AssignedNative<F>), Error> {
1507 let val2 = (x.value().zip(y.value()).zip(cond.value()))
1513 .map(|((x, y), b)| if b { x } else { y })
1514 .copied();
1515 let val1 = x.value().copied() + y.value().copied() - val2;
1516
1517 layouter.assign_region(
1521 || "cond swap",
1522 |mut region| {
1523 self.copy_in_row(&mut region, &cond.0, &self.config.value_cols[0], 0)?;
1524 self.copy_in_row(&mut region, x, &self.config.value_cols[1], 0)?;
1525 self.copy_in_row(&mut region, y, &self.config.value_cols[2], 0)?;
1526 let fst = region.assign_advice(|| "fst", self.config.value_cols[3], 0, || val1)?;
1527 let snd = region.assign_advice(|| "snd", self.config.value_cols[4], 0, || val2)?;
1528 let mut coeffs = [F::ZERO; NB_ARITH_COLS];
1529 coeffs[2] = F::ONE; coeffs[4] = -F::ONE; self.custom(&mut region, &coeffs, F::ZERO, (F::ONE, -F::ONE), F::ZERO, 0)?;
1532 self.config.q_12_minus_34.enable(&mut region, 0)?;
1533 Ok((fst, snd))
1534 },
1535 )
1536 }
1537}
1538
1539impl<F> ControlFlowInstructions<F, AssignedBit<F>> for NativeChip<F>
1540where
1541 F: CircuitField,
1542{
1543 fn select(
1544 &self,
1545 layouter: &mut impl Layouter<F>,
1546 cond: &AssignedBit<F>,
1547 x: &AssignedBit<F>,
1548 y: &AssignedBit<F>,
1549 ) -> Result<AssignedBit<F>, Error> {
1550 self.select(layouter, cond, &x.0, &y.0).map(AssignedBit)
1551 }
1552
1553 fn cond_swap(
1554 &self,
1555 layouter: &mut impl Layouter<F>,
1556 cond: &AssignedBit<F>,
1557 x: &AssignedBit<F>,
1558 y: &AssignedBit<F>,
1559 ) -> Result<(AssignedBit<F>, AssignedBit<F>), Error> {
1560 self.cond_swap(layouter, cond, &x.0, &y.0)
1561 .map(|(fst, snd)| (AssignedBit(fst), AssignedBit(snd)))
1562 }
1563}
1564
1565impl<F> FieldInstructions<F, AssignedNative<F>> for NativeChip<F>
1566where
1567 F: CircuitField,
1568{
1569 fn order(&self) -> BigUint {
1570 F::modulus()
1571 }
1572}
1573
1574impl<F> CanonicityInstructions<F, AssignedNative<F>> for NativeChip<F>
1575where
1576 F: CircuitField,
1577{
1578 fn le_bits_lower_than(
1579 &self,
1580 layouter: &mut impl Layouter<F>,
1581 bits: &[AssignedBit<F>],
1582 bound: BigUint,
1583 ) -> Result<AssignedBit<F>, Error> {
1584 let geq = self.le_bits_geq_than(layouter, bits, bound)?;
1585 self.not(layouter, &geq)
1586 }
1587
1588 fn le_bits_geq_than(
1589 &self,
1590 layouter: &mut impl Layouter<F>,
1591 bits: &[AssignedBit<F>],
1592 bound: BigUint,
1593 ) -> Result<AssignedBit<F>, Error> {
1594 if bound.is_zero() {
1596 return self.assign_fixed(layouter, true);
1597 }
1598
1599 if bits.len() < bound.bits() as usize {
1601 return self.assign_fixed(layouter, false);
1602 }
1603
1604 if bits.len() == 1 {
1609 return Ok(bits[0].clone());
1610 }
1611
1612 let msb_pos = bits.len() - 1;
1613
1614 let rest_is_geq = {
1615 let mut rest_bound = bound.clone();
1616 rest_bound.set_bit(msb_pos as u64, false);
1617 self.le_bits_geq_than(layouter, &bits[0..msb_pos], rest_bound)?
1618 };
1619
1620 if bound.bit(msb_pos as u64) {
1621 self.and(layouter, &[bits[msb_pos].clone(), rest_is_geq])
1622 } else {
1623 self.or(layouter, &[bits[msb_pos].clone(), rest_is_geq])
1624 }
1625 }
1626}
1627
1628#[cfg(any(test, feature = "testing"))]
1629impl<F: CircuitField> FromScratch<F> for NativeChip<F> {
1630 type Config = NativeConfig;
1631
1632 fn new_from_scratch(config: &Self::Config) -> Self {
1633 NativeChip::new(config, &())
1634 }
1635
1636 fn configure_from_scratch(
1637 meta: &mut ConstraintSystem<F>,
1638 advice_columns: &mut Vec<Column<Advice>>,
1639 fixed_columns: &mut Vec<Column<Fixed>>,
1640 instance_columns: &[Column<Instance>; 2],
1641 ) -> Self::Config {
1642 while advice_columns.len() < NB_ARITH_COLS {
1643 advice_columns.push(meta.advice_column());
1644 }
1645 while fixed_columns.len() < NB_ARITH_FIXED_COLS {
1646 fixed_columns.push(meta.fixed_column());
1647 }
1648 let advice_cols: [_; NB_ARITH_COLS] = advice_columns[..NB_ARITH_COLS].try_into().unwrap();
1649 let fixed_cols: [_; NB_ARITH_FIXED_COLS] =
1650 fixed_columns[..NB_ARITH_FIXED_COLS].try_into().unwrap();
1651 NativeChip::configure(meta, &(advice_cols, fixed_cols, *instance_columns))
1652 }
1653
1654 fn load_from_scratch(
1655 &self,
1656 _layouter: &mut impl midnight_proofs::circuit::Layouter<F>,
1657 ) -> Result<(), Error> {
1658 Ok(())
1659 }
1660}
1661
1662#[cfg(test)]
1663mod tests {
1664 use ff::FromUniformBytes;
1665 use midnight_curves::Fq as BlsScalar;
1666
1667 use super::*;
1668 use crate::instructions::{
1669 arithmetic, assertions, binary, canonicity, control_flow,
1670 conversions::{
1671 self,
1672 tests::Operation::{Convert, UnsafeConvert},
1673 },
1674 equality, public_input, zero,
1675 };
1676
1677 macro_rules! test {
1678 ($mod:ident, $op:ident) => {
1679 #[test]
1680 fn $op() {
1681 $mod::tests::$op::<BlsScalar, AssignedNative<BlsScalar>, NativeChip<BlsScalar>>(
1682 "native_chip",
1683 );
1684 }
1685 };
1686 }
1687 test!(assertions, test_assertions);
1688
1689 test!(public_input, test_public_inputs);
1690
1691 test!(arithmetic, test_add);
1692 test!(arithmetic, test_sub);
1693 test!(arithmetic, test_mul);
1694 test!(arithmetic, test_div);
1695 test!(arithmetic, test_neg);
1696 test!(arithmetic, test_inv);
1697 test!(arithmetic, test_pow);
1698 test!(arithmetic, test_linear_combination);
1699 test!(arithmetic, test_add_and_mul);
1700
1701 test!(equality, test_is_equal);
1702
1703 test!(zero, test_zero_assertions);
1704 test!(zero, test_is_zero);
1705
1706 test!(control_flow, test_select);
1707 test!(control_flow, test_cond_assert_equal);
1708 test!(control_flow, test_cond_swap);
1709
1710 test!(canonicity, test_canonical);
1711 test!(canonicity, test_le_bits_lower_and_geq);
1712
1713 macro_rules! test {
1714 ($mod:ident, $op:ident) => {
1715 #[test]
1716 fn $op() {
1717 $mod::tests::$op::<BlsScalar, NativeChip<BlsScalar>>("native_chip");
1718 }
1719 };
1720 }
1721
1722 test!(binary, test_and);
1723 test!(binary, test_or);
1724 test!(binary, test_xor);
1725 test!(binary, test_not);
1726
1727 fn test_generic_conversion_to_bit<F>(name: &str)
1728 where
1729 F: CircuitField + FromUniformBytes<64> + Ord,
1730 {
1731 [
1732 (
1733 F::ZERO,
1734 Some(false),
1735 Convert,
1736 true,
1737 true,
1738 name,
1739 "convert_to_bit",
1740 ),
1741 (F::ZERO, Some(true), Convert, false, false, "", ""),
1742 (F::ONE, Some(true), Convert, true, false, "", ""),
1743 (F::ONE, Some(false), Convert, false, false, "", ""),
1744 (F::from(2), None, Convert, false, false, "", ""),
1745 (
1746 F::from(2),
1747 None,
1748 UnsafeConvert,
1749 true,
1750 true,
1751 name,
1752 "unsafe_convert_to_bit",
1753 ),
1754 ]
1755 .into_iter()
1756 .for_each(
1757 |(x, expected, operation, must_pass, cost_model, chip_name, op_name)| {
1758 conversions::tests::run::<F, AssignedNative<F>, AssignedBit<F>, NativeChip<F>>(
1759 x, expected, operation, must_pass, cost_model, chip_name, op_name,
1760 )
1761 },
1762 );
1763 }
1764
1765 #[test]
1766 fn test_conversion_to_bit() {
1767 test_generic_conversion_to_bit::<BlsScalar>("native_chip")
1768 }
1769
1770 fn test_generic_conversion_from_bit<F>(name: &str)
1771 where
1772 F: CircuitField + FromUniformBytes<64> + Ord,
1773 {
1774 [
1775 (
1776 false,
1777 Some(F::ZERO),
1778 Convert,
1779 true,
1780 true,
1781 name,
1782 "convert_from_bit",
1783 ),
1784 (false, Some(F::ONE), Convert, false, false, "", ""),
1785 (true, Some(F::ONE), Convert, true, false, "", ""),
1786 (true, Some(F::ZERO), Convert, false, false, "", ""),
1787 (
1788 false,
1789 None,
1790 UnsafeConvert,
1791 true,
1792 true,
1793 name,
1794 "unsafe_convert_from_bit",
1795 ),
1796 (true, None, UnsafeConvert, true, false, "", ""),
1797 ]
1798 .into_iter()
1799 .for_each(
1800 |(x, expected, operation, must_pass, cost_model, chip_name, op_name)| {
1801 conversions::tests::run::<F, AssignedBit<F>, AssignedNative<F>, NativeChip<F>>(
1802 x, expected, operation, must_pass, cost_model, chip_name, op_name,
1803 )
1804 },
1805 );
1806 }
1807
1808 #[test]
1809 fn test_conversion_from_bit() {
1810 test_generic_conversion_from_bit::<BlsScalar>("native_chip");
1811 }
1812}