fixed_bigint/fixeduint/
roots_impl.rs1use crate::fixeduint::FixedUInt;
2use crate::machineword::MachineWord;
3use const_num_traits::{CheckedPow, Nct};
4use num_integer::Roots;
5use num_traits::{FromPrimitive, One, Zero};
6
7impl<T: MachineWord, const N: usize> Roots for FixedUInt<T, N, Nct> {
8 fn nth_root(&self, n: u32) -> Self {
9 if n == 0 {
10 panic!("nth_root: n must be non-zero");
11 }
12
13 if self.is_zero() {
14 return Self::zero();
15 }
16
17 if self.is_one() || n == 1 {
18 return *self;
19 }
20
21 let bit_len = self.bit_length();
22 if n > bit_len {
23 return Self::one();
24 }
25
26 let initial_exp = bit_len.div_ceil(n).max(1);
28 let mut x = Self::one() << (initial_exp as usize);
29
30 let n_val = Self::from_u32(n).expect("n too large for FixedUInt");
32 let n_minus_1 = Self::from_u32(n - 1).expect("n too large for FixedUInt");
33
34 loop {
39 let quotient = match CheckedPow::checked_pow(x, n - 1) {
40 Some(p) if p.is_zero() => break,
41 Some(p) => *self / p,
42 None => Self::zero(),
43 };
44
45 let numerator = x * n_minus_1 + quotient;
46 let x_new = numerator / n_val;
47
48 if x_new >= x {
49 break;
50 }
51
52 x = x_new;
53 }
54
55 while CheckedPow::checked_pow(x, n).is_none_or(|p| p > *self) {
58 x -= Self::one();
59 }
60
61 let mut x_plus_one = x + Self::one();
62 while CheckedPow::checked_pow(x_plus_one, n).is_some_and(|p| p <= *self) {
63 x += Self::one();
64 x_plus_one = x + Self::one();
65 }
66
67 x
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74 use num_integer::Roots;
75 use num_traits::One;
76
77 #[test]
78 fn test_sqrt_basic() {
79 type TestInt = FixedUInt<u32, 2>;
80
81 assert_eq!(TestInt::from(0u8).sqrt(), TestInt::from(0u8));
82 assert_eq!(TestInt::from(1u8).sqrt(), TestInt::from(1u8));
83 assert_eq!(TestInt::from(4u8).sqrt(), TestInt::from(2u8));
84 assert_eq!(TestInt::from(9u8).sqrt(), TestInt::from(3u8));
85 assert_eq!(TestInt::from(16u8).sqrt(), TestInt::from(4u8));
86 assert_eq!(TestInt::from(25u8).sqrt(), TestInt::from(5u8));
87
88 assert_eq!(TestInt::from(2u8).sqrt(), TestInt::from(1u8));
90 assert_eq!(TestInt::from(3u8).sqrt(), TestInt::from(1u8));
91 assert_eq!(TestInt::from(8u8).sqrt(), TestInt::from(2u8));
92 assert_eq!(TestInt::from(15u8).sqrt(), TestInt::from(3u8));
93 assert_eq!(TestInt::from(24u8).sqrt(), TestInt::from(4u8));
94 }
95
96 #[test]
97 fn test_cbrt_basic() {
98 type TestInt = FixedUInt<u32, 2>;
99
100 assert_eq!(TestInt::from(0u8).cbrt(), TestInt::from(0u8));
101 assert_eq!(TestInt::from(1u8).cbrt(), TestInt::from(1u8));
102 assert_eq!(TestInt::from(8u8).cbrt(), TestInt::from(2u8));
103 assert_eq!(TestInt::from(27u8).cbrt(), TestInt::from(3u8));
104 assert_eq!(TestInt::from(64u8).cbrt(), TestInt::from(4u8));
105 assert_eq!(TestInt::from(125u8).cbrt(), TestInt::from(5u8));
106
107 assert_eq!(TestInt::from(2u8).cbrt(), TestInt::from(1u8));
109 assert_eq!(TestInt::from(7u8).cbrt(), TestInt::from(1u8));
110 assert_eq!(TestInt::from(26u8).cbrt(), TestInt::from(2u8));
111 assert_eq!(TestInt::from(63u8).cbrt(), TestInt::from(3u8));
112 }
113
114 #[test]
115 fn test_nth_root_basic() {
116 type TestInt = FixedUInt<u32, 2>;
117
118 assert_eq!(TestInt::from(16u8).nth_root(4), TestInt::from(2u8));
120 assert_eq!(TestInt::from(81u8).nth_root(4), TestInt::from(3u8));
121 assert_eq!(TestInt::from(15u8).nth_root(4), TestInt::from(1u8));
122 assert_eq!(TestInt::from(80u8).nth_root(4), TestInt::from(2u8));
123
124 assert_eq!(TestInt::from(32u8).nth_root(5), TestInt::from(2u8));
126 assert_eq!(TestInt::from(243u8).nth_root(5), TestInt::from(3u8));
127 assert_eq!(TestInt::from(31u8).nth_root(5), TestInt::from(1u8));
128
129 assert_eq!(TestInt::from(42u8).nth_root(1), TestInt::from(42u8));
131 assert_eq!(TestInt::from(123u8).nth_root(1), TestInt::from(123u8));
132 }
133
134 #[test]
135 fn test_nth_root_edge_cases() {
136 type TestInt = FixedUInt<u32, 2>;
137
138 assert_eq!(TestInt::from(0u8).nth_root(2), TestInt::from(0u8));
140 assert_eq!(TestInt::from(1u8).nth_root(2), TestInt::from(1u8));
141 assert_eq!(TestInt::from(0u8).nth_root(10), TestInt::from(0u8));
142 assert_eq!(TestInt::from(1u8).nth_root(10), TestInt::from(1u8));
143
144 assert_eq!(TestInt::from(2u8).nth_root(100), TestInt::from(1u8));
146 assert_eq!(TestInt::from(1000u16).nth_root(50), TestInt::from(1u8));
147 }
148
149 #[test]
150 #[should_panic(expected = "nth_root: n must be non-zero")]
151 fn test_nth_root_zero_n() {
152 let x = FixedUInt::<u32, 2>::from(16u8);
153 x.nth_root(0);
154 }
155
156 #[test]
157 fn test_root_correctness() {
158 type TestInt = FixedUInt<u32, 2>;
159
160 for x in 1..=100u16 {
162 let x_int = TestInt::from(x);
163
164 let sqrt_x = x_int.sqrt();
166 assert!(sqrt_x.pow(2) <= x_int);
167 assert!((sqrt_x + TestInt::one()).pow(2) > x_int);
168
169 let cbrt_x = x_int.cbrt();
171 assert!(cbrt_x.pow(3) <= x_int);
172 assert!((cbrt_x + TestInt::one()).pow(3) > x_int);
173
174 let root4_x = x_int.nth_root(4);
176 assert!(root4_x.pow(4) <= x_int);
177 assert!((root4_x + TestInt::one()).pow(4) > x_int);
178 }
179 }
180
181 #[test]
185 fn sqrt_of_max_does_not_overflow_panic() {
186 assert_eq!(
187 FixedUInt::<u32, 1>::from(u32::MAX).sqrt(),
188 FixedUInt::<u32, 1>::from(0xFFFFu32)
189 );
190 assert_eq!(
191 FixedUInt::<u8, 1>::from(255u8).sqrt(),
192 FixedUInt::<u8, 1>::from(15u8)
193 );
194 assert_eq!(
196 FixedUInt::<u16, 1>::from(65025u16).sqrt(),
197 FixedUInt::<u16, 1>::from(255u16)
198 ); assert_eq!(
200 FixedUInt::<u16, 1>::from(65535u16).sqrt(),
201 FixedUInt::<u16, 1>::from(255u16)
202 );
203 }
204}