pub mod elementary;
pub mod rules;
pub mod special;
pub use rules::{
AntiderivativeRule, AntiderivativeRuleType, ConstantOfIntegration, DerivativeRule,
DerivativeRuleType, Domain, DomainRangeData, EvaluationMethod, MathIdentity, Range,
RecurrenceRule, SpecialValue, ThreeTermRecurrence,
};
pub use elementary::{ElementaryProperties, UserProperties, UserProperty};
pub use special::{
AsymptoticData, DifferentialEquation, GeneratingFunction, GeneratingFunctionType,
OrthogonalityData, PolynomialFamily, PolynomialProperties, RodriguesFormula, SpecialProperties,
};
use crate::core::Expression;
#[derive(Debug, Clone)]
pub enum FunctionProperties {
Elementary(Box<ElementaryProperties>),
Special(Box<SpecialProperties>),
Polynomial(Box<PolynomialProperties>),
UserDefined(Box<UserProperties>),
}
impl FunctionProperties {
#[inline(always)]
pub fn has_derivative(&self) -> bool {
match self {
FunctionProperties::Elementary(props) => props.derivative_rule.is_some(),
FunctionProperties::Special(props) => props.has_derivative,
FunctionProperties::Polynomial(_props) => true,
FunctionProperties::UserDefined(_) => false,
}
}
#[inline(always)]
pub fn has_antiderivative(&self) -> bool {
match self {
FunctionProperties::Elementary(props) => props.antiderivative_rule.is_some(),
FunctionProperties::Special(props) => props.has_antiderivative,
FunctionProperties::Polynomial(_props) => true,
FunctionProperties::UserDefined(_) => false,
}
}
#[inline(always)]
pub fn get_derivative_rule(&self) -> Option<&DerivativeRule> {
match self {
FunctionProperties::Elementary(props) => props.derivative_rule.as_ref(),
FunctionProperties::Special(_props) => None,
FunctionProperties::Polynomial(_props) => None,
FunctionProperties::UserDefined(_) => None,
}
}
pub fn get_derivative_expression(&self, arg: &Expression) -> Option<Expression> {
let rule = self.get_derivative_rule()?;
match &rule.rule_type {
DerivativeRuleType::SimpleFunctionSubstitution(func_name) => {
Some(Expression::function(func_name, vec![arg.clone()]))
}
DerivativeRuleType::Custom { builder } => Some(builder(arg)),
DerivativeRuleType::ChainRule(_) => None,
DerivativeRuleType::ProductRule => None,
DerivativeRuleType::QuotientRule => None,
}
}
#[inline(always)]
pub fn get_antiderivative_rule(&self) -> Option<&AntiderivativeRule> {
match self {
FunctionProperties::Elementary(props) => props.antiderivative_rule.as_ref(),
FunctionProperties::Special(props) => props.antiderivative_rule.as_ref(),
FunctionProperties::Polynomial(props) => Some(&props.antiderivative_rule),
FunctionProperties::UserDefined(_) => None,
}
}
#[inline(always)]
pub fn special_value_count(&self) -> usize {
match self {
FunctionProperties::Elementary(props) => props.special_values.len(),
FunctionProperties::Special(props) => props.special_values.len(),
FunctionProperties::Polynomial(props) => props.special_values.len(),
FunctionProperties::UserDefined(_) => 0,
}
}
#[inline(always)]
pub fn family(&self) -> super::intelligence::FunctionFamily {
match self {
FunctionProperties::Elementary(_) => super::intelligence::FunctionFamily::Elementary,
FunctionProperties::Special(_) => super::intelligence::FunctionFamily::Special,
FunctionProperties::Polynomial(_) => super::intelligence::FunctionFamily::Polynomial,
FunctionProperties::UserDefined(_) => super::intelligence::FunctionFamily::UserDefined,
}
}
#[inline(always)]
pub fn wolfram_name(&self) -> Option<&'static str> {
match self {
FunctionProperties::Elementary(props) => props.wolfram_name,
FunctionProperties::Special(props) => props.wolfram_name,
FunctionProperties::Polynomial(props) => props.wolfram_name,
FunctionProperties::UserDefined(props) => props.wolfram_name,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::expr;
use crate::symbol;
#[test]
fn test_function_properties_size() {
use std::mem::size_of;
assert!(
size_of::<FunctionProperties>() <= 32,
"FunctionProperties size: {} bytes (expected <= 32)",
size_of::<FunctionProperties>()
);
assert!(
size_of::<ElementaryProperties>() <= 256,
"ElementaryProperties size: {} bytes (expected <= 256)",
size_of::<ElementaryProperties>()
);
}
#[test]
fn test_hot_path_methods() {
let props: FunctionProperties =
FunctionProperties::Elementary(Box::new(ElementaryProperties {
derivative_rule: Some(DerivativeRule {
rule_type: DerivativeRuleType::SimpleFunctionSubstitution("cos".to_string()),
result_template: "cos(x)".to_string(),
}),
antiderivative_rule: Some(AntiderivativeRule {
rule_type: AntiderivativeRuleType::Simple {
antiderivative_fn: "cos".to_string(),
coefficient: Expression::integer(-1),
},
result_template: "-cos(x) + C".to_string(),
constant_handling: ConstantOfIntegration::AddConstant,
}),
special_values: vec![],
identities: Box::new(vec![]),
domain_range: Box::new(DomainRangeData {
domain: Domain::Real,
range: Range::Bounded(Expression::integer(-1), Expression::integer(1)),
singularities: vec![],
}),
periodicity: Some(Expression::mul(vec![
Expression::integer(2),
Expression::pi(),
])),
wolfram_name: Some("Sin"),
}));
assert!(props.has_derivative());
assert!(props.has_antiderivative());
assert_eq!(props.special_value_count(), 0);
assert_eq!(props.wolfram_name(), Some("Sin"));
let rule = props.get_antiderivative_rule();
assert!(rule.is_some());
if let Some(r) = rule {
assert_eq!(r.result_template, "-cos(x) + C");
assert_eq!(r.constant_handling, ConstantOfIntegration::AddConstant);
}
}
#[test]
fn test_derivative_expression_simple() {
let x = symbol!(x);
let props: FunctionProperties =
FunctionProperties::Elementary(Box::new(ElementaryProperties {
derivative_rule: Some(DerivativeRule {
rule_type: DerivativeRuleType::SimpleFunctionSubstitution("cos".to_string()),
result_template: "cos(x)".to_string(),
}),
antiderivative_rule: None,
special_values: vec![],
identities: Box::new(vec![]),
domain_range: Box::new(DomainRangeData {
domain: Domain::Real,
range: Range::Bounded(expr!(-1), expr!(1)),
singularities: vec![],
}),
periodicity: None,
wolfram_name: Some("Sin"),
}));
let derivative = props.get_derivative_expression(&x.into());
assert!(derivative.is_some());
if let Some(d) = derivative {
assert_eq!(d.to_string(), "cos(x)");
}
}
#[test]
fn test_wolfram_name_getter() {
let props_sin: FunctionProperties =
FunctionProperties::Elementary(Box::new(ElementaryProperties {
derivative_rule: None,
antiderivative_rule: None,
special_values: vec![],
identities: Box::new(vec![]),
domain_range: Box::new(DomainRangeData {
domain: Domain::Real,
range: Range::Bounded(expr!(-1), expr!(1)),
singularities: vec![],
}),
periodicity: None,
wolfram_name: Some("Sin"),
}));
assert_eq!(props_sin.wolfram_name(), Some("Sin"));
let props_user: FunctionProperties =
FunctionProperties::UserDefined(Box::new(UserProperties {
definition: None,
properties: vec![],
derivatives: std::collections::HashMap::new(),
domain: None,
wolfram_name: None,
}));
assert_eq!(props_user.wolfram_name(), None);
}
}