use crate::error::{OrdinalError, Result};
use crate::ordinal::Ordinal;
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct CnfTerm {
exponent: Ordinal,
multiplicity: u32,
}
impl CnfTerm {
pub fn new(exponent: &Ordinal, multiplicity: u32) -> Result<Self> {
if multiplicity == 0 {
return Err(OrdinalError::CnfTermConstructionError);
}
Ok(CnfTerm {
exponent: exponent.clone(),
multiplicity,
})
}
pub fn new_finite(n: u32) -> Self {
assert!(n > 0, "CnfTerm multiplicity must be positive, got 0");
CnfTerm {
exponent: Ordinal::Finite(0),
multiplicity: n,
}
}
pub fn exponent(&self) -> Ordinal {
self.exponent.clone()
}
pub fn exponent_ref(&self) -> &Ordinal {
&self.exponent
}
pub fn multiplicity(&self) -> u32 {
self.multiplicity
}
pub(crate) fn from_parts(exponent: Ordinal, multiplicity: u32) -> Result<Self> {
if multiplicity == 0 {
return Err(OrdinalError::CnfTermConstructionError);
}
Ok(CnfTerm {
exponent,
multiplicity,
})
}
pub fn into_parts(self) -> (Ordinal, u32) {
(self.exponent, self.multiplicity)
}
pub fn is_limit(&self) -> bool {
!self.is_finite()
}
pub fn is_successor(&self) -> bool {
self.is_finite()
}
pub fn is_finite(&self) -> bool {
matches!(self.exponent, Ordinal::Finite(0))
}
}
impl std::fmt::Display for CnfTerm {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let exponent = self.exponent_ref();
if self.is_finite() {
return write!(f, "{}", self.multiplicity);
}
write!(f, "ω")?;
if !matches!(exponent, Ordinal::Finite(1)) {
write!(f, "^{exponent}")?;
}
if self.multiplicity != 1 {
write!(f, " * {}", self.multiplicity)?;
}
Ok(())
}
}
impl PartialOrd for CnfTerm {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for CnfTerm {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.exponent_ref()
.cmp(other.exponent_ref())
.then_with(|| self.multiplicity.cmp(&other.multiplicity))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_cnf_term() {
let one = Ordinal::one();
let cnf_term = CnfTerm::new(&one, 1).unwrap();
assert_eq!(cnf_term.exponent(), one);
assert_eq!(cnf_term.multiplicity(), 1);
}
#[test]
fn test_cnf_term_is_limit() {
let one = Ordinal::one();
let cnf_term = CnfTerm::new(&one, 1).unwrap();
assert!(cnf_term.is_limit());
}
#[test]
fn test_cnf_term_is_successor() {
let one = Ordinal::one();
let cnf_term = CnfTerm::new(&one, 1).unwrap();
assert!(!cnf_term.is_successor());
}
#[test]
fn test_cnf_term_is_finite() {
let finite = Ordinal::new_finite(0);
let cnf_term = CnfTerm::new(&finite, 1).unwrap();
assert!(cnf_term.is_finite());
}
#[test]
fn test_cnf_term_display() {
let one = Ordinal::one();
let cnf_term = CnfTerm::new(&one, 1).unwrap();
assert_eq!(format!("{}", cnf_term), "ω");
}
#[test]
fn test_cnf_term_display_with_multiplicity() {
let one = Ordinal::one();
let cnf_term = CnfTerm::new(&one, 2).unwrap();
assert_eq!(format!("{}", cnf_term), "ω * 2");
}
#[test]
fn test_cnf_term_display_with_exponent() {
let two = Ordinal::new_finite(2);
let cnf_term = CnfTerm::new(&two, 1).unwrap();
assert_eq!(format!("{}", cnf_term), "ω^2");
}
#[test]
fn test_partial_eq() {
let one = Ordinal::one();
let cnf_term1 = CnfTerm::new(&one, 1).unwrap();
let cnf_term2 = CnfTerm::new(&one, 1).unwrap();
assert_eq!(cnf_term1, cnf_term2);
}
#[test]
fn test_partial_eq_different_exponents() {
let one = Ordinal::one();
let two = Ordinal::new_finite(2);
let cnf_term1 = CnfTerm::new(&one, 1).unwrap();
let cnf_term2 = CnfTerm::new(&two, 1).unwrap();
assert_ne!(cnf_term1, cnf_term2);
}
#[test]
fn test_partial_eq_different_multiplicities() {
let one = Ordinal::one();
let cnf_term1 = CnfTerm::new(&one, 1).unwrap();
let cnf_term2 = CnfTerm::new(&one, 2).unwrap();
assert_ne!(cnf_term1, cnf_term2);
}
#[test]
fn test_ord_same_exponent() {
let one = Ordinal::one();
let cnf_term1 = CnfTerm::new(&one, 1).unwrap();
let cnf_term2 = CnfTerm::new(&one, 2).unwrap();
assert!(cnf_term1 < cnf_term2);
}
#[test]
fn test_ord_different_exponents() {
let one = Ordinal::one();
let two = Ordinal::new_finite(2);
let cnf_term1 = CnfTerm::new(&one, 1).unwrap();
let cnf_term2 = CnfTerm::new(&two, 1).unwrap();
assert!(cnf_term1 < cnf_term2);
}
#[test]
fn test_partial_ord_same_exponent() {
let one = Ordinal::one();
let cnf_term1 = CnfTerm::new(&one, 1).unwrap();
let cnf_term2 = CnfTerm::new(&one, 2).unwrap();
assert!(cnf_term1.partial_cmp(&cnf_term2).unwrap() == std::cmp::Ordering::Less);
}
#[test]
fn test_partial_ord_different_exponents() {
let one = Ordinal::one();
let two = Ordinal::new_finite(2);
let cnf_term1 = CnfTerm::new(&one, 1).unwrap();
let cnf_term2 = CnfTerm::new(&two, 1).unwrap();
assert!(cnf_term1.partial_cmp(&cnf_term2).unwrap() == std::cmp::Ordering::Less);
}
#[test]
fn test_nested_transfinite_exponent() {
let omega = Ordinal::omega();
let term = CnfTerm::new(&omega, 1).unwrap();
assert!(term.is_limit());
assert!(!term.is_finite());
assert_eq!(term.exponent(), omega);
}
#[test]
fn test_deeply_nested_exponent_display() {
let omega_squared = Ordinal::builder().omega_power(2).build().unwrap();
let term = CnfTerm::new(&omega_squared, 1).unwrap();
assert_eq!(format!("{}", term), "ω^ω^2");
}
#[test]
fn test_exponent_ref_avoids_clone() {
let omega = Ordinal::omega();
let term = CnfTerm::new(&omega, 5).unwrap();
assert_eq!(term.exponent_ref(), &omega);
assert_eq!(*term.exponent_ref(), term.exponent());
}
#[test]
fn test_large_multiplicity() {
let one = Ordinal::one();
let term = CnfTerm::new(&one, u32::MAX).unwrap();
assert_eq!(term.multiplicity(), u32::MAX);
}
}