diff --git a/src/poly/flat.rs b/src/poly/flat.rs index 16f7ac0..5c5df2a 100644 --- a/src/poly/flat.rs +++ b/src/poly/flat.rs @@ -1,13 +1,13 @@ use itertools::Itertools; use std::fmt::{self, Display}; -use std::ops::{Add, Sub, BitXor, Mul}; +use std::ops::{Add, BitXor, Mul, Sub}; -use crate::fmt::num_to_superscript; use super::var::{StaticVar, Var}; +use crate::fmt::num_to_superscript; use std::collections::HashMap; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct Poly { mono: HashMap, i32>, } @@ -58,25 +58,25 @@ pub struct Mono { pub term: Vec<(V, u32)>, } -impl Mono{ - pub fn contains(&self, other:&Mono)->bool{ - let mut self_it=self.term.iter().peekable(); - let mut other_it=other.term.iter().peekable(); +impl Mono { + pub fn contains(&self, other: &Mono) -> bool { + let mut self_it = self.term.iter().peekable(); + let mut other_it = other.term.iter().peekable(); - while let Some((o_term,o_exp))=other_it.peek(){ - if let Some((s_term,s_exp))=self_it.peek(){ - if s_termo_term{ + } else if s_term > o_term { return false; - }else if o_exp<=s_exp{ + } else if o_exp <= s_exp { self_it.next(); other_it.next(); - }else{ + } else { return false; } - }else{ + } else { return false; } } @@ -103,11 +103,9 @@ impl> FromIterator<(U, u32)> for Mono { term.sort(); // Check duplicate variables - assert!( - (term[..]) - .windows(2) - .all(|window| window[0].0 != window[1].0) - ); + assert!((term[..]) + .windows(2) + .all(|window| window[0].0 != window[1].0)); Mono { term } } @@ -122,7 +120,7 @@ impl Display for Mono { .iter() .map(|(t, p)| match p { 1 => format!("{t}"), - _ => format!("{t}{}",num_to_superscript(p.to_string())), + _ => format!("{t}{}", num_to_superscript(p.to_string())), }) .join("") ) @@ -223,3 +221,106 @@ impl Sub for Poly { } } +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_mono_contains() { + let a: Mono = [("x", 2), ("y", 1)].into(); + + // Lower exponent of same variable is contained + assert!(a.contains(&Mono::from([("x", 1)]))); + + // Higher exponent of same variable is not contained + assert!(!a.contains(&Mono::from([("x", 3)]))); + + // Identical monomial is contained + assert!(a.contains(&Mono::from([("x", 2), ("y", 1)]))); + + // Variable absent from self is not contained + assert!(!a.contains(&Mono::from([("x", 2), ("z", 1)]))); + + // Subset of variables with lower exponents is contained + assert!(a.contains(&Mono::from([("x", 1), ("y", 1)]))); + + // Single variable with exact exponent is contained + assert!(a.contains(&Mono::from([("x", 2)]))); + + // Insufficient exponent in self means not contained + assert!(!Mono::::from([("x", 1)]).contains(&Mono::from([("x", 2)]))); + + // Missing variable in self means not contained + assert!(!Mono::::from([("x", 1), ("y", 1)]).contains(&Mono::from([("x", 2)]))); + assert!(!Mono::::from([("x", 1)]).contains(&Mono::from([("x", 1), ("y", 1)]))); + } + + #[test] + fn test_mono_mul() { + // Same variable: exponents add + let a: Mono = [("x", 2)].into(); + let b: Mono = [("x", 3)].into(); + assert_eq!(a * b, Mono::from([("x", 5)])); + + // Disjoint variables: both appear in result + let a: Mono = [("x", 2)].into(); + let b: Mono = [("y", 3)].into(); + assert_eq!(a * b, Mono::from([("x", 2), ("y", 3)])); + + // Mixed: shared and disjoint variables + let a: Mono = [("x", 1), ("y", 2)].into(); + let b: Mono = [("y", 1), ("z", 3)].into(); + assert_eq!(a * b, Mono::from([("x", 1), ("y", 3), ("z", 3)])); + + // Commutativity + let a: Mono = [("x", 2), ("z", 1)].into(); + let b: Mono = [("y", 3)].into(); + assert_eq!(a.clone() * b.clone(), b * a); + + // Multiply by constant monomial (empty term vec = 1) + let a: Mono = [("x", 4)].into(); + let one: Mono = Mono { term: vec![] }; + assert_eq!(a.clone() * one, a); + } + + #[test] + fn test_poly_add() { + // Distinct monomials are collected as separate terms + let a: Poly = [(1, [("x", 2)]), (2, [("y", 1)])].into(); + let b: Poly = [(3, [("z", 1)])].into(); + let expected: Poly = [(1, [("x", 2)]), (2, [("y", 1)]), (3, [("z", 1)])].into(); + assert_eq!(a + b, expected); + + // Coefficients of matching monomials are summed + let a: Poly = [(2, [("x", 1)])].into(); + let b: Poly = [(3, [("x", 1)])].into(); + let expected: Poly = [(5, [("x", 1)])].into(); + assert_eq!(a + b, expected); + + // Terms that cancel sum to zero are dropped + let a: Poly = [(1, [("x", 1)])].into(); + let b: Poly = [(-1, [("x", 1)])].into(); + let expected: Poly = Poly::default(); + assert_eq!(a + b, expected); + } + + #[test] + fn test_poly_sub() { + // Distinct monomials are collected as separate terms with negated rhs coefficients + let a: Poly = [(3, [("x", 2)])].into(); + let b: Poly = [(1, [("y", 1)])].into(); + let expected: Poly = [(3, [("x", 2)]), (-1, [("y", 1)])].into(); + assert_eq!(a - b, expected); + + // Coefficients of matching monomials are subtracted + let a: Poly = [(5, [("x", 1)])].into(); + let b: Poly = [(3, [("x", 1)])].into(); + let expected: Poly = [(2, [("x", 1)])].into(); + assert_eq!(a - b, expected); + + // Subtracting equal polynomials yields zero + let a: Poly = [(4, [("x", 2)]), (1, [("y", 1)])].into(); + let b: Poly = [(4, [("x", 2)]), (1, [("y", 1)])].into(); + assert_eq!(a - b, Poly::default()); + } +}