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
128
129
130
131
132
133
134
135
136
|
use std::{
f64::consts::PI,
ops::{Add, Div, Sub},
sync::LazyLock,
};
pub const EPSILON: f64 = 1.0e-7;
pub static SIN: LazyLock<[f32; 65536]> =
LazyLock::new(|| std::array::from_fn(|i| f64::sin((i as f64) * PI * 2. / 65536.) as f32));
/// A sine function that uses a lookup table.
pub fn sin(x: f32) -> f32 {
let x = x * 10430.378;
let x = x as i32 as usize & 0xFFFF;
SIN[x]
}
/// A cosine function that uses a lookup table.
pub fn cos(x: f32) -> f32 {
let x = x * 10430.378 + 16384.;
let x = x as i32 as usize & 0xFFFF;
SIN[x]
}
pub fn binary_search<
T: Ord + PartialOrd + Add<Output = T> + Sub<Output = T> + Div<Output = T> + From<u8> + Copy,
>(
mut min: T,
max: T,
predicate: impl Fn(T) -> bool,
) -> T {
let mut diff = max - min;
while diff > T::from(0) {
let diff_mid = diff / T::from(2);
let mid = min + diff_mid;
if predicate(mid) {
diff = diff_mid;
} else {
min = mid + T::from(1);
diff = diff - (diff_mid + T::from(1));
}
}
min
}
pub fn lcm(a: u32, b: u32) -> u64 {
let gcd = gcd(a, b);
(a as u64) * (b / gcd) as u64
}
pub fn gcd(mut a: u32, mut b: u32) -> u32 {
while b != 0 {
let t = b;
b = a % b;
a = t;
}
a
}
pub fn lerp<T: num_traits::Float>(amount: T, a: T, b: T) -> T {
a + amount * (b - a)
}
pub fn ceil_log2(x: u32) -> u32 {
u32::BITS - x.leading_zeros()
}
pub fn fract(x: f64) -> f64 {
let x_int = x as i64 as f64;
let floor = if x < x_int { x_int - 1. } else { x_int };
x - floor
}
// these are copied from the java standard library, we don't calculate the
// consts ourself to make sure it's the same as java
pub fn to_radians(degrees: f64) -> f64 {
degrees * 0.017453292519943295
}
pub fn to_degrees(radians: f64) -> f64 {
radians * 57.29577951308232
}
/// Returns either -1, 0, or 1, depending on whether the number is negative,
/// zero, or positive.
///
/// This function exists because f64::signum doesn't check for 0.
pub fn sign(num: f64) -> f64 {
if num == 0. { 0. } else { num.signum() }
}
pub fn sign_as_int(num: f64) -> i32 {
if num == 0. { 0 } else { num.signum() as i32 }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_gcd() {
assert_eq!(gcd(0, 0), 0);
assert_eq!(gcd(1, 1), 1);
assert_eq!(gcd(0, 1), 1);
assert_eq!(gcd(1, 0), 1);
assert_eq!(gcd(12, 8), 4);
assert_eq!(gcd(8, 12), 4);
assert_eq!(gcd(12, 9), 3);
assert_eq!(gcd(9, 12), 3);
assert_eq!(gcd(12, 7), 1);
assert_eq!(gcd(7, 12), 1);
}
#[test]
fn test_sin() {
const PI: f32 = std::f32::consts::PI;
// check that they're close enough
fn assert_sin_eq_enough(number: f32) {
let a = sin(number);
let b = f32::sin(number);
assert!((a - b).abs() < 0.01, "sin({number}) failed, {a} != {b}");
}
assert_sin_eq_enough(0.0);
assert_sin_eq_enough(PI / 2.0);
assert_sin_eq_enough(PI);
assert_sin_eq_enough(PI * 2.0);
assert_sin_eq_enough(PI * 3.0 / 2.0);
assert_sin_eq_enough(-PI / 2.0);
assert_sin_eq_enough(-PI);
assert_sin_eq_enough(-PI * 2.0);
assert_sin_eq_enough(-PI * 3.0 / 2.0);
}
}
|