;; clojure.core — lazy tier. Canonical CLJS-based lazy seq fns.
;; Loaded after 30-macros.clj, so lazy-seq macro is available.
;;
;; Each fn ported from CLJS core.cljs, stripped of chunked-seq branches.

;; --- distinct --- (transducer + lazy collection arity; value-based dedup)
(defn distinct
  ([]
   (fn [rf]
     (let [seen (volatile! #{})]
       (fn ([] (rf)) ([result] (rf result))
         ([result input]
          (if (contains? @seen input)
            result
            (do (vswap! seen conj input) (rf result input))))))))
  ([coll]
   (let [step (fn step [xs seen]
                (lazy-seq
                  ((fn [[f :as xs] seen]
                     (when-let [s (seq xs)]
                       (if (contains? seen f)
                         (recur (rest s) seen)
                         (cons f (step (rest s) (conj seen f))))))
                    xs seen)))]
     (step coll #{}))))


;; --- keep ---
(defn keep
  ([f]
   (fn [rf]
     (fn ([] (rf)) ([result] (rf result))
       ([result input]
        (let [v (f input)]
          (if (nil? v) result (rf result v)))))))
  ([f coll]
   (lazy-seq
    (when-let [s (seq coll)]
      (let [x (f (first s))]
        (if (nil? x)
          (keep f (rest s))
          (cons x (keep f (rest s)))))))))

;; --- keep-indexed ---
(defn keep-indexed
  ([f]
   (fn [rf]
     (let [ia (volatile! -1)]
       (fn ([] (rf)) ([result] (rf result))
         ([result input]
          (let [i (vswap! ia inc)
                v (f i input)]
            (if (nil? v) result (rf result v))))))))
  ([f coll]
   (letfn [(keepi [idx coll]
             (lazy-seq
               (when-let [s (seq coll)]
                 (let [x (f idx (first s))]
                   (if (nil? x)
                     (keepi (inc idx) (rest s))
                     (cons x (keepi (inc idx) (rest s))))))))]
     (keepi 0 coll))))

;; --- map-indexed ---
(defn map-indexed
  ([f]
   (fn [rf]
     (let [i (volatile! -1)]
       (fn ([] (rf)) ([result] (rf result))
         ([result input] (rf result (f (vswap! i inc) input)))))))
  ([f coll]
   (letfn [(mapi [idx coll]
             (lazy-seq
               (when-let [s (seq coll)]
                 (cons (f idx (first s)) (mapi (inc idx) (rest s))))))]
     (mapi 0 coll))))

;; --- cycle ---
(defn cycle [coll]
  (if-let [vals (seq coll)]
    (let [n (count vals)]
      (letfn [(cstep [i]
                (lazy-seq
                  (cons (nth vals (mod i n)) (cstep (inc i)))))]
        (cstep 0)))
    ()))

;; --- repeatedly --- ((f) throws on a non-fn; (take n …) throws on a non-number
;; count — both now enforced in the seed (jolt-call / core-take), so the canonical
;; CLJ form matches the repeatedly.cljc exception cases.)
(defn repeatedly
  ([f] (lazy-seq (cons (f) (repeatedly f))))
  ([n f] (take n (repeatedly f))))

;; --- repeat ---
(defn repeat
  ([x] (lazy-seq (cons x (repeat x))))
  ([n x] (take n (repeat x))))

;; --- iterate ---
(defn iterate [f x]
  (lazy-seq (cons x (iterate f (f x)))))


;; --- partition-all --- (transducer + [n coll] + [n step coll])
;; The collection arities realize EXACTLY n per chunk via a first/rest loop and
;; continue from the advanced cursor (not a re-drop / nthrest), so they realize
;; minimally — matching the Janet pstep the §6.3 laziness counters were written
;; against. (A take/nthrest form is correct but over-realizes.)
(defn partition-all
  ([n]
   (fn [rf]
     (let [a (volatile! [])]
       (fn
         ([] (rf))
         ([result]
          (let [result (if (zero? (count @a))
                         result
                         (let [v @a] (vreset! a []) (unreduced (rf result v))))]
            (rf result)))
         ([result input]
          (vswap! a conj input)
          (if (= n (count @a))
            (let [v @a] (vreset! a []) (rf result v))
            result))))))
  ([n coll]
   (letfn [(go [s]
             (lazy-seq
               (when (seq s)
                 (loop [i 0 chunk [] cur s]
                   (if (and (< i n) (seq cur))
                     (recur (inc i) (conj chunk (first cur)) (rest cur))
                     (cons chunk (go cur)))))))]
     (go coll)))
  ([n step coll]
   (letfn [(go [s]
             (lazy-seq
               (when (seq s)
                 (cons (take n s) (go (nthrest s step))))))]
     (go coll))))

;; --- Phase 2 leaf batch 3 (jolt-ded): canonical lazy + transducer arities ----

(defn interpose
  ([sep]
   (fn [rf]
     (let [started (volatile! false)]
       (fn
         ([] (rf))
         ([result] (rf result))
         ([result input]
          (if (deref started)
            (let [sepr (rf result sep)]
              (if (reduced? sepr)
                sepr
                (rf sepr input)))
            (do (vreset! started true)
                (rf result input))))))))
  ([sep coll]
   (drop 1 (interleave (repeat sep) coll))))

(defn take-nth
  ([n]
   (fn [rf]
     (let [iv (volatile! -1)]
       (fn
         ([] (rf))
         ([result] (rf result))
         ([result input]
          (let [i (vswap! iv inc)]
            (if (zero? (rem i n))
              (rf result input)
              result)))))))
  ([n coll]
   (lazy-seq
     (when-let [s (seq coll)]
       (cons (first s) (take-nth n (drop n s)))))))

;; --- pmap family (jolt-oeu): parallel map over real-thread futures ----------
;; Each element's work runs on its own OS thread with SNAPSHOT semantics
;; (futures marshal captured state — pure fns only, mutations don't propagate
;; back). All futures are spawned up front (doall), then derefed in order:
;; coarse-grained work only, as with Clojure's pmap.

(defn pmap
  ([f coll]
   (map deref (doall (map (fn [x] (future (f x))) coll))))
  ([f coll & colls]
   (pmap (fn [xs] (apply f xs)) (apply map vector coll colls))))

(defn pcalls [& fns] (pmap (fn [f] (f)) fns))
