min_max 0.1.5 → 0.1.7

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: 6d739946dcb0939e2e05893222c927f4dea0eda65da5c0a00156402de3e49c29
4
- data.tar.gz: 61b4453a94b93bb2ccefd0787a751cef5d3ef380ac0ed591c73c3b0afddc394b
3
+ metadata.gz: 2b43d258b5b9ccddc81016bb4c69c186df448ab7c51ae712f136ceed6218a145
4
+ data.tar.gz: 8c22dd5c5ac6ced73a789dc6813f62bc51168e3e48e2e2ca1b689187be7af8ac
5
5
  SHA512:
6
- metadata.gz: 70dd4402ca82a60cffbc451ea134ca9fc34391f28311d23726d22055bfc01db9ba62002f0d54c76282888993613f2bf5618dcfa7a964da39c6995747401f3a3f
7
- data.tar.gz: 31ada4f8ed50624701e389c6a1d50b83b75a585ef703d2f18c6511498dc9373647fa1f88d959ccdcbf56336d38dc550e1ce96710644f65970f2341549f843d53
6
+ metadata.gz: b02f4bdcbe8d60e8e2dfba61218a7958e8d97dd54a8939a0befcd363c796e049f11b7f7f58e014c8f8ead40773ea4ad9695fc69a89022e0bec6ac738ae4968ac
7
+ data.tar.gz: b55fc832a26ce35e955e275ea812b64de15f541fea714a57e107f078d287e3436a76a041a6152863ea1b9680282c0864096cb7784f1aaed525cf5b253b989465
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # MinMax Heap
2
2
 
