dijkstra_fast 1.4.2 → 1.5.2

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.
@@ -24,8 +24,10 @@ module DijkstraFast
24
24
  ##
25
25
  class Graph
26
26
 
27
+ include DijkstraFast::ShortestPath
28
+
27
29
  def initialize
28
- @nodes = {}
30
+ @edges = {}
29
31
  end
30
32
 
31
33
  # Adds a weighted edge to the graph. This represents a possible path from the
@@ -36,27 +38,15 @@ module DijkstraFast
36
38
  # If not provided, a default distance of `1` is used.
37
39
  # @return [nil]
38
40
  def add(source, dest, distance: 1)
39
- _add_edge(node(source), node(dest), distance) unless source == dest
40
- end
41
+ return if source == dest
41
42
 
42
- # Finds the shortest path between items, returning both the path as well as
43
- # the total distance travelled.
44
- # @param source [Object] Any Ruby object that represents the source item
45
- # @param dest [Object] Any Ruby object that represents the destination item
46
- # @return [BestPath]
47
- def shortest_path(source, dest)
48
- best_path = BestPath.new
49
- best_path.distance = _shortest_path(node(source), node(dest), best_path.path)
50
- if best_path.path.empty? || best_path.distance.nil? || best_path.distance < 0
51
- raise NoPathExistsError
52
- end
53
- best_path
43
+ @edges[source] ||= []
44
+ @edges[source] << [dest, distance]
54
45
  end
55
46
 
56
- private
57
-
58
- def node(obj)
59
- @nodes[obj] ||= @nodes.size # Auto-increment id
47
+ def connections(source, &block)
48
+ @edges[source]&.each(&block)
49
+ nil
60
50
  end
61
51
 
62
52
  end
