yargi 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.md +54 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +23 -0
- data/LICENCE.md +22 -0
- data/Manifest.txt +15 -0
- data/README.md +128 -0
- data/Rakefile +23 -0
- data/examples/fs2dot.rb +1 -1
- data/examples/random.rb +20 -0
- data/lib/yargi.rb +8 -10
- data/lib/yargi/decorate.rb +55 -0
- data/lib/yargi/digraph.rb +71 -46
- data/lib/yargi/digraph_edge.rb +23 -23
- data/lib/yargi/digraph_vertex.rb +27 -27
- data/lib/yargi/edge_set.rb +12 -12
- data/lib/yargi/element_set.rb +54 -54
- data/lib/yargi/loader.rb +1 -0
- data/lib/yargi/markable.rb +12 -12
- data/lib/yargi/predicate.rb +62 -43
- data/lib/yargi/random.rb +98 -0
- data/lib/yargi/version.rb +14 -0
- data/lib/yargi/vertex_set.rb +12 -12
- data/spec/spec_helper.rb +2 -0
- data/spec/test_decorate.rb +28 -0
- data/spec/test_digraph.rb +42 -0
- data/spec/test_random.rb +45 -0
- data/spec/test_yargi.rb +8 -0
- data/tasks/debug_mail.rake +75 -0
- data/tasks/debug_mail.txt +13 -0
- data/tasks/gem.rake +68 -0
- data/tasks/spec_test.rake +71 -0
- data/tasks/unit_test.rake +76 -0
- data/tasks/yard.rake +51 -0
- data/test/test_all.rb +1 -1
- data/test/yargi/README-example.gif +0 -0
- data/test/yargi/digraph_set_features_test.rb +14 -16
- data/test/yargi/digraph_test.rb +33 -33
- data/test/yargi/digraph_vertex_test.rb +9 -9
- data/test/yargi/documentation_test.rb +7 -7
- data/test/yargi/edge_set_test.rb +3 -3
- data/test/yargi/element_set_test.rb +2 -2
- data/test/yargi/hypotheses_test.rb +4 -4
- data/test/yargi/markable_test.rb +11 -11
- data/test/yargi/predicate_test.rb +14 -14
- data/test/yargi/source-sink.gif +0 -0
- data/test/yargi/vertex_set_test.rb +7 -7
- data/yargi.gemspec +187 -0
- data/yargi.noespec +23 -0
- metadata +110 -38
- data/CONTRIBUTE +0 -11
- data/LICENCE +0 -25
- data/README +0 -79
- data/examples/fs2dot.dot +0 -78
- data/examples/fs2dot.gif +0 -0
    
        data/CHANGELOG.md
    ADDED
    
    | @@ -0,0 +1,54 @@ | |
| 1 | 
            +
            # 0.2.0 / 2012-02-17
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            * Enhancements
         | 
| 4 | 
            +
             | 
| 5 | 
            +
              * Digraph.new now yields self if a block is given. This allows building a 
         | 
| 6 | 
            +
                graph more smoothly:
         | 
| 7 | 
            +
                
         | 
| 8 | 
            +
                    graph = Digraph.new{|d|
         | 
| 9 | 
            +
                      d.add_n_vertices(5)
         | 
| 10 | 
            +
                      ...
         | 
| 11 | 
            +
                    }
         | 
| 12 | 
            +
                    
         | 
| 13 | 
            +
              * An integer is now automatically recognized as a selection predicate. This 
         | 
| 14 | 
            +
                means that the following will also work:
         | 
| 15 | 
            +
                
         | 
| 16 | 
            +
                    # connect 1-th and 2-th vertices
         | 
| 17 | 
            +
                    graph.connect(1, 2)
         | 
| 18 | 
            +
             | 
| 19 | 
            +
              * Added Digraph#ith_vertex and Digraph#ith_edge 
         | 
| 20 | 
            +
              * Added Digraph#vertex_count and Digraph#edge_count
         | 
| 21 | 
            +
              * Added Digraph.random to generate graph for tests and benchmarks
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            * Bug fixes
         | 
| 24 | 
            +
             | 
| 25 | 
            +
              * Misused of $STDERR has been replaced by $stderr
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            * Internals & Devel
         | 
| 28 | 
            +
             | 
| 29 | 
            +
              * The project structure is now handled by Noe.
         | 
| 30 | 
            +
             | 
| 31 | 
            +
            # 0.1.2
         | 
| 32 | 
            +
             | 
| 33 | 
            +
            * Enhancements
         | 
| 34 | 
            +
             | 
| 35 | 
            +
              * Markable.add_marks also accepts a block to mimic add_n_vertices and 
         | 
| 36 | 
            +
                ElementSet.add_marks
         | 
| 37 | 
            +
              * EdgeSet forwards source= and target= to its elements, allowing bulk 
         | 
| 38 | 
            +
                reconnection
         | 
| 39 | 
            +
              * Digraph.to_dot_attributes made much more smart on typical array values
         | 
| 40 | 
            +
             | 
| 41 | 
            +
            * Bug fixes
         | 
| 42 | 
            +
             | 
| 43 | 
            +
              * Markable.has_key? is used to avoid predicate side-effects on v[:mark] when 
         | 
| 44 | 
            +
                the mark is false
         | 
| 45 | 
            +
                
         | 
| 46 | 
            +
            # 0.1.1
         | 
| 47 | 
            +
             | 
| 48 | 
            +
              * Bug fix in dot generation
         | 
| 49 | 
            +
              * Examples started and documentation improved
         | 
