Skip to main content

relp_num/rational/small/
absorb.rs

1//! # Reading narrow values into a fixed width rational
2//!
3//! These types are fields, so they can serve as the accumulator too, and generic code that names a
4//! field has to be able to act on them with the same narrow values. The bodies go through the
5//! operators written for each pair, exactly as the arbitrary precision ones do.
6//!
7//! An integer wider than the rational it acts on has no operators here and gets no implementation:
8//! the conversion could not be relied on to fit.
9
10use crate::{Absorb, Rational8, Rational16, Rational32, Rational64, Rational128, RationalUsize};
11use crate::fixed::{Binary, One, SignedOne, Zero};
12
13macro_rules! absorb_small {
14    ($name:ty; $($int:ty),* $(,)?) => {
15        /// The accumulator reads itself.
16        impl Absorb<$name> for $name {
17            #[inline]
18            fn from_narrow(narrow: &$name) -> Self { *narrow }
19            #[inline]
20            fn add_narrow(&mut self, narrow: &$name) { *self += narrow }
21            #[inline]
22            fn sub_narrow(&mut self, narrow: &$name) { *self -= narrow }
23            #[inline]
24            fn mul_narrow(&self, narrow: &$name) -> Self { let mut r = *self; r *= narrow; r }
25            #[inline]
26            fn add_mul_narrow(&mut self, narrow: &$name, other: &Self) {
27                let mut product = *other;
28                product *= narrow;
29                *self += product;
30            }
31        }
32
33        impl Absorb<One> for $name {
34            #[inline]
35            fn from_narrow(_: &One) -> Self { <Self as num_traits::One>::one() }
36            #[inline]
37            fn add_narrow(&mut self, _: &One) { *self += One }
38            #[inline]
39            fn sub_narrow(&mut self, _: &One) { *self -= One }
40            #[inline]
41            fn mul_narrow(&self, _: &One) -> Self { *self }
42            #[inline]
43            fn add_mul_narrow(&mut self, _: &One, other: &Self) { *self += other }
44        }
45
46        impl Absorb<Zero> for $name {
47            #[inline]
48            fn from_narrow(_: &Zero) -> Self { <Self as num_traits::Zero>::zero() }
49            #[inline]
50            fn add_narrow(&mut self, _: &Zero) {}
51            #[inline]
52            fn sub_narrow(&mut self, _: &Zero) {}
53            #[inline]
54            fn mul_narrow(&self, _: &Zero) -> Self { <Self as num_traits::Zero>::zero() }
55            #[inline]
56            fn add_mul_narrow(&mut self, _: &Zero, _: &Self) {}
57        }
58
59        impl Absorb<Binary> for $name {
60            #[inline]
61            fn from_narrow(narrow: &Binary) -> Self {
62                match narrow {
63                    Binary::Zero => <Self as num_traits::Zero>::zero(),
64                    Binary::One => <Self as num_traits::One>::one(),
65                }
66            }
67            #[inline]
68            fn add_narrow(&mut self, narrow: &Binary) {
69                match narrow { Binary::Zero => {}, Binary::One => *self += One }
70            }
71            #[inline]
72            fn sub_narrow(&mut self, narrow: &Binary) {
73                match narrow { Binary::Zero => {}, Binary::One => *self -= One }
74            }
75            #[inline]
76            fn mul_narrow(&self, narrow: &Binary) -> Self {
77                match narrow {
78                    Binary::Zero => <Self as num_traits::Zero>::zero(),
79                    Binary::One => *self,
80                }
81            }
82            #[inline]
83            fn add_mul_narrow(&mut self, narrow: &Binary, other: &Self) {
84                match narrow { Binary::Zero => {}, Binary::One => *self += other }
85            }
86        }
87
88        impl Absorb<SignedOne> for $name {
89            #[inline]
90            fn from_narrow(narrow: &SignedOne) -> Self {
91                match narrow {
92                    SignedOne::PlusOne => <Self as num_traits::One>::one(),
93                    SignedOne::MinusOne => -<Self as num_traits::One>::one(),
94                }
95            }
96            #[inline]
97            fn add_narrow(&mut self, narrow: &SignedOne) {
98                match narrow {
99                    SignedOne::PlusOne => *self += One,
100                    SignedOne::MinusOne => *self -= One,
101                }
102            }
103            #[inline]
104            fn sub_narrow(&mut self, narrow: &SignedOne) {
105                match narrow {
106                    SignedOne::PlusOne => *self -= One,
107                    SignedOne::MinusOne => *self += One,
108                }
109            }
110            #[inline]
111            fn mul_narrow(&self, narrow: &SignedOne) -> Self {
112                match narrow {
113                    SignedOne::PlusOne => *self,
114                    SignedOne::MinusOne => -*self,
115                }
116            }
117            #[inline]
118            fn add_mul_narrow(&mut self, narrow: &SignedOne, other: &Self) {
119                match narrow {
120                    SignedOne::PlusOne => *self += other,
121                    SignedOne::MinusOne => *self -= other,
122                }
123            }
124        }
125
126        $(
127            impl Absorb<$int> for $name {
128                #[inline]
129                fn from_narrow(narrow: &$int) -> Self { Self::from(*narrow) }
130                #[inline]
131                fn add_narrow(&mut self, narrow: &$int) { *self += *narrow }
132                #[inline]
133                fn sub_narrow(&mut self, narrow: &$int) { *self -= *narrow }
134                #[inline]
135                fn mul_narrow(&self, narrow: &$int) -> Self { let mut r = *self; r *= *narrow; r }
136                #[inline]
137                fn add_mul_narrow(&mut self, narrow: &$int, other: &Self) {
138                    let mut product = *other;
139                    product *= *narrow;
140                    *self += product;
141                }
142            }
143        )*
144
145        /// An absent value is zero, so it contributes nothing.
146        impl<N> Absorb<Option<N>> for $name where $name: Absorb<N> {
147            #[inline]
148            fn from_narrow(narrow: &Option<N>) -> Self {
149                match narrow {
150                    None => <Self as num_traits::Zero>::zero(),
151                    Some(value) => <$name as Absorb<N>>::from_narrow(value),
152                }
153            }
154            #[inline]
155            fn add_narrow(&mut self, narrow: &Option<N>) {
156                if let Some(value) = narrow { self.add_narrow(value) }
157            }
158            #[inline]
159            fn sub_narrow(&mut self, narrow: &Option<N>) {
160                if let Some(value) = narrow { self.sub_narrow(value) }
161            }
162            #[inline]
163            fn mul_narrow(&self, narrow: &Option<N>) -> Self {
164                match narrow {
165                    None => <Self as num_traits::Zero>::zero(),
166                    Some(value) => self.mul_narrow(value),
167                }
168            }
169            #[inline]
170            fn add_mul_narrow(&mut self, narrow: &Option<N>, other: &Self) {
171                if let Some(value) = narrow { self.add_mul_narrow(value, other) }
172            }
173        }
174
175        /// A provider that hands out references to what it stores is read through them.
176        impl<N> Absorb<&N> for $name where $name: Absorb<N> {
177            #[inline]
178            fn from_narrow(narrow: &&N) -> Self { <$name as Absorb<N>>::from_narrow(narrow) }
179            #[inline]
180            fn add_narrow(&mut self, narrow: &&N) { self.add_narrow(*narrow) }
181            #[inline]
182            fn sub_narrow(&mut self, narrow: &&N) { self.sub_narrow(*narrow) }
183            #[inline]
184            fn mul_narrow(&self, narrow: &&N) -> Self { self.mul_narrow(*narrow) }
185            #[inline]
186            fn add_mul_narrow(&mut self, narrow: &&N, other: &Self) {
187                self.add_mul_narrow(*narrow, other)
188            }
189        }
190    }
191}
192
193absorb_small!(Rational8; u8, i8);
194absorb_small!(Rational16; u8, u16, i8, i16);
195absorb_small!(Rational32; u8, u16, u32, i8, i16, i32);
196absorb_small!(Rational64; u8, u16, u32, u64, i8, i16, i32, i64);
197absorb_small!(Rational128; u8, u16, u32, u64, u128, i8, i16, i32, i64, i128);
198
199/// [`RationalUsize`] has neither the marker operators nor a conversion from a machine integer, so
200/// its bodies are written against the multiplicative identity instead. That is the slower form —
201/// it materialises a one — and it is written out here rather than inherited, so that adding those
202/// operators later is visibly an improvement rather than a silent one.
203macro_rules! absorb_by_identity {
204    ($name:ty) => {
205        impl Absorb<$name> for $name {
206            #[inline]
207            fn from_narrow(narrow: &$name) -> Self { *narrow }
208            #[inline]
209            fn add_narrow(&mut self, narrow: &$name) { *self += narrow }
210            #[inline]
211            fn sub_narrow(&mut self, narrow: &$name) { *self -= narrow }
212            #[inline]
213            fn mul_narrow(&self, narrow: &$name) -> Self { let mut r = *self; r *= narrow; r }
214            #[inline]
215            fn add_mul_narrow(&mut self, narrow: &$name, other: &Self) {
216                let mut product = *other;
217                product *= narrow;
218                *self += product;
219            }
220        }
221
222        impl Absorb<One> for $name {
223            #[inline]
224            fn from_narrow(_: &One) -> Self { <Self as num_traits::One>::one() }
225            #[inline]
226            fn add_narrow(&mut self, _: &One) { *self += <Self as num_traits::One>::one() }
227            #[inline]
228            fn sub_narrow(&mut self, _: &One) { *self -= <Self as num_traits::One>::one() }
229            #[inline]
230            fn mul_narrow(&self, _: &One) -> Self { *self }
231            #[inline]
232            fn add_mul_narrow(&mut self, _: &One, other: &Self) { *self += other }
233        }
234
235        impl Absorb<Zero> for $name {
236            #[inline]
237            fn from_narrow(_: &Zero) -> Self { <Self as num_traits::Zero>::zero() }
238            #[inline]
239            fn add_narrow(&mut self, _: &Zero) {}
240            #[inline]
241            fn sub_narrow(&mut self, _: &Zero) {}
242            #[inline]
243            fn mul_narrow(&self, _: &Zero) -> Self { <Self as num_traits::Zero>::zero() }
244            #[inline]
245            fn add_mul_narrow(&mut self, _: &Zero, _: &Self) {}
246        }
247
248        impl<N> Absorb<Option<N>> for $name where $name: Absorb<N> {
249            #[inline]
250            fn from_narrow(narrow: &Option<N>) -> Self {
251                match narrow {
252                    None => <Self as num_traits::Zero>::zero(),
253                    Some(value) => <$name as Absorb<N>>::from_narrow(value),
254                }
255            }
256            #[inline]
257            fn add_narrow(&mut self, narrow: &Option<N>) {
258                if let Some(value) = narrow { self.add_narrow(value) }
259            }
260            #[inline]
261            fn sub_narrow(&mut self, narrow: &Option<N>) {
262                if let Some(value) = narrow { self.sub_narrow(value) }
263            }
264            #[inline]
265            fn mul_narrow(&self, narrow: &Option<N>) -> Self {
266                match narrow {
267                    None => <Self as num_traits::Zero>::zero(),
268                    Some(value) => self.mul_narrow(value),
269                }
270            }
271            #[inline]
272            fn add_mul_narrow(&mut self, narrow: &Option<N>, other: &Self) {
273                if let Some(value) = narrow { self.add_mul_narrow(value, other) }
274            }
275        }
276
277        impl<N> Absorb<&N> for $name where $name: Absorb<N> {
278            #[inline]
279            fn from_narrow(narrow: &&N) -> Self { <$name as Absorb<N>>::from_narrow(narrow) }
280            #[inline]
281            fn add_narrow(&mut self, narrow: &&N) { self.add_narrow(*narrow) }
282            #[inline]
283            fn sub_narrow(&mut self, narrow: &&N) { self.sub_narrow(*narrow) }
284            #[inline]
285            fn mul_narrow(&self, narrow: &&N) -> Self { self.mul_narrow(*narrow) }
286            #[inline]
287            fn add_mul_narrow(&mut self, narrow: &&N, other: &Self) {
288                self.add_mul_narrow(*narrow, other)
289            }
290        }
291    }
292}
293
294absorb_by_identity!(RationalUsize);
295
296#[cfg(test)]
297mod test {
298    use crate::{Absorb, Rational64, RationalUsize, R32, R64, R8};
299    use crate::fixed::{Binary, One, SignedOne, Zero};
300
301    /// Every implementation has to agree with converting and then operating, as for the arbitrary
302    /// precision accumulator.
303    fn check<T>(value: &T, wide: Rational64)
304    where
305        Rational64: Absorb<T>,
306    {
307        assert_eq!(Rational64::from_narrow(value), wide, "from_narrow");
308
309        let start = R64!(5, 3);
310
311        let mut by_override = start;
312        by_override.add_narrow(value);
313        assert_eq!(by_override, start + wide, "add_narrow");
314
315        let mut by_override = start;
316        by_override.sub_narrow(value);
317        assert_eq!(by_override, start - wide, "sub_narrow");
318
319        assert_eq!(start.mul_narrow(value), wide * start, "mul_narrow");
320
321        for other in [R64!(0), R64!(1), R64!(-7, 5), start] {
322            let mut fused = start;
323            fused.add_mul_narrow(value, &other);
324            assert_eq!(fused, start + wide * other, "add_mul_narrow by {other}");
325        }
326    }
327
328    #[test]
329    fn markers() {
330        check(&One, R64!(1));
331        check(&Zero, R64!(0));
332        check(&Binary::Zero, R64!(0));
333        check(&Binary::One, R64!(1));
334        check(&SignedOne::PlusOne, R64!(1));
335        check(&SignedOne::MinusOne, R64!(-1));
336    }
337
338    #[test]
339    fn integers_and_self() {
340        check(&7_i32, R64!(7));
341        check(&-7_i64, R64!(-7));
342        check(&0_u8, R64!(0));
343        check(&R64!(-9, 4), R64!(-9, 4));
344    }
345
346    #[test]
347    fn options_and_references() {
348        check(&Some(One), R64!(1));
349        check(&None::<One>, R64!(0));
350        check(&&SignedOne::MinusOne, R64!(-1));
351        check(&Some(&7_i32), R64!(7));
352    }
353
354    /// A fixed width accumulator is a field, so generic code may pick one, at any of its widths.
355    #[test]
356    fn every_width_is_an_accumulator() {
357        let mut small = R8!(1, 2);
358        small.add_narrow(&One);
359        assert_eq!(small, R8!(3, 2));
360        assert_eq!(small.mul_narrow(&SignedOne::MinusOne), R8!(-3, 2));
361
362        let mut medium = R32!(1, 4);
363        medium.add_mul_narrow(&3_i32, &R32!(1, 2));
364        assert_eq!(medium, R32!(7, 4));
365
366        // `RationalUsize` has no marker operators, so its bodies go through the identity; the
367        // answers have to be the same.
368        let mut pointer_width = RationalUsize::new(1, 2).unwrap();
369        pointer_width.add_narrow(&One);
370        assert_eq!(pointer_width, RationalUsize::new(3, 2).unwrap());
371        assert_eq!(
372            pointer_width.mul_narrow(&Zero),
373            RationalUsize::new(0, 1).unwrap(),
374        );
375    }
376}