1use ark_ff::{BigInteger, One, PrimeField, Zero};
13use ark_r1cs_std::{
14 GR1CSVar,
15 alloc::{AllocVar, AllocationMode},
16 boolean::Boolean,
17 convert::ToBitsGadget,
18 fields::{FieldVar, fp::FpVar},
19 prelude::EqGadget,
20 select::CondSelectGadget,
21};
22use ark_relations::gr1cs::{ConstraintSystemRef, Namespace, SynthesisError};
23use ark_std::{
24 borrow::Borrow,
25 cmp::{max, min},
26 fmt::Debug,
27 marker::PhantomData,
28 ops::Index,
29};
30use num_bigint::{BigInt, BigUint, Sign};
31use num_integer::Integer;
32use num_traits::Signed;
33
34use crate::{
35 algebra::{
36 field::{SonobeField, TwoStageFieldVar},
37 ops::{
38 bits::{FromBitsGadget, ToBitsGadgetExt},
39 eq::EquivalenceGadget,
40 matrix::{MatrixGadget, SparseMatrixVar},
41 },
42 },
43 transcripts::AbsorbableVar,
44};
45
46#[derive(Debug, Default, Clone, PartialEq)]
56pub struct Bounds(pub BigInt, pub BigInt);
57
58impl Bounds {
59 pub fn zero() -> Self {
61 Self::default()
62 }
63}
64
65impl Bounds {
66 pub fn add(&self, other: &Self) -> Self {
68 Self(&self.0 + &other.0, &self.1 + &other.1)
73 }
74
75 pub fn sub(&self, other: &Self) -> Self {
77 Self(&self.0 - &other.1, &self.1 - &other.0)
82 }
83
84 pub fn add_many(limbs: &[Self]) -> Self {
86 Self(
87 limbs.iter().map(|l| &l.0).sum(),
88 limbs.iter().map(|l| &l.1).sum(),
89 )
90 }
91
92 pub fn mul(&self, other: &Self) -> Self {
94 let ll = &self.0 * &other.0;
101 let lu = &self.0 * &other.1;
102 let ul = &self.1 * &other.0;
103 let uu = &self.1 * &other.1;
104
105 Self(
108 min(min(&ll, &lu), min(&ul, &uu)).clone(),
109 max(max(&ll, &lu), max(&ul, &uu)).clone(),
110 )
111 }
112
113 pub fn shl(&self, shift: usize) -> Self {
116 Self(&self.0 << shift, &self.1 << shift)
119 }
120
121 pub fn shr_narrower(&self, shift: usize) -> Self {
125 let d = BigInt::from(1u64) << shift;
126 Self(self.0.div_ceil(&d), self.1.div_floor(&d))
127 }
128
129 pub fn shr_wider(&self, shift: usize) -> Self {
133 let d = BigInt::from(1u64) << shift;
134 Self(self.0.div_floor(&d), self.1.div_ceil(&d))
135 }
136
137 pub fn filter_safe<F: PrimeField>(self) -> Option<Self> {
140 let limit = BigInt::from_biguint(Sign::Plus, F::MODULUS_MINUS_ONE_DIV_TWO.into());
143 (self.0 >= -&limit && self.1 <= limit && &self.1 - &self.0 <= limit).then_some(self)
144 }
145}
146
147fn compose<F: SonobeField>(limbs: impl Borrow<[F]>) -> BigInt {
148 let mut r = BigInt::zero();
149
150 for &limb in limbs.borrow().iter().rev() {
151 r <<= F::BITS_PER_LIMB;
152 r += if limb.into_bigint() > F::MODULUS_MINUS_ONE_DIV_TWO {
153 BigInt::from_biguint(Sign::Minus, (-limb).into())
154 } else {
155 BigInt::from_biguint(Sign::Plus, limb.into())
156 };
157 }
158 r
159}
160
161#[derive(Debug, Clone)]
180pub struct LimbedVar<F: PrimeField, Cfg, const ALIGNED: bool> {
181 _cfg: PhantomData<Cfg>,
182 pub(crate) limbs: Vec<FpVar<F>>,
183 bounds: Vec<Bounds>,
184}
185
186pub type EmulatedIntVar<F> = LimbedVar<F, (), true>;
191pub type EmulatedFieldVar<Base, Target> = LimbedVar<Base, Target, true>;
196
197impl<F: SonobeField, const ALIGNED: bool> GR1CSVar<F> for LimbedVar<F, (), ALIGNED> {
198 type Value = BigInt; fn cs(&self) -> ConstraintSystemRef<F> {
201 self.limbs.cs()
202 }
203
204 fn value(&self) -> Result<Self::Value, SynthesisError> {
205 self.limbs.value().map(compose)
206 }
207}
208
209impl<Base: SonobeField, Target: SonobeField, const ALIGNED: bool> GR1CSVar<Base>
210 for LimbedVar<Base, Target, ALIGNED>
211{
212 type Value = Target; fn cs(&self) -> ConstraintSystemRef<Base> {
215 self.limbs.cs()
216 }
217
218 fn value(&self) -> Result<Self::Value, SynthesisError> {
219 let v = compose(self.limbs.value()?);
220 bigint_to_field_element(v).ok_or(SynthesisError::Unsatisfiable)
221 }
222}
223
224fn bigint_to_field_element<F: PrimeField>(v: BigInt) -> Option<F> {
225 let (sign, abs) = v.into_parts();
226 if abs >= F::MODULUS.into() {
227 return None;
228 }
229 match sign {
230 Sign::Plus | Sign::NoSign => Some(F::from(abs)),
231 Sign::Minus => Some(-F::from(abs)),
232 }
233}
234
235impl<F: SonobeField, Cfg, const ALIGNED: bool> LimbedVar<F, Cfg, ALIGNED> {
236 pub fn new(limbs: Vec<FpVar<F>>, bounds: Vec<Bounds>) -> Self {
239 Self {
240 _cfg: PhantomData,
241 limbs,
242 bounds,
243 }
244 }
245
246 fn ubound(&self) -> BigInt {
249 let mut r = BigInt::zero();
250
251 for i in self.bounds.iter().rev() {
252 r <<= F::BITS_PER_LIMB;
253 r += &i.1;
254 }
255
256 r
257 }
258
259 fn lbound(&self) -> BigInt {
262 let mut r = BigInt::zero();
263
264 for i in self.bounds.iter().rev() {
265 r <<= F::BITS_PER_LIMB;
266 r += &i.0;
267 }
268
269 r
270 }
271}
272
273impl<F: SonobeField, Cfg> LimbedVar<F, Cfg, true> {
274 pub fn from_bounded_bits_le(
277 bits: &[Boolean<F>],
278 bounds: Bounds,
279 ) -> Result<Self, SynthesisError> {
280 Ok(Self::new(
281 bits.chunks(F::BITS_PER_LIMB)
282 .map(Boolean::le_bits_to_fp)
283 .collect::<Result<_, _>>()?,
284 compute_bounds(&bounds.0, &bounds.1, F::BITS_PER_LIMB),
285 ))
286 }
287
288 pub fn enforce_lt(&self, other: &Self) -> Result<(), SynthesisError> {
295 let delta = other.sub_unaligned(self)?;
301 let len = delta.limbs.len();
302
303 if len == 0 {
306 return Err(SynthesisError::Unsatisfiable);
307 }
308
309 let helper = {
317 let cs = delta.limbs.cs();
318 let mut helper = vec![false; len];
319 for i in (0..len).rev() {
320 let limb = delta.limbs[i].value().unwrap_or_default().into_bigint();
321 if !limb.is_zero() && limb <= F::MODULUS_MINUS_ONE_DIV_TWO {
322 helper[i] = true;
323 break;
324 }
325 }
326 Vec::<Boolean<F>>::new_variable_with_inferred_mode(cs, || Ok(helper))?
327 };
328
329 let mut p = FpVar::<F>::zero();
331 let mut r = FpVar::zero();
335 for (b, d) in helper.into_iter().zip(delta.limbs) {
336 p += b.select(&d, &FpVar::zero())?;
338 r.mul_equals(&d, &FpVar::zero())?;
349 r += FpVar::from(b);
351 }
352
353 r.enforce_equal(&FpVar::one())?;
356
357 let max_ub = delta.bounds.iter().map(|b| &b.1).max().unwrap();
374 if !max_ub.is_positive() {
375 return Err(SynthesisError::Unsatisfiable);
379 }
380 (p - FpVar::one()).enforce_bit_length(max_ub.bits() as usize)?;
381
382 Ok(())
383 }
384}
385
386impl<F: SonobeField, Cfg> From<LimbedVar<F, Cfg, true>> for LimbedVar<F, Cfg, false> {
387 fn from(v: LimbedVar<F, Cfg, true>) -> Self {
388 Self::new(v.limbs, v.bounds)
389 }
390}
391
392impl<F: SonobeField, Cfg, const LHS_ALIGNED: bool> LimbedVar<F, Cfg, LHS_ALIGNED> {
393 pub fn add_unaligned<const RHS_ALIGNED: bool>(
396 &self,
397 other: &LimbedVar<F, Cfg, RHS_ALIGNED>,
398 ) -> Result<LimbedVar<F, Cfg, false>, SynthesisError> {
399 let mut limbs = vec![FpVar::zero(); max(self.limbs.len(), other.limbs.len())];
400 let mut bounds = vec![Bounds::zero(); limbs.len()];
401 for (i, v) in self.limbs.iter().enumerate() {
402 bounds[i] = bounds[i]
403 .add(&self.bounds[i])
404 .filter_safe::<F>()
405 .ok_or(SynthesisError::Unsatisfiable)?;
406 limbs[i] += v;
407 }
408 for (i, v) in other.limbs.iter().enumerate() {
409 bounds[i] = bounds[i]
410 .add(&other.bounds[i])
411 .filter_safe::<F>()
412 .ok_or(SynthesisError::Unsatisfiable)?;
413 limbs[i] += v;
414 }
415 Ok(LimbedVar::new(limbs, bounds))
416 }
417
418 pub fn sub_unaligned<const RHS_ALIGNED: bool>(
421 &self,
422 other: &LimbedVar<F, Cfg, RHS_ALIGNED>,
423 ) -> Result<LimbedVar<F, Cfg, false>, SynthesisError> {
424 let mut limbs = vec![FpVar::zero(); max(self.limbs.len(), other.limbs.len())];
425 let mut bounds = vec![Bounds::zero(); limbs.len()];
426 for (i, v) in self.limbs.iter().enumerate() {
427 bounds[i] = bounds[i]
428 .add(&self.bounds[i])
429 .filter_safe::<F>()
430 .ok_or(SynthesisError::Unsatisfiable)?;
431 limbs[i] += v;
432 }
433 for (i, v) in other.limbs.iter().enumerate() {
434 bounds[i] = bounds[i]
435 .sub(&other.bounds[i])
436 .filter_safe::<F>()
437 .ok_or(SynthesisError::Unsatisfiable)?;
438 limbs[i] -= v;
439 }
440 Ok(LimbedVar::new(limbs, bounds))
441 }
442
443 pub fn mul_unaligned<const RHS_ALIGNED: bool>(
450 &self,
451 other: &LimbedVar<F, Cfg, RHS_ALIGNED>,
452 ) -> Result<LimbedVar<F, Cfg, false>, SynthesisError> {
453 let len = self.limbs.len() + other.limbs.len() - 1;
454 if self.limbs.is_constant() || other.limbs.is_constant() {
455 let bounds = (0..len)
458 .map(|i| {
459 let start = max(i + 1, other.bounds.len()) - other.bounds.len();
460 let end = min(i + 1, self.bounds.len());
461 Bounds::add_many(
462 &(start..end)
463 .map(|j| self.bounds[j].mul(&other.bounds[i - j]))
464 .collect::<Vec<_>>(),
465 )
466 .filter_safe::<F>()
467 })
468 .collect::<Option<Vec<_>>>()
469 .ok_or(SynthesisError::Unsatisfiable)?;
470
471 let limbs = (0..len)
472 .map(|i| {
473 let start = max(i + 1, other.limbs.len()) - other.limbs.len();
474 let end = min(i + 1, self.limbs.len());
475 (start..end)
476 .map(|j| &self.limbs[j] * &other.limbs[i - j])
477 .sum()
478 })
479 .collect();
480 return Ok(LimbedVar::new(limbs, bounds));
481 }
482 let (limbs, bounds) = {
485 let cs = self.limbs.cs().or(other.limbs.cs());
486 let mut limbs = vec![F::zero(); len];
487 let mut bounds = vec![Bounds::zero(); len];
488 for i in 0..self.limbs.len() {
489 for j in 0..other.limbs.len() {
490 limbs[i + j] += self.limbs[i].value().unwrap_or_default()
491 * other.limbs[j].value().unwrap_or_default();
492 bounds[i + j] = bounds[i + j].add(&self.bounds[i].mul(&other.bounds[j]))
493 }
494 }
495 (
496 Vec::new_variable_with_inferred_mode(cs, || Ok(limbs))?,
497 bounds
498 .into_iter()
499 .map(|b| b.filter_safe::<F>())
500 .collect::<Option<_>>()
501 .ok_or(SynthesisError::Unsatisfiable)?,
502 )
503 };
504 for c in 1..=len {
505 let c = F::from(c as u64);
506 let mut t = F::one();
507 let mut c_powers = vec![];
508 for _ in 0..len {
509 c_powers.push(t);
510 t *= c;
511 }
512 let l = self
514 .limbs
515 .iter()
516 .zip(&c_powers)
517 .map(|(v, t)| v * *t)
518 .sum::<FpVar<_>>();
519 let r = other
521 .limbs
522 .iter()
523 .zip(&c_powers)
524 .map(|(v, t)| v * *t)
525 .sum::<FpVar<_>>();
526 let o = limbs
528 .iter()
529 .zip(&c_powers)
530 .map(|(v, t)| v * *t)
531 .sum::<FpVar<_>>();
532 l.mul_equals(&r, &o)?;
534 }
535
536 Ok(LimbedVar::new(limbs, bounds))
537 }
538
539 pub fn enforce_equal_unaligned<const RHS_ALIGNED: bool>(
546 &self,
547 other: &LimbedVar<F, Cfg, RHS_ALIGNED>,
548 ) -> Result<(), SynthesisError> {
549 let diff = self.sub_unaligned(other)?;
552
553 let mut carry = FpVar::zero();
554 let mut carry_bounds = Bounds::zero();
555 let mut group_bounds = Bounds::zero();
556 let mut offset = 0;
557 let inv = F::from(BigUint::one() << F::BITS_PER_LIMB)
559 .inverse()
560 .unwrap();
561
562 for (limb, bounds) in diff.limbs.iter().zip(&diff.bounds) {
596 if let Some(new_group_bounds) = group_bounds.add(&bounds.shl(offset)).filter_safe::<F>()
597 {
598 carry = (carry + limb) * inv;
599 carry_bounds = carry_bounds.add(bounds).shr_narrower(F::BITS_PER_LIMB);
600 group_bounds = new_group_bounds;
601 offset += F::BITS_PER_LIMB;
602 } else {
603 debug_assert!(carry_bounds.shl(offset).0 >= group_bounds.0);
607 debug_assert!(carry_bounds.shl(offset).1 <= group_bounds.1);
608
609 (&carry
613 - bigint_to_field_element::<F>(carry_bounds.0.clone())
614 .ok_or(SynthesisError::Unsatisfiable)?)
615 .enforce_bit_length(
616 (&carry_bounds.1 - &carry_bounds.0 + BigInt::one()).bits() as usize
617 )?;
618
619 carry = (carry + limb) * inv;
620 carry_bounds = carry_bounds.add(bounds).shr_narrower(F::BITS_PER_LIMB);
621 offset = F::BITS_PER_LIMB;
627 group_bounds = carry_bounds.shl(offset);
628 }
629 }
630
631 carry.enforce_equal(&FpVar::zero())?;
632
633 Ok(())
634 }
635}
636
637impl<Base: SonobeField, Target: SonobeField, const LHS_ALIGNED: bool>
638 LimbedVar<Base, Target, LHS_ALIGNED>
639{
640 pub fn modulo(&self) -> Result<LimbedVar<Base, Target, true>, SynthesisError> {
648 let cs = self.cs();
649 let m = BigInt::from_biguint(Sign::Plus, Target::MODULUS.into());
650 let (q, r) = {
652 let v = compose(self.limbs.value().unwrap_or_default());
653 let q = v.div_floor(&m);
654 let r = v - &q * &m;
655
656 (
657 LimbedVar::new_variable_with_inferred_mode(cs.clone(), || {
658 Ok((
659 q,
660 Bounds(self.lbound().div_floor(&m), self.ubound().div_floor(&m)),
661 ))
662 })?,
663 LimbedVar::new_variable_with_inferred_mode(cs.clone(), || {
664 Ok((r, Bounds(Zero::zero(), m.clone())))
665 })?,
666 )
667 };
668
669 let m = LimbedVar::constant(m);
670
671 q.mul_unaligned(&m)?
673 .add_unaligned(&r)?
674 .enforce_equal_unaligned(self)?;
675 r.enforce_lt(&m)?;
677
678 Ok(r)
679 }
680
681 pub fn enforce_congruent<const RHS_ALIGNED: bool>(
684 &self,
685 other: &LimbedVar<Base, Target, RHS_ALIGNED>,
686 ) -> Result<(), SynthesisError> {
687 let cs = self.cs();
688 let m = BigInt::from_biguint(Sign::Plus, Target::MODULUS.into());
689 let q = LimbedVar::new_variable_with_inferred_mode(cs.clone(), || {
691 let x = compose(self.limbs.value().unwrap_or_default());
692 let y = compose(other.limbs.value().unwrap_or_default());
693 Ok((
694 (x - y).div_floor(&m),
695 Bounds(
696 (self.lbound() - other.ubound()).div_floor(&m),
697 (self.ubound() - other.lbound()).div_floor(&m),
698 ),
699 ))
700 })?;
701
702 let m = LimbedVar::constant(m);
703
704 self.sub_unaligned(other)?
706 .enforce_equal_unaligned(&q.mul_unaligned(&m)?)
707 }
708}
709
710impl<Base: SonobeField, Target: SonobeField> EquivalenceGadget<LimbedVar<Base, Target, true>>
713 for LimbedVar<Base, Target, true>
714{
715 fn enforce_equivalent(&self, other: &Self) -> Result<(), SynthesisError> {
716 self.enforce_equal(other)
717 }
718}
719
720impl<Base: SonobeField, Target: SonobeField> EquivalenceGadget<LimbedVar<Base, Target, true>>
721 for LimbedVar<Base, Target, false>
722{
723 fn enforce_equivalent(
724 &self,
725 other: &LimbedVar<Base, Target, true>,
726 ) -> Result<(), SynthesisError> {
727 self.enforce_congruent(other)
728 }
729}
730
731impl<Base: SonobeField, Target: SonobeField> EquivalenceGadget<LimbedVar<Base, Target, false>>
732 for LimbedVar<Base, Target, true>
733{
734 fn enforce_equivalent(
735 &self,
736 other: &LimbedVar<Base, Target, false>,
737 ) -> Result<(), SynthesisError> {
738 self.enforce_congruent(other)
739 }
740}
741
742impl<Base: SonobeField, Target: SonobeField> EquivalenceGadget<LimbedVar<Base, Target, false>>
743 for LimbedVar<Base, Target, false>
744{
745 fn enforce_equivalent(
746 &self,
747 other: &LimbedVar<Base, Target, false>,
748 ) -> Result<(), SynthesisError> {
749 self.enforce_congruent(other)
750 }
751}
752
753impl<F: SonobeField> EquivalenceGadget<LimbedVar<F, (), true>> for LimbedVar<F, (), true> {
754 fn enforce_equivalent(&self, other: &LimbedVar<F, (), true>) -> Result<(), SynthesisError> {
755 self.enforce_equal(other)
756 }
757}
758
759impl<F: SonobeField> EquivalenceGadget<LimbedVar<F, (), true>> for LimbedVar<F, (), false> {
760 fn enforce_equivalent(&self, other: &LimbedVar<F, (), true>) -> Result<(), SynthesisError> {
761 self.enforce_equal_unaligned(other)
762 }
763}
764
765impl<F: SonobeField> EquivalenceGadget<LimbedVar<F, (), false>> for LimbedVar<F, (), true> {
766 fn enforce_equivalent(&self, other: &LimbedVar<F, (), false>) -> Result<(), SynthesisError> {
767 self.enforce_equal_unaligned(other)
768 }
769}
770
771impl<F: SonobeField> EquivalenceGadget<LimbedVar<F, (), false>> for LimbedVar<F, (), false> {
772 fn enforce_equivalent(&self, other: &LimbedVar<F, (), false>) -> Result<(), SynthesisError> {
773 self.enforce_equal_unaligned(other)
774 }
775}
776
777impl<Base: SonobeField, Target: SonobeField> TryFrom<LimbedVar<Base, Target, false>>
778 for LimbedVar<Base, Target, true>
779{
780 type Error = SynthesisError;
781
782 fn try_from(v: LimbedVar<Base, Target, false>) -> Result<Self, Self::Error> {
783 v.modulo()
784 }
785}
786
787impl<Base: SonobeField, Target: SonobeField> TwoStageFieldVar for LimbedVar<Base, Target, true> {
788 type Intermediate = LimbedVar<Base, Target, false>;
789}
790
791impl<F: SonobeField, Cfg> EqGadget<F> for LimbedVar<F, Cfg, true> {
793 fn is_eq(&self, other: &Self) -> Result<Boolean<F>, SynthesisError> {
794 if self.limbs.len() != other.limbs.len() {
795 return Err(SynthesisError::Unsatisfiable);
796 }
797 if self.bounds.len() != other.bounds.len() {
798 return Err(SynthesisError::Unsatisfiable);
799 }
800 let mut bits = vec![];
801 for i in 0..self.limbs.len() {
802 if self.bounds[i] != other.bounds[i] {
803 return Err(SynthesisError::Unsatisfiable);
804 }
805 bits.push(self.limbs[i].is_eq(&other.limbs[i])?);
806 }
807 if bits.is_empty() {
808 Ok(Boolean::TRUE)
809 } else {
810 Boolean::kary_and(&bits)
811 }
812 }
813
814 fn enforce_equal(&self, other: &Self) -> Result<(), SynthesisError> {
815 if self.limbs.len() != other.limbs.len() {
816 return Err(SynthesisError::Unsatisfiable);
817 }
818 if self.bounds.len() != other.bounds.len() {
819 return Err(SynthesisError::Unsatisfiable);
820 }
821 for i in 0..self.limbs.len() {
822 if self.bounds[i] != other.bounds[i] {
823 return Err(SynthesisError::Unsatisfiable);
824 }
825 self.limbs[i].enforce_equal(&other.limbs[i])?;
826 }
827 Ok(())
828 }
829
830 fn conditional_enforce_equal(
831 &self,
832 other: &Self,
833 should_enforce: &Boolean<F>,
834 ) -> Result<(), SynthesisError> {
835 if should_enforce.is_constant() {
836 if should_enforce.value()? {
837 return self.enforce_equal(other);
838 } else {
839 return Ok(()); }
841 }
842 self.is_eq(other)?
843 .conditional_enforce_equal(&Boolean::TRUE, should_enforce)
844 }
845}
846
847impl<F: SonobeField, Cfg> FromBitsGadget<F> for LimbedVar<F, Cfg, true> {
848 fn from_bits_le(bits: &[Boolean<F>]) -> Result<Self, SynthesisError> {
849 Self::from_bounded_bits_le(
850 bits,
851 Bounds(
852 BigInt::zero(),
853 (BigInt::one() << bits.len()) - BigInt::one(),
854 ),
855 )
856 }
857}
858
859impl<F: PrimeField, Cfg: Clone> CondSelectGadget<F> for LimbedVar<F, Cfg, true> {
860 fn conditionally_select(
861 cond: &Boolean<F>,
862 true_value: &Self,
863 false_value: &Self,
864 ) -> Result<Self, SynthesisError> {
865 if true_value.limbs.len() != false_value.limbs.len() {
866 return Err(SynthesisError::Unsatisfiable);
867 }
868 if true_value.bounds.len() != false_value.bounds.len() {
869 return Err(SynthesisError::Unsatisfiable);
870 }
871 let mut limbs = vec![];
872 let mut bounds = vec![];
873 for i in 0..true_value.limbs.len() {
874 if true_value.bounds[i] != false_value.bounds[i] {
875 return Err(SynthesisError::Unsatisfiable);
876 }
877 limbs.push(cond.select(&true_value.limbs[i], &false_value.limbs[i])?);
878 bounds.push(true_value.bounds[i].clone());
879 }
880 Ok(Self {
881 _cfg: PhantomData,
882 limbs,
883 bounds,
884 })
885 }
886}
887
888impl<F: PrimeField, Cfg> ToBitsGadget<F> for LimbedVar<F, Cfg, true> {
889 fn to_bits_le(&self) -> Result<Vec<Boolean<F>>, SynthesisError> {
890 for bound in &self.bounds {
891 if bound.0 < BigInt::zero() {
892 return Err(SynthesisError::Unsatisfiable);
893 }
894 }
895 Ok(self
896 .limbs
897 .iter()
898 .zip(&self.bounds)
899 .map(|(limb, bound)| limb.to_n_bits_le(bound.1.bits() as usize))
900 .collect::<Result<Vec<_>, _>>()?
901 .concat())
902 }
903}
904
905impl<F: PrimeField, Cfg> AbsorbableVar<F> for LimbedVar<F, Cfg, true> {
906 fn absorb_into(&self, dest: &mut Vec<FpVar<F>>) -> Result<(), SynthesisError> {
907 let bits_per_limb = F::MODULUS_BIT_SIZE as usize - 1;
908
909 self.to_bits_le()?
910 .chunks(bits_per_limb)
911 .try_for_each(|i| Boolean::le_bits_to_fp(i).map(|v| dest.push(v)))
912 }
913}
914
915impl<CF: SonobeField, Cfg> MatrixGadget<LimbedVar<CF, Cfg, false>>
916 for SparseMatrixVar<LimbedVar<CF, Cfg, false>>
917{
918 fn mul_vector(
919 &self,
920 v: &impl Index<usize, Output = LimbedVar<CF, Cfg, false>>,
921 ) -> Result<Vec<LimbedVar<CF, Cfg, false>>, SynthesisError> {
922 self.0
923 .iter()
924 .map(|row| {
925 let len = row
926 .iter()
927 .map(|(value, col_i)| value.limbs.len() + v[*col_i].limbs.len() - 1)
928 .max()
929 .unwrap_or(0);
930 let bounds = (0..len)
935 .map(|i| {
936 Bounds::add_many(
937 &row.iter()
938 .flat_map(|(value, col_i)| {
939 let start =
940 max(i + 1, v[*col_i].bounds.len()) - v[*col_i].bounds.len();
941 let end = min(i + 1, value.bounds.len());
942 (start..end)
943 .map(|j| value.bounds[j].mul(&v[*col_i].bounds[i - j]))
944 })
945 .collect::<Vec<_>>(),
946 )
947 .filter_safe::<CF>()
948 })
949 .collect::<Option<Vec<_>>>()
950 .ok_or(SynthesisError::Unsatisfiable)?;
951 let limbs = (0..len)
952 .map(|i| {
953 row.iter()
954 .flat_map(|(value, col_i)| {
955 let start =
956 max(i + 1, v[*col_i].limbs.len()) - v[*col_i].limbs.len();
957 let end = min(i + 1, value.limbs.len());
958 (start..end).map(|j| &value.limbs[j] * &v[*col_i].limbs[i - j])
959 })
960 .sum()
961 })
962 .collect();
963 Ok(LimbedVar::new(limbs, bounds))
964 })
965 .collect()
966 }
967}
968
969fn compute_bounds(lb: &BigInt, ub: &BigInt, bits_per_limb: usize) -> Vec<Bounds> {
970 let len = max(lb.bits(), ub.bits()) as usize;
971 let (n_full_limbs, n_remaining_bits) = len.div_rem(&bits_per_limb);
972
973 let mut bounds = vec![
974 Bounds(
975 if lb.is_negative() {
976 BigInt::one() - (BigInt::one() << bits_per_limb)
977 } else {
978 BigInt::zero()
979 },
980 if ub.is_positive() {
981 (BigInt::one() << bits_per_limb) - BigInt::one()
982 } else {
983 BigInt::zero()
984 },
985 );
986 n_full_limbs
987 ];
988
989 if !n_remaining_bits.is_zero() {
990 let d = BigInt::one() << (len - n_remaining_bits);
991 bounds.push(Bounds(lb.div_floor(&d), ub.div_ceil(&d)));
992 }
993
994 bounds
995}
996
997impl<F: SonobeField, Cfg> AllocVar<(BigInt, Bounds), F> for LimbedVar<F, Cfg, true> {
998 fn new_variable<T: Borrow<(BigInt, Bounds)>>(
999 cs: impl Into<Namespace<F>>,
1000 f: impl FnOnce() -> Result<T, SynthesisError>,
1001 mode: AllocationMode,
1002 ) -> Result<Self, SynthesisError> {
1003 let cs = cs.into().cs();
1004 let v = f()?;
1005 let (x, Bounds(lb, ub)) = v.borrow();
1006
1007 if x < lb || x > ub {
1008 return Err(SynthesisError::Unsatisfiable);
1009 }
1010
1011 let len = max(lb.bits(), ub.bits()) as usize;
1012
1013 let x_is_neg = x.is_negative();
1014 let mut x_bits = x
1015 .magnitude()
1016 .to_radix_le(2)
1017 .into_iter()
1018 .map(|i| i == 1)
1019 .collect::<Vec<_>>();
1020 x_bits.resize(len, false);
1021
1022 let x_is_neg = if !lb.is_negative() {
1023 Boolean::FALSE
1024 } else if !ub.is_positive() {
1025 Boolean::TRUE
1026 } else {
1027 Boolean::new_variable(cs.clone(), || Ok(x_is_neg), mode)?
1028 };
1029 let x_bits = Vec::new_variable(cs, || Ok(x_bits), mode)?;
1030
1031 let limbs = x_bits
1032 .chunks(F::BITS_PER_LIMB)
1033 .map(|chunk| {
1034 let limb_abs = Boolean::le_bits_to_fp(chunk)?;
1035 x_is_neg.select(&limb_abs.negate()?, &limb_abs)
1036 })
1037 .collect::<Result<_, _>>()?;
1038
1039 let bounds = compute_bounds(lb, ub, F::BITS_PER_LIMB);
1040
1041 let var = Self::new(limbs, bounds);
1042
1043 #[allow(clippy::if_same_then_else)]
1058 if lb.is_zero() && ub + BigInt::one() == BigInt::one() << len {
1059 } else if BigInt::one() - lb == BigInt::one() << len && ub.is_zero() {
1060 } else if BigInt::one() - lb == BigInt::one() << len
1061 && ub + BigInt::one() == BigInt::one() << len
1062 {
1063 } else {
1064 var.enforce_lt(&Self::constant(ub + BigInt::one()))?;
1065 Self::constant(lb - BigInt::one()).enforce_lt(&var)?;
1066 }
1067
1068 Ok(var)
1069 }
1070
1071 fn new_constant(
1072 _cs: impl Into<Namespace<F>>,
1073 t: impl Borrow<(BigInt, Bounds)>,
1074 ) -> Result<Self, SynthesisError> {
1075 let (x, Bounds(lb, ub)) = t.borrow();
1076
1077 if x < lb || x > ub {
1078 return Err(SynthesisError::Unsatisfiable);
1079 }
1080
1081 let bits = x
1084 .magnitude()
1085 .to_radix_le(2)
1086 .into_iter()
1087 .map(|i| i == 1)
1088 .collect::<Vec<_>>();
1089
1090 let (limbs, bounds) = bits
1091 .chunks(F::BITS_PER_LIMB)
1092 .map(F::BigInt::from_bits_le)
1093 .map(|v| {
1094 let v_field = if x.is_negative() {
1095 -F::from(v)
1096 } else {
1097 F::from(v)
1098 };
1099 let v_bigint = BigInt::from_biguint(x.sign(), v.into());
1100 (FpVar::constant(v_field), Bounds(v_bigint.clone(), v_bigint))
1101 })
1102 .unzip::<_, _, Vec<_>, Vec<_>>();
1103
1104 Ok(Self::new(limbs, bounds))
1105 }
1106}
1107
1108impl<F: SonobeField, G: SonobeField, Cfg> AllocVar<G, F> for LimbedVar<F, Cfg, true> {
1109 fn new_variable<T: Borrow<G>>(
1110 cs: impl Into<Namespace<F>>,
1111 f: impl FnOnce() -> Result<T, SynthesisError>,
1112 mode: AllocationMode,
1113 ) -> Result<Self, SynthesisError> {
1114 Self::new_variable(
1115 cs,
1116 || {
1117 f().map(|v| {
1118 (
1119 v.borrow().into_bigint().into().into(),
1120 Bounds(Zero::zero(), (-G::one()).into_bigint().into().into()),
1121 )
1122 })
1123 },
1124 mode,
1125 )
1126 }
1127}
1128
1129impl<F: SonobeField, Cfg> LimbedVar<F, Cfg, true> {
1130 pub fn constant(x: BigInt) -> Self {
1133 Self::new_constant(ConstraintSystemRef::None, (x.clone(), Bounds(x.clone(), x))).unwrap()
1136 }
1137}
1138
1139macro_rules! impl_binary_op {
1140 (
1141 $trait: ident,
1142 $fn: ident,
1143 |$lhs_i:tt : &$lhs:ty, $rhs_i:tt : &$rhs:ty| -> $out:ty $body:block,
1144 ($($params:tt)+),
1145 ) => {
1146 impl<$($params)+> core::ops::$trait<&$rhs> for &$lhs
1147 {
1148 type Output = $out;
1149
1150 fn $fn(self, other: &$rhs) -> Self::Output {
1151 let $lhs_i = self;
1152 let $rhs_i = other;
1153 $body
1154 }
1155 }
1156
1157 impl<$($params)+> core::ops::$trait<$rhs> for &$lhs
1158 {
1159 type Output = $out;
1160
1161 fn $fn(self, other: $rhs) -> Self::Output {
1162 core::ops::$trait::$fn(self, &other)
1163 }
1164 }
1165
1166 impl<$($params)+> core::ops::$trait<&$rhs> for $lhs
1167 {
1168 type Output = $out;
1169
1170 fn $fn(self, other: &$rhs) -> Self::Output {
1171 core::ops::$trait::$fn(&self, other)
1172 }
1173 }
1174
1175 impl<$($params)+> core::ops::$trait<$rhs> for $lhs
1176 {
1177 type Output = $out;
1178
1179 fn $fn(self, other: $rhs) -> Self::Output {
1180 core::ops::$trait::$fn(&self, &other)
1181 }
1182 }
1183 }
1184}
1185
1186macro_rules! impl_assignment_op {
1187 (
1188 $assign_trait: ident,
1189 $assign_fn: ident,
1190 |$lhs_i:tt : &mut $lhs:ty, $rhs_i:tt : &$rhs:ty| $body:block,
1191 ($($params:tt)+),
1192 ) => {
1193 impl<$($params)+> core::ops::$assign_trait<$rhs> for $lhs
1194 {
1195 fn $assign_fn(&mut self, other: $rhs) {
1196 core::ops::$assign_trait::$assign_fn(self, &other)
1197 }
1198 }
1199
1200 impl<$($params)+> core::ops::$assign_trait<&$rhs> for $lhs
1201 {
1202 fn $assign_fn(&mut self, other: &$rhs) {
1203 let $lhs_i = self;
1204 let $rhs_i = other;
1205 $body
1206 }
1207 }
1208 }
1209}
1210
1211impl_binary_op!(
1212 Add,
1213 add,
1214 |a: &LimbedVar<F, Cfg, LHS_ALIGNED>, b: &LimbedVar<F, Cfg, RHS_ALIGNED>| -> LimbedVar<F, Cfg, false> {
1215 a.add_unaligned(b).unwrap()
1216 },
1217 (F: SonobeField, Cfg, const LHS_ALIGNED: bool, const RHS_ALIGNED: bool),
1218);
1219
1220impl_assignment_op!(
1221 AddAssign,
1222 add_assign,
1223 |a: &mut LimbedVar<F, Cfg, false>, b: &LimbedVar<F, Cfg, ALIGNED>| {
1224 *a = a.add_unaligned(b).unwrap()
1225 },
1226 (F: SonobeField, Cfg, const ALIGNED: bool),
1227);
1228
1229impl_binary_op!(
1230 Sub,
1231 sub,
1232 |a: &LimbedVar<F, Cfg, SELF_ALIGNED>, b: &LimbedVar<F, Cfg, OTHER_ALIGNED>| -> LimbedVar<F, Cfg, false> {
1233 a.sub_unaligned(b).unwrap()
1234 },
1235 (F: SonobeField, Cfg, const SELF_ALIGNED: bool, const OTHER_ALIGNED: bool),
1236);
1237
1238impl_assignment_op!(
1239 SubAssign,
1240 sub_assign,
1241 |a: &mut LimbedVar<F, Cfg, false>, b: &LimbedVar<F, Cfg, OTHER_ALIGNED>| {
1242 *a = a.sub_unaligned(b).unwrap()
1243 },
1244 (F: SonobeField, Cfg, const OTHER_ALIGNED: bool),
1245);
1246
1247impl_binary_op!(
1248 Mul,
1249 mul,
1250 |a: &LimbedVar<F, Cfg, SELF_ALIGNED>, b: &LimbedVar<F, Cfg, OTHER_ALIGNED>| -> LimbedVar<F, Cfg, false> {
1251 a.mul_unaligned(b).unwrap()
1252 },
1253 (F: SonobeField, Cfg, const SELF_ALIGNED: bool, const OTHER_ALIGNED: bool),
1254);
1255
1256impl_assignment_op!(
1257 MulAssign,
1258 mul_assign,
1259 |a: &mut LimbedVar<F, Cfg, false>, b: &LimbedVar<F, Cfg, OTHER_ALIGNED>| {
1260 *a = a.mul_unaligned(b).unwrap()
1261 },
1262 (F: SonobeField, Cfg, const OTHER_ALIGNED: bool),
1263);
1264
1265#[cfg(test)]
1266mod tests {
1267 use ark_ff::Field;
1268 use ark_pallas::{Fq, Fr};
1269 use ark_relations::gr1cs::ConstraintSystem;
1270 use ark_std::{
1271 UniformRand,
1272 error::Error,
1273 rand::{Rng, thread_rng},
1274 };
1275 use num_bigint::RandBigInt;
1276 #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
1277 use wasm_bindgen_test::wasm_bindgen_test as test;
1278
1279 use super::*;
1280
1281 #[test]
1282 fn test_eq() -> Result<(), Box<dyn Error>> {
1283 let cs = ConstraintSystem::<Fr>::new_ref();
1284
1285 let zero = LimbedVar::<Fr, (), true>::new(vec![], vec![]);
1286 let zero2 = LimbedVar::<Fr, (), true>::new(
1287 vec![
1288 FpVar::new_witness(cs.clone(), || {
1289 Ok(Fr::from(BigUint::one() << Fr::BITS_PER_LIMB))
1290 })?,
1291 FpVar::new_witness(cs.clone(), || Ok(-Fr::one()))?,
1292 ],
1293 vec![
1294 Bounds(
1295 -(BigInt::one() << (Fr::BITS_PER_LIMB * 2)),
1296 BigInt::one() << (Fr::BITS_PER_LIMB * 2),
1297 ),
1298 Bounds(
1299 -(BigInt::one() << (Fr::BITS_PER_LIMB * 2)),
1300 BigInt::one() << (Fr::BITS_PER_LIMB * 2),
1301 ),
1302 ],
1303 );
1304 let zero3 = LimbedVar::<Fr, (), true>::new(
1305 vec![
1306 FpVar::new_witness(cs.clone(), || {
1307 Ok(Fr::from(BigUint::one() << Fr::BITS_PER_LIMB))
1308 })?,
1309 FpVar::new_witness(cs.clone(), || Ok(-Fr::one()))?,
1310 ],
1311 vec![
1312 Bounds(
1313 BigInt::zero(),
1314 BigInt::from_biguint(Sign::Plus, Fr::MODULUS_MINUS_ONE_DIV_TWO.into()),
1315 ),
1316 Bounds(
1317 -BigInt::from_biguint(Sign::Plus, Fr::MODULUS_MINUS_ONE_DIV_TWO.into()),
1318 BigInt::zero(),
1319 ),
1320 ],
1321 );
1322
1323 zero.enforce_equal_unaligned(&zero2)?;
1324 zero.enforce_equal_unaligned(&zero3)?;
1325
1326 let rng = &mut thread_rng();
1327
1328 let n_limbs = 100;
1329
1330 let coeffs = (0..n_limbs)
1331 .map(|_| if rng.gen_bool(0.5) {
1332 -Fr::one()
1333 } else {
1334 Fr::one()
1335 } * Fr::from(rng.gen_biguint(Fr::BITS_PER_LIMB as u64 * 2 - 1)))
1336 .collect::<Vec<_>>();
1337 let unaligned = LimbedVar::<Fr, (), true>::new(
1338 Vec::new_witness(cs.clone(), || Ok(&coeffs[..]))?,
1339 vec![
1340 Bounds(
1341 -(BigInt::one() << (Fr::BITS_PER_LIMB * 2)),
1342 BigInt::one() << (Fr::BITS_PER_LIMB * 2),
1343 );
1344 n_limbs
1345 ],
1346 );
1347
1348 let aligned = EmulatedIntVar::new_witness(cs.clone(), || {
1349 let v = compose(&coeffs[..]);
1350 Ok((
1351 v,
1352 Bounds(
1353 BigInt::one() - (BigInt::one() << (Fr::BITS_PER_LIMB * 2 * n_limbs)),
1354 (BigInt::one() << (Fr::BITS_PER_LIMB * 2 * n_limbs)) - BigInt::one(),
1355 ),
1356 ))
1357 })?;
1358 aligned.enforce_equal_unaligned(&unaligned)?;
1359
1360 assert!(cs.is_satisfied()?);
1361
1362 let mut unaligned_incorrect = unaligned.clone();
1363 unaligned_incorrect.limbs[0] = if coeffs[0].is_zero() {
1364 FpVar::new_witness(cs.clone(), || Ok(Fr::one()))?
1365 } else {
1366 FpVar::new_witness(cs.clone(), || Ok(-coeffs[0]))?
1367 };
1368 aligned.enforce_equal_unaligned(&unaligned_incorrect)?;
1369
1370 assert!(!cs.is_satisfied()?);
1371
1372 Ok(())
1373 }
1374
1375 #[test]
1376 fn test_enforce_equal_unaligned_rejects_multiple_of_modulus() -> Result<(), Box<dyn Error>> {
1377 let cs = ConstraintSystem::<Fr>::new_ref();
1378
1379 let mask = (BigUint::one() << Fr::BITS_PER_LIMB) - BigUint::one();
1381 let mut vals = vec![];
1382
1383 let mut t: BigUint = Fr::MODULUS.into();
1384 t <<= Fr::BITS_PER_LIMB;
1385 while !t.is_zero() {
1386 vals.push(Fr::from(&t & &mask));
1387 t >>= Fr::BITS_PER_LIMB;
1388 }
1389 assert_eq!(compose(&vals[..]) >> Fr::BITS_PER_LIMB, Fr::MODULUS.into());
1390
1391 let mut bounds = vec![Bounds(BigInt::zero(), BigInt::zero())];
1392 bounds.push(Bounds(
1395 BigInt::zero(),
1396 BigInt::one() << (Fr::MODULUS_BIT_SIZE - 2),
1397 ));
1398 bounds.resize(
1399 vals.len(),
1400 Bounds(BigInt::zero(), BigInt::one() << (Fr::BITS_PER_LIMB + 1)),
1401 );
1402
1403 let v = EmulatedIntVar::new(Vec::new_witness(cs.clone(), || Ok(vals))?, bounds);
1404
1405 assert_eq!(v.value()? >> Fr::BITS_PER_LIMB, Fr::MODULUS.into());
1406 assert!(cs.is_satisfied()?);
1407
1408 v.enforce_equal_unaligned(&EmulatedIntVar::constant(Zero::zero()))?;
1409
1410 assert!(!cs.is_satisfied()?);
1412
1413 Ok(())
1414 }
1415
1416 #[test]
1417 fn test_alloc() -> Result<(), Box<dyn Error>> {
1418 let rng = &mut thread_rng();
1419
1420 let size = 1024;
1421 let zero = BigInt::zero();
1422 let max: BigInt = (BigInt::one() << size) - BigInt::one();
1423
1424 let mut bounds = vec![(zero.clone(), max.clone())];
1425
1426 bounds.push((-&max, zero.clone()));
1427 bounds.push((-&max, max.clone()));
1428 bounds.push((rng.gen_bigint_range(&-&max, &zero), zero.clone()));
1429 bounds.push((zero.clone(), rng.gen_bigint_range(&zero, &max)));
1430 bounds.push((
1431 rng.gen_bigint_range(&-&max, &zero),
1432 rng.gen_bigint_range(&zero, &max),
1433 ));
1434 bounds.push({
1435 let lb = rng.gen_bigint_range(&-&max, &zero);
1436 (lb.clone(), rng.gen_bigint_range(&lb, &zero))
1437 });
1438 bounds.push({
1439 let lb = rng.gen_bigint_range(&zero, &max);
1440 (lb.clone(), rng.gen_bigint_range(&lb, &max))
1441 });
1442
1443 for (lb, ub) in bounds {
1444 let mut v = vec![
1445 lb.clone(),
1446 ub.clone(),
1447 &lb + BigInt::one(),
1448 &ub - BigInt::one(),
1449 ];
1450 if BigInt::zero() >= lb && BigInt::zero() <= ub {
1451 v.push(BigInt::zero());
1452 }
1453 for _ in 0..10 {
1454 v.push(rng.gen_bigint_range(&lb, &ub));
1455 }
1456 for a in v {
1457 let cs = ConstraintSystem::<Fr>::new_ref();
1458
1459 let a_var = EmulatedIntVar::new_witness(cs.clone(), || {
1460 Ok((a.clone(), Bounds(lb.clone(), ub.clone())))
1461 })?;
1462
1463 let a_const = EmulatedIntVar::<Fr>::constant(a.clone());
1464
1465 assert_eq!(a, a_var.value()?);
1466 assert_eq!(a, a_const.value()?);
1467 assert!(cs.is_satisfied()?);
1468 }
1469 }
1470
1471 Ok(())
1472 }
1473
1474 #[test]
1475 fn test_mul_bigint() -> Result<(), Box<dyn Error>> {
1476 let cs = ConstraintSystem::<Fr>::new_ref();
1477
1478 let size = 2048;
1479
1480 let rng = &mut thread_rng();
1481 let a = rng.gen_bigint(size as u64);
1482 let b = rng.gen_bigint(size as u64);
1483 let ab = &a * &b;
1484 let aab = &a * &ab;
1485 let abb = &ab * &b;
1486
1487 let a_var = EmulatedIntVar::new_witness(cs.clone(), || {
1488 Ok((
1489 a,
1490 Bounds(
1491 BigInt::one() - (BigInt::one() << size),
1492 (BigInt::one() << size) - BigInt::one(),
1493 ),
1494 ))
1495 })?;
1496 let b_var = EmulatedIntVar::new_witness(cs.clone(), || {
1497 Ok((
1498 b,
1499 Bounds(
1500 BigInt::one() - (BigInt::one() << size),
1501 (BigInt::one() << size) - BigInt::one(),
1502 ),
1503 ))
1504 })?;
1505 let ab_var = EmulatedIntVar::new_witness(cs.clone(), || {
1506 Ok((
1507 ab,
1508 Bounds(
1509 BigInt::one() - (BigInt::one() << (size * 2)),
1510 (BigInt::one() << (size * 2)) - BigInt::one(),
1511 ),
1512 ))
1513 })?;
1514 let aab_var = EmulatedIntVar::new_witness(cs.clone(), || {
1515 Ok((
1516 aab,
1517 Bounds(
1518 BigInt::one() - (BigInt::one() << (size * 3)),
1519 (BigInt::one() << (size * 3)) - BigInt::one(),
1520 ),
1521 ))
1522 })?;
1523 let abb_var = EmulatedIntVar::new_witness(cs.clone(), || {
1524 Ok((
1525 abb,
1526 Bounds(
1527 BigInt::one() - (BigInt::one() << (size * 3)),
1528 (BigInt::one() << (size * 3)) - BigInt::one(),
1529 ),
1530 ))
1531 })?;
1532
1533 let neg_a_var = EmulatedFieldVar::constant(BigInt::zero()) - &a_var;
1534 let neg_b_var = EmulatedFieldVar::constant(BigInt::zero()) - &b_var;
1535 let neg_ab_var = EmulatedFieldVar::constant(BigInt::zero()) - &ab_var;
1536 let neg_aab_var = EmulatedFieldVar::constant(BigInt::zero()) - &aab_var;
1537 let neg_abb_var = EmulatedFieldVar::constant(BigInt::zero()) - &abb_var;
1538
1539 a_var
1540 .mul_unaligned(&b_var)?
1541 .enforce_equal_unaligned(&ab_var)?;
1542 neg_a_var
1543 .mul_unaligned(&neg_b_var)?
1544 .enforce_equal_unaligned(&ab_var)?;
1545 a_var
1546 .mul_unaligned(&neg_b_var)?
1547 .enforce_equal_unaligned(&neg_ab_var)?;
1548 neg_a_var
1549 .mul_unaligned(&b_var)?
1550 .enforce_equal_unaligned(&neg_ab_var)?;
1551
1552 a_var
1553 .mul_unaligned(&ab_var)?
1554 .enforce_equal_unaligned(&aab_var)?;
1555 neg_a_var
1556 .mul_unaligned(&neg_ab_var)?
1557 .enforce_equal_unaligned(&aab_var)?;
1558 a_var
1559 .mul_unaligned(&neg_ab_var)?
1560 .enforce_equal_unaligned(&neg_aab_var)?;
1561 neg_a_var
1562 .mul_unaligned(&ab_var)?
1563 .enforce_equal_unaligned(&neg_aab_var)?;
1564
1565 ab_var
1566 .mul_unaligned(&b_var)?
1567 .enforce_equal_unaligned(&abb_var)?;
1568 neg_ab_var
1569 .mul_unaligned(&neg_b_var)?
1570 .enforce_equal_unaligned(&abb_var)?;
1571 ab_var
1572 .mul_unaligned(&neg_b_var)?
1573 .enforce_equal_unaligned(&neg_abb_var)?;
1574 neg_ab_var
1575 .mul_unaligned(&b_var)?
1576 .enforce_equal_unaligned(&neg_abb_var)?;
1577
1578 assert!(cs.is_satisfied()?);
1579 Ok(())
1580 }
1581
1582 #[test]
1583 fn test_mul_fq() -> Result<(), Box<dyn Error>> {
1584 let cs = ConstraintSystem::<Fr>::new_ref();
1585
1586 let rng = &mut thread_rng();
1587 let a = Fq::rand(rng);
1588 let b = Fq::rand(rng);
1589 let ab = a * b;
1590 let aab = a * ab;
1591 let abb = ab * b;
1592
1593 let a_var = EmulatedFieldVar::<Fr, Fq>::new_witness(cs.clone(), || Ok(a))?;
1594 let b_var = EmulatedFieldVar::new_witness(cs.clone(), || Ok(b))?;
1595 let ab_var = EmulatedFieldVar::new_witness(cs.clone(), || Ok(ab))?;
1596 let aab_var = EmulatedFieldVar::new_witness(cs.clone(), || Ok(aab))?;
1597 let abb_var = EmulatedFieldVar::new_witness(cs.clone(), || Ok(abb))?;
1598
1599 let neg_a_var = EmulatedFieldVar::constant(BigInt::zero()) - &a_var;
1600 let neg_b_var = EmulatedFieldVar::constant(BigInt::zero()) - &b_var;
1601 let neg_ab_var = EmulatedFieldVar::constant(BigInt::zero()) - &ab_var;
1602 let neg_aab_var = EmulatedFieldVar::constant(BigInt::zero()) - &aab_var;
1603 let neg_abb_var = EmulatedFieldVar::constant(BigInt::zero()) - &abb_var;
1604
1605 a_var.mul_unaligned(&b_var)?.enforce_congruent(&ab_var)?;
1606 neg_a_var
1607 .mul_unaligned(&neg_b_var)?
1608 .enforce_congruent(&ab_var)?;
1609 a_var
1610 .mul_unaligned(&neg_b_var)?
1611 .enforce_congruent(&neg_ab_var)?;
1612 neg_a_var
1613 .mul_unaligned(&b_var)?
1614 .enforce_congruent(&neg_ab_var)?;
1615
1616 a_var.mul_unaligned(&ab_var)?.enforce_congruent(&aab_var)?;
1617 neg_a_var
1618 .mul_unaligned(&neg_ab_var)?
1619 .enforce_congruent(&aab_var)?;
1620 a_var
1621 .mul_unaligned(&neg_ab_var)?
1622 .enforce_congruent(&neg_aab_var)?;
1623 neg_a_var
1624 .mul_unaligned(&ab_var)?
1625 .enforce_congruent(&neg_aab_var)?;
1626
1627 ab_var.mul_unaligned(&b_var)?.enforce_congruent(&abb_var)?;
1628 neg_ab_var
1629 .mul_unaligned(&neg_b_var)?
1630 .enforce_congruent(&abb_var)?;
1631 ab_var
1632 .mul_unaligned(&neg_b_var)?
1633 .enforce_congruent(&neg_abb_var)?;
1634 neg_ab_var
1635 .mul_unaligned(&b_var)?
1636 .enforce_congruent(&neg_abb_var)?;
1637
1638 assert_eq!(a_var.mul_unaligned(&b_var)?.modulo()?.value()?, ab);
1639 assert_eq!(neg_a_var.mul_unaligned(&neg_b_var)?.modulo()?.value()?, ab);
1640 assert_eq!(a_var.mul_unaligned(&neg_b_var)?.modulo()?.value()?, -ab);
1641 assert_eq!(neg_a_var.mul_unaligned(&b_var)?.modulo()?.value()?, -ab);
1642
1643 assert_eq!(a_var.mul_unaligned(&ab_var)?.modulo()?.value()?, aab);
1644 assert_eq!(
1645 neg_a_var.mul_unaligned(&neg_ab_var)?.modulo()?.value()?,
1646 aab
1647 );
1648 assert_eq!(a_var.mul_unaligned(&neg_ab_var)?.modulo()?.value()?, -aab);
1649 assert_eq!(neg_a_var.mul_unaligned(&ab_var)?.modulo()?.value()?, -aab);
1650
1651 assert_eq!(ab_var.mul_unaligned(&b_var)?.modulo()?.value()?, abb);
1652 assert_eq!(
1653 neg_ab_var.mul_unaligned(&neg_b_var)?.modulo()?.value()?,
1654 abb
1655 );
1656 assert_eq!(ab_var.mul_unaligned(&neg_b_var)?.modulo()?.value()?, -abb);
1657 assert_eq!(neg_ab_var.mul_unaligned(&b_var)?.modulo()?.value()?, -abb);
1658
1659 assert!(cs.is_satisfied()?);
1660 Ok(())
1661 }
1662
1663 #[test]
1664 fn test_pow() -> Result<(), Box<dyn Error>> {
1665 let cs = ConstraintSystem::<Fr>::new_ref();
1666
1667 let rng = &mut thread_rng();
1668
1669 let a = Fq::rand(rng);
1670
1671 let a_var = EmulatedFieldVar::<Fr, Fq>::new_witness(cs.clone(), || Ok(a))?;
1672
1673 let mut r_var = a_var.clone();
1674 for _ in 0..16 {
1675 r_var = r_var.mul_unaligned(&r_var)?.modulo()?;
1676 }
1677 r_var = r_var.mul_unaligned(&a_var)?.modulo()?;
1678 assert_eq!(a.pow([65537u64]), r_var.value()?);
1679 assert!(cs.is_satisfied()?);
1680 Ok(())
1681 }
1682
1683 #[test]
1684 fn test_vec_vec_mul() -> Result<(), Box<dyn Error>> {
1685 let cs = ConstraintSystem::<Fr>::new_ref();
1686
1687 let len = 1000;
1688
1689 let rng = &mut thread_rng();
1690 let a = (0..len).map(|_| Fq::rand(rng)).collect::<Vec<Fq>>();
1691 let b = (0..len).map(|_| Fq::rand(rng)).collect::<Vec<Fq>>();
1692
1693 let a_var = Vec::<EmulatedFieldVar<Fr, Fq>>::new_witness(cs.clone(), || Ok(&a[..]))?;
1694 let b_var = Vec::<EmulatedFieldVar<Fr, Fq>>::new_witness(cs.clone(), || Ok(&b[..]))?;
1695
1696 let mut c = Fq::zero();
1697 let mut r_var: LimbedVar<Fr, Fq, false> =
1698 EmulatedFieldVar::constant(BigUint::zero().into()).into();
1699 for i in 0..len {
1700 c += a[i] * b[i];
1701 r_var = r_var.add_unaligned(&a_var[i].mul_unaligned(&b_var[i])?)?;
1702 }
1703 let c_var = EmulatedFieldVar::new_witness(cs.clone(), || Ok(c))?;
1704 r_var.enforce_congruent(&c_var)?;
1705
1706 assert!(cs.is_satisfied()?);
1707 Ok(())
1708 }
1709}