;; lib/minikanren/tests/conde.sx — Phase 2 piece C tests for `conde`.
;;
;; Note on ordering: conde clauses are wrapped in Zzz (inverse-eta delay),
;; so applying the conde goal to a substitution returns thunks. mk-mplus
;; suspends-and-swaps when its left operand is paused, giving fair
;; interleaving — this is exactly what makes recursive relations work,
;; but it does mean conde answers can interleave rather than appear in
;; strict left-to-right clause order.

;; --- single-clause conde ≡ conj of clause body ---

(mk-test
  "conde-one-clause"
  (let ((q (mk-var "q"))) (run* q (conde ((== q 7)))))
  (list 7))

(mk-test
  "conde-one-clause-multi-goals"
  (let
    ((q (mk-var "q")))
    (run* q (conde ((fresh (x) (== x 5) (== q (list x x)))))))
  (list (list 5 5)))

;; --- multi-clause: produces one row per clause (interleaved) ---

(mk-test
  "conde-three-clauses-as-set"
  (let
    ((qs (run* q (conde ((== q 1)) ((== q 2)) ((== q 3))))))
    (and
      (= (len qs) 3)
      (and
        (some (fn (x) (= x 1)) qs)
        (and
          (some (fn (x) (= x 2)) qs)
          (some (fn (x) (= x 3)) qs)))))
  true)

(mk-test
  "conde-mixed-success-failure-as-set"
  (let
    ((qs (run* q (conde ((== q "a")) ((== 1 2)) ((== q "b"))))))
    (and
      (= (len qs) 2)
      (and (some (fn (x) (= x "a")) qs) (some (fn (x) (= x "b")) qs))))
  true)

;; --- conde with conjuncts inside clauses ---

(mk-test
  "conde-clause-conj-as-set"
  (let
    ((rows (run* q (fresh (x y) (conde ((== x 1) (== y 10)) ((== x 2) (== y 20))) (== q (list x y))))))
    (and
      (= (len rows) 2)
      (and
        (some (fn (r) (= r (list 1 10))) rows)
        (some (fn (r) (= r (list 2 20))) rows))))
  true)

;; --- nested conde ---

(mk-test
  "conde-nested-yields-three"
  (let
    ((qs (run* q (conde ((conde ((== q 1)) ((== q 2)))) ((== q 3))))))
    (and
      (= (len qs) 3)
      (and
        (some (fn (x) (= x 1)) qs)
        (and
          (some (fn (x) (= x 2)) qs)
          (some (fn (x) (= x 3)) qs)))))
  true)

;; --- conde all clauses fail → empty stream ---

(mk-test
  "conde-all-fail"
  (run*
    q
    (conde ((== 1 2)) ((== 3 4))))
  (list))

;; --- empty conde: no clauses ⇒ fail ---

(mk-test "conde-no-clauses" (run* q (conde)) (list))

(mk-tests-run!)
