use crate::Field;
pub trait Absorb<N>: Sized {
fn from_narrow(narrow: &N) -> Self;
fn add_narrow(&mut self, narrow: &N);
fn sub_narrow(&mut self, narrow: &N);
fn mul_narrow(&self, narrow: &N) -> Self;
fn add_mul_narrow(&mut self, narrow: &N, other: &Self);
}
pub trait AbsorbAll<N>: Field + Absorb<N> {}
impl<N, W: Field + Absorb<N>> AbsorbAll<N> for W {}
#[cfg(test)]
mod test {
use crate::{Absorb, AbsorbAll, Field, FieldRef, Rational8, Rational16, Rational32, Rational64, Rational128, RationalBig, RB};
use crate::fixed::{Binary, One, SignedOne, Zero};
fn check<T>(value: &T, wide: RationalBig)
where
RationalBig: Absorb<T>,
{
assert_eq!(RationalBig::from_narrow(value), wide, "from_narrow");
let start = RB!(5, 3);
let mut by_override = start.clone();
by_override.add_narrow(value);
assert_eq!(by_override, start.clone() + wide.clone(), "add_narrow");
let mut by_override = start.clone();
by_override.sub_narrow(value);
assert_eq!(by_override, start.clone() - wide.clone(), "sub_narrow");
assert_eq!(start.mul_narrow(value), wide.clone() * start.clone(), "mul_narrow");
assert_eq!(RB!(0).mul_narrow(value), RB!(0), "mul_narrow by zero");
for other in [RB!(0), RB!(1), RB!(-7, 5), start.clone()] {
let mut fused = start.clone();
fused.add_mul_narrow(value, &other);
assert_eq!(
fused,
start.clone() + wide.clone() * other.clone(),
"add_mul_narrow by {other}",
);
}
}
#[test]
fn markers_agree_with_converting() {
check(&One, RB!(1));
check(&Zero, RB!(0));
check(&Binary::Zero, RB!(0));
check(&Binary::One, RB!(1));
check(&SignedOne::PlusOne, RB!(1));
check(&SignedOne::MinusOne, RB!(-1));
}
#[test]
fn options_and_references_agree_with_converting() {
check(&Some(One), RB!(1));
check(&None::<One>, RB!(0));
check(&Some(SignedOne::MinusOne), RB!(-1));
check(&&One, RB!(1));
check(&Some(&SignedOne::MinusOne), RB!(-1));
check(&None::<Rational64>, RB!(0));
check(&Some(Rational64::new(3, 4).unwrap()), RB!(3, 4));
}
#[test]
fn integers_and_rationals_agree_with_converting() {
check(&7_i32, RB!(7));
check(&-7_i64, RB!(-7));
check(&0_u8, RB!(0));
check(&0_i16, RB!(0));
check(&Rational8::new(3, 4).unwrap(), RB!(3, 4));
check(&Rational16::new(-3, 4).unwrap(), RB!(-3, 4));
check(&Rational32::new(3, 4).unwrap(), RB!(3, 4));
check(&Rational64::new(3, 4).unwrap(), RB!(3, 4));
check(&Rational128::new(3, 4).unwrap(), RB!(3, 4));
check(&RB!(-9, 4), RB!(-9, 4));
}
#[test]
fn pointer_widths() {
check(&7_usize, RB!(7));
check(&7_isize, RB!(7));
check(&(-7_isize), RB!(-7));
}
fn with_field_ref<F: Field>(a: &F, b: &F) -> F
where
for<'r> &'r F: FieldRef<F>,
{
&(a * b) + a
}
fn with_absorb<F: AbsorbAll<F>>(a: &F, b: &F) -> F {
let mut result = a.mul_narrow(b);
result.add_narrow(a);
result
}
#[test]
fn one_bound_replaces_the_higher_ranked_pair() {
let a = RB!(3, 2);
let b = RB!(5, 7);
assert_eq!(with_field_ref(&a, &b), with_absorb(&a, &b));
assert_eq!(with_absorb(&a, &b), RB!(3, 2) * RB!(5, 7) + RB!(3, 2));
}
fn inner_product<W: AbsorbAll<N>, N>(column: &[(usize, N)], pi: &[W]) -> W {
let mut total = W::zero();
for (index, value) in column {
total.add_mul_narrow(value, &pi[*index]);
}
total
}
#[test]
fn inner_product_over_any_narrow_type() {
let pi = [RB!(5), RB!(4), RB!(-1, 2)];
assert_eq!(inner_product(&[(0, One), (2, One)], &pi), RB!(9, 2));
assert_eq!(
inner_product(&[(0, SignedOne::MinusOne), (1, SignedOne::PlusOne)], &pi),
RB!(-1),
);
let column = [(0, Rational64::new(1, 5).unwrap()), (1, Rational64::new(1, 2).unwrap())];
assert_eq!(inner_product(&column, &pi), RB!(3));
assert_eq!(inner_product(&[(1, 3_i64)], &pi), RB!(12));
assert_eq!(inner_product(&[(0, RB!(2))], &pi), RB!(10));
}
#[test]
fn references_and_absent_values() {
let pi = [RB!(5), RB!(4)];
let column: Vec<(usize, Option<One>)> = vec![(0, Some(One)), (1, None)];
assert_eq!(inner_product(&column, &pi), RB!(5));
let stored = [One, One];
let column: Vec<(usize, &One)> = vec![(0, &stored[0]), (1, &stored[1])];
assert_eq!(inner_product(&column, &pi), RB!(9));
}
}