3
- The MinMax Heap gem provides a high-performance minmax heap implementation for Ruby, written in Rust.
3
+ The MinMax Heap gem provides a high-performance minmax heap implementation for Ruby, written in Rust.
4
4
  The gem wraps the excellent [min-max-heap-rs](https://github.com/tov/min-max-heap-rs) Rust library.
5
- It allows for the creation of a min-max-heap and supporting operations like pushing and popping multiple items, iterating over heap items, and converting heaps to arrays.
5
+ It allows for the creation of a min-max-heap and supporting operations like pushing and popping multiple items, iterating over heap items, and converting heaps to arrays.
6
6
 
7
7
  ## Features
8
8
 
@@ -13,9 +13,9 @@ It allows for the creation of a min-max-heap and supporting operations like push
13
13
  - Convert heap to array with `#to_a`, `#to_a_asc` and `#to_a_desc`.
14
14
 
15
15
  ## Prequisites
16
- - You must have a working Rust compiler installed on your system.
16
+ - You must have a working Rust compiler installed on your system.
17
17
  ```bash
18
- curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs |
18
+ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs |
19
19
  ```
20
20
 
21
21
  ## Installation
@@ -79,6 +79,21 @@ heap.to_a_desc # => [5, 3, 1]
79
79
  heap.to_a # => Heap order
80
80
  ```
81
81
 
82
+ ## Iterate in order
83
+ ```ruby
84
+ heap.each_asc #<Enumerator: ...>
85
+ heap.each_desc #<Enumerator: ...>
86
+ ```
87
+
88
+ ## Peek at min and max items
89
+ ```ruby
90
+ heap.peek_min # => 1
91
+ heap.first # => 1
92
+
93
+ heap.peek_max # => 1
94
+ heap.last # => 1
95
+ ```
96
+
82
97
  ## Size
83
98
  ```ruby
84
99
  heap.size # => 4
@@ -100,3 +115,13 @@ heap.count(item)
100
115
  heap.contains?(item)
101
116
  ```
102
117
 
118
+ ## Performance
119
+ You can run the `benchmarks/benchmarks.rb` file inside this repository for comparison to other popular heap libraries:
120
+ * [rb_heap](https://github.com/florian/rb_heap)
121
+ * [algorithms](https://github.com/kanwei/algorithms)
122
+ * [ruby-heap](https://github.com/general-CbIC/ruby-heap)
123
+ * [pqueue](https://github.com/rubyworks/pqueue)
124
+
125
+ min-max should be the fastest to pop from a large heap, often by a significant margin, while also offering both min and max operations from a single heap.
126
+ Some options are faster at pushing individual items, but the difference is within the same order of magnitude.
127
+ Batch pushing to min-max also significantly increases insert speed.
@@ -73,13 +73,13 @@ pqueue = PQueue.new()
73
73
  mm_hp = MinMax[]
74
74
 
75
75
  Benchmark.bm do |x|
76
- x.report("push_algos_heap"){ data.each{|d| pqueue.push(d) } }
76
+ x.report("push_pqueue_heap"){ data.each{|d| pqueue.push(d) } }
77
77
  x.report("push_mm_heap"){ data.each{|d| mm_hp.push(d) } }
78
78
  x.report("push_mm_heap_batches"){ mm_hp = MinMax[]; data.each_slice(1000){|d| mm_hp.push(*d) } }
79
79
  end
80
80
 
81
81
  Benchmark.bm do |x|
82
- x.report("pop_algos_heap"){ 100_000.times{|d| minheap.pop } }
82
+ x.report("pop_pqueue_heap"){ 100_000.times{|d| pqueue.pop } }
83
83
  x.report("pop_mm_heap"){ 100_000.times{|d| mm_hp.pop_min } }
84
84
  x.report("pop_mm_heap_batches"){ 1000.times{|d| mm_hp.pop_min(100) } }
85
85
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class MinMax
4
- VERSION = "0.1.5"
4
+ VERSION = "0.1.7"
5
5
  end
data/lib/min_max.rb CHANGED
@@ -6,7 +6,7 @@ require_relative "min_max/min_max"
6
6
  class MinMax
7
7
  class Error < StandardError; end
8
8
 
9
- attr_reader :priority_blk, :storage, :mtx
9
+ attr_reader :priority_blk, :storage
10
10
 
11
11
  def self.[](*args, &blk)
12
12
  new(*args, &blk)
@@ -16,8 +16,7 @@ class MinMax
16
16
  self._new.tap{|s|
17
17
  s.instance_eval{
18
18
  @priority_blk = (blk || proc{|x| x.respond_to?(:priority) ? x.priority : x.to_i })
19
- @storage = Hash.new{|h,k| h[k] = [0, nil] }
20
- @mtx ||= Mutex.new
19
+ @storage = Hash.new{|h,k| h[k] = [0, nil] }.compare_by_identity
21
20
  }
22
21
  s.push(*args)
23
22
  }
@@ -25,13 +24,13 @@ class MinMax
25
24
 
26
25
  def push(*args)
27
26
  mapped = args.map do |a|
28
- hash = a.hash
29
- entry = self.storage[hash]
27
+ object_id = a.object_id
28
+ entry = self.storage[object_id]
30
29
  entry[0] += 1
31
30
  entry[1] ||= a
32
31
  [
33
32
  (self.priority_blk.call(a) rescue 0),
34
- hash
33
+ object_id
35
34
  ]
36
35
  end
37
36
  _push(mapped)
@@ -51,19 +50,27 @@ class MinMax
51
50
  popped.kind_of?(Array) ? popped.map{|p| retrieve(p) } : retrieve(popped)
52
51
  end
53
52
 
54
- def peek_min(*args)
55
- peeked = _peek_min(*args)
56
- peeked.kind_of?(Array) ? peeked.map{|p| retrieve(p, false) } : retrieve(popped, false)
53
+ def peek_min
54
+ retrieve(_peek_min, false)
57
55
  end
58
56
 
59
- def peek_max(*args)
60
- peeked = _peek_max(*args)
61
- peeked.kind_of?(Array) ? peeked.map{|p| retrieve(p, false) } : retrieve(popped, false)
57
+ def peek_max
58
+ retrieve(_peek_max, false)
59
+ end
60
+
61
+ def first
62
+ peek_min
63
+ end
64
+
65
+ def last
66
+ peek_max
62
67
  end
63
68
 
64
69
  def each(*args, &blk)
65
70
  if block_given?
66
- mtx.synchronize { _each(*args).map{|p| blk[retrieve(p, false)] } }
71
+ _each(*args) do |p|
72
+ blk[retrieve(p, false)]
73
+ end
67
74
  else
68
75
  to_enum(:each, *args)
69
76
  end
@@ -74,31 +81,42 @@ class MinMax
74
81
  end
75
82
 
76
83
  def count(val)
77
- counts.has_key?(val.hash) ? counts[val.hash] : 0
84
+ storage.has_key?(val.object_id) ? storage[val.object_id].first : 0
78
85
  end
79
86
 
80
87
  def contains?(val)
81
- counts.has_key?(val.hash) && counts[val.hash] > 0
88
+ storage.has_key?(val.object_id)
82
89
  end
83
90
 
84
91
  def to_a_asc
85
- mtx.synchronize { _to_a_asc.map{|p| retrieve(p, false) } }
92
+ return to_enum(:to_a_asc) unless block_given?
93
+ _to_a_asc.map{|p| retrieve(p, false) }
86
94
  end
87
95
 
88
96
  def to_a_desc
89
- mtx.synchronize { _to_a_desc.map{|p| retrieve(p, false) } }
97
+ return to_enum(:to_a_desc) unless block_given?
98
+ _to_a_desc.map{|p| retrieve(p, false) }
99
+ end
100
+
101
+ def each_asc
102
+ return to_enum(:each_asc) unless block_given?
103
+ _to_a_asc.each{|p| yield retrieve(p, false) }
104
+ end
105
+
106
+ def each_desc
107
+ return to_enum(:each_desc) unless block_given?
108
+ _to_a_desc.map{|p| retrieve(p, false) }
90
109
  end
91
110
 
92
111
  def inspect
93
- "MinMax[#{_each.first(10).map{|v| retrieve(v, false).to_s }.join(", ")}#{size > 10 ? ", ..." : ""}]"
112
+ "MinMax[#{each.first(10).map(&:to_s).join(", ")}#{size > 10 ? ", ..." : ""}]"
94
113
  end
95
114
 
96
115
  private
97
- def retrieve(hash, remove=true)
98
- entry = self.storage[hash]
99
- self.storage.delete(hash) if remove && (entry[0] -= 1) == 0
116
+ def retrieve(object_id, remove=true)
117
+ entry = self.storage[object_id]
118
+ self.storage.delete(object_id) if remove && (entry[0] -= 1) == 0
100
119
  entry[1]
101
120
  end
102
121
 
103
122
  end
104
-
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: min_max
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Wouter Coppieters
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2024-02-25 00:00:00.000000000 Z
10
+ date: 2025-01-03 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: rb_sys
@@ -79,7 +78,6 @@ licenses:
79
78
  metadata:
80
79
  allowed_push_host: https://rubygems.org
81
80
  homepage_uri: https://github.com/wouterken/min_max
82
- post_install_message:
83
81
  rdoc_options: []
84
82
  require_paths:
85
83
  - lib
@@ -94,8 +92,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
94
92
  - !ruby/object:Gem::Version
95
93
  version: 3.3.11
96
94
  requirements: []
97
- rubygems_version: 3.4.19
98
- signing_key:
95
+ rubygems_version: 3.6.2
99
96
  specification_version: 4
100
97
  summary: A min max heap extension for Ruby
101
98
  test_files: []