pairing_heap 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 90dd09750c4fddd39d50f8cc84425b7c88faa7a31912a23e828525dfe1987f1f
4
- data.tar.gz: 4710ff54026a0f62b6f8f535a48b3683277534c319832ea4e08de8e122467fd2
3
+ metadata.gz: afe0b373b7c015091b7d7f19ba3d8d0004d1f7c257aad0fe678cf235dc288541
4
+ data.tar.gz: d3b93b4741c9255b67f0ca232cb881a32fa965b4390c903c4eda96b992961176
5
5
  SHA512:
6
- metadata.gz: 9aaf8a8b4a665fa53cc6a1f725e8a743a2bd7a9752ad0e0b0d0f8f7a6efb970693276b7731c778ef3785653595d3cfdd22afbad43511aa3f3dd093739ea74f67
7
- data.tar.gz: 495d1219cd93cabefaba75956729f18dbb0a69eb4af9cee4122920e9b260fb0fa3b6d66170c86eb1f398c71049f34f622f75346ade10a0f9ef7093c5ddffeefd
6
+ metadata.gz: d039d5f4a3b996a5f7b1f3193be96291ffa8d53537e45fa4fb6d61fbb83e9a1b897156c64918da8f08b3f0c78e905ef5e4588598495e3c655319e84a201bb037
7
+ data.tar.gz: 29669debc6da2984b31e986c63ae36776bd48ccf6febab79d0a8f74b304c1242d65aabeba2b2ff7362476448722c4c52bb6c170a8ebbd857d1c3e930e41a79c4
@@ -8,7 +8,7 @@ jobs:
8
8
  matrix:
9
9
  os: [ubuntu-latest, macos-latest]
10
10
  # Due to https://github.com/actions/runner/issues/849, we have to use quotes for '3.0'
11
- ruby: ['2.3', '2.7', '3.0', '3.1', head, jruby, jruby-head, truffleruby, truffleruby-head]
11
+ ruby: ['2.7', '3.0', '3.1', '3.2', head, jruby, jruby-head, truffleruby, truffleruby-head]
12
12
  runs-on: ${{ matrix.os }}
13
13
  steps:
14
14
  - uses: actions/checkout@v3
@@ -16,6 +16,6 @@ jobs:
16
16
  with:
17
17
  ruby-version: ${{ matrix.ruby }}
18
18
  - run: |
19
- gem install bundler -v 2.2.3
19
+ gem install bundler -v 2.3.6
20
20
  bundle install
21
21
  bundle exec rake
data/.standard.yml ADDED
@@ -0,0 +1,3 @@
1
+ ruby_version: 3.2.0
2
+ ignore:
3
+ - 'test/fib.rb'
data/Gemfile.lock CHANGED
@@ -1,21 +1,63 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- pairing_heap (0.2.0)
4
+ pairing_heap (2.0.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
+ ast (2.4.2)
10
+ codecov (0.6.0)
11
+ simplecov (>= 0.15, < 0.22)
12
+ docile (1.4.0)
13
+ json (2.6.3)
14
+ language_server-protocol (3.17.0.2)
9
15
  minitest (5.15.0)
16
+ parallel (1.22.1)
17
+ parser (3.1.3.0)
18
+ ast (~> 2.4.1)
19
+ rainbow (3.1.1)
10
20
  rake (13.0.6)
21
+ regexp_parser (2.6.1)
22
+ rexml (3.2.5)
23
+ rubocop (1.40.0)
24
+ json (~> 2.3)
25
+ parallel (~> 1.10)
26
+ parser (>= 3.1.2.1)
27
+ rainbow (>= 2.2.2, < 4.0)
28
+ regexp_parser (>= 1.8, < 3.0)
29
+ rexml (>= 3.2.5, < 4.0)
30
+ rubocop-ast (>= 1.23.0, < 2.0)
31
+ ruby-progressbar (~> 1.7)
32
+ unicode-display_width (>= 1.4.0, < 3.0)
33
+ rubocop-ast (1.24.0)
34
+ parser (>= 3.1.1.0)
35
+ rubocop-performance (1.15.1)
36
+ rubocop (>= 1.7.0, < 2.0)
37
+ rubocop-ast (>= 0.4.0)
38
+ ruby-progressbar (1.11.0)
39
+ simplecov (0.21.2)
40
+ docile (~> 1.1)
41
+ simplecov-html (~> 0.11)
42
+ simplecov_json_formatter (~> 0.1)
43
+ simplecov-html (0.12.3)
44
+ simplecov_json_formatter (0.1.4)
45
+ standard (1.20.0)
46
+ language_server-protocol (~> 3.17.0.2)
47
+ rubocop (= 1.40.0)
48
+ rubocop-performance (= 1.15.1)
49
+ unicode-display_width (2.3.0)
11
50
 
12
51
  PLATFORMS
13
52
  x86_64-darwin-21
53
+ x86_64-darwin-22
14
54
 
15
55
  DEPENDENCIES
56
+ codecov (= 0.6.0)
16
57
  minitest (~> 5.0)
17
58
  pairing_heap!
18
59
  rake (~> 13.0)
60
+ standard (~> 1.20)
19
61
 
20
62
  BUNDLED WITH
21
63
  2.3.6
data/README.md CHANGED
@@ -1,4 +1,5 @@
1
1
  # PairingHeap
2
+ [![Ruby Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://github.com/testdouble/standard)
2
3
 
3
4
  PairingHeap is a pure Ruby priority queue implementation using a pairing heap as the underlying data structure. While a pairing heap is asymptotically less efficient than the Fibonacci heap, it is usually faster in practice. This makes it a popular choice for Prim's MST or Dijkstra's algorithm implementations.
4
5
 
@@ -36,8 +37,8 @@ simple_heap.push(:a, 1)
36
37
  simple_heap.push(:b, 2)
37
38
  simple_heap.push(:c, 3)
38
39
  simple_heap.peek # => :a
39
- simple_heap.peek_priority # => [:a, 1]
40
- simple_heap.pop_priority # => [:a, 1]
40
+ simple_heap.peek_priority # => 1
41
+ simple_heap.pop_with_priority # => [:a, 1]
41
42
  simple_heap.pop # => :b
42
43
 
43
44
  # Min priority queue
@@ -930,14 +931,14 @@ Heaps that support change_priority operation use it. Heaps that do not support i
930
931
  <td>pairing_heap (PairingHeap)</td>
931
932
  <td>1.359x slower</td>
932
933
  </tr>
933
- <tr>
934
- <td>lazy_priority_queue</td>
935
- <td>2.115x slower</td>
936
- </tr>
937
934
  <tr>
938
935
  <td>Fibonacci</td>
939
936
  <td>1.824x slower</td>
940
937
  </tr>
938
+ <tr>
939
+ <td>lazy_priority_queue</td>
940
+ <td>2.115x slower</td>
941
+ </tr>
941
942
  <tr>
942
943
  <th colspan="4">truffleruby 22.2.0, like ruby 3.0.3, GraalVM CE JVM [x86_64-darwin]</th>
943
944
  </tr>
data/Rakefile CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "bundler/gem_tasks"
4
4
  require "rake/testtask"
5
+ require "standard/rake"
5
6
 
6
7
  Rake::TestTask.new(:test) do |t|
7
8
  t.libs << "test"
@@ -9,4 +10,4 @@ Rake::TestTask.new(:test) do |t|
9
10
  t.test_files = FileList["test/**/*_test.rb"]
10
11
  end
11
12
 
12
- task default: %i[test]
13
+ task default: %i[test standard]
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PairingHeap
4
- VERSION = "1.0.0"
4
+ VERSION = "2.0.0"
5
5
  end
data/lib/pairing_heap.rb CHANGED
@@ -57,12 +57,12 @@ module PairingHeap
57
57
  end
58
58
 
59
59
  def remove_from_parents_list!
60
- if self.prev_sibling
61
- self.prev_sibling.next_sibling = self.next_sibling
62
- self.next_sibling.prev_sibling = self.prev_sibling if self.next_sibling
63
- elsif self.parent.subheaps.equal?(self)
64
- self.parent.subheaps = self.next_sibling
65
- self.next_sibling.prev_sibling = nil if self.next_sibling
60
+ if prev_sibling
61
+ prev_sibling.next_sibling = next_sibling
62
+ next_sibling.prev_sibling = prev_sibling if next_sibling
63
+ else # parent.subheaps must equal self
64
+ parent.subheaps = next_sibling
65
+ next_sibling.prev_sibling = nil if next_sibling
66
66
  end
67
67
  self.prev_sibling = nil
68
68
  self.next_sibling = nil
@@ -70,7 +70,8 @@ module PairingHeap
70
70
  end
71
71
  private_constant :Node
72
72
 
73
- # @param &block Optional heap property priority comparator. `<:=.to_proc` by default
73
+ # @yield [l_priority, r_priority] Optional heap property priority comparator. `<:=.to_proc` by default
74
+ # @yieldreturn [boolean] if `l_priority` is more prioritary than `r_priority`, or the priorities are equal
74
75
  def initialize(&block)
75
76
  @root = nil
76
77
  @nodes = {}
@@ -83,20 +84,20 @@ module PairingHeap
83
84
  # @param priority Priority of the element
84
85
  # @raise [ArgumentError] if the element is already in the heap
85
86
  # @return [PairingHeap]
86
- def push(elem, priority)
87
+ def push(elem, priority = elem)
87
88
  raise ArgumentError, "Element already in the heap" if @nodes.key?(elem)
88
89
 
89
90
  node = Node.new(elem, priority)
90
91
  @nodes[elem] = node
91
- if @root
92
- @root = meld(@root, node)
92
+ @root = if @root
93
+ meld(@root, node)
93
94
  else
94
- @root = node
95
+ node
95
96
  end
96
97
  self
97
98
  end
98
- alias enqueue push
99
- alias offer push
99
+ alias_method :enqueue, :push
100
+ alias_method :offer, :push
100
101
 
101
102
  # Returns the element at the top of the heap
102
103
  # Time Complexity: O(1)
@@ -104,10 +105,12 @@ module PairingHeap
104
105
  @root&.elem
105
106
  end
106
107
 
108
+ # @return [Object]
107
109
  def peek_priority
108
110
  @root&.priority
109
111
  end
110
112
 
113
+ # @return [Array(Object, Object)]
111
114
  def peek_with_priority
112
115
  [@root&.elem, @root&.priority]
113
116
  end
@@ -129,7 +132,7 @@ module PairingHeap
129
132
  def size
130
133
  @nodes.size
131
134
  end
132
- alias length size
135
+ alias_method :length, :size
133
136
 
134
137
  # Removes element from the top of the heap and returns it
135
138
  # Time Complexity: O(N)
@@ -148,9 +151,17 @@ module PairingHeap
148
151
  end
149
152
  elem
150
153
  end
151
- alias dequeue pop
154
+ alias_method :dequeue, :pop
152
155
 
156
+ # @return [Object]
153
157
  def pop_priority
158
+ node = @root
159
+ pop
160
+ node.priority
161
+ end
162
+
163
+ # @return [Array(Object, Object)]
164
+ def pop_with_priority
154
165
  node = @root
155
166
  pop
156
167
  [node.elem, node.priority]
@@ -213,13 +224,33 @@ module PairingHeap
213
224
  # Returns priority of the provided element
214
225
  # Time Complexity: O(1)
215
226
  # @raise [ArgumentError] if the element is not in the heap
227
+ # @return [Object]
216
228
  def get_priority(elem)
217
229
  node = @nodes[elem]
218
230
  raise ArgumentError, "Provided element is not in heap" if node.nil?
219
231
  node.priority
220
232
  end
221
233
 
234
+ # Returns a pair where first element is success flag, and second element is priority
235
+ # Time Complexity: O(1)
236
+ # @return [Array(false, nil)] if the element is not in heap
237
+ # @return [Array(true, Object)] if the element is in heap;
238
+ # second element of returned tuple is the priority
239
+ def get_priority_if_exists(elem)
240
+ node = @nodes[elem]
241
+ return [false, nil] if node.nil?
242
+ [true, node.priority]
243
+ end
244
+
245
+ # Returns enumerator of elements. No order guarantees are provided.
246
+ # @return [Enumerator]
247
+ def each
248
+ return to_enum(__method__) { size } unless block_given?
249
+ NodeVisitor.visit_node(@root) { |x| yield x.elem }
250
+ end
251
+
222
252
  private
253
+
223
254
  include MergePairs
224
255
 
225
256
  def meld(left, right)
@@ -251,7 +282,8 @@ module PairingHeap
251
282
  end
252
283
  private_constant :Node
253
284
 
254
- # @param &block Optional heap property priority comparator. `<:=.to_proc` by default
285
+ # @yield [l_priority, r_priority] Optional heap property priority comparator. `<:=.to_proc` by default
286
+ # @yieldreturn [boolean] if `l_priority` is more prioritary than `r_priority`, or the priorities are equal
255
287
  def initialize(&block)
256
288
  @root = nil
257
289
  @order = block || :<=.to_proc
@@ -263,18 +295,18 @@ module PairingHeap
263
295
  # @param elem Element to be pushed
264
296
  # @param priority Priority of the element
265
297
  # @return [PairingHeap]
266
- def push(elem, priority)
298
+ def push(elem, priority = elem)
267
299
  node = Node.new(elem, priority)
268
- if @root
269
- @root = meld(@root, node)
300
+ @root = if @root
301
+ meld(@root, node)
270
302
  else
271
- @root = node
303
+ node
272
304
  end
273
305
  @size += 1
274
306
  self
275
307
  end
276
- alias enqueue push
277
- alias offer push
308
+ alias_method :enqueue, :push
309
+ alias_method :offer, :push
278
310
 
279
311
  # Returns the element at the top of the heap
280
312
  # Time Complexity: O(1)
@@ -282,10 +314,12 @@ module PairingHeap
282
314
  @root&.elem
283
315
  end
284
316
 
317
+ # @return [Object]
285
318
  def peek_priority
286
319
  @root&.priority
287
320
  end
288
321
 
322
+ # @return [Array(Object, Object)]
289
323
  def peek_with_priority
290
324
  [@root&.elem, @root&.priority]
291
325
  end
@@ -304,15 +338,13 @@ module PairingHeap
304
338
 
305
339
  # Time Complexity: O(1)
306
340
  # @return [Integer]
307
- def size
308
- @size
309
- end
310
- alias length size
341
+ attr_reader :size
342
+ alias_method :length, :size
311
343
 
312
- # Removes element from the top of the heap and returns it
344
+ # Removes an element from the top of the heap and returns it
313
345
  # Time Complexity: O(N)
314
346
  # Amortized time Complexity: O(log(N))
315
- # @raise [ArgumEntError] if the heap is empty
347
+ # @raise [ArgumentError] if the heap is empty
316
348
  def pop
317
349
  raise ArgumentError, "Cannot remove from an empty heap" if @root.nil?
318
350
  @size -= 1
@@ -323,21 +355,31 @@ module PairingHeap
323
355
 
324
356
  elem
325
357
  end
326
- alias dequeue pop
358
+ alias_method :dequeue, :pop
327
359
 
360
+ # @return [Object]
328
361
  def pop_priority
329
362
  node = @root
330
363
  pop
331
364
  node.priority
332
365
  end
333
366
 
367
+ # @return [Array(Object, Object)]
334
368
  def pop_with_priority
335
369
  node = @root
336
370
  pop
337
371
  [node.elem, node.priority]
338
372
  end
339
373
 
374
+ # Returns enumerator of elements. No order guarantees are provided.
375
+ # @return [Enumerator]
376
+ def each
377
+ return to_enum(__method__) { size } unless block_given?
378
+ NodeVisitor.visit_node(@root) { |x| yield x.elem }
379
+ end
380
+
340
381
  private
382
+
341
383
  include MergePairs
342
384
 
343
385
  def meld(left, right)
@@ -354,16 +396,15 @@ module PairingHeap
354
396
  end
355
397
  end
356
398
 
357
-
358
399
  # Priority queue where the smallest priority is the most prioritary
359
400
  class MinPriorityQueue < PairingHeap
360
401
  def initialize
361
402
  super(&:<=)
362
403
  end
363
404
 
364
- alias decrease_key change_priority
365
- alias min peek
366
- alias extract_min dequeue
405
+ alias_method :decrease_key, :change_priority
406
+ alias_method :min, :peek
407
+ alias_method :extract_min, :dequeue
367
408
  end
368
409
 
369
410
  # Priority queue where the highest priority is the most prioritary
@@ -372,14 +413,14 @@ module PairingHeap
372
413
  super(&:>=)
373
414
  end
374
415
 
375
- alias increase_key change_priority
376
- alias max peek
377
- alias extract_max dequeue
416
+ alias_method :increase_key, :change_priority
417
+ alias_method :max, :peek
418
+ alias_method :extract_max, :dequeue
378
419
  end
379
420
 
380
421
  # Priority queue with change_priority, that accepts changing to a less prioritary priority
381
422
  class SafeChangePriorityQueue < PairingHeap
382
- # Changes a priority of the element to a more prioritary one
423
+ # Changes a priority of the element
383
424
  # Time Complexity: O(N)
384
425
  # Amortized Time Complexity: O(log(N))
385
426
  # @raise [ArgumentError] if the element is not in the heap
@@ -394,4 +435,22 @@ module PairingHeap
394
435
  end
395
436
  end
396
437
  end
438
+
439
+ module NodeVisitor
440
+ extend self
441
+
442
+ def visit_node(node, &block)
443
+ return unless node
444
+
445
+ block.call(node)
446
+
447
+ if node.subheaps
448
+ visit_node(node.subheaps, &block)
449
+ end
450
+ if node.next_sibling
451
+ visit_node(node.next_sibling, &block)
452
+ end
453
+ end
454
+ end
455
+ private_constant :NodeVisitor
397
456
  end
data/pairing_heap.gemspec CHANGED
@@ -3,19 +3,19 @@
3
3
  require_relative "lib/pairing_heap/version"
4
4
 
5
5
  Gem::Specification.new do |spec|
6
- spec.name = "pairing_heap"
7
- spec.version = PairingHeap::VERSION
8
- spec.authors = ["Marcin Henryk Bartkowiak"]
9
- spec.email = ["mhbartkowiak@gmail.com"]
10
-
11
- spec.summary = "Performant priority queue in pure ruby with support for changing priority"
12
- spec.description = "Performant priority queue in pure ruby with support for changing priority using pairing heap data structure"
13
- spec.homepage = "https://github.com/mhib/pairing_heap"
14
- spec.license = "MIT"
6
+ spec.name = "pairing_heap"
7
+ spec.version = PairingHeap::VERSION
8
+ spec.authors = ["Marcin Henryk Bartkowiak"]
9
+ spec.email = ["mhbartkowiak@gmail.com"]
10
+
11
+ spec.summary = "Performant priority queue in pure ruby with support for changing priority"
12
+ spec.description = "Performant priority queue in pure ruby with support for changing priority using pairing heap data structure"
13
+ spec.homepage = "https://github.com/mhib/pairing_heap"
14
+ spec.license = "MIT"
15
15
  spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
16
16
 
17
- spec.metadata["homepage_uri"] = spec.homepage
18
- spec.metadata["source_code_uri"] = spec.homepage
17
+ spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["source_code_uri"] = spec.homepage
19
19
  spec.metadata["documentation_uri"] = "https://rubydoc.info/gems/pairing_heap"
20
20
 
21
21
  # Specify which files should be added to the gem when it is released.
@@ -23,8 +23,8 @@ Gem::Specification.new do |spec|
23
23
  spec.files = Dir.chdir(File.expand_path(__dir__)) do
24
24
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
25
25
  end
26
- spec.bindir = "exe"
27
- spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
26
+ spec.bindir = "exe"
27
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
28
28
  spec.require_paths = ["lib"]
29
29
 
30
30
  # Uncomment to register a new dependency of your gem
@@ -32,6 +32,8 @@ Gem::Specification.new do |spec|
32
32
 
33
33
  spec.add_development_dependency "minitest", "~> 5.0"
34
34
  spec.add_development_dependency "rake", "~> 13.0"
35
+ spec.add_development_dependency "codecov", "0.6.0"
36
+ spec.add_development_dependency "standard", "~> 1.20"
35
37
 
36
38
  # For more information and examples about making a new gem, checkout our
37
39
  # guide at: https://bundler.io/guides/creating_gem.html
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pairing_heap
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marcin Henryk Bartkowiak
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-09-04 00:00:00.000000000 Z
11
+ date: 2022-12-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -38,6 +38,34 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '13.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: codecov
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '='
46
+ - !ruby/object:Gem::Version
47
+ version: 0.6.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.6.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: standard
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.20'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.20'
41
69
  description: Performant priority queue in pure ruby with support for changing priority
42
70
  using pairing heap data structure
43
71
  email:
@@ -48,7 +76,7 @@ extra_rdoc_files: []
48
76
  files:
49
77
  - ".github/workflows/main.yml"
50
78
  - ".gitignore"
51
- - ".rubocop.yml"
79
+ - ".standard.yml"
52
80
  - Gemfile
53
81
  - Gemfile.lock
54
82
  - LICENSE.txt
@@ -81,7 +109,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
81
109
  - !ruby/object:Gem::Version
82
110
  version: '0'
83
111
  requirements: []
84
- rubygems_version: 3.3.3
112
+ rubygems_version: 3.4.1
85
113
  signing_key:
86
114
  specification_version: 4
87
115
  summary: Performant priority queue in pure ruby with support for changing priority
data/.rubocop.yml DELETED
@@ -1,17 +0,0 @@
1
- AllCops:
2
- Exclude:
3
- - 'test/fib.rb'
4
-
5
- Style/InfiniteLoop:
6
- Enabled: false
7
-
8
- Style/StringLiterals:
9
- Enabled: false
10
- EnforcedStyle: double_quotes
11
-
12
- Style/StringLiteralsInInterpolation:
13
- Enabled: true
14
- EnforcedStyle: double_quotes
15
-
16
- Layout/LineLength:
17
- Max: 120