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}