Skip to main content

relp_num/rational/big/
absorb.rs

1//! # Reading narrow values into an arbitrary precision rational
2//!
3//! One implementation per pair, each written in terms of the operators that this crate already
4//! defines for that pair. Those operators are where the shortcuts live: adding an integer leaves
5//! the denominator alone, multiplying by a value that fits in a word takes a single word gcd
6//! rather than a binary one over every word, and multiplying by [`One`] touches nothing.
7//!
8//! Nothing here converts a narrow value and hands it to the general routine unless the pair has no
9//! operators at all, and where that happens it is written out by [`absorb_by_widening`] rather
10//! than inherited from a default, so it is visible in the source.
11
12use crate::{Absorb, Rational8, Rational16, Rational32, Rational64, Rational128};
13use crate::fixed::{Binary, One, SignedOne, Zero};
14use crate::rational::big::Big;
15
16/// Read a narrow value through the operators written for it and the wide type.
17///
18/// The multiplication goes through `MulAssign` on a clone rather than `Mul` on a reference,
19/// because the assigning form is the one every narrow type here has.
20macro_rules! absorb_by_ops {
21    ($($narrow:ty),+ $(,)?) => {$(
22        impl<const S: usize> Absorb<$narrow> for Big<S> {
23            #[inline]
24            fn from_narrow(narrow: &$narrow) -> Self {
25                Self::from(*narrow)
26            }
27            #[inline]
28            fn add_narrow(&mut self, narrow: &$narrow) {
29                *self += *narrow;
30            }
31            #[inline]
32            fn sub_narrow(&mut self, narrow: &$narrow) {
33                *self -= *narrow;
34            }
35            #[inline]
36            fn mul_narrow(&self, narrow: &$narrow) -> Self {
37                let mut result = self.clone();
38                result *= *narrow;
39                result
40            }
41            #[inline]
42            fn add_mul_narrow(&mut self, narrow: &$narrow, other: &Self) {
43                let mut product = other.clone();
44                product *= *narrow;
45                *self += product;
46            }
47        }
48    )+}
49}
50
51absorb_by_ops!(u8, u16, u32, u64, usize);
52absorb_by_ops!(i8, i16, i32, i64, isize);
53absorb_by_ops!(Rational8, Rational16, Rational32, Rational64);
54
55/// Read a narrow value by converting it, for a pair that has no operators of its own.
56///
57/// This is the body every method would have had by default. It is written out because it is the
58/// expensive one: converting produces a value whose denominator carries no information, and the
59/// general routine then does the work of finding that out.
60macro_rules! absorb_by_widening {
61    ($($narrow:ty),+ $(,)?) => {$(
62        impl<const S: usize> Absorb<$narrow> for Big<S> {
63            #[inline]
64            fn from_narrow(narrow: &$narrow) -> Self {
65                Self::from(*narrow)
66            }
67            #[inline]
68            fn add_narrow(&mut self, narrow: &$narrow) {
69                *self += Self::from(*narrow);
70            }
71            #[inline]
72            fn sub_narrow(&mut self, narrow: &$narrow) {
73                *self -= Self::from(*narrow);
74            }
75            #[inline]
76            fn mul_narrow(&self, narrow: &$narrow) -> Self {
77                let mut result = self.clone();
78                result *= Self::from(*narrow);
79                result
80            }
81            #[inline]
82            fn add_mul_narrow(&mut self, narrow: &$narrow, other: &Self) {
83                let mut product = other.clone();
84                product *= Self::from(*narrow);
85                *self += product;
86            }
87        }
88    )+}
89}
90
91// `Rational128` converts but has no operators against `Big`, so it goes the long way around.
92absorb_by_widening!(Rational128);
93
94impl<const S: usize> Absorb<One> for Big<S> {
95    #[inline]
96    fn from_narrow(_: &One) -> Self {
97        Self::from(One)
98    }
99    #[inline]
100    fn add_narrow(&mut self, _: &One) {
101        *self += One;
102    }
103    #[inline]
104    fn sub_narrow(&mut self, _: &One) {
105        *self -= One;
106    }
107    #[inline]
108    fn mul_narrow(&self, _: &One) -> Self {
109        self.clone()
110    }
111    #[inline]
112    fn add_mul_narrow(&mut self, _: &One, other: &Self) {
113        *self += other;
114    }
115}
116
117impl<const S: usize> Absorb<Zero> for Big<S> {
118    #[inline]
119    fn from_narrow(_: &Zero) -> Self {
120        Self::from(Zero)
121    }
122    #[inline]
123    fn add_narrow(&mut self, _: &Zero) {}
124    #[inline]
125    fn sub_narrow(&mut self, _: &Zero) {}
126    #[inline]
127    fn mul_narrow(&self, _: &Zero) -> Self {
128        Self::from(Zero)
129    }
130    #[inline]
131    fn add_mul_narrow(&mut self, _: &Zero, _: &Self) {}
132}
133
134impl<const S: usize> Absorb<Binary> for Big<S> {
135    #[inline]
136    fn from_narrow(narrow: &Binary) -> Self {
137        Self::from(*narrow)
138    }
139    #[inline]
140    fn add_narrow(&mut self, narrow: &Binary) {
141        match narrow {
142            Binary::Zero => {}
143            Binary::One => *self += One,
144        }
145    }
146    #[inline]
147    fn sub_narrow(&mut self, narrow: &Binary) {
148        match narrow {
149            Binary::Zero => {}
150            Binary::One => *self -= One,
151        }
152    }
153    #[inline]
154    fn mul_narrow(&self, narrow: &Binary) -> Self {
155        match narrow {
156            Binary::Zero => Self::from(Zero),
157            Binary::One => self.clone(),
158        }
159    }
160    #[inline]
161    fn add_mul_narrow(&mut self, narrow: &Binary, other: &Self) {
162        match narrow {
163            Binary::Zero => {}
164            Binary::One => *self += other,
165        }
166    }
167}
168
169impl<const S: usize> Absorb<SignedOne> for Big<S> {
170    #[inline]
171    fn from_narrow(narrow: &SignedOne) -> Self {
172        match narrow {
173            SignedOne::PlusOne => Self::from(One),
174            SignedOne::MinusOne => -Self::from(One),
175        }
176    }
177    #[inline]
178    fn add_narrow(&mut self, narrow: &SignedOne) {
179        match narrow {
180            SignedOne::PlusOne => *self += One,
181            SignedOne::MinusOne => *self -= One,
182        }
183    }
184    #[inline]
185    fn sub_narrow(&mut self, narrow: &SignedOne) {
186        match narrow {
187            SignedOne::PlusOne => *self -= One,
188            SignedOne::MinusOne => *self += One,
189        }
190    }
191    #[inline]
192    fn mul_narrow(&self, narrow: &SignedOne) -> Self {
193        match narrow {
194            SignedOne::PlusOne => self.clone(),
195            SignedOne::MinusOne => -self.clone(),
196        }
197    }
198    #[inline]
199    fn add_mul_narrow(&mut self, narrow: &SignedOne, other: &Self) {
200        match narrow {
201            SignedOne::PlusOne => *self += other,
202            SignedOne::MinusOne => *self -= other,
203        }
204    }
205}
206
207/// The wide type reads itself, which is what makes a bound of `Absorb<F>` on a field cover its own
208/// arithmetic as well as the narrow arithmetic.
209impl<const S: usize> Absorb<Big<S>> for Big<S> {
210    #[inline]
211    fn from_narrow(narrow: &Big<S>) -> Self {
212        narrow.clone()
213    }
214    #[inline]
215    fn add_narrow(&mut self, narrow: &Big<S>) {
216        *self += narrow;
217    }
218    #[inline]
219    fn sub_narrow(&mut self, narrow: &Big<S>) {
220        *self -= narrow;
221    }
222    #[inline]
223    fn mul_narrow(&self, narrow: &Big<S>) -> Self {
224        self * narrow
225    }
226    #[inline]
227    fn add_mul_narrow(&mut self, narrow: &Big<S>, other: &Self) {
228        *self += other * narrow;
229    }
230}
231
232/// An absent value is zero, so it contributes nothing.
233///
234/// Sparse rows are stored as `Option`s to keep the zeros out. This lift is written for the wide
235/// type rather than once for every wide type at once, because `Self` has to be local to the crate
236/// that writes the implementation; a crate defining its own accumulator writes its own.
237impl<const S: usize, N> Absorb<Option<N>> for Big<S>
238where
239    Big<S>: Absorb<N>,
240{
241    #[inline]
242    fn from_narrow(narrow: &Option<N>) -> Self {
243        match narrow {
244            None => Self::from(Zero),
245            Some(value) => <Big<S> as Absorb<N>>::from_narrow(value),
246        }
247    }
248    #[inline]
249    fn add_narrow(&mut self, narrow: &Option<N>) {
250        if let Some(value) = narrow {
251            self.add_narrow(value);
252        }
253    }
254    #[inline]
255    fn sub_narrow(&mut self, narrow: &Option<N>) {
256        if let Some(value) = narrow {
257            self.sub_narrow(value);
258        }
259    }
260    #[inline]
261    fn mul_narrow(&self, narrow: &Option<N>) -> Self {
262        match narrow {
263            None => Self::from(Zero),
264            Some(value) => self.mul_narrow(value),
265        }
266    }
267    #[inline]
268    fn add_mul_narrow(&mut self, narrow: &Option<N>, other: &Self) {
269        if let Some(value) = narrow {
270            self.add_mul_narrow(value, other);
271        }
272    }
273}
274
275/// A matrix provider that hands out references to what it stores is read through them.
276impl<const S: usize, N> Absorb<&N> for Big<S>
277where
278    Big<S>: Absorb<N>,
279{
280    #[inline]
281    fn from_narrow(narrow: &&N) -> Self {
282        <Big<S> as Absorb<N>>::from_narrow(narrow)
283    }
284    #[inline]
285    fn add_narrow(&mut self, narrow: &&N) {
286        self.add_narrow(*narrow);
287    }
288    #[inline]
289    fn sub_narrow(&mut self, narrow: &&N) {
290        self.sub_narrow(*narrow);
291    }
292    #[inline]
293    fn mul_narrow(&self, narrow: &&N) -> Self {
294        self.mul_narrow(*narrow)
295    }
296    #[inline]
297    fn add_mul_narrow(&mut self, narrow: &&N, other: &Self) {
298        self.add_mul_narrow(*narrow, other);
299    }
300}