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
1053#[derive(Clone, Debug, PartialEq, Eq)]
1058#[must_use]
1059pub struct AssignedBit<F: CircuitField>(pub(crate) AssignedNative<F>);
1060
1061impl<F: CircuitField> Hash for AssignedBit<F> {
1062 fn hash<H: Hasher>(&self, state: &mut H) {
1063 self.0.hash(state)
1064 }
1065}
1066
1067impl<F: CircuitField> InnerValue for AssignedBit<F> {
1068 type Element = bool;
1069
1070 fn value(&self) -> Value<bool> {
1071 self.0.value().map(|b| !F::is_zero_vartime(b))
1072 }
1073}
1074
1075impl<F: CircuitField> From<AssignedBit<F>> for AssignedNative<F> {
1076 fn from(bit: AssignedBit<F>) -> Self {
1077 bit.0
1078 }
1079}
1080
1081impl<F> ConversionInstructions<F, AssignedNative<F>, AssignedBit<F>> for NativeChip<F>
1082where
1083 F: CircuitField,
1084{
1085 fn convert_value(&self, x: &F) -> Option<bool> {
1086 let is_zero: bool = F::is_zero(x).into();
1087 Some(!is_zero)
1088 }
1089
1090 fn convert(
1091 &self,
1092 layouter: &mut impl Layouter<F>,
1093 x: &AssignedNative<F>,
1094 ) -> Result<AssignedBit<F>, Error> {
1095 let b_value = x.value().map(|v| !F::is_zero_vartime(v));
1096 let b: AssignedBit<F> = self.assign(layouter, b_value)?;
1097 self.assert_equal(layouter, x, &b.0)?;
1098 Ok(b)
1099 }
1100}
1101
1102impl<F> ConversionInstructions<F, AssignedBit<F>, AssignedNative<F>> for NativeChip<F>
1103where
1104 F: CircuitField,
1105{
1106 fn convert_value(&self, x: &bool) -> Option<F> {
1107 Some(if *x { F::from(1) } else { F::from(0) })
1108 }
1109
1110 fn convert(
1111 &self,
1112 _layouter: &mut impl Layouter<F>,
1113 bit: &AssignedBit<F>,
1114 ) -> Result<AssignedNative<F>, Error> {
1115 Ok(bit.clone().0)
1116 }
1117}
1118
1119impl<F> UnsafeConversionInstructions<F, AssignedNative<F>, AssignedBit<F>> for NativeChip<F>
1120where
1121 F: CircuitField,
1122{
1123 fn convert_unsafe(
1132 &self,
1133 _layouter: &mut impl Layouter<F>,
1134 x: &AssignedNative<F>,
1135 ) -> Result<AssignedBit<F>, Error> {
1136 #[cfg(not(test))]
1137 x.value().map(|&x| {
1138 assert!(
1139 x == F::ZERO || x == F::ONE,
1140 "Trying to convert {:?} to an AssignedBit!",
1141 x
1142 );
1143 });
1144 Ok(AssignedBit(x.clone()))
1145 }
1146}
1147
1148#[cfg(test)]
1149impl<F> UnsafeConversionInstructions<F, AssignedBit<F>, AssignedNative<F>> for NativeChip<F>
1150where
1151 F: CircuitField,
1152{
1153 fn convert_unsafe(
1154 &self,
1155 layouter: &mut impl Layouter<F>,
1156 x: &AssignedBit<F>,
1157 ) -> Result<AssignedNative<F>, Error> {
1158 self.convert(layouter, x)
1159 }
1160}
1161
1162impl<F> BinaryInstructions<F> for NativeChip<F>
1163where
1164 F: CircuitField,
1165{
1166 fn and(
1167 &self,
1168 layouter: &mut impl Layouter<F>,
1169 bits: &[AssignedBit<F>],
1170 ) -> Result<AssignedBit<F>, Error> {
1171 let mut acc = bits.first().unwrap().0.clone();
1172 for b in bits.iter().skip(1) {
1173 acc = self.mul(layouter, &acc, &b.0, None)?;
1174 }
1175 Ok(AssignedBit(acc))
1176 }
1177
1178 fn or(
1179 &self,
1180 layouter: &mut impl Layouter<F>,
1181 bits: &[AssignedBit<F>],
1182 ) -> Result<AssignedBit<F>, Error> {
1183 let mut acc = bits.first().unwrap().0.clone();
1184 for b in bits.iter().skip(1) {
1185 acc = self.add_and_mul(
1187 layouter,
1188 (F::ONE, &acc),
1189 (F::ONE, &b.0),
1190 (F::ZERO, &acc),
1191 F::ZERO,
1192 -F::ONE,
1193 )?;
1194 }
1195 Ok(AssignedBit(acc))
1196 }
1197
1198 fn xor(
1199 &self,
1200 layouter: &mut impl Layouter<F>,
1201 bits: &[AssignedBit<F>],
1202 ) -> Result<AssignedBit<F>, Error> {
1203 let mut acc = bits.first().unwrap().0.clone();
1204 for b in bits.iter().skip(1) {
1205 acc = self.add_and_mul(
1207 layouter,
1208 (F::ONE, &acc),
1209 (F::ONE, &b.0),
1210 (F::ZERO, &acc),
1211 F::ZERO,
1212 -F::from(2),
1213 )?;
1214 }
1215 Ok(AssignedBit(acc))
1216 }
1217
1218 fn not(
1219 &self,
1220 layouter: &mut impl Layouter<F>,
1221 bit: &AssignedBit<F>,
1222 ) -> Result<AssignedBit<F>, Error> {
1223 let neg_bit = self.linear_combination(layouter, &[(-F::ONE, bit.0.clone())], F::ONE)?;
1224 Ok(AssignedBit(neg_bit))
1225 }
1226}
1227
1228impl<F> EqualityInstructions<F, AssignedNative<F>> for NativeChip<F>
1229where
1230 F: CircuitField + From<u64> + Neg<Output = F>,
1231{
1232 fn is_equal(
1233 &self,
1234 layouter: &mut impl Layouter<F>,
1235 x: &AssignedNative<F>,
1236 y: &AssignedNative<F>,
1237 ) -> Result<AssignedBit<F>, Error> {
1238 let value_cols = &self.config.value_cols;
1244 let res_val = x.value().zip(y.value()).map(|(x, y)| F::from((x == y) as u64));
1245 let aux_val = x.value().zip(y.value()).map(|(x, y)| (*x - *y).invert().unwrap_or(F::ONE));
1246
1247 let res = layouter.assign_region(
1249 || "is_equal (i)",
1250 |mut region| {
1251 region.assign_advice(|| "aux", value_cols[0], 0, || aux_val)?;
1252 self.copy_in_row(&mut region, x, &value_cols[1], 0)?;
1253 self.copy_in_row(&mut region, y, &value_cols[2], 0)?;
1254 let res = region.assign_advice(|| "res", value_cols[4], 0, || res_val)?;
1255 let mut coeffs = [F::ZERO; NB_ARITH_COLS];
1256 coeffs[4] = F::ONE; self.custom(&mut region, &coeffs, F::ZERO, (F::ONE, -F::ONE), -F::ONE, 0)?;
1258 Ok(res)
1259 },
1260 )?;
1261
1262 let must_be_zero = self.add_and_double_mul(
1264 layouter,
1265 (F::ZERO, &res),
1266 (F::ZERO, x),
1267 (F::ZERO, y),
1268 F::ZERO,
1269 (F::ONE, -F::ONE),
1270 )?;
1271 self.assert_zero(layouter, &must_be_zero)?;
1272
1273 Ok(AssignedBit(res))
1275 }
1276
1277 fn is_not_equal(
1278 &self,
1279 layouter: &mut impl Layouter<F>,
1280 x: &AssignedNative<F>,
1281 y: &AssignedNative<F>,
1282 ) -> Result<AssignedBit<F>, Error> {
1283 let value_cols = &self.config.value_cols;
1289 let res_val = x.value().zip(y.value()).map(|(x, y)| F::from((x != y) as u64));
1290 let aux_val = x.value().zip(y.value()).map(|(x, y)| (*x - *y).invert().unwrap_or(F::ONE));
1291
1292 let res = layouter.assign_region(
1294 || "is_not_equal (i)",
1295 |mut region| {
1296 region.assign_advice(|| "aux", value_cols[0], 0, || aux_val)?;
1297 self.copy_in_row(&mut region, x, &value_cols[1], 0)?;
1298 self.copy_in_row(&mut region, y, &value_cols[2], 0)?;
1299 let res = region.assign_advice(|| "res", value_cols[4], 0, || res_val)?;
1300 let mut coeffs = [F::ZERO; NB_ARITH_COLS];
1301 coeffs[4] = -F::ONE; self.custom(&mut region, &coeffs, F::ZERO, (F::ONE, -F::ONE), F::ZERO, 0)?;
1303 Ok(res)
1304 },
1305 )?;
1306
1307 let must_be_zero = self.add_and_double_mul(
1309 layouter,
1310 (F::ZERO, &res),
1311 (F::ONE, x),
1312 (-F::ONE, y),
1313 F::ZERO,
1314 (-F::ONE, F::ONE),
1315 )?;
1316 self.assert_zero(layouter, &must_be_zero)?;
1317
1318 Ok(AssignedBit(res))
1320 }
1321
1322 fn is_equal_to_fixed(
1323 &self,
1324 layouter: &mut impl Layouter<F>,
1325 x: &AssignedNative<F>,
1326 c: F,
1327 ) -> Result<AssignedBit<F>, Error> {
1328 let value_cols = &self.config.value_cols;
1334 let res_val = x.value().map(|x| F::from((*x == c) as u64));
1335 let aux_val = x.value().map(|x| (*x - c).invert().unwrap_or(F::ONE));
1336
1337 let res = layouter.assign_region(
1339 || "is_equal (i)",
1340 |mut region| {
1341 region.assign_advice(|| "aux", value_cols[0], 0, || aux_val)?;
1342 self.copy_in_row(&mut region, x, &value_cols[1], 0)?;
1343 let res = region.assign_advice(|| "res", value_cols[4], 0, || res_val)?;
1344 let mut coeffs = [F::ZERO; NB_ARITH_COLS];
1345 coeffs[0] = -c; coeffs[4] = F::ONE; self.custom(&mut region, &coeffs, F::ZERO, (F::ONE, F::ZERO), -F::ONE, 0)?;
1348 Ok(res)
1349 },
1350 )?;
1351
1352 let must_be_zero = self.add_and_mul(
1354 layouter,
1355 (-c, &res),
1356 (F::ZERO, x),
1357 (F::ZERO, x),
1358 F::ZERO,
1359 F::ONE,
1360 )?;
1361 self.assert_zero(layouter, &must_be_zero)?;
1362
1363 Ok(AssignedBit(res))
1365 }
1366
1367 fn is_not_equal_to_fixed(
1368 &self,
1369 layouter: &mut impl Layouter<F>,
1370 x: &AssignedNative<F>,
1371 c: F,
1372 ) -> Result<AssignedBit<F>, Error> {
1373 let value_cols = &self.config.value_cols;
1379 let res_val = x.value().map(|x| F::from((*x != c) as u64));
1380 let aux_val = x.value().map(|x| (*x - c).invert().unwrap_or(F::ONE));
1381
1382 let res = layouter.assign_region(
1384 || "is_not_equal (i)",
1385 |mut region| {
1386 region.assign_advice(|| "aux", value_cols[0], 0, || aux_val)?;
1387 self.copy_in_row(&mut region, x, &value_cols[1], 0)?;
1388 let res = region.assign_advice(|| "res", value_cols[4], 0, || res_val)?;
1389 let mut coeffs = [F::ZERO; NB_ARITH_COLS];
1390 coeffs[0] = -c; coeffs[4] = -F::ONE; self.custom(&mut region, &coeffs, F::ZERO, (F::ONE, F::ZERO), F::ZERO, 0)?;
1393 Ok(res)
1394 },
1395 )?;
1396
1397 let must_be_zero =
1399 self.add_and_mul(layouter, (c, &res), (F::ONE, x), (F::ZERO, x), -c, -F::ONE)?;
1400 self.assert_zero(layouter, &must_be_zero)?;
1401
1402 Ok(AssignedBit(res))
1404 }
1405}
1406
1407impl<F> EqualityInstructions<F, AssignedBit<F>> for NativeChip<F>
1408where
1409 F: CircuitField + From<u64> + Neg<Output = F>,
1410{
1411 fn is_equal(
1412 &self,
1413 layouter: &mut impl Layouter<F>,
1414 a: &AssignedBit<F>,
1415 b: &AssignedBit<F>,
1416 ) -> Result<AssignedBit<F>, Error> {
1417 self.add_and_mul(
1419 layouter,
1420 (-F::ONE, &a.0),
1421 (-F::ONE, &b.0),
1422 (F::ZERO, &a.0),
1423 F::ONE,
1424 F::from(2),
1425 )
1426 .map(AssignedBit)
1427 }
1428
1429 fn is_not_equal(
1430 &self,
1431 layouter: &mut impl Layouter<F>,
1432 a: &AssignedBit<F>,
1433 b: &AssignedBit<F>,
1434 ) -> Result<AssignedBit<F>, Error> {
1435 self.add_and_mul(
1437 layouter,
1438 (F::ONE, &a.0),
1439 (F::ONE, &b.0),
1440 (F::ZERO, &a.0),
1441 F::ZERO,
1442 -F::from(2),
1443 )
1444 .map(AssignedBit)
1445 }
1446
1447 fn is_equal_to_fixed(
1448 &self,
1449 layouter: &mut impl Layouter<F>,
1450 b: &AssignedBit<F>,
1451 constant: bool,
1452 ) -> Result<AssignedBit<F>, Error> {
1453 let assigned_constant = self.assign_fixed(layouter, constant)?;
1454 self.is_equal(layouter, b, &assigned_constant)
1455 }
1456
1457 fn is_not_equal_to_fixed(
1458 &self,
1459 layouter: &mut impl Layouter<F>,
1460 b: &AssignedBit<F>,
1461 constant: bool,
1462 ) -> Result<AssignedBit<F>, Error> {
1463 let assigned_constant = self.assign_fixed(layouter, constant)?;
1464 self.is_not_equal(layouter, b, &assigned_constant)
1465 }
1466}
1467
1468impl<F> ControlFlowInstructions<F, AssignedNative<F>> for NativeChip<F>
1469where
1470 F: CircuitField + From<u64> + Neg<Output = F>,
1471{
1472 fn select(
1473 &self,
1474 layouter: &mut impl Layouter<F>,
1475 cond: &AssignedBit<F>,
1476 x: &AssignedNative<F>,
1477 y: &AssignedNative<F>,
1478 ) -> Result<AssignedNative<F>, Error> {
1479 self.add_and_double_mul(
1483 layouter,
1484 (F::ZERO, &cond.0),
1485 (F::ZERO, x),
1486 (F::ONE, y),
1487 F::ZERO,
1488 (F::ONE, -F::ONE),
1489 )
1490 }
1491
1492 fn cond_swap(
1493 &self,
1494 layouter: &mut impl Layouter<F>,
1495 cond: &AssignedBit<F>,
1496 x: &AssignedNative<F>,
1497 y: &AssignedNative<F>,
1498 ) -> Result<(AssignedNative<F>, AssignedNative<F>), Error> {
1499 let val2 = (x.value().zip(y.value()).zip(cond.value()))
1505 .map(|((x, y), b)| if b { x } else { y })
1506 .copied();
1507 let val1 = x.value().copied() + y.value().copied() - val2;
1508
1509 layouter.assign_region(
1513 || "cond swap",
1514 |mut region| {
1515 self.copy_in_row(&mut region, &cond.0, &self.config.value_cols[0], 0)?;
1516 self.copy_in_row(&mut region, x, &self.config.value_cols[1], 0)?;
1517 self.copy_in_row(&mut region, y, &self.config.value_cols[2], 0)?;
1518 let fst = region.assign_advice(|| "fst", self.config.value_cols[3], 0, || val1)?;
1519 let snd = region.assign_advice(|| "snd", self.config.value_cols[4], 0, || val2)?;
1520 let mut coeffs = [F::ZERO; NB_ARITH_COLS];
1521 coeffs[2] = F::ONE; coeffs[4] = -F::ONE; self.custom(&mut region, &coeffs, F::ZERO, (F::ONE, -F::ONE), F::ZERO, 0)?;
1524 self.config.q_12_minus_34.enable(&mut region, 0)?;
1525 Ok((fst, snd))
1526 },
1527 )
1528 }
1529}
1530
1531impl<F> ControlFlowInstructions<F, AssignedBit<F>> for NativeChip<F>
1532where
1533 F: CircuitField,
1534{
1535 fn select(
1536 &self,
1537 layouter: &mut impl Layouter<F>,
1538 cond: &AssignedBit<F>,
1539 x: &AssignedBit<F>,
1540 y: &AssignedBit<F>,
1541 ) -> Result<AssignedBit<F>, Error> {
1542 self.select(layouter, cond, &x.0, &y.0).map(AssignedBit)
1543 }
1544
1545 fn cond_swap(
1546 &self,
1547 layouter: &mut impl Layouter<F>,
1548 cond: &AssignedBit<F>,
1549 x: &AssignedBit<F>,
1550 y: &AssignedBit<F>,
1551 ) -> Result<(AssignedBit<F>, AssignedBit<F>), Error> {
1552 self.cond_swap(layouter, cond, &x.0, &y.0)
1553 .map(|(fst, snd)| (AssignedBit(fst), AssignedBit(snd)))
1554 }
1555}
1556
1557impl<F> FieldInstructions<F, AssignedNative<F>> for NativeChip<F>
1558where
1559 F: CircuitField,
1560{
1561 fn order(&self) -> BigUint {
1562 F::modulus()
1563 }
1564}
1565
1566impl<F> CanonicityInstructions<F, AssignedNative<F>> for NativeChip<F>
1567where
1568 F: CircuitField,
1569{
1570 fn le_bits_lower_than(
1571 &self,
1572 layouter: &mut impl Layouter<F>,
1573 bits: &[AssignedBit<F>],
1574 bound: BigUint,
1575 ) -> Result<AssignedBit<F>, Error> {
1576 let geq = self.le_bits_geq_than(layouter, bits, bound)?;
1577 self.not(layouter, &geq)
1578 }
1579
1580 fn le_bits_geq_than(
1581 &self,
1582 layouter: &mut impl Layouter<F>,
1583 bits: &[AssignedBit<F>],
1584 bound: BigUint,
1585 ) -> Result<AssignedBit<F>, Error> {
1586 if bound.is_zero() {
1588 return self.assign_fixed(layouter, true);
1589 }
1590
1591 if bits.len() < bound.bits() as usize {
1593 return self.assign_fixed(layouter, false);
1594 }
1595
1596 if bits.len() == 1 {
1601 return Ok(bits[0].clone());
1602 }
1603
1604 let msb_pos = bits.len() - 1;
1605
1606 let rest_is_geq = {
1607 let mut rest_bound = bound.clone();
1608 rest_bound.set_bit(msb_pos as u64, false);
1609 self.le_bits_geq_than(layouter, &bits[0..msb_pos], rest_bound)?
1610 };
1611
1612 if bound.bit(msb_pos as u64) {
1613 self.and(layouter, &[bits[msb_pos].clone(), rest_is_geq])
1614 } else {
1615 self.or(layouter, &[bits[msb_pos].clone(), rest_is_geq])
1616 }
1617 }
1618}
1619
1620#[cfg(any(test, feature = "testing"))]
1621impl<F: CircuitField> FromScratch<F> for NativeChip<F> {
1622 type Config = NativeConfig;
1623
1624 fn new_from_scratch(config: &Self::Config) -> Self {
1625 NativeChip::new(config, &())
1626 }
1627
1628 fn configure_from_scratch(
1629 meta: &mut ConstraintSystem<F>,
1630 advice_columns: &mut Vec<Column<Advice>>,
1631 fixed_columns: &mut Vec<Column<Fixed>>,
1632 instance_columns: &[Column<Instance>; 2],
1633 ) -> Self::Config {
1634 while advice_columns.len() < NB_ARITH_COLS {
1635 advice_columns.push(meta.advice_column());
1636 }
1637 while fixed_columns.len() < NB_ARITH_FIXED_COLS {
1638 fixed_columns.push(meta.fixed_column());
1639 }
1640 let advice_cols: [_; NB_ARITH_COLS] = advice_columns[..NB_ARITH_COLS].try_into().unwrap();
1641 let fixed_cols: [_; NB_ARITH_FIXED_COLS] =
1642 fixed_columns[..NB_ARITH_FIXED_COLS].try_into().unwrap();
1643 NativeChip::configure(meta, &(advice_cols, fixed_cols, *instance_columns))
1644 }
1645
1646 fn load_from_scratch(
1647 &self,
1648 _layouter: &mut impl midnight_proofs::circuit::Layouter<F>,
1649 ) -> Result<(), Error> {
1650 Ok(())
1651 }
1652}
1653
1654#[cfg(test)]
1655mod tests {
1656 use ff::FromUniformBytes;
1657 use midnight_curves::Fq as BlsScalar;
1658
1659 use super::*;
1660 use crate::instructions::{
1661 arithmetic, assertions, binary, canonicity, control_flow,
1662 conversions::{
1663 self,
1664 tests::Operation::{Convert, UnsafeConvert},
1665 },
1666 equality, public_input, zero,
1667 };
1668
1669 macro_rules! test {
1670 ($mod:ident, $op:ident) => {
1671 #[test]
1672 fn $op() {
1673 $mod::tests::$op::<BlsScalar, AssignedNative<BlsScalar>, NativeChip<BlsScalar>>(
1674 "native_chip",
1675 );
1676 }
1677 };
1678 }
1679 test!(assertions, test_assertions);
1680
1681 test!(public_input, test_public_inputs);
1682
1683 test!(arithmetic, test_add);
1684 test!(arithmetic, test_sub);
1685 test!(arithmetic, test_mul);
1686 test!(arithmetic, test_div);
1687 test!(arithmetic, test_neg);
1688 test!(arithmetic, test_inv);
1689 test!(arithmetic, test_pow);
1690 test!(arithmetic, test_linear_combination);
1691 test!(arithmetic, test_add_and_mul);
1692
1693 test!(equality, test_is_equal);
1694
1695 test!(zero, test_zero_assertions);
1696 test!(zero, test_is_zero);
1697
1698 test!(control_flow, test_select);
1699 test!(control_flow, test_cond_assert_equal);
1700 test!(control_flow, test_cond_swap);
1701
1702 test!(canonicity, test_canonical);
1703 test!(canonicity, test_le_bits_lower_and_geq);
1704
1705 macro_rules! test {
1706 ($mod:ident, $op:ident) => {
1707 #[test]
1708 fn $op() {
1709 $mod::tests::$op::<BlsScalar, NativeChip<BlsScalar>>("native_chip");
1710 }
1711 };
1712 }
1713
1714 test!(binary, test_and);
1715 test!(binary, test_or);
1716 test!(binary, test_xor);
1717 test!(binary, test_not);
1718
1719 fn test_generic_conversion_to_bit<F>(name: &str)
1720 where
1721 F: CircuitField + FromUniformBytes<64> + Ord,
1722 {
1723 [
1724 (
1725 F::ZERO,
1726 Some(false),
1727 Convert,
1728 true,
1729 true,
1730 name,
1731 "convert_to_bit",
1732 ),
1733 (F::ZERO, Some(true), Convert, false, false, "", ""),
1734 (F::ONE, Some(true), Convert, true, false, "", ""),
1735 (F::ONE, Some(false), Convert, false, false, "", ""),
1736 (F::from(2), None, Convert, false, false, "", ""),
1737 (
1738 F::from(2),
1739 None,
1740 UnsafeConvert,
1741 true,
1742 true,
1743 name,
1744 "unsafe_convert_to_bit",
1745 ),
1746 ]
1747 .into_iter()
1748 .for_each(
1749 |(x, expected, operation, must_pass, cost_model, chip_name, op_name)| {
1750 conversions::tests::run::<F, AssignedNative<F>, AssignedBit<F>, NativeChip<F>>(
1751 x, expected, operation, must_pass, cost_model, chip_name, op_name,
1752 )
1753 },
1754 );
1755 }
1756
1757 #[test]
1758 fn test_conversion_to_bit() {
1759 test_generic_conversion_to_bit::<BlsScalar>("native_chip")
1760 }
1761
1762 fn test_generic_conversion_from_bit<F>(name: &str)
1763 where
1764 F: CircuitField + FromUniformBytes<64> + Ord,
1765 {
1766 [
1767 (
1768 false,
1769 Some(F::ZERO),
1770 Convert,
1771 true,
1772 true,
1773 name,
1774 "convert_from_bit",
1775 ),
1776 (false, Some(F::ONE), Convert, false, false, "", ""),
1777 (true, Some(F::ONE), Convert, true, false, "", ""),
1778 (true, Some(F::ZERO), Convert, false, false, "", ""),
1779 (
1780 false,
1781 None,
1782 UnsafeConvert,
1783 true,
1784 true,
1785 name,
1786 "unsafe_convert_from_bit",
1787 ),
1788 (true, None, UnsafeConvert, true, false, "", ""),
1789 ]
1790 .into_iter()
1791 .for_each(
1792 |(x, expected, operation, must_pass, cost_model, chip_name, op_name)| {
1793 conversions::tests::run::<F, AssignedBit<F>, AssignedNative<F>, NativeChip<F>>(
1794 x, expected, operation, must_pass, cost_model, chip_name, op_name,
1795 )
1796 },
1797 );
1798 }
1799
1800 #[test]
1801 fn test_conversion_from_bit() {
1802 test_generic_conversion_from_bit::<BlsScalar>("native_chip");
1803 }
1804}