use serde::{Deserialize, Serialize};
use crate::TyVar;
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum TyNat {
Lit(u64),
Var(TyVar),
Add(Box<TyNat>, Box<TyNat>),
Mul(Box<TyNat>, Box<TyNat>),
}
impl TyNat {
#[must_use]
pub fn lit(n: u64) -> Self {
Self::Lit(n)
}
#[must_use]
pub fn zero() -> Self {
Self::Lit(0)
}
#[must_use]
pub fn one() -> Self {
Self::Lit(1)
}
#[must_use]
pub fn add(a: TyNat, b: TyNat) -> Self {
match (&a, &b) {
(TyNat::Lit(x), TyNat::Lit(y)) => TyNat::Lit(x + y),
_ => Self::Add(Box::new(a), Box::new(b)),
}
}
#[must_use]
pub fn mul(a: TyNat, b: TyNat) -> Self {
match (&a, &b) {
(TyNat::Lit(x), TyNat::Lit(y)) => TyNat::Lit(x * y),
_ => Self::Mul(Box::new(a), Box::new(b)),
}
}
#[must_use]
pub fn is_lit(&self) -> bool {
matches!(self, Self::Lit(_))
}
#[must_use]
pub fn as_lit(&self) -> Option<u64> {
match self {
Self::Lit(n) => Some(*n),
_ => None,
}
}
#[must_use]
pub fn is_ground(&self) -> bool {
match self {
Self::Lit(_) => true,
Self::Var(_) => false,
Self::Add(a, b) | Self::Mul(a, b) => a.is_ground() && b.is_ground(),
}
}
#[must_use]
pub fn eval(&self) -> Option<u64> {
match self {
Self::Lit(n) => Some(*n),
Self::Var(_) => None,
Self::Add(a, b) => Some(a.eval()? + b.eval()?),
Self::Mul(a, b) => Some(a.eval()? * b.eval()?),
}
}
#[must_use]
pub fn free_vars(&self) -> Vec<TyVar> {
let mut vars = Vec::new();
self.collect_free_vars(&mut vars);
vars
}
fn collect_free_vars(&self, vars: &mut Vec<TyVar>) {
match self {
Self::Lit(_) => {}
Self::Var(v) => {
if !vars.contains(v) {
vars.push(v.clone());
}
}
Self::Add(a, b) | Self::Mul(a, b) => {
a.collect_free_vars(vars);
b.collect_free_vars(vars);
}
}
}
#[must_use]
pub fn subst(&self, var: &TyVar, replacement: &TyNat) -> Self {
match self {
Self::Lit(n) => Self::Lit(*n),
Self::Var(v) if v.id == var.id => replacement.clone(),
Self::Var(v) => Self::Var(v.clone()),
Self::Add(a, b) => Self::add(a.subst(var, replacement), b.subst(var, replacement)),
Self::Mul(a, b) => Self::mul(a.subst(var, replacement), b.subst(var, replacement)),
}
}
}
impl std::fmt::Display for TyNat {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Lit(n) => write!(f, "{n}"),
Self::Var(v) => write!(f, "n{}", v.id),
Self::Add(a, b) => write!(f, "({a} + {b})"),
Self::Mul(a, b) => write!(f, "({a} * {b})"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Kind;
#[test]
fn test_literal_naturals() {
let n = TyNat::lit(42);
assert!(n.is_lit());
assert!(n.is_ground());
assert_eq!(n.as_lit(), Some(42));
assert_eq!(n.eval(), Some(42));
}
#[test]
fn test_variable_naturals() {
let v = TyVar::new(0, Kind::Nat);
let n = TyNat::Var(v);
assert!(!n.is_lit());
assert!(!n.is_ground());
assert_eq!(n.as_lit(), None);
assert_eq!(n.eval(), None);
}
#[test]
fn test_add_literals() {
let a = TyNat::lit(10);
let b = TyNat::lit(20);
let sum = TyNat::add(a, b);
assert_eq!(sum, TyNat::lit(30));
}
#[test]
fn test_add_with_variable() {
let v = TyVar::new(0, Kind::Nat);
let a = TyNat::Var(v.clone());
let b = TyNat::lit(10);
let sum = TyNat::add(a, b);
assert!(!sum.is_lit());
assert!(!sum.is_ground());
}
#[test]
fn test_mul_literals() {
let a = TyNat::lit(3);
let b = TyNat::lit(4);
let prod = TyNat::mul(a, b);
assert_eq!(prod, TyNat::lit(12));
}
#[test]
fn test_free_vars() {
let v1 = TyVar::new(0, Kind::Nat);
let v2 = TyVar::new(1, Kind::Nat);
let n = TyNat::add(TyNat::Var(v1.clone()), TyNat::Var(v2.clone()));
let vars = n.free_vars();
assert_eq!(vars.len(), 2);
assert!(vars.contains(&v1));
assert!(vars.contains(&v2));
}
#[test]
fn test_substitution() {
let v = TyVar::new(0, Kind::Nat);
let n = TyNat::add(TyNat::Var(v.clone()), TyNat::lit(5));
let result = n.subst(&v, &TyNat::lit(10));
assert_eq!(result.eval(), Some(15));
}
#[test]
fn test_display() {
assert_eq!(format!("{}", TyNat::lit(42)), "42");
let v = TyVar::new(0, Kind::Nat);
assert_eq!(format!("{}", TyNat::Var(v)), "n0");
}
}