@@ -0,0 +1,68 @@
1
+ module DijkstraFast
2
+ ##
3
+ # Do not use this class. For internal purposes only.
4
+ ##
5
+ class Native
6
+
7
+ private
8
+
9
+ def initialize(delegate)
10
+ @delegate = delegate
11
+ @ids = {}
12
+ @nodes = {}
13
+ end
14
+
15
+ def connections(source_id)
16
+ @delegate.connections(node(source_id)) do |dest, distance = 1|
17
+ yield node_id(dest), distance
18
+ end
19
+ end
20
+
21
+ def shortest_path(source, dest, progress: false)
22
+ if !progress
23
+ progress = nil
24
+ elsif !progress.is_a?(Proc)
25
+ progress_bar = create_progress_bar(progress)
26
+ progress = lambda do |completed, total|
27
+ progress_bar.total = total
28
+ progress_bar.progress = completed
29
+ end
30
+ end
31
+
32
+ distance, path = _shortest_path(node_id(source), node_id(dest), progress)
33
+ [distance, @ids.invert.values_at(*path)]
34
+ ensure
35
+ progress_bar&.clear
36
+ end
37
+
38
+ def node_id(node)
39
+ id = @ids[node]
40
+ if id.nil?
41
+ id = @ids[node] = @ids.size + 1 # Auto-incrementing id (starting at 1 to leave 0 = nil)
42
+ @nodes[id] = node
43
+ end
44
+ id
45
+ end
46
+
47
+ def node(id)
48
+ @nodes.fetch(id)
49
+ end
50
+
51
+ def create_progress_bar(config)
52
+ require 'progressbar' unless defined?(ProgressBar)
53
+ return ProgressBar.create(config) if config.is_a?(Hash)
54
+
55
+ format = config.is_a?(String) ? config : "%a %b\u{15E7}%i %p%% %t"
56
+ ProgressBar.create(
57
+ format: format,
58
+ progress_mark: ' ',
59
+ remainder_mark: "\u{FF65}",
60
+ total: 100,
61
+ autofinish: false,
62
+ )
63
+ rescue LoadError
64
+ raise LoadError, 'The default implementation of progress reporting requires the progressbar gem'
65
+ end
66
+
67
+ end
68
+ end
@@ -0,0 +1,96 @@
1
+ module DijkstraFast
2
+ ##
3
+ # Interface to allow arbitrary classes to calculate the shortest path between
4
+ # two "items" using a native implementation of Dijkstra's algorithm.
5
+ #
6
+ # @example
7
+ #
8
+ # class GraphOne
9
+ # include DijkstraFast::ShortestPath
10
+ #
11
+ # def connections(source)
12
+ # source.find_next do |dest, distance|
13
+ # yield dest, distance
14
+ # end
15
+ # end
16
+ # end
17
+ #
18
+ # distance, path = GraphOne.new.shortest_path(source, dest)
19
+ #
20
+ # @example
21
+ #
22
+ # class GraphTwo
23
+ # include DijkstraFast::ShortestPath
24
+ #
25
+ # def initialize
26
+ # # All edges have distance 1
27
+ # @edges = { 1 => [2, 3], 2 => [3] }
28
+ # end
29
+ #
30
+ # def connections(source, &block)
31
+ # @edges.fetch(source, []).each(&block)
32
+ # end
33
+ # end
34
+ #
35
+ # distance = GraphTwo.new.shortest_distance(source, dest)
36
+ #
37
+ # @example
38
+ #
39
+ # class GraphThree
40
+ # include DijkstraFast::ShortestPath
41
+ #
42
+ # def connections(source)
43
+ # case source
44
+ # when 'A'
45
+ # yield 'B', 3
46
+ # yield 'C' # Default distance 1
47
+ # when 'B'
48
+ # yield 'C', 10
49
+ # end
50
+ # end
51
+ # end
52
+ #
53
+ # distance, path = GraphThree.new.shortest_path(source, dest)
54
+ #
55
+ # @see README
56
+ # @see https://en.wikipedia.org/wiki/Dijkstra's_algorithm
57
+ ##
58
+ module ShortestPath
59
+
60
+ # Returns the edges originating at source
61
+ # @param source [Object] Any Ruby object that represents the source item
62
+ # @yield [Object, int] A connection to destination object with given distance
63
+ # @return [nil]
64
+ def connections(source)
65
+ # Does nothing by default but should be implemented by class
66
+ end
67
+
68
+ # Finds the shortest path between items, returning both the path as well as
69
+ # the total distance travelled.
70
+ # @param source [Object] Any Ruby object that represents the source item
71
+ # @param dest [Object] Any Ruby object that represents the destination item
72
+ # @param progress [Boolean|String|Proc] If falsey (default), no progress will
73
+ # be displayed. If a Proc is given, it will be yielded progress
74
+ # (completed / total) as progress is made. If otherwise truthy the
75
+ # ruby-progressbar gem will be required and displayed; if passed a
76
+ # String it will be used as the progress bar's format.
77
+ # @return [Array<int, Array>]
78
+ def shortest_path(source, dest, progress: false)
79
+ Native.new(self).send(:shortest_path, source, dest, progress: progress)
80
+ end
81
+
82
+ # Finds the shortest distance between items
83
+ # @param source [Object] Any Ruby object that represents the source item
84
+ # @param dest [Object] Any Ruby object that represents the destination item
85
+ # @param progress [Boolean|String|Proc] If falsey (default), no progress will
86
+ # be displayed. If a Proc is given, it will be yielded progress
87
+ # (completed / total) as progress is made. If otherwise truthy the
88
+ # ruby-progressbar gem will be required and displayed; if passed a
89
+ # String it will be used as the progress bar's format.
90
+ # @return [int]
91
+ def shortest_distance(source, dest, progress: false)
92
+ shortest_path(source, dest, progress: progress).first
93
+ end
94
+
95
+ end
96
+ end
@@ -1,6 +1,6 @@
1
1
  module DijkstraFast
2
2
 
3
3
  # Current gem version
4
- VERSION = '1.4.2'
4
+ VERSION = '1.5.2'
5
5
 
6
6
  end
data/lib/dijkstra_fast.rb CHANGED
@@ -1,22 +1,92 @@
1
1
  ##
2
- # Provides native implementation of Dijkstra's algorithm for finding the shortest
3
- # path between two vertices in a large, sparse graph.
2
+ # When graph nodes "know" which nodes they are connected to, one of the two
3
+ # class methods on this module can be be used directly to calculate the shortest
4
+ # distance between nodes. Node objects must implement a method (by default
5
+ # <code>connections</code>) which yields all connected nodes and (optionally) the
6
+ # distance to that connected node. If the yield only supplies the connected
7
+ # node, a defualt distance of <code>1</code> is used.
4
8
  #
