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