mathhook_core/functions/
properties.rs

1//! Mathematical Function Properties
2//!
3//! Defines the mathematical properties and capabilities for all function types.
4//! Inspired by SymPy's comprehensive function system but optimized for performance.
5
6pub mod elementary;
7pub mod rules;
8pub mod special;
9
10pub use rules::{
11    AntiderivativeRule, AntiderivativeRuleType, ConstantOfIntegration, DerivativeRule,
12    DerivativeRuleType, Domain, DomainRangeData, EvaluationMethod, MathIdentity, Range,
13    RecurrenceRule, SpecialValue, ThreeTermRecurrence,
14};
15
16pub use elementary::{ElementaryProperties, UserProperties, UserProperty};
17pub use special::{
18    AsymptoticData, DifferentialEquation, GeneratingFunction, GeneratingFunctionType,
19    OrthogonalityData, PolynomialFamily, PolynomialProperties, RodriguesFormula, SpecialProperties,
20};
21
22use crate::core::Expression;
23
24/// Mathematical properties for all function types
25///
26/// Boxed variants to minimize memory footprint
27/// while providing comprehensive mathematical intelligence.
28///
29/// ## Memory Layout Optimization
30/// - Uses `Box<T>` to keep enum size small (8 bytes per variant)
31/// - Reduces memory fragmentation through consistent allocation patterns
32/// - Enables efficient cache utilization for hot path operations
33#[derive(Debug, Clone)]
34pub enum FunctionProperties {
35    /// Elementary functions: sin, cos, exp, log, etc.
36    Elementary(Box<ElementaryProperties>),
37
38    /// Special functions: gamma, bessel, zeta, etc.
39    Special(Box<SpecialProperties>),
40
41    /// Polynomial families: legendre, hermite, laguerre, etc.
42    Polynomial(Box<PolynomialProperties>),
43
44    /// User-defined functions: f, g, h, etc.
45    UserDefined(Box<UserProperties>),
46}
47
48impl FunctionProperties {
49    /// Check if function has derivative rule
50    ///
51    /// Hot path method for performance-critical operations
52    #[inline(always)]
53    pub fn has_derivative(&self) -> bool {
54        match self {
55            FunctionProperties::Elementary(props) => props.derivative_rule.is_some(),
56            FunctionProperties::Special(props) => props.has_derivative,
57            FunctionProperties::Polynomial(_props) => true,
58            FunctionProperties::UserDefined(_) => false,
59        }
60    }
61
62    /// Check if function has antiderivative rule
63    ///
64    /// Hot path method for performance-critical operations
65    #[inline(always)]
66    pub fn has_antiderivative(&self) -> bool {
67        match self {
68            FunctionProperties::Elementary(props) => props.antiderivative_rule.is_some(),
69            FunctionProperties::Special(props) => props.has_antiderivative,
70            FunctionProperties::Polynomial(_props) => true,
71            FunctionProperties::UserDefined(_) => false,
72        }
73    }
74
75    /// Get derivative rule if available
76    ///
77    /// Returns a reference to the derivative rule for registry-based differentiation
78    #[inline(always)]
79    pub fn get_derivative_rule(&self) -> Option<&DerivativeRule> {
80        match self {
81            FunctionProperties::Elementary(props) => props.derivative_rule.as_ref(),
82            FunctionProperties::Special(_props) => None,
83            FunctionProperties::Polynomial(_props) => None,
84            FunctionProperties::UserDefined(_) => None,
85        }
86    }
87
88    /// Get derivative expression using registry rules
89    ///
90    /// Computes the derivative of f(arg) with respect to arg using the
91    /// registered derivative rule. The chain rule (multiplying by d(arg)/dx)
92    /// must be applied separately.
93    ///
94    /// # Arguments
95    ///
96    /// * `arg` - The argument expression to the function
97    ///
98    /// # Returns
99    ///
100    /// Returns the derivative expression, or None if no derivative rule exists
101    ///
102    /// # Examples
103    ///
104    /// ```
105    /// use mathhook_core::functions::intelligence::get_universal_registry;
106    /// use mathhook_core::{expr, symbol};
107    ///
108    /// let registry = get_universal_registry();
109    /// let x = symbol!(x);
110    ///
111    /// if let Some(props) = registry.get_properties("sin") {
112    ///     let derivative = props.get_derivative_expression(&x.into());
113    /// }
114    /// ```
115    pub fn get_derivative_expression(&self, arg: &Expression) -> Option<Expression> {
116        let rule = self.get_derivative_rule()?;
117
118        match &rule.rule_type {
119            DerivativeRuleType::SimpleFunctionSubstitution(func_name) => {
120                Some(Expression::function(func_name, vec![arg.clone()]))
121            }
122            DerivativeRuleType::Custom { builder } => Some(builder(arg)),
123            DerivativeRuleType::ChainRule(_) => None,
124            DerivativeRuleType::ProductRule => None,
125            DerivativeRuleType::QuotientRule => None,
126        }
127    }
128
129    /// Get antiderivative rule if available
130    ///
131    /// Returns a reference to the antiderivative rule for registry-based integration
132    #[inline(always)]
133    pub fn get_antiderivative_rule(&self) -> Option<&AntiderivativeRule> {
134        match self {
135            FunctionProperties::Elementary(props) => props.antiderivative_rule.as_ref(),
136            FunctionProperties::Special(props) => props.antiderivative_rule.as_ref(),
137            FunctionProperties::Polynomial(props) => Some(&props.antiderivative_rule),
138            FunctionProperties::UserDefined(_) => None,
139        }
140    }
141
142    /// Get special value count for caching optimization
143    #[inline(always)]
144    pub fn special_value_count(&self) -> usize {
145        match self {
146            FunctionProperties::Elementary(props) => props.special_values.len(),
147            FunctionProperties::Special(props) => props.special_values.len(),
148            FunctionProperties::Polynomial(props) => props.special_values.len(),
149            FunctionProperties::UserDefined(_) => 0,
150        }
151    }
152
153    /// Get function family for quick classification
154    #[inline(always)]
155    pub fn family(&self) -> super::intelligence::FunctionFamily {
156        match self {
157            FunctionProperties::Elementary(_) => super::intelligence::FunctionFamily::Elementary,
158            FunctionProperties::Special(_) => super::intelligence::FunctionFamily::Special,
159            FunctionProperties::Polynomial(_) => super::intelligence::FunctionFamily::Polynomial,
160            FunctionProperties::UserDefined(_) => super::intelligence::FunctionFamily::UserDefined,
161        }
162    }
163
164    /// Get Wolfram Language function name
165    ///
166    /// Used for Wolfram formatting without hardcoded function name matching.
167    /// Returns the Wolfram name if registered, otherwise None.
168    ///
169    /// # Examples
170    ///
171    /// ```
172    /// use mathhook_core::functions::intelligence::get_universal_registry;
173    ///
174    /// let registry = get_universal_registry();
175    /// if let Some(props) = registry.get_properties("sin") {
176    ///     assert_eq!(props.wolfram_name(), Some("Sin"));
177    /// }
178    /// if let Some(props) = registry.get_properties("ln") {
179    ///     assert_eq!(props.wolfram_name(), Some("Log"));
180    /// }
181    /// ```
182    #[inline(always)]
183    pub fn wolfram_name(&self) -> Option<&'static str> {
184        match self {
185            FunctionProperties::Elementary(props) => props.wolfram_name,
186            FunctionProperties::Special(props) => props.wolfram_name,
187            FunctionProperties::Polynomial(props) => props.wolfram_name,
188            FunctionProperties::UserDefined(props) => props.wolfram_name,
189        }
190    }
191}
192
193#[cfg(test)]
194mod tests {
195    use super::*;
196    use crate::expr;
197    use crate::symbol;
198
199    #[test]
200    fn test_function_properties_size() {
201        use std::mem::size_of;
202
203        assert!(
204            size_of::<FunctionProperties>() <= 32,
205            "FunctionProperties size: {} bytes (expected <= 32)",
206            size_of::<FunctionProperties>()
207        );
208
209        assert!(
210            size_of::<ElementaryProperties>() <= 256,
211            "ElementaryProperties size: {} bytes (expected <= 256)",
212            size_of::<ElementaryProperties>()
213        );
214    }
215
216    #[test]
217    fn test_hot_path_methods() {
218        let props: FunctionProperties =
219            FunctionProperties::Elementary(Box::new(ElementaryProperties {
220                derivative_rule: Some(DerivativeRule {
221                    rule_type: DerivativeRuleType::SimpleFunctionSubstitution("cos".to_string()),
222                    result_template: "cos(x)".to_string(),
223                }),
224                antiderivative_rule: Some(AntiderivativeRule {
225                    rule_type: AntiderivativeRuleType::Simple {
226                        antiderivative_fn: "cos".to_string(),
227                        coefficient: Expression::integer(-1),
228                    },
229                    result_template: "-cos(x) + C".to_string(),
230                    constant_handling: ConstantOfIntegration::AddConstant,
231                }),
232                special_values: vec![],
233                identities: Box::new(vec![]),
234                domain_range: Box::new(DomainRangeData {
235                    domain: Domain::Real,
236                    range: Range::Bounded(Expression::integer(-1), Expression::integer(1)),
237                    singularities: vec![],
238                }),
239                periodicity: Some(Expression::mul(vec![
240                    Expression::integer(2),
241                    Expression::pi(),
242                ])),
243                wolfram_name: Some("Sin"),
244            }));
245
246        assert!(props.has_derivative());
247        assert!(props.has_antiderivative());
248        assert_eq!(props.special_value_count(), 0);
249        assert_eq!(props.wolfram_name(), Some("Sin"));
250
251        let rule = props.get_antiderivative_rule();
252        assert!(rule.is_some());
253        if let Some(r) = rule {
254            assert_eq!(r.result_template, "-cos(x) + C");
255            assert_eq!(r.constant_handling, ConstantOfIntegration::AddConstant);
256        }
257    }
258
259    #[test]
260    fn test_derivative_expression_simple() {
261        let x = symbol!(x);
262        let props: FunctionProperties =
263            FunctionProperties::Elementary(Box::new(ElementaryProperties {
264                derivative_rule: Some(DerivativeRule {
265                    rule_type: DerivativeRuleType::SimpleFunctionSubstitution("cos".to_string()),
266                    result_template: "cos(x)".to_string(),
267                }),
268                antiderivative_rule: None,
269                special_values: vec![],
270                identities: Box::new(vec![]),
271                domain_range: Box::new(DomainRangeData {
272                    domain: Domain::Real,
273                    range: Range::Bounded(expr!(-1), expr!(1)),
274                    singularities: vec![],
275                }),
276                periodicity: None,
277                wolfram_name: Some("Sin"),
278            }));
279
280        let derivative = props.get_derivative_expression(&x.into());
281        assert!(derivative.is_some());
282
283        if let Some(d) = derivative {
284            assert_eq!(d.to_string(), "cos(x)");
285        }
286    }
287
288    #[test]
289    fn test_wolfram_name_getter() {
290        let props_sin: FunctionProperties =
291            FunctionProperties::Elementary(Box::new(ElementaryProperties {
292                derivative_rule: None,
293                antiderivative_rule: None,
294                special_values: vec![],
295                identities: Box::new(vec![]),
296                domain_range: Box::new(DomainRangeData {
297                    domain: Domain::Real,
298                    range: Range::Bounded(expr!(-1), expr!(1)),
299                    singularities: vec![],
300                }),
301                periodicity: None,
302                wolfram_name: Some("Sin"),
303            }));
304
305        assert_eq!(props_sin.wolfram_name(), Some("Sin"));
306
307        let props_user: FunctionProperties =
308            FunctionProperties::UserDefined(Box::new(UserProperties {
309                definition: None,
310                properties: vec![],
311                derivatives: std::collections::HashMap::new(),
312                domain: None,
313                wolfram_name: None,
314            }));
315
316        assert_eq!(props_user.wolfram_name(), None);
317    }
318}