5
- # Underlying algorithm is implemented in C using a priority queue. Edges are
6
- # represented using linked lists rather than an adjacency matrix to reduce memory
7
- # footprint when operating on very large graphs where the average number of edges
8
- # between nodes is relatively small (e.g. < 1/10 the number of nodes). See
9
+ # It is important to ensure that <code>hash</code> and <code>eql?</code> are properly implemented so
10
+ # that two objects that are logically the same are treatest thusly.
9
11
  #
10
- # @see README
11
- # @see https://en.wikipedia.org/wiki/Dijkstra's_algorithm
12
+ # @example
13
+ #
14
+ # class SomethingWithConnections
15
+ # def find_next
16
+ # yield SomethingWithConnections.new('C'), 2
17
+ # yield SomethingWithConnections.new('D'), 4
18
+ # end
19
+ #
20
+ # def hash
21
+ # # implement me
22
+ # end
23
+ #
24
+ # def eql?(other)
25
+ # # implement me
26
+ # end
27
+ # end
28
+ #
29
+ # a = SomethingWithConnections.new('A')
30
+ # b = SomethingWithConnections.new('B')
31
+ # distance, path = DijkstraFast.shortest_path(a, b, connections: :find_next)
32
+ #
33
+ # @example
34
+ #
35
+ # class SomethingElseWithConnections
36
+ # def connections
37
+ # yield # implement me
38
+ # yield # implement me
39
+ # end
40
+ # end
41
+ #
42
+ # a = SomethingElseWithConnections.new
43
+ # b = SomethingElseWithConnections.new
44
+ # distance = DijkstraFast.shortest_distance(a, b)
12
45
  ##
13
46
  module DijkstraFast
14
47
 
15
- autoload :BestPath, 'dijkstra_fast/best_path'
16
48
  autoload :Graph, 'dijkstra_fast/graph'
49
+ autoload :Native, 'dijkstra_fast/native'
17
50
  autoload :NoPathExistsError, 'dijkstra_fast/no_path_exists_error'
51
+ autoload :PriorityQueue, 'dijkstra_fast/priority_queue'
52
+ autoload :ShortestPath, 'dijkstra_fast/shortest_path'
18
53
  autoload :VERSION, 'dijkstra_fast/version'
19
54
 
55
+ # Finds the shortest path between items, returning both the path as well as
56
+ # the total distance travelled.
57
+ # @param source [Object] Any Ruby object that represents the source item
58
+ # @param dest [Object] Any Ruby object that represents the destination item
59
+ # @param progress [Boolean|String|Proc] If falsey (default), no progress will
60
+ # be displayed. If a Proc is given, it will be yielded progress
61
+ # (completed / total) as progress is made. If otherwise truthy the
62
+ # ruby-progressbar gem will be required and displayed; if passed a
63
+ # String it will be used as the progress bar's format.
64
+ # @param connections [Symbol|String] The method to call on each item to
65
+ # obtain adjacent items in the graph. Defaults to 'connections'
66
+ # @return [Array<int, Array>]
67
+ def self.shortest_path(source, dest, connections: 'connections', progress: false)
68
+ clazz = Class.new { include DijkstraFast::ShortestPath }
69
+ clazz.define_method(:connections) do |source, &block|
70
+ source.send(connections, &block)
71
+ end
72
+ clazz.new.shortest_path(source, dest, progress: progress)
73
+ end
74
+
75
+ # Finds the shortest distance between items
76
+ # @param source [Object] Any Ruby object that represents the source item
77
+ # @param dest [Object] Any Ruby object that represents the destination item
78
+ # @param progress [Boolean|String|Proc] If falsey (default), no progress will
79
+ # be displayed. If a Proc is given, it will be yielded progress
80
+ # (completed / total) as progress is made. If otherwise truthy the
81
+ # ruby-progressbar gem will be required and displayed; if passed a
82
+ # String it will be used as the progress bar's format.
83
+ # @param connections [Symbol|String] The method to call on each item to
84
+ # obtain adjacent items in the graph.
85
+ # @return [int]
86
+ def self.shortest_distance(source, dest, connections: 'connections', progress: false)
87
+ shortest_path(source, dest, connections: connections, progress: progress).first
88
+ end
89
+
20
90
  end
