;;; stream library

;;; This file is part of the Scheme Untergrund Library.

;;; Copyright (c) 2002-2003 by Martin Gasbichler.
;;; Copyright (c) 2002 by Eric Knauel
;;; Copyright (c) 2002 by Matthias Neubauer
;;; For copyright information, see the file COPYING which comes with
;;; the distribution.

;;;; constructors

(define the-empty-stream
  (delay '()))

;; -> stream
(define (make-empty-stream)
  the-empty-stream)

;; a stream(a) -> stream(a)
(define (make-stream head tail-stream)
  (delay
    (cons head
	  tail-stream)))

;; (() -> a) stream(a) -> stream(a)
(define (make-stream-lazily head-thunk tail-stream)
  (delay
    (cons (head-thunk)
	  tail-stream)))

(define-syntax stream
  (syntax-rules ()
   ((stream ?h ?t) (make-stream-lazily (lambda () ?h) ?t))))

;;;; predicates

;; stream -> bool
(define (stream-empty? s)
  (null? (force s)))

;;;; destructors

;; stream -> a
(define (stream-head stream)
  (car (force stream)))

;; stream -> stream
(define (stream-tail stream)
  (cdr (force stream)))

;;;; the usual suspects

;; CHANGED
;; (a -> b) stream(a) -> stream(b)
(define (stream-map proc stream)
  (delay
    (let ((stream_ (force stream)))
      (cond
       ((null? stream_) stream_)
       (else
	(cons (proc (car stream_))
	      (stream-map proc (cdr stream_))))))))

;; NEW
;; (a -> b) . list(stream(a)) -> stream(b)
(define (stream-zip-with proc . streams)
  (delay
    (let* ((streams_ (map force streams))
	   (finished? 
	    (or (null? streams_)
		(let loop ((streams_ streams_))
		  (and (not (null? streams_))
		       (or (null? (car streams_))
			   (loop (cdr streams_))))))))
      (if finished? 
	  '()
	  (cons (apply proc (map car streams_))
		(apply stream-zip-with proc (map cdr streams_)))))))

;; CHANGED
;; (a ->* ) -> stream(a) ->*
(define (stream-for-each proc stream)
  (if (not (stream-empty? stream))
      (begin
	(proc (stream-head stream))
	(stream-for-each proc (stream-tail stream)))))

;; stream-filter : (a -> bool) stream(a) -> stream(a)
(define (stream-filter pred? stream)
  (delay 
    (let ((stream_ (force stream)))
      (cond
       ((null? stream_) stream_)
       (else
	(let ((head (car stream_)))
	  (if (pred? head)
	      (cons head
		    (stream-filter pred? (cdr stream_)))
	      (force (stream-filter pred? (cdr stream_))))))))))

;; stream-filter-map : (a -> (union b #f)) -> stream(a) -> stream(b)
(define (stream-filter-map proc stream)
  (delay
    (let ((stream_ (force stream)))
	(cond
	 ((null? stream_) stream_)
	 (else
	  (let ((head (proc (car stream_))))
	    (if head
		(cons head
		      (stream-filter-map proc (cdr stream_)))
		(force (stream-filter-map proc (cdr stream_))))))))))

;; CHANGED
;; stream-unfold : (b -> (union (cons a b) #f)) b -> stream(a)
(define (stream-unfold gen-fun start)
  (delay
    (let ((res (gen-fun start)))
      (if res
	  (cons (car res)
		(stream-unfold gen-fun (cdr res)))
	  '()))))

;; NEW
;;
;; this is Richard Bird and Jeremy Gibbon's "stream" from AFP4
;; transforms a stream by alternating between producer and consumer
;;
;; stream-transform : (b -> (cons list(a) b)) ->
;;                    (b c -> b)
;;                    b
;;                    stream(c) ->
;;                    stream(a)
;;
(define (stream-transform producer consumer state stream)
  (delay
    (let* ((ys&state1 (producer state))
	   (ys (car ys&state1))
	   (state1 (cdr ys&state1))
	   (stream_ (force stream))
	   (as1
	    (cond
	     ((null? stream_) (delay stream_))
	     (else
	      (stream-transform producer 
				consumer
				(consumer state1 (car stream_))
				(cdr stream_))))))
      (force (stream-prepend ys as1)))))

;; stream-take : integer stream(a) -> list(a)
(define (stream-take n stream)
  (if (zero? n) '()
      (let ((stream_ (force stream)))
	(if (null? stream_) stream_
	    (cons (car stream_)
		  (stream-take (- n 1)
			       (cdr stream_)))))))

;; NEW
;; stream-drop : integer stream(a) -> stream(a)
(define (stream-drop n stream)
  (if (zero? n) stream
      (delay 
	(let ((stream_ (force stream)))
	  (if (null? stream_) stream_
	      (if (> n 0)
		  (force (stream-drop (- n 1) (cdr stream_)))
		  (error "stream-drop: negative argument")))))))

;; stream-fold-right : (a b -> b) b stream(a) -> b
(define (stream-fold-right kons knil stream)
  (let loop ((stream stream))
    (if (stream-empty? stream)
	knil
	(kons (stream-head stream)
	      (loop (stream-tail stream))))))

;; CHANGED
;; stream-foldr-lazily : (a (promise b) -> (promise b))
;;                       (promise b)
;;                       stream(a) -> 
;;                       (promise b)
(define (stream-fold-right-lazily kons knil stream)
  (delay
    (let ((stream_ (force stream)))
      (cond
       ((null? stream_) (force knil))
       (else
	(force (kons (car stream_) 
		     (stream-fold-right-lazily kons knil (cdr stream)))))))))

;; NEW
;; stream-prepend : list(a) stream(a) -> stream(a)
(define (stream-prepend l s)
  (if (null? l)
      s
      (delay
	(cons (car l) 
	      (stream-prepend (cdr l) s)))))

;; CHANGED
;; list->stream : list(a) -> stream(a)
(define (list->stream l)
  (stream-unfold (lambda (l) 
		   (and (not (null? l))
			l))
		 l))

;; CHANGED
;; stream-from : integer -> stream(integer)
(define (stream-from n)
  (stream-unfold (lambda (s) (cons s (+ s 1))) n))

;; NEW
;; stream-from-to : integer integer -> stream(integer)
(define (stream-from-to n m)
  (stream-unfold 
   (lambda (s) 
     (and (<= s m)
	  (cons s (+ s 1))))
   n))

;; NEW
;; stream-from-then : integer integer -> stream(integer)
(define (stream-from-then n n1)
  (stream-unfold (lambda (s) (cons s (+ s (- n1 n)))) n))

;; NEW
;; stream-from-to : integer integer integer -> stream(integer)
(define (stream-from-then-to n n1 m)
  (stream-unfold 
   (lambda (s) 
     (and (<= s m)
	  (cons s (+ s (- n1 n)))))
   n))

;; NEW
;; stream-ref : stream(a) integer -> a
(define (stream-ref s n)
  (if (zero? n)
      (stream-head s)
      (if (> n 0)
	  (stream-ref (stream-tail s) (- n 1))
	  (error "stream-ref: invalid reference"))))

;; stream-iterate : (a -> a) -> a -> stream(a)
(define (stream-iterate f a)
  (stream-unfold (lambda (s) (cons s (f s))) a))

;; NEW
;; stream-repeat : a -> stream(a)
(define (stream-repeat a)
  (letrec ((s (delay (cons a s))))
    s))

;; NEW 
;; stream-cycle : list(a) -> stream(a)
(define (stream-cycle l)
  (letrec ((s (stream-prepend l s)))
    s))

;; NEW
;; stream-take-while : (a -> boolean) stream(a) -> stream(a)
(define (stream-take-while p stream)
  (delay
    (let ((stream_ (force stream)))
      (cond 
       ((null? stream_) stream_)
       (else
	(let ((head (car stream_)))
	  (if (p head)
	      (cons head (stream-take-while p (cdr stream_)))
	      '())))))))

;; NEW
;; stream-drop-while : (a -> boolean) stream(a) -> stream(a)
(define (stream-drop-while p stream)
  (delay
    (let ((stream_ (force stream)))
      (cond 
       ((null? stream_) stream_)
       (else
	(let ((head (car stream_)))
	  (if (p head)
	      (force (stream-drop-while p (cdr stream_)))
	      stream_)))))))

;; some tests

;(define test-stream-zip-with
;  (stream-zip-with
;   +
;   (stream-from-to 1 10)
;   (stream-from 42)
;   (stream-from 100)))

;(define test-stream-transform
;  (stream-transform 
;   (lambda (state) 
;     (cons (list state (* state state))
;	   (+ state 1)))
;   (lambda (state1 c)
;     (* state1 c))
;   13
;   (stream-from 1)))