1use super::{
2 params::{get_params, OptimizationType},
3 reduce::{bigint_to_basefield, limbs_to_bigint, Reducer},
4 AllocatedMulResultVar,
5};
6use crate::{
7 convert::{ToBitsGadget, ToBytesGadget, ToConstraintFieldGadget},
8 fields::fp::FpVar,
9 prelude::*,
10};
11use ark_ff::{BigInteger, PrimeField};
12use ark_relations::{
13 ns,
14 r1cs::{
15 ConstraintSystemRef, Namespace, OptimizationGoal, Result as R1CSResult, SynthesisError,
16 },
17};
18use ark_std::{
19 borrow::Borrow,
20 cmp::{max, min},
21 marker::PhantomData,
22 vec,
23 vec::Vec,
24};
25
26#[derive(Debug)]
28#[must_use]
29pub struct AllocatedEmulatedFpVar<TargetF: PrimeField, BaseF: PrimeField> {
30 pub cs: ConstraintSystemRef<BaseF>,
32 pub limbs: Vec<FpVar<BaseF>>,
34 pub num_of_additions_over_normal_form: BaseF,
37 pub is_in_the_normal_form: bool,
41 #[doc(hidden)]
42 pub target_phantom: PhantomData<TargetF>,
43}
44
45impl<TargetF: PrimeField, BaseF: PrimeField> AllocatedEmulatedFpVar<TargetF, BaseF> {
46 pub fn cs(&self) -> ConstraintSystemRef<BaseF> {
48 self.cs.clone()
49 }
50
51 pub fn limbs_to_value(limbs: Vec<BaseF>, optimization_type: OptimizationType) -> TargetF {
53 let params = get_params(
54 TargetF::MODULUS_BIT_SIZE as usize,
55 BaseF::MODULUS_BIT_SIZE as usize,
56 optimization_type,
57 );
58
59 let base_repr = TargetF::ONE.into_bigint() << (params.bits_per_limb - 1) as u32;
63
64 let mut base = TargetF::from_bigint(base_repr).unwrap();
65 base.double_in_place();
66
67 let mut result = TargetF::zero();
68 let mut power = TargetF::one();
69
70 for limb in limbs.iter().rev() {
71 let mut val = TargetF::zero();
72 let mut cur = TargetF::one();
73
74 for bit in limb.into_bigint().to_bits_be().iter().rev() {
75 if *bit {
76 val += &cur;
77 }
78 cur.double_in_place();
79 }
80
81 result += &(val * power);
82 power *= &base;
83 }
84
85 result
86 }
87
88 pub fn value(&self) -> R1CSResult<TargetF> {
90 let mut limbs = Vec::new();
91 for limb in self.limbs.iter() {
92 limbs.push(limb.value()?);
93 }
94
95 Ok(Self::limbs_to_value(limbs, self.get_optimization_type()))
96 }
97
98 pub fn constant(cs: ConstraintSystemRef<BaseF>, value: TargetF) -> R1CSResult<Self> {
100 let optimization_type = match cs.optimization_goal() {
101 OptimizationGoal::None => OptimizationType::Constraints,
102 OptimizationGoal::Constraints => OptimizationType::Constraints,
103 OptimizationGoal::Weight => OptimizationType::Weight,
104 };
105
106 let limbs_value = Self::get_limbs_representations(&value, optimization_type)?;
107
108 let mut limbs = Vec::new();
109
110 for limb_value in limbs_value.iter() {
111 limbs.push(FpVar::<BaseF>::new_constant(ns!(cs, "limb"), limb_value)?);
112 }
113
114 Ok(Self {
115 cs,
116 limbs,
117 num_of_additions_over_normal_form: BaseF::zero(),
118 is_in_the_normal_form: true,
119 target_phantom: PhantomData,
120 })
121 }
122
123 pub fn one(cs: ConstraintSystemRef<BaseF>) -> R1CSResult<Self> {
125 Self::constant(cs, TargetF::one())
126 }
127
128 pub fn zero(cs: ConstraintSystemRef<BaseF>) -> R1CSResult<Self> {
130 Self::constant(cs, TargetF::zero())
131 }
132
133 #[tracing::instrument(target = "r1cs")]
135 pub fn add(&self, other: &Self) -> R1CSResult<Self> {
136 assert_eq!(self.get_optimization_type(), other.get_optimization_type());
137
138 let mut limbs = Vec::new();
139 for (this_limb, other_limb) in self.limbs.iter().zip(other.limbs.iter()) {
140 limbs.push(this_limb + other_limb);
141 }
142
143 let mut res = Self {
144 cs: self.cs(),
145 limbs,
146 num_of_additions_over_normal_form: self
147 .num_of_additions_over_normal_form
148 .add(&other.num_of_additions_over_normal_form)
149 .add(&BaseF::one()),
150 is_in_the_normal_form: false,
151 target_phantom: PhantomData,
152 };
153
154 Reducer::<TargetF, BaseF>::post_add_reduce(&mut res)?;
155 Ok(res)
156 }
157
158 #[tracing::instrument(target = "r1cs")]
160 pub fn add_constant(&self, other: &TargetF) -> R1CSResult<Self> {
161 let other_limbs = Self::get_limbs_representations(other, self.get_optimization_type())?;
162
163 let mut limbs = Vec::new();
164 for (this_limb, other_limb) in self.limbs.iter().zip(other_limbs.iter()) {
165 limbs.push(this_limb + *other_limb);
166 }
167
168 let mut res = Self {
169 cs: self.cs(),
170 limbs,
171 num_of_additions_over_normal_form: self
172 .num_of_additions_over_normal_form
173 .add(&BaseF::one()),
174 is_in_the_normal_form: false,
175 target_phantom: PhantomData,
176 };
177
178 Reducer::<TargetF, BaseF>::post_add_reduce(&mut res)?;
179
180 Ok(res)
181 }
182
183 #[tracing::instrument(target = "r1cs")]
185 pub fn sub_without_reduce(&self, other: &Self) -> R1CSResult<Self> {
186 assert_eq!(self.get_optimization_type(), other.get_optimization_type());
187
188 let params = get_params(
189 TargetF::MODULUS_BIT_SIZE as usize,
190 BaseF::MODULUS_BIT_SIZE as usize,
191 self.get_optimization_type(),
192 );
193
194 let mut surfeit = overhead!(other.num_of_additions_over_normal_form + BaseF::one()) + 1;
196 let mut other = other.clone();
197 if (surfeit + params.bits_per_limb > BaseF::MODULUS_BIT_SIZE as usize - 1)
198 || (surfeit
199 + (TargetF::MODULUS_BIT_SIZE as usize
200 - params.bits_per_limb * (params.num_limbs - 1))
201 > BaseF::MODULUS_BIT_SIZE as usize - 1)
202 {
203 Reducer::reduce(&mut other)?;
204 surfeit = overhead!(other.num_of_additions_over_normal_form + BaseF::ONE) + 1;
205 }
206
207 let mut pad_non_top_limb = BaseF::ONE.into_bigint();
209 let mut pad_top_limb = pad_non_top_limb;
210
211 pad_non_top_limb <<= (surfeit + params.bits_per_limb) as u32;
212 let pad_non_top_limb = BaseF::from_bigint(pad_non_top_limb).unwrap();
213
214 pad_top_limb <<= (surfeit + TargetF::MODULUS_BIT_SIZE as usize
215 - params.bits_per_limb * (params.num_limbs - 1)) as u32;
216 let pad_top_limb = BaseF::from_bigint(pad_top_limb).unwrap();
217
218 let mut pad_limbs = Vec::with_capacity(self.limbs.len());
219 pad_limbs.push(pad_top_limb);
220 for _ in 0..self.limbs.len() - 1 {
221 pad_limbs.push(pad_non_top_limb);
222 }
223
224 let pad_to_kp_gap = Self::limbs_to_value(pad_limbs, self.get_optimization_type()).neg();
226 let pad_to_kp_limbs =
227 Self::get_limbs_representations(&pad_to_kp_gap, self.get_optimization_type())?;
228
229 let mut limbs = Vec::with_capacity(self.limbs.len());
231 for (i, ((this_limb, other_limb), pad_to_kp_limb)) in self
232 .limbs
233 .iter()
234 .zip(&other.limbs)
235 .zip(&pad_to_kp_limbs)
236 .enumerate()
237 {
238 if i != 0 {
239 limbs.push(this_limb + pad_non_top_limb + *pad_to_kp_limb - other_limb);
240 } else {
241 limbs.push(this_limb + pad_top_limb + *pad_to_kp_limb - other_limb);
242 }
243 }
244
245 let result = AllocatedEmulatedFpVar::<TargetF, BaseF> {
246 cs: self.cs(),
247 limbs,
248 num_of_additions_over_normal_form: self.num_of_additions_over_normal_form
249 + (other.num_of_additions_over_normal_form + BaseF::one())
250 + (other.num_of_additions_over_normal_form + BaseF::one()),
251 is_in_the_normal_form: false,
252 target_phantom: PhantomData,
253 };
254
255 Ok(result)
256 }
257
258 #[tracing::instrument(target = "r1cs")]
260 pub fn sub(&self, other: &Self) -> R1CSResult<Self> {
261 assert_eq!(self.get_optimization_type(), other.get_optimization_type());
262
263 let mut result = self.sub_without_reduce(other)?;
264 Reducer::<TargetF, BaseF>::post_add_reduce(&mut result)?;
265 Ok(result)
266 }
267
268 #[tracing::instrument(target = "r1cs")]
270 pub fn sub_constant(&self, other: &TargetF) -> R1CSResult<Self> {
271 self.sub(&Self::constant(self.cs(), *other)?)
272 }
273
274 #[tracing::instrument(target = "r1cs")]
276 pub fn mul(&self, other: &Self) -> R1CSResult<Self> {
277 assert_eq!(self.get_optimization_type(), other.get_optimization_type());
278
279 self.mul_without_reduce(&other)?.reduce()
280 }
281
282 pub fn mul_constant(&self, other: &TargetF) -> R1CSResult<Self> {
284 self.mul(&Self::constant(self.cs(), *other)?)
285 }
286
287 #[tracing::instrument(target = "r1cs")]
289 pub fn negate(&self) -> R1CSResult<Self> {
290 Self::zero(self.cs())?.sub(self)
291 }
292
293 #[tracing::instrument(target = "r1cs")]
295 pub fn inverse(&self) -> R1CSResult<Self> {
296 let inverse = Self::new_witness(self.cs(), || {
297 Ok(self.value()?.inverse().unwrap_or_else(TargetF::zero))
298 })?;
299
300 let actual_result = self.clone().mul(&inverse)?;
301 actual_result.conditional_enforce_equal(&Self::one(self.cs())?, &Boolean::TRUE)?;
302 Ok(inverse)
303 }
304
305 pub fn get_limbs_representations(
309 elem: &TargetF,
310 optimization_type: OptimizationType,
311 ) -> R1CSResult<Vec<BaseF>> {
312 Self::get_limbs_representations_from_big_integer(&elem.into_bigint(), optimization_type)
313 }
314
315 pub fn get_limbs_representations_from_big_integer(
317 elem: &<TargetF as PrimeField>::BigInt,
318 optimization_type: OptimizationType,
319 ) -> R1CSResult<Vec<BaseF>> {
320 let params = get_params(
321 TargetF::MODULUS_BIT_SIZE as usize,
322 BaseF::MODULUS_BIT_SIZE as usize,
323 optimization_type,
324 );
325
326 let mut limbs: Vec<BaseF> = Vec::new();
328 let mut cur = *elem;
329 for _ in 0..params.num_limbs {
330 let cur_bits = cur.to_bits_be(); let cur_mod_r = <BaseF as PrimeField>::BigInt::from_bits_be(
332 &cur_bits[cur_bits.len() - params.bits_per_limb..],
333 ); limbs.push(BaseF::from_bigint(cur_mod_r).unwrap());
335 cur >>= params.bits_per_limb as u32;
336 }
337
338 limbs.reverse();
340
341 Ok(limbs)
342 }
343
344 #[tracing::instrument(target = "r1cs")]
349 pub fn mul_without_reduce(
350 &self,
351 other: &Self,
352 ) -> R1CSResult<AllocatedMulResultVar<TargetF, BaseF>> {
353 assert_eq!(self.get_optimization_type(), other.get_optimization_type());
354
355 let params = get_params(
356 TargetF::MODULUS_BIT_SIZE as usize,
357 BaseF::MODULUS_BIT_SIZE as usize,
358 self.get_optimization_type(),
359 );
360
361 let mut self_reduced = self.clone();
363 let mut other_reduced = other.clone();
364 Reducer::<TargetF, BaseF>::pre_mul_reduce(&mut self_reduced, &mut other_reduced)?;
365
366 let mut prod_limbs = Vec::new();
367 if self.get_optimization_type() == OptimizationType::Weight {
368 let zero = FpVar::<BaseF>::zero();
369
370 for _ in 0..2 * params.num_limbs - 1 {
371 prod_limbs.push(zero.clone());
372 }
373
374 for i in 0..params.num_limbs {
375 for j in 0..params.num_limbs {
376 prod_limbs[i + j] =
377 &prod_limbs[i + j] + (&self_reduced.limbs[i] * &other_reduced.limbs[j]);
378 }
379 }
380 } else {
381 let cs = self.cs().or(other.cs());
382
383 for z_index in 0..2 * params.num_limbs - 1 {
384 prod_limbs.push(FpVar::new_witness(ns!(cs, "limb product"), || {
385 let mut z_i = BaseF::zero();
386 for i in 0..=min(params.num_limbs - 1, z_index) {
387 let j = z_index - i;
388 if j < params.num_limbs {
389 z_i += &self_reduced.limbs[i]
390 .value()?
391 .mul(&other_reduced.limbs[j].value()?);
392 }
393 }
394
395 Ok(z_i)
396 })?);
397 }
398
399 for c in 0..(2 * params.num_limbs - 1) {
400 let c_pows: Vec<_> = (0..(2 * params.num_limbs - 1))
401 .map(|i| BaseF::from((c + 1) as u128).pow(&vec![i as u64]))
402 .collect();
403
404 let x = self_reduced
405 .limbs
406 .iter()
407 .zip(c_pows.iter())
408 .map(|(var, c_pow)| var * *c_pow)
409 .fold(FpVar::zero(), |sum, i| sum + i);
410
411 let y = other_reduced
412 .limbs
413 .iter()
414 .zip(c_pows.iter())
415 .map(|(var, c_pow)| var * *c_pow)
416 .fold(FpVar::zero(), |sum, i| sum + i);
417
418 let z = prod_limbs
419 .iter()
420 .zip(c_pows.iter())
421 .map(|(var, c_pow)| var * *c_pow)
422 .fold(FpVar::zero(), |sum, i| sum + i);
423
424 z.enforce_equal(&(x * y))?;
425 }
426 }
427
428 Ok(AllocatedMulResultVar {
429 cs: self.cs(),
430 limbs: prod_limbs,
431 prod_of_num_of_additions: (self_reduced.num_of_additions_over_normal_form
432 + BaseF::one())
433 * (other_reduced.num_of_additions_over_normal_form + BaseF::one()),
434 target_phantom: PhantomData,
435 })
436 }
437
438 pub(crate) fn frobenius_map(&self, _power: usize) -> R1CSResult<Self> {
439 Ok(self.clone())
440 }
441
442 pub(crate) fn conditional_enforce_equal(
443 &self,
444 other: &Self,
445 should_enforce: &Boolean<BaseF>,
446 ) -> R1CSResult<()> {
447 assert_eq!(self.get_optimization_type(), other.get_optimization_type());
448
449 let params = get_params(
450 TargetF::MODULUS_BIT_SIZE as usize,
451 BaseF::MODULUS_BIT_SIZE as usize,
452 self.get_optimization_type(),
453 );
454
455 let p_representations =
457 AllocatedEmulatedFpVar::<TargetF, BaseF>::get_limbs_representations_from_big_integer(
458 &<TargetF as PrimeField>::MODULUS,
459 self.get_optimization_type(),
460 )?;
461 let p_bigint = limbs_to_bigint(params.bits_per_limb, &p_representations);
462
463 let mut p_gadget_limbs = Vec::new();
464 for limb in p_representations.iter() {
465 p_gadget_limbs.push(FpVar::<BaseF>::Constant(*limb));
466 }
467 let p_gadget = AllocatedEmulatedFpVar::<TargetF, BaseF> {
468 cs: self.cs(),
469 limbs: p_gadget_limbs,
470 num_of_additions_over_normal_form: BaseF::one(),
471 is_in_the_normal_form: false,
472 target_phantom: PhantomData,
473 };
474
475 let cs = self.cs().or(other.cs()).or(should_enforce.cs());
477 let mut delta = self.sub_without_reduce(other)?;
478 delta = should_enforce.select(&delta, &Self::zero(cs.clone())?)?;
479
480 let k_gadget = FpVar::<BaseF>::new_witness(ns!(cs, "k"), || {
482 let mut delta_limbs_values = Vec::<BaseF>::new();
483 for limb in delta.limbs.iter() {
484 delta_limbs_values.push(limb.value()?);
485 }
486
487 let delta_bigint = limbs_to_bigint(params.bits_per_limb, &delta_limbs_values);
488
489 Ok(bigint_to_basefield::<BaseF>(&(delta_bigint / p_bigint)))
490 })?;
491
492 let surfeit = overhead!(delta.num_of_additions_over_normal_form + BaseF::one()) + 1;
493 Reducer::<TargetF, BaseF>::limb_to_bits(&k_gadget, surfeit)?;
494
495 let mut kp_gadget_limbs = Vec::new();
497 for limb in p_gadget.limbs.iter() {
498 kp_gadget_limbs.push(limb * &k_gadget);
499 }
500
501 Reducer::<TargetF, BaseF>::group_and_check_equality(
503 surfeit,
504 params.bits_per_limb,
505 params.bits_per_limb,
506 &delta.limbs,
507 &kp_gadget_limbs,
508 )?;
509
510 Ok(())
511 }
512
513 #[tracing::instrument(target = "r1cs")]
514 pub(crate) fn conditional_enforce_not_equal(
515 &self,
516 other: &Self,
517 should_enforce: &Boolean<BaseF>,
518 ) -> R1CSResult<()> {
519 assert_eq!(self.get_optimization_type(), other.get_optimization_type());
520
521 let cs = self.cs().or(other.cs()).or(should_enforce.cs());
522
523 let _ = should_enforce
524 .select(&self.sub(other)?, &Self::one(cs)?)?
525 .inverse()?;
526
527 Ok(())
528 }
529
530 pub(crate) fn get_optimization_type(&self) -> OptimizationType {
531 match self.cs().optimization_goal() {
532 OptimizationGoal::None => OptimizationType::Constraints,
533 OptimizationGoal::Constraints => OptimizationType::Constraints,
534 OptimizationGoal::Weight => OptimizationType::Weight,
535 }
536 }
537
538 fn new_variable_unchecked<T: Borrow<TargetF>>(
541 cs: impl Into<Namespace<BaseF>>,
542 f: impl FnOnce() -> Result<T, SynthesisError>,
543 mode: AllocationMode,
544 ) -> R1CSResult<Self> {
545 let ns = cs.into();
546 let cs = ns.cs();
547
548 let optimization_type = match cs.optimization_goal() {
549 OptimizationGoal::None => OptimizationType::Constraints,
550 OptimizationGoal::Constraints => OptimizationType::Constraints,
551 OptimizationGoal::Weight => OptimizationType::Weight,
552 };
553
554 let zero = TargetF::zero();
555
556 let elem = match f() {
557 Ok(t) => *(t.borrow()),
558 Err(_) => zero,
559 };
560 let elem_representations = Self::get_limbs_representations(&elem, optimization_type)?;
561 let mut limbs = Vec::new();
562
563 for limb in elem_representations.iter() {
564 limbs.push(FpVar::<BaseF>::new_variable(
565 ark_relations::ns!(cs, "alloc"),
566 || Ok(limb),
567 mode,
568 )?);
569 }
570
571 let num_of_additions_over_normal_form = if mode != AllocationMode::Witness {
572 BaseF::zero()
573 } else {
574 BaseF::one()
575 };
576
577 Ok(Self {
578 cs,
579 limbs,
580 num_of_additions_over_normal_form,
581 is_in_the_normal_form: mode != AllocationMode::Witness,
582 target_phantom: PhantomData,
583 })
584 }
585
586 fn enforce_in_range(&self, cs: impl Into<Namespace<BaseF>>) -> R1CSResult<Vec<Boolean<BaseF>>> {
591 let ns = cs.into();
592 let cs = ns.cs();
593 let optimization_type = match cs.optimization_goal() {
594 OptimizationGoal::None => OptimizationType::Constraints,
595 OptimizationGoal::Constraints => OptimizationType::Constraints,
596 OptimizationGoal::Weight => OptimizationType::Weight,
597 };
598 let params = get_params(
599 TargetF::MODULUS_BIT_SIZE as usize,
600 BaseF::MODULUS_BIT_SIZE as usize,
601 optimization_type,
602 );
603 let mut bits = Vec::new();
604 for limb in self.limbs.iter().rev().take(params.num_limbs - 1) {
605 bits.extend(
606 Reducer::<TargetF, BaseF>::limb_to_bits(limb, params.bits_per_limb)?
607 .into_iter()
608 .rev(),
609 );
610 }
611
612 bits.extend(
613 Reducer::<TargetF, BaseF>::limb_to_bits(
614 &self.limbs[0],
615 TargetF::MODULUS_BIT_SIZE as usize - (params.num_limbs - 1) * params.bits_per_limb,
616 )?
617 .into_iter()
618 .rev(),
619 );
620 Ok(bits)
621 }
622
623 pub fn new_witness_with_le_bits<T: Borrow<TargetF>>(
629 cs: impl Into<Namespace<BaseF>>,
630 f: impl FnOnce() -> Result<T, SynthesisError>,
631 ) -> R1CSResult<(Self, Vec<Boolean<BaseF>>)> {
632 let ns = cs.into();
633 let cs = ns.cs();
634 let this = Self::new_variable_unchecked(ns!(cs, "alloc"), f, AllocationMode::Witness)?;
635 let bits = this.enforce_in_range(ns!(cs, "bits"))?;
636 Ok((this, bits))
637 }
638}
639
640impl<TargetF: PrimeField, BaseF: PrimeField> ToBitsGadget<BaseF>
641 for AllocatedEmulatedFpVar<TargetF, BaseF>
642{
643 #[tracing::instrument(target = "r1cs")]
644 fn to_bits_le(&self) -> R1CSResult<Vec<Boolean<BaseF>>> {
645 let params = get_params(
646 TargetF::MODULUS_BIT_SIZE as usize,
647 BaseF::MODULUS_BIT_SIZE as usize,
648 self.get_optimization_type(),
649 );
650
651 let mut self_normal = self.clone();
654 Reducer::<TargetF, BaseF>::pre_eq_reduce(&mut self_normal)?;
655
656 let mut bits = Vec::<Boolean<BaseF>>::new();
658 for limb in self_normal.limbs.iter() {
659 bits.extend_from_slice(&Reducer::<TargetF, BaseF>::limb_to_bits(
660 &limb,
661 params.bits_per_limb,
662 )?);
663 }
664 bits.reverse();
665
666 let mut b = TargetF::characteristic().to_vec();
667 assert_eq!(b[0] % 2, 1);
668 b[0] -= 1; let run = Boolean::<BaseF>::enforce_smaller_or_equal_than_le(&bits, b)?;
670
671 assert!(run.is_empty());
675
676 Ok(bits)
677 }
678}
679
680impl<TargetF: PrimeField, BaseF: PrimeField> ToBytesGadget<BaseF>
681 for AllocatedEmulatedFpVar<TargetF, BaseF>
682{
683 #[tracing::instrument(target = "r1cs")]
684 fn to_bytes_le(&self) -> R1CSResult<Vec<UInt8<BaseF>>> {
685 let mut bits = self.to_bits_le()?;
686
687 let num_bits = TargetF::BigInt::NUM_LIMBS * 64;
688 assert!(bits.len() <= num_bits);
689 bits.resize_with(num_bits, || Boolean::FALSE);
690
691 let bytes = bits.chunks(8).map(UInt8::from_bits_le).collect();
692 Ok(bytes)
693 }
694}
695
696impl<TargetF: PrimeField, BaseF: PrimeField> CondSelectGadget<BaseF>
697 for AllocatedEmulatedFpVar<TargetF, BaseF>
698{
699 #[tracing::instrument(target = "r1cs")]
700 fn conditionally_select(
701 cond: &Boolean<BaseF>,
702 true_value: &Self,
703 false_value: &Self,
704 ) -> R1CSResult<Self> {
705 assert_eq!(
706 true_value.get_optimization_type(),
707 false_value.get_optimization_type()
708 );
709
710 let mut limbs_sel = Vec::with_capacity(true_value.limbs.len());
711
712 for (x, y) in true_value.limbs.iter().zip(&false_value.limbs) {
713 limbs_sel.push(FpVar::<BaseF>::conditionally_select(cond, x, y)?);
714 }
715
716 Ok(Self {
717 cs: true_value.cs().or(false_value.cs()),
718 limbs: limbs_sel,
719 num_of_additions_over_normal_form: max(
720 true_value.num_of_additions_over_normal_form,
721 false_value.num_of_additions_over_normal_form,
722 ),
723 is_in_the_normal_form: true_value.is_in_the_normal_form
724 && false_value.is_in_the_normal_form,
725 target_phantom: PhantomData,
726 })
727 }
728}
729
730impl<TargetF: PrimeField, BaseF: PrimeField> TwoBitLookupGadget<BaseF>
731 for AllocatedEmulatedFpVar<TargetF, BaseF>
732{
733 type TableConstant = TargetF;
734
735 #[tracing::instrument(target = "r1cs")]
736 fn two_bit_lookup(
737 bits: &[Boolean<BaseF>],
738 constants: &[Self::TableConstant],
739 ) -> R1CSResult<Self> {
740 debug_assert!(bits.len() == 2);
741 debug_assert!(constants.len() == 4);
742
743 let cs = bits.cs();
744
745 let optimization_type = match cs.optimization_goal() {
746 OptimizationGoal::None => OptimizationType::Constraints,
747 OptimizationGoal::Constraints => OptimizationType::Constraints,
748 OptimizationGoal::Weight => OptimizationType::Weight,
749 };
750
751 let params = get_params(
752 TargetF::MODULUS_BIT_SIZE as usize,
753 BaseF::MODULUS_BIT_SIZE as usize,
754 optimization_type,
755 );
756 let mut limbs_constants = Vec::new();
757 for _ in 0..params.num_limbs {
758 limbs_constants.push(Vec::new());
759 }
760
761 for constant in constants.iter() {
762 let representations =
763 AllocatedEmulatedFpVar::<TargetF, BaseF>::get_limbs_representations(
764 constant,
765 optimization_type,
766 )?;
767
768 for (i, representation) in representations.iter().enumerate() {
769 limbs_constants[i].push(*representation);
770 }
771 }
772
773 let mut limbs = Vec::new();
774 for limbs_constant in limbs_constants.iter() {
775 limbs.push(FpVar::<BaseF>::two_bit_lookup(bits, limbs_constant)?);
776 }
777
778 Ok(AllocatedEmulatedFpVar::<TargetF, BaseF> {
779 cs,
780 limbs,
781 num_of_additions_over_normal_form: BaseF::zero(),
782 is_in_the_normal_form: true,
783 target_phantom: PhantomData,
784 })
785 }
786}
787
788impl<TargetF: PrimeField, BaseF: PrimeField> ThreeBitCondNegLookupGadget<BaseF>
789 for AllocatedEmulatedFpVar<TargetF, BaseF>
790{
791 type TableConstant = TargetF;
792
793 #[tracing::instrument(target = "r1cs")]
794 fn three_bit_cond_neg_lookup(
795 bits: &[Boolean<BaseF>],
796 b0b1: &Boolean<BaseF>,
797 constants: &[Self::TableConstant],
798 ) -> R1CSResult<Self> {
799 debug_assert!(bits.len() == 3);
800 debug_assert!(constants.len() == 4);
801
802 let cs = bits.cs().or(b0b1.cs());
803
804 let optimization_type = match cs.optimization_goal() {
805 OptimizationGoal::None => OptimizationType::Constraints,
806 OptimizationGoal::Constraints => OptimizationType::Constraints,
807 OptimizationGoal::Weight => OptimizationType::Weight,
808 };
809
810 let params = get_params(
811 TargetF::MODULUS_BIT_SIZE as usize,
812 BaseF::MODULUS_BIT_SIZE as usize,
813 optimization_type,
814 );
815
816 let mut limbs_constants = Vec::new();
817 for _ in 0..params.num_limbs {
818 limbs_constants.push(Vec::new());
819 }
820
821 for constant in constants.iter() {
822 let representations =
823 AllocatedEmulatedFpVar::<TargetF, BaseF>::get_limbs_representations(
824 constant,
825 optimization_type,
826 )?;
827
828 for (i, representation) in representations.iter().enumerate() {
829 limbs_constants[i].push(*representation);
830 }
831 }
832
833 let mut limbs = Vec::new();
834 for limbs_constant in limbs_constants.iter() {
835 limbs.push(FpVar::<BaseF>::three_bit_cond_neg_lookup(
836 bits,
837 b0b1,
838 limbs_constant,
839 )?);
840 }
841
842 Ok(AllocatedEmulatedFpVar::<TargetF, BaseF> {
843 cs,
844 limbs,
845 num_of_additions_over_normal_form: BaseF::zero(),
846 is_in_the_normal_form: true,
847 target_phantom: PhantomData,
848 })
849 }
850}
851
852impl<TargetF: PrimeField, BaseF: PrimeField> AllocVar<TargetF, BaseF>
853 for AllocatedEmulatedFpVar<TargetF, BaseF>
854{
855 fn new_variable<T: Borrow<TargetF>>(
856 cs: impl Into<Namespace<BaseF>>,
857 f: impl FnOnce() -> Result<T, SynthesisError>,
858 mode: AllocationMode,
859 ) -> R1CSResult<Self> {
860 let ns = cs.into();
861 let cs = ns.cs();
862 let this = Self::new_variable_unchecked(ns!(cs, "alloc"), f, mode)?;
863 if mode == AllocationMode::Witness {
864 this.enforce_in_range(ns!(cs, "bits"))?;
865 }
866 Ok(this)
867 }
868}
869
870impl<TargetF: PrimeField, BaseF: PrimeField> ToConstraintFieldGadget<BaseF>
871 for AllocatedEmulatedFpVar<TargetF, BaseF>
872{
873 fn to_constraint_field(&self) -> R1CSResult<Vec<FpVar<BaseF>>> {
874 let bits = self.to_bits_le()?;
877
878 let params = get_params(
880 TargetF::MODULUS_BIT_SIZE as usize,
881 BaseF::MODULUS_BIT_SIZE as usize,
882 OptimizationType::Weight,
883 );
884
885 let mut limbs = bits
887 .chunks(params.bits_per_limb)
888 .map(|chunk| {
889 let mut limb = FpVar::<BaseF>::zero();
890 let mut w = BaseF::one();
891 for b in chunk.iter() {
892 limb += FpVar::from(b.clone()) * w;
893 w.double_in_place();
894 }
895 limb
896 })
897 .collect::<Vec<FpVar<BaseF>>>();
898
899 limbs.reverse();
900
901 Ok(limbs)
903 }
904}
905
906impl<TargetF: PrimeField, BaseF: PrimeField> Clone for AllocatedEmulatedFpVar<TargetF, BaseF> {
909 fn clone(&self) -> Self {
910 AllocatedEmulatedFpVar {
911 cs: self.cs(),
912 limbs: self.limbs.clone(),
913 num_of_additions_over_normal_form: self.num_of_additions_over_normal_form,
914 is_in_the_normal_form: self.is_in_the_normal_form,
915 target_phantom: PhantomData,
916 }
917 }
918}