Skip to main content

fixed_bigint/heapless/
ilog.rs

1//! `const_num_traits::Ilog2` / `Ilog10` / `Ilog` for `HeaplessBigInt<_, Nct>`.
2//!
3//! All return a plain `u32` bit/digit count, so there is no result width to
4//! preserve. Nct-only: the divide-down loops and the zero/base guards branch
5//! on value. `ilog2` reads the highest set bit directly (`bit_length - 1`);
6//! `ilog10`/`ilog` count divide-downs like `FixedUInt`.
7
8use super::HeaplessBigInt;
9use crate::MachineWord;
10use const_num_traits::{CarryingMul, Ilog, Ilog2, Ilog10, Nct, Zero};
11
12impl<T, const CAP: usize> Ilog2 for HeaplessBigInt<T, CAP, Nct>
13where
14    T: MachineWord,
15{
16    fn ilog2(self) -> u32 {
17        match <Self as Ilog2>::checked_ilog2(self) {
18            Some(v) => v,
19            None => panic!("ilog2: argument is zero"),
20        }
21    }
22
23    fn checked_ilog2(self) -> Option<u32> {
24        if <Self as Zero>::is_zero(&self) {
25            return None;
26        }
27        // ilog2 = position of the highest set bit = bit_length - 1.
28        Some(self.bit_length() as u32 - 1)
29    }
30}
31
32impl<T, const CAP: usize> Ilog10 for HeaplessBigInt<T, CAP, Nct>
33where
34    T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
35{
36    fn ilog10(self) -> u32 {
37        match <Self as Ilog10>::checked_ilog10(self) {
38            Some(v) => v,
39            None => panic!("ilog10: argument is zero"),
40        }
41    }
42
43    fn checked_ilog10(self) -> Option<u32> {
44        if <Self as Zero>::is_zero(&self) {
45            return None;
46        }
47        let ten: Self = From::from(10u8);
48        let mut n = self;
49        let mut count = 0u32;
50        while n >= ten {
51            n /= ten;
52            count += 1;
53        }
54        Some(count)
55    }
56}
57
58impl<T, const CAP: usize> Ilog for HeaplessBigInt<T, CAP, Nct>
59where
60    T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
61{
62    fn ilog(self, base: Self) -> u32 {
63        match <Self as Ilog>::checked_ilog(self, base) {
64            Some(v) => v,
65            None => panic!("ilog: argument is zero or base is less than 2"),
66        }
67    }
68
69    fn checked_ilog(self, base: Self) -> Option<u32> {
70        if <Self as Zero>::is_zero(&self) {
71            return None;
72        }
73        let two: Self = From::from(2u8);
74        if base < two {
75            return None;
76        }
77        // Route the common bases through the O(width) bit/decimal paths instead
78        // of the O(width²) divide-down loop.
79        if base == two {
80            return <Self as Ilog2>::checked_ilog2(self);
81        }
82        let ten: Self = From::from(10u8);
83        if base == ten {
84            return <Self as Ilog10>::checked_ilog10(self);
85        }
86        let mut n = self;
87        let mut count = 0u32;
88        while n >= base {
89            n /= base;
90            count += 1;
91        }
92        Some(count)
93    }
94}
95
96// `&Self` mirrors so `(&h).ilog(&base)` resolves without an explicit copy.
97impl<T, const CAP: usize> Ilog2 for &HeaplessBigInt<T, CAP, Nct>
98where
99    T: MachineWord,
100{
101    fn ilog2(self) -> u32 {
102        <HeaplessBigInt<T, CAP, Nct> as Ilog2>::ilog2(*self)
103    }
104
105    fn checked_ilog2(self) -> Option<u32> {
106        <HeaplessBigInt<T, CAP, Nct> as Ilog2>::checked_ilog2(*self)
107    }
108}
109
110impl<T, const CAP: usize> Ilog10 for &HeaplessBigInt<T, CAP, Nct>
111where
112    T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
113{
114    fn ilog10(self) -> u32 {
115        <HeaplessBigInt<T, CAP, Nct> as Ilog10>::ilog10(*self)
116    }
117
118    fn checked_ilog10(self) -> Option<u32> {
119        <HeaplessBigInt<T, CAP, Nct> as Ilog10>::checked_ilog10(*self)
120    }
121}
122
123impl<T, const CAP: usize> Ilog for &HeaplessBigInt<T, CAP, Nct>
124where
125    T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
126{
127    fn ilog(self, base: Self) -> u32 {
128        <HeaplessBigInt<T, CAP, Nct> as Ilog>::ilog(*self, *base)
129    }
130
131    fn checked_ilog(self, base: Self) -> Option<u32> {
132        <HeaplessBigInt<T, CAP, Nct> as Ilog>::checked_ilog(*self, *base)
133    }
134}
135
136#[cfg(test)]
137mod tests {
138    use super::HeaplessBigInt;
139    use const_num_traits::{Ilog, Ilog2, Ilog10};
140
141    type H = HeaplessBigInt<u8, 2>;
142
143    #[test]
144    fn ilog2_values() {
145        for (n, r) in [(1u16, 0u32), (2, 1), (3, 1), (8, 3), (255, 7), (32768, 15)] {
146            assert_eq!(Ilog2::ilog2(H::from(n)), r, "ilog2({n})");
147        }
148        assert_eq!(Ilog2::checked_ilog2(H::from(0u8)), None);
149    }
150
151    #[test]
152    fn ilog10_values() {
153        for (n, r) in [(1u16, 0u32), (9, 0), (10, 1), (999, 2), (10000, 4)] {
154            assert_eq!(Ilog10::ilog10(H::from(n)), r, "ilog10({n})");
155        }
156        assert_eq!(Ilog10::checked_ilog10(H::from(0u8)), None);
157    }
158
159    #[test]
160    fn ilog_base_values() {
161        assert_eq!(Ilog::ilog(H::from(27u8), H::from(3u8)), 3);
162        assert_eq!(Ilog::ilog(H::from(256u16), H::from(16u8)), 2);
163        // Zero argument and base < 2 are None.
164        assert_eq!(Ilog::checked_ilog(H::from(0u8), H::from(2u8)), None);
165        assert_eq!(Ilog::checked_ilog(H::from(10u8), H::from(1u8)), None);
166    }
167
168    // The base-2/10 fast paths must agree with both the divide-down loop and
169    // the dedicated ilog2/ilog10.
170    #[test]
171    fn ilog_common_base_fast_paths() {
172        for n in [1u16, 2, 3, 8, 255, 1024, 40000] {
173            assert_eq!(
174                Ilog::ilog(H::from(n), H::from(2u8)),
175                Ilog2::ilog2(H::from(n)),
176                "ilog(base 2, {n})"
177            );
178        }
179        for n in [1u16, 9, 10, 99, 1000, 40000] {
180            assert_eq!(
181                Ilog::ilog(H::from(n), H::from(10u8)),
182                Ilog10::ilog10(H::from(n)),
183                "ilog(base 10, {n})"
184            );
185        }
186    }
187
188    #[test]
189    fn byref_matches_value() {
190        let a = H::from(27u8);
191        let base = H::from(3u8);
192        let r = &a; // dispatch through the `&Self` mirror
193        assert_eq!(Ilog2::ilog2(r), Ilog2::ilog2(a));
194        assert_eq!(Ilog2::checked_ilog2(r), Ilog2::checked_ilog2(a));
195        assert_eq!(Ilog10::ilog10(r), Ilog10::ilog10(a));
196        assert_eq!(Ilog10::checked_ilog10(r), Ilog10::checked_ilog10(a));
197        assert_eq!(Ilog::ilog(r, &base), Ilog::ilog(a, base));
198        assert_eq!(Ilog::checked_ilog(r, &base), Ilog::checked_ilog(a, base));
199    }
200}