1use std::{
19 cmp::{max, min},
20 fmt::Debug,
21 hash::{Hash, Hasher},
22 marker::PhantomData,
23};
24
25use midnight_proofs::{
26 circuit::{Chip, Layouter, Value},
27 plonk::{Advice, Column, ConstraintSystem, Error},
28};
29use num_bigint::{BigInt as BI, BigUint, ToBigInt};
30use num_integer::Integer;
31use num_traits::{One, Signed, Zero};
32#[cfg(any(test, feature = "testing"))]
33use {
34 crate::testing_utils::{FromScratch, Sampleable},
35 midnight_proofs::plonk::{Fixed, Instance},
36 rand::RngCore,
37};
38
39use super::gates::{
40 mul::{self, MulConfig},
41 norm::{self, NormConfig},
42};
43use crate::{
44 field::foreign::{
45 params::{check_params, FieldEmulationParams},
46 util::{bi_from_limbs, bi_to_limbs},
47 },
48 instructions::{
49 ArithInstructions, AssertionInstructions, AssignmentInstructions, CanonicityInstructions,
50 ControlFlowInstructions, ConversionInstructions, DecompositionInstructions,
51 EqualityInstructions, FieldInstructions, NativeInstructions, PublicInputInstructions,
52 ScalarFieldInstructions, ZeroInstructions,
53 },
54 types::{AssignedBit, AssignedByte, AssignedNative, InnerConstants, InnerValue, Instantiable},
55 utils::util::bigint_to_fe,
56 CircuitField,
57};
58
59#[derive(Clone, Debug)]
82#[must_use]
83pub struct AssignedField<F, K, P>
84where
85 F: CircuitField,
86 K: CircuitField,
87 P: FieldEmulationParams<F, K>,
88{
89 limb_values: Vec<AssignedNative<F>>,
90 limb_bounds: Vec<(BI, BI)>,
91 _marker: PhantomData<(K, P)>,
92}
93
94impl<F, K, P> PartialEq for AssignedField<F, K, P>
95where
96 F: CircuitField,
97 K: CircuitField,
98 P: FieldEmulationParams<F, K>,
99{
100 fn eq(&self, other: &Self) -> bool {
101 self.limb_values
102 .iter()
103 .zip(other.limb_values.iter())
104 .all(|(s, o)| s.cell() == o.cell())
105 }
106}
107
108impl<F: CircuitField, K: CircuitField, P: FieldEmulationParams<F, K>> Eq
109 for AssignedField<F, K, P>
110{
111}
112
113impl<F, K, P> Hash for AssignedField<F, K, P>
114where
115 F: CircuitField,
116 K: CircuitField,
117 P: FieldEmulationParams<F, K>,
118{
119 fn hash<H: Hasher>(&self, state: &mut H) {
120 self.limb_values.iter().for_each(|elem| elem.hash(state));
121 }
122}
123
124impl<F, K, P> Instantiable<F> for AssignedField<F, K, P>
125where
126 F: CircuitField,
127 K: CircuitField,
128 P: FieldEmulationParams<F, K>,
129{
130 fn as_public_input(element: &K) -> Vec<F> {
131 let element_as_bi = (*element - K::ONE).to_biguint().into();
133 let base = BI::from(2).pow(P::LOG2_BASE);
134 let nb_limbs_per_batch = (F::CAPACITY / P::LOG2_BASE) as usize;
135 bi_to_limbs(P::NB_LIMBS, &base, &element_as_bi)
136 .chunks(nb_limbs_per_batch)
137 .map(|chunk| bigint_to_fe::<F>(&bi_from_limbs(&base, chunk)))
138 .collect()
139 }
140}
141
142impl<F, K, P> InnerValue for AssignedField<F, K, P>
143where
144 F: CircuitField,
145 K: CircuitField,
146 P: FieldEmulationParams<F, K>,
147{
148 type Element = K;
149
150 fn value(&self) -> Value<K> {
151 let bi_limbs = self
152 .limb_values
153 .iter()
154 .zip(self.limb_bounds.iter())
155 .map(|(xi, (lower_bound, _))| {
156 let shift = BI::abs(lower_bound);
159 let fe_shift = bigint_to_fe::<F>(&shift);
160 xi.value().map(|xv| {
161 let bi: BI = (*xv + fe_shift).to_biguint().into();
162 bi - &shift
163 })
164 })
165 .collect::<Vec<_>>();
166 let bi_limbs: Value<Vec<BI>> = Value::from_iter(bi_limbs);
167 let base = BI::from(2).pow(P::LOG2_BASE);
168 bi_limbs.map(|limbs| bigint_to_fe::<K>(&(BI::one() + bi_from_limbs(&base, &limbs))))
169 }
170}
171
172impl<F: CircuitField, K: CircuitField, P> InnerConstants for AssignedField<F, K, P>
173where
174 F: CircuitField,
175 K: CircuitField,
176 P: FieldEmulationParams<F, K>,
177{
178 fn inner_zero() -> K {
179 K::ZERO
180 }
181
182 fn inner_one() -> K {
183 K::ONE
184 }
185}
186
187impl<F, K, P> AssignedField<F, K, P>
188where
189 F: CircuitField,
190 K: CircuitField,
191 P: FieldEmulationParams<F, K>,
192{
193 pub(crate) fn from_limbs_unsafe(limb_values: Vec<AssignedNative<F>>) -> Self {
199 debug_assert!(limb_values.len() as u32 == P::NB_LIMBS);
200 Self {
201 limb_values,
202 limb_bounds: well_formed_bounds::<F, K, P>(),
203 _marker: PhantomData,
204 }
205 }
206}
207
208#[cfg(any(test, feature = "testing"))]
209impl<F, K, P> Sampleable for AssignedField<F, K, P>
210where
211 F: CircuitField,
212 K: CircuitField,
213 P: FieldEmulationParams<F, K>,
214{
215 fn sample_inner(rng: impl RngCore) -> K {
216 K::random(rng)
217 }
218}
219
220pub fn nb_field_chip_columns<F, K, P>() -> usize
222where
223 F: CircuitField,
224 K: CircuitField,
225 P: FieldEmulationParams<F, K>,
226{
227 P::NB_LIMBS as usize + max(P::NB_LIMBS as usize, 1 + P::moduli().len())
228}
229
230pub fn well_formed_log2_bounds<F, K, P>() -> Vec<u32>
237where
238 F: CircuitField,
239 K: CircuitField,
240 P: FieldEmulationParams<F, K>,
241{
242 let m = &K::modulus().to_bigint().unwrap();
247 let log2_msl_bound = m.bits() as u32 - (P::NB_LIMBS - 1) * P::LOG2_BASE;
248 let mut bounds = vec![log2_msl_bound];
249 bounds.resize(P::NB_LIMBS as usize, P::LOG2_BASE);
250 bounds.into_iter().rev().collect::<Vec<_>>()
251}
252
253#[derive(Clone, Debug)]
261pub struct FieldChipConfig {
262 mul_config: mul::MulConfig,
263 norm_config: norm::NormConfig,
264 pub x_cols: Vec<Column<Advice>>,
266 pub y_cols: Vec<Column<Advice>>,
268 pub z_cols: Vec<Column<Advice>>,
270 pub u_col: Column<Advice>,
272 pub v_cols: Vec<Column<Advice>>,
274}
275
276#[derive(Clone, Debug)]
278pub struct FieldChip<F, K, P, N>
279where
280 F: CircuitField,
281 K: CircuitField,
282 P: FieldEmulationParams<F, K>,
283 N: NativeInstructions<F>,
284{
285 config: FieldChipConfig,
286 pub(crate) native_gadget: N,
287 _marker: PhantomData<(F, K, P, N)>,
288}
289
290impl<F, K, P> AssignedField<F, K, P>
291where
292 F: CircuitField,
293 K: CircuitField,
294 P: FieldEmulationParams<F, K>,
295{
296 pub fn modulus(&self) -> BI {
298 K::modulus().to_bigint().unwrap().clone()
299 }
300
301 pub fn is_well_formed(&self) -> bool {
307 self.limb_bounds.iter().zip(well_formed_log2_bounds::<F, K, P>()).all(
308 |((lower, upper), expected_upper)| {
309 assert!(lower <= upper);
310 !BI::is_negative(lower) && upper.bits() <= expected_upper as u64
311 },
312 )
313 }
314
315 pub fn limb_values(&self) -> Vec<AssignedNative<F>> {
319 self.limb_values.clone()
320 }
321
322 pub fn bigint_limbs(&self) -> Value<Vec<BI>> {
324 let limbs = self
325 .limb_values
326 .iter()
327 .zip(self.limb_bounds.iter())
328 .map(|(xi, (lbound, _))| {
329 let shift = BI::abs(lbound);
332 let fe_shift = bigint_to_fe::<F>(&shift);
333 xi.value().map(|xv| {
334 let bi: BI = (*xv + fe_shift).to_biguint().into();
335 bi - &shift
336 })
337 })
338 .collect::<Vec<_>>();
339 Value::from_iter(limbs)
340 }
341}
342
343fn well_formed_bounds<F, K, P>() -> Vec<(BI, BI)>
348where
349 F: CircuitField,
350 K: CircuitField,
351 P: FieldEmulationParams<F, K>,
352{
353 well_formed_log2_bounds::<F, K, P>()
354 .into_iter()
355 .map(|log2_base| (BI::zero(), BI::from(2).pow(log2_base) - BI::one()))
356 .collect()
357}
358
359fn limbs_of_zero<F, K, P>() -> Vec<BI>
361where
362 F: CircuitField,
363 K: CircuitField,
364 P: FieldEmulationParams<F, K>,
365{
366 bi_to_limbs(
367 P::NB_LIMBS,
368 &BI::from(2).pow(P::LOG2_BASE),
369 &(K::modulus().to_bigint().unwrap() - BI::one()),
370 )
371}
372
373impl<F, K, P, N> Chip<F> for FieldChip<F, K, P, N>
374where
375 F: CircuitField,
376 K: CircuitField,
377 P: FieldEmulationParams<F, K>,
378 N: NativeInstructions<F>,
379{
380 type Config = FieldChipConfig;
381 type Loaded = ();
382 fn config(&self) -> &Self::Config {
383 &self.config
384 }
385 fn loaded(&self) -> &Self::Loaded {
386 &()
387 }
388}
389
390impl<F, K, P, N> AssignmentInstructions<F, AssignedField<F, K, P>> for FieldChip<F, K, P, N>
391where
392 F: CircuitField,
393 K: CircuitField,
394 P: FieldEmulationParams<F, K>,
395 N: NativeInstructions<F>,
396{
397 fn assign(
398 &self,
399 layouter: &mut impl Layouter<F>,
400 x: Value<K>,
401 ) -> Result<AssignedField<F, K, P>, Error> {
402 let base = BI::from(2).pow(P::LOG2_BASE);
403 let x = x.map(|v| {
406 let bi = (v - K::ONE).to_biguint().into();
407 bi_to_limbs(P::NB_LIMBS, &base, &bi)
408 });
409
410 let x_cells = (0..P::NB_LIMBS)
412 .map(|i| x.clone().map(|limbs| bigint_to_fe::<F>(&limbs[i as usize])))
413 .zip(well_formed_log2_bounds::<F, K, P>().iter())
414 .map(|(xi_value, log2_bound)| {
415 self.native_gadget.assign_lower_than_fixed(
416 layouter,
417 xi_value,
418 &(BigUint::one() << *log2_bound),
419 )
420 })
421 .collect::<Result<Vec<_>, Error>>()?;
422
423 Ok(AssignedField::<F, K, P> {
424 limb_values: x_cells,
425 limb_bounds: well_formed_bounds::<F, K, P>(),
426 _marker: PhantomData,
427 })
428 }
429
430 fn assign_fixed(
431 &self,
432 layouter: &mut impl Layouter<F>,
433 constant: K,
434 ) -> Result<AssignedField<F, K, P>, Error> {
435 let base = BI::from(2).pow(P::LOG2_BASE);
436 let constant = (constant - K::ONE).to_biguint().into();
439 let constant_limbs = bi_to_limbs(P::NB_LIMBS, &base, &constant);
440 let constant_cells = constant_limbs
441 .iter()
442 .map(|x| self.native_gadget.assign_fixed(layouter, bigint_to_fe::<F>(x)))
443 .collect::<Result<Vec<_>, _>>()?;
444
445 Ok(AssignedField::<F, K, P> {
455 limb_values: constant_cells,
456 limb_bounds: well_formed_bounds::<F, K, P>(),
457 _marker: PhantomData,
458 })
459 }
460}
461
462impl<F, K, P> From<AssignedField<F, K, P>> for Vec<AssignedNative<F>>
465where
466 F: CircuitField,
467 K: CircuitField,
468 P: FieldEmulationParams<F, K>,
469{
470 fn from(x: AssignedField<F, K, P>) -> Self {
471 x.limb_values()
472 }
473}
474
475impl<F, K, P, N> PublicInputInstructions<F, AssignedField<F, K, P>> for FieldChip<F, K, P, N>
476where
477 F: CircuitField,
478 K: CircuitField,
479 P: FieldEmulationParams<F, K>,
480 N: NativeInstructions<F>,
481{
482 fn as_public_input(
483 &self,
484 layouter: &mut impl Layouter<F>,
485 assigned: &AssignedField<F, K, P>,
486 ) -> Result<Vec<AssignedNative<F>>, Error> {
487 let assigned = self.normalize(layouter, assigned)?;
488 let nb_limbs_per_batch = (F::CAPACITY / P::LOG2_BASE) as usize;
489 let base = BI::from(2).pow(P::LOG2_BASE);
490 assigned
491 .limb_values
492 .chunks(nb_limbs_per_batch)
493 .map(|chunk| {
494 let terms: Vec<(F, AssignedNative<F>)> = chunk
495 .iter()
496 .enumerate()
497 .map(|(i, limb)| (bigint_to_fe::<F>(&base.pow(i as u32)), limb.clone()))
498 .collect();
499 self.native_gadget.linear_combination(layouter, &terms, F::ZERO)
500 })
501 .collect()
502 }
503
504 fn constrain_as_public_input(
505 &self,
506 layouter: &mut impl Layouter<F>,
507 assigned: &AssignedField<F, K, P>,
508 ) -> Result<(), Error> {
509 self.as_public_input(layouter, assigned)?
510 .iter()
511 .try_for_each(|c| self.native_gadget.constrain_as_public_input(layouter, c))
512 }
513
514 fn assign_as_public_input(
515 &self,
516 layouter: &mut impl Layouter<F>,
517 value: Value<K>,
518 ) -> Result<AssignedField<F, K, P>, Error> {
519 let assigned = self.assign(layouter, value)?;
522 self.constrain_as_public_input(layouter, &assigned)?;
523 Ok(assigned)
524 }
525}
526
527impl<F, K, P, N> AssertionInstructions<F, AssignedField<F, K, P>> for FieldChip<F, K, P, N>
528where
529 F: CircuitField,
530 K: CircuitField,
531 P: FieldEmulationParams<F, K>,
532 N: NativeInstructions<F>,
533{
534 fn assert_equal(
535 &self,
536 layouter: &mut impl Layouter<F>,
537 x: &AssignedField<F, K, P>,
538 y: &AssignedField<F, K, P>,
539 ) -> Result<(), Error> {
540 let x = self.normalize(layouter, x)?;
546 let y = self.normalize(layouter, y)?;
547 x.limb_values
548 .iter()
549 .zip(y.limb_values.iter())
550 .map(|(xi, yi)| self.native_gadget.assert_equal(layouter, xi, yi))
551 .collect::<Result<Vec<_>, _>>()?;
552 Ok(())
553 }
554
555 fn assert_not_equal(
556 &self,
557 layouter: &mut impl Layouter<F>,
558 x: &AssignedField<F, K, P>,
559 y: &AssignedField<F, K, P>,
560 ) -> Result<(), Error> {
561 let diff = self.sub(layouter, x, y)?;
562 self.assert_non_zero(layouter, &diff)
563 }
564
565 fn assert_equal_to_fixed(
566 &self,
567 layouter: &mut impl Layouter<F>,
568 x: &AssignedField<F, K, P>,
569 constant: K,
570 ) -> Result<(), Error> {
571 let x = self.normalize(layouter, x)?;
577 let constant_limbs = {
578 let constant = (constant - K::ONE).to_biguint().into();
579 let base = BI::from(2).pow(P::LOG2_BASE);
580 bi_to_limbs(P::NB_LIMBS, &base, &constant)
581 };
582 x.limb_values
583 .iter()
584 .zip(constant_limbs.iter())
585 .map(|(xi, ki)| {
586 self.native_gadget.assert_equal_to_fixed(layouter, xi, bigint_to_fe::<F>(ki))
587 })
588 .collect::<Result<Vec<_>, _>>()?;
589 Ok(())
590 }
591
592 fn assert_not_equal_to_fixed(
593 &self,
594 layouter: &mut impl Layouter<F>,
595 x: &AssignedField<F, K, P>,
596 constant: K,
597 ) -> Result<(), Error> {
598 let diff = self.add_constant(layouter, x, -constant)?;
599 self.assert_non_zero(layouter, &diff)
600 }
601}
602
603impl<F, K, P, N> EqualityInstructions<F, AssignedField<F, K, P>> for FieldChip<F, K, P, N>
604where
605 F: CircuitField,
606 K: CircuitField,
607 P: FieldEmulationParams<F, K>,
608 N: NativeInstructions<F>,
609{
610 fn is_equal(
611 &self,
612 layouter: &mut impl Layouter<F>,
613 x: &AssignedField<F, K, P>,
614 y: &AssignedField<F, K, P>,
615 ) -> Result<AssignedBit<F>, Error> {
616 let diff = self.sub(layouter, x, y)?;
617 self.is_zero(layouter, &diff)
618 }
619
620 fn is_not_equal(
621 &self,
622 layouter: &mut impl Layouter<F>,
623 x: &AssignedField<F, K, P>,
624 y: &AssignedField<F, K, P>,
625 ) -> Result<AssignedBit<F>, Error> {
626 let b = self.is_equal(layouter, x, y)?;
627 self.native_gadget.not(layouter, &b)
628 }
629
630 fn is_equal_to_fixed(
631 &self,
632 layouter: &mut impl Layouter<F>,
633 x: &AssignedField<F, K, P>,
634 constant: <AssignedField<F, K, P> as InnerValue>::Element,
635 ) -> Result<AssignedBit<F>, Error> {
636 let diff = self.add_constant(layouter, x, -constant)?;
637 self.is_zero(layouter, &diff)
638 }
639
640 fn is_not_equal_to_fixed(
641 &self,
642 layouter: &mut impl Layouter<F>,
643 x: &AssignedField<F, K, P>,
644 constant: <AssignedField<F, K, P> as InnerValue>::Element,
645 ) -> Result<AssignedBit<F>, Error> {
646 let b = self.is_equal_to_fixed(layouter, x, constant)?;
647 self.native_gadget.not(layouter, &b)
648 }
649}
650
651impl<F, K, P, N> ZeroInstructions<F, AssignedField<F, K, P>> for FieldChip<F, K, P, N>
652where
653 F: CircuitField,
654 K: CircuitField,
655 P: FieldEmulationParams<F, K>,
656 N: NativeInstructions<F>,
657{
658 fn assert_non_zero(
659 &self,
660 layouter: &mut impl Layouter<F>,
661 x: &AssignedField<F, K, P>,
662 ) -> Result<(), Error> {
663 let b = self.is_zero(layouter, x)?;
664 self.native_gadget.assert_equal_to_fixed(layouter, &b, false)
665 }
666
667 fn is_zero(
668 &self,
669 layouter: &mut impl Layouter<F>,
670 x: &AssignedField<F, K, P>,
671 ) -> Result<AssignedBit<F>, Error> {
672 let x = self.normalize(layouter, x)?;
675 let bs = x
676 .limb_values
677 .iter()
678 .zip(limbs_of_zero::<F, K, P>().iter())
679 .map(|(xi, ci)| {
680 self.native_gadget.is_equal_to_fixed(layouter, xi, bigint_to_fe::<F>(ci))
681 })
682 .collect::<Result<Vec<_>, _>>()?;
683 self.native_gadget.and(layouter, &bs)
684 }
685}
686
687impl<F, K, P, N> ControlFlowInstructions<F, AssignedField<F, K, P>> for FieldChip<F, K, P, N>
688where
689 F: CircuitField,
690 K: CircuitField,
691 P: FieldEmulationParams<F, K>,
692 N: NativeInstructions<F>,
693{
694 fn select(
695 &self,
696 layouter: &mut impl Layouter<F>,
697 cond: &AssignedBit<F>,
698 x: &AssignedField<F, K, P>,
699 y: &AssignedField<F, K, P>,
700 ) -> Result<AssignedField<F, K, P>, Error> {
701 let z_limb_values = x
702 .limb_values
703 .iter()
704 .zip(y.limb_values.iter())
705 .map(|(xi, yi)| self.native_gadget.select(layouter, cond, xi, yi))
706 .collect::<Result<Vec<_>, _>>()?;
707 let z_limb_bounds = x
708 .limb_bounds
709 .iter()
710 .zip(y.limb_bounds.iter())
711 .map(|(xi_bounds, yi_bounds)| {
712 (
713 min(&xi_bounds.0, &yi_bounds.0).clone(),
714 max(&xi_bounds.1, &yi_bounds.1).clone(),
715 )
716 })
717 .collect::<Vec<_>>();
718 Ok(AssignedField::<F, K, P> {
719 limb_values: z_limb_values,
720 limb_bounds: z_limb_bounds,
721 _marker: PhantomData,
722 })
723 }
724}
725
726impl<F, K, P, N> ArithInstructions<F, AssignedField<F, K, P>> for FieldChip<F, K, P, N>
727where
728 F: CircuitField,
729 K: CircuitField,
730 P: FieldEmulationParams<F, K>,
731 N: NativeInstructions<F>,
732{
733 fn linear_combination(
734 &self,
735 layouter: &mut impl Layouter<F>,
736 terms: &[(K, AssignedField<F, K, P>)],
737 constant: K,
738 ) -> Result<AssignedField<F, K, P>, Error> {
739 if terms.is_empty() {
740 return self.assign_fixed(layouter, constant);
741 }
742
743 if terms.iter().all(|(c, _)| *c == K::ONE || *c == -K::ONE) {
745 let (pos, neg): (Vec<_>, Vec<_>) = terms.iter().partition(|(c, _)| *c == K::ONE);
746 let pos: Vec<_> = pos.into_iter().map(|(_, x)| x.clone()).collect();
747 let neg: Vec<_> = neg.into_iter().map(|(_, x)| x.clone()).collect();
748 return self.sum(layouter, &pos, &neg, constant);
749 }
750
751 let init: AssignedField<F, K, P> = self.assign_fixed(layouter, constant)?;
753 let res = terms.iter().try_fold(init, |acc, (c, x)| {
754 let prod = self.mul_by_constant(layouter, x, *c)?;
755 self.add(layouter, &acc, &prod)
756 })?;
757 self.normalize_if_approaching_limit(layouter, &res)
758 }
759
760 fn add(
761 &self,
762 layouter: &mut impl Layouter<F>,
763 x: &AssignedField<F, K, P>,
764 y: &AssignedField<F, K, P>,
765 ) -> Result<AssignedField<F, K, P>, Error> {
766 let zero: AssignedField<F, K, P> = self.assign_fixed(layouter, K::ZERO)?;
767
768 if x == &zero {
769 return Ok(y.clone());
770 }
771
772 if y == &zero {
773 return Ok(x.clone());
774 }
775
776 let mut constants = vec![BI::one()];
783 constants.resize(P::NB_LIMBS as usize, BI::zero());
784
785 let z_limb_values = x
786 .limb_values
787 .iter()
788 .zip(y.limb_values.iter())
789 .zip(constants.iter().map(|ci| bigint_to_fe::<F>(ci)))
790 .map(|((xi, yi), ci)| {
791 self.native_gadget.linear_combination(
792 layouter,
793 &[(F::ONE, xi.clone()), (F::ONE, yi.clone())],
794 ci,
795 )
796 })
797 .collect::<Result<Vec<_>, _>>()?;
798
799 let z = AssignedField::<F, K, P> {
800 limb_values: z_limb_values,
801 limb_bounds: x
802 .limb_bounds
803 .iter()
804 .zip(y.limb_bounds.iter())
805 .zip(constants.iter())
806 .map(|((xi_bounds, yi_bounds), ci)| {
807 (
808 &xi_bounds.0 + &yi_bounds.0 + ci,
809 &xi_bounds.1 + &yi_bounds.1 + ci,
810 )
811 })
812 .collect(),
813 _marker: PhantomData,
814 };
815 self.normalize_if_approaching_limit(layouter, &z)
816 }
817
818 fn sub(
819 &self,
820 layouter: &mut impl Layouter<F>,
821 x: &AssignedField<F, K, P>,
822 y: &AssignedField<F, K, P>,
823 ) -> Result<AssignedField<F, K, P>, Error> {
824 let zero: AssignedField<F, K, P> = self.assign_fixed(layouter, K::ZERO)?;
825
826 if y == &zero {
827 return Ok(x.clone());
828 }
829
830 let mut constants = vec![BI::from(-1)];
837 constants.resize(P::NB_LIMBS as usize, BI::zero());
838
839 let z_limb_values = x
840 .limb_values
841 .iter()
842 .zip(y.limb_values.iter())
843 .zip(constants.iter().map(|ci| bigint_to_fe::<F>(ci)))
844 .map(|((xi, yi), ci)| {
845 self.native_gadget.linear_combination(
846 layouter,
847 &[(F::ONE, xi.clone()), (-F::ONE, yi.clone())],
848 ci,
849 )
850 })
851 .collect::<Result<Vec<_>, _>>()?;
852
853 let z = AssignedField::<F, K, P> {
854 limb_values: z_limb_values,
855 limb_bounds: x
856 .limb_bounds
857 .iter()
858 .zip(y.limb_bounds.iter())
859 .zip(constants.iter())
860 .map(|((xi_bounds, yi_bounds), ci)| {
861 (
862 &xi_bounds.0 - &yi_bounds.1 + ci,
863 &xi_bounds.1 - &yi_bounds.0 + ci,
864 )
865 })
866 .collect(),
867 _marker: PhantomData,
868 };
869 self.normalize_if_approaching_limit(layouter, &z)
870 }
871
872 fn mul(
873 &self,
874 layouter: &mut impl Layouter<F>,
875 x: &AssignedField<F, K, P>,
876 y: &AssignedField<F, K, P>,
877 multiplying_constant: Option<K>,
878 ) -> Result<AssignedField<F, K, P>, Error> {
879 let zero: AssignedField<F, K, P> = self.assign_fixed(layouter, K::ZERO)?;
880 let one: AssignedField<F, K, P> = self.assign_fixed(layouter, K::ONE)?;
881
882 if x == &zero || y == &zero {
883 return Ok(zero);
884 }
885
886 if x == &one {
887 return Ok(y.clone());
888 }
889
890 if y == &one {
891 return Ok(x.clone());
892 }
893
894 let y = match multiplying_constant {
895 None => y.clone(),
896 Some(k) => self.mul_by_constant(layouter, y, k)?,
897 };
898 self.assign_mul(layouter, x, &y, false)
899 }
900
901 fn div(
902 &self,
903 layouter: &mut impl Layouter<F>,
904 x: &AssignedField<F, K, P>,
905 y: &AssignedField<F, K, P>,
906 ) -> Result<AssignedField<F, K, P>, Error> {
907 let one: AssignedField<F, K, P> = self.assign_fixed(layouter, K::ONE)?;
908 if y == &one {
909 return Ok(x.clone());
910 }
911
912 let y = self.normalize(layouter, y)?;
913 self.assert_non_zero(layouter, &y)?;
914 self.assign_mul(layouter, x, &y, true)
915 }
916
917 fn neg(
918 &self,
919 layouter: &mut impl Layouter<F>,
920 x: &AssignedField<F, K, P>,
921 ) -> Result<AssignedField<F, K, P>, Error> {
922 let zero: AssignedField<F, K, P> = self.assign_fixed(layouter, K::ZERO)?;
923
924 if x == &zero {
925 return Ok(zero);
926 }
927
928 let mut constants = vec![BI::from(-2)];
935 constants.resize(P::NB_LIMBS as usize, BI::zero());
936
937 let z_limb_values = x
938 .limb_values
939 .iter()
940 .zip(constants.iter().map(|ci| bigint_to_fe::<F>(ci)))
941 .map(|(xi, ci)| {
942 self.native_gadget.linear_combination(layouter, &[(-F::ONE, xi.clone())], ci)
943 })
944 .collect::<Result<Vec<_>, _>>()?;
945
946 let z = AssignedField::<F, K, P> {
947 limb_values: z_limb_values,
948 limb_bounds: x
949 .limb_bounds
950 .iter()
951 .zip(constants.iter())
952 .map(|(xi_bounds, ci)| (-&xi_bounds.1 + ci, -&xi_bounds.0 + ci))
953 .collect(),
954 _marker: PhantomData,
955 };
956 self.normalize_if_approaching_limit(layouter, &z)
957 }
958
959 fn inv(
960 &self,
961 layouter: &mut impl Layouter<F>,
962 x: &AssignedField<F, K, P>,
963 ) -> Result<AssignedField<F, K, P>, Error> {
964 let one: AssignedField<F, K, P> = self.assign_fixed(layouter, K::ONE)?;
965
966 if x == &one {
967 return Ok(one);
968 }
969
970 self.assign_mul(layouter, &one, x, true)
973 }
974
975 fn inv0(
976 &self,
977 layouter: &mut impl Layouter<F>,
978 x: &AssignedField<F, K, P>,
979 ) -> Result<AssignedField<F, K, P>, Error> {
980 let is_zero = self.is_zero(layouter, x)?;
981 let zero = self.assign_fixed(layouter, K::ZERO)?;
982 let one = self.assign_fixed(layouter, K::ONE)?;
983 let invertible = self.select(layouter, &is_zero, &one, x)?;
984 let inverse = self.assign_mul(layouter, &one, &invertible, true)?;
985 self.select(layouter, &is_zero, &zero, &inverse)
986 }
987
988 fn add_constant(
989 &self,
990 layouter: &mut impl Layouter<F>,
991 x: &AssignedField<F, K, P>,
992 k: K,
993 ) -> Result<AssignedField<F, K, P>, Error> {
994 if k.is_zero().into() {
1002 return Ok(x.clone());
1003 }
1004
1005 let base = BI::from(2).pow(P::LOG2_BASE);
1006 let k_limbs = bi_to_limbs(P::NB_LIMBS, &base, &k.to_biguint().into());
1007
1008 let z_limb_values = {
1009 self.native_gadget.add_constants(
1010 layouter,
1011 &x.limb_values,
1012 &k_limbs.iter().map(bigint_to_fe::<F>).collect::<Vec<_>>(),
1013 )?
1014 };
1015
1016 let z = AssignedField::<F, K, P> {
1017 limb_values: z_limb_values,
1018 limb_bounds: x
1019 .limb_bounds
1020 .iter()
1021 .zip(k_limbs.iter())
1022 .map(|(xi_bound, ki)| (&xi_bound.0 + ki, &xi_bound.1 + ki))
1023 .collect(),
1024 _marker: PhantomData,
1025 };
1026 self.normalize_if_approaching_limit(layouter, &z)
1027 }
1028
1029 fn mul_by_constant(
1030 &self,
1031 layouter: &mut impl Layouter<F>,
1032 x: &AssignedField<F, K, P>,
1033 k: K,
1034 ) -> Result<AssignedField<F, K, P>, Error> {
1035 if k.is_zero().into() {
1036 return self.assign_fixed(layouter, K::ZERO);
1037 }
1038
1039 if k == K::ONE {
1040 return Ok(x.clone());
1041 }
1042
1043 if k == -K::ONE {
1044 return self.neg(layouter, x);
1045 }
1046
1047 let threshold =
1053 P::max_limb_bound().div_floor(&(BI::from(1000) * BI::from(2).pow(P::LOG2_BASE)));
1054 if BI::from(k.to_biguint()) > threshold {
1055 let assigned_k = self.assign_fixed(layouter, k)?;
1056 return self.assign_mul(layouter, x, &assigned_k, false);
1057 }
1058
1059 let k_as_bigint: BI = k.to_biguint().into();
1071 let kv = bigint_to_fe::<F>(&k_as_bigint);
1072 let mut constants = vec![k_as_bigint.clone() - BI::one()];
1075 constants.resize(P::NB_LIMBS as usize, BI::zero());
1076
1077 let z_limb_values = x
1078 .limb_values
1079 .iter()
1080 .zip(constants.iter().map(|ci| bigint_to_fe::<F>(ci)))
1081 .map(|(xi, ci)| {
1082 self.native_gadget.linear_combination(layouter, &[(kv, xi.clone())], ci)
1083 })
1084 .collect::<Result<Vec<_>, _>>()?;
1085
1086 let limb_bounds = x
1087 .limb_bounds
1088 .iter()
1089 .zip(constants.iter())
1090 .map(|(xi_bounds, ci)| {
1091 (
1092 &xi_bounds.0 * k_as_bigint.clone() + ci,
1093 &xi_bounds.1 * k_as_bigint.clone() + ci,
1094 )
1095 })
1096 .collect();
1097
1098 let z = AssignedField::<F, K, P> {
1099 limb_values: z_limb_values,
1100 limb_bounds,
1101 _marker: PhantomData,
1102 };
1103 self.normalize_if_approaching_limit(layouter, &z)
1104 }
1105}
1106
1107impl<F, K, P, N> FieldInstructions<F, AssignedField<F, K, P>> for FieldChip<F, K, P, N>
1108where
1109 F: CircuitField,
1110 K: CircuitField,
1111 P: FieldEmulationParams<F, K>,
1112 N: NativeInstructions<F>,
1113{
1114 fn order(&self) -> BigUint {
1115 K::modulus()
1116 }
1117}
1118
1119impl<F, K, P, N> ScalarFieldInstructions<F> for FieldChip<F, K, P, N>
1120where
1121 F: CircuitField,
1122 K: CircuitField,
1123 P: FieldEmulationParams<F, K>,
1124 N: NativeInstructions<F>,
1125{
1126 type Scalar = AssignedField<F, K, P>;
1127}
1128
1129impl<F, K, P, N> DecompositionInstructions<F, AssignedField<F, K, P>> for FieldChip<F, K, P, N>
1130where
1131 F: CircuitField,
1132 K: CircuitField,
1133 P: FieldEmulationParams<F, K>,
1134 N: NativeInstructions<F>,
1135{
1136 fn assigned_to_le_bits(
1137 &self,
1138 layouter: &mut impl Layouter<F>,
1139 x: &AssignedField<F, K, P>,
1140 nb_bits: Option<usize>,
1141 enforce_canonical: bool,
1142 ) -> Result<Vec<AssignedBit<F>>, Error> {
1143 let mut x = self.add_constant(layouter, x, K::ONE)?;
1145 if enforce_canonical {
1146 x = self.make_canonical(layouter, &x)?;
1147 };
1148 let mut bits = vec![];
1149 x.limb_values
1150 .iter()
1151 .zip(well_formed_log2_bounds::<F, K, P>().iter())
1152 .map(|(cell, log2_bound)| {
1153 self.native_gadget.assigned_to_le_bits(
1154 layouter,
1155 cell,
1156 Some(*log2_bound as usize),
1157 true,
1158 )
1159 })
1160 .collect::<Result<Vec<_>, _>>()?
1161 .into_iter()
1162 .for_each(|new_bits| bits.extend(new_bits));
1163
1164 let nb_bits = min(
1165 K::NUM_BITS as usize,
1166 nb_bits.unwrap_or(K::NUM_BITS as usize),
1167 );
1168
1169 bits[nb_bits..]
1172 .iter()
1173 .try_for_each(|byte| self.native_gadget.assert_equal_to_fixed(layouter, byte, false))?;
1174 bits = bits[0..nb_bits].to_vec();
1175
1176 if enforce_canonical && nb_bits == K::NUM_BITS as usize {
1179 let canonical = self.is_canonical(layouter, &bits)?;
1180 self.assert_equal_to_fixed(layouter, &canonical, true)?;
1181 }
1182 Ok(bits)
1183 }
1184
1185 fn assigned_to_le_bytes(
1186 &self,
1187 layouter: &mut impl Layouter<F>,
1188 x: &AssignedField<F, K, P>,
1189 nb_bytes: Option<usize>,
1190 ) -> Result<Vec<AssignedByte<F>>, Error> {
1191 let nb_bytes = nb_bytes.unwrap_or(K::NUM_BITS.div_ceil(8) as usize);
1192 let bits = self.assigned_to_le_bits(layouter, x, Some(nb_bytes * 8), true)?;
1194 let bytes = bits
1195 .chunks(8)
1196 .map(|chunk| {
1197 let terms = chunk
1198 .iter()
1199 .enumerate()
1200 .map(|(i, bit)| (F::from(1 << i), bit.clone().into()))
1201 .collect::<Vec<_>>();
1202 let byte = self.native_gadget.linear_combination(layouter, &terms, F::ZERO)?;
1203 self.native_gadget.convert_unsafe(layouter, &byte)
1204 })
1205 .collect::<Result<Vec<AssignedByte<F>>, Error>>()?;
1206
1207 bytes[nb_bytes..]
1210 .iter()
1211 .try_for_each(|byte| self.native_gadget.assert_equal_to_fixed(layouter, byte, 0u8))?;
1212 Ok(bytes[0..nb_bytes].to_vec())
1213 }
1214
1215 fn assigned_from_le_bits(
1216 &self,
1217 layouter: &mut impl Layouter<F>,
1218 bits: &[AssignedBit<F>],
1219 ) -> Result<AssignedField<F, K, P>, Error> {
1220 let mut coeff = K::ONE;
1221 let mut terms = vec![];
1222 for chunk in bits.chunks(P::LOG2_BASE as usize) {
1223 let mut native_coeff = F::ONE;
1224 let mut native_terms = vec![];
1225 for b in chunk.iter() {
1226 let bit: AssignedNative<F> = b.clone().into();
1227 native_terms.push((native_coeff, bit));
1228 native_coeff = native_coeff + native_coeff;
1229 }
1230 let term = {
1231 let limb =
1232 self.native_gadget.linear_combination(layouter, &native_terms, F::ZERO)?;
1233 self.assigned_field_from_limb(layouter, &limb)?
1234 };
1235 terms.push((coeff, term));
1236 coeff = bigint_to_fe::<K>(&BI::from(2).pow(P::LOG2_BASE)) * coeff;
1237 }
1238 let x = self.linear_combination(layouter, &terms, K::ZERO)?;
1239 self.normalize(layouter, &x)
1240 }
1241
1242 fn assigned_from_le_bytes(
1243 &self,
1244 layouter: &mut impl Layouter<F>,
1245 bytes: &[AssignedByte<F>],
1246 ) -> Result<AssignedField<F, K, P>, Error> {
1247 let mut coeff = K::ONE;
1248 let mut terms = vec![];
1249 let nb_bytes_per_chunk = P::LOG2_BASE / 8;
1250 for chunk in bytes.chunks(nb_bytes_per_chunk as usize) {
1251 let mut native_coeff = F::ONE;
1252 let mut native_terms = vec![];
1253 for b in chunk.iter() {
1254 let byte: AssignedNative<F> = b.clone().into();
1255 native_terms.push((native_coeff, byte));
1256 native_coeff = F::from(256) * native_coeff;
1257 }
1258 let term = {
1259 let limb =
1260 self.native_gadget.linear_combination(layouter, &native_terms, F::ZERO)?;
1261 self.assigned_field_from_limb(layouter, &limb)?
1262 };
1263 terms.push((coeff, term));
1264 coeff = bigint_to_fe::<K>(&BI::from(2).pow(8 * nb_bytes_per_chunk)) * coeff;
1265 }
1266 let x = self.linear_combination(layouter, &terms, K::ZERO)?;
1267 self.normalize(layouter, &x)
1268 }
1269
1270 fn assigned_to_le_chunks(
1271 &self,
1272 layouter: &mut impl Layouter<F>,
1273 x: &AssignedField<F, K, P>,
1274 nb_bits_per_chunk: usize,
1275 nb_chunks: Option<usize>,
1276 ) -> Result<Vec<AssignedNative<F>>, Error> {
1277 assert!(nb_bits_per_chunk < F::NUM_BITS as usize);
1278 if P::LOG2_BASE % (nb_bits_per_chunk as u32) == 0 {
1279 let nb_chunks_per_limb = (P::LOG2_BASE / (nb_bits_per_chunk as u32)) as usize;
1280 let mut nb_missing_chunks =
1281 nb_chunks.unwrap_or(nb_chunks_per_limb * P::NB_LIMBS as usize);
1282 let x = self.add_constant(layouter, x, K::ONE)?;
1284 let x = self.normalize(layouter, &x)?;
1285 let chunks = x
1286 .limb_values
1287 .iter()
1288 .map(|limb| {
1289 let nb_chunks_on_this_limb = min(nb_missing_chunks, nb_chunks_per_limb);
1290 nb_missing_chunks -= nb_chunks_on_this_limb;
1291 self.native_gadget.assigned_to_le_chunks(
1292 layouter,
1293 limb,
1294 nb_bits_per_chunk,
1295 Some(nb_chunks_on_this_limb),
1296 )
1297 })
1298 .collect::<Result<Vec<_>, Error>>()?
1299 .concat();
1300 assert_eq!(nb_missing_chunks, 0);
1301 Ok(chunks)
1302 }
1303 else {
1306 let bits = self.assigned_to_le_bits(layouter, x, None, false)?;
1307 bits.chunks(nb_bits_per_chunk)
1308 .map(|bits_of_chunk| {
1309 self.native_gadget.assigned_from_le_bits(layouter, bits_of_chunk)
1310 })
1311 .collect::<Result<Vec<_>, Error>>()
1312 }
1313 }
1314}
1315
1316impl<F, K, P, N> FieldChip<F, K, P, N>
1317where
1318 F: CircuitField,
1319 K: CircuitField,
1320 P: FieldEmulationParams<F, K>,
1321 N: NativeInstructions<F>,
1322{
1323 pub fn new(config: &FieldChipConfig, native_gadget: &N) -> Self {
1325 Self {
1326 config: config.clone(),
1327 native_gadget: native_gadget.clone(),
1328 _marker: PhantomData,
1329 }
1330 }
1331
1332 pub fn configure(
1336 meta: &mut ConstraintSystem<F>,
1337 advice_columns: &[Column<Advice>],
1338 nb_parallel_range_checks: usize,
1339 max_bit_len: u32,
1340 ) -> FieldChipConfig {
1341 check_params::<F, K, P>();
1342
1343 let nb_limbs = P::NB_LIMBS;
1344 let x_cols = advice_columns[..(nb_limbs as usize)].to_vec();
1345 let y_cols = x_cols.clone();
1346 let z_cols = advice_columns[(nb_limbs as usize)..(2 * nb_limbs as usize)].to_vec();
1347
1348 x_cols.iter().chain(z_cols.iter()).for_each(|&col| meta.enable_equality(col));
1349
1350 let u_col = advice_columns[nb_limbs as usize];
1351 let v_cols = advice_columns
1352 [(nb_limbs as usize + 1)..(nb_limbs as usize + 1 + P::moduli().len())]
1353 .to_vec();
1354
1355 let mul_config = MulConfig::configure::<F, K, P>(
1356 meta,
1357 &x_cols,
1358 &z_cols,
1359 nb_parallel_range_checks,
1360 max_bit_len,
1361 );
1362 let norm_config = NormConfig::configure::<F, K, P>(
1363 meta,
1364 &x_cols,
1365 &z_cols,
1366 nb_parallel_range_checks,
1367 max_bit_len,
1368 );
1369
1370 FieldChipConfig {
1371 mul_config,
1372 norm_config,
1373 x_cols,
1374 y_cols,
1375 z_cols,
1376 u_col,
1377 v_cols,
1378 }
1379 }
1380
1381 fn assign_mul(
1388 &self,
1389 layouter: &mut impl Layouter<F>,
1390 x: &AssignedField<F, K, P>,
1391 y: &AssignedField<F, K, P>,
1392 division: bool,
1393 ) -> Result<AssignedField<F, K, P>, Error> {
1394 let base = BI::from(2).pow(P::LOG2_BASE);
1395 let nb_limbs = P::NB_LIMBS;
1396
1397 let x = self.normalize(layouter, x)?;
1398 let y = self.normalize(layouter, y)?;
1399
1400 y.value().error_if_known_and(|yv| division && K::is_zero(yv).into())?;
1401
1402 let zv = x
1403 .value()
1404 .zip(y.value())
1405 .map(|(xv, yv)| {
1406 if division {
1407 xv * yv.invert().unwrap()
1408 } else {
1409 xv * yv
1410 }
1411 })
1412 .map(|z| bi_to_limbs(nb_limbs, &base, &(z - K::ONE).to_biguint().into()));
1413 let z_values = (0..nb_limbs)
1414 .map(|i| zv.clone().map(|zs| bigint_to_fe::<F>(&zs[i as usize])))
1415 .collect::<Vec<_>>();
1416
1417 let z_limbs = z_values
1419 .iter()
1420 .zip(well_formed_log2_bounds::<F, K, P>().iter())
1421 .map(|(&z_value, &log2_bound)| {
1422 self.native_gadget.assign_lower_than_fixed(
1423 layouter,
1424 z_value,
1425 &(BigUint::one() << log2_bound),
1426 )
1427 })
1428 .collect::<Result<Vec<_>, Error>>()?;
1429
1430 let z = AssignedField::<F, K, P> {
1431 limb_values: z_limbs,
1432 limb_bounds: well_formed_bounds::<F, K, P>(),
1433 _marker: PhantomData,
1434 };
1435
1436 let (l, r) = if !division { (&x, &z) } else { (&z, &x) };
1439 mul::assert_mul::<F, K, P, N>(
1440 layouter,
1441 l,
1442 &y,
1443 r,
1444 &self.config.mul_config,
1445 &self.native_gadget,
1446 )?;
1447
1448 Ok(z)
1449 }
1450}
1451
1452impl<F, K, P, N> FieldChip<F, K, P, N>
1453where
1454 F: CircuitField,
1455 K: CircuitField,
1456 P: FieldEmulationParams<F, K>,
1457 N: NativeInstructions<F>,
1458{
1459 pub fn sum(
1464 &self,
1465 layouter: &mut impl Layouter<F>,
1466 positives: &[AssignedField<F, K, P>],
1467 negatives: &[AssignedField<F, K, P>],
1468 constant: K,
1469 ) -> Result<AssignedField<F, K, P>, Error> {
1470 if positives.is_empty() && negatives.is_empty() {
1471 return self.assign_fixed(layouter, constant);
1472 }
1473
1474 let base = BI::from(2).pow(P::LOG2_BASE);
1475 let p = positives.len();
1476 let n = negatives.len();
1477
1478 let constant_repr: BI = (constant - K::ONE).to_biguint().into();
1490 let constant_limbs = bi_to_limbs(P::NB_LIMBS, &base, &constant_repr);
1491 let mut offsets = constant_limbs;
1492 offsets[0] += BI::from(p) - BI::from(n);
1493
1494 let z_limb_values = (0..P::NB_LIMBS as usize)
1495 .map(|i| {
1496 let pos_terms: Vec<(F, AssignedNative<F>)> =
1497 positives.iter().map(|x| (F::ONE, x.limb_values[i].clone())).collect();
1498 let neg_terms: Vec<(F, AssignedNative<F>)> =
1499 negatives.iter().map(|x| (-F::ONE, x.limb_values[i].clone())).collect();
1500 let native_terms: Vec<_> = pos_terms.into_iter().chain(neg_terms).collect();
1501 self.native_gadget.linear_combination(
1502 layouter,
1503 &native_terms,
1504 bigint_to_fe::<F>(&offsets[i]),
1505 )
1506 })
1507 .collect::<Result<Vec<_>, _>>()?;
1508
1509 let all_terms = positives
1512 .iter()
1513 .zip(std::iter::repeat(false))
1514 .chain(negatives.iter().zip(std::iter::repeat(true)));
1515 let init_bounds: Vec<(BI, BI)> = (0..P::NB_LIMBS as usize)
1516 .map(|i| (offsets[i].clone(), offsets[i].clone()))
1517 .collect();
1518 let z_bounds = all_terms.fold(init_bounds, |acc, (x, negated)| {
1519 acc.into_iter()
1520 .zip(x.limb_bounds.iter())
1521 .map(|((lo, hi), b)| {
1522 if negated {
1523 (lo - &b.1, hi - &b.0)
1524 } else {
1525 (lo + &b.0, hi + &b.1)
1526 }
1527 })
1528 .collect()
1529 });
1530
1531 let z = AssignedField::<F, K, P> {
1532 limb_values: z_limb_values,
1533 limb_bounds: z_bounds,
1534 _marker: PhantomData,
1535 };
1536 self.normalize_if_approaching_limit(layouter, &z)
1537 }
1538
1539 pub(crate) fn normalize(
1542 &self,
1543 layouter: &mut impl Layouter<F>,
1544 x: &AssignedField<F, K, P>,
1545 ) -> Result<AssignedField<F, K, P>, Error> {
1546 if x.is_well_formed() {
1547 Ok(x.clone())
1548 } else {
1549 self.make_canonical(layouter, x)
1550 }
1551 }
1552
1553 pub(crate) fn normalize_if_approaching_limit(
1556 &self,
1557 layouter: &mut impl Layouter<F>,
1558 x: &AssignedField<F, K, P>,
1559 ) -> Result<AssignedField<F, K, P>, Error> {
1560 let threshold: BI = P::max_limb_bound() / 10;
1562 let dangerous_lower_bounds = x.limb_bounds.iter().any(|b| b.0 < -threshold.clone());
1563 let dangerous_upper_bounds = x.limb_bounds.iter().any(|b| b.1 > threshold);
1564 if dangerous_lower_bounds || dangerous_upper_bounds {
1565 self.make_canonical(layouter, x)
1566 } else {
1567 Ok(x.clone())
1568 }
1569 }
1570
1571 fn make_canonical(
1574 &self,
1575 layouter: &mut impl Layouter<F>,
1576 x: &AssignedField<F, K, P>,
1577 ) -> Result<AssignedField<F, K, P>, Error> {
1578 let max_limb_bound = P::max_limb_bound();
1579 x.limb_bounds.iter().for_each(|(lower, upper)| {
1580 if lower < &(-&max_limb_bound) || upper > &max_limb_bound {
1581 panic!(
1582 "make_canonical: the limb bounds of the input: [{}, {}] exceed the
1583 maximum limb bound value {}; consider applying a normalization
1584 earlier, when the bounds are still within the permited range;
1585 increasing the [max_limb_bound] of your FieldEmulationParams could also
1586 help, if possible.",
1587 lower, upper, max_limb_bound
1588 );
1589 }
1590 });
1591 let z_limbs = norm::normalize::<F, K, P, N>(
1592 layouter,
1593 x,
1594 &self.config.norm_config,
1595 &self.native_gadget,
1596 )?;
1597 let z = AssignedField::<F, K, P> {
1598 limb_values: z_limbs,
1599 limb_bounds: well_formed_bounds::<F, K, P>(),
1600 _marker: PhantomData,
1601 };
1602 Ok(z)
1603 }
1604
1605 fn assigned_field_from_limb(
1608 &self,
1609 layouter: &mut impl Layouter<F>,
1610 limb: &AssignedNative<F>,
1611 ) -> Result<AssignedField<F, K, P>, Error> {
1612 let least_significant_limb = self.native_gadget.add_constant(layouter, limb, -F::ONE)?;
1614 let mut limb_values = vec![least_significant_limb];
1615 let mut limb_bounds = well_formed_bounds::<F, K, P>();
1616 let zero = self.native_gadget.assign_fixed(layouter, F::ZERO)?;
1617 limb_values.resize(P::NB_LIMBS as usize, zero);
1618 limb_bounds[0] = (limb_bounds[0].clone().0 - 1, limb_bounds[0].clone().1 - 1);
1619 Ok(AssignedField::<F, K, P> {
1620 limb_values,
1621 limb_bounds,
1622 _marker: PhantomData,
1623 })
1624 }
1625}
1626
1627impl<F, K, P, N> AssignmentInstructions<F, AssignedBit<F>> for FieldChip<F, K, P, N>
1629where
1630 F: CircuitField,
1631 K: CircuitField,
1632 P: FieldEmulationParams<F, K>,
1633 N: NativeInstructions<F>,
1634{
1635 fn assign(
1636 &self,
1637 layouter: &mut impl Layouter<F>,
1638 value: Value<bool>,
1639 ) -> Result<AssignedBit<F>, Error> {
1640 self.native_gadget.assign(layouter, value)
1641 }
1642
1643 fn assign_fixed(
1644 &self,
1645 layouter: &mut impl Layouter<F>,
1646 constant: bool,
1647 ) -> Result<AssignedBit<F>, Error> {
1648 self.native_gadget.assign_fixed(layouter, constant)
1649 }
1650}
1651
1652impl<F, K, P, N> AssertionInstructions<F, AssignedBit<F>> for FieldChip<F, K, P, N>
1654where
1655 F: CircuitField,
1656 K: CircuitField,
1657 P: FieldEmulationParams<F, K>,
1658 N: NativeInstructions<F>,
1659{
1660 fn assert_equal(
1661 &self,
1662 layouter: &mut impl Layouter<F>,
1663 x: &AssignedBit<F>,
1664 y: &AssignedBit<F>,
1665 ) -> Result<(), Error> {
1666 self.native_gadget.assert_equal(layouter, x, y)
1667 }
1668
1669 fn assert_not_equal(
1670 &self,
1671 layouter: &mut impl Layouter<F>,
1672 x: &AssignedBit<F>,
1673 y: &AssignedBit<F>,
1674 ) -> Result<(), Error> {
1675 self.native_gadget.assert_not_equal(layouter, x, y)
1676 }
1677
1678 fn assert_equal_to_fixed(
1679 &self,
1680 layouter: &mut impl Layouter<F>,
1681 x: &AssignedBit<F>,
1682 constant: bool,
1683 ) -> Result<(), Error> {
1684 self.native_gadget.assert_equal_to_fixed(layouter, x, constant)
1685 }
1686
1687 fn assert_not_equal_to_fixed(
1688 &self,
1689 layouter: &mut impl Layouter<F>,
1690 x: &AssignedBit<F>,
1691 constant: bool,
1692 ) -> Result<(), Error> {
1693 self.native_gadget.assert_not_equal_to_fixed(layouter, x, constant)
1694 }
1695}
1696
1697impl<F, K, P, N> ConversionInstructions<F, AssignedBit<F>, AssignedField<F, K, P>>
1698 for FieldChip<F, K, P, N>
1699where
1700 F: CircuitField,
1701 K: CircuitField,
1702 P: FieldEmulationParams<F, K>,
1703 N: NativeInstructions<F>,
1704{
1705 fn convert_value(&self, x: &bool) -> Option<K> {
1706 Some(if *x { K::ONE } else { K::ZERO })
1707 }
1708
1709 fn convert(
1710 &self,
1711 layouter: &mut impl Layouter<F>,
1712 x: &AssignedBit<F>,
1713 ) -> Result<AssignedField<F, K, P>, Error> {
1714 let x: AssignedNative<F> = x.clone().into();
1715 self.assigned_field_from_limb(layouter, &x)
1716 }
1717}
1718
1719impl<F, K, P, N> ConversionInstructions<F, AssignedByte<F>, AssignedField<F, K, P>>
1720 for FieldChip<F, K, P, N>
1721where
1722 F: CircuitField,
1723 K: CircuitField,
1724 P: FieldEmulationParams<F, K>,
1725 N: NativeInstructions<F>,
1726{
1727 fn convert_value(&self, x: &u8) -> Option<K> {
1728 Some(K::from(*x as u64))
1729 }
1730
1731 fn convert(
1732 &self,
1733 layouter: &mut impl Layouter<F>,
1734 x: &AssignedByte<F>,
1735 ) -> Result<AssignedField<F, K, P>, Error> {
1736 let x: AssignedNative<F> = x.clone().into();
1737 self.assigned_field_from_limb(layouter, &x)
1738 }
1739}
1740
1741impl<F, K, P, N> CanonicityInstructions<F, AssignedField<F, K, P>> for FieldChip<F, K, P, N>
1743where
1744 F: CircuitField,
1745 K: CircuitField,
1746 P: FieldEmulationParams<F, K>,
1747 N: NativeInstructions<F>,
1748{
1749 fn le_bits_lower_than(
1750 &self,
1751 layouter: &mut impl Layouter<F>,
1752 bits: &[AssignedBit<F>],
1753 bound: BigUint,
1754 ) -> Result<AssignedBit<F>, Error> {
1755 self.native_gadget.le_bits_lower_than(layouter, bits, bound)
1756 }
1757
1758 fn le_bits_geq_than(
1759 &self,
1760 layouter: &mut impl Layouter<F>,
1761 bits: &[AssignedBit<F>],
1762 bound: BigUint,
1763 ) -> Result<AssignedBit<F>, Error> {
1764 self.native_gadget.le_bits_geq_than(layouter, bits, bound)
1765 }
1766}
1767
1768#[derive(Clone, Debug)]
1769#[cfg(any(test, feature = "testing"))]
1770pub struct FieldChipConfigForTests<F, N>
1773where
1774 F: CircuitField,
1775 N: NativeInstructions<F> + FromScratch<F>,
1776{
1777 native_gadget_config: <N as FromScratch<F>>::Config,
1778 field_chip_config: FieldChipConfig,
1779}
1780
1781#[cfg(any(test, feature = "testing"))]
1782impl<F, K, P, N> FromScratch<F> for FieldChip<F, K, P, N>
1783where
1784 F: CircuitField,
1785 K: CircuitField,
1786 P: FieldEmulationParams<F, K>,
1787 N: NativeInstructions<F> + FromScratch<F>,
1788{
1789 type Config = FieldChipConfigForTests<F, N>;
1790
1791 fn new_from_scratch(config: &FieldChipConfigForTests<F, N>) -> Self {
1792 let native_gadget = <N as FromScratch<F>>::new_from_scratch(&config.native_gadget_config);
1793 FieldChip::new(&config.field_chip_config, &native_gadget)
1794 }
1795
1796 fn load_from_scratch(&self, layouter: &mut impl Layouter<F>) -> Result<(), Error> {
1797 self.native_gadget.load_from_scratch(layouter)
1798 }
1799
1800 fn configure_from_scratch(
1801 meta: &mut ConstraintSystem<F>,
1802 advice_columns: &mut Vec<Column<Advice>>,
1803 fixed_columns: &mut Vec<Column<Fixed>>,
1804 instance_columns: &[Column<Instance>; 2],
1805 ) -> FieldChipConfigForTests<F, N> {
1806 let native_gadget_config = <N as FromScratch<F>>::configure_from_scratch(
1807 meta,
1808 advice_columns,
1809 fixed_columns,
1810 instance_columns,
1811 );
1812 let nb_parallel_range_checks = 4;
1814 let max_bit_len = 8;
1815 let field_chip_config = {
1816 let nb_fc_cols = nb_field_chip_columns::<F, K, P>();
1817 while advice_columns.len() < nb_fc_cols {
1818 advice_columns.push(meta.advice_column());
1819 }
1820 FieldChip::<F, K, P, N>::configure(
1821 meta,
1822 &advice_columns[..nb_fc_cols],
1823 nb_parallel_range_checks,
1824 max_bit_len,
1825 )
1826 };
1827 FieldChipConfigForTests {
1828 native_gadget_config,
1829 field_chip_config,
1830 }
1831 }
1832}
1833
1834#[cfg(test)]
1835mod tests {
1836 use midnight_curves::{
1837 k256::{Fp as K256Base, Fq as K256Scalar},
1838 p256::{Fp as P256Base, Fq as P256Scalar},
1839 Fq as BlsScalar,
1840 };
1841
1842 use super::*;
1843 use crate::{
1844 field::{
1845 decomposition::chip::P2RDecompositionChip, foreign::params::MultiEmulationParams,
1846 NativeChip, NativeGadget,
1847 },
1848 instructions::{
1849 arithmetic, assertions, control_flow, decomposition, equality, public_input, zero,
1850 },
1851 };
1852
1853 macro_rules! test_generic {
1854 ($mod:ident, $op:ident, $native:ident, $emulated:ident, $name:expr) => {
1855 $mod::tests::$op::<
1856 $native,
1857 AssignedField<$native, $emulated, MultiEmulationParams>,
1858 FieldChip<
1859 $native,
1860 $emulated,
1861 MultiEmulationParams,
1862 NativeGadget<$native, P2RDecompositionChip<$native>, NativeChip<$native>>,
1863 >,
1864 >($name);
1865 };
1866 }
1867
1868 macro_rules! test {
1869 ($mod:ident, $op:ident) => {
1870 #[test]
1871 fn $op() {
1872 test_generic!($mod, $op, BlsScalar, K256Base, "field_chip_k256_base");
1873 test_generic!($mod, $op, BlsScalar, K256Scalar, "field_chip_k256_scalar");
1874 test_generic!($mod, $op, BlsScalar, P256Base, "field_chip_p256_base");
1875 test_generic!($mod, $op, BlsScalar, P256Scalar, "field_chip_p256_scalar");
1876 }
1877 };
1878 }
1879
1880 test!(assertions, test_assertions);
1881
1882 test!(public_input, test_public_inputs);
1883
1884 test!(equality, test_is_equal);
1885
1886 test!(zero, test_zero_assertions);
1887 test!(zero, test_is_zero);
1888
1889 test!(control_flow, test_select);
1890 test!(control_flow, test_cond_assert_equal);
1891 test!(control_flow, test_cond_swap);
1892
1893 test!(arithmetic, test_add);
1894 test!(arithmetic, test_sub);
1895 test!(arithmetic, test_mul);
1896 test!(arithmetic, test_div);
1897 test!(arithmetic, test_neg);
1898 test!(arithmetic, test_inv);
1899 test!(arithmetic, test_pow);
1900 test!(arithmetic, test_linear_combination);
1901 test!(arithmetic, test_add_and_mul);
1902
1903 macro_rules! test_generic {
1904 ($mod:ident, $op:ident, $native:ident, $emulated:ident, $name:expr) => {
1905 $mod::tests::$op::<
1906 $native,
1907 AssignedField<$native, $emulated, MultiEmulationParams>,
1908 FieldChip<
1909 $native,
1910 $emulated,
1911 MultiEmulationParams,
1912 NativeGadget<$native, P2RDecompositionChip<$native>, NativeChip<$native>>,
1913 >,
1914 NativeGadget<$native, P2RDecompositionChip<$native>, NativeChip<$native>>,
1915 >($name);
1916 };
1917 }
1918
1919 macro_rules! test {
1920 ($mod:ident, $op:ident) => {
1921 #[test]
1922 fn $op() {
1923 test_generic!($mod, $op, BlsScalar, K256Base, "field_chip_k256_base");
1924 test_generic!($mod, $op, BlsScalar, K256Scalar, "field_chip_k256_scalar");
1925 test_generic!($mod, $op, BlsScalar, P256Base, "field_chip_p256_base");
1926 test_generic!($mod, $op, BlsScalar, P256Scalar, "field_chip_p256_scalar");
1927 }
1928 };
1929 }
1930
1931 test!(decomposition, test_bit_decomposition);
1932 test!(decomposition, test_byte_decomposition);
1933 test!(decomposition, test_sgn0);
1934}