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