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