mathhook_core/functions/special/
bessel.rs1use crate::core::{Expression, Number};
40use std::f64::consts::PI;
41
42pub fn bessel_j(n: i32, x: &Expression) -> Expression {
60 match x {
61 Expression::Number(Number::Integer(0)) => Expression::Number(if n == 0 {
62 Number::Integer(1)
63 } else {
64 Number::Integer(0)
65 }),
66 Expression::Number(Number::Float(val)) => {
67 Expression::Number(Number::Float(bessel_j_float(n, *val)))
68 }
69 Expression::Number(Number::Integer(val)) => {
70 Expression::Number(Number::Float(bessel_j_float(n, *val as f64)))
71 }
72 _ => Expression::function(
73 "bessel_j",
74 vec![Expression::Number(Number::Integer(n as i64)), x.clone()],
75 ),
76 }
77}
78
79pub fn bessel_y(n: i32, x: &Expression) -> Expression {
97 match x {
98 Expression::Number(Number::Integer(0)) => Expression::function(
99 "bessel_y",
100 vec![Expression::Number(Number::Integer(n as i64)), x.clone()],
101 ),
102 Expression::Number(Number::Float(val)) => {
103 Expression::Number(Number::Float(bessel_y_float(n, *val)))
104 }
105 Expression::Number(Number::Integer(val)) if *val != 0 => {
106 Expression::Number(Number::Float(bessel_y_float(n, *val as f64)))
107 }
108 _ => Expression::function(
109 "bessel_y",
110 vec![Expression::Number(Number::Integer(n as i64)), x.clone()],
111 ),
112 }
113}
114
115fn bessel_j_float(n: i32, x: f64) -> f64 {
119 if x.is_nan() || x.is_infinite() {
120 return f64::NAN;
121 }
122 if x.abs() < 1e-10 {
123 return if n == 0 { 1.0 } else { 0.0 };
124 }
125 if n < 0 {
126 let result = bessel_j_float(-n, x);
127 return if (-n) % 2 == 0 { result } else { -result };
128 }
129 match n {
130 0 => bessel_j0(x),
131 1 => bessel_j1(x),
132 _ => bessel_jn_recurrence(n, x),
133 }
134}
135
136fn bessel_j0(x: f64) -> f64 {
138 let ax = x.abs();
139 if ax < 8.0 {
140 let y = x * x;
141 let ans1 = 57568490574.0
142 + y * (-13362590354.0
143 + y * (651619640.7 + y * (-11214424.18 + y * (77392.33017 + y * (-184.9052456)))));
144 let ans2 = 57568490411.0
145 + y * (1029532985.0
146 + y * (9494680.718 + y * (59272.64853 + y * (267.8532712 + y * 1.0))));
147 ans1 / ans2
148 } else {
149 let z = 8.0 / ax;
150 let y = z * z;
151 let xx = ax - 0.785398164;
152 let ans1 = 1.0
153 + y * (-0.1098628627e-2
154 + y * (0.2734510407e-4 + y * (-0.2073370639e-5 + y * 0.2093887211e-6)));
155 let ans2 = -0.1562499995e-1
156 + y * (0.1430488765e-3
157 + y * (-0.6911147651e-5 + y * (0.7621095161e-6 - y * 0.934935152e-7)));
158 (2.0 / PI / ax).sqrt() * (ans1 * xx.cos() - z * ans2 * xx.sin())
159 }
160}
161
162fn bessel_j1(x: f64) -> f64 {
164 let ax = x.abs();
165 if ax < 8.0 {
166 let y = x * x;
167 let ans1 = x
168 * (72362614232.0
169 + y * (-7895059235.0
170 + y * (242396853.1
171 + y * (-2972611.439 + y * (15704.48260 + y * (-30.16036606))))));
172 let ans2 = 144725228442.0
173 + y * (2300535178.0
174 + y * (18583304.74 + y * (99447.43394 + y * (376.9991397 + y * 1.0))));
175 ans1 / ans2
176 } else {
177 let z = 8.0 / ax;
178 let y = z * z;
179 let xx = ax - 2.356194491;
180 let ans1 = 1.0
181 + y * (0.183105e-2
182 + y * (-0.3516396496e-4 + y * (0.2457520174e-5 + y * (-0.240337019e-6))));
183 let ans2 = 0.04687499995
184 + y * (-0.2002690873e-3
185 + y * (0.8449199096e-5 + y * (-0.88228987e-6 + y * 0.105787412e-6)));
186 let ans = (2.0 / PI / ax).sqrt() * (ans1 * xx.cos() - z * ans2 * xx.sin());
187 if x < 0.0 {
188 -ans
189 } else {
190 ans
191 }
192 }
193}
194
195fn bessel_jn_recurrence(n: i32, x: f64) -> f64 {
199 let mut jn_minus_1 = bessel_j0(x);
200 let mut jn = bessel_j1(x);
201 for k in 1..n {
202 let jn_plus_1 = (2.0 * k as f64 / x) * jn - jn_minus_1;
203 jn_minus_1 = jn;
204 jn = jn_plus_1;
205 }
206 jn
207}
208
209fn bessel_y_float(n: i32, x: f64) -> f64 {
213 if x.is_nan() || x.is_infinite() {
214 return f64::NAN;
215 }
216 if x <= 0.0 {
217 return f64::NEG_INFINITY;
218 }
219 if n < 0 {
220 let result = bessel_y_float(-n, x);
221 return if (-n) % 2 == 0 { result } else { -result };
222 }
223 match n {
224 0 => bessel_y0(x),
225 1 => bessel_y1(x),
226 _ => bessel_yn_recurrence(n, x),
227 }
228}
229
230fn bessel_y0(x: f64) -> f64 {
232 if x < 8.0 {
233 let j0 = bessel_j0(x);
234 let y = x * x;
235 let ans1 = -2957821389.0
236 + y * (7062834065.0
237 + y * (-512359803.6 + y * (10879881.29 + y * (-86327.92757 + y * 228.4622733))));
238 let ans2 = 40076544269.0
239 + y * (745249964.8
240 + y * (7189466.438 + y * (47447.26470 + y * (226.1030244 + y * 1.0))));
241 (ans1 / ans2) + (2.0 / PI) * j0 * x.ln()
242 } else {
243 let z = 8.0 / x;
244 let y = z * z;
245 let xx = x - 0.785398164;
246 let ans1 = 1.0
247 + y * (-0.1098628627e-2
248 + y * (0.2734510407e-4 + y * (-0.2073370639e-5 + y * 0.2093887211e-6)));
249 let ans2 = -0.1562499995e-1
250 + y * (0.1430488765e-3
251 + y * (-0.6911147651e-5 + y * (0.7621095161e-6 + y * (-0.934945152e-7))));
252 (2.0 / PI / x).sqrt() * (ans1 * xx.sin() + z * ans2 * xx.cos())
253 }
254}
255
256fn bessel_y1(x: f64) -> f64 {
258 if x < 8.0 {
259 let j1 = bessel_j1(x);
260 let y = x * x;
261 let ans1 = x
262 * (-0.4900604943e13
263 + y * (0.1275274390e13
264 + y * (-0.5153438139e11
265 + y * (0.7349264551e9 + y * (-0.4237922726e7 + y * 0.8511937935e4)))));
266 let ans2 = 0.2499580570e14
267 + y * (0.4244419664e12
268 + y * (0.3733650367e10
269 + y * (0.2245904002e8 + y * (0.1020426050e6 + y * (0.3549632885e3 + y)))));
270 (ans1 / ans2) + (2.0 / PI) * (j1 * x.ln() - 1.0 / x)
271 } else {
272 let z = 8.0 / x;
273 let y = z * z;
274 let xx = x - 2.356194491;
275 let ans1 = 1.0
276 + y * (0.183105e-2
277 + y * (-0.3516396496e-4 + y * (0.2457520174e-5 + y * (-0.240337019e-6))));
278 let ans2 = 0.04687499995
279 + y * (-0.2002690873e-3
280 + y * (0.8449199096e-5 + y * (-0.88228987e-6 + y * 0.105787412e-6)));
281 (2.0 / PI / x).sqrt() * (ans1 * xx.sin() + z * ans2 * xx.cos())
282 }
283}
284
285fn bessel_yn_recurrence(n: i32, x: f64) -> f64 {
289 let mut yn_minus_1 = bessel_y0(x);
290 let mut yn = bessel_y1(x);
291 for k in 1..n {
292 let yn_plus_1 = (2.0 * k as f64 / x) * yn - yn_minus_1;
293 yn_minus_1 = yn;
294 yn = yn_plus_1;
295 }
296 yn
297}
298
299#[cfg(test)]
300mod tests {
301 use super::*;
302
303 #[test]
304 fn test_bessel_j0_at_zero() {
305 assert_eq!(
306 bessel_j(0, &Expression::Number(Number::Integer(0))),
307 Expression::Number(Number::Integer(1))
308 );
309 }
310
311 #[test]
312 fn test_bessel_jn_at_zero() {
313 for n in 1..5 {
314 assert_eq!(
315 bessel_j(n, &Expression::Number(Number::Integer(0))),
316 Expression::Number(Number::Integer(0))
317 );
318 }
319 }
320
321 #[test]
322 fn test_bessel_j0_numerical_accuracy() {
323 if let Expression::Number(Number::Float(val)) =
324 bessel_j(0, &Expression::Number(Number::Float(1.0)))
325 {
326 assert!(
327 (val - 0.7651976865579666).abs() < 1e-8,
328 "J_0(1) = {}, expected {}",
329 val,
330 0.7651976865579666
331 );
332 } else {
333 panic!("Expected Float");
334 }
335 }
336
337 #[test]
338 fn test_bessel_j1_numerical_accuracy() {
339 if let Expression::Number(Number::Float(val)) =
340 bessel_j(1, &Expression::Number(Number::Float(1.0)))
341 {
342 assert!((val - 0.44005058574493355).abs() < 1e-10);
343 } else {
344 panic!("Expected Float");
345 }
346 }
347
348 #[test]
349 fn test_bessel_j_negative_order() {
350 let x = Expression::Number(Number::Float(1.0));
351 if let (Expression::Number(Number::Float(v1)), Expression::Number(Number::Float(v_m1))) =
352 (bessel_j(1, &x), bessel_j(-1, &x))
353 {
354 assert!((v1 + v_m1).abs() < 1e-10);
355 } else {
356 panic!("Expected Float");
357 }
358 }
359
360 #[test]
361 fn test_bessel_y0_numerical_accuracy() {
362 if let Expression::Number(Number::Float(val)) =
363 bessel_y(0, &Expression::Number(Number::Float(1.0)))
364 {
365 assert!(
366 (val - 0.08825696421567697).abs() < 1e-8,
367 "Y_0(1) = {}, expected {}",
368 val,
369 0.08825696421567697
370 );
371 } else {
372 panic!("Expected Float");
373 }
374 }
375
376 #[test]
377 fn test_bessel_y1_numerical_accuracy() {
378 if let Expression::Number(Number::Float(val)) =
379 bessel_y(1, &Expression::Number(Number::Float(1.0)))
380 {
381 assert!((val + 0.7812128213002887).abs() < 1e-9);
382 } else {
383 panic!("Expected Float");
384 }
385 }
386
387 #[test]
388 fn test_bessel_recurrence_j2() {
389 if let Expression::Number(Number::Float(val)) =
390 bessel_j(2, &Expression::Number(Number::Float(2.0)))
391 {
392 assert!(
393 (val - 0.3528340286156376).abs() < 1e-8,
394 "J_2(2) = {}, expected {}",
395 val,
396 0.3528340286156376
397 );
398 } else {
399 panic!("Expected Float");
400 }
401 }
402
403 #[test]
404 fn test_bessel_recurrence_y2() {
405 if let Expression::Number(Number::Float(val)) =
406 bessel_y(2, &Expression::Number(Number::Float(2.0)))
407 {
408 assert!(
409 (val + 0.6174081041906827).abs() < 1e-8,
410 "Y_2(2) = {}, expected {}",
411 val,
412 -0.6174081041906827
413 );
414 } else {
415 panic!("Expected Float");
416 }
417 }
418
419 #[test]
420 fn test_bessel_symbolic_fallback() {
421 match bessel_j(0, &Expression::symbol(crate::core::Symbol::scalar("x"))) {
422 Expression::Function { name, args } => {
423 assert_eq!(name.as_ref(), "bessel_j");
424 assert_eq!(args.len(), 2);
425 }
426 _ => panic!("Expected symbolic function"),
427 }
428 }
429
430 #[test]
431 fn test_bessel_j_input_validation_nan() {
432 if let Expression::Number(Number::Float(val)) =
433 bessel_j(0, &Expression::Number(Number::Float(f64::NAN)))
434 {
435 assert!(val.is_nan());
436 } else {
437 panic!("Expected Float");
438 }
439 }
440
441 #[test]
442 fn test_bessel_j_input_validation_infinity() {
443 if let Expression::Number(Number::Float(val)) =
444 bessel_j(0, &Expression::Number(Number::Float(f64::INFINITY)))
445 {
446 assert!(val.is_nan());
447 } else {
448 panic!("Expected Float");
449 }
450 }
451
452 #[test]
453 fn test_bessel_y_negative_x() {
454 if let Expression::Number(Number::Float(val)) =
455 bessel_y(0, &Expression::Number(Number::Float(-1.0)))
456 {
457 assert!(val.is_infinite() && val.is_sign_negative());
458 } else {
459 panic!("Expected Float");
460 }
461 }
462
463 #[test]
464 fn test_bessel_y_zero() {
465 if let Expression::Number(Number::Float(val)) =
466 bessel_y(0, &Expression::Number(Number::Float(0.0)))
467 {
468 assert!(val.is_infinite() && val.is_sign_negative());
469 } else {
470 panic!("Expected Float");
471 }
472 }
473
474 #[test]
475 fn test_bessel_j_large_argument() {
476 if let Expression::Number(Number::Float(val)) =
477 bessel_j(0, &Expression::Number(Number::Float(20.0)))
478 {
479 assert!((val - 0.1670246643).abs() < 1e-8);
480 } else {
481 panic!("Expected Float");
482 }
483 }
484
485 #[test]
486 fn test_bessel_j_high_order() {
487 if let Expression::Number(Number::Float(val)) =
488 bessel_j(5, &Expression::Number(Number::Float(10.0)))
489 {
490 assert!(
491 (val + 0.23406).abs() < 1e-2,
492 "J_5(10) = {}, expected approximately {}",
493 val,
494 -0.23406
495 );
496 } else {
497 panic!("Expected Float");
498 }
499 }
500
501 #[test]
502 fn test_bessel_j_negative_x_symmetry_even() {
503 let (j_pos, j_neg) = (
504 bessel_j(0, &Expression::Number(Number::Float(2.5))),
505 bessel_j(0, &Expression::Number(Number::Float(-2.5))),
506 );
507 if let (Expression::Number(Number::Float(vp)), Expression::Number(Number::Float(vn))) =
508 (j_pos, j_neg)
509 {
510 assert!((vp - vn).abs() < 1e-10);
511 } else {
512 panic!("Expected Float");
513 }
514 }
515
516 #[test]
517 fn test_bessel_j_negative_x_symmetry_odd() {
518 let (j_pos, j_neg) = (
519 bessel_j(1, &Expression::Number(Number::Float(2.5))),
520 bessel_j(1, &Expression::Number(Number::Float(-2.5))),
521 );
522 if let (Expression::Number(Number::Float(vp)), Expression::Number(Number::Float(vn))) =
523 (j_pos, j_neg)
524 {
525 assert!((vp + vn).abs() < 1e-10);
526 } else {
527 panic!("Expected Float");
528 }
529 }
530
531 #[test]
532 fn test_bessel_recurrence_relation_verification() {
533 let (x, n) = (5.0, 3);
534 let j_nm1 = bessel_j_float(n - 1, x);
535 let j_n = bessel_j_float(n, x);
536 let j_np1 = bessel_j_float(n + 1, x);
537 let recurrence = (2.0 * n as f64 / x) * j_n - j_nm1;
538 assert!((j_np1 - recurrence).abs() < 1e-10);
539 }
540
541 #[test]
542 fn test_bessel_j_first_zero() {
543 if let Expression::Number(Number::Float(val)) =
544 bessel_j(0, &Expression::Number(Number::Float(2.4048255577)))
545 {
546 assert!(val.abs() < 1e-6);
547 } else {
548 panic!("Expected Float");
549 }
550 }
551}