use crate::core::{Expression, Number, Symbol};
use std::cmp::Ordering;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MonomialOrder {
Lex,
Grlex,
Grevlex,
}
pub trait MonomialOrdering {
fn compare_monomials(
&self,
mono1: &Expression,
mono2: &Expression,
variables: &[Symbol],
) -> Ordering;
fn leading_monomial(&self, poly: &Expression, variables: &[Symbol]) -> Expression;
fn leading_coefficient(&self, poly: &Expression, variables: &[Symbol]) -> Expression;
}
impl MonomialOrdering for MonomialOrder {
fn compare_monomials(
&self,
mono1: &Expression,
mono2: &Expression,
variables: &[Symbol],
) -> Ordering {
let exp1 = extract_exponents(mono1, variables);
let exp2 = extract_exponents(mono2, variables);
match self {
MonomialOrder::Lex => compare_lex(&exp1, &exp2),
MonomialOrder::Grlex => compare_grlex(&exp1, &exp2),
MonomialOrder::Grevlex => compare_grevlex(&exp1, &exp2),
}
}
fn leading_monomial(&self, poly: &Expression, variables: &[Symbol]) -> Expression {
match poly {
Expression::Add(terms) => {
let mut leading = terms[0].clone();
for term in &terms[1..] {
if self.compare_monomials(term, &leading, variables) == Ordering::Greater {
leading = term.clone();
}
}
extract_monomial_part(&leading)
}
_ => extract_monomial_part(poly),
}
}
fn leading_coefficient(&self, poly: &Expression, variables: &[Symbol]) -> Expression {
match poly {
Expression::Add(terms) => {
let mut leading = terms[0].clone();
for term in &terms[1..] {
if self.compare_monomials(term, &leading, variables) == Ordering::Greater {
leading = term.clone();
}
}
extract_coefficient(&leading)
}
_ => extract_coefficient(poly),
}
}
}
fn extract_exponents(mono: &Expression, variables: &[Symbol]) -> Vec<i64> {
let mut exponents = vec![0i64; variables.len()];
let mono_part = extract_monomial_part(mono);
match mono_part {
Expression::Symbol(s) => {
if let Some(idx) = variables.iter().position(|v| v == &s) {
exponents[idx] = 1;
}
}
Expression::Pow(base, exp) => {
if let Expression::Symbol(s) = base.as_ref() {
if let Some(idx) = variables.iter().position(|v| v == s) {
if let Expression::Number(Number::Integer(i)) = exp.as_ref() {
exponents[idx] = *i;
}
}
}
}
Expression::Mul(factors) => {
for factor in factors.iter() {
let factor_exp = extract_exponents(factor, variables);
for (i, e) in factor_exp.iter().enumerate() {
exponents[i] += e;
}
}
}
_ => {}
}
exponents
}
fn extract_monomial_part(term: &Expression) -> Expression {
match term {
Expression::Mul(factors) => {
let mut mono_factors = Vec::new();
for factor in factors.iter() {
if !is_numeric(factor) {
mono_factors.push(factor.clone());
}
}
if mono_factors.is_empty() {
Expression::integer(1)
} else if mono_factors.len() == 1 {
mono_factors[0].clone()
} else {
Expression::mul(mono_factors)
}
}
_ => {
if is_numeric(term) {
Expression::integer(1)
} else {
term.clone()
}
}
}
}
fn extract_coefficient(term: &Expression) -> Expression {
match term {
Expression::Mul(factors) => {
let mut coeffs = Vec::new();
for factor in factors.iter() {
if is_numeric(factor) {
coeffs.push(factor.clone());
}
}
if coeffs.is_empty() {
Expression::integer(1)
} else if coeffs.len() == 1 {
coeffs[0].clone()
} else {
Expression::mul(coeffs)
}
}
Expression::Number(_) => term.clone(),
_ => Expression::integer(1),
}
}
fn is_numeric(expr: &Expression) -> bool {
matches!(expr, Expression::Number(_))
}
fn compare_lex(exp1: &[i64], exp2: &[i64]) -> Ordering {
for (e1, e2) in exp1.iter().zip(exp2.iter()) {
match e1.cmp(e2) {
Ordering::Equal => continue,
other => return other,
}
}
Ordering::Equal
}
fn compare_grlex(exp1: &[i64], exp2: &[i64]) -> Ordering {
let deg1: i64 = exp1.iter().sum();
let deg2: i64 = exp2.iter().sum();
match deg1.cmp(°2) {
Ordering::Equal => compare_lex(exp1, exp2),
other => other,
}
}
fn compare_grevlex(exp1: &[i64], exp2: &[i64]) -> Ordering {
let deg1: i64 = exp1.iter().sum();
let deg2: i64 = exp2.iter().sum();
match deg1.cmp(°2) {
Ordering::Equal => {
for (e1, e2) in exp1.iter().zip(exp2.iter()).rev() {
match e2.cmp(e1) {
Ordering::Equal => continue,
other => return other,
}
}
Ordering::Equal
}
other => other,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::symbol;
#[test]
fn test_extract_exponents_simple() {
let x = symbol!(x);
let y = symbol!(y);
let vars = vec![x.clone(), y.clone()];
let mono = Expression::mul(vec![
Expression::pow(Expression::symbol(x.clone()), Expression::integer(2)),
Expression::symbol(y.clone()),
]);
let exp = extract_exponents(&mono, &vars);
assert_eq!(exp, vec![2, 1]);
}
#[test]
fn test_lex_ordering() {
let x = symbol!(x);
let y = symbol!(y);
let vars = vec![x.clone(), y.clone()];
let mono1 = Expression::pow(Expression::symbol(x.clone()), Expression::integer(2));
let mono2 = Expression::pow(Expression::symbol(y.clone()), Expression::integer(3));
let order = MonomialOrder::Lex;
let cmp = order.compare_monomials(&mono1, &mono2, &vars);
assert_eq!(cmp, Ordering::Greater);
}
#[test]
fn test_grlex_ordering() {
let x = symbol!(x);
let y = symbol!(y);
let vars = vec![x.clone(), y.clone()];
let mono1 = Expression::pow(Expression::symbol(x.clone()), Expression::integer(1));
let mono2 = Expression::pow(Expression::symbol(y.clone()), Expression::integer(2));
let order = MonomialOrder::Grlex;
let cmp = order.compare_monomials(&mono1, &mono2, &vars);
assert_eq!(cmp, Ordering::Less);
}
#[test]
fn test_grevlex_ordering() {
let x = symbol!(x);
let y = symbol!(y);
let vars = vec![x.clone(), y.clone()];
let mono1 = Expression::mul(vec![
Expression::symbol(x.clone()),
Expression::symbol(y.clone()),
]);
let mono2 = Expression::pow(Expression::symbol(x.clone()), Expression::integer(2));
let order = MonomialOrder::Grevlex;
let cmp = order.compare_monomials(&mono1, &mono2, &vars);
assert_eq!(cmp, Ordering::Less);
}
#[test]
fn test_leading_monomial() {
let x = symbol!(x);
let y = symbol!(y);
let vars = vec![x.clone(), y.clone()];
let poly = Expression::add(vec![
Expression::pow(Expression::symbol(x.clone()), Expression::integer(2)),
Expression::mul(vec![Expression::integer(3), Expression::symbol(y.clone())]),
]);
let order = MonomialOrder::Lex;
let lm = order.leading_monomial(&poly, &vars);
assert_eq!(
lm,
Expression::pow(Expression::symbol(x), Expression::integer(2))
);
}
}