dijkstra_fast 1.4.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: '0806b283afa40238d914c51d06c587a4dc053d65431bb68c55b6a06a827bb245'
4
+ data.tar.gz: c070c8d2f5bfb38b3fe25469190ef460ca1581a3c670b1715cb3696b6e46373b
5
+ SHA512:
6
+ metadata.gz: 04640eac7496a7fd98dae710d4582f27397b86f4782e9c6fec3eb4a4ef4d46aff809c33a7295dee419c56f09a276214937b09beb1a03220bcbcf3c65d977e2d6
7
+ data.tar.gz: ee26aad2a4a9ab681754e4b8682f8b465e94bc36797da88f3d2ef62de260f4e69e9f2c80dd6856962f3e80a88f3e31cc3e871d9ac9d5d0146a6cfb10906856c7
data/.codeclimate.yml ADDED
@@ -0,0 +1,24 @@
1
+ ---
2
+ engines:
3
+ duplication:
4
+ enabled: true
5
+ config:
6
+ languages:
7
+ - ruby
8
+ checks:
9
+ Similar code:
10
+ enabled: false
11
+ fixme:
12
+ enabled: true
13
+ rubocop:
14
+ enabled: true
15
+ exclude_fingerprints:
16
+ - 4218049e28199ed950d3cd721df86dce
17
+ - c8179d0de3a9df18a2c45750d3f8647e
18
+ - 03f6eee11d86507da564695007106721
19
+ channel: rubocop-1-23-0
20
+ ratings:
21
+ paths:
22
+ - "**.rb"
23
+ exclude_paths:
24
+ - spec/
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.rake_tasks
3
+ /.yardoc
4
+ /Gemfile.lock
5
+ /_yardoc/
6
+ /coverage/
7
+ /html/
8
+ /doc/
9
+ /pkg/
10
+ /spec/reports/
11
+ /tmp/
12
+
13
+ *.bundle
14
+ *.so
15
+ extconf.rb
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.rubocop.yml ADDED
@@ -0,0 +1,195 @@
1
+ AllCops:
2
+ Exclude:
3
+ - dijkstra.gemspec
4
+ - Rakefile
5
+ - ext/**/*
6
+ - tmp/**/*
7
+ Gemspec/DateAssignment:
8
+ Enabled: true
9
+ Gemspec/RequireMFA:
10
+ Enabled: true
11
+ Layout/EmptyLinesAroundAttributeAccessor:
12
+ Enabled: true
13
+ Layout/EmptyLinesAroundClassBody:
14
+ EnforcedStyle: empty_lines_except_namespace
15
+ Layout/EmptyLinesAroundModuleBody:
16
+ EnforcedStyle: empty_lines_except_namespace
17
+ Layout/ExtraSpacing:
18
+ Enabled: false
19
+ Layout/HashAlignment:
20
+ EnforcedHashRocketStyle: table
21
+ EnforcedColonStyle: table
22
+ Layout/LineEndStringConcatenationIndentation:
23
+ Enabled: true
24
+ Layout/LineLength:
25
+ Max: 120
26
+ Enabled: false
27
+ Layout/SpaceAroundMethodCallOperator:
28
+ Enabled: true
29
+ Layout/SpaceBeforeBrackets:
30
+ Enabled: true
31
+ Lint/AmbiguousAssignment:
32
+ Enabled: true
33
+ Lint/AmbiguousOperatorPrecedence:
34
+ Enabled: true
35
+ Lint/AmbiguousRange:
36
+ Enabled: true
37
+ Lint/DeprecatedConstants:
38
+ Enabled: true
39
+ Lint/DeprecatedOpenSSLConstant:
40
+ Enabled: true
41
+ Lint/DuplicateBranch:
42
+ Enabled: true
43
+ Lint/DuplicateRegexpCharacterClassElement:
44
+ Enabled: true
45
+ Lint/EmptyBlock:
46
+ Enabled: true
47
+ Lint/EmptyClass:
48
+ Enabled: true
49
+ Lint/EmptyInPattern:
50
+ Enabled: true
51
+ Lint/IncompatibleIoSelectWithFiberScheduler:
52
+ Enabled: true
53
+ Lint/LambdaWithoutLiteralBlock:
54
+ Enabled: true
55
+ Lint/MixedRegexpCaptureTypes:
56
+ Enabled: true
57
+ Lint/NoReturnInBeginEndBlocks:
58
+ Enabled: true
59
+ Lint/NumberedParameterAssignment:
60
+ Enabled: true
61
+ Lint/OrAssignmentToConstant:
62
+ Enabled: true
63
+ Lint/RaiseException:
64
+ Enabled: true
65
+ Lint/RedundantDirGlobSort:
66
+ Enabled: true
67
+ Lint/RequireRelativeSelfPath:
68
+ Enabled: true
69
+ Lint/StructNewOverride:
70
+ Enabled: true
71
+ Lint/SymbolConversion:
72
+ Enabled: true
73
+ Lint/ToEnumArguments:
74
+ Enabled: true
75
+ Lint/TripleQuotes:
76
+ Enabled: true
77
+ Lint/UnexpectedBlockArity:
78
+ Enabled: true
79
+ Lint/UnmodifiedReduceAccumulator:
80
+ Enabled: true
81
+ Lint/UselessRuby2Keywords:
82
+ Enabled: true
83
+ Metrics/AbcSize:
84
+ Max: 50
85
+ Enabled: false
86
+ Metrics/BlockLength:
87
+ Max: 50
88
+ Enabled: false
89
+ Metrics/ClassLength:
90
+ Max: 50
91
+ Enabled: false
92
+ Metrics/CyclomaticComplexity:
93
+ Max: 30
94
+ Enabled: false
95
+ Metrics/MethodLength:
96
+ Max: 20
97
+ Enabled: false
98
+ Metrics/ModuleLength:
99
+ Max: 1000
100
+ Enabled: false
101
+ Metrics/PerceivedComplexity:
102
+ Max: 30
103
+ Enabled: false
104
+ Security/IoMethods:
105
+ Enabled: true
106
+ Security/MarshalLoad:
107
+ Enabled: false
108
+ Style/AndOr:
109
+ Enabled: false
110
+ Style/ArgumentsForwarding:
111
+ Enabled: true
112
+ Style/CaseEquality:
113
+ Enabled: false
114
+ Style/CollectionCompact:
115
+ Enabled: true
116
+ Style/DocumentDynamicEvalDefinition:
117
+ Enabled: true
118
+ Style/Documentation:
119
+ Enabled: false
120
+ Style/DoubleNegation:
121
+ Enabled: false
122
+ Style/EndlessMethod:
123
+ Enabled: true
124
+ Style/ExponentialNotation:
125
+ Enabled: true
126
+ Style/FrozenStringLiteralComment:
127
+ Enabled: false
128
+ Style/GuardClause:
129
+ Enabled: false
130
+ Style/HashConversion:
131
+ Enabled: true
132
+ Style/HashEachMethods:
133
+ Enabled: true
134
+ Style/HashExcept:
135
+ Enabled: true
136
+ Style/HashSyntax:
137
+ Enabled: true
138
+ Style/HashTransformKeys:
139
+ Enabled: true
140
+ Style/HashTransformValues:
141
+ Enabled: true
142
+ Style/IfUnlessModifier:
143
+ Enabled: false
144
+ Style/IfWithBooleanLiteralBranches:
145
+ Enabled: true
146
+ Style/InPatternThen:
147
+ Enabled: true
148
+ Style/MultilineBlockChain:
149
+ Enabled: false
150
+ Style/MultilineIfModifier:
151
+ Enabled: false
152
+ Style/MultilineInPatternThen:
153
+ Enabled: true
154
+ Style/MutableConstant:
155
+ Enabled: false
156
+ Style/NegatedIfElseCondition:
157
+ Enabled: true
158
+ Style/NilLambda:
159
+ Enabled: true
160
+ Style/NumberedParameters:
161
+ Enabled: true
162
+ Style/NumberedParametersLimit:
163
+ Enabled: true
164
+ Style/OpenStructUse:
165
+ Enabled: true
166
+ Style/QuotedSymbols:
167
+ Enabled: true
168
+ Style/RedundantArgument:
169
+ Enabled: true
170
+ Style/RedundantRegexpCharacterClass:
171
+ Enabled: true
172
+ Style/RedundantRegexpEscape:
173
+ Enabled: true
174
+ Style/RedundantSelfAssignmentBranch:
175
+ Enabled: true
176
+ Style/RescueModifier:
177
+ Enabled: false
178
+ Style/RescueStandardError:
179
+ Enabled: false
180
+ Style/SelectByRegexp:
181
+ Enabled: true
182
+ Style/SlicingWithRange:
183
+ Enabled: true
184
+ Style/StringChars:
185
+ Enabled: true
186
+ Style/SwapValues:
187
+ Enabled: true
188
+ Style/TrailingCommaInArguments:
189
+ EnforcedStyleForMultiline: comma
190
+ Style/TrailingCommaInArrayLiteral:
191
+ EnforcedStyleForMultiline: consistent_comma
192
+ Style/TrailingCommaInHashLiteral:
193
+ EnforcedStyleForMultiline: consistent_comma
194
+ Style/ZeroLengthPredicate:
195
+ Enabled: false
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.0.3
data/.travis.yml ADDED
@@ -0,0 +1,16 @@
1
+ env:
2
+ global:
3
+ - CC_TEST_REPORTER_ID=b5370328873f97034f05a0c49b63b7d13b6eeaed129f1bdf17509f183cd6a2f7
4
+ language: ruby
5
+ rvm:
6
+ - 3.0.3
7
+ before_install: gem install bundler -v 2.2.32
8
+ before_script:
9
+ - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
10
+ - chmod +x ./cc-test-reporter
11
+ - ./cc-test-reporter before-build
12
+ - bundle exec rake compile
13
+ script:
14
+ - bundle exec rspec
15
+ after_script:
16
+ - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
@@ -0,0 +1,49 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, and in the interest of
4
+ fostering an open and welcoming community, we pledge to respect all people who
5
+ contribute through reporting issues, posting feature requests, updating
6
+ documentation, submitting pull requests or patches, and other activities.
7
+
8
+ We are committed to making participation in this project a harassment-free
9
+ experience for everyone, regardless of level of experience, gender, gender
10
+ identity and expression, sexual orientation, disability, personal appearance,
11
+ body size, race, ethnicity, age, religion, or nationality.
12
+
13
+ Examples of unacceptable behavior by participants include:
14
+
15
+ * The use of sexualized language or imagery
16
+ * Personal attacks
17
+ * Trolling or insulting/derogatory comments
18
+ * Public or private harassment
19
+ * Publishing other's private information, such as physical or electronic
20
+ addresses, without explicit permission
21
+ * Other unethical or unprofessional conduct
22
+
23
+ Project maintainers have the right and responsibility to remove, edit, or
24
+ reject comments, commits, code, wiki edits, issues, and other contributions
25
+ that are not aligned to this Code of Conduct, or to ban temporarily or
26
+ permanently any contributor for other behaviors that they deem inappropriate,
27
+ threatening, offensive, or harmful.
28
+
29
+ By adopting this Code of Conduct, project maintainers commit themselves to
30
+ fairly and consistently applying these principles to every aspect of managing
31
+ this project. Project maintainers who do not follow or enforce the Code of
32
+ Conduct may be permanently removed from the project team.
33
+
34
+ This code of conduct applies both within project spaces and in public spaces
35
+ when an individual is representing the project or its community.
36
+
37
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
38
+ reported by contacting a project maintainer at david@bloomfire.com. All
39
+ complaints will be reviewed and investigated and will result in a response that
40
+ is deemed necessary and appropriate to the circumstances. Maintainers are
41
+ obligated to maintain confidentiality with regard to the reporter of an
42
+ incident.
43
+
44
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
45
+ version 1.3.0, available at
46
+ [http://contributor-covenant.org/version/1/3/0/][version]
47
+
48
+ [homepage]: http://contributor-covenant.org
49
+ [version]: http://contributor-covenant.org/version/1/3/0/
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 David McCullars
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,69 @@
1
+ # Dijkstra (Fast!)
2
+
3
+ * README: https://github.com/david-mccullars/dijkstra_fast
4
+ * Documentation: http://www.rubydoc.info/github/david-mccullars/dijkstra_fast
5
+ * Bug Reports: https://github.com/david-mccullars/dijkstra_fast/issues
6
+
7
+
8
+ ## Status
9
+
10
+ [![Gem Version](https://badge.fury.io/rb/dijkstra_fast.svg)](https://badge.fury.io/rb/dijkstra_fast)
11
+ [![Travis Build Status](https://travis-ci.org/david-mccullars/dijkstra_fast.svg?branch=master)](https://travis-ci.org/david-mccullars/dijkstra_fast)
12
+ [![Code Climate](https://codeclimate.com/github/david-mccullars/dijkstra_fast/badges/gpa.svg)](https://codeclimate.com/github/david-mccullars/dijkstra_fast)
13
+ [![Test Coverage](https://codeclimate.com/github/david-mccullars/dijkstra_fast/badges/coverage.svg)](https://codeclimate.com/github/david-mccullars/dijkstra_fast/coverage)
14
+
15
+
16
+ ## Description
17
+
18
+ [Dijkstra](https://en.wikipedia.org/wiki/Dijkstra's_algorithm) is a commonly
19
+ used algorithm for finding the shortest path through a graph or network.
20
+
21
+
22
+ ## Features
23
+
24
+ * Native implementation of Dijkstra's algorithm intended for use on large,
25
+ sparse graphs for which an array of arrays is inefficient.
26
+
27
+
28
+ ## Installation
29
+
30
+ ```
31
+ gem install dijkstra_fast
32
+ ```
33
+
34
+ ## Requirements
35
+
36
+ * Ruby 3.0 or higher
37
+
38
+ ## Usage
39
+
40
+ **Dijkstra**
41
+
42
+ ```ruby
43
+ require 'dijkstra_fast'
44
+
45
+ graph = DijkstraFast::Graph.new
46
+ graph.add("A", "B", distance: 5)
47
+ graph.add("A", "C", distance: 8)
48
+ graph.add("B", "C", distance: 2)
49
+ best_path = graph.shortest_path("A", "C")
50
+ best_path.path
51
+
52
+ => ["A", "B", "C"]
53
+
54
+ best_path.distance
55
+
56
+ => 7
57
+ ```
58
+
59
+
60
+ ## License
61
+
62
+ MIT. See the `LICENSE` file.
63
+
64
+
65
+ ## References
66
+
67
+ > Cormen, Thomas H.; Leiserson, Charles E.; Rivest, Ronald L.; Stein, Clifford (2001). "Section 24.3: Dijkstra's algorithm". Introduction to Algorithms (Second ed.). MIT Press and McGraw–Hill. pp. 595–601. ISBN 0-262-03293-7.
68
+
69
+ > Knuth, D.E. (1977). "A Generalization of Dijkstra's Algorithm". Information Processing Letters. 6 (1): 1–5. doi:10.1016/0020-0190(77)90002-3.
data/Rakefile ADDED
@@ -0,0 +1,17 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/extensiontask"
3
+ require "rspec/core/rake_task"
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task :default => :spec
8
+
9
+ require 'rdoc/task'
10
+ RDoc::Task.new do |rdoc|
11
+ rdoc.main = "README.md"
12
+ rdoc.rdoc_files.include("README.md", "lib/**/*.rb")
13
+ end
14
+
15
+ Rake::ExtensionTask.new('dijkstra_fast') do |ext|
16
+ ext.lib_dir = 'lib/dijkstra_fast'
17
+ end
@@ -0,0 +1,39 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'dijkstra_fast/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'dijkstra_fast'
7
+ spec.version = DijkstraFast::VERSION
8
+ spec.authors = ['David McCullars']
9
+ spec.email = ['david.mccullars@gmail.com']
10
+
11
+ spec.summary = '(Native) implementation of Dijkstra algorithm for large, sparse graphs'
12
+ spec.description = <<~DESCRIPTION
13
+ Native implementation of Dijkstra algorithm for finding the shortest path
14
+ between two vertices in a large, sparse graphs. Underlying algorithm is
15
+ implemented in C using a priority queue. Edges are represented using linked
16
+ lists rather than an adjacency matrix to reduce memory footprint when operating
17
+ on very large graphs where the average number of edges between nodes is
18
+ relatively small (e.g. < 1/10 the number of nodes). See
19
+ https://en.wikipedia.org/wiki/Dijkstra's_algorithm for additional information.
20
+ DESCRIPTION
21
+ spec.homepage = 'https://github.com/david-mccullars/dijkstra_fast'
22
+ spec.license = 'MIT'
23
+
24
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
25
+ spec.bindir = 'exe'
26
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
+ spec.extensions = ['ext/dijkstra_fast/extconf.rb']
28
+ spec.require_paths = ['lib']
29
+
30
+ spec.add_development_dependency 'bundler'
31
+ spec.add_development_dependency 'rake'
32
+ spec.add_development_dependency 'rake-compiler'
33
+ spec.add_development_dependency 'rspec'
34
+ spec.add_development_dependency 'rubocop'
35
+ spec.add_development_dependency 'rubocop-rake'
36
+ spec.add_development_dependency 'rubocop-rspec'
37
+ spec.add_development_dependency 'simplecov'
38
+ spec.add_development_dependency 'yard'
39
+ end
@@ -0,0 +1,5 @@
1
+ #include <dijkstra_graph.h>
2
+
3
+ void Init_dijkstra_fast() {
4
+ Init_dijkstra_graph();
5
+ }
@@ -0,0 +1,234 @@
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
+ }
@@ -0,0 +1,83 @@
1
+ #ifndef DIJKSTRA_GRAPH_H
2
+ #define DIJKSTRA_GRAPH_H
3
+
4
+ #include <ruby.h>
5
+
6
+ struct VertexListStruct;
7
+ typedef struct VertexListStruct* VertexList;
8
+
9
+ typedef struct VertexListStruct {
10
+ struct VertexStruct *vertex;
11
+ struct VertexListStruct *next;
12
+ } VertexListStruct;
13
+
14
+ //////////////////////////////////////////////////////////////////////////////////////
15
+
16
+ struct EdgeListStruct;
17
+ typedef struct EdgeListStruct* EdgeList;
18
+
19
+ typedef struct EdgeListStruct {
20
+ struct EdgeStruct *edge;
21
+ struct EdgeListStruct *next;
22
+ } EdgeListStruct;
23
+
24
+ //////////////////////////////////////////////////////////////////////////////////////
25
+
26
+ struct VertexStruct;
27
+ typedef struct VertexStruct* Vertex;
28
+
29
+ typedef struct VertexStruct {
30
+ EdgeList edges;
31
+ int id;
32
+ VALUE label;
33
+ } VertexStruct;
34
+
35
+ //////////////////////////////////////////////////////////////////////////////////////
36
+
37
+ struct EdgeStruct;
38
+ typedef struct EdgeStruct* Edge;
39
+
40
+ typedef struct EdgeStruct {
41
+ Vertex source;
42
+ Vertex dest;
43
+ int distance;
44
+ } EdgeStruct;
45
+
46
+ //////////////////////////////////////////////////////////////////////////////////////
47
+
48
+ struct GraphStruct;
49
+ typedef struct GraphStruct* Graph;
50
+
51
+ typedef struct GraphStruct {
52
+ unsigned long vertex_count;
53
+ VertexList vertices;
54
+ st_table *vertex_lookup;
55
+ } GraphStruct;
56
+
57
+ //////////////////////////////////////////////////////////////////////////////////////
58
+
59
+ void free_graph(void *data);
60
+ void free_vertex(Vertex n);
61
+ void free_vertex_list(VertexList vertices, void (*free_item)(Vertex));
62
+ void free_edge(Edge e);
63
+ void free_edge_list(EdgeList edges, void (*free_item)(Edge));
64
+
65
+ //////////////////////////////////////////////////////////////////////////////////////
66
+
67
+ Vertex add_vertex(Graph g, VALUE label);
68
+ Edge add_edge(Vertex source, Vertex dest, int distance);
69
+ Edge add_edge_with_labels(Graph g, VALUE source_label, VALUE dest_label, int distance);
70
+ Vertex lookup_vertex(Graph g, VALUE label, bool create_if_missing);
71
+
72
+ //////////////////////////////////////////////////////////////////////////////////////
73
+
74
+ int shortest_path(Graph g, Vertex source, Vertex dest, VALUE best_path);
75
+
76
+ //////////////////////////////////////////////////////////////////////////////////////
77
+
78
+ void Init_dijkstra_graph();
79
+ VALUE dijkstra_graph_allocate(VALUE self);
80
+ VALUE dijkstra_graph_add_edge(VALUE self, VALUE source_label, VALUE dest_label, VALUE distance);
81
+ VALUE dijkstra_graph_shortest_path(VALUE self, VALUE source_label, VALUE dest_label, VALUE best_path);
82
+
83
+ #endif
@@ -0,0 +1,3 @@
1
+ require "mkmf"
2
+
3
+ create_makefile('dijkstra_fast/dijkstra_fast')
@@ -0,0 +1,128 @@
1
+ #include <prioritized_item_list.h>
2
+
3
+ PrioritizedItemList make_prioritized_item_list(int capacity) {
4
+ int i;
5
+
6
+ PrioritizedItemList list = malloc(sizeof(PrioritizedItemListStruct));
7
+ list->priorities = malloc(capacity * sizeof(PrioritizedItem));
8
+ list->indices = malloc(capacity * sizeof(int));
9
+ list->capacity = capacity;
10
+ list->last = capacity - 1;
11
+ for (i = 0; i < capacity; i++) {
12
+ list->priorities[i] = malloc(sizeof(PrioritizedItemStruct));
13
+ list->priorities[i]->item = i;
14
+ list->priorities[i]->priority = INT_MAX;
15
+ list->indices[i] = i;
16
+ }
17
+ return list;
18
+ }
19
+
20
+ bool empty_prioritized_item_list(PrioritizedItemList list) {
21
+ return list->last < 0;
22
+ }
23
+
24
+ bool in_prioritized_item_list(PrioritizedItemList list, int item) {
25
+ return list->indices[item] <= list->last;
26
+ }
27
+
28
+ void swap_prioritized_items(PrioritizedItemList list, int i, int j) {
29
+ PrioritizedItem tmp = list->priorities[i];
30
+ list->priorities[i] = list->priorities[j];
31
+ list->priorities[j] = tmp;
32
+ list->indices[list->priorities[i]->item] = i;
33
+ list->indices[list->priorities[j]->item] = j;
34
+ }
35
+
36
+ void reprioritize_right(PrioritizedItemList list, int i) {
37
+ int orig_i = i;
38
+ int wi, wj_left, wj_right;
39
+ int j_left, j_right;
40
+ wi = list->priorities[i]->priority;
41
+
42
+ while (true) {
43
+ j_left = (i << 1) + 1;
44
+ j_right = j_left + 1;
45
+
46
+ wj_left = j_left <= list->last ? list->priorities[j_left]->priority : INT_MAX;
47
+ wj_right = j_right <= list->last ? list->priorities[j_right]->priority : INT_MAX;
48
+
49
+ if (wj_right < wi && wj_right < wj_left) {
50
+ swap_prioritized_items(list, i, j_right);
51
+ i = j_right;
52
+
53
+ } else if (wj_left < wi) {
54
+ swap_prioritized_items(list, i, j_left);
55
+ i = j_left;
56
+
57
+ } else {
58
+ return;
59
+ }
60
+ }
61
+ }
62
+
63
+ void reprioritize_left(PrioritizedItemList list, int i) {
64
+ int wi, wj;
65
+ int j;
66
+ wi = list->priorities[i]->priority;
67
+
68
+ while (i > 0) {
69
+ j = (i - 1) >> 1;
70
+ wj = list->priorities[j]->priority;
71
+
72
+ if (wj > wi) {
73
+ swap_prioritized_items(list, i, j);
74
+ i = j;
75
+
76
+ } else {
77
+ return;
78
+ }
79
+ }
80
+ }
81
+
82
+ int next_prioritized_item(PrioritizedItemList list) {
83
+ PrioritizedItem item;
84
+
85
+ if (empty_prioritized_item_list(list)) {
86
+ return -1;
87
+ }
88
+
89
+ item = list->priorities[0];
90
+ swap_prioritized_items(list, 0, list->last);
91
+ list->last--;
92
+ reprioritize_right(list, 0);
93
+
94
+ return item->item;
95
+ }
96
+
97
+ void update_prioritized_item(PrioritizedItemList list, int item, int priority) {
98
+ int i = list->indices[item];
99
+ list->priorities[i]->priority = priority;
100
+
101
+ if (i <= list->last) {
102
+ reprioritize_left(list, i);
103
+ }
104
+ }
105
+
106
+ int get_priority(PrioritizedItemList list, int item) {
107
+ int index = list->indices[item];
108
+ return list->priorities[index]->priority;
109
+ }
110
+
111
+ // For debugging only
112
+ void print_prioritized_item_list(PrioritizedItemList list) {
113
+ for (int i = 0; i <= list->last; i++) {
114
+ if (i > 0) printf(", ");
115
+ printf("%d (%d)", list->priorities[i]->item, list->priorities[i]->priority);
116
+ }
117
+ printf("\n");
118
+ }
119
+
120
+ void free_prioritized_item_list(PrioritizedItemList list) {
121
+ for (int i = 0; i < list->capacity; i++) {
122
+ free(list->priorities[i]);
123
+ }
124
+
125
+ free(list->priorities);
126
+ free(list->indices);
127
+ free(list);
128
+ }
@@ -0,0 +1,39 @@
1
+ #ifndef PRIORITIZED_ITEM_LIST_H
2
+ #define PRIORITIZED_ITEM_LIST_H
3
+
4
+ #include <ruby.h>
5
+
6
+ struct PrioritizedItemStruct;
7
+ typedef struct PrioritizedItemStruct* PrioritizedItem;
8
+
9
+ typedef struct PrioritizedItemStruct {
10
+ int item;
11
+ int priority;
12
+ } PrioritizedItemStruct;
13
+
14
+ //////////////////////////////////////////////////////////////////////////////////////
15
+
16
+ struct PrioritizedItemListStruct;
17
+ typedef struct PrioritizedItemListStruct* PrioritizedItemList;
18
+
19
+ typedef struct PrioritizedItemListStruct {
20
+ PrioritizedItem *priorities;
21
+ int *indices;
22
+ int capacity;
23
+ int last;
24
+ } PrioritizedItemListStruct;
25
+
26
+ //////////////////////////////////////////////////////////////////////////////////////
27
+
28
+ PrioritizedItemList make_prioritized_item_list(int capacity);
29
+ bool empty_prioritized_item_list(PrioritizedItemList list);
30
+ bool in_prioritized_item_list(PrioritizedItemList list, int item);
31
+ int next_prioritized_item(PrioritizedItemList list);
32
+ void update_prioritized_item(PrioritizedItemList list, int item, int priority);
33
+ void print_prioritized_item_list(PrioritizedItemList list);
34
+ int get_priority(PrioritizedItemList list, int item);
35
+ void free_prioritized_item_list(PrioritizedItemList list);
36
+
37
+ //////////////////////////////////////////////////////////////////////////////////////
38
+
39
+ #endif
@@ -0,0 +1,17 @@
1
+ module DijkstraFast
2
+ ##
3
+ # The "best path" from applying Dijkstra's algorithm contains both the ordered
4
+ # list of nodes travelled as well as the total distance travelled.
5
+ ##
6
+ class BestPath
7
+
8
+ attr_reader :path
9
+ attr_accessor :distance
10
+
11
+ def initialize
12
+ @path = []
13
+ @distance = 0
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,63 @@
1
+ module DijkstraFast
2
+ ##
3
+ # Dijkstra's algorithm finds the shortest "distance" between two items within a
4
+ # collection. For any two items within that collection that are "connected"
5
+ # there should be an associated "distance" between them. We can represent this
6
+ # collection of items as nodes in a directed graph, and we can represent the
7
+ # associated connections as weighted edges.
8
+ #
9
+ # = Example
10
+ #
11
+ # graph = DijkstraFast::Graph.new
12
+ # graph.add("A", "B", distance: 5)
13
+ # graph.add("A", "C", distance: 8)
14
+ # graph.add("B", "C", distance: 2)
15
+ # best_path = graph.shortest_path("A", "C")
16
+ # best_path.path
17
+ #
18
+ # => ["A", "B", "C"]
19
+ #
20
+ # best_path.distance
21
+ #
22
+ # => 7
23
+ #
24
+ ##
25
+ class Graph
26
+
27
+ def initialize
28
+ @nodes = {}
29
+ end
30
+
31
+ # Adds a weighted edge to the graph. This represents a possible path from the
32
+ # source item to the destination item with corresponding "distance."
33
+ # @param source [Object] Any Ruby object that represents the source item
34
+ # @param dest [Object] Any Ruby object that represents the destination item
35
+ # @param distance [Integer] Optional distance between source and destination.
36
+ # If not provided, a default distance of `1` is used.
37
+ # @return [nil]
38
+ def add(source, dest, distance: 1)
39
+ _add_edge(node(source), node(dest), distance) unless source == dest
40
+ end
41
+
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
54
+ end
55
+
56
+ private
57
+
58
+ def node(obj)
59
+ @nodes[obj] ||= @nodes.size # Auto-increment id
60
+ end
61
+
62
+ end
63
+ end
@@ -0,0 +1,6 @@
1
+ module DijkstraFast
2
+ ##
3
+ # Raised when no path exists between the given source and destination items.
4
+ ##
5
+ class NoPathExistsError < StandardError; end
6
+ end
@@ -0,0 +1,6 @@
1
+ module DijkstraFast
2
+
3
+ # Current gem version
4
+ VERSION = '1.4.2'
5
+
6
+ end
@@ -0,0 +1,22 @@
1
+ ##
2
+ # Provides native implementation of Dijkstra's algorithm for finding the shortest
3
+ # path between two vertices in a large, sparse graph.
4
+ #
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
+ #
10
+ # @see README
11
+ # @see https://en.wikipedia.org/wiki/Dijkstra's_algorithm
12
+ ##
13
+ module DijkstraFast
14
+
15
+ autoload :BestPath, 'dijkstra_fast/best_path'
16
+ autoload :Graph, 'dijkstra_fast/graph'
17
+ autoload :NoPathExistsError, 'dijkstra_fast/no_path_exists_error'
18
+ autoload :VERSION, 'dijkstra_fast/version'
19
+
20
+ end
21
+
22
+ require 'dijkstra_fast/dijkstra_fast'
metadata ADDED
@@ -0,0 +1,200 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dijkstra_fast
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.4.2
5
+ platform: ruby
6
+ authors:
7
+ - David McCullars
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2021-12-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
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'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake-compiler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop
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'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop-rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop-rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: simplecov
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: yard
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ description: |
140
+ Native implementation of Dijkstra algorithm for finding the shortest path
141
+ between two vertices in a large, sparse graphs. Underlying algorithm is
142
+ implemented in C using a priority queue. Edges are represented using linked
143
+ lists rather than an adjacency matrix to reduce memory footprint when operating
144
+ on very large graphs where the average number of edges between nodes is
145
+ relatively small (e.g. < 1/10 the number of nodes). See
146
+ https://en.wikipedia.org/wiki/Dijkstra's_algorithm for additional information.
147
+ email:
148
+ - david.mccullars@gmail.com
149
+ executables: []
150
+ extensions:
151
+ - ext/dijkstra_fast/extconf.rb
152
+ extra_rdoc_files: []
153
+ files:
154
+ - ".codeclimate.yml"
155
+ - ".gitignore"
156
+ - ".rspec"
157
+ - ".rubocop.yml"
158
+ - ".ruby-version"
159
+ - ".travis.yml"
160
+ - CODE_OF_CONDUCT.md
161
+ - Gemfile
162
+ - LICENSE
163
+ - README.md
164
+ - Rakefile
165
+ - dijkstra_fast.gemspec
166
+ - ext/dijkstra_fast/dijkstra_fast.c
167
+ - ext/dijkstra_fast/dijkstra_graph.c
168
+ - ext/dijkstra_fast/dijkstra_graph.h
169
+ - ext/dijkstra_fast/extconf.rb
170
+ - ext/dijkstra_fast/prioritized_item_list.c
171
+ - ext/dijkstra_fast/prioritized_item_list.h
172
+ - lib/dijkstra_fast.rb
173
+ - lib/dijkstra_fast/best_path.rb
174
+ - lib/dijkstra_fast/graph.rb
175
+ - lib/dijkstra_fast/no_path_exists_error.rb
176
+ - lib/dijkstra_fast/version.rb
177
+ homepage: https://github.com/david-mccullars/dijkstra_fast
178
+ licenses:
179
+ - MIT
180
+ metadata: {}
181
+ post_install_message:
182
+ rdoc_options: []
183
+ require_paths:
184
+ - lib
185
+ required_ruby_version: !ruby/object:Gem::Requirement
186
+ requirements:
187
+ - - ">="
188
+ - !ruby/object:Gem::Version
189
+ version: '0'
190
+ required_rubygems_version: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
195
+ requirements: []
196
+ rubygems_version: 3.2.32
197
+ signing_key:
198
+ specification_version: 4
199
+ summary: "(Native) implementation of Dijkstra algorithm for large, sparse graphs"
200
+ test_files: []