mathhook_core/
simplify.rs

1//! Minimal overhead, maximum performance implementation
2//!
3//! Uses modular architecture for specialized simplification of different expression types.
4//! Individual modules handle specific aspects following project guidelines.
5
6use crate::core::{Expression, Number};
7use crate::matrices::operations::MatrixOperations;
8use num_traits::ToPrimitive;
9pub mod arithmetic;
10mod constants;
11mod functions;
12
13/// Trait for simplifying expressions
14///
15/// Simplification transforms expressions into equivalent but simpler symbolic forms
16/// through algebraic reduction. Unlike evaluation, simplification:
17/// - Does NOT substitute variables
18/// - Does NOT check domain restrictions
19/// - Does NOT compute numerical values (except constant folding)
20/// - DOES preserve mathematical equivalence
21///
22/// # Simplification vs Evaluation
23///
24/// **Use `simplify()` when:** You need algebraic reduction while keeping expressions symbolic
25/// **Use `evaluate()` when:** You need numerical values with domain checking
26/// **Use `evaluate_with_context()` when:** You need to substitute variables and compute
27///
28/// # Examples
29///
30/// ```rust
31/// use mathhook_core::{expr, symbol};
32/// use mathhook_core::simplify::Simplify;
33/// use mathhook_core::Expression;
34///
35/// // Combine like terms
36/// let x = symbol!(x);
37/// let x_expr = Expression::symbol(x.clone());
38/// let sum = Expression::add(vec![x_expr.clone(), x_expr.clone()]);
39/// assert_eq!(sum.simplify(), Expression::mul(vec![expr!(2), x_expr.clone()]));
40///
41/// // Apply identities
42/// assert_eq!(Expression::mul(vec![x_expr.clone(), expr!(1)]).simplify(), x_expr.clone());
43/// assert_eq!(Expression::add(vec![x_expr.clone(), expr!(0)]).simplify(), x_expr.clone());
44/// assert_eq!(Expression::mul(vec![expr!(0), x_expr.clone()]).simplify(), expr!(0));
45///
46/// // Constant folding (algebraic, not "evaluation")
47/// assert_eq!(Expression::add(vec![expr!(2), expr!(3)]).simplify(), expr!(5));
48/// ```
49///
50/// # Domain Safety
51///
52/// Simplification does NOT validate mathematical domains:
53///
54/// ```rust
55/// use mathhook_core::{symbol, Expression};
56/// use mathhook_core::simplify::Simplify;
57///
58/// // No error - stays symbolic or simplifies to i (complex domain)
59/// let sqrt_neg = Expression::function("sqrt".to_string(), vec![Expression::integer(-1)]);
60/// let result = sqrt_neg.simplify();
61/// // For domain checking, use evaluate() instead
62/// ```
63///
64/// # Idempotency
65///
66/// Simplification is idempotent (applying twice yields same result):
67///
68/// ```rust
69/// use mathhook_core::{symbol, Expression};
70/// use mathhook_core::simplify::Simplify;
71///
72/// let x = symbol!(x);
73/// let x_expr = Expression::symbol(x.clone());
74/// let expr = Expression::add(vec![x_expr.clone(), x_expr.clone(), x_expr.clone()]);
75/// assert_eq!(expr.simplify().simplify(), expr.simplify());
76/// ```
77pub trait Simplify {
78    /// Simplify expression using algebraic reduction rules
79    ///
80    /// Returns a mathematically equivalent expression in canonical simplified form.
81    /// Never fails (always returns `Expression`, not `Result`).
82    fn simplify(&self) -> Self;
83}
84
85impl Simplify for Expression {
86    #[inline(always)]
87    fn simplify(&self) -> Self {
88        match self {
89            Expression::Number(num) => Self::normalize_number(num),
90            Expression::Symbol(_) => self.clone(),
91
92            // Delegate arithmetic operations to specialized arithmetic module
93            Expression::Add(terms) => arithmetic::simplify_addition(terms),
94            Expression::Mul(factors) => arithmetic::simplify_multiplication(factors),
95            Expression::Pow(base, exp) => arithmetic::simplify_power(base, exp),
96
97            // Delegate function simplification to functions module
98            Expression::Function { name, args } => functions::simplify_function(name, args),
99
100            // Delegate constant simplification to constants module
101            Expression::Constant(constant) => constants::simplify_constant(constant),
102
103            // Delegate complex and matrix operations to specialized modules
104            Expression::Complex(_) => Expression::simplify_complex(self),
105            Expression::Matrix(_) => self.simplify_matrix(),
106
107            // Handle remaining expression types with proper simplification
108            Expression::Relation(relation) => {
109                // Simplify both sides of relations (equations, inequalities)
110                let simplified_left = relation.left.simplify();
111                let simplified_right = relation.right.simplify();
112                Expression::relation(simplified_left, simplified_right, relation.relation_type)
113            }
114            Expression::Piecewise(piecewise) => {
115                // Simplify each piece's expression and condition
116                let simplified_pieces: Vec<_> = piecewise
117                    .pieces
118                    .iter()
119                    .map(|piece| (piece.0.simplify(), piece.1.simplify()))
120                    .collect();
121                let simplified_default = piecewise.default.as_ref().map(|expr| expr.simplify());
122                Expression::piecewise(simplified_pieces, simplified_default)
123            }
124            Expression::Set(set_elements) => {
125                // Simplify each element in the set
126                let simplified_elements: Vec<_> =
127                    set_elements.iter().map(|elem| elem.simplify()).collect();
128                Expression::set(simplified_elements)
129            }
130            Expression::Interval(interval) => {
131                // Simplify interval bounds
132                let simplified_start = interval.start.simplify();
133                let simplified_end = interval.end.simplify();
134                Expression::interval(
135                    simplified_start,
136                    simplified_end,
137                    interval.start_inclusive,
138                    interval.end_inclusive,
139                )
140            }
141            Expression::Calculus(calc_op) => {
142                // Simplify calculus operations by simplifying their sub-expressions
143                use crate::core::expression::data_types::CalculusData;
144                match calc_op.as_ref() {
145                    CalculusData::Derivative {
146                        expression,
147                        variable,
148                        order,
149                    } => {
150                        let simplified_expr = expression.simplify();
151                        Expression::Calculus(Box::new(CalculusData::Derivative {
152                            expression: simplified_expr,
153                            variable: variable.clone(),
154                            order: *order,
155                        }))
156                    }
157                    CalculusData::Integral {
158                        integrand,
159                        variable,
160                        bounds,
161                    } => {
162                        let simplified_integrand = integrand.simplify();
163                        let simplified_bounds = bounds
164                            .as_ref()
165                            .map(|(start, end)| (start.simplify(), end.simplify()));
166                        Expression::Calculus(Box::new(CalculusData::Integral {
167                            integrand: simplified_integrand,
168                            variable: variable.clone(),
169                            bounds: simplified_bounds,
170                        }))
171                    }
172                    CalculusData::Limit {
173                        expression,
174                        variable,
175                        point,
176                        direction,
177                    } => {
178                        let simplified_expr = expression.simplify();
179                        let simplified_point = point.simplify();
180                        Expression::Calculus(Box::new(CalculusData::Limit {
181                            expression: simplified_expr,
182                            variable: variable.clone(),
183                            point: simplified_point,
184                            direction: *direction,
185                        }))
186                    }
187                    CalculusData::Sum {
188                        expression,
189                        variable,
190                        start,
191                        end,
192                    } => {
193                        let simplified_expr = expression.simplify();
194                        let simplified_start = start.simplify();
195                        let simplified_end = end.simplify();
196                        Expression::Calculus(Box::new(CalculusData::Sum {
197                            expression: simplified_expr,
198                            variable: variable.clone(),
199                            start: simplified_start,
200                            end: simplified_end,
201                        }))
202                    }
203                    CalculusData::Product {
204                        expression,
205                        variable,
206                        start,
207                        end,
208                    } => {
209                        let simplified_expr = expression.simplify();
210                        let simplified_start = start.simplify();
211                        let simplified_end = end.simplify();
212                        Expression::Calculus(Box::new(CalculusData::Product {
213                            expression: simplified_expr,
214                            variable: variable.clone(),
215                            start: simplified_start,
216                            end: simplified_end,
217                        }))
218                    }
219                }
220            }
221            Expression::MethodCall(method_data) => {
222                let simplified_object = method_data.object.simplify();
223                let simplified_args: Vec<Expression> =
224                    method_data.args.iter().map(|arg| arg.simplify()).collect();
225
226                // Try to evaluate the method call if possible
227                let method_call = Expression::method_call(
228                    simplified_object,
229                    &method_data.method_name,
230                    simplified_args,
231                );
232                method_call.evaluate_method_call()
233            }
234        }
235    }
236}
237
238impl Expression {
239    /// Normalize Number by converting BigInteger to Integer when it fits in i64
240    fn normalize_number(num: &Number) -> Self {
241        match num {
242            Number::BigInteger(bi) => {
243                if let Some(i) = bi.to_i64() {
244                    Expression::Number(Number::Integer(i))
245                } else {
246                    Expression::Number(num.clone())
247                }
248            }
249            _ => Expression::Number(num.clone()),
250        }
251    }
252}
253
254#[cfg(test)]
255mod tests {
256    use super::*;
257    use crate::{expr, function};
258
259    #[test]
260    fn test_basic_simplification() {
261        // Test integer addition
262        let expr = expr!(2 + 3);
263        assert_eq!(expr.simplify(), expr!(5));
264
265        // Test multiplication
266        let expr = expr!(2 * 3);
267        assert_eq!(expr.simplify(), expr!(6));
268
269        // Test power
270        let expr = expr!(x ^ 1);
271        assert_eq!(expr.simplify(), expr!(x));
272    }
273
274    #[test]
275    fn test_function_simplification() {
276        // Test sin(0) = 0
277        let expr = function!(sin, expr!(0));
278        assert_eq!(expr.simplify(), expr!(0));
279
280        // Test cos(0) = 1
281        let expr = function!(cos, expr!(0));
282        assert_eq!(expr.simplify(), expr!(1));
283    }
284
285    #[test]
286    fn test_zero_detection() {
287        // Test zero multiplication
288        let expr = expr!(0 * 5);
289        let result = expr.simplify();
290        assert_eq!(result, expr!(0));
291    }
292}