arnaucube
arnaucube/ark-r1cs-stdPublic Self-hosted repository
ark-r1cs-std/r1cs-std/src/fields/fp/cmp.rs
r1cs-std/src/fields/fp/cmp.rsRaw
  1use crate::{
  2    boolean::Boolean,
  3    fields::{fp::FpVar, FieldVar},
  4    prelude::*,
  5    ToBitsGadget,
  6};
  7use algebra::PrimeField;
  8use core::cmp::Ordering;
  9use r1cs_core::{lc, SynthesisError, Variable};
 10
 11impl<F: PrimeField> FpVar<F> {
 12    /// This function enforces the ordering between `self` and `other`. The
 13    /// constraint system will not be satisfied otherwise. If `self` should
 14    /// also be checked for equality, e.g. `self <= other` instead of `self < other`, set
 15    /// `should_also_check_quality` to `true`. This variant verifies `self` and `other`
 16    /// are `<= (p-1)/2`.
 17    pub fn enforce_cmp(
 18        &self,
 19        other: &FpVar<F>,
 20        ordering: Ordering,
 21        should_also_check_equality: bool,
 22    ) -> Result<(), SynthesisError> {
 23        let (left, right) = self.process_cmp_inputs(other, ordering, should_also_check_equality)?;
 24        left.enforce_smaller_than(&right)
 25    }
 26
 27    /// This function enforces the ordering between `self` and `other`. The
 28    /// constraint system will not be satisfied otherwise. If `self` should
 29    /// also be checked for equality, e.g. `self <= other` instead of `self < other`, set
 30    /// `should_also_check_quality` to `true`. This variant assumes `self` and `other`
 31    /// are `<= (p-1)/2` and does not generate constraints to verify that.
 32    pub fn enforce_cmp_unchecked(
 33        &self,
 34        other: &FpVar<F>,
 35        ordering: Ordering,
 36        should_also_check_equality: bool,
 37    ) -> Result<(), SynthesisError> {
 38        let (left, right) = self.process_cmp_inputs(other, ordering, should_also_check_equality)?;
 39        left.enforce_smaller_than_unchecked(&right)
 40    }
 41
 42    /// This function checks the ordering between `self` and `other`. It outputs self
 43    /// `Boolean` that contains the result - `1` if true, `0` otherwise. The
 44    /// constraint system will be satisfied in any case. If `self` should
 45    /// also be checked for equality, e.g. `self <= other` instead of `self < other`, set
 46    /// `should_also_check_quality` to `true`. This variant verifies `self` and `other`
 47    /// are `<= (p-1)/2`.
 48    pub fn is_cmp(
 49        &self,
 50        other: &FpVar<F>,
 51        ordering: Ordering,
 52        should_also_check_equality: bool,
 53    ) -> Result<Boolean<F>, SynthesisError> {
 54        let (left, right) = self.process_cmp_inputs(other, ordering, should_also_check_equality)?;
 55        left.is_smaller_than(&right)
 56    }
 57
 58    /// This function checks the ordering between `self` and `other`. It outputs a
 59    /// `Boolean` that contains the result - `1` if true, `0` otherwise. The
 60    /// constraint system will be satisfied in any case. If `self` should
 61    /// also be checked for equality, e.g. `self <= other` instead of `self < other`, set
 62    /// `should_also_check_quality` to `true`. This variant assumes `self` and `other`
 63    /// are `<= (p-1)/2` and does not generate constraints to verify that.
 64    pub fn is_cmp_unchecked(
 65        &self,
 66        other: &FpVar<F>,
 67        ordering: Ordering,
 68        should_also_check_equality: bool,
 69    ) -> Result<Boolean<F>, SynthesisError> {
 70        let (left, right) = self.process_cmp_inputs(other, ordering, should_also_check_equality)?;
 71        left.is_smaller_than_unchecked(&right)
 72    }
 73
 74    fn process_cmp_inputs(
 75        &self,
 76        other: &Self,
 77        ordering: Ordering,
 78        should_also_check_equality: bool,
 79    ) -> Result<(Self, Self), SynthesisError> {
 80        let (left, right) = match ordering {
 81            Ordering::Less => (self, other),
 82            Ordering::Greater => (other, self),
 83            Ordering::Equal => Err(SynthesisError::Unsatisfiable)?,
 84        };
 85        let right_for_check = if should_also_check_equality {
 86            right + F::one()
 87        } else {
 88            right.clone()
 89        };
 90
 91        Ok((left.clone(), right_for_check))
 92    }
 93
 94    // Helper function to enforce `self <= (p-1)/2`.
 95    pub fn enforce_smaller_or_equal_than_mod_minus_one_div_two(
 96        &self,
 97    ) -> Result<(), SynthesisError> {
 98        let _ = Boolean::enforce_smaller_or_equal_than(
 99            &self.to_bits()?,
100            F::modulus_minus_one_div_two(),
101        )?;
102        Ok(())
103    }
104
105    /// Helper function to check `self < other` and output a result bit. This function
106    /// verifies `self` and `other` are `<= (p-1)/2`.
107    fn is_smaller_than(&self, other: &FpVar<F>) -> Result<Boolean<F>, SynthesisError> {
108        self.enforce_smaller_or_equal_than_mod_minus_one_div_two()?;
109        other.enforce_smaller_or_equal_than_mod_minus_one_div_two()?;
110        self.is_smaller_than_unchecked(other)
111    }
112
113    /// Helper function to check `self < other` and output a result bit. This function
114    /// assumes `self` and `other` are `<= (p-1)/2` and does not generate constraints
115    /// to verify that.
116    fn is_smaller_than_unchecked(&self, other: &FpVar<F>) -> Result<Boolean<F>, SynthesisError> {
117        Ok((self - other).double()?.to_bits()?.last().unwrap().clone())
118    }
119
120    /// Helper function to enforce `self < other`. This function verifies `self` and `other`
121    /// are `<= (p-1)/2`.
122    fn enforce_smaller_than(&self, other: &FpVar<F>) -> Result<(), SynthesisError> {
123        self.enforce_smaller_or_equal_than_mod_minus_one_div_two()?;
124        other.enforce_smaller_or_equal_than_mod_minus_one_div_two()?;
125        self.enforce_smaller_than_unchecked(other)
126    }
127
128    /// Helper function to enforce `self < other`. This function assumes `self` and `other`
129    /// are `<= (p-1)/2` and does not generate constraints to verify that.
130    fn enforce_smaller_than_unchecked(&self, other: &FpVar<F>) -> Result<(), SynthesisError> {
131        let cs = [self, other].cs().unwrap();
132        let is_smaller_than = self.is_smaller_than_unchecked(other)?;
133        let lc_one = lc!() + Variable::One;
134        cs.enforce_constraint(is_smaller_than.lc(), lc_one.clone(), lc_one)
135    }
136}
137
138#[cfg(test)]
139mod test {
140    use rand::{Rng, SeedableRng};
141    use rand_xorshift::XorShiftRng;
142    use std::cmp::Ordering;
143
144    use crate::{alloc::AllocVar, fields::fp::FpVar};
145    use algebra::{bls12_381::Fr, PrimeField, UniformRand};
146    use r1cs_core::ConstraintSystem;
147
148    #[test]
149    fn test_cmp() {
150        let mut rng = &mut XorShiftRng::from_seed([
151            0x5d, 0xbe, 0x62, 0x59, 0x8d, 0x31, 0x3d, 0x76, 0x32, 0x37, 0xdb, 0x17, 0xe5, 0xbc,
152            0x06, 0x54,
153        ]);
154        fn rand_in_range<R: Rng>(rng: &mut R) -> Fr {
155            let pminusonedivtwo: Fr = Fr::modulus_minus_one_div_two().into();
156            let mut r;
157            loop {
158                r = Fr::rand(rng);
159                if r <= pminusonedivtwo {
160                    break;
161                }
162            }
163            r
164        }
165        for i in 0..10 {
166            let cs = ConstraintSystem::<Fr>::new_ref();
167            let a = rand_in_range(&mut rng);
168            let a_var = FpVar::<Fr>::new_witness(cs.ns("a"), || Ok(a)).unwrap();
169            let b = rand_in_range(&mut rng);
170            let b_var = FpVar::<Fr>::new_witness(cs.ns("b"), || Ok(b)).unwrap();
171
172            match a.cmp(&b) {
173                Ordering::Less => {
174                    a_var.enforce_cmp(&b_var, Ordering::Less, false).unwrap();
175                    a_var.enforce_cmp(&b_var, Ordering::Less, true).unwrap();
176                }
177                Ordering::Greater => {
178                    a_var.enforce_cmp(&b_var, Ordering::Greater, false).unwrap();
179                    a_var.enforce_cmp(&b_var, Ordering::Greater, true).unwrap();
180                }
181                _ => {}
182            }
183
184            if i == 0 {
185                println!("number of constraints: {}", cs.num_constraints());
186            }
187            assert!(cs.is_satisfied().unwrap());
188        }
189
190        for _i in 0..10 {
191            let cs = ConstraintSystem::<Fr>::new_ref();
192            let a = rand_in_range(&mut rng);
193            let a_var = FpVar::<Fr>::new_witness(cs.ns("a"), || Ok(a)).unwrap();
194            let b = rand_in_range(&mut rng);
195            let b_var = FpVar::<Fr>::new_witness(cs.ns("b"), || Ok(b)).unwrap();
196
197            match b.cmp(&a) {
198                Ordering::Less => {
199                    a_var.enforce_cmp(&b_var, Ordering::Less, false).unwrap();
200                    a_var.enforce_cmp(&b_var, Ordering::Less, true).unwrap();
201                }
202                Ordering::Greater => {
203                    a_var.enforce_cmp(&b_var, Ordering::Greater, false).unwrap();
204                    a_var.enforce_cmp(&b_var, Ordering::Greater, true).unwrap();
205                }
206                _ => {}
207            }
208
209            assert!(!cs.is_satisfied().unwrap());
210        }
211
212        for _i in 0..10 {
213            let cs = ConstraintSystem::<Fr>::new_ref();
214            let a = rand_in_range(&mut rng);
215            let a_var = FpVar::<Fr>::new_witness(cs.ns("a"), || Ok(a)).unwrap();
216            a_var.enforce_cmp(&a_var, Ordering::Less, false).unwrap();
217
218            assert!(!cs.is_satisfied().unwrap());
219        }
220
221        for _i in 0..10 {
222            let cs = ConstraintSystem::<Fr>::new_ref();
223            let a = rand_in_range(&mut rng);
224            let a_var = FpVar::<Fr>::new_witness(cs.ns("a"), || Ok(a)).unwrap();
225            a_var.enforce_cmp(&a_var, Ordering::Less, true).unwrap();
226            assert!(cs.is_satisfied().unwrap());
227        }
228    }
229}