toolbox_rs/
math.rs

1use num::{Integer, PrimInt};
2
3pub fn choose(n: u64, k: u64) -> u64 {
4    if k > n {
5        return 0;
6    }
7
8    if k == 0 || k == n {
9        return 1;
10    }
11
12    let k = if k > n - k { n - k } else { k };
13
14    let mut result = 1;
15    for i in 1..=k {
16        result = result * (n - i + 1) / i;
17    }
18
19    result
20}
21
22/// calculate the largest power of 2 less or equal to n
23pub fn prev_power_of_two<T: PrimInt>(n: T) -> T {
24    if n == T::zero() {
25        return T::zero();
26    }
27    let leading_zeros = n.leading_zeros() as usize;
28    let sizeof = 8 * std::mem::size_of::<T>();
29    T::one() << (sizeof - leading_zeros - 1)
30}
31
32/// computes the least-significant bit set.
33pub fn lsb_index<T: Integer + PrimInt>(n: T) -> Option<u32> {
34    if n == T::zero() {
35        return None;
36    }
37
38    Some(n.trailing_zeros())
39}
40
41/// computes the least-significant bit set. Doesn't return the correct answer if the input is zero
42pub fn non_zero_lsb_index<T: Integer + PrimInt>(n: T) -> u32 {
43    if n == T::zero() {
44        return 0;
45    }
46
47    n.trailing_zeros()
48}
49
50/// Evaluates a polynomial using Horner's method.
51///
52/// Given a polynomial in the form a₀xⁿ + a₁xⁿ⁻¹ + ... + aₙ₋₁x + aₙ,
53/// the coefficients should be provided in reverse order: [a₀, a₁, ..., aₙ].
54///
55/// # Arguments
56///
57/// * `x` - The value at which to evaluate the polynomial
58/// * `coefficients` - The coefficients of the polynomial in descending order of degree
59///
60/// # Examples
61///
62/// ```
63/// use toolbox_rs::math::horner;
64///
65/// // Evaluate 2x² + 3x + 1 at x = 2
66/// let coefficients = [2.0, 3.0, 1.0];
67/// assert_eq!(horner(2.0, &coefficients), 15.0);
68///
69/// // Evaluate constant polynomial f(x) = 5
70/// assert_eq!(horner(42.0, &[5.0]), 5.0);
71///
72/// // Empty coefficient array represents the zero polynomial
73/// assert_eq!(horner(1.0, &[]), 0.0);
74/// ```
75pub fn horner(x: f64, coefficients: &[f64]) -> f64 {
76    coefficients.iter().fold(0.0, |acc, &coeff| acc * x + coeff)
77}
78
79/// Encodes a signed integer into an unsigned integer using zigzag encoding.
80///
81/// ZigZag encoding maps signed integers to unsigned integers in a way that preserves
82/// magnitude ordering while using fewer bits for small negative values.
83///
84/// # Examples
85///
86/// ```
87/// use toolbox_rs::math::zigzag_encode;
88///
89/// assert_eq!(zigzag_encode(0i32), 0u32);
90/// assert_eq!(zigzag_encode(-1i32), 1u32);
91/// assert_eq!(zigzag_encode(1i32), 2u32);
92/// assert_eq!(zigzag_encode(-2i32), 3u32);
93/// ```
94pub fn zigzag_encode(value: i32) -> u32 {
95    ((value << 1) ^ (value >> 31)) as u32
96}
97
98#[cfg(test)]
99mod tests {
100    use crate::math::{
101        choose, horner, lsb_index, non_zero_lsb_index, prev_power_of_two, zigzag_encode,
102    };
103
104    #[test]
105    fn some_well_known_n_choose_k_values() {
106        let test_cases = [
107            ((64u64, 1u64), 64u64),
108            ((64, 63), 64),
109            ((9, 4), 126),
110            ((10, 5), 252),
111            ((50, 2), 1_225),
112            ((5, 2), 10),
113            ((10, 4), 210),
114            ((37, 17), 15905368710),
115            ((52, 5), 2598960),
116        ];
117
118        test_cases.into_iter().for_each(|((n, k), expected)| {
119            assert_eq!(choose(n, k), expected);
120        });
121    }
122
123    #[test]
124    fn lsb_well_known_values() {
125        assert_eq!(lsb_index(0), None);
126        assert_eq!(lsb_index(10), Some(1));
127        assert_eq!(lsb_index(16), Some(4));
128        assert_eq!(lsb_index(255), Some(0));
129        assert_eq!(lsb_index(1024), Some(10));
130        assert_eq!(lsb_index(72057594037927936_i64), Some(56));
131    }
132
133    #[test]
134    fn lsb_index_well_known_values() {
135        assert_eq!(non_zero_lsb_index(0), 0);
136        assert_eq!(non_zero_lsb_index(10), 1);
137        assert_eq!(non_zero_lsb_index(16), 4);
138        assert_eq!(non_zero_lsb_index(255), 0);
139        assert_eq!(non_zero_lsb_index(1024), 10);
140        assert_eq!(non_zero_lsb_index(72057594037927936_i64), 56);
141    }
142
143    #[test]
144    fn largest_power_of_two_less_or_equal() {
145        assert_eq!(prev_power_of_two(16_u8), 16);
146        assert_eq!(prev_power_of_two(17_i32), 16);
147        assert_eq!(prev_power_of_two(0x5555555555555_u64), 0x4000000000000)
148    }
149
150    #[test]
151    fn test_horner1() {
152        // Test of polynom: 2x² + 3x + 1
153        let coefficients = [2.0, 3.0, 1.0];
154        assert_eq!(horner(0.0, &coefficients), 1.0);
155        assert_eq!(horner(1.0, &coefficients), 6.0);
156        assert_eq!(horner(2.0, &coefficients), 15.0);
157    }
158
159    #[test]
160    fn test_horner2() {
161        // Test of polynom: x³ - 2x² + 3x - 4
162        let coefficients = [1.0, -2.0, 3.0, -4.0];
163        assert!((horner(0.0, &coefficients) - (-4.0)).abs() < 1e-10);
164        assert!((horner(1.0, &coefficients) - (-2.0)).abs() < 1e-10);
165        assert!((horner(2.0, &coefficients) - 2.0).abs() < 1e-10);
166    }
167
168    #[test]
169    fn test_horner3() {
170        // Test of empty polynom
171        assert_eq!(horner(1.0, &[]), 0.0);
172    }
173
174    #[test]
175    fn test_horner4() {
176        // Test of constant polynom
177        assert_eq!(horner(42.0, &[5.0]), 5.0);
178    }
179
180    #[test]
181    fn test_zigzag_encode() {
182        assert_eq!(zigzag_encode(0), 0);
183        assert_eq!(zigzag_encode(-1), 1);
184        assert_eq!(zigzag_encode(1), 2);
185        assert_eq!(zigzag_encode(-2), 3);
186        assert_eq!(zigzag_encode(i32::MIN), u32::MAX);
187    }
188}