mathhook_core/functions/elementary/
abs.rs1use crate::core::{Expression, Number, Symbol};
7use crate::functions::properties::*;
8use std::collections::HashMap;
9use std::sync::Arc;
10
11pub 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 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 pub fn get_properties(&self) -> HashMap<String, FunctionProperties> {
57 self.properties.clone()
58 }
59
60 pub fn has_function(&self, name: &str) -> bool {
76 self.properties.contains_key(name)
77 }
78
79 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
187pub 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
246fn 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
262fn is_negation(terms: &[Expression]) -> bool {
264 terms.len() == 2 && matches!(terms[0], Expression::Number(Number::Integer(-1)))
265}
266
267fn extract_negation_argument(terms: &[Expression]) -> Expression {
269 terms[1].clone()
270}
271
272fn is_square(exp: &Expression) -> bool {
274 matches!(exp, Expression::Number(Number::Integer(2)))
275}
276
277fn 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}