use crate::core::{Expression, Number, BinaryOperator, UnaryOperator};
use crate::engine::error::ComputeError;
use std::collections::HashMap;
use num_bigint::BigInt;
use num_rational::BigRational;
use num_traits::{Zero, Signed};
#[derive(Debug, Clone, PartialEq)]
pub struct PolynomialTerm {
pub coefficient: Number,
pub variables: HashMap<String, i32>,
}
impl PolynomialTerm {
pub fn new(coefficient: Number, variables: HashMap<String, i32>) -> Self {
Self { coefficient, variables }
}
pub fn constant(coefficient: Number) -> Self {
Self {
coefficient,
variables: HashMap::new(),
}
}
pub fn variable(var: String, power: i32, coefficient: Number) -> Self {
let mut variables = HashMap::new();
if power != 0 {
variables.insert(var, power);
}
Self { coefficient, variables }
}
pub fn is_constant(&self) -> bool {
self.variables.is_empty()
}
pub fn is_zero(&self) -> bool {
self.coefficient.is_zero()
}
pub fn degree(&self) -> i32 {
self.variables.values().sum()
}
pub fn degree_of(&self, var: &str) -> i32 {
self.variables.get(var).copied().unwrap_or(0)
}
pub fn multiply(&self, other: &PolynomialTerm) -> PolynomialTerm {
let mut new_variables = self.variables.clone();
for (var, power) in &other.variables {
let current_power = new_variables.get(var).copied().unwrap_or(0);
let new_power = current_power + power;
if new_power == 0 {
new_variables.remove(var);
} else {
new_variables.insert(var.clone(), new_power);
}
}
PolynomialTerm {
coefficient: self.coefficient.clone() * other.coefficient.clone(),
variables: new_variables,
}
}
pub fn divide(&self, other: &PolynomialTerm) -> Result<PolynomialTerm, ComputeError> {
if other.coefficient.is_zero() {
return Err(ComputeError::DivisionByZero);
}
let mut new_variables = self.variables.clone();
for (var, other_power) in &other.variables {
let self_power = self.variables.get(var).copied().unwrap_or(0);
if self_power < *other_power {
return Err(ComputeError::UnsupportedOperation {
operation: "多项式除法:被除数的变量次数小于除数".to_string()
});
}
let new_power = self_power - other_power;
if new_power == 0 {
new_variables.remove(var);
} else {
new_variables.insert(var.clone(), new_power);
}
}
Ok(PolynomialTerm {
coefficient: self.coefficient.clone() / other.coefficient.clone(),
variables: new_variables,
})
}
pub fn to_expression(&self) -> Expression {
if self.is_zero() {
return Expression::Number(Number::zero());
}
let mut result = Expression::Number(self.coefficient.clone());
for (var, power) in &self.variables {
let var_expr = if *power == 1 {
Expression::Variable(var.clone())
} else {
Expression::power(
Expression::Variable(var.clone()),
Expression::Number(Number::integer(*power as i64))
)
};
result = if result == Expression::Number(Number::one()) {
var_expr
} else {
Expression::multiply(result, var_expr)
};
}
result
}
pub fn is_like_term(&self, other: &PolynomialTerm) -> bool {
self.variables == other.variables
}
}
#[derive(Debug, Clone)]
pub struct Polynomial {
pub terms: Vec<PolynomialTerm>,
}
impl Polynomial {
pub fn new(terms: Vec<PolynomialTerm>) -> Self {
let mut poly = Self { terms };
poly.simplify();
poly
}
fn gcd_numbers_static(a: &Number, b: &Number) -> Number {
match (a, b) {
(Number::Integer(a_int), Number::Integer(b_int)) => {
use num_integer::Integer;
Number::Integer(a_int.gcd(b_int))
}
(Number::Rational(a_rat), Number::Rational(b_rat)) => {
use num_integer::Integer;
let a_num = a_rat.numer();
let a_den = a_rat.denom();
let b_num = b_rat.numer();
let b_den = b_rat.denom();
let gcd_num = a_num.gcd(b_num);
let lcm_den = a_den.lcm(b_den);
Number::Rational(BigRational::new(gcd_num, lcm_den))
}
_ => Number::one(), }
}
pub fn zero() -> Self {
Self { terms: vec![] }
}
pub fn constant(value: Number) -> Self {
if value.is_zero() {
Self::zero()
} else {
Self {
terms: vec![PolynomialTerm::constant(value)],
}
}
}
pub fn variable(var: String) -> Self {
Self {
terms: vec![PolynomialTerm::variable(var, 1, Number::one())],
}
}
pub fn is_zero(&self) -> bool {
self.terms.is_empty() || self.terms.iter().all(|term| term.is_zero())
}
pub fn is_constant(&self) -> bool {
self.terms.len() <= 1 && self.terms.iter().all(|term| term.is_constant())
}
pub fn degree(&self) -> i32 {
self.terms.iter().map(|term| term.degree()).max().unwrap_or(0)
}
pub fn degree_of(&self, var: &str) -> i32 {
self.terms.iter().map(|term| term.degree_of(var)).max().unwrap_or(0)
}
pub fn simplify(&mut self) {
self.terms.retain(|term| !term.is_zero());
let mut simplified_terms = Vec::new();
let mut processed = vec![false; self.terms.len()];
for i in 0..self.terms.len() {
if processed[i] {
continue;
}
let mut combined_coefficient = self.terms[i].coefficient.clone();
processed[i] = true;
for j in (i + 1)..self.terms.len() {
if !processed[j] && self.terms[i].is_like_term(&self.terms[j]) {
combined_coefficient = combined_coefficient + self.terms[j].coefficient.clone();
processed[j] = true;
}
}
if !combined_coefficient.is_zero() {
simplified_terms.push(PolynomialTerm {
coefficient: combined_coefficient,
variables: self.terms[i].variables.clone(),
});
}
}
self.terms = simplified_terms;
self.terms.sort_by(|a, b| {
let degree_cmp = b.degree().cmp(&a.degree());
if degree_cmp != std::cmp::Ordering::Equal {
return degree_cmp;
}
let a_vars: Vec<_> = a.variables.keys().collect();
let b_vars: Vec<_> = b.variables.keys().collect();
a_vars.cmp(&b_vars)
});
}
pub fn add(&self, other: &Polynomial) -> Polynomial {
let mut terms = self.terms.clone();
terms.extend(other.terms.clone());
Polynomial::new(terms)
}
pub fn subtract(&self, other: &Polynomial) -> Polynomial {
let mut terms = self.terms.clone();
for term in &other.terms {
terms.push(PolynomialTerm {
coefficient: -term.coefficient.clone(),
variables: term.variables.clone(),
});
}
Polynomial::new(terms)
}
pub fn multiply(&self, other: &Polynomial) -> Polynomial {
if self.is_zero() || other.is_zero() {
return Polynomial::zero();
}
let mut terms = Vec::new();
for term1 in &self.terms {
for term2 in &other.terms {
terms.push(term1.multiply(term2));
}
}
Polynomial::new(terms)
}
pub fn divide(&self, divisor: &Polynomial) -> Result<(Polynomial, Polynomial), ComputeError> {
if divisor.is_zero() {
return Err(ComputeError::DivisionByZero);
}
if self.is_zero() {
return Ok((Polynomial::zero(), Polynomial::zero()));
}
if divisor.is_constant() {
let divisor_const = &divisor.terms[0].coefficient;
let mut quotient_terms = Vec::new();
for term in &self.terms {
quotient_terms.push(PolynomialTerm {
coefficient: term.coefficient.clone() / divisor_const.clone(),
variables: term.variables.clone(),
});
}
return Ok((Polynomial::new(quotient_terms), Polynomial::zero()));
}
let mut dividend = self.clone();
let mut quotient = Polynomial::zero();
while !dividend.is_zero() && dividend.degree() >= divisor.degree() {
let dividend_leading = ÷nd.terms[0];
let divisor_leading = &divisor.terms[0];
let quotient_term = dividend_leading.divide(divisor_leading)?;
quotient.terms.push(quotient_term.clone());
let subtrahend = Polynomial::new(vec![quotient_term]).multiply(divisor);
dividend = dividend.subtract(&subtrahend);
}
quotient.simplify();
dividend.simplify();
Ok((quotient, dividend))
}
pub fn gcd(&self, other: &Polynomial) -> Result<Polynomial, ComputeError> {
if self.is_zero() {
return Ok(other.clone());
}
if other.is_zero() {
return Ok(self.clone());
}
let mut a = self.clone();
let mut b = other.clone();
while !b.is_zero() {
let (_, remainder) = a.divide(&b)?;
a = b;
b = remainder;
}
if let Some(leading_term) = a.terms.first_mut() {
if leading_term.coefficient.is_negative() {
for term in &mut a.terms {
term.coefficient = -term.coefficient.clone();
}
}
}
if !a.terms.is_empty() {
let mut gcd_coeff = a.terms[0].coefficient.clone();
for term in &a.terms[1..] {
gcd_coeff = Polynomial::gcd_numbers_static(&gcd_coeff, &term.coefficient);
}
if gcd_coeff != Number::one() && !gcd_coeff.is_zero() {
for term in &mut a.terms {
term.coefficient = term.coefficient.clone() / gcd_coeff.clone();
}
}
}
Ok(a)
}
pub fn to_expression(&self) -> Expression {
if self.is_zero() {
return Expression::Number(Number::zero());
}
if self.terms.len() == 1 {
return self.terms[0].to_expression();
}
let mut result = self.terms[0].to_expression();
for term in &self.terms[1..] {
let term_expr = term.to_expression();
if term.coefficient.is_negative() {
let positive_term = PolynomialTerm {
coefficient: -term.coefficient.clone(),
variables: term.variables.clone(),
};
result = Expression::subtract(result, positive_term.to_expression());
} else {
result = Expression::add(result, term_expr);
}
}
result
}
}
pub struct PolynomialEngine;
impl PolynomialEngine {
pub fn new() -> Self {
Self
}
pub fn expression_to_polynomial(&self, expr: &Expression) -> Result<Polynomial, ComputeError> {
match expr {
Expression::Number(n) => Ok(Polynomial::constant(n.clone())),
Expression::Variable(var) => Ok(Polynomial::variable(var.clone())),
Expression::BinaryOp { op, left, right } => {
let left_poly = self.expression_to_polynomial(left)?;
let right_poly = self.expression_to_polynomial(right)?;
match op {
BinaryOperator::Add => Ok(left_poly.add(&right_poly)),
BinaryOperator::Subtract => Ok(left_poly.subtract(&right_poly)),
BinaryOperator::Multiply => Ok(left_poly.multiply(&right_poly)),
BinaryOperator::Power => {
if let Expression::Number(exp) = right.as_ref() {
if let Some(exp_int) = exp.to_integer() {
if exp_int >= BigInt::zero() {
use num_traits::ToPrimitive;
if let Some(exp_u32) = exp_int.to_u32() {
return Ok(self.power(&left_poly, exp_u32));
}
}
}
}
Err(ComputeError::UnsupportedOperation {
operation: "多项式只支持正整数幂".to_string()
})
}
_ => Err(ComputeError::UnsupportedOperation {
operation: format!("多项式不支持 {:?} 运算", op)
})
}
}
Expression::UnaryOp { op, operand } => {
match op {
UnaryOperator::Negate => {
let poly = self.expression_to_polynomial(operand)?;
let mut negated_terms = Vec::new();
for term in poly.terms {
negated_terms.push(PolynomialTerm {
coefficient: -term.coefficient,
variables: term.variables,
});
}
Ok(Polynomial::new(negated_terms))
}
UnaryOperator::Plus => self.expression_to_polynomial(operand),
_ => Err(ComputeError::UnsupportedOperation {
operation: format!("多项式不支持 {:?} 运算", op)
})
}
}
_ => Err(ComputeError::UnsupportedOperation {
operation: "表达式无法转换为多项式".to_string()
})
}
}
fn power(&self, poly: &Polynomial, exponent: u32) -> Polynomial {
if exponent == 0 {
return Polynomial::constant(Number::one());
}
if exponent == 1 {
return poly.clone();
}
let half_power = self.power(poly, exponent / 2);
let result = half_power.multiply(&half_power);
if exponent % 2 == 0 {
result
} else {
result.multiply(poly)
}
}
pub fn expand(&self, expr: &Expression) -> Result<Expression, ComputeError> {
let poly = self.expression_to_polynomial(expr)?;
Ok(poly.to_expression())
}
pub fn collect(&self, expr: &Expression, var: &str) -> Result<Expression, ComputeError> {
let poly = self.expression_to_polynomial(expr)?;
let mut terms_by_power: HashMap<i32, Vec<PolynomialTerm>> = HashMap::new();
for term in poly.terms {
let power = term.degree_of(var);
terms_by_power.entry(power).or_insert_with(Vec::new).push(term);
}
let mut new_terms = Vec::new();
for (power, terms) in terms_by_power {
let mut combined_poly = Polynomial::zero();
for term in terms {
let mut coeff_variables = term.variables.clone();
coeff_variables.remove(var);
let coeff_term = PolynomialTerm {
coefficient: term.coefficient,
variables: coeff_variables,
};
combined_poly.terms.push(coeff_term);
}
combined_poly.simplify();
for mut coeff_term in combined_poly.terms {
if power != 0 {
coeff_term.variables.insert(var.to_string(), power);
}
new_terms.push(coeff_term);
}
}
let result_poly = Polynomial::new(new_terms);
Ok(result_poly.to_expression())
}
pub fn factor(&self, expr: &Expression) -> Result<Expression, ComputeError> {
let poly = self.expression_to_polynomial(expr)?;
if poly.is_zero() || poly.terms.len() <= 1 {
return Ok(expr.clone());
}
let mut gcd_coeff = poly.terms[0].coefficient.clone();
for term in &poly.terms[1..] {
gcd_coeff = self.gcd_numbers(&gcd_coeff, &term.coefficient);
}
let mut common_variables = poly.terms[0].variables.clone();
for term in &poly.terms[1..] {
let mut new_common = HashMap::new();
for (var, power) in &common_variables {
if let Some(term_power) = term.variables.get(var) {
let min_power = (*power).min(*term_power);
if min_power > 0 {
new_common.insert(var.clone(), min_power);
}
}
}
common_variables = new_common;
}
if gcd_coeff == Number::one() && common_variables.is_empty() {
return Ok(expr.clone());
}
let common_factor = PolynomialTerm {
coefficient: gcd_coeff.clone(),
variables: common_variables.clone(),
};
let mut factored_terms = Vec::new();
for term in &poly.terms {
let mut new_variables = term.variables.clone();
for (var, common_power) in &common_variables {
let current_power = new_variables.get(var).copied().unwrap_or(0);
let new_power = current_power - common_power;
if new_power == 0 {
new_variables.remove(var);
} else {
new_variables.insert(var.clone(), new_power);
}
}
factored_terms.push(PolynomialTerm {
coefficient: term.coefficient.clone() / gcd_coeff.clone(),
variables: new_variables,
});
}
let factored_poly = Polynomial::new(factored_terms);
let common_factor_expr = common_factor.to_expression();
let factored_expr = factored_poly.to_expression();
Ok(Expression::multiply(common_factor_expr, factored_expr))
}
fn gcd_numbers(&self, a: &Number, b: &Number) -> Number {
match (a, b) {
(Number::Integer(a_int), Number::Integer(b_int)) => {
use num_integer::Integer;
Number::Integer(a_int.gcd(b_int))
}
(Number::Rational(a_rat), Number::Rational(b_rat)) => {
use num_integer::Integer;
let a_num = a_rat.numer();
let a_den = a_rat.denom();
let b_num = b_rat.numer();
let b_den = b_rat.denom();
let gcd_num = a_num.gcd(b_num);
let lcm_den = a_den.lcm(b_den);
Number::Rational(BigRational::new(gcd_num, lcm_den))
}
_ => Number::one(), }
}
pub fn polynomial_divide(&self, dividend: &Expression, divisor: &Expression) -> Result<(Expression, Expression), ComputeError> {
let dividend_poly = self.expression_to_polynomial(dividend)?;
let divisor_poly = self.expression_to_polynomial(divisor)?;
let (quotient, remainder) = dividend_poly.divide(&divisor_poly)?;
Ok((quotient.to_expression(), remainder.to_expression()))
}
pub fn polynomial_gcd(&self, a: &Expression, b: &Expression) -> Result<Expression, ComputeError> {
let poly_a = self.expression_to_polynomial(a)?;
let poly_b = self.expression_to_polynomial(b)?;
let gcd_poly = poly_a.gcd(&poly_b)?;
Ok(gcd_poly.to_expression())
}
}
impl Default for PolynomialEngine {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
#[path = "polynomial_tests.rs"]
mod polynomial_tests;