  ;;Checkers that work with collections.
 
-
-(defn- all-expected-permutations [expected]
-  (cond (and (not-any? extended-fn? expected)
-	     (not-any? regex? expected))
-	[expected]
-
-	(<= (count expected) 4)
-	(permutations expected)
-
-	:else
-	(rotations expected)))
-
-(defn- total-match? [comparison]
+(defn- inexact-checker? 
+  "Can the checker potentially match non-unique elements
+   in a seq? (Ex: regex #'a+' can match 'a' and 'aa'.)"
+  [checker]
+  (or (extended-fn? checker)
+      (regex? checker)))
+
+(defn- feasible-permutations 
+  "Permute the given list if it contains inexact checkers.
+   Only produces all permutations for short lists."
+  [checkers]
+  (cond (not-any? inexact-checker? checkers)
+        [checkers]
+
+        (<= (count checkers) 4)
+        (permutations checkers)
+
+        :else
+        (rotations checkers)))
+
+(defn- total-match?
+  "Have all the expected elements have been discovered?"
+  [comparison]
   (= (count (:expected-found comparison))
      (count (:expected comparison))))
 
-(defn- closer-match? [candidate best-so-far]
+(defn- closer-match? 
+  "Did the candidate match more expected elements than before?"
+  [candidate best-so-far]
   (> (count (:actual-found candidate))
      (count (:actual-found best-so-far))))
 
 (defn- better-of [candidate best-so-far]
   (if (closer-match? candidate best-so-far) candidate best-so-far))
 
-(defn- tack-on-to [hashmap & kvs]
-  (merge-with conj hashmap (apply (partial assoc {}) kvs)))
-
-(defn- collection-like? [thing]
+(defn- collection-like?
+  "Extend coll? to include strings."
+  [thing]
   (or (coll? thing)
       (string? thing)))
 
-(defn- right-hand-singleton? [thing]
+(defn- right-hand-singleton? 
+  "The kind of thing that, in (contains X), means (contains [X])"
+  [thing]
   (or (not (coll? thing)) (map? thing)))
 
 (defn- same-lengths? [actual expected]
   (= (count actual) (count expected)))
 
-(defn- expected-fits? [actual expected]
+(defn- expected-fits? 
+  "Could expected fit as a subsequence of actual?"
+  [actual expected]
   (>= (count actual) (count expected)))
 
-
-(defn- midje-try-re [re string function]
+(defn- noted-falsehood
+  "Produce a partially constructed chatty falsehood that contains
+   a :notes key with the strings."
+  [& strings ]
+  (tag-as-chatty-falsehood {:notes strings}))
+
+(defn- try-re 
+  "Use the function (re-find or re-matches) to apply re to the thing.
+   If function blows up, return a chatty failure about it."
+  [re thing function]
   (try
-    (function re string)
+    (function re thing)
     (catch Exception ex
-      (tag-as-chatty-falsehood {
-				:notes [(str (pr-str re)
-					     " can't be used on "
-					     (pr-str string)
-					     ", a " (type string) ".")]}))))
-  
-
--
-(defn in-any-order--one-permutation [actual expected kind]
-  (let [starting-candidate  {:actual-found [], :expected-found [],
-			     :expected-skipped-over [], :expected expected }
-	gaps-ok? (some #{:gaps-ok} kind)]
+      (noted-falsehood (format "%s can't be used on %s, a %s."
+                               (pr-str re) (pr-str thing) (type thing) ".")))))
+
+(defn- base-starting-candidate
+  "A data structure that represents which actual elements, matching
+   expected elements, have been found from an original set of expected
+   elements."
+  [expected]
+  {:actual-found [], :expected-found [], :expected expected })
+
++;; The code that compares two sequentials.
+
+(defn in-any-order--one-permutation
+  "Compare actual elements to expected, which is one of perhaps many
+   permutations of the original expected list. kind is a subset of
+   #{:gaps-ok :in-any-order}."
+  [actual expected kind]
+  (let [starting-candidate (assoc (base-starting-candidate expected) :expected-skipped-over [])
+        gaps-ok? (some #{:gaps-ok} kind)]
     (loop [walking-actual   actual
-	   walking-expected expected
-	   best-so-far      starting-candidate
-	   candidate        starting-candidate]
+           walking-expected expected
+           best-so-far      starting-candidate
+           candidate        starting-candidate]
 
       ;; (prn "walking actual" walking-actual "walking expected" walking-expected)
       (cond (or (empty? walking-actual) (empty? walking-expected))
-	    (better-of candidate best-so-far)
-
-	    (extended-= (first walking-actual) (first walking-expected))
-	    ;; window good so far, keep working on it
-	    (recur (rest walking-actual)
-		   (concat (:expected-skipped-over candidate) (rest walking-expected))
-		   best-so-far
-		   (merge 
-		    (tack-on-to candidate
-				:actual-found (first walking-actual)
-				:expected-found (first walking-expected))
-		    {:expected-skipped-over []}))
-
-	    (not (empty? (rest walking-expected)))
-	    ;; Perhaps another value in the current expected permutation will work.
-	    ;; We can, after all, be in any order.
-	    (recur walking-actual
-		   (rest walking-expected)
-		   best-so-far
-		   (tack-on-to candidate
-			       :expected-skipped-over (first walking-expected)))
-
-	    (not (empty? (rest walking-actual)))
-	    ;; OK, there's no match for this actual element in the whole expected.
-	    (if gaps-ok?
-	      ;; Since gaps are OK, we can drop the bad actual element and look for next one.
-	      (recur (rest walking-actual)
-		     (concat (:expected-skipped-over candidate) walking-expected)
-		     (better-of candidate best-so-far)
-		     (merge candidate {:expected-skipped-over []}))
-
-	      ;; Start again with a new actual: the tail of what we started with.
-	      (recur (rest (concat (:actual-found candidate) walking-actual))
-		   expected
-		   (better-of candidate best-so-far)
+            (better-of candidate best-so-far)
+
+            (extended-= (first walking-actual) (first walking-expected))
+            ;; A palpable hit! Try any remainder.
+            (recur (rest walking-actual)
+                   (concat (:expected-skipped-over candidate) (rest walking-expected))
+                   best-so-far
+                   (merge 
+                    (tack-on-to candidate
+                                :actual-found (first walking-actual)
+                                :expected-found (first walking-expected))
+                    {:expected-skipped-over []}))
+
+            (not (empty? (rest walking-expected)))
+            ;; Perhaps the next expected element will work. We can, after all, be in any order.
+            (recur walking-actual
+                   (rest walking-expected)
+                   best-so-far
+                   (tack-on-to candidate
+                               :expected-skipped-over (first walking-expected)))
+
+            (not (empty? (rest walking-actual)))
+            ;; OK, there's no match for this actual element in the whole expected.
+            (if gaps-ok?
+              ;; Since gaps are OK, we can drop the bad actual element and check the next one.
+              (recur (rest walking-actual)
+                     (concat (:expected-skipped-over candidate) walking-expected)
+                     (better-of candidate best-so-far)
+                     (merge candidate {:expected-skipped-over []}))
+
+              ;; This actual is blown. Try the next one.
+              (recur (rest (concat (:actual-found candidate) walking-actual))
+                   expected
+                   (better-of candidate best-so-far)
                    starting-candidate))
 
-	    :else 
-	    (better-of candidate best-so-far)))))
-
-(defmulti seq-comparison (fn [actual expected kind]
-			   (or (some #{:in-any-order} kind) :strict-order)))
+            :else 
+            (better-of candidate best-so-far)))))
+
+(defmulti seq-comparison
+  "Compare an actual seq to an expected seq, governed by kind."
+  (fn [actual expected kind]
+    (or (some #{:in-any-order} kind) :strict-order)))
 
 (defmethod seq-comparison :in-any-order
   [actual expected kind]
-
-  (loop [expected-permutations (all-expected-permutations expected)
-	 best-so-far {:actual-found [], :expected-found [],
-		      :expected-skipped-over [], :expected expected }]
+  (loop [expected-permutations (feasible-permutations expected)
+         best-so-far (base-starting-candidate expected)]
     (if (empty? expected-permutations)
       best-so-far
       (let [comparison (in-any-order--one-permutation actual
-						      (first expected-permutations)
-						      kind)]
-	(if (total-match? comparison)
-	  comparison
-	  (recur (rest expected-permutations)
-		 (better-of comparison best-so-far)))))))
+                                                      (first expected-permutations)
+                                                      kind)]
+        (if (total-match? comparison)
+          comparison
+          (recur (rest expected-permutations)
+                 (better-of comparison best-so-far)))))))
 
 (defmethod seq-comparison :strict-order
   [actual expected kind]
-  (let [starting-candidate  {:actual-found [], :expected-found [], :expected expected }
-	gaps-ok? (some #{:gaps-ok} kind)]
-    
-    (loop [actual           actual
-	   walking-actual   actual
+  (let [starting-candidate (base-starting-candidate expected)
+        gaps-ok? (some #{:gaps-ok} kind)]
+
+    ;; This embeds two loops. walking-actual controls the inner loop. It walks
+    ;; until success or it hits a mismatch. actual controls the outer loop.
+    ;; Upon each mismatch, it tries again with the #'rest of itself.
+    (loop [actual           actual  
+           walking-actual   actual
            walking-expected expected
            best-so-far      starting-candidate
            candidate        starting-candidate]

      (cond (or (empty? walking-actual) (empty? walking-expected))
            (better-of candidate best-so-far)

	    (extended-= (first walking-actual) (first walking-expected))
	    ;; actual good so far, keep working on it
	    (recur actual
	           (rest walking-actual)
		   (rest walking-expected)
		   best-so-far
		   (tack-on-to candidate
			       :actual-found (first walking-actual)
			       :expected-found (first walking-expected)))

	    (and gaps-ok? (not (empty? (rest walking-actual))))
	    ;; This is a gap in the walking actual. Skip it.
            (recur actual
	           (rest walking-actual)
		   walking-expected
		   best-so-far
                   candidate)

	    (not (empty? actual))
	    ;; See if we can find something better later on.
	    (recur (rest actual)
		   (rest actual)
		   expected
		   (better-of candidate best-so-far)
		   starting-candidate)))))

; Searches through particular types of actual results

(defn standardized-arguments [actual expected kind]
  ;; The choice to make these (throw Error) rather than be falsey is purely
  ;; one of implementation convenience.
  (cond (regex? expected)
	(cond (and (not (sequential? actual))
		   (not (empty? kind)))

	:else
	[actual expected kind]))


(defn- best-seq-match-actual [comparison]
  (str "Best match found: " (pr-str (:actual-found comparison))))

(defn- best-seq-match-expected [comparison expected]
  (if (or (some extended-fn? expected)
          (some regex? expected))
    (str "      It matched: ["
         (apply str
                (interpose " "
                           (map #(cond (and (fn? %) (:name (meta %)))
                                       (:name (meta %))

                                       :else
                                       (pr-str %))
                                (:expected-found comparison))))
         "] (in that order)")))
    
(defn- best-map-match-actual [comparison]
  (str "Best match found: {"
       (apply str
              (interpose ", "
                         (sort (map (fn [[k v]] (str (pr-str k) " " (pr-str v)))
                                    (:actual-found comparison)))))
       "}."))

(defn- best-map-match-expected [comparison expected]
  (if (or (some extended-fn? (vals expected))
          (some regex? (vals expected)))
    (str "      It matched: {"
         (apply str
                (interpose ", "
                           (sort (map (fn [[k v]]
                                        (cond (and (fn? v) (:name (meta v)))
                                              (str (pr-str k) " " (:name (meta v)))
                                              
                                              :else
                                              (str (pr-str k) " " (pr-str v))))
                                      (:expected-found comparison)))))
         "}.")))
    

;; TODO: try different combinations?
(defn map-comparison--one-permutation [actual expected keys]
  ;;  (prn "map-comparison" actual expected)
  (reduce (fn [so-far key]
            ;; (println so-far key)
            (if (and (find actual key)
                     (extended-= (get actual key) (get expected key)))
              (merge-with merge so-far {:actual-found {key (get actual key)}
                                        :expected-found {key (get expected key)} })
              so-far))
          {:actual-found {} :expected-found {} :expected expected}
          keys))

(defn map-comparison [actual expected kind]
  (loop [expected-permutations (feasible-permutations (keys expected))
         best-so-far {:actual-found [], :expected-found [],
                      :expected expected }]
    (if (empty? expected-permutations)
      best-so-far
      (let [comparison (map-comparison--one-permutation actual
                                                        expected
                                                        (first expected-permutations))]
        (if (total-match? comparison)
          comparison
          (recur (rest expected-permutations)
                 (better-of comparison best-so-far)))))))


(defn map-match? [actual expected kind]
;  (println 'map-match actual expected kind)
  (let [comparison (map-comparison actual expected kind)
        mismatch-description (fn [comparison expected]
                               (remove nil? [ (best-map-match-actual comparison)
                                              (best-map-match-expected comparison expected) ]))]
    (or (total-match? comparison)
        (tag-as-chatty-falsehood
         {
          :notes (mismatch-description comparison expected)}))))

(defn full-sequence-match? [actual expected kind]
  (let [comparison (seq-comparison actual expected kind)
        mismatch-description (fn [comparison expected]
                               (remove nil? [ (best-seq-match-actual comparison)
                                              (best-seq-match-expected comparison expected) ]))]
    (or (total-match? comparison)
        (tag-as-chatty-falsehood
         {
          :notes (mismatch-description comparison expected) }))))

(defn- actual-x-contains? [actual expected kind]
  ;;  (prn 'actual-x-contains? actual expected kind)
  (cond (map? actual)
        (map-match? actual expected kind)

        :else
        (full-sequence-match? actual expected kind))
  )

(defn- add-actual [actual result]
