1use ark_ff::{BigInteger, PrimeField};
2use ark_relations::gr1cs::{
3 ConstraintSystemRef, LinearCombination, Namespace, SynthesisError, Variable,
4};
5use ark_std::{borrow::Borrow, iter::Sum, vec::Vec};
6use itertools::zip_eq;
7
8use crate::{boolean::AllocatedBool, convert::ToConstraintFieldGadget, prelude::*, Assignment};
9
10mod cmp;
11
12#[derive(Debug, Clone)]
15#[must_use]
16pub struct AllocatedFp<F: PrimeField> {
17 pub(crate) value: Option<F>,
18 pub variable: Variable,
20 pub cs: ConstraintSystemRef<F>,
22}
23
24impl<F: PrimeField> AllocatedFp<F> {
25 pub fn new(value: Option<F>, variable: Variable, cs: ConstraintSystemRef<F>) -> Self {
28 Self {
29 value,
30 variable,
31 cs,
32 }
33 }
34}
35
36#[derive(Clone, Debug)]
38#[must_use]
39pub enum FpVar<F: PrimeField> {
40 Constant(F),
43 Var(AllocatedFp<F>),
45}
46
47impl<F: PrimeField> FpVar<F> {
48 pub fn to_bits_le_with_top_bits_zero(
53 &self,
54 size: usize,
55 ) -> Result<(Vec<Boolean<F>>, Self), SynthesisError> {
56 assert!(size < F::MODULUS_BIT_SIZE as usize);
57 let cs = self.cs();
58 let mode = if self.is_constant() {
59 AllocationMode::Constant
60 } else {
61 AllocationMode::Witness
62 };
63
64 let value = self.value().map(|f| f.into_bigint());
65 let lower_bits = (0..size)
66 .map(|i| {
67 Boolean::new_variable(cs.clone(), || value.map(|v| v.get_bit(i as usize)), mode)
68 })
69 .collect::<Result<Vec<_>, _>>()?;
70 let lower_bits_fp = Boolean::le_bits_to_fp(&lower_bits)?;
71 let rest = self - &lower_bits_fp;
72 rest.enforce_equal(&Self::zero())?;
73 Ok((lower_bits, rest))
74 }
75}
76
77impl<F: PrimeField> GR1CSVar<F> for FpVar<F> {
78 type Value = F;
79
80 fn cs(&self) -> ConstraintSystemRef<F> {
81 match self {
82 Self::Constant(_) => ConstraintSystemRef::None,
83 Self::Var(a) => a.cs.clone(),
84 }
85 }
86
87 fn value(&self) -> Result<Self::Value, SynthesisError> {
88 match self {
89 Self::Constant(v) => Ok(*v),
90 Self::Var(v) => v.value(),
91 }
92 }
93}
94
95impl<F: PrimeField> From<Boolean<F>> for FpVar<F> {
96 fn from(other: Boolean<F>) -> Self {
97 if let Boolean::Constant(b) = other {
98 Self::Constant(F::from(b as u8))
99 } else {
100 let cs = other.cs();
102 let variable = cs.new_lc(|| other.lc()).unwrap();
103 Self::Var(AllocatedFp::new(
104 other.value().ok().map(|b| F::from(b as u8)),
105 variable,
106 cs,
107 ))
108 }
109 }
110}
111
112impl<F: PrimeField> From<AllocatedFp<F>> for FpVar<F> {
113 fn from(other: AllocatedFp<F>) -> Self {
114 Self::Var(other)
115 }
116}
117
118impl<'a, F: PrimeField> FieldOpsBounds<'a, F, Self> for FpVar<F> {}
119impl<'a, F: PrimeField> FieldOpsBounds<'a, F, FpVar<F>> for &'a FpVar<F> {}
120
121impl<F: PrimeField> AllocatedFp<F> {
122 pub fn from(other: Boolean<F>) -> Self {
125 let cs = other.cs();
126 let variable = cs.new_lc(|| other.lc()).unwrap();
127 Self::new(other.value().ok().map(|b| F::from(b as u8)), variable, cs)
128 }
129
130 pub fn value(&self) -> Result<F, SynthesisError> {
133 self.value.ok_or(SynthesisError::AssignmentMissing)
134 }
135
136 #[tracing::instrument(target = "gr1cs")]
140 pub fn add(&self, other: &Self) -> Self {
141 let value = match (self.value, other.value) {
142 (Some(val1), Some(val2)) => Some(val1 + &val2),
143 (..) => None,
144 };
145
146 let variable = self
147 .cs
148 .new_lc(|| lc![self.variable, other.variable])
149 .unwrap();
150 AllocatedFp::new(value, variable, self.cs.clone())
151 }
152
153 pub fn add_many<B: Borrow<Self>>(iter: &[B]) -> Option<Self> {
160 let mut has_value = true;
161 let mut value = F::zero();
162 let mut cs = ConstraintSystemRef::None;
163
164 let mut num_iters = 0;
165
166 for variable in iter {
167 let variable = variable.borrow();
168 cs = cs.or(variable.cs.clone());
169 if variable.value.is_none() {
170 has_value = false;
171 } else {
172 value += variable.value.unwrap();
173 }
174 num_iters += 1;
175 }
176 if num_iters == 0 {
177 return None; }
179
180 let variable = cs
181 .new_lc(|| {
182 let lc = iter
183 .iter()
184 .map(|variable| (F::ONE, variable.borrow().variable))
185 .collect();
186 let mut lc = LinearCombination(lc);
187 lc.compactify();
188 lc
189 })
190 .unwrap();
191 if has_value {
192 Some(AllocatedFp::new(Some(value), variable, cs))
193 } else {
194 Some(AllocatedFp::new(None, variable, cs))
195 }
196 }
197
198 pub fn linear_combination<B1, B2, I1>(this: I1, other: &[B2]) -> Option<Self>
208 where
209 B1: Borrow<F>,
210 B2: Borrow<Self>,
211 I1: IntoIterator<Item = B1, IntoIter: Clone>,
212 {
213 let mut cs = ConstraintSystemRef::None;
214 let mut has_value = true;
215 let mut value = F::zero();
216
217 let mut num_iters = 0;
218 let zipped = zip_eq(this, other);
219 for (coeff, variable) in zipped.clone() {
220 let coeff = *coeff.borrow();
221 let variable = variable.borrow();
222 cs = cs.or(variable.cs.clone());
223 if variable.value.is_none() {
224 has_value = false;
225 } else {
226 value += coeff * variable.value.unwrap();
227 }
228 num_iters += 1;
229 }
230 if num_iters == 0 {
231 return None; }
233
234 let variable = cs
235 .new_lc(|| {
236 let lc = zipped
237 .map(|(coeff, variable)| (*coeff.borrow(), variable.borrow().variable))
238 .collect::<Vec<_>>();
239 let mut lc = LinearCombination(lc);
240 lc.compactify();
242 lc
243 })
244 .unwrap();
245
246 if has_value {
247 Some(AllocatedFp::new(Some(value), variable, cs))
248 } else {
249 Some(AllocatedFp::new(None, variable, cs))
250 }
251 }
252
253 pub fn inner_product<B1, B2, I1, I2>(this: I1, other: I2) -> Option<Self>
263 where
264 B1: Borrow<Self>,
265 B2: Borrow<Self>,
266 I1: IntoIterator<Item = B1>,
267 I2: IntoIterator<Item = B2>,
268 {
269 let mut cs = ConstraintSystemRef::None;
270 let mut has_value = true;
271 let mut value = F::zero();
272 let this = this.into_iter();
273 let mut new_lc = Vec::with_capacity(this.size_hint().0);
274
275 let mut num_iters = 0;
276 for (v1, v2) in zip_eq(this, other) {
277 let v1 = v1.borrow();
278 let v2 = v2.borrow();
279 cs = cs.or(v1.cs.clone()).or(v2.cs.clone());
280 match (v1.value, v2.value) {
281 (Some(val1), Some(val2)) => value += val1 * val2,
282 (..) => has_value = false,
283 }
284 if v1.cs.is_none() && v2.cs.is_none() {
285 let v1 = v1.value?;
287 let v2 = v2.value?;
288 let product = v1 * v2;
289 new_lc.push((product, Variable::One));
290 }
291 if v1.cs.is_none() {
292 let v1 = v1.value?;
294 new_lc.push((v1, v2.variable));
295 } else if v2.cs.is_none() {
296 let v2 = v2.value?;
298 new_lc.push((v2, v1.variable));
299 } else {
300 let product = v1.mul(v2);
301 new_lc.push((F::ONE, product.variable));
302 }
303 num_iters += 1;
304 }
305 if num_iters == 0 {
306 return None; }
308 let variable = cs
309 .new_lc(|| {
310 let mut lc = LinearCombination(new_lc);
311 lc.compactify();
313 lc
314 })
315 .unwrap();
316
317 if has_value {
318 Some(AllocatedFp::new(Some(value), variable, cs))
319 } else {
320 Some(AllocatedFp::new(None, variable, cs))
321 }
322 }
323
324 #[tracing::instrument(target = "gr1cs")]
328 pub fn sub(&self, other: &Self) -> Self {
329 let value = match (self.value, other.value) {
330 (Some(val1), Some(val2)) => Some(val1 - &val2),
331 (..) => None,
332 };
333
334 let variable = self
335 .cs
336 .new_lc(|| lc_diff![self.variable, other.variable])
337 .unwrap();
338 AllocatedFp::new(value, variable, self.cs.clone())
339 }
340
341 #[tracing::instrument(target = "gr1cs")]
345 pub fn mul(&self, other: &Self) -> Self {
346 let product = AllocatedFp::new_witness(self.cs.clone(), || {
347 Ok(self.value.get()? * &other.value.get()?)
348 })
349 .unwrap();
350 self.cs
351 .enforce_r1cs_constraint(
352 || self.variable.into(),
353 || other.variable.into(),
354 || product.variable.into(),
355 )
356 .unwrap();
357 product
358 }
359
360 #[tracing::instrument(target = "gr1cs")]
364 pub fn add_constant(&self, other: F) -> Self {
365 if other.is_zero() {
366 self.clone()
367 } else {
368 let value = self.value.map(|val| val + other);
369 let variable = self
370 .cs
371 .new_lc(|| lc![(F::ONE, self.variable), (other, Variable::One)])
372 .unwrap();
373 AllocatedFp::new(value, variable, self.cs.clone())
374 }
375 }
376
377 #[tracing::instrument(target = "gr1cs")]
381 pub fn sub_constant(&self, other: F) -> Self {
382 self.add_constant(-other)
383 }
384
385 #[tracing::instrument(target = "gr1cs")]
389 pub fn mul_constant(&self, other: F) -> Self {
390 if other.is_one() {
391 self.clone()
392 } else {
393 let value = self.value.map(|val| val * other);
394 let variable = self.cs.new_lc(|| (other, self.variable).into()).unwrap();
395 AllocatedFp::new(value, variable, self.cs.clone())
396 }
397 }
398
399 #[tracing::instrument(target = "gr1cs")]
403 pub fn double(&self) -> Result<Self, SynthesisError> {
404 let value = self.value.map(|val| val.double());
405 let variable = self.cs.new_lc(|| (F::ONE.double(), self.variable).into())?;
406 Ok(Self::new(value, variable, self.cs.clone()))
407 }
408
409 #[tracing::instrument(target = "gr1cs")]
413 pub fn negate(&self) -> Self {
414 let mut result = self.clone();
415 result.negate_in_place();
416 result
417 }
418
419 #[tracing::instrument(target = "gr1cs")]
423 pub fn negate_in_place(&mut self) -> &mut Self {
424 if let Some(val) = self.value.as_mut() {
425 *val = -(*val);
426 }
427 self.variable = self.cs.new_lc(|| lc!() - self.variable).unwrap();
428 self
429 }
430
431 #[tracing::instrument(target = "gr1cs")]
435 pub fn square(&self) -> Result<Self, SynthesisError> {
436 Ok(self.mul(self))
437 }
438
439 #[tracing::instrument(target = "gr1cs")]
443 pub fn inverse(&self) -> Result<Self, SynthesisError> {
444 let inverse = Self::new_witness(self.cs.clone(), || {
445 Ok(self.value.get()?.inverse().unwrap_or(F::ZERO))
446 })?;
447
448 self.cs.enforce_r1cs_constraint(
449 || self.variable.into(),
450 || inverse.variable.into(),
451 || Variable::One.into(),
452 )?;
453 Ok(inverse)
454 }
455
456 #[tracing::instrument(target = "gr1cs")]
458 pub fn frobenius_map(&self, _: usize) -> Result<Self, SynthesisError> {
459 Ok(self.clone())
460 }
461
462 #[tracing::instrument(target = "gr1cs")]
466 pub fn mul_equals(&self, other: &Self, result: &Self) -> Result<(), SynthesisError> {
467 self.cs.enforce_r1cs_constraint(
468 || self.variable.into(),
469 || other.variable.into(),
470 || result.variable.into(),
471 )
472 }
473
474 #[tracing::instrument(target = "gr1cs")]
478 pub fn square_equals(&self, result: &Self) -> Result<(), SynthesisError> {
479 self.cs.enforce_r1cs_constraint(
480 || self.variable.into(),
481 || self.variable.into(),
482 || result.variable.into(),
483 )
484 }
485
486 #[tracing::instrument(target = "gr1cs")]
490 pub fn is_eq(&self, other: &Self) -> Result<Boolean<F>, SynthesisError> {
491 self.is_neq(other).map(core::ops::Not::not)
492 }
493
494 #[tracing::instrument(target = "gr1cs")]
498 pub fn is_neq(&self, other: &Self) -> Result<Boolean<F>, SynthesisError> {
499 let is_not_equal = Boolean::from(AllocatedBool::new_witness_without_booleanity_check(
502 self.cs.clone(),
503 || Ok(self.value.get()? != other.value.get()?),
504 )?);
505 let multiplier = self.cs.new_witness_variable(|| {
506 let self_value = self.value.get()?;
507 let other_value = other.value.get()?;
508 if self_value != other_value {
509 Ok((self_value - other_value).inverse().unwrap_or(F::ZERO))
510 } else {
511 Ok(F::one())
512 }
513 })?;
514
515 let difference = self.cs.new_lc(|| lc_diff![self.variable, other.variable])?;
560 self.cs.enforce_r1cs_constraint(
561 || difference.into(),
562 || multiplier.into(),
563 || is_not_equal.lc(),
564 )?;
565 let is_equal = !&is_not_equal;
566 self.cs
567 .enforce_r1cs_constraint(|| difference.into(), || is_equal.lc(), || lc!())?;
568 Ok(is_not_equal)
569 }
570
571 #[tracing::instrument(target = "gr1cs")]
575 pub fn conditional_enforce_equal(
576 &self,
577 other: &Self,
578 should_enforce: &Boolean<F>,
579 ) -> Result<(), SynthesisError> {
580 self.cs.enforce_r1cs_constraint(
581 || lc_diff![self.variable, other.variable],
582 || should_enforce.lc(),
583 || lc!(),
584 )
585 }
586
587 #[tracing::instrument(target = "gr1cs")]
591 pub fn conditional_enforce_not_equal(
592 &self,
593 other: &Self,
594 should_enforce: &Boolean<F>,
595 ) -> Result<(), SynthesisError> {
596 let multiplier = Self::new_witness(self.cs.clone(), || {
606 if should_enforce.value()? {
607 Ok((self.value.get()? - other.value.get()?)
608 .inverse()
609 .unwrap_or(F::ZERO))
610 } else {
611 Ok(F::zero())
612 }
613 })?;
614
615 self.cs.enforce_r1cs_constraint(
616 || lc_diff![self.variable, other.variable],
617 || multiplier.variable.into(),
618 || should_enforce.lc(),
619 )?;
620 Ok(())
621 }
622}
623
624impl<F: PrimeField> ToBitsGadget<F> for AllocatedFp<F> {
628 #[tracing::instrument(target = "gr1cs")]
634 fn to_bits_le(&self) -> Result<Vec<Boolean<F>>, SynthesisError> {
635 let bits = self.to_non_unique_bits_le()?;
636 Boolean::enforce_in_field_le(&bits)?;
637 Ok(bits)
638 }
639
640 #[tracing::instrument(target = "gr1cs")]
641 fn to_non_unique_bits_le(&self) -> Result<Vec<Boolean<F>>, SynthesisError> {
642 let cs = self.cs.clone();
643 use ark_ff::BitIteratorBE;
644 let mut bits = if let Some(value) = self.value {
645 let field_char = BitIteratorBE::new(F::characteristic());
646 let bits: Vec<_> = BitIteratorBE::new(value.into_bigint())
647 .zip(field_char)
648 .skip_while(|(_, c)| !c)
649 .map(|(b, _)| Some(b))
650 .collect();
651 assert_eq!(bits.len(), F::MODULUS_BIT_SIZE as usize);
652 bits
653 } else {
654 vec![None; F::MODULUS_BIT_SIZE as usize]
655 };
656
657 bits.reverse();
659
660 let bits: Vec<_> = bits
661 .into_iter()
662 .map(|b| Boolean::new_witness(cs.clone(), || b.get()))
663 .collect::<Result<_, _>>()?;
664
665 let lc = || {
666 let mut coeff = F::one();
667 let lc = bits
668 .iter()
669 .map(|bit| {
670 let c = coeff;
671 coeff.double_in_place();
672 (c, bit.variable())
673 })
674 .chain([(-F::ONE, self.variable)])
675 .collect::<Vec<_>>();
676 let mut lc = LinearCombination(lc);
677 lc.compactify();
678 lc
679 };
680
681 cs.enforce_r1cs_constraint(|| lc!(), || lc!(), lc)?;
682
683 Ok(bits)
684 }
685}
686
687impl<F: PrimeField> ToBytesGadget<F> for AllocatedFp<F> {
688 #[tracing::instrument(target = "gr1cs")]
694 fn to_bytes_le(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
695 let num_bits = F::BigInt::NUM_LIMBS * 64;
696 let mut bits = self.to_bits_le()?;
697 let remainder = core::iter::repeat(Boolean::FALSE).take(num_bits - bits.len());
698 bits.extend(remainder);
699 let bytes = bits
700 .chunks(8)
701 .map(|chunk| UInt8::from_bits_le(chunk))
702 .collect();
703 Ok(bytes)
704 }
705
706 #[tracing::instrument(target = "gr1cs")]
707 fn to_non_unique_bytes_le(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
708 let num_bits = F::BigInt::NUM_LIMBS * 64;
709 let mut bits = self.to_non_unique_bits_le()?;
710 let remainder = core::iter::repeat(Boolean::FALSE).take(num_bits - bits.len());
711 bits.extend(remainder);
712 let bytes = bits
713 .chunks(8)
714 .map(|chunk| UInt8::from_bits_le(chunk))
715 .collect();
716 Ok(bytes)
717 }
718}
719
720impl<F: PrimeField> ToConstraintFieldGadget<F> for AllocatedFp<F> {
721 #[tracing::instrument(target = "gr1cs")]
722 fn to_constraint_field(&self) -> Result<Vec<FpVar<F>>, SynthesisError> {
723 Ok(vec![self.clone().into()])
724 }
725}
726
727impl<F: PrimeField> CondSelectGadget<F> for AllocatedFp<F> {
728 #[inline]
729 #[tracing::instrument(target = "gr1cs")]
730 fn conditionally_select(
731 cond: &Boolean<F>,
732 true_val: &Self,
733 false_val: &Self,
734 ) -> Result<Self, SynthesisError> {
735 match cond {
736 &Boolean::Constant(true) => Ok(true_val.clone()),
737 &Boolean::Constant(false) => Ok(false_val.clone()),
738 _ => {
739 let cs = cond.cs();
740 let result = Self::new_witness(cs.clone(), || {
741 cond.value()
742 .and_then(|c| if c { true_val } else { false_val }.value.get())
743 })?;
744 cs.enforce_r1cs_constraint(
750 || cond.lc(),
751 || lc_diff![true_val.variable, false_val.variable],
752 || lc_diff![result.variable, false_val.variable],
753 )?;
754
755 Ok(result)
756 },
757 }
758 }
759}
760
761impl<F: PrimeField> TwoBitLookupGadget<F> for AllocatedFp<F> {
764 type TableConstant = F;
765 #[tracing::instrument(target = "gr1cs")]
766 fn two_bit_lookup(b: &[Boolean<F>], c: &[Self::TableConstant]) -> Result<Self, SynthesisError> {
767 debug_assert_eq!(b.len(), 2);
768 debug_assert_eq!(c.len(), 4);
769 let result = Self::new_witness(b.cs(), || {
770 let lsb = usize::from(b[0].value()?);
771 let msb = usize::from(b[1].value()?);
772 let index = lsb + (msb << 1);
773 Ok(c[index])
774 })?;
775 let one = Variable::One;
776 b.cs().enforce_r1cs_constraint(
777 || b[1].lc() * (c[3] - &c[2] - &c[1] + &c[0]) + (c[1] - &c[0], one),
778 || b[0].lc(),
779 || lc!() + result.variable - (c[0], one) + b[1].lc() * (c[0] - &c[2]),
780 )?;
781
782 Ok(result)
783 }
784}
785
786impl<F: PrimeField> ThreeBitCondNegLookupGadget<F> for AllocatedFp<F> {
787 type TableConstant = F;
788
789 #[tracing::instrument(target = "gr1cs")]
790 fn three_bit_cond_neg_lookup(
791 b: &[Boolean<F>],
792 b0b1: &Boolean<F>,
793 c: &[Self::TableConstant],
794 ) -> Result<Self, SynthesisError> {
795 debug_assert_eq!(b.len(), 3);
796 debug_assert_eq!(c.len(), 4);
797 let result = Self::new_witness(b.cs(), || {
798 let lsb = usize::from(b[0].value()?);
799 let msb = usize::from(b[1].value()?);
800 let index = lsb + (msb << 1);
801 let intermediate = c[index];
802
803 let is_negative = b[2].value()?;
804 let y = if is_negative {
805 -intermediate
806 } else {
807 intermediate
808 };
809 Ok(y)
810 })?;
811
812 b.cs().enforce_r1cs_constraint(
814 || {
815 b0b1.lc() * (c[3] - &c[2] - &c[1] + &c[0])
816 + b[0].lc() * (c[1] - &c[0])
817 + b[1].lc() * (c[2] - &c[0])
818 + (c[0], Variable::One)
819 },
820 || b[2].lc() * F::from(2u64).neg() + (F::one(), Variable::One),
821 || result.variable.into(),
822 )?;
823
824 Ok(result)
825 }
826}
827
828impl<F: PrimeField> AllocVar<F, F> for AllocatedFp<F> {
829 fn new_variable<T: Borrow<F>>(
830 cs: impl Into<Namespace<F>>,
831 f: impl FnOnce() -> Result<T, SynthesisError>,
832 mode: AllocationMode,
833 ) -> Result<Self, SynthesisError> {
834 let ns = cs.into();
835 let cs = ns.cs();
836 if mode == AllocationMode::Constant {
837 let v = *f()?.borrow();
838 let lc = cs.new_lc(|| (v, Variable::One).into())?;
839 Ok(Self::new(Some(v), lc, cs))
840 } else {
841 let mut value = None;
842 let value_generator = || {
843 value = Some(*f()?.borrow());
844 value.ok_or(SynthesisError::AssignmentMissing)
845 };
846 let variable = if mode == AllocationMode::Input {
847 cs.new_input_variable(value_generator)?
848 } else {
849 cs.new_witness_variable(value_generator)?
850 };
851 Ok(Self::new(value, variable, cs))
852 }
853 }
854}
855
856impl<F: PrimeField> FieldVar<F, F> for FpVar<F> {
857 fn constant(f: F) -> Self {
858 Self::Constant(f)
859 }
860
861 fn zero() -> Self {
862 Self::Constant(F::zero())
863 }
864
865 fn one() -> Self {
866 Self::Constant(F::one())
867 }
868
869 #[tracing::instrument(target = "gr1cs")]
870 fn double(&self) -> Result<Self, SynthesisError> {
871 match self {
872 Self::Constant(c) => Ok(Self::Constant(c.double())),
873 Self::Var(v) => Ok(Self::Var(v.double()?)),
874 }
875 }
876
877 #[tracing::instrument(target = "gr1cs")]
878 fn negate(&self) -> Result<Self, SynthesisError> {
879 match self {
880 Self::Constant(c) => Ok(Self::Constant(-*c)),
881 Self::Var(v) => Ok(Self::Var(v.negate())),
882 }
883 }
884
885 #[tracing::instrument(target = "gr1cs")]
886 fn square(&self) -> Result<Self, SynthesisError> {
887 match self {
888 Self::Constant(c) => Ok(Self::Constant(c.square())),
889 Self::Var(v) => Ok(Self::Var(v.square()?)),
890 }
891 }
892
893 #[tracing::instrument(target = "gr1cs")]
895 fn mul_equals(&self, other: &Self, result: &Self) -> Result<(), SynthesisError> {
896 use FpVar::*;
897 match (self, other, result) {
898 (Constant(_), Constant(_), Constant(_)) => Ok(()),
899 (Constant(_), Constant(_), _) | (Constant(_), Var(_), _) | (Var(_), Constant(_), _) => {
900 result.enforce_equal(&(self * other))
901 }, (Var(v1), Var(v2), Var(v3)) => v1.mul_equals(v2, v3),
903 (Var(v1), Var(v2), Constant(f)) => {
904 let cs = v1.cs.clone();
905 let v3 = AllocatedFp::new_constant(cs, f).unwrap();
906 v1.mul_equals(v2, &v3)
907 },
908 }
909 }
910
911 #[tracing::instrument(target = "gr1cs")]
913 fn square_equals(&self, result: &Self) -> Result<(), SynthesisError> {
914 use FpVar::*;
915 match (self, result) {
916 (Constant(_), Constant(_)) => Ok(()),
917 (Constant(f), Var(r)) => {
918 let cs = r.cs.clone();
919 let v = AllocatedFp::new_witness(cs, || Ok(f))?;
920 v.square_equals(&r)
921 },
922 (Var(v), Constant(f)) => {
923 let cs = v.cs.clone();
924 let r = AllocatedFp::new_witness(cs, || Ok(f))?;
925 v.square_equals(&r)
926 },
927 (Var(v1), Var(v2)) => v1.square_equals(v2),
928 }
929 }
930
931 #[tracing::instrument(target = "gr1cs")]
932 fn inverse(&self) -> Result<Self, SynthesisError> {
933 match self {
934 FpVar::Var(v) => v.inverse().map(FpVar::Var),
935 FpVar::Constant(f) => f.inverse().get().map(FpVar::Constant),
936 }
937 }
938
939 #[tracing::instrument(target = "gr1cs")]
943 fn inner_product(this: &[Self], other: &[Self]) -> Result<Self, SynthesisError> {
944 if this.len() != other.len() {
945 return Err(SynthesisError::Unsatisfiable);
946 }
947
948 let mut lc_vars = vec![];
949 let mut lc_coeffs = vec![];
950 let mut sum_constants = F::zero();
951 let (vars_left, vars_right): (Vec<_>, Vec<_>) = this
953 .iter()
954 .zip(other)
955 .filter_map(|(x, y)| match (x, y) {
956 (FpVar::Constant(x), FpVar::Constant(y)) => {
957 sum_constants += *x * y;
959 None
960 },
961 (FpVar::Constant(x), FpVar::Var(y)) | (FpVar::Var(y), FpVar::Constant(x)) => {
962 lc_vars.push(y);
964 lc_coeffs.push(*x);
965 None
966 },
967 (FpVar::Var(x), FpVar::Var(y)) => Some((x, y)),
969 })
970 .unzip();
971 let sum_constants = FpVar::Constant(sum_constants);
972 let sum_lc = AllocatedFp::linear_combination(lc_coeffs, &lc_vars).map(FpVar::Var);
973 let sum_variables = AllocatedFp::inner_product(vars_left, vars_right).map(FpVar::Var);
974
975 match (sum_lc, sum_variables) {
976 (Some(a), Some(b)) => Ok(a + b + sum_constants),
977 (Some(a), None) | (None, Some(a)) => Ok(a + sum_constants),
978 (None, None) => Ok(sum_constants),
979 }
980 }
981
982 #[tracing::instrument(target = "gr1cs")]
983 fn frobenius_map(&self, power: usize) -> Result<Self, SynthesisError> {
984 match self {
985 FpVar::Var(v) => v.frobenius_map(power).map(FpVar::Var),
986 FpVar::Constant(f) => {
987 let mut f = *f;
988 f.frobenius_map_in_place(power);
989 Ok(FpVar::Constant(f))
990 },
991 }
992 }
993
994 #[tracing::instrument(target = "gr1cs")]
995 fn frobenius_map_in_place(&mut self, power: usize) -> Result<&mut Self, SynthesisError> {
996 *self = self.frobenius_map(power)?;
997 Ok(self)
998 }
999}
1000
1001impl_ops!(
1002 FpVar<F>,
1003 F,
1004 Add,
1005 add,
1006 AddAssign,
1007 add_assign,
1008 |this: &'a FpVar<F>, other: &'a FpVar<F>| {
1009 use FpVar::*;
1010 match (this, other) {
1011 (Constant(c1), Constant(c2)) => Constant(*c1 + *c2),
1012 (Constant(c), Var(v)) | (Var(v), Constant(c)) => Var(v.add_constant(*c)),
1013 (Var(v1), Var(v2)) => Var(v1.add(v2)),
1014 }
1015 },
1016 |this: &'a FpVar<F>, other: F| { this + &FpVar::Constant(other) },
1017 F: PrimeField,
1018);
1019
1020impl_ops!(
1021 FpVar<F>,
1022 F,
1023 Sub,
1024 sub,
1025 SubAssign,
1026 sub_assign,
1027 |this: &'a FpVar<F>, other: &'a FpVar<F>| {
1028 use FpVar::*;
1029 match (this, other) {
1030 (Constant(c1), Constant(c2)) => Constant(*c1 - *c2),
1031 (Var(v), Constant(c)) => Var(v.sub_constant(*c)),
1032 (Constant(c), Var(v)) => Var(v.sub_constant(*c).negate()),
1033 (Var(v1), Var(v2)) => Var(v1.sub(v2)),
1034 }
1035 },
1036 |this: &'a FpVar<F>, other: F| { this - &FpVar::Constant(other) },
1037 F: PrimeField
1038);
1039
1040impl_ops!(
1041 FpVar<F>,
1042 F,
1043 Mul,
1044 mul,
1045 MulAssign,
1046 mul_assign,
1047 |this: &'a FpVar<F>, other: &'a FpVar<F>| {
1048 use FpVar::*;
1049 match (this, other) {
1050 (Constant(c1), Constant(c2)) => Constant(*c1 * *c2),
1051 (Constant(c), Var(v)) | (Var(v), Constant(c)) => Var(v.mul_constant(*c)),
1052 (Var(v1), Var(v2)) => Var(v1.mul(v2)),
1053 }
1054 },
1055 |this: &'a FpVar<F>, other: F| {
1056 if other.is_zero() {
1057 FpVar::zero()
1058 } else {
1059 this * &FpVar::Constant(other)
1060 }
1061 },
1062 F: PrimeField
1063);
1064
1065impl<F: PrimeField> EqGadget<F> for FpVar<F> {
1069 #[tracing::instrument(target = "gr1cs")]
1070 fn is_eq(&self, other: &Self) -> Result<Boolean<F>, SynthesisError> {
1071 match (self, other) {
1072 (Self::Constant(c1), Self::Constant(c2)) => Ok(Boolean::Constant(c1 == c2)),
1073 (Self::Constant(c), Self::Var(v)) | (Self::Var(v), Self::Constant(c)) => {
1074 let cs = v.cs.clone();
1075 let c = AllocatedFp::new_constant(cs, c)?;
1076 c.is_eq(v)
1077 },
1078 (Self::Var(v1), Self::Var(v2)) => v1.is_eq(v2),
1079 }
1080 }
1081
1082 #[tracing::instrument(target = "gr1cs")]
1083 fn conditional_enforce_equal(
1084 &self,
1085 other: &Self,
1086 should_enforce: &Boolean<F>,
1087 ) -> Result<(), SynthesisError> {
1088 match (self, other) {
1089 (Self::Constant(_), Self::Constant(_)) => Ok(()),
1090 (Self::Constant(c), Self::Var(v)) | (Self::Var(v), Self::Constant(c)) => {
1091 let cs = v.cs.clone();
1092 let c = AllocatedFp::new_constant(cs, c)?;
1093 c.conditional_enforce_equal(v, should_enforce)
1094 },
1095 (Self::Var(v1), Self::Var(v2)) => v1.conditional_enforce_equal(v2, should_enforce),
1096 }
1097 }
1098
1099 #[tracing::instrument(target = "gr1cs")]
1100 fn conditional_enforce_not_equal(
1101 &self,
1102 other: &Self,
1103 should_enforce: &Boolean<F>,
1104 ) -> Result<(), SynthesisError> {
1105 match (self, other) {
1106 (Self::Constant(_), Self::Constant(_)) => Ok(()),
1107 (Self::Constant(c), Self::Var(v)) | (Self::Var(v), Self::Constant(c)) => {
1108 let cs = v.cs.clone();
1109 let c = AllocatedFp::new_constant(cs, c)?;
1110 c.conditional_enforce_not_equal(v, should_enforce)
1111 },
1112 (Self::Var(v1), Self::Var(v2)) => v1.conditional_enforce_not_equal(v2, should_enforce),
1113 }
1114 }
1115}
1116
1117impl<F: PrimeField> ToBitsGadget<F> for FpVar<F> {
1118 #[tracing::instrument(target = "gr1cs")]
1119 fn to_bits_le(&self) -> Result<Vec<Boolean<F>>, SynthesisError> {
1120 match self {
1121 Self::Constant(_) => self.to_non_unique_bits_le(),
1122 Self::Var(v) => v.to_bits_le(),
1123 }
1124 }
1125
1126 #[tracing::instrument(target = "gr1cs")]
1127 fn to_non_unique_bits_le(&self) -> Result<Vec<Boolean<F>>, SynthesisError> {
1128 use ark_ff::BitIteratorLE;
1129 match self {
1130 Self::Constant(c) => Ok(BitIteratorLE::new(&c.into_bigint())
1131 .take((F::MODULUS_BIT_SIZE) as usize)
1132 .map(Boolean::constant)
1133 .collect::<Vec<_>>()),
1134 Self::Var(v) => v.to_non_unique_bits_le(),
1135 }
1136 }
1137}
1138
1139impl<F: PrimeField> ToBytesGadget<F> for FpVar<F> {
1140 #[tracing::instrument(target = "gr1cs")]
1143 fn to_bytes_le(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
1144 match self {
1145 Self::Constant(c) => Ok(UInt8::constant_vec(
1146 c.into_bigint().to_bytes_le().as_slice(),
1147 )),
1148 Self::Var(v) => v.to_bytes_le(),
1149 }
1150 }
1151
1152 #[tracing::instrument(target = "gr1cs")]
1153 fn to_non_unique_bytes_le(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
1154 match self {
1155 Self::Constant(c) => Ok(UInt8::constant_vec(
1156 c.into_bigint().to_bytes_le().as_slice(),
1157 )),
1158 Self::Var(v) => v.to_non_unique_bytes_le(),
1159 }
1160 }
1161}
1162
1163impl<F: PrimeField> ToConstraintFieldGadget<F> for FpVar<F> {
1164 #[tracing::instrument(target = "gr1cs")]
1165 fn to_constraint_field(&self) -> Result<Vec<FpVar<F>>, SynthesisError> {
1166 Ok(vec![self.clone()])
1167 }
1168}
1169
1170impl<F: PrimeField> CondSelectGadget<F> for FpVar<F> {
1171 #[tracing::instrument(target = "gr1cs")]
1172 fn conditionally_select(
1173 cond: &Boolean<F>,
1174 true_value: &Self,
1175 false_value: &Self,
1176 ) -> Result<Self, SynthesisError> {
1177 match cond {
1178 &Boolean::Constant(true) => Ok(true_value.clone()),
1179 &Boolean::Constant(false) => Ok(false_value.clone()),
1180 _ => {
1181 match (true_value, false_value) {
1182 (Self::Constant(t), Self::Constant(f)) => {
1183 let is = AllocatedFp::from(cond.clone());
1184 let not = AllocatedFp::from(!cond);
1185 Ok(is.mul_constant(*t).add(¬.mul_constant(*f)).into())
1187 },
1188 (..) => {
1189 let cs = cond.cs();
1190 let true_value = match true_value {
1191 Self::Constant(f) => AllocatedFp::new_constant(cs.clone(), f)?,
1192 Self::Var(v) => v.clone(),
1193 };
1194 let false_value = match false_value {
1195 Self::Constant(f) => AllocatedFp::new_constant(cs, f)?,
1196 Self::Var(v) => v.clone(),
1197 };
1198 cond.select(&true_value, &false_value).map(Self::Var)
1199 },
1200 }
1201 },
1202 }
1203 }
1204}
1205
1206impl<F: PrimeField> TwoBitLookupGadget<F> for FpVar<F> {
1209 type TableConstant = F;
1210
1211 #[tracing::instrument(target = "gr1cs")]
1212 fn two_bit_lookup(b: &[Boolean<F>], c: &[Self::TableConstant]) -> Result<Self, SynthesisError> {
1213 debug_assert_eq!(b.len(), 2);
1214 debug_assert_eq!(c.len(), 4);
1215 if b.is_constant() {
1216 let lsb = usize::from(b[0].value()?);
1217 let msb = usize::from(b[1].value()?);
1218 let index = lsb + (msb << 1);
1219 Ok(Self::Constant(c[index]))
1220 } else {
1221 AllocatedFp::two_bit_lookup(b, c).map(Self::Var)
1222 }
1223 }
1224}
1225
1226impl<F: PrimeField> ThreeBitCondNegLookupGadget<F> for FpVar<F> {
1227 type TableConstant = F;
1228
1229 #[tracing::instrument(target = "gr1cs")]
1230 fn three_bit_cond_neg_lookup(
1231 b: &[Boolean<F>],
1232 b0b1: &Boolean<F>,
1233 c: &[Self::TableConstant],
1234 ) -> Result<Self, SynthesisError> {
1235 debug_assert_eq!(b.len(), 3);
1236 debug_assert_eq!(c.len(), 4);
1237
1238 if b.cs().or(b0b1.cs()).is_none() {
1239 let lsb = usize::from(b[0].value()?);
1242 let msb = usize::from(b[1].value()?);
1243 let index = lsb + (msb << 1);
1244 let intermediate = c[index];
1245
1246 let is_negative = b[2].value()?;
1247 let y = if is_negative {
1248 -intermediate
1249 } else {
1250 intermediate
1251 };
1252 Ok(Self::Constant(y))
1253 } else {
1254 AllocatedFp::three_bit_cond_neg_lookup(b, b0b1, c).map(Self::Var)
1255 }
1256 }
1257}
1258
1259impl<F: PrimeField> AllocVar<F, F> for FpVar<F> {
1260 fn new_variable<T: Borrow<F>>(
1261 cs: impl Into<Namespace<F>>,
1262 f: impl FnOnce() -> Result<T, SynthesisError>,
1263 mode: AllocationMode,
1264 ) -> Result<Self, SynthesisError> {
1265 if mode == AllocationMode::Constant {
1266 Ok(Self::Constant(*f()?.borrow()))
1267 } else {
1268 AllocatedFp::new_variable(cs, f, mode).map(Self::Var)
1269 }
1270 }
1271}
1272
1273impl<'a, F: PrimeField> Sum<&'a FpVar<F>> for FpVar<F> {
1274 fn sum<I: Iterator<Item = &'a FpVar<F>>>(iter: I) -> FpVar<F> {
1275 let mut sum_constants = F::zero();
1276 let variables: Vec<_> = iter
1277 .filter_map(|x| match x {
1278 FpVar::Constant(c) => {
1279 sum_constants += c;
1280 None
1281 },
1282 FpVar::Var(v) => Some(v),
1283 })
1284 .collect();
1285 if variables.is_empty() {
1287 return FpVar::Constant(sum_constants);
1288 }
1289 AllocatedFp::add_many(&variables).map_or(FpVar::Constant(sum_constants), |sum_vars| {
1290 FpVar::Var(sum_vars) + sum_constants
1291 })
1292 }
1293}
1294
1295impl<'a, F: PrimeField> Sum<FpVar<F>> for FpVar<F> {
1296 fn sum<I: Iterator<Item = FpVar<F>>>(iter: I) -> FpVar<F> {
1297 let mut sum_constants = F::zero();
1298 let variables: Vec<_> = iter
1299 .filter_map(|x| match x {
1300 FpVar::Constant(c) => {
1301 sum_constants += c;
1302 None
1303 },
1304 FpVar::Var(v) => Some(v),
1305 })
1306 .collect();
1307 if variables.is_empty() {
1309 return FpVar::Constant(sum_constants);
1310 }
1311 AllocatedFp::add_many(&variables).map_or(FpVar::Constant(sum_constants), |sum_vars| {
1312 FpVar::Var(sum_vars) + sum_constants
1313 })
1314 }
1315}
1316
1317#[cfg(test)]
1318mod test {
1319 use crate::{
1320 alloc::AllocVar,
1321 eq::EqGadget,
1322 fields::{fp::FpVar, FieldVar},
1323 test_utils::{combination, modes},
1324 GR1CSVar,
1325 };
1326 use ark_relations::gr1cs::ConstraintSystem;
1327 use ark_std::{UniformRand, Zero};
1328 use ark_test_curves::bls12_381::Fr;
1329
1330 #[test]
1331 fn test_inner_product() {
1332 let mut rng = ark_std::test_rng();
1333 let cs = ConstraintSystem::new_ref();
1334
1335 for (a_mode, b_mode) in combination(modes()) {
1336 let a = (0..10)
1337 .map(|_| FpVar::new_variable(cs.clone(), || Ok(Fr::rand(&mut rng)), a_mode).ok())
1338 .collect::<Option<Vec<_>>>()
1339 .unwrap();
1340 let b = (0..10)
1341 .map(|_| FpVar::new_variable(cs.clone(), || Ok(Fr::rand(&mut rng)), b_mode).ok())
1342 .collect::<Option<Vec<_>>>()
1343 .unwrap();
1344 let a = [a, b].concat();
1345 let b = a.iter().rev().cloned().collect::<Vec<_>>();
1346 let inner_product: FpVar<Fr> = FpVar::inner_product(&a, &b).unwrap();
1347 let mut expected = Fr::zero();
1348 for (x, y) in a.iter().zip(b) {
1349 expected += x.value().unwrap() * y.value().unwrap();
1350 }
1351 inner_product
1352 .enforce_equal(&FpVar::Constant(expected))
1353 .unwrap();
1354
1355 assert!(cs.is_satisfied().unwrap());
1356 }
1357 }
1358
1359 #[test]
1360 fn test_sum_fpvar() {
1361 let mut rng = ark_std::test_rng();
1362 let cs = ConstraintSystem::new_ref();
1363
1364 for (a_mode, b_mode) in combination(modes()) {
1365 let a = (0..10)
1366 .map(|_| FpVar::new_variable(cs.clone(), || Ok(Fr::rand(&mut rng)), a_mode).ok())
1367 .collect::<Option<Vec<_>>>()
1368 .unwrap();
1369 let b = (0..10)
1370 .map(|_| FpVar::new_variable(cs.clone(), || Ok(Fr::rand(&mut rng)), b_mode).ok())
1371 .collect::<Option<Vec<_>>>()
1372 .unwrap();
1373 let v = [a, b].concat();
1374 let sum: FpVar<Fr> = v.iter().sum();
1375
1376 let sum_expected = v.iter().map(|x| x.value().unwrap()).sum();
1377 sum.enforce_equal(&FpVar::Constant(sum_expected)).unwrap();
1378
1379 assert!(cs.is_satisfied().unwrap());
1380 assert_eq!(sum.value().unwrap(), sum_expected);
1381 }
1382 }
1383}