use crate::cmp::CmpGadget;
use super::*;
impl<const N: usize, T: PrimUInt, F: PrimeField + From<T>> CmpGadget<F> for UInt<N, T, F> {
fn is_ge(&self, other: &Self) -> Result<Boolean<F>, SynthesisError> {
if N + 1 < ((F::MODULUS_BIT_SIZE - 1) as usize) {
let a = self.to_fp()?;
let b = other.to_fp()?;
let (bits, _) = (a - b + F::from(T::max_value()) + F::one())
.to_bits_le_with_top_bits_zero(N + 1)?;
Ok(bits.last().unwrap().clone())
} else {
unimplemented!("bit sizes larger than modulus size not yet supported")
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
alloc::{AllocVar, AllocationMode},
prelude::EqGadget,
uint::test_utils::{run_binary_exhaustive, run_binary_random},
R1CSVar,
};
use ark_ff::PrimeField;
use ark_test_curves::bls12_381::Fr;
fn uint_gt<T: PrimUInt, const N: usize, F: PrimeField + From<T>>(
a: UInt<N, T, F>,
b: UInt<N, T, F>,
) -> Result<(), SynthesisError> {
let cs = a.cs().or(b.cs());
let both_constant = a.is_constant() && b.is_constant();
let expected_mode = if both_constant {
AllocationMode::Constant
} else {
AllocationMode::Witness
};
let computed = a.is_gt(&b)?;
let expected =
Boolean::new_variable(cs.clone(), || Ok(a.value()? > b.value()?), expected_mode)?;
assert_eq!(expected.value(), computed.value());
expected.enforce_equal(&computed)?;
if !both_constant {
assert!(cs.is_satisfied().unwrap());
}
Ok(())
}
fn uint_lt<T: PrimUInt, const N: usize, F: PrimeField + From<T>>(
a: UInt<N, T, F>,
b: UInt<N, T, F>,
) -> Result<(), SynthesisError> {
let cs = a.cs().or(b.cs());
let both_constant = a.is_constant() && b.is_constant();
let expected_mode = if both_constant {
AllocationMode::Constant
} else {
AllocationMode::Witness
};
let computed = a.is_lt(&b)?;
let expected =
Boolean::new_variable(cs.clone(), || Ok(a.value()? < b.value()?), expected_mode)?;
assert_eq!(expected.value(), computed.value());
expected.enforce_equal(&computed)?;
if !both_constant {
assert!(cs.is_satisfied().unwrap());
}
Ok(())
}
fn uint_ge<T: PrimUInt, const N: usize, F: PrimeField + From<T>>(
a: UInt<N, T, F>,
b: UInt<N, T, F>,
) -> Result<(), SynthesisError> {
let cs = a.cs().or(b.cs());
let both_constant = a.is_constant() && b.is_constant();
let expected_mode = if both_constant {
AllocationMode::Constant
} else {
AllocationMode::Witness
};
let computed = a.is_ge(&b)?;
let expected =
Boolean::new_variable(cs.clone(), || Ok(a.value()? >= b.value()?), expected_mode)?;
assert_eq!(expected.value(), computed.value());
expected.enforce_equal(&computed)?;
if !both_constant {
assert!(cs.is_satisfied().unwrap());
}
Ok(())
}
fn uint_le<T: PrimUInt, const N: usize, F: PrimeField + From<T>>(
a: UInt<N, T, F>,
b: UInt<N, T, F>,
) -> Result<(), SynthesisError> {
let cs = a.cs().or(b.cs());
let both_constant = a.is_constant() && b.is_constant();
let expected_mode = if both_constant {
AllocationMode::Constant
} else {
AllocationMode::Witness
};
let computed = a.is_le(&b)?;
let expected =
Boolean::new_variable(cs.clone(), || Ok(a.value()? <= b.value()?), expected_mode)?;
assert_eq!(expected.value(), computed.value());
expected.enforce_equal(&computed)?;
if !both_constant {
assert!(cs.is_satisfied().unwrap());
}
Ok(())
}
#[test]
fn u8_gt() {
run_binary_exhaustive(uint_gt::<u8, 8, Fr>).unwrap()
}
#[test]
fn u16_gt() {
run_binary_random::<1000, 16, _, _>(uint_gt::<u16, 16, Fr>).unwrap()
}
#[test]
fn u32_gt() {
run_binary_random::<1000, 32, _, _>(uint_gt::<u32, 32, Fr>).unwrap()
}
#[test]
fn u64_gt() {
run_binary_random::<1000, 64, _, _>(uint_gt::<u64, 64, Fr>).unwrap()
}
#[test]
fn u128_gt() {
run_binary_random::<1000, 128, _, _>(uint_gt::<u128, 128, Fr>).unwrap()
}
#[test]
fn u8_lt() {
run_binary_exhaustive(uint_lt::<u8, 8, Fr>).unwrap()
}
#[test]
fn u16_lt() {
run_binary_random::<1000, 16, _, _>(uint_lt::<u16, 16, Fr>).unwrap()
}
#[test]
fn u32_lt() {
run_binary_random::<1000, 32, _, _>(uint_lt::<u32, 32, Fr>).unwrap()
}
#[test]
fn u64_lt() {
run_binary_random::<1000, 64, _, _>(uint_lt::<u64, 64, Fr>).unwrap()
}
#[test]
fn u128_lt() {
run_binary_random::<1000, 128, _, _>(uint_lt::<u128, 128, Fr>).unwrap()
}
#[test]
fn u8_le() {
run_binary_exhaustive(uint_le::<u8, 8, Fr>).unwrap()
}
#[test]
fn u16_le() {
run_binary_random::<1000, 16, _, _>(uint_le::<u16, 16, Fr>).unwrap()
}
#[test]
fn u32_le() {
run_binary_random::<1000, 32, _, _>(uint_le::<u32, 32, Fr>).unwrap()
}
#[test]
fn u64_le() {
run_binary_random::<1000, 64, _, _>(uint_le::<u64, 64, Fr>).unwrap()
}
#[test]
fn u128_le() {
run_binary_random::<1000, 128, _, _>(uint_le::<u128, 128, Fr>).unwrap()
}
#[test]
fn u8_ge() {
run_binary_exhaustive(uint_ge::<u8, 8, Fr>).unwrap()
}
#[test]
fn u16_ge() {
run_binary_random::<1000, 16, _, _>(uint_ge::<u16, 16, Fr>).unwrap()
}
#[test]
fn u32_ge() {
run_binary_random::<1000, 32, _, _>(uint_ge::<u32, 32, Fr>).unwrap()
}
#[test]
fn u64_ge() {
run_binary_random::<1000, 64, _, _>(uint_ge::<u64, 64, Fr>).unwrap()
}
#[test]
fn u128_ge() {
run_binary_random::<1000, 128, _, _>(uint_ge::<u128, 128, Fr>).unwrap()
}
}