1use std::{
2 f64::consts::PI,
3 ops::{Add, Div, Sub},
4 sync::LazyLock,
5};
6
7pub const EPSILON: f64 = 1.0e-7;
8
9pub static SIN: LazyLock<Box<[f32; 65536]>> = LazyLock::new(|| {
11 (0..65536)
12 .map(|i| f64::sin((i as f64) * PI * 2. / 65536.) as f32)
13 .collect::<Box<[f32]>>()
14 .try_into()
15 .unwrap()
16});
17
18pub fn sin(x: f32) -> f32 {
20 let x = x * 10430.378;
21 let x = x as i32 as usize & 0xFFFF;
22 SIN[x]
23}
24
25pub fn cos(x: f32) -> f32 {
27 let x = x * 10430.378 + 16384.;
28 let x = x as i32 as usize & 0xFFFF;
29 SIN[x]
30}
31
32pub fn binary_search<
33 T: Ord + PartialOrd + Add<Output = T> + Sub<Output = T> + Div<Output = T> + From<u8> + Copy,
34>(
35 mut min: T,
36 max: T,
37 predicate: impl Fn(T) -> bool,
38) -> T {
39 let mut diff = max - min;
40 while diff > T::from(0) {
41 let diff_mid = diff / T::from(2);
42 let mid = min + diff_mid;
43 if predicate(mid) {
44 diff = diff_mid;
45 } else {
46 min = mid + T::from(1);
47 diff = diff - (diff_mid + T::from(1));
48 }
49 }
50
51 min
52}
53
54pub fn lcm(a: u32, b: u32) -> u64 {
55 let gcd = gcd(a, b);
56 (a as u64) * (b / gcd) as u64
57}
58pub fn gcd(mut a: u32, mut b: u32) -> u32 {
59 while b != 0 {
60 let t = b;
61 b = a % b;
62 a = t;
63 }
64 a
65}
66
67pub fn lerp<T: num_traits::Float>(amount: T, a: T, b: T) -> T {
68 a + amount * (b - a)
69}
70
71pub fn ceil_log2(x: u32) -> u32 {
72 u32::BITS - x.saturating_sub(1).leading_zeros()
73}
74
75pub fn fract(x: f64) -> f64 {
76 let x_int = x as i64 as f64;
77 let floor = if x < x_int { x_int - 1. } else { x_int };
78 x - floor
79}
80
81pub fn to_radians(degrees: f64) -> f64 {
84 degrees * 0.017453292519943295
85}
86pub fn to_degrees(radians: f64) -> f64 {
87 radians * 57.29577951308232
88}
89
90pub fn sign(num: f64) -> f64 {
95 if num == 0. { 0. } else { num.signum() }
96}
97pub fn sign_as_int(num: f64) -> i32 {
98 if num == 0. { 0 } else { num.signum() as i32 }
99}
100
101pub fn ceil_long(x: f64) -> i64 {
102 let x_i64 = x as i64;
103 if x > x_i64 as f64 { x_i64 + 1 } else { x_i64 }
104}
105
106pub fn equal(a: f64, b: f64) -> bool {
107 (b - a).abs() < 1.0e-5
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113
114 #[test]
115 fn test_gcd() {
116 assert_eq!(gcd(0, 0), 0);
117 assert_eq!(gcd(1, 1), 1);
118
119 assert_eq!(gcd(0, 1), 1);
120 assert_eq!(gcd(1, 0), 1);
121
122 assert_eq!(gcd(12, 8), 4);
123 assert_eq!(gcd(8, 12), 4);
124
125 assert_eq!(gcd(12, 9), 3);
126 assert_eq!(gcd(9, 12), 3);
127
128 assert_eq!(gcd(12, 7), 1);
129 assert_eq!(gcd(7, 12), 1);
130 }
131
132 #[test]
133 fn test_sin() {
134 const PI: f32 = std::f32::consts::PI;
135 fn assert_sin_eq_enough(number: f32) {
137 let a = sin(number);
138 let b = f32::sin(number);
139 assert!((a - b).abs() < 0.01, "sin({number}) failed, {a} != {b}");
140 }
141 assert_sin_eq_enough(0.0);
142 assert_sin_eq_enough(PI / 2.0);
143 assert_sin_eq_enough(PI);
144 assert_sin_eq_enough(PI * 2.0);
145 assert_sin_eq_enough(PI * 3.0 / 2.0);
146 assert_sin_eq_enough(-PI / 2.0);
147 assert_sin_eq_enough(-PI);
148 assert_sin_eq_enough(-PI * 2.0);
149 assert_sin_eq_enough(-PI * 3.0 / 2.0);
150 }
151}