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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// Copyright (c) 2018-2020 Thomas Kramer.
// SPDX-FileCopyrightText: 2018-2022 Thomas Kramer
//
// SPDX-License-Identifier: AGPL-3.0-or-later

//! Math helper functions.

use num_traits::PrimInt;

/// Implement the fast approximate computation of '1/sqrt(x)' for a type.
pub trait FastInvSqrt {
    /// Fast approximate computation of 1/sqrt(x).
    ///
    /// See: <https://en.wikipedia.org/wiki/Fast_inverse_square_root>
    /// And: <http://www.lomont.org/papers/2003/InvSqrt.pdf>
    fn fast_invsqrt(x: Self) -> Self;
}

impl FastInvSqrt for f32 {
    #[inline]
    fn fast_invsqrt(x: Self) -> Self {
        fast_invsqrt_32(x)
    }
}

impl FastInvSqrt for f64 {
    #[inline]
    fn fast_invsqrt(x: Self) -> Self {
        fast_invsqrt_64(x)
    }
}

/// Fast approximate computation of 1/sqrt(x).
/// The error should be below 0.2%.
///
/// See: <https://en.wikipedia.org/wiki/Fast_inverse_square_root>
/// And: <http://www.lomont.org/papers/2003/InvSqrt.pdf>
pub fn fast_invsqrt_32(x: f32) -> f32 {
    let approx = 0x5f375a86u32;
    let xhalf = 0.5 * x;
    let i = x.to_bits();
    let i = approx - (i >> 1);
    let y = f32::from_bits(i);
    y * (1.5 - xhalf * y * y)
}

/// Fast approximate computation of 1/sqrt(x).
/// The error should be below 0.2%.
///
/// See: <https://en.wikipedia.org/wiki/Fast_inverse_square_root>
/// And: <http://www.lomont.org/papers/2003/InvSqrt.pdf>
pub fn fast_invsqrt_64(x: f64) -> f64 {
    let approx = 0x5fe6eb50c7aa19f9u64;
    let xhalf = 0.5 * x;
    let i = x.to_bits();
    let i = approx - (i >> 1);
    let y = f64::from_bits(i);
    y * (1.5 - xhalf * y * y)
}

#[test]
fn test_fast_invsqrt_32() {
    for i in 1..1000000 {
        let x = i as f32;
        let inv_sqrt_approx = fast_invsqrt_32(x);
        let inv_sqrt = 1. / x.sqrt();
        let abs_diff = (inv_sqrt - inv_sqrt_approx).abs();
        assert!(abs_diff / inv_sqrt < 0.002, "Error should be below 0.2%.")
    }
}

#[test]
fn test_fast_invsqrt_64() {
    for i in 1..1000000 {
        let x = i as f64;
        let inv_sqrt_approx = fast_invsqrt_64(x);
        let inv_sqrt = 1. / x.sqrt();
        let abs_diff = (inv_sqrt - inv_sqrt_approx).abs();
        assert!(abs_diff / inv_sqrt < 0.002, "Error should be below 0.2%.")
    }
}

/// Compute square root of integers using Newtons method.
/// Returns the biggest integer which is smaller or equal to the actual square root of `n`.
/// Similar to `(i as f64).sqrt().floor() as T` but without type conversions.
///
/// See: <https://en.wikipedia.org/wiki/Integer_square_root>
///
/// # Panics
/// Panics when given a negative number.
///
/// # Example
/// ```
/// use iron_shapes::math::int_sqrt_floor;
/// assert_eq!(int_sqrt_floor(16), 4);
/// assert_eq!(int_sqrt_floor(17), 4);
/// assert_eq!(int_sqrt_floor(24), 4);
/// assert_eq!(int_sqrt_floor(25), 5);
/// ```
pub fn int_sqrt_floor<T: PrimInt>(n: T) -> T {
    assert!(
        n >= T::zero(),
        "Cannot compute the square root of a negative number."
    );
    let one = T::one();
    let two = one + one;
    let mut x = n;
    let mut y = (x + one) / two;
    while y < x {
        x = y;
        y = (x + (n / x)) / two;
    }
    x
}

#[test]
pub fn test_int_sqrt_floor_i32() {
    for i in 0..100000i32 {
        let sqrt = int_sqrt_floor(i);
        let sqrt_reference = (i as f64).sqrt().floor() as i32;
        assert_eq!(sqrt, sqrt_reference);

        let i_lower = sqrt * sqrt;
        let i_upper = (sqrt + 1) * (sqrt + 1);
        assert!(i_lower <= i && i < i_upper);
    }
}