1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
//! 平方根。

/// 平方根。
///
/// $\\lfloor\\sqrt{n}\\rfloor$ を求める。
///
/// # Complexity
/// $O(\\log(n))$ time.
///
/// Newton 法で $O(\\log(\\log(n)))$ time にするべき?
///
/// # Examples
/// ```
/// use nekolib::math::Sqrt;
///
/// assert_eq!(0_i32.sqrt(), 0);
/// assert_eq!(9_i32.sqrt(), 3);
/// assert_eq!(12_i32.sqrt(), 3);
/// assert_eq!(16_i32.sqrt(), 4);
///
/// assert_eq!(u128::MAX.sqrt(), (1 << 64) - 1);
/// ```
///
/// ```should_panic
/// use nekolib::math::Sqrt;
///
/// (-1_i32).sqrt();
/// ```
pub trait Sqrt {
    fn sqrt(self) -> Self;
}

macro_rules! impl_uint {
    ($t:ty) => {
        impl Sqrt for $t {
            fn sqrt(self) -> Self {
                let pred = |i: $t| {
                    i.checked_pow(2).map(|i2| i2 <= self).unwrap_or(false)
                };
                let mut ok = 0;
                let mut bad = 1;
                while pred(bad) {
                    bad *= 2;
                }
                while bad - ok > 1 {
                    let mid = ok + (bad - ok) / 2;
                    *(if pred(mid) { &mut ok } else { &mut bad }) = mid;
                }
                ok
            }
        }
    };
    ( $($t:ty)* ) => { $(impl_uint!($t);)* };
}

macro_rules! impl_int {
    ($t:ty) => {
        impl Sqrt for $t {
            fn sqrt(self) -> Self {
                assert!(self >= 0, "must be non-negative");
                let pred = |i: $t| {
                    i.checked_pow(2).map(|i2| i2 <= self).unwrap_or(false)
                };
                let mut ok = 0;
                let mut bad = 1;
                while pred(bad) {
                    bad *= 2;
                }
                while bad - ok > 1 {
                    let mid = ok + (bad - ok) / 2;
                    *(if pred(mid) { &mut ok } else { &mut bad }) = mid;
                }
                ok
            }
        }
    };
    ( $($t:ty)* ) => { $(impl_int!($t);)* };
}

impl_uint!(u8 u16 u32 u64 u128 usize);
impl_int!(i8 i16 i32 i64 i128 isize);