; intersecting number wheels

(define-syntax define-generator
  (lambda (x)
    (syntax-case x (lambda)
      ((stx name (lambda formals e0 e1 ...))
         (with-syntax ((yield (datum->syntax (syntax stx) 'yield)))
           (syntax (define name
             (lambda formals
               (let ((resume #f) (return #f))
                 (define yield
                   (lambda args
                     (call-with-current-continuation
                      (lambda (cont)
                        (set! resume cont)
                        (apply return args)))))
                 (lambda ()
                   (call-with-current-continuation
                    (lambda (cont)
                      (set! return cont)
                      (cond (resume (resume))
                      (else (let () e0 e1 ...)
                            (error 'name "unexpected return"))))))))))))
        ((stx (name . formals) e0 e1 ...)
          (syntax (stx name (lambda formals e0 e1 ...)))))))

(define (cycle xs)
  (define (last-pair xs)
    (if (null? (cdr xs)) xs
      (last-pair (cdr xs))))
  (set-cdr! (last-pair xs) xs) xs)

(define (take-gen n gen)
  (let loop ((n n) (gs (list)))
    (if (zero? n) (reverse gs)
      (loop (- n 1) (cons (gen) gs)))))

(define-generator (wheel xs)
  (let loop ((ws (cycle xs)))
    (if (integer? (car ws))
        (yield (car ws))
        (yield ((eval (car ws)))))
    (loop (cdr ws))))

(define a (wheel '(1 2 3)))
(display (take-gen 20 a)) (newline)

(define a (wheel '(1 b 2)))
(define b (wheel '(3 4)))
(display (take-gen 20 a)) (newline)

(define a (wheel '(1 d d)))
(define d (wheel '(6 7 8)))
(display (take-gen 20 a)) (newline)

(define a (wheel '(1 b c)))
(define b (wheel '(3 4)))
(define c (wheel '(5 b)))
(display (take-gen 20 a)) (newline)