 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Searches ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
+; Basic idea behind heterogenous search: 
+;   We can generate expansions for nodes in different ways.
+;   Always assume we have an HFS within a recursive ID.  
+
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Flat search ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-(defn hfs-simple-children [hfs] (map hfs->simple-node (hfs-children hfs)))
+(defn expand-hfs [hfs] (map hfs->simple-node (hfs-children hfs)))
 
 (defn make-fast-flat-search [root-hfs]
-  (is/make-flat-incremental-dijkstra (hfs->simple-node root-hfs) (comp hfs-simple-children :data)))
+  (is/make-flat-incremental-dijkstra (hfs->simple-node root-hfs) (comp expand-hfs :data)))
 
 (defn make-flat-search  [root-hfs]
-  (is/make-recursive-incremental-dijkstra (hfs->simple-node root-hfs) (comp hfs-simple-children :data)))
+  (is/make-recursive-incremental-dijkstra (hfs->simple-node root-hfs) (comp expand-hfs :data)))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Recursive search ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-;; TODO: recursive recursively is where polymorphism fits in.
-
-(declare make-recursive-search)
-
-
-(defn make-abstracted-subgoal-search [hfs]    
+
+(defn abstract-subsearch [hfs searchify-fn]    
   (is/WrappedSubSearch 
    (let [[name abstract-hfs-fn] (drop-hfs hfs)]
      (is/get-cached-search *problem-cache* name
        (let [dropped (abstract-hfs-fn)]
          (is/make-recursive-ef-search 
-          (hfs->simple-node dropped) (hfs-simple-children dropped)
-          #(make-abstracted-subgoal-search (:data %)))))) 
+          (hfs->simple-node dropped) (expand-hfs dropped)
+          #(searchify-fn (:data %)))))) 
    (:reward hfs) 
    #(is/offset-simple-summary % (:reward hfs))
    #(->> % :data (lift-hfs hfs) hfs->simple-node)))
 
-
-(defn make-recursive-search [root-hfs] 
-  (:search (make-abstracted-subgoal-search root-hfs)))
-
+(def recursive-subsearch (fn [hfs] (abstract-subsearch hfs recursive-subsearch)))
+
+(defn make-recursive-search [root-hfs] (:search (recursive-subsearch root-hfs)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Hybrid flat/rec search ;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defn make-acyclic-searchify [root-hfs]
+  (let [root-cycle-level (hfs-cycle-level root-hfs)]
+    #(if (and root-cycle-level (= (hfs-cycle-level %) root-cycle-level))
+        (expand-hfs %)
+      (abstract-subsearch % (make-acyclic-searchify %)))))
+
+(defn make-acyclic-recursive-search [root-hfs] 
+  (:search (abstract-subsearch root-hfs (make-acyclic-searchify root-hfs))))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Inverted ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 

(defn sahucs-simple [henv]
  (hierarchical-search henv nil make-recursive-search))

(defn sahucs-inverted [henv]
  (hierarchical-search henv nil make-inverted-search))


(defn hfs-can-reach? [hfs s]
  (or (seq (:remaining-actions hfs)) (= s (:state hfs))))

(defn hfs-cycle-level [hfs]
  (when-let [a (first (:remaining-actions hfs))] (hierarchy/cycle-level a (:state hfs))))

(defn hfs-optimistic-map [hfs]
  (let [{:keys [state remaining-actions]} hfs]
    (assert (util/singleton? remaining-actions))
