pairing_heap 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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