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}