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}