#|
 | Copyright (c) 2017 Koz Ross 
 |
 | Permission is hereby granted, free of charge, to any person obtaining a copy of
 | this software and associated documentation files (the "Software"), to deal in
 | the Software without restriction, including without limitation the rights to
 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 | the Software, and to permit persons to whom the Software is furnished to do so,
 | subject to the following conditions:
 |
 | The above copyright notice and this permission notice shall be included in all
 | copies or substantial portions of the Software.
 |
 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 |#

(import 
  (scheme base)
  (srfi 121)
  (only (srfi 1) unfold)
  (cyclone test))

(define g
  (make-coroutine-generator
    (lambda (yield)
      (let loop ((i 0))
        (when (< i 3)
          (yield i)
          (loop (+ i 1)))))))

(define (for-each-digit proc n)
  (when (> n 0)
    (receive (div rem) (truncate/ n 10)
      (proc rem)
      (for-each-digit proc div))))

(test-group 
  "generator constructors"
  (test '() (generator->list (generator)))
  (test '(1 2 3) (generator->list (generator 1 2 3)))
  (test '(8 9 10) (generator->list (make-iota-generator 3 8)))
  (test '(8 10 12) (generator->list (make-iota-generator 3 8 2)))
  (test '(3 4 5 6) (generator->list (make-range-generator 3) 4))
  (test '(3 4 5 6 7) (generator->list (make-range-generator 3 8)))
  (test '(3 5 7) (generator->list (make-range-generator 3 8 2)))
  (test '(0 1 2) (generator->list g))
  (test '(1 2 3 4 5) 
        (generator->list (list->generator (list 1 2 3 4 5))))
  (test '(1 2 3 4 5) 
        (generator->list (vector->generator (vector 1 2 3 4 5))))
  (test '(5 4 3 2 1) 
        (generator->list (reverse-vector->generator (vector 1 2 3 4 5))))
  (test '(#\a #\b #\c #\d #\e)
        (generator->list (string->generator "abcde")))
  (test '(10 20 30) 
        (generator->list (bytevector->generator (bytevector 10 20 30))))
  (test '(5 4 3 2 1) 
        (generator->list (make-for-each-generator for-each-digit 12345)))
  (test '(0 2 4 6 8 10)
        (generator->list
          (make-unfold-generator (lambda (s) (> s 5))
                                 (lambda (s) (* s 2))
                                 (lambda (s) (+ s 1))
                                 0))))

(define g (make-range-generator 1 5))

(define g1 (generator 1 2 3))

(define g2 (generator 4 5 6 7))

(define (proc . args)
  (values (apply + args) (apply + args)))

(define (small? x) (< x 3))

(test-group
  "generator operators"
  (test '(a b 0 1) 
        (generator->list (gcons* 'a 'b (make-range-generator 0 2))))
  (test '(0 1 2 0 1)
        (generator->list (gappend (make-range-generator 0 3)
                                  (make-range-generator 0 2))))
  (test '() (generator->list (gappend)))
  (test '(15 22 31) (generator->list (gcombine proc 10 g1 g2)))
  (test '(1 3 5 7 9) 
        (generator->list (gfilter odd? (make-range-generator 1 11))))
  (test '(2 4 6 8 10)
        (generator->list (gremove odd? (make-range-generator 1 11))))
  (test '(1 2 3) (generator->list (gtake g 3)))
  (test '(4) (generator->list g))
  (test '(1 2) 
        (generator->list (gtake (make-range-generator 1 3) 3)))
  (test '(1 2 0)
        (generator->list (gtake (make-range-generator 1 3) 3 0)))
  (test '(3 4) 
        (generator->list (gdrop (make-range-generator 1 5) 2)))
  (set! g (make-range-generator 1 5))
  (test '(1 2) (generator->list (gtake-while small? g)))
  (set! g (make-range-generator 1 5))
  (test '(3 4) (generator->list (gdrop-while small? g)))
  (test '() 
        (generator->list (gdrop-while (lambda args #t) (generator 1 2 3))))
  (test '(0.0 1.0 0 2)
        (generator->list (gdelete 1 (generator 0.0 1.0 0 1 2))))
  (test '(0.0 0 2)
        (generator->list (gdelete 1 (generator 0.0 1.0 0 1 2) =)))
  (test '(a c e)
        (generator->list (gindex (list->generator '(a b c d e f))
                                 (list->generator '(0 2 4)))))
  (test '(a d e)
        (generator->list (gselect (list->generator '(a b c d e f))
                                  (list->generator '(#t #f #f #t #t #f)))))
  (test '(1 2 3)
        (generator->list (gdelete-neighbor-dups (generator 1 1 2 2 3 3) =)))
  (test '(1)
        (generator->list (gdelete-neighbor-dups (generator 1 2 3) 
                                                (lambda args #t)))))

(define n 0)

(test-group
  "generator consumers"
  (test '(1 2 3) (generator->list (generator 1 2 3 4 5) 3))
  (test '(5 4 3 2 1) (generator->reverse-list (generator 1 2 3 4 5)))
  (test #(1 2 3 4 5) (generator->vector (generator 1 2 3 4 5))) 
  (test #(1 2 3) (generator->vector (generator 1 2 3 4 5) 3))
  (test "abc" (generator->string (generator #\a #\b #\c)))
  (test 10 (generator-fold + 0 (generator 1 2 3 4)))
  (generator-for-each (lambda v (set! n (apply + v)))
                      (generator 1)
                      (generator 2)
                      (generator 3))
  (test 6 n)
  (test 3 (generator-find (lambda (x) (> x 2)) (make-range-generator 1 5)))
  (test 2 (generator-count odd? (make-range-generator 1 5)))
  (set! g (make-range-generator 2 5))
  (test-assert (generator-any odd? g))
  (test '(4) (generator->list g))
  (set! g (make-range-generator 2 5))
  (test-not (generator-every odd? g))
  (test '(3 4) (generator->list g))
  (test '(#\a #\b #\c)
        (generator-unfold (make-for-each-generator string-for-each
                                                   "abc") unfold)))

(test-exit)