min_max 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 1111038380ae3a10c7d27e970e3f689243adaf93a0d56341f7838a8edb6e70df
4
+ data.tar.gz: 9cfcbcd91ff7676988c18f3abff722a28f2ca22f1f5e78daaf55e090219db37b
5
+ SHA512:
6
+ metadata.gz: dae2da6ad5bb703b5aca311f80b9af0254c8adc746e406870600a4f343892225edc2a8dd598de08b407926a23bd053eecca900c7cf4265679af4158ac112cbe1
7
+ data.tar.gz: 264f99dd737e0e0a3592c94c9007d03f5fd7cc3838dd91a9ca479bcb79faf2ea10276bce9dad98b9ccd1b2fca12a097dee90683fcf6671006f58c7f753d2c9bd
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --require spec_helper
data/Cargo.lock ADDED
@@ -0,0 +1,304 @@
1
+ # This file is automatically @generated by Cargo.
2
+ # It is not intended for manual editing.
3
+ version = 3
4
+
5
+ [[package]]
6
+ name = "aho-corasick"
7
+ version = "1.0.4"
8
+ source = "registry+https://github.com/rust-lang/crates.io-index"
9
+ checksum = "6748e8def348ed4d14996fa801f4122cd763fff530258cdc03f64b25f89d3a5a"
10
+ dependencies = [
11
+ "memchr",
12
+ ]
13
+
14
+ [[package]]
15
+ name = "bindgen"
16
+ version = "0.66.1"
17
+ source = "registry+https://github.com/rust-lang/crates.io-index"
18
+ checksum = "f2b84e06fc203107bfbad243f4aba2af864eb7db3b1cf46ea0a023b0b433d2a7"
19
+ dependencies = [
20
+ "bitflags",
21
+ "cexpr",
22
+ "clang-sys",
23
+ "lazy_static",
24
+ "lazycell",
25
+ "peeking_take_while",
26
+ "proc-macro2",
27
+ "quote",
28
+ "regex",
29
+ "rustc-hash",
30
+ "shlex",
31
+ "syn",
32
+ ]
33
+
34
+ [[package]]
35
+ name = "bitflags"
36
+ version = "2.4.0"
37
+ source = "registry+https://github.com/rust-lang/crates.io-index"
38
+ checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
39
+
40
+ [[package]]
41
+ name = "cexpr"
42
+ version = "0.6.0"
43
+ source = "registry+https://github.com/rust-lang/crates.io-index"
44
+ checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
45
+ dependencies = [
46
+ "nom",
47
+ ]
48
+
49
+ [[package]]
50
+ name = "cfg-if"
51
+ version = "1.0.0"
52
+ source = "registry+https://github.com/rust-lang/crates.io-index"
53
+ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
54
+
55
+ [[package]]
56
+ name = "clang-sys"
57
+ version = "1.6.1"
58
+ source = "registry+https://github.com/rust-lang/crates.io-index"
59
+ checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f"
60
+ dependencies = [
61
+ "glob",
62
+ "libc",
63
+ "libloading",
64
+ ]
65
+
66
+ [[package]]
67
+ name = "glob"
68
+ version = "0.3.1"
69
+ source = "registry+https://github.com/rust-lang/crates.io-index"
70
+ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
71
+
72
+ [[package]]
73
+ name = "lazy_static"
74
+ version = "1.4.0"
75
+ source = "registry+https://github.com/rust-lang/crates.io-index"
76
+ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
77
+
78
+ [[package]]
79
+ name = "lazycell"
80
+ version = "1.3.0"
81
+ source = "registry+https://github.com/rust-lang/crates.io-index"
82
+ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
83
+
84
+ [[package]]
85
+ name = "libc"
86
+ version = "0.2.153"
87
+ source = "registry+https://github.com/rust-lang/crates.io-index"
88
+ checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
89
+
90
+ [[package]]
91
+ name = "libloading"
92
+ version = "0.7.4"
93
+ source = "registry+https://github.com/rust-lang/crates.io-index"
94
+ checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
95
+ dependencies = [
96
+ "cfg-if",
97
+ "winapi",
98
+ ]
99
+
100
+ [[package]]
101
+ name = "magnus"
102
+ version = "0.6.1"
103
+ source = "registry+https://github.com/rust-lang/crates.io-index"
104
+ checksum = "0516897a45f8ce8270a8910bcb94cd83538b19b6ae3a0c281a765df170b64695"
105
+ dependencies = [
106
+ "magnus-macros",
107
+ "rb-sys",
108
+ "rb-sys-env",
109
+ "seq-macro",
110
+ ]
111
+
112
+ [[package]]
113
+ name = "magnus-macros"
114
+ version = "0.6.0"
115
+ source = "registry+https://github.com/rust-lang/crates.io-index"
116
+ checksum = "5968c820e2960565f647819f5928a42d6e874551cab9d88d75e3e0660d7f71e3"
117
+ dependencies = [
118
+ "proc-macro2",
119
+ "quote",
120
+ "syn",
121
+ ]
122
+
123
+ [[package]]
124
+ name = "memchr"
125
+ version = "2.5.0"
126
+ source = "registry+https://github.com/rust-lang/crates.io-index"
127
+ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
128
+
129
+ [[package]]
130
+ name = "min-max-heap"
131
+ version = "1.3.0"
132
+ source = "registry+https://github.com/rust-lang/crates.io-index"
133
+ checksum = "2687e6cf9c00f48e9284cf9fd15f2ef341d03cc7743abf9df4c5f07fdee50b18"
134
+
135
+ [[package]]
136
+ name = "min_max"
137
+ version = "0.1.0"
138
+ dependencies = [
139
+ "lazy_static",
140
+ "magnus",
141
+ "min-max-heap",
142
+ ]
143
+
144
+ [[package]]
145
+ name = "minimal-lexical"
146
+ version = "0.2.1"
147
+ source = "registry+https://github.com/rust-lang/crates.io-index"
148
+ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
149
+
150
+ [[package]]
151
+ name = "nom"
152
+ version = "7.1.3"
153
+ source = "registry+https://github.com/rust-lang/crates.io-index"
154
+ checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
155
+ dependencies = [
156
+ "memchr",
157
+ "minimal-lexical",
158
+ ]
159
+
160
+ [[package]]
161
+ name = "peeking_take_while"
162
+ version = "0.1.2"
163
+ source = "registry+https://github.com/rust-lang/crates.io-index"
164
+ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
165
+
166
+ [[package]]
167
+ name = "proc-macro2"
168
+ version = "1.0.66"
169
+ source = "registry+https://github.com/rust-lang/crates.io-index"
170
+ checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
171
+ dependencies = [
172
+ "unicode-ident",
173
+ ]
174
+
175
+ [[package]]
176
+ name = "quote"
177
+ version = "1.0.33"
178
+ source = "registry+https://github.com/rust-lang/crates.io-index"
179
+ checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
180
+ dependencies = [
181
+ "proc-macro2",
182
+ ]
183
+
184
+ [[package]]
185
+ name = "rb-sys"
186
+ version = "0.9.81"
187
+ source = "registry+https://github.com/rust-lang/crates.io-index"
188
+ checksum = "a57240b308b155b09dce81e32829966a99f52d1088b45957e4283e526c5317a1"
189
+ dependencies = [
190
+ "rb-sys-build",
191
+ ]
192
+
193
+ [[package]]
194
+ name = "rb-sys-build"
195
+ version = "0.9.81"
196
+ source = "registry+https://github.com/rust-lang/crates.io-index"
197
+ checksum = "f24ce877a4c5d07f06f6aa6fec3ac95e4b357b9f73b0f5445d8cbb7266d410e8"
198
+ dependencies = [
199
+ "bindgen",
200
+ "lazy_static",
201
+ "proc-macro2",
202
+ "quote",
203
+ "regex",
204
+ "shell-words",
205
+ "syn",
206
+ ]
207
+
208
+ [[package]]
209
+ name = "rb-sys-env"
210
+ version = "0.1.2"
211
+ source = "registry+https://github.com/rust-lang/crates.io-index"
212
+ checksum = "a35802679f07360454b418a5d1735c89716bde01d35b1560fc953c1415a0b3bb"
213
+
214
+ [[package]]
215
+ name = "regex"
216
+ version = "1.9.3"
217
+ source = "registry+https://github.com/rust-lang/crates.io-index"
218
+ checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a"
219
+ dependencies = [
220
+ "aho-corasick",
221
+ "memchr",
222
+ "regex-automata",
223
+ "regex-syntax",
224
+ ]
225
+
226
+ [[package]]
227
+ name = "regex-automata"
228
+ version = "0.3.6"
229
+ source = "registry+https://github.com/rust-lang/crates.io-index"
230
+ checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69"
231
+ dependencies = [
232
+ "aho-corasick",
233
+ "memchr",
234
+ "regex-syntax",
235
+ ]
236
+
237
+ [[package]]
238
+ name = "regex-syntax"
239
+ version = "0.7.4"
240
+ source = "registry+https://github.com/rust-lang/crates.io-index"
241
+ checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2"
242
+
243
+ [[package]]
244
+ name = "rustc-hash"
245
+ version = "1.1.0"
246
+ source = "registry+https://github.com/rust-lang/crates.io-index"
247
+ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
248
+
249
+ [[package]]
250
+ name = "seq-macro"
251
+ version = "0.3.5"
252
+ source = "registry+https://github.com/rust-lang/crates.io-index"
253
+ checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4"
254
+
255
+ [[package]]
256
+ name = "shell-words"
257
+ version = "1.1.0"
258
+ source = "registry+https://github.com/rust-lang/crates.io-index"
259
+ checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
260
+
261
+ [[package]]
262
+ name = "shlex"
263
+ version = "1.1.0"
264
+ source = "registry+https://github.com/rust-lang/crates.io-index"
265
+ checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3"
266
+
267
+ [[package]]
268
+ name = "syn"
269
+ version = "2.0.29"
270
+ source = "registry+https://github.com/rust-lang/crates.io-index"
271
+ checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a"
272
+ dependencies = [
273
+ "proc-macro2",
274
+ "quote",
275
+ "unicode-ident",
276
+ ]
277
+
278
+ [[package]]
279
+ name = "unicode-ident"
280
+ version = "1.0.11"
281
+ source = "registry+https://github.com/rust-lang/crates.io-index"
282
+ checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
283
+
284
+ [[package]]
285
+ name = "winapi"
286
+ version = "0.3.9"
287
+ source = "registry+https://github.com/rust-lang/crates.io-index"
288
+ checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
289
+ dependencies = [
290
+ "winapi-i686-pc-windows-gnu",
291
+ "winapi-x86_64-pc-windows-gnu",
292
+ ]
293
+
294
+ [[package]]
295
+ name = "winapi-i686-pc-windows-gnu"
296
+ version = "0.4.0"
297
+ source = "registry+https://github.com/rust-lang/crates.io-index"
298
+ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
299
+
300
+ [[package]]
301
+ name = "winapi-x86_64-pc-windows-gnu"
302
+ version = "0.4.0"
303
+ source = "registry+https://github.com/rust-lang/crates.io-index"
304
+ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
data/Cargo.toml ADDED
@@ -0,0 +1,12 @@
1
+ # This Cargo.toml is here to let externals tools (IDEs, etc.) know that this is
2
+ # a Rust project. Your extensions dependencies should be added to the Cargo.toml
3
+ # in the ext/ directory.
4
+
5
+ [workspace]
6
+ members = ["./ext/min_max"]
7
+ resolver = "2"
8
+
9
+ [profile.release]
10
+ opt-level = 3
11
+ lto = "fat"
12
+ codegen-units = 1
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2023 Wouter Coppieters
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,87 @@
1
+ # MinMax
2
+
3
+ The MinMax gem provides a high-performance minmax heap implementation for Ruby, written in Rust.
4
+ This gem 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
+ ## Features
7
+
8
+ - MinMax heap implementation.
9
+ - Sorts according to #priority or #to_i by default. Can be overridden using a custom priority block.
10
+ - Efficient push and pop operations for single or multiple items.
11
+ - Iteration support with `#each`.
12
+ - Convert heap to array with `#to_a`, `#to_a_asc` and `#to_a_desc`.
13
+
14
+ ## Prequisites
15
+ - You must have a working Rust compiler installed on your system.
16
+ ```bash
17
+ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs |
18
+ ```
19
+
20
+ ## Installation
21
+
22
+ Add this line to your application's Gemfile:
23
+
24
+ ```ruby
25
+ gem 'min_max'
26
+ ```
27
+
28
+ And then execute:
29
+
30
+ ```bash
31
+ bundle install
32
+ ```
33
+
34
+ Or install it yourself as:
35
+ ```bash
36
+ gem install min_max
37
+ ```
38
+
39
+ ## Usage
40
+
41
+ Instantiate a heap
42
+ ```ruby
43
+ heap = MinMax.new
44
+ # Alternate syntax
45
+ heap = MinMax[6,3,2,6]
46
+
47
+ # Custom priority block
48
+ heap = MinMax.new{|x| -x }
49
+ heap = MinMax[1,4,5,-43]{|x| -x }
50
+
51
+ ```
52
+
53
+ ### Adding items
54
+ ```ruby
55
+ heap.push(5, 3, 7, 1)
56
+ ```
57
+
58
+ ### Popping items
59
+ ```ruby
60
+ heap.pop_max # => 7
61
+ heap.pop_min(3) # => [1, 3, 5]
62
+ ```
63
+
64
+ ## Iterating over heap items
65
+ ```ruby
66
+ heap.each.with_index { |item, i| puts "#{i}: #{item}" }
67
+ ```
68
+
69
+ ## To array
70
+ ```ruby
71
+ heap.to_a_asc # => [1, 3, 5]
72
+ heap.to_a_desc # => [5, 3, 1]
73
+ heap.to_a # => Heap order
74
+ ```
75
+
76
+ ## Size
77
+ ```ruby
78
+ heap.size # => 4
79
+ heap.length # Alias for size
80
+ ```
81
+
82
+ ## Clear
83
+ ```ruby
84
+ heap.clear
85
+ ```
86
+
87
+
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rb_sys/extensiontask"
5
+
6
+ task build: :compile
7
+
8
+ RbSys::ExtensionTask.new("min_max") do |ext|
9
+ ext.lib_dir = "lib/min_max"
10
+ end
11
+
12
+ task default: :compile
@@ -0,0 +1,15 @@
1
+ [package]
2
+ name = "min_max"
3
+ version = "0.1.0"
4
+ edition = "2021"
5
+ authors = ["Wouter Coppieters <wc@pico.net.nz>"]
6
+ license = "MIT"
7
+ publish = false
8
+
9
+ [lib]
10
+ crate-type = ["cdylib"]
11
+
12
+ [dependencies]
13
+ magnus = { version = "0.6.1" }
14
+ min-max-heap = "1.3.0"
15
+ lazy_static = "1.4.0"
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mkmf"
4
+ require "rb_sys/mkmf"
5
+
6
+ create_rust_makefile("min_max/min_max")
@@ -0,0 +1,324 @@
1
+ use magnus::block::Proc;
2
+ use magnus::scan_args::scan_args;
3
+ use magnus::value::{BoxValue, ReprValue};
4
+ use magnus::{
5
+ block::{block_given, Yield},
6
+ define_class, function, method,
7
+ prelude::*,
8
+ Error, IntoValue, RArray, Ruby, Value,
9
+ };
10
+ use magnus::{eval, DataTypeFunctions, RHash, RString, TypedData};
11
+ use min_max_heap::MinMaxHeap;
12
+ use std::cell::RefCell;
13
+ use std::collections::HashMap;
14
+ use std::sync::{Arc, RwLock};
15
+
16
+ #[derive(Debug, Clone)]
17
+ struct PriorityOrderableValue(i64, usize);
18
+
19
+ #[macro_use]
20
+ extern crate lazy_static;
21
+
22
+ struct MEM(
23
+ RwLock<HashMap<usize, BoxValue<Value>>>,
24
+ RwLock<HashMap<usize, RefCell<usize>>>,
25
+ );
26
+
27
+ impl MEM {
28
+ fn new() -> Self {
29
+ MEM(RwLock::new(HashMap::new()), RwLock::new(HashMap::new()))
30
+ }
31
+ }
32
+
33
+ unsafe impl Sync for MEM {}
34
+
35
+ lazy_static! {
36
+ static ref MEMORY: MEM = MEM::new();
37
+ }
38
+
39
+ fn get_rb_obj(key: usize) -> Option<Value> {
40
+ let binding = (*MEMORY).0.read().unwrap();
41
+ let v = binding.get(&key).unwrap();
42
+ Some(**v)
43
+ }
44
+
45
+ fn pop_rb_obj(key: usize) -> Option<Value> {
46
+ let binding = (*MEMORY).1.read().unwrap();
47
+ let mut count = binding.get(&key).unwrap().borrow_mut();
48
+ if *count == 0 {
49
+ return None;
50
+ }
51
+ *count -= 1;
52
+ let res = if *count == 0 {
53
+ let val = (*MEMORY).0.write().unwrap().remove(&key).unwrap();
54
+ Some(*val)
55
+ } else {
56
+ let binding = (*MEMORY).0.read().unwrap();
57
+ let v = binding.get(&key).unwrap();
58
+ Some(**v)
59
+ };
60
+ res
61
+ }
62
+
63
+ fn push_rb_obj(key: usize, value: Value) -> usize {
64
+ let mut binding = (*MEMORY).1.write().unwrap();
65
+ let mut count = binding.entry(key).or_insert(RefCell::new(0)).borrow_mut();
66
+
67
+ *count += 1;
68
+ if *count == 1 {
69
+ (*MEMORY)
70
+ .0
71
+ .write()
72
+ .unwrap()
73
+ .insert(key, BoxValue::new(value));
74
+ };
75
+ return key;
76
+ }
77
+
78
+ impl PartialOrd for PriorityOrderableValue {
79
+ fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
80
+ Some(self.cmp(other))
81
+ }
82
+ }
83
+
84
+ impl Ord for PriorityOrderableValue {
85
+ fn cmp(&self, other: &Self) -> std::cmp::Ordering {
86
+ match (self, other) {
87
+ (PriorityOrderableValue(p1, _), PriorityOrderableValue(p2, _)) => p1.cmp(p2),
88
+ }
89
+ }
90
+ }
91
+
92
+ impl Eq for PriorityOrderableValue {}
93
+
94
+ impl PartialEq for PriorityOrderableValue {
95
+ fn eq(&self, other: &Self) -> bool {
96
+ match (self, other) {
97
+ (PriorityOrderableValue(p1, v1), PriorityOrderableValue(p2, v2)) => {
98
+ let v1_obj = get_rb_obj(*v1).unwrap();
99
+ let v2_obj = get_rb_obj(*v2).unwrap();
100
+ p1.eq(p2) && (v1_obj).eql(v2_obj).unwrap_or_default()
101
+ }
102
+ }
103
+ }
104
+ }
105
+
106
+ unsafe impl Send for RubyMinMaxHeap {}
107
+ #[derive(DataTypeFunctions, TypedData, Clone)]
108
+ #[magnus(class = "MinMax", size, free_immediately, mark)]
109
+ struct RubyMinMaxHeap {
110
+ heap: Arc<RefCell<MinMaxHeap<PriorityOrderableValue>>>,
111
+ priority_proc: Arc<Proc>,
112
+ }
113
+
114
+ impl RubyMinMaxHeap {
115
+ fn new(args: &[Value]) -> Result<Self, Error> {
116
+ let args = scan_args::<(), (), (), (), RHash, Option<Proc>>(args)?;
117
+ let prc: Option<Proc> = args.block;
118
+
119
+ Ok(RubyMinMaxHeap {
120
+ heap: Arc::new(RefCell::new(MinMaxHeap::new())),
121
+ priority_proc: prc
122
+ .unwrap_or_else(|| {
123
+ let value: Value =
124
+ eval("Proc.new {|x| x.respond_to?(:priority) ? x.priority : x.to_i }")
125
+ .unwrap();
126
+ Proc::from_value(value).unwrap()
127
+ })
128
+ .into(),
129
+ })
130
+ }
131
+
132
+ fn push(&self, value: &[Value]) -> Result<(), Error> {
133
+ let mut hp = self.heap.borrow_mut();
134
+ value.iter().for_each(|v| {
135
+ let priority: i64 = (*self.priority_proc).call([*v]).unwrap_or_default();
136
+ let key = v.hash().unwrap().to_usize().unwrap();
137
+ hp.push(PriorityOrderableValue(priority, push_rb_obj(key, *v)));
138
+ });
139
+ Ok(())
140
+ }
141
+
142
+ fn peek_max(&self) -> Result<Option<Value>, Error> {
143
+ let hp = self.heap.borrow();
144
+ match hp.peek_max() {
145
+ Some(PriorityOrderableValue(_, value)) => Ok(get_rb_obj(*value)),
146
+ _ => Ok(None),
147
+ }
148
+ }
149
+
150
+ fn peek_min(&self) -> Result<Option<Value>, Error> {
151
+ let hp = self.heap.borrow();
152
+ match hp.peek_min() {
153
+ Some(PriorityOrderableValue(_, value)) => Ok(get_rb_obj(*value)),
154
+ _ => Ok(None),
155
+ }
156
+ }
157
+
158
+ fn pop_max(&self, args: &[Value]) -> Result<Option<Value>, Error> {
159
+ let args = scan_args::<(), (Option<i32>,), (), (), (), ()>(args)?;
160
+ let (count,): (Option<i32>,) = args.optional;
161
+
162
+ if let Some(c) = count {
163
+ let mut result = vec![];
164
+ for _ in 0..c {
165
+ match { self.heap.borrow_mut() }.pop_max() {
166
+ Some(PriorityOrderableValue(_, value)) => {
167
+ result.push(pop_rb_obj(value).unwrap())
168
+ }
169
+ _ => break,
170
+ }
171
+ }
172
+ let ary = RArray::new();
173
+ ary.cat(&result)?;
174
+ Ok(Some(ary.into_value()))
175
+ } else {
176
+ let mut hp = self.heap.borrow_mut();
177
+ let val = hp.pop_max();
178
+ match val {
179
+ Some(PriorityOrderableValue(_, value)) => Ok(pop_rb_obj(value)),
180
+ _ => Ok(None),
181
+ }
182
+ }
183
+ }
184
+
185
+ fn pop_min(&self, args: &[Value]) -> Result<Option<Value>, Error> {
186
+ let args = scan_args::<(), (Option<i32>,), (), (), (), ()>(args)?;
187
+ let (count,): (Option<i32>,) = args.optional;
188
+
189
+ if let Some(c) = count {
190
+ let mut result = vec![];
191
+ for _ in 0..c {
192
+ match { self.heap.borrow_mut().pop_min() } {
193
+ Some(PriorityOrderableValue(_, value)) => {
194
+ result.push(pop_rb_obj(value).unwrap())
195
+ }
196
+ _ => break,
197
+ }
198
+ }
199
+ let ary = RArray::new();
200
+ ary.cat(&result)?;
201
+ Ok(Some(ary.into_value()))
202
+ } else {
203
+ match { self.heap.borrow_mut().pop_min() } {
204
+ Some(PriorityOrderableValue(_, value)) => Ok(pop_rb_obj(value)),
205
+ _ => Ok(None),
206
+ }
207
+ }
208
+ }
209
+
210
+ fn is_empty(&self) -> bool {
211
+ self.heap.borrow().is_empty()
212
+ }
213
+
214
+ fn size(&self) -> usize {
215
+ self.heap.borrow().len()
216
+ }
217
+
218
+ fn each(&self) -> Yield<Box<dyn Iterator<Item = Value>>> {
219
+ if block_given() {
220
+ let iter = self
221
+ .heap
222
+ .borrow()
223
+ .clone()
224
+ .into_vec()
225
+ .into_iter()
226
+ .map(|orderable_value| match orderable_value {
227
+ PriorityOrderableValue(_, value) => get_rb_obj(value).unwrap(),
228
+ });
229
+ Yield::Iter(Box::new(iter))
230
+ } else {
231
+ Yield::Enumerator(self.clone().into_value().enumeratorize("each", ()))
232
+ }
233
+ }
234
+
235
+ fn to_a(&self) -> Result<RArray, Error> {
236
+ let each: Value = self.clone().into_value().funcall("each", ())?;
237
+ Ok(each.funcall("to_a", ())?)
238
+ }
239
+
240
+ fn inspect(&self) -> Result<RString, Error> {
241
+ let mut visited = std::collections::HashSet::new();
242
+ Ok(self.build_inspect(&mut visited)?)
243
+ }
244
+
245
+ fn build_inspect(
246
+ &self,
247
+ visited: &mut std::collections::HashSet<*const Self>,
248
+ ) -> Result<RString, Error> {
249
+ // Check for circular reference
250
+ if !visited.insert(self as *const Self) {
251
+ return Ok(RString::new("[Circular Reference Detected]"));
252
+ }
253
+
254
+ let to_a: Value = self.to_a()?.into_value();
255
+ let r: String = match to_a.funcall("inspect", ()) {
256
+ Ok(inspect_value) => inspect_value,
257
+ Err(_) => return Ok(RString::new("[Error inspecting array]")),
258
+ };
259
+
260
+ // Ensure the current object is removed from the visited set to allow re-visiting in future calls
261
+ visited.remove(&(self as *const Self));
262
+
263
+ Ok(RString::new(&format!("MinMax{}", r)))
264
+ }
265
+
266
+ fn to_a_asc(&self) -> Result<RArray, Error> {
267
+ let ary = RArray::new();
268
+
269
+ let sorted: Vec<Value> = self
270
+ .heap
271
+ .borrow()
272
+ .clone()
273
+ .into_vec_asc()
274
+ .iter()
275
+ .map(|orderable_value| match orderable_value {
276
+ PriorityOrderableValue(_, value) => get_rb_obj(*value).unwrap(),
277
+ })
278
+ .collect();
279
+ ary.cat(&sorted)?;
280
+ Ok(ary)
281
+ }
282
+
283
+ fn to_a_desc(&self) -> Result<RArray, Error> {
284
+ let ary = RArray::new();
285
+
286
+ let sorted: Vec<Value> = self
287
+ .heap
288
+ .borrow()
289
+ .clone()
290
+ .into_vec_desc()
291
+ .iter()
292
+ .map(|orderable_value| match orderable_value {
293
+ PriorityOrderableValue(_, value) => get_rb_obj(*value).unwrap(),
294
+ })
295
+ .collect();
296
+ ary.cat(&sorted)?;
297
+ Ok(ary)
298
+ }
299
+
300
+ fn clear(&self) -> Option<()> {
301
+ Some(self.heap.borrow_mut().clear())
302
+ }
303
+ }
304
+
305
+ #[magnus::init]
306
+ fn init(ruby: &Ruby) -> Result<(), Error> {
307
+ let rb_c_min_max = define_class("MinMax", ruby.class_object())?;
308
+ rb_c_min_max.define_singleton_method("new", function!(RubyMinMaxHeap::new, -1))?;
309
+ rb_c_min_max.define_method("_push", method!(RubyMinMaxHeap::push, -1))?;
310
+ rb_c_min_max.define_method("_pop_max", method!(RubyMinMaxHeap::pop_max, -1))?;
311
+ rb_c_min_max.define_method("_pop_min", method!(RubyMinMaxHeap::pop_min, -1))?;
312
+ rb_c_min_max.define_method("empty?", method!(RubyMinMaxHeap::is_empty, 0))?;
313
+ rb_c_min_max.define_method("each", method!(RubyMinMaxHeap::each, 0))?;
314
+ rb_c_min_max.define_method("peek_min", method!(RubyMinMaxHeap::peek_min, 0))?;
315
+ rb_c_min_max.define_method("peek_max", method!(RubyMinMaxHeap::peek_max, 0))?;
316
+ rb_c_min_max.define_method("to_a", method!(RubyMinMaxHeap::to_a, 0))?;
317
+ rb_c_min_max.define_method("to_a_asc", method!(RubyMinMaxHeap::to_a_asc, 0))?;
318
+ rb_c_min_max.define_method("to_a_desc", method!(RubyMinMaxHeap::to_a_desc, 0))?;
319
+ rb_c_min_max.define_method("clear", method!(RubyMinMaxHeap::clear, 0))?;
320
+ rb_c_min_max.define_method("size", method!(RubyMinMaxHeap::size, 0))?;
321
+ rb_c_min_max.define_method("length", method!(RubyMinMaxHeap::size, 0))?;
322
+ rb_c_min_max.define_method("inspect", method!(RubyMinMaxHeap::inspect, 0))?;
323
+ Ok(())
324
+ }
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MinMax
4
+ VERSION = "0.1.0"
5
+
6
+ end
data/lib/min_max.rb ADDED
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "min_max/version"
4
+ require_relative "min_max/min_max"
5
+
6
+ class MinMax
7
+ class Error < StandardError; end
8
+
9
+ def self.[](*args, &blk)
10
+ new(&blk).tap{|s| s.push(*args) }
11
+ end
12
+
13
+ def mtx
14
+ @_mtx ||= Mutex.new
15
+ end
16
+
17
+ def push(*args)
18
+ mtx.synchronize { _push(*args) }
19
+ end
20
+
21
+ def add(*args)
22
+ mtx.synchronize { _push(*args) }
23
+ end
24
+
25
+ def pop_max(*args)
26
+ mtx.synchronize { _pop_max(*args) }
27
+ end
28
+
29
+ def pop_min(*args)
30
+ mtx.synchronize { _pop_min(*args) }
31
+ end
32
+ end
33
+
data/min_max.gemspec ADDED
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/min_max/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "min_max"
7
+ spec.version = MinMax::VERSION
8
+ spec.authors = ["Wouter Coppieters"]
9
+ spec.email = ["wc@pico.net.nz"]
10
+
11
+ spec.summary = "A min max heap extension for Ruby"
12
+ spec.description = "A min max heap extension for Ruby"
13
+ spec.homepage = "https://github.com/wouterken/min_max"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = ">= 2.7.0"
16
+ spec.required_rubygems_version = ">= 3.3.11"
17
+
18
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
19
+
20
+ spec.metadata["homepage_uri"] = spec.homepage
21
+
22
+ # Specify which files should be added to the gem when it is released.
23
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
+ spec.files = Dir.chdir(__dir__) do
25
+ `git ls-files -z`.split("\x0").reject do |f|
26
+ (File.expand_path(f) == __FILE__) ||
27
+ f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor Gemfile])
28
+ end
29
+ end
30
+ spec.bindir = "exe"
31
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
32
+ spec.require_paths = ["lib"]
33
+ spec.extensions = ["ext/min_max/extconf.rb"]
34
+
35
+ # needed until rubygems supports Rust support is out of beta
36
+ spec.add_dependency "rb_sys", "~> 0.9.39"
37
+
38
+ # only needed when developing or packaging your gem
39
+ spec.add_development_dependency "rake-compiler", "~> 1.2.0"
40
+ spec.add_development_dependency "rspec"
41
+
42
+ # Uncomment to register a new dependency of your gem
43
+ # spec.add_dependency "example-gem", "~> 1.0"
44
+
45
+ # For more information and examples about making a new gem, check out our
46
+ # guide at: https://bundler.io/guides/creating_gem.html
47
+ end
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: min_max
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Wouter Coppieters
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2024-02-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rb_sys
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.9.39
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.9.39
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake-compiler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 1.2.0
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 1.2.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
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
+ description: A min max heap extension for Ruby
56
+ email:
57
+ - wc@pico.net.nz
58
+ executables: []
59
+ extensions:
60
+ - ext/min_max/extconf.rb
61
+ extra_rdoc_files: []
62
+ files:
63
+ - ".rspec"
64
+ - Cargo.lock
65
+ - Cargo.toml
66
+ - LICENSE.txt
67
+ - README.md
68
+ - Rakefile
69
+ - ext/min_max/Cargo.toml
70
+ - ext/min_max/extconf.rb
71
+ - ext/min_max/src/lib.rs
72
+ - lib/min_max.rb
73
+ - lib/min_max/version.rb
74
+ - min_max.gemspec
75
+ homepage: https://github.com/wouterken/min_max
76
+ licenses:
77
+ - MIT
78
+ metadata:
79
+ allowed_push_host: https://rubygems.org
80
+ homepage_uri: https://github.com/wouterken/min_max
81
+ post_install_message:
82
+ rdoc_options: []
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: 2.7.0
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: 3.3.11
95
+ requirements: []
96
+ rubygems_version: 3.4.19
97
+ signing_key:
98
+ specification_version: 4
99
+ summary: A min max heap extension for Ruby
100
+ test_files: []