require 'rgl/graph_wrapper' module RGL # Module GraphVisitor defines the # {https://www.boost.org/libs/graph/doc/visitor_concepts.html BGL Visitor Concepts}. # # Visitors provide a mechanism for extending an algorithm (i.e., for # customizing what is done at each step of the algorithm). They allow users # to insert their own operations at various steps within a graph algorithm. # # Graph algorithms typically have multiple event points where one may want to # insert a call-back. Therefore, visitors have several methods that # correspond to the various event points. Each algorithm has a different # set of event points. The following are common to both DFS and BFS search: # # * examine_vertex # * examine_edge # * tree_edge # * back_edge # * forward_edge # * finish_vertex # # These methods are all named +handle_*+ and can be set to appropriate blocks, # using the methods +set_*_event_handler+, which are defined for each event # mentioned above. # # As an alternative, you can also override the +handle_*+ methods in a # subclass, to configure the algorithm (as an example, see TarjanSccVisitor). # # During a graph traversal, vertices are *colored* using the colors :GRAY # (when waiting) and :BLACK when finished. All other vertices are :WHITE. # The +color_map+ is also maintained in the visitor. # module GraphVisitor include GraphWrapper # @return [Hash] a map which store the colors for each vertex attr_reader :color_map # Create a new GraphVisitor on _graph_. # @param [Graph] graph def initialize(graph) super(graph) reset end # Mark each vertex unvisited (i.e. :WHITE) # def reset @color_map = Hash.new(:WHITE) end # Returns true if vertex _v_ is colored :BLACK (i.e. finished). # def finished_vertex?(v) @color_map[v] == :BLACK end # Shall we follow the edge (u,v); i.e. v has color :WHITE # def follow_edge?(u, v) @color_map[v] == :WHITE end # Attach a map to the visitor which records the distance of a visited # vertex to the start vertex. # # This is similar to BGLs # {https://www.boost.org/libs/graph/doc/distance_recorder.html distance_recorder}. # # After the +distance_map+ is attached, the visitor has a new method # +distance_to_root+, which answers the distance to the start vertex. # def attach_distance_map(map = Hash.new(0)) @distance_map = map # add distance map support to the current visitor instance extend(DistanceMapSupport) end module DistanceMapSupport def handle_tree_edge(u, v) super @distance_map[v] = @distance_map[u] + 1 end # Answer the distance to the start vertex. def distance_to_root(v) @distance_map[v] end end # module DistanceMapSupport module ClassMethods # Defines an event handler. # def def_event_handlers(*events) events.each do |event| params = event.to_s.include?('edge') ? 'u, v' : 'u' handler = "@#{event}_event_handler" class_eval <<-END def handle_#{event}(#{params}) #{handler}.call(#{params}) if defined? #{handler} end def set_#{event}_event_handler(&block) #{handler} = block end END end end alias def_event_handler def_event_handlers end # module ClassMethods extend ClassMethods # add class methods to GraphVisitor class itself def self.included(base) # when GraphVisitor is included into a class/module, add class methods as well base.extend ClassMethods end def_event_handlers :examine_vertex, :examine_edge, :tree_edge, :back_edge, :forward_edge, :finish_vertex end # module GraphVisitor end # module RGL