                           (map deref))))
          #{})))))
 
-(defn qvar?
+(defn- qvar?
   "Returns true, if x is a qvar (a symbol prefixed with '?')"
   [x] (and (symbol? x) (= \? (.charAt ^String (name x) 0))))
 
-(defn bind-translator
+(defn- bind-translator
   [vs? vp? vo? s p o]
   (if vs?
     (if vp?

                           (fn [r] (not= (r 1) (r 2))))
    :else                (constantly true)))

(defn score-collect-join
  [vertex]
  (let [state @(:state vertex)]
    (if (and (seq (::f/uncollected state))
             (== (count (::f/signal-map @(:state vertex))) 2))
      1 0)))

(defn- sole-user?
  [src user]
  (let [n (f/neighbors src)]
    (and (== 1 (count n)) (= user (first n)))))

(defrecord BasicTripleQuery [id acc result pattern]
  #?@(:clj
       [clojure.lang.IDeref (deref [_] @result)]
       :cljs
       [IDeref (-deref [_] @result)])
  ITripleQuery
  (raw-pattern
    [_] pattern)
  (result-vertex
    [_] result)
  f/IGraphComponent
  (add-to-graph!
    [_ g]
    (let [{:keys [subj pred obj]} (triple-indices g)
          acc  (f/add-vertex!
                g {} {::f/collect-fn collect-select})
          res  (f/add-vertex!
                g nil
                {::f/collect-fn       (aggregate-select g)
                 ::f/score-collect-fn (score-collect-min-signal-vals 3)})
          [s p o] pattern
          this (assoc _ :acc acc :result res)]
      (f/add-edge! g subj acc signal-index-select [0 s])
      (f/add-edge! g pred acc signal-index-select [1 p])
      (f/add-edge! g obj  acc signal-index-select [2 o])
      (f/add-edge! g acc  res f/signal-forward nil)
      (register-query! g this)))
  (remove-from-graph!
    [_ g]
    (f/remove-vertex! g result)
    (f/remove-vertex! g acc)
    (assoc _ :acc nil :result nil)))

(defn add-query!
  [g id pattern]
  (f/add-to-graph!
   (map->BasicTripleQuery {:id id :pattern pattern}) g))

(defrecord ParametricTripleQuery
    [id sub-query result pattern]
  #?@(:clj
       [clojure.lang.IDeref (deref [_] @result)]
       :cljs
       [IDeref (-deref [_] @result)])
  ITripleQuery
  (raw-pattern
    [_] (mapv #(if-not (qvar? %) %) pattern))
  (result-vertex
    [_] result)
  f/IGraphComponent
  (add-to-graph!
    [_ g]
    (let [[s p o]    pattern
          vs?        (qvar? s), vp? (qvar? p), vo? (qvar? o)
          vmap       (bind-translator vs? vp? vo? s p o)
          verify     (triple-verifier s p o vs? vp? vo?)
          res-tx     (comp (map #(if (verify %) (vmap %))) (filter identity))
          collect-fn (f/collect-pure
                      (fn [_ incoming]
                        (if-let [res (seq (peek incoming))]
                          (into #{} res-tx res)
                          #{})))
          q-pattern  (raw-pattern _)
          sub-q      (or (query-for-pattern g q-pattern) (add-query! g id q-pattern))
          res        (f/add-vertex! g nil {::f/collect-fn collect-fn})]
      (f/add-edge! g (result-vertex sub-q) res f/signal-forward nil)
      (assoc _ :sub-query sub-q :result res)))
  (remove-from-graph!
    [_ g]
    (f/remove-vertex! g result)
    (when (sole-user? (result-vertex sub-query) result)
      (f/remove-from-graph! sub-query g))
    (assoc _ :sub-query nil :result nil)))

(defrecord TripleQueryJoin [id lhs rhs result]
  #?@(:clj
       [clojure.lang.IDeref (deref [_] @result)]
       :cljs
       [IDeref (-deref [_] @result)])
  ITripleQuery
  (raw-pattern
    [_] nil)
  (result-vertex
    [_] result)
  f/IGraphComponent
  (add-to-graph!
    [_ g]
    (let [lhs-id (:id (result-vertex lhs))
          rhs-id (:id (result-vertex rhs))
          join   (f/add-vertex!
                  g nil
                  {::f/collect-fn
                   (fn [vertex]
                     (let [sig-map (::f/signal-map @(:state vertex))
                           a (sig-map lhs-id)
                           b (sig-map rhs-id)]
                       (debug (:id vertex) :join-sets a b)
                       (reset! (:value vertex) (set/join a b))))
                   ::f/score-collect-fn
                   score-collect-join})]
      (f/add-edge! g (result-vertex lhs) join f/signal-forward nil)
      (f/add-edge! g (result-vertex rhs) join f/signal-forward nil)
      (assoc _ :result join)))
  (remove-from-graph!
    [_ g]
    (f/remove-vertex! g result)
    (when (sole-user? (result-vertex lhs) result)
      (f/remove-from-graph! lhs g))
    (when (sole-user? (result-vertex rhs) result)
      (f/remove-from-graph! rhs g))))

(defn random-id [] (keyword (gensym)))

(defn add-param-query!
  [g id pattern]
  (f/add-to-graph!
   (map->ParametricTripleQuery {:id id :pattern pattern}) g))

(defn add-join!
  ([g lhs rhs]
   (add-join! g ))
  ([g id lhs rhs]
   (f/add-to-graph!
    (map->TripleQueryJoin {:id id :lhs lhs :rhs rhs}) g)))

(defn add-query-join!
  [g a b & more]
  (reduce
   (fn [acc p]
     (add-join! g (random-id) acc (add-param-query! g (random-id) p)))
   (add-join!
    g (random-id)
    (add-param-query! g (random-id) a)
    (add-param-query! g (random-id) b))
   more))

(defn add-query-filter!
  [g q flt]
  (let [tx (comp (mapcat identity) (filter flt))
        fv (f/add-vertex!
            g nil
            {::f/collect-fn
             (f/collect-pure
              (fn [_ incoming] (sequence tx incoming)))})]
    (f/add-edge! g (result-vertex q) fv f/signal-forward nil)
    fv))

(defn collect-inference
  [g production]
  (fn [vertex]
    (let [prev @vertex
          in   (reduce into #{} (::f/uncollected @(:state vertex)))
          adds (set/difference in prev)
          inferred (mapcat production adds)]
      (debug :additions adds)
      (doseq [[op t :as inf] inferred]
        (case op
          :+ (do (debug :add-triple t)

        (fn [r] {o (r 2)})
        (fn [_] {})))))

(defn- triple-verifier
  [ts tp to vars varp varo]
  (cond
    (and vars varp varo) (cond
