 (def #^HashMap *subproblem-cache* nil)
 
 ; Dependencies on plans
-(declare make-initial-plan plan-summary expand-plan)
+(declare make-initial-plan plan-summary plan-output-set expand-plan)
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Output Set Nodes ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 

  "Get or create child osn corresponding to this output-set."
  [osn child-output-set]
  (or (-> osn :child-map-atom deref child-output-set)
      (swap! (:child-map-atom osn) assoc child-output-set (make-child-own osn child-output-set))))

(defn add-plans! "Add plans, refresh summary." [osn new-plans]
  (swap! (:plans-atom osn) concat new-plans)
  (swap! (:plan-summary-atom osn) #(apply max-summary % (map plan-summary new-plans))))

(defn refine-osn-plan!
  "Refine a single plan stored at this osn."
  [osn min-reward]
  (let [[best-plan best-summary rest-plans rest-summary]
          (extract-best-and-summaries plan-summary @(:plans-atom osn))
        new-plans (group-by plan-output-set
                            (expand-plan best-plan (next-min-reward rest-summary min-reward)))]
    (reset! (:plans-atom osn) (concat (get new-plans (:output-set osn)) rest-plans))
    (refresh-plan-summary! osn)
    (doseq [[child-output-set more-plans] (dissoc new-plans (:output-set osn))]
      (add-plans! (get-osn-child! osn child-output-set) plans))))

(defn refine-osn-child!
  "Refine a single child osn."
  [osn min-reward excluded-child-set]
  (let [[best-child _ _ next-summary]
          (max-thing-summary-and-next tree-summary (vals @(:child-map-atom osn)))]
    (refine-osn! best-child (next-min-reward next-summary min-reward) nil)
    (refresh-descendant-summary! best-child)))

(defn refine-osn!
  "Repeatedly refine shallowest best plan covered by osn until solved or below min-reward."
  [osn min-reward excluded-child-set]
  (while (refinable-summary? (broom-summary osn excluded-child-set) min-reward)
    (let [[[best-op] _ _ next-summary]
          (max-thing-summary-and-next
           second
           [[#(do (refine-osn! (:parent-node osn) % nil) (refresh-ancestor-summary! osn))
               (ancestor-summary   osn)]
            [#(refine-osn-plan!  osn %)
               (plan-summary       osn)]
            [#(refine-osn-child! osn % excluded-child-set)
             (descendant-summary osn excluded-child-set)]])]
      (best-op (next-min-reward next-summary min-reward)))))



  (when-let [[[_ out-set out-summary] out-nodes]
             (map-state
              update-pn-input
              (cons set-changed? (plan-node-output-set-and-summary (last prefix-nodes)))
              remaining-nodes)]
    (Plan. input-constraint input-set (concat prefix-nodes out-nodes) out-set out-summary)))

;; Need some meta-level basis to choose what to do...
;; For now, just pick a node at random, try to refine it, then try to split on its output.
(defn expand-plan [plan min-reward]
  (if (not (refinable-summary? (:summary plan) min-reward)) [plan]
      (let [{:keys [input-constraint input-set plan-nodes output-set summary]} plan
            [pre-nodes [ref-node & post-nodes]] (split-at (rand-int (count plan-nodes)) plan-nodes)
;            _ (println summary (count pre-nodes) (class ref-node) (count post-nodes))       
            [pre-set pre-summary]  (if (seq pre-nodes) (plan-node-output-set-and-summary (last pre-nodes)) [input-set zero-summary])
            sub-min-reward (- min-reward
                              (- (max-reward summary)
                                 (- (max-reward (:output-summary ref-node)) (max-reward pre-summary))))]
        (filter identity
                (for [next-pn (split-pn-output (refine-pn ref-node pre-summary sub-min-reward) pre-set pre-summary sub-min-reward)]
                  (propagate-plan-changes input-constraint input-set (concat pre-nodes [next-pn]) post-nodes
                                          (not= (:output-set next-pn) (:output-set ref-node))))))))




