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}