pub trait Absorb<N>: Sized {
// Required methods
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);
}Expand description
What a wide accumulator does with a narrow value.
The wide type is Self, so every implementation sees both types concretely and can be written
in terms of the operators that exist for the pair.
§No defaults
Every method has to be written out. A default body would have to be phrased in terms of
from_narrow, which is the slow path: it materialises a wide value and
hands it to the general routine, so a forgotten override would cost orders of magnitude at the
larger widths and cost nothing at the smallest, which is where it would be tested. Requiring
each body makes leaving one out a compile error rather than a benchmark result.
Two macros write the bodies, so the burden is one line per pair in practice:
absorb_by_ops! for a pair that has operators, absorb_by_widening! for one that does not.
§Extending it
A crate downstream can implement this for its own narrow type against a wide type from here:
impl<const S: usize> Absorb<MyCoefficient> for Big<S> is a foreign trait for a foreign Self
with a local type parameter, which the orphan rule permits. Such an implementation sees Big
concretely and can be as fast as one written here.
A crate that brings its own wide type implements this for it against the narrow types it wants
to read, which is always allowed because Self is then local.
§Agreement
The four operating methods have to agree with from_narrow:
w.add_narrow(n) equals w += W::from_narrow(n), w.sub_narrow(n) equals
w -= W::from_narrow(n), w.mul_narrow(n) equals w * W::from_narrow(n), and
w.add_mul_narrow(n, v) equals w += v * W::from_narrow(n). The tests in this crate check
every implementation against those four statements.
§Example
use relp_num::fixed::One;
use relp_num::{Absorb, RationalBig, RB};
let mut accumulator = RB!(1, 2);
// No `RationalBig` is materialised, and no gcd is taken: the denominator is added into the
// numerator where it stands.
accumulator.add_narrow(&One);
assert_eq!(accumulator, RB!(3, 2));
assert_eq!(RB!(7, 2).mul_narrow(&One), RB!(7, 2));
assert_eq!(<RationalBig as Absorb<One>>::from_narrow(&One), RB!(1));Required Methods§
Sourcefn from_narrow(narrow: &N) -> Self
fn from_narrow(narrow: &N) -> Self
The narrow value in the wide type.
Sourcefn add_narrow(&mut self, narrow: &N)
fn add_narrow(&mut self, narrow: &N)
Add a narrow value to this one.
Sourcefn sub_narrow(&mut self, narrow: &N)
fn sub_narrow(&mut self, narrow: &N)
Subtract a narrow value from this one.
Sourcefn mul_narrow(&self, narrow: &N) -> Self
fn mul_narrow(&self, narrow: &N) -> Self
Multiply this value by a narrow one.
Sourcefn add_mul_narrow(&mut self, narrow: &N, other: &Self)
fn add_mul_narrow(&mut self, narrow: &N, other: &Self)
Add the product of a narrow value and a wide one to this value.
This is the operation an inner product is made of, and the reason it is a method of its own rather than a multiplication followed by an addition: for a narrow value that is one, the multiplication is not a cheap multiplication but no multiplication, and the intermediate it would have produced is not a cheap allocation but no allocation.
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".
Implementors§
impl Absorb<Binary> for Rational8
impl Absorb<Binary> for Rational16
impl Absorb<Binary> for Rational32
impl Absorb<Binary> for Rational64
impl Absorb<Binary> for Rational128
impl Absorb<One> for Rational8
impl Absorb<One> for Rational16
impl Absorb<One> for Rational32
impl Absorb<One> for Rational64
impl Absorb<One> for Rational128
impl Absorb<One> for RationalUsize
impl Absorb<SignedOne> for Rational8
impl Absorb<SignedOne> for Rational16
impl Absorb<SignedOne> for Rational32
impl Absorb<SignedOne> for Rational64
impl Absorb<SignedOne> for Rational128
impl Absorb<Zero> for Rational8
impl Absorb<Zero> for Rational16
impl Absorb<Zero> for Rational32
impl Absorb<Zero> for Rational64
impl Absorb<Zero> for Rational128
impl Absorb<Zero> for RationalUsize
impl Absorb<i8> for Rational8
impl Absorb<i8> for Rational16
impl Absorb<i8> for Rational32
impl Absorb<i8> for Rational64
impl Absorb<i8> for Rational128
impl Absorb<i16> for Rational16
impl Absorb<i16> for Rational32
impl Absorb<i16> for Rational64
impl Absorb<i16> for Rational128
impl Absorb<i32> for Rational32
impl Absorb<i32> for Rational64
impl Absorb<i32> for Rational128
impl Absorb<i64> for Rational64
impl Absorb<i64> for Rational128
impl Absorb<i128> for Rational128
impl Absorb<u8> for Rational8
impl Absorb<u8> for Rational16
impl Absorb<u8> for Rational32
impl Absorb<u8> for Rational64
impl Absorb<u8> for Rational128
impl Absorb<u16> for Rational16
impl Absorb<u16> for Rational32
impl Absorb<u16> for Rational64
impl Absorb<u16> for Rational128
impl Absorb<u32> for Rational32
impl Absorb<u32> for Rational64
impl Absorb<u32> for Rational128
impl Absorb<u64> for Rational64
impl Absorb<u64> for Rational128
impl Absorb<u128> for Rational128
impl<N> Absorb<&N> for Rational8
A provider that hands out references to what it stores is read through them.
impl<N> Absorb<&N> for Rational16where
Rational16: Absorb<N>,
A provider that hands out references to what it stores is read through them.
impl<N> Absorb<&N> for Rational32where
Rational32: Absorb<N>,
A provider that hands out references to what it stores is read through them.
impl<N> Absorb<&N> for Rational64where
Rational64: Absorb<N>,
A provider that hands out references to what it stores is read through them.
impl<N> Absorb<&N> for Rational128where
Rational128: Absorb<N>,
A provider that hands out references to what it stores is read through them.
impl<N> Absorb<&N> for RationalUsizewhere
RationalUsize: Absorb<N>,
impl<N> Absorb<Option<N>> for Rational8
An absent value is zero, so it contributes nothing.
impl<N> Absorb<Option<N>> for Rational16where
Rational16: Absorb<N>,
An absent value is zero, so it contributes nothing.
impl<N> Absorb<Option<N>> for Rational32where
Rational32: Absorb<N>,
An absent value is zero, so it contributes nothing.
impl<N> Absorb<Option<N>> for Rational64where
Rational64: Absorb<N>,
An absent value is zero, so it contributes nothing.
impl<N> Absorb<Option<N>> for Rational128where
Rational128: Absorb<N>,
An absent value is zero, so it contributes nothing.
impl<N> Absorb<Option<N>> for RationalUsizewhere
RationalUsize: Absorb<N>,
impl<const S: usize, N> Absorb<&N> for Big<S>
A matrix provider that hands out references to what it stores is read through them.
impl<const S: usize, N> Absorb<Option<N>> for Big<S>
An absent value is zero, so it contributes nothing.
Sparse rows are stored as Options to keep the zeros out. This lift is written for the wide
type rather than once for every wide type at once, because Self has to be local to the crate
that writes the implementation; a crate defining its own accumulator writes its own.