21
91
 
22
92
  require 'dijkstra_fast/dijkstra_fast'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dijkstra_fast
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.2
4
+ version: 1.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - David McCullars
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-12-23 00:00:00.000000000 Z
11
+ date: 2022-01-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: github-markup
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: rake
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -52,6 +66,20 @@ dependencies:
52
66
  - - ">="
53
67
  - !ruby/object:Gem::Version
54
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: redcarpet
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
55
83
  - !ruby/object:Gem::Dependency
56
84
  name: rspec
57
85
  requirement: !ruby/object:Gem::Requirement
@@ -157,6 +185,7 @@ files:
157
185
  - ".rubocop.yml"
158
186
  - ".ruby-version"
159
187
  - ".travis.yml"
188
+ - ".yardopts"
160
189
  - CODE_OF_CONDUCT.md
161
190
  - Gemfile
162
191
  - LICENSE
@@ -164,20 +193,26 @@ files:
164
193
  - Rakefile
165
194
  - dijkstra_fast.gemspec
166
195
  - ext/dijkstra_fast/dijkstra_fast.c
167
- - ext/dijkstra_fast/dijkstra_graph.c
168
- - ext/dijkstra_fast/dijkstra_graph.h
196
+ - ext/dijkstra_fast/errors.c
197
+ - ext/dijkstra_fast/errors.h
198
+ - ext/dijkstra_fast/expand_capacity.h
169
199
  - ext/dijkstra_fast/extconf.rb
170
- - ext/dijkstra_fast/prioritized_item_list.c
171
- - ext/dijkstra_fast/prioritized_item_list.h
200
+ - ext/dijkstra_fast/native.c
201
+ - ext/dijkstra_fast/native.h
202
+ - ext/dijkstra_fast/native_shortest_path.c
203
+ - ext/dijkstra_fast/priority_queue.c
204
+ - ext/dijkstra_fast/priority_queue.h
172
205
  - lib/dijkstra_fast.rb
173
- - lib/dijkstra_fast/best_path.rb
174
206
  - lib/dijkstra_fast/graph.rb
207
+ - lib/dijkstra_fast/native.rb
175
208
  - lib/dijkstra_fast/no_path_exists_error.rb
209
+ - lib/dijkstra_fast/shortest_path.rb
176
210
  - lib/dijkstra_fast/version.rb
177
211
  homepage: https://github.com/david-mccullars/dijkstra_fast
178
212
  licenses:
179
213
  - MIT
180
- metadata: {}
214
+ metadata:
215
+ rubygems_mfa_required: 'true'
181
216
  post_install_message:
182
217
  rdoc_options: []
183
218
  require_paths:
@@ -186,7 +221,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
186
221
  requirements:
187
222
  - - ">="
188
223
  - !ruby/object:Gem::Version
189
- version: '0'
224
+ version: 3.0.0
190
225
  required_rubygems_version: !ruby/object:Gem::Requirement
191
226
  requirements:
192
227
  - - ">="
