1use std::{
19 cmp::{max, min},
20 fmt::Debug,
21 hash::{Hash, Hasher},
22 marker::PhantomData,
23};
24
25use ff::PrimeField;
26use midnight_proofs::{
27 circuit::{Chip, Layouter, Value},
28 plonk::{Advice, Column, ConstraintSystem, Error},
29};
30use num_bigint::{BigInt as BI, BigUint, ToBigInt};
31use num_integer::Integer;
32use num_traits::{One, Signed, Zero};
33#[cfg(any(test, feature = "testing"))]
34use {
35 crate::testing_utils::{FromScratch, Sampleable},
36 midnight_proofs::plonk::Instance,
37 rand::RngCore,
38};
39
40use super::gates::{
41 mul::{self, MulConfig},
42 norm::{self, NormConfig},
43};
44use crate::{
45 field::foreign::{
46 params::{check_params, FieldEmulationParams},
47 util::{bi_from_limbs, bi_to_limbs},
48 },
49 instructions::{
50 ArithInstructions, AssertionInstructions, AssignmentInstructions, CanonicityInstructions,
51 ControlFlowInstructions, ConversionInstructions, DecompositionInstructions,
52 EqualityInstructions, FieldInstructions, NativeInstructions, PublicInputInstructions,
53 ScalarFieldInstructions, ZeroInstructions,
54 },
55 types::{AssignedBit, AssignedByte, AssignedNative, InnerConstants, InnerValue, Instantiable},
56 utils::util::{bigint_to_fe, fe_to_bigint, modulus},
57};
58
59#[derive(Clone, Debug)]
82#[must_use]
83pub struct AssignedField<F, K, P>
84where
85 F: PrimeField,
86 K: PrimeField,
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: PrimeField,
97 K: PrimeField,
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: PrimeField, K: PrimeField, P: FieldEmulationParams<F, K>> Eq for AssignedField<F, K, P> {}
109
110impl<F, K, P> Hash for AssignedField<F, K, P>
111where
112 F: PrimeField,
113 K: PrimeField,
114 P: FieldEmulationParams<F, K>,
115{
116 fn hash<H: Hasher>(&self, state: &mut H) {
117 self.limb_values.iter().for_each(|elem| elem.hash(state));
118 }
119}
120
121impl<F, K, P> Instantiable<F> for AssignedField<F, K, P>
122where
123 F: PrimeField,
124 K: PrimeField,
125 P: FieldEmulationParams<F, K>,
126{
127 fn as_public_input(element: &K) -> Vec<F> {
128 let element_as_bi = fe_to_bigint(&(*element - K::ONE));
130 let base = BI::from(2).pow(P::LOG2_BASE);
131 bi_to_limbs(P::NB_LIMBS, &base, &element_as_bi)
132 .iter()
133 .map(|x| bigint_to_fe::<F>(x))
134 .collect()
135 }
136
137 fn from_public_input(fields: &[F]) -> Option<K> {
138 let base = BI::from(2).pow(P::LOG2_BASE);
139
140 if fields.len() != P::NB_LIMBS as usize {
141 return None;
142 }
143
144 let limbs_as_bi = fields.iter().map(|f| fe_to_bigint(f)).collect::<Vec<_>>();
145 let element_as_bi = bi_from_limbs(&base, &limbs_as_bi) + BI::one();
146 Some(bigint_to_fe::<K>(&element_as_bi))
147 }
148}
149
150impl<F, K, P> InnerValue for AssignedField<F, K, P>
151where
152 F: PrimeField,
153 K: PrimeField,
154 P: FieldEmulationParams<F, K>,
155{
156 type Element = K;
157
158 fn value(&self) -> Value<K> {
159 let bi_limbs = self
160 .limb_values
161 .iter()
162 .zip(self.limb_bounds.iter())
163 .map(|(xi, (lower_bound, _))| {
164 let shift = BI::abs(lower_bound);
167 let fe_shift = bigint_to_fe::<F>(&shift);
168 xi.value().map(|xv| fe_to_bigint::<F>(&(*xv + fe_shift)) - &shift)
169 })
170 .collect::<Vec<_>>();
171 let bi_limbs: Value<Vec<BI>> = Value::from_iter(bi_limbs);
172 let base = BI::from(2).pow(P::LOG2_BASE);
173 bi_limbs.map(|limbs| bigint_to_fe::<K>(&(BI::one() + bi_from_limbs(&base, &limbs))))
174 }
175}
176
177impl<F: PrimeField, K: PrimeField, P> InnerConstants for AssignedField<F, K, P>
178where
179 F: PrimeField,
180 K: PrimeField,
181 P: FieldEmulationParams<F, K>,
182{
183 fn inner_zero() -> K {
184 K::ZERO
185 }
186
187 fn inner_one() -> K {
188 K::ONE
189 }
190}
191
192impl<F, K, P> AssignedField<F, K, P>
193where
194 F: PrimeField,
195 K: PrimeField,
196 P: FieldEmulationParams<F, K>,
197{
198 pub(crate) fn from_limbs_unsafe(limb_values: Vec<AssignedNative<F>>) -> Self {
204 debug_assert!(limb_values.len() as u32 == P::NB_LIMBS);
205 Self {
206 limb_values,
207 limb_bounds: well_formed_bounds::<F, K, P>(),
208 _marker: PhantomData,
209 }
210 }
211}
212
213#[cfg(any(test, feature = "testing"))]
214impl<F, K, P> Sampleable for AssignedField<F, K, P>
215where
216 F: PrimeField,
217 K: PrimeField,
218 P: FieldEmulationParams<F, K>,
219{
220 fn sample_inner(rng: impl RngCore) -> K {
221 K::random(rng)
222 }
223}
224
225pub fn nb_field_chip_columns<F, K, P>() -> usize
227where
228 F: PrimeField,
229 K: PrimeField,
230 P: FieldEmulationParams<F, K>,
231{
232 P::NB_LIMBS as usize + max(P::NB_LIMBS as usize, 1 + P::moduli().len())
233}
234
235pub fn well_formed_log2_bounds<F, K, P>() -> Vec<u32>
242where
243 F: PrimeField,
244 K: PrimeField,
245 P: FieldEmulationParams<F, K>,
246{
247 let m = &modulus::<K>().to_bigint().unwrap();
252 let log2_msl_bound = m.bits() as u32 - (P::NB_LIMBS - 1) * P::LOG2_BASE;
253 let mut bounds = vec![log2_msl_bound];
254 bounds.resize(P::NB_LIMBS as usize, P::LOG2_BASE);
255 bounds.into_iter().rev().collect::<Vec<_>>()
256}
257
258#[derive(Clone, Debug)]
266pub struct FieldChipConfig {
267 mul_config: mul::MulConfig,
268 norm_config: norm::NormConfig,
269 pub x_cols: Vec<Column<Advice>>,
271 pub y_cols: Vec<Column<Advice>>,
273 pub z_cols: Vec<Column<Advice>>,
275 pub u_col: Column<Advice>,
277 pub v_cols: Vec<Column<Advice>>,
279}
280
281#[derive(Clone, Debug)]
283pub struct FieldChip<F, K, P, N>
284where
285 F: PrimeField,
286 K: PrimeField,
287 P: FieldEmulationParams<F, K>,
288 N: NativeInstructions<F>,
289{
290 config: FieldChipConfig,
291 pub(crate) native_gadget: N,
292 _marker: PhantomData<(F, K, P, N)>,
293}
294
295impl<F, K, P> AssignedField<F, K, P>
296where
297 F: PrimeField,
298 K: PrimeField,
299 P: FieldEmulationParams<F, K>,
300{
301 pub fn modulus(&self) -> BI {
303 modulus::<K>().to_bigint().unwrap().clone()
304 }
305
306 pub fn is_well_formed(&self) -> bool {
312 self.limb_bounds.iter().zip(well_formed_log2_bounds::<F, K, P>()).all(
313 |((lower, upper), expected_upper)| {
314 assert!(lower <= upper);
315 !BI::is_negative(lower) && upper.bits() <= expected_upper as u64
316 },
317 )
318 }
319
320 pub fn limb_values(&self) -> Vec<AssignedNative<F>> {
324 self.limb_values.clone()
325 }
326
327 pub fn bigint_limbs(&self) -> Value<Vec<BI>> {
329 let limbs = self
330 .limb_values
331 .iter()
332 .zip(self.limb_bounds.iter())
333 .map(|(xi, (lbound, _))| {
334 let shift = BI::abs(lbound);
337 let fe_shift = bigint_to_fe::<F>(&shift);
338 xi.value().map(|xv| fe_to_bigint::<F>(&(*xv + fe_shift)) - &shift)
339 })
340 .collect::<Vec<_>>();
341 Value::from_iter(limbs)
342 }
343}
344
345fn well_formed_bounds<F, K, P>() -> Vec<(BI, BI)>
350where
351 F: PrimeField,
352 K: PrimeField,
353 P: FieldEmulationParams<F, K>,
354{
355 well_formed_log2_bounds::<F, K, P>()
356 .into_iter()
357 .map(|log2_base| (BI::zero(), BI::from(2).pow(log2_base) - BI::one()))
358 .collect()
359}
360
361fn limbs_of_zero<F, K, P>() -> Vec<BI>
363where
364 F: PrimeField,
365 K: PrimeField,
366 P: FieldEmulationParams<F, K>,
367{
368 bi_to_limbs(
369 P::NB_LIMBS,
370 &BI::from(2).pow(P::LOG2_BASE),
371 &(modulus::<K>().to_bigint().unwrap() - BI::one()),
372 )
373}
374
375impl<F, K, P, N> Chip<F> for FieldChip<F, K, P, N>
376where
377 F: PrimeField,
378 K: PrimeField,
379 P: FieldEmulationParams<F, K>,
380 N: NativeInstructions<F>,
381{
382 type Config = FieldChipConfig;
383 type Loaded = ();
384 fn config(&self) -> &Self::Config {
385 &self.config
386 }
387 fn loaded(&self) -> &Self::Loaded {
388 &()
389 }
390}
391
392impl<F, K, P, N> AssignmentInstructions<F, AssignedField<F, K, P>> for FieldChip<F, K, P, N>
393where
394 F: PrimeField,
395 K: PrimeField,
396 P: FieldEmulationParams<F, K>,
397 N: NativeInstructions<F>,
398{
399 fn assign(
400 &self,
401 layouter: &mut impl Layouter<F>,
402 x: Value<K>,
403 ) -> Result<AssignedField<F, K, P>, Error> {
404 let base = BI::from(2).pow(P::LOG2_BASE);
405 let x = x.map(|v| {
408 let bi = fe_to_bigint(&(v - K::ONE));
409 bi_to_limbs(P::NB_LIMBS, &base, &bi)
410 });
411
412 let x_cells = (0..P::NB_LIMBS)
414 .map(|i| x.clone().map(|limbs| bigint_to_fe::<F>(&limbs[i as usize])))
415 .zip(well_formed_log2_bounds::<F, K, P>().iter())
416 .map(|(xi_value, log2_bound)| {
417 self.native_gadget.assign_lower_than_fixed(
418 layouter,
419 xi_value,
420 &(BigUint::one() << *log2_bound),
421 )
422 })
423 .collect::<Result<Vec<_>, Error>>()?;
424
425 Ok(AssignedField::<F, K, P> {
426 limb_values: x_cells,
427 limb_bounds: well_formed_bounds::<F, K, P>(),
428 _marker: PhantomData,
429 })
430 }
431
432 fn assign_fixed(
433 &self,
434 layouter: &mut impl Layouter<F>,
435 constant: K,
436 ) -> Result<AssignedField<F, K, P>, Error> {
437 let base = BI::from(2).pow(P::LOG2_BASE);
438 let constant = fe_to_bigint(&(constant - K::ONE));
441 let constant_limbs = bi_to_limbs(P::NB_LIMBS, &base, &constant);
442 let constant_cells = constant_limbs
443 .iter()
444 .map(|x| self.native_gadget.assign_fixed(layouter, bigint_to_fe::<F>(x)))
445 .collect::<Result<Vec<_>, _>>()?;
446
447 Ok(AssignedField::<F, K, P> {
457 limb_values: constant_cells,
458 limb_bounds: well_formed_bounds::<F, K, P>(),
459 _marker: PhantomData,
460 })
461 }
462}
463
464impl<F, K, P> From<AssignedField<F, K, P>> for Vec<AssignedNative<F>>
467where
468 F: PrimeField,
469 K: PrimeField,
470 P: FieldEmulationParams<F, K>,
471{
472 fn from(x: AssignedField<F, K, P>) -> Self {
473 x.limb_values()
474 }
475}
476
477impl<F, K, P, N> PublicInputInstructions<F, AssignedField<F, K, P>> for FieldChip<F, K, P, N>
478where
479 F: PrimeField,
480 K: PrimeField,
481 P: FieldEmulationParams<F, K>,
482 N: NativeInstructions<F>,
483{
484 fn as_public_input(
485 &self,
486 layouter: &mut impl Layouter<F>,
487 assigned: &AssignedField<F, K, P>,
488 ) -> Result<Vec<AssignedNative<F>>, Error> {
489 let assigned = self.normalize(layouter, assigned)?;
490 Ok(assigned.limb_values)
491 }
492
493 fn constrain_as_public_input(
494 &self,
495 layouter: &mut impl Layouter<F>,
496 assigned: &AssignedField<F, K, P>,
497 ) -> Result<(), Error> {
498 self.as_public_input(layouter, assigned)?
499 .iter()
500 .try_for_each(|c| self.native_gadget.constrain_as_public_input(layouter, c))
501 }
502
503 fn assign_as_public_input(
504 &self,
505 layouter: &mut impl Layouter<F>,
506 value: Value<K>,
507 ) -> Result<AssignedField<F, K, P>, Error> {
508 let base = BI::from(2).pow(P::LOG2_BASE);
509 let x = value.map(|v| bi_to_limbs(P::NB_LIMBS, &base, &fe_to_bigint(&(v - K::ONE))));
511 let limbs = (0..P::NB_LIMBS)
512 .map(|i| x.clone().map(|limbs| bigint_to_fe::<F>(&limbs[i as usize])))
513 .collect::<Vec<_>>();
514 let assigned_limbs = self.native_gadget.assign_many(layouter, &limbs)?;
517 let assigned_field = AssignedField::<F, K, P> {
518 limb_values: assigned_limbs,
519 limb_bounds: well_formed_bounds::<F, K, P>(),
520 _marker: PhantomData,
521 };
522 self.constrain_as_public_input(layouter, &assigned_field)?;
523 Ok(assigned_field)
524 }
525}
526
527impl<F, K, P, N> AssertionInstructions<F, AssignedField<F, K, P>> for FieldChip<F, K, P, N>
528where
529 F: PrimeField,
530 K: PrimeField,
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 = fe_to_bigint(&(constant - K::ONE));
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: PrimeField,
606 K: PrimeField,
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: PrimeField,
654 K: PrimeField,
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: PrimeField,
690 K: PrimeField,
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: PrimeField,
729 K: PrimeField,
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 let init: AssignedField<F, K, P> = self.assign_fixed(layouter, constant)?;
741 let res = terms.iter().try_fold(init, |acc, (c, x)| {
742 let prod = self.mul_by_constant(layouter, x, *c)?;
743 self.add(layouter, &acc, &prod)
744 })?;
745 self.normalize_if_approaching_limit(layouter, &res)
746 }
747
748 fn add(
749 &self,
750 layouter: &mut impl Layouter<F>,
751 x: &AssignedField<F, K, P>,
752 y: &AssignedField<F, K, P>,
753 ) -> Result<AssignedField<F, K, P>, Error> {
754 let zero: AssignedField<F, K, P> = self.assign_fixed(layouter, K::ZERO)?;
755
756 if x == &zero {
757 return Ok(y.clone());
758 }
759
760 if y == &zero {
761 return Ok(x.clone());
762 }
763
764 let mut constants = vec![BI::one()];
771 constants.resize(P::NB_LIMBS as usize, BI::zero());
772
773 let z_limb_values = x
774 .limb_values
775 .iter()
776 .zip(y.limb_values.iter())
777 .zip(constants.iter().map(|ci| bigint_to_fe::<F>(ci)))
778 .map(|((xi, yi), ci)| {
779 self.native_gadget.linear_combination(
780 layouter,
781 &[(F::ONE, xi.clone()), (F::ONE, yi.clone())],
782 ci,
783 )
784 })
785 .collect::<Result<Vec<_>, _>>()?;
786
787 let z = AssignedField::<F, K, P> {
788 limb_values: z_limb_values,
789 limb_bounds: x
790 .limb_bounds
791 .iter()
792 .zip(y.limb_bounds.iter())
793 .zip(constants.iter())
794 .map(|((xi_bounds, yi_bounds), ci)| {
795 (
796 &xi_bounds.0 + &yi_bounds.0 + ci,
797 &xi_bounds.1 + &yi_bounds.1 + ci,
798 )
799 })
800 .collect(),
801 _marker: PhantomData,
802 };
803 self.normalize_if_approaching_limit(layouter, &z)
804 }
805
806 fn sub(
807 &self,
808 layouter: &mut impl Layouter<F>,
809 x: &AssignedField<F, K, P>,
810 y: &AssignedField<F, K, P>,
811 ) -> Result<AssignedField<F, K, P>, Error> {
812 let zero: AssignedField<F, K, P> = self.assign_fixed(layouter, K::ZERO)?;
813
814 if y == &zero {
815 return Ok(x.clone());
816 }
817
818 let mut constants = vec![BI::from(-1)];
825 constants.resize(P::NB_LIMBS as usize, BI::zero());
826
827 let z_limb_values = x
828 .limb_values
829 .iter()
830 .zip(y.limb_values.iter())
831 .zip(constants.iter().map(|ci| bigint_to_fe::<F>(ci)))
832 .map(|((xi, yi), ci)| {
833 self.native_gadget.linear_combination(
834 layouter,
835 &[(F::ONE, xi.clone()), (-F::ONE, yi.clone())],
836 ci,
837 )
838 })
839 .collect::<Result<Vec<_>, _>>()?;
840
841 let z = AssignedField::<F, K, P> {
842 limb_values: z_limb_values,
843 limb_bounds: x
844 .limb_bounds
845 .iter()
846 .zip(y.limb_bounds.iter())
847 .zip(constants.iter())
848 .map(|((xi_bounds, yi_bounds), ci)| {
849 (
850 &xi_bounds.0 - &yi_bounds.1 + ci,
851 &xi_bounds.1 - &yi_bounds.0 + ci,
852 )
853 })
854 .collect(),
855 _marker: PhantomData,
856 };
857 self.normalize_if_approaching_limit(layouter, &z)
858 }
859
860 fn mul(
861 &self,
862 layouter: &mut impl Layouter<F>,
863 x: &AssignedField<F, K, P>,
864 y: &AssignedField<F, K, P>,
865 multiplying_constant: Option<K>,
866 ) -> Result<AssignedField<F, K, P>, Error> {
867 let zero: AssignedField<F, K, P> = self.assign_fixed(layouter, K::ZERO)?;
868 let one: AssignedField<F, K, P> = self.assign_fixed(layouter, K::ONE)?;
869
870 if x == &zero || y == &zero {
871 return Ok(zero);
872 }
873
874 if x == &one {
875 return Ok(y.clone());
876 }
877
878 if y == &one {
879 return Ok(x.clone());
880 }
881
882 let y = match multiplying_constant {
883 None => y.clone(),
884 Some(k) => self.mul_by_constant(layouter, y, k)?,
885 };
886 self.assign_mul(layouter, x, &y, false)
887 }
888
889 fn div(
890 &self,
891 layouter: &mut impl Layouter<F>,
892 x: &AssignedField<F, K, P>,
893 y: &AssignedField<F, K, P>,
894 ) -> Result<AssignedField<F, K, P>, Error> {
895 let one: AssignedField<F, K, P> = self.assign_fixed(layouter, K::ONE)?;
896 if y == &one {
897 return Ok(x.clone());
898 }
899
900 let y = self.normalize(layouter, y)?;
901 self.assert_non_zero(layouter, &y)?;
902 self.assign_mul(layouter, x, &y, true)
903 }
904
905 fn neg(
906 &self,
907 layouter: &mut impl Layouter<F>,
908 x: &AssignedField<F, K, P>,
909 ) -> Result<AssignedField<F, K, P>, Error> {
910 let zero: AssignedField<F, K, P> = self.assign_fixed(layouter, K::ZERO)?;
911
912 if x == &zero {
913 return Ok(zero);
914 }
915
916 let mut constants = vec![BI::from(-2)];
923 constants.resize(P::NB_LIMBS as usize, BI::zero());
924
925 let z_limb_values = x
926 .limb_values
927 .iter()
928 .zip(constants.iter().map(|ci| bigint_to_fe::<F>(ci)))
929 .map(|(xi, ci)| {
930 self.native_gadget.linear_combination(layouter, &[(-F::ONE, xi.clone())], ci)
931 })
932 .collect::<Result<Vec<_>, _>>()?;
933
934 let z = AssignedField::<F, K, P> {
935 limb_values: z_limb_values,
936 limb_bounds: x
937 .limb_bounds
938 .iter()
939 .zip(constants.iter())
940 .map(|(xi_bounds, ci)| (-&xi_bounds.1 + ci, -&xi_bounds.0 + ci))
941 .collect(),
942 _marker: PhantomData,
943 };
944 self.normalize_if_approaching_limit(layouter, &z)
945 }
946
947 fn inv(
948 &self,
949 layouter: &mut impl Layouter<F>,
950 x: &AssignedField<F, K, P>,
951 ) -> Result<AssignedField<F, K, P>, Error> {
952 let one: AssignedField<F, K, P> = self.assign_fixed(layouter, K::ONE)?;
953
954 if x == &one {
955 return Ok(one);
956 }
957
958 self.assign_mul(layouter, &one, x, true)
961 }
962
963 fn inv0(
964 &self,
965 layouter: &mut impl Layouter<F>,
966 x: &AssignedField<F, K, P>,
967 ) -> Result<AssignedField<F, K, P>, Error> {
968 let is_zero = self.is_zero(layouter, x)?;
969 let zero = self.assign_fixed(layouter, K::ZERO)?;
970 let one = self.assign_fixed(layouter, K::ONE)?;
971 let invertible = self.select(layouter, &is_zero, &one, x)?;
972 let inverse = self.assign_mul(layouter, &one, &invertible, true)?;
973 self.select(layouter, &is_zero, &zero, &inverse)
974 }
975
976 fn add_constant(
977 &self,
978 layouter: &mut impl Layouter<F>,
979 x: &AssignedField<F, K, P>,
980 k: K,
981 ) -> Result<AssignedField<F, K, P>, Error> {
982 if k.is_zero().into() {
990 return Ok(x.clone());
991 }
992
993 let base = BI::from(2).pow(P::LOG2_BASE);
994 let k_limbs = bi_to_limbs(P::NB_LIMBS, &base, &fe_to_bigint(&k));
995
996 let z_limb_values = {
997 self.native_gadget.add_constants(
998 layouter,
999 &x.limb_values,
1000 &k_limbs.iter().map(bigint_to_fe::<F>).collect::<Vec<_>>(),
1001 )?
1002 };
1003
1004 let z = AssignedField::<F, K, P> {
1005 limb_values: z_limb_values,
1006 limb_bounds: x
1007 .limb_bounds
1008 .iter()
1009 .zip(k_limbs.iter())
1010 .map(|(xi_bound, ki)| (&xi_bound.0 + ki, &xi_bound.1 + ki))
1011 .collect(),
1012 _marker: PhantomData,
1013 };
1014 self.normalize_if_approaching_limit(layouter, &z)
1015 }
1016
1017 fn mul_by_constant(
1018 &self,
1019 layouter: &mut impl Layouter<F>,
1020 x: &AssignedField<F, K, P>,
1021 k: K,
1022 ) -> Result<AssignedField<F, K, P>, Error> {
1023 if k.is_zero().into() {
1024 return self.assign_fixed(layouter, K::ZERO);
1025 }
1026
1027 if k == K::ONE {
1028 return Ok(x.clone());
1029 }
1030
1031 let threshold =
1037 P::max_limb_bound().div_floor(&(BI::from(1000) * BI::from(2).pow(P::LOG2_BASE)));
1038 if fe_to_bigint(&k) > threshold {
1039 let assigned_k = self.assign_fixed(layouter, k)?;
1040 return self.assign_mul(layouter, x, &assigned_k, false);
1041 }
1042
1043 let k_as_bigint = fe_to_bigint(&k);
1055 let kv = bigint_to_fe::<F>(&k_as_bigint);
1056 let mut constants = vec![k_as_bigint.clone() - BI::one()];
1059 constants.resize(P::NB_LIMBS as usize, BI::zero());
1060
1061 let z_limb_values = x
1062 .limb_values
1063 .iter()
1064 .zip(constants.iter().map(|ci| bigint_to_fe::<F>(ci)))
1065 .map(|(xi, ci)| {
1066 self.native_gadget.linear_combination(layouter, &[(kv, xi.clone())], ci)
1067 })
1068 .collect::<Result<Vec<_>, _>>()?;
1069
1070 let limb_bounds = x
1071 .limb_bounds
1072 .iter()
1073 .zip(constants.iter())
1074 .map(|(xi_bounds, ci)| {
1075 (
1076 &xi_bounds.0 * k_as_bigint.clone() + ci,
1077 &xi_bounds.1 * k_as_bigint.clone() + ci,
1078 )
1079 })
1080 .collect();
1081
1082 let z = AssignedField::<F, K, P> {
1083 limb_values: z_limb_values,
1084 limb_bounds,
1085 _marker: PhantomData,
1086 };
1087 self.normalize_if_approaching_limit(layouter, &z)
1088 }
1089}
1090
1091impl<F, K, P, N> FieldInstructions<F, AssignedField<F, K, P>> for FieldChip<F, K, P, N>
1092where
1093 F: PrimeField,
1094 K: PrimeField,
1095 P: FieldEmulationParams<F, K>,
1096 N: NativeInstructions<F>,
1097{
1098 fn order(&self) -> BigUint {
1099 modulus::<K>()
1100 }
1101}
1102
1103impl<F, K, P, N> ScalarFieldInstructions<F> for FieldChip<F, K, P, N>
1104where
1105 F: PrimeField,
1106 K: PrimeField,
1107 P: FieldEmulationParams<F, K>,
1108 N: NativeInstructions<F>,
1109{
1110 type Scalar = AssignedField<F, K, P>;
1111}
1112
1113impl<F, K, P, N> DecompositionInstructions<F, AssignedField<F, K, P>> for FieldChip<F, K, P, N>
1114where
1115 F: PrimeField,
1116 K: PrimeField,
1117 P: FieldEmulationParams<F, K>,
1118 N: NativeInstructions<F>,
1119{
1120 fn assigned_to_le_bits(
1121 &self,
1122 layouter: &mut impl Layouter<F>,
1123 x: &AssignedField<F, K, P>,
1124 nb_bits: Option<usize>,
1125 enforce_canonical: bool,
1126 ) -> Result<Vec<AssignedBit<F>>, Error> {
1127 let mut x = self.add_constant(layouter, x, K::ONE)?;
1129 if enforce_canonical {
1130 x = self.make_canonical(layouter, &x)?;
1131 };
1132 let mut bits = vec![];
1133 x.limb_values
1134 .iter()
1135 .zip(well_formed_log2_bounds::<F, K, P>().iter())
1136 .map(|(cell, log2_bound)| {
1137 self.native_gadget.assigned_to_le_bits(
1138 layouter,
1139 cell,
1140 Some(*log2_bound as usize),
1141 true,
1142 )
1143 })
1144 .collect::<Result<Vec<_>, _>>()?
1145 .into_iter()
1146 .for_each(|new_bits| bits.extend(new_bits));
1147
1148 let nb_bits = nb_bits.unwrap_or(K::NUM_BITS as usize);
1151 bits[nb_bits..]
1152 .iter()
1153 .try_for_each(|byte| self.native_gadget.assert_equal_to_fixed(layouter, byte, false))?;
1154 let bits = bits[0..nb_bits].to_vec();
1155 if enforce_canonical && nb_bits >= K::NUM_BITS as usize {
1156 let canonical = self.is_canonical(layouter, &bits)?;
1157 self.assert_equal_to_fixed(layouter, &canonical, true)?;
1158 }
1159 Ok(bits)
1160 }
1161
1162 fn assigned_to_le_bytes(
1163 &self,
1164 layouter: &mut impl Layouter<F>,
1165 x: &AssignedField<F, K, P>,
1166 nb_bytes: Option<usize>,
1167 ) -> Result<Vec<AssignedByte<F>>, Error> {
1168 let nb_bytes = nb_bytes.unwrap_or(K::NUM_BITS.div_ceil(8) as usize);
1169 let bits = self.assigned_to_le_bits(layouter, x, Some(nb_bytes * 8), true)?;
1171 let bytes = bits
1172 .chunks(8)
1173 .map(|chunk| {
1174 let terms = chunk
1175 .iter()
1176 .enumerate()
1177 .map(|(i, bit)| (F::from(1 << i), bit.clone().into()))
1178 .collect::<Vec<_>>();
1179 let byte = self.native_gadget.linear_combination(layouter, &terms, F::ZERO)?;
1180 self.native_gadget.convert_unsafe(layouter, &byte)
1181 })
1182 .collect::<Result<Vec<AssignedByte<F>>, Error>>()?;
1183
1184 bytes[nb_bytes..]
1187 .iter()
1188 .try_for_each(|byte| self.native_gadget.assert_equal_to_fixed(layouter, byte, 0u8))?;
1189 Ok(bytes[0..nb_bytes].to_vec())
1190 }
1191
1192 fn assigned_from_le_bits(
1193 &self,
1194 layouter: &mut impl Layouter<F>,
1195 bits: &[AssignedBit<F>],
1196 ) -> Result<AssignedField<F, K, P>, Error> {
1197 let mut coeff = K::ONE;
1198 let mut terms = vec![];
1199 for chunk in bits.chunks(P::LOG2_BASE as usize) {
1200 let mut native_coeff = F::ONE;
1201 let mut native_terms = vec![];
1202 for b in chunk.iter() {
1203 let bit: AssignedNative<F> = b.clone().into();
1204 native_terms.push((native_coeff, bit));
1205 native_coeff = native_coeff + native_coeff;
1206 }
1207 let term = {
1208 let limb =
1209 self.native_gadget.linear_combination(layouter, &native_terms, F::ZERO)?;
1210 self.assigned_field_from_limb(layouter, &limb)?
1211 };
1212 terms.push((coeff, term));
1213 coeff = bigint_to_fe::<K>(&BI::from(2).pow(P::LOG2_BASE)) * coeff;
1214 }
1215 let x = self.linear_combination(layouter, &terms, K::ZERO)?;
1216 self.normalize(layouter, &x)
1217 }
1218
1219 fn assigned_from_le_bytes(
1220 &self,
1221 layouter: &mut impl Layouter<F>,
1222 bytes: &[AssignedByte<F>],
1223 ) -> Result<AssignedField<F, K, P>, Error> {
1224 let mut coeff = K::ONE;
1225 let mut terms = vec![];
1226 let nb_bytes_per_chunk = P::LOG2_BASE / 8;
1227 for chunk in bytes.chunks(nb_bytes_per_chunk as usize) {
1228 let mut native_coeff = F::ONE;
1229 let mut native_terms = vec![];
1230 for b in chunk.iter() {
1231 let byte: AssignedNative<F> = b.clone().into();
1232 native_terms.push((native_coeff, byte));
1233 native_coeff = F::from(256) * native_coeff;
1234 }
1235 let term = {
1236 let limb =
1237 self.native_gadget.linear_combination(layouter, &native_terms, F::ZERO)?;
1238 self.assigned_field_from_limb(layouter, &limb)?
1239 };
1240 terms.push((coeff, term));
1241 coeff = bigint_to_fe::<K>(&BI::from(2).pow(8 * nb_bytes_per_chunk)) * coeff;
1242 }
1243 let x = self.linear_combination(layouter, &terms, K::ZERO)?;
1244 self.normalize(layouter, &x)
1245 }
1246
1247 fn assigned_to_le_chunks(
1248 &self,
1249 layouter: &mut impl Layouter<F>,
1250 x: &AssignedField<F, K, P>,
1251 nb_bits_per_chunk: usize,
1252 nb_chunks: Option<usize>,
1253 ) -> Result<Vec<AssignedNative<F>>, Error> {
1254 assert!(nb_bits_per_chunk < F::NUM_BITS as usize);
1255 if P::LOG2_BASE % (nb_bits_per_chunk as u32) == 0 {
1256 let nb_chunks_per_limb = (P::LOG2_BASE / (nb_bits_per_chunk as u32)) as usize;
1257 let mut nb_missing_chunks =
1258 nb_chunks.unwrap_or(nb_chunks_per_limb * P::NB_LIMBS as usize);
1259 let x = self.add_constant(layouter, x, K::ONE)?;
1261 let x = self.normalize(layouter, &x)?;
1262 let chunks = x
1263 .limb_values
1264 .iter()
1265 .map(|limb| {
1266 let nb_chunks_on_this_limb = min(nb_missing_chunks, nb_chunks_per_limb);
1267 nb_missing_chunks -= nb_chunks_on_this_limb;
1268 self.native_gadget.assigned_to_le_chunks(
1269 layouter,
1270 limb,
1271 nb_bits_per_chunk,
1272 Some(nb_chunks_on_this_limb),
1273 )
1274 })
1275 .collect::<Result<Vec<_>, Error>>()?
1276 .concat();
1277 assert_eq!(nb_missing_chunks, 0);
1278 Ok(chunks)
1279 }
1280 else {
1283 let bits = self.assigned_to_le_bits(layouter, x, None, false)?;
1284 bits.chunks(nb_bits_per_chunk)
1285 .map(|bits_of_chunk| {
1286 self.native_gadget.assigned_from_le_bits(layouter, bits_of_chunk)
1287 })
1288 .collect::<Result<Vec<_>, Error>>()
1289 }
1290 }
1291}
1292
1293impl<F, K, P, N> FieldChip<F, K, P, N>
1294where
1295 F: PrimeField,
1296 K: PrimeField,
1297 P: FieldEmulationParams<F, K>,
1298 N: NativeInstructions<F>,
1299{
1300 pub fn new(config: &FieldChipConfig, native_gadget: &N) -> Self {
1302 Self {
1303 config: config.clone(),
1304 native_gadget: native_gadget.clone(),
1305 _marker: PhantomData,
1306 }
1307 }
1308
1309 pub fn configure(
1313 meta: &mut ConstraintSystem<F>,
1314 advice_columns: &[Column<Advice>],
1315 ) -> FieldChipConfig {
1316 check_params::<F, K, P>();
1317
1318 let nb_limbs = P::NB_LIMBS;
1319 let x_cols = advice_columns[..(nb_limbs as usize)].to_vec();
1320 let y_cols = x_cols.clone();
1321 let z_cols = advice_columns[(nb_limbs as usize)..(2 * nb_limbs as usize)].to_vec();
1322
1323 x_cols.iter().chain(z_cols.iter()).for_each(|&col| meta.enable_equality(col));
1324
1325 let u_col = advice_columns[nb_limbs as usize];
1326 let v_cols = advice_columns
1327 [(nb_limbs as usize + 1)..(nb_limbs as usize + 1 + P::moduli().len())]
1328 .to_vec();
1329
1330 let mul_config = MulConfig::configure::<F, K, P>(meta, &x_cols, &z_cols);
1331 let norm_config = NormConfig::configure::<F, K, P>(meta, &x_cols, &z_cols);
1332
1333 FieldChipConfig {
1334 mul_config,
1335 norm_config,
1336 x_cols,
1337 y_cols,
1338 z_cols,
1339 u_col,
1340 v_cols,
1341 }
1342 }
1343
1344 fn assign_mul(
1351 &self,
1352 layouter: &mut impl Layouter<F>,
1353 x: &AssignedField<F, K, P>,
1354 y: &AssignedField<F, K, P>,
1355 division: bool,
1356 ) -> Result<AssignedField<F, K, P>, Error> {
1357 let base = BI::from(2).pow(P::LOG2_BASE);
1358 let nb_limbs = P::NB_LIMBS;
1359
1360 let x = self.normalize(layouter, x)?;
1361 let y = self.normalize(layouter, y)?;
1362
1363 y.value().error_if_known_and(|yv| division && K::is_zero(yv).into())?;
1364
1365 let zv = x
1366 .value()
1367 .zip(y.value())
1368 .map(|(xv, yv)| {
1369 if division {
1370 xv * yv.invert().unwrap()
1371 } else {
1372 xv * yv
1373 }
1374 })
1375 .map(|z| bi_to_limbs(nb_limbs, &base, &fe_to_bigint(&(z - K::ONE))));
1376 let z_values = (0..nb_limbs)
1377 .map(|i| zv.clone().map(|zs| bigint_to_fe::<F>(&zs[i as usize])))
1378 .collect::<Vec<_>>();
1379
1380 let z_limbs = z_values
1382 .iter()
1383 .zip(well_formed_log2_bounds::<F, K, P>().iter())
1384 .map(|(&z_value, &log2_bound)| {
1385 self.native_gadget.assign_lower_than_fixed(
1386 layouter,
1387 z_value,
1388 &(BigUint::one() << log2_bound),
1389 )
1390 })
1391 .collect::<Result<Vec<_>, Error>>()?;
1392
1393 let z = AssignedField::<F, K, P> {
1394 limb_values: z_limbs,
1395 limb_bounds: well_formed_bounds::<F, K, P>(),
1396 _marker: PhantomData,
1397 };
1398
1399 let (l, r) = if !division { (&x, &z) } else { (&z, &x) };
1402 mul::assert_mul::<F, K, P, N>(
1403 layouter,
1404 l,
1405 &y,
1406 r,
1407 &self.config.mul_config,
1408 &self.native_gadget,
1409 )?;
1410
1411 Ok(z)
1412 }
1413}
1414
1415impl<F, K, P, N> FieldChip<F, K, P, N>
1416where
1417 F: PrimeField,
1418 K: PrimeField,
1419 P: FieldEmulationParams<F, K>,
1420 N: NativeInstructions<F>,
1421{
1422 pub(crate) fn normalize(
1425 &self,
1426 layouter: &mut impl Layouter<F>,
1427 x: &AssignedField<F, K, P>,
1428 ) -> Result<AssignedField<F, K, P>, Error> {
1429 if x.is_well_formed() {
1430 Ok(x.clone())
1431 } else {
1432 self.make_canonical(layouter, x)
1433 }
1434 }
1435
1436 pub(crate) fn normalize_if_approaching_limit(
1439 &self,
1440 layouter: &mut impl Layouter<F>,
1441 x: &AssignedField<F, K, P>,
1442 ) -> Result<AssignedField<F, K, P>, Error> {
1443 let threshold: BI = P::max_limb_bound() / 10;
1445 let dangerous_lower_bounds = x.limb_bounds.iter().any(|b| b.0 < -threshold.clone());
1446 let dangerous_upper_bounds = x.limb_bounds.iter().any(|b| b.1 > threshold);
1447 if dangerous_lower_bounds || dangerous_upper_bounds {
1448 self.make_canonical(layouter, x)
1449 } else {
1450 Ok(x.clone())
1451 }
1452 }
1453
1454 fn make_canonical(
1457 &self,
1458 layouter: &mut impl Layouter<F>,
1459 x: &AssignedField<F, K, P>,
1460 ) -> Result<AssignedField<F, K, P>, Error> {
1461 let max_limb_bound = P::max_limb_bound();
1462 x.limb_bounds.iter().for_each(|(lower, upper)| {
1463 if lower < &(-&max_limb_bound) || upper > &max_limb_bound {
1464 panic!(
1465 "make_canonical: the limb bounds of the input: [{}, {}] exceed the
1466 maximum limb bound value {}; consider applying a normalization
1467 earlier, when the bounds are still within the permited range;
1468 increasing the [max_limb_bound] of your FieldEmulationParams could also
1469 help, if possible.",
1470 lower, upper, max_limb_bound
1471 );
1472 }
1473 });
1474 let z_limbs = norm::normalize::<F, K, P, N>(
1475 layouter,
1476 x,
1477 &self.config.norm_config,
1478 &self.native_gadget,
1479 )?;
1480 let z = AssignedField::<F, K, P> {
1481 limb_values: z_limbs,
1482 limb_bounds: well_formed_bounds::<F, K, P>(),
1483 _marker: PhantomData,
1484 };
1485 Ok(z)
1486 }
1487
1488 fn assigned_field_from_limb(
1491 &self,
1492 layouter: &mut impl Layouter<F>,
1493 limb: &AssignedNative<F>,
1494 ) -> Result<AssignedField<F, K, P>, Error> {
1495 let least_significant_limb = self.native_gadget.add_constant(layouter, limb, -F::ONE)?;
1497 let mut limb_values = vec![least_significant_limb];
1498 let mut limb_bounds = well_formed_bounds::<F, K, P>();
1499 let zero = self.native_gadget.assign_fixed(layouter, F::ZERO)?;
1500 limb_values.resize(P::NB_LIMBS as usize, zero);
1501 limb_bounds[0] = (limb_bounds[0].clone().0 - 1, limb_bounds[0].clone().1 - 1);
1502 Ok(AssignedField::<F, K, P> {
1503 limb_values,
1504 limb_bounds,
1505 _marker: PhantomData,
1506 })
1507 }
1508}
1509
1510impl<F, K, P, N> AssignmentInstructions<F, AssignedBit<F>> for FieldChip<F, K, P, N>
1512where
1513 F: PrimeField,
1514 K: PrimeField,
1515 P: FieldEmulationParams<F, K>,
1516 N: NativeInstructions<F>,
1517{
1518 fn assign(
1519 &self,
1520 layouter: &mut impl Layouter<F>,
1521 value: Value<bool>,
1522 ) -> Result<AssignedBit<F>, Error> {
1523 self.native_gadget.assign(layouter, value)
1524 }
1525
1526 fn assign_fixed(
1527 &self,
1528 layouter: &mut impl Layouter<F>,
1529 constant: bool,
1530 ) -> Result<AssignedBit<F>, Error> {
1531 self.native_gadget.assign_fixed(layouter, constant)
1532 }
1533}
1534
1535impl<F, K, P, N> AssertionInstructions<F, AssignedBit<F>> for FieldChip<F, K, P, N>
1537where
1538 F: PrimeField,
1539 K: PrimeField,
1540 P: FieldEmulationParams<F, K>,
1541 N: NativeInstructions<F>,
1542{
1543 fn assert_equal(
1544 &self,
1545 layouter: &mut impl Layouter<F>,
1546 x: &AssignedBit<F>,
1547 y: &AssignedBit<F>,
1548 ) -> Result<(), Error> {
1549 self.native_gadget.assert_equal(layouter, x, y)
1550 }
1551
1552 fn assert_not_equal(
1553 &self,
1554 layouter: &mut impl Layouter<F>,
1555 x: &AssignedBit<F>,
1556 y: &AssignedBit<F>,
1557 ) -> Result<(), Error> {
1558 self.native_gadget.assert_not_equal(layouter, x, y)
1559 }
1560
1561 fn assert_equal_to_fixed(
1562 &self,
1563 layouter: &mut impl Layouter<F>,
1564 x: &AssignedBit<F>,
1565 constant: bool,
1566 ) -> Result<(), Error> {
1567 self.native_gadget.assert_equal_to_fixed(layouter, x, constant)
1568 }
1569
1570 fn assert_not_equal_to_fixed(
1571 &self,
1572 layouter: &mut impl Layouter<F>,
1573 x: &AssignedBit<F>,
1574 constant: bool,
1575 ) -> Result<(), Error> {
1576 self.native_gadget.assert_not_equal_to_fixed(layouter, x, constant)
1577 }
1578}
1579
1580impl<F, K, P, N> ConversionInstructions<F, AssignedBit<F>, AssignedField<F, K, P>>
1581 for FieldChip<F, K, P, N>
1582where
1583 F: PrimeField,
1584 K: PrimeField,
1585 P: FieldEmulationParams<F, K>,
1586 N: NativeInstructions<F>,
1587{
1588 fn convert_value(&self, x: &bool) -> Option<K> {
1589 Some(if *x { K::ONE } else { K::ZERO })
1590 }
1591
1592 fn convert(
1593 &self,
1594 layouter: &mut impl Layouter<F>,
1595 x: &AssignedBit<F>,
1596 ) -> Result<AssignedField<F, K, P>, Error> {
1597 let x: AssignedNative<F> = x.clone().into();
1598 self.assigned_field_from_limb(layouter, &x)
1599 }
1600}
1601
1602impl<F, K, P, N> ConversionInstructions<F, AssignedByte<F>, AssignedField<F, K, P>>
1603 for FieldChip<F, K, P, N>
1604where
1605 F: PrimeField,
1606 K: PrimeField,
1607 P: FieldEmulationParams<F, K>,
1608 N: NativeInstructions<F>,
1609{
1610 fn convert_value(&self, x: &u8) -> Option<K> {
1611 Some(K::from(*x as u64))
1612 }
1613
1614 fn convert(
1615 &self,
1616 layouter: &mut impl Layouter<F>,
1617 x: &AssignedByte<F>,
1618 ) -> Result<AssignedField<F, K, P>, Error> {
1619 let x: AssignedNative<F> = x.clone().into();
1620 self.assigned_field_from_limb(layouter, &x)
1621 }
1622}
1623
1624impl<F, K, P, N> CanonicityInstructions<F, AssignedField<F, K, P>> for FieldChip<F, K, P, N>
1626where
1627 F: PrimeField,
1628 K: PrimeField,
1629 P: FieldEmulationParams<F, K>,
1630 N: NativeInstructions<F>,
1631{
1632 fn le_bits_lower_than(
1633 &self,
1634 layouter: &mut impl Layouter<F>,
1635 bits: &[AssignedBit<F>],
1636 bound: BigUint,
1637 ) -> Result<AssignedBit<F>, Error> {
1638 self.native_gadget.le_bits_lower_than(layouter, bits, bound)
1639 }
1640
1641 fn le_bits_geq_than(
1642 &self,
1643 layouter: &mut impl Layouter<F>,
1644 bits: &[AssignedBit<F>],
1645 bound: BigUint,
1646 ) -> Result<AssignedBit<F>, Error> {
1647 self.native_gadget.le_bits_geq_than(layouter, bits, bound)
1648 }
1649}
1650
1651#[derive(Clone, Debug)]
1652#[cfg(any(test, feature = "testing"))]
1653pub struct FieldChipConfigForTests<F, N>
1656where
1657 F: PrimeField,
1658 N: NativeInstructions<F> + FromScratch<F>,
1659{
1660 native_gadget_config: <N as FromScratch<F>>::Config,
1661 field_chip_config: FieldChipConfig,
1662}
1663
1664#[cfg(any(test, feature = "testing"))]
1665impl<F, K, P, N> FromScratch<F> for FieldChip<F, K, P, N>
1666where
1667 F: PrimeField,
1668 K: PrimeField,
1669 P: FieldEmulationParams<F, K>,
1670 N: NativeInstructions<F> + FromScratch<F>,
1671{
1672 type Config = FieldChipConfigForTests<F, N>;
1673
1674 fn new_from_scratch(config: &FieldChipConfigForTests<F, N>) -> Self {
1675 let native_gadget = <N as FromScratch<F>>::new_from_scratch(&config.native_gadget_config);
1676 FieldChip::new(&config.field_chip_config, &native_gadget)
1677 }
1678
1679 fn load_from_scratch(&self, layouter: &mut impl Layouter<F>) -> Result<(), Error> {
1680 self.native_gadget.load_from_scratch(layouter)
1681 }
1682
1683 fn configure_from_scratch(
1684 meta: &mut ConstraintSystem<F>,
1685 instance_columns: &[Column<Instance>; 2],
1686 ) -> FieldChipConfigForTests<F, N> {
1687 let native_gadget_config =
1688 <N as FromScratch<F>>::configure_from_scratch(meta, instance_columns);
1689 let field_chip_config = {
1690 let advice_cols = (0..nb_field_chip_columns::<F, K, P>())
1691 .map(|_| meta.advice_column())
1692 .collect::<Vec<_>>();
1693 FieldChip::<F, K, P, N>::configure(meta, &advice_cols)
1694 };
1695 FieldChipConfigForTests {
1696 native_gadget_config,
1697 field_chip_config,
1698 }
1699 }
1700}
1701
1702#[cfg(test)]
1703mod tests {
1704 use midnight_curves::{
1705 secp256k1::{Fp as secp256k1Base, Fq as secp256k1Scalar},
1706 Fq as BlsScalar,
1707 };
1708
1709 use super::*;
1710 use crate::{
1711 field::{
1712 decomposition::chip::P2RDecompositionChip, foreign::params::MultiEmulationParams,
1713 NativeChip, NativeGadget,
1714 },
1715 instructions::{
1716 arithmetic, assertions, control_flow, decomposition, equality, public_input, zero,
1717 },
1718 };
1719
1720 macro_rules! test_generic {
1721 ($mod:ident, $op:ident, $native:ident, $emulated:ident, $name:expr) => {
1722 $mod::tests::$op::<
1723 $native,
1724 AssignedField<$native, $emulated, MultiEmulationParams>,
1725 FieldChip<
1726 $native,
1727 $emulated,
1728 MultiEmulationParams,
1729 NativeGadget<$native, P2RDecompositionChip<$native>, NativeChip<$native>>,
1730 >,
1731 >($name);
1732 };
1733 }
1734
1735 macro_rules! test {
1736 ($mod:ident, $op:ident) => {
1737 #[test]
1738 fn $op() {
1739 test_generic!($mod, $op, BlsScalar, secp256k1Base, "field_chip_secp_base");
1740 test_generic!(
1741 $mod,
1742 $op,
1743 BlsScalar,
1744 secp256k1Scalar,
1745 "field_chip_secp_scalar"
1746 );
1747 }
1748 };
1749 }
1750
1751 test!(assertions, test_assertions);
1752
1753 test!(public_input, test_public_inputs);
1754
1755 test!(equality, test_is_equal);
1756
1757 test!(zero, test_zero_assertions);
1758 test!(zero, test_is_zero);
1759
1760 test!(control_flow, test_select);
1761 test!(control_flow, test_cond_assert_equal);
1762 test!(control_flow, test_cond_swap);
1763
1764 test!(arithmetic, test_add);
1765 test!(arithmetic, test_sub);
1766 test!(arithmetic, test_mul);
1767 test!(arithmetic, test_div);
1768 test!(arithmetic, test_neg);
1769 test!(arithmetic, test_inv);
1770 test!(arithmetic, test_pow);
1771 test!(arithmetic, test_linear_combination);
1772 test!(arithmetic, test_add_and_mul);
1773
1774 macro_rules! test_generic {
1775 ($mod:ident, $op:ident, $native:ident, $emulated:ident, $name:expr) => {
1776 $mod::tests::$op::<
1777 $native,
1778 AssignedField<$native, $emulated, MultiEmulationParams>,
1779 FieldChip<
1780 $native,
1781 $emulated,
1782 MultiEmulationParams,
1783 NativeGadget<$native, P2RDecompositionChip<$native>, NativeChip<$native>>,
1784 >,
1785 NativeGadget<$native, P2RDecompositionChip<$native>, NativeChip<$native>>,
1786 >($name);
1787 };
1788 }
1789
1790 macro_rules! test {
1791 ($mod:ident, $op:ident) => {
1792 #[test]
1793 fn $op() {
1794 test_generic!($mod, $op, BlsScalar, secp256k1Base, "field_chip_secp_base");
1795 test_generic!(
1796 $mod,
1797 $op,
1798 BlsScalar,
1799 secp256k1Scalar,
1800 "field_chip_secp_scalar"
1801 );
1802 }
1803 };
1804 }
1805
1806 test!(decomposition, test_bit_decomposition);
1807 test!(decomposition, test_byte_decomposition);
1808 test!(decomposition, test_sgn0);
1809}