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 fn from_public_input(fields: &[F]) -> Option<K> {
142 let base = BI::from(2).pow(P::LOG2_BASE);
143 let nb_limbs_per_batch = (F::CAPACITY / P::LOG2_BASE) as usize;
144 if fields.len() != (P::NB_LIMBS as usize).div_ceil(nb_limbs_per_batch) {
145 return None;
146 }
147 let limbs: Vec<BI> = fields
148 .iter()
149 .flat_map(|f| {
150 let bi: BI = f.to_biguint().into();
151 bi_to_limbs(nb_limbs_per_batch as u32, &base, &bi)
152 })
153 .collect();
154 let (head, tail) = limbs.split_at(P::NB_LIMBS as usize);
155 if tail.iter().any(|l| !l.is_zero()) {
156 return None;
157 }
158 let element_as_bi = bi_from_limbs(&base, head) + BI::one();
159 Some(bigint_to_fe::<K>(&element_as_bi))
160 }
161}
162
163impl<F, K, P> InnerValue for AssignedField<F, K, P>
164where
165 F: CircuitField,
166 K: CircuitField,
167 P: FieldEmulationParams<F, K>,
168{
169 type Element = K;
170
171 fn value(&self) -> Value<K> {
172 let bi_limbs = self
173 .limb_values
174 .iter()
175 .zip(self.limb_bounds.iter())
176 .map(|(xi, (lower_bound, _))| {
177 let shift = BI::abs(lower_bound);
180 let fe_shift = bigint_to_fe::<F>(&shift);
181 xi.value().map(|xv| {
182 let bi: BI = (*xv + fe_shift).to_biguint().into();
183 bi - &shift
184 })
185 })
186 .collect::<Vec<_>>();
187 let bi_limbs: Value<Vec<BI>> = Value::from_iter(bi_limbs);
188 let base = BI::from(2).pow(P::LOG2_BASE);
189 bi_limbs.map(|limbs| bigint_to_fe::<K>(&(BI::one() + bi_from_limbs(&base, &limbs))))
190 }
191}
192
193impl<F: CircuitField, K: CircuitField, P> InnerConstants for AssignedField<F, K, P>
194where
195 F: CircuitField,
196 K: CircuitField,
197 P: FieldEmulationParams<F, K>,
198{
199 fn inner_zero() -> K {
200 K::ZERO
201 }
202
203 fn inner_one() -> K {
204 K::ONE
205 }
206}
207
208impl<F, K, P> AssignedField<F, K, P>
209where
210 F: CircuitField,
211 K: CircuitField,
212 P: FieldEmulationParams<F, K>,
213{
214 pub(crate) fn from_limbs_unsafe(limb_values: Vec<AssignedNative<F>>) -> Self {
220 debug_assert!(limb_values.len() as u32 == P::NB_LIMBS);
221 Self {
222 limb_values,
223 limb_bounds: well_formed_bounds::<F, K, P>(),
224 _marker: PhantomData,
225 }
226 }
227}
228
229#[cfg(any(test, feature = "testing"))]
230impl<F, K, P> Sampleable for AssignedField<F, K, P>
231where
232 F: CircuitField,
233 K: CircuitField,
234 P: FieldEmulationParams<F, K>,
235{
236 fn sample_inner(rng: impl RngCore) -> K {
237 K::random(rng)
238 }
239}
240
241pub fn nb_field_chip_columns<F, K, P>() -> usize
243where
244 F: CircuitField,
245 K: CircuitField,
246 P: FieldEmulationParams<F, K>,
247{
248 P::NB_LIMBS as usize + max(P::NB_LIMBS as usize, 1 + P::moduli().len())
249}
250
251pub fn well_formed_log2_bounds<F, K, P>() -> Vec<u32>
258where
259 F: CircuitField,
260 K: CircuitField,
261 P: FieldEmulationParams<F, K>,
262{
263 let m = &K::modulus().to_bigint().unwrap();
268 let log2_msl_bound = m.bits() as u32 - (P::NB_LIMBS - 1) * P::LOG2_BASE;
269 let mut bounds = vec![log2_msl_bound];
270 bounds.resize(P::NB_LIMBS as usize, P::LOG2_BASE);
271 bounds.into_iter().rev().collect::<Vec<_>>()
272}
273
274#[derive(Clone, Debug)]
282pub struct FieldChipConfig {
283 mul_config: mul::MulConfig,
284 norm_config: norm::NormConfig,
285 pub x_cols: Vec<Column<Advice>>,
287 pub y_cols: Vec<Column<Advice>>,
289 pub z_cols: Vec<Column<Advice>>,
291 pub u_col: Column<Advice>,
293 pub v_cols: Vec<Column<Advice>>,
295}
296
297#[derive(Clone, Debug)]
299pub struct FieldChip<F, K, P, N>
300where
301 F: CircuitField,
302 K: CircuitField,
303 P: FieldEmulationParams<F, K>,
304 N: NativeInstructions<F>,
305{
306 config: FieldChipConfig,
307 pub(crate) native_gadget: N,
308 _marker: PhantomData<(F, K, P, N)>,
309}
310
311impl<F, K, P> AssignedField<F, K, P>
312where
313 F: CircuitField,
314 K: CircuitField,
315 P: FieldEmulationParams<F, K>,
316{
317 pub fn modulus(&self) -> BI {
319 K::modulus().to_bigint().unwrap().clone()
320 }
321
322 pub fn is_well_formed(&self) -> bool {
328 self.limb_bounds.iter().zip(well_formed_log2_bounds::<F, K, P>()).all(
329 |((lower, upper), expected_upper)| {
330 assert!(lower <= upper);
331 !BI::is_negative(lower) && upper.bits() <= expected_upper as u64
332 },
333 )
334 }
335
336 pub fn limb_values(&self) -> Vec<AssignedNative<F>> {
340 self.limb_values.clone()
341 }
342
343 pub fn bigint_limbs(&self) -> Value<Vec<BI>> {
345 let limbs = self
346 .limb_values
347 .iter()
348 .zip(self.limb_bounds.iter())
349 .map(|(xi, (lbound, _))| {
350 let shift = BI::abs(lbound);
353 let fe_shift = bigint_to_fe::<F>(&shift);
354 xi.value().map(|xv| {
355 let bi: BI = (*xv + fe_shift).to_biguint().into();
356 bi - &shift
357 })
358 })
359 .collect::<Vec<_>>();
360 Value::from_iter(limbs)
361 }
362}
363
364fn well_formed_bounds<F, K, P>() -> Vec<(BI, BI)>
369where
370 F: CircuitField,
371 K: CircuitField,
372 P: FieldEmulationParams<F, K>,
373{
374 well_formed_log2_bounds::<F, K, P>()
375 .into_iter()
376 .map(|log2_base| (BI::zero(), BI::from(2).pow(log2_base) - BI::one()))
377 .collect()
378}
379
380fn limbs_of_zero<F, K, P>() -> Vec<BI>
382where
383 F: CircuitField,
384 K: CircuitField,
385 P: FieldEmulationParams<F, K>,
386{
387 bi_to_limbs(
388 P::NB_LIMBS,
389 &BI::from(2).pow(P::LOG2_BASE),
390 &(K::modulus().to_bigint().unwrap() - BI::one()),
391 )
392}
393
394impl<F, K, P, N> Chip<F> for FieldChip<F, K, P, N>
395where
396 F: CircuitField,
397 K: CircuitField,
398 P: FieldEmulationParams<F, K>,
399 N: NativeInstructions<F>,
400{
401 type Config = FieldChipConfig;
402 type Loaded = ();
403 fn config(&self) -> &Self::Config {
404 &self.config
405 }
406 fn loaded(&self) -> &Self::Loaded {
407 &()
408 }
409}
410
411impl<F, K, P, N> AssignmentInstructions<F, AssignedField<F, K, P>> for FieldChip<F, K, P, N>
412where
413 F: CircuitField,
414 K: CircuitField,
415 P: FieldEmulationParams<F, K>,
416 N: NativeInstructions<F>,
417{
418 fn assign(
419 &self,
420 layouter: &mut impl Layouter<F>,
421 x: Value<K>,
422 ) -> Result<AssignedField<F, K, P>, Error> {
423 let base = BI::from(2).pow(P::LOG2_BASE);
424 let x = x.map(|v| {
427 let bi = (v - K::ONE).to_biguint().into();
428 bi_to_limbs(P::NB_LIMBS, &base, &bi)
429 });
430
431 let x_cells = (0..P::NB_LIMBS)
433 .map(|i| x.clone().map(|limbs| bigint_to_fe::<F>(&limbs[i as usize])))
434 .zip(well_formed_log2_bounds::<F, K, P>().iter())
435 .map(|(xi_value, log2_bound)| {
436 self.native_gadget.assign_lower_than_fixed(
437 layouter,
438 xi_value,
439 &(BigUint::one() << *log2_bound),
440 )
441 })
442 .collect::<Result<Vec<_>, Error>>()?;
443
444 Ok(AssignedField::<F, K, P> {
445 limb_values: x_cells,
446 limb_bounds: well_formed_bounds::<F, K, P>(),
447 _marker: PhantomData,
448 })
449 }
450
451 fn assign_fixed(
452 &self,
453 layouter: &mut impl Layouter<F>,
454 constant: K,
455 ) -> Result<AssignedField<F, K, P>, Error> {
456 let base = BI::from(2).pow(P::LOG2_BASE);
457 let constant = (constant - K::ONE).to_biguint().into();
460 let constant_limbs = bi_to_limbs(P::NB_LIMBS, &base, &constant);
461 let constant_cells = constant_limbs
462 .iter()
463 .map(|x| self.native_gadget.assign_fixed(layouter, bigint_to_fe::<F>(x)))
464 .collect::<Result<Vec<_>, _>>()?;
465
466 Ok(AssignedField::<F, K, P> {
476 limb_values: constant_cells,
477 limb_bounds: well_formed_bounds::<F, K, P>(),
478 _marker: PhantomData,
479 })
480 }
481}
482
483impl<F, K, P> From<AssignedField<F, K, P>> for Vec<AssignedNative<F>>
486where
487 F: CircuitField,
488 K: CircuitField,
489 P: FieldEmulationParams<F, K>,
490{
491 fn from(x: AssignedField<F, K, P>) -> Self {
492 x.limb_values()
493 }
494}
495
496impl<F, K, P, N> PublicInputInstructions<F, AssignedField<F, K, P>> for FieldChip<F, K, P, N>
497where
498 F: CircuitField,
499 K: CircuitField,
500 P: FieldEmulationParams<F, K>,
501 N: NativeInstructions<F>,
502{
503 fn as_public_input(
504 &self,
505 layouter: &mut impl Layouter<F>,
506 assigned: &AssignedField<F, K, P>,
507 ) -> Result<Vec<AssignedNative<F>>, Error> {
508 let assigned = self.normalize(layouter, assigned)?;
509 let nb_limbs_per_batch = (F::CAPACITY / P::LOG2_BASE) as usize;
510 let base = BI::from(2).pow(P::LOG2_BASE);
511 assigned
512 .limb_values
513 .chunks(nb_limbs_per_batch)
514 .map(|chunk| {
515 let terms: Vec<(F, AssignedNative<F>)> = chunk
516 .iter()
517 .enumerate()
518 .map(|(i, limb)| (bigint_to_fe::<F>(&base.pow(i as u32)), limb.clone()))
519 .collect();
520 self.native_gadget.linear_combination(layouter, &terms, F::ZERO)
521 })
522 .collect()
523 }
524
525 fn constrain_as_public_input(
526 &self,
527 layouter: &mut impl Layouter<F>,
528 assigned: &AssignedField<F, K, P>,
529 ) -> Result<(), Error> {
530 self.as_public_input(layouter, assigned)?
531 .iter()
532 .try_for_each(|c| self.native_gadget.constrain_as_public_input(layouter, c))
533 }
534
535 fn assign_as_public_input(
536 &self,
537 layouter: &mut impl Layouter<F>,
538 value: Value<K>,
539 ) -> Result<AssignedField<F, K, P>, Error> {
540 let assigned = self.assign(layouter, value)?;
543 self.constrain_as_public_input(layouter, &assigned)?;
544 Ok(assigned)
545 }
546}
547
548impl<F, K, P, N> AssertionInstructions<F, AssignedField<F, K, P>> for FieldChip<F, K, P, N>
549where
550 F: CircuitField,
551 K: CircuitField,
552 P: FieldEmulationParams<F, K>,
553 N: NativeInstructions<F>,
554{
555 fn assert_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 x = self.normalize(layouter, x)?;
567 let y = self.normalize(layouter, y)?;
568 x.limb_values
569 .iter()
570 .zip(y.limb_values.iter())
571 .map(|(xi, yi)| self.native_gadget.assert_equal(layouter, xi, yi))
572 .collect::<Result<Vec<_>, _>>()?;
573 Ok(())
574 }
575
576 fn assert_not_equal(
577 &self,
578 layouter: &mut impl Layouter<F>,
579 x: &AssignedField<F, K, P>,
580 y: &AssignedField<F, K, P>,
581 ) -> Result<(), Error> {
582 let diff = self.sub(layouter, x, y)?;
583 self.assert_non_zero(layouter, &diff)
584 }
585
586 fn assert_equal_to_fixed(
587 &self,
588 layouter: &mut impl Layouter<F>,
589 x: &AssignedField<F, K, P>,
590 constant: K,
591 ) -> Result<(), Error> {
592 let x = self.normalize(layouter, x)?;
598 let constant_limbs = {
599 let constant = (constant - K::ONE).to_biguint().into();
600 let base = BI::from(2).pow(P::LOG2_BASE);
601 bi_to_limbs(P::NB_LIMBS, &base, &constant)
602 };
603 x.limb_values
604 .iter()
605 .zip(constant_limbs.iter())
606 .map(|(xi, ki)| {
607 self.native_gadget.assert_equal_to_fixed(layouter, xi, bigint_to_fe::<F>(ki))
608 })
609 .collect::<Result<Vec<_>, _>>()?;
610 Ok(())
611 }
612
613 fn assert_not_equal_to_fixed(
614 &self,
615 layouter: &mut impl Layouter<F>,
616 x: &AssignedField<F, K, P>,
617 constant: K,
618 ) -> Result<(), Error> {
619 let diff = self.add_constant(layouter, x, -constant)?;
620 self.assert_non_zero(layouter, &diff)
621 }
622}
623
624impl<F, K, P, N> EqualityInstructions<F, AssignedField<F, K, P>> for FieldChip<F, K, P, N>
625where
626 F: CircuitField,
627 K: CircuitField,
628 P: FieldEmulationParams<F, K>,
629 N: NativeInstructions<F>,
630{
631 fn is_equal(
632 &self,
633 layouter: &mut impl Layouter<F>,
634 x: &AssignedField<F, K, P>,
635 y: &AssignedField<F, K, P>,
636 ) -> Result<AssignedBit<F>, Error> {
637 let diff = self.sub(layouter, x, y)?;
638 self.is_zero(layouter, &diff)
639 }
640
641 fn is_not_equal(
642 &self,
643 layouter: &mut impl Layouter<F>,
644 x: &AssignedField<F, K, P>,
645 y: &AssignedField<F, K, P>,
646 ) -> Result<AssignedBit<F>, Error> {
647 let b = self.is_equal(layouter, x, y)?;
648 self.native_gadget.not(layouter, &b)
649 }
650
651 fn is_equal_to_fixed(
652 &self,
653 layouter: &mut impl Layouter<F>,
654 x: &AssignedField<F, K, P>,
655 constant: <AssignedField<F, K, P> as InnerValue>::Element,
656 ) -> Result<AssignedBit<F>, Error> {
657 let diff = self.add_constant(layouter, x, -constant)?;
658 self.is_zero(layouter, &diff)
659 }
660
661 fn is_not_equal_to_fixed(
662 &self,
663 layouter: &mut impl Layouter<F>,
664 x: &AssignedField<F, K, P>,
665 constant: <AssignedField<F, K, P> as InnerValue>::Element,
666 ) -> Result<AssignedBit<F>, Error> {
667 let b = self.is_equal_to_fixed(layouter, x, constant)?;
668 self.native_gadget.not(layouter, &b)
669 }
670}
671
672impl<F, K, P, N> ZeroInstructions<F, AssignedField<F, K, P>> for FieldChip<F, K, P, N>
673where
674 F: CircuitField,
675 K: CircuitField,
676 P: FieldEmulationParams<F, K>,
677 N: NativeInstructions<F>,
678{
679 fn assert_non_zero(
680 &self,
681 layouter: &mut impl Layouter<F>,
682 x: &AssignedField<F, K, P>,
683 ) -> Result<(), Error> {
684 let b = self.is_zero(layouter, x)?;
685 self.native_gadget.assert_equal_to_fixed(layouter, &b, false)
686 }
687
688 fn is_zero(
689 &self,
690 layouter: &mut impl Layouter<F>,
691 x: &AssignedField<F, K, P>,
692 ) -> Result<AssignedBit<F>, Error> {
693 let x = self.normalize(layouter, x)?;
696 let bs = x
697 .limb_values
698 .iter()
699 .zip(limbs_of_zero::<F, K, P>().iter())
700 .map(|(xi, ci)| {
701 self.native_gadget.is_equal_to_fixed(layouter, xi, bigint_to_fe::<F>(ci))
702 })
703 .collect::<Result<Vec<_>, _>>()?;
704 self.native_gadget.and(layouter, &bs)
705 }
706}
707
708impl<F, K, P, N> ControlFlowInstructions<F, AssignedField<F, K, P>> for FieldChip<F, K, P, N>
709where
710 F: CircuitField,
711 K: CircuitField,
712 P: FieldEmulationParams<F, K>,
713 N: NativeInstructions<F>,
714{
715 fn select(
716 &self,
717 layouter: &mut impl Layouter<F>,
718 cond: &AssignedBit<F>,
719 x: &AssignedField<F, K, P>,
720 y: &AssignedField<F, K, P>,
721 ) -> Result<AssignedField<F, K, P>, Error> {
722 let z_limb_values = x
723 .limb_values
724 .iter()
725 .zip(y.limb_values.iter())
726 .map(|(xi, yi)| self.native_gadget.select(layouter, cond, xi, yi))
727 .collect::<Result<Vec<_>, _>>()?;
728 let z_limb_bounds = x
729 .limb_bounds
730 .iter()
731 .zip(y.limb_bounds.iter())
732 .map(|(xi_bounds, yi_bounds)| {
733 (
734 min(&xi_bounds.0, &yi_bounds.0).clone(),
735 max(&xi_bounds.1, &yi_bounds.1).clone(),
736 )
737 })
738 .collect::<Vec<_>>();
739 Ok(AssignedField::<F, K, P> {
740 limb_values: z_limb_values,
741 limb_bounds: z_limb_bounds,
742 _marker: PhantomData,
743 })
744 }
745}
746
747impl<F, K, P, N> ArithInstructions<F, AssignedField<F, K, P>> for FieldChip<F, K, P, N>
748where
749 F: CircuitField,
750 K: CircuitField,
751 P: FieldEmulationParams<F, K>,
752 N: NativeInstructions<F>,
753{
754 fn linear_combination(
755 &self,
756 layouter: &mut impl Layouter<F>,
757 terms: &[(K, AssignedField<F, K, P>)],
758 constant: K,
759 ) -> Result<AssignedField<F, K, P>, Error> {
760 if terms.is_empty() {
761 return self.assign_fixed(layouter, constant);
762 }
763
764 if terms.iter().all(|(c, _)| *c == K::ONE || *c == -K::ONE) {
766 let (pos, neg): (Vec<_>, Vec<_>) = terms.iter().partition(|(c, _)| *c == K::ONE);
767 let pos: Vec<_> = pos.into_iter().map(|(_, x)| x.clone()).collect();
768 let neg: Vec<_> = neg.into_iter().map(|(_, x)| x.clone()).collect();
769 return self.sum(layouter, &pos, &neg, constant);
770 }
771
772 let init: AssignedField<F, K, P> = self.assign_fixed(layouter, constant)?;
774 let res = terms.iter().try_fold(init, |acc, (c, x)| {
775 let prod = self.mul_by_constant(layouter, x, *c)?;
776 self.add(layouter, &acc, &prod)
777 })?;
778 self.normalize_if_approaching_limit(layouter, &res)
779 }
780
781 fn add(
782 &self,
783 layouter: &mut impl Layouter<F>,
784 x: &AssignedField<F, K, P>,
785 y: &AssignedField<F, K, P>,
786 ) -> Result<AssignedField<F, K, P>, Error> {
787 let zero: AssignedField<F, K, P> = self.assign_fixed(layouter, K::ZERO)?;
788
789 if x == &zero {
790 return Ok(y.clone());
791 }
792
793 if y == &zero {
794 return Ok(x.clone());
795 }
796
797 let mut constants = vec![BI::one()];
804 constants.resize(P::NB_LIMBS as usize, BI::zero());
805
806 let z_limb_values = x
807 .limb_values
808 .iter()
809 .zip(y.limb_values.iter())
810 .zip(constants.iter().map(|ci| bigint_to_fe::<F>(ci)))
811 .map(|((xi, yi), ci)| {
812 self.native_gadget.linear_combination(
813 layouter,
814 &[(F::ONE, xi.clone()), (F::ONE, yi.clone())],
815 ci,
816 )
817 })
818 .collect::<Result<Vec<_>, _>>()?;
819
820 let z = AssignedField::<F, K, P> {
821 limb_values: z_limb_values,
822 limb_bounds: x
823 .limb_bounds
824 .iter()
825 .zip(y.limb_bounds.iter())
826 .zip(constants.iter())
827 .map(|((xi_bounds, yi_bounds), ci)| {
828 (
829 &xi_bounds.0 + &yi_bounds.0 + ci,
830 &xi_bounds.1 + &yi_bounds.1 + ci,
831 )
832 })
833 .collect(),
834 _marker: PhantomData,
835 };
836 self.normalize_if_approaching_limit(layouter, &z)
837 }
838
839 fn sub(
840 &self,
841 layouter: &mut impl Layouter<F>,
842 x: &AssignedField<F, K, P>,
843 y: &AssignedField<F, K, P>,
844 ) -> Result<AssignedField<F, K, P>, Error> {
845 let zero: AssignedField<F, K, P> = self.assign_fixed(layouter, K::ZERO)?;
846
847 if y == &zero {
848 return Ok(x.clone());
849 }
850
851 let mut constants = vec![BI::from(-1)];
858 constants.resize(P::NB_LIMBS as usize, BI::zero());
859
860 let z_limb_values = x
861 .limb_values
862 .iter()
863 .zip(y.limb_values.iter())
864 .zip(constants.iter().map(|ci| bigint_to_fe::<F>(ci)))
865 .map(|((xi, yi), ci)| {
866 self.native_gadget.linear_combination(
867 layouter,
868 &[(F::ONE, xi.clone()), (-F::ONE, yi.clone())],
869 ci,
870 )
871 })
872 .collect::<Result<Vec<_>, _>>()?;
873
874 let z = AssignedField::<F, K, P> {
875 limb_values: z_limb_values,
876 limb_bounds: x
877 .limb_bounds
878 .iter()
879 .zip(y.limb_bounds.iter())
880 .zip(constants.iter())
881 .map(|((xi_bounds, yi_bounds), ci)| {
882 (
883 &xi_bounds.0 - &yi_bounds.1 + ci,
884 &xi_bounds.1 - &yi_bounds.0 + ci,
885 )
886 })
887 .collect(),
888 _marker: PhantomData,
889 };
890 self.normalize_if_approaching_limit(layouter, &z)
891 }
892
893 fn mul(
894 &self,
895 layouter: &mut impl Layouter<F>,
896 x: &AssignedField<F, K, P>,
897 y: &AssignedField<F, K, P>,
898 multiplying_constant: Option<K>,
899 ) -> Result<AssignedField<F, K, P>, Error> {
900 let zero: AssignedField<F, K, P> = self.assign_fixed(layouter, K::ZERO)?;
901 let one: AssignedField<F, K, P> = self.assign_fixed(layouter, K::ONE)?;
902
903 if x == &zero || y == &zero {
904 return Ok(zero);
905 }
906
907 if x == &one {
908 return Ok(y.clone());
909 }
910
911 if y == &one {
912 return Ok(x.clone());
913 }
914
915 let y = match multiplying_constant {
916 None => y.clone(),
917 Some(k) => self.mul_by_constant(layouter, y, k)?,
918 };
919 self.assign_mul(layouter, x, &y, false)
920 }
921
922 fn div(
923 &self,
924 layouter: &mut impl Layouter<F>,
925 x: &AssignedField<F, K, P>,
926 y: &AssignedField<F, K, P>,
927 ) -> Result<AssignedField<F, K, P>, Error> {
928 let one: AssignedField<F, K, P> = self.assign_fixed(layouter, K::ONE)?;
929 if y == &one {
930 return Ok(x.clone());
931 }
932
933 let y = self.normalize(layouter, y)?;
934 self.assert_non_zero(layouter, &y)?;
935 self.assign_mul(layouter, x, &y, true)
936 }
937
938 fn neg(
939 &self,
940 layouter: &mut impl Layouter<F>,
941 x: &AssignedField<F, K, P>,
942 ) -> Result<AssignedField<F, K, P>, Error> {
943 let zero: AssignedField<F, K, P> = self.assign_fixed(layouter, K::ZERO)?;
944
945 if x == &zero {
946 return Ok(zero);
947 }
948
949 let mut constants = vec![BI::from(-2)];
956 constants.resize(P::NB_LIMBS as usize, BI::zero());
957
958 let z_limb_values = x
959 .limb_values
960 .iter()
961 .zip(constants.iter().map(|ci| bigint_to_fe::<F>(ci)))
962 .map(|(xi, ci)| {
963 self.native_gadget.linear_combination(layouter, &[(-F::ONE, xi.clone())], ci)
964 })
965 .collect::<Result<Vec<_>, _>>()?;
966
967 let z = AssignedField::<F, K, P> {
968 limb_values: z_limb_values,
969 limb_bounds: x
970 .limb_bounds
971 .iter()
972 .zip(constants.iter())
973 .map(|(xi_bounds, ci)| (-&xi_bounds.1 + ci, -&xi_bounds.0 + ci))
974 .collect(),
975 _marker: PhantomData,
976 };
977 self.normalize_if_approaching_limit(layouter, &z)
978 }
979
980 fn inv(
981 &self,
982 layouter: &mut impl Layouter<F>,
983 x: &AssignedField<F, K, P>,
984 ) -> Result<AssignedField<F, K, P>, Error> {
985 let one: AssignedField<F, K, P> = self.assign_fixed(layouter, K::ONE)?;
986
987 if x == &one {
988 return Ok(one);
989 }
990
991 self.assign_mul(layouter, &one, x, true)
994 }
995
996 fn inv0(
997 &self,
998 layouter: &mut impl Layouter<F>,
999 x: &AssignedField<F, K, P>,
1000 ) -> Result<AssignedField<F, K, P>, Error> {
1001 let is_zero = self.is_zero(layouter, x)?;
1002 let zero = self.assign_fixed(layouter, K::ZERO)?;
1003 let one = self.assign_fixed(layouter, K::ONE)?;
1004 let invertible = self.select(layouter, &is_zero, &one, x)?;
1005 let inverse = self.assign_mul(layouter, &one, &invertible, true)?;
1006 self.select(layouter, &is_zero, &zero, &inverse)
1007 }
1008
1009 fn add_constant(
1010 &self,
1011 layouter: &mut impl Layouter<F>,
1012 x: &AssignedField<F, K, P>,
1013 k: K,
1014 ) -> Result<AssignedField<F, K, P>, Error> {
1015 if k.is_zero().into() {
1023 return Ok(x.clone());
1024 }
1025
1026 let base = BI::from(2).pow(P::LOG2_BASE);
1027 let k_limbs = bi_to_limbs(P::NB_LIMBS, &base, &k.to_biguint().into());
1028
1029 let z_limb_values = {
1030 self.native_gadget.add_constants(
1031 layouter,
1032 &x.limb_values,
1033 &k_limbs.iter().map(bigint_to_fe::<F>).collect::<Vec<_>>(),
1034 )?
1035 };
1036
1037 let z = AssignedField::<F, K, P> {
1038 limb_values: z_limb_values,
1039 limb_bounds: x
1040 .limb_bounds
1041 .iter()
1042 .zip(k_limbs.iter())
1043 .map(|(xi_bound, ki)| (&xi_bound.0 + ki, &xi_bound.1 + ki))
1044 .collect(),
1045 _marker: PhantomData,
1046 };
1047 self.normalize_if_approaching_limit(layouter, &z)
1048 }
1049
1050 fn mul_by_constant(
1051 &self,
1052 layouter: &mut impl Layouter<F>,
1053 x: &AssignedField<F, K, P>,
1054 k: K,
1055 ) -> Result<AssignedField<F, K, P>, Error> {
1056 if k.is_zero().into() {
1057 return self.assign_fixed(layouter, K::ZERO);
1058 }
1059
1060 if k == K::ONE {
1061 return Ok(x.clone());
1062 }
1063
1064 if k == -K::ONE {
1065 return self.neg(layouter, x);
1066 }
1067
1068 let threshold =
1074 P::max_limb_bound().div_floor(&(BI::from(1000) * BI::from(2).pow(P::LOG2_BASE)));
1075 if BI::from(k.to_biguint()) > threshold {
1076 let assigned_k = self.assign_fixed(layouter, k)?;
1077 return self.assign_mul(layouter, x, &assigned_k, false);
1078 }
1079
1080 let k_as_bigint: BI = k.to_biguint().into();
1092 let kv = bigint_to_fe::<F>(&k_as_bigint);
1093 let mut constants = vec![k_as_bigint.clone() - BI::one()];
1096 constants.resize(P::NB_LIMBS as usize, BI::zero());
1097
1098 let z_limb_values = x
1099 .limb_values
1100 .iter()
1101 .zip(constants.iter().map(|ci| bigint_to_fe::<F>(ci)))
1102 .map(|(xi, ci)| {
1103 self.native_gadget.linear_combination(layouter, &[(kv, xi.clone())], ci)
1104 })
1105 .collect::<Result<Vec<_>, _>>()?;
1106
1107 let limb_bounds = x
1108 .limb_bounds
1109 .iter()
1110 .zip(constants.iter())
1111 .map(|(xi_bounds, ci)| {
1112 (
1113 &xi_bounds.0 * k_as_bigint.clone() + ci,
1114 &xi_bounds.1 * k_as_bigint.clone() + ci,
1115 )
1116 })
1117 .collect();
1118
1119 let z = AssignedField::<F, K, P> {
1120 limb_values: z_limb_values,
1121 limb_bounds,
1122 _marker: PhantomData,
1123 };
1124 self.normalize_if_approaching_limit(layouter, &z)
1125 }
1126}
1127
1128impl<F, K, P, N> FieldInstructions<F, AssignedField<F, K, P>> for FieldChip<F, K, P, N>
1129where
1130 F: CircuitField,
1131 K: CircuitField,
1132 P: FieldEmulationParams<F, K>,
1133 N: NativeInstructions<F>,
1134{
1135 fn order(&self) -> BigUint {
1136 K::modulus()
1137 }
1138}
1139
1140impl<F, K, P, N> ScalarFieldInstructions<F> for FieldChip<F, K, P, N>
1141where
1142 F: CircuitField,
1143 K: CircuitField,
1144 P: FieldEmulationParams<F, K>,
1145 N: NativeInstructions<F>,
1146{
1147 type Scalar = AssignedField<F, K, P>;
1148}
1149
1150impl<F, K, P, N> DecompositionInstructions<F, AssignedField<F, K, P>> for FieldChip<F, K, P, N>
1151where
1152 F: CircuitField,
1153 K: CircuitField,
1154 P: FieldEmulationParams<F, K>,
1155 N: NativeInstructions<F>,
1156{
1157 fn assigned_to_le_bits(
1158 &self,
1159 layouter: &mut impl Layouter<F>,
1160 x: &AssignedField<F, K, P>,
1161 nb_bits: Option<usize>,
1162 enforce_canonical: bool,
1163 ) -> Result<Vec<AssignedBit<F>>, Error> {
1164 let mut x = self.add_constant(layouter, x, K::ONE)?;
1166 if enforce_canonical {
1167 x = self.make_canonical(layouter, &x)?;
1168 };
1169 let mut bits = vec![];
1170 x.limb_values
1171 .iter()
1172 .zip(well_formed_log2_bounds::<F, K, P>().iter())
1173 .map(|(cell, log2_bound)| {
1174 self.native_gadget.assigned_to_le_bits(
1175 layouter,
1176 cell,
1177 Some(*log2_bound as usize),
1178 true,
1179 )
1180 })
1181 .collect::<Result<Vec<_>, _>>()?
1182 .into_iter()
1183 .for_each(|new_bits| bits.extend(new_bits));
1184
1185 let nb_bits = min(
1186 K::NUM_BITS as usize,
1187 nb_bits.unwrap_or(K::NUM_BITS as usize),
1188 );
1189
1190 bits[nb_bits..]
1193 .iter()
1194 .try_for_each(|byte| self.native_gadget.assert_equal_to_fixed(layouter, byte, false))?;
1195 bits = bits[0..nb_bits].to_vec();
1196
1197 if enforce_canonical && nb_bits == K::NUM_BITS as usize {
1200 let canonical = self.is_canonical(layouter, &bits)?;
1201 self.assert_equal_to_fixed(layouter, &canonical, true)?;
1202 }
1203 Ok(bits)
1204 }
1205
1206 fn assigned_to_le_bytes(
1207 &self,
1208 layouter: &mut impl Layouter<F>,
1209 x: &AssignedField<F, K, P>,
1210 nb_bytes: Option<usize>,
1211 ) -> Result<Vec<AssignedByte<F>>, Error> {
1212 let nb_bytes = nb_bytes.unwrap_or(K::NUM_BITS.div_ceil(8) as usize);
1213 let bits = self.assigned_to_le_bits(layouter, x, Some(nb_bytes * 8), true)?;
1215 let bytes = bits
1216 .chunks(8)
1217 .map(|chunk| {
1218 let terms = chunk
1219 .iter()
1220 .enumerate()
1221 .map(|(i, bit)| (F::from(1 << i), bit.clone().into()))
1222 .collect::<Vec<_>>();
1223 let byte = self.native_gadget.linear_combination(layouter, &terms, F::ZERO)?;
1224 self.native_gadget.convert_unsafe(layouter, &byte)
1225 })
1226 .collect::<Result<Vec<AssignedByte<F>>, Error>>()?;
1227
1228 bytes[nb_bytes..]
1231 .iter()
1232 .try_for_each(|byte| self.native_gadget.assert_equal_to_fixed(layouter, byte, 0u8))?;
1233 Ok(bytes[0..nb_bytes].to_vec())
1234 }
1235
1236 fn assigned_from_le_bits(
1237 &self,
1238 layouter: &mut impl Layouter<F>,
1239 bits: &[AssignedBit<F>],
1240 ) -> Result<AssignedField<F, K, P>, Error> {
1241 let mut coeff = K::ONE;
1242 let mut terms = vec![];
1243 for chunk in bits.chunks(P::LOG2_BASE as usize) {
1244 let mut native_coeff = F::ONE;
1245 let mut native_terms = vec![];
1246 for b in chunk.iter() {
1247 let bit: AssignedNative<F> = b.clone().into();
1248 native_terms.push((native_coeff, bit));
1249 native_coeff = native_coeff + native_coeff;
1250 }
1251 let term = {
1252 let limb =
1253 self.native_gadget.linear_combination(layouter, &native_terms, F::ZERO)?;
1254 self.assigned_field_from_limb(layouter, &limb)?
1255 };
1256 terms.push((coeff, term));
1257 coeff = bigint_to_fe::<K>(&BI::from(2).pow(P::LOG2_BASE)) * coeff;
1258 }
1259 let x = self.linear_combination(layouter, &terms, K::ZERO)?;
1260 self.normalize(layouter, &x)
1261 }
1262
1263 fn assigned_from_le_bytes(
1264 &self,
1265 layouter: &mut impl Layouter<F>,
1266 bytes: &[AssignedByte<F>],
1267 ) -> Result<AssignedField<F, K, P>, Error> {
1268 let mut coeff = K::ONE;
1269 let mut terms = vec![];
1270 let nb_bytes_per_chunk = P::LOG2_BASE / 8;
1271 for chunk in bytes.chunks(nb_bytes_per_chunk as usize) {
1272 let mut native_coeff = F::ONE;
1273 let mut native_terms = vec![];
1274 for b in chunk.iter() {
1275 let byte: AssignedNative<F> = b.clone().into();
1276 native_terms.push((native_coeff, byte));
1277 native_coeff = F::from(256) * native_coeff;
1278 }
1279 let term = {
1280 let limb =
1281 self.native_gadget.linear_combination(layouter, &native_terms, F::ZERO)?;
1282 self.assigned_field_from_limb(layouter, &limb)?
1283 };
1284 terms.push((coeff, term));
1285 coeff = bigint_to_fe::<K>(&BI::from(2).pow(8 * nb_bytes_per_chunk)) * coeff;
1286 }
1287 let x = self.linear_combination(layouter, &terms, K::ZERO)?;
1288 self.normalize(layouter, &x)
1289 }
1290
1291 fn assigned_to_le_chunks(
1292 &self,
1293 layouter: &mut impl Layouter<F>,
1294 x: &AssignedField<F, K, P>,
1295 nb_bits_per_chunk: usize,
1296 nb_chunks: Option<usize>,
1297 ) -> Result<Vec<AssignedNative<F>>, Error> {
1298 assert!(nb_bits_per_chunk < F::NUM_BITS as usize);
1299 if P::LOG2_BASE % (nb_bits_per_chunk as u32) == 0 {
1300 let nb_chunks_per_limb = (P::LOG2_BASE / (nb_bits_per_chunk as u32)) as usize;
1301 let mut nb_missing_chunks =
1302 nb_chunks.unwrap_or(nb_chunks_per_limb * P::NB_LIMBS as usize);
1303 let x = self.add_constant(layouter, x, K::ONE)?;
1305 let x = self.normalize(layouter, &x)?;
1306 let chunks = x
1307 .limb_values
1308 .iter()
1309 .map(|limb| {
1310 let nb_chunks_on_this_limb = min(nb_missing_chunks, nb_chunks_per_limb);
1311 nb_missing_chunks -= nb_chunks_on_this_limb;
1312 self.native_gadget.assigned_to_le_chunks(
1313 layouter,
1314 limb,
1315 nb_bits_per_chunk,
1316 Some(nb_chunks_on_this_limb),
1317 )
1318 })
1319 .collect::<Result<Vec<_>, Error>>()?
1320 .concat();
1321 assert_eq!(nb_missing_chunks, 0);
1322 Ok(chunks)
1323 }
1324 else {
1327 let bits = self.assigned_to_le_bits(layouter, x, None, false)?;
1328 bits.chunks(nb_bits_per_chunk)
1329 .map(|bits_of_chunk| {
1330 self.native_gadget.assigned_from_le_bits(layouter, bits_of_chunk)
1331 })
1332 .collect::<Result<Vec<_>, Error>>()
1333 }
1334 }
1335}
1336
1337impl<F, K, P, N> FieldChip<F, K, P, N>
1338where
1339 F: CircuitField,
1340 K: CircuitField,
1341 P: FieldEmulationParams<F, K>,
1342 N: NativeInstructions<F>,
1343{
1344 pub fn new(config: &FieldChipConfig, native_gadget: &N) -> Self {
1346 Self {
1347 config: config.clone(),
1348 native_gadget: native_gadget.clone(),
1349 _marker: PhantomData,
1350 }
1351 }
1352
1353 pub fn configure(
1357 meta: &mut ConstraintSystem<F>,
1358 advice_columns: &[Column<Advice>],
1359 nb_parallel_range_checks: usize,
1360 max_bit_len: u32,
1361 ) -> FieldChipConfig {
1362 check_params::<F, K, P>();
1363
1364 let nb_limbs = P::NB_LIMBS;
1365 let x_cols = advice_columns[..(nb_limbs as usize)].to_vec();
1366 let y_cols = x_cols.clone();
1367 let z_cols = advice_columns[(nb_limbs as usize)..(2 * nb_limbs as usize)].to_vec();
1368
1369 x_cols.iter().chain(z_cols.iter()).for_each(|&col| meta.enable_equality(col));
1370
1371 let u_col = advice_columns[nb_limbs as usize];
1372 let v_cols = advice_columns
1373 [(nb_limbs as usize + 1)..(nb_limbs as usize + 1 + P::moduli().len())]
1374 .to_vec();
1375
1376 let mul_config = MulConfig::configure::<F, K, P>(
1377 meta,
1378 &x_cols,
1379 &z_cols,
1380 nb_parallel_range_checks,
1381 max_bit_len,
1382 );
1383 let norm_config = NormConfig::configure::<F, K, P>(
1384 meta,
1385 &x_cols,
1386 &z_cols,
1387 nb_parallel_range_checks,
1388 max_bit_len,
1389 );
1390
1391 FieldChipConfig {
1392 mul_config,
1393 norm_config,
1394 x_cols,
1395 y_cols,
1396 z_cols,
1397 u_col,
1398 v_cols,
1399 }
1400 }
1401
1402 fn assign_mul(
1409 &self,
1410 layouter: &mut impl Layouter<F>,
1411 x: &AssignedField<F, K, P>,
1412 y: &AssignedField<F, K, P>,
1413 division: bool,
1414 ) -> Result<AssignedField<F, K, P>, Error> {
1415 let base = BI::from(2).pow(P::LOG2_BASE);
1416 let nb_limbs = P::NB_LIMBS;
1417
1418 let x = self.normalize(layouter, x)?;
1419 let y = self.normalize(layouter, y)?;
1420
1421 y.value().error_if_known_and(|yv| division && K::is_zero(yv).into())?;
1422
1423 let zv = x
1424 .value()
1425 .zip(y.value())
1426 .map(|(xv, yv)| {
1427 if division {
1428 xv * yv.invert().unwrap()
1429 } else {
1430 xv * yv
1431 }
1432 })
1433 .map(|z| bi_to_limbs(nb_limbs, &base, &(z - K::ONE).to_biguint().into()));
1434 let z_values = (0..nb_limbs)
1435 .map(|i| zv.clone().map(|zs| bigint_to_fe::<F>(&zs[i as usize])))
1436 .collect::<Vec<_>>();
1437
1438 let z_limbs = z_values
1440 .iter()
1441 .zip(well_formed_log2_bounds::<F, K, P>().iter())
1442 .map(|(&z_value, &log2_bound)| {
1443 self.native_gadget.assign_lower_than_fixed(
1444 layouter,
1445 z_value,
1446 &(BigUint::one() << log2_bound),
1447 )
1448 })
1449 .collect::<Result<Vec<_>, Error>>()?;
1450
1451 let z = AssignedField::<F, K, P> {
1452 limb_values: z_limbs,
1453 limb_bounds: well_formed_bounds::<F, K, P>(),
1454 _marker: PhantomData,
1455 };
1456
1457 let (l, r) = if !division { (&x, &z) } else { (&z, &x) };
1460 mul::assert_mul::<F, K, P, N>(
1461 layouter,
1462 l,
1463 &y,
1464 r,
1465 &self.config.mul_config,
1466 &self.native_gadget,
1467 )?;
1468
1469 Ok(z)
1470 }
1471}
1472
1473impl<F, K, P, N> FieldChip<F, K, P, N>
1474where
1475 F: CircuitField,
1476 K: CircuitField,
1477 P: FieldEmulationParams<F, K>,
1478 N: NativeInstructions<F>,
1479{
1480 pub fn sum(
1485 &self,
1486 layouter: &mut impl Layouter<F>,
1487 positives: &[AssignedField<F, K, P>],
1488 negatives: &[AssignedField<F, K, P>],
1489 constant: K,
1490 ) -> Result<AssignedField<F, K, P>, Error> {
1491 if positives.is_empty() && negatives.is_empty() {
1492 return self.assign_fixed(layouter, constant);
1493 }
1494
1495 let base = BI::from(2).pow(P::LOG2_BASE);
1496 let p = positives.len();
1497 let n = negatives.len();
1498
1499 let constant_repr: BI = (constant - K::ONE).to_biguint().into();
1511 let constant_limbs = bi_to_limbs(P::NB_LIMBS, &base, &constant_repr);
1512 let mut offsets = constant_limbs;
1513 offsets[0] += BI::from(p) - BI::from(n);
1514
1515 let z_limb_values = (0..P::NB_LIMBS as usize)
1516 .map(|i| {
1517 let pos_terms: Vec<(F, AssignedNative<F>)> =
1518 positives.iter().map(|x| (F::ONE, x.limb_values[i].clone())).collect();
1519 let neg_terms: Vec<(F, AssignedNative<F>)> =
1520 negatives.iter().map(|x| (-F::ONE, x.limb_values[i].clone())).collect();
1521 let native_terms: Vec<_> = pos_terms.into_iter().chain(neg_terms).collect();
1522 self.native_gadget.linear_combination(
1523 layouter,
1524 &native_terms,
1525 bigint_to_fe::<F>(&offsets[i]),
1526 )
1527 })
1528 .collect::<Result<Vec<_>, _>>()?;
1529
1530 let all_terms = positives
1533 .iter()
1534 .zip(std::iter::repeat(false))
1535 .chain(negatives.iter().zip(std::iter::repeat(true)));
1536 let init_bounds: Vec<(BI, BI)> = (0..P::NB_LIMBS as usize)
1537 .map(|i| (offsets[i].clone(), offsets[i].clone()))
1538 .collect();
1539 let z_bounds = all_terms.fold(init_bounds, |acc, (x, negated)| {
1540 acc.into_iter()
1541 .zip(x.limb_bounds.iter())
1542 .map(|((lo, hi), b)| {
1543 if negated {
1544 (lo - &b.1, hi - &b.0)
1545 } else {
1546 (lo + &b.0, hi + &b.1)
1547 }
1548 })
1549 .collect()
1550 });
1551
1552 let z = AssignedField::<F, K, P> {
1553 limb_values: z_limb_values,
1554 limb_bounds: z_bounds,
1555 _marker: PhantomData,
1556 };
1557 self.normalize_if_approaching_limit(layouter, &z)
1558 }
1559
1560 pub(crate) fn normalize(
1563 &self,
1564 layouter: &mut impl Layouter<F>,
1565 x: &AssignedField<F, K, P>,
1566 ) -> Result<AssignedField<F, K, P>, Error> {
1567 if x.is_well_formed() {
1568 Ok(x.clone())
1569 } else {
1570 self.make_canonical(layouter, x)
1571 }
1572 }
1573
1574 pub(crate) fn normalize_if_approaching_limit(
1577 &self,
1578 layouter: &mut impl Layouter<F>,
1579 x: &AssignedField<F, K, P>,
1580 ) -> Result<AssignedField<F, K, P>, Error> {
1581 let threshold: BI = P::max_limb_bound() / 10;
1583 let dangerous_lower_bounds = x.limb_bounds.iter().any(|b| b.0 < -threshold.clone());
1584 let dangerous_upper_bounds = x.limb_bounds.iter().any(|b| b.1 > threshold);
1585 if dangerous_lower_bounds || dangerous_upper_bounds {
1586 self.make_canonical(layouter, x)
1587 } else {
1588 Ok(x.clone())
1589 }
1590 }
1591
1592 fn make_canonical(
1595 &self,
1596 layouter: &mut impl Layouter<F>,
1597 x: &AssignedField<F, K, P>,
1598 ) -> Result<AssignedField<F, K, P>, Error> {
1599 let max_limb_bound = P::max_limb_bound();
1600 x.limb_bounds.iter().for_each(|(lower, upper)| {
1601 if lower < &(-&max_limb_bound) || upper > &max_limb_bound {
1602 panic!(
1603 "make_canonical: the limb bounds of the input: [{}, {}] exceed the
1604 maximum limb bound value {}; consider applying a normalization
1605 earlier, when the bounds are still within the permited range;
1606 increasing the [max_limb_bound] of your FieldEmulationParams could also
1607 help, if possible.",
1608 lower, upper, max_limb_bound
1609 );
1610 }
1611 });
1612 let z_limbs = norm::normalize::<F, K, P, N>(
1613 layouter,
1614 x,
1615 &self.config.norm_config,
1616 &self.native_gadget,
1617 )?;
1618 let z = AssignedField::<F, K, P> {
1619 limb_values: z_limbs,
1620 limb_bounds: well_formed_bounds::<F, K, P>(),
1621 _marker: PhantomData,
1622 };
1623 Ok(z)
1624 }
1625
1626 fn assigned_field_from_limb(
1629 &self,
1630 layouter: &mut impl Layouter<F>,
1631 limb: &AssignedNative<F>,
1632 ) -> Result<AssignedField<F, K, P>, Error> {
1633 let least_significant_limb = self.native_gadget.add_constant(layouter, limb, -F::ONE)?;
1635 let mut limb_values = vec![least_significant_limb];
1636 let mut limb_bounds = well_formed_bounds::<F, K, P>();
1637 let zero = self.native_gadget.assign_fixed(layouter, F::ZERO)?;
1638 limb_values.resize(P::NB_LIMBS as usize, zero);
1639 limb_bounds[0] = (limb_bounds[0].clone().0 - 1, limb_bounds[0].clone().1 - 1);
1640 Ok(AssignedField::<F, K, P> {
1641 limb_values,
1642 limb_bounds,
1643 _marker: PhantomData,
1644 })
1645 }
1646}
1647
1648impl<F, K, P, N> AssignmentInstructions<F, AssignedBit<F>> for FieldChip<F, K, P, N>
1650where
1651 F: CircuitField,
1652 K: CircuitField,
1653 P: FieldEmulationParams<F, K>,
1654 N: NativeInstructions<F>,
1655{
1656 fn assign(
1657 &self,
1658 layouter: &mut impl Layouter<F>,
1659 value: Value<bool>,
1660 ) -> Result<AssignedBit<F>, Error> {
1661 self.native_gadget.assign(layouter, value)
1662 }
1663
1664 fn assign_fixed(
1665 &self,
1666 layouter: &mut impl Layouter<F>,
1667 constant: bool,
1668 ) -> Result<AssignedBit<F>, Error> {
1669 self.native_gadget.assign_fixed(layouter, constant)
1670 }
1671}
1672
1673impl<F, K, P, N> AssertionInstructions<F, AssignedBit<F>> for FieldChip<F, K, P, N>
1675where
1676 F: CircuitField,
1677 K: CircuitField,
1678 P: FieldEmulationParams<F, K>,
1679 N: NativeInstructions<F>,
1680{
1681 fn assert_equal(
1682 &self,
1683 layouter: &mut impl Layouter<F>,
1684 x: &AssignedBit<F>,
1685 y: &AssignedBit<F>,
1686 ) -> Result<(), Error> {
1687 self.native_gadget.assert_equal(layouter, x, y)
1688 }
1689
1690 fn assert_not_equal(
1691 &self,
1692 layouter: &mut impl Layouter<F>,
1693 x: &AssignedBit<F>,
1694 y: &AssignedBit<F>,
1695 ) -> Result<(), Error> {
1696 self.native_gadget.assert_not_equal(layouter, x, y)
1697 }
1698
1699 fn assert_equal_to_fixed(
1700 &self,
1701 layouter: &mut impl Layouter<F>,
1702 x: &AssignedBit<F>,
1703 constant: bool,
1704 ) -> Result<(), Error> {
1705 self.native_gadget.assert_equal_to_fixed(layouter, x, constant)
1706 }
1707
1708 fn assert_not_equal_to_fixed(
1709 &self,
1710 layouter: &mut impl Layouter<F>,
1711 x: &AssignedBit<F>,
1712 constant: bool,
1713 ) -> Result<(), Error> {
1714 self.native_gadget.assert_not_equal_to_fixed(layouter, x, constant)
1715 }
1716}
1717
1718impl<F, K, P, N> ConversionInstructions<F, AssignedBit<F>, AssignedField<F, K, P>>
1719 for FieldChip<F, K, P, N>
1720where
1721 F: CircuitField,
1722 K: CircuitField,
1723 P: FieldEmulationParams<F, K>,
1724 N: NativeInstructions<F>,
1725{
1726 fn convert_value(&self, x: &bool) -> Option<K> {
1727 Some(if *x { K::ONE } else { K::ZERO })
1728 }
1729
1730 fn convert(
1731 &self,
1732 layouter: &mut impl Layouter<F>,
1733 x: &AssignedBit<F>,
1734 ) -> Result<AssignedField<F, K, P>, Error> {
1735 let x: AssignedNative<F> = x.clone().into();
1736 self.assigned_field_from_limb(layouter, &x)
1737 }
1738}
1739
1740impl<F, K, P, N> ConversionInstructions<F, AssignedByte<F>, AssignedField<F, K, P>>
1741 for FieldChip<F, K, P, N>
1742where
1743 F: CircuitField,
1744 K: CircuitField,
1745 P: FieldEmulationParams<F, K>,
1746 N: NativeInstructions<F>,
1747{
1748 fn convert_value(&self, x: &u8) -> Option<K> {
1749 Some(K::from(*x as u64))
1750 }
1751
1752 fn convert(
1753 &self,
1754 layouter: &mut impl Layouter<F>,
1755 x: &AssignedByte<F>,
1756 ) -> Result<AssignedField<F, K, P>, Error> {
1757 let x: AssignedNative<F> = x.clone().into();
1758 self.assigned_field_from_limb(layouter, &x)
1759 }
1760}
1761
1762impl<F, K, P, N> CanonicityInstructions<F, AssignedField<F, K, P>> for FieldChip<F, K, P, N>
1764where
1765 F: CircuitField,
1766 K: CircuitField,
1767 P: FieldEmulationParams<F, K>,
1768 N: NativeInstructions<F>,
1769{
1770 fn le_bits_lower_than(
1771 &self,
1772 layouter: &mut impl Layouter<F>,
1773 bits: &[AssignedBit<F>],
1774 bound: BigUint,
1775 ) -> Result<AssignedBit<F>, Error> {
1776 self.native_gadget.le_bits_lower_than(layouter, bits, bound)
1777 }
1778
1779 fn le_bits_geq_than(
1780 &self,
1781 layouter: &mut impl Layouter<F>,
1782 bits: &[AssignedBit<F>],
1783 bound: BigUint,
1784 ) -> Result<AssignedBit<F>, Error> {
1785 self.native_gadget.le_bits_geq_than(layouter, bits, bound)
1786 }
1787}
1788
1789#[derive(Clone, Debug)]
1790#[cfg(any(test, feature = "testing"))]
1791pub struct FieldChipConfigForTests<F, N>
1794where
1795 F: CircuitField,
1796 N: NativeInstructions<F> + FromScratch<F>,
1797{
1798 native_gadget_config: <N as FromScratch<F>>::Config,
1799 field_chip_config: FieldChipConfig,
1800}
1801
1802#[cfg(any(test, feature = "testing"))]
1803impl<F, K, P, N> FromScratch<F> for FieldChip<F, K, P, N>
1804where
1805 F: CircuitField,
1806 K: CircuitField,
1807 P: FieldEmulationParams<F, K>,
1808 N: NativeInstructions<F> + FromScratch<F>,
1809{
1810 type Config = FieldChipConfigForTests<F, N>;
1811
1812 fn new_from_scratch(config: &FieldChipConfigForTests<F, N>) -> Self {
1813 let native_gadget = <N as FromScratch<F>>::new_from_scratch(&config.native_gadget_config);
1814 FieldChip::new(&config.field_chip_config, &native_gadget)
1815 }
1816
1817 fn load_from_scratch(&self, layouter: &mut impl Layouter<F>) -> Result<(), Error> {
1818 self.native_gadget.load_from_scratch(layouter)
1819 }
1820
1821 fn configure_from_scratch(
1822 meta: &mut ConstraintSystem<F>,
1823 advice_columns: &mut Vec<Column<Advice>>,
1824 fixed_columns: &mut Vec<Column<Fixed>>,
1825 instance_columns: &[Column<Instance>; 2],
1826 ) -> FieldChipConfigForTests<F, N> {
1827 let native_gadget_config = <N as FromScratch<F>>::configure_from_scratch(
1828 meta,
1829 advice_columns,
1830 fixed_columns,
1831 instance_columns,
1832 );
1833 let nb_parallel_range_checks = 4;
1835 let max_bit_len = 8;
1836 let field_chip_config = {
1837 let nb_fc_cols = nb_field_chip_columns::<F, K, P>();
1838 while advice_columns.len() < nb_fc_cols {
1839 advice_columns.push(meta.advice_column());
1840 }
1841 FieldChip::<F, K, P, N>::configure(
1842 meta,
1843 &advice_columns[..nb_fc_cols],
1844 nb_parallel_range_checks,
1845 max_bit_len,
1846 )
1847 };
1848 FieldChipConfigForTests {
1849 native_gadget_config,
1850 field_chip_config,
1851 }
1852 }
1853}
1854
1855#[cfg(test)]
1856mod tests {
1857 use midnight_curves::{
1858 k256::{Fp as K256Base, Fq as K256Scalar},
1859 p256::{Fp as P256Base, Fq as P256Scalar},
1860 Fq as BlsScalar,
1861 };
1862
1863 use super::*;
1864 use crate::{
1865 field::{
1866 decomposition::chip::P2RDecompositionChip, foreign::params::MultiEmulationParams,
1867 NativeChip, NativeGadget,
1868 },
1869 instructions::{
1870 arithmetic, assertions, control_flow, decomposition, equality, public_input, zero,
1871 },
1872 };
1873
1874 macro_rules! test_generic {
1875 ($mod:ident, $op:ident, $native:ident, $emulated:ident, $name:expr) => {
1876 $mod::tests::$op::<
1877 $native,
1878 AssignedField<$native, $emulated, MultiEmulationParams>,
1879 FieldChip<
1880 $native,
1881 $emulated,
1882 MultiEmulationParams,
1883 NativeGadget<$native, P2RDecompositionChip<$native>, NativeChip<$native>>,
1884 >,
1885 >($name);
1886 };
1887 }
1888
1889 macro_rules! test {
1890 ($mod:ident, $op:ident) => {
1891 #[test]
1892 fn $op() {
1893 test_generic!($mod, $op, BlsScalar, K256Base, "field_chip_k256_base");
1894 test_generic!($mod, $op, BlsScalar, K256Scalar, "field_chip_k256_scalar");
1895 test_generic!($mod, $op, BlsScalar, P256Base, "field_chip_p256_base");
1896 test_generic!($mod, $op, BlsScalar, P256Scalar, "field_chip_p256_scalar");
1897 }
1898 };
1899 }
1900
1901 test!(assertions, test_assertions);
1902
1903 test!(public_input, test_public_inputs);
1904
1905 test!(equality, test_is_equal);
1906
1907 test!(zero, test_zero_assertions);
1908 test!(zero, test_is_zero);
1909
1910 test!(control_flow, test_select);
1911 test!(control_flow, test_cond_assert_equal);
1912 test!(control_flow, test_cond_swap);
1913
1914 test!(arithmetic, test_add);
1915 test!(arithmetic, test_sub);
1916 test!(arithmetic, test_mul);
1917 test!(arithmetic, test_div);
1918 test!(arithmetic, test_neg);
1919 test!(arithmetic, test_inv);
1920 test!(arithmetic, test_pow);
1921 test!(arithmetic, test_linear_combination);
1922 test!(arithmetic, test_add_and_mul);
1923
1924 macro_rules! test_generic {
1925 ($mod:ident, $op:ident, $native:ident, $emulated:ident, $name:expr) => {
1926 $mod::tests::$op::<
1927 $native,
1928 AssignedField<$native, $emulated, MultiEmulationParams>,
1929 FieldChip<
1930 $native,
1931 $emulated,
1932 MultiEmulationParams,
1933 NativeGadget<$native, P2RDecompositionChip<$native>, NativeChip<$native>>,
1934 >,
1935 NativeGadget<$native, P2RDecompositionChip<$native>, NativeChip<$native>>,
1936 >($name);
1937 };
1938 }
1939
1940 macro_rules! test {
1941 ($mod:ident, $op:ident) => {
1942 #[test]
1943 fn $op() {
1944 test_generic!($mod, $op, BlsScalar, K256Base, "field_chip_k256_base");
1945 test_generic!($mod, $op, BlsScalar, K256Scalar, "field_chip_k256_scalar");
1946 test_generic!($mod, $op, BlsScalar, P256Base, "field_chip_p256_base");
1947 test_generic!($mod, $op, BlsScalar, P256Scalar, "field_chip_p256_scalar");
1948 }
1949 };
1950 }
1951
1952 test!(decomposition, test_bit_decomposition);
1953 test!(decomposition, test_byte_decomposition);
1954 test!(decomposition, test_sgn0);
1955}