;; lib/relations/tests/shape.sx — extension: siblings, degree, undirected
;; connectivity.

(define relations-st-pass 0)
(define relations-st-fail 0)
(define relations-st-failures (list))

(define
  relations-st-check!
  (fn
    (name got expected)
    (if
      (= got expected)
      (set! relations-st-pass (+ relations-st-pass 1))
      (do
        (set! relations-st-fail (+ relations-st-fail 1))
        (append!
          relations-st-failures
          (str name "\n    expected: " expected "\n    got:      " got))))))

(define
  relations-st-subset?
  (fn
    (xs ys)
    (cond
      ((= (len xs) 0) true)
      ((relations-member? (first xs) ys)
        (relations-st-subset? (rest xs) ys))
      (else false))))

(define
  relations-st-set=?
  (fn
    (xs ys)
    (and
      (= (len xs) (len ys))
      (relations-st-subset? xs ys)
      (relations-st-subset? ys xs))))

;; A small tree plus a disconnected pair.
;;   parent: p->a, p->b, p->c, a->d ; q->r (disconnected)
;;   member: m->x, m->y (a different kind, same db)
(define
  relations-st-fixture
  (fn
    ()
    (relations-build-db
      (list
        (relations-rel (quote p) (quote a) (quote parent))
        (relations-rel (quote p) (quote b) (quote parent))
        (relations-rel (quote p) (quote c) (quote parent))
        (relations-rel (quote a) (quote d) (quote parent))
        (relations-rel (quote q) (quote r) (quote parent))
        (relations-rel (quote m) (quote x) (quote member))
        (relations-rel (quote m) (quote y) (quote member))))))

(define
  relations-st-run-all!
  (fn
    ()
    (let
      ((db (relations-st-fixture)))
      (do
        (relations-st-check!
          "siblings of a"
          (relations-st-set=?
            (relations-siblings db (quote a) (quote parent))
            (list (quote b) (quote c)))
          true)
        (relations-st-check!
          "only child has no siblings"
          (relations-siblings db (quote d) (quote parent))
          (list))
        (relations-st-check!
          "siblings respect kind"
          (relations-st-set=?
            (relations-siblings db (quote x) (quote member))
            (list (quote y)))
          true)
        (relations-st-check!
          "no cross-kind siblings"
          (relations-siblings db (quote a) (quote member))
          (list))
        (relations-st-check!
          "out-degree of p"
          (relations-out-degree db (quote p) (quote parent))
          3)
        (relations-st-check!
          "out-degree of a"
          (relations-out-degree db (quote a) (quote parent))
          1)
        (relations-st-check!
          "out-degree of leaf"
          (relations-out-degree db (quote d) (quote parent))
          0)
        (relations-st-check!
          "in-degree of a"
          (relations-in-degree db (quote a) (quote parent))
          1)
        (relations-st-check!
          "in-degree of root"
          (relations-in-degree db (quote p) (quote parent))
          0)
        (relations-st-check!
          "siblings are connected"
          (relations-connected? db (quote b) (quote c) (quote parent))
          true)
        (relations-st-check!
          "cousin connected (b <-> d)"
          (relations-connected? db (quote b) (quote d) (quote parent))
          true)
        (relations-st-check!
          "self connected"
          (relations-connected? db (quote a) (quote a) (quote parent))
          true)
        (relations-st-check!
          "disconnected components not connected"
          (relations-connected? db (quote a) (quote q) (quote parent))
          false)
        (relations-st-check!
          "directed-unreachable but undirected-connected"
          (and
            (not
              (relations-reachable? db (quote b) (quote c) (quote parent)))
            (relations-connected? db (quote b) (quote c) (quote parent)))
          true)
        (relations-st-check!
          "connectivity respects kind"
          (relations-connected? db (quote a) (quote x) (quote member))
          false)
        (do
          (relations/load!
            (list
              (relations-rel (quote g) (quote h) (quote parent))
              (relations-rel (quote g) (quote i) (quote parent))))
          (relations-st-check!
            "api siblings"
            (relations-st-set=?
              (relations/siblings (quote h) (quote parent))
              (list (quote i)))
            true)
          (relations-st-check!
            "api out-degree"
            (relations/out-degree (quote g) (quote parent))
            2)
          (relations-st-check!
            "api connected"
            (relations/connected? (quote h) (quote i) (quote parent))
            true)
          (relations/load! (list)))))))

(define
  relations-shape-tests-run!
  (fn
    ()
    (do
      (set! relations-st-pass 0)
      (set! relations-st-fail 0)
      (set! relations-st-failures (list))
      (relations-st-run-all!)
      {:failures relations-st-failures :total (+ relations-st-pass relations-st-fail) :passed relations-st-pass :failed relations-st-fail})))
