git.fiddlerwoaroof.com
sequences.lisp
bf1c216d
 ;;; Extensible sequences, based on the proposal by Christophe Rhodes.
 
 ;;;; This software is part of the SBCL system. See the README file for
 ;;;; more information.
 
 ;;;; This software is in the public domain and is provided with
 ;;;; absolutely no warranty. See the COPYING and CREDITS files for
 ;;;; more information.
 (cl:defpackage :sequence
   (:use)
   (:import-from :cl
                 #:type-error-datum #:lambda #:&key #:&optional #:&rest
                 #:&whole #:&body #:error #:null #:format #:class-of #:zerop
                 #:multiple-value-bind #:cons #:cond #:and #:make-array
                 #:array-element-type #:defvar #:defparameter
                 #:make-list #:unless #:= #:let #:let* #:t #:make-array
                 #:nil #:define-condition #:defun #:if #:eql
                 #:setf #:defgeneric #:< #:rplacd #:nthcdr #:fill-pointer
                 #:adjust-array #:declare #:ignore #:defmethod
                 #:type-error #:apply #:array-total-size #:1- #:-
                 #:array-has-fill-pointer-p #:>= #:or #:aref #:1+
                 #:values #:car #:cdr #:cddr #:last #:not #:<=
                 #:loop #:eq #:do* #:typecase #:gensym #:push
                 #:mapcar #:defmacro #:fdefinition #:complement
                 #:functionp #:flet #:list* #:funcall #:setq #:incf
                 #:when #:do #:identity #:return #:floor #:ceiling #:+
                 #:return-from #:> #:+ #:dotimes #:labels #:function
                 #:ignorable #:prog1 #:minusp #:progn #:type #:the)
   (:export #:protocol-unimplemented #:protocol-unimplemented-operation
            #:merge #:with-sequence-iterator-functions #:make-sequence-like
            #:missing-arg #:emptyp #:length #:elt #:adjust-sequence
            #:make-sequence-iterator #:make-list-iterator #:make-vector-iterator
            #:make-simple-sequence-iterator #:iterator-step #:iterator-endp
            #:iterator-element #:iterator-index #:iterator-copy
            #:with-sequence-iterator #:find-if-not #:position #:position-if-not
            #:position-if #:subseq #:copy-seq #:fill #:nsubstitute
            #:nsubstitute-if #:nsubstitute-if-not #:substitute #:substitute-if
            #:substitute-if-not #:replace #:nreverse #:reverse #:concatenate
            #:reduce #:mismatch #:search #:delete #:delete-if #:delete-if-not
            #:remove #:remove-if #:remove-if-not #:delete-duplicates
            #:remove-duplicates #:sort #:stable-sort))
 (uiop:define-package :gen-cl-user
     (:mix :sequence :cl))
 
 (cl:in-package :sequence)
 
 (defun missing-arg ()
   (error "A required &KEY or &OPTIONAL argument was not supplied."))
 
 
 ;;;; basic protocol
 (define-condition protocol-unimplemented (type-error
                                           #+sbcl reference-condition)
   ((operation :initarg :operation
               :reader protocol-unimplemented-operation))
   (:default-initargs
    :operation (missing-arg)
     :references '((:sbcl :node "Extensible Sequences")))
   (:report
    (lambda (condition stream)
      (let ((operation (protocol-unimplemented-operation condition))
            (datum (type-error-datum condition)))
        (format stream "~@<The operation ~
                        ~/sb-ext:print-symbol-with-prefix/ is not ~
                        implemented for ~A which is an instance of the ~
                        ~/sb-ext:print-symbol-with-prefix/ subclass ~
                        ~S.~@:>"
                operation datum 'sequence (class-of datum)))))
   (:documentation
    "This error is signaled if a sequence operation is applied to an
    instance of a sequence class that does not support the
    operation."))
 
 (defun protocol-unimplemented (operation sequence)
   (error 'protocol-unimplemented
          :datum sequence
          :expected-type '(or list vector)
          :operation operation))
 
 (defgeneric emptyp (sequence)
   (:method ((s cl:list)) (null s))
   (:method ((s cl:vector)) (zerop (cl:length s)))
   (:method ((s cl:sequence)) (zerop (cl:length s)))
   (:documentation
    "Returns T if SEQUENCE is an empty sequence and NIL
    otherwise. Signals an error if SEQUENCE is not a sequence."))
 
 (defgeneric length (sequence)
   (:method ((s cl:list)) (cl:length s))
   (:method ((s cl:vector)) (cl:length s))
   (:method ((s cl:sequence))
     (protocol-unimplemented 'length s))
   (:documentation
    "Returns the length of SEQUENCE or signals a PROTOCOL-UNIMPLEMENTED
    error if the sequence protocol is not implemented for the class of
    SEQUENCE."))
 
 (defgeneric elt (sequence index)
   (:method ((s cl:list) index) (cl:elt s index))
   (:method ((s cl:vector) index) (cl:elt s index))
   (:method ((s cl:sequence) index)
     (cl:declare (cl:ignore index))
     (protocol-unimplemented 'elt s))
   (:documentation
    "Returns the element at position INDEX of SEQUENCE or signals a
    PROTOCOL-UNIMPLEMENTED error if the sequence protocol is not
    implemented for the class of SEQUENCE."))
 
 (defgeneric (setf elt) (new-value sequence index)
   (:argument-precedence-order sequence new-value index)
   (:method (new-value (s cl:list) index) (setf (cl:elt s index) new-value))
   (:method (new-value (s cl:vector) index) (setf (cl:elt s index) new-value))
   (:method (new-value (s cl:sequence) index)
     (cl:declare (cl:ignore index new-value))
     (protocol-unimplemented '(setf elt) s))
   (:documentation
    "Replaces the element at position INDEX of SEQUENCE with NEW-VALUE
    and returns NEW-VALUE or signals a PROTOCOL-UNIMPLEMENTED error if
    the sequence protocol is not implemented for the class of
    SEQUENCE."))
 
 (defgeneric make-sequence-like
     (sequence length &key initial-element initial-contents)
   (:method ((s cl:list) length
             &key (initial-element nil iep) (initial-contents nil icp))
     (cond
       ((and icp iep) (error "supplied both ~S and ~S to ~S"
                             :initial-element
                             :initial-contents
                             'make-sequence-like))
       (iep (make-list length :initial-element initial-element))
       (icp (unless (= (length initial-contents) length)
              (error "length mismatch in ~S" 'make-sequence-like))
            (let ((result (make-list length)))
              (replace result initial-contents)
              result))
       (t (make-list length))))
   (:method ((s cl:vector) length
             &key (initial-element nil iep) (initial-contents nil icp))
     (cond
       ((and icp iep) (error "supplied both ~S and ~S to ~S"
                             :initial-element
                             :initial-contents
                             'make-sequence-like))
       (iep (make-array length :element-type (array-element-type s)
                        :initial-element initial-element))
       (icp (make-array length :element-type (array-element-type s)
                        :initial-contents initial-contents))
       (t (make-array length :element-type (array-element-type s)))))
   (:method ((s cl:sequence) length &key initial-element initial-contents)
     (cl:declare (cl:ignore initial-element initial-contents length))
     (protocol-unimplemented 'make-sequence-like s))
   (:documentation
    "Returns a freshly allocated sequence of length LENGTH and of the
    same class as SEQUENCE. Elements of the new sequence are
    initialized to INITIAL-ELEMENT, if supplied, initialized to
    INITIAL-CONTENTS if supplied, or identical to the elements of
    SEQUENCE if neither is supplied. Signals a PROTOCOL-UNIMPLEMENTED
    error if the sequence protocol is not implemented for the class of
    SEQUENCE."))
 
 (defgeneric adjust-sequence
     (sequence length &key initial-element initial-contents)
   (:method ((s cl:list) length &key initial-element (initial-contents nil icp))
     (if (eql length 0)
         nil
         (let ((olength (length s)))
           (cond
             ((eql length olength) (if icp (replace s initial-contents) s))
             ((< length olength)
              (rplacd (nthcdr (1- length) s) nil)
              (if icp (replace s initial-contents) s))
             ((null s)
              (let ((return (make-list length :initial-element initial-element)))
                (if icp (replace return initial-contents) return)))
             (t (rplacd (nthcdr (1- olength) s)
                        (make-list (- length olength)
                                   :initial-element initial-element))
                (if icp (replace s initial-contents) s))))))
   (:method ((s cl:vector) length
             &rest args &key (initial-contents nil icp) initial-element)
     (declare (ignore initial-element))
     (cond
       ((and (array-has-fill-pointer-p s)
             (>= (array-total-size s) length))
        (setf (fill-pointer s) length)
        (if icp (replace s initial-contents) s))
       ((eql (length s) length)
        (if icp (replace s initial-contents) s))
       (t (apply #'adjust-array s length args))))
   (:method ((s cl:sequence) length &rest args)
     (declare (ignore args length))
     (protocol-unimplemented 'adjust-sequence s))
   (:documentation
    "Return destructively modified SEQUENCE or a freshly allocated
    sequence of the same class as SEQUENCE of length LENGTH. Elements
    of the returned sequence are initialized to INITIAL-ELEMENT, if
    supplied, initialized to INITIAL-CONTENTS if supplied, or identical
    to the elements of SEQUENCE if neither is supplied. Signals a
    PROTOCOL-UNIMPLEMENTED error if the sequence protocol is not
    implemented for the class of SEQUENCE."))
 
 
 ;;;; iterator protocol
 
 ;;; The general protocol
 
 (defgeneric make-sequence-iterator (sequence &key from-end start end)
   (:method ((s cl:vector) &key from-end (start 0) end)
     (make-vector-iterator s from-end start end))
   (:method ((s cl:list) &key from-end (start 0) end)
     (make-list-iterator s from-end start end))
   (:method ((s cl:sequence) &key from-end (start 0) end)
     (multiple-value-bind (iterator limit from-end)
         (make-simple-sequence-iterator
          s :from-end from-end :start start :end end)
       (values iterator limit from-end
               #'iterator-step #'iterator-endp
               #'iterator-element #'(setf iterator-element)
               #'iterator-index #'iterator-copy)))
   (:method ((s t) &key from-end start end)
     (declare (ignore from-end start end))
     (error 'type-error
            :datum s
            :expected-type 'sequence))
   (:documentation
    "Returns a sequence iterator for SEQUENCE or, if START and/or END
    are supplied, the subsequence bounded by START and END as nine
    values:
 
    1. iterator state
    2. limit
    3. from-end
    4. step function
    5. endp function
    6. element function
    7. setf element function
    8. index function
    9. copy state function
 
    If FROM-END is NIL, the constructed iterator visits the specified
    elements in the order in which they appear in SEQUENCE. Otherwise,
    the elements are visited in the opposite order."))
 
 ;;; magic termination value for list :from-end t
 (defvar *exhausted* (cons nil nil))
 
 (defun make-list-iterator (list from-end start end)
   (multiple-value-bind (iterator limit from-end)
       (if from-end
           (let* ((termination (if (= start 0) *exhausted* (nthcdr (1- start) list)))
                  (init (if (<= (or end (cl:length list)) start)
                            termination
                            (if end
                                (last list (- (cl:length list)
                                              (1- end)))
                                (last list)))))
             (values init termination t))
           (cond
             ((not end) (values (nthcdr start list) nil nil))
             (t (let ((st (nthcdr start list)))
                  (values st (nthcdr (- end start) st) nil)))))
     (values iterator limit from-end
             (if from-end
                 (lambda (list iterator from-end)
                   (declare (ignore from-end))
                   (if (eq iterator list)
                       *exhausted*
                       (do* ((cdr list (cdr cdr)))
                            ((eq (cdr cdr) iterator) cdr)))
                   (1+ iterator))
                 (lambda (list iterator from-end)
                   (declare (ignore list from-end))
                   (cdr iterator)))
             (lambda (list iterator limit from-end)
               (declare (ignore list from-end))
               (eq iterator limit))
             (lambda (list iterator)
               (declare (ignore list))
               (car iterator))
             (lambda (new-value list iterator)
               (declare (ignore list))
               (setf (car iterator) new-value))
             (lambda (list iterator)
               (loop for cdr on list
                  for i from 0
                  when (eq cdr iterator)
                  return i))
             (lambda (list iterator)
               (declare (ignore list))
               iterator))))
 
 (defun make-vector-iterator (vector from-end start end)
   (let* ((end (or end (length vector)))
          (iterator (if from-end
                        (1- end)
                        start))
          (limit (if from-end
                     (1- start)
                     end)))
     (values iterator limit from-end
             (if from-end
                 (lambda (sequence iterator from-end)
                   (declare (ignore sequence from-end))
                   (1- iterator))
                 (lambda (sequence iterator from-end)
                   (declare (ignore sequence from-end))
                   (1+ iterator)))
             (lambda (sequence iterator limit from-end)
               (declare (ignore sequence from-end))
               (= iterator limit))
             (lambda (sequence iterator)
               (aref sequence iterator))
             (lambda (new-value sequence iterator)
               (setf (aref sequence iterator) new-value))
             (lambda (sequence iterator)
               (declare (ignore sequence))
               iterator)
             (lambda (sequence iterator)
               (declare (ignore sequence))
               iterator))))
 
 ;;; the simple protocol: the simple iterator returns three values,
 ;;; STATE, LIMIT and FROM-END.
 (defgeneric make-simple-sequence-iterator
     (sequence &key from-end start end)
   (:method ((s cl:list) &key from-end (start 0) end)
     (if from-end
         (let* ((termination (if (= start 0) *exhausted* (nthcdr (1- start) s)))
                (init (if (<= (or end (length s)) start)
                          termination
                          (if end (last s (- (length s) (1- end))) (last s)))))
           (values init termination t))
         (cond
           ((not end) (values (nthcdr start s) nil nil))
           (t (let ((st (nthcdr start s)))
                (values st (nthcdr (- end start) st) nil))))))
   (:method ((s cl:vector) &key from-end (start 0) end)
     (let ((end (or end (length s))))
       (if from-end
           (values (1- end) (1- start) t)
           (values start end nil))))
   (:method ((s cl:sequence) &key from-end (start 0) end)
     (let ((end (or end (length s))))
       (if from-end
           (values (1- end) (1- start) from-end)
           (values start end nil))))
   (:documentation
    "Returns a sequence iterator for SEQUENCE, START, END and FROM-END
    as three values:
 
    1. iterator state
    2. limit
    3. from-end
 
    The returned iterator can be used with the generic iterator
    functions ITERATOR-STEP, ITERATOR-ENDP, ITERATOR-ELEMENT, (SETF
    ITERATOR-ELEMENT), ITERATOR-INDEX and ITERATOR-COPY."))
 
 (defgeneric iterator-step (sequence iterator from-end)
   (:method ((s cl:list) iterator from-end)
     (if from-end
         (if (eq iterator s)
             *exhausted*
             (do* ((xs s (cdr xs)))
                  ((eq (cdr xs) iterator) xs)))
         (cdr iterator)))
   (:method ((s cl:vector) iterator from-end)
     (if from-end
         (1- iterator)
         (1+ iterator)))
   (:method ((s cl:sequence) iterator from-end)
     (if from-end
         (1- iterator)
         (1+ iterator)))
   (:documentation
    "Moves ITERATOR one position forward or backward in SEQUENCE
    depending on the iteration direction encoded in FROM-END."))
 
 (defgeneric iterator-endp (sequence iterator limit from-end)
   (:method ((s cl:list) iterator limit from-end)
     (declare (ignore from-end))
     (eq iterator limit))
   (:method ((s cl:vector) iterator limit from-end)
     (declare (ignore from-end))
     (= iterator limit))
   (:method ((s cl:sequence) iterator limit from-end)
     (declare (ignore from-end))
     (= iterator limit))
   (:documentation
    "Returns non-NIL when ITERATOR has reached LIMIT (which may
    correspond to the end of SEQUENCE) with respect to the iteration
    direction encoded in FROM-END."))
 
 (defgeneric iterator-element (sequence iterator)
   (:method ((s cl:list) iterator)
     (car iterator))
   (:method ((s cl:vector) iterator)
     (aref s iterator))
   (:method ((s cl:sequence) iterator)
     (elt s iterator))
   (:documentation
    "Returns the element of SEQUENCE associated to the position of
    ITERATOR."))
 
 (defgeneric (setf iterator-element) (new-value sequence iterator)
   (:method (o (s cl:list) iterator)
     (setf (car iterator) o))
   (:method (o (s cl:vector) iterator)
     (setf (aref s iterator) o))
   (:method (o (s cl:sequence) iterator)
     (setf (elt s iterator) o))
   (:documentation
    "Destructively modifies SEQUENCE by replacing the sequence element
    associated to position of ITERATOR with NEW-VALUE."))
 
 (defgeneric iterator-index (sequence iterator)
   (:method ((s cl:list) iterator)
     ;; FIXME: this sucks.  (In my defence, it is the equivalent of the
     ;; Apple implementation in Dylan...)
     (loop for l on s for i from 0 when (eq l iterator) return i))
   (:method ((s cl:vector) iterator) iterator)
   (:method ((s cl:sequence) iterator) iterator)
   (:documentation
    "Returns the position of ITERATOR in SEQUENCE."))
 
 (defgeneric iterator-copy (sequence iterator)
   (:method ((s cl:list) iterator) iterator)
   (:method ((s cl:vector) iterator) iterator)
   (:method ((s cl:sequence) iterator) iterator)
   (:documentation
    "Returns a copy of ITERATOR which also traverses SEQUENCE but can
    be mutated independently of ITERATOR."))
 
 (defun %make-sequence-iterator (sequence from-end start end)
   (typecase sequence
     (cl:vector
      (make-vector-iterator sequence from-end start end))
     (cl:list
      (make-list-iterator sequence from-end start end))
     (t
      (make-sequence-iterator sequence
                              :end end
                              :start start
                              :from-end from-end))))
 
 (defmacro with-sequence-iterator
     ((&whole vars
              &optional iterator limit from-end-p
              step endp element set-element index copy)
                (sequence &key from-end (start 0) end) &body body)
   "Executes BODY with the elements of VARS bound to the iteration
   state returned by MAKE-SEQUENCE-ITERATOR for SEQUENCE and
   ARGS. Elements of VARS may be NIL in which case the corresponding
   value returned by MAKE-SEQUENCE-ITERATOR is ignored."
   (declare (ignore iterator limit from-end-p
                    step endp element set-element index copy))
   (let* ((ignored '())
          (vars (mapcar (lambda (x)
                          (or x (let ((name (gensym)))
                                  (push name ignored)
                                  name)))
                        vars)))
     `(multiple-value-bind (,@vars)
          (%make-sequence-iterator ,sequence ,from-end ,start ,end)
        (declare (cl:type function ,@(nthcdr 3 vars))
                 (ignore ,@ignored))
        ,@body)))
 
 (defmacro with-sequence-iterator-functions
     ((step endp elt setf index copy)
      (sequence &rest args &key from-end start end)
      &body body)
   "Executes BODY with the names STEP, ENDP, ELT, SETF, INDEX and COPY
   bound to local functions which execute the iteration state query and
   mutation functions returned by MAKE-SEQUENCE-ITERATOR for SEQUENCE
   and ARGS. STEP, ENDP, ELT, SETF, INDEX and COPY have dynamic
   extent."
   (declare (ignore from-end start end))
   (let ((nstate (gensym "STATE")) (nlimit (gensym "LIMIT"))
         (nfrom-end (gensym "FROM-END-")) (nstep (gensym "STEP"))
         (nendp (gensym "ENDP")) (nelt (gensym "ELT"))
         (nsetf (gensym "SETF")) (nindex (gensym "INDEX"))
         (ncopy (gensym "COPY")))
     `(with-sequence-iterator
          (,nstate ,nlimit ,nfrom-end ,nstep ,nendp ,nelt ,nsetf ,nindex ,ncopy)
          (,sequence,@args)
        (declare (cl:ignorable ,nstate ,nlimit ,nfrom-end ,nstep ,nendp ,nelt ,nsetf ,nindex ,ncopy))
        (flet ((,step () (setq ,nstate (funcall ,nstep ,sequence,nstate ,nfrom-end)))
               (,endp () (funcall ,nendp ,sequence,nstate ,nlimit ,nfrom-end))
               (,elt () (funcall ,nelt ,sequence,nstate))
               (,setf (new-value) (funcall ,nsetf new-value ,sequence,nstate))
               (,index () (funcall ,nindex ,sequence,nstate))
               (,copy () (funcall ,ncopy ,sequence,nstate)))
          (declare (cl:dynamic-extent #',step #',endp #',elt
                                      #',setf #',index #',copy))
          ,@body))))
 
 (defmacro define-shadowing-generic (name (&rest args) &body options)
   (let* ((seq-position (cl:position 'sequence args))
          (pre-args (cl:subseq args 0 seq-position))
          (raw-post-args (cl:subseq args (1+ seq-position)))
          (cl-name (cl:intern (cl:symbol-name name) :cl))
          (tail (cl:member #\& raw-post-args
                           :key (lambda (x)
                                  (cl:elt (cl:symbol-name x)
                                          0))))
          (post-args (if (and tail (not (eql (car tail) '&optional)))
                         `(,@(loop for x in raw-post-args
                                for rest on raw-post-args
                                while (not (eq rest tail))
                                collect x)
                             &rest r)
                         raw-post-args)))
     (unless seq-position
       (error "no sequence argument"))
     `(defgeneric ,name ,args
        (:method (,@pre-args (sequence cl:sequence) ,@post-args)
          (declare (cl:inline))
          (apply #',cl-name ,@pre-args sequence
                 ,@(cl:remove #\& post-args
                              :key (lambda (x)
                                     (cl:elt (cl:symbol-name x)
                                             0)))))
        ,@options)))
 
 (define-shadowing-generic find-if-not
     (pred sequence &key from-end start end key)
   (:argument-precedence-order sequence pred))
 
 (define-shadowing-generic position
     (item sequence &key from-end start end test test-not key)
   (:argument-precedence-order sequence item))
 
 
 (define-shadowing-generic position-if (pred sequence &key from-end start end key)
   (:argument-precedence-order sequence pred))
 
 
 (define-shadowing-generic position-if-not
     (pred sequence &key from-end start end key)
   (:argument-precedence-order sequence pred))
 
 
 (define-shadowing-generic subseq (sequence start &optional end))
 
 
 (define-shadowing-generic copy-seq (sequence))
 
 
 (define-shadowing-generic fill (sequence item &key start end))
 
 
 (define-shadowing-generic nsubstitute
     (new old sequence &key start end from-end test test-not count key)
   (:argument-precedence-order sequence new old))
 
 
 (define-shadowing-generic nsubstitute-if
     (new predicate sequence &key start end from-end count key)
   (:argument-precedence-order sequence new predicate))
 
 
 (define-shadowing-generic nsubstitute-if-not
     (new predicate sequence &key start end from-end count key)
   (:argument-precedence-order sequence new predicate))
 
 
 (define-shadowing-generic substitute
     (new old sequence &key start end from-end test test-not count key)
   (:argument-precedence-order sequence new old))
 
 
 (define-shadowing-generic substitute-if
     (new predicate sequence &key start end from-end count key)
   (:argument-precedence-order sequence new predicate))
 
 
 (define-shadowing-generic substitute-if-not
     (new predicate sequence &key start end from-end count key)
   (:argument-precedence-order sequence new predicate))
 
 
 (defun %sequence-replace (sequence1 sequence2 start1 end1 start2 end2)
   (with-sequence-iterator (state1 limit1 from-end1 step1 endp1 elt1 setelt1)
       (sequence1 :start start1 :end end1)
     (declare (ignore elt1))
     (with-sequence-iterator (state2 limit2 from-end2 step2 endp2 elt2)
         (sequence2 :start start2 :end end2)
       (do ()
           ((or (funcall endp1 sequence1 state1 limit1 from-end1)
                (funcall endp2 sequence2 state2 limit2 from-end2))
            sequence1)
         (funcall setelt1 (funcall elt2 sequence2 state2) sequence1 state1)
         (setq state1 (funcall step1 sequence1 state1 from-end1))
         (setq state2 (funcall step2 sequence2 state2 from-end2))))))
 
 (defgeneric replace
     (sequence1 sequence2 &key start1 end1 start2 end2)
   (:argument-precedence-order sequence2 sequence1))
 
 
 (define-shadowing-generic nreverse (sequence))
 
 
 (define-shadowing-generic reverse (sequence))
 
 
 (defgeneric concatenate (result-prototype &rest sequences)
   (:documentation
    "Implements CL:CONCATENATE for extended sequences.
 
     RESULT-PROTOTYPE corresponds to the RESULT-TYPE of CL:CONCATENATE
     but receives a prototype instance of an extended sequence class
     instead of a type specifier. By dispatching on RESULT-PROTOTYPE,
     methods on this generic function specify how extended sequence
     classes act when they are specified as the result type in a
     CL:CONCATENATE call. RESULT-PROTOTYPE may not be fully initialized
     and thus should only be used for dispatch and to determine its
     class."))
 
 
 
 (define-shadowing-generic reduce
     (function sequence &key from-end start end initial-value)
   (:argument-precedence-order sequence function))
 
 
 (defgeneric mismatch (sequence1 sequence2 &key from-end start1 end1
                                             start2 end2 test test-not key))
 
 
 (defgeneric search (sequence1 sequence2 &key from-end start1 end1
                                           start2 end2 test test-not key))
 
 
 (define-shadowing-generic delete
     (item sequence &key from-end test test-not start end count key)
   (:argument-precedence-order sequence item))
 
 
 (define-shadowing-generic delete-if
     (predicate sequence &key from-end start end count key)
   (:argument-precedence-order sequence predicate))
 
 
 (define-shadowing-generic delete-if-not
     (predicate sequence &key from-end start end count key)
   (:argument-precedence-order sequence predicate))
 
 
 (define-shadowing-generic remove
     (item sequence &key from-end test test-not start end count key)
   (:argument-precedence-order sequence item))
 
 
 (define-shadowing-generic remove-if
     (predicate sequence &key from-end start end count key)
   (:argument-precedence-order sequence predicate))
 
 
 (define-shadowing-generic remove-if-not
     (predicate sequence &key from-end start end count key)
   (:argument-precedence-order sequence predicate))
 
 
 (define-shadowing-generic delete-duplicates
     (sequence &key from-end test test-not start end key))
 
 
 (define-shadowing-generic remove-duplicates
     (sequence &key from-end test test-not start end key))
 
 (define-shadowing-generic sort (sequence predicate &key key))
 
 
 (define-shadowing-generic stable-sort (sequence predicate &key key))
 
 
 (defgeneric merge (result-prototype sequence1 sequence2 predicate &key key)
   (:documentation
    "Implements CL:MERGE for extended sequences.
 
     RESULT-PROTOTYPE corresponds to the RESULT-TYPE of CL:MERGE but
     receives a prototype instance of an extended sequence class
     instead of a type specifier. By dispatching on RESULT-PROTOTYPE,
     methods on this generic function specify how extended sequence
     classes act when they are specified as the result type in a
     CL:MERGE call. RESULT-PROTOTYPE may not be fully initialized and
     thus should only be used for dispatch and to determine its class.
 
     Another difference to CL:MERGE is that PREDICATE is a function,
     not a function designator."))
 
 (defun %coerce-callable-to-fun (callable)
   (cl:etypecase callable
     (function callable)
     (cl:symbol (cl:symbol-function callable))))
 
 (defmethod sequence:merge ((result-prototype cl:sequence) (sequence1 cl:sequence) (sequence2 cl:sequence)
                            (predicate cl:function) &key key)
   (let ((key-function (when key
                         (%coerce-callable-to-fun key)))
         (result (sequence:make-sequence-like
                  result-prototype (+ (length sequence1) (length sequence2))))
         endp1 elt1 key1 endp2 elt2 key2)
     (sequence:with-sequence-iterator-functions
         (step-result endp-result elt-result setelt-result index-result copy-result) (result)
       ;; TODO allow nil and fewer number of elements
       (declare (ignorable #'endp-result #'elt-result #'copy-result))
       (sequence:with-sequence-iterator-functions
           (step1 endp1 elt1 setelt1 index1 copy1) (sequence1)
         (declare (ignorable #'setelt1 #'copy1))
         (sequence:with-sequence-iterator-functions
             (step2 endp2 elt2 setelt2 index2 copy2) (sequence2)
           (declare (ignorable #'setelt2 #'copy2))
           (labels ((pop/no-key1 ()
                      (unless (setf endp1 (endp1))
                        (setf elt1 (elt1))))
                    (pop/no-key2 ()
                      (unless (setf endp2 (endp2))
                        (setf elt2 (elt2))))
                    (pop/key1 ()
                      (unless (setf endp1 (endp1))
                        (setf key1 (funcall (the function key-function)
                                            (setf elt1 (elt1))))))
                    (pop/key2 ()
                      (unless (setf endp2 (endp2))
                        (setf key2 (funcall (the function key-function)
                                            (setf elt2 (elt2))))))
                    (pop-one/no-key ()
                      (if (funcall predicate elt2 elt1) ; see comment in MERGE-LIST*
                          (prog1 elt2 (step2) (pop/no-key2))
                          (prog1 elt1 (step1) (pop/no-key1))))
                    (pop-one/key ()
                      (if (funcall predicate key2 key1)
                          (prog1 elt2 (step2) (pop/key2))
                          (prog1 elt1 (step1) (pop/key1)))))
             (declare (cl:dynamic-extent #'pop/no-key1 #'pop/no-key2
                                         #'pop/key1 #'pop/key2
                                         #'pop-one/no-key #'pop-one/key))
             ;; Populate ENDP{1,2}, ELT{1,2} and maybe KEY{1,2}.
             (cond (key-function (pop/key1) (pop/key2))
                   (t (pop/no-key1) (pop/no-key2)))
             (loop with pop-one = (if key-function #'pop-one/key #'pop-one/no-key) do
                  (cond
                    (endp2 ; batch-replace rest of SEQUENCE1 if SEQUENCE2 exhausted
                     (unless endp1
                       (replace result sequence1 :start1 (index-result) :start2 (index1)))
                     (return))
                    (endp1
                     (unless endp2
                       (replace result sequence2 :start1 (index-result) :start2 (index2)))
                     (return))
                    (t
                     (setelt-result (funcall pop-one))
                     (step-result))))))))
     result))