use std::fmt;
use std::collections::HashMap;
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct LevelId(u32);
impl LevelId {
pub(crate) fn new(id: u32) -> Self {
Self(id)
}
pub fn raw(self) -> u32 {
self.0
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Level {
Zero,
Const(u32),
Param(u32),
Succ(LevelId),
Max(LevelId, LevelId),
IMax(LevelId, LevelId),
}
impl Level {
pub fn is_zero(&self) -> bool {
matches!(self, Level::Zero)
}
pub fn is_const(&self) -> bool {
matches!(self, Level::Const(_))
}
pub fn from_u32(n: u32) -> Self {
if n == 0 {
Level::Zero
} else {
Level::Const(n)
}
}
}
impl fmt::Display for Level {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Level::Zero => write!(f, "0"),
Level::Const(n) => write!(f, "{}", n),
Level::Param(n) => write!(f, "u{}", n),
Level::Succ(id) => write!(f, "(succ {})", id.0),
Level::Max(a, b) => write!(f, "(max {} {})", a.0, b.0),
Level::IMax(a, b) => write!(f, "(imax {} {})", a.0, b.0),
}
}
}
pub struct LevelArena {
levels: Vec<Level>,
cache: HashMap<Level, LevelId>,
}
impl LevelArena {
pub fn new() -> Self {
let mut arena = Self {
levels: Vec::new(),
cache: HashMap::new(),
};
arena.intern(Level::Zero);
arena
}
pub fn intern(&mut self, level: Level) -> LevelId {
if let Some(&id) = self.cache.get(&level) {
return id;
}
let id = LevelId::new(self.levels.len() as u32);
self.levels.push(level.clone());
self.cache.insert(level, id);
id
}
pub fn get(&self, id: LevelId) -> Option<&Level> {
self.levels.get(id.0 as usize)
}
pub fn zero(&mut self) -> LevelId {
self.intern(Level::Zero)
}
pub fn constant(&mut self, n: u32) -> LevelId {
self.intern(Level::from_u32(n))
}
pub fn param(&mut self, n: u32) -> LevelId {
self.intern(Level::Param(n))
}
pub fn succ(&mut self, id: LevelId) -> LevelId {
self.intern(Level::Succ(id))
}
pub fn max(&mut self, a: LevelId, b: LevelId) -> LevelId {
self.intern(Level::Max(a, b))
}
pub fn imax(&mut self, a: LevelId, b: LevelId) -> LevelId {
self.intern(Level::IMax(a, b))
}
pub fn normalize(&mut self, id: LevelId) -> LevelId {
let level = self.get(id).unwrap().clone();
match level {
Level::Succ(inner) => {
let normalized = self.normalize(inner);
if let Some(Level::Const(n)) = self.get(normalized) {
return self.constant(n + 1);
}
self.succ(normalized)
}
Level::Max(a, b) => {
let a_norm = self.normalize(a);
let b_norm = self.normalize(b);
if let (Some(Level::Const(n)), Some(Level::Const(m))) =
(self.get(a_norm), self.get(b_norm))
{
return self.constant((*n).max(*m));
}
self.max(a_norm, b_norm)
}
Level::IMax(a, b) => {
let a_norm = self.normalize(a);
let b_norm = self.normalize(b);
if let Some(Level::Zero) = self.get(b_norm) {
return self.zero();
}
if let (Some(Level::Const(n)), Some(Level::Const(m))) =
(self.get(a_norm), self.get(b_norm))
{
return self.constant((*n).max(*m));
}
self.imax(a_norm, b_norm)
}
_ => id,
}
}
}
impl Default for LevelArena {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_level_interning() {
let mut arena = LevelArena::new();
let zero1 = arena.zero();
let zero2 = arena.zero();
assert_eq!(zero1, zero2);
let one = arena.constant(1);
let succ_zero = arena.succ(zero1);
assert_ne!(one, succ_zero); }
#[test]
fn test_level_normalization() {
let mut arena = LevelArena::new();
let zero = arena.zero();
let one = arena.constant(1);
let two = arena.constant(2);
let max = arena.max(one, two);
let normalized = arena.normalize(max);
assert_eq!(normalized, two);
}
#[test]
fn test_imax_reduction() {
let mut arena = LevelArena::new();
let zero = arena.zero();
let one = arena.constant(1);
let imax = arena.imax(one, zero);
let normalized = arena.normalize(imax);
assert_eq!(normalized, zero);
}
}