use crate::core::expression::Expression;
use crate::core::symbol::Symbol;
use crate::simplify::Simplify;
#[derive(Debug, Clone)]
pub struct CharacteristicPolynomial {
pub coefficients: Vec<Expression>,
pub variable: Symbol,
}
impl CharacteristicPolynomial {
pub fn new(coefficients: Vec<Expression>, variable: Symbol) -> Self {
Self {
coefficients,
variable,
}
}
pub fn degree(&self) -> usize {
if self.coefficients.is_empty() {
0
} else {
self.coefficients.len() - 1
}
}
pub fn to_expression(&self) -> Expression {
if self.coefficients.is_empty() {
return Expression::integer(0);
}
let mut terms = Vec::new();
for (power, coeff) in self.coefficients.iter().enumerate() {
if coeff.is_zero() {
continue;
}
let term = if power == 0 {
coeff.clone()
} else if power == 1 {
Expression::mul(vec![
coeff.clone(),
Expression::symbol(self.variable.clone()),
])
} else {
Expression::mul(vec![
coeff.clone(),
Expression::pow(
Expression::symbol(self.variable.clone()),
Expression::integer(power as i64),
),
])
};
terms.push(term);
}
if terms.is_empty() {
Expression::integer(0)
} else if terms.len() == 1 {
terms[0].clone()
} else {
Expression::add(terms).simplify()
}
}
pub fn evaluate(&self, value: &Expression) -> Expression {
if self.coefficients.is_empty() {
return Expression::integer(0);
}
let mut result = self.coefficients.last().unwrap().clone();
for coeff in self.coefficients.iter().rev().skip(1) {
result = Expression::add(vec![
coeff.clone(),
Expression::mul(vec![value.clone(), result]),
])
.simplify();
}
result
}
pub fn add(&self, other: &CharacteristicPolynomial) -> CharacteristicPolynomial {
let max_len = self.coefficients.len().max(other.coefficients.len());
let mut coefficients = vec![Expression::integer(0); max_len];
for (i, coeff) in coefficients.iter_mut().enumerate().take(max_len) {
let coeff1 = if i < self.coefficients.len() {
self.coefficients[i].clone()
} else {
Expression::integer(0)
};
let coeff2 = if i < other.coefficients.len() {
other.coefficients[i].clone()
} else {
Expression::integer(0)
};
*coeff = Expression::add(vec![coeff1, coeff2]).simplify();
}
CharacteristicPolynomial {
coefficients,
variable: self.variable.clone(),
}
}
pub fn multiply(&self, other: &CharacteristicPolynomial) -> CharacteristicPolynomial {
if self.coefficients.is_empty() || other.coefficients.is_empty() {
return CharacteristicPolynomial::new(vec![], self.variable.clone());
}
let result_len = self.coefficients.len() + other.coefficients.len() - 1;
let mut result_coeffs = vec![Expression::integer(0); result_len];
for (i, c1) in self.coefficients.iter().enumerate() {
for (j, c2) in other.coefficients.iter().enumerate() {
let product = Expression::mul(vec![c1.clone(), c2.clone()]).simplify();
result_coeffs[i + j] =
Expression::add(vec![result_coeffs[i + j].clone(), product]).simplify();
}
}
CharacteristicPolynomial::new(result_coeffs, self.variable.clone())
}
pub fn format(&self) -> String {
if self.coefficients.is_empty() {
return "0".to_owned();
}
let mut parts = Vec::new();
for (power, coeff) in self.coefficients.iter().enumerate() {
if coeff.is_zero() {
continue;
}
let mut part = String::new();
if !coeff.is_one() || power == 0 {
part.push_str(&coeff.to_string());
}
if power > 0 {
if !coeff.is_one() {
part.push('·');
}
part.push_str(self.variable.name.as_ref());
if power > 1 {
part.push('^');
part.push_str(&power.to_string());
}
}
parts.push(part);
}
if parts.is_empty() {
"0".to_owned()
} else {
parts.join(" + ")
}
}
}
pub struct CharacteristicPolynomialBuilder;
impl CharacteristicPolynomialBuilder {
pub fn add(
&self,
poly1: &CharacteristicPolynomial,
poly2: &CharacteristicPolynomial,
) -> CharacteristicPolynomial {
let max_len = poly1.coefficients.len().max(poly2.coefficients.len());
let mut coefficients = vec![Expression::integer(0); max_len];
for (i, coeff) in coefficients.iter_mut().enumerate().take(max_len) {
let coeff1 = if i < poly1.coefficients.len() {
poly1.coefficients[i].clone()
} else {
Expression::integer(0)
};
let coeff2 = if i < poly2.coefficients.len() {
poly2.coefficients[i].clone()
} else {
Expression::integer(0)
};
*coeff = Expression::add(vec![coeff1, coeff2]).simplify();
}
CharacteristicPolynomial {
coefficients,
variable: poly1.variable.clone(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{expr, symbol};
#[test]
fn test_new_polynomial() {
let lambda = symbol!(lambda);
let poly =
CharacteristicPolynomial::new(vec![expr!(1), expr!(2), expr!(3)], lambda.clone());
assert_eq!(poly.coefficients.len(), 3);
assert_eq!(poly.degree(), 2);
}
#[test]
fn test_polynomial_degree() {
let lambda = symbol!(lambda);
let poly1 = CharacteristicPolynomial::new(vec![expr!(1)], lambda.clone());
assert_eq!(poly1.degree(), 0);
let poly2 = CharacteristicPolynomial::new(vec![expr!(1), expr!(2)], lambda.clone());
assert_eq!(poly2.degree(), 1);
let poly3 =
CharacteristicPolynomial::new(vec![expr!(1), expr!(2), expr!(3)], lambda.clone());
assert_eq!(poly3.degree(), 2);
let poly_empty = CharacteristicPolynomial::new(vec![], lambda);
assert_eq!(poly_empty.degree(), 0);
}
#[test]
fn test_polynomial_to_expression() {
let lambda = symbol!(lambda);
let poly = CharacteristicPolynomial::new(vec![expr!(6), expr!(-5), expr!(1)], lambda);
let expr = poly.to_expression();
assert!(!expr.is_zero());
}
#[test]
fn test_polynomial_evaluate() {
let lambda = symbol!(lambda);
let poly = CharacteristicPolynomial::new(vec![expr!(6), expr!(-5), expr!(1)], lambda);
let result = poly.evaluate(&expr!(2));
assert_eq!(result.simplify(), expr!(0));
let result = poly.evaluate(&expr!(3));
assert_eq!(result.simplify(), expr!(0));
}
#[test]
fn test_polynomial_addition() {
let lambda = symbol!(lambda);
let poly1 = CharacteristicPolynomial::new(vec![expr!(1), expr!(2)], lambda.clone());
let poly2 = CharacteristicPolynomial::new(vec![expr!(3), expr!(4)], lambda.clone());
let sum = poly1.add(&poly2);
assert_eq!(sum.coefficients.len(), 2);
assert_eq!(sum.coefficients[0].simplify(), expr!(4));
assert_eq!(sum.coefficients[1].simplify(), expr!(6));
}
#[test]
fn test_polynomial_addition_different_lengths() {
let lambda = symbol!(lambda);
let poly1 =
CharacteristicPolynomial::new(vec![expr!(1), expr!(2), expr!(3)], lambda.clone());
let poly2 = CharacteristicPolynomial::new(vec![expr!(4), expr!(5)], lambda.clone());
let sum = poly1.add(&poly2);
assert_eq!(sum.coefficients.len(), 3);
assert_eq!(sum.coefficients[0].simplify(), expr!(5));
assert_eq!(sum.coefficients[1].simplify(), expr!(7));
assert_eq!(sum.coefficients[2].simplify(), expr!(3));
}
#[test]
fn test_polynomial_multiplication() {
let lambda = symbol!(lambda);
let poly1 = CharacteristicPolynomial::new(vec![expr!(1), expr!(1)], lambda.clone());
let poly2 = CharacteristicPolynomial::new(vec![expr!(2), expr!(1)], lambda.clone());
let product = poly1.multiply(&poly2);
assert_eq!(product.degree(), 2);
assert_eq!(product.coefficients[0].simplify(), expr!(2));
assert_eq!(product.coefficients[1].simplify(), expr!(3));
assert_eq!(product.coefficients[2].simplify(), expr!(1));
}
#[test]
fn test_polynomial_format() {
let lambda = symbol!(lambda);
let poly = CharacteristicPolynomial::new(vec![expr!(6), expr!(-5), expr!(1)], lambda);
let formatted = poly.format();
assert!(formatted.contains(poly.variable.name.as_ref()));
}
#[test]
fn test_builder_add() {
let lambda = symbol!(lambda);
let builder = CharacteristicPolynomialBuilder;
let poly1 = CharacteristicPolynomial::new(vec![expr!(1), expr!(2)], lambda.clone());
let poly2 = CharacteristicPolynomial::new(vec![expr!(3), expr!(4)], lambda.clone());
let sum = builder.add(&poly1, &poly2);
assert_eq!(sum.coefficients.len(), 2);
assert_eq!(sum.coefficients[0].simplify(), expr!(4));
assert_eq!(sum.coefficients[1].simplify(), expr!(6));
}
}