mathhook_core/parser/
constants.rs

1//! Constants for mathematical function names and optimization
2//!
3//! This module provides compile-time constants for function names and common
4//! mathematical values to improve readability, maintainability, and performance.
5
6use crate::core::Expression;
7use once_cell::sync::Lazy;
8use std::collections::HashMap;
9
10/// Mathematical function names (compile-time optimization)
11pub mod function_names {
12    // Basic trigonometric functions
13    pub const SIN: &str = "sin";
14    pub const COS: &str = "cos";
15    pub const TAN: &str = "tan";
16
17    // Hyperbolic functions
18    pub const SINH: &str = "sinh";
19    pub const COSH: &str = "cosh";
20    pub const TANH: &str = "tanh";
21    pub const SECH: &str = "sech";
22    pub const CSCH: &str = "csch";
23    pub const COTH: &str = "coth";
24
25    // Inverse trigonometric functions
26    pub const ARCSIN: &str = "arcsin";
27    pub const ARCCOS: &str = "arccos";
28    pub const ARCTAN: &str = "arctan";
29    pub const ARCSEC: &str = "arcsec";
30    pub const ARCCSC: &str = "arccsc";
31    pub const ARCCOT: &str = "arccot";
32
33    // Extended trigonometric functions
34    pub const SEC: &str = "sec";
35    pub const CSC: &str = "csc";
36    pub const COT: &str = "cot";
37
38    // Logarithmic functions
39    pub const LN: &str = "ln";
40    pub const LOG: &str = "log";
41    pub const LOG10: &str = "log10";
42    pub const LOG2: &str = "log2";
43
44    // Other basic functions
45    pub const SQRT: &str = "sqrt";
46    pub const ABS: &str = "abs";
47    pub const EXP: &str = "exp";
48
49    // Factorial functions
50    pub const FACTORIAL: &str = "factorial";
51    pub const DOUBLE_FACTORIAL: &str = "double_factorial";
52
53    // Special functions
54    pub const GAMMA: &str = "gamma";
55    pub const BESSEL_J: &str = "bessel_j";
56    pub const BESSEL_Y: &str = "bessel_y";
57    pub const BESSEL_I: &str = "bessel_i";
58    pub const BESSEL_K: &str = "bessel_k";
59    pub const LEGENDRE_P: &str = "legendre_p";
60    pub const LEGENDRE_Q: &str = "legendre_q";
61    pub const HERMITE: &str = "hermite";
62    pub const HERMITE_PHYSICIST: &str = "hermite_physicist";
63    pub const LAGUERRE: &str = "laguerre";
64    pub const LAGUERRE_ASSOCIATED: &str = "laguerre_associated";
65    pub const CHEBYSHEV_FIRST: &str = "chebyshev_first";
66    pub const CHEBYSHEV_SECOND: &str = "chebyshev_second";
67
68    // Calculus functions
69    pub const DERIVATIVE: &str = "derivative";
70    pub const INTEGRAL: &str = "integral";
71    pub const LIMIT: &str = "limit";
72    pub const SUM: &str = "sum";
73    pub const PRODUCT: &str = "product";
74
75    // Text functions
76    pub const TEXT_RE: &str = "text_re";
77    pub const TEXT_IM: &str = "text_im";
78    pub const TEXT_QR: &str = "text_qr";
79    pub const TEXT_PRIM_ROOT: &str = "text_primroot";
80
81    // Complex analysis
82    pub const CONJUGATE: &str = "conjugate";
83    pub const REAL: &str = "real";
84    pub const IMAG: &str = "imag";
85    pub const ARG: &str = "arg";
86
87    // Utility Functions
88    pub const SIGN: &str = "sign";
89    pub const FLOOR: &str = "floor";
90    pub const CEILING: &str = "ceiling";
91    pub const ROUND: &str = "round";
92    pub const MAX: &str = "max";
93    pub const MIN: &str = "min";
94
95    // Note: Matrix operations (det, trace, rank, inverse, transpose, eigenvalues, eigenvectors)
96    // are now handled as MethodCall expressions, not function constants
97
98    // Statistical Functions
99    pub const MEAN: &str = "mean";
100    pub const MEDIAN: &str = "median";
101    pub const VARIANCE: &str = "variance";
102    pub const STD: &str = "std";
103
104    // Number Theory Functions (additional)
105    pub const GCD_CAPS: &str = "gcd";
106    pub const LCM_CAPS: &str = "lcm";
107    pub const BINOMIAL: &str = "binomial";
108
109    // Number theory
110    pub const CYCLOTOMIC_POLYNOMIAL: &str = "cyclotomic_polynomial";
111    pub const MINIMAL_POLYNOMIAL: &str = "minimal_polynomial";
112    pub const GROEBNER_BASIS: &str = "groebner_basis";
113    pub const RESULTANT: &str = "resultant";
114    pub const DISCRIMINANT: &str = "discriminant";
115    pub const POLYNOMIAL_GCD: &str = "polynomial_gcd";
116    pub const RIEMANN_ZETA: &str = "riemann_zeta";
117    pub const RIEMANN_SIEGEL_THETA: &str = "riemann_siegel_theta";
118    pub const MOEBIUS_MU: &str = "moebius_mu";
119    pub const EULER_PHI: &str = "euler_phi";
120    pub const PRIME_PI: &str = "prime_pi";
121}
122
123/// Common mathematical values (lazy initialization for performance)
124pub mod math_constants {
125    use super::*;
126
127    pub static ZERO: Lazy<Expression> = Lazy::new(|| Expression::integer(0));
128    pub static ONE: Lazy<Expression> = Lazy::new(|| Expression::integer(1));
129    pub static MINUS_ONE: Lazy<Expression> = Lazy::new(|| Expression::integer(-1));
130    pub static TWO: Lazy<Expression> = Lazy::new(|| Expression::integer(2));
131    pub static HALF: Lazy<Expression> = Lazy::new(|| Expression::rational(1, 2));
132    pub static PI: Lazy<Expression> = Lazy::new(Expression::pi);
133    pub static E: Lazy<Expression> = Lazy::new(Expression::e);
134    pub static I: Lazy<Expression> = Lazy::new(Expression::i);
135}
136
137/// Special function name mapping for indexed functions
138///
139/// Used for functions like J_n(x), P_l^m(x) where the base name needs
140/// to be mapped to the appropriate function name.
141pub static SPECIAL_FUNCTION_MAP: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| {
142    let mut map = HashMap::new();
143
144    // Bessel functions
145    map.insert("J", function_names::BESSEL_J);
146    map.insert("Y", function_names::BESSEL_Y);
147    map.insert("I", function_names::BESSEL_I);
148    map.insert("K", function_names::BESSEL_K);
149
150    // Legendre functions
151    map.insert("P", function_names::LEGENDRE_P);
152    map.insert("Q", function_names::LEGENDRE_Q);
153
154    // Hermite polynomials
155    map.insert("H", function_names::HERMITE);
156    map.insert("He", function_names::HERMITE_PHYSICIST);
157
158    // Laguerre polynomials
159    map.insert("L", function_names::LAGUERRE);
160    map.insert("La", function_names::LAGUERRE_ASSOCIATED);
161
162    // Chebyshev polynomials
163    map.insert("T", function_names::CHEBYSHEV_FIRST);
164    map.insert("U", function_names::CHEBYSHEV_SECOND);
165
166    map
167});
168
169/// Wolfram function name mapping for readability
170///
171/// Maps Wolfram Language function names to our internal function names.
172/// Uses HashMap for complex lookups where readability matters more than
173/// micro-performance optimizations.
174pub static WOLFRAM_FUNCTION_MAP: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| {
175    let mut map = HashMap::new();
176
177    // Basic functions
178    map.insert("Sin", function_names::SIN);
179    map.insert("Cos", function_names::COS);
180    map.insert("Tan", function_names::TAN);
181    map.insert("Log", function_names::LN);
182    map.insert("Sqrt", function_names::SQRT);
183    map.insert("Exp", function_names::EXP);
184
185    // Hyperbolic functions
186    map.insert("Sinh", function_names::SINH);
187    map.insert("Cosh", function_names::COSH);
188    map.insert("Tanh", function_names::TANH);
189
190    // Inverse functions
191    map.insert("ArcSin", function_names::ARCSIN);
192    map.insert("ArcCos", function_names::ARCCOS);
193    map.insert("ArcTan", function_names::ARCTAN);
194
195    // Special functions
196    map.insert("BesselJ", function_names::BESSEL_J);
197    map.insert("BesselY", function_names::BESSEL_Y);
198    map.insert("BesselI", function_names::BESSEL_I);
199    map.insert("BesselK", function_names::BESSEL_K);
200    map.insert("LegendreP", function_names::LEGENDRE_P);
201    map.insert("LegendreQ", function_names::LEGENDRE_Q);
202    map.insert("HermiteH", function_names::HERMITE);
203    map.insert("LaguerreL", function_names::LAGUERRE);
204    map.insert("ChebyshevT", function_names::CHEBYSHEV_FIRST);
205    map.insert("ChebyshevU", function_names::CHEBYSHEV_SECOND);
206
207    // Advanced functions
208    map.insert(
209        "CyclotomicPolynomial",
210        function_names::CYCLOTOMIC_POLYNOMIAL,
211    );
212    map.insert("MinimalPolynomial", function_names::MINIMAL_POLYNOMIAL);
213    map.insert("GroebnerBasis", function_names::GROEBNER_BASIS);
214    map.insert("Resultant", function_names::RESULTANT);
215    map.insert("Discriminant", function_names::DISCRIMINANT);
216    map.insert("PolynomialGCD", function_names::POLYNOMIAL_GCD);
217    map.insert("RiemannSiegelTheta", function_names::RIEMANN_ZETA);
218    map.insert("MoebiusMu", function_names::MOEBIUS_MU);
219    map.insert("EulerPhi", function_names::EULER_PHI);
220    map.insert("PrimePi", function_names::PRIME_PI);
221
222    // Extended trigonometric functions
223    map.insert("Sec", function_names::SEC);
224    map.insert("Csc", function_names::CSC);
225    map.insert("Cot", function_names::COT);
226
227    // Extended hyperbolic functions
228    map.insert("Sech", function_names::SECH);
229    map.insert("Csch", function_names::CSCH);
230    map.insert("Coth", function_names::COTH);
231
232    // Extended inverse functions
233    map.insert("ArcSec", function_names::ARCSEC);
234    map.insert("ArcCsc", function_names::ARCCSC);
235    map.insert("ArcCot", function_names::ARCCOT);
236
237    // Utility functions
238    map.insert("Abs", function_names::ABS);
239    map.insert("Sign", function_names::SIGN);
240    map.insert("Max", function_names::MAX);
241    map.insert("Min", function_names::MIN);
242    map.insert("Floor", function_names::FLOOR);
243    map.insert("Ceiling", function_names::CEILING);
244    map.insert("Round", function_names::ROUND);
245
246    // Complex functions
247    map.insert("Re", function_names::REAL);
248    map.insert("Im", function_names::IMAG);
249    map.insert("Conjugate", function_names::CONJUGATE);
250    map.insert("Arg", function_names::ARG);
251
252    // Note: Matrix operations (Det, Tr, Inverse, Transpose, Eigenvalues, Eigenvectors)
253    // are handled as matrix methods, not simple functions
254
255    // Number theory functions
256    map.insert("GCD", function_names::GCD_CAPS);
257    map.insert("LCM", function_names::LCM_CAPS);
258    map.insert("Factorial", function_names::FACTORIAL);
259    map.insert("Binomial", function_names::BINOMIAL);
260
261    // Statistical functions
262    map.insert("Mean", function_names::MEAN);
263    map.insert("Median", function_names::MEDIAN);
264    map.insert("Variance", function_names::VARIANCE);
265    map.insert("StandardDeviation", function_names::STD);
266
267    map
268});
269
270/// Standard function name mapping for simple functions like sin(x), cos(x)
271///
272/// Note: LaTeX functions (\sin, \cos) are handled by explicit tokens in grammar
273/// Note: Wolfram functions (Sin, Cos) use WOLFRAM_FUNCTION_MAP
274pub static STANDARD_FUNCTION_MAP: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| {
275    let mut map = HashMap::new();
276
277    // Basic trigonometric functions
278    map.insert("sin", function_names::SIN);
279    map.insert("cos", function_names::COS);
280    map.insert("tan", function_names::TAN);
281    map.insert("sec", function_names::SEC);
282    map.insert("csc", function_names::CSC);
283    map.insert("cot", function_names::COT);
284
285    // Hyperbolic functions
286    map.insert("sinh", function_names::SINH);
287    map.insert("cosh", function_names::COSH);
288    map.insert("tanh", function_names::TANH);
289    map.insert("sech", function_names::SECH);
290    map.insert("csch", function_names::CSCH);
291    map.insert("coth", function_names::COTH);
292
293    // Inverse trigonometric functions
294    map.insert("arcsin", function_names::ARCSIN);
295    map.insert("arccos", function_names::ARCCOS);
296    map.insert("arctan", function_names::ARCTAN);
297    map.insert("arcsec", function_names::ARCSEC);
298    map.insert("arccsc", function_names::ARCCSC);
299    map.insert("arccot", function_names::ARCCOT);
300    map.insert("asin", function_names::ARCSIN); // Alternative names
301    map.insert("acos", function_names::ARCCOS);
302    map.insert("atan", function_names::ARCTAN);
303
304    // Logarithmic functions
305    map.insert("ln", function_names::LN);
306    map.insert("log", function_names::LOG);
307    map.insert("log10", function_names::LOG10);
308    map.insert("log2", function_names::LOG2);
309
310    // Other basic functions
311    map.insert("sqrt", function_names::SQRT);
312    map.insert("abs", function_names::ABS);
313    map.insert("exp", function_names::EXP);
314    map.insert("sign", function_names::SIGN);
315    map.insert("floor", function_names::FLOOR);
316    map.insert("ceiling", function_names::CEILING);
317    map.insert("round", function_names::ROUND);
318    map.insert("max", function_names::MAX);
319    map.insert("min", function_names::MIN);
320
321    // Special functions
322    map.insert("gamma", function_names::GAMMA);
323    map.insert("factorial", function_names::FACTORIAL);
324
325    // Complex functions
326    map.insert("real", function_names::REAL);
327    map.insert("imag", function_names::IMAG);
328    map.insert("conj", function_names::CONJUGATE);
329    map.insert("arg", function_names::ARG);
330
331    // Note: Matrix operations (det, trace, rank) are handled as matrix methods, not simple functions
332
333    // Statistical functions
334    map.insert("mean", function_names::MEAN);
335    map.insert("median", function_names::MEDIAN);
336    map.insert("var", function_names::VARIANCE);
337    map.insert("std", function_names::STD);
338
339    // Number theory functions
340    map.insert("gcd", function_names::GCD_CAPS);
341    map.insert("lcm", function_names::LCM_CAPS);
342
343    map
344});
345
346/// Efficient function name lookup with fallback
347///
348/// # Examples
349///
350/// ```rust
351/// use mathhook_core::parser::constants::resolve_wolfram_function;
352///
353/// assert_eq!(resolve_wolfram_function("Sin"), Some("sin"));
354/// assert_eq!(resolve_wolfram_function("UnknownFunction"), None);
355/// ```
356pub fn resolve_wolfram_function(name: &str) -> Option<&'static str> {
357    WOLFRAM_FUNCTION_MAP.get(name).copied()
358}
359
360/// Convert PascalCase function names to snake_case
361///
362/// This provides a flexible, generic way to convert Wolfram function names
363/// (like "BesselJ", "ArcSin") to consistent snake_case format.
364///
365/// # Examples
366///
367/// ```rust
368/// use mathhook_core::parser::constants::pascal_to_snake_case;
369///
370/// assert_eq!(pascal_to_snake_case("Sin"), "sin");
371/// assert_eq!(pascal_to_snake_case("BesselJ"), "bessel_j");
372/// assert_eq!(pascal_to_snake_case("ArcSin"), "arc_sin");
373/// assert_eq!(pascal_to_snake_case("DiracDelta"), "dirac_delta");
374/// ```
375pub fn pascal_to_snake_case(name: &str) -> String {
376    let mut result = String::with_capacity(name.len() + 4); // Reserve extra space for underscores
377    let chars = name.chars().peekable();
378
379    for ch in chars {
380        if ch.is_uppercase() {
381            // Add underscore before uppercase letters (except the first character)
382            if !result.is_empty() {
383                result.push('_');
384            }
385            // to_lowercase() always returns at least one character for valid Unicode
386            for lowercase_ch in ch.to_lowercase() {
387                result.push(lowercase_ch);
388            }
389        } else {
390            result.push(ch);
391        }
392    }
393
394    result
395}
396
397/// Resolve special function name for indexed functions
398///
399/// # Examples
400///
401/// ```rust
402/// use mathhook_core::parser::constants::resolve_special_function;
403///
404/// assert_eq!(resolve_special_function("J"), Some("bessel_j"));
405/// assert_eq!(resolve_special_function("P"), Some("legendre_p"));
406/// assert_eq!(resolve_special_function("Unknown"), None);
407/// ```
408pub fn resolve_special_function(name: &str) -> Option<&'static str> {
409    SPECIAL_FUNCTION_MAP.get(name).copied()
410}
411
412/// Resolve standard function name (for simple functions like sin(x))
413///
414/// # Examples
415///
416/// ```rust
417/// use mathhook_core::parser::constants::resolve_standard_function;
418///
419/// assert_eq!(resolve_standard_function("sin"), Some("sin"));
420/// assert_eq!(resolve_standard_function("asin"), Some("arcsin"));
421/// assert_eq!(resolve_standard_function("unknown"), None);
422/// ```
423pub fn resolve_standard_function(name: &str) -> Option<&'static str> {
424    STANDARD_FUNCTION_MAP.get(name).copied()
425}
426
427#[cfg(test)]
428mod tests {
429    use super::*;
430
431    #[test]
432    fn test_wolfram_function_resolution() {
433        assert_eq!(resolve_wolfram_function("Sin"), Some(function_names::SIN));
434        assert_eq!(resolve_wolfram_function("Cos"), Some(function_names::COS));
435        assert_eq!(
436            resolve_wolfram_function("BesselJ"),
437            Some(function_names::BESSEL_J)
438        );
439        assert_eq!(resolve_wolfram_function("UnknownFunction"), None);
440    }
441
442    #[test]
443    fn test_pascal_to_snake_case() {
444        // Basic cases
445        assert_eq!(pascal_to_snake_case("Sin"), "sin");
446        assert_eq!(pascal_to_snake_case("Cos"), "cos");
447
448        // Multi-word cases
449        assert_eq!(pascal_to_snake_case("BesselJ"), "bessel_j");
450        assert_eq!(pascal_to_snake_case("ArcSin"), "arc_sin");
451        assert_eq!(pascal_to_snake_case("DiracDelta"), "dirac_delta");
452        assert_eq!(pascal_to_snake_case("HeavisideTheta"), "heaviside_theta");
453
454        // Complex cases
455        assert_eq!(
456            pascal_to_snake_case("Hypergeometric2F1"),
457            "hypergeometric2_f1"
458        );
459        assert_eq!(pascal_to_snake_case("LegendreP"), "legendre_p");
460
461        // Edge cases
462        assert_eq!(pascal_to_snake_case("A"), "a");
463        assert_eq!(pascal_to_snake_case("AB"), "a_b");
464        assert_eq!(pascal_to_snake_case("ABC"), "a_b_c");
465    }
466
467    #[test]
468    fn test_special_function_resolution() {
469        assert_eq!(
470            resolve_special_function("J"),
471            Some(function_names::BESSEL_J)
472        );
473        assert_eq!(
474            resolve_special_function("P"),
475            Some(function_names::LEGENDRE_P)
476        );
477        assert_eq!(resolve_special_function("H"), Some(function_names::HERMITE));
478        assert_eq!(resolve_special_function("Unknown"), None);
479    }
480
481    #[test]
482    fn test_math_constants_initialization() {
483        // Test that lazy constants can be accessed without panicking
484        let _zero = &*math_constants::ZERO;
485        let _one = &*math_constants::ONE;
486        let _pi = &*math_constants::PI;
487    }
488}