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