Skip to main content

const_num_traits/ops/
log.rs

1//! Integer logarithms, mirroring the `ilog2` / `ilog10` / `ilog` inherent
2//! methods (stable in std since Rust 1.67) and their checked variants.
3//!
4//! Split per base: `ilog2` only needs `leading_zeros` — cheap
5//! and constant-time-implementable — while `ilog10` and `ilog` require
6//! data-dependent division loops. Bundling them would force the expensive
7//! capability onto types that can only offer the cheap one. The panicking
8//! and checked variants stay paired within each trait: they share one
9//! capability, and the panicking form derives from the checked one.
10//!
11//! **CT tiers**: [`Ilog2::checked_ilog2`] is Tier B (a `leading_zeros`
12//! instruction plus an `Option`); the panicking `ilog2` and all of
13//! [`Ilog10`]/[`Ilog`] are Tier C (data-dependent panic / division loops).
14
15c0nst::c0nst! {
16/// Base 2 integer logarithm, rounded down.
17pub c0nst trait Ilog2: Sized {
18    /// Returns the base 2 logarithm of the number, rounded down.
19    ///
20    /// # Panics
21    ///
22    /// Panics if the number is zero (or negative, for signed types).
23    ///
24    /// ```
25    /// use const_num_traits::Ilog2;
26    ///
27    /// assert_eq!(Ilog2::ilog2(32u32), 5);
28    /// assert_eq!(Ilog2::ilog2(33u32), 5);
29    /// ```
30    fn ilog2(self) -> u32;
31
32    /// Returns the base 2 logarithm of the number, rounded down, or `None`
33    /// if the number is zero (or negative, for signed types).
34    fn checked_ilog2(self) -> Option<u32>;
35}
36}
37
38c0nst::c0nst! {
39/// Base 10 integer logarithm, rounded down.
40pub c0nst trait Ilog10: Sized {
41    /// Returns the base 10 logarithm of the number, rounded down.
42    ///
43    /// # Panics
44    ///
45    /// Panics if the number is zero (or negative, for signed types).
46    fn ilog10(self) -> u32;
47
48    /// Returns the base 10 logarithm of the number, rounded down, or `None`
49    /// if the number is zero (or negative, for signed types).
50    fn checked_ilog10(self) -> Option<u32>;
51}
52}
53
54c0nst::c0nst! {
55/// Integer logarithm with respect to an arbitrary base, rounded down.
56pub c0nst trait Ilog: Sized {
57    /// Returns the logarithm of the number with respect to `base`, rounded
58    /// down.
59    ///
60    /// # Panics
61    ///
62    /// Panics if the number is zero (or negative, for signed types), or if
63    /// `base` is less than 2.
64    ///
65    /// ```
66    /// use const_num_traits::Ilog;
67    ///
68    /// assert_eq!(Ilog::ilog(125u32, 5), 3);
69    /// ```
70    fn ilog(self, base: Self) -> u32;
71
72    /// Returns the logarithm of the number with respect to `base`, rounded
73    /// down, or `None` if the number is zero (or negative, for signed
74    /// types) or if `base` is less than 2.
75    fn checked_ilog(self, base: Self) -> Option<u32>;
76}
77}
78
79macro_rules! ilog_impl {
80    ($($t:ty)*) => {$(
81        c0nst::c0nst! {
82        c0nst impl Ilog2 for $t {
83            #[inline]
84            fn ilog2(self) -> u32 {
85                <$t>::ilog2(self)
86            }
87
88            #[inline]
89            fn checked_ilog2(self) -> Option<u32> {
90                <$t>::checked_ilog2(self)
91            }
92        }
93        }
94
95        c0nst::c0nst! {
96        c0nst impl Ilog10 for $t {
97            #[inline]
98            fn ilog10(self) -> u32 {
99                <$t>::ilog10(self)
100            }
101
102            #[inline]
103            fn checked_ilog10(self) -> Option<u32> {
104                <$t>::checked_ilog10(self)
105            }
106        }
107        }
108
109        c0nst::c0nst! {
110        c0nst impl Ilog for $t {
111            #[inline]
112            fn ilog(self, base: Self) -> u32 {
113                <$t>::ilog(self, base)
114            }
115
116            #[inline]
117            fn checked_ilog(self, base: Self) -> Option<u32> {
118                <$t>::checked_ilog(self, base)
119            }
120        }
121        }
122    )*};
123}
124
125ilog_impl!(usize u8 u16 u32 u64 u128);
126ilog_impl!(isize i8 i16 i32 i64 i128);
127
128#[cfg(test)]
129mod tests {
130    use super::*;
131
132    #[test]
133    fn ilog() {
134        assert_eq!(Ilog2::ilog2(1u8), 0);
135        assert_eq!(Ilog2::ilog2(u128::MAX), 127);
136        assert_eq!(Ilog10::ilog10(999u16), 2);
137        assert_eq!(Ilog10::ilog10(1000u16), 3);
138        assert_eq!(Ilog::ilog(80i32, 3), 3);
139        assert_eq!(Ilog2::checked_ilog2(0u8), None);
140        assert_eq!(Ilog2::checked_ilog2(-1i8), None);
141        assert_eq!(Ilog10::checked_ilog10(100i64), Some(2));
142        assert_eq!(Ilog::checked_ilog(5u8, 1), None);
143        assert_eq!(Ilog::checked_ilog(5u8, 5), Some(1));
144    }
145}