arnaucube
arnaucube/ark-r1cs-stdPublic Self-hosted repository
ark-r1cs-std/src/fields/emulated_fp/allocated_mul_result.rs
src/fields/emulated_fp/allocated_mul_result.rsRaw
  1use super::{
  2    params::{get_params, OptimizationType},
  3    reduce::{bigint_to_basefield, limbs_to_bigint, Reducer},
  4    AllocatedEmulatedFpVar,
  5};
  6use crate::{fields::fp::FpVar, prelude::*};
  7use ark_ff::PrimeField;
  8use ark_relations::{
  9    ns,
 10    r1cs::{ConstraintSystemRef, OptimizationGoal, Result as R1CSResult},
 11};
 12use ark_std::{marker::PhantomData, vec::Vec};
 13use num_bigint::BigUint;
 14
 15/// The allocated form of `MulResultVar` (introduced below)
 16#[derive(Debug)]
 17#[must_use]
 18pub struct AllocatedMulResultVar<TargetF: PrimeField, BaseF: PrimeField> {
 19    /// Constraint system reference
 20    pub cs: ConstraintSystemRef<BaseF>,
 21    /// Limbs of the intermediate representations
 22    pub limbs: Vec<FpVar<BaseF>>,
 23    /// The cumulative num of additions
 24    pub prod_of_num_of_additions: BaseF,
 25    #[doc(hidden)]
 26    pub target_phantom: PhantomData<TargetF>,
 27}
 28
 29impl<TargetF: PrimeField, BaseF: PrimeField> From<&AllocatedEmulatedFpVar<TargetF, BaseF>>
 30    for AllocatedMulResultVar<TargetF, BaseF>
 31{
 32    fn from(src: &AllocatedEmulatedFpVar<TargetF, BaseF>) -> Self {
 33        let params = get_params(
 34            TargetF::MODULUS_BIT_SIZE as usize,
 35            BaseF::MODULUS_BIT_SIZE as usize,
 36            src.get_optimization_type(),
 37        );
 38
 39        let mut limbs = src.limbs.clone();
 40        limbs.reverse();
 41        limbs.resize(2 * params.num_limbs - 1, FpVar::<BaseF>::zero());
 42        limbs.reverse();
 43
 44        let prod_of_num_of_additions = src.num_of_additions_over_normal_form + &BaseF::one();
 45
 46        Self {
 47            cs: src.cs(),
 48            limbs,
 49            prod_of_num_of_additions,
 50            target_phantom: PhantomData,
 51        }
 52    }
 53}
 54
 55impl<TargetF: PrimeField, BaseF: PrimeField> AllocatedMulResultVar<TargetF, BaseF> {
 56    /// Get the CS
 57    pub fn cs(&self) -> ConstraintSystemRef<BaseF> {
 58        self.cs.clone()
 59    }
 60
 61    /// Get the value of the multiplication result
 62    pub fn value(&self) -> R1CSResult<TargetF> {
 63        let params = get_params(
 64            TargetF::MODULUS_BIT_SIZE as usize,
 65            BaseF::MODULUS_BIT_SIZE as usize,
 66            self.get_optimization_type(),
 67        );
 68
 69        let p_representations =
 70            AllocatedEmulatedFpVar::<TargetF, BaseF>::get_limbs_representations_from_big_integer(
 71                &<TargetF as PrimeField>::MODULUS,
 72                self.get_optimization_type(),
 73            )?;
 74        let p_bigint = limbs_to_bigint(params.bits_per_limb, &p_representations);
 75
 76        let mut limbs_values = Vec::<BaseF>::new();
 77        for limb in self.limbs.iter() {
 78            limbs_values.push(limb.value().unwrap_or_default());
 79        }
 80        let value_bigint = limbs_to_bigint(params.bits_per_limb, &limbs_values);
 81
 82        let res = bigint_to_basefield::<TargetF>(&(value_bigint % p_bigint));
 83        Ok(res)
 84    }
 85
 86    /// Constraints for reducing the result of a multiplication mod p, to get an
 87    /// original representation.
 88    pub fn reduce(&self) -> R1CSResult<AllocatedEmulatedFpVar<TargetF, BaseF>> {
 89        let params = get_params(
 90            TargetF::MODULUS_BIT_SIZE as usize,
 91            BaseF::MODULUS_BIT_SIZE as usize,
 92            self.get_optimization_type(),
 93        );
 94
 95        // Step 1: get p
 96        let p_representations =
 97            AllocatedEmulatedFpVar::<TargetF, BaseF>::get_limbs_representations_from_big_integer(
 98                &<TargetF as PrimeField>::MODULUS,
 99                self.get_optimization_type(),
100            )?;
101        let p_bigint = limbs_to_bigint(params.bits_per_limb, &p_representations);
102
103        let mut p_gadget_limbs = Vec::new();
104        for limb in p_representations.iter() {
105            p_gadget_limbs.push(FpVar::<BaseF>::new_constant(self.cs(), limb)?);
106        }
107        let p_gadget = AllocatedEmulatedFpVar::<TargetF, BaseF> {
108            cs: self.cs(),
109            limbs: p_gadget_limbs,
110            num_of_additions_over_normal_form: BaseF::one(),
111            is_in_the_normal_form: false,
112            target_phantom: PhantomData,
113        };
114
115        // Step 2: compute surfeit
116        let surfeit = overhead!(self.prod_of_num_of_additions + BaseF::one()) + 1 + 1;
117
118        // Step 3: allocate k
119        let k_bits = {
120            let mut res = Vec::new();
121
122            let mut limbs_values = Vec::<BaseF>::new();
123            for limb in self.limbs.iter() {
124                limbs_values.push(limb.value().unwrap_or_default());
125            }
126
127            let value_bigint = limbs_to_bigint(params.bits_per_limb, &limbs_values);
128            let mut k_cur = value_bigint / p_bigint;
129
130            let total_len = TargetF::MODULUS_BIT_SIZE as usize + surfeit;
131
132            for _ in 0..total_len {
133                res.push(Boolean::<BaseF>::new_witness(self.cs(), || {
134                    Ok(&k_cur % 2u64 == BigUint::from(1u64))
135                })?);
136                k_cur /= 2u64;
137            }
138            res
139        };
140
141        let k_limbs = {
142            let zero = FpVar::Constant(BaseF::zero());
143            let mut limbs = Vec::new();
144
145            let mut k_bits_cur = k_bits.clone();
146
147            for i in 0..params.num_limbs {
148                let this_limb_size = if i != params.num_limbs - 1 {
149                    params.bits_per_limb
150                } else {
151                    k_bits.len() - (params.num_limbs - 1) * params.bits_per_limb
152                };
153
154                let this_limb_bits = k_bits_cur[0..this_limb_size].to_vec();
155                k_bits_cur = k_bits_cur[this_limb_size..].to_vec();
156
157                let mut limb = zero.clone();
158                let mut cur = BaseF::one();
159
160                for bit in this_limb_bits.iter() {
161                    limb += &(FpVar::<BaseF>::from(bit.clone()) * cur);
162                    cur.double_in_place();
163                }
164                limbs.push(limb);
165            }
166
167            limbs.reverse();
168            limbs
169        };
170
171        let k_gadget = AllocatedEmulatedFpVar::<TargetF, BaseF> {
172            cs: self.cs(),
173            limbs: k_limbs,
174            num_of_additions_over_normal_form: self.prod_of_num_of_additions,
175            is_in_the_normal_form: false,
176            target_phantom: PhantomData,
177        };
178
179        let cs = self.cs();
180
181        let r_gadget = AllocatedEmulatedFpVar::<TargetF, BaseF>::new_witness(ns!(cs, "r"), || {
182            Ok(self.value()?)
183        })?;
184
185        let params = get_params(
186            TargetF::MODULUS_BIT_SIZE as usize,
187            BaseF::MODULUS_BIT_SIZE as usize,
188            self.get_optimization_type(),
189        );
190
191        // Step 1: reduce `self` and `other` if neceessary
192        let mut prod_limbs = Vec::new();
193        let zero = FpVar::<BaseF>::zero();
194
195        for _ in 0..2 * params.num_limbs - 1 {
196            prod_limbs.push(zero.clone());
197        }
198
199        for i in 0..params.num_limbs {
200            for j in 0..params.num_limbs {
201                prod_limbs[i + j] = &prod_limbs[i + j] + (&p_gadget.limbs[i] * &k_gadget.limbs[j]);
202            }
203        }
204
205        let mut kp_plus_r_gadget = Self {
206            cs,
207            limbs: prod_limbs,
208            prod_of_num_of_additions: (p_gadget.num_of_additions_over_normal_form + BaseF::one())
209                * (k_gadget.num_of_additions_over_normal_form + BaseF::one()),
210            target_phantom: PhantomData,
211        };
212
213        let kp_plus_r_limbs_len = kp_plus_r_gadget.limbs.len();
214        for (i, limb) in r_gadget.limbs.iter().rev().enumerate() {
215            kp_plus_r_gadget.limbs[kp_plus_r_limbs_len - 1 - i] += limb;
216        }
217
218        Reducer::<TargetF, BaseF>::group_and_check_equality(
219            surfeit,
220            2 * params.bits_per_limb,
221            params.bits_per_limb,
222            &self.limbs,
223            &kp_plus_r_gadget.limbs,
224        )?;
225
226        Ok(r_gadget)
227    }
228
229    /// Add unreduced elements.
230    #[tracing::instrument(target = "r1cs")]
231    pub fn add(&self, other: &Self) -> R1CSResult<Self> {
232        assert_eq!(self.get_optimization_type(), other.get_optimization_type());
233
234        let mut new_limbs = Vec::new();
235
236        for (l1, l2) in self.limbs.iter().zip(other.limbs.iter()) {
237            let new_limb = l1 + l2;
238            new_limbs.push(new_limb);
239        }
240
241        Ok(Self {
242            cs: self.cs(),
243            limbs: new_limbs,
244            prod_of_num_of_additions: self.prod_of_num_of_additions
245                + other.prod_of_num_of_additions,
246            target_phantom: PhantomData,
247        })
248    }
249
250    /// Add native constant elem
251    #[tracing::instrument(target = "r1cs")]
252    pub fn add_constant(&self, other: &TargetF) -> R1CSResult<Self> {
253        let mut other_limbs = AllocatedEmulatedFpVar::<TargetF, BaseF>::get_limbs_representations(
254            other,
255            self.get_optimization_type(),
256        )?;
257        other_limbs.reverse();
258
259        let mut new_limbs = Vec::new();
260
261        for (i, limb) in self.limbs.iter().rev().enumerate() {
262            if i < other_limbs.len() {
263                new_limbs.push(limb + other_limbs[i]);
264            } else {
265                new_limbs.push((*limb).clone());
266            }
267        }
268
269        new_limbs.reverse();
270
271        Ok(Self {
272            cs: self.cs(),
273            limbs: new_limbs,
274            prod_of_num_of_additions: self.prod_of_num_of_additions + BaseF::one(),
275            target_phantom: PhantomData,
276        })
277    }
278
279    pub(crate) fn get_optimization_type(&self) -> OptimizationType {
280        match self.cs().optimization_goal() {
281            OptimizationGoal::None => OptimizationType::Constraints,
282            OptimizationGoal::Constraints => OptimizationType::Constraints,
283            OptimizationGoal::Weight => OptimizationType::Weight,
284        }
285    }
286}