   (-seq [this]
         (seq (.keys this))))
 
-(defn solver [num-vars constraints constraint->ixes]
-  (let [constraint->los (amake [i (alength constraint->ixes)]
-                               (make-array (alength (aget constraint->ixes i))))
-        constraint->his (amake [i (alength constraint->ixes)]
-                               (make-array (alength (aget constraint->ixes i))))
-        los (least-key num-vars)
-        his (greatest-key num-vars)]
-    (Solver. constraints constraint->ixes constraint->los constraint->his los his 0 #js [] #js [] #js [] false)))
+(defn solver [num-vars constraints]
+  (let [los (least-key num-vars)
+        his (greatest-key num-vars)
+        var->constraint->watching? (amake [_ (* num-vars (alength constraints))] false)
+        constraint->dirty? (amake [_ (alength constraints)] true)]
+    (Solver. constraints false 0
+             los his var->constraint->watching? constraint->dirty?
+             #js [] #js [] #js [] #js [] #js [])))
 
 ;; TESTS
 

      _ (.assoc! tree2 #js ["d" "c"] 0)
      _ (.assoc! tree2 #js ["b" "d"] 0)
      s (solver 3
                #js [(contains (iterator tree1))
                     (contains (iterator tree2))]
                #js [#js [0 2]
                     #js [1 2]])
      ]
  [(.next s) (.next s) (.next s) (.next s) (.next s)]
  )


;; los and his are inclusive

(deftype Solver [constraints ^:mutable failed? ^:mutable depth
                 ^:mutable los ^:mutable his ^:mutable var->constraint->watching? ^:mutable constraint->dirty?
                 pushed-los pushed-his pushed-var->constraint->watching? pushed-constraint->dirty? pushed-splitters]
  Object
  (reset [this]
         (dotimes [i (alength constraints)]
           (.reset (aget constraints i)))
         (set! depth 0)
         (set! failed? false)
         (dotimes [i (alength los)]
           (aset los i least)
           (aset his i greatest))
         (dotimes [i (alength var->constraint->watching?)]
           (aset var->constraint->watching? i false))
         (dotimes [i (alength constraint->dirty?)]
           (aset constraint->dirty? i true))
         (aclear pushed-los)
         (aclear pushed-his)
         (aclear pushed-var->constraint->watching?)
         (aclear pushed-constraint->dirty?)
         (aclear pushed-splitters))
  (set-lo [this var new-lo]
          (when-not (identical? (aget los var) new-lo)
            (if (val-lt (aget his var) new-lo)
              (set! failed? true)
              (do
                (aset los var new-lo)
                (.set-dirty this var)))))
  (set-hi [this var new-hi]
          (when-not (identical? (aget his var) new-hi)
            (if (val-lt new-hi (aget los var))
              (set! failed? true)
              (do
                (aset his var new-hi)
                (.set-dirty this var)))))
  (set-eq [this var new-val]
          (let [old-lo (aget los var)
                old-hi (aget his var)]
            (when-not (and (identical? old-lo new-val) (identical? old-hi new-val))
              (if (or (val-lt new-val old-hi) (val-lt old-hi new-val))
                (set! failed? true)
                (do
                  (aset los var new-val))
                  (aset his var new-val)
                  (.set-dirty this var)))))
  (set-watch [this constraint var value]
             (aset var->constraint->watching? (+ (* (alength constraints) var) constraint) value))
  (set-dirty [this var]
             (let [start (* (alength constraints) var)]
               (dotimes [constraint (alength constraints)]
                 (when (true? (aget var->constraint->watching? (+ start constraint)))
                   (aset constraint->dirty? constraint true)))))
  (split [this]
         (debug :splitting-left los his)
         (set! depth (+ depth 1))
         (apush pushed-los (aclone los))
         (apush pushed-his (aclone his))
         (apush pushed-var->constraint->watching? (aclone var->constraint->watching?))
         (apush pushed-constraint->dirty? (aclone constraint->dirty?))
         (loop [splitter 0]
           (if (< splitter (alength constraints))
             (if (.split-left (aget constraints splitter) this splitter)
               (do
                 (apush pushed-splitters splitter)
                 (debug :split-left los his splitter))
               (recur (+ splitter 1)))
             (assert false "Can't split anything!"))))
  (backtrack [this]
             (set! failed? false)
             (set! depth (- depth 1))
             (set! los (.pop pushed-los))
             (set! his (.pop pushed-his))
             (set! var->constraint->watching? (.pop pushed-var->constraint->watching?))
             (set! constraint->dirty? (.pop pushed-constraint->dirty?))
             (let [splitter (.pop pushed-splitters)]
               (debug :splitting-right los his splitter)
               (.split-right (aget constraints splitter) this splitter)
               (debug :split-right los his splitter failed?)))
  (next [this]
        (debug :next los his pushed-los pushed-his)
        (loop [constraint 0]
          (if (true? failed?)
            (do
              (debug :failed)
              (when (> depth 0)
                (.backtrack this)
                (recur 0)))
            (if (< constraint (alength constraints))
              (if (false? (aget constraint->dirty? constraint))
                (recur (+ constraint 1))
                (do
                  (debug :propagating constraint los his)
                  (aset constraint->dirty? constraint false)
                  (.propagate (aget constraints constraint) this constraint)
                  (debug :propagated constraint los his)
                  (recur 0)))
              (if (key= los his)
                (do
                  (set! failed? true) ;; force solver to backtrack next time
                  (debug :done los)
                  (aclone los))
                (do
                  (.split this)
                  (recur 0)))))))
  (keys [this]
       (let [results #js []]
         (loop []
