diff --git a/ipa-core/src/ff/boolean_array.rs b/ipa-core/src/ff/boolean_array.rs index 1689c5b1e..711aa4f18 100644 --- a/ipa-core/src/ff/boolean_array.rs +++ b/ipa-core/src/ff/boolean_array.rs @@ -461,6 +461,13 @@ macro_rules! boolean_array_impl { } } + impl std::ops::Mul for $name { + type Output = Self; + fn mul(self, rhs: Boolean) -> Self::Output { + std::ops::Mul::mul(self, &rhs) + } + } + impl From<$name> for Store { fn from(v: $name) -> Self { v.0 @@ -619,6 +626,16 @@ macro_rules! boolean_array_impl { assert_eq!(iter.len(), $bits - 1 - i); } } + + #[test] + fn owned_iterator(a: $name) { + let mut iter = a.into_iter().enumerate(); + assert_eq!(iter.len(), $bits); + while let Some((i, b)) = iter.next() { + assert_eq!(bool::from(b), a.0[i]); + assert_eq!(iter.len(), $bits - 1 - i); + } + } } #[test] @@ -723,3 +740,79 @@ where self.array } } + +#[cfg(all(test, unit_test))] +mod tests { + use rand::{thread_rng, Rng}; + + use super::*; + + // It does not seem worth running these tests for every BA type, although + // it would be worth writing a version for the largest BA type (which + // requires replacing truncate_from). + + #[test] + #[allow(clippy::clone_on_copy, clippy::op_ref)] + pub fn add_sub() { + let mut rng = thread_rng(); + let a = rng.gen::(); + let b = rng.gen::(); + + let xor = BA8::truncate_from(a ^ b); + + let a = BA8::truncate_from(a); + let b = BA8::truncate_from(b); + + assert_eq!(&a + &b, xor); + assert_eq!(&a + b.clone(), xor); + assert_eq!(a.clone() + &b, xor); + assert_eq!(a.clone() + b.clone(), xor); + + let mut tmp = a.clone(); + tmp += &b; + assert_eq!(tmp, xor); + + let mut tmp = a.clone(); + tmp += b; + assert_eq!(tmp, xor); + + // Sub not implemented yet for &BA + //assert_eq!(&a - &b, xor); + //assert_eq!(&a - b.clone(), xor); + assert_eq!(a.clone() - &b, xor); + assert_eq!(a.clone() - b.clone(), xor); + + let mut tmp = a.clone(); + tmp -= &b; + assert_eq!(tmp, xor); + + let mut tmp = a.clone(); + tmp -= b; + assert_eq!(tmp, xor); + + assert_eq!(-a, a); + assert_eq!(a + (-a), BA8::ZERO); + } + + #[test] + #[allow(clippy::clone_on_copy, clippy::op_ref)] + pub fn mul() { + let mut rng = thread_rng(); + let a = rng.gen::(); + let b = rng.gen::(); + let c = rng.gen::(); + + let prod = BA8::truncate_from(a & b); + + let mut a = BA8::truncate_from(a); + let b = BA8::truncate_from(b); + + a *= b; + assert_eq!(a, prod); + + assert_eq!(a * Boolean::from(false), BA8::ZERO); + assert_eq!(a * Boolean::from(true), a); + assert_eq!(a * c, if bool::from(c) { a } else { BA8::ZERO }); + assert_eq!(a * &c, if bool::from(c) { a } else { BA8::ZERO }); + } +} diff --git a/ipa-core/src/protocol/ipa_prf/boolean_ops/comparison_and_subtraction_sequential.rs b/ipa-core/src/protocol/ipa_prf/boolean_ops/comparison_and_subtraction_sequential.rs index 3dec75a79..43abfeacc 100644 --- a/ipa-core/src/protocol/ipa_prf/boolean_ops/comparison_and_subtraction_sequential.rs +++ b/ipa-core/src/protocol/ipa_prf/boolean_ops/comparison_and_subtraction_sequential.rs @@ -394,6 +394,7 @@ mod test { #[test] #[ignore] // benchmark + #[cfg(not(coverage))] fn semi_honest_compare_gt_novec() { run(|| async move { let world = TestWorld::default(); @@ -440,6 +441,7 @@ mod test { #[test] #[ignore] // benchmark + #[cfg(not(coverage))] fn semi_honest_compare_gt_vec() { run(|| async move { const N: usize = 256; diff --git a/ipa-core/src/secret_sharing/decomposed.rs b/ipa-core/src/secret_sharing/decomposed.rs index 3861ffb98..83555d3ab 100644 --- a/ipa-core/src/secret_sharing/decomposed.rs +++ b/ipa-core/src/secret_sharing/decomposed.rs @@ -177,3 +177,99 @@ impl ArrayAccessRef for BitDecomposed { src } } + +#[cfg(all(test, unit_test))] +mod tests { + use proptest::prelude::*; + + use super::*; + + const MAX_TEST_SIZE: usize = 1024; + + impl Arbitrary for BitDecomposed + where + S: Debug, + Vec: Arbitrary, + { + type Parameters = as Arbitrary>::Parameters; + type Strategy = prop::strategy::Map< as Arbitrary>::Strategy, fn(Vec) -> Self>; + + fn arbitrary_with(args: Self::Parameters) -> Self::Strategy { + Vec::::arbitrary_with(args).prop_map(|bits| Self { bits }) + } + } + + prop_compose! { + fn val_and_index() + (vec in prop::collection::vec(any::(), 2..MAX_TEST_SIZE)) + (index in 0..vec.len(), vec in Just(vec)) + -> (BitDecomposed, usize) { + (BitDecomposed { bits: vec }, index) + } + } + + proptest! { + #[test] + fn arrayaccess_get_set( + (mut a, ix) in val_and_index(), + b: u8, + c: u8, + d: u8, + ) { + prop_assert_eq!(a.get(0), Some(&a.bits[0])); + a.set(0, &b); + prop_assert_eq!(a.get(0), Some(&b)); + prop_assert_eq!(a.get(ix), Some(&a.bits[ix])); + a.set(ix, &c); + prop_assert_eq!(a.get(ix), Some(&c)); + prop_assert_eq!(a.get(a.len() - 1), Some(&a.bits[a.len() - 1])); + a.set(a.len() - 1, &d); + prop_assert_eq!(a.get(a.len() - 1), Some(&d)); + prop_assert_eq!(a.get(a.len()), None); + let BitDecomposed { + bits: mut a_mod, + } = a.clone(); + a_mod[0] = b; + a_mod[ix] = c; + a_mod[a.len() - 1] = d; + prop_assert_eq!(a.bits, a_mod); + } + + #[test] + fn arrayaccess_iter(val in any::>()) { + let mut iter = val.iter().enumerate(); + prop_assert_eq!(iter.len(), val.len()); + while let Some((i, v)) = iter.next() { + prop_assert_eq!(v, &val.bits[i]); + prop_assert_eq!(iter.len(), val.len() - 1 - i); + } + } + + #[test] + fn arrayaccess_make_ref(val in any::()) { + prop_assert_eq!( as ArrayAccessRef>::make_ref(&val), &val); + } + } + + #[test] + fn arraybuild() { + let mut b = BitDecomposed::::builder(); + b.push(1); + b.push(2); + b.push(3); + assert_eq!( + b.build(), + BitDecomposed { + bits: vec![1, 2, 3] + } + ); + } + + proptest! { + #[test] + fn arraybuild_with_capacity(capacity in 0..MAX_TEST_SIZE) { + let b = BitDecomposed::::builder().with_capacity(capacity); + prop_assert!(b.bits.capacity() >= capacity); + } + } +} diff --git a/ipa-core/src/secret_sharing/mod.rs b/ipa-core/src/secret_sharing/mod.rs index 73fe80a9b..4270dd6d5 100644 --- a/ipa-core/src/secret_sharing/mod.rs +++ b/ipa-core/src/secret_sharing/mod.rs @@ -253,6 +253,7 @@ pub trait SharedValueArray: pub trait FieldArray: SharedValueArray + FromRandom + + for<'a> Mul + for<'a> Mul<&'a F, Output = Self> + for<'a> Mul<&'a Self, Output = Self> {