mathhook_core/core/expression/operations.rs
1//! Mathematical operations and queries for expressions
2//!
3//! Methods for checking properties and extracting information from expressions.
4
5use super::Expression;
6use crate::core::{Number, Symbol};
7use crate::matrices::unified::CoreMatrixOps;
8use crate::simplify::Simplify;
9use num_traits::Signed;
10use std::sync::Arc;
11
12impl Expression {
13 /// Check if the expression is zero (robust version with simplification)
14 ///
15 /// This method simplifies the expression first before checking if it equals zero.
16 /// It correctly detects zero for expressions like:
17 /// - Literal zeros: `0`, `0.0`, `0/1`
18 /// - Symbolic zeros: `x - x`, `0 * y`, `sin(0)`
19 /// - Simplified zeros: `(x + 1) - (x + 1)`
20 ///
21 /// # Performance Note
22 ///
23 /// This method calls `simplify()`, which may be expensive for complex expressions.
24 /// For performance-critical code where you only need to check literal zeros,
25 /// use `is_zero_fast()` instead.
26 ///
27 /// # Examples
28 ///
29 /// ```rust
30 /// use mathhook_core::simplify::Simplify;
31 /// use mathhook_core::Expression;
32 ///
33 /// // Literal zero
34 /// assert!(Expression::integer(0).is_zero());
35 ///
36 /// // Symbolic zero after simplification
37 /// let expr = Expression::mul(vec![Expression::integer(0), Expression::integer(5)]);
38 /// assert!(expr.is_zero());
39 /// ```
40 pub fn is_zero(&self) -> bool {
41 match self {
42 Expression::Number(n) => n.is_zero(),
43 _ => {
44 let simplified = self.simplify();
45 matches!(simplified, Expression::Number(n) if n.is_zero())
46 }
47 }
48 }
49
50 /// Fast literal zero check without simplification
51 ///
52 /// This is a performance-optimized version that only checks if the expression
53 /// is literally `Number(0)`. It does NOT simplify the expression first.
54 ///
55 /// Use this in performance-critical loops where you know the expression
56 /// is already in simplified form, or where you specifically want to check
57 /// for literal zeros only.
58 ///
59 /// # Examples
60 ///
61 /// ```rust
62 /// use mathhook_core::Expression;
63 ///
64 /// // Detects literal zero
65 /// assert!(Expression::integer(0).is_zero_fast());
66 ///
67 /// // Mul constructor auto-simplifies, so this IS detected
68 /// let expr = Expression::mul(vec![Expression::integer(0), Expression::integer(5)]);
69 /// assert!(expr.is_zero_fast()); // Simplified to 0 by constructor
70 ///
71 /// // Example of what is_zero_fast() does NOT detect (without simplification):
72 /// // If we had a raw unsimplified Mul expression, is_zero_fast() wouldn't detect it
73 /// ```
74 #[inline(always)]
75 pub fn is_zero_fast(&self) -> bool {
76 matches!(self, Expression::Number(n) if n.is_zero())
77 }
78
79 /// Check if the expression is one (robust version with simplification)
80 ///
81 /// This method simplifies the expression first before checking if it equals one.
82 /// It correctly detects one for expressions like:
83 /// - Literal ones: `1`, `1.0`, `2/2`
84 /// - Symbolic ones: `x / x`, `x^0`, `cos(0)`
85 /// - Simplified ones: `(x + 1) / (x + 1)`
86 ///
87 /// # Performance Note
88 ///
89 /// This method calls `simplify()`, which may be expensive for complex expressions.
90 /// For performance-critical code where you only need to check literal ones,
91 /// use `is_one_fast()` instead.
92 ///
93 /// # Examples
94 ///
95 /// ```rust
96 /// use mathhook_core::simplify::Simplify;
97 /// use mathhook_core::Expression;
98 ///
99 /// // Literal one
100 /// assert!(Expression::integer(1).is_one());
101 ///
102 /// // Symbolic one after simplification
103 /// let expr = Expression::pow(Expression::integer(5), Expression::integer(0));
104 /// assert!(expr.is_one());
105 /// ```
106 pub fn is_one(&self) -> bool {
107 match self {
108 Expression::Number(n) => n.is_one(),
109 _ => {
110 let simplified = self.simplify();
111 matches!(simplified, Expression::Number(n) if n.is_one())
112 }
113 }
114 }
115
116 /// Fast literal one check without simplification
117 ///
118 /// This is a performance-optimized version that only checks if the expression
119 /// is literally `Number(1)`. It does NOT simplify the expression first.
120 ///
121 /// Use this in performance-critical loops where you know the expression
122 /// is already in simplified form, or where you specifically want to check
123 /// for literal ones only.
124 ///
125 /// # Examples
126 ///
127 /// ```rust
128 /// use mathhook_core::Expression;
129 ///
130 /// // Detects literal one
131 /// assert!(Expression::integer(1).is_one_fast());
132 ///
133 /// // Pow constructor auto-simplifies, so x^0 = 1 IS detected
134 /// let expr = Expression::pow(Expression::integer(5), Expression::integer(0));
135 /// assert!(expr.is_one_fast()); // Simplified to 1 by constructor
136 ///
137 /// // is_one_fast() checks ONLY for literal Number(1)
138 /// // It does not simplify complex expressions first
139 /// ```
140 #[inline(always)]
141 pub fn is_one_fast(&self) -> bool {
142 matches!(self, Expression::Number(n) if n.is_one())
143 }
144
145 /// Get the numeric coefficient if this is a simple numeric expression
146 #[inline]
147 pub fn as_number(&self) -> Option<&Number> {
148 match self {
149 Expression::Number(n) => Some(n),
150 _ => None,
151 }
152 }
153
154 /// Get the symbol if this is a simple symbol expression
155 #[inline]
156 pub fn as_symbol(&self) -> Option<&Symbol> {
157 match self {
158 Expression::Symbol(s) => Some(s),
159 _ => None,
160 }
161 }
162
163 /// Check if this expression is a negative number
164 ///
165 /// Returns true if the expression is a negative integer, rational, or float.
166 /// Returns false for symbolic expressions (even if they might evaluate to negative).
167 ///
168 /// # Examples
169 ///
170 /// ```rust
171 /// use mathhook_core::Expression;
172 ///
173 /// assert!(Expression::integer(-5).is_negative_number());
174 /// assert!(Expression::rational(-1, 2).is_negative_number());
175 /// assert!(!Expression::integer(5).is_negative_number());
176 /// assert!(!Expression::symbol("x").is_negative_number()); // Symbolic, not a number
177 /// ```
178 #[inline]
179 pub fn is_negative_number(&self) -> bool {
180 match self {
181 Expression::Number(Number::Integer(i)) => *i < 0,
182 Expression::Number(Number::Rational(r)) => r.is_negative(),
183 Expression::Number(Number::Float(f)) => *f < 0.0,
184 _ => false,
185 }
186 }
187
188 /// Check if this expression is a positive number
189 ///
190 /// Returns true if the expression is a positive integer, rational, or float.
191 /// Returns false for symbolic expressions (even if they might evaluate to positive).
192 ///
193 /// # Examples
194 ///
195 /// ```rust
196 /// use mathhook_core::Expression;
197 ///
198 /// assert!(Expression::integer(5).is_positive_number());
199 /// assert!(Expression::rational(1, 2).is_positive_number());
200 /// assert!(!Expression::integer(-5).is_positive_number());
201 /// assert!(!Expression::symbol("x").is_positive_number()); // Symbolic, not a number
202 /// ```
203 #[inline]
204 pub fn is_positive_number(&self) -> bool {
205 match self {
206 Expression::Number(Number::Integer(i)) => *i > 0,
207 Expression::Number(Number::Rational(r)) => r.is_positive(),
208 Expression::Number(Number::Float(f)) => *f > 0.0,
209 _ => false,
210 }
211 }
212
213 /// Evaluate method calls on expressions
214 ///
215 /// This handles method calls like matrix.det(), matrix.trace(), etc.
216 /// by calling the appropriate methods on the underlying objects.
217 pub fn evaluate_method_call(&self) -> Expression {
218 match self {
219 Expression::MethodCall(method_data) => {
220 let object = &method_data.object;
221 let method_name = &method_data.method_name;
222 let _args = &method_data.args;
223
224 if let Expression::Matrix(matrix) = object {
225 match method_name.as_ref() {
226 "det" | "determinant" => matrix
227 .determinant()
228 .unwrap_or_else(|_| Expression::function("undefined", vec![])),
229 "trace" => matrix.trace(),
230 "transpose" => Expression::Matrix(Arc::new(matrix.transpose())),
231 "inverse" => Expression::Matrix(Arc::new(matrix.inverse())),
232 _ => self.clone(),
233 }
234 } else {
235 let evaluated_object = object.evaluate_method_call();
236 if let Expression::Matrix(matrix) = &evaluated_object {
237 match method_name.as_ref() {
238 "det" | "determinant" => matrix
239 .determinant()
240 .unwrap_or_else(|_| Expression::function("undefined", vec![])),
241 "trace" => matrix.trace(),
242 "transpose" => Expression::Matrix(Arc::new(matrix.transpose())),
243 "inverse" => Expression::Matrix(Arc::new(matrix.inverse())),
244 _ => self.clone(),
245 }
246 } else {
247 self.clone()
248 }
249 }
250 }
251 _ => self.clone(),
252 }
253 }
254
255 /// Check if this expression represents a mathematical function
256 ///
257 /// Returns true for expressions like sin(x), cos(x), etc.
258 /// Now integrated with Universal Function Intelligence System
259 pub fn is_function(&self) -> bool {
260 match self {
261 Expression::Function { name, .. } => {
262 use crate::functions::intelligence::UNIVERSAL_REGISTRY;
263 UNIVERSAL_REGISTRY.has_intelligence(name)
264 }
265 _ => false,
266 }
267 }
268
269 /// Get function intelligence properties if available
270 ///
271 /// Seamless integration between core expressions and function intelligence
272 pub fn get_function_intelligence(&self) -> Option<&crate::functions::FunctionProperties> {
273 if let Expression::Function { name, .. } = self {
274 use crate::functions::intelligence::UNIVERSAL_REGISTRY;
275 UNIVERSAL_REGISTRY.get_properties(name)
276 } else {
277 None
278 }
279 }
280
281 /// Generate educational explanation for function expressions
282 ///
283 /// Perfect integration with the educational system
284 pub fn explain_function(&self) -> Vec<crate::educational::step_by_step::Step> {
285 if let Expression::Function { name, args } = self {
286 use crate::functions::intelligence::UNIVERSAL_REGISTRY;
287 UNIVERSAL_REGISTRY.explain_function(name, args)
288 } else {
289 vec![crate::educational::step_by_step::Step::new(
290 "Expression Type",
291 "This is not a function expression".to_owned(),
292 )]
293 }
294 }
295}