mathhook_core/functions/elementary/
abs.rs

1//! Absolute value function intelligence
2//!
3//! Complete mathematical intelligence for the absolute value function
4//! with derivatives, antiderivatives, special values, and simplification rules.
5
6use crate::core::{Expression, Number, Symbol};
7use crate::functions::properties::*;
8use std::collections::HashMap;
9use std::sync::Arc;
10
11/// Absolute Value Function Intelligence
12///
13/// Dedicated intelligence system for the absolute value function
14/// with complete mathematical properties.
15pub struct AbsoluteValueIntelligence {
16    properties: HashMap<String, FunctionProperties>,
17}
18
19impl Default for AbsoluteValueIntelligence {
20    fn default() -> Self {
21        Self::new()
22    }
23}
24
25impl AbsoluteValueIntelligence {
26    /// Create new absolute value intelligence system
27    ///
28    /// # Examples
29    ///
30    /// ```
31    /// use mathhook_core::functions::elementary::abs::AbsoluteValueIntelligence;
32    ///
33    /// let intelligence = AbsoluteValueIntelligence::new();
34    /// assert!(intelligence.has_function("abs"));
35    /// ```
36    pub fn new() -> Self {
37        let mut intelligence = Self {
38            properties: HashMap::with_capacity(1),
39        };
40
41        intelligence.initialize_abs();
42        intelligence
43    }
44
45    /// Get absolute value function properties
46    ///
47    /// # Examples
48    ///
49    /// ```
50    /// use mathhook_core::functions::elementary::abs::AbsoluteValueIntelligence;
51    ///
52    /// let intelligence = AbsoluteValueIntelligence::new();
53    /// let props = intelligence.get_properties();
54    /// assert!(props.contains_key("abs"));
55    /// ```
56    pub fn get_properties(&self) -> HashMap<String, FunctionProperties> {
57        self.properties.clone()
58    }
59
60    /// Check if function is absolute value
61    ///
62    /// # Arguments
63    ///
64    /// * `name` - The function name to check
65    ///
66    /// # Examples
67    ///
68    /// ```
69    /// use mathhook_core::functions::elementary::abs::AbsoluteValueIntelligence;
70    ///
71    /// let intelligence = AbsoluteValueIntelligence::new();
72    /// assert!(intelligence.has_function("abs"));
73    /// assert!(!intelligence.has_function("sin"));
74    /// ```
75    pub fn has_function(&self, name: &str) -> bool {
76        self.properties.contains_key(name)
77    }
78
79    /// Initialize absolute value function
80    fn initialize_abs(&mut self) {
81        self.properties.insert(
82            "abs".to_owned(),
83            FunctionProperties::Elementary(Box::new(ElementaryProperties {
84                derivative_rule: Some(DerivativeRule {
85                    rule_type: DerivativeRuleType::Custom {
86                        builder: Arc::new(|arg: &Expression| {
87                            let abs_arg = Expression::function("abs", vec![arg.clone()]);
88                            Expression::mul(vec![
89                                arg.clone(),
90                                Expression::pow(abs_arg, Expression::integer(-1)),
91                            ])
92                        }),
93                    },
94                    result_template: "x/|x| for x ≠ 0".to_owned(),
95                }),
96                antiderivative_rule: Some(AntiderivativeRule {
97                    rule_type: AntiderivativeRuleType::Custom {
98                        builder: Arc::new(|var: Symbol| {
99                            Expression::mul(vec![
100                                Expression::rational(1, 2),
101                                Expression::mul(vec![
102                                    Expression::symbol(var.clone()),
103                                    Expression::function("abs", vec![Expression::symbol(var)]),
104                                ]),
105                            ])
106                        }),
107                    },
108                    result_template: "∫|x|dx = x|x|/2 + C".to_owned(),
109                    constant_handling: ConstantOfIntegration::AddConstant,
110                }),
111                special_values: vec![
112                    SpecialValue {
113                        input: "0".to_owned(),
114                        output: Expression::integer(0),
115                        latex_explanation: "|0| = 0".to_owned(),
116                    },
117                    SpecialValue {
118                        input: "1".to_owned(),
119                        output: Expression::integer(1),
120                        latex_explanation: "|1| = 1".to_owned(),
121                    },
122                    SpecialValue {
123                        input: "-1".to_owned(),
124                        output: Expression::integer(1),
125                        latex_explanation: "|-1| = 1".to_owned(),
126                    },
127                ],
128                identities: Box::new(vec![
129                    MathIdentity {
130                        name: "Even Function".to_owned(),
131                        lhs: Expression::function(
132                            "abs",
133                            vec![Expression::mul(vec![
134                                Expression::integer(-1),
135                                Expression::symbol("x"),
136                            ])],
137                        ),
138                        rhs: Expression::function("abs", vec![Expression::symbol("x")]),
139                        conditions: vec!["x ∈ ℝ".to_owned()],
140                    },
141                    MathIdentity {
142                        name: "Product Rule".to_owned(),
143                        lhs: Expression::function(
144                            "abs",
145                            vec![Expression::mul(vec![
146                                Expression::symbol("a"),
147                                Expression::symbol("b"),
148                            ])],
149                        ),
150                        rhs: Expression::mul(vec![
151                            Expression::function("abs", vec![Expression::symbol("a")]),
152                            Expression::function("abs", vec![Expression::symbol("b")]),
153                        ]),
154                        conditions: vec!["a, b ∈ ℂ".to_owned()],
155                    },
156                    MathIdentity {
157                        name: "Quotient Rule".to_owned(),
158                        lhs: Expression::function(
159                            "abs",
160                            vec![Expression::mul(vec![
161                                Expression::symbol("a"),
162                                Expression::pow(Expression::symbol("b"), Expression::integer(-1)),
163                            ])],
164                        ),
165                        rhs: Expression::mul(vec![
166                            Expression::function("abs", vec![Expression::symbol("a")]),
167                            Expression::pow(
168                                Expression::function("abs", vec![Expression::symbol("b")]),
169                                Expression::integer(-1),
170                            ),
171                        ]),
172                        conditions: vec!["a, b ∈ ℂ, b ≠ 0".to_owned()],
173                    },
174                ]),
175                domain_range: Box::new(DomainRangeData {
176                    domain: Domain::Real,
177                    range: Range::Bounded(Expression::integer(0), Expression::infinity()),
178                    singularities: vec![],
179                }),
180                periodicity: None,
181                wolfram_name: None,
182            })),
183        );
184    }
185}
186
187/// Simplify absolute value expressions
188///
189/// Applies mathematical simplification rules for absolute value.
190///
191/// # Simplification Rules
192///
193/// - |0| = 0
194/// - |-x| = |x|
195/// - |x²| = x² (squares are always non-negative)
196/// - |a*b| = |a|*|b|
197/// - |a/b| = |a|/|b|
198/// - ||x|| = |x|
199///
200/// # Arguments
201///
202/// * `arg` - The argument to the absolute value function
203///
204/// # Returns
205///
206/// Simplified expression
207///
208/// # Examples
209///
210/// ```
211/// use mathhook_core::core::Expression;
212/// use mathhook_core::functions::elementary::abs::simplify_abs;
213///
214/// let zero = Expression::integer(0);
215/// assert_eq!(simplify_abs(&zero), Expression::integer(0));
216///
217/// let neg_five = Expression::integer(-5);
218/// assert_eq!(simplify_abs(&neg_five), Expression::integer(5));
219///
220/// let squared = Expression::pow(Expression::symbol("x"), Expression::integer(2));
221/// assert_eq!(simplify_abs(&squared), squared);
222/// ```
223pub fn simplify_abs(arg: &Expression) -> Expression {
224    match arg {
225        Expression::Number(n) => evaluate_abs_number(n),
226
227        Expression::Mul(terms) if is_negation(terms) => {
228            let inner = extract_negation_argument(terms);
229            Expression::function("abs", vec![inner])
230        }
231
232        Expression::Pow(base, exp) if is_square(exp) => {
233            Expression::pow((**base).clone(), (**exp).clone())
234        }
235
236        Expression::Mul(terms) => simplify_abs_product(terms),
237
238        Expression::Function { name, args } if name.as_ref() == "abs" && args.len() == 1 => {
239            Expression::function("abs", vec![args[0].clone()])
240        }
241
242        _ => Expression::function("abs", vec![arg.clone()]),
243    }
244}
245
246/// Evaluate absolute value for numeric arguments
247fn evaluate_abs_number(n: &Number) -> Expression {
248    use num_rational::BigRational;
249    use num_traits::Signed;
250
251    match n {
252        Number::Integer(i) => Expression::integer(i.abs()),
253        Number::Float(f) => Expression::float(f.abs()),
254        Number::BigInteger(bi) => Expression::big_integer(bi.abs()),
255        Number::Rational(r) => Expression::Number(Number::rational(BigRational::new(
256            r.numer().abs(),
257            r.denom().clone(),
258        ))),
259    }
260}
261
262/// Check if expression is a negation (-x)
263fn is_negation(terms: &[Expression]) -> bool {
264    terms.len() == 2 && matches!(terms[0], Expression::Number(Number::Integer(-1)))
265}
266
267/// Extract argument from negation expression
268fn extract_negation_argument(terms: &[Expression]) -> Expression {
269    terms[1].clone()
270}
271
272/// Check if exponent represents squaring (power of 2)
273fn is_square(exp: &Expression) -> bool {
274    matches!(exp, Expression::Number(Number::Integer(2)))
275}
276
277/// Simplify absolute value of a product: |a*b| = |a|*|b|
278fn simplify_abs_product(terms: &[Expression]) -> Expression {
279    Expression::mul(
280        terms
281            .iter()
282            .map(|term| Expression::function("abs", vec![term.clone()]))
283            .collect(),
284    )
285}
286
287#[cfg(test)]
288mod tests {
289    use super::*;
290
291    #[test]
292    fn test_abs_intelligence_creation() {
293        let intelligence = AbsoluteValueIntelligence::new();
294        assert!(intelligence.has_function("abs"));
295
296        let props = intelligence.get_properties();
297        assert!(props.contains_key("abs"));
298    }
299
300    #[test]
301    fn test_abs_properties() {
302        let intelligence = AbsoluteValueIntelligence::new();
303        let props = intelligence.get_properties();
304        let abs_props = props.get("abs").unwrap();
305
306        assert!(abs_props.has_derivative());
307        assert!(abs_props.has_antiderivative());
308        assert_eq!(abs_props.special_value_count(), 3);
309    }
310
311    #[test]
312    fn test_simplify_abs_zero() {
313        let result = simplify_abs(&Expression::integer(0));
314        assert_eq!(result, Expression::integer(0));
315    }
316
317    #[test]
318    fn test_simplify_abs_positive() {
319        let result = simplify_abs(&Expression::integer(5));
320        assert_eq!(result, Expression::integer(5));
321    }
322
323    #[test]
324    fn test_simplify_abs_negative() {
325        let result = simplify_abs(&Expression::integer(-5));
326        assert_eq!(result, Expression::integer(5));
327    }
328
329    #[test]
330    fn test_simplify_abs_negation() {
331        let expr = Expression::mul(vec![Expression::integer(-1), Expression::symbol("x")]);
332        let result = simplify_abs(&expr);
333        assert_eq!(
334            result,
335            Expression::function("abs", vec![Expression::symbol("x")])
336        );
337    }
338
339    #[test]
340    fn test_simplify_abs_square() {
341        let expr = Expression::pow(Expression::symbol("x"), Expression::integer(2));
342        let result = simplify_abs(&expr);
343        assert_eq!(result, expr);
344    }
345}