1use serde::{Deserialize, Serialize};
24
25use crate::TyVar;
26
27#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
33pub enum TyNat {
34 Lit(u64),
38
39 Var(TyVar),
43
44 Add(Box<TyNat>, Box<TyNat>),
48
49 Mul(Box<TyNat>, Box<TyNat>),
53}
54
55impl TyNat {
56 #[must_use]
58 pub fn lit(n: u64) -> Self {
59 Self::Lit(n)
60 }
61
62 #[must_use]
64 pub fn zero() -> Self {
65 Self::Lit(0)
66 }
67
68 #[must_use]
70 pub fn one() -> Self {
71 Self::Lit(1)
72 }
73
74 #[must_use]
76 pub fn add(a: TyNat, b: TyNat) -> Self {
77 match (&a, &b) {
79 (TyNat::Lit(x), TyNat::Lit(y)) => TyNat::Lit(x + y),
80 _ => Self::Add(Box::new(a), Box::new(b)),
81 }
82 }
83
84 #[must_use]
86 pub fn mul(a: TyNat, b: TyNat) -> Self {
87 match (&a, &b) {
89 (TyNat::Lit(x), TyNat::Lit(y)) => TyNat::Lit(x * y),
90 _ => Self::Mul(Box::new(a), Box::new(b)),
91 }
92 }
93
94 #[must_use]
96 pub fn is_lit(&self) -> bool {
97 matches!(self, Self::Lit(_))
98 }
99
100 #[must_use]
102 pub fn as_lit(&self) -> Option<u64> {
103 match self {
104 Self::Lit(n) => Some(*n),
105 _ => None,
106 }
107 }
108
109 #[must_use]
111 pub fn is_ground(&self) -> bool {
112 match self {
113 Self::Lit(_) => true,
114 Self::Var(_) => false,
115 Self::Add(a, b) | Self::Mul(a, b) => a.is_ground() && b.is_ground(),
116 }
117 }
118
119 #[must_use]
123 pub fn eval(&self) -> Option<u64> {
124 match self {
125 Self::Lit(n) => Some(*n),
126 Self::Var(_) => None,
127 Self::Add(a, b) => Some(a.eval()? + b.eval()?),
128 Self::Mul(a, b) => Some(a.eval()? * b.eval()?),
129 }
130 }
131
132 #[must_use]
134 pub fn free_vars(&self) -> Vec<TyVar> {
135 let mut vars = Vec::new();
136 self.collect_free_vars(&mut vars);
137 vars
138 }
139
140 fn collect_free_vars(&self, vars: &mut Vec<TyVar>) {
141 match self {
142 Self::Lit(_) => {}
143 Self::Var(v) => {
144 if !vars.contains(v) {
145 vars.push(v.clone());
146 }
147 }
148 Self::Add(a, b) | Self::Mul(a, b) => {
149 a.collect_free_vars(vars);
150 b.collect_free_vars(vars);
151 }
152 }
153 }
154
155 #[must_use]
157 pub fn subst(&self, var: &TyVar, replacement: &TyNat) -> Self {
158 match self {
159 Self::Lit(n) => Self::Lit(*n),
160 Self::Var(v) if v.id == var.id => replacement.clone(),
161 Self::Var(v) => Self::Var(v.clone()),
162 Self::Add(a, b) => Self::add(a.subst(var, replacement), b.subst(var, replacement)),
163 Self::Mul(a, b) => Self::mul(a.subst(var, replacement), b.subst(var, replacement)),
164 }
165 }
166}
167
168impl std::fmt::Display for TyNat {
169 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
170 match self {
171 Self::Lit(n) => write!(f, "{n}"),
172 Self::Var(v) => write!(f, "n{}", v.id),
173 Self::Add(a, b) => write!(f, "({a} + {b})"),
174 Self::Mul(a, b) => write!(f, "({a} * {b})"),
175 }
176 }
177}
178
179#[cfg(test)]
180mod tests {
181 use super::*;
182 use crate::Kind;
183
184 #[test]
185 fn test_literal_naturals() {
186 let n = TyNat::lit(42);
187 assert!(n.is_lit());
188 assert!(n.is_ground());
189 assert_eq!(n.as_lit(), Some(42));
190 assert_eq!(n.eval(), Some(42));
191 }
192
193 #[test]
194 fn test_variable_naturals() {
195 let v = TyVar::new(0, Kind::Nat);
196 let n = TyNat::Var(v);
197 assert!(!n.is_lit());
198 assert!(!n.is_ground());
199 assert_eq!(n.as_lit(), None);
200 assert_eq!(n.eval(), None);
201 }
202
203 #[test]
204 fn test_add_literals() {
205 let a = TyNat::lit(10);
206 let b = TyNat::lit(20);
207 let sum = TyNat::add(a, b);
208 assert_eq!(sum, TyNat::lit(30));
210 }
211
212 #[test]
213 fn test_add_with_variable() {
214 let v = TyVar::new(0, Kind::Nat);
215 let a = TyNat::Var(v.clone());
216 let b = TyNat::lit(10);
217 let sum = TyNat::add(a, b);
218 assert!(!sum.is_lit());
220 assert!(!sum.is_ground());
221 }
222
223 #[test]
224 fn test_mul_literals() {
225 let a = TyNat::lit(3);
226 let b = TyNat::lit(4);
227 let prod = TyNat::mul(a, b);
228 assert_eq!(prod, TyNat::lit(12));
229 }
230
231 #[test]
232 fn test_free_vars() {
233 let v1 = TyVar::new(0, Kind::Nat);
234 let v2 = TyVar::new(1, Kind::Nat);
235 let n = TyNat::add(TyNat::Var(v1.clone()), TyNat::Var(v2.clone()));
236 let vars = n.free_vars();
237 assert_eq!(vars.len(), 2);
238 assert!(vars.contains(&v1));
239 assert!(vars.contains(&v2));
240 }
241
242 #[test]
243 fn test_substitution() {
244 let v = TyVar::new(0, Kind::Nat);
245 let n = TyNat::add(TyNat::Var(v.clone()), TyNat::lit(5));
246 let result = n.subst(&v, &TyNat::lit(10));
247 assert_eq!(result.eval(), Some(15));
248 }
249
250 #[test]
251 fn test_display() {
252 assert_eq!(format!("{}", TyNat::lit(42)), "42");
253 let v = TyVar::new(0, Kind::Nat);
254 assert_eq!(format!("{}", TyNat::Var(v)), "n0");
255 }
256}