mathhook_core/algebra/
rational.rs

1//! Rational expression operations and simplification
2//! Handles rational functions, fraction simplification, and rational arithmetic
3//!
4//! # Noncommutative Simplification
5//!
6//! For noncommutative expressions (matrices, operators):
7//! - (AB)/(AC) can simplify to B/C only if A is left-invertible and cancellable
8//! - (BA)/(CA) can simplify to B/C only if A is right-invertible and cancellable
9//! - In general, simplification with noncommutative terms is NOT always valid
10//! - This implementation currently preserves order and does NOT auto-simplify noncommutative rationals
11
12use crate::core::{Expression, Number};
13use num_bigint::BigInt;
14use num_rational::BigRational;
15use num_traits::{One, Zero};
16
17/// Trait for rational expression operations
18pub trait RationalSimplify {
19    fn simplify_rational(&self) -> Self;
20    fn rationalize(&self) -> Self;
21    fn to_rational_form(&self) -> (Expression, Expression); // (numerator, denominator)
22}
23
24impl RationalSimplify for Expression {
25    /// Simplify rational expressions by canceling common factors
26    fn simplify_rational(&self) -> Self {
27        match self {
28            // Handle division represented as multiplication by inverse
29            Expression::Mul(factors) => self.simplify_rational_multiplication(factors),
30
31            // Handle fractions in other forms
32            Expression::Pow(base, exp) => {
33                // Check for negative exponents (which represent division)
34                if let Expression::Number(Number::Integer(n)) = exp.as_ref() {
35                    if *n < 0 {
36                        // x^(-n) = 1/x^n
37                        let positive_exp = Expression::integer(-n);
38                        let denominator = Expression::pow(base.as_ref().clone(), positive_exp);
39                        return self
40                            .create_rational_division(&Expression::integer(1), &denominator);
41                    }
42                }
43                self.clone()
44            }
45
46            _ => self.clone(),
47        }
48    }
49
50    /// Convert to rational form (numerator/denominator)
51    fn to_rational_form(&self) -> (Expression, Expression) {
52        match self {
53            Expression::Number(Number::Rational(r)) => (
54                Expression::big_integer(r.numer().clone()),
55                Expression::big_integer(r.denom().clone()),
56            ),
57
58            Expression::Mul(factors) => {
59                let (num_factors, den_factors) = self.separate_rational_factors(factors);
60
61                let numerator = if num_factors.is_empty() {
62                    Expression::integer(1)
63                } else {
64                    Expression::mul(num_factors)
65                };
66
67                let denominator = if den_factors.is_empty() {
68                    Expression::integer(1)
69                } else {
70                    Expression::mul(den_factors)
71                };
72
73                (numerator, denominator)
74            }
75
76            Expression::Pow(base, exp) => {
77                if let Expression::Number(Number::Integer(n)) = exp.as_ref() {
78                    if *n < 0 {
79                        // Negative exponent: move to denominator
80                        let positive_exp = Expression::integer(-n);
81                        let denominator = Expression::pow(base.as_ref().clone(), positive_exp);
82                        return (Expression::integer(1), denominator);
83                    }
84                }
85                (self.clone(), Expression::integer(1))
86            }
87
88            _ => (self.clone(), Expression::integer(1)),
89        }
90    }
91
92    /// Rationalize denominators (remove radicals from denominators)
93    fn rationalize(&self) -> Self {
94        // This is a complex operation - simplified implementation for now
95        self.clone()
96    }
97}
98
99impl Expression {
100    /// Simplify rational expressions in multiplication
101    fn simplify_rational_multiplication(&self, factors: &[Expression]) -> Expression {
102        let (numerator_factors, denominator_factors) = self.separate_rational_factors(factors);
103
104        // Simplify by canceling common factors
105
106        self.cancel_common_factors(&numerator_factors, &denominator_factors)
107    }
108
109    /// Separate factors into numerator and denominator parts
110    fn separate_rational_factors(
111        &self,
112        factors: &[Expression],
113    ) -> (Vec<Expression>, Vec<Expression>) {
114        let mut numerator_factors = Vec::new();
115        let mut denominator_factors = Vec::new();
116
117        for factor in factors {
118            match factor {
119                // Negative exponents go to denominator
120                Expression::Pow(base, exp) => {
121                    if let Expression::Number(Number::Integer(n)) = exp.as_ref() {
122                        if *n < 0 {
123                            let positive_exp = Expression::integer(-n);
124                            denominator_factors
125                                .push(Expression::pow(base.as_ref().clone(), positive_exp));
126                        } else {
127                            numerator_factors.push(factor.clone());
128                        }
129                    } else {
130                        numerator_factors.push(factor.clone());
131                    }
132                }
133
134                // Regular factors go to numerator
135                _ => {
136                    numerator_factors.push(factor.clone());
137                }
138            }
139        }
140
141        (numerator_factors, denominator_factors)
142    }
143
144    /// Cancel common factors between numerator and denominator
145    fn cancel_common_factors(
146        &self,
147        num_factors: &[Expression],
148        den_factors: &[Expression],
149    ) -> Expression {
150        if den_factors.is_empty() {
151            // No denominator, just return numerator
152            if num_factors.is_empty() {
153                return Expression::integer(1);
154            } else if num_factors.len() == 1 {
155                return num_factors[0].clone();
156            } else {
157                return Expression::mul(num_factors.to_vec());
158            }
159        }
160
161        let numerator = if num_factors.is_empty() {
162            Expression::integer(1)
163        } else {
164            Expression::mul(num_factors.to_vec())
165        };
166
167        let denominator = Expression::mul(den_factors.to_vec());
168
169        // Find GCD of numerator and denominator
170        let gcd = numerator.gcd(&denominator);
171
172        if !gcd.is_one() {
173            // Cancel common factor
174            let simplified_num = self.divide_expressions(&numerator, &gcd);
175            let simplified_den = self.divide_expressions(&denominator, &gcd);
176
177            self.create_rational_division(&simplified_num, &simplified_den)
178        } else {
179            self.create_rational_division(&numerator, &denominator)
180        }
181    }
182
183    /// Create a rational division expression
184    fn create_rational_division(
185        &self,
186        numerator: &Expression,
187        denominator: &Expression,
188    ) -> Expression {
189        if denominator.is_one() {
190            numerator.clone()
191        } else if numerator.is_zero() {
192            Expression::integer(0)
193        } else {
194            // Represent as multiplication by inverse (negative exponent)
195            Expression::mul(vec![
196                numerator.clone(),
197                Expression::pow(denominator.clone(), Expression::integer(-1)),
198            ])
199        }
200    }
201
202    /// Divide two expressions (simplified division)
203    fn divide_expressions(&self, dividend: &Expression, divisor: &Expression) -> Expression {
204        match (dividend, divisor) {
205            // Numeric division
206            (Expression::Number(Number::Integer(a)), Expression::Number(Number::Integer(b))) => {
207                if !b.is_zero() {
208                    let rational = BigRational::new(BigInt::from(*a), BigInt::from(*b));
209                    if rational.denom().is_one() {
210                        Expression::big_integer(rational.numer().clone())
211                    } else {
212                        Expression::number(Number::rational(rational))
213                    }
214                } else {
215                    dividend.clone() // Division by zero - return original
216                }
217            }
218
219            // Same expressions divide to 1
220            _ if dividend == divisor => Expression::integer(1),
221
222            // Multiplication division
223            (Expression::Mul(factors), _) => {
224                let mut remaining_factors = factors.as_ref().clone();
225                if let Some(pos) = remaining_factors.iter().position(|f| f == divisor) {
226                    remaining_factors.remove(pos);
227                    if remaining_factors.is_empty() {
228                        Expression::integer(1)
229                    } else if remaining_factors.len() == 1 {
230                        remaining_factors[0].clone()
231                    } else {
232                        Expression::mul(remaining_factors)
233                    }
234                } else {
235                    dividend.clone()
236                }
237            }
238
239            // Default: return original
240            _ => dividend.clone(),
241        }
242    }
243
244    /// Add rational expressions: a/b + c/d = (ad + bc)/(bd)
245    pub fn add_rationals(&self, other: &Expression) -> Expression {
246        let (num1, den1) = self.to_rational_form();
247        let (num2, den2) = other.to_rational_form();
248
249        if den1 == den2 {
250            // Same denominator: just add numerators
251            let new_num = Expression::add(vec![num1, num2]);
252            self.create_rational_division(&new_num, &den1)
253        } else {
254            // Different denominators: find common denominator
255            let new_num = Expression::add(vec![
256                Expression::mul(vec![num1, den2.clone()]),
257                Expression::mul(vec![num2, den1.clone()]),
258            ]);
259            let new_den = Expression::mul(vec![den1, den2]);
260
261            self.create_rational_division(&new_num, &new_den)
262        }
263    }
264
265    /// Multiply rational expressions: (a/b) * (c/d) = (ac)/(bd)
266    pub fn multiply_rationals(&self, other: &Expression) -> Expression {
267        let (num1, den1) = self.to_rational_form();
268        let (num2, den2) = other.to_rational_form();
269
270        let new_num = Expression::mul(vec![num1, num2]);
271        let new_den = Expression::mul(vec![den1, den2]);
272
273        self.create_rational_division(&new_num, &new_den)
274    }
275
276    /// Simplify complex rational expressions
277    pub fn simplify_complex_rational(&self) -> Expression {
278        // Handle nested fractions and complex rational expressions
279        match self {
280            Expression::Mul(factors) => {
281                // Look for patterns like (a/b) * (c/d)
282                let mut rational_parts = Vec::new();
283                let mut other_parts = Vec::new();
284
285                for factor in factors.iter() {
286                    if self.is_rational_expression(factor) {
287                        rational_parts.push(factor.clone());
288                    } else {
289                        other_parts.push(factor.clone());
290                    }
291                }
292
293                if rational_parts.len() > 1 {
294                    // Multiply rational parts together
295                    let mut result = rational_parts[0].clone();
296                    for rational in &rational_parts[1..] {
297                        result = result.multiply_rationals(rational);
298                    }
299
300                    // Combine with other parts
301                    if !other_parts.is_empty() {
302                        other_parts.push(result);
303                        Expression::mul(other_parts)
304                    } else {
305                        result
306                    }
307                } else {
308                    self.clone()
309                }
310            }
311            _ => self.clone(),
312        }
313    }
314
315    /// Check if an expression is a rational expression
316    fn is_rational_expression(&self, expr: &Expression) -> bool {
317        match expr {
318            Expression::Number(Number::Rational(_)) => true,
319            Expression::Pow(_, exp) => {
320                // Negative exponents indicate rational expressions
321                if let Expression::Number(Number::Integer(n)) = exp.as_ref() {
322                    *n < 0
323                } else {
324                    false
325                }
326            }
327            _ => false,
328        }
329    }
330
331    /// Extract rational coefficient from expression
332    pub fn extract_rational_coefficient(&self) -> (BigRational, Expression) {
333        match self {
334            Expression::Number(Number::Rational(r)) => ((**r).clone(), Expression::integer(1)),
335            Expression::Number(Number::Integer(n)) => {
336                (BigRational::from(BigInt::from(*n)), Expression::integer(1))
337            }
338            Expression::Mul(factors) => {
339                let mut coefficient = BigRational::one();
340                let mut non_rational_factors = Vec::new();
341
342                for factor in factors.iter() {
343                    match factor {
344                        Expression::Number(Number::Rational(r)) => {
345                            coefficient *= r.as_ref();
346                        }
347                        Expression::Number(Number::Integer(n)) => {
348                            coefficient *= BigRational::from(BigInt::from(*n));
349                        }
350                        _ => {
351                            non_rational_factors.push(factor.clone());
352                        }
353                    }
354                }
355
356                let remaining = if non_rational_factors.is_empty() {
357                    Expression::integer(1)
358                } else if non_rational_factors.len() == 1 {
359                    non_rational_factors[0].clone()
360                } else {
361                    Expression::mul(non_rational_factors)
362                };
363
364                (coefficient, remaining)
365            }
366            _ => (BigRational::one(), self.clone()),
367        }
368    }
369
370    /// Extract rational coefficient as string parts (FFI-friendly, no BigRational)
371    ///
372    /// Returns (numerator, denominator, remainder) where numerator/denominator
373    /// is the rational coefficient and remainder is the non-rational part.
374    pub fn extract_rational_parts(&self) -> (String, String, Expression) {
375        let (rational, remainder) = self.extract_rational_coefficient();
376        (
377            rational.numer().to_string(),
378            rational.denom().to_string(),
379            remainder,
380        )
381    }
382}
383
384#[cfg(test)]
385mod tests {
386    use super::*;
387    use crate::symbol;
388
389    #[test]
390    fn test_rational_detection() {
391        // Test basic rational number
392        let rational = Expression::number(Number::rational(BigRational::new(
393            BigInt::from(3),
394            BigInt::from(4),
395        )));
396
397        let (num, den) = rational.to_rational_form();
398
399        assert_eq!(num, Expression::integer(3));
400        assert_eq!(den, Expression::integer(4));
401    }
402
403    #[test]
404    fn test_simple_rational_combination() {
405        // Test 1/2 + 1/3 = 5/6
406        let half = Expression::number(Number::rational(BigRational::new(
407            BigInt::from(1),
408            BigInt::from(2),
409        )));
410        let third = Expression::number(Number::rational(BigRational::new(
411            BigInt::from(1),
412            BigInt::from(3),
413        )));
414
415        let result = half.add_rationals(&third);
416        println!("1/2 + 1/3 = {}", result);
417
418        // Should be 5/6
419        assert!(!result.is_zero());
420    }
421
422    #[test]
423    fn test_rational_simplification() {
424        let x = symbol!(x);
425
426        // Test x^(-1) = 1/x
427        let expr = Expression::pow(Expression::symbol(x.clone()), Expression::integer(-1));
428        let result = expr.simplify_rational();
429
430        println!("x^(-1) simplified = {}", result);
431        assert!(!result.is_zero());
432    }
433
434    #[test]
435    fn test_rational_multiplication() {
436        // Test (2/3) * (3/4) = 6/12 = 1/2
437        let frac1 = Expression::number(Number::rational(BigRational::new(
438            BigInt::from(2),
439            BigInt::from(3),
440        )));
441        let frac2 = Expression::number(Number::rational(BigRational::new(
442            BigInt::from(3),
443            BigInt::from(4),
444        )));
445
446        let result = frac1.multiply_rationals(&frac2);
447        println!("(2/3) * (3/4) = {}", result);
448
449        assert!(!result.is_zero());
450    }
451
452    #[test]
453    fn test_common_factor_cancellation() {
454        let x = symbol!(x);
455
456        // Test (6x) / (9x) = 2/3 (if implemented)
457        let numerator =
458            Expression::mul(vec![Expression::integer(6), Expression::symbol(x.clone())]);
459        let denominator =
460            Expression::mul(vec![Expression::integer(9), Expression::symbol(x.clone())]);
461
462        let expr = Expression::integer(1).create_rational_division(&numerator, &denominator);
463        let result = expr.simplify_rational();
464
465        println!("(6x)/(9x) simplified = {}", result);
466        assert!(!result.is_zero());
467    }
468
469    #[test]
470    fn test_extract_rational_coefficient() {
471        let x = symbol!(x);
472
473        let expr = Expression::mul(vec![
474            Expression::number(Number::rational(BigRational::new(
475                BigInt::from(3),
476                BigInt::from(4),
477            ))),
478            Expression::symbol(x.clone()),
479        ]);
480
481        let (coeff, remaining) = expr.extract_rational_coefficient();
482
483        println!(
484            "Coefficient: {}, Remaining: {}",
485            Expression::number(Number::rational(coeff)),
486            remaining
487        );
488
489        assert_eq!(remaining, Expression::symbol(x));
490    }
491}