1#![doc = include_str!("../README.md")]
2#![no_std]
3#![forbid(unsafe_code)]
4
5mod atan;
6mod atan2;
7mod ln;
8mod pow;
9pub use atan::atan;
10pub use atan2::atan2;
11pub use ln::ln;
12pub use pow::pow;
13
14use core::f64::{
15 self,
16 consts::{PI, TAU},
17};
18
19const TAYLOR_SERIES_SUMS: usize = 16;
21
22pub const fn cos(mut x: f64) -> f64 {
32 while x < TAU {
34 x += TAU;
35 }
36 while x > TAU {
37 x -= TAU;
38 }
39 let div = (x / PI) as u32;
40 x -= div as f64 * PI;
41 let sign = if div % 2 != 0 { -1.0 } else { 1.0 };
42
43 let mut result = 1.0;
44 let mut inter = 1.0;
45 let num = x * x;
46
47 let mut i = 1;
48 while i <= TAYLOR_SERIES_SUMS {
49 let comp = 2.0 * i as f64;
50 let den = comp * (comp - 1.0);
51 inter *= num / den;
52 if i % 2 == 0 {
53 result += inter;
54 } else {
55 result -= inter;
56 }
57 i += 1;
58 }
59
60 sign * result
61}
62
63pub const fn sin(x: f64) -> f64 {
73 cos(x - PI / 2.0)
74}
75
76pub const fn tan(x: f64) -> f64 {
86 sin(x) / cos(x)
87}
88
89pub const fn cot(x: f64) -> f64 {
99 let sin_calc = sin(x);
100 if sin_calc == 0.0 {
101 f64::INFINITY
102 } else {
103 cos(x) / sin_calc
104 }
105}
106
107pub const fn sec(x: f64) -> f64 {
117 let cos_calc = cos(x);
118 if cos_calc == 0.0 {
119 f64::INFINITY
120 } else {
121 1.0 / cos_calc
122 }
123}
124
125pub const fn csc(x: f64) -> f64 {
135 let sin_calc = sin(x);
136 if sin_calc == 0.0 {
137 f64::INFINITY
138 } else {
139 1.0 / sin_calc
140 }
141}
142
143pub const fn sinh(x: f64) -> f64 {
151 (exp(x) - exp(-x)) / 2.0
152}
153
154pub const fn cosh(x: f64) -> f64 {
162 (exp(x) + exp(-x)) / 2.0
163}
164
165pub const fn asin(x: f64) -> f64 {
173 if x.is_infinite() || x.abs() > 1.0 {
174 return f64::NAN;
175 } else if x == 1.0 {
176 return f64::consts::FRAC_PI_2;
177 } else if x == -1.0 {
178 return -f64::consts::FRAC_PI_2;
179 }
180
181 const RANGE_REDUCTION_THRESHOLD: f64 = 0.5;
185 if x.abs() > RANGE_REDUCTION_THRESHOLD {
186 let sign = x.signum();
187 let abs_x = x.abs();
188
189 let y = sqrt((1.0 - abs_x) / 2.0);
190 return sign * (f64::consts::FRAC_PI_2 - 2.0 * asin(y));
191 }
192
193 let mut n = 1;
194 let mut s = x;
195
196 while n < TAYLOR_SERIES_SUMS {
197 let numer1 = factorial(2.0 * n as f64);
198 let numer2 = expi(x, 2 * n as isize + 1);
199
200 let denom1 = expi(4.0, n as isize);
202 let denom2 = factorial(n as f64) * factorial(n as f64);
203 let denom3 = 2.0 * n as f64 + 1.0;
204
205 let f1 = numer1 / denom2;
207 let f2 = numer2 / denom1;
208
209 s += f1 * f2 / denom3;
210
211 n += 1;
212 }
213
214 s
215}
216
217pub const fn acos(x: f64) -> f64 {
226 if x.is_infinite() || x.abs() > 1.0 {
227 f64::NAN
228 } else {
229 f64::consts::FRAC_PI_2 - asin(x)
230 }
231}
232
233pub const fn asinh(x: f64) -> f64 {
241 if x.is_nan() {
242 f64::NAN
243 } else if x.is_infinite() {
244 x
245 } else {
246 ln(x + sqrt(x * x + 1.0))
247 }
248}
249
250pub const fn acosh(x: f64) -> f64 {
258 if x.is_nan() {
259 f64::NAN
260 } else if x.is_infinite() {
261 x
262 } else if x < 1.0 {
263 f64::NAN
264 } else {
265 ln(x + sqrt(x * x - 1.0))
266 }
267}
268
269pub const fn exp(x: f64) -> f64 {
271 let mut i = 1;
272 let mut s = 1.0;
273
274 while i < 16 {
275 s += expi(x, i) / factorial(i as f64);
276 i += 1;
277 }
278
279 s
280}
281
282pub const fn expi(x: f64, mut pow: isize) -> f64 {
284 let mut o = 1.0;
285
286 while pow > 0 {
287 o *= x;
288 pow -= 1;
289 }
290 while pow < 0 {
291 o /= x;
292 pow += 1;
293 }
294
295 o
296}
297
298pub const fn factorial(mut x: f64) -> f64 {
300 if x == 0.0 || x == 1.0 {
301 1.0
302 } else {
303 let mut s = 1.0;
304 while x > 1.0 {
305 s *= x;
306 x -= 1.0;
307 }
308 s
309 }
310}
311
312pub const fn sqrt(x: f64) -> f64 {
314 if x.is_nan() || x < 0.0 {
315 return f64::NAN;
316 } else if x.is_infinite() || x == 0.0 {
317 return x;
318 }
319
320 let mut current_guess = 1.0;
322
323 let mut i = 0;
324 while i < TAYLOR_SERIES_SUMS {
325 current_guess = 0.5 * (current_guess + x / current_guess);
326 i += 1;
327 }
328
329 current_guess
330}
331
332pub const fn fabs(x: f64) -> f64 {
333 if x > 0.0 {
334 x
335 } else {
336 -x
337 }
338}
339
340#[cfg(test)]
341mod tests {
342 use core::f64::consts::{E, PI};
343
344 use crate::{cos, cosh, exp, expi, factorial, ln, sin, sinh, sqrt};
345
346 macro_rules! float_eq {
347 ($lhs:expr, $rhs:expr) => {
348 assert!(($lhs - $rhs).abs() < 0.0001, "lhs: {}, rhs: {}", $lhs, $rhs);
349 };
350 }
351
352 #[test]
353 fn test_factorial() {
354 assert_eq!(factorial(0.0), 1.0);
355 assert_eq!(factorial(1.0), 1.0);
356 assert_eq!(factorial(2.0), 2.0);
357 assert_eq!(factorial(3.0), 6.0);
358 assert_eq!(factorial(4.0), 24.0);
359 assert_eq!(factorial(5.0), 120.0);
360 }
361
362 #[test]
363 fn test_expi() {
364 assert_eq!(expi(2.0, 0), 1.0);
365 assert_eq!(expi(2.0, 4), 16.0);
366 assert_eq!(expi(2.0, 5), 32.0);
367 assert_eq!(expi(3.0, 3), 27.0);
368 }
369
370 #[test]
371 fn test_exp() {
372 float_eq!(exp(0.0), 1.0);
373 float_eq!(exp(1.0), E);
374 }
375
376 #[test]
377 fn test_sqrt() {
378 float_eq!(sqrt(0.0), 0.0);
379 float_eq!(sqrt(1.0), 1.0);
380 float_eq!(sqrt(4.0), 2.0);
381 float_eq!(sqrt(9.0), 3.0);
382 float_eq!(sqrt(16.0), 4.0);
383 float_eq!(sqrt(25.0), 5.0);
384 }
385
386 #[test]
387 fn test_cos() {
388 float_eq!(cos(0.0), 0.0_f64.cos());
389 float_eq!(cos(1.0), 1.0_f64.cos());
390 float_eq!(cos(PI), PI.cos());
391 float_eq!(cos(PI * 8.0), (PI * 8.0).cos());
392 }
393
394 #[test]
395 fn test_sin() {
396 float_eq!(sin(0.0), 0.0_f64.sin());
397 float_eq!(sin(1.0), 1.0_f64.sin());
398 float_eq!(sin(PI), PI.sin());
399 float_eq!(sin(PI * 8.0), (PI * 8.0).sin());
400 }
401
402 #[test]
403 fn test_sinh() {
404 for x in [0.0, 0.5, 1.0, 1.5, 2.0, 2.5] {
405 float_eq!(sinh(x), x.sinh());
406 }
407 }
408
409 #[test]
410 fn test_cosh() {
411 for x in [0.0, 0.5, 1.0, 1.5, 2.0, 2.5] {
412 float_eq!(cosh(x), x.cosh());
413 }
414 }
415
416 #[test]
417 fn test_ln() {
418 float_eq!(ln(1.0), 1.0_f64.ln());
421 float_eq!(ln(2.0), 2.0_f64.ln());
422 float_eq!(ln(10.0), 10.0_f64.ln());
423 float_eq!(ln(1_000.0), 1_000.0_f64.ln());
424 }
425}