| 50 | 
            +
              * Web site index generation using wlang
         | 
| 51 | 
            +
             | 
| 52 | 
            +
            # 0.1.0
         | 
| 53 | 
            +
             | 
| 54 | 
            +
              * Birthday!
         | 
    
        data/Gemfile
    ADDED
    
    
    
        data/Gemfile.lock
    ADDED
    
    | @@ -0,0 +1,23 @@ | |
| 1 | 
            +
            GEM
         | 
| 2 | 
            +
              remote: http://rubygems.org/
         | 
| 3 | 
            +
              specs:
         | 
| 4 | 
            +
                diff-lcs (1.1.3)
         | 
| 5 | 
            +
                rake (0.9.2.2)
         | 
| 6 | 
            +
                rspec (2.8.0)
         | 
| 7 | 
            +
                  rspec-core (~> 2.8.0)
         | 
| 8 | 
            +
                  rspec-expectations (~> 2.8.0)
         | 
| 9 | 
            +
                  rspec-mocks (~> 2.8.0)
         | 
| 10 | 
            +
                rspec-core (2.8.0)
         | 
| 11 | 
            +
                rspec-expectations (2.8.0)
         | 
| 12 | 
            +
                  diff-lcs (~> 1.1.2)
         | 
| 13 | 
            +
                rspec-mocks (2.8.0)
         | 
| 14 | 
            +
                wlang (0.10.2)
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            PLATFORMS
         | 
| 17 | 
            +
              java
         | 
| 18 | 
            +
              ruby
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            DEPENDENCIES
         | 
| 21 | 
            +
              rake (~> 0.9.2)
         | 
| 22 | 
            +
              rspec (~> 2.8.0)
         | 
| 23 | 
            +
              wlang (~> 0.10.2)
         | 
    
        data/LICENCE.md
    ADDED
    
    | @@ -0,0 +1,22 @@ | |
| 1 | 
            +
            # The MIT Licence
         | 
| 2 | 
            +
             | 
| 3 | 
            +
                Copyright (c) 2009-2011 - Bernard Lambeau
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                Permission is hereby granted, free of charge, to any person obtaining
         | 
| 6 | 
            +
                a copy of this software and associated documentation files (the
         | 
| 7 | 
            +
                "Software"), to deal in the Software without restriction, including
         | 
| 8 | 
            +
                without limitation the rights to use, copy, modify, merge, publish,
         | 
| 9 | 
            +
                distribute, sublicense, and/or sell copies of the Software, and to
         | 
| 10 | 
            +
                permit persons to whom the Software is furnished to do so, subject to
         | 
| 11 | 
            +
                the following conditions:
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                The above copyright notice and this permission notice shall be
         | 
| 14 | 
            +
                included in all copies or substantial portions of the Software.
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
         | 
| 17 | 
            +
                EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
         | 
| 18 | 
            +
                MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
         | 
| 19 | 
            +
                NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
         | 
| 20 | 
            +
                LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
         | 
| 21 | 
            +
                OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
         | 
| 22 | 
            +
                WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
         | 
    
        data/Manifest.txt
    ADDED
    
    
    
        data/README.md
    ADDED
    
    | @@ -0,0 +1,128 @@ | |
| 1 | 
            +
            # Yargi
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            Yet Another Ruby Graph Library 
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            ## Links
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            * http://rubydoc.info/github/blambeau/yargi/master/frames
         | 
| 8 | 
            +
            * http://github.com/blambeau/yargi
         | 
| 9 | 
            +
            * http://rubygems.org/gems/yargi
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            ## Description
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            Yargi provides a powerful **mutable** digraph implementation. It has been 
         | 