@@ -1,234 +0,0 @@
1
- #include <ruby.h>
2
- #include <dijkstra_graph.h>
3
- #include <prioritized_item_list.h>
4
-
5
- const size_t VERTEX_LIST_SIZE = sizeof(VertexListStruct);
6
- const size_t EDGE_LIST_SIZE = sizeof(EdgeListStruct);
7
- const size_t VERTEX_SIZE = sizeof(VertexStruct);
8
- const size_t EDGE_SIZE = sizeof(EdgeStruct);
9
- const size_t GRAPH_SIZE = sizeof(GraphStruct);
10
-
11
- static const rb_data_type_t graph_typed_data = {
12
- "Dijkstra/Graph",
13
- { 0, free_graph, },
14
- 0, 0,
15
- RUBY_TYPED_FREE_IMMEDIATELY,
16
- };
17
-
18
- //////////////////////////////////////////////////////////////////////////////////////
19
-
20
- void Init_dijkstra_graph() {
21
- VALUE DijkstraFastModule, GraphClass;
22
-
23
- DijkstraFastModule = rb_const_get(rb_cObject, rb_intern("DijkstraFast"));
24
- GraphClass = rb_const_get(DijkstraFastModule, rb_intern("Graph"));
25
-
26
- rb_define_alloc_func(GraphClass, dijkstra_graph_allocate);
27
- rb_define_private_method(GraphClass, "_add_edge", dijkstra_graph_add_edge, 3);
28
- rb_define_private_method(GraphClass, "_shortest_path", dijkstra_graph_shortest_path, 3);
29
- }
30
-
31
- VALUE dijkstra_graph_allocate(VALUE self) {
32
- Graph g = malloc(GRAPH_SIZE);
33
-
34
- // Grab a reference to the hash type used by a generic Ruby {}
35
- // which accepts any key and any value. We'll need this type to create
36
- // a st_table in which to put arbitrary VALUE keys. This hash type
37
- // should be a static constant and thus should be safe to utilize without
38
- // fear of garbage collection.
39
- const struct st_hash_type *objhash = rb_hash_tbl(rb_hash_new(), "dijkstra.c", 1)->type;
40
-
41
- g->vertex_count = 0;
42
- g->vertices = NULL;
43
- g->vertex_lookup = st_init_table_with_size(objhash, 0);
44
-
45
- return TypedData_Wrap_Struct(self, &graph_typed_data, g);
46
- }
47
-
48
- VALUE dijkstra_graph_add_edge(VALUE self, VALUE source_label, VALUE dest_label, VALUE distance) {
49
- Graph g;
50
-
51
- TypedData_Get_Struct(self, GraphStruct, &graph_typed_data, g);
52
- add_edge_with_labels(g, source_label, dest_label, NUM2INT(distance));
53
- return Qnil;
54
- }
55
-
56
- VALUE dijkstra_graph_shortest_path(VALUE self, VALUE source_label, VALUE dest_label, VALUE best_path) {
57
- Graph g;
58
- Vertex source, dest;
59
-
60
- TypedData_Get_Struct(self, GraphStruct, &graph_typed_data, g);
61
- source = lookup_vertex(g, source_label, false);
62
- dest = lookup_vertex(g, dest_label, false);
63
-
64
- if (source == NULL || dest == NULL) {
65
- return Qnil;
66
- } else {
67
- return INT2NUM(shortest_path(g, source, dest, best_path));
68
- }
69
- }
70
-
71
- //////////////////////////////////////////////////////////////////////////////////////
72
-
73
- void free_graph(void *data) {
74
- Graph g = (Graph)data;
75
-
76
- struct EdgeListStruct **vertices;
77
-
78
- free_vertex_list(g->vertices, free_vertex);
79
- free(g->vertex_lookup);
80
- free(g);
81
- }
82
-
83
- void free_vertex(Vertex n) {
84
- free_edge_list(n->edges, free_edge);
85
- free(n);
86
- }
87
-
88
- void free_vertex_list(VertexList vertices, void (*free_item)(Vertex)) {
89
- VertexList tmp;
90
- while (vertices != NULL) {
91
- tmp = vertices;
92
- vertices = vertices->next;
93
- if (free_item) {
94
- free_item(tmp->vertex);
95
- }
96
- free(tmp);
97
- }
98
- }
99
-
100
- void free_edge(Edge e) {
101
- // Assume source and destination vertices were allocated elsewhere and will be free'd elsewhere
102
- free(e);
103
- }
104
-
105
- void free_edge_list(EdgeList edges, void (*free_item)(Edge)) {
106
- EdgeList tmp;
107
- while (edges != NULL) {
108
- tmp = edges;
109
- edges = edges->next;
110
- if (free_item) {
111
- free_item(tmp->edge);
112
- }
113
- free(tmp);
114
- }
115
- }
116
-
117
- //////////////////////////////////////////////////////////////////////////////////////
118
-
119
- Vertex add_vertex(Graph g, VALUE label) {
120
- VertexList tmp = malloc(VERTEX_LIST_SIZE);
121
-
122
- tmp->vertex = malloc(VERTEX_SIZE);
123
- tmp->vertex->id = g->vertices != NULL ? g->vertices->vertex->id + 1 : 0; // Auto-incrementing id
124
- tmp->vertex->label = label;
125
- tmp->vertex->edges = NULL;
126
-
127
- tmp->next = g->vertices;
128
- g->vertices = tmp;
129
- g->vertex_count += 1;
130
-
131
- return tmp->vertex;
132
- }
133
-
134
- VertexList add_vertex_to_list(VertexList list, VALUE label) {
135
- VertexList tmp = malloc(VERTEX_LIST_SIZE);
136
-
137
- tmp->vertex = malloc(VERTEX_SIZE);
138
- tmp->vertex->label = label;
139
- tmp->vertex->edges = NULL;
140
-
141
- tmp->next = list;
142
- return tmp;
143
- }
144
-
145
- Edge add_edge(Vertex source, Vertex dest, int distance) {
146
- EdgeList tmp = malloc(EDGE_LIST_SIZE);
147
-
148
- tmp->edge = malloc(EDGE_SIZE);
149
- tmp->edge->source = source;
150
- tmp->edge->dest = dest;
151
- tmp->edge->distance = distance;
152
-
153
- tmp->next = source->edges;
154
- source->edges = tmp;
155
-
156
- return tmp->edge;
157
- }
158
-
159
- Edge add_edge_with_labels(Graph g, VALUE source_label, VALUE dest_label, int distance) {
160
- Vertex source, dest;
161
-
162
- source = lookup_vertex(g, source_label, true);
163
- dest = lookup_vertex(g, dest_label, true);
164
-
165
- return add_edge(source, dest, distance);
166
- }
167
-
168
- Vertex lookup_vertex(Graph g, VALUE label, bool create_if_missing) {
169
- Vertex n = NULL;
170
-
171
- if (!st_lookup(g->vertex_lookup, (st_data_t)label, (st_data_t *)&n)) {
172
- if (!create_if_missing) return NULL;
173
- n = add_vertex(g, label);
174
- st_add_direct(g->vertex_lookup, (st_data_t)label, (st_data_t)n);
175
- }
176
- return n;
177
- }
178
-
179
- //////////////////////////////////////////////////////////////////////////////////////
180
-
181
- int shortest_path(Graph g, Vertex source, Vertex dest, VALUE best_path) {
182
- Vertex *items, *prevs;
183
- PrioritizedItemList list;
184
-
185
- int d, du, dv;
186
- Vertex u, v;
187
- VertexList vl;
188
- EdgeList el;
189
- bool reached = source == dest;
190
-
191
- items = malloc(g->vertex_count * sizeof(Vertex));
192
- prevs = malloc(g->vertex_count * sizeof(Vertex));
193
- list = make_prioritized_item_list(g->vertex_count);
194
-
195
- for (vl = g->vertices; vl != NULL; vl = vl->next) {
196
- v = vl->vertex;
197
- items[v->id] = v;
198
- prevs[v->id] = NULL;
199
- }
200
-
201
- update_prioritized_item(list, source->id, 0);
202
-
203
- while (!empty_prioritized_item_list(list)) {
204
- u = items[next_prioritized_item(list)];
205
- du = get_priority(list, u->id);
206
- for (el = u->edges; el != NULL; el = el->next) {
207
- v = el->edge->dest;
208
- dv = get_priority(list, v->id);
209
- d = du + el->edge->distance;
210
- if (d < 0) d = INT_MAX; // Wrapped around
211
-
212
- if (in_prioritized_item_list(list, v->id) && d < dv) {
213
- update_prioritized_item(list, v->id, d);
214
- prevs[v->id] = u;
215
- reached = reached || v == dest;
216
- }
217
- }
218
- }
219
-
220
- if (reached) {
221
- for (v = dest; v != NULL; v = prevs[v->id]) {
222
- rb_ary_unshift(best_path, v->label);
223
- }
224
- d = get_priority(list, dest->id);
225
- } else {
226
- d = -1;
227
- }
228
-
229
- free(items);
230
- free(prevs);
231
- free_prioritized_item_list(list);
232
-
233
- return d;
234
- }