lib/rgl/graph_visitor.rb
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