1use std::f64;
8use std::f64::consts::PI;
9
10const SQPIO2: f64 = 1.253_314_137_315_500_3;
12
13const SQ2PI: f64 = 2.506_628_274_631_000_7;
15
16const GAMMA_STIRLING_MIN: f64 = 11.5;
18
19const GAMMA_OVERFLOW_ARG: f64 = 172.0;
22
23const GAMMA_UNDERFLOW_ARG: f64 = 200.0;
27
28const MAX_ITER: usize = 5000;
30
31fn evalpoly(x: f64, coeffs: &[f64]) -> f64 {
33 let mut result = 0.0;
34 for &coeff in coeffs.iter().rev() {
35 result = result * x + coeff;
36 }
37 result
38}
39
40fn sinpi(x: f64) -> f64 {
48 let n = x.round();
49 let s = (PI * (x - n)).sin();
50 if n.rem_euclid(2.0) == 0.0 { s } else { -s }
51}
52
53fn gamma_stirling_factors(x: f64) -> (f64, f64, f64) {
59 let coefs = [
60 1.0,
61 8.333_333_333_333_331e-2,
62 3.472_222_222_230_075e-3,
63 -2.681_327_161_876_304_3e-3,
64 -2.294_719_747_873_185_4e-4,
65 7.840_334_842_744_753e-4,
66 6.989_332_260_623_193e-5,
67 -5.950_237_554_056_33e-4,
68 -2.363_848_809_501_759e-5,
69 7.147_391_378_143_611e-4,
70 ];
71 let w = evalpoly(1.0 / x, &coefs);
72 let v = x.powf(0.5 * x - 0.25);
73 (v, v / x.exp(), w)
74}
75
76pub fn gamma_func(x: f64) -> f64 {
106 if x.is_nan() {
107 return x;
108 }
109 if x <= 0.0 && x == x.floor() {
110 return if x == 0.0 { 1.0 / x } else { f64::NAN };
113 }
114 if x > 0.0 {
115 return gamma_positive(x);
116 }
117 if x > -1.0 {
118 return gamma_positive(1.0 + x) / x;
120 }
121
122 let ax = -x;
125 let s = ax * sinpi(x);
126 if ax <= GAMMA_STIRLING_MIN {
127 return PI / (s * gamma_positive(ax));
128 }
129 if ax > GAMMA_UNDERFLOW_ARG {
130 return 0.0_f64.copysign(s);
131 }
132 let (v, t, w) = gamma_stirling_factors(ax);
136 PI / (s * SQ2PI * w * v) / t
137}
138
139fn gamma_positive(x: f64) -> f64 {
141 if x > GAMMA_STIRLING_MIN {
142 if x > GAMMA_OVERFLOW_ARG {
143 return f64::INFINITY;
145 }
146 let (v, t, w) = gamma_stirling_factors(x);
147 return SQ2PI * v * t * w;
148 }
149
150 let p = [
151 1.0,
152 8.378_004_301_573_126e-1,
153 3.629_515_436_640_239_3e-1,
154 1.113_062_816_019_361_6e-1,
155 2.385_363_243_461_108_3e-2,
156 4.092_666_828_394_036e-3,
157 4.542_931_960_608_009_3e-4,
158 4.212_760_487_471_622e-5,
159 ];
160
161 let q = [
162 1.0,
163 4.150_160_950_588_455_7e-1,
164 -2.243_510_905_670_329_2e-1,
165 -4.633_887_671_244_534e-2,
166 2.773_706_565_840_073e-2,
167 -7.955_933_682_494_738e-4,
168 -1.237_799_246_653_152_3e-3,
169 2.346_584_059_160_635e-4,
170 -1.397_148_517_476_170_5e-5,
171 ];
172
173 let mut x = x;
175 let mut z = 1.0;
176 while x >= 3.0 {
177 x -= 1.0;
178 z *= x;
179 }
180
181 while x < 2.0 {
182 z /= x;
183 x += 1.0;
184 }
185
186 if x == 2.0 {
187 return z;
188 }
189
190 x -= 2.0;
191 let p_val = evalpoly(x, &p);
192 let q_val = evalpoly(x, &q);
193
194 z * p_val / q_val
195}
196
197pub fn cyl_bessel_j(nu: f64, x: f64) -> f64 {
202 let eps = f64::EPSILON;
203 let mut _sum = 0.0;
204 let mut term = (x / 2.0).powf(nu) / gamma_func(nu + 1.0);
205 _sum = term;
206
207 for m in 1..1000 {
208 term *= -(x * x / 4.0) / (m as f64 * (nu + m as f64));
209 _sum += term;
210 if term.abs() < _sum.abs() * eps {
211 break;
212 }
213 }
214
215 _sum
216}
217
218fn spherical_bessel_j_generic(nu: f64, x: f64) -> f64 {
221 SQPIO2 * cyl_bessel_j(nu + 0.5, x) / x.sqrt()
222}
223
224fn spherical_bessel_j_small_args(nu: f64, x: f64) -> f64 {
226 if x == 0.0 {
227 return if nu == 0.0 { 1.0 } else { 0.0 };
228 }
229
230 let x2 = (x * x) / 4.0;
231 let coef = [
232 1.0,
233 -1.0 / (1.5 + nu), -1.0 / (5.0 + nu),
235 -1.0 / ((21.0 / 2.0) + nu), -1.0 / (18.0 + nu),
237 ];
238
239 let a = SQPIO2 / (gamma_func(1.5 + nu) * 2.0_f64.powf(nu + 0.5));
240 x.powf(nu) * a * evalpoly(x2, &coef)
241}
242
243fn spherical_bessel_j_small_args_cutoff(nu: f64, x: f64) -> bool {
245 (x * x) / (4.0 * nu + 110.0) < f64::EPSILON
246}
247
248fn bessel_j_ratio_jnu_jnum1(n: f64, x: f64) -> f64 {
250 let xinv = 1.0 / x;
251 let xinv2 = 2.0 * xinv;
252 let mut d = x / (2.0 * n);
253 let mut a = d;
254 let mut h = a;
255 let mut b = (2.0 * n + 2.0) * xinv;
256
257 for _i in 0..MAX_ITER {
258 d = 1.0 / (b - d);
259 a *= b * d - 1.0;
260 h += a;
261 b += xinv2;
262
263 if (a / h).abs() <= f64::EPSILON {
264 break;
265 }
266 }
267
268 h
269}
270
271fn spherical_bessel_y_forward_recurrence(nu: i32, x: f64) -> (f64, f64) {
274 let xinv = 1.0 / x;
275 let s = x.sin();
276 let c = x.cos();
277 let mut s_y0 = -c * xinv;
278 let mut s_y1 = xinv * (s_y0 - s);
279 let mut nu_start = 1.0;
280
281 while nu_start < nu as f64 + 0.5 {
282 let temp = s_y1;
283 s_y1 = (2.0 * nu_start + 1.0) * xinv * s_y1 - s_y0;
284 s_y0 = temp;
285 nu_start += 1.0;
286 }
287
288 (s_y0, s_y1)
289}
290
291fn spherical_bessel_j_recurrence(nu: i32, x: f64) -> f64 {
293 if x >= nu as f64 {
294 let xinv = 1.0 / x;
295 let s = x.sin();
296 let c = x.cos();
297 let mut s_j0 = s * xinv;
298 let mut s_j1 = (s_j0 - c) * xinv;
299 let mut nu_start = 1.0;
300
301 while nu_start < nu as f64 + 0.5 {
302 let temp = s_j1;
303 s_j1 = (2.0 * nu_start + 1.0) * xinv * s_j1 - s_j0;
304 s_j0 = temp;
305 nu_start += 1.0;
306 }
307
308 s_j0
309 } else {
310 let (s_ynm1, s_yn) = spherical_bessel_y_forward_recurrence(nu, x);
313 let h = bessel_j_ratio_jnu_jnum1(nu as f64 + 1.5, x);
314 1.0 / (x * x * (h * s_ynm1 - s_yn))
315 }
316}
317
318fn spherical_bessel_j_positive_args(nu: i32, x: f64) -> f64 {
320 if spherical_bessel_j_small_args_cutoff(nu as f64, x) {
321 spherical_bessel_j_small_args(nu as f64, x)
322 } else if (x >= nu as f64 && nu < 250) || (x < nu as f64 && nu < 60) {
323 spherical_bessel_j_recurrence(nu, x)
325 } else {
326 spherical_bessel_j_generic(nu as f64, x)
327 }
328}
329
330pub fn spherical_bessel_j(n: i32, x: f64) -> f64 {
334 if x < 0.0 {
336 panic!("sphericalBesselJ requires non-negative x");
337 }
338
339 if n < 0 {
341 let result = spherical_bessel_j_positive_args(-n, x);
342 if n % 2 == 0 { result } else { -result }
343 } else {
344 spherical_bessel_j_positive_args(n, x)
345 }
346}
347
348#[cfg(test)]
349mod tests {
350 use super::*;
351
352 #[test]
353 fn test_gamma_function() {
354 assert!((gamma_func(1.0) - 1.0).abs() < 1e-10);
356 assert!((gamma_func(2.0) - 1.0).abs() < 1e-10);
357 assert!((gamma_func(3.0) - 2.0).abs() < 1e-10);
358 assert!((gamma_func(4.0) - 6.0).abs() < 1e-10);
359
360 assert!((gamma_func(0.5) - 1.7724538509055159).abs() < 1e-10); }
363
364 const GAMMA_RTOL: f64 = 1e-14;
379
380 const BESSEL_RTOL: f64 = 2e-14;
387
388 fn rel_err(got: f64, want: f64) -> f64 {
389 ((got - want) / want).abs()
390 }
391
392 #[test]
393 fn test_gamma_negative_non_integer_reference_values() {
394 let cases = [
397 (-0.5, -3.544907701811032),
398 (-1.5, 2.363271801207355),
399 (-2.5, -0.9453087204829419),
400 (-3.5, 0.2700882058522691),
401 (-0.1, -10.686287021193193),
402 (-1.0e-200, -1.0e200),
403 (-0.999, -1000.4241966812758),
404 (-1.001, 999.5786270024664),
405 (-3.0 + 2f64.powi(-30), -1.789569708760196e8),
406 (-10.1, -2.2134165830856185e-6),
407 (-11.3, 4.656958619058061e-8),
408 (-11.7, 1.7234064143490278e-8),
409 (-12.5, -1.836606483859281e-9),
410 (-20.5, -2.834656574391335e-19),
411 (-30.7, -1.3275373492818893e-33),
412 (-100.25, -1.503087709322751e-158),
413 (-150.9, -1.9468126352925122e-264),
414 (-170.5, -3.3127395215386074e-308),
415 (-175.0 + 2f64.powi(-40), -9.778221578627872e-307),
417 ];
418 for (x, want) in cases {
419 let got = gamma_func(x);
420 let err = rel_err(got, want);
421 assert!(
422 err <= GAMMA_RTOL,
423 "gamma_func({x:e}) = {got:e}, expected {want:e} (relative error {err:e})"
424 );
425 }
426 }
427
428 #[test]
429 fn test_gamma_positive_reference_values() {
430 let cases = [
432 (0.5, 1.772453850905516),
433 (2.5, 1.329340388179137),
434 (11.5, 1.1899423083962249e7),
435 (11.6, 1.5131318919703094e7),
436 (12.5, 1.3684336546556586e8),
437 (13.0, 4.790016e8),
438 (20.0, 1.21645100408832e17),
439 (25.0, 6.204484017332394e23),
440 (50.5, 4.29046291235196e63),
441 (100.0, 9.332621544394415e155),
442 (170.5, 5.56209241456e305),
443 (171.5, 9.4833675668248e307),
444 ];
445 for (x, want) in cases {
446 let got = gamma_func(x);
447 let err = rel_err(got, want);
448 assert!(
449 err <= GAMMA_RTOL,
450 "gamma_func({x:e}) = {got:e}, expected {want:e} (relative error {err:e})"
451 );
452 }
453 }
454
455 #[test]
456 fn test_gamma_recurrence() {
457 const RECURRENCE_RTOL: f64 = 2e-14;
464 let fracs = [
465 2f64.powi(-20),
466 0.125,
467 0.25,
468 0.5,
469 0.75,
470 0.875,
471 1.0 - 2f64.powi(-20),
472 ];
473 for k in 0..170 {
474 for f in fracs {
475 for x in [-(k as f64 + f), k as f64 + f] {
476 let lhs = gamma_func(x + 1.0);
477 let rhs = x * gamma_func(x);
478 let err = rel_err(rhs, lhs);
479 assert!(
480 err <= RECURRENCE_RTOL,
481 "Γ({x:e} + 1) = {lhs:e} but x Γ(x) = {rhs:e} (relative error {err:e})"
482 );
483 }
484 }
485 }
486 }
487
488 #[test]
489 fn test_gamma_poles_and_special_values() {
490 for x in [
492 -1.0,
493 -2.0,
494 -3.0,
495 -171.0,
496 -1e10,
497 -(2f64.powi(52)),
498 -1e300,
499 f64::MIN,
500 ] {
501 let got = gamma_func(x);
502 assert!(got.is_nan(), "gamma_func({x:e}) = {got:e}, expected NaN");
503 }
504 assert_eq!(gamma_func(0.0), f64::INFINITY);
506 assert_eq!(gamma_func(-0.0), f64::NEG_INFINITY);
507 assert_eq!(gamma_func(f64::INFINITY), f64::INFINITY);
508 assert!(gamma_func(f64::NEG_INFINITY).is_nan());
509 assert!(gamma_func(f64::NAN).is_nan());
510
511 for x in [171.7, 172.0, 709.0, 710.0, 1000.0, 1e300] {
513 let got = gamma_func(x);
514 assert_eq!(got, f64::INFINITY, "gamma_func({x:e}) = {got:e}");
515 }
516
517 let (got, want) = (gamma_func(-171.5), 1.9316265431712e-310);
521 assert!(
522 (got - want).abs() <= GAMMA_RTOL * want + 2.0 * f64::from_bits(1),
523 "gamma_func(-171.5) = {got:e}, expected {want:e}"
524 );
525
526 for (x, negative) in [
529 (-184.5, true),
530 (-200.5, true),
531 (-201.5, false),
532 (-1000.25, true),
533 (-(1e15 + 0.5), true),
534 ] {
535 let got = gamma_func(x);
536 assert!(
537 got == 0.0 && got.is_sign_negative() == negative,
538 "gamma_func({x:e}) = {got:e}, expected {}0",
539 if negative { "-" } else { "+" }
540 );
541 }
542 }
543
544 #[test]
545 fn test_cyl_bessel_j_orders_reaching_gamma_reflection_and_stirling() {
546 let cases = [
552 (-0.5, 1.0, 0.4310988680183761),
553 (-1.5, 1.0, -1.1024955751601793),
554 (-2.5, 2.0, 0.8282206324443038),
555 (-12.7, 1.0, 3.9444125287326807e11),
556 (11.5, 1.0, 2.4730845703448897e-12),
557 (12.0, 2.0, 1.9326951487239857e-9),
558 ];
559 for (nu, x, want) in cases {
560 let got = cyl_bessel_j(nu, x);
561 let err = rel_err(got, want);
562 assert!(
563 err <= BESSEL_RTOL,
564 "J_{nu}({x}) = {got:e}, expected {want:e} (relative error {err:e})"
565 );
566 }
567 }
568
569 #[test]
570 fn test_spherical_bessel_j_small_args_high_order() {
571 let x = 1e-8;
575 let cases = [
576 (11, 3.162213889372793e-100),
577 (12, 1.2648855557491175e-109),
578 (13, 4.6847613175893235e-119),
579 (14, 1.6154349370997668e-128),
580 (15, 5.2110804422573123e-138),
581 ];
582 for (n, want) in cases {
583 assert!(spherical_bessel_j_small_args_cutoff(n as f64, x));
584 let got = spherical_bessel_j(n, x);
585 let err = rel_err(got, want);
586 assert!(
587 err <= BESSEL_RTOL,
588 "j_{n}({x:e}) = {got:e}, expected {want:e} (relative error {err:e})"
589 );
590 }
591 }
592
593 #[test]
594 fn test_cylindrical_bessel_j() {
595 let j0_1 = cyl_bessel_j(0.0, 1.0);
597 let expected_j0_1 = 0.765_197_686_557_966_6;
598 assert!((j0_1 - expected_j0_1).abs() < 1e-10);
599
600 let j1_1 = cyl_bessel_j(1.0, 1.0);
601 let expected_j1_1 = 0.440_050_585_744_933_5;
602 assert!((j1_1 - expected_j1_1).abs() < 1e-10);
603 }
604
605 #[test]
606 fn test_spherical_bessel_j_basic() {
607 let x = 1.0;
609 let j0 = spherical_bessel_j(0, x);
610 let expected_j0 = x.sin() / x;
611 println!("j_0({}) = {}, expected = {}", x, j0, expected_j0);
612 assert!((j0 - expected_j0).abs() < 1e-10);
613
614 let j1 = spherical_bessel_j(1, x);
616 let expected_j1 = x.sin() / (x * x) - x.cos() / x;
617 println!("j_1({}) = {}, expected = {}", x, j1, expected_j1);
618 assert!((j1 - expected_j1).abs() < 1e-10);
619
620 let j0_zero = spherical_bessel_j(0, 0.0);
622 println!("j_0(0) = {}, expected = 1.0", j0_zero);
623 assert!((j0_zero - 1.0).abs() < 1e-10);
624
625 let j1_zero = spherical_bessel_j(1, 0.0);
627 println!("j_1(0) = {}, expected = 0.0", j1_zero);
628 assert!(j1_zero.abs() < 1e-10);
629 }
630
631 #[test]
632 fn test_spherical_bessel_j_various_values() {
633 let test_cases = [
636 (0, 0.1, 0.9983341664682815),
637 (0, 0.5, 0.958_851_077_208_406),
638 (0, 1.0, 0.8414709848078965),
639 (0, 2.0, 0.4546487134128409),
640 (0, 5.0, -0.1917848549326277),
641 (1, 0.1, 0.0333000128900053),
643 (1, 0.5, 0.1625370306360665),
644 (1, 1.0, 0.3011686789397568),
645 (1, 2.0, 0.43539777497999166),
647 (1, 5.0, -0.0950894080791708),
649 ];
650
651 for (n, x, expected) in test_cases {
652 let result = spherical_bessel_j(n, x);
653 println!(
654 "j_{}({}) = {}, expected = {}, diff = {}",
655 n,
656 x,
657 result,
658 expected,
659 (result - expected).abs()
660 );
661
662 assert!(
664 result.is_finite(),
665 "j_{}({}) should be finite, got {}",
666 n,
667 x,
668 result
669 );
670
671 if (result - expected).abs() > 1e-6 {
673 println!(
674 "WARNING: j_{}({}) accuracy issue: got {}, expected {}, diff = {}",
675 n,
676 x,
677 result,
678 expected,
679 (result - expected).abs()
680 );
681 }
682 }
683 }
684
685 #[test]
686 fn test_debug_spherical_bessel_j() {
687 println!("=== Debug j_1(0.1) ===");
689 let x = 0.1;
690 let n = 1;
691
692 let cutoff = spherical_bessel_j_small_args_cutoff(n as f64, x);
694 println!("small_args_cutoff: {}", cutoff);
695
696 if cutoff {
697 let small_result = spherical_bessel_j_small_args(n as f64, x);
698 println!("small_args result: {}", small_result);
699 }
700
701 let recurrence_result = spherical_bessel_j_recurrence(n, x);
702 println!("recurrence result: {}", recurrence_result);
703
704 let generic_result = spherical_bessel_j_generic(n as f64, x);
705 println!("generic result: {}", generic_result);
706
707 let final_result = spherical_bessel_j(n, x);
708 println!("final result: {}", final_result);
709
710 let expected = x.sin() / (x * x) - x.cos() / x;
712 println!("expected (sin(x)/x^2 - cos(x)/x): {}", expected);
713 }
714
715 #[test]
716 fn test_spherical_bessel_j_large_values() {
717 let large_x = 100.0;
719 let j0_large = spherical_bessel_j(0, large_x);
720 println!("j_0({}) = {}", large_x, j0_large);
721 assert!(j0_large.is_finite());
722
723 let j1_large = spherical_bessel_j(1, large_x);
724 println!("j_1({}) = {}", large_x, j1_large);
725 assert!(j1_large.is_finite());
726 }
727
728 #[test]
729 fn test_spherical_bessel_j_cpp_style_high_order() {
730 let refs = [
735 0.8414709848078965,
736 0.30116867893975674,
737 0.06203505201137386,
738 0.009006581117112517,
739 0.0010110158084137527,
740 9.256115861125818e-5,
741 7.156936310087086e-6,
742 4.790134198739489e-7,
743 2.82649880221473e-8,
744 1.4913765025551456e-9,
745 7.116552640047314e-11,
746 3.09955185479008e-12,
747 1.2416625969871055e-13,
748 4.604637677683788e-15,
749 1.5895759875169764e-16,
750 5.1326861154437626e-18,
751 ];
752
753 let x = 1.0;
754 for (l, &expected) in refs.iter().enumerate() {
755 let expected: f64 = expected;
756 let result = spherical_bessel_j(l as i32, x);
757
758 let relative_tolerance = 1e-6;
760 let absolute_tolerance = 1e-12;
761
762 if expected.abs() > absolute_tolerance {
764 let relative_error = (result - expected).abs() / expected.abs();
765 assert!(
766 relative_error <= relative_tolerance,
767 "j_{}({}) relative error too large: {} > {}",
768 l,
769 x,
770 relative_error,
771 relative_tolerance
772 );
773 } else {
774 assert!(
776 (result - expected).abs() <= absolute_tolerance,
777 "j_{}({}) absolute error too large: {} > {}",
778 l,
779 x,
780 (result - expected).abs(),
781 absolute_tolerance
782 );
783 }
784
785 assert!(
787 result.is_finite(),
788 "j_{}({}) should be finite, got {}",
789 l,
790 x,
791 result
792 );
793 }
794 }
795
796 #[test]
797 fn test_spherical_bessel_j_zero_argument() {
798 let j0_zero = spherical_bessel_j(0, 0.0);
801 assert!(
802 (j0_zero - 1.0).abs() < 1e-15,
803 "j_0(0) should be 1, got {}",
804 j0_zero
805 );
806
807 for n in 1..=10 {
808 let jn_zero = spherical_bessel_j(n, 0.0);
809 assert!(
810 jn_zero.abs() < 1e-15,
811 "j_{}(0) should be 0, got {}",
812 n,
813 jn_zero
814 );
815 }
816 }
817
818 #[test]
819 fn test_spherical_bessel_j_negative_orders() {
820 for n in -5..0 {
822 let result_neg = spherical_bessel_j(n, 1.0);
823 let result_pos = spherical_bessel_j(-n, 1.0);
824 let expected = if n % 2 == 0 { result_pos } else { -result_pos };
825 assert!(
826 (result_neg - expected).abs() < 1e-15,
827 "j_{}(1.0) should be {} for negative n, got {}",
828 n,
829 expected,
830 result_neg
831 );
832 }
833 }
834
835 #[test]
836 fn test_spherical_bessel_j_small_arguments() {
837 let small_x_values = [1e-10, 1e-8, 1e-6, 1e-4, 1e-2];
839
840 for &x in &small_x_values {
841 for n in 0..=5 {
842 let result = spherical_bessel_j(n, x);
843 assert!(result.is_finite(), "j_{}({}) should be finite", n, x);
844
845 if x < 1e-6 && n < 3 {
847 let expected_approx = x.powi(n) / double_factorial(2 * n + 1);
848 let relative_error = (result - expected_approx).abs() / expected_approx.abs();
849 assert!(
850 relative_error < 1e-6,
851 "j_{}({}) small argument approximation failed: relative error = {}",
852 n,
853 x,
854 relative_error
855 );
856 }
857 }
858 }
859 }
860
861 #[test]
862 fn test_spherical_bessel_j_large_arguments() {
863 let large_x_values = [10.0, 50.0, 100.0, 500.0];
865
866 for &x in &large_x_values {
867 for n in 0..=3 {
868 let result = spherical_bessel_j(n, x);
869 assert!(result.is_finite(), "j_{}({}) should be finite", n, x);
870
871 let expected_approx = (x - (n as f64) * PI / 2.0).sin() / x;
873 let relative_error =
874 (result - expected_approx).abs() / expected_approx.abs().max(1e-10);
875
876 if x > 50.0 && relative_error > 1e-2 {
877 println!(
878 "WARNING: j_{}({}) large argument approximation: got {}, expected ≈ {}, relative error = {}",
879 n, x, result, expected_approx, relative_error
880 );
881 }
882 }
883 }
884 }
885
886 fn double_factorial(n: i32) -> f64 {
888 if n <= 0 {
889 1.0
890 } else if n % 2 == 0 {
891 let half_n = n / 2;
893 2.0_f64.powi(half_n) * factorial(half_n)
894 } else {
895 let half_n_minus_1 = (n - 1) / 2;
897 factorial(n) / (2.0_f64.powi(half_n_minus_1) * factorial(half_n_minus_1))
898 }
899 }
900
901 fn factorial(n: i32) -> f64 {
903 if n <= 1 {
904 1.0
905 } else {
906 let mut result = 1.0;
907 for i in 2..=n {
908 result *= i as f64;
909 }
910 result
911 }
912 }
913}