ark_r1cs_std/uint/add/
saturating.rsuse ark_ff::PrimeField;
use ark_relations::r1cs::SynthesisError;
use crate::uint::*;
use crate::{boolean::Boolean, R1CSVar};
impl<const N: usize, T: PrimUInt, F: PrimeField> UInt<N, T, F> {
pub fn saturating_add_in_place(&mut self, other: &Self) {
let result = Self::saturating_add_many(&[self.clone(), other.clone()]).unwrap();
*self = result;
}
pub fn saturating_add(&self, other: &Self) -> Self {
let mut result = self.clone();
result.saturating_add_in_place(other);
result
}
#[tracing::instrument(target = "r1cs", skip(operands))]
pub fn saturating_add_many(operands: &[Self]) -> Result<Self, SynthesisError>
where
F: PrimeField,
{
let (sum_bits, value) = Self::add_many_helper(operands, |a, b| a.saturating_add(b))?;
if operands.is_constant() {
Ok(UInt::constant(value.unwrap()))
} else if sum_bits.len() == N {
Ok(UInt::from_bits_le(&sum_bits))
} else {
let (bottom_bits, top_bits) = sum_bits.split_at(N);
let bits = TryFrom::try_from(bottom_bits.to_vec()).unwrap();
let candidate_result = UInt { bits, value };
let overflow_occurred = Boolean::kary_or(&top_bits)?;
overflow_occurred.select(&Self::MAX, &candidate_result)
}
}
}
#[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_saturating_add<T: PrimUInt, const N: usize, F: PrimeField>(
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 computed = a.saturating_add(&b);
let expected_mode = if both_constant {
AllocationMode::Constant
} else {
AllocationMode::Witness
};
let expected = UInt::new_variable(
cs.clone(),
|| Ok(a.value()?.saturating_add(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_saturating_add() {
run_binary_exhaustive(uint_saturating_add::<u8, 8, Fr>).unwrap()
}
#[test]
fn u16_saturating_add() {
run_binary_random::<1000, 16, _, _>(uint_saturating_add::<u16, 16, Fr>).unwrap()
}
#[test]
fn u32_saturating_add() {
run_binary_random::<1000, 32, _, _>(uint_saturating_add::<u32, 32, Fr>).unwrap()
}
#[test]
fn u64_saturating_add() {
run_binary_random::<1000, 64, _, _>(uint_saturating_add::<u64, 64, Fr>).unwrap()
}
#[test]
fn u128_saturating_add() {
run_binary_random::<1000, 128, _, _>(uint_saturating_add::<u128, 128, Fr>).unwrap()
}
}