(defparameter *implementations*
  (list
   ;; ideone.com/g6m1EK
   (defun find-second-smallest-number-r1 (list)
     (loop for n in list
           for second = n then (if (< first n second) n second)
           minimize n into first
           finally (return (if (> second first) second nil))))

   (defun find-second-smallest-number-r2-UNIQ-AND-SORT (list)
     (let ((result (if (null list)
                       nil
                       (nth 1 (sort (remove-duplicates list) #'<)))))
       ;; sort 函数によって比較函数 < が適用されることで、数以外を含む
       ;; ときはエラーとなることを期待したが、要素が一つだけのときは比
       ;; 較されないためエラーも起きない。
       (when (and (eq result nil)
                  (not (null list))
                  (not (subtypep (type-of (first list)) 'real)))
         (error 'type-error :expected-type 'real :datum (first list)))
       result))

   (defun find-second-smallest-number-r3 (list)
     (when (eq (nth 1 list) nil)
       ;; (nth 1 list) の値が nil のとき、LIST は空リストであるか、要
       ;; 素数が 1 であるか、または先頭から二つ目の要素が nil である。
       ;; このとき LIST の要素に大小比較可能な数以外の要素が含まれる場
       ;; 合はコンディション type-error を送出する。
       (loop for x in list
             unless (subtypep (type-of x) 'real)
               do (error 'type-error :expected-type 'real :datum x)))
     (loop for n in list
           for 2nd = nil then (if (= n 1st)
                                  2nd
                                  (if (null 2nd)
                                      (max n 1st)
                                      (if (< n 1st)
                                          (max n 1st)
                                          (min n 2nd))))
           minimize n into 1st
           finally (return 2nd)))))

(defparameter *test-cases*
  (let ((N 100000)
        (type-error (make-condition 'type-error :datum nil :expected-type t)))
    `(((4 5 1 7 1 2 8 9 2 7) 2)
      ((2 2 2 2 2 2 2 2 2 2) nil)
      ((1) nil)
      ((2 1) 2)
      ((1 2) 2)
      ((3 2 1) 2)
      ((1/2 1/3 1/42) 1/3)
      ((,pi 3.14) ,pi)
      ((3.141592653589793d0 ,(log #c(-1 0))) ,type-error)
      ((0) nil)
      (() nil)
      ((t) ,type-error)
      ((1 nil) ,type-error)
      ((1 2 3 nil) ,type-error)
      (,(cons 2 (make-list N :initial-element 1)) 2)
      (,(cons 2 (make-list N :initial-element nil)) ,type-error))))

(defun run-tests (fn test-cases &optional (detailed-output-p t))
  (flet ((test (expected result)
           (if (and detailed-output-p (subtypep (type-of expected) 'condition))
               (subtypep (type-of result) (type-of expected))
               (eql result expected))))
    (loop with *print-length* = 10
          with *print-right-margin* = 5000
          for (arg expected) in test-cases
          for result = (handler-case (funcall fn arg)
                         (error (condition)
                           (if detailed-output-p
                               condition
                               (class-name (class-of condition)))))
          for all-tests-passed-p = (test expected result)
            then (and all-tests-passed-p (test expected result))
          do (format t (if detailed-output-p
                           "~:[❌~;✓~] ~:S → ~S~%"
                           "~*~:S → ~A~%")
                     (test expected result)
                     arg
                     result)
          finally (return all-tests-passed-p))))

(defun make-string-sink ()
  (make-array 0 :element-type 'character :fill-pointer 0 :adjustable t))
(defun clear-string-sink (sink)
  (setf (fill-pointer sink) 0))

(let ((debug nil))
  (let ((fn (car (last *implementations*))))
    (run-tests fn *test-cases* nil))
  (format t "~%~50@{=~}~2%" t)
  (let ((tests-output (make-string-sink))
        all-tests-passed-p)
    (dolist (fn *implementations*)
      #+clisp (format t "~2%")
      (format t "Testing ‘~(~A~)’... " fn)
      (with-output-to-string (*standard-output* tests-output)
        (setq all-tests-passed-p (run-tests fn *test-cases*)))
      (format t "~:[FAILED~;OK~]~%" all-tests-passed-p)
      (if all-tests-passed-p
          ;; テストを通過したら速度も計測。
          (time (loop repeat 100000 do (funcall fn (caar *test-cases*))))
          (if debug
              (write-line tests-output)
              (terpri)))
      (clear-string-sink tests-output))))
