mathhook_core/core/expression/evaluation/
dispatch.rs

1//! Function dispatch for expression evaluation
2//!
3//! Contains the performance-critical function dispatch table that routes
4//! function calls to their implementations.
5//!
6//! This module provides:
7//! - `evaluate_function_dispatch()` - O(1) dispatch via compiler jump table
8//! - `evaluate_orthogonal_polynomial()` - helper for orthogonal polynomial evaluation
9
10use super::super::Expression;
11use crate::core::{MathConstant, Number};
12use crate::simplify::Simplify;
13use num_traits::ToPrimitive;
14use std::collections::HashMap;
15
16/// Performance-critical function dispatch
17///
18/// Routes function calls to their implementations using a compiler-optimized
19/// jump table for O(1) dispatch overhead (<10ns).
20///
21/// # Arguments
22///
23/// * `name` - Function name (e.g., "sin", "cos", "gamma")
24/// * `args` - Function arguments
25///
26/// # Returns
27///
28/// Some(Expression) if the function can be evaluated, None otherwise
29///
30/// # Performance
31///
32/// This function is marked `#[inline]` and uses a match statement that the
33/// compiler optimizes to a jump table. Dispatch overhead is <10ns.
34///
35/// # Supported Functions
36///
37/// ## Elementary Functions
38/// - Trigonometric: sin, cos, tan, arcsin, arccos, arctan
39/// - Hyperbolic: sinh, cosh, tanh
40/// - Exponential/Logarithmic: exp, ln, log10
41/// - Roots: sqrt
42/// - Rounding: sign, floor, ceil, round
43/// - Absolute value: abs
44///
45/// ## Special Functions
46/// - Gamma family: gamma, digamma, polygamma, beta, factorial
47/// - Bessel functions: bessel_j, bessel_y
48/// - Error functions: erf, erfc
49/// - Zeta function: zeta
50///
51/// ## Number Theory
52/// - gcd, lcm, mod, isprime
53///
54/// ## Polynomial Operations
55/// - degree, roots, expand, factor
56///
57/// ## Orthogonal Polynomials
58/// - Legendre: legendrep, legendre_p
59/// - Chebyshev first kind: chebyshevt, chebyshev_t
60/// - Chebyshev second kind: chebyshevu, chebyshev_u
61/// - Hermite: hermiteh, hermite
62/// - Laguerre: laguerrel, laguerre
63#[inline]
64pub fn evaluate_function_dispatch(name: &str, args: &[Expression]) -> Option<Expression> {
65    if args.is_empty() {
66        return None;
67    }
68
69    match name {
70        "sin" => Some(crate::functions::elementary::trigonometric::sin(&args[0])),
71        "cos" => Some(crate::functions::elementary::trigonometric::cos(&args[0])),
72        "tan" => Some(crate::functions::elementary::trigonometric::tan(&args[0])),
73        "arcsin" | "asin" => Some(crate::functions::elementary::trigonometric::arcsin(
74            &args[0],
75        )),
76        "arccos" | "acos" => Some(crate::functions::elementary::trigonometric::arccos(
77            &args[0],
78        )),
79        "arctan" | "atan" => Some(crate::functions::elementary::trigonometric::arctan(
80            &args[0],
81        )),
82        "abs" => Some(crate::functions::elementary::abs_eval::abs(&args[0])),
83        "sqrt" => Some(crate::functions::elementary::sqrt_eval::sqrt(&args[0])),
84        "exp" => Some(crate::functions::elementary::exp_eval::exp(&args[0])),
85        "ln" => Some(crate::functions::elementary::log_eval::ln(&args[0])),
86        "log10" | "log" => Some(crate::functions::elementary::log_eval::log10(&args[0])),
87        "sign" => Some(crate::functions::elementary::rounding::sign(&args[0])),
88        "floor" => Some(crate::functions::elementary::rounding::floor(&args[0])),
89        "ceil" => Some(crate::functions::elementary::rounding::ceil(&args[0])),
90        "round" => Some(crate::functions::elementary::rounding::round(&args[0])),
91        "sinh" => Some(crate::functions::elementary::hyperbolic_eval::sinh(
92            &args[0],
93        )),
94        "cosh" => Some(crate::functions::elementary::hyperbolic_eval::cosh(
95            &args[0],
96        )),
97        "tanh" => Some(crate::functions::elementary::hyperbolic_eval::tanh(
98            &args[0],
99        )),
100        "gamma" => Some(crate::functions::special::gamma::gamma(&args[0])),
101        "digamma" => Some(crate::functions::special::digamma(&args[0])),
102        "polygamma" if args.len() >= 2 => {
103            if let Expression::Number(Number::Integer(n)) = &args[0] {
104                return Some(crate::functions::special::polygamma(*n as i32, &args[1]));
105            }
106            None
107        }
108        "bessel_j" | "besselj" if args.len() >= 2 => {
109            if let Expression::Number(Number::Integer(n)) = &args[0] {
110                let order = (*n) as i32;
111                return Some(crate::functions::special::bessel_j(order, &args[1]));
112            }
113            None
114        }
115        "bessel_y" | "bessely" if args.len() >= 2 => {
116            if let Expression::Number(Number::Integer(n)) = &args[0] {
117                let order = (*n) as i32;
118                return Some(crate::functions::special::bessel_y(order, &args[1]));
119            }
120            None
121        }
122        "zeta" => Some(crate::functions::special::zeta(&args[0])),
123        "erf" => Some(crate::functions::special::erf(&args[0])),
124        "erfc" => Some(crate::functions::special::erfc(&args[0])),
125        "factorial" => Some(crate::functions::special::factorial(&args[0])),
126        "beta" if args.len() >= 2 => Some(crate::functions::special::beta(&args[0], &args[1])),
127        "gcd" if args.len() >= 2 => Some(args[0].gcd(&args[1])),
128        "lcm" if args.len() >= 2 => Some(crate::functions::number_theory_eval::lcm(
129            &args[0], &args[1],
130        )),
131        "mod" if args.len() >= 2 => Some(crate::functions::number_theory_eval::modulo(
132            &args[0], &args[1],
133        )),
134        "isprime" => Some(crate::functions::number_theory_eval::isprime(&args[0])),
135        "degree" if args.len() >= 2 => {
136            if let Expression::Symbol(var) = &args[1] {
137                return Some(crate::functions::polynomials::degree(&args[0], var));
138            }
139            None
140        }
141        "roots" if args.len() >= 2 => {
142            if let Expression::Symbol(var) = &args[1] {
143                return Some(crate::functions::polynomials::roots(&args[0], var));
144            }
145            None
146        }
147        "expand" => Some(crate::functions::polynomials::expand(&args[0])),
148        "factor" => Some(crate::functions::polynomials::factor(&args[0])),
149        "undefined" => Some(Expression::constant(MathConstant::Undefined)),
150        "legendrep" | "legendre_p" if args.len() >= 2 => evaluate_orthogonal_polynomial(
151            &args[0],
152            &args[1],
153            crate::functions::polynomials::symbolic::expand_legendre_symbolic,
154        ),
155        "chebyshevt" | "chebyshev_t" | "chebyshev_first" if args.len() >= 2 => {
156            evaluate_orthogonal_polynomial(
157                &args[0],
158                &args[1],
159                crate::functions::polynomials::symbolic::expand_chebyshev_first_symbolic,
160            )
161        }
162        "chebyshevu" | "chebyshev_u" | "chebyshev_second" if args.len() >= 2 => {
163            evaluate_orthogonal_polynomial(
164                &args[0],
165                &args[1],
166                crate::functions::polynomials::symbolic::expand_chebyshev_second_symbolic,
167            )
168        }
169        "hermiteh" | "hermite" if args.len() >= 2 => evaluate_orthogonal_polynomial(
170            &args[0],
171            &args[1],
172            crate::functions::polynomials::symbolic::expand_hermite_symbolic,
173        ),
174        "laguerrel" | "laguerre" if args.len() >= 2 => evaluate_orthogonal_polynomial(
175            &args[0],
176            &args[1],
177            crate::functions::polynomials::symbolic::expand_laguerre_symbolic,
178        ),
179        _ => None,
180    }
181}
182
183/// Helper for evaluating orthogonal polynomials
184///
185/// Extracts the degree `n` from the first argument and substitutes the
186/// variable from the second argument into the expanded polynomial.
187///
188/// # Arguments
189///
190/// * `degree_arg` - Expression containing the polynomial degree (must be non-negative integer)
191/// * `var_arg` - Expression for the variable (typically a Symbol, but can be any expression)
192/// * `expander` - Function that expands the polynomial symbolically
193///
194/// # Returns
195///
196/// Some(Expression) if degree is a valid non-negative integer, None otherwise
197///
198/// # Example
199///
200/// For `legendrep(2, x)`:
201/// 1. Extract n=2 from degree_arg
202/// 2. Call expander(2) to get P_2(x) = (3x^2 - 1)/2
203/// 3. If var_arg is just `x`, return the expanded form
204/// 4. Otherwise substitute var_arg for x and simplify
205pub fn evaluate_orthogonal_polynomial<F>(
206    degree_arg: &Expression,
207    var_arg: &Expression,
208    expander: F,
209) -> Option<Expression>
210where
211    F: Fn(usize) -> Expression,
212{
213    let n = match degree_arg {
214        Expression::Number(Number::Integer(i)) if *i >= 0 => *i as usize,
215        Expression::Number(Number::BigInteger(bi)) => {
216            if let Some(n) = bi.to_u64() {
217                n as usize
218            } else {
219                return None;
220            }
221        }
222        _ => return None,
223    };
224
225    let expanded = expander(n);
226
227    match var_arg {
228        Expression::Symbol(sym) if sym.name() == "x" => Some(expanded),
229        _ => {
230            let mut subs = HashMap::new();
231            subs.insert("x".to_owned(), var_arg.clone());
232            Some(expanded.substitute(&subs).simplify())
233        }
234    }
235}