arnaucube
arnaucube/ark-r1cs-stdPublic Self-hosted repository
ark-r1cs-std/r1cs-std/src/fields/cubic_extension.rs
r1cs-std/src/fields/cubic_extension.rsRaw
  1use algebra::{
  2    fields::{CubicExtField, CubicExtParameters, Field},
  3    One, Zero,
  4};
  5use core::{borrow::Borrow, marker::PhantomData};
  6use r1cs_core::{ConstraintSystemRef, Namespace, SynthesisError};
  7
  8use crate::fields::fp::FpVar;
  9use crate::{
 10    fields::{FieldOpsBounds, FieldVar},
 11    prelude::*,
 12    Assignment, ToConstraintFieldGadget, Vec,
 13};
 14
 15#[derive(Derivative)]
 16#[derivative(Debug(bound = "BF: core::fmt::Debug"), Clone(bound = "BF: Clone"))]
 17#[must_use]
 18pub struct CubicExtVar<BF: FieldVar<P::BaseField, P::BasePrimeField>, P: CubicExtVarParams<BF>>
 19where
 20    for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>,
 21{
 22    pub c0: BF,
 23    pub c1: BF,
 24    pub c2: BF,
 25    #[derivative(Debug = "ignore")]
 26    _params: PhantomData<P>,
 27}
 28
 29pub trait CubicExtVarParams<BF: FieldVar<Self::BaseField, Self::BasePrimeField>>:
 30    CubicExtParameters
 31where
 32    for<'a> &'a BF: FieldOpsBounds<'a, Self::BaseField, BF>,
 33{
 34    fn mul_base_field_vars_by_frob_coeff(c1: &mut BF, c2: &mut BF, power: usize);
 35}
 36
 37impl<BF: FieldVar<P::BaseField, P::BasePrimeField>, P: CubicExtVarParams<BF>> CubicExtVar<BF, P>
 38where
 39    for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>,
 40{
 41    #[inline]
 42    pub fn new(c0: BF, c1: BF, c2: BF) -> Self {
 43        let _params = PhantomData;
 44        Self {
 45            c0,
 46            c1,
 47            c2,
 48            _params,
 49        }
 50    }
 51
 52    /// Multiply a BF by cubic nonresidue P::NONRESIDUE.
 53    #[inline]
 54    pub fn mul_base_field_by_nonresidue(fe: &BF) -> Result<BF, SynthesisError> {
 55        Ok(fe * P::NONRESIDUE)
 56    }
 57
 58    /// Multiply a CubicExtVar by an element of `P::BaseField`.
 59    #[inline]
 60    pub fn mul_by_base_field_constant(&self, fe: P::BaseField) -> Self {
 61        let c0 = &self.c0 * fe;
 62        let c1 = &self.c1 * fe;
 63        let c2 = &self.c2 * fe;
 64        Self::new(c0, c1, c2)
 65    }
 66
 67    #[inline]
 68    pub fn mul_assign_by_base_field_constant(&mut self, fe: P::BaseField) {
 69        *self = (&*self).mul_by_base_field_constant(fe);
 70    }
 71}
 72
 73impl<BF, P> R1CSVar<P::BasePrimeField> for CubicExtVar<BF, P>
 74where
 75    BF: FieldVar<P::BaseField, P::BasePrimeField>,
 76    for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>,
 77    P: CubicExtVarParams<BF>,
 78{
 79    type Value = CubicExtField<P>;
 80
 81    fn cs(&self) -> Option<ConstraintSystemRef<P::BasePrimeField>> {
 82        [&self.c0, &self.c1, &self.c2].cs()
 83    }
 84
 85    #[inline]
 86    fn value(&self) -> Result<Self::Value, SynthesisError> {
 87        match (self.c0.value(), self.c1.value(), self.c2.value()) {
 88            (Ok(c0), Ok(c1), Ok(c2)) => Ok(CubicExtField::new(c0, c1, c2)),
 89            (..) => Err(SynthesisError::AssignmentMissing),
 90        }
 91    }
 92}
 93
 94impl<BF, P> From<Boolean<P::BasePrimeField>> for CubicExtVar<BF, P>
 95where
 96    BF: FieldVar<P::BaseField, P::BasePrimeField>,
 97    for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>,
 98    P: CubicExtVarParams<BF>,
 99{
100    fn from(other: Boolean<P::BasePrimeField>) -> Self {
101        let c0 = BF::from(other);
102        let c1 = BF::zero();
103        let c2 = BF::zero();
104        Self::new(c0, c1, c2)
105    }
106}
107
108impl<'a, BF, P> FieldOpsBounds<'a, CubicExtField<P>, CubicExtVar<BF, P>> for CubicExtVar<BF, P>
109where
110    BF: FieldVar<P::BaseField, P::BasePrimeField>,
111    for<'b> &'b BF: FieldOpsBounds<'b, P::BaseField, BF>,
112    P: CubicExtVarParams<BF>,
113{
114}
115impl<'a, BF, P> FieldOpsBounds<'a, CubicExtField<P>, CubicExtVar<BF, P>> for &'a CubicExtVar<BF, P>
116where
117    BF: FieldVar<P::BaseField, P::BasePrimeField>,
118    for<'b> &'b BF: FieldOpsBounds<'b, P::BaseField, BF>,
119    P: CubicExtVarParams<BF>,
120{
121}
122
123impl<BF, P> FieldVar<CubicExtField<P>, P::BasePrimeField> for CubicExtVar<BF, P>
124where
125    BF: FieldVar<P::BaseField, P::BasePrimeField>,
126    for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>,
127    P: CubicExtVarParams<BF>,
128{
129    fn constant(other: CubicExtField<P>) -> Self {
130        let c0 = BF::constant(other.c0);
131        let c1 = BF::constant(other.c1);
132        let c2 = BF::constant(other.c2);
133        Self::new(c0, c1, c2)
134    }
135
136    fn zero() -> Self {
137        let c0 = BF::zero();
138        let c1 = BF::zero();
139        let c2 = BF::zero();
140        Self::new(c0, c1, c2)
141    }
142
143    fn one() -> Self {
144        let c0 = BF::one();
145        let c1 = BF::zero();
146        let c2 = BF::zero();
147        Self::new(c0, c1, c2)
148    }
149
150    #[inline]
151    #[tracing::instrument(target = "r1cs")]
152    fn double(&self) -> Result<Self, SynthesisError> {
153        let c0 = self.c0.double()?;
154        let c1 = self.c1.double()?;
155        let c2 = self.c2.double()?;
156        Ok(Self::new(c0, c1, c2))
157    }
158
159    #[inline]
160    #[tracing::instrument(target = "r1cs")]
161    fn negate(&self) -> Result<Self, SynthesisError> {
162        let mut result = self.clone();
163        result.c0.negate_in_place()?;
164        result.c1.negate_in_place()?;
165        result.c2.negate_in_place()?;
166        Ok(result)
167    }
168
169    /// Use the Chung-Hasan asymmetric squaring formula.
170    ///
171    /// (Devegili OhEig Scott Dahab --- Multiplication and Squaring on
172    /// Abstract Pairing-Friendly
173    /// Fields.pdf; Section 4 (CH-SQR2))
174    #[inline]
175    #[tracing::instrument(target = "r1cs")]
176    fn square(&self) -> Result<Self, SynthesisError> {
177        let a = self.c0.clone();
178        let b = self.c1.clone();
179        let c = self.c2.clone();
180
181        let s0 = a.square()?;
182        let ab = &a * &b;
183        let s1 = ab.double()?;
184        let s2 = (&a - &b + &c).square()?;
185        let s3 = (&b * &c).double()?;
186        let s4 = c.square()?;
187
188        let c0 = Self::mul_base_field_by_nonresidue(&s3)? + &s0;
189        let c1 = Self::mul_base_field_by_nonresidue(&s4)? + &s1;
190        let c2 = s1 + &s2 + &s3 - &s0 - &s4;
191
192        Ok(Self::new(c0, c1, c2))
193    }
194
195    #[tracing::instrument(target = "r1cs")]
196    fn mul_equals(&self, other: &Self, result: &Self) -> Result<(), SynthesisError> {
197        // Karatsuba multiplication for cubic extensions:
198        //     v0 = A.c0 * B.c0
199        //     v1 = A.c1 * B.c1
200        //     v2 = A.c2 * B.c2
201        //     result.c0 = v0 + β((a1 + a2)(b1 + b2) − v1 − v2)
202        //     result.c1 = (a0 + a1)(b0 + b1) − v0 − v1 + βv2
203        //     result.c2 = (a0 + a2)(b0 + b2) − v0 + v1 − v2,
204        // We enforce this with six constraints:
205        //
206        //     v0 = A.c0 * B.c0
207        //     v1 = A.c1 * B.c1
208        //     v2 = A.c2 * B.c2
209        //
210        //     result.c0 - v0 + \beta*(v1 + v2) = β(a1 + a2)(b1 + b2))
211        //     result.c1 + v0 + v1 - βv2        = (a0 + a1)(b0 + b1)
212        //     result.c2 + v0 - v1 + v2         = (a0 + a2)(b0 + b2)
213        // Reference:
214        // "Multiplication and Squaring on Pairing-Friendly Fields"
215        // Devegili, OhEigeartaigh, Scott, Dahab
216        //
217        // This implementation adapted from
218        // https://github.com/ZencashOfficial/ginger-lib/blob/development/r1cs/gadgets/std/src/fields/fp3.rs
219        let v0 = &self.c0 * &other.c0;
220        let v1 = &self.c1 * &other.c1;
221        let v2 = &self.c2 * &other.c2;
222
223        // Check c0
224        let nr_a1_plus_a2 = (&self.c1 + &self.c2) * P::NONRESIDUE;
225        let b1_plus_b2 = &other.c1 + &other.c2;
226        let nr_v1 = &v1 * P::NONRESIDUE;
227        let nr_v2 = &v2 * P::NONRESIDUE;
228        let to_check = &result.c0 - &v0 + &nr_v1 + &nr_v2;
229        nr_a1_plus_a2.mul_equals(&b1_plus_b2, &to_check)?;
230
231        // Check c1
232        let a0_plus_a1 = &self.c0 + &self.c1;
233        let b0_plus_b1 = &other.c0 + &other.c1;
234        let to_check = &result.c1 - &nr_v2 + &v0 + &v1;
235        a0_plus_a1.mul_equals(&b0_plus_b1, &to_check)?;
236
237        // Check c2
238        let a0_plus_a2 = &self.c0 + &self.c2;
239        let b0_plus_b2 = &other.c0 + &other.c2;
240        let to_check = &result.c2 + &v0 - &v1 + &v2;
241        a0_plus_a2.mul_equals(&b0_plus_b2, &to_check)?;
242        Ok(())
243    }
244
245    #[tracing::instrument(target = "r1cs")]
246    fn frobenius_map(&self, power: usize) -> Result<Self, SynthesisError> {
247        let mut result = self.clone();
248        result.c0.frobenius_map_in_place(power)?;
249        result.c1.frobenius_map_in_place(power)?;
250        result.c2.frobenius_map_in_place(power)?;
251
252        P::mul_base_field_vars_by_frob_coeff(&mut result.c1, &mut result.c2, power);
253        Ok(result)
254    }
255
256    #[tracing::instrument(target = "r1cs")]
257    fn inverse(&self) -> Result<Self, SynthesisError> {
258        let cs = self.cs().get()?.clone();
259        let one = Self::new_constant(cs.clone(), CubicExtField::one())?;
260
261        let inverse = Self::new_witness(self.cs().get()?.clone(), || {
262            self.value()
263                .map(|f| f.inverse().unwrap_or(CubicExtField::zero()))
264        })?;
265        self.mul_equals(&inverse, &one)?;
266        Ok(inverse)
267    }
268}
269
270impl_bounded_ops!(
271    CubicExtVar<BF, P>,
272    CubicExtField<P>,
273    Add,
274    add,
275    AddAssign,
276    add_assign,
277    |this: &'a CubicExtVar<BF, P>, other: &'a CubicExtVar<BF, P>| {
278        let c0 = &this.c0 + &other.c0;
279        let c1 = &this.c1 + &other.c1;
280        let c2 = &this.c2 + &other.c2;
281        CubicExtVar::new(c0, c1, c2)
282    },
283    |this: &'a CubicExtVar<BF, P>, other: CubicExtField<P>| {
284        this + CubicExtVar::constant(other)
285    },
286    (BF: FieldVar<P::BaseField, P::BasePrimeField>, P: CubicExtVarParams<BF>),
287    for<'b> &'b BF: FieldOpsBounds<'b, P::BaseField, BF>,
288);
289impl_bounded_ops!(
290    CubicExtVar<BF, P>,
291    CubicExtField<P>,
292    Sub,
293    sub,
294    SubAssign,
295    sub_assign,
296    |this: &'a CubicExtVar<BF, P>, other: &'a CubicExtVar<BF, P>| {
297        let c0 = &this.c0 - &other.c0;
298        let c1 = &this.c1 - &other.c1;
299        let c2 = &this.c2 - &other.c2;
300        CubicExtVar::new(c0, c1, c2)
301    },
302    |this: &'a CubicExtVar<BF, P>, other: CubicExtField<P>| {
303        this - CubicExtVar::constant(other)
304    },
305    (BF: FieldVar<P::BaseField, P::BasePrimeField>, P: CubicExtVarParams<BF>),
306    for<'b> &'b BF: FieldOpsBounds<'b, P::BaseField, BF>,
307);
308impl_bounded_ops!(
309    CubicExtVar<BF, P>,
310    CubicExtField<P>,
311    Mul,
312    mul,
313    MulAssign,
314    mul_assign,
315    |this: &'a CubicExtVar<BF, P>, other: &'a CubicExtVar<BF, P>| {
316        // Karatsuba multiplication for cubic extensions:
317        //     v0 = A.c0 * B.c0
318        //     v1 = A.c1 * B.c1
319        //     v2 = A.c2 * B.c2
320        //     result.c0 = v0 + β((a1 + a2)(b1 + b2) − v1 − v2)
321        //     result.c1 = (a0 + a1)(b0 + b1) − v0 − v1 + βv2
322        //     result.c2 = (a0 + a2)(b0 + b2) − v0 + v1 − v2,
323        //
324        // Reference:
325        // "Multiplication and Squaring on Pairing-Friendly Fields"
326        // Devegili, OhEigeartaigh, Scott, Dahab
327        let v0 = &this.c0 * &other.c0;
328        let v1 = &this.c1 * &other.c1;
329        let v2 = &this.c2 * &other.c2;
330        let c0 =
331            (((&this.c1 + &this.c2) * (&other.c1 + &other.c2) - &v1 - &v2) * P::NONRESIDUE) + &v0 ;
332        let c1 =
333            (&this.c0 + &this.c1) * (&other.c0 + &other.c1) - &v0 - &v1 + (&v2 * P::NONRESIDUE);
334        let c2 =
335            (&this.c0 + &this.c2) * (&other.c0 + &other.c2) - &v0 + &v1 - &v2;
336
337        CubicExtVar::new(c0, c1, c2)
338    },
339    |this: &'a CubicExtVar<BF, P>, other: CubicExtField<P>| {
340        this * CubicExtVar::constant(other)
341    },
342    (BF: FieldVar<P::BaseField, P::BasePrimeField>, P: CubicExtVarParams<BF>),
343    for<'b> &'b BF: FieldOpsBounds<'b, P::BaseField, BF>,
344);
345
346impl<BF, P> EqGadget<P::BasePrimeField> for CubicExtVar<BF, P>
347where
348    BF: FieldVar<P::BaseField, P::BasePrimeField>,
349    for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>,
350    P: CubicExtVarParams<BF>,
351{
352    #[tracing::instrument(target = "r1cs")]
353    fn is_eq(&self, other: &Self) -> Result<Boolean<P::BasePrimeField>, SynthesisError> {
354        let b0 = self.c0.is_eq(&other.c0)?;
355        let b1 = self.c1.is_eq(&other.c1)?;
356        let b2 = self.c2.is_eq(&other.c2)?;
357        b0.and(&b1)?.and(&b2)
358    }
359
360    #[inline]
361    #[tracing::instrument(target = "r1cs")]
362    fn conditional_enforce_equal(
363        &self,
364        other: &Self,
365        condition: &Boolean<P::BasePrimeField>,
366    ) -> Result<(), SynthesisError> {
367        self.c0.conditional_enforce_equal(&other.c0, condition)?;
368        self.c1.conditional_enforce_equal(&other.c1, condition)?;
369        self.c2.conditional_enforce_equal(&other.c2, condition)?;
370        Ok(())
371    }
372
373    #[inline]
374    #[tracing::instrument(target = "r1cs")]
375    fn conditional_enforce_not_equal(
376        &self,
377        other: &Self,
378        condition: &Boolean<P::BasePrimeField>,
379    ) -> Result<(), SynthesisError> {
380        let is_equal = self.is_eq(other)?;
381        is_equal
382            .and(condition)?
383            .enforce_equal(&Boolean::Constant(false))
384    }
385}
386
387impl<BF, P> ToBitsGadget<P::BasePrimeField> for CubicExtVar<BF, P>
388where
389    BF: FieldVar<P::BaseField, P::BasePrimeField>,
390    for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>,
391    P: CubicExtVarParams<BF>,
392{
393    #[tracing::instrument(target = "r1cs")]
394    fn to_bits_le(&self) -> Result<Vec<Boolean<P::BasePrimeField>>, SynthesisError> {
395        let mut c0 = self.c0.to_bits_le()?;
396        let mut c1 = self.c1.to_bits_le()?;
397        let mut c2 = self.c2.to_bits_le()?;
398        c0.append(&mut c1);
399        c0.append(&mut c2);
400        Ok(c0)
401    }
402
403    #[tracing::instrument(target = "r1cs")]
404    fn to_non_unique_bits_le(&self) -> Result<Vec<Boolean<P::BasePrimeField>>, SynthesisError> {
405        let mut c0 = self.c0.to_non_unique_bits_le()?;
406        let mut c1 = self.c1.to_non_unique_bits_le()?;
407        let mut c2 = self.c2.to_non_unique_bits_le()?;
408        c0.append(&mut c1);
409        c0.append(&mut c2);
410        Ok(c0)
411    }
412}
413
414impl<BF, P> ToBytesGadget<P::BasePrimeField> for CubicExtVar<BF, P>
415where
416    BF: FieldVar<P::BaseField, P::BasePrimeField>,
417    for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>,
418    P: CubicExtVarParams<BF>,
419{
420    #[tracing::instrument(target = "r1cs")]
421    fn to_bytes(&self) -> Result<Vec<UInt8<P::BasePrimeField>>, SynthesisError> {
422        let mut c0 = self.c0.to_bytes()?;
423        let mut c1 = self.c1.to_bytes()?;
424        let mut c2 = self.c2.to_bytes()?;
425        c0.append(&mut c1);
426        c0.append(&mut c2);
427
428        Ok(c0)
429    }
430
431    #[tracing::instrument(target = "r1cs")]
432    fn to_non_unique_bytes(&self) -> Result<Vec<UInt8<P::BasePrimeField>>, SynthesisError> {
433        let mut c0 = self.c0.to_non_unique_bytes()?;
434        let mut c1 = self.c1.to_non_unique_bytes()?;
435        let mut c2 = self.c2.to_non_unique_bytes()?;
436
437        c0.append(&mut c1);
438        c0.append(&mut c2);
439
440        Ok(c0)
441    }
442}
443
444impl<BF, P> ToConstraintFieldGadget<P::BasePrimeField> for CubicExtVar<BF, P>
445where
446    BF: FieldVar<P::BaseField, P::BasePrimeField>,
447    for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>,
448    P: CubicExtVarParams<BF>,
449    BF: ToConstraintFieldGadget<P::BasePrimeField>,
450{
451    #[tracing::instrument(target = "r1cs")]
452    fn to_constraint_field(&self) -> Result<Vec<FpVar<P::BasePrimeField>>, SynthesisError> {
453        let mut res = Vec::new();
454
455        res.extend_from_slice(&self.c0.to_constraint_field()?);
456        res.extend_from_slice(&self.c1.to_constraint_field()?);
457        res.extend_from_slice(&self.c2.to_constraint_field()?);
458
459        Ok(res)
460    }
461}
462
463impl<BF, P> CondSelectGadget<P::BasePrimeField> for CubicExtVar<BF, P>
464where
465    BF: FieldVar<P::BaseField, P::BasePrimeField>,
466    for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>,
467    P: CubicExtVarParams<BF>,
468{
469    #[inline]
470    #[tracing::instrument(target = "r1cs")]
471    fn conditionally_select(
472        cond: &Boolean<P::BasePrimeField>,
473        true_value: &Self,
474        false_value: &Self,
475    ) -> Result<Self, SynthesisError> {
476        let c0 = BF::conditionally_select(cond, &true_value.c0, &false_value.c0)?;
477        let c1 = BF::conditionally_select(cond, &true_value.c1, &false_value.c1)?;
478        let c2 = BF::conditionally_select(cond, &true_value.c2, &false_value.c2)?;
479        Ok(Self::new(c0, c1, c2))
480    }
481}
482
483impl<BF, P> TwoBitLookupGadget<P::BasePrimeField> for CubicExtVar<BF, P>
484where
485    BF: FieldVar<P::BaseField, P::BasePrimeField>
486        + TwoBitLookupGadget<P::BasePrimeField, TableConstant = P::BaseField>,
487    for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>,
488    P: CubicExtVarParams<BF>,
489{
490    type TableConstant = CubicExtField<P>;
491
492    #[tracing::instrument(target = "r1cs")]
493    fn two_bit_lookup(
494        b: &[Boolean<P::BasePrimeField>],
495        c: &[Self::TableConstant],
496    ) -> Result<Self, SynthesisError> {
497        let c0s = c.iter().map(|f| f.c0).collect::<Vec<_>>();
498        let c1s = c.iter().map(|f| f.c1).collect::<Vec<_>>();
499        let c2s = c.iter().map(|f| f.c2).collect::<Vec<_>>();
500        let c0 = BF::two_bit_lookup(b, &c0s)?;
501        let c1 = BF::two_bit_lookup(b, &c1s)?;
502        let c2 = BF::two_bit_lookup(b, &c2s)?;
503        Ok(Self::new(c0, c1, c2))
504    }
505}
506
507impl<BF, P> ThreeBitCondNegLookupGadget<P::BasePrimeField> for CubicExtVar<BF, P>
508where
509    BF: FieldVar<P::BaseField, P::BasePrimeField>
510        + ThreeBitCondNegLookupGadget<P::BasePrimeField, TableConstant = P::BaseField>,
511    for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>,
512    P: CubicExtVarParams<BF>,
513{
514    type TableConstant = CubicExtField<P>;
515
516    #[tracing::instrument(target = "r1cs")]
517    fn three_bit_cond_neg_lookup(
518        b: &[Boolean<P::BasePrimeField>],
519        b0b1: &Boolean<P::BasePrimeField>,
520        c: &[Self::TableConstant],
521    ) -> Result<Self, SynthesisError> {
522        let c0s = c.iter().map(|f| f.c0).collect::<Vec<_>>();
523        let c1s = c.iter().map(|f| f.c1).collect::<Vec<_>>();
524        let c2s = c.iter().map(|f| f.c2).collect::<Vec<_>>();
525        let c0 = BF::three_bit_cond_neg_lookup(b, b0b1, &c0s)?;
526        let c1 = BF::three_bit_cond_neg_lookup(b, b0b1, &c1s)?;
527        let c2 = BF::three_bit_cond_neg_lookup(b, b0b1, &c2s)?;
528        Ok(Self::new(c0, c1, c2))
529    }
530}
531
532impl<BF, P> AllocVar<CubicExtField<P>, P::BasePrimeField> for CubicExtVar<BF, P>
533where
534    BF: FieldVar<P::BaseField, P::BasePrimeField>,
535    for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>,
536    P: CubicExtVarParams<BF>,
537{
538    fn new_variable<T: Borrow<CubicExtField<P>>>(
539        cs: impl Into<Namespace<P::BasePrimeField>>,
540        f: impl FnOnce() -> Result<T, SynthesisError>,
541        mode: AllocationMode,
542    ) -> Result<Self, SynthesisError> {
543        let ns = cs.into();
544        let cs = ns.cs();
545
546        use SynthesisError::*;
547        let (c0, c1, c2) = match f() {
548            Ok(fe) => (Ok(fe.borrow().c0), Ok(fe.borrow().c1), Ok(fe.borrow().c2)),
549            Err(_) => (
550                Err(AssignmentMissing),
551                Err(AssignmentMissing),
552                Err(AssignmentMissing),
553            ),
554        };
555
556        let c0 = BF::new_variable(r1cs_core::ns!(cs, "c0"), || c0, mode)?;
557        let c1 = BF::new_variable(r1cs_core::ns!(cs, "c1"), || c1, mode)?;
558        let c2 = BF::new_variable(r1cs_core::ns!(cs, "c2"), || c2, mode)?;
559        Ok(Self::new(c0, c1, c2))
560    }
561}