;; lib/minikanren/tests/graph.sx — directed-graph reachability via patho.

(define
  test-edges
  (list (list :a :b) (list :b :c) (list :c :d) (list :a :c) (list :d :e)))

(define edgeo (fn (from to) (membero (list from to) test-edges)))

(define
  patho
  (fn
    (x y path)
    (conde
      ((edgeo x y) (== path (list x y)))
      ((fresh (z mid-path) (edgeo x z) (patho z y mid-path) (conso x mid-path path))))))

;; --- direct edges ---

(mk-test "patho-direct" (run* q (patho :a :b q)) (list (list :a :b)))

(mk-test "patho-no-direct-edge" (run* q (patho :e :a q)) (list))

;; --- indirect ---

(mk-test
  "patho-multi-hop"
  (let
    ((paths (run* q (patho :a :d q))))
    (and
      (= (len paths) 2)
      (and
        (some (fn (p) (= p (list :a :b :c :d))) paths)
        (some (fn (p) (= p (list :a :c :d))) paths))))
  true)

(mk-test
  "patho-to-leaf"
  (let
    ((paths (run* q (patho :a :e q))))
    (and
      (= (len paths) 2)
      (and
        (some (fn (p) (= p (list :a :b :c :d :e))) paths)
        (some (fn (p) (= p (list :a :c :d :e))) paths))))
  true)

;; --- enumeration with multiplicity ---
;; Each path contributes one tuple, so reachable nodes can repeat. Here
;; targets are: b (1 path), c (2 paths), d (2 paths), e (2 paths) = 7.

(mk-test
  "patho-enumerate-from-a-with-multiplicity"
  (let
    ((targets (run* q (fresh (path) (patho :a q path)))))
    (and
      (= (len targets) 7)
      (and
        (some (fn (t) (= t :b)) targets)
        (and
          (some (fn (t) (= t :c)) targets)
          (and
            (some (fn (t) (= t :d)) targets)
            (some (fn (t) (= t :e)) targets))))))
  true)

;; --- unreachable target ---

(mk-test "patho-unreachable" (run* q (patho :a :z q)) (list))

(mk-tests-run!)
