1use super::TLExpr;
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
46pub enum ModalSystem {
47 K,
49 T,
51 S4,
53 S5,
55 D,
57 B,
59}
60
61impl ModalSystem {
62 pub fn axioms(&self) -> Vec<&'static str> {
64 match self {
65 ModalSystem::K => vec!["K"],
66 ModalSystem::T => vec!["K", "T"],
67 ModalSystem::S4 => vec!["K", "T", "4"],
68 ModalSystem::S5 => vec!["K", "T", "4", "5"],
69 ModalSystem::D => vec!["K", "D"],
70 ModalSystem::B => vec!["K", "T", "B"],
71 }
72 }
73
74 pub fn has_axiom(&self, axiom: &str) -> bool {
76 self.axioms().contains(&axiom)
77 }
78
79 pub fn description(&self) -> &'static str {
81 match self {
82 ModalSystem::K => "Basic modal logic - distribution of necessity over implication",
83 ModalSystem::T => "Reflexive logic - what is necessary is true",
84 ModalSystem::S4 => "Transitive logic - necessity of necessity",
85 ModalSystem::S5 => "Euclidean logic - full modal equivalence",
86 ModalSystem::D => "Deontic logic - consistency (obligation implies permission)",
87 ModalSystem::B => "Symmetric logic - truth implies necessary possibility",
88 }
89 }
90}
91
92pub fn verify_axiom_k(expr: &TLExpr) -> bool {
96 matches!(
97 expr,
98 TLExpr::Imply(box1, box2) if matches!(
99 (&**box1, &**box2),
100 (
101 TLExpr::Box(impl1),
102 TLExpr::Imply(box_p, box_q)
103 ) if matches!(
104 (&**impl1, &**box_p, &**box_q),
105 (
106 TLExpr::Imply(p1, q1),
107 TLExpr::Box(p2),
108 TLExpr::Box(q2)
109 ) if p1 == p2 && q1 == q2
110 )
111 )
112 )
113}
114
115pub fn verify_axiom_t(expr: &TLExpr) -> bool {
119 matches!(
120 expr,
121 TLExpr::Imply(box_p, p) if matches!(
122 (&**box_p, &**p),
123 (TLExpr::Box(inner), outer) if **inner == *outer
124 )
125 )
126}
127
128pub fn verify_axiom_4(expr: &TLExpr) -> bool {
132 matches!(
133 expr,
134 TLExpr::Imply(box_p, box_box_p) if matches!(
135 (&**box_p, &**box_box_p),
136 (TLExpr::Box(p1), TLExpr::Box(box_p2)) if matches!(
137 &**box_p2,
138 TLExpr::Box(p2) if p1 == p2
139 )
140 )
141 )
142}
143
144pub fn verify_axiom_5(expr: &TLExpr) -> bool {
148 matches!(
149 expr,
150 TLExpr::Imply(dia_p, box_dia_p) if matches!(
151 (&**dia_p, &**box_dia_p),
152 (TLExpr::Diamond(p1), TLExpr::Box(dia_p2)) if matches!(
153 &**dia_p2,
154 TLExpr::Diamond(p2) if p1 == p2
155 )
156 )
157 )
158}
159
160pub fn verify_axiom_d(expr: &TLExpr) -> bool {
164 matches!(
165 expr,
166 TLExpr::Imply(box_p, dia_p) if matches!(
167 (&**box_p, &**dia_p),
168 (TLExpr::Box(p1), TLExpr::Diamond(p2)) if p1 == p2
169 )
170 )
171}
172
173pub fn verify_axiom_b(expr: &TLExpr) -> bool {
177 matches!(
178 expr,
179 TLExpr::Imply(p, box_dia_p) if matches!(
180 &**box_dia_p,
181 TLExpr::Box(dia_p) if matches!(
182 &**dia_p,
183 TLExpr::Diamond(p2) if **p == **p2
184 )
185 )
186 )
187}
188
189pub fn apply_axiom_k(expr: &TLExpr) -> Option<TLExpr> {
193 if let TLExpr::And(left, right) = expr {
194 if let (TLExpr::Box(impl_expr), TLExpr::Box(p_expr)) = (&**left, &**right) {
196 if let TLExpr::Imply(p1, q) = &**impl_expr {
197 if p1 == p_expr {
198 return Some(TLExpr::modal_box((**q).clone()));
200 }
201 }
202 }
203 if let (TLExpr::Box(p_expr), TLExpr::Box(impl_expr)) = (&**left, &**right) {
205 if let TLExpr::Imply(p1, q) = &**impl_expr {
206 if p1 == p_expr {
207 return Some(TLExpr::modal_box((**q).clone()));
208 }
209 }
210 }
211 }
212 None
213}
214
215pub fn apply_axiom_t(expr: &TLExpr) -> Option<TLExpr> {
219 if let TLExpr::Box(inner) = expr {
220 Some((**inner).clone())
221 } else {
222 None
223 }
224}
225
226#[allow(dead_code)]
230pub fn apply_axiom_4_forward(expr: &TLExpr) -> Option<TLExpr> {
231 if let TLExpr::Box(_inner) = expr {
232 Some(TLExpr::modal_box(expr.clone()))
233 } else {
234 None
235 }
236}
237
238#[allow(dead_code)]
242pub fn apply_axiom_4_backward(expr: &TLExpr) -> Option<TLExpr> {
243 if let TLExpr::Box(inner) = expr {
244 if let TLExpr::Box(_) = &**inner {
245 return Some((**inner).clone());
246 }
247 }
248 None
249}
250
251#[allow(dead_code)]
255pub fn apply_axiom_5(expr: &TLExpr) -> Option<TLExpr> {
256 if let TLExpr::Diamond(_) = expr {
257 Some(TLExpr::modal_box(expr.clone()))
258 } else {
259 None
260 }
261}
262
263pub fn normalize_s5(expr: &TLExpr) -> TLExpr {
268 match expr {
269 TLExpr::Box(inner) if matches!(**inner, TLExpr::Box(_)) => normalize_s5(inner),
271 TLExpr::Diamond(inner) if matches!(**inner, TLExpr::Diamond(_)) => normalize_s5(inner),
273 TLExpr::Box(inner) if matches!(**inner, TLExpr::Diamond(_)) => normalize_s5(inner),
275 TLExpr::Diamond(inner) if matches!(**inner, TLExpr::Box(_)) => normalize_s5(inner),
277 TLExpr::Box(inner) => TLExpr::modal_box(normalize_s5(inner)),
279 TLExpr::Diamond(inner) => TLExpr::modal_diamond(normalize_s5(inner)),
280 TLExpr::And(l, r) => TLExpr::and(normalize_s5(l), normalize_s5(r)),
281 TLExpr::Or(l, r) => TLExpr::or(normalize_s5(l), normalize_s5(r)),
282 TLExpr::Not(e) => TLExpr::negate(normalize_s5(e)),
283 TLExpr::Imply(l, r) => TLExpr::imply(normalize_s5(l), normalize_s5(r)),
284 _ => expr.clone(),
285 }
286}
287
288pub fn modal_depth(expr: &TLExpr) -> usize {
292 match expr {
293 TLExpr::Box(inner) | TLExpr::Diamond(inner) => 1 + modal_depth(inner),
294 TLExpr::And(l, r) | TLExpr::Or(l, r) | TLExpr::Imply(l, r) => {
295 modal_depth(l).max(modal_depth(r))
296 }
297 TLExpr::Not(e) => modal_depth(e),
298 _ => 0,
299 }
300}
301
302pub fn is_modal_free(expr: &TLExpr) -> bool {
304 modal_depth(expr) == 0
305}
306
307pub fn extract_modal_subformulas(expr: &TLExpr) -> Vec<TLExpr> {
309 let mut formulas = Vec::new();
310 extract_modal_subformulas_rec(expr, &mut formulas);
311 formulas
312}
313
314fn extract_modal_subformulas_rec(expr: &TLExpr, acc: &mut Vec<TLExpr>) {
315 match expr {
316 TLExpr::Box(_) | TLExpr::Diamond(_) => {
317 acc.push(expr.clone());
318 if let TLExpr::Box(inner) | TLExpr::Diamond(inner) = expr {
320 extract_modal_subformulas_rec(inner, acc);
321 }
322 }
323 TLExpr::And(l, r) | TLExpr::Or(l, r) | TLExpr::Imply(l, r) => {
324 extract_modal_subformulas_rec(l, acc);
325 extract_modal_subformulas_rec(r, acc);
326 }
327 TLExpr::Not(e) => extract_modal_subformulas_rec(e, acc),
328 _ => {}
329 }
330}
331
332pub fn is_theorem_in_system(expr: &TLExpr, system: ModalSystem) -> bool {
337 let axioms = system.axioms();
339
340 (axioms.contains(&"K") && verify_axiom_k(expr))
341 || (axioms.contains(&"T") && verify_axiom_t(expr))
342 || (axioms.contains(&"4") && verify_axiom_4(expr))
343 || (axioms.contains(&"5") && verify_axiom_5(expr))
344 || (axioms.contains(&"D") && verify_axiom_d(expr))
345 || (axioms.contains(&"B") && verify_axiom_b(expr))
346}
347
348#[cfg(test)]
349mod tests {
350 use super::*;
351
352 #[test]
353 fn test_modal_system_axioms() {
354 assert_eq!(ModalSystem::K.axioms(), vec!["K"]);
355 assert_eq!(ModalSystem::T.axioms(), vec!["K", "T"]);
356 assert_eq!(ModalSystem::S4.axioms(), vec!["K", "T", "4"]);
357 assert_eq!(ModalSystem::S5.axioms(), vec!["K", "T", "4", "5"]);
358 }
359
360 #[test]
361 fn test_verify_axiom_k() {
362 let p = TLExpr::pred("p", vec![]);
364 let q = TLExpr::pred("q", vec![]);
365
366 let expr = TLExpr::imply(
367 TLExpr::modal_box(TLExpr::imply(p.clone(), q.clone())),
368 TLExpr::imply(TLExpr::modal_box(p), TLExpr::modal_box(q)),
369 );
370
371 assert!(verify_axiom_k(&expr));
372 }
373
374 #[test]
375 fn test_verify_axiom_t() {
376 let p = TLExpr::pred("p", vec![]);
378 let expr = TLExpr::imply(TLExpr::modal_box(p.clone()), p);
379
380 assert!(verify_axiom_t(&expr));
381 }
382
383 #[test]
384 fn test_verify_axiom_4() {
385 let p = TLExpr::pred("p", vec![]);
387 let expr = TLExpr::imply(
388 TLExpr::modal_box(p.clone()),
389 TLExpr::modal_box(TLExpr::modal_box(p)),
390 );
391
392 assert!(verify_axiom_4(&expr));
393 }
394
395 #[test]
396 fn test_verify_axiom_5() {
397 let p = TLExpr::pred("p", vec![]);
399 let expr = TLExpr::imply(
400 TLExpr::modal_diamond(p.clone()),
401 TLExpr::modal_box(TLExpr::modal_diamond(p)),
402 );
403
404 assert!(verify_axiom_5(&expr));
405 }
406
407 #[test]
408 fn test_apply_axiom_k() {
409 let p = TLExpr::pred("p", vec![]);
411 let q = TLExpr::pred("q", vec![]);
412
413 let expr = TLExpr::and(
414 TLExpr::modal_box(TLExpr::imply(p.clone(), q.clone())),
415 TLExpr::modal_box(p),
416 );
417
418 let result = apply_axiom_k(&expr).unwrap();
419 assert_eq!(result, TLExpr::modal_box(q));
420 }
421
422 #[test]
423 fn test_apply_axiom_t() {
424 let p = TLExpr::pred("p", vec![]);
425 let box_p = TLExpr::modal_box(p.clone());
426
427 let result = apply_axiom_t(&box_p).unwrap();
428 assert_eq!(result, p);
429 }
430
431 #[test]
432 fn test_normalize_s5() {
433 let p = TLExpr::pred("p", vec![]);
435 let expr = TLExpr::modal_box(TLExpr::modal_box(p.clone()));
436
437 let normalized = normalize_s5(&expr);
438 assert_eq!(normalized, TLExpr::modal_box(p.clone()));
439
440 let expr2 = TLExpr::modal_box(TLExpr::modal_diamond(p.clone()));
442 let normalized2 = normalize_s5(&expr2);
443 assert_eq!(normalized2, TLExpr::modal_diamond(p));
444 }
445
446 #[test]
447 fn test_modal_depth() {
448 let p = TLExpr::pred("p", vec![]);
449 assert_eq!(modal_depth(&p), 0);
450
451 let box_p = TLExpr::modal_box(p.clone());
452 assert_eq!(modal_depth(&box_p), 1);
453
454 let box_box_p = TLExpr::modal_box(TLExpr::modal_box(p));
455 assert_eq!(modal_depth(&box_box_p), 2);
456 }
457
458 #[test]
459 fn test_is_modal_free() {
460 let p = TLExpr::pred("p", vec![]);
461 assert!(is_modal_free(&p));
462
463 let box_p = TLExpr::modal_box(p);
464 assert!(!is_modal_free(&box_p));
465 }
466
467 #[test]
468 fn test_extract_modal_subformulas() {
469 let p = TLExpr::pred("p", vec![]);
470 let q = TLExpr::pred("q", vec![]);
471
472 let expr = TLExpr::and(TLExpr::modal_box(p.clone()), TLExpr::modal_diamond(q));
474
475 let subformulas = extract_modal_subformulas(&expr);
476 assert_eq!(subformulas.len(), 2);
477 }
478
479 #[test]
480 fn test_is_theorem_in_system() {
481 let p = TLExpr::pred("p", vec![]);
483 let axiom_t = TLExpr::imply(TLExpr::modal_box(p.clone()), p);
484
485 assert!(is_theorem_in_system(&axiom_t, ModalSystem::T));
486 assert!(is_theorem_in_system(&axiom_t, ModalSystem::S4));
487 assert!(is_theorem_in_system(&axiom_t, ModalSystem::S5));
488 assert!(!is_theorem_in_system(&axiom_t, ModalSystem::K)); }
490}