;-*- Mode:     Lisp -*-
;;;; Author:   Paul Dietz
;;;; Created:  Sun Aug 18 10:10:04 2002
;;;; Contains: Tests for the MAP-INTO function

(in-package :cl-test)

(deftest map-into-list.1
  (let ((a (copy-seq '(a b c d e f)))
        (b nil))
    (map-into a #'(lambda (x) (push x b) x)  '(1 2 3 4 5 6))
    (values a b))
  (1 2 3 4 5 6)
  (6 5 4 3 2 1))

(deftest map-into-list.2
  (let ((a (copy-seq '(a b c d e f g))))
    (map-into a #'identity '(1 2 3))
    a)
  (1 2 3 d e f g))

(deftest map-into-list.3
  (let ((a (copy-seq '(a b c))))
    (map-into a #'identity '(1 2 3 4 5 6))
    a)
  (1 2 3))

(deftest map-into-list.4
  (let ((a (copy-seq '(a b c d e f)))
        (b nil))
    (map-into a #'(lambda (x y) (let ((z (+ x y))) (push z b) z))
              '(1 2 3 4 5 6)
              '(10 11 12 13 14 15))
    (values a b))
  (11 13 15 17 19 21)
  (21 19 17 15 13 11))

(deftest map-into-list.5
  (let ((a (copy-seq '(a b c d e f))))
    (map-into a 'identity '(1 2 3 4 5 6))
    a)
  (1 2 3 4 5 6))

(deftest map-into-list.6
  (let ((b nil))
    (values
     (map-into nil #'(lambda (x y) (let ((z (+ x y))) (push z b) z))
               '(1 2 3 4 5 6)
               '(10 11 12 13 14 15))
     b))
  nil nil)

(deftest map-into-list.7
  (let ((a (copy-seq '(a b c d e f))))
    (map-into a #'(lambda () 1))
    a)
  (1 1 1 1 1 1))

(deftest map-into-list.8
  (let ((a (copy-seq '(a b c d e f)))
        (s2 (make-array '(6) :initial-element 'x
                        :fill-pointer 4)))
    (map-into a #'identity s2)
    a)
  (x x x x e f))

(deftest map-into-array.1
  (let ((a (copy-seq #(a b c d e f)))
        b)
    (map-into a #'(lambda (x) (push x b) x) '(1 2 3 4 5 6))
    (values a b))
  #(1 2 3 4 5 6)
  (6 5 4 3 2 1))

(deftest map-into-array.2
  (let ((a (copy-seq #(a b c d e f g h)))
        b)
    (map-into a #'(lambda (x) (push x b) x) '(1 2 3 4 5 6))
    (values a b))
  #(1 2 3 4 5 6 g h)
  (6 5 4 3 2 1))

(deftest map-into-array.3
  (let ((a (copy-seq #(a b c d)))
        b)
    (map-into a #'(lambda (x) (push x b) x) '(1 2 3 4 5 6))
    (values a b))
  #(1 2 3 4)
  (4 3 2 1))

(deftest map-into-array.4
  (let ((a (copy-seq #(a b c d e f)))
        b)
    (map-into a #'(lambda (x) (push x b) x) #(1 2 3 4 5 6))
    (values a b))
  #(1 2 3 4 5 6)
  (6 5 4 3 2 1))

(deftest map-into-array.5
  (let ((a (copy-seq #(a b c d e f g h)))
        b)
    (map-into a #'(lambda (x) (push x b) x) #(1 2 3 4 5 6))
    (values a b))
  #(1 2 3 4 5 6 g h)
  (6 5 4 3 2 1))

(deftest map-into-array.6
  (let ((a (copy-seq #(a b c d)))
        b)
    (map-into a #'(lambda (x) (push x b) x) #(1 2 3 4 5 6))
    (values a b))
  #(1 2 3 4)
  (4 3 2 1))

;;; Tests of mapping into arrays with fill pointers
(deftest map-into-array.7
  (let ((a (make-array 6 :initial-element 'x
                       :fill-pointer 3)))
    (map-into a #'identity '(1 2 3))
    a)
  #(1 2 3))

(deftest map-into-array.8
  (let ((a (make-array 6 :initial-element 'x
                       :fill-pointer 3)))
    (map-into a #'identity '(1 2))
    a)
  #(1 2))

(deftest map-into-array.9
  (let ((a (make-array 6 :initial-element 'x
                       :fill-pointer 3)))
    (map-into a #'identity '(1 2 3 4 5))
    (and (eqlt (fill-pointer a) 5)
         a))
  #(1 2 3 4 5))

(deftest map-into-array.10
  (let ((a (make-array 6 :initial-element 'x
                       :fill-pointer 3)))
    (map-into a #'(lambda () 'y))
    (and (eqlt (fill-pointer a) 6)
         a))
  #(y y y y y y))

(deftest map-into-array.11
  (let ((a (copy-seq #(a b c d e f)))
        (s2 (make-array '(6) :initial-element 'x
                        :fill-pointer 4)))
    (map-into a #'identity s2)
    a)
  #(x x x x e f))

;;; mapping into strings

(deftest map-into-string.1
  (let ((a (copy-seq "abcdef")))
    (map-into a #'identity "123456")
    (values (not (not (stringp a))) a))
  t
  "123456")

(deftest map-into-string.2
  (let ((a (copy-seq "abcdef")))
    (map-into a #'identity "1234")
    (values (not (not (stringp a))) a))
  t
  "1234ef")

(deftest map-into-string.3
  (let ((a (copy-seq "abcd")))
    (map-into a #'identity "123456")
    (values (not (not (stringp a))) a))
  t
  "1234")

(deftest map-into-string.4
  (let ((a (make-array 6 :initial-element #\x
                       :element-type 'character
                       :fill-pointer 3)))
    (map-into a #'identity "abcde")
    (values
     (fill-pointer a)
     (aref a 5)
     a))
  5
  #\x
  "abcde")

(deftest map-into-string.5
  (let ((a (make-array 6 :initial-element #\x
                       :element-type 'character
                       :fill-pointer 3)))
    (map-into a #'(lambda () #\y))
    (values (fill-pointer a)
            a))
  6
  "yyyyyy")

(deftest map-into-string.6
  (let ((a (make-array 6 :initial-element #\x
                       :element-type 'character)))
    (map-into a #'(lambda () #\y))
    a)
  "yyyyyy")

(deftest map-into-string.7
  (let ((a (make-array 6 :initial-element #\x
                       :element-type 'base-char
                       :fill-pointer 3)))
    (map-into a #'identity "abcde")
    (values (fill-pointer a)
            (aref a 5)
            a))
  5
  #\x
  "abcde")

(deftest map-into-string.8
  (let ((a (make-array 6 :initial-element #\x
                       :element-type 'base-char
                       :fill-pointer 3)))
    (map-into a #'(lambda () #\y))
    (values (fill-pointer a)
            a))
  6
  "yyyyyy")

(deftest map-into-string.9
  (let ((a (make-array 6 :initial-element #\x
                       :element-type 'base-char)))
    (map-into a #'(lambda () #\y))
    a)
  "yyyyyy")

(deftest map-into-string.10
  (let ((a (copy-seq "abcdef"))
        (s2 (make-array '(6) :initial-element #\x
                        :fill-pointer 4)))
    (map-into a #'identity s2)
    a)
  "xxxxef")

(deftest map-into-string.11
  (let ((a (make-array 6 :initial-element #\x
                       :element-type 'character
                       :fill-pointer 3)))
    (map-into a #'identity "abcd")
    (values
     (fill-pointer a)
     (aref a 4)
     (aref a 5)
     a))
  4
  #\x
  #\x
  "abcd")

(deftest map-into-string.12
  (let ((a (make-array 6 :initial-element #\x
                       :element-type 'character
                       :fill-pointer 3)))
    (map-into a #'identity "abcdefgh")
    (values
     (fill-pointer a)
     a))
  6
  "abcdef")

(deftest map-into-string.13
  (do-special-strings
   (s (copy-seq "12345") nil)
   (let ((s2 (map-into s #'identity "abcde")))
     (assert (eq s s2))
     (assert (string= s2 "abcde"))))
  nil)

(deftest map-into-string.14
  (do-special-strings
   (s "abcde" nil)
   (let* ((s1 (copy-seq "123456"))
          (s2 (map-into s1 #'identity s)))
     (assert (eq s1 s2))
     (assert (string= s2 "abcde6"))))
  nil)

;;; Tests on bit vectors

(deftest map-into.bit-vector.1
  (let ((v (copy-seq #*0100110)))
    (map-into v #'(lambda (x) (- 1 x)) v)
    (and (bit-vector-p v)
         v))
  #*1011001)

(deftest map-into.bit-vector.2
  (let ((v (copy-seq #*0100110)))
    (map-into v #'(lambda () 0))
    (and (bit-vector-p v)
         v))
  #*0000000)

(deftest map-into.bit-vector.3
  (let ((v (copy-seq #*0100110)))
    (map-into v #'identity '(0 1 1 1 0 0 1))
    (and (bit-vector-p v)
         v))
  #*0111001)

(deftest map-into.bit-vector.4
  (let ((v (copy-seq #*0100110)))
    (map-into v #'identity '(0 1 1 1))
    (and (bit-vector-p v)
         v))
  #*0111110)

(deftest map-into.bit-vector.5
  (let ((v (copy-seq #*0100110)))
    (map-into v #'identity '(0 1 1 1 0 0 1 4 5 6 7))
    (and (bit-vector-p v)
         v))
  #*0111001)

(deftest map-into.bit-vector.6
  (let ((v (make-array '(8) :initial-contents '(0 1 0 0 1 1 0 1)
                       :fill-pointer 4
                       :element-type 'bit)))
    (map-into v #'(lambda () 1))
    (and (bit-vector-p v)
         v))
  #*11111111)

(deftest map-into.bit-vector.7
  (let ((v (make-array '(8) :initial-contents '(0 1 0 0 1 1 0 1)
                       :fill-pointer 4
                       :element-type 'bit)))
    (map-into v  #'identity v)
    (and (bit-vector-p v)
         v))
  #*0100)

(deftest map-into.bit-vector.8
  (let ((v (make-array '(8) :initial-contents '(0 1 0 0 1 1 0 1)
                       :fill-pointer 4
                       :element-type 'bit)))
    (map-into v #'identity '(1 1 1 1 1 1))
    (and (bit-vector-p v)
         (values (fill-pointer v)
                 v)))
  6
  #*111111)

(deftest map-into.bit-vector.9
  (let ((v (make-array '(8) :initial-contents '(0 1 0 0 1 1 0 1)
                       :fill-pointer 4
                       :element-type 'bit)))
    (map-into v #'identity '(1 1 1 1 1 1 0 0 1 1 1))
    (and (bit-vector-p v)
         (values (fill-pointer v)
                 v)))
  8
  #*11111100)

;;; Other specialized vectors

(deftest map-into.specialized-vector.1
  (do-special-integer-vectors
   (v #(1 2 3 4) nil)
   (let ((result (list nil nil nil nil)))
     (assert (eq (map-into result #'identity v) result))
     (assert (equal result '(1 2 3 4)))))
  nil)

(deftest map-into.specialized-vector.2
  (do-special-integer-vectors
   (v #(1 2 3) nil)
   (let ((result (list nil nil nil nil)))
     (assert (eq (map-into result #'identity v) result))
     (assert (equal result '(1 2 3 nil)))))
  nil)

(deftest map-into.specialized-vector.3
  (do-special-integer-vectors
   (v #(1 1 0 1 1) nil)
   (let ((result (list nil nil nil nil)))
     (assert (eq (map-into result #'identity v) result))
     (assert (equal result '(1 1 0 1)))))
  nil)

(deftest map-into.specialized-vector.4
  (do-special-integer-vectors
   (v #(1 2 1 2 2) nil)
   (let ((v2 #(2 1 2 2 1)))
     (assert (eq (map-into v #'identity v2) v))
     (assert (equalp v #(2 1 2 2 1)))))
  nil)

(deftest map-into.specialized-vector.5
  (let ((len 10))
    (loop for etype in '(short-float single-float double-float long-float)
          for vals = (loop for i below len collect (coerce i etype))
          for vec = (make-array len :initial-contents vals :element-type etype)
          for target = (loop repeat len collect nil)
          for result = (map-into target #'identity vec)
          unless (and (eq target result)
                      (= (length result) len)
                      (= (length vec) len)
                      (equal vals result))
          collect (list etype vals vec result)))
  nil)

(deftest map-into.specialized-vector.6
  (let ((len 10))
    (loop for cetype in '(short-float single-float double-float long-float)
          for etype = `(complex ,cetype)
          for vals = (loop for i from 1 to len collect (complex (coerce i cetype)
                                                                (coerce (- i) cetype)))
          for vec = (make-array len :initial-contents vals :element-type etype)
          for target = (loop repeat len collect nil)
          for result = (map-into target #'identity vec)
          unless (and (eq target result)
                      (= (length result) len)
                      (= (length vec) len)
                      (equal vals result))
          collect (list etype vals vec result)))
  nil)

(deftest map-into.specialized-vector.7
  (let ((len 10))
    (loop for etype in '(short-float single-float double-float long-float)
          for vals = (loop for i below len collect (coerce i etype))
          for target = (make-array len :initial-contents vals :element-type etype)
          for result = (map-into target #'identity vals)
          unless (and (eq target result)
                      (= (length result) len)
                      (every #'= result vals))
          collect (list etype vals result)))
  nil)

(deftest map-into.specialized-vector.8
  (let ((len 10))
    (loop for cetype in '(short-float single-float double-float long-float)
          for etype = `(complex ,cetype)
          for vals = (loop for i from 1 to len collect (complex (coerce i cetype)
                                                                (coerce (- i) cetype)))
          for target = (make-array len :initial-contents vals :element-type etype)
          for result = (map-into target #'identity vals)
          unless (and (eq target result)
                      (= (length result) len)
                      (every #'= result vals))
          collect (list etype vals result)))
  nil)

;;; Error cases

(deftest map-into.error.1
  (check-type-error #'(lambda (x) (map-into x (constantly nil))) #'sequencep)
  nil)

;;; The next test was changed because if the first argument
;;; is NIL, map-into is said to 'return nil immediately', so
;;; the 'should be prepared' notation for the error checking
;;; means that error checking may be skipped.
(deftest map-into.error.2
  (and (locally (declare (optimize (safety 3)))
                (handler-case (eval '(map-into nil #'identity 'a))
                              (type-error () nil)))
       :bad)
  nil)

(deftest map-into.error.3
  (check-type-error #'(lambda (x) (map-into (copy-seq '(a b c)) #'cons '(d e f) x))
                    #'sequencep)
  nil)

(deftest map-into.error.4
  (signals-error (map-into) program-error)
  t)

(deftest map-into.error.5
  (signals-error (map-into (list 'a 'b 'c)) program-error)
  t)

(deftest map-into.error.6
  (signals-error (locally (map-into 'a #'(lambda () nil)) t)
                 type-error)
  t)

(deftest map-into.error.7
  (signals-error (map-into (list 'a 'b 'c) #'cons '(a b c)) program-error)
  t)

(deftest map-into.error.8
  (signals-error (map-into (list 'a 'b 'c) #'car '(a b c)) type-error)
  t)

;;; Order of evaluation tests

(deftest map-into.order.1
  (let ((i 0) a b c)
    (values
     (map-into (progn (setf a (incf i)) (list 1 2 3 4))
               (progn (setf b (incf i)) #'identity)
               (progn (setf c (incf i)) '(a b c d)))
     i a b c))
  (a b c d) 3 1 2 3)

(deftest map-into.order.2
  (let ((i 0) a b c d)
    (values
     (map-into (progn (setf a (incf i)) (list 1 2 3 4))
               (progn (setf b (incf i)) #'list)
               (progn (setf c (incf i)) '(a b c d))
               (progn (setf d (incf i)) '(e f g h)))
     i a b c d))
  ((a e) (b f) (c g) (d h)) 4 1 2 3 4)
