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}