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}