            (set! upper (.pop split-uppers))))))
 
 (defn contains [iterator ixes]
-  (let [seek-key (least-key (alength ixes))
-        lower (.seek-gte iterator seek-key)
-        upper (greatest-key (alength ixes))]
-    (Contains. iterator seek-key lower upper ixes #js [] #js [] #js [] #js [] #js [])))
+  (let [lower (least-key (alength ixes))
+        upper (greatest-key (alength ixes))
+        key (.seek-gte iterator lower)]
+    (aset lower 0 (aget key 0))
+    (Contains. iterator lower upper ixes #js [] #js [] #js [] #js [] #js [])))
+
+;; SOLVER
+
+(deftype Solver [constraints ^:mutable los ^:mutable his ^:mutable depth pushed-los pushed-his ^:mutable end?]
+  Object
+  (reset [this]
+         (dotimes [i (alength constraints)]
+           (.reset (aget constraints i)))
+         (dotimes [i (alength los)]
+           (aset los i least)
+           (aset his i greatest))
+         (set! depth 0)
+         (aclear pushed-los)
+         (aclear pushed-his)
+         (set! end? false))
+  (split [this current]
+         (debug :splitting los his)
+         (loop [splitter current]
+           (if (true? (.split (aget constraints splitter)))
+             (do
+               (apush pushed-los (aclone los))
+               (apush pushed-his (aclone his))
+               (set! depth (+ depth 1))
+               (dotimes [i (alength constraints)
+                         :when (not (identical? i splitter))]
+                 (.push (aget constraints i)))
+               splitter)
+             (let [new-splitter (mod (+ splitter 1) (alength constraints))]
+               (if (identical? new-splitter current)
+                 (assert false "Can't split anything!")
+                 (recur new-splitter))))))
+  (pop [this]
+       (set! los (.pop pushed-los))
+       (set! his (.pop pushed-his))
+       (set! depth (- depth 1))
+       (dotimes [i (alength constraints)]
+         (.pop (aget constraints i))))
+  (next [this]
+        (debug :next los his pushed-los pushed-his)
+        (when (false? end?)
+          (loop [current 0
+                 last-changed (- (alength constraints) 1)]
+            (let [result (.propagate (aget constraints current) los his)]
+              (debug :next-loop current result los his pushed-los pushed-his)
+              (condp identical? result
+                changed (recur (mod (+ current 1) (alength constraints)) current)
+                unchanged (if (not (identical? current last-changed))
+                            (recur (mod (+ current 1) (alength constraints)) last-changed)
+                            (if (key= los his)
+                              (let [result (aclone los)]
+                                (if (> depth 0)
+                                  (.pop this)
+                                  (set! end? true))
+                                result)
+                              (let [splitter (.split this current)]
+                                (recur splitter (mod (+ splitter 1) (alength constraints))))))
+                failed (if (> depth 0)
+                         (do
+                           (.pop this)
+                           (recur current (mod (- current 1) (alength constraints))))
+                         (do
+                           (set! end? true)
+                           nil))))))))
+
+(defn solver [constraints num-vars]
+  (Solver. constraints (least-key num-vars) (greatest-key num-vars) 0 #js [] #js [] false))
 
 ;; TESTS
 

(def failed 2)

;; lower and upper are inclusive
;; lower is aliased to iterator, must not be mutated
(deftype Contains [iterator seek-key ^:mutable lower ^:mutable upper ixes pushed|split pushed-nodes pushed-ixes split-lowers split-uppers]
  Object
  (reset [this]
         (.reset iterator)
         (dotimes [i (alength seek-key)]
           (aset seek-key i least))
         (set! lower (.seek-gte iterator seek-key))
         (dotimes [i (alength upper)]
           (aset upper i greatest))
         (aclear pushed-nodes)
         (aclear pushed-ixes)
         (aclear split-lowers)
         (aclear split-uppers))
  (propagate [this los his]
             (if (nil? lower)
               failed
               (do
                 (dotimes [i (alength ixes)]
                   (aset seek-key i (aget los (aget ixes i))))
                 (when (key-lt lower seek-key)
                   (set! lower (.seek-gte iterator seek-key)))
                 (if (or (nil? lower)
                         (key-lt upper lower))
                   failed
                   (let [changed? false]
                     (dotimes [i (alength ixes)]
                       (let [ix (aget ixes i)
                             new-lo (aget lower i)
                             old-hi (aget his ix)
                             new-hi (aget upper i)]
                         (when (val-lt (aget los ix) new-lo)
                           (aset los ix new-lo)
                           (set!! changed? true))
                         (if (val-lt new-hi old-hi)
                           (do
                             (aset his ix new-hi)
                             (set!! changed? true))
                           (aset upper i old-hi))))
                     (if (true? changed?)
                       changed
                       unchanged))))))
  (push [this]
        (apush pushed|split true)
        (apush pushed-nodes (.-node iterator))

            [cemerick.double-check.properties :as prop :include-macros true]
            [cemerick.pprng :as pprng]
            clojure.set)
  (:require-macros [aurora.macros :refer [debug check apush apush* amake aclear typeof set!! dofrom]]))

;; COMPARISONS

