   (reset [this solver constraint]
          (dotimes [i (alength vars)]
            (.set-watch solver (aget vars i) constraint true)))
+  (val [this solver constraint]
+       1)
   (split-left [this solver constraint]
               false)
   (split-right [this solver constraint])


;; los and his are inclusive

;; propagate updates the current lo/hi for each var and set the solver to failed
;; split-left either:
;;   breaks the solutions into two branches, sets the left branch, returns true
;;   has only one possible solution, does nothing, returns false
;; split-right:
;;   breaks the solutions into two branches, sets the right branch

(deftype Contains [iterator vars scratch-key]
  Object
  (reset [this solver constraint]
         (.reset iterator))
  (propagate [this solver constraint]
             (let [los (.-los solver)
                   his (.-his solver)]


(defn run-product-join-prop [min-keys key-len updates]
  (let [product-tree (tree min-keys key-len)
        [tree _] (apply-to-tree (tree min-keys key-len) updates)
        elems (.elems tree)
        _ (dotimes [i (alength elems)]
            (dotimes [j (alength elems)]
              (when (and (== 0 (mod i 2)) (== 0 (mod j 2)))
                (.update product-tree (.concat (aget elems i) (aget elems j)) (* (aget elems (+ i 1)) (aget elems (+ j 1)))))))
        solver (solver
                (* 2 key-len)
                #js [(contains (iterator tree) (into-array (range 0 key-len)))
                     (contains (iterator tree) (into-array (range key-len (* 2 key-len))))])]
    (prim= (.elems product-tree) (.elems solver))))

(defn product-join-prop [key-len]
  (prop/for-all [min-keys gen/s-pos-int
                 updates (gen/vector (gen-update key-len))]
                (run-product-join-prop min-keys key-len updates)))

(comment
  (dc/quick-check 1000 (least-prop 1))
