ark_r1cs_std/fields/emulated_fp/
allocated_field_var.rs

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/// The allocated version of `EmulatedFpVar` (introduced below)
27#[derive(Debug)]
28#[must_use]
29pub struct AllocatedEmulatedFpVar<TargetF: PrimeField, BaseF: PrimeField> {
30    /// Constraint system reference
31    pub cs: ConstraintSystemRef<BaseF>,
32    /// The limbs, each of which is a BaseF gadget.
33    pub limbs: Vec<FpVar<BaseF>>,
34    /// Number of additions done over this gadget, using which the gadget
35    /// decides when to reduce.
36    pub num_of_additions_over_normal_form: BaseF,
37    /// Whether the limb representation is the normal form (using only the bits
38    /// specified in the parameters, and the representation is strictly within
39    /// the range of TargetF).
40    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    /// Return cs
47    pub fn cs(&self) -> ConstraintSystemRef<BaseF> {
48        self.cs.clone()
49    }
50
51    /// Obtain the value of limbs
52    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        // Convert 2^{(params.bits_per_limb - 1)} into the TargetF and then double
60        // the base This is because 2^{(params.bits_per_limb)} might indeed be
61        // larger than the target field's prime.
62        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    /// Obtain the value of a emulated field element
89    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    /// Obtain the emulated field element of a constant value
99    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    /// Obtain the emulated field element of one
124    pub fn one(cs: ConstraintSystemRef<BaseF>) -> R1CSResult<Self> {
125        Self::constant(cs, TargetF::one())
126    }
127
128    /// Obtain the emulated field element of zero
129    pub fn zero(cs: ConstraintSystemRef<BaseF>) -> R1CSResult<Self> {
130        Self::constant(cs, TargetF::zero())
131    }
132
133    /// Add a emulated field element
134    #[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    /// Add a constant
159    #[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    /// Subtract a emulated field element, without the final reduction step
184    #[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        // Step 1: reduce the `other` if needed
195        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        // Step 2: construct the padding
208        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        // Step 3: prepare to pad the padding to k * p for some k
225        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        // Step 4: the result is self + pad + pad_to_kp - other
230        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    /// Subtract a emulated field element
259    #[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    /// Subtract a constant
269    #[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    /// Multiply a emulated field element
275    #[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    /// Multiply a constant
283    pub fn mul_constant(&self, other: &TargetF) -> R1CSResult<Self> {
284        self.mul(&Self::constant(self.cs(), *other)?)
285    }
286
287    /// Compute the negate of a emulated field element
288    #[tracing::instrument(target = "r1cs")]
289    pub fn negate(&self) -> R1CSResult<Self> {
290        Self::zero(self.cs())?.sub(self)
291    }
292
293    /// Compute the inverse of a emulated field element
294    #[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    /// Convert a `TargetF` element into limbs (not constraints)
306    /// This is an internal function that would be reused by a number of other
307    /// functions
308    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    /// Obtain the limbs directly from a big int
316    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        // push the lower limbs first
327        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(); // `to_bits` is big endian
331            let cur_mod_r = <BaseF as PrimeField>::BigInt::from_bits_be(
332                &cur_bits[cur_bits.len() - params.bits_per_limb..],
333            ); // therefore, the lowest `bits_per_non_top_limb` bits is what we want.
334            limbs.push(BaseF::from_bigint(cur_mod_r).unwrap());
335            cur >>= params.bits_per_limb as u32;
336        }
337
338        // then we reserve, so that the limbs are ``big limb first''
339        limbs.reverse();
340
341        Ok(limbs)
342    }
343
344    /// for advanced use, multiply and output the intermediate representations
345    /// (without reduction) This intermediate representations can be added
346    /// with each other, and they can later be reduced back to the
347    /// `EmulatedFpVar`.
348    #[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        // Step 1: reduce `self` and `other` if neceessary
362        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        // Get p
456        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        // Get delta = self - other
476        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        // Allocate k = delta / p
481        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        // Compute k * p
496        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        // Enforce delta = kp
502        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    /// Allocates a new variable, but does not check that the allocation's limbs
539    /// are in-range.
540    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    /// Check that this element is in-range; i.e., each limb is in-range, and
587    /// the whole number is less than the modulus.
588    ///
589    /// Returns the bits of the element, in little-endian form
590    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    /// Allocates a new non-native field witness with value given by the
624    /// function `f`.  Enforces that the field element has value in `[0, modulus)`,
625    /// and returns the bits of its binary representation.
626    /// The bits are in little-endian (i.e., the bit at index 0 is the LSB) and the
627    /// bit-vector is empty in non-witness allocation modes.
628    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        // Reduce to the normal form
652        // Though, a malicious prover can make it slightly larger than p
653        let mut self_normal = self.clone();
654        Reducer::<TargetF, BaseF>::pre_eq_reduce(&mut self_normal)?;
655
656        // Therefore, we convert it to bits and enforce that it is in the field
657        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; // This works, because the LSB is one, so there's no borrows.
669        let run = Boolean::<BaseF>::enforce_smaller_or_equal_than_le(&bits, b)?;
670
671        // We should always end in a "run" of zeros, because
672        // the characteristic is an odd prime. So, this should
673        // be empty.
674        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        // provide a unique representation of the emulated variable
875        // step 1: convert it into a bit sequence
876        let bits = self.to_bits_le()?;
877
878        // step 2: obtain the parameters for weight-optimized (often, fewer limbs)
879        let params = get_params(
880            TargetF::MODULUS_BIT_SIZE as usize,
881            BaseF::MODULUS_BIT_SIZE as usize,
882            OptimizationType::Weight,
883        );
884
885        // step 3: assemble the limbs
886        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        // step 4: output the limbs
902        Ok(limbs)
903    }
904}
905
906// Implementation of a few traits
907
908impl<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}