arnaucube
arnaucube/ark-r1cs-stdPublic Self-hosted repository
ark-r1cs-std/src/fields/emulated_fp/field_var.rs
src/fields/emulated_fp/field_var.rsRaw
  1use super::{params::OptimizationType, AllocatedEmulatedFpVar, MulResultVar};
  2use crate::{
  3    boolean::Boolean,
  4    fields::{fp::FpVar, FieldVar},
  5    prelude::*,
  6    R1CSVar, ToConstraintFieldGadget,
  7};
  8use ark_ff::{BigInteger, PrimeField};
  9use ark_relations::r1cs::{ConstraintSystemRef, Namespace, Result as R1CSResult, SynthesisError};
 10use ark_std::{
 11    borrow::Borrow,
 12    hash::{Hash, Hasher},
 13    vec::Vec,
 14};
 15
 16/// A gadget for representing non-native (`TargetF`) field elements over the
 17/// constraint field (`BaseF`).
 18#[derive(Clone, Debug)]
 19#[must_use]
 20pub enum EmulatedFpVar<TargetF: PrimeField, BaseF: PrimeField> {
 21    /// Constant
 22    Constant(TargetF),
 23    /// Allocated gadget
 24    Var(AllocatedEmulatedFpVar<TargetF, BaseF>),
 25}
 26
 27impl<TargetF: PrimeField, BaseF: PrimeField> PartialEq for EmulatedFpVar<TargetF, BaseF> {
 28    fn eq(&self, other: &Self) -> bool {
 29        self.value()
 30            .unwrap_or_default()
 31            .eq(&other.value().unwrap_or_default())
 32    }
 33}
 34
 35impl<TargetF: PrimeField, BaseF: PrimeField> Eq for EmulatedFpVar<TargetF, BaseF> {}
 36
 37impl<TargetF: PrimeField, BaseF: PrimeField> Hash for EmulatedFpVar<TargetF, BaseF> {
 38    fn hash<H: Hasher>(&self, state: &mut H) {
 39        self.value().unwrap_or_default().hash(state);
 40    }
 41}
 42
 43impl<TargetF: PrimeField, BaseF: PrimeField> R1CSVar<BaseF> for EmulatedFpVar<TargetF, BaseF> {
 44    type Value = TargetF;
 45
 46    fn cs(&self) -> ConstraintSystemRef<BaseF> {
 47        match self {
 48            Self::Constant(_) => ConstraintSystemRef::None,
 49            Self::Var(a) => a.cs(),
 50        }
 51    }
 52
 53    fn value(&self) -> R1CSResult<Self::Value> {
 54        match self {
 55            Self::Constant(v) => Ok(*v),
 56            Self::Var(v) => v.value(),
 57        }
 58    }
 59}
 60
 61impl<TargetF: PrimeField, BaseF: PrimeField> From<Boolean<BaseF>>
 62    for EmulatedFpVar<TargetF, BaseF>
 63{
 64    fn from(other: Boolean<BaseF>) -> Self {
 65        if let Boolean::Constant(b) = other {
 66            Self::Constant(<TargetF as From<u128>>::from(b as u128))
 67        } else {
 68            // `other` is a variable
 69            let one = Self::Constant(TargetF::one());
 70            let zero = Self::Constant(TargetF::zero());
 71            Self::conditionally_select(&other, &one, &zero).unwrap()
 72        }
 73    }
 74}
 75
 76impl<TargetF: PrimeField, BaseF: PrimeField> From<AllocatedEmulatedFpVar<TargetF, BaseF>>
 77    for EmulatedFpVar<TargetF, BaseF>
 78{
 79    fn from(other: AllocatedEmulatedFpVar<TargetF, BaseF>) -> Self {
 80        Self::Var(other)
 81    }
 82}
 83
 84impl<'a, TargetF: PrimeField, BaseF: PrimeField> FieldOpsBounds<'a, TargetF, Self>
 85    for EmulatedFpVar<TargetF, BaseF>
 86{
 87}
 88
 89impl<'a, TargetF: PrimeField, BaseF: PrimeField>
 90    FieldOpsBounds<'a, TargetF, EmulatedFpVar<TargetF, BaseF>>
 91    for &'a EmulatedFpVar<TargetF, BaseF>
 92{
 93}
 94
 95impl<TargetF: PrimeField, BaseF: PrimeField> FieldVar<TargetF, BaseF>
 96    for EmulatedFpVar<TargetF, BaseF>
 97{
 98    fn zero() -> Self {
 99        Self::Constant(TargetF::zero())
100    }
101
102    fn one() -> Self {
103        Self::Constant(TargetF::one())
104    }
105
106    fn constant(v: TargetF) -> Self {
107        Self::Constant(v)
108    }
109
110    #[tracing::instrument(target = "r1cs")]
111    fn negate(&self) -> R1CSResult<Self> {
112        match self {
113            Self::Constant(c) => Ok(Self::Constant(-*c)),
114            Self::Var(v) => Ok(Self::Var(v.negate()?)),
115        }
116    }
117
118    #[tracing::instrument(target = "r1cs")]
119    fn inverse(&self) -> R1CSResult<Self> {
120        match self {
121            Self::Constant(c) => Ok(Self::Constant(c.inverse().unwrap_or_default())),
122            Self::Var(v) => Ok(Self::Var(v.inverse()?)),
123        }
124    }
125
126    #[tracing::instrument(target = "r1cs")]
127    fn frobenius_map(&self, power: usize) -> R1CSResult<Self> {
128        match self {
129            Self::Constant(c) => Ok(Self::Constant({
130                let mut tmp = *c;
131                tmp.frobenius_map_in_place(power);
132                tmp
133            })),
134            Self::Var(v) => Ok(Self::Var(v.frobenius_map(power)?)),
135        }
136    }
137}
138
139impl_bounded_ops!(
140    EmulatedFpVar<TargetF, BaseF>,
141    TargetF,
142    Add,
143    add,
144    AddAssign,
145    add_assign,
146    |this: &'a EmulatedFpVar<TargetF, BaseF>, other: &'a EmulatedFpVar<TargetF, BaseF>| {
147        use EmulatedFpVar::*;
148        match (this, other) {
149            (Constant(c1), Constant(c2)) => Constant(*c1 + c2),
150            (Constant(c), Var(v)) | (Var(v), Constant(c)) => Var(v.add_constant(c).unwrap()),
151            (Var(v1), Var(v2)) => Var(v1.add(v2).unwrap()),
152        }
153    },
154    |this: &'a EmulatedFpVar<TargetF, BaseF>, other: TargetF| { this + &EmulatedFpVar::Constant(other) },
155    (TargetF: PrimeField, BaseF: PrimeField),
156);
157
158impl_bounded_ops!(
159    EmulatedFpVar<TargetF, BaseF>,
160    TargetF,
161    Sub,
162    sub,
163    SubAssign,
164    sub_assign,
165    |this: &'a EmulatedFpVar<TargetF, BaseF>, other: &'a EmulatedFpVar<TargetF, BaseF>| {
166        use EmulatedFpVar::*;
167        match (this, other) {
168            (Constant(c1), Constant(c2)) => Constant(*c1 - c2),
169            (Var(v), Constant(c)) => Var(v.sub_constant(c).unwrap()),
170            (Constant(c), Var(v)) => Var(v.sub_constant(c).unwrap().negate().unwrap()),
171            (Var(v1), Var(v2)) => Var(v1.sub(v2).unwrap()),
172        }
173    },
174    |this: &'a EmulatedFpVar<TargetF, BaseF>, other: TargetF| {
175        this - &EmulatedFpVar::Constant(other)
176    },
177    (TargetF: PrimeField, BaseF: PrimeField),
178);
179
180impl_bounded_ops!(
181    EmulatedFpVar<TargetF, BaseF>,
182    TargetF,
183    Mul,
184    mul,
185    MulAssign,
186    mul_assign,
187    |this: &'a EmulatedFpVar<TargetF, BaseF>, other: &'a EmulatedFpVar<TargetF, BaseF>| {
188        use EmulatedFpVar::*;
189        match (this, other) {
190            (Constant(c1), Constant(c2)) => Constant(*c1 * c2),
191            (Constant(c), Var(v)) | (Var(v), Constant(c)) => Var(v.mul_constant(c).unwrap()),
192            (Var(v1), Var(v2)) => Var(v1.mul(v2).unwrap()),
193        }
194    },
195    |this: &'a EmulatedFpVar<TargetF, BaseF>, other: TargetF| {
196        if other.is_zero() {
197            EmulatedFpVar::zero()
198        } else {
199            this * &EmulatedFpVar::Constant(other)
200        }
201    },
202    (TargetF: PrimeField, BaseF: PrimeField),
203);
204
205/// *************************************************************************
206/// *************************************************************************
207
208impl<TargetF: PrimeField, BaseF: PrimeField> EqGadget<BaseF> for EmulatedFpVar<TargetF, BaseF> {
209    #[tracing::instrument(target = "r1cs")]
210    fn is_eq(&self, other: &Self) -> R1CSResult<Boolean<BaseF>> {
211        let cs = self.cs().or(other.cs());
212
213        if cs == ConstraintSystemRef::None {
214            Ok(Boolean::Constant(self.value()? == other.value()?))
215        } else {
216            let should_enforce_equal =
217                Boolean::new_witness(cs, || Ok(self.value()? == other.value()?))?;
218
219            self.conditional_enforce_equal(other, &should_enforce_equal)?;
220            self.conditional_enforce_not_equal(other, &should_enforce_equal.not())?;
221
222            Ok(should_enforce_equal)
223        }
224    }
225
226    #[tracing::instrument(target = "r1cs")]
227    fn conditional_enforce_equal(
228        &self,
229        other: &Self,
230        should_enforce: &Boolean<BaseF>,
231    ) -> R1CSResult<()> {
232        match (self, other) {
233            (Self::Constant(c1), Self::Constant(c2)) => {
234                if c1 != c2 {
235                    should_enforce.enforce_equal(&Boolean::FALSE)?;
236                }
237                Ok(())
238            },
239            (Self::Constant(c), Self::Var(v)) | (Self::Var(v), Self::Constant(c)) => {
240                let cs = v.cs();
241                let c = AllocatedEmulatedFpVar::new_constant(cs, c)?;
242                c.conditional_enforce_equal(v, should_enforce)
243            },
244            (Self::Var(v1), Self::Var(v2)) => v1.conditional_enforce_equal(v2, should_enforce),
245        }
246    }
247
248    #[tracing::instrument(target = "r1cs")]
249    fn conditional_enforce_not_equal(
250        &self,
251        other: &Self,
252        should_enforce: &Boolean<BaseF>,
253    ) -> R1CSResult<()> {
254        match (self, other) {
255            (Self::Constant(c1), Self::Constant(c2)) => {
256                if c1 == c2 {
257                    should_enforce.enforce_equal(&Boolean::FALSE)?;
258                }
259                Ok(())
260            },
261            (Self::Constant(c), Self::Var(v)) | (Self::Var(v), Self::Constant(c)) => {
262                let cs = v.cs();
263                let c = AllocatedEmulatedFpVar::new_constant(cs, c)?;
264                c.conditional_enforce_not_equal(v, should_enforce)
265            },
266            (Self::Var(v1), Self::Var(v2)) => v1.conditional_enforce_not_equal(v2, should_enforce),
267        }
268    }
269}
270
271impl<TargetF: PrimeField, BaseF: PrimeField> ToBitsGadget<BaseF> for EmulatedFpVar<TargetF, BaseF> {
272    #[tracing::instrument(target = "r1cs")]
273    fn to_bits_le(&self) -> R1CSResult<Vec<Boolean<BaseF>>> {
274        match self {
275            Self::Constant(_) => self.to_non_unique_bits_le(),
276            Self::Var(v) => v.to_bits_le(),
277        }
278    }
279
280    #[tracing::instrument(target = "r1cs")]
281    fn to_non_unique_bits_le(&self) -> R1CSResult<Vec<Boolean<BaseF>>> {
282        use ark_ff::BitIteratorLE;
283        match self {
284            Self::Constant(c) => Ok(BitIteratorLE::new(&c.into_bigint())
285                .take((TargetF::MODULUS_BIT_SIZE) as usize)
286                .map(Boolean::constant)
287                .collect::<Vec<_>>()),
288            Self::Var(v) => v.to_non_unique_bits_le(),
289        }
290    }
291}
292
293impl<TargetF: PrimeField, BaseF: PrimeField> ToBytesGadget<BaseF>
294    for EmulatedFpVar<TargetF, BaseF>
295{
296    /// Outputs the unique byte decomposition of `self` in *little-endian*
297    /// form.
298    #[tracing::instrument(target = "r1cs")]
299    fn to_bytes(&self) -> R1CSResult<Vec<UInt8<BaseF>>> {
300        match self {
301            Self::Constant(c) => Ok(UInt8::constant_vec(
302                c.into_bigint().to_bytes_le().as_slice(),
303            )),
304
305            Self::Var(v) => v.to_bytes(),
306        }
307    }
308
309    #[tracing::instrument(target = "r1cs")]
310    fn to_non_unique_bytes(&self) -> R1CSResult<Vec<UInt8<BaseF>>> {
311        match self {
312            Self::Constant(c) => Ok(UInt8::constant_vec(
313                c.into_bigint().to_bytes_le().as_slice(),
314            )),
315            Self::Var(v) => v.to_non_unique_bytes(),
316        }
317    }
318}
319
320impl<TargetF: PrimeField, BaseF: PrimeField> CondSelectGadget<BaseF>
321    for EmulatedFpVar<TargetF, BaseF>
322{
323    #[tracing::instrument(target = "r1cs")]
324    fn conditionally_select(
325        cond: &Boolean<BaseF>,
326        true_value: &Self,
327        false_value: &Self,
328    ) -> R1CSResult<Self> {
329        match cond {
330            Boolean::Constant(true) => Ok(true_value.clone()),
331            Boolean::Constant(false) => Ok(false_value.clone()),
332            _ => {
333                let cs = cond.cs();
334                let true_value = match true_value {
335                    Self::Constant(f) => AllocatedEmulatedFpVar::new_constant(cs.clone(), f)?,
336                    Self::Var(v) => v.clone(),
337                };
338                let false_value = match false_value {
339                    Self::Constant(f) => AllocatedEmulatedFpVar::new_constant(cs, f)?,
340                    Self::Var(v) => v.clone(),
341                };
342                cond.select(&true_value, &false_value).map(Self::Var)
343            },
344        }
345    }
346}
347
348/// Uses two bits to perform a lookup into a table
349/// `b` is little-endian: `b[0]` is LSB.
350impl<TargetF: PrimeField, BaseF: PrimeField> TwoBitLookupGadget<BaseF>
351    for EmulatedFpVar<TargetF, BaseF>
352{
353    type TableConstant = TargetF;
354
355    #[tracing::instrument(target = "r1cs")]
356    fn two_bit_lookup(b: &[Boolean<BaseF>], c: &[Self::TableConstant]) -> R1CSResult<Self> {
357        debug_assert_eq!(b.len(), 2);
358        debug_assert_eq!(c.len(), 4);
359        if b.cs().is_none() {
360            // We're in the constant case
361
362            let lsb = b[0].value()? as usize;
363            let msb = b[1].value()? as usize;
364            let index = lsb + (msb << 1);
365            Ok(Self::Constant(c[index]))
366        } else {
367            AllocatedEmulatedFpVar::two_bit_lookup(b, c).map(Self::Var)
368        }
369    }
370}
371
372impl<TargetF: PrimeField, BaseF: PrimeField> ThreeBitCondNegLookupGadget<BaseF>
373    for EmulatedFpVar<TargetF, BaseF>
374{
375    type TableConstant = TargetF;
376
377    #[tracing::instrument(target = "r1cs")]
378    fn three_bit_cond_neg_lookup(
379        b: &[Boolean<BaseF>],
380        b0b1: &Boolean<BaseF>,
381        c: &[Self::TableConstant],
382    ) -> R1CSResult<Self> {
383        debug_assert_eq!(b.len(), 3);
384        debug_assert_eq!(c.len(), 4);
385
386        if b.cs().or(b0b1.cs()).is_none() {
387            // We're in the constant case
388
389            let lsb = b[0].value()? as usize;
390            let msb = b[1].value()? as usize;
391            let index = lsb + (msb << 1);
392            let intermediate = c[index];
393
394            let is_negative = b[2].value()?;
395            let y = if is_negative {
396                -intermediate
397            } else {
398                intermediate
399            };
400            Ok(Self::Constant(y))
401        } else {
402            AllocatedEmulatedFpVar::three_bit_cond_neg_lookup(b, b0b1, c).map(Self::Var)
403        }
404    }
405}
406
407impl<TargetF: PrimeField, BaseF: PrimeField> AllocVar<TargetF, BaseF>
408    for EmulatedFpVar<TargetF, BaseF>
409{
410    fn new_variable<T: Borrow<TargetF>>(
411        cs: impl Into<Namespace<BaseF>>,
412        f: impl FnOnce() -> Result<T, SynthesisError>,
413        mode: AllocationMode,
414    ) -> R1CSResult<Self> {
415        let ns = cs.into();
416        let cs = ns.cs();
417
418        if cs == ConstraintSystemRef::None || mode == AllocationMode::Constant {
419            Ok(Self::Constant(*f()?.borrow()))
420        } else {
421            AllocatedEmulatedFpVar::new_variable(cs, f, mode).map(Self::Var)
422        }
423    }
424}
425
426impl<TargetF: PrimeField, BaseF: PrimeField> ToConstraintFieldGadget<BaseF>
427    for EmulatedFpVar<TargetF, BaseF>
428{
429    #[tracing::instrument(target = "r1cs")]
430    fn to_constraint_field(&self) -> R1CSResult<Vec<FpVar<BaseF>>> {
431        // Use one group element to represent the optimization type.
432        //
433        // By default, the constant is converted in the weight-optimized type, because
434        // it results in fewer elements.
435        match self {
436            Self::Constant(c) => Ok(AllocatedEmulatedFpVar::get_limbs_representations(
437                c,
438                OptimizationType::Weight,
439            )?
440            .into_iter()
441            .map(FpVar::constant)
442            .collect()),
443            Self::Var(v) => v.to_constraint_field(),
444        }
445    }
446}
447
448impl<TargetF: PrimeField, BaseF: PrimeField> EmulatedFpVar<TargetF, BaseF> {
449    /// The `mul_without_reduce` for `EmulatedFpVar`
450    #[tracing::instrument(target = "r1cs")]
451    pub fn mul_without_reduce(&self, other: &Self) -> R1CSResult<MulResultVar<TargetF, BaseF>> {
452        match self {
453            Self::Constant(c) => match other {
454                Self::Constant(other_c) => Ok(MulResultVar::Constant(*c * other_c)),
455                Self::Var(other_v) => {
456                    let self_v =
457                        AllocatedEmulatedFpVar::<TargetF, BaseF>::new_constant(self.cs(), c)?;
458                    Ok(MulResultVar::Var(other_v.mul_without_reduce(&self_v)?))
459                },
460            },
461            Self::Var(v) => {
462                let other_v = match other {
463                    Self::Constant(other_c) => {
464                        AllocatedEmulatedFpVar::<TargetF, BaseF>::new_constant(self.cs(), other_c)?
465                    },
466                    Self::Var(other_v) => other_v.clone(),
467                };
468                Ok(MulResultVar::Var(v.mul_without_reduce(&other_v)?))
469            },
470        }
471    }
472}