;; lib/minikanren/tests/condu.sx — Phase 2 piece D tests for `onceo` and `condu`.

;; --- onceo: at most one answer ---

(mk-test
  "onceo-single-success-passes-through"
  (let
    ((q (mk-var "q")))
    (let
      ((res (stream-take 5 ((onceo (== q 7)) empty-s))))
      (map (fn (s) (mk-walk q s)) res)))
  (list 7))

(mk-test
  "onceo-multi-success-trimmed-to-one"
  (let
    ((q (mk-var "q")))
    (let
      ((res (stream-take 5 ((onceo (mk-disj (== q 1) (== q 2) (== q 3))) empty-s))))
      (map (fn (s) (mk-walk q s)) res)))
  (list 1))

(mk-test
  "onceo-failure-stays-failure"
  ((onceo (== 1 2)) empty-s)
  (list))

(mk-test
  "onceo-conde-trimmed"
  (let
    ((q (mk-var "q")))
    (let
      ((res (stream-take 5 ((onceo (conde ((== q "a")) ((== q "b")))) empty-s))))
      (map (fn (s) (mk-walk q s)) res)))
  (list "a"))

;; --- condu: first clause with successful head wins ---

(mk-test
  "condu-first-clause-wins"
  (let
    ((q (mk-var "q")))
    (let
      ((res (stream-take 10 ((condu ((== q 1)) ((== q 2))) empty-s))))
      (map (fn (s) (mk-walk q s)) res)))
  (list 1))

(mk-test
  "condu-skips-failing-head"
  (let
    ((q (mk-var "q")))
    (let
      ((res (stream-take 10 ((condu ((== 1 2)) ((== q 100)) ((== q 200))) empty-s))))
      (map (fn (s) (mk-walk q s)) res)))
  (list 100))

(mk-test
  "condu-all-fail-empty"
  ((condu ((== 1 2)) ((== 3 4)))
    empty-s)
  (list))

(mk-test "condu-empty-clauses-fail" ((condu) empty-s) (list))

;; --- condu commits head's first answer; rest-goals can still backtrack
;;     within that committed substitution but cannot revisit other heads. ---

(mk-test
  "condu-head-onceo-rest-runs"
  (let
    ((q (mk-var "q")) (r (mk-var "r")))
    (let
      ((res (stream-take 10 ((condu ((mk-disj (== q 1) (== q 2)) (== r 99))) empty-s))))
      (map (fn (s) (list (mk-walk q s) (mk-walk r s))) res)))
  (list (list 1 99)))

(mk-test
  "condu-rest-goals-can-fail-the-clause"
  (let
    ((q (mk-var "q")))
    (let
      ((res (stream-take 10 ((condu ((== q 1) (== 2 3)) ((== q 99))) empty-s))))
      (map (fn (s) (mk-walk q s)) res)))
  (list))

(mk-tests-run!)
