1use lang::{Formula, Term};
7use log::trace;
8use std::collections::HashMap;
9
10mod clausal;
11pub mod lang;
12mod resolution;
13mod unification;
14
15type Arity = HashMap<String, usize>;
16
17#[derive(Debug, PartialEq)]
18pub enum ProverError {
19 ArityError,
20 TimeoutError,
21}
22
23pub fn is_valid(formula: Formula, limit_in_seconds: u64) -> Result<bool, ProverError> {
40 if !check_arity(&formula) {
41 return Err(ProverError::ArityError);
42 }
43 trace!("Negating {formula}");
44 let negated = Formula::Neg(Box::new(formula));
45 let clausal = clausal::to_clausal(negated);
46 resolution::refute_resolution(clausal, limit_in_seconds)
47}
48
49fn check_arity(formula: &Formula) -> bool {
50 let mut pred_arity = HashMap::new();
51 let mut func_arity = HashMap::new();
52 check_arity_in_formula(formula, &mut pred_arity, &mut func_arity)
53}
54
55fn check_arity_in_formula(
56 formula: &Formula,
57 pred_arity: &mut Arity,
58 func_arity: &mut Arity,
59) -> bool {
60 match formula {
61 Formula::Pred(pred) => {
62 if let Some(arity) = pred_arity.get(pred.get_id()) {
63 if arity != &pred.get_args().len() {
64 return false;
65 }
66 } else {
67 pred_arity.insert(pred.get_id().to_string(), pred.get_args().len());
68 }
69 pred.get_args()
70 .iter()
71 .all(|arg| check_arity_in_term(arg, pred_arity, func_arity))
72 }
73 Formula::True => true,
74 Formula::False => true,
75 Formula::And(left, right) => {
76 check_arity_in_formula(left, pred_arity, func_arity)
77 && check_arity_in_formula(right, pred_arity, func_arity)
78 }
79 Formula::Or(left, right) => {
80 check_arity_in_formula(left, pred_arity, func_arity)
81 && check_arity_in_formula(right, pred_arity, func_arity)
82 }
83 Formula::Neg(subformula) => check_arity_in_formula(subformula, pred_arity, func_arity),
84 Formula::Imply(left, right) => {
85 check_arity_in_formula(left, pred_arity, func_arity)
86 && check_arity_in_formula(right, pred_arity, func_arity)
87 }
88 Formula::Iff(left, right) => {
89 check_arity_in_formula(left, pred_arity, func_arity)
90 && check_arity_in_formula(right, pred_arity, func_arity)
91 }
92 Formula::Forall(_, subformula) => {
93 check_arity_in_formula(subformula, pred_arity, func_arity)
94 }
95 Formula::Exists(_, subformula) => {
96 check_arity_in_formula(subformula, pred_arity, func_arity)
97 }
98 }
99}
100
101fn check_arity_in_term(term: &Term, pred_arity: &mut Arity, func_arity: &mut Arity) -> bool {
102 match term {
103 Term::Obj(_) => true,
104 Term::Var(_) => true,
105 Term::Fun(f) => {
106 if let Some(arity) = func_arity.get(f.get_id()) {
107 if arity != &f.get_args().len() {
108 return false;
109 }
110 } else {
111 func_arity.insert(f.get_id().to_string(), f.get_args().len());
112 }
113 f.get_args()
114 .iter()
115 .all(|arg| check_arity_in_term(arg, pred_arity, func_arity))
116 }
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123 use crate::lang::{Pred, Term, Var};
124
125 const DEFAULT_LIMIT: u64 = 60;
126
127 #[test]
128 fn test_valid_formula() {
129 let formula = Neg!(Exists!(
130 "y",
131 Forall!(
132 "z",
133 Iff!(
134 Pred!("p", [Var!("z"), Var!("y")]),
135 Neg!(Exists!(
136 "x",
137 And!(
138 Pred!("p", [Var!("z"), Var!("x")]),
139 Pred!("p", [Var!("x"), Var!("z")])
140 )
141 ))
142 )
143 )
144 )); assert!(is_valid(formula, DEFAULT_LIMIT).unwrap());
146 }
147
148 #[test]
149 fn test_invalid_formula() {
150 let formula = Imply!(
151 Exists!("x", Pred!("p", [Var!("x")])),
152 Forall!("x", Pred!("p", [Var!("x")]))
153 ); assert!(!is_valid(formula, DEFAULT_LIMIT).unwrap());
155 }
156
157 #[test]
158 fn test_unknown_formula() {
159 let formula = Neg!(Forall!(
160 "x",
161 Forall!(
162 "y",
163 And!(
164 Or!(Pred!("p", [Var!("x")]), Neg!(Pred!("q", [Var!("x")]))),
165 Or!(Neg!(Pred!("p", [Var!("y")])), Pred!("q", [Var!("y")]))
166 )
167 )
168 )); assert!(is_valid(formula, 5) == Err(ProverError::TimeoutError));
170 }
171}