(load #P"../src/unified-matcher.lisp")

; tests for 'var?'
(assert (um::var? '?x))
(assert (not (um::var? 'y)))
(assert (not (um::var? 3)))

; tests for 'unify' and 'subst-bind'
(assert (eql (um:unify 'a 'a) nil))
(assert (eql (um:unify 'a 'b) um:fail))
(assert (equal (um:unify '?x 'a)
               '((?x . a))))

(assert (eql (um:subst-bind um:fail '?x)
             um:fail))
(assert (eql (um:subst-bind nil '?x)
             '?x))
(assert (eql (um:subst-bind nil 'a)
             'a))
(assert (eql (um:subst-bind nil 3)
             3))

(assert (eql (um:subst-bind '((?x . a)) '?x)
             'a))

(let ((bind (um:unify '(?x ?y) '(a b))))
  (assert (equal bind
                 '((?y . b) (?x . a))))
  (assert (eql (um:subst-bind bind '?x)
               'a))
  (assert (eql (um:subst-bind bind '?y)
               'b)))

(let ((bind (um:unify '(?x . ?y) '(a b c))))
  (assert (equal bind
                 '((?y b c) (?x . a))))
  (assert (eql (um:subst-bind bind '?x)
               'a))
  (assert (equal (um:subst-bind bind '?y)
                 '(b c))))

(let ((bind (um:unify '(?x a ?z) '(b ?y ?x))))
  (assert (equal bind
                 '((?z . ?x) (?y . a) (?x . b))))
  (assert (eql (um:subst-bind bind '?x) 'b))
  (assert (eql (um:subst-bind bind '?y) 'a))
  (assert (eql (um:subst-bind bind '?z) 'b)))

(assert (equal (um:unify '(?x a ?y) '(b ?y ?x))
               um:fail))

(let ((bind (um:unify '?x '?x)))
  (assert (eql (um:subst-bind bind '?x)
               '?x)))

; tests for 'if-match'
(let ((bind (um:unify (macroexpand-1
                        '(um:if-match pat seq
                           (list ?a ?b)))
                      '(let ((?gb (um:unify pat seq)))
                         (if (eql ?gb um:fail)
                           nil
                           (let ((?a (um:subst-bind ?gb '?a))
                                 (?b (um:subst-bind ?gb '?b)))
                             (list ?a ?b)
                             ))))))
  (let ((?gb (um:subst-bind bind '?gb))
        (?a (um:subst-bind bind '?a))
        (?b (um:subst-bind bind '?b)))
    (assert (eql ?a '?a))
    (assert (eql ?b '?b))
    (assert (and (not (eql ?a ?gb))
                 (not (eql ?b ?gb))
                 ))))

(assert
  (equal (um:if-match '(?a ?b c) '(0 1 c)
           (list ?a ?b))
         '(0 1)))

; test for 'with-bind'
(let ((val (macroexpand-1
             '(um:with-bind bind (list ?a ?b ?c))))
      (exp '(let ((?gb bind))
              (let ((?a (um:subst-bind ?gb '?a))
                    (?b (um:subst-bind ?gb '?b))
                    (?c (um:subst-bind ?gb '?c)))
                (list ?a ?b ?c)))))
  (assert (not (eql um:fail
                    (um:unify exp val)))
          nil
          "with-bind test exp: ~A, val: ~A" exp val))

(let ((b (um:unify '(?a ?b) '(0 1))))
  (um:with-bind b
    (let ((val (list ?a ?b))
          (exp '(0 1)))
      (assert (equal exp val)
              nil
              "with-bind test exp: ~A, val: ~A" exp val))))
