gpl_math/
approximations.rs1use {
4 num_traits::{CheckedShl, CheckedShr, PrimInt},
5 std::cmp::Ordering,
6};
7
8pub fn sqrt<T: PrimInt + CheckedShl + CheckedShr>(radicand: T) -> Option<T> {
18 match radicand.cmp(&T::zero()) {
19 Ordering::Less => return None, Ordering::Equal => return Some(T::zero()), _ => {}
22 }
23
24 let max_shift: u32 = T::zero().leading_zeros() - 1;
26 let shift: u32 = (max_shift - radicand.leading_zeros()) & !1;
27 let mut bit = T::one().checked_shl(shift)?;
28
29 let mut n = radicand;
30 let mut result = T::zero();
31 while bit != T::zero() {
32 let result_with_bit = result.checked_add(&bit)?;
33 if n >= result_with_bit {
34 n = n.checked_sub(&result_with_bit)?;
35 result = result.checked_shr(1)?.checked_add(&bit)?;
36 } else {
37 result = result.checked_shr(1)?;
38 }
39 bit = bit.checked_shr(2)?;
40 }
41 Some(result)
42}
43
44#[cfg(test)]
45mod tests {
46 use {super::*, proptest::prelude::*};
47
48 fn check_square_root(radicand: u128) {
49 let root = sqrt(radicand).unwrap();
50 let lower_bound = root.saturating_sub(1).checked_pow(2).unwrap();
51 let upper_bound = root.checked_add(1).unwrap().checked_pow(2).unwrap();
52 assert!(radicand as u128 <= upper_bound);
53 assert!(radicand as u128 >= lower_bound);
54 }
55
56 #[test]
57 fn test_square_root_min_max() {
58 let test_roots = [0, u64::MAX];
59 for i in test_roots.iter() {
60 check_square_root(*i as u128);
61 }
62 }
63
64 proptest! {
65 #[test]
66 fn test_square_root(a in 0..u64::MAX) {
67 check_square_root(a as u128);
68 }
69 }
70}