#[cfg(feature = "ffi")]
mod ffi;
mod int;
pub use int::*;
mod float;
pub use float::*;
use opendp_derive::bootstrap;
use crate::core::{Metric, MetricSpace, Transformation};
use crate::domains::{AtomDomain, VectorDomain};
use crate::error::*;
use crate::metrics::{AbsoluteDistance, InsertDeleteDistance, SymmetricDistance};
use crate::traits::CheckAtom;
use crate::transformations::{make_ordered_random, make_unordered};
use int::signs_agree;
#[cfg(all(test, feature = "partials"))]
mod test;
#[bootstrap(features("contrib"), generics(MI(suppress), T(suppress)))]
pub fn make_sum<MI, T>(
input_domain: VectorDomain<AtomDomain<T>>,
input_metric: MI,
) -> Fallible<Transformation<VectorDomain<AtomDomain<T>>, MI, AtomDomain<T>, AbsoluteDistance<T>>>
where
MI: Metric,
T: MakeSum<MI>,
(VectorDomain<AtomDomain<T>>, MI): MetricSpace,
(AtomDomain<T>, AbsoluteDistance<T>): MetricSpace,
{
T::make_sum(input_domain, input_metric)
}
#[doc(hidden)]
pub trait MakeSum<MI: Metric>: CheckAtom
where
(VectorDomain<AtomDomain<Self>>, MI): MetricSpace,
(AtomDomain<Self>, AbsoluteDistance<Self>): MetricSpace,
{
fn make_sum(
input_domain: VectorDomain<AtomDomain<Self>>,
input_metric: MI,
) -> Fallible<
Transformation<
VectorDomain<AtomDomain<Self>>,
MI,
AtomDomain<Self>,
AbsoluteDistance<Self>,
>,
>;
}
macro_rules! impl_make_sum_int {
($($ty:ty)+) => {
$(impl MakeSum<SymmetricDistance> for $ty {
fn make_sum(
input_domain: VectorDomain<AtomDomain<Self>>,
_input_metric: SymmetricDistance,
) -> Fallible<Transformation<VectorDomain<AtomDomain<Self>>, SymmetricDistance, AtomDomain<Self>, AbsoluteDistance<Self>>> {
let bounds = input_domain.element_domain.bounds
.ok_or_else(|| err!(MakeTransformation, "`input_domain` must be bounded. Use `make_clamp` to bound data."))?
.get_closed()?;
if let Some(size) = input_domain.size {
if !can_int_sum_overflow(size, bounds) {
make_sized_bounded_int_checked_sum(size, bounds)
} else if signs_agree(bounds) {
make_sized_bounded_int_monotonic_sum(size, bounds)
} else {
make_sized_bounded_int_split_sum(size, bounds)
}
} else {
if signs_agree(bounds) {
make_bounded_int_monotonic_sum(bounds)
} else {
make_bounded_int_split_sum(bounds)
}
}
}
})+
$(impl MakeSum<InsertDeleteDistance> for $ty {
fn make_sum(
input_domain: VectorDomain<AtomDomain<Self>>,
input_metric: InsertDeleteDistance,
) -> Fallible<Transformation<VectorDomain<AtomDomain<Self>>, InsertDeleteDistance, AtomDomain<Self>, AbsoluteDistance<Self>>> {
let bounds = input_domain.element_domain.bounds
.ok_or_else(|| err!(MakeTransformation, "`input_domain` must be bounded. Use `make_clamp` to bound data."))?
.get_closed()?;
if let Some(size) = input_domain.size {
if !can_int_sum_overflow(size, bounds) {
let domain = VectorDomain::new(AtomDomain::new_closed(bounds)?).with_size(size);
make_unordered(domain, input_metric)? >> make_sized_bounded_int_checked_sum(size, bounds)?
} else {
make_sized_bounded_int_ordered_sum(size, bounds)
}
} else {
make_bounded_int_ordered_sum(bounds)
}
}
})+
};
}
impl_make_sum_int! { u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize }
const DEFAULT_SIZE_LIMIT: usize = 1_048_576; macro_rules! impl_make_sum_float {
($($ty:ty)+) => {
$(impl MakeSum<SymmetricDistance> for $ty {
fn make_sum(
input_domain: VectorDomain<AtomDomain<Self>>,
input_metric: SymmetricDistance,
) -> Fallible<Transformation<VectorDomain<AtomDomain<Self>>, SymmetricDistance, AtomDomain<Self>, AbsoluteDistance<Self>>> {
let bounds = input_domain.element_domain.bounds.as_ref()
.ok_or_else(|| err!(MakeTransformation, "`input_domain` must be bounded. Use `make_clamp` to bound data."))?
.get_closed()?;
if let Some(size) = input_domain.size {
if !Pairwise::<Self>::can_float_sum_overflow(size, bounds)? {
make_sized_bounded_float_checked_sum::<Pairwise<_>>(size, bounds)
} else {
make_ordered_random(input_domain, input_metric)? >> make_sized_bounded_float_ordered_sum::<Pairwise<_>>(size, bounds)?
}
} else {
if !Pairwise::<Self>::can_float_sum_overflow(DEFAULT_SIZE_LIMIT, bounds)? {
make_bounded_float_checked_sum::<Pairwise<_>>(DEFAULT_SIZE_LIMIT, bounds)
} else {
make_ordered_random(input_domain, input_metric)? >> make_bounded_float_ordered_sum::<Pairwise<_>>(DEFAULT_SIZE_LIMIT, bounds)?
}
}
}
})+
$(impl MakeSum<InsertDeleteDistance> for $ty {
fn make_sum(
input_domain: VectorDomain<AtomDomain<Self>>,
input_metric: InsertDeleteDistance,
) -> Fallible<Transformation<VectorDomain<AtomDomain<Self>>, InsertDeleteDistance, AtomDomain<Self>, AbsoluteDistance<Self>>> {
let bounds = input_domain.element_domain.bounds.as_ref()
.ok_or_else(|| err!(MakeTransformation, "`input_domain` must be bounded. Use `make_clamp` to bound data."))?
.get_closed()?;
if let Some(size) = input_domain.size {
if !Pairwise::<Self>::can_float_sum_overflow(size, bounds)? {
make_unordered(input_domain, input_metric)? >> make_sized_bounded_float_checked_sum::<Pairwise<_>>(size, bounds)?
} else {
make_sized_bounded_float_ordered_sum::<Pairwise<_>>(size, bounds)
}
} else {
if !Pairwise::<Self>::can_float_sum_overflow(DEFAULT_SIZE_LIMIT, bounds)? {
make_unordered(input_domain, input_metric)? >> make_bounded_float_checked_sum::<Pairwise<_>>(DEFAULT_SIZE_LIMIT, bounds)?
} else {
make_bounded_float_ordered_sum::<Pairwise<_>>(DEFAULT_SIZE_LIMIT, bounds)
}
}
}
})+
};
}
impl_make_sum_float! { f32 f64 }