   (is (= (->ACE :a 0 :c :e) 
 	 (join-ace (->A :a 0 :b :d)(->B :b 0 :c)(->D :d 0 :e)))))
 
+;;; calculate all permutations for a given list of sets...
+;;; e.g. 
+;;; (permute [[]] [[:a1 :a2][:b1 :b2 :b3][:c1]]) ->
+;;; ([:a1 :b1 :c1] [:a1 :b2 :c1] [:a1 :b3 :c1] [:a2 :b1 :c1] [:a2 :b2 :c1] [:a2 :b3 :c1])
+
+;;; TODO: should use recurse
+(defn- permute [state [head & tail]]
+  (if (empty? head)
+    state
+    (permute (mapcat (fn [i] (map (fn [j] (conj i j)) head)) state) tail)))
+
+;;; consider outer joins
+;; (defn- permute2 [inner state [head & tail]]
+;;   (if (empty? tail)
+;;     state
+;;     (if-let [head (if (and (empty? head) inner) nil [nil])]
+;; 	(permute (mapcat (fn [i] (map (fn [j] (conj i j)) head)) state) tail))))
+
+(deftest test-permute
+  (is (= 
+       [[:a1 :b1 :c1] [:a1 :b2 :c1] [:a1 :b3 :c1] [:a2 :b1 :c1] [:a2 :b2 :c1] [:a2 :b3 :c1]]
+       (permute [[]] [[:a1 :a2][:b1 :b2 :b3][:c1]]))))
+
 (deftest test-join-model
   (let [as (versioned-optimistic-map-model (str :as) :name :version >)
 	bs (versioned-optimistic-map-model (str :bs) :name :version >)

;;--------------------------------------------------------------------------------
;; impl

(defn- lhs-upsert [old-indeces i ks v]
  (let [old-index (nth old-indeces i)
	new-index (mapv (fn [index key] (group index (key v) v)) old-index ks)
	new-indeces (assoc old-indeces i new-index)]
    new-indeces))

;;; TODO

  )

(defn join-model [name joins join-fn]
  (let [state (apply join-views join-fn joins)]
    (->JoinModel name state (atom []))))

;;--------------------------------------------------------------------------------
