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