use crate::core::{Expression, Number};
use num_bigint::BigInt;
use num_rational::BigRational;
use num_traits::{One, Zero};
pub trait RationalSimplify {
fn simplify_rational(&self) -> Self;
fn rationalize(&self) -> Self;
fn to_rational_form(&self) -> (Expression, Expression); }
impl RationalSimplify for Expression {
fn simplify_rational(&self) -> Self {
match self {
Expression::Mul(factors) => self.simplify_rational_multiplication(factors),
Expression::Pow(base, exp) => {
if let Expression::Number(Number::Integer(n)) = exp.as_ref() {
if *n < 0 {
let positive_exp = Expression::integer(-n);
let denominator = Expression::pow(base.as_ref().clone(), positive_exp);
return self
.create_rational_division(&Expression::integer(1), &denominator);
}
}
self.clone()
}
_ => self.clone(),
}
}
fn to_rational_form(&self) -> (Expression, Expression) {
match self {
Expression::Number(Number::Rational(r)) => (
Expression::big_integer(r.numer().clone()),
Expression::big_integer(r.denom().clone()),
),
Expression::Mul(factors) => {
let (num_factors, den_factors) = self.separate_rational_factors(factors);
let numerator = if num_factors.is_empty() {
Expression::integer(1)
} else {
Expression::mul(num_factors)
};
let denominator = if den_factors.is_empty() {
Expression::integer(1)
} else {
Expression::mul(den_factors)
};
(numerator, denominator)
}
Expression::Pow(base, exp) => {
if let Expression::Number(Number::Integer(n)) = exp.as_ref() {
if *n < 0 {
let positive_exp = Expression::integer(-n);
let denominator = Expression::pow(base.as_ref().clone(), positive_exp);
return (Expression::integer(1), denominator);
}
}
(self.clone(), Expression::integer(1))
}
_ => (self.clone(), Expression::integer(1)),
}
}
fn rationalize(&self) -> Self {
self.clone()
}
}
impl Expression {
fn simplify_rational_multiplication(&self, factors: &[Expression]) -> Expression {
let (numerator_factors, denominator_factors) = self.separate_rational_factors(factors);
self.cancel_common_factors(&numerator_factors, &denominator_factors)
}
fn separate_rational_factors(
&self,
factors: &[Expression],
) -> (Vec<Expression>, Vec<Expression>) {
let mut numerator_factors = Vec::new();
let mut denominator_factors = Vec::new();
for factor in factors {
match factor {
Expression::Pow(base, exp) => {
if let Expression::Number(Number::Integer(n)) = exp.as_ref() {
if *n < 0 {
let positive_exp = Expression::integer(-n);
denominator_factors
.push(Expression::pow(base.as_ref().clone(), positive_exp));
} else {
numerator_factors.push(factor.clone());
}
} else {
numerator_factors.push(factor.clone());
}
}
_ => {
numerator_factors.push(factor.clone());
}
}
}
(numerator_factors, denominator_factors)
}
fn cancel_common_factors(
&self,
num_factors: &[Expression],
den_factors: &[Expression],
) -> Expression {
if den_factors.is_empty() {
if num_factors.is_empty() {
return Expression::integer(1);
} else if num_factors.len() == 1 {
return num_factors[0].clone();
} else {
return Expression::mul(num_factors.to_vec());
}
}
let numerator = if num_factors.is_empty() {
Expression::integer(1)
} else {
Expression::mul(num_factors.to_vec())
};
let denominator = Expression::mul(den_factors.to_vec());
let gcd = numerator.gcd(&denominator);
if !gcd.is_one() {
let simplified_num = self.divide_expressions(&numerator, &gcd);
let simplified_den = self.divide_expressions(&denominator, &gcd);
self.create_rational_division(&simplified_num, &simplified_den)
} else {
self.create_rational_division(&numerator, &denominator)
}
}
fn create_rational_division(
&self,
numerator: &Expression,
denominator: &Expression,
) -> Expression {
if denominator.is_one() {
numerator.clone()
} else if numerator.is_zero() {
Expression::integer(0)
} else {
Expression::mul(vec![
numerator.clone(),
Expression::pow(denominator.clone(), Expression::integer(-1)),
])
}
}
fn divide_expressions(&self, dividend: &Expression, divisor: &Expression) -> Expression {
match (dividend, divisor) {
(Expression::Number(Number::Integer(a)), Expression::Number(Number::Integer(b))) => {
if !b.is_zero() {
let rational = BigRational::new(BigInt::from(*a), BigInt::from(*b));
if rational.denom().is_one() {
Expression::big_integer(rational.numer().clone())
} else {
Expression::number(Number::rational(rational))
}
} else {
dividend.clone() }
}
_ if dividend == divisor => Expression::integer(1),
(Expression::Mul(factors), _) => {
let mut remaining_factors = factors.as_ref().clone();
if let Some(pos) = remaining_factors.iter().position(|f| f == divisor) {
remaining_factors.remove(pos);
if remaining_factors.is_empty() {
Expression::integer(1)
} else if remaining_factors.len() == 1 {
remaining_factors[0].clone()
} else {
Expression::mul(remaining_factors)
}
} else {
dividend.clone()
}
}
_ => dividend.clone(),
}
}
pub fn add_rationals(&self, other: &Expression) -> Expression {
let (num1, den1) = self.to_rational_form();
let (num2, den2) = other.to_rational_form();
if den1 == den2 {
let new_num = Expression::add(vec![num1, num2]);
self.create_rational_division(&new_num, &den1)
} else {
let new_num = Expression::add(vec![
Expression::mul(vec![num1, den2.clone()]),
Expression::mul(vec![num2, den1.clone()]),
]);
let new_den = Expression::mul(vec![den1, den2]);
self.create_rational_division(&new_num, &new_den)
}
}
pub fn multiply_rationals(&self, other: &Expression) -> Expression {
let (num1, den1) = self.to_rational_form();
let (num2, den2) = other.to_rational_form();
let new_num = Expression::mul(vec![num1, num2]);
let new_den = Expression::mul(vec![den1, den2]);
self.create_rational_division(&new_num, &new_den)
}
pub fn simplify_complex_rational(&self) -> Expression {
match self {
Expression::Mul(factors) => {
let mut rational_parts = Vec::new();
let mut other_parts = Vec::new();
for factor in factors.iter() {
if self.is_rational_expression(factor) {
rational_parts.push(factor.clone());
} else {
other_parts.push(factor.clone());
}
}
if rational_parts.len() > 1 {
let mut result = rational_parts[0].clone();
for rational in &rational_parts[1..] {
result = result.multiply_rationals(rational);
}
if !other_parts.is_empty() {
other_parts.push(result);
Expression::mul(other_parts)
} else {
result
}
} else {
self.clone()
}
}
_ => self.clone(),
}
}
fn is_rational_expression(&self, expr: &Expression) -> bool {
match expr {
Expression::Number(Number::Rational(_)) => true,
Expression::Pow(_, exp) => {
if let Expression::Number(Number::Integer(n)) = exp.as_ref() {
*n < 0
} else {
false
}
}
_ => false,
}
}
pub fn extract_rational_coefficient(&self) -> (BigRational, Expression) {
match self {
Expression::Number(Number::Rational(r)) => ((**r).clone(), Expression::integer(1)),
Expression::Number(Number::Integer(n)) => {
(BigRational::from(BigInt::from(*n)), Expression::integer(1))
}
Expression::Mul(factors) => {
let mut coefficient = BigRational::one();
let mut non_rational_factors = Vec::new();
for factor in factors.iter() {
match factor {
Expression::Number(Number::Rational(r)) => {
coefficient *= r.as_ref();
}
Expression::Number(Number::Integer(n)) => {
coefficient *= BigRational::from(BigInt::from(*n));
}
_ => {
non_rational_factors.push(factor.clone());
}
}
}
let remaining = if non_rational_factors.is_empty() {
Expression::integer(1)
} else if non_rational_factors.len() == 1 {
non_rational_factors[0].clone()
} else {
Expression::mul(non_rational_factors)
};
(coefficient, remaining)
}
_ => (BigRational::one(), self.clone()),
}
}
pub fn extract_rational_parts(&self) -> (String, String, Expression) {
let (rational, remainder) = self.extract_rational_coefficient();
(
rational.numer().to_string(),
rational.denom().to_string(),
remainder,
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::symbol;
#[test]
fn test_rational_detection() {
let rational = Expression::number(Number::rational(BigRational::new(
BigInt::from(3),
BigInt::from(4),
)));
let (num, den) = rational.to_rational_form();
assert_eq!(num, Expression::integer(3));
assert_eq!(den, Expression::integer(4));
}
#[test]
fn test_simple_rational_combination() {
let half = Expression::number(Number::rational(BigRational::new(
BigInt::from(1),
BigInt::from(2),
)));
let third = Expression::number(Number::rational(BigRational::new(
BigInt::from(1),
BigInt::from(3),
)));
let result = half.add_rationals(&third);
println!("1/2 + 1/3 = {}", result);
assert!(!result.is_zero());
}
#[test]
fn test_rational_simplification() {
let x = symbol!(x);
let expr = Expression::pow(Expression::symbol(x.clone()), Expression::integer(-1));
let result = expr.simplify_rational();
println!("x^(-1) simplified = {}", result);
assert!(!result.is_zero());
}
#[test]
fn test_rational_multiplication() {
let frac1 = Expression::number(Number::rational(BigRational::new(
BigInt::from(2),
BigInt::from(3),
)));
let frac2 = Expression::number(Number::rational(BigRational::new(
BigInt::from(3),
BigInt::from(4),
)));
let result = frac1.multiply_rationals(&frac2);
println!("(2/3) * (3/4) = {}", result);
assert!(!result.is_zero());
}
#[test]
fn test_common_factor_cancellation() {
let x = symbol!(x);
let numerator =
Expression::mul(vec![Expression::integer(6), Expression::symbol(x.clone())]);
let denominator =
Expression::mul(vec![Expression::integer(9), Expression::symbol(x.clone())]);
let expr = Expression::integer(1).create_rational_division(&numerator, &denominator);
let result = expr.simplify_rational();
println!("(6x)/(9x) simplified = {}", result);
assert!(!result.is_zero());
}
#[test]
fn test_extract_rational_coefficient() {
let x = symbol!(x);
let expr = Expression::mul(vec![
Expression::number(Number::rational(BigRational::new(
BigInt::from(3),
BigInt::from(4),
))),
Expression::symbol(x.clone()),
]);
let (coeff, remaining) = expr.extract_rational_coefficient();
println!(
"Coefficient: {}, Remaining: {}",
Expression::number(Number::rational(coeff)),
remaining
);
assert_eq!(remaining, Expression::symbol(x));
}
}