min_max 0.1.3 → 0.1.4
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 +4 -4
- data/README.md +18 -3
- data/ext/min_max/src/lib.rs +10 -35
- data/lib/min_max/version.rb +1 -1
- data/lib/min_max.rb +34 -33
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 48b9b64396908ffd54a712e8a8e9b4eb6546e0538a6c168a69d67304dc6fcf1d
|
4
|
+
data.tar.gz: 487b79da6a3a3f99ab408af1feb17ebe33c21c08b7c64b9e20e5b259bcf764bf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '086026e3dfe0851390e4090e700aed60a27eaaf70ae53beb9a50164fd78a024797513994b2bd3c67995b29a655c6c8ca755b19ff42797e12190fae9bac10f4c6'
|
7
|
+
data.tar.gz: b7cebaf1da79607d638628c1ef2d54ff11126902e9c1072d4c2bfd1db883db662d323c9e760134c76941747f447494141122e2a4dad6dd0ddc9a65682bfcfd9f
|
data/README.md
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
-
# MinMax
|
1
|
+
# MinMax Heap
|
2
2
|
|
3
|
-
The MinMax gem provides a high-performance minmax heap implementation for Ruby, written in Rust.
|
4
|
-
|
3
|
+
The MinMax Heap gem provides a high-performance minmax heap implementation for Ruby, written in Rust.
|
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
6
|
|
6
7
|
## Features
|
7
8
|
|
@@ -23,8 +24,13 @@ Add this line to your application's Gemfile:
|
|
23
24
|
|
24
25
|
```ruby
|
25
26
|
gem 'min_max'
|
27
|
+
|
28
|
+
# or manually specify target . E.g.
|
29
|
+
|
30
|
+
CARGO_BUILD_TARGET=x86_64-apple-darwin gem install min_max
|
26
31
|
```
|
27
32
|
|
33
|
+
|
28
34
|
And then execute:
|
29
35
|
|
30
36
|
```bash
|
@@ -84,4 +90,13 @@ heap.length # Alias for size
|
|
84
90
|
heap.clear
|
85
91
|
```
|
86
92
|
|
93
|
+
## Count number of times an item is in the heap
|
94
|
+
```ruby
|
95
|
+
heap.count(item)
|
96
|
+
```
|
97
|
+
|
98
|
+
## Check if item is contained in the heap
|
99
|
+
```ruby
|
100
|
+
heap.contains?(item)
|
101
|
+
```
|
87
102
|
|
data/ext/min_max/src/lib.rs
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
use magnus::scan_args::scan_args;
|
2
|
-
use magnus::value::
|
2
|
+
use magnus::value::ReprValue;
|
3
3
|
use magnus::{
|
4
4
|
block::{block_given, Yield},
|
5
5
|
define_class, function, method,
|
@@ -9,32 +9,11 @@ use magnus::{
|
|
9
9
|
use magnus::{DataTypeFunctions, Integer, TypedData};
|
10
10
|
use min_max_heap::MinMaxHeap;
|
11
11
|
use std::cell::RefCell;
|
12
|
-
use std::
|
13
|
-
use std::sync::{Arc, RwLock};
|
12
|
+
use std::rc::Rc;
|
14
13
|
|
15
14
|
#[derive(Debug, Clone)]
|
16
15
|
struct PriorityOrderableValue(i64, i64);
|
17
16
|
|
18
|
-
#[macro_use]
|
19
|
-
extern crate lazy_static;
|
20
|
-
|
21
|
-
struct MEM(
|
22
|
-
RwLock<HashMap<i64, BoxValue<Value>>>,
|
23
|
-
RwLock<HashMap<i64, RefCell<usize>>>,
|
24
|
-
);
|
25
|
-
|
26
|
-
impl MEM {
|
27
|
-
fn new() -> Self {
|
28
|
-
MEM(RwLock::new(HashMap::new()), RwLock::new(HashMap::new()))
|
29
|
-
}
|
30
|
-
}
|
31
|
-
|
32
|
-
unsafe impl Sync for MEM {}
|
33
|
-
|
34
|
-
lazy_static! {
|
35
|
-
static ref MEMORY: MEM = MEM::new();
|
36
|
-
}
|
37
|
-
|
38
17
|
impl PartialOrd for PriorityOrderableValue {
|
39
18
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
40
19
|
Some(self.cmp(other))
|
@@ -43,9 +22,8 @@ impl PartialOrd for PriorityOrderableValue {
|
|
43
22
|
|
44
23
|
impl Ord for PriorityOrderableValue {
|
45
24
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
46
|
-
|
47
|
-
|
48
|
-
}
|
25
|
+
let (PriorityOrderableValue(p1, _), PriorityOrderableValue(p2, _)) = (self, other);
|
26
|
+
p1.cmp(p2)
|
49
27
|
}
|
50
28
|
}
|
51
29
|
|
@@ -53,11 +31,8 @@ impl Eq for PriorityOrderableValue {}
|
|
53
31
|
|
54
32
|
impl PartialEq for PriorityOrderableValue {
|
55
33
|
fn eq(&self, other: &Self) -> bool {
|
56
|
-
|
57
|
-
|
58
|
-
p1.eq(p2) && (v1).eq(v2)
|
59
|
-
}
|
60
|
-
}
|
34
|
+
let (PriorityOrderableValue(p1, v1), PriorityOrderableValue(p2, v2)) = (self, other);
|
35
|
+
p1.eq(p2) && (v1).eq(v2)
|
61
36
|
}
|
62
37
|
}
|
63
38
|
|
@@ -65,13 +40,13 @@ unsafe impl Send for RubyMinMaxHeap {}
|
|
65
40
|
#[derive(DataTypeFunctions, TypedData, Clone)]
|
66
41
|
#[magnus(class = "MinMax", size, free_immediately, mark)]
|
67
42
|
struct RubyMinMaxHeap {
|
68
|
-
heap:
|
43
|
+
heap: Rc<RefCell<MinMaxHeap<PriorityOrderableValue>>>,
|
69
44
|
}
|
70
45
|
|
71
46
|
impl RubyMinMaxHeap {
|
72
47
|
fn new() -> Result<Self, Error> {
|
73
48
|
Ok(RubyMinMaxHeap {
|
74
|
-
heap:
|
49
|
+
heap: Rc::new(RefCell::new(MinMaxHeap::new())),
|
75
50
|
})
|
76
51
|
}
|
77
52
|
|
@@ -213,8 +188,8 @@ impl RubyMinMaxHeap {
|
|
213
188
|
Ok(ary)
|
214
189
|
}
|
215
190
|
|
216
|
-
fn clear(&self)
|
217
|
-
|
191
|
+
fn clear(&self) {
|
192
|
+
self.heap.borrow_mut().clear()
|
218
193
|
}
|
219
194
|
}
|
220
195
|
|
data/lib/min_max/version.rb
CHANGED
data/lib/min_max.rb
CHANGED
@@ -6,40 +6,31 @@ require_relative "min_max/min_max"
|
|
6
6
|
class MinMax
|
7
7
|
class Error < StandardError; end
|
8
8
|
|
9
|
+
attr_reader :priority_blk, :storage, :counts, :mtx
|
10
|
+
|
9
11
|
def self.[](*args, &blk)
|
10
12
|
new(*args, &blk)
|
11
13
|
end
|
12
14
|
|
13
15
|
def self.new(*args, &blk)
|
14
16
|
self._new.tap{|s|
|
15
|
-
s.
|
17
|
+
s.instance_eval{
|
18
|
+
@priority_blk = (blk || proc{|x| (x.respond_to?(:priority) ? x.priority : x.to_i ) rescue 0})
|
19
|
+
@storage = Hash.new
|
20
|
+
@counts = Hash.new(0)
|
21
|
+
@mtx ||= Mutex.new
|
22
|
+
}
|
16
23
|
s.push(*args)
|
17
24
|
}
|
18
25
|
end
|
19
26
|
|
20
|
-
def priority_blk=(bll)
|
21
|
-
@priority_blk = bll
|
22
|
-
end
|
23
|
-
|
24
|
-
def storage
|
25
|
-
@storage ||= Hash.new
|
26
|
-
end
|
27
|
-
|
28
|
-
def counts
|
29
|
-
@counts ||= Hash.new(0)
|
30
|
-
end
|
31
|
-
|
32
|
-
def mtx
|
33
|
-
@_mtx ||= Mutex.new
|
34
|
-
end
|
35
|
-
|
36
27
|
def push(*args)
|
37
28
|
mtx.synchronize do
|
38
29
|
mapped = args.map do |a|
|
39
|
-
counts[a.hash] += 1
|
40
|
-
storage[a.hash] = a
|
30
|
+
self.counts[a.hash] += 1
|
31
|
+
self.storage[a.hash] = a
|
41
32
|
[
|
42
|
-
|
33
|
+
self.priority_blk.call(a),
|
43
34
|
a.hash
|
44
35
|
]
|
45
36
|
end
|
@@ -66,18 +57,6 @@ class MinMax
|
|
66
57
|
}
|
67
58
|
end
|
68
59
|
|
69
|
-
def retrieve(hash, remove=true)
|
70
|
-
if remove
|
71
|
-
if (counts[hash] -= 1) == 0
|
72
|
-
storage.delete(hash)
|
73
|
-
else
|
74
|
-
storage[hash]
|
75
|
-
end
|
76
|
-
else
|
77
|
-
storage[hash]
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
60
|
def peek_min(*args)
|
82
61
|
peeked = _peek_min(*args)
|
83
62
|
peeked.kind_of?(Array) ? peeked.map{|p| retrieve(p, false) } : retrieve(popped, false)
|
@@ -100,6 +79,14 @@ class MinMax
|
|
100
79
|
each.to_a
|
101
80
|
end
|
102
81
|
|
82
|
+
def count(val)
|
83
|
+
counts.has_key?(val.hash) ? counts[val.hash] : 0
|
84
|
+
end
|
85
|
+
|
86
|
+
def contains?(val)
|
87
|
+
counts.has_key?(val.hash) && counts[val.hash] > 0
|
88
|
+
end
|
89
|
+
|
103
90
|
def to_a_asc
|
104
91
|
mtx.synchronize { _to_a_asc.map{|p| retrieve(p, false) } }
|
105
92
|
end
|
@@ -109,7 +96,21 @@ class MinMax
|
|
109
96
|
end
|
110
97
|
|
111
98
|
def inspect
|
112
|
-
"MinMax[#{
|
99
|
+
"MinMax[#{_each.first(10).map{|v| retrieve(v, false).to_s }.join(", ")}#{size > 10 ? ", ..." : ""}]"
|
113
100
|
end
|
101
|
+
|
102
|
+
private
|
103
|
+
def retrieve(hash, remove=true)
|
104
|
+
if remove
|
105
|
+
if (self.counts[hash] -= 1) == 0
|
106
|
+
self.storage.delete(hash)
|
107
|
+
else
|
108
|
+
self.storage[hash]
|
109
|
+
end
|
110
|
+
else
|
111
|
+
self.storage[hash]
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
114
115
|
end
|
115
116
|
|