ftml_solver/impls/equality.rs
1use std::borrow::Cow;
2
3use crate::{
4 CheckRef, impls::solving::TermExtSolvable, rules::implicits::ImplicitExtApp,
5 split::SplitStrategy, trace::CheckingTask,
6};
7use ftml_ontology::terms::{
8 ApplicationTerm, Argument, BindingTerm, BoundArgument, MaybeSequence, Term, eq::Alpha,
9};
10use ftml_solver_trace::traceref;
11
12fn same_shape(lhs: &Term, rhs: &Term) -> bool {
13 if lhs.is_solvable().is_some() || rhs.is_solvable().is_some() {
14 return true;
15 }
16 match (lhs, rhs) {
17 (Term::Symbol { uri: a, .. }, Term::Symbol { uri: b, .. }) if *a == *b => true,
18 (Term::Var { .. }, Term::Var { .. })
19 | (Term::Label { .. }, Term::Label { .. })
20 | (Term::Number(_), Term::Number(_))
21 | (Term::Field(_), Term::Field(_)) => true,
22 (Term::Application(a), Term::Application(b)) if a.arguments.len() == b.arguments.len() => {
23 same_shape(&a.head, &b.head)
24 }
25 (Term::Bound(a), Term::Bound(b)) if a.arguments.len() == b.arguments.len() => {
26 same_shape(&a.head, &b.head)
27 }
28 _ => false,
29 }
30 /*matches!(
31 (lhs, rhs),
32 (Term::Symbol { .. }, Term::Symbol { .. })
33 | (Term::Var { .. }, Term::Var { .. })
34 | (Term::Field(_), Term::Field(_))
35 | (Term::Label { .. }, Term::Label { .. })
36 | (Term::Number(_), Term::Number(_))
37 | (Term::Application(_), Term::Application(_))
38 | (Term::Bound(_), Term::Bound(_))
39 )*/
40 /*
41 if r {
42 tracing::warn!(
43 "Same shape:\n - {:?}\n - {:?}",
44 lhs.debug_short(),
45 rhs.debug_short()
46 );
47 } else {
48 tracing::error!(
49 "Not same shape:\n - {:?}\n - {:?}",
50 lhs.debug_short(),
51 rhs.debug_short()
52 );
53 }
54 r
55 */
56}
57
58impl<'t, Split: SplitStrategy> CheckRef<'t, '_, Split> {
59 pub fn check_equality(&mut self, lhs: &'t Term, rhs: &'t Term) -> Option<bool> {
60 tracing::debug!(
61 "Checking equality {:?} == {:?}",
62 lhs.debug_short(),
63 rhs.debug_short()
64 );
65 self.wrap_check(CheckingTask::Equality(lhs, rhs), |slf| {
66 slf.check_equality_i(lhs, rhs)
67 })
68 }
69 pub(crate) fn check_equality_i(&mut self, lhs: &'t Term, rhs: &'t Term) -> Option<bool> {
70 if lhs.alpha_equal(rhs) {
71 self.comment("trivial");
72 return Some(true);
73 }
74 if let Some(unk) = lhs.is_solvable() {
75 return self.solve_equality(unk, rhs);
76 }
77 if let Some(unk) = rhs.is_solvable() {
78 return self.solve_equality(unk, lhs);
79 }
80 let lhs = self.subst(lhs.clone());
81 let rhs = self.subst(rhs.clone());
82 self.scoped(|slf| {
83 match slf.simplify_rules_two(
84 slf.top.rules.equality(),
85 &lhs,
86 &rhs,
87 |_, rl, lhs, rhs| rl.applicable(lhs, rhs),
88 |slf, rl, lhs, rhs| rl.apply(slf, lhs, rhs),
89 |lhs, rhs| {
90 lhs.alpha_equal(rhs)
91 || lhs.is_solvable().is_some()
92 || rhs.is_solvable().is_some()
93 },
94 ) {
95 either::Left(Some(opt)) => {
96 if !opt {
97 slf.failure("Disproven");
98 }
99 return Some(opt);
100 }
101 either::Right((lhs, rhs)) => {
102 if lhs.alpha_equal(&rhs) {
103 slf.comment("trivial");
104 return Some(true);
105 }
106 if let Some(unk) = lhs.is_solvable() {
107 return slf.solve_equality(unk, &rhs);
108 }
109 if let Some(unk) = rhs.is_solvable() {
110 return slf.solve_equality(unk, &lhs);
111 }
112 slf.comment("Trying congruence");
113 return slf.scoped(|slf| slf.congruence(&lhs, &rhs));
114 }
115 either::Left(None) => (),
116 }
117
118 slf.comment("Trying congruence");
119 slf.congruence(&lhs, &rhs)
120 })
121 }
122
123 fn congruence(&mut self, lhs: &'t Term, rhs: &'t Term) -> Option<bool> {
124 tracing::debug!("Trying congruence");
125 let Some((lhs, rhs)) = self.simplify_until_two(lhs, rhs, |_, lhs, rhs| {
126 lhs.unapply_implicits(false).is_some()
127 || rhs.unapply_implicits(false).is_some()
128 || same_shape(lhs, rhs)
129 }) else {
130 return self.congruence_cont(lhs, rhs);
131 };
132 match (lhs, rhs) {
133 (Cow::Borrowed(lhs), Cow::Borrowed(rhs)) => self.congruence_i(lhs, rhs),
134 (lhs, rhs) => self.scoped(|slf| slf.congruence_i(&lhs, &rhs)),
135 }
136 }
137
138 fn congruence_i(&mut self, lhs: &'t Term, rhs: &'t Term) -> Option<bool> {
139 if lhs.unapply_implicits(false).is_some() || rhs.unapply_implicits(false).is_some() {
140 let nlhs = self
141 .simplify_implicit(lhs)
142 .ok()
143 .flatten()
144 .map_or(Cow::Borrowed(lhs), Cow::Owned);
145 let nrhs = self
146 .simplify_implicit(rhs)
147 .ok()
148 .flatten()
149 .map_or(Cow::Borrowed(rhs), Cow::Owned);
150 if !lhs.alpha_equal(&nlhs) || !rhs.alpha_equal(&nrhs) {
151 return self.scoped(|slf| slf.congruence(&nlhs, &nrhs));
152 }
153 }
154 let r = match (lhs, rhs) {
155 (Term::Application(l), Term::Application(r))
156 if l.arguments.len() == r.arguments.len() =>
157 {
158 match self.traced(CheckingTask::Strategy("Trying congruence"), |slf| {
159 slf.congruence_app(l, r)
160 }) {
161 Ok(r) => Some(r),
162 Err(l) => {
163 self.add_msg(l.into());
164 self.congruence_cont(lhs, rhs)
165 }
166 }
167 }
168 (Term::Bound(l), Term::Bound(r)) if l.arguments.len() == r.arguments.len() => {
169 match self.traced(CheckingTask::Strategy("Trying congruence"), |slf| {
170 slf.congruence_bind(l, r)
171 }) {
172 Ok(r) => Some(r),
173 Err(l) => {
174 self.add_msg(l.into());
175 self.congruence_cont(lhs, rhs)
176 }
177 }
178 }
179 (Term::Field(a), Term::Field(b)) if a.key == b.key => {
180 self.congruence_cont(&a.record, &b.record)
181 }
182 (Term::Field(_), Term::Field(_)) => Some(false),
183 (Term::Number(a), Term::Number(b)) => Some(a == b),
184 _ => self.congruence_cont(lhs, rhs),
185 };
186 if r.is_some() {
187 return r;
188 }
189 if let Some(nlhs) = self.simplify_one(super::simplify::Expansion::Full, lhs) {
190 return self.scoped(|slf| slf.congruence(rhs, &nlhs));
191 }
192 if let Some(nrhs) = self.simplify_one(super::simplify::Expansion::Full, rhs) {
193 return self.scoped(|slf| slf.congruence(&nrhs, lhs));
194 }
195 None
196 }
197
198 fn congruence_cont(&mut self, lhs: &'t Term, rhs: &'t Term) -> Option<bool> {
199 self.add_msg(traceref!("shapes don't match: ", lhs, " and ", rhs).into());
200 /*
201 // LAST RESORT
202 let nlhs = self
203 .simplify_full(super::simplify::Expansion::Full, lhs)
204 .map_or(Cow::Borrowed(lhs), Cow::Owned);
205 let nrhs = self
206 .simplify_full(super::simplify::Expansion::Full, rhs)
207 .map_or(Cow::Borrowed(rhs), Cow::Owned);
208 if *lhs != *nlhs || *rhs != *nrhs {
209 self.scoped(|slf| slf.check_equality_i(&nlhs, &nrhs))
210 } else {
211 None
212 }
213 */
214 None
215 /*
216 // todo: preserve logs on recursive fail
217 if let Some(lhs) = self.simplify_one(true, lhs) {
218 if self.alpha_equal(&lhs, rhs) {
219 self.comment("trivial");
220 return Some(true);
221 }
222 return self.scoped(|slf| slf.check_equality_i(&lhs, rhs));
223 }
224 if let Some(rhs) = self.simplify_one(true, rhs) {
225 if self.alpha_equal(lhs, &rhs) {
226 self.comment("trivial");
227 return Some(true);
228 }
229 return self.scoped(|slf| slf.check_equality_i(lhs, &rhs));
230 }
231 None
232 */
233 }
234
235 // invariant: lhs.arguments.len() == rhs.arguments.len()
236 fn congruence_app(
237 &mut self,
238 lhs: &'t ApplicationTerm,
239 rhs: &'t ApplicationTerm,
240 ) -> Option<bool> {
241 tracing::trace!("Comparing operators");
242 self.comment("Comparing operators");
243 if !self.check_equality(&lhs.head, &rhs.head)? {
244 return None;
245 }
246 for (i, (a, b)) in lhs.arguments.iter().zip(&rhs.arguments).enumerate() {
247 tracing::trace!("Comparing argument {}", i + 1);
248 self.counter("Comparing arguments ", i + 1);
249 if let (Argument::Simple(a), Argument::Simple(b)) = (a, b) {
250 if !self.check_equality(a, b)? {
251 return None;
252 }
253 } else if let (
254 Argument::Sequence(MaybeSequence::One(a)),
255 Argument::Sequence(MaybeSequence::One(b)),
256 ) = (a, b)
257 {
258 if !self.check_equality(a, b)? {
259 return None;
260 }
261 } else if let (
262 Argument::Sequence(MaybeSequence::Seq(a)),
263 Argument::Sequence(MaybeSequence::Seq(b)),
264 ) = (a, b)
265 {
266 if a.len() != b.len() {
267 return None;
268 }
269 for (a, b) in a.iter().zip(b.iter()) {
270 if !self.check_equality(a, b)? {
271 return None;
272 }
273 }
274 } else {
275 self.failure("Arguments don't match");
276 return None;
277 }
278 }
279 Some(true)
280 }
281
282 // invariant: lhs.arguments.len() == rhs.arguments.len()
283 fn congruence_bind(&mut self, lhs: &'t BindingTerm, rhs: &'t BindingTerm) -> Option<bool> {
284 self.comment("Comparing operators");
285 if !self.check_equality(&lhs.head, &rhs.head)? {
286 return None;
287 }
288 let mut substs = Alpha::new();
289 macro_rules! maybe_subst {
290 ($a:expr,$b:expr) => {
291 if substs.is_empty()
292 || !$a.has_free_such_that(|av| substs.iter().any(|(v, _)| *v == av.name()))
293 {
294 if !self.check_equality($a, $b)? {
295 return None;
296 }
297 } else {
298 let subst = substs
299 .iter()
300 .map(|(n, v)| {
301 (
302 n,
303 Term::Var {
304 variable: (*v).clone(),
305 presentation: None,
306 },
307 )
308 })
309 .collect::<smallvec::SmallVec<_, 2>>();
310 let r = match $a / &*subst {
311 Cow::Borrowed(_) => self.check_equality($a, $b)?,
312 Cow::Owned(a) => self.scoped(|slf| slf.check_equality(&a, $b))?,
313 };
314 if !r {
315 return None;
316 }
317 }
318 };
319 }
320 for (i, (a, b)) in lhs.arguments.iter().zip(&rhs.arguments).enumerate() {
321 self.counter("Comparing arguments ", i + 1);
322 match (a, b) {
323 (BoundArgument::Simple(a), BoundArgument::Simple(b)) => {
324 maybe_subst!(a, b);
325 }
326 (BoundArgument::Bound(a), BoundArgument::Bound(b))
327 | (
328 BoundArgument::BoundSeq(MaybeSequence::One(a)),
329 BoundArgument::BoundSeq(MaybeSequence::One(b)),
330 ) => {
331 match (a.tp.as_ref(), b.tp.as_ref()) {
332 (Some(a), Some(b)) => {
333 maybe_subst!(a, b);
334 }
335 (None, None) => (),
336 _ => return None,
337 }
338 match (a.df.as_ref(), b.df.as_ref()) {
339 (Some(a), Some(b)) => {
340 maybe_subst!(a, b);
341 }
342 (None, None) => (),
343 _ => return None,
344 }
345 self.extend_context(b);
346 if a.var.name() != b.var.name() {
347 substs.push((a.var.name(), &b.var));
348 }
349 }
350 (
351 BoundArgument::BoundSeq(MaybeSequence::Seq(sa)),
352 BoundArgument::BoundSeq(MaybeSequence::Seq(sb)),
353 ) if sa.len() == sb.len() => {
354 for (a, b) in sa.iter().zip(sb.iter()) {
355 match (a.tp.as_ref(), b.tp.as_ref()) {
356 (Some(a), Some(b)) => {
357 maybe_subst!(a, b);
358 }
359 (None, None) => (),
360 _ => return None,
361 }
362 match (a.df.as_ref(), b.df.as_ref()) {
363 (Some(a), Some(b)) => {
364 maybe_subst!(a, b);
365 }
366 (None, None) => (),
367 _ => return None,
368 }
369 self.extend_context(b);
370 if a.var.name() != b.var.name() {
371 substs.push((a.var.name(), &b.var));
372 }
373 }
374 }
375 _ => {
376 self.failure(format!("Arguments do not match: {a:?} <--> {b:?}"));
377 return None;
378 }
379 }
380 }
381 Some(true)
382 }
383}
384
385/*
386*
387// -----------------------------------------------------------
388
389pub fn alpha_equal_traced(lhs: &Term, rhs: &Term) -> bool {
390 alpha_equal_with_traced(lhs, rhs, &mut Alpha::default())
391}
392
393const CHECK: bool = true;
394
395macro_rules! rep_eq {
396 (false@$lhs:expr,$rhs:expr) => {
397 if CHECK {
398 ::tracing::error!(
399 "Not equal ({},{}): {:?} and {:?}",
400 line!(),
401 column!(),
402 $lhs,
403 $rhs
404 );
405 false
406 } else {
407 false
408 }
409 };
410 ($lhs:expr,$rhs:expr) => {
411 if CHECK {
412 $lhs == $rhs || {
413 ::tracing::error!(
414 "Not equal ({},{}): {:?} and {:?}",
415 line!(),
416 column!(),
417 $lhs,
418 $rhs
419 );
420 false
421 }
422 } else {
423 $lhs == $rhs
424 }
425 };
426}
427
428pub fn alpha_equal_with_traced<'t>(lhs: &'t Term, rhs: &'t Term, alpha: &mut Alpha<'t>) -> bool {
429 if lhs == rhs {
430 return true;
431 }
432 match (lhs, rhs) {
433 (Term::Var { variable: v1, .. }, Term::Var { variable: v2, .. }) => {
434 rep_eq!(v1.name(), v2.name())
435 || alpha.iter().any(|(a, b)| {
436 (*a == v1.name() && b.name() == v2.name())
437 || (b.name() == v1.name() && *a == v2.name())
438 })
439 }
440 (Term::Application(a), Term::Application(b))
441 if rep_eq!(a.arguments.len(), b.arguments.len()) =>
442 {
443 alpha_equal_with_traced(&a.head, &b.head, alpha)
444 && a.arguments
445 .iter()
446 .zip(b.arguments.iter())
447 .all(|(a, b)| alpha_arg_traced(a, b, alpha))
448 }
449 (Term::Bound(a), Term::Bound(b)) if rep_eq!(a.arguments.len(), b.arguments.len()) => {
450 let mut pop = 0;
451 if !alpha_equal_with_traced(&a.head, &b.head, alpha)
452 || a.arguments.iter().zip(b.arguments.iter()).any(|(a, b)| {
453 alpha_barg_traced(a, b, alpha)
454 .inspect(|i| pop += i)
455 .is_none()
456 })
457 {
458 return false;
459 }
460 for _ in 0..pop {
461 alpha.pop();
462 }
463 true
464 }
465 (Term::Field(a), Term::Field(b)) => {
466 alpha_equal_with_traced(&a.record, &b.record, alpha) && a.key == b.key
467 }
468 (
469 Term::Label {
470 name: na,
471 df: da,
472 tp: ta,
473 },
474 Term::Label {
475 name: nb,
476 df: db,
477 tp: tb,
478 },
479 ) if *na == *nb => {
480 match (da, db) {
481 (Some(a), Some(b)) => {
482 if !alpha_equal_with_traced(a, b, alpha) {
483 return false;
484 }
485 }
486 (None, None) => (),
487 _ => return false,
488 }
489 match (ta, tb) {
490 (Some(a), Some(b)) => alpha_equal_with_traced(a, b, alpha),
491 (None, None) => true,
492 _ => false,
493 }
494 }
495 (Term::Number(a), Term::Number(b)) => rep_eq!(a, b),
496 _ => rep_eq!(false@lhs,rhs),
497 }
498}
499
500fn alpha_arg_traced<'t>(lhs: &'t Argument, rhs: &'t Argument, alpha: &mut Alpha<'t>) -> bool {
501 match (lhs, rhs) {
502 (Argument::Simple(lhs), Argument::Simple(rhs))
503 | (
504 Argument::Sequence(MaybeSequence::One(lhs)),
505 Argument::Sequence(MaybeSequence::One(rhs)),
506 ) => alpha_equal_with_traced(lhs, rhs, alpha),
507 (
508 Argument::Sequence(MaybeSequence::Seq(lhs)),
509 Argument::Sequence(MaybeSequence::Seq(rhs)),
510 ) if lhs.len() == rhs.len() => lhs
511 .iter()
512 .zip(rhs.iter())
513 .all(|(lhs, rhs)| alpha_equal_with_traced(lhs, rhs, alpha)),
514 _ => rep_eq!(false@lhs,rhs),
515 }
516}
517fn alpha_barg_traced<'t>(
518 lhs: &'t BoundArgument,
519 rhs: &'t BoundArgument,
520 alpha: &mut Alpha<'t>,
521) -> Option<usize> {
522 macro_rules! ret {
523 ($e:expr) => {
524 if $e { Some(0) } else { None }
525 };
526 }
527 match (lhs, rhs) {
528 (BoundArgument::Simple(lhs), BoundArgument::Simple(rhs))
529 | (
530 BoundArgument::Sequence(MaybeSequence::One(lhs)),
531 BoundArgument::Sequence(MaybeSequence::One(rhs)),
532 ) => ret!(alpha_equal_with_traced(lhs, rhs, alpha)),
533 (
534 BoundArgument::Sequence(MaybeSequence::Seq(lhs)),
535 BoundArgument::Sequence(MaybeSequence::Seq(rhs)),
536 ) if rep_eq!(lhs.len(), rhs.len()) => ret!(
537 lhs.iter()
538 .zip(rhs.iter())
539 .all(|(lhs, rhs)| alpha_equal_with_traced(lhs, rhs, alpha))
540 ),
541 (BoundArgument::Bound(lhs), BoundArgument::Bound(rhs))
542 | (
543 BoundArgument::BoundSeq(MaybeSequence::One(lhs)),
544 BoundArgument::BoundSeq(MaybeSequence::One(rhs)),
545 ) => {
546 if alpha_cv_traced(lhs, rhs, alpha) {
547 Some(1)
548 } else {
549 None
550 }
551 }
552 (
553 BoundArgument::BoundSeq(MaybeSequence::Seq(lhs)),
554 BoundArgument::BoundSeq(MaybeSequence::Seq(rhs)),
555 ) if rep_eq!(lhs.len(), rhs.len()) => {
556 if lhs
557 .iter()
558 .zip(rhs.iter())
559 .all(|(a, b)| alpha_cv_traced(a, b, alpha))
560 {
561 Some(lhs.len())
562 } else {
563 None
564 }
565 }
566 _ => None,
567 }
568}
569fn alpha_cv_traced<'t>(
570 lhs: &'t ComponentVar,
571 rhs: &'t ComponentVar,
572 alpha: &mut Alpha<'t>,
573) -> bool {
574 match (lhs.tp.as_ref(), rhs.tp.as_ref()) {
575 (Some(lhs), Some(rhs)) => {
576 if !alpha_equal_with_traced(lhs, rhs, alpha) {
577 return false;
578 }
579 }
580 (None, None) => (),
581 _ => return rep_eq!(false@lhs,rhs),
582 }
583 match (lhs.df.as_ref(), rhs.df.as_ref()) {
584 (Some(lhs), Some(rhs)) => {
585 if !alpha_equal_with_traced(lhs, rhs, alpha) {
586 return false;
587 }
588 }
589 (None, None) => (),
590 _ => return rep_eq!(false@lhs,rhs),
591 }
592 alpha.push((lhs.var.name(), &rhs.var));
593 true
594}
595
596// -----------------------------------------------------------
597*/