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