ark_r1cs_std/fields/
cubic_extension.rs

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