   #+cljs
   (:require-macros [cljs.core.match.macros :refer [match]]))
 
-(defn ^:private count-before
+(def ^:private nop? (some-fn nil? #(= (second %) 0) #(= (second %) [])))
+
+(defn pack
+  [edit]
+  (let [edit (remove nop? edit)]
+    (reduce
+      (fn [edit op]
+        (let [[o1 p1] (peek edit)
+              [o2 p2] op]
+          (if (= o1 o2)
+            (conj (pop edit)
+                  (case o1
+                    :retain [:retain (+ p1 p2)]
+                    :delete [:delete (+ p1 p2)]
+                    :insert [:insert (into p1 p2)]))
+            (conj edit op))))
+      (vec (take 1 edit))
+      (rest edit))))
+
+(defn ^:private pack-pairs
+  [pairs]
+  (map pack (apply mapv vector pairs)))
+
+(defn count-before
   [edit]
   (letfn
     [(inc-count

            (recur (+ key p) (+ index p) (rest edit))
            (recur key (+ index p) (rest edit)))))
      key)))

(defn ^:private common-prefix
  [a b]
  (let [a-count (count a)
        b-count (count b)]
    (loop [i 0]
      (if (and (< i a-count) (< i b-count) (= (nth a i) (nth b i)))
        (recur (inc i))
        i))))

(defn ^:private common-suffix
  [a b]
  (let [a-count (count a)
        b-count (count b)]
    (loop [i 0]
      (if (and (< i a-count) (< i b-count)
               (= (nth a (- a-count i 1)) (nth b (- b-count i 1))))
        (recur (inc i))
        i))))

(defn ^:private create-edit
  [a b pre suf]
  (pack [[:retain pre]
         [:delete (- (count a) pre suf)]
         [:insert (subvec b pre (- (count b) suf))]
         [:retain suf]]))

(defn diff
  [a b]
  (if (= a b)
    [[:retain (count a)]]
    (let [a (vec a)
          b (vec b)
          pre (common-prefix a b)
          suf (common-suffix a b)]
      (if (< pre suf)
        (let [a' (subvec a 0 (- (count a) suf))
              b' (subvec b 0 (- (count b) suf))
              pre (common-prefix a' b')]
          (create-edit a b pre suf))
        (let [a' (subvec a pre)
              b' (subvec b pre)
              suf (common-suffix a' b')]
          (create-edit a b pre suf))))))

           [[:retain _] [:delete _]] (conj out op2)
           ;; We need these cases for weird edge cases
           ;; e.g. (compose [[:delete 0]] [[:retain 0]])
           [nil _  ] out
           [_   nil] out))]
      (->> (align-compose edit1 edit2)
           (reduce compare-ops [])
           pack)))
  ([edit1 edit2 & more]
    (reduce compose (compose edit1 edit2) more)))

(defn ^:private align-transform
  [edit1 edit2]
  (loop [edit1 edit1, edit2 edit2, out [[] []]]
    (let [op1 (first edit1) op2 (first edit2)]
      (cond
        (nil? op1) (into out (map #(vector nil %) edit2))
        (nil? op2) (into out (map #(vector % nil) edit1))
        (= :insert (first op1)) (recur (rest edit1) edit2 (conj out [op1 nil]))
        (= :insert (first op2)) (recur edit1 (rest edit2) (conj out [nil op2]))
        :else
        (let [[o1 p1] op1
              [o2 p2] op2]
          (case (compare p1 p2)
            -1 (recur (rest edit1)
                      (cons [o2 (- p2 p1)] (rest edit2))
                      (conj out [op1 [o2 p1]]))
            0  (recur (rest edit1) (rest edit2)
                      (conj out [op1 op2]))
            1  (recur (cons [o1 (- p1 p2)] (rest edit1))
                      (rest edit2)
                      (conj out [[o1 p2] op2]))))))))

(defn transform
  [edit1 edit2]
  (assert (= (count-before edit1) (count-before edit2))
          (str "transom/transform: length mismatch" \newline
               "Edit 1: " edit1 \newline
               "Edit 2: " edit2))
  (letfn
    [(compare-ops
       [out [op1 op2]]
       (match [op1 op2]
         [[:insert p1] _           ] (conj out [op1 [:retain (count p1)]])
         [_            [:insert p2]] (conj out [[:retain (count p2)] op2])
         [[:retain _ ] [:retain _ ]] (conj out [op1 op2])
         [[:retain p1] [:delete p2]] (conj out [nil op2])
         [[:delete p1] [:retain p2]] (conj out [op1 nil])
         :else (conj out [nil nil])))]
    (->> (align-transform edit1 edit2)
         (reduce compare-ops [])
         pack-pairs)))

(defn transform-key
  [key edit destructive?]
