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 // It's okay to use `to_non_unique_bits` bits here because we're enforcing
99 // self <= (p-1)/2, which implies self < p.
100 let _ = Boolean::enforce_smaller_or_equal_than_le(
101 &self.to_non_unique_bits_le()?,
102 F::modulus_minus_one_div_two(),
103 )?;
104 Ok(())
105 }
106
107 /// Helper function to check `self < other` and output a result bit. This function
108 /// verifies `self` and `other` are `<= (p-1)/2`.
109 fn is_smaller_than(&self, other: &FpVar<F>) -> Result<Boolean<F>, SynthesisError> {
110 self.enforce_smaller_or_equal_than_mod_minus_one_div_two()?;
111 other.enforce_smaller_or_equal_than_mod_minus_one_div_two()?;
112 self.is_smaller_than_unchecked(other)
113 }
114
115 /// Helper function to check `self < other` and output a result bit. This function
116 /// assumes `self` and `other` are `<= (p-1)/2` and does not generate constraints
117 /// to verify that.
118 fn is_smaller_than_unchecked(&self, other: &FpVar<F>) -> Result<Boolean<F>, SynthesisError> {
119 Ok((self - other)
120 .double()?
121 .to_bits_le()?
122 .first()
123 .unwrap()
124 .clone())
125 }
126
127 /// Helper function to enforce `self < other`. This function verifies `self` and `other`
128 /// are `<= (p-1)/2`.
129 fn enforce_smaller_than(&self, other: &FpVar<F>) -> Result<(), SynthesisError> {
130 self.enforce_smaller_or_equal_than_mod_minus_one_div_two()?;
131 other.enforce_smaller_or_equal_than_mod_minus_one_div_two()?;
132 self.enforce_smaller_than_unchecked(other)
133 }
134
135 /// Helper function to enforce `self < other`. This function assumes `self` and `other`
136 /// are `<= (p-1)/2` and does not generate constraints to verify that.
137 fn enforce_smaller_than_unchecked(&self, other: &FpVar<F>) -> Result<(), SynthesisError> {
138 let cs = [self, other].cs().unwrap();
139 let is_smaller_than = self.is_smaller_than_unchecked(other)?;
140 let lc_one = lc!() + Variable::One;
141 cs.enforce_constraint(is_smaller_than.lc(), lc_one.clone(), lc_one)
142 }
143}
144
145#[cfg(test)]
146mod test {
147 use rand::{Rng, SeedableRng};
148 use rand_xorshift::XorShiftRng;
149 use std::cmp::Ordering;
150
151 use crate::{alloc::AllocVar, fields::fp::FpVar};
152 use algebra::{bls12_381::Fr, PrimeField, UniformRand};
153 use r1cs_core::ConstraintSystem;
154
155 #[test]
156 fn test_cmp() {
157 let mut rng = &mut XorShiftRng::from_seed([
158 0x5d, 0xbe, 0x62, 0x59, 0x8d, 0x31, 0x3d, 0x76, 0x32, 0x37, 0xdb, 0x17, 0xe5, 0xbc,
159 0x06, 0x54,
160 ]);
161 fn rand_in_range<R: Rng>(rng: &mut R) -> Fr {
162 let pminusonedivtwo: Fr = Fr::modulus_minus_one_div_two().into();
163 let mut r;
164 loop {
165 r = Fr::rand(rng);
166 if r <= pminusonedivtwo {
167 break;
168 }
169 }
170 r
171 }
172 for i in 0..10 {
173 let cs = ConstraintSystem::<Fr>::new_ref();
174 let a = rand_in_range(&mut rng);
175 let a_var = FpVar::<Fr>::new_witness(cs.ns("a"), || Ok(a)).unwrap();
176 let b = rand_in_range(&mut rng);
177 let b_var = FpVar::<Fr>::new_witness(cs.ns("b"), || Ok(b)).unwrap();
178
179 match a.cmp(&b) {
180 Ordering::Less => {
181 a_var.enforce_cmp(&b_var, Ordering::Less, false).unwrap();
182 a_var.enforce_cmp(&b_var, Ordering::Less, true).unwrap();
183 }
184 Ordering::Greater => {
185 a_var.enforce_cmp(&b_var, Ordering::Greater, false).unwrap();
186 a_var.enforce_cmp(&b_var, Ordering::Greater, true).unwrap();
187 }
188 _ => {}
189 }
190
191 if i == 0 {
192 println!("number of constraints: {}", cs.num_constraints());
193 }
194 assert!(cs.is_satisfied().unwrap());
195 }
196 println!("Finished with satisfaction tests");
197
198 for _i in 0..10 {
199 let cs = ConstraintSystem::<Fr>::new_ref();
200 let a = rand_in_range(&mut rng);
201 let a_var = FpVar::<Fr>::new_witness(cs.ns("a"), || Ok(a)).unwrap();
202 let b = rand_in_range(&mut rng);
203 let b_var = FpVar::<Fr>::new_witness(cs.ns("b"), || Ok(b)).unwrap();
204
205 match b.cmp(&a) {
206 Ordering::Less => {
207 a_var.enforce_cmp(&b_var, Ordering::Less, false).unwrap();
208 a_var.enforce_cmp(&b_var, Ordering::Less, true).unwrap();
209 }
210 Ordering::Greater => {
211 a_var.enforce_cmp(&b_var, Ordering::Greater, false).unwrap();
212 a_var.enforce_cmp(&b_var, Ordering::Greater, true).unwrap();
213 }
214 _ => {}
215 }
216
217 assert!(!cs.is_satisfied().unwrap());
218 }
219
220 for _i in 0..10 {
221 let cs = ConstraintSystem::<Fr>::new_ref();
222 let a = rand_in_range(&mut rng);
223 let a_var = FpVar::<Fr>::new_witness(cs.ns("a"), || Ok(a)).unwrap();
224 a_var.enforce_cmp(&a_var, Ordering::Less, false).unwrap();
225
226 assert!(!cs.is_satisfied().unwrap());
227 }
228
229 for _i in 0..10 {
230 let cs = ConstraintSystem::<Fr>::new_ref();
231 let a = rand_in_range(&mut rng);
232 let a_var = FpVar::<Fr>::new_witness(cs.ns("a"), || Ok(a)).unwrap();
233 a_var.enforce_cmp(&a_var, Ordering::Less, true).unwrap();
234 assert!(cs.is_satisfied().unwrap());
235 }
236 }
237}