use super::monomial_order::{MonomialOrder, MonomialOrdering};
use crate::core::{Expression, Number, Symbol};
pub fn s_polynomial(
f: &Expression,
g: &Expression,
variables: &[Symbol],
order: &MonomialOrder,
) -> Expression {
if f.is_zero() || g.is_zero() {
return Expression::integer(0);
}
let lt_f = order.leading_monomial(f, variables);
let lt_g = order.leading_monomial(g, variables);
let lc_f = order.leading_coefficient(f, variables);
let lc_g = order.leading_coefficient(g, variables);
let lcm_lt = monomial_lcm(<_f, <_g, variables);
let coeff_f = Expression::div(lcm_lt.clone(), Expression::mul(vec![lc_f, lt_f]));
let coeff_g = Expression::div(lcm_lt, Expression::mul(vec![lc_g, lt_g]));
let term1 = Expression::mul(vec![coeff_f, f.clone()]);
let term2 = Expression::mul(vec![coeff_g, g.clone()]);
term1 - term2
}
fn monomial_lcm(mono1: &Expression, mono2: &Expression, variables: &[Symbol]) -> Expression {
let exp1 = extract_exponents(mono1, variables);
let exp2 = extract_exponents(mono2, variables);
let mut lcm_factors = Vec::new();
for (i, (e1, e2)) in exp1.iter().zip(exp2.iter()).enumerate() {
let max_exp = (*e1).max(*e2);
if max_exp > 0 {
let var_expr = Expression::symbol(variables[i].clone());
if max_exp == 1 {
lcm_factors.push(var_expr);
} else {
lcm_factors.push(Expression::pow(var_expr, Expression::integer(max_exp)));
}
}
}
if lcm_factors.is_empty() {
Expression::integer(1)
} else if lcm_factors.len() == 1 {
lcm_factors[0].clone()
} else {
Expression::mul(lcm_factors)
}
}
fn extract_exponents(mono: &Expression, variables: &[Symbol]) -> Vec<i64> {
let mut exponents = vec![0i64; variables.len()];
match mono {
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() {
if !matches!(factor, Expression::Number(_)) {
let factor_exp = extract_exponents(factor, variables);
for (i, e) in factor_exp.iter().enumerate() {
exponents[i] += e;
}
}
}
}
_ => {}
}
exponents
}
#[cfg(test)]
mod tests {
use super::*;
use crate::symbol;
#[test]
fn test_monomial_lcm_simple() {
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::symbol(y.clone());
let lcm = monomial_lcm(&mono1, &mono2, &vars);
let expected = Expression::mul(vec![
Expression::pow(Expression::symbol(x), Expression::integer(2)),
Expression::symbol(y),
]);
assert_eq!(lcm, expected);
}
#[test]
fn test_monomial_lcm_overlap() {
let x = symbol!(x);
let y = symbol!(y);
let vars = vec![x.clone(), y.clone()];
let mono1 = Expression::mul(vec![
Expression::pow(Expression::symbol(x.clone()), Expression::integer(2)),
Expression::symbol(y.clone()),
]);
let mono2 = Expression::mul(vec![
Expression::symbol(x.clone()),
Expression::pow(Expression::symbol(y.clone()), Expression::integer(3)),
]);
let lcm = monomial_lcm(&mono1, &mono2, &vars);
let expected = Expression::mul(vec![
Expression::pow(Expression::symbol(x), Expression::integer(2)),
Expression::pow(Expression::symbol(y), Expression::integer(3)),
]);
assert_eq!(lcm, expected);
}
#[test]
fn test_s_polynomial_basic() {
let x = symbol!(x);
let y = symbol!(y);
let vars = vec![x.clone(), y.clone()];
let f = Expression::add(vec![
Expression::pow(Expression::symbol(x.clone()), Expression::integer(2)),
Expression::symbol(y.clone()),
]);
let g = Expression::add(vec![
Expression::mul(vec![
Expression::symbol(x.clone()),
Expression::symbol(y.clone()),
]),
Expression::integer(1),
]);
let s = s_polynomial(&f, &g, &vars, &MonomialOrder::Lex);
assert!(!s.is_zero());
}
#[test]
fn test_s_polynomial_with_zero() {
let x = symbol!(x);
let vars = vec![x.clone()];
let f = Expression::symbol(x.clone());
let g = Expression::integer(0);
let s = s_polynomial(&f, &g, &vars, &MonomialOrder::Lex);
assert!(s.is_zero());
}
#[test]
fn test_s_polynomial_identical() {
let x = symbol!(x);
let vars = vec![x.clone()];
let f = Expression::pow(Expression::symbol(x.clone()), Expression::integer(2));
let g = f.clone();
let s = s_polynomial(&f, &g, &vars, &MonomialOrder::Lex);
assert!(s.is_zero());
}
}