1#![allow(dead_code)]
8
9use crate::error::{SpecialError, SpecialResult};
10use std::collections::HashMap;
11use std::f64::consts::PI;
12use std::sync::Mutex;
13
14lazy_static::lazy_static! {
15 static ref POLYLOG_CACHE: Mutex<HashMap<(i32, i32), f64>> = Mutex::new(HashMap::new());
17
18 static ref SPECIAL_VALUES: Mutex<HashMap<&'static str, f64>> = {
20 let mut m = HashMap::new();
21 m.insert("euler_mascheroni", 0.577_215_664_901_532_9);
23 m.insert("pi_squared_div_6", PI * PI / 6.0);
24 m.insert("pi_squared_div_12", PI * PI / 12.0);
25 m.insert("pi_fourth_div_90", PI.powi(4) / 90.0);
26 Mutex::new(m)
27 };
28
29 static ref BESSEL_J0_LOOKUP: [f64; 1000] = {
31 let mut table = [0.0; 1000];
32 #[allow(clippy::needless_range_loop)]
33 for i in 0..1000 {
34 let x = i as f64 * 0.01; table[i] = bessel_j0_series(x);
36 }
37 table
38 };
39
40 static ref GAMMA_LOOKUP: [f64; 1000] = {
42 let mut table = [0.0; 1000];
43 #[allow(clippy::needless_range_loop)]
44 for i in 0..1000 {
45 let x = 0.1 + i as f64 * 0.01; table[i] = gamma_stirling(x);
47 }
48 table
49 };
50}
51
52#[allow(dead_code)]
62pub fn get_constant(name: &'static str) -> f64 {
63 if let Some(value) = SPECIAL_VALUES.lock().expect("Operation failed").get(name) {
64 return *value;
65 }
66
67 let value = match name {
69 "euler_mascheroni" => 0.577_215_664_901_532_9,
70 "pi_squared_div_6" => PI * PI / 6.0,
71 "pi_squared_div_12" => PI * PI / 12.0,
72 "pi_fourth_div_90" => PI.powi(4) / 90.0,
73 _ => 0.0, };
75
76 SPECIAL_VALUES
78 .lock()
79 .expect("Operation failed")
80 .insert(name, value);
81 value
82}
83
84#[allow(dead_code)]
95pub fn get_cached_polylog(s: f64, x: f64) -> Option<f64> {
96 let s_int = s.round() as i32;
98 let x_int = (x * 1000.0).round() as i32; if (s - s_int as f64).abs() < f64::EPSILON && s_int > 0 && s_int <= 10 {
102 if let Some(value) = POLYLOG_CACHE
103 .lock()
104 .expect("Operation failed")
105 .get(&(s_int, x_int))
106 {
107 return Some(*value);
108 }
109 }
110
111 None
112}
113
114#[allow(dead_code)]
122pub fn cache_polylog(s: f64, x: f64, value: f64) {
123 let s_int = s.round() as i32;
125 let x_int = (x * 1000.0).round() as i32; if (s - s_int as f64).abs() < f64::EPSILON && s_int > 0 && s_int <= 10 {
129 let mut cache = POLYLOG_CACHE.lock().expect("Operation failed");
131 if cache.len() < 10000 {
132 cache.insert((s_int, x_int), value);
134 }
135 }
136}
137
138#[allow(dead_code)]
148pub fn ln_1p_optimized(x: f64) -> f64 {
149 if x.abs() < 1e-4 {
150 let x2 = x * x;
152 x - x2 / 2.0 + x2 * x / 3.0 - x2 * x2 / 4.0
153 } else {
154 (1.0 + x).ln()
155 }
156}
157
158#[allow(dead_code)]
168pub fn exponential_integral_pade(x: f64) -> f64 {
169 if x <= 0.0 {
171 return -exponential_integral_e1_pade(-x);
172 }
173
174 if x < 6.0 {
175 let num_coeffs = [1.0, 7.5, 18.75, 18.75, 7.5, 1.0];
177
178 let den_coeffs = [1.0, -7.5, 18.75, -18.75, 7.5, -1.0];
180
181 let mut num = 0.0;
183 let mut den = 0.0;
184 let z = x;
185
186 for i in 0..6 {
187 num = num * z + num_coeffs[5 - i];
188 den = den * z + den_coeffs[5 - i];
189 }
190
191 let euler_mascheroni = get_constant("euler_mascheroni");
192 euler_mascheroni + x.ln() + x * (num / den)
193 } else {
194 let mut sum = 1.0;
196 let mut term = 1.0;
197
198 for k in 1..10 {
199 term *= k as f64 / x;
200 sum += term;
201
202 if term.abs() < 1e-15 * sum.abs() {
203 break;
204 }
205 }
206
207 sum * x.exp() / x
208 }
209}
210
211#[allow(dead_code)]
221pub fn exponential_integral_e1_pade(x: f64) -> f64 {
222 assert!(x > 0.0, "E₁(x) is only defined for x > 0");
223
224 if x < 1.0 {
225 let euler_mascheroni = get_constant("euler_mascheroni");
227 let mut sum = -euler_mascheroni - x.ln();
228 let mut term = -1.0;
229 let mut factorial = 1.0;
230
231 for k in 1..15 {
232 let k_f64 = k as f64;
233 term *= -x / k_f64;
234 factorial *= k_f64;
235 let contribution = term / (k_f64 * factorial);
236 sum -= contribution;
237
238 if contribution.abs() < 1e-15 * sum.abs() {
239 break;
240 }
241 }
242
243 -sum
244 } else {
245 let mut b = x + 1.0;
247 let mut c = 1.0 / 1e-30; let mut d = 1.0 / b;
249 let mut h = d;
250
251 for i in 1..20 {
252 let a = -(i * i) as f64;
254 b += 2.0;
255 d = 1.0 / (b + a * d);
256 c = b + a / c;
257 let del = c * d;
258 h *= del;
259
260 if (del - 1.0).abs() < 1e-10 {
261 break;
262 }
263 }
264
265 h * (-x).exp()
266 }
267}
268
269pub mod simd {
271 use super::*;
272
273 pub fn exp_simd(values: &[f64]) -> Vec<f64> {
278 values
281 .iter()
282 .map(|&x| {
283 if x > 700.0 {
284 f64::INFINITY
285 } else if x < -700.0 {
286 0.0
287 } else {
288 x.exp()
289 }
290 })
291 .collect()
292 }
293
294 pub fn ln_simd(values: &[f64]) -> Vec<f64> {
296 values
297 .iter()
298 .map(|&x| {
299 if x <= 0.0 {
300 f64::NAN
301 } else if x == 1.0 {
302 0.0
303 } else {
304 x.ln()
305 }
306 })
307 .collect()
308 }
309
310 pub fn sin_simd(values: &[f64]) -> Vec<f64> {
312 values
313 .iter()
314 .map(|&x| {
315 if x.is_infinite() || x.is_nan() {
316 f64::NAN
317 } else {
318 let reduced = x % (2.0 * PI);
320 reduced.sin()
321 }
322 })
323 .collect()
324 }
325
326 pub fn cos_simd(values: &[f64]) -> Vec<f64> {
328 values
329 .iter()
330 .map(|&x| {
331 if x.is_infinite() || x.is_nan() {
332 f64::NAN
333 } else {
334 let reduced = x % (2.0 * PI);
336 reduced.cos()
337 }
338 })
339 .collect()
340 }
341
342 pub fn gamma_simd(values: &[f64]) -> Vec<f64> {
344 use crate::gamma::gamma;
345 values.iter().map(|&x| gamma(x)).collect()
346 }
347}
348
349pub mod lookup_tables {
351 use super::*;
352 use std::sync::OnceLock;
353
354 static FACTORIAL_TABLE: OnceLock<Vec<f64>> = OnceLock::new();
356
357 fn get_factorial_table() -> &'static Vec<f64> {
359 FACTORIAL_TABLE.get_or_init(|| {
360 let mut table = Vec::with_capacity(21);
361 table.push(1.0); for i in 1..=20 {
363 table.push(table[i - 1] * i as f64);
364 }
365 table
366 })
367 }
368
369 pub fn factorial_lookup(n: u32) -> Option<f64> {
371 if n <= 20 {
372 Some(get_factorial_table()[n as usize])
373 } else {
374 None
375 }
376 }
377
378 static DOUBLE_FACTORIAL_TABLE: OnceLock<Vec<f64>> = OnceLock::new();
380
381 fn get_double_factorial_table() -> &'static Vec<f64> {
382 DOUBLE_FACTORIAL_TABLE.get_or_init(|| {
383 let mut table = Vec::with_capacity(31);
384 table.push(1.0); table.push(1.0); let mut even_val = 2.0;
389 let mut odd_val = 1.0;
391
392 for i in 2..=30 {
393 if i % 2 == 0 {
394 even_val *= i as f64;
395 table.push(even_val);
396 } else {
397 odd_val *= i as f64;
398 table.push(odd_val);
399 }
400 }
401 table
402 })
403 }
404
405 pub fn double_factorial_lookup(n: u32) -> Option<f64> {
407 if n <= 30 {
408 Some(get_double_factorial_table()[n as usize])
409 } else {
410 None
411 }
412 }
413
414 static GAMMA_TABLE: OnceLock<Vec<(f64, f64)>> = OnceLock::new();
416
417 fn get_gamma_table() -> &'static Vec<(f64, f64)> {
418 GAMMA_TABLE.get_or_init(|| {
419 vec![
420 (0.5, (PI).sqrt()),
421 (1.0, 1.0),
422 (1.5, (PI).sqrt() / 2.0),
423 (2.0, 1.0),
424 (2.5, 3.0 * (PI).sqrt() / 4.0),
425 (3.0, 2.0),
426 (3.5, 15.0 * (PI).sqrt() / 8.0),
427 (4.0, 6.0),
428 (4.5, 105.0 * (PI).sqrt() / 16.0),
429 (5.0, 24.0),
430 ]
431 })
432 }
433
434 pub fn gamma_lookup(x: f64) -> Option<f64> {
436 let table = get_gamma_table();
437 for &(arg, value) in table {
438 if (x - arg).abs() < 1e-15 {
439 return Some(value);
440 }
441 }
442 None
443 }
444
445 static J0_ZEROS_TABLE: OnceLock<Vec<f64>> = OnceLock::new();
447
448 fn get_j0_zeros_table() -> &'static Vec<f64> {
449 J0_ZEROS_TABLE.get_or_init(|| {
450 vec![
451 2.404_825_557_695_773,
452 5.520_078_110_286_311,
453 8.653_727_912_911_013,
454 11.791_534_439_014_281,
455 14.930_917_708_487_787,
456 18.071_063_967_910_924,
457 21.211_636_629_879_26,
458 24.352_471_530_749_302,
459 27.493_479_132_040_253,
460 30.634_606_468_431_976,
461 ]
462 })
463 }
464
465 pub fn j0_zero(n: usize) -> Option<f64> {
467 let table = get_j0_zeros_table();
468 if n < table.len() {
469 Some(table[n])
470 } else {
471 if n > 0 {
473 let n_f = n as f64;
474 Some(PI * (n_f + 0.25))
475 } else {
476 None
477 }
478 }
479 }
480}
481
482pub mod poly_approx {
484 use super::*;
485
486 const ERF_COEFFS: &[f64] = &[
488 std::f64::consts::FRAC_2_SQRT_PI, -0.3761263890318376, 0.1128379167095513, -0.0268661098637320, 0.0052308506508171, ];
494
495 pub fn erf_approx(x: f64) -> f64 {
497 if x.abs() > 1.0 {
498 return if x > 0.0 { 1.0 } else { -1.0 };
499 }
500
501 let x2 = x * x;
502 let mut result = 0.0;
503 let mut power = x;
504
505 for &coeff in ERF_COEFFS {
506 result += coeff * power;
507 power *= x2;
508 }
509
510 result
511 }
512
513 const EXP_NEG_X2_COEFFS: &[f64] = &[
515 1.0,
516 -1.0,
517 0.5,
518 -0.16666666666666666,
519 0.041666666666666664,
520 -0.008333333333333333,
521 ];
522
523 pub fn exp_neg_x2_approx(x: f64) -> f64 {
525 let x2 = x * x;
526 if x2 > 5.0 {
527 return 0.0;
528 }
529
530 (-x2).exp()
533 }
534
535 pub fn cos_chebyshev_approx(x: f64) -> f64 {
537 let t = x / PI;
539 if t.abs() > 1.0 {
540 return x.cos(); }
542
543 let coeffs = [
545 1.0,
546 0.0,
547 -4.934_802_200_544_679,
548 0.0,
549 4.0587121264167687,
550 0.0,
551 -1.3352627688545894,
552 ];
553
554 chebyshev_eval(t, &coeffs)
555 }
556
557 fn chebyshev_eval(x: f64, coeffs: &[f64]) -> f64 {
559 if coeffs.is_empty() {
560 return 0.0;
561 }
562 if coeffs.len() == 1 {
563 return coeffs[0];
564 }
565
566 let mut b0 = 0.0;
567 let mut b1 = 0.0;
568 let mut b2 = 0.0;
569
570 for &coeff in coeffs.iter().rev() {
571 b2 = b1;
572 b1 = b0;
573 b0 = coeff + 2.0 * x * b1 - b2;
574 }
575
576 0.5 * (b0 - b2)
577 }
578}
579
580pub mod enhanced_caching {
582 use super::*;
583 use std::sync::OnceLock;
584
585 static GAMMA_CACHE: OnceLock<Mutex<HashMap<u64, f64>>> = OnceLock::new();
587
588 fn f64_to_key(x: f64) -> u64 {
590 x.to_bits()
592 }
593
594 fn get_gamma_cache() -> &'static Mutex<HashMap<u64, f64>> {
595 GAMMA_CACHE.get_or_init(|| Mutex::new(HashMap::new()))
596 }
597
598 pub fn gamma_cached(x: f64) -> f64 {
600 let key = f64_to_key(x);
601
602 if let Ok(cache) = get_gamma_cache().lock() {
604 if let Some(&cached_value) = cache.get(&key) {
605 return cached_value;
606 }
607 }
608
609 use crate::gamma::gamma;
611 let result = gamma(x);
612
613 if let Ok(mut cache) = get_gamma_cache().lock() {
614 if cache.len() < 1000 {
616 cache.insert(key, result);
617 }
618 }
619
620 result
621 }
622
623 pub fn clear_gamma_cache() {
625 if let Ok(mut cache) = get_gamma_cache().lock() {
626 cache.clear();
627 }
628 }
629
630 static BINOMIAL_CACHE: OnceLock<Mutex<HashMap<(u32, u32), f64>>> = OnceLock::new();
632
633 fn get_binomial_cache() -> &'static Mutex<HashMap<(u32, u32), f64>> {
634 BINOMIAL_CACHE.get_or_init(|| Mutex::new(HashMap::new()))
635 }
636
637 pub fn binomial_cached(n: u32, k: u32) -> SpecialResult<f64> {
639 let key = (n, k);
640
641 if let Ok(cache) = get_binomial_cache().lock() {
643 if let Some(&cached_value) = cache.get(&key) {
644 return Ok(cached_value);
645 }
646 }
647
648 use crate::combinatorial::binomial;
650 let result = binomial(n, k)?;
651
652 if let Ok(mut cache) = get_binomial_cache().lock() {
654 if cache.len() < 1000 {
655 cache.insert(key, result);
656 }
657 }
658
659 Ok(result)
660 }
661}
662
663pub mod adaptive {
665 use super::*;
666
667 pub fn gamma_adaptive(x: f64) -> f64 {
669 if let Some(cached) = lookup_tables::gamma_lookup(x) {
671 return cached;
672 }
673
674 if x.fract() == 0.0 && x > 0.0 && x <= 21.0 {
676 if let Some(factorial) = lookup_tables::factorial_lookup((x as u32) - 1) {
677 return factorial;
678 }
679 }
680
681 enhanced_caching::gamma_cached(x)
683 }
684
685 pub fn exp_adaptive(x: f64) -> f64 {
687 if x > 700.0 {
689 return f64::INFINITY;
690 }
691 if x < -700.0 {
692 return 0.0;
693 }
694
695 if x.abs() < 0.1 {
697 return 1.0 + x + 0.5 * x * x + x * x * x / 6.0;
698 }
699
700 x.exp()
702 }
703
704 pub fn sin_adaptive(x: f64) -> f64 {
706 if x.is_infinite() || x.is_nan() {
707 return f64::NAN;
708 }
709
710 if x.abs() < 0.1 {
712 let x2 = x * x;
713 return x * (1.0 - x2 / 6.0 + x2 * x2 / 120.0);
714 }
715
716 let reduced = x % (2.0 * PI);
718 reduced.sin()
719 }
720}
721
722pub mod vectorized {
724 use super::*;
725
726 pub fn exp_vectorized(input: &[f64], output: &mut [f64]) -> SpecialResult<()> {
728 if input.len() != output.len() {
729 return Err(SpecialError::DomainError(
730 "Input and output arrays must have the same length".to_string(),
731 ));
732 }
733
734 for (i, &x) in input.iter().enumerate() {
735 output[i] = adaptive::exp_adaptive(x);
736 }
737
738 Ok(())
739 }
740
741 pub fn sin_vectorized(input: &[f64], output: &mut [f64]) -> SpecialResult<()> {
743 if input.len() != output.len() {
744 return Err(SpecialError::DomainError(
745 "Input and output arrays must have the same length".to_string(),
746 ));
747 }
748
749 for (i, &x) in input.iter().enumerate() {
750 output[i] = adaptive::sin_adaptive(x);
751 }
752
753 Ok(())
754 }
755
756 pub fn gamma_vectorized(input: &[f64], output: &mut [f64]) -> SpecialResult<()> {
758 if input.len() != output.len() {
759 return Err(SpecialError::DomainError(
760 "Input and output arrays must have the same length".to_string(),
761 ));
762 }
763
764 for (i, &x) in input.iter().enumerate() {
765 output[i] = adaptive::gamma_adaptive(x);
766 }
767
768 Ok(())
769 }
770
771 pub fn batch_compute<F>(input: &[f64], output: &mut [f64], func: F) -> SpecialResult<()>
773 where
774 F: Fn(f64) -> f64,
775 {
776 if input.len() != output.len() {
777 return Err(SpecialError::DomainError(
778 "Input and output arrays must have the same length".to_string(),
779 ));
780 }
781
782 const CHUNK_SIZE: usize = 64;
784
785 for chunk in input.chunks(CHUNK_SIZE).zip(output.chunks_mut(CHUNK_SIZE)) {
786 let (input_chunk, output_chunk) = chunk;
787 for (i, &x) in input_chunk.iter().enumerate() {
788 output_chunk[i] = func(x);
789 }
790 }
791
792 Ok(())
793 }
794}
795
796#[allow(dead_code)]
798fn bessel_j0_series(x: f64) -> f64 {
799 if x.abs() < 1e-14 {
800 return 1.0;
801 }
802
803 let x_half = x / 2.0;
804 let x_half_squared = x_half * x_half;
805
806 let mut sum = 1.0;
807 let mut term = 1.0;
808 let mut k = 1;
809
810 while k <= 50 {
811 term *= -x_half_squared / ((k * k) as f64);
812 sum += term;
813
814 if term.abs() < 1e-15 * sum.abs() {
815 break;
816 }
817 k += 1;
818 }
819
820 sum
821}
822
823#[allow(dead_code)]
825fn gamma_stirling(x: f64) -> f64 {
826 if x < 0.5 {
827 return PI / ((PI * x).sin() * gamma_stirling(1.0 - x));
829 }
830
831 let ln_gamma = (x - 0.5) * x.ln() - x + 0.5 * (2.0_f64 * PI).ln() + 1.0 / (12.0 * x)
833 - 1.0 / (360.0 * x.powi(3))
834 + 1.0 / (1260.0 * x.powi(5));
835 ln_gamma.exp()
836}
837
838#[allow(dead_code)]
840pub fn bessel_j0_fast(x: f64) -> f64 {
841 let abs_x = x.abs();
842
843 if abs_x < 10.0 {
844 let index = (abs_x * 100.0) as usize;
845 if index < 999 {
846 let x1 = index as f64 * 0.01;
848 let x2 = (index + 1) as f64 * 0.01;
849 let y1 = BESSEL_J0_LOOKUP[index];
850 let y2 = BESSEL_J0_LOOKUP[index + 1];
851 return y1 + (y2 - y1) * (abs_x - x1) / (x2 - x1);
852 }
853 }
854
855 bessel_j0_series(abs_x)
857}
858
859#[allow(dead_code)]
861pub fn gamma_fast(x: f64) -> f64 {
862 if (x - 1.0).abs() < 1e-14 {
864 return 1.0;
865 } if (x - 2.0).abs() < 1e-14 {
867 return 1.0;
868 } if (x - 3.0).abs() < 1e-14 {
870 return 2.0;
871 } if (x - 4.0).abs() < 1e-14 {
873 return 6.0;
874 } if (x - 5.0).abs() < 1e-14 {
876 return 24.0;
877 } if x > 0.1 && x < 10.1 {
880 let index = ((x - 0.1) * 100.0) as usize;
881 if index < 999 {
882 let x1 = 0.1 + index as f64 * 0.01;
884 let x2 = 0.1 + (index + 1) as f64 * 0.01;
885 let y1 = GAMMA_LOOKUP[index];
886 let y2 = GAMMA_LOOKUP[index + 1];
887 return y1 + (y2 - y1) * (x - x1) / (x2 - x1);
888 }
889 }
890
891 gamma_stirling(x)
893}
894
895#[cfg(test)]
896mod tests {
897 use super::*;
898 use approx::assert_relative_eq;
899
900 #[test]
901 fn test_simd_operations() {
902 let values = vec![0.0, 1.0, 2.0, -1.0];
903
904 let exp_results = simd::exp_simd(&values);
905 assert_relative_eq!(exp_results[0], 1.0, epsilon = 1e-10);
906 assert_relative_eq!(exp_results[1], std::f64::consts::E, epsilon = 1e-10);
907
908 let ln_results = simd::ln_simd(&[1.0, std::f64::consts::E, 10.0]);
909 assert_relative_eq!(ln_results[0], 0.0, epsilon = 1e-10);
910 assert_relative_eq!(ln_results[1], 1.0, epsilon = 1e-10);
911 }
912
913 #[test]
914 fn test_lookup_tables() {
915 assert_eq!(lookup_tables::factorial_lookup(0), Some(1.0));
917 assert_eq!(lookup_tables::factorial_lookup(5), Some(120.0));
918 assert_eq!(lookup_tables::factorial_lookup(25), None);
919
920 assert_relative_eq!(
922 lookup_tables::gamma_lookup(0.5).expect("Operation failed"),
923 (PI).sqrt(),
924 epsilon = 1e-10
925 );
926 assert_eq!(lookup_tables::gamma_lookup(1.0), Some(1.0));
927
928 assert!(lookup_tables::j0_zero(0).is_some());
930 assert!(lookup_tables::j0_zero(100).is_some()); }
932
933 #[test]
934 fn test_polynomial_approximations() {
935 assert_relative_eq!(poly_approx::erf_approx(0.0), 0.0, epsilon = 1e-3);
937 assert_relative_eq!(poly_approx::erf_approx(0.5), 0.5205, epsilon = 1e-2);
938
939 assert_relative_eq!(poly_approx::exp_neg_x2_approx(0.0), 1.0, epsilon = 1e-10);
941 assert_relative_eq!(
942 poly_approx::exp_neg_x2_approx(1.0),
943 (-1.0_f64).exp(),
944 epsilon = 1e-1
945 );
946 }
947
948 #[test]
949 fn test_enhanced_caching() {
950 enhanced_caching::clear_gamma_cache();
952
953 let x = 2.5;
955 let result1 = enhanced_caching::gamma_cached(x);
956 let result2 = enhanced_caching::gamma_cached(x); assert_eq!(result1, result2);
958
959 let binom1 = enhanced_caching::binomial_cached(10, 3).expect("Operation failed");
961 let binom2 = enhanced_caching::binomial_cached(10, 3).expect("Operation failed"); assert_eq!(binom1, binom2);
963 assert_eq!(binom1, 120.0);
964 }
965
966 #[test]
967 fn test_adaptive_algorithms() {
968 assert_relative_eq!(adaptive::gamma_adaptive(1.0), 1.0, epsilon = 1e-10);
970 assert_relative_eq!(adaptive::gamma_adaptive(5.0), 24.0, epsilon = 1e-10);
971
972 assert_relative_eq!(adaptive::exp_adaptive(0.0), 1.0, epsilon = 1e-10);
974 assert_relative_eq!(
975 adaptive::exp_adaptive(1.0),
976 std::f64::consts::E,
977 epsilon = 1e-10
978 );
979
980 assert_relative_eq!(adaptive::sin_adaptive(0.0), 0.0, epsilon = 1e-10);
982 assert_relative_eq!(adaptive::sin_adaptive(PI / 2.0), 1.0, epsilon = 1e-10);
983 }
984
985 #[test]
986 fn test_vectorized_operations() {
987 let input = vec![0.0, 1.0, 2.0];
988 let mut output = vec![0.0; 3];
989
990 vectorized::exp_vectorized(&input, &mut output).expect("Operation failed");
992 assert_relative_eq!(output[0], 1.0, epsilon = 1e-10);
993 assert_relative_eq!(output[1], std::f64::consts::E, epsilon = 1e-10);
994
995 let sin_input = vec![0.0, PI / 2.0, PI];
997 let mut sin_output = vec![0.0; 3];
998 vectorized::sin_vectorized(&sin_input, &mut sin_output).expect("Operation failed");
999 assert_relative_eq!(sin_output[0], 0.0, epsilon = 1e-10);
1000 assert_relative_eq!(sin_output[1], 1.0, epsilon = 1e-10);
1001 assert_relative_eq!(sin_output[2], 0.0, epsilon = 1e-10);
1002 }
1003
1004 #[test]
1005 fn test_batch_compute() {
1006 let input = vec![1.0, 2.0, 3.0, 4.0];
1007 let mut output = vec![0.0; 4];
1008
1009 vectorized::batch_compute(&input, &mut output, |x| x * x).expect("Operation failed");
1010
1011 for (i, &expected) in [1.0, 4.0, 9.0, 16.0].iter().enumerate() {
1012 assert_relative_eq!(output[i], expected, epsilon = 1e-10);
1013 }
1014 }
1015
1016 #[test]
1017 fn test_bessel_j0_fast() {
1018 assert_relative_eq!(bessel_j0_fast(0.0), 1.0, epsilon = 1e-10);
1020 assert_relative_eq!(bessel_j0_fast(1.0), 0.7651976865579666, epsilon = 1e-8);
1021 assert_relative_eq!(bessel_j0_fast(2.0), 0.22389077914123566, epsilon = 1e-8);
1022
1023 for x in [0.5, 1.5, 3.0, 5.0] {
1025 let fast_result = bessel_j0_fast(x);
1026 let series_result = bessel_j0_series(x);
1027 assert_relative_eq!(fast_result, series_result, epsilon = 1e-6);
1028 }
1029 }
1030
1031 #[test]
1032 fn test_gamma_fast() {
1033 assert_relative_eq!(gamma_fast(1.0), 1.0, epsilon = 1e-10);
1035 assert_relative_eq!(gamma_fast(2.0), 1.0, epsilon = 1e-10);
1036 assert_relative_eq!(gamma_fast(3.0), 2.0, epsilon = 1e-10);
1037 assert_relative_eq!(gamma_fast(4.0), 6.0, epsilon = 1e-10);
1038
1039 for x in [0.5, 1.5, 2.5, 5.0, 8.0] {
1041 let fast_result = gamma_fast(x);
1042 let stirling_result = gamma_stirling(x);
1043 assert_relative_eq!(fast_result, stirling_result, epsilon = 1e-6);
1044 }
1045 }
1046}