;;; 5_2.ss
;;; Copyright 1984-2017 Cisco Systems, Inc.
;;; 
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;; 
;;; http://www.apache.org/licenses/LICENSE-2.0
;;; 
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.

;;; list and pair functions

(begin
(define atom?
   (lambda (x)
      (not (pair? x))))

(define list?
   (lambda (x)
      (let loop ([hare x] [tortoise x])
         (if (pair? hare)
             (let ([hare (cdr hare)])
                (if (pair? hare)
                    (and (not (eq? hare tortoise))
                         (loop (cdr hare) (cdr tortoise)))
                    (null? hare)))
             (null? hare)))))

(define null?
   (lambda (x)
      (eq? x '())))

(define caar (lambda (x) (#2%caar x)))
(define cadr (lambda (x) (#2%cadr x)))
(define cdar (lambda (x) (#2%cdar x)))
(define cddr (lambda (x) (#2%cddr x)))
(define caaar (lambda (x) (#2%caaar x)))
(define caadr (lambda (x) (#2%caadr x)))
(define cadar (lambda (x) (#2%cadar x)))
(define caddr (lambda (x) (#2%caddr x)))
(define cdaar (lambda (x) (#2%cdaar x)))
(define cdadr (lambda (x) (#2%cdadr x)))
(define cddar (lambda (x) (#2%cddar x)))
(define cdddr (lambda (x) (#2%cdddr x)))
(define caaaar (lambda (x) (#2%caaaar x)))
(define caaadr (lambda (x) (#2%caaadr x)))
(define caadar (lambda (x) (#2%caadar x)))
(define caaddr (lambda (x) (#2%caaddr x)))
(define cadaar (lambda (x) (#2%cadaar x)))
(define cadadr (lambda (x) (#2%cadadr x)))
(define caddar (lambda (x) (#2%caddar x)))
(define cadddr (lambda (x) (#2%cadddr x))) 
(define cdaaar (lambda (x) (#2%cdaaar x)))
(define cdaadr (lambda (x) (#2%cdaadr x)))
(define cdadar (lambda (x) (#2%cdadar x)))
(define cdaddr (lambda (x) (#2%cdaddr x)))
(define cddaar (lambda (x) (#2%cddaar x)))
(define cddadr (lambda (x) (#2%cddadr x)))
(define cdddar (lambda (x) (#2%cdddar x)))
(define cddddr (lambda (x) (#2%cddddr x)))

(define $list-length)
(define length)
(define list-ref)
(define list-tail)
(define list-head)

(let ()
   (define improper-list-error
      (lambda (who ls)
         ($oops who "~s is not a proper list" ls)))
   (define circular-list-error
      (lambda (who ls)
         ($oops who "~s is circular" ls)))
   (define index-range-error
      (lambda (who ls n)
         ($oops who "index ~s is out of range for list ~s" n ls)))
   (define index-pair-error
      (lambda (who ls n)
         ($oops who "index ~s reaches a non-pair in ~s" n ls)))
   (define index-type-error
      (lambda (who n)
         ($oops who "index ~s is not an exact nonnegative integer" n)))
   (define index-range/improper-list-error
      (lambda (who tail ls n)
         (if (null? tail)
             (index-range-error who ls n)
             (index-pair-error who ls n))))
   (define list-length
     (lambda (ls who)
       (let loop ([hare ls] [i 0])
         (if (pair? hare)
             (let ([hare (cdr hare)])
               (if (pair? hare)
                   (if (fx<= i 10000)
                       (loop (cdr hare) (fx+ i 2))
                       (let loop ([hare hare] [tortoise hare] [i (fx+ i 1)])
                         (if (pair? hare)
                             (let ([hare (cdr hare)])
                               (if (pair? hare)
                                   (if (eq? hare tortoise)
                                       (circular-list-error who ls)
                                       (loop (cdr hare)
                                             (cdr tortoise)
                                             (fx+ i 2)))
                                   (if (null? hare)
                                       (fx+ i 1)
                                       (improper-list-error who ls))))
                             (if (null? hare)
                                 i
                                 (improper-list-error who ls)))))
                   (if (null? hare)
                       (fx+ i 1)
                       (improper-list-error who ls))))
             (if (null? hare)
                 i
                 (improper-list-error who ls))))))
   (define list-tail-cycle
     (lambda (ls n)
       (let loop ((fast (cdr ls)) (i 1))
         (if (eq? fast ls)
             (let ((i (remainder n i)))
               (do ((ls ls (cdr ls)) (i i (fx- i 1)))
                   ((fx= i 0) ls)))
             (loop (cdr fast) (fx+ i 1))))))
   (define fx-list-tail
     (lambda (fast slow i)
       (if (fx> i 0)
           (if (pair? fast)
               (let ((fast (cdr fast)))
                 (if (fx> i 1)
                     (if (not (eq? fast slow))
                         (if (pair? fast)
                             (fx-list-tail (cdr fast) (cdr slow) (fx- i 2))
                             (values 'error fast i))
                         (values 'cycle fast (fx- i 1)))
                     (values 'okay fast (fx- i 1))))
               (values 'error fast i))
           (values 'okay fast i))))
   (set! $list-length (lambda (ls who) (list-length ls who)))
   (set! length
      (lambda (ls)
         (list-length ls 'length)))
   (set! list-ref
     (lambda (ls n)
       (cond
         [(and (fixnum? n) (fx<= 0 n 1000))
          (let loop ([l ls] [i n])
            (if (pair? l)
                (if (fx> i 1)
                    (let ([l (cdr l)])
                      (if (pair? l)
                          (loop (cdr l) (fx- i 2))
                          (index-range/improper-list-error 'list-ref l ls n)))
                    (if (fx= i 0)
                        (car l)
                        (let ([l (cdr l)])
                          (if (pair? l)
                              (car l)
                              (index-range/improper-list-error 'list-ref l ls n)))))
                (index-range/improper-list-error 'list-ref l ls n)))]
         [(and (or (fixnum? n) (bignum? n)) (>= n 0))
          (let ((m (min n (most-positive-fixnum))))
            (call-with-values
              (lambda () (fx-list-tail ls ls m))
              (lambda (what fast i)
                (cond
                  [(and (eq? what 'okay) (pair? fast))
                   ; can't happen with bignum input
                   (car fast)]
                  [(eq? what 'cycle)
                   (car (list-tail-cycle fast (+ i (- n m))))]
                  [else (index-range/improper-list-error 'list-ref fast ls n)]))))]
         [else (index-type-error 'list-ref n)])))
   (set! list-tail
     (lambda (ls n)
       (cond
         [(and (fixnum? n) (fx<= 0 n 1000))
          (let loop ([l ls] [i n])
            (if (fx> i 1)
                (if (pair? l)
                    (let ([l (cdr l)])
                      (if (pair? l)
                          (loop (cdr l) (fx- i 2))
                          (index-range/improper-list-error 'list-tail l ls n)))
                    (index-range/improper-list-error 'list-tail l ls n))
                (if (fx= i 0)
                    l
                    (if (pair? l)
                        (cdr l)
                        (index-range/improper-list-error 'list-tail l ls n)))))]
         [(and (or (fixnum? n) (bignum? n)) (>= n 0))
          (let ((m (min n (most-positive-fixnum))))
            (call-with-values
              (lambda () (fx-list-tail ls ls m))
              (lambda (what fast i)
                (cond
                  [(eq? what 'okay) ; can't happen with bignum input
                   fast]
                  [(eq? what 'cycle) (list-tail-cycle fast (+ i (- n m)))]
                  [else (index-range/improper-list-error 'list-tail fast ls n)]))))]
         [else (index-type-error 'list-tail n)])))
   (set! list-head
     (lambda (orig-ls orig-n)
       (unless (and (fixnum? orig-n) (fx>= orig-n 0))
         ($oops 'list-head "invalid index ~s" orig-n))
       (let f ([ls orig-ls] [n orig-n])
         (cond
           [(fx<= n 1)
            (if (fx= n 0)
                '()
                (if (pair? ls)
                    (list (car ls))
                    (index-range/improper-list-error 'list-head ls orig-ls orig-n)))]
           [(pair? ls)
            (let ([a (car ls)] [ls (cdr ls)])
              (if (pair? ls)
                  (list* a (car ls) (f (cdr ls) (fx- n 2)))
                  (index-range/improper-list-error 'list-head ls orig-ls orig-n)))]
           [else (index-range/improper-list-error 'list-head ls orig-ls orig-n)]))))
   (set! last-pair
     (lambda (ls)
       (unless (pair? ls)
         ($oops 'last-pair "~s is not a pair" ls))
       (let loop ((fast ls) (slow ls))
         (let ((fast1 (cdr fast)))
           (if (pair? fast1)
               (let ((fast2 (cdr fast1)))
                 (if (pair? fast2)
                     (if (not (eq? fast1 slow))
                         (loop fast2 (cdr slow))
                         (circular-list-error 'last-pair ls))
                     fast1))
               fast))))))

(define make-list
   (rec make-list
      (case-lambda
         [(n) (make-list n (void))]
         [(n x)
          (unless (and (fixnum? n) (fx>= n 0))
             ($oops 'make-list "invalid size ~s" n))
          (let loop ([n n] [ls '()])
             (if (fx= n 0) ls (loop (fx- n 1) (cons x ls))))])))

(define-who list-copy
  (lambda (ls)
    ($list-length ls who)
    (let f ([ls ls])
      (if (null? ls)
          ls
          (cons (car ls) (f (cdr ls)))))))

(define-who append
  (rec append
    (case-lambda
      [() '()]
      [(x1 x2)
       ($list-length x1 who)
       (let f ([ls x1])
         (if (null? ls) x2 (cons (car ls) (f (cdr ls)))))]
      [(x1 . xr)
       (let f ([x1 x1] [xr xr])
         (if (null? xr) x1 (append x1 (f (car xr) (cdr xr)))))])))

(define-who append!
  (let ()
    (define (do-append! x1 x2)
      (if (null? x1)
          x2
          (let f ([ls x1])
            (if (null? (cdr ls))
                (begin (set-cdr! ls x2) x1)
                (f (cdr ls))))))
    (case-lambda
      [() '()]
      [(x1 x2)
       ($list-length x1 who)
       (do-append! x1 x2)]
      [(x1 . xr)
       (let f ([x1 x1] [xr xr])
         (if (null? xr)
             x1
             (begin
               ($list-length x1 who) ; make sure all checks occur before first set-cdr!
               (do-append! x1 (f (car xr) (cdr xr))))))])))

(define-who reverse
  (lambda (ls)
    ($list-length ls who)
    (do ([ls ls (cdr ls)] [a '() (cons (car ls) a)])
        ((null? ls) a))))

(define-who reverse!
  (lambda (ls)
    (#%$list-length ls who)
    (let loop ([l ls] [a '()])
      (cond
        [(pair? l) (let ([x (cdr l)]) (set-cdr! l a) (loop x l))]
        [(null? l) a]
        [else
         (let loop ([l a] [a l])
           (let ([x (cdr l)]) (set-cdr! l a) (loop x l)))]))))

(let ()
(define-syntax do-assoc
  (syntax-rules ()
    ((_ x alist who pred?)
     (let loop ((fast alist) (slow alist))
       (cond
         [(pair? fast)
          (let ((a (car fast)))
            (if (pair? a)
                (if (pred? (car a) x)
                    a
                    (let ((fast (cdr fast)))
                      (cond
                        [(pair? fast)
                         (let ((a (car fast)))
                           (if (pair? a)
                               (if (pred? (car a) x)
                                   a
                                   (if (eq? fast slow)
                                       (cyclic-alist who alist)
                                       (loop (cdr fast) (cdr slow))))
                               (improper-alist who alist)))]
                        [(null? fast) #f]
                        [else (improper-alist who alist)])))
                (improper-alist who alist)))]
         [(null? fast) #f]
         [else (improper-alist who alist)])))))

(define improper-alist
  (lambda (who alist)
    ($oops who "improperly formed alist ~s" alist)))

(define cyclic-alist
  (lambda (who alist)
    ($oops who "cyclic alist ~s" alist)))

(define ass-eq?
  (lambda (x alist who)
    (do-assoc x alist who eq?)))

(set! assq
  (lambda (x alist)
    (ass-eq? x alist 'assq)))

(set! assv
  (lambda (x alist)
    (if (or (symbol? x) (fixmediate? x))
        (ass-eq? x alist 'assv)
        (do-assoc x alist 'assv eqv?))))

(set! assoc
  (lambda (x alist)
    (cond
      [(string? x)
       (do-assoc x alist 'assoc
         (lambda (x y) (and (string? x) (string=? x y))))]
      [(or (symbol? x) (fixmediate? x))
       (ass-eq? x alist 'assoc)]
      [else
       (do-assoc x alist 'assoc equal?)])))

(set! assp
  (lambda (pred? alist)
    (unless (procedure? pred?)
      ($oops 'assp "~s is not a procedure" pred?))
    (let loop ((fast alist) (slow alist))
      (cond
        [(pair? fast)
         (let ((a (car fast)))
           (if (pair? a)
               (if (pred? (car a))
                   a
                   (let ((fast (cdr fast)))
                     (cond
                       [(pair? fast)
                        (let ((a (car fast)))
                          (if (pair? a)
                              (if (pred? (car a))
                                  a
                                  (if (eq? fast slow)
                                      (cyclic-alist 'assp alist)
                                      (loop (cdr fast) (cdr slow))))
                              (improper-alist 'assp alist)))]
                       [(null? fast) #f]
                       [else (improper-alist 'assp alist)])))
               (improper-alist 'assp alist)))]
        [(null? fast) #f]
        [else (improper-alist 'assp alist)]))))
)

(let ()
(define improper-list
  (lambda (who ls)
    ($oops who "improper list ~s" ls)))

(define cyclic-list
  (lambda (who ls)
    ($oops who "cyclic list ~s" ls)))

(define-syntax do-member
  (syntax-rules ()
    ((_ x ls who pred?)
     (let loop ((fast ls) (slow ls))
       (cond
         [(pair? fast)
          (if (pred? (car fast) x)
              fast
              (let ((fast (cdr fast)))
                (cond
                  [(pair? fast)
                   (if (pred? (car fast) x)
                       fast
                       (if (eq? fast slow)
                           (cyclic-list who ls)
                           (loop (cdr fast) (cdr slow))))]
                  [(null? fast) #f]
                  [else (improper-list who ls)])))]
         [(null? fast) #f]
         [else (improper-list who ls)])))))

(define mem-eq?
  (lambda (x ls who)
    (do-member x ls who eq?)))

(set! memq
  (lambda (x ls)
    (mem-eq? x ls 'memq)))

(set! memv
  (lambda (x ls)
    (if (or (symbol? x) (fixnum? x) (char? x) (procedure? x))
        (mem-eq? x ls 'memv)
        (do-member x ls 'memv eqv?))))

(set! member
  (lambda (x ls)
    (cond
      [(string? x)
       (do-member x ls 'member
         (lambda (x y) (and (string? x) (string=? x y))))]
      [(or (symbol? x) (fixnum? x) (char? x) (procedure? x))
       (mem-eq? x ls 'member)]
      [else
       (do-member x ls 'member equal?)])))

(set! memp
  (lambda (pred? ls)
    (unless (procedure? pred?)
      ($oops 'memp "~s is not a procedure" pred?))
   (let loop ((fast ls) (slow ls))
     (cond
       [(pair? fast)
        (if (pred? (car fast))
            fast
            (let ((fast (cdr fast)))
              (cond
                [(pair? fast)
                 (if (pred? (car fast))
                     fast
                     (if (eq? fast slow)
                         (cyclic-list 'memp ls)
                         (loop (cdr fast) (cdr slow))))]
                [(null? fast) #f]
                [else (improper-list 'memp ls)])))]
       [(null? fast) #f]
       [else (improper-list 'memp ls)]))))

(set! find
  (lambda (pred? ls)
    (unless (procedure? pred?)
      ($oops 'find "~s is not a procedure" pred?))
   (let loop ((fast ls) (slow ls))
     (cond
       [(pair? fast)
        (if (pred? (car fast))
            (car fast)
            (let ((fast (cdr fast)))
              (cond
                [(pair? fast)
                 (if (pred? (car fast))
                     (car fast)
                     (if (eq? fast slow)
                         (cyclic-list 'find ls)
                         (loop (cdr fast) (cdr slow))))]
                [(null? fast) #f]
                [else (improper-list 'find ls)])))]
       [(null? fast) #f]
       [else (improper-list 'find ls)]))))
)

(let ()
(define improper-list
  (lambda (who ls)
    ($oops who "~s is not a proper list" ls)))

(define-syntax do-remove
  (syntax-rules ()
    ((_ x ls pred?)
     (let f ((x x) (fast ls) (slow ls))
       (if (pair? fast)
           (let ((fast1 (cdr fast)))
             (if (pair? fast1)
                 (and (not (eq? fast1 slow))
                      (let ((fast2 (cdr fast1)))
                        (let ((rest (f x fast2 (cdr slow))))
                          (and rest
                               (if (not (pred? (car fast) x))
                                   (if (not (pred? (car fast1) x))
                                       (if (eq? rest fast2)
                                           fast
                                           (list* (car fast) (car fast1) rest))
                                       (cons (car fast) rest))
                                   (if (not (pred? (car fast1) x))
                                       (if (eq? rest fast2)
                                           fast1
                                           (cons (car fast1) rest))
                                       rest))))))
                 (and (null? fast1)
                      (if (not (pred? (car fast) x))
                          fast
                          '()))))
           (and (null? fast) '()))))))

(define rem-eq?
  (lambda (x l)
    (do-remove x l eq?)))

(set! remq
  (lambda (x ls)
    (or (rem-eq? x ls)
        (improper-list 'remq ls))))

(set! remv
  (lambda (x ls)
    (or (if (or (symbol? x) (fixnum? x) (char? x) (procedure? x))
            (rem-eq? x ls)
            (do-remove x ls eqv?))
        (improper-list 'remv ls))))

(set! remove
  (lambda (x ls)
    (or (cond
          [(string? x)
           (do-remove x ls 
             (lambda (x y) (and (string? x) (string=? x y))))]
          [(or (symbol? x) (fixnum? x) (char? x) (procedure? x))
           (rem-eq? x ls)]
          [else
           (do-remove x ls equal?)])
        (improper-list 'remove ls))))

(set! remp
  (lambda (pred? ls)
    (unless (procedure? pred?)
      ($oops 'remp "~s is not a procedure" pred?))
    (or (let f ((pred? pred?) (fast ls) (slow ls))
          (if (pair? fast)
              (let ((fast1 (cdr fast)))
                (if (pair? fast1)
                    (and (not (eq? fast1 slow))
                         (let ((fast2 (cdr fast1)))
                           (let ((rest (f pred? fast2 (cdr slow))))
                             (and rest
                                  (if (not (pred? (car fast)))
                                      (if (not (pred? (car fast1)))
                                          (if (eq? rest fast2)
                                              fast
                                              (list* (car fast) (car fast1) rest))
                                          (cons (car fast) rest))
                                      (if (not (pred? (car fast1)))
                                          (if (eq? rest fast2)
                                              fast1
                                              (cons (car fast1) rest))
                                          rest))))))
                    (and (null? fast1)
                         (if (not (pred? (car fast)))
                             fast
                             '()))))
              (and (null? fast) '())))
        (improper-list 'remp ls))))


(set! filter
  (lambda (pred? ls)
    (unless (procedure? pred?)
      ($oops 'filter "~s is not a procedure" pred?))
    (or (let f ((pred? pred?) (fast ls) (slow ls))
          (if (pair? fast)
              (let ((fast1 (cdr fast)))
                (if (pair? fast1)
                    (and (not (eq? fast1 slow))
                         (let ((fast2 (cdr fast1)))
                           (let ((rest (f pred? fast2 (cdr slow))))
                             (and rest
                                  (if (pred? (car fast))
                                      (if (pred? (car fast1))
                                          (if (eq? rest fast2)
                                              fast
                                              (list* (car fast) (car fast1) rest))
                                          (cons (car fast) rest))
                                      (if (pred? (car fast1))
                                          (if (eq? rest fast2)
                                              fast1
                                              (cons (car fast1) rest))
                                          rest))))))
                    (and (null? fast1)
                         (if (pred? (car fast))
                             fast
                             '()))))
              (and (null? fast) '())))
        (improper-list 'filter ls))))

(set! partition
  (lambda (pred? ls)
    (unless (procedure? pred?)
      ($oops 'partition "~s is not a procedure" pred?))
    (let f ([pred? pred?] [fast ls] [slow ls] [ls ls])
      (if (pair? fast)
          (let ([fast1 (cdr fast)])
            (if (pair? fast1)
                (if (eq? fast1 slow)
                    (improper-list 'partition ls)
                    (let ([fast2 (cdr fast1)])
                      (let-values ([(ins outs) (f pred? fast2 (cdr slow) ls)])
                        (if (pred? (car fast))
                            (if (pred? (car fast1))
                                (values
                                  (if (eq? ins fast2)
                                      fast
                                      (list* (car fast) (car fast1) ins))
                                  outs)
                                (values
                                  (cons (car fast) ins)
                                  (if (eq? outs fast2)
                                      fast1
                                      (cons (car fast1) outs))))
                            (if (pred? (car fast1))
                                (values
                                  (if (eq? ins fast2)
                                      fast1
                                      (cons (car fast1) ins))
                                  (cons (car fast) outs))
                                (values
                                  ins
                                  (if (eq? outs fast2)
                                      fast
                                      (list* (car fast) (car fast1) outs))))))))
                (if (null? fast1)
                    (if (pred? (car fast))
                        (values fast '())
                        (values '() fast))
                    (improper-list 'partition ls))))
          (if (null? fast)
              (values '() '())
              (improper-list 'partition ls))))))
)

(let ()
(define-syntax do-rem!
  (syntax-rules ()
    ((_ pred?)
     (rec rem!
       (lambda (x ls)
         (if (not (null? ls))
             (if (not (pred? (car ls) x))
                 (begin
                   (let loop ((ls (cdr ls)) (prev ls))
                     (unless (null? ls)
                       (if (not (pred? (car ls) x))
                           (loop (cdr ls) ls)
                           (set-cdr! prev (rem! x (cdr ls))))))
                   ls)
                 (rem! x (cdr ls)))
             '()))))))

(define rem-eq?! (do-rem! eq?))

(set! remq!
  (lambda (x ls)
    ($list-length ls 'remq!)
    (rem-eq?! x ls)))

(set! remv!
  (lambda (x ls)
    ($list-length ls 'remv!)
    (if (or (symbol? x) (fixnum? x) (char? x) (procedure? x))
        (rem-eq?! x ls)
        ((do-rem! eqv?) x ls))))

(set! remove!
  (lambda (x ls)
    ($list-length ls 'remove!)
    (if (or (symbol? x) (fixnum? x) (char? x) (procedure? x))
        (rem-eq?! x ls)
        ((do-rem! equal?) x ls))))
)

(define substq
   (lambda (new old tree)
      (let f ([tree tree])
         (if (eq? old tree)
             new
             (if (pair? tree)
                 (let ([a (f (car tree))] [d (f (cdr tree))])
                    (if (and (eq? a (car tree)) (eq? d (cdr tree)))
                        tree
                        (cons a d)))
                 tree)))))

(define substq!
   (lambda (new old tree)
      (let f ([tree tree])
         (if (eq? old tree)
             new
             (if (pair? tree)
                 (begin
                    (set-car! tree (f (car tree)))
                    (set-cdr! tree (f (cdr tree)))
                    tree)
                 tree)))))

(define substv
   (lambda (new old tree)
      (let f ([tree tree])
         (if (eqv? old tree)
             new
             (if (pair? tree)
                 (let ([a (f (car tree))] [d (f (cdr tree))])
                    (if (and (eq? a (car tree)) (eq? d (cdr tree)))
                        tree
                        (cons a d)))
                 tree)))))

(define substv!
   (lambda (new old tree)
      (let f ([tree tree])
         (if (eqv? old tree)
             new
             (if (pair? tree)
                 (begin
                    (set-car! tree (f (car tree)))
                    (set-cdr! tree (f (cdr tree)))
                    tree)
                 tree)))))

(define subst
   (lambda (new old tree)
      (let f ([tree tree])
         (if (equal? old tree)
             new
             (if (pair? tree)
                 (let ([a (f (car tree))] [d (f (cdr tree))])
                    (if (and (eq? a (car tree)) (eq? d (cdr tree)))
                        tree
                        (cons a d)))
                 tree)))))

(define subst!
   (lambda (new old tree)
      (let f ([tree tree])
         (if (equal? old tree)
             new
             (if (pair? tree)
                 (begin
                    (set-car! tree (f (car tree)))
                    (set-cdr! tree (f (cdr tree)))
                    tree)
                 tree)))))

(let ()
  (define ($iota n ls)
    (if (fx> n 0)
        ($iota (fx- n 2) (list* (fx- n 1) n ls))
        (if (fx= n 0)
            (cons 0 ls)
            ls)))

 ; (iota n) => (0 1 ... n-1)
  (set! iota
    (lambda (n)
      (unless (and (fixnum? n) (fx>= n 0))
        ($oops 'iota "~s is not a nonnegative fixnum" n))
      ($iota (fx- n 1) '())))

 ; (enumerate '(a1 a2 ... aN)) => (0 1 ... n-1)
  (set! enumerate
    (lambda (ls)
      ($iota (fx- ($list-length ls 'enumerate) 1) '()))))

(define list-assuming-immutable?
  ;; Use list bits to record discovered listness:
  ;;   0 => unknown
  ;;   1 => is a list
  ;;   2 => not a list
  ;; Record this information half-way to the point that the
  ;; decision is made (i.e., a kind of path compression)
  (lambda (v)
    (or (null? v)
        (and (pair? v)
             (let loop ([fast (cdr v)] [slow v] [slow-step? #f])
               (let ([return (lambda (bits)
                               ($list-bits-set! slow bits)
                               (fx= bits 1))])
                 (cond
                   [(null? fast) (return 1)]
                   [(not (pair? fast)) (return 2)]
                   [(eq? fast slow) (return 2)] ; cycle
                   [else
                    (let ([bits ($list-bits-ref fast)])
                      (cond
                        [(fx= bits 0)
                         (if slow-step?
                             (loop (cdr fast) (cdr slow) #f)
                             (loop (cdr fast) slow #t))]
                        [else
                         (return bits)]))])))))))

)