| 14 | 
            +
            designed to allow full mutability the easy way. A quick tour below.
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            ## Quick tour
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            ### Digraph, Vertex and Edge
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            Unlike {http://rubyforge.org/projects/rgl/ RGL}, Yargi implements graph 
         | 
| 21 | 
            +
            components through concrete classes, not modules (that is, in a 
         | 
| 22 | 
            +
            standard-but-closed way). See Digraph, Digraph::Vertex and Digraph::Edge 
         | 
| 23 | 
            +
            respectively.
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            ### Markable pattern
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            Graphs, vertices and edges are markable through a Hash-like API: users can 
         | 
| 28 | 
            +
            install their own key/value pairs on each graph element. When keys are Symbol
         | 
| 29 | 
            +
            objects, accessors are automatically generated to provide a friendly 
         | 
| 30 | 
            +
            object-oriented API (this feature must be used with care, for obvious reasons).
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                graph = Yargi::Digraph.new
         | 
| 33 | 
            +
                v1 = graph.add_vertex(:kind => "simple vertex")
         | 
| 34 | 
            +
                puts v1.kind    
         | 
| 35 | 
            +
                # => "simple vertex"
         | 
| 36 | 
            +
             | 
| 37 | 
            +
            ### Typed elements
         | 
| 38 | 
            +
             | 
| 39 | 
            +
            Graph elements (vertices and edges) can be tagged with your own modules (at 
         | 
| 40 | 
            +
            creation, or later). This is the standard Yargi way to apply a 'select-and-do' 
         | 
| 41 | 
            +
            feature described below:
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                # These are node types
         | 
| 44 | 
            +
                module Diamond; end
         | 
| 45 | 
            +
                module Circle; end
         | 
| 46 | 
            +
                
         | 
| 47 | 
            +
                # Let build a graph with 5 diamonds
         | 
| 48 | 
            +
                graph = Yargi::Digraph.new
         | 
| 49 | 
            +
                graph.add_n_vertices(5, Diamond) do |v,i|
         | 
| 50 | 
            +
                  v[:color] = (i%2==0 ? 'red' : 'blue')
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                # Let add 5 circles
         | 
| 54 | 
            +
                graph.add_n_vertices(5, Circle)
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                # connect all diamonds to all circles
         | 
| 57 | 
            +
                graph.connect(Diamond, Circle)  
         | 
| 58 | 
            +
             | 
| 59 | 
            +
            ### Selection mechanism 
         | 
| 60 | 
            +
             | 
| 61 | 
            +
            Yargi helps you finding the nodes and edges you are looking for through a 
         | 
| 62 | 
            +
            declarative selection mechanism: almost all methods that return a set of 
         | 
| 63 | 
            +
            vertices or edges (Vertex.out_edges, for example) accept a predicate argument 
         | 
| 64 | 
            +
            to filter the result, module names being most-used shortcuts. 
         | 
| 65 | 
            +
            See Yargi::Predicate for details.
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                # [... previous example continued ...]
         | 
| 68 | 
            +
                graph.vertices(Diamond)                       # selects all diamonds
         | 
| 69 | 
            +
                graph.vertices(Diamond){|v| v.color=='blue'}  # selects blue diamonds only
         | 
| 70 | 
            +
                
         | 
| 71 | 
            +
                # Proc variant, find sink states
         | 
| 72 | 
            +
                sink = Yargi.predicate {|v| v.out_edges.empty?}
         | 
| 73 | 
            +
                graph.vertices(sink & Circle)                 # select all Circle sink states (no one)
         | 
| 74 | 
            +
                
         | 
| 75 | 
            +
                # Or selection
         | 
| 76 | 
            +
                is_blue = Yargi.predicate {|v| Diamond===v and v.color=='blue'}
         | 
| 77 | 
            +
                graph.vertices(is_blue|Circle)                # select blue diamonds and circles
         | 
| 78 | 
            +
             | 
| 79 | 
            +
            ### VertexSet and EdgeSet 
         | 
| 80 | 
            +
             | 
| 81 | 
            +
            The selection mechanism always returns arrays ... being instances of 
         | 
| 82 | 
            +
            Yargi::VertexSet and Yargi::EdgeSet. These classes help you walking your graph 
         | 
| 83 | 
            +
            easily:
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                # [... previous example continued ...]
         | 
| 86 | 
            +
                circles = graph.vertices(Diamond).adjacent
         | 
| 87 | 
            +
                puts graph.vertices(Circle)==circles        
         | 
| 88 | 
            +
                # => true
         | 
| 89 | 
            +
                
         | 
| 90 | 
            +
            ### Select-and-do
         | 
| 91 | 
            +
             | 
| 92 | 
            +
            Many graph methods accept sets of vertices and edges as well as selection queries
         | 
| 93 | 
            +
            to work. Instead of connecting one source to one target at a time by passing the 
         | 
| 94 | 
            +
            vertices, describe what you want and Yardi does it. Add, connect, remove, mark,
         | 
| 95 | 
            +
            reconnect many vertices/edges with a single method call:
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                graph.vertices(Diamond).add_marks(:label => '', :shape => 'diamond')
         | 
| 98 | 
            +
                graph.vertices(Circle).add_marks(:label => '', :shape => 'circle')
         | 
| 99 | 
            +
                puts graph.to_dot
         | 
| 100 | 
            +
                graph.remove_vertices(Circle) # remove all circles
         | 
| 101 | 
            +
             | 
| 102 | 
            +
            ### Mutable graphs 
         | 
| 103 | 
            +
             | 
| 104 | 
            +
            Graphs here are mutable, mutable and mutable and this is the reason why Yargi
         | 
| 105 | 
            +
            exists. It comes from a project where manipulating graphs by reconnecting edges,
         | 
| 106 | 
            +
            removing vertices is the norm, not the exception.
         | 
| 107 | 
            +
             | 
| 108 | 
            +
            ### Complexity don't care
         | 
| 109 | 
            +
             | 
| 110 | 
            +
            The digraph implementation uses an incident list data structure. This graph
         | 
| 111 | 
            +
            library has not been designed with efficiency in mind so that complexities 
         | 
| 112 | 
            +
            are not documented nor guaranteed. That is not to say that improvements are
         | 
| 113 | 
            +
            not welcome, of course.
         | 
| 114 | 
            +
             | 
| 115 | 
            +
            ## Distribution & Credits
         | 
| 116 | 
            +
             | 
| 117 | 
            +
            _Yargi_ is freely available (under a MIT licence) as a 'yargi' gem on 
         | 
| 118 | 
            +
            {http://rubygems.org/gems/yargi rubygems.org}. Use 'gem install yargi' to 
         | 
| 119 | 
            +
            install it. The sources are on {http://github.com/blambeau/yargi github}, which
         | 
| 120 | 
            +
            is also the place where bugs and issues can be reported.
         | 
| 121 | 
            +
             | 
| 122 | 
            +
            This work is supported by the {http://www.uclouvain.be/en-ingi.html department 
         | 
| 123 | 
            +
            of computer science} of the {http://www.uclouvain.be/en-index.html University of 
         | 
| 124 | 
            +
            Louvain} (EPL/INGI, Universite Catholique de Louvain, UCL, Louvain-la-Neuve, 
         | 
| 125 | 
            +
            Belgium).
         | 
| 126 | 
            +
             | 
| 127 | 
            +
            This work was also partially supported by the Regional Government of Wallonia 
         | 
| 128 | 
            +
            (GISELE project, RW Conv. 616425).
         | 
    
        data/Rakefile
    ADDED
    
    | @@ -0,0 +1,23 @@ | |
| 1 | 
            +
            begin
         | 
| 2 | 
            +
              gem "bundler", "~> 1.0"
         | 
| 3 | 
            +
              require "bundler/setup"
         | 
| 4 | 
            +
            rescue LoadError => ex
         | 
| 5 | 
            +
              puts ex.message
         | 
| 6 | 
            +
              abort "Bundler failed to load, (did you run 'gem install bundler' ?)"
         | 
| 7 | 
            +
            end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            # Dynamically load the gem spec
         | 
| 10 | 
            +
            $gemspec_file = File.expand_path('../yargi.gemspec', __FILE__)
         | 
| 11 | 
            +
            $gemspec      = Kernel.eval(File.read($gemspec_file))
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            # We run tests by default
         | 
| 14 | 
            +
            task :default => :test
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            #
         | 
| 17 | 
            +
            # Install all tasks found in tasks folder
         | 
| 18 | 
            +
            #
         | 
| 19 | 
            +
            # See .rake files there for complete documentation.
         | 
| 20 | 
            +
            #
         | 
| 21 | 
            +
            Dir["tasks/*.rake"].each do |taskfile|
         | 
| 22 | 
            +
              load taskfile
         | 
| 23 | 
            +
            end
         | 
    
        data/examples/fs2dot.rb
    CHANGED
    
    | @@ -50,7 +50,7 @@ graph.vertices(FileVertex){|v| /\.rb (.*)$/ =~ v.label}.add_marks( | |
| 50 50 | 
             
            graph.vertices(FileVertex){|v| /\.rb (.*)$/ =~ v.label}.in_adjacent.add_marks do |v|
         | 
| 51 51 | 
             
              {:fillcolor => 'gold', :fontcolor => 'black'}
         | 
| 52 52 | 
             
            end
         | 
| 53 | 
            -
             | 
| 53 | 
            +
             | 
| 54 54 | 
             
            # Save it
         | 
| 55 55 | 
             
            File.open(File.join(File.dirname(__FILE__), 'fs2dot.dot'), 'w') do |f|
         | 
| 56 56 | 
             
              f << graph.to_dot
         | 
    
        data/examples/random.rb
    ADDED
    
    | @@ -0,0 +1,20 @@ | |
| 1 | 
            +
            $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
         | 
| 2 | 
            +
            require 'yargi'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            COLORS = ["blue", "red", "yellow", "green"]
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            # We build a random graph with approximately 20 vertices
         | 
| 7 | 
            +
            # and 60 edges (will be stripped by default). Each vertex
         | 
| 8 | 
            +
            # will have a color picked at random.
         | 
| 9 | 
            +
            graph = Yargi::Digraph.random(20,60) do |r|
         | 
| 10 | 
            +
              r.vertex_builder = lambda{|v,i|
         | 
| 11 | 
            +
                v[:color] = COLORS[Kernel.rand(COLORS.size)]
         | 
| 12 | 
            +
              }
         | 
| 13 | 
            +
            end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            # The label of each vertex will be its depth
         | 
| 16 | 
            +
            Yargi::Decorate::DEPTH.execute(graph)
         | 
| 17 | 
            +
            graph.vertices.add_marks{|v| {:label => v[:depth]}}
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            # Print as a dot graph
         | 
| 20 | 
            +
            puts graph.to_dot
         | 
    
        data/lib/yargi.rb
    CHANGED
    
    | @@ -1,25 +1,22 @@ | |
| 1 | 
            +
            require 'yargi/version'
         | 
| 2 | 
            +
            require 'yargi/loader'
         | 
| 1 3 | 
             
            require 'yargi/predicate'
         | 
| 2 | 
            -
             | 
| 3 4 | 
             
            module Yargi
         | 
| 4 | 
            -
             | 
| 5 | 
            -
              # Current Yargi version
         | 
| 6 | 
            -
              VERSION = "0.1.2".freeze
         | 
| 7 | 
            -
              
         | 
| 5 | 
            +
             | 
| 8 6 | 
             
              # When _what_ is not nil, converts it to a predicate (typically a module).
         | 
| 9 7 | 
             
              # Otherwise, a block is expected, which is converted to a LambdaPredicate.
         | 
| 10 8 | 
             
              # Otherwise, return ALL.
         | 
| 11 9 | 
             
              def self.predicate(what=nil, &block)
         | 
| 12 10 | 
             
                Predicate.to_predicate(what, &block)
         | 
| 13 11 | 
             
              end
         | 
| 14 | 
            -
             | 
| 12 | 
            +
             | 
| 15 13 | 
             
              # Predicates that always return true
         | 
| 16 14 | 
             
              ALL = Yargi::Predicate.to_predicate(true)
         | 
| 17 | 
            -
             | 
| 15 | 
            +
             | 
| 18 16 | 
             
              # Predicates that always return false
         | 
| 19 17 | 
             
              NONE = Yargi::Predicate.to_predicate(false)
         | 
| 20 | 
            -
              
         | 
| 21 | 
            -
            end
         | 
| 22 18 |  | 
| 19 | 
            +
            end
         | 
| 23 20 | 
             
            require 'yargi/markable'
         | 
| 24 21 | 
             
            require 'yargi/digraph'
         | 
| 25 22 | 
             
            require 'yargi/digraph_vertex'
         | 
| @@ -27,4 +24,5 @@ require 'yargi/digraph_edge' | |
| 27 24 | 
             
            require 'yargi/element_set'
         | 
| 28 25 | 
             
            require 'yargi/vertex_set'
         | 
| 29 26 | 
             
            require 'yargi/edge_set'
         | 
| 30 | 
            -
             | 
| 27 | 
            +
            require 'yargi/decorate'
         | 
| 28 | 
            +
            require 'yargi/random'
         | 
| @@ -0,0 +1,55 @@ | |
| 1 | 
            +
            module Yargi
         | 
| 2 | 
            +
              class Decorate
         | 
| 3 | 
            +
                attr_accessor :key
         | 
| 4 | 
            +
                attr_accessor :bottom
         | 
| 5 | 
            +
                attr_accessor :d0
         | 
| 6 | 
            +
                attr_accessor :suppremum
         | 
| 7 | 
            +
                attr_accessor :propagate
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                def initialize
         | 
| 10 | 
            +
                  yield(self) if block_given?
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                def execute(digraph, initials = digraph.vertices(0))
         | 
| 14 | 
            +
                  # all to bottom except initial states
         | 
| 15 | 
            +
                  digraph.each_vertex{|s| s[key] = bottom}
         | 
| 16 | 
            +
                  initials.each{|s| s[key] = d0}
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  # main loop
         | 
| 19 | 
            +
                  to_explore = initials
         | 
| 20 | 
            +
                  until to_explore.empty?
         | 
| 21 | 
            +
                    source = to_explore.pop
         | 
| 22 | 
            +
                    source.out_edges.each do |edge|
         | 
| 23 | 
            +
                      target = edge.target
         | 
| 24 | 
            +
                      p_decor = propagate.call(source[key], edge)
         | 
| 25 | 
            +
                      p_decor = suppremum.call(target[key], p_decor)
         | 
| 26 | 
            +
                      unless p_decor == target[key]
         | 
| 27 | 
            +
                        target[key] = p_decor
         | 
| 28 | 
            +
                        to_explore << target unless to_explore.include?(target)
         | 
| 29 | 
            +
                      end
         | 
| 30 | 
            +
                    end
         | 
| 31 | 
            +
                  end
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                DEPTH = Decorate.new{|d|
         | 
| 35 | 
            +
                  d.key       = :depth
         | 
| 36 | 
            +
                  d.bottom    = 1.0/0
         | 
| 37 | 
            +
                  d.d0        = 0
         | 
| 38 | 
            +
                  d.suppremum = lambda{|d1,d2| d1 < d2 ? d1 : d2}
         | 
| 39 | 
            +
                  d.propagate = lambda{|d,e| d+1}
         | 
| 40 | 
            +
                }
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                SHORTEST_PATH = Decorate.new{|d|
         | 
| 43 | 
            +
                  d.key       = :shortest_path
         | 
| 44 | 
            +
                  d.bottom    = nil
         | 
| 45 | 
            +
                  d.d0        = []
         | 
| 46 | 
            +
                  d.suppremum = lambda{|d1,d2|
         | 
| 47 | 
            +
                    d1.nil? ? d2 : (d2.nil? ? d1 : (d1.size < d2.size ? d1 : d2))
         | 
| 48 | 
            +
                  }
         | 
| 49 | 
            +
                  d.propagate = lambda{|d,e|
         | 
| 50 | 
            +
                    d.nil? ? [e] : (d + [e])
         | 
| 51 | 
            +
                  }
         | 
| 52 | 
            +
                }
         | 
| 53 | 
            +
             | 
| 54 | 
            +
              end # class Decorate
         | 
| 55 | 
            +
            end # module Yargi
         | 
    
        data/lib/yargi/digraph.rb
    CHANGED
    
    | @@ -6,12 +6,12 @@ module Yargi | |
| 6 6 | 
             
              #
         | 
| 7 7 | 
             
              # Directed graph implementation.
         | 
| 8 8 | 
             
              #
         | 
| 9 | 
            -
              # * All selection methods (vertices, each_vertex, edges, each_edge) implement the | 
| 10 | 
            -
              #   predicate-selection mechanism (they accept a predicate filter as well as a | 
| 9 | 
            +
              # * All selection methods (vertices, each_vertex, edges, each_edge) implement the
         | 
| 10 | 
            +
              #   predicate-selection mechanism (they accept a predicate filter as well as a
         | 
| 11 11 | 
             
              #   predicate block, with a AND semantics when used conjointly).
         | 
| 12 12 | 
             
              # * Removal methods (remove_vertex, remove_vertices, remove_edge, remove_edges)
         | 
| 13 | 
            -
              #   accept varying arguments that are automatically converted to set of vertices | 
| 14 | 
            -
              #   or edges. Recognized arguments are Vertex and Edge instances, VertexSet and | 
| 13 | 
            +
              #   accept varying arguments that are automatically converted to set of vertices
         | 
| 14 | 
            +
              #   or edges. Recognized arguments are Vertex and Edge instances, VertexSet and
         | 
| 15 15 | 
             
              #   EdgeSet instances, Array instances, and predicates. When multiple arguments
         | 
| 16 16 | 
             
              #   are used conjointly, a OR-semantics is applied.
         | 
| 17 17 | 
             
              #
         | 
| @@ -21,22 +21,33 @@ module Yargi | |
| 21 21 | 
             
              #
         | 
| 22 22 | 
             
              class Digraph
         | 
| 23 23 | 
             
                include Yargi::Markable
         | 
| 24 | 
            -
             | 
| 24 | 
            +
             | 
| 25 25 | 
             
                # Creates an empty graph instance
         | 
| 26 26 | 
             
                def initialize
         | 
| 27 27 | 
             
                  @vertices = VertexSet[]
         | 
| 28 28 | 
             
                  @edges = EdgeSet[]
         | 
| 29 29 | 
             
                  @marks = {}
         | 
| 30 | 
            +
                  yield(self) if block_given?
         | 
| 30 31 | 
             
                end
         | 
| 31 | 
            -
             | 
| 32 | 
            +
             | 
| 32 33 | 
             
                ### Vertex management ################################################
         | 
| 33 | 
            -
             | 
| 34 | 
            +
             | 
| 35 | 
            +
                # Returns number of graph vertices
         | 
| 36 | 
            +
                def vertex_count
         | 
| 37 | 
            +
                  @vertices.size
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                # Returns the i-th vertex
         | 
| 41 | 
            +
                def ith_vertex(i)
         | 
| 42 | 
            +
                  @vertices[i]
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
             | 
| 34 45 | 
             
                # Returns all graph vertices for which the 'filter and block' predicate
         | 
| 35 46 | 
             
                # evaluates to true (see Yargi::Predicate).
         | 
| 36 47 | 
             
                def vertices(filter=nil, &block)
         | 
| 37 48 | 
             
                  @vertices.filter(filter, &block)
         | 
| 38 49 | 
             
                end
         | 
| 39 | 
            -
             | 
| 50 | 
            +
             | 
| 40 51 | 
             
                # Calls block on each graph vertex for with the 'filter and block' predicate
         | 
| 41 52 | 
             
                # evaluates to true.
         | 
| 42 53 | 
             
                def each_vertex(filter=nil, &block)
         | 
| @@ -46,20 +57,20 @@ module Yargi | |
| 46 57 | 
             
                    vertices(filter).each &block
         | 
| 47 58 | 
             
                  end
         | 
| 48 59 | 
             
                end
         | 
| 49 | 
            -
             | 
| 50 | 
            -
                # Adds a vertex. _args_ can be module instances or hashes, | 
| 51 | 
            -
                # which are all installed on the vertex _v_ using  <tt>v.tag</tt> | 
| 52 | 
            -
                # and <tt>v.add_marks</tt>, respectively. | 
| 60 | 
            +
             | 
| 61 | 
            +
                # Adds a vertex. _args_ can be module instances or hashes,
         | 
| 62 | 
            +
                # which are all installed on the vertex _v_ using  <tt>v.tag</tt>
         | 
| 63 | 
            +
                # and <tt>v.add_marks</tt>, respectively.
         | 
| 53 64 | 
             
                def add_vertex(*args)
         | 
| 54 65 | 
             
                  vertex = Digraph::Vertex.new(self, @vertices.length)
         | 
| 55 66 | 
             
                  apply_arg_conventions(vertex, args)
         | 
| 56 67 | 
             
                  @vertices << vertex
         | 
| 57 68 | 
             
                  vertex
         | 
| 58 69 | 
             
                end
         | 
| 59 | 
            -
             | 
| 60 | 
            -
                # Creates n vertices. _args_ can be module instances or hashes, | 
| 61 | 
            -
                # which are all installed on vertices _v_ using  <tt>v.tag</tt> | 
| 62 | 
            -
                # and <tt>v.add_marks</tt>, respectively. If a block is given, | 
| 70 | 
            +
             | 
| 71 | 
            +
                # Creates n vertices. _args_ can be module instances or hashes,
         | 
| 72 | 
            +
                # which are all installed on vertices _v_ using  <tt>v.tag</tt>
         | 
| 73 | 
            +
                # and <tt>v.add_marks</tt>, respectively. If a block is given,
         | 
| 63 74 | 
             
                # it is called after each vertex creation. The vertex is passed
         | 
| 64 75 | 
             
                # as first argument and the iteration index (from 0 to n-1) as
         | 
| 65 76 | 
             
                # second one.
         | 
| @@ -72,8 +83,8 @@ module Yargi | |
| 72 83 | 
             
                  end
         | 
| 73 84 | 
             
                  VertexSet.new(vertices)
         | 
| 74 85 | 
             
                end
         | 
| 75 | 
            -
             | 
| 76 | 
            -
                # Removes all vertices returned by evaluating the _vertices_ selection | 
| 86 | 
            +
             | 
| 87 | 
            +
                # Removes all vertices returned by evaluating the _vertices_ selection
         | 
| 77 88 | 
             
                # expression.
         | 
| 78 89 | 
             
                def remove_vertices(*vertices)
         | 
| 79 90 | 
             
                  vertices = to_vertices(*vertices).sort{|v1,v2| v2<=>v1}
         | 
| @@ -85,16 +96,26 @@ module Yargi | |
| 85 96 | 
             
                  @vertices.each_with_index {|v,i| v.index=i}
         | 
| 86 97 | 
             
                  self
         | 
| 87 98 | 
             
                end
         | 
| 88 | 
            -
                alias :remove_vertex :remove_vertices | 
| 89 | 
            -
             | 
| 99 | 
            +
                alias :remove_vertex :remove_vertices
         | 
| 100 | 
            +
             | 
| 90 101 | 
             
                ### Edge management ##################################################
         | 
| 91 | 
            -
             | 
| 102 | 
            +
             | 
| 103 | 
            +
                # Returns number of graph edges
         | 
| 104 | 
            +
                def edge_count
         | 
| 105 | 
            +
                  @edges.size
         | 
| 106 | 
            +
                end
         | 
| 107 | 
            +
             | 
| 108 | 
            +
                # Returns the i-th edge
         | 
| 109 | 
            +
                def ith_edge(i)
         | 
| 110 | 
            +
                  @edges[i]
         | 
| 111 | 
            +
                end
         | 
| 112 | 
            +
             | 
| 92 113 | 
             
                # Returns all graph edges for which the 'filter and block' predicate
         | 
| 93 114 | 
             
                # evaluates to true (see Yargi::Predicate).
         | 
| 94 115 | 
             
                def edges(filter=nil, &block)
         | 
| 95 116 | 
             
                  @edges.filter(filter, &block)
         | 
| 96 117 | 
             
                end
         | 
| 97 | 
            -
             | 
| 118 | 
            +
             | 
| 98 119 | 
             
                # Calls block on each graph edge for with the 'filter and block' predicate
         | 
| 99 120 | 
             
                # evaluates to true.
         | 
| 100 121 | 
             
                def each_edge(filter=nil, &block)
         | 
| @@ -104,10 +125,10 @@ module Yargi | |
| 104 125 | 
             
                    edges(filter).each &block
         | 
| 105 126 | 
             
                  end
         | 
| 106 127 | 
             
                end
         | 
| 107 | 
            -
             | 
| 108 | 
            -
                # Connects source to target state(s). _source_ and _target_ may be any | 
| 109 | 
            -
                # selection expression that can lead to vertex sets. _args_ can be module | 
| 110 | 
            -
                # instances or hashes,  which are all installed on edges _e_ using | 
| 128 | 
            +
             | 
| 129 | 
            +
                # Connects source to target state(s). _source_ and _target_ may be any
         | 
| 130 | 
            +
                # selection expression that can lead to vertex sets. _args_ can be module
         | 
| 131 | 
            +
                # instances or hashes,  which are all installed on edges _e_ using
         | 
| 111 132 | 
             
                # <tt>e.tag</tt> and <tt>e.add_marks</tt>, respectively.
         | 
| 112 133 | 
             
                def add_edge(source, target, *args)
         | 
| 113 134 | 
             
                  if Vertex===source and Vertex===target
         | 
| @@ -129,7 +150,7 @@ module Yargi | |
| 129 150 | 
             
                  end
         | 
| 130 151 | 
             
                end
         | 
| 131 152 | 
             
                alias :connect :add_edge
         | 
| 132 | 
            -
             | 
| 153 | 
            +
             | 
| 133 154 | 
             
                # Adds many edges at once
         | 
| 134 155 | 
             
                def add_edges(*extremities)
         | 
| 135 156 | 
             
                  extremities.collect do |extr|
         | 
| @@ -137,8 +158,8 @@ module Yargi | |
| 137 158 | 
             
                  end
         | 
| 138 159 | 
             
                end
         | 
| 139 160 | 
             
                alias :connect_all :add_edges
         | 
| 140 | 
            -
             | 
| 141 | 
            -
                # Removes all edges returned by evaluating the _edges_ selection | 
| 161 | 
            +
             | 
| 162 | 
            +
                # Removes all edges returned by evaluating the _edges_ selection
         | 
| 142 163 | 
             
                # expression.
         | 
| 143 164 | 
             
                def remove_edges(*edges)
         | 
| 144 165 | 
             
                  edges = to_edges(edges).sort{|e1,e2| e2<=>e1}
         | 
| @@ -152,9 +173,9 @@ module Yargi | |
| 152 173 | 
             
                  self
         | 
| 153 174 | 
             
                end
         | 
| 154 175 | 
             
                alias :remove_edge :remove_edges
         | 
| 155 | 
            -
             | 
| 176 | 
            +
             | 
| 156 177 | 
             
                # Reconnects some edge(s). _source_ and _target_ are expected to be
         | 
| 157 | 
            -
                # Vertex instances, or nil. _edges_ may be any selection expression | 
| 178 | 
            +
                # Vertex instances, or nil. _edges_ may be any selection expression
         | 
| 158 179 | 
             
                # that can be converted to an edge set. This method reconnects all
         | 
| 159 180 | 
             
                # edges to the specified source and target vertices (at least one is
         | 
| 160 181 | 
             
                # expected not to be nil).
         | 
| @@ -165,7 +186,7 @@ module Yargi | |
| 165 186 | 
             
                    if source
         | 
| 166 187 | 
             
                      edge.source.remove_out_edge(edge)
         | 
| 167 188 | 
             
                      source.add_out_edge(edge)
         | 
| 168 | 
            -
                    end | 
| 189 | 
            +
                    end
         | 
| 169 190 | 
             
                    if target
         | 
| 170 191 | 
             
                      edge.target.remove_in_edge(edge)
         | 
| 171 192 | 
             
                      target.add_in_edge(edge)
         | 
| @@ -176,7 +197,7 @@ module Yargi | |
| 176 197 | 
             
                end
         | 
| 177 198 |  | 
| 178 199 | 
             
                ### Standard exports #################################################
         | 
| 179 | 
            -
             | 
| 200 | 
            +
             | 
| 180 201 | 
             
                # Encodes this graph for dot graphviz
         | 
| 181 202 | 
             
                def to_dot(buffer='')
         | 
| 182 203 | 
             
                  buffer << "digraph G {\n"
         | 
| @@ -189,10 +210,10 @@ module Yargi | |
| 189 210 | 
             
                  end
         | 
| 190 211 | 
             
                  buffer << "}\n"
         | 
| 191 212 | 
             
                end
         | 
| 192 | 
            -
             | 
| 213 | 
            +
             | 
| 193 214 | 
             
                ### Argument conventions #############################################
         | 
| 194 215 | 
             
                protected
         | 
| 195 | 
            -
             | 
| 216 | 
            +
             | 
| 196 217 | 
             
                # Converts a hash to dot attributes
         | 
| 197 218 | 
             
                def to_dot_attributes(hash)
         | 
| 198 219 | 
             
                  # TODO: fix uncompatible key names
         | 
| @@ -214,36 +235,38 @@ module Yargi | |
| 214 235 | 
             
                  end
         | 
| 215 236 | 
             
                  buffer
         | 
| 216 237 | 
             
                end
         | 
| 217 | 
            -
             | 
| 238 | 
            +
             | 
| 218 239 | 
             
                # Checks if _arg_ looks like an element set
         | 
| 219 240 | 
             
                def looks_a_set?(arg)
         | 
| 220 241 | 
             
                  Array===arg or ElementSet===arg
         | 
| 221 242 | 
             
                end
         | 
| 222 | 
            -
             | 
| 243 | 
            +
             | 
| 223 244 | 
             
                # Checks graph sanity
         | 
| 224 245 | 
             
                def check_sanity
         | 
| 225 | 
            -
                  @vertices.each_with_index do |v,i| | 
| 246 | 
            +
                  @vertices.each_with_index do |v,i|
         | 
| 226 247 | 
             
                    raise "Removed vertex in vertex list" unless v.index==i
         | 
| 227 248 | 
             
                    v.in_edges.each do |ine|
         | 
| 228 249 | 
             
                      raise "Removed edge in vertex incoming edges" if ine.index<0
         | 
| 229 | 
            -
                      raise "Vertex and edge don't agree on target" unless ine.target==v | 
| 250 | 
            +
                      raise "Vertex and edge don't agree on target" unless ine.target==v
         | 
| 230 251 | 
             
                    end
         | 
| 231 252 | 
             
                    v.out_edges.each do |oute|
         | 
| 232 253 | 
             
                      raise "Removed edge in vertex outgoing edges" if oute.index<0
         | 
| 233 | 
            -
                      raise "Vertex and edge don't agree on source" unless oute.source==v | 
| 254 | 
            +
                      raise "Vertex and edge don't agree on source" unless oute.source==v
         | 
| 234 255 | 
             
                    end
         | 
| 235 256 | 
             
                  end
         | 
| 236 | 
            -
                  @edges.each_with_index do |e,i| | 
| 257 | 
            +
                  @edges.each_with_index do |e,i|
         | 
| 237 258 | 
             
                    raise "Removed edge in edge list" unless e.index==i
         | 
| 238 259 | 
             
                    raise "Edge in-connected to a removed vertex" if e.source.index<0
         | 
| 239 260 | 
             
                    raise "Edge out-connected to a removed vertex" if e.target.index<0
         | 
| 240 261 | 
             
                  end
         | 
| 241 262 | 
             
                end
         | 
| 242 | 
            -
             | 
| 263 | 
            +
             | 
| 243 264 | 
             
                # Applies argument conventions about selection of vertices
         | 
| 244 265 | 
             
                def to_vertices(*args)
         | 
| 245 266 | 
             
                  selected = args.collect do |arg|
         | 
| 246 267 | 
             
                    case arg
         | 
| 268 | 
            +
                      when Integer
         | 
| 269 | 
            +
                        [@vertices[arg]]
         | 
| 247 270 | 
             
                      when VertexSet
         | 
| 248 271 | 
             
                        arg
         | 
| 249 272 | 
             
                      when Array
         | 
| @@ -257,11 +280,13 @@ module Yargi | |
| 257 280 | 
             
                  end.flatten.uniq
         | 
| 258 281 | 
             
                  VertexSet.new(selected)
         | 
| 259 282 | 
             
                end
         | 
| 260 | 
            -
             | 
| 283 | 
            +
             | 
| 261 284 | 
             
                # Applies argument conventions about selection of edges
         | 
| 262 285 | 
             
                def to_edges(*args)
         | 
| 263 286 | 
             
                  selected = args.collect do |arg|
         | 
| 264 287 | 
             
                    case arg
         | 
| 288 | 
            +
                      when Integer
         | 
| 289 | 
            +
                        [@edges[arg]]
         | 
| 265 290 | 
             
                      when EdgeSet
         | 
| 266 291 | 
             
                        arg
         | 
| 267 292 | 
             
                      when Array
         | 
| @@ -275,7 +300,7 @@ module Yargi | |
| 275 300 | 
             
                  end.flatten.uniq
         | 
| 276 301 | 
             
                  EdgeSet.new(selected)
         | 
| 277 302 | 
             
                end
         | 
| 278 | 
            -
             | 
| 303 | 
            +
             | 
| 279 304 | 
             
                # Applies argument conventions on _element_
         | 
| 280 305 | 
             
                def apply_arg_conventions(element, args)
         | 
| 281 306 | 
             
                  args.each do |arg|
         | 
| @@ -290,7 +315,7 @@ module Yargi | |
| 290 315 | 
             
                  end
         | 
| 291 316 | 
             
                  element
         | 
| 292 317 | 
             
                end
         | 
| 293 | 
            -
             | 
| 318 | 
            +
             | 
| 294 319 | 
             
              end # class Digraph
         | 
| 295 | 
            -
             | 
| 320 | 
            +
             | 
| 296 321 | 
             
            end
         |