use crate::{CnfTerm, Ordinal, OrdinalError, Result};
#[derive(Debug, Clone)]
pub struct OrdinalBuilder {
terms: Vec<CnfTerm>,
last_exponent: Option<Ordinal>,
error: Option<OrdinalError>,
}
impl Default for OrdinalBuilder {
fn default() -> Self {
Self::new()
}
}
impl OrdinalBuilder {
pub fn new() -> Self {
OrdinalBuilder {
terms: Vec::new(),
last_exponent: None,
error: None,
}
}
pub fn term(mut self, exponent: Ordinal, multiplicity: u32) -> Self {
if self.error.is_some() {
return self;
}
if let Some(ref last) = self.last_exponent {
if exponent >= *last {
self.error = Some(OrdinalError::TransfiniteConstructionError);
return self;
}
}
let term = if exponent == Ordinal::zero() {
if multiplicity == 0 {
self.error = Some(OrdinalError::CnfTermConstructionError);
return self;
}
CnfTerm::new_finite(multiplicity)
} else {
match CnfTerm::new(&exponent, multiplicity) {
Ok(t) => t,
Err(e) => {
self.error = Some(e);
return self;
}
}
};
self.last_exponent = Some(exponent);
self.terms.push(term);
self
}
pub fn omega(self) -> Self {
self.term(Ordinal::one(), 1)
}
pub fn omega_times(self, multiplicity: u32) -> Self {
self.term(Ordinal::one(), multiplicity)
}
pub fn omega_power(self, exponent: u32) -> Self {
self.term(Ordinal::new_finite(exponent), 1)
}
pub fn omega_power_times(self, exponent: u32, multiplicity: u32) -> Self {
self.term(Ordinal::new_finite(exponent), multiplicity)
}
pub fn omega_exp(self, exponent: Ordinal) -> Self {
self.term(exponent, 1)
}
pub fn omega_exp_times(self, exponent: Ordinal, multiplicity: u32) -> Self {
self.term(exponent, multiplicity)
}
pub fn plus(self, n: u32) -> Self {
self.term(Ordinal::zero(), n)
}
pub fn build(self) -> Result<Ordinal> {
if let Some(e) = self.error {
return Err(e);
}
if self.terms.is_empty() {
return Ok(Ordinal::zero());
}
if self.terms.len() == 1 && self.terms[0].is_finite() {
return Ok(Ordinal::new_finite(self.terms[0].multiplicity()));
}
Ok(Ordinal::transfinite_unchecked(self.terms))
}
pub fn build_unchecked(self) -> Ordinal {
self.build().expect("OrdinalBuilder: invalid construction")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty_builder_returns_zero() {
assert_eq!(OrdinalBuilder::new().build().unwrap(), Ordinal::zero());
}
#[test]
fn test_single_omega() {
assert_eq!(
OrdinalBuilder::new().omega().build().unwrap(),
Ordinal::omega()
);
}
#[test]
fn test_omega_squared() {
let result = OrdinalBuilder::new().omega_power(2).build().unwrap();
assert!(result.is_transfinite());
}
#[test]
fn test_finite_only() {
assert_eq!(
OrdinalBuilder::new().plus(42).build().unwrap(),
Ordinal::new_finite(42)
);
}
#[test]
fn test_compound_ordinal() {
let result = OrdinalBuilder::new()
.omega_power(2)
.omega_times(3)
.plus(7)
.build()
.unwrap();
let expected = Ordinal::new_transfinite(&[
CnfTerm::new(&Ordinal::new_finite(2), 1).unwrap(),
CnfTerm::new(&Ordinal::one(), 3).unwrap(),
CnfTerm::new_finite(7),
])
.unwrap();
assert_eq!(result, expected);
}
#[test]
fn test_rejects_wrong_order() {
assert!(OrdinalBuilder::new()
.omega()
.omega_power(2)
.build()
.is_err());
}
#[test]
fn test_rejects_zero_multiplicity() {
assert!(OrdinalBuilder::new().omega_times(0).build().is_err());
}
#[test]
#[should_panic]
fn test_build_unchecked_panics() {
OrdinalBuilder::new()
.omega()
.omega_power(2)
.build_unchecked();
}
}