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 +7 -0
- data/.codeclimate.yml +24 -0
- data/.gitignore +15 -0
- data/.rspec +2 -0
- data/.rubocop.yml +195 -0
- data/.ruby-version +1 -0
- data/.travis.yml +16 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +3 -0
- data/LICENSE +21 -0
- data/README.md +69 -0
- data/Rakefile +17 -0
- data/dijkstra_fast.gemspec +39 -0
- data/ext/dijkstra_fast/dijkstra_fast.c +5 -0
- data/ext/dijkstra_fast/dijkstra_graph.c +234 -0
- data/ext/dijkstra_fast/dijkstra_graph.h +83 -0
- data/ext/dijkstra_fast/extconf.rb +3 -0
- data/ext/dijkstra_fast/prioritized_item_list.c +128 -0
- data/ext/dijkstra_fast/prioritized_item_list.h +39 -0
- data/lib/dijkstra_fast/best_path.rb +17 -0
- data/lib/dijkstra_fast/graph.rb +63 -0
- data/lib/dijkstra_fast/no_path_exists_error.rb +6 -0
- data/lib/dijkstra_fast/version.rb +6 -0
- data/lib/dijkstra_fast.rb +22 -0
- metadata +200 -0
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
data/.rspec
ADDED
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
|
data/CODE_OF_CONDUCT.md
ADDED
|
@@ -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
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
|
+
[](https://badge.fury.io/rb/dijkstra_fast)
|
|
11
|
+
[](https://travis-ci.org/david-mccullars/dijkstra_fast)
|
|
12
|
+
[](https://codeclimate.com/github/david-mccullars/dijkstra_fast)
|
|
13
|
+
[](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,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,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,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: []
|