;; Chibi Scheme versions of any and every

(define (any pred ls)
  (if (null? (cdr ls))
      (pred (car ls))
      ((lambda (x) (if x x (any pred (cdr ls)))) (pred (car ls)))))

(define (every pred ls)
  (if (null? (cdr ls))
      (pred (car ls))
      (if (pred (car ls)) (every pred (cdr ls)) #f)))



;; generator
(define (generator . args)
        (lambda () (if (null? args)
                       (eof-object)
                       (let ((next (car args)))
                            (set! args (cdr args))
                            next))))


;; make-iota-generator
(define make-iota-generator
  (case-lambda ((count) (make-iota-generator count 0 1))
               ((count start) (make-iota-generator count start 1))
               ((count start step) (make-iota count start step))))

;; make-iota
(define (make-iota count start step)
  (lambda ()
    (cond
      ((<= count 0)
       (eof-object))
      (else
        (let ((result start))
          (set! count (- count 1))
          (set! start (+ start step))
          result)))))


;; make-range-generator
(define make-range-generator
        (case-lambda ((start end) (make-range-generator start end 1))
                     ((start) (make-infinite-range-generator start))
                     ((start end step)
                      (set! start (- (+ start step) step))
                      (lambda () (if (< start end)
                                     (let ((v start))
                                          (set! start (+ start step))
                                          v)
                                     (eof-object))))))

(define (make-infinite-range-generator start)
  (lambda ()
    (let ((result start))
      (set! start (+ start 1))
      result)))



;; make-coroutine-generator
(define (make-coroutine-generator proc)
        (define return #f)
        (define resume #f)
        (define yield (lambda (v) (call/cc (lambda (r) (set! resume r) (return v)))))
        (lambda () (call/cc (lambda (cc) (set! return cc)
                                         (if resume
                                             (resume (if #f #f))  ; void? or yield again?
                                             (begin (proc yield)
                                                    (set! resume (lambda (v) (return (eof-object))))
                                                    (return (eof-object))))))))


;; list->generator
(define (list->generator lst)
        (lambda () (if (null? lst)
                       (eof-object)
                       (let ((next (car lst)))
                            (set! lst (cdr lst))
                            next))))


;; vector->generator
(define vector->generator
        (case-lambda ((vec) (vector->generator vec 0 (vector-length vec))) 
                     ((vec start) (vector->generator vec start (vector-length vec))) 
                     ((vec start end)
                      (lambda () (if (>= start end)
                                     (eof-object)
                                     (let ((next (vector-ref vec start)))
                                          (set! start (+ start 1))
                                          next))))))


;; reverse-vector->generator
(define reverse-vector->generator
        (case-lambda ((vec) (reverse-vector->generator vec 0 (vector-length vec))) 
                     ((vec start) (reverse-vector->generator vec start (vector-length vec))) 
                     ((vec start end)
                      (lambda () (if (>= start end)
                                     (eof-object)
                                     (let ((next (vector-ref vec (- end 1))))
                                          (set! end (- end 1))
                                          next))))))


;; string->generator
(define string->generator
        (case-lambda ((str) (string->generator str 0 (string-length str))) 
                     ((str start) (string->generator str start (string-length str))) 
                     ((str start end)
                      (lambda () (if (>= start end)
                                     (eof-object)
                                     (let ((next (string-ref str start)))
                                          (set! start (+ start 1))
                                          next))))))


;; bytevector->generator
(define bytevector->generator
        (case-lambda ((str) (bytevector->generator str 0 (bytevector-length str))) 
                     ((str start) (bytevector->generator str start (bytevector-length str))) 
                     ((str start end)
                      (lambda () (if (>= start end)
                                     (eof-object)
                                     (let ((next (bytevector-u8-ref str start)))
                                          (set! start (+ start 1))
                                          next))))))


;; make-for-each-generator
;FIXME: seems to fail test
(define (make-for-each-generator for-each obj)
        (make-coroutine-generator (lambda (yield) (for-each yield obj))))


;; make-unfold-generator
(define (make-unfold-generator stop? mapper successor seed)
        (make-coroutine-generator (lambda (yield) 
            (let loop ((s seed))
                 (if (stop? s)
                     (if #f #f)
                     (begin (yield (mapper s))
                            (loop (successor s))))))))


;; gcons*
(define (gcons* . args)
        (lambda () (if (null? args)
                       (eof-object)
                       (if (= (length args) 1)
                           ((car args))
                           (let ((v (car args)))
                                (set! args (cdr args))
                                v)))))
        

;; gappend
(define (gappend . args)
        (lambda () (if (null? args)
                       (eof-object)
                       (let loop ((v ((car args))))
                                 (if (eof-object? v)
                                     (begin (set! args (cdr args))
                                            (if (null? args)
                                                (eof-object) 
                                                (loop ((car args)))))
                                     v)))))
        


;; gcombine
(define (gcombine proc seed . gens)
  (lambda ()
    (define items (map (lambda (x) (x)) gens))
    (if (any eof-object? items)
      (eof-object)
      (let ()
        (define-values (value newseed) (apply proc (append items (list seed))))
        (set! seed newseed)
        value))))

;; gfilter
(define (gfilter pred gen)
        (lambda () (let loop ()
                        (let ((next (gen)))
                             (if (or (eof-object? next) 
                                     (pred next))
                                 next
                                 (loop))))))
             


;; gremove
(define (gremove pred gen)
        (gfilter (lambda (v) (not (pred v))) gen))
             


;; gtake
(define gtake
        (case-lambda ((gen k) (gtake gen k (eof-object)))
                     ((gen k padding)
                      (make-coroutine-generator (lambda (yield)
                          (if (> k 0) 
                              (let loop ((i 0) (v (gen)))
                                   (begin (if (eof-object? v) (yield padding) (yield v))
                                          (if (< (+ 1 i) k)
                                              (loop (+ 1 i) (gen))
                                              (eof-object))))
                              (eof-object)))))))
             


;; gdrop
(define (gdrop gen k)
  (lambda () (do () ((<= k 0)) (set! k (- k 1)) (gen))
          (gen)))
             


;; gdrop-while
(define (gdrop-while pred gen)
  (define found #f)
  (lambda ()
    (let loop ()
      (let ((val (gen)))
        (cond (found val)
              ((and (not (eof-object? val)) (pred val)) (loop))
              (else (set! found #t) val))))))


;; gtake-while
(define (gtake-while pred gen)
        (lambda () (let ((next (gen)))
                        (if (eof-object? next)
                            next
                            (if (pred next)
                                next
                                (begin (set! gen (generator))
                                       (gen)))))))
             


;; gdelete
(define gdelete
        (case-lambda ((item gen) (gdelete item gen equal?))
                     ((item gen ==)
                      (lambda () (let loop ((v (gen)))
                                      (cond
                                        ((eof-object? v) (eof-object))
                                        ((== item v) (loop (gen)))
                                        (else v)))))))
             


;; gdelete-neighbor-dups
(define gdelete-neighbor-dups
        (case-lambda ((gen)
                      (gdelete-neighbor-dups gen equal?))
                     ((gen ==)
                      (define firsttime #t)
                      (define prev #f)
                      (lambda () (if firsttime
                                     (begin (set! firsttime #f)
                                            (set! prev (gen))
                                            prev)
                                     (let loop ((v (gen)))
                                          (cond
                                            ((eof-object? v)
                                              v)
                                            ((== prev v)
                                             (loop (gen)))
                                            (else
                                             (set! prev v)
                                              v))))))))


;; gindex
(define (gindex value-gen index-gen)
  (let ((done? #f) (count 0))
    (lambda ()
      (if done?
        (eof-object)
        (let loop ((value (value-gen)) (index (index-gen)))
          (cond
            ((or (eof-object? value) (eof-object? index))
             (set! done? #t)
             (eof-object))
            ((= index count)
             (set! count (+ count 1))
             value)
            (else
              (set! count (+ count 1))
              (loop (value-gen) index))))))))


;; gselect
(define (gselect value-gen truth-gen)
  (let ((done? #f))
    (lambda ()
      (if done?
        (eof-object)
        (let loop ((value (value-gen)) (truth (truth-gen)))
          (cond
            ((or (eof-object? value) (eof-object? truth))
             (set! done? #t)
             (eof-object))
            (truth value)
            (else (loop (value-gen) (truth-gen)))))))))


;; generator->list
(define generator->list
        (case-lambda ((gen) (generator->list gen +inf.0))
                     ((gen n) 
                      (let ((next (gen)))
                           (if (or (eof-object? next)
                                   (= 0 n))
                               '()
                               (cons next (generator->list gen (- n 1))))))))
     


;; generator->reverse-list
(define generator->reverse-list
        (case-lambda ((gen) (generator->reverse-list gen +inf.0))
                     ((gen n)
                      (define (build-reversed lst m)
                              (let ((next (gen)))
                                   (if (or (eof-object? next)
                                           (>= m n))
                                       lst
                                       (build-reversed (cons next lst) (+ 1 m)))))
                      (build-reversed '() 0))))
     
;; generator->vector
(define generator->vector
        (case-lambda ((gen) (list->vector (generator->list gen)))
                     ((gen n) (list->vector (generator->list gen n)))))


;; generator->vector!
(define (generator->vector! vector at gen)
  (let loop ((value (gen)) (count 0) (at at))
    (cond
      ((eof-object? value) count)
      ((>= at (vector-length vector)) count)
      (else (begin
              (vector-set! vector at value)
              (loop (gen) (+ count 1) (+ at 1)))))))


;; generator->string
(define generator->string
        (case-lambda ((gen) (list->string (generator->list gen)))
                     ((gen n) (list->string (generator->list gen n)))))




;; generator-fold
(define (generator-fold f seed . gs)
        (define (inner-fold seed)
                (let ((vs (map (lambda (g) (g)) gs)))
                     (if (any eof-object? vs)
                         seed
                         (inner-fold (apply f (append vs (list seed)))))))
        (inner-fold seed))
     


;; generator-for-each
(define (generator-for-each f . gs)
        (let loop ()
             (let ((vs (map (lambda (g) (g)) gs)))
                  (if (any eof-object? vs)
                      (if #f #f)
                      (begin (apply f vs) 
                             (loop))))))


;; generator-find
(define (generator-find pred g)
        (let loop ((v (g)))
                  ; A literal interpretation might say it only terminates on #eof if (pred #eof) but I think this makes more sense...
                  (if (or (pred v) (eof-object? v))
                      v
                      (loop (g)))))


;; generator-count
(define (generator-count pred g)
        (generator-fold (lambda (v n) (if (pred v) (+ 1 n) n)) 0 g))


;; generator-any
(define (generator-any pred g)
        (let loop ((v (g)))
             (if (eof-object? v)
                 #f
                 (if (pred v)
                     #t
                     (loop (g))))))


;; generator-every
(define (generator-every pred g)
        (let loop ((v (g)))
             (if (eof-object? v)
                 #t
                 (if (pred v)
                     (loop (g))
                     #f ; the spec would have me return #f, but I think it must simply be wrong...
                     ))))


;; generator-unfold 
(define (generator-unfold g unfold . args)
        (apply unfold eof-object? (lambda (x) x) (lambda (x) (g)) (g) args))



