Skip to main content

ark_r1cs_std/fields/fp/
mod.rs

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/// Represents a variable in the constraint system whose
13/// value can be an arbitrary field element.
14#[derive(Debug, Clone)]
15#[must_use]
16pub struct AllocatedFp<F: PrimeField> {
17    pub(crate) value: Option<F>,
18    /// The allocated variable corresponding to `self` in `self.cs`.
19    pub variable: Variable,
20    /// The constraint system that `self` was allocated in.
21    pub cs: ConstraintSystemRef<F>,
22}
23
24impl<F: PrimeField> AllocatedFp<F> {
25    /// Constructs a new `AllocatedFp` from a (optional) value, a low-level
26    /// Variable, and a `ConstraintSystemRef`.
27    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/// Represent variables corresponding to a field element in `F`.
37#[derive(Clone, Debug)]
38#[must_use]
39pub enum FpVar<F: PrimeField> {
40    /// Represents a constant in the constraint system, which means that
41    /// it does not have a corresponding variable.
42    Constant(F),
43    /// Represents an allocated variable constant in the constraint system.
44    Var(AllocatedFp<F>),
45}
46
47impl<F: PrimeField> FpVar<F> {
48    /// Decomposes `self` into a vector of `bits` and a remainder `rest` such
49    /// that
50    /// * `bits.len() == size`, and
51    /// * `rest == 0`.
52    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            // `other` is a variable
101            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    /// Constructs `Self` from a `Boolean`: if `other` is false, this outputs
123    /// `zero`, else it outputs `one`.
124    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    /// Returns the value assigned to `self` in the underlying constraint system
131    /// (if a value was assigned).
132    pub fn value(&self) -> Result<F, SynthesisError> {
133        self.value.ok_or(SynthesisError::AssignmentMissing)
134    }
135
136    /// Outputs `self + other`.
137    ///
138    /// This does not create any constraints.
139    #[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    /// Add many allocated Fp elements together.
154    ///
155    /// This does not create any constraints and only creates one linear
156    /// combination.
157    ///
158    /// Returns `None` if you pass an empty iterator.
159    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; // No elements to add
178        }
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    /// Computes the inner product of two iterators of `AllocatedFp` elements.
199    ///
200    ///
201    /// This does not create any constraints and only creates one linear
202    /// combination.
203    ///
204    /// # Panics
205    ///
206    /// Panics if the iterators are of different lengths.
207    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; // No elements to add
232        }
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                // sorts and compacts
241                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    /// Computes the inner product of two iterators of `AllocatedFp` elements.
254    ///
255    ///
256    /// This does not create any constraints and only creates one linear
257    /// combination.
258    ///
259    /// # Panics
260    ///
261    /// Panics if the iterators are of different lengths.
262    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                // both v1 and v2 should be constants
286                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                // v1 should be a constant
293                let v1 = v1.value?;
294                new_lc.push((v1, v2.variable));
295            } else if v2.cs.is_none() {
296                // v2 should be a constant
297                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; // No elements to compute the inner product
307        }
308        let variable = cs
309            .new_lc(|| {
310                let mut lc = LinearCombination(new_lc);
311                // sorts and compacts
312                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    /// Outputs `self - other`.
325    ///
326    /// This does not create any constraints.
327    #[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    /// Outputs `self * other`.
342    ///
343    /// This requires *one* constraint.
344    #[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    /// Output `self + other`
361    ///
362    /// This does not create any constraints.
363    #[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    /// Output `self - other`
378    ///
379    /// This does not create any constraints.
380    #[tracing::instrument(target = "gr1cs")]
381    pub fn sub_constant(&self, other: F) -> Self {
382        self.add_constant(-other)
383    }
384
385    /// Output `self * other`
386    ///
387    /// This does not create any constraints.
388    #[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    /// Output `self + self`
400    ///
401    /// This does not create any constraints.
402    #[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    /// Output `-self`
410    ///
411    /// This does not create any constraints.
412    #[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    /// Sets `self = -self`
420    ///
421    /// This does not create any constraints.
422    #[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    /// Outputs `self * self`
432    ///
433    /// This requires *one* constraint.
434    #[tracing::instrument(target = "gr1cs")]
435    pub fn square(&self) -> Result<Self, SynthesisError> {
436        Ok(self.mul(self))
437    }
438
439    /// Outputs `result` such that `result * self = 1`.
440    ///
441    /// This requires *one* constraint.
442    #[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    /// This is a no-op for prime fields.
457    #[tracing::instrument(target = "gr1cs")]
458    pub fn frobenius_map(&self, _: usize) -> Result<Self, SynthesisError> {
459        Ok(self.clone())
460    }
461
462    /// Enforces that `self * other = result`.
463    ///
464    /// This requires *one* constraint.
465    #[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    /// Enforces that `self * self = result`.
475    ///
476    /// This requires *one* constraint.
477    #[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    /// Outputs the bit `self == other`.
487    ///
488    /// This requires two constraints.
489    #[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    /// Outputs the bit `self != other`.
495    ///
496    /// This requires two constraints.
497    #[tracing::instrument(target = "gr1cs")]
498    pub fn is_neq(&self, other: &Self) -> Result<Boolean<F>, SynthesisError> {
499        // We don't need to enforce `is_not_equal` to be boolean here;
500        // see the comments above the constraints below for why.
501        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        // Completeness:
516        // Case 1: self != other:
517        // ----------------------
518        //   constraint 1:
519        //   (self - other) * multiplier = is_not_equal
520        //   => (non_zero) * multiplier = 1 (satisfied, because multiplier = 1/(self -
521        // other)
522        //
523        //   constraint 2:
524        //   (self - other) * not(is_not_equal) = 0
525        //   => (non_zero) * not(1) = 0
526        //   => (non_zero) * 0 = 0
527        //
528        // Case 2: self == other:
529        // ----------------------
530        //   constraint 1:
531        //   (self - other) * multiplier = is_not_equal
532        //   => 0 * multiplier = 0 (satisfied, because multiplier = 1
533        //
534        //   constraint 2:
535        //   (self - other) * not(is_not_equal) = 0
536        //   => 0 * not(0) = 0
537        //   => 0 * 1 = 0
538        //
539        // --------------------------------------------------------------------
540        //
541        // Soundness:
542        // Case 1: self != other, but is_not_equal != 1.
543        // --------------------------------------------
544        //   constraint 2:
545        //   (self - other) * not(is_not_equal) = 0
546        //   => (non_zero) * (1 - is_not_equal) = 0
547        //   => non_zero = 0 (contradiction) || 1 - is_not_equal = 0 (contradiction)
548        //
549        // Case 2: self == other, but is_not_equal != 0.
550        // --------------------------------------------
551        //   constraint 1:
552        //   (self - other) * multiplier = is_not_equal
553        //   0 * multiplier = is_not_equal != 0 (unsatisfiable)
554        //
555        // That is, constraint 1 enforces that if self == other, then `is_not_equal = 0`
556        // and constraint 2 enforces that if self != other, then `is_not_equal = 1`.
557        // Since these are the only possible two cases, `is_not_equal` is always
558        // constrained to 0 or 1.
559        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    /// Enforces that self == other if `should_enforce.is_eq(&Boolean::TRUE)`.
572    ///
573    /// This requires one constraint.
574    #[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    /// Enforces that self != other if `should_enforce.is_eq(&Boolean::TRUE)`.
588    ///
589    /// This requires one constraint.
590    #[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        // The high level logic is as follows:
597        // We want to check that self - other != 0. We do this by checking that
598        // (self - other).inverse() exists. In more detail, we check the following:
599        // If `should_enforce == true`, then we set `multiplier = (self -
600        // other).inverse()`, and check that (self - other) * multiplier == 1.
601        // (i.e., that the inverse exists)
602        //
603        // If `should_enforce == false`, then we set `multiplier == 0`, and check that
604        // (self - other) * 0 == 0, which is always satisfied.
605        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
624/// *************************************************************************
625/// *************************************************************************
626
627impl<F: PrimeField> ToBitsGadget<F> for AllocatedFp<F> {
628    /// Outputs the unique bit-wise decomposition of `self` in *little-endian*
629    /// form.
630    ///
631    /// This method enforces that the output is in the field, i.e.
632    /// it invokes `Boolean::enforce_in_field_le` on the bit decomposition.
633    #[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        // Convert to little-endian
658        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    /// Outputs the unique byte decomposition of `self` in *little-endian*
689    /// form.
690    ///
691    /// This method enforces that the decomposition represents
692    /// an integer that is less than `F::MODULUS`.
693    #[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                // a = self; b = other; c = cond;
745                //
746                // r = c * a + (1  - c) * b
747                // r = b + c * (a - b)
748                // c * (a - b) = r - b
749                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
761/// Uses two bits to perform a lookup into a table
762/// `b` is little-endian: `b[0]` is LSB.
763impl<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        // enforce y * (1 - 2 * b_2) == res
813        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    /// Enforce that `self * other == result`.
894    #[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            }, // this multiplication should be free
902            (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    /// Enforce that `self * self == result`.
912    #[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    /// Computes the inner product of two slices of `FpVar`.
940    /// This is faster for the `ConstraintSystem` to process as it directly creates
941    /// the minimal number of linear combinations.
942    #[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        // constants, linear_combinations, and variables separately
952        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                    // If both are constants, we can sum them directly
958                    sum_constants += *x * y;
959                    None
960                },
961                (FpVar::Constant(x), FpVar::Var(y)) | (FpVar::Var(y), FpVar::Constant(x)) => {
962                    // If one is a constant, we can treat it as a linear combination
963                    lc_vars.push(y);
964                    lc_coeffs.push(*x);
965                    None
966                },
967                // If both are variables, we keep them for the inner product
968                (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
1065/// *************************************************************************
1066/// *************************************************************************
1067
1068impl<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    /// Outputs the unique byte decomposition of `self` in *little-endian*
1141    /// form.
1142    #[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                        // cond * t + (1 - cond) * f
1186                        Ok(is.mul_constant(*t).add(&not.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
1206/// Uses two bits to perform a lookup into a table
1207/// `b` is little-endian: `b[0]` is LSB.
1208impl<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            // We only have constants
1240
1241            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        // Can't use `AllocatedFp::add_many` with an empty iterator: it panics.
1286        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        // Can't use `AllocatedFp::add_many` with an empty iterator: it panics.
1308        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}