 
 ;; Correctness: Each clause must appear at least once in the plan
 ;; Heuristic: Each Recall clause is used at most once in the plan
-;; Heuristic: Each Filter/Let clause is used at most once per path in the pln
-
-(defn rule->flow-plan [flow-plan rule]
-  (loop [flow-plan flow-plan
-         shape []
-         clauses (:clauses rule)]
-    (if-let [[new-flow-plan new-shape clause] (first (map #(clause->flow-plan flow-plan shape %) clauses))]
-      (recur new-flow-plan new-shape (filter #(not= clause %) clauses))
-      (do (assert (empty? clauses) (str "Cannot join " (pr-str shape) " with " (pr-str clauses)))
-        flow-plan))))
+;; Heuristic: Each Filter/Let clause is used at most once per path in the plan
+
+
+(defn rule->flow-plan [rule]
+  (let [recalls (filter #(= Recall (type %)) (:clauses rule))
+        computes (filter #(= Compute (type %)) (:clauses rule))
+        outputs (filter #(= Output (type %)) (:clauses rule))
+        computes? (fn [plan node vars computes]
+                    (if-let [[plan node vars] (first (map #(compute? plan node vars) computes)))
+        node->remaining-computes (reduce recalls empty-flow-plan )]
+    (loop [plan plan
+           node->remaining-computes (zipmap recall-nodes (repeat computes))])

(def empty-flow-plan
  (FlowPlan. [(Union. #{}) (Union. #{}) (Union. #{}) (Union. #{})] {}))

(defn output-flow [flow-plan node memory]
  (update-in flow-plan [:node->flow (memory->node memory) :nodes] conj node))

(defn add-flow [{:keys [node->flow flow->node] :as flow-plan} flow]

(defrecord Recall [memory pattern]) ;; memory is one of :known :pretended :remembered :forgotten :known&pretended
(defrecord Compute [pattern])
(defrecord Output [memory pattern]) ;; memory is one of :pretended :remembered :forgotten
;; (defrecord OutputMany [memory vars expr]) ;; memory is one of :pretended :remembered :forgotten

;; horrible non-relational things
(deffact Let "Let [name] be the result of [vars] [expr]")
(deffact When "When [vars] [expr]")

(defn recall->node [plan {:keys [memory pattern]}]
  (let [[plan node] (add-flow plan (FilterMap. (memory->nodes memory) (pattern->deconstructor pattern)))]
    [plan node (pattern->vars pattern)]))

;; if clause can be calculated somehow then return a new [plan node vars] pair that calculates it
;; TODO make this extensible
(defn compute->node [plan node vars {:keys [pattern]}]
  (condp = (.-shape pattern)
    Let (when (every? (set vars) (:vars pattern))
          (let [let-fun (expr->fun vars (:expr pattern))
                filter-map-fun (fn [fact]
                                 (let [new-value (.apply let-fun (.-values fact))
                                       new-values (aclone (.-values fact))]
                                   (apush new-values new-value)
                                   (Fact. nil new-values)))
                [plan node] (add-flow plan (->FilterMap [node] filter-map-fun))]
            [plan node (conj vars (:name pattern))]))
    When (when (every? (set vars) (:vars pattern))
           (let [when-fun (expr->fun vars (:expr pattern))
                 filter-map-fun (fn [fact]
                                  (when (.apply when-fun (.-values fact))
                                    fact))
                 [plan node] (add-flow plan (->FilterMap [node] filter-map-fun))]
             [plan node vars]))))

(defn computes->node [plan node vars computes]
  (let [orig-node node] ;; too much shadowing :S
    (loop [plan plan
           node node
           vars vars
           computes-remaining computes
           computes-unapplied []]
      (if-let [[compute & computes-remaining] (seq computes-remaining)]
        (if-let [[new-plan new-node new-vars] (compute->node plan node vars compute)]
          (recur new-plan new-node new-vars computes-remaining computes-unapplied)
          (recur plan node vars computes-remaining (conj computes-unapplied compute)))
        (if (= node orig-node)
          [plan node vars computes-unapplied]
          (computes? plan node vars computes-unapplied))))))

(defn output->node [plan node vars {:keys [memory pattern]}]
  (assert (every? (set vars) (pattern->vars pattern)))
  (let [[plan output-node] (add-flow plan (FilterMap. [node] (pattern->constructor pattern)))
        plan (add-output plan output-node memory)]
    plan))


;; RULES

