1use std::collections::HashMap;
2use std::convert::TryInto;
3
4use crate::ast::Sym;
5use crate::search::{Resolved, Resolver, SolutionState};
6use crate::term_arena::{AppTerm, ArgRange, Term, TermId};
7use crate::universe::SymbolStorage;
8
9#[derive(Clone)]
44pub struct ArithmeticResolver {
45 exp_map: HashMap<Sym, Exp>,
46 pred_map: HashMap<Sym, Pred>,
47}
48
49impl ArithmeticResolver {
50 pub fn new<T: SymbolStorage>(symbols: &mut T) -> Self {
51 let exps = [
52 ("add", Exp::Add),
53 ("sub", Exp::Sub),
54 ("mul", Exp::Mul),
55 ("div", Exp::Div),
56 ("rem", Exp::Rem),
57 ("pow", Exp::Pow),
58 ];
59 let preds = [("is", Pred::Is)];
60 Self {
61 exp_map: symbols.build_sym_map(exps),
62 pred_map: symbols.build_sym_map(preds),
63 }
64 }
65
66 fn eval_exp(&self, solution: &SolutionState, exp: TermId) -> Option<i64> {
67 match solution.follow_vars(exp).1 {
69 Term::Var(_) => None,
71 Term::App(AppTerm(sym, arg_range)) => {
72 let op = self.exp_map.get(&sym)?;
73 let [a1, a2] = solution.terms().get_args_fixed(arg_range)?;
74 let v1 = self.eval_exp(solution, a1)?;
75 let v2 = self.eval_exp(solution, a2)?;
76 let ret = match op {
78 Exp::Add => v1.checked_add(v2)?,
79 Exp::Sub => v1.checked_sub(v2)?,
80 Exp::Mul => v1.checked_mul(v2)?,
81 Exp::Div => v1.checked_div(v2)?,
82 Exp::Rem => v1.checked_rem(v2)?,
83 Exp::Pow => v1.checked_pow(v2.try_into().ok()?)?,
84 };
85 Some(ret)
86 }
87 Term::Int(i) => Some(i),
88 _ => None,
90 }
91 }
92
93 fn resolve_is(
94 &mut self,
95 args: ArgRange,
96 context: &mut crate::search::ResolveContext,
97 ) -> Option<Resolved<()>> {
98 let [left, right] = context.solution().terms().get_args_fixed(args)?;
99 let right_val = self.eval_exp(context.solution(), right)?;
101
102 let (_left_id, left_term) = context.solution().follow_vars(left);
104 match left_term {
105 Term::Var(var) => {
106 let result_term = context.solution_mut().terms_mut().int(right_val);
108 context
109 .solution_mut()
110 .set_var(var, result_term)
111 .then_some(Resolved::Success)
112 }
113 Term::Int(left_val) => (left_val == right_val).then_some(Resolved::Success),
114 _ => None,
116 }
117 }
118}
119
120#[derive(Clone)]
121enum Exp {
122 Add,
123 Sub,
124 Mul,
125 Div,
126 Rem,
127 Pow,
128}
129
130#[derive(Clone)]
131enum Pred {
132 Is,
133}
134
135impl Resolver for ArithmeticResolver {
136 type Choice = ();
138
139 fn resolve(
140 &mut self,
141 _goal_id: crate::term_arena::TermId,
142 AppTerm(sym, args): crate::term_arena::AppTerm,
143 context: &mut crate::search::ResolveContext,
144 ) -> Option<Resolved<Self::Choice>> {
145 let pred = self.pred_map.get(&sym)?;
146 match pred {
147 Pred::Is => self.resolve_is(args, context),
148 }
149 }
150
151 fn resume(
152 &mut self,
153 _choice: &mut Self::Choice,
154 _goal_id: crate::term_arena::TermId,
155 _context: &mut crate::search::ResolveContext,
156 ) -> bool {
157 false
158 }
159}
160
161#[cfg(test)]
162mod tests {
163 use crate::ast::Term;
164 use crate::query_dfs;
165 use crate::resolve::ResolverExt;
166 use crate::search::Solution;
167 use crate::textual::TextualUniverse;
168
169 use super::ArithmeticResolver;
170
171 #[test]
172 fn simple() {
173 let tu = TextualUniverse::new();
174 let mut query = tu
175 .prepare_query("is(X, add(3, mul(3, sub(6, div(10, rem(10, pow(2,3))))))).")
176 .unwrap();
177 let resolver = ArithmeticResolver::new(&mut query.symbols_mut());
178 let mut results = query_dfs(resolver.or_else(tu.resolver()), query.query());
179 assert_eq!(results.next(), Some(Solution(vec![Some(Term::Int(6))])));
180 assert!(results.next().is_none());
181 }
182
183 #[test]
184 fn complex() {
185 let mut tu = TextualUniverse::new();
186 let mut arith = ArithmeticResolver::new(&mut tu.symbols);
187 tu.load_str(
188 r"
189 eq(Exp1, Exp2) :- is(X, Exp1), is(X, Exp2), !.
190 eq(Exp1, Exp2) :- is(Exp1, Exp2), !.
191 eq(Exp1, Exp2) :- is(Exp2, Exp1), !.
192 ",
193 )
194 .unwrap();
195 {
196 let query = tu.prepare_query("eq(add(2, 2), pow(2, 2)).").unwrap();
197 let mut results = query_dfs(arith.by_ref().or_else(tu.resolver()), query.query());
198 assert_eq!(results.next(), Some(Solution(vec![])));
199 assert!(results.next().is_none());
200 }
201 {
202 let query = tu.prepare_query("eq(X, pow(2, 2)).").unwrap();
203 let mut results = query_dfs(arith.by_ref().or_else(tu.resolver()), query.query());
204 assert_eq!(results.next(), Some(Solution(vec![Some(Term::Int(4))])));
205 assert!(results.next().is_none());
206 }
207 {
208 let query = tu.prepare_query("eq(add(2, 2), X).").unwrap();
209 let mut results = query_dfs(arith.by_ref().or_else(tu.resolver()), query.query());
210 assert_eq!(results.next(), Some(Solution(vec![Some(Term::Int(4))])));
211 assert!(results.next().is_none());
212 }
213 {
214 let query = tu.prepare_query("eq(2, 2).").unwrap();
215 let mut results = query_dfs(arith.by_ref().or_else(tu.resolver()), query.query());
216 assert_eq!(results.next(), Some(Solution(vec![])));
217 assert!(results.next().is_none());
218 }
219 }
220}