 
 (defn ixes-of [vector values]
   (into-array (map #(ix-of vector %) values)))
+
+;; EXPRS
+
+(defn expr->fun [vars expr]
+  (apply js/Function (conj (vec vars) (str "return " expr ";"))))
 
 ;; PATTERNS
 


  )

;; FLOWS

(defrecord Union [nodes])
(defrecord FilterMap [nodes fun])
(defrecord Index [nodes key-ixes])
(defrecord Lookup [nodes index-node key-ixes val-ixes])

(defn flow->nodes [flow]
  (:nodes flow))

;; FLOW PLAN

(defrecord FlowPlan [node->flow flow->node])

(defn flow-plan->flow-state [{:keys [node->flow]}]
  (let [node->state (into-array (for [_ node->flow] nil))
        node->out-nodes (into-array (for [_ node->flow] #js []))
        node->facts (into-array (for [_ node->flow] #js []))
        node->update! (into-array (for [_ node->flow] nil))]
    (dotimes [node (count node->flow)]
      (let [flow (nth node->flow node)]
        (aset node->state node
              (condp = (type flow)
                Union (transient #{})
                FilterMap nil
                Index (transient {})
                Lookup nil))
        (doseq [in-node (:nodes flow)]
          (apush (aget node->out-nodes in-node) node))
        (aset node->update! node
              (condp = (type flow)
                Union (union-update!)
                FilterMap (filter-map-update! (:fun flow))
                Index (index-update! (:key-ixes flow))
                Lookup (lookup-update! (:index-node flow) (:key-ixes flow) (:val-ixes flow))))))
    (FlowState. node->state node->out-nodes node->facts node->update!)))

(defn memory->node [memory]
  (case memory
    :known 0
    :pretended 1
    :remembered 2
    :forgotten 3))

(defn memory->nodes [memory]
  (case memory
    :known [0]
    :pretended [1]
    :remembered [2]
    :forgotten [3]
    :known&pretended [0 1]))

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

(defn add-output [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]
  (if-let [node (flow->node flow)]
    [flow-plan node]
    (let [node (count node->flow)
          node->flow (conj node->flow flow)
          flow->node (assoc flow->node flow node)]
      [(FlowPlan. node->flow flow->node) node])))

(comment
  (deffact edge "[x] has an edge to [y]")
  (deffact connected "[x] is connected to [y]")

  (def known&pretended [0 1])

  (let [plan empty-flow-plan
        [plan edges] (add-flow plan (FilterMap. known&pretended (fn [x] (when (= edge (.-shape x)) x))))
        [plan connecteds] (add-flow plan (FilterMap. known&pretended (fn [x] (when (= connected (.-shape x)) x))))
        [plan edges-index] (add-flow plan (Index. [edges] #js [1]))
        [plan connecteds-index] (add-flow plan (Index. [connecteds] #js [0]))
        [plan edges-lookup] (add-flow plan (Lookup. [edges-index] connecteds-index #js [1] #js [0 3]))
        [plan connecteds-lookup] (add-flow plan (Lookup. [connecteds-index] edges-index #js [0] #js [2 1]))
        [plan new-connecteds] (add-flow plan (FilterMap. [edges edges-lookup connecteds-lookup] (fn [x] (->connected (fact-ix x 0) (fact-ix x 1)))))
        plan (add-output plan new-connecteds :pretended)
        state (flow-plan->flow-state plan)]
    (apush* (aget (:node->facts state) 0) #js [(->edge 0 1) (->edge 1 2) (->edge 2 3) (->edge 3 1)])
    (-> (fixpoint! state) :node->state (aget 1) persistent!))
  )

;; IXES

(defn ix-of [vector value]

  ((pattern->deconstructor (->eg 'a 'a 'a)) (->eg "a" "a" "a"))
  )

;; FUNLIKE

(defrecord ShapeFun [shape])
(defrecord ConstructorFun [vars pattern])
(defrecord DeconstructorFun [pattern])
(defrecord WhenFun [vars expr])
(defrecord LetFun [vars expr])

(defn funlike->fun [funlike]
  (condp = (type funlike)
    ShapeFun (let [{:keys [shape]} funlike]
               (fn [x]
                 (when (= shape (.-shape x))
                   x)))
    ConstructorFun (let [{:keys [vars pattern]} funlike]
                     (pattern->constructor pattern vars))
    DeconstructorFun (let [{:keys [pattern]} funlike]
                       (pattern->deconstructor pattern))
    WhenFun (let [{:keys [vars expr]} funlike
                  when-fun (expr->fun vars expr)]
              (fn [fact]
                (when (.apply when-fun nil (.-values fact))
                  fact)))
    LetFun (let [{:keys [vars expr]} funlike
                 let-fun (expr->fun vars expr)]
             (fn [fact]
               (let [values (.-values fact)
                     new-value (.apply let-fun nil values)
                     new-values (aclone values)]
                 (apush new-values new-value)
                 (Fact. nil new-values nil))))))

;; FLOWS

(defrecord Union [nodes])
(defrecord FilterMap [nodes funlike])
(defrecord Index [nodes key-ixes])
(defrecord Lookup [nodes index-node key-ixes val-ixes])

(defn flow->nodes [flow]
  (:nodes flow))

;; FLOW PLAN

(defrecord FlowPlan [node->flow flow->node])

(defn flow-plan->flow-state [{:keys [node->flow]}]
  (let [node->state (into-array (for [_ node->flow] nil))
        node->out-nodes (into-array (for [_ node->flow] #js []))
        node->facts (into-array (for [_ node->flow] #js []))
        node->update! (into-array (for [_ node->flow] nil))]
    (dotimes [node (count node->flow)]
      (let [flow (nth node->flow node)]
        (aset node->state node
              (condp = (type flow)
                Union (transient #{})
                FilterMap nil
                Index (transient {})
                Lookup nil))
        (doseq [in-node (:nodes flow)]
          (apush (aget node->out-nodes in-node) node))
        (aset node->update! node
              (condp = (type flow)
                Union (union-update!)
                FilterMap (filter-map-update! (funlike->fun (:funlike flow)))
                Index (index-update! (:key-ixes flow))
                Lookup (lookup-update! (:index-node flow) (:key-ixes flow) (:val-ixes flow))))))
    (FlowState. node->state node->out-nodes node->facts node->update!)))

(defn memory->node [memory]
  (case memory
    :known 0
    :pretended 1
    :remembered 2
    :forgotten 3))

(defn memory->nodes [memory]
  (case memory
    :known [0]
    :pretended [1]
    :remembered [2]
    :forgotten [3]
    :known&pretended [0 1]))

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

(defn add-output [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]
  (if-let [node (flow->node flow)]
    [flow-plan node]
    (let [node (count node->flow)
          node->flow (conj node->flow flow)
          flow->node (assoc flow->node flow node)]
      [(FlowPlan. node->flow flow->node) node])))

(comment
  (deffact edge "[x] has an edge to [y]")
  (deffact connected "[x] is connected to [y]")

  (def known&pretended [0 1])

  (let [plan empty-flow-plan
        [plan edges] (add-flow plan (FilterMap. known&pretended (ShapeFun. edge)))
        [plan connecteds] (add-flow plan (FilterMap. known&pretended (ShapeFun. connected)))
        [plan edges-index] (add-flow plan (Index. [edges] #js [1]))
        [plan connecteds-index] (add-flow plan (Index. [connecteds] #js [0]))
        [plan edges-lookup] (add-flow plan (Lookup. [edges-index] connecteds-index #js [1] #js [0 3]))
        [plan connecteds-lookup] (add-flow plan (Lookup. [connecteds-index] edges-index #js [0] #js [2 1]))
        [plan new-connecteds] (add-flow plan (FilterMap. [edges edges-lookup connecteds-lookup] (ConstructorFun. '[x z] (->connected 'x 'z))))
        plan (add-output plan new-connecteds :pretended)
        state (flow-plan->flow-state plan)]
    (apush* (aget (:node->facts state) 0) #js [(->edge 0 1) (->edge 1 2) (->edge 2 3) (->edge 3 1)])
    (-> (fixpoint! state) :node->state (aget 1) persistent!))
  )

;; CLAUSES

