mycelium_util/
math.rs

1//! Math utilities.
2
3/// Variadic version of [`core::cmp::max`].
4///
5/// # Examples
6///
7/// ```
8/// let max = mycelium_util::math::max![1, 2, 3, 4];
9/// assert_eq!(max, 4);
10/// ```
11#[macro_export]
12macro_rules! max {
13    ($arg:expr) => { $arg };
14    ($arg1:expr, $($arg:expr),+) => {
15        core::cmp::max($arg1, $crate::max!( $($arg),+ ))
16    };
17}
18
19/// Variadic version of [`core::cmp::min`].
20///
21/// # Examples
22///
23/// ```
24/// let min = mycelium_util::math::min![1, 2, 3, 4];
25/// assert_eq!(min, 1);
26/// ```
27#[macro_export]
28macro_rules! min {
29    ($arg:expr) => { $arg };
30    ($arg1:expr, $($arg:expr),+) => {
31        core::cmp::min($arg1, $crate::min!( $($arg),+ ))
32    };
33}
34
35#[doc(inline)]
36pub use max;
37
38#[doc(inline)]
39pub use min;
40
41/// Extension trait adding logarithm methods to integers.
42pub trait Logarithm: Sized {
43    /// Returns `ceiling(log2(self))`.
44    fn log2_ceil(self) -> Self;
45
46    /// Returns `log2(self)`.
47    fn log2(self) -> Self;
48
49    /// Returns the integer logarithm base `base` of `self`, or `None` if it is
50    /// not possible  to take the log base `base` of `self`.
51    fn checked_log(self, base: Self) -> Option<Self>;
52
53    /// Returns the integer logarithm base `base` of `self`.
54    fn log(self, base: Self) -> Self;
55}
56
57impl Logarithm for usize {
58    #[inline(always)]
59    fn log2_ceil(self) -> usize {
60        usize_const_log2_ceil(self)
61    }
62
63    #[inline(always)]
64    fn log2(self) -> usize {
65        usize_const_log2(self)
66    }
67
68    #[inline(always)]
69    fn checked_log(self, base: usize) -> Option<Self> {
70        usize_const_checked_log(self, base)
71    }
72
73    #[inline(always)]
74    fn log(self, base: usize) -> Self {
75        match self.checked_log(base) {
76            Some(log) => log,
77            None => panic!("cannot take log base {base} of {self}"),
78        }
79    }
80}
81
82/// Returns `ceiling(log2(n))`.
83///
84/// This is exposed in addition to the [`Logarithm`] extension trait because it
85/// is a  `const fn`, while trait methods cannot be `const fn`s.
86#[inline(always)]
87pub const fn usize_const_log2_ceil(n: usize) -> usize {
88    n.next_power_of_two().trailing_zeros() as usize
89}
90
91/// Returns `log2(n)`.
92///
93/// This is exposed in addition to the [`Logarithm`] extension trait because it
94/// is a
95/// `const fn`, while trait methods cannot be `const fn`s.
96#[inline(always)]
97pub const fn usize_const_log2(n: usize) -> usize {
98    (usize::BITS - 1) as usize - n.leading_zeros() as usize
99}
100
101/// Returns the `base` logarithm of `n`.
102///
103/// This is exposed in addition to the [`Logarithm`] extension trait because it
104/// is a `const fn`, while trait methods cannot be `const fn`s.
105#[inline(always)]
106pub const fn usize_const_checked_log(mut n: usize, base: usize) -> Option<usize> {
107    if n == 0 || base <= 1 {
108        return None;
109    }
110
111    if base == 2 {
112        return Some(usize_const_log2(n));
113    }
114
115    let mut log = 0;
116    while n >= base {
117        n /= base;
118        log += 1;
119    }
120    Some(log)
121}
122
123#[cfg(all(test, not(loom)))]
124#[test]
125fn test_log2_ceil() {
126    assert_eq!(0, 0.log2_ceil());
127    assert_eq!(0, 1.log2_ceil());
128    assert_eq!(1, 2.log2_ceil());
129    assert_eq!(5, 32.log2_ceil());
130    assert_eq!(10, 1024.log2_ceil());
131}