;;; -*- mode: lisp -*-
;;; $Id: lists.poplisp,v 1.0 2002/05/03 12:23:00 dada Exp $

(defparameter *SIZE* 10000)

(declaim (fixnum *SIZE*) (inline xcons push-queue))

(defvar *free-conses*)

(defun xcons (A B)
  (let ((x *free-conses*))
    (if x (progn (setf *free-conses* (cdr x) (car x) A (cdr x) B) x) (cons A B))))

(defmacro xpop (X)
  `(prog1 (car ,x) (psetf ,x (cdr ,x) (cdr ,x) *free-conses* *free-conses* ,x)))

(defun push-queue (item queue &aux (new (xcons item nil)))
  (if (cdr queue) (setf (cddr queue) new) (setf (car queue) new))
  (setf (cdr queue) new)
  (car queue))

(defmacro with-collector ((name) &body body)
  (let ((collector (gensym)))
    `(let ((,collector (xcons nil nil)))
       (flet ((,name (value) (push-queue value ,collector)))
         (car ,collector)))))

(defun test-list ()
  (let* ((L1 (with-collector (conc) (loop for x fixnum from 1 to *SIZE* do (conc x))))
         (L2 (with-collector (conc) (loop for x in L1 do (conc x))))
         (L3 nil))
    ;; Move items from left of L2 to right of L3 (preserve order)
    (setf L3 (with-collector (conc) (loop while L2 do (conc (xpop L2)))))
    ;; Move from tail of L3 to tail of L2 (reversing list)
    ;; start by reversing L3 so we can pop from the front
    (setf L3 (nreverse L3))
    (setf L2 (with-collector (conc) (loop while L3 do (conc (xpop L3)))))
    ;; Reverse L1 in place
    (setf L1 (nreverse L1))
    ;; Check that (first L1) == *SIZE*
    (assert (= (the fixnum (first L1)) *SIZE*))
    ;; Compare L1 and L2 for equality
    (assert (equal L1 L2))
    ;; Return the length -- and return the conses to the free list
    (prog1 (length (the list L1))
      (setf *free-conses* (nconc *free-conses* L3 L2 L1)))))

(let ((n (parse-integer (or (car pop11::poparglist) "1")))
    (num 0) (*free-conses* nil))
(loop repeat n fixnum do (setf num (test-list)))
(format t "~D~%" num))