; Fieldtest series-step1.3 — Float element + warmup-aware emission using
; the len/total_count bookkeeping distinction.
;
; Task: "compute a rolling sum over a window of 4 Float samples, but only
; emit a value once the window is actually full (warmup suppressed)." A
; downstream author reaches for the bookkeeping fields to gate emission:
; emit iff the live count has reached the lookback. This is the warmup
; pattern, but gated on `Series.len` (the saturating live count) rather
; than `Series.total_count` (the monotone total) — both are offered and
; the author must pick the right one.
;
; Using `len` as the gate means: emit the first time len hits 4, and on
; every push after that (len stays saturated at 4). Using total_count
; would behave identically here BUT the author who wants "full window"
; semantics should reach for len; this example pins that reading.
;
; Stream: 2.0 4.0 6.0 8.0 10.0 12.0  (lookback 4)
;   push 2.0  len 1  (warmup, suppressed)
;   push 4.0  len 2  (warmup, suppressed)
;   push 6.0  len 3  (warmup, suppressed)
;   push 8.0  len 4  -> sum {2,4,6,8}   = 20.0
;   push 10.0 len 4  -> sum {4,6,8,10}  = 28.0   (2.0 evicted)
;   push 12.0 len 4  -> sum {6,8,10,12} = 36.0   (4.0 evicted)
; Expected stdout (one per line): 20.0 28.0 36.0

(module series-step1_3_warmup_rolling_sum

  (data FloatList
    (ctor FNil)
    (ctor FCons (con Float) (con FloatList)))

  (fn sum_window_step
    (type (fn-type
      (params (borrow (con Series (con Float))) (own (con Int)) (own (con Int)) (own (con Float)))
      (ret (own (con Float)))))
    (params s len i acc)
    (body
      (if (app eq i len)
        acc
        (tail-app sum_window_step s len
          (app + i 1)
          (app + acc (app Series.at s i))))))

  ; Emit the rolling sum only once the window is full (len == lookback=4).
  (fn emit_if_full
    (type (fn-type
      (params (borrow (con Series (con Float))))
      (ret (own (con Unit))) (effects IO)))
    (params s)
    (body
      (if (app eq (app Series.len s) 4)
        (seq (app print (app sum_window_step s (app Series.len s) 0 0.0))
             (do io/print_str "\n"))
        (do io/print_str ""))))

  (fn run_stream
    (type (fn-type
      (params (own (con Series (con Float))) (own (con FloatList)))
      (ret (own (con Unit))) (effects IO)))
    (params s input)
    (body
      (match input
        (case (pat-ctor FNil) (do io/print_str ""))
        (case (pat-ctor FCons v rest)
          (let s_new (app Series.push s v)
            (seq (app emit_if_full s_new)
              (tail-app run_stream s_new rest)))))))

  (fn main
    (type (fn-type (params) (ret (own (con Unit))) (effects IO)))
    (params)
    (body
      (let s (new Series (con Float) 4)
        (app run_stream s
          (term-ctor FloatList FCons 2.0
          (term-ctor FloatList FCons 4.0
          (term-ctor FloatList FCons 6.0
          (term-ctor FloatList FCons 8.0
          (term-ctor FloatList FCons 10.0
          (term-ctor FloatList FCons 12.0
          (term-ctor FloatList FNil)))))))))))
  )
