num_runtime_fmt/numeric_trait/
impls.rs

1//! This module contains implementations of [`Numeric`][crate::Numeric] for several types, plus helpers which can
2//! ease implementation for your own type.
3
4use std::ops::{BitAnd, ShrAssign};
5
6macro_rules! impl_iter {
7    ($iter:ident) => {
8        impl<N> $iter<N> {
9            /// Create a new digit iterator for this value.
10            ///
11            /// Note also that the trait bounds specified here are only necessary and enforced when
12            /// compiled in debug mode. They enable a debug assertion.
13            #[cfg(debug_assertions)]
14            pub fn new(n: N) -> Self
15            where
16                N: Default + PartialOrd,
17            {
18                assert!(n >= N::default(), "n must not be negative");
19                $iter(n)
20            }
21
22            /// Create a new digit iterator for this value.
23            #[cfg(not(debug_assertions))]
24            pub fn new(n: N) -> Self {
25                $iter(n)
26            }
27        }
28
29        impl<N> Iterator for $iter<N>
30        where
31            N: Clone + From<u8> + BitAnd<Output = N> + ShrAssign + PartialEq,
32        {
33            type Item = char;
34
35            fn next(&mut self) -> Option<Self::Item> {
36                if self.0 == 0.into() {
37                    return None;
38                }
39                let digit = self.0.clone() & Self::MASK.into();
40                self.0 >>= Self::WIDTH.into();
41                // this isn't an _efficient_ approach, but it avoids needing a bound to convert
42                // from N to u8, which won't always be implemented for interesting types.
43                //
44                // A custom runtime formatting library can be excused a few inefficiencies.
45                for maybe_digit in 0..=Self::MASK {
46                    if digit == maybe_digit.into() {
47                        if maybe_digit < 10 {
48                            return Some((maybe_digit + b'0') as char);
49                        } else {
50                            return Some((maybe_digit - 10 + b'a') as char);
51                        }
52                    }
53                }
54                panic!(
55                    "no digit matched when computing {}",
56                    std::any::type_name::<$iter<N>>()
57                );
58            }
59        }
60    };
61}
62
63/// Iterator over binary digits of `N`.
64pub struct BinIter<N>(N);
65
66impl<N> BinIter<N> {
67    const WIDTH: u8 = 1;
68    const MASK: u8 = 1;
69}
70
71impl_iter!(BinIter);
72
73/// Iterator over octal digits of `N`.
74pub struct OctIter<N>(N);
75
76impl<N> OctIter<N> {
77    const WIDTH: u8 = 3;
78    const MASK: u8 = 0b0111;
79}
80
81impl_iter!(OctIter);
82
83/// Iterator over hexadecimal digits of `N`.
84pub struct HexIter<N>(N);
85
86impl<N> HexIter<N> {
87    const WIDTH: u8 = 4;
88    const MASK: u8 = 0b1111;
89}
90
91impl_iter!(HexIter);
92
93/// Iterator over the decimal digits of a number.
94///
95/// This implementation defers to the standard `format!` macro to determine the digits of the number.
96pub struct DecIter(Vec<char>);
97
98impl DecIter {
99    /// Create iterators over the digits of a number left and right of the decimal respectively.
100    ///
101    /// Note that `n` must not be negative in order for this to work properly.
102    /// If `n` has a type which can possibly be negative, take its absolute value manually.
103    ///
104    /// This implementation defers to the standard `format!` machinery to actually encode the number
105    /// as decimal.
106    ///
107    /// The left iterator handles digits of magnitude >= 1; the right iterator handles fractional digits.
108    pub fn new<N>(n: N) -> (DecIter, Option<DecIter>)
109    where
110        N: ToString,
111    {
112        let s = n.to_string();
113        debug_assert!(s.chars().all(|c| c == '.' || ('0'..='9').contains(&c)));
114        debug_assert!(s.chars().filter(|&c| c == '.').count() <= 1);
115        let mut found_decimal = false;
116        let (left, mut right): (Vec<_>, Vec<_>) = s.chars().partition(|&c| {
117            found_decimal |= c == '.';
118            !found_decimal
119        });
120
121        // reverse so we pop in the correct sequence
122        right.reverse();
123        // eliminate the decimal from the list
124        right.pop();
125
126        let right = if right.is_empty() || right == ['0'] {
127            None
128        } else {
129            Some(DecIter(right))
130        };
131        (DecIter(left), right)
132    }
133}
134
135impl Iterator for DecIter {
136    type Item = char;
137
138    fn next(&mut self) -> Option<Self::Item> {
139        self.0.pop()
140    }
141}
142
143macro_rules! impl_for {
144    (unsigned_int $type:ident) => {
145        mod $type {
146            use super::{BinIter, DecIter, HexIter, OctIter};
147            use crate::Numeric;
148
149            impl Numeric for $type {
150                type BinIter = BinIter<$type>;
151                type OctIter = OctIter<$type>;
152                type DecLeftIter = DecIter;
153                type DecRightIter = DecIter;
154                type HexIter = HexIter<$type>;
155
156                fn binary(&self) -> Option<Self::BinIter> {
157                    Some(BinIter::new(*self))
158                }
159
160                fn octal(&self) -> Option<Self::OctIter> {
161                    Some(OctIter::new(*self))
162                }
163
164                fn hex(&self) -> Option<Self::HexIter> {
165                    Some(HexIter::new(*self))
166                }
167
168                fn decimal(&self) -> (Self::DecLeftIter, Option<Self::DecRightIter>) {
169                    DecIter::new(*self)
170                }
171
172                fn is_negative(&self) -> bool {
173                    false
174                }
175            }
176        }
177    };
178    (signed_int $type:ident) => {
179        mod $type {
180            use super::{BinIter, DecIter, HexIter, OctIter};
181            use crate::Numeric;
182
183            impl Numeric for $type {
184                type BinIter = BinIter<$type>;
185                type OctIter = OctIter<$type>;
186                type DecLeftIter = DecIter;
187                type DecRightIter = DecIter;
188                type HexIter = HexIter<$type>;
189
190                fn binary(&self) -> Option<Self::BinIter> {
191                    Some(BinIter::new(*self))
192                }
193
194                fn octal(&self) -> Option<Self::OctIter> {
195                    Some(OctIter::new(*self))
196                }
197
198                fn hex(&self) -> Option<Self::HexIter> {
199                    Some(HexIter::new(*self))
200                }
201
202                fn decimal(&self) -> (Self::DecLeftIter, Option<Self::DecRightIter>) {
203                    DecIter::new(self.abs())
204                }
205
206                fn is_negative(&self) -> bool {
207                    *self < 0
208                }
209            }
210        }
211    };
212    (float $type:ident) => {
213        mod $type {
214            use super::DecIter;
215            use crate::Numeric;
216            use std::iter::Empty;
217
218            impl Numeric for $type {
219                type BinIter = Empty<char>;
220                type OctIter = Empty<char>;
221                type DecLeftIter = DecIter;
222                type DecRightIter = DecIter;
223                type HexIter = Empty<char>;
224
225                fn binary(&self) -> Option<Self::BinIter> {
226                    None
227                }
228
229                fn octal(&self) -> Option<Self::OctIter> {
230                    None
231                }
232
233                fn hex(&self) -> Option<Self::HexIter> {
234                    None
235                }
236
237                fn decimal(&self) -> (Self::DecLeftIter, Option<Self::DecRightIter>) {
238                    DecIter::new(self.abs())
239                }
240
241                fn is_negative(&self) -> bool {
242                    *self < 0.0
243                }
244            }
245        }
246    };
247}
248
249impl_for!(unsigned_int u8);
250impl_for!(unsigned_int u16);
251impl_for!(unsigned_int u32);
252impl_for!(unsigned_int u64);
253impl_for!(unsigned_int u128);
254impl_for!(unsigned_int usize);
255// TODO: impl for i8
256impl_for!(signed_int i16);
257impl_for!(signed_int i32);
258impl_for!(signed_int i64);
259impl_for!(signed_int i128);
260impl_for!(signed_int isize);
261impl_for!(float f32);
262impl_for!(float f64);
263
264#[cfg(test)]
265mod tests {
266    macro_rules! suite_for {
267        (dec: int $( $type_int:ident ),+ ; float $( $type_float:ident ),+ ) => {
268            #[allow(non_snake_case)]
269            mod DecIter {
270                use super::super::DecIter as Iter;
271                use std::convert::TryInto;
272
273                $(
274                    #[test]
275                    fn $type_int() {
276                        // don't check 0: that needs special casing for all these impls
277                        for n in 1..=$type_int::MAX.min(1024.try_into().unwrap_or($type_int::MAX)) {
278                            let expect = format!("{}", n);
279                            let (left, right) = Iter::new(n);
280                            assert!(right.is_none(), "ints should not have a fractional part");
281
282                            let mut digits: Vec<_> = left.map(|d| d.to_string()).collect();
283                            digits.reverse();
284                            let actual = digits.join("");
285                            dbg!(&actual, &expect);
286                            assert_eq!(actual, expect);
287                        }
288                    }
289                )+
290
291                $(
292                    #[test]
293                    fn $type_float() {
294                        // don't check 0: that needs special casing for all these impls
295                        for n in 1..=1024 {
296                            let n = n as $type_float / 20.0;
297                            let expect = format!("{}", n);
298                            let (left, right) = Iter::new(n);
299
300                            let mut actual: Vec<_> = left.map(|d| d.to_string()).collect();
301                            actual.reverse();
302                            if let Some(right) = right {
303                                actual.push('.'.into());
304                                actual.extend(right.map(|d| d.to_string()));
305                            }
306
307                            let actual = actual.join("");
308                            dbg!(&actual, &expect);
309                            assert_eq!(actual, expect);
310                        }
311                    }
312                )+
313            }
314        };
315        ($iter:ident, $fmt:literal, $( $test_type:ident ),+) => {
316            #[allow(non_snake_case)]
317            mod $iter {
318                use super::super::$iter as Iter;
319                use std::convert::TryInto;
320
321                $(
322                    #[test]
323                    fn $test_type() {
324                        // don't check 0: that needs special casing for all these impls
325                        for n in 1..=$test_type::MAX.min(1024.try_into().unwrap_or($test_type::MAX)) {
326                            let expect = format!($fmt, n);
327                            let mut digits: Vec<_> = Iter::new(n).map(|d| d.to_string()).collect();
328                            digits.reverse();
329                            let actual = digits.join("");
330                            dbg!(&actual, &expect);
331                            assert_eq!(actual, expect);
332                        }
333                    }
334                )+
335            }
336        };
337    }
338
339    // we don't test `i8` because it doesn't implement From<u8>.
340    // however, circumstantial evidence suggests that the implementation would probably work fine.
341
342    suite_for!(BinIter, "{:b}", u8, u16, u32, u64, u128, usize, i16, i32, i64, i128, isize);
343    suite_for!(OctIter, "{:o}", u8, u16, u32, u64, u128, usize, i16, i32, i64, i128, isize);
344    suite_for!(HexIter, "{:x}", u8, u16, u32, u64, u128, usize, i16, i32, i64, i128, isize);
345    suite_for!(dec: int u8, u16, u32, u64, u128, usize, i16, i32, i64, i128, isize; float f32, f64);
346}