Skip to main content

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*/