1pub 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#[derive(Debug, Clone)]
34pub enum FunctionProperties {
35 Elementary(Box<ElementaryProperties>),
37
38 Special(Box<SpecialProperties>),
40
41 Polynomial(Box<PolynomialProperties>),
43
44 UserDefined(Box<UserProperties>),
46}
47
48impl FunctionProperties {
49 #[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 #[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 #[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 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 #[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 #[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 #[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 #[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}