;; Phase 5 collection tests — methods on SequenceableCollection / Array /
;; String / Symbol. Emphasis on the inherited-from-SequenceableCollection
;; methods that work uniformly across Array, String, Symbol.

(set! st-test-pass 0)
(set! st-test-fail 0)
(set! st-test-fails (list))

(st-bootstrap-classes!)
(define ev (fn (src) (smalltalk-eval src)))
(define evp (fn (src) (smalltalk-eval-program src)))

;; ── 1. inject:into: (fold) ──
(st-test "Array inject:into: sum"
  (ev "#(1 2 3 4) inject: 0 into: [:a :b | a + b]") 10)

(st-test "Array inject:into: product"
  (ev "#(2 3 4) inject: 1 into: [:a :b | a * b]") 24)

(st-test "Array inject:into: empty array → initial"
  (ev "#() inject: 99 into: [:a :b | a + b]") 99)

;; ── 2. detect: / detect:ifNone: ──
(st-test "detect: finds first match"
  (ev "#(1 3 5 7) detect: [:x | x > 4]") 5)

(st-test "detect: returns nil if no match"
  (ev "#(1 2 3) detect: [:x | x > 10]") nil)

(st-test "detect:ifNone: invokes block on miss"
  (ev "#(1 2 3) detect: [:x | x > 10] ifNone: [#none]")
  (make-symbol "none"))

;; ── 3. count: ──
(st-test "count: matches"
  (ev "#(1 2 3 4 5 6) count: [:x | x > 3]") 3)

(st-test "count: zero matches"
  (ev "#(1 2 3) count: [:x | x > 100]") 0)

;; ── 4. allSatisfy: / anySatisfy: ──
(st-test "allSatisfy: when all match"
  (ev "#(2 4 6) allSatisfy: [:x | x > 0]") true)

(st-test "allSatisfy: when one fails"
  (ev "#(2 4 -1) allSatisfy: [:x | x > 0]") false)

(st-test "anySatisfy: when at least one matches"
  (ev "#(1 2 3) anySatisfy: [:x | x > 2]") true)

(st-test "anySatisfy: when none match"
  (ev "#(1 2 3) anySatisfy: [:x | x > 100]") false)

;; ── 5. includes: ──
(st-test "includes: found"  (ev "#(1 2 3) includes: 2") true)
(st-test "includes: missing" (ev "#(1 2 3) includes: 99") false)

;; ── 6. indexOf: / indexOf:ifAbsent: ──
(st-test "indexOf: returns 1-based index"
  (ev "#(10 20 30 40) indexOf: 30") 3)

(st-test "indexOf: missing returns 0"
  (ev "#(1 2 3) indexOf: 99") 0)

(st-test "indexOf:ifAbsent: invokes block"
  (ev "#(1 2 3) indexOf: 99 ifAbsent: [-1]") -1)

;; ── 7. reject: (complement of select:) ──
(st-test "reject: removes matching"
  (ev "#(1 2 3 4 5) reject: [:x | x > 3]")
  (list 1 2 3))

;; ── 8. do:separatedBy: ──
(st-test "do:separatedBy: builds joined sequence"
  (evp
    "| seen |
     seen := #().
     #(1 2 3) do: [:e | seen := seen , (Array with: e)]
              separatedBy: [seen := seen , #(0)].
     ^ seen")
  (list 1 0 2 0 3))

;; Array with: shim for the test (inherited from earlier exception tests
;; in a separate suite — define here for safety).
(st-class-add-class-method! "Array" "with:"
  (st-parse-method "with: x | a | a := Array new: 1. a at: 1 put: x. ^ a"))

;; ── 9. String inherits the same methods ──
(st-test "String includes:"
  (ev "'abcde' includes: $c") true)

(st-test "String count:"
  (ev "'banana' count: [:c | c = $a]") 3)

(st-test "String inject:into: concatenates"
  (ev "'abc' inject: '' into: [:acc :c | acc , c , c]")
  "aabbcc")

(st-test "String allSatisfy:"
  (ev "'abc' allSatisfy: [:c | c = $a or: [c = $b or: [c = $c]]]") true)

;; ── 10. String primitives: at:, copyFrom:to:, do:, first, last ──
(st-test "String at: 1-indexed"  (ev "'hello' at: 1") "h")
(st-test "String at: middle"      (ev "'hello' at: 3") "l")
(st-test "String first"           (ev "'hello' first") "h")
(st-test "String last"            (ev "'hello' last") "o")
(st-test "String copyFrom:to:"
  (ev "'helloworld' copyFrom: 3 to: 7") "llowo")

;; ── 11. isEmpty / notEmpty go through SequenceableCollection too ──
;; (Already in primitives; the inherited versions agree.)
(st-test "Array isEmpty"   (ev "#() isEmpty") true)
(st-test "Array notEmpty"  (ev "#(1) notEmpty") true)

(list st-test-pass st-test-fail)
