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