d_heap 0.2.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +10 -0
- data/Gemfile.lock +10 -1
- data/README.md +42 -41
- data/d_heap.gemspec +2 -0
- data/ext/d_heap/d_heap.c +283 -176
- data/ext/d_heap/d_heap.h +11 -35
- data/ext/d_heap/extconf.rb +2 -0
- data/lib/d_heap/version.rb +1 -1
- metadata +17 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5a20fe814944fa8945bc2cc0ce87f810c8bf8d21102b8c454ae5d639f0548576
|
4
|
+
data.tar.gz: 65a1af345ae84c7f6e5da8af89a00730ffc4cbdb6bb2cac59461c3728eb0ad28
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 27c240634013397033925ee258de08135587c31c7a046a902c028842680dadec20e2ccc0f76570e6f7db34d2292e5dae2ae7d17449436f890498697de4352c91
|
7
|
+
data.tar.gz: 8eb2cba120747cc7788c42f264d2109ef1afee8a28caa7effb8bcb1ff36ed7c99a0556b48b7d9e9479f6e8be8945eea3bf1ddb7f608c13c63252684643154179
|
data/.rubocop.yml
CHANGED
@@ -44,6 +44,7 @@ Layout/EmptyLineBetweenDefs:
|
|
44
44
|
Layout/EmptyLinesAroundAttributeAccessor:
|
45
45
|
inherit_mode:
|
46
46
|
merge:
|
47
|
+
- Exclude
|
47
48
|
- AllowedMethods
|
48
49
|
Enabled: true
|
49
50
|
AllowedMethods:
|
@@ -139,8 +140,17 @@ Style/TernaryParentheses:
|
|
139
140
|
Enabled: false
|
140
141
|
|
141
142
|
Style/BlockDelimiters:
|
143
|
+
inherit_mode:
|
144
|
+
merge:
|
145
|
+
- Exclude
|
146
|
+
- ProceduralMethods
|
147
|
+
- IgnoredMethods
|
148
|
+
- FunctionalMethods
|
142
149
|
EnforcedStyle: semantic
|
143
150
|
AllowBracesOnProceduralOneLiners: true
|
151
|
+
IgnoredMethods:
|
152
|
+
- expect
|
153
|
+
|
144
154
|
|
145
155
|
Style/FormatString:
|
146
156
|
EnforcedStyle: percent
|
data/Gemfile.lock
CHANGED
@@ -1,12 +1,15 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
d_heap (0.
|
4
|
+
d_heap (0.3.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
9
|
ast (2.4.1)
|
10
|
+
benchmark-malloc (0.2.0)
|
11
|
+
benchmark-perf (0.6.0)
|
12
|
+
benchmark-trend (0.4.0)
|
10
13
|
diff-lcs (1.4.4)
|
11
14
|
parallel (1.19.2)
|
12
15
|
parser (2.7.2.0)
|
@@ -21,6 +24,11 @@ GEM
|
|
21
24
|
rspec-core (~> 3.10.0)
|
22
25
|
rspec-expectations (~> 3.10.0)
|
23
26
|
rspec-mocks (~> 3.10.0)
|
27
|
+
rspec-benchmark (0.6.0)
|
28
|
+
benchmark-malloc (~> 0.2)
|
29
|
+
benchmark-perf (~> 0.6)
|
30
|
+
benchmark-trend (~> 0.4)
|
31
|
+
rspec (>= 3.0)
|
24
32
|
rspec-core (3.10.0)
|
25
33
|
rspec-support (~> 3.10.0)
|
26
34
|
rspec-expectations (3.10.0)
|
@@ -52,6 +60,7 @@ DEPENDENCIES
|
|
52
60
|
rake (~> 13.0)
|
53
61
|
rake-compiler
|
54
62
|
rspec (~> 3.10)
|
63
|
+
rspec-benchmark
|
55
64
|
rubocop (~> 1.0)
|
56
65
|
|
57
66
|
BUNDLED WITH
|
data/README.md
CHANGED
@@ -14,6 +14,48 @@ worst-case time complexity. In the worst case, a _d_-ary heap requires only
|
|
14
14
|
Although you should probably just stick with the default _d_ value of `4`, it
|
15
15
|
may be worthwhile to benchmark your specific scenario.
|
16
16
|
|
17
|
+
## Usage
|
18
|
+
|
19
|
+
The simplest way to use it is simply with `#push` and `#pop`. Push takes a
|
20
|
+
score and a value, and pop returns the value with the current minimum score.
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
require "d_heap"
|
24
|
+
|
25
|
+
heap = DHeap.new # defaults to a 4-ary heap
|
26
|
+
|
27
|
+
# storing [score, value] tuples
|
28
|
+
heap.push Time.now + 5*60, Task.new(1)
|
29
|
+
heap.push Time.now + 30, Task.new(2)
|
30
|
+
heap.push Time.now + 60, Task.new(3)
|
31
|
+
heap.push Time.now + 5, Task.new(4)
|
32
|
+
|
33
|
+
# peeking and popping (using last to get the task and ignore the time)
|
34
|
+
heap.pop.last # => Task[4]
|
35
|
+
heap.pop.last # => Task[2]
|
36
|
+
heap.peak.last # => Task[3], but don't pop it
|
37
|
+
heap.pop.last # => Task[3]
|
38
|
+
heap.pop.last # => Task[1]
|
39
|
+
```
|
40
|
+
|
41
|
+
Read the `rdoc` for more detailed documentation and examples.
|
42
|
+
|
43
|
+
## Installation
|
44
|
+
|
45
|
+
Add this line to your application's Gemfile:
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
gem 'd_heap'
|
49
|
+
```
|
50
|
+
|
51
|
+
And then execute:
|
52
|
+
|
53
|
+
$ bundle install
|
54
|
+
|
55
|
+
Or install it yourself as:
|
56
|
+
|
57
|
+
$ gem install d_heap
|
58
|
+
|
17
59
|
## Motivation
|
18
60
|
|
19
61
|
Sometimes you just need a priority queue, right? With a regular queue, you
|
@@ -49,47 +91,6 @@ unnecessary. This is definitely hotspot code, and the basic ruby implementation
|
|
49
91
|
would work fine, if not for that `<=>` overhead. Until then... this gem gets
|
50
92
|
the job done.
|
51
93
|
|
52
|
-
## Installation
|
53
|
-
|
54
|
-
Add this line to your application's Gemfile:
|
55
|
-
|
56
|
-
```ruby
|
57
|
-
gem 'd_heap'
|
58
|
-
```
|
59
|
-
|
60
|
-
And then execute:
|
61
|
-
|
62
|
-
$ bundle install
|
63
|
-
|
64
|
-
Or install it yourself as:
|
65
|
-
|
66
|
-
$ gem install d_heap
|
67
|
-
|
68
|
-
## Usage
|
69
|
-
|
70
|
-
The simplest way to use it is simply with `#push` and `#pop`. Push will
|
71
|
-
|
72
|
-
```ruby
|
73
|
-
require "d_heap"
|
74
|
-
|
75
|
-
heap = DHeap.new # defaults to a 4-ary heap
|
76
|
-
|
77
|
-
# storing [time, task] tuples
|
78
|
-
heap << [Time.now + 5*60, Task.new(1)]
|
79
|
-
heap << [Time.now + 30, Task.new(2)]
|
80
|
-
heap << [Time.now + 60, Task.new(3)]
|
81
|
-
heap << [Time.now + 5, Task.new(4)]
|
82
|
-
|
83
|
-
# peeking and popping (using last to get the task and ignore the time)
|
84
|
-
heap.pop.last # => Task[4]
|
85
|
-
heap.pop.last # => Task[2]
|
86
|
-
heap.peak.last # => Task[3]
|
87
|
-
heap.pop.last # => Task[3]
|
88
|
-
heap.pop.last # => Task[1]
|
89
|
-
```
|
90
|
-
|
91
|
-
Read the `rdoc` for more detailed documentation and examples.
|
92
|
-
|
93
94
|
## TODOs...
|
94
95
|
|
95
96
|
_TODO:_ In addition to a basic _d_-ary heap class (`DHeap`), this library
|
data/d_heap.gemspec
CHANGED
data/ext/d_heap/d_heap.c
CHANGED
@@ -1,27 +1,29 @@
|
|
1
1
|
#include "d_heap.h"
|
2
2
|
|
3
|
-
ID
|
3
|
+
ID id_cmp; // <=>
|
4
|
+
ID id_ivar_values;
|
5
|
+
ID id_ivar_scores;
|
4
6
|
ID id_ivar_d;
|
5
7
|
|
6
|
-
#define
|
7
|
-
|
8
|
-
|
9
|
-
#define
|
10
|
-
#define
|
11
|
-
|
12
|
-
#define
|
13
|
-
#define
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
8
|
+
#define Get_DHeap(hobj, hstruct) ((hstruct) = get_dheap_struct(hobj))
|
9
|
+
|
10
|
+
#define DHEAP_IDX_LAST(heap) (DHEAP_SIZE(heap) - 1)
|
11
|
+
#define DHEAP_IDX_PARENT(heap, idx) ((idx - 1) / heap->d)
|
12
|
+
#define DHEAP_IDX_CHILD0(heap, idx) ((idx * heap->d) + 1)
|
13
|
+
|
14
|
+
#define DHEAP_SIZE(heap) (RARRAY_LEN((heap)->scores))
|
15
|
+
#define DHEAP_VALUE(heap, idx) RARRAY_AREF((heap)->values, idx)
|
16
|
+
#define DHEAP_SCORE(heap, idx) RARRAY_AREF((heap)->scores, idx)
|
17
|
+
#define DHEAP_ASSIGN(heap, idx, scr, val) \
|
18
|
+
rb_ary_store(heap->scores, idx, scr); \
|
19
|
+
rb_ary_store(heap->values, idx, val);
|
20
|
+
#define DHEAP_APPEND(heap, scr, val) \
|
21
|
+
rb_ary_push((heap)->scores, scr); \
|
22
|
+
rb_ary_push((heap)->values, val);
|
23
|
+
#define DHEAP_DROP_LAST(heap) ( \
|
24
|
+
rb_ary_pop(heap->scores), \
|
25
|
+
rb_ary_pop(heap->values) \
|
22
26
|
) // score, value
|
23
|
-
#define IDX_PARENT(idx) ((idx - 1) / d)
|
24
|
-
#define IDX_CHILD0(idx) ((idx * d) + 1)
|
25
27
|
|
26
28
|
#define DHEAP_Check_d_size(d) \
|
27
29
|
if (d < 2) { \
|
@@ -31,64 +33,219 @@ ID id_ivar_d;
|
|
31
33
|
rb_raise(rb_eArgError, "DHeap d=%d is too large", d); \
|
32
34
|
}
|
33
35
|
|
34
|
-
#define
|
35
|
-
if (
|
36
|
-
rb_raise(rb_eIndexError, "
|
36
|
+
#define DHEAP_Check_Index(index, last_index) \
|
37
|
+
if (index < 0) { \
|
38
|
+
rb_raise(rb_eIndexError, "DHeap index %ld too small", index); \
|
37
39
|
} \
|
38
|
-
else if (last_index <
|
39
|
-
rb_raise(rb_eIndexError, "
|
40
|
+
else if (last_index < index) { \
|
41
|
+
rb_raise(rb_eIndexError, "DHeap index %ld too large", index); \
|
40
42
|
}
|
41
43
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
44
|
+
struct dheap_struct {
|
45
|
+
int d;
|
46
|
+
VALUE scores;
|
47
|
+
VALUE values;
|
48
|
+
};
|
49
|
+
typedef struct dheap_struct dheap_t;
|
50
|
+
|
51
|
+
static void
|
52
|
+
dheap_compact(void *ptr)
|
53
|
+
{
|
54
|
+
dheap_t *heap = ptr;
|
55
|
+
if (heap->scores) dheap_gc_location( heap->scores );
|
56
|
+
if (heap->values) dheap_gc_location( heap->values );
|
57
|
+
}
|
58
|
+
|
59
|
+
static void
|
60
|
+
dheap_mark(void *ptr)
|
61
|
+
{
|
62
|
+
dheap_t *heap = ptr;
|
63
|
+
if (heap->scores) rb_gc_mark_movable(heap->scores);
|
64
|
+
if (heap->values) rb_gc_mark_movable(heap->values);
|
65
|
+
}
|
66
|
+
|
67
|
+
static size_t
|
68
|
+
dheap_memsize(const void *ptr)
|
69
|
+
{
|
70
|
+
const dheap_t *heap = ptr;
|
71
|
+
size_t size = sizeof(*heap);
|
72
|
+
return size;
|
73
|
+
}
|
74
|
+
|
75
|
+
static const rb_data_type_t dheap_data_type = {
|
76
|
+
"DHeap",
|
77
|
+
{
|
78
|
+
(void (*)(void*))dheap_mark,
|
79
|
+
(void (*)(void*))RUBY_DEFAULT_FREE,
|
80
|
+
(size_t (*)(const void *))dheap_memsize,
|
81
|
+
dheap_compact_callback(dheap_compact),
|
82
|
+
},
|
83
|
+
0, 0,
|
84
|
+
RUBY_TYPED_FREE_IMMEDIATELY,
|
85
|
+
};
|
86
|
+
|
87
|
+
static VALUE
|
88
|
+
dheap_s_alloc(VALUE klass)
|
89
|
+
{
|
90
|
+
VALUE obj;
|
91
|
+
dheap_t *heap;
|
92
|
+
|
93
|
+
obj = TypedData_Make_Struct(klass, dheap_t, &dheap_data_type, heap);
|
94
|
+
heap->d = DHEAP_DEFAULT_D;
|
95
|
+
heap->scores = Qnil;
|
96
|
+
heap->values = Qnil;
|
97
|
+
|
98
|
+
return obj;
|
99
|
+
}
|
100
|
+
|
101
|
+
static inline dheap_t *
|
102
|
+
get_dheap_struct(VALUE self)
|
103
|
+
{
|
104
|
+
dheap_t *heap;
|
105
|
+
TypedData_Get_Struct(self, dheap_t, &dheap_data_type, heap);
|
106
|
+
Check_Type(heap->scores, T_ARRAY);
|
107
|
+
return heap;
|
108
|
+
}
|
109
|
+
|
110
|
+
/*
|
111
|
+
* @overload initialize(d = DHeap::DEFAULT_D)
|
112
|
+
* Initialize a _d_-ary min-heap.
|
113
|
+
*
|
114
|
+
* @param d [Integer] maximum number of children per parent
|
115
|
+
*/
|
116
|
+
static VALUE
|
117
|
+
dheap_initialize(int argc, VALUE *argv, VALUE self) {
|
118
|
+
rb_check_arity(argc, 0, 1);
|
119
|
+
dheap_t *heap;
|
120
|
+
TypedData_Get_Struct(self, dheap_t, &dheap_data_type, heap);
|
121
|
+
|
122
|
+
int d = DHEAP_DEFAULT_D;
|
123
|
+
if (argc) {
|
124
|
+
d = NUM2INT(argv[0]);
|
125
|
+
}
|
126
|
+
DHEAP_Check_d_size(d);
|
127
|
+
heap->d = d;
|
128
|
+
|
129
|
+
heap->scores = rb_ary_new_capa(10000);
|
130
|
+
heap->values = rb_ary_new_capa(10000);
|
131
|
+
|
132
|
+
return self;
|
133
|
+
}
|
134
|
+
|
135
|
+
/*
|
136
|
+
static inline VALUE
|
137
|
+
make_dheap_element(VALUE score, VALUE value)
|
138
|
+
{
|
139
|
+
elem_t *elem;
|
140
|
+
VALUE obj = TypedData_Make_Struct(
|
141
|
+
rb_cObject,
|
142
|
+
elem_t,
|
143
|
+
&dheap_elem_type,
|
144
|
+
elem);
|
145
|
+
elem->score = score;
|
146
|
+
elem->value = value;
|
147
|
+
return obj;
|
148
|
+
}
|
149
|
+
|
150
|
+
#define IsDHeapElem(value) rb_typeddata_is_kind_of((value), &dheap_elem_type)
|
151
|
+
#define Get_DHeap_Elem(value) \
|
152
|
+
TypedData_Get_Struct((obj), elem_t, &dheap_elem_type, (elem))
|
153
|
+
|
154
|
+
static inline elem_t *
|
155
|
+
get_dheap_element(VALUE obj)
|
156
|
+
{
|
157
|
+
elem_t *elem;
|
158
|
+
TypedData_Get_Struct((obj), elem_t, &dheap_elem_type, (elem));
|
159
|
+
return elem;
|
160
|
+
}
|
161
|
+
|
162
|
+
*/
|
163
|
+
|
164
|
+
#define CMP_LT(a, b) (optimized_cmp(a, b) < 0)
|
165
|
+
#define CMP_LTE(a, b) (optimized_cmp(a, b) <= 0)
|
166
|
+
#define CMP_GT(a, b) (optimized_cmp(a, b) > 0)
|
167
|
+
#define CMP_GTE(a, b) (optimized_cmp(a, b) >= 0)
|
168
|
+
|
169
|
+
/*
|
170
|
+
* short-circuit evaluation for a few basic types.
|
171
|
+
*
|
172
|
+
* Only Integer, Float, and String are optimized,
|
173
|
+
* and only when both arguments are the same type.
|
174
|
+
*/
|
175
|
+
static inline int
|
176
|
+
optimized_cmp(VALUE a, VALUE b) {
|
177
|
+
if (a == b) // Fixnum equality and object equality
|
178
|
+
return 0;
|
179
|
+
if (FIXNUM_P(a) && FIXNUM_P(b))
|
180
|
+
return (FIX2LONG(a) < FIX2LONG(b)) ? -1 : 1;
|
181
|
+
if (RB_FLOAT_TYPE_P(a) && RB_FLOAT_TYPE_P(b))
|
182
|
+
{
|
183
|
+
double x, y;
|
184
|
+
x = RFLOAT_VALUE(a);
|
185
|
+
y = RFLOAT_VALUE(b);
|
186
|
+
if (isnan(x) || isnan(y)) rb_cmperr(a, b); // raise ArgumentError
|
187
|
+
return (x < y) ? -1 : ((x == y) ? 0 : 1);
|
188
|
+
}
|
189
|
+
if (RB_TYPE_P(a, T_BIGNUM) && RB_TYPE_P(b, T_BIGNUM))
|
190
|
+
return FIX2INT(rb_big_cmp(a, b));
|
191
|
+
if (STRING_P(a) && STRING_P(b))
|
192
|
+
return rb_str_cmp(a, b);
|
193
|
+
|
194
|
+
// give up on an optimized version and just call (a <=> b)
|
195
|
+
return rb_cmpint(rb_funcallv(a, id_cmp, 1, &b), a, b);
|
196
|
+
}
|
50
197
|
|
51
198
|
VALUE
|
52
|
-
dheap_ary_sift_up(
|
53
|
-
|
199
|
+
dheap_ary_sift_up(dheap_t *heap, long sift_index) {
|
200
|
+
long last_index = DHEAP_IDX_LAST(heap);
|
201
|
+
DHEAP_Check_Index(sift_index, last_index);
|
202
|
+
|
203
|
+
VALUE sift_value = DHEAP_VALUE(heap, sift_index);
|
204
|
+
VALUE sift_score = DHEAP_SCORE(heap, sift_index);
|
205
|
+
|
54
206
|
// sift it up to where it belongs
|
55
207
|
for (long parent_index; 0 < sift_index; sift_index = parent_index) {
|
56
|
-
debug(rb_sprintf("sift up(%"PRIsVALUE", %d, %ld)",
|
57
|
-
parent_index =
|
58
|
-
VALUE parent_score = DHEAP_SCORE(
|
208
|
+
debug(rb_sprintf("sift up(%"PRIsVALUE", %d, %ld)", heap->values, heap->d, sift_index));
|
209
|
+
parent_index = DHEAP_IDX_PARENT(heap, sift_index);
|
210
|
+
VALUE parent_score = DHEAP_SCORE(heap, parent_index);
|
59
211
|
|
60
212
|
// parent is smaller: heap is restored
|
61
213
|
if (CMP_LTE(parent_score, sift_score)) break;
|
62
214
|
|
63
215
|
// parent is larger: swap and continue sifting up
|
64
|
-
VALUE parent_value = DHEAP_VALUE(
|
65
|
-
DHEAP_ASSIGN(
|
66
|
-
DHEAP_ASSIGN(
|
216
|
+
VALUE parent_value = DHEAP_VALUE(heap, parent_index);
|
217
|
+
DHEAP_ASSIGN(heap, sift_index, parent_score, parent_value);
|
218
|
+
DHEAP_ASSIGN(heap, parent_index, sift_score, sift_value);
|
67
219
|
}
|
68
|
-
debug(rb_sprintf("sifted (%"PRIsVALUE", %d, %ld)",
|
220
|
+
debug(rb_sprintf("sifted (%"PRIsVALUE", %d, %ld)", heap->values, heap->d, sift_index));
|
69
221
|
return LONG2NUM(sift_index);
|
70
222
|
}
|
71
223
|
|
72
224
|
VALUE
|
73
|
-
dheap_ary_sift_down(
|
74
|
-
|
225
|
+
dheap_ary_sift_down(dheap_t *heap, long sift_index) {
|
226
|
+
long last_index = DHEAP_IDX_LAST(heap);
|
227
|
+
DHEAP_Check_Index(sift_index, last_index);
|
228
|
+
|
229
|
+
VALUE sift_value = DHEAP_VALUE(heap, sift_index);
|
230
|
+
VALUE sift_score = DHEAP_SCORE(heap, sift_index);
|
75
231
|
|
76
232
|
// iteratively sift it down to where it belongs
|
77
233
|
for (long child_index; sift_index < last_index; sift_index = child_index) {
|
78
|
-
debug(rb_sprintf("sift dn(%"PRIsVALUE", %d, %ld)",
|
234
|
+
debug(rb_sprintf("sift dn(%"PRIsVALUE", %d, %ld)", heap->values, heap->d, sift_index));
|
79
235
|
// find first child index, and break if we've reached the last layer
|
80
|
-
long child_idx0 = child_index =
|
236
|
+
long child_idx0 = child_index = DHEAP_IDX_CHILD0(heap, sift_index);
|
81
237
|
if (last_index < child_idx0) break;
|
82
238
|
|
83
239
|
// find the min child (and its child_index)
|
84
240
|
// requires "d" comparisons to find min child and compare to sift_score
|
85
|
-
|
241
|
+
long last_sibidx = child_idx0 + heap->d - 1;
|
242
|
+
if (last_index < last_sibidx) last_sibidx = last_index;
|
243
|
+
VALUE child_score = DHEAP_SCORE(heap, child_idx0);
|
86
244
|
child_index = child_idx0;
|
87
|
-
for (
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
VALUE sibling_score = DHEAP_SCORE(heap_array, sibling_index);
|
245
|
+
for (long sibling_index = child_idx0 + 1;
|
246
|
+
sibling_index <= last_sibidx;
|
247
|
+
++sibling_index) {
|
248
|
+
VALUE sibling_score = DHEAP_SCORE(heap, sibling_index);
|
92
249
|
|
93
250
|
if (CMP_LT(sibling_score, child_score)) {
|
94
251
|
child_score = sibling_score;
|
@@ -100,106 +257,45 @@ dheap_ary_sift_down(VALUE heap_array, int d, long sift_index) {
|
|
100
257
|
if (CMP_LTE(sift_score, child_score)) break;
|
101
258
|
|
102
259
|
// child is smaller: swap and continue sifting down
|
103
|
-
VALUE child_value = DHEAP_VALUE(
|
104
|
-
DHEAP_ASSIGN(
|
105
|
-
DHEAP_ASSIGN(
|
260
|
+
VALUE child_value = DHEAP_VALUE(heap, child_index);
|
261
|
+
DHEAP_ASSIGN(heap, sift_index, child_score, child_value);
|
262
|
+
DHEAP_ASSIGN(heap, child_index, sift_score, sift_value);
|
106
263
|
}
|
107
|
-
debug(rb_sprintf("sifted (%"PRIsVALUE", %d, %ld)",
|
264
|
+
debug(rb_sprintf("sifted (%"PRIsVALUE", %d, %ld)", heap->values, heap->d, sift_index));
|
108
265
|
return LONG2NUM(sift_index);
|
109
266
|
}
|
110
267
|
|
111
|
-
#define DHEAP_Load_Sift_Vals(heap_array, dval, idxval) \
|
112
|
-
Check_Type(dval, T_FIXNUM); \
|
113
|
-
int dint = FIX2INT(dval); \
|
114
|
-
long idx = NUM2LONG(idxval);
|
115
|
-
|
116
|
-
/*
|
117
|
-
* Treats a +heap_array+ as a +d+-ary heap and sifts up from +sift_index+ to
|
118
|
-
* restore the heap property for all nodes between it and the root of the tree.
|
119
|
-
*
|
120
|
-
* The array is interpreted as holding two entries for each node, a score and a
|
121
|
-
* value. The scores will held in every even-numbered array index and the
|
122
|
-
* values in every odd numbered index. The array is flat, not an array of
|
123
|
-
* length=2 arrays.
|
124
|
-
*
|
125
|
-
* Time complexity: <b>O(log n / log d)</b> <i>(worst-case)</i>
|
126
|
-
*
|
127
|
-
* @param heap_array [Array] the array which is treated a heap.
|
128
|
-
* @param d [Integer] the maximum number of children per parent
|
129
|
-
* @param sift_index [Integer] the index to start sifting from
|
130
|
-
* @return [Integer] the new index for the object that starts at +sift_index+.
|
131
|
-
*/
|
132
|
-
static VALUE
|
133
|
-
dheap_sift_up_s(VALUE unused, VALUE heap_array, VALUE d, VALUE sift_index) {
|
134
|
-
DHEAP_Load_Sift_Vals(heap_array, d, sift_index);
|
135
|
-
return dheap_ary_sift_up(heap_array, dint, idx);
|
136
|
-
}
|
137
|
-
|
138
|
-
/*
|
139
|
-
* Treats +heap_array+ as a +d+-ary heap and sifts down from +sift_index+ to
|
140
|
-
* restore the heap property. If all _d_ subtrees below +sift_index+ are already
|
141
|
-
* heaps, this method ensures the entire subtree rooted at +sift_index+ will be
|
142
|
-
* a heap.
|
143
|
-
*
|
144
|
-
* The array is interpreted as holding two entries for each node, a score and a
|
145
|
-
* value. The scores will held in every even-numbered array index and the
|
146
|
-
* values in every odd numbered index. The array is flat, not an array of
|
147
|
-
* length=2 arrays.
|
148
|
-
*
|
149
|
-
* Time complexity: <b>O(d log n / log d)</b> <i>(worst-case)</i>
|
150
|
-
*
|
151
|
-
* @param heap_array [Array] the array which is treated a heap.
|
152
|
-
* @param d [Integer] the maximum number of children per parent
|
153
|
-
* @param sift_index [Integer] the index to start sifting down from
|
154
|
-
* @return [Integer] the new index for the object that starts at +sift_index+.
|
155
|
-
*/
|
156
|
-
static VALUE
|
157
|
-
dheap_sift_down_s(VALUE unused, VALUE heap_array, VALUE d, VALUE sift_index) {
|
158
|
-
DHEAP_Load_Sift_Vals(heap_array, d, sift_index);
|
159
|
-
return dheap_ary_sift_down(heap_array, dint, idx);
|
160
|
-
}
|
161
|
-
|
162
|
-
/*
|
163
|
-
* @overload initialize(d = DHeap::DEFAULT_D)
|
164
|
-
* Initialize a _d_-ary min-heap.
|
165
|
-
*
|
166
|
-
* @param d [Integer] maximum number of children per parent
|
167
|
-
*/
|
168
|
-
static VALUE
|
169
|
-
dheap_initialize(int argc, VALUE *argv, VALUE self) {
|
170
|
-
rb_check_arity(argc, 0, 1);
|
171
|
-
int d = DHEAP_DEFAULT_D;
|
172
|
-
if (argc) {
|
173
|
-
d = NUM2INT(argv[0]);
|
174
|
-
}
|
175
|
-
DHEAP_Check_d_size(d);
|
176
|
-
rb_ivar_set(self, id_ivar_d, INT2FIX(d));
|
177
|
-
rb_ivar_set(self, id_ivar_a, rb_ary_new());
|
178
|
-
return self;
|
179
|
-
}
|
180
|
-
|
181
268
|
/*
|
182
269
|
* @return [Integer] the number of elements in the heap
|
183
270
|
*/
|
184
|
-
static VALUE
|
185
|
-
|
186
|
-
|
271
|
+
static VALUE
|
272
|
+
dheap_size(VALUE self)
|
273
|
+
{
|
274
|
+
dheap_t *heap = get_dheap_struct(self);
|
275
|
+
long size = DHEAP_SIZE(heap);
|
187
276
|
return LONG2NUM(size);
|
188
277
|
}
|
189
278
|
|
190
279
|
/*
|
191
280
|
* @return [Boolean] is the heap empty?
|
192
281
|
*/
|
193
|
-
static VALUE
|
194
|
-
|
195
|
-
|
282
|
+
static VALUE
|
283
|
+
dheap_empty_p(VALUE self)
|
284
|
+
{
|
285
|
+
dheap_t *heap = get_dheap_struct(self);
|
286
|
+
long size = DHEAP_SIZE(heap);
|
196
287
|
return size == 0 ? Qtrue : Qfalse;
|
197
288
|
}
|
198
289
|
|
199
290
|
/*
|
200
291
|
* @return [Integer] the maximum number of children per parent
|
201
292
|
*/
|
202
|
-
static VALUE
|
293
|
+
static VALUE
|
294
|
+
dheap_attr_d(VALUE self)
|
295
|
+
{
|
296
|
+
dheap_t *heap = get_dheap_struct(self);
|
297
|
+
return INT2FIX(heap->d);
|
298
|
+
}
|
203
299
|
|
204
300
|
/*
|
205
301
|
* Freezes the heap as well as its underlying array, but does <i>not</i>
|
@@ -209,20 +305,13 @@ static VALUE dheap_attr_d(VALUE self) { return DHEAP_GET_D(self); }
|
|
209
305
|
*/
|
210
306
|
static VALUE
|
211
307
|
dheap_freeze(VALUE self) {
|
212
|
-
|
308
|
+
dheap_t *heap = get_dheap_struct(self);
|
213
309
|
ID id_freeze = rb_intern("freeze");
|
214
|
-
rb_funcall(
|
310
|
+
rb_funcall(heap->scores, id_freeze, 0);
|
311
|
+
rb_funcall(heap->values, id_freeze, 0);
|
215
312
|
return rb_call_super(0, NULL);
|
216
313
|
}
|
217
314
|
|
218
|
-
static VALUE
|
219
|
-
dheap_ary_push(VALUE ary, int d, VALUE val, VALUE scr)
|
220
|
-
{
|
221
|
-
DHEAP_APPEND(ary, scr, val);
|
222
|
-
long last_index = DHEAP_LAST_IDX(ary);
|
223
|
-
return dheap_ary_sift_up(ary, d, last_index);
|
224
|
-
}
|
225
|
-
|
226
315
|
/*
|
227
316
|
* @overload push(score, value = score)
|
228
317
|
*
|
@@ -244,11 +333,11 @@ dheap_push(int argc, VALUE *argv, VALUE self) {
|
|
244
333
|
VALUE scr = argv[0];
|
245
334
|
VALUE val = argc < 2 ? scr : argv[1];
|
246
335
|
|
247
|
-
|
248
|
-
VALUE dval = DHEAP_GET_D(self);
|
249
|
-
int d = FIX2INT(dval);
|
336
|
+
dheap_t *heap = get_dheap_struct(self);
|
250
337
|
|
251
|
-
|
338
|
+
DHEAP_APPEND(heap, scr, val);
|
339
|
+
long last_index = DHEAP_IDX_LAST(heap);
|
340
|
+
return dheap_ary_sift_up(heap, last_index);
|
252
341
|
}
|
253
342
|
|
254
343
|
/*
|
@@ -267,18 +356,29 @@ dheap_left_shift(VALUE self, VALUE value) {
|
|
267
356
|
return self;
|
268
357
|
}
|
269
358
|
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
359
|
+
/*
|
360
|
+
dheap_t hstruct; \
|
361
|
+
Get_DHeap(self, hstruct); \
|
362
|
+
VALUE values = hstruct.values; \
|
363
|
+
VALUE scores = hstruct.scores; \
|
364
|
+
VALUE dval = INT2FIX(hstruct.d); \
|
365
|
+
*/
|
366
|
+
|
367
|
+
static inline void
|
368
|
+
dheap_pop_swap_last_and_sift_down(dheap_t *heap, long last_index)
|
369
|
+
{
|
370
|
+
if (last_index == 0) {
|
371
|
+
DHEAP_DROP_LAST(heap);
|
372
|
+
}
|
373
|
+
else
|
374
|
+
{
|
375
|
+
VALUE sift_value = DHEAP_VALUE(heap, last_index);
|
376
|
+
VALUE sift_score = DHEAP_SCORE(heap, last_index);
|
377
|
+
DHEAP_ASSIGN(heap, 0, sift_score, sift_value);
|
378
|
+
DHEAP_DROP_LAST(heap);
|
379
|
+
dheap_ary_sift_down(heap, 0);
|
380
|
+
}
|
381
|
+
}
|
282
382
|
|
283
383
|
/*
|
284
384
|
* Returns the next value on the heap to be popped without popping it.
|
@@ -288,8 +388,9 @@ dheap_left_shift(VALUE self, VALUE value) {
|
|
288
388
|
*/
|
289
389
|
static VALUE
|
290
390
|
dheap_peek(VALUE self) {
|
291
|
-
|
292
|
-
|
391
|
+
dheap_t *heap = get_dheap_struct(self);
|
392
|
+
if (DHEAP_IDX_LAST(heap) < 0) return Qnil;
|
393
|
+
return DHEAP_VALUE(heap, 0);
|
293
394
|
}
|
294
395
|
|
295
396
|
/*
|
@@ -299,11 +400,13 @@ dheap_peek(VALUE self) {
|
|
299
400
|
*/
|
300
401
|
static VALUE
|
301
402
|
dheap_pop(VALUE self) {
|
302
|
-
|
303
|
-
|
304
|
-
VALUE pop_value = DHEAP_VALUE(ary, 0);
|
403
|
+
dheap_t *heap = get_dheap_struct(self);
|
404
|
+
long last_index = DHEAP_IDX_LAST(heap);
|
305
405
|
|
306
|
-
|
406
|
+
if (last_index < 0) return Qnil;
|
407
|
+
VALUE pop_value = DHEAP_VALUE(heap, 0);
|
408
|
+
|
409
|
+
dheap_pop_swap_last_and_sift_down(heap, last_index);
|
307
410
|
return pop_value;
|
308
411
|
}
|
309
412
|
|
@@ -316,14 +419,16 @@ dheap_pop(VALUE self) {
|
|
316
419
|
*/
|
317
420
|
static VALUE
|
318
421
|
dheap_pop_lte(VALUE self, VALUE max_score) {
|
319
|
-
|
422
|
+
dheap_t *heap = get_dheap_struct(self);
|
423
|
+
long last_index = DHEAP_IDX_LAST(heap);
|
424
|
+
|
320
425
|
if (last_index < 0) return Qnil;
|
321
|
-
VALUE pop_value = DHEAP_VALUE(
|
426
|
+
VALUE pop_value = DHEAP_VALUE(heap, 0);
|
322
427
|
|
323
|
-
VALUE pop_score = DHEAP_SCORE(
|
428
|
+
VALUE pop_score = DHEAP_SCORE(heap, 0);
|
324
429
|
if (max_score && !CMP_LTE(pop_score, max_score)) return Qnil;
|
325
430
|
|
326
|
-
|
431
|
+
dheap_pop_swap_last_and_sift_down(heap, last_index);
|
327
432
|
return pop_value;
|
328
433
|
}
|
329
434
|
|
@@ -336,14 +441,16 @@ dheap_pop_lte(VALUE self, VALUE max_score) {
|
|
336
441
|
*/
|
337
442
|
static VALUE
|
338
443
|
dheap_pop_lt(VALUE self, VALUE max_score) {
|
339
|
-
|
444
|
+
dheap_t *heap = get_dheap_struct(self);
|
445
|
+
long last_index = DHEAP_IDX_LAST(heap);
|
446
|
+
|
340
447
|
if (last_index < 0) return Qnil;
|
341
|
-
VALUE pop_value = DHEAP_VALUE(
|
448
|
+
VALUE pop_value = DHEAP_VALUE(heap, 0);
|
342
449
|
|
343
|
-
VALUE pop_score = DHEAP_SCORE(
|
450
|
+
VALUE pop_score = DHEAP_SCORE(heap, 0);
|
344
451
|
if (max_score && !CMP_LT(pop_score, max_score)) return Qnil;
|
345
452
|
|
346
|
-
|
453
|
+
dheap_pop_swap_last_and_sift_down(heap, last_index);
|
347
454
|
return pop_value;
|
348
455
|
}
|
349
456
|
|
@@ -351,16 +458,16 @@ void
|
|
351
458
|
Init_d_heap(void)
|
352
459
|
{
|
353
460
|
id_cmp = rb_intern_const("<=>");
|
354
|
-
|
461
|
+
id_ivar_values = rb_intern_const("values");
|
462
|
+
id_ivar_scores = rb_intern_const("scores");
|
355
463
|
id_ivar_d = rb_intern_const("d");
|
356
464
|
|
357
465
|
rb_cDHeap = rb_define_class("DHeap", rb_cObject);
|
466
|
+
rb_define_alloc_func(rb_cDHeap, dheap_s_alloc);
|
467
|
+
|
358
468
|
rb_define_const(rb_cDHeap, "MAX_D", INT2NUM(DHEAP_MAX_D));
|
359
469
|
rb_define_const(rb_cDHeap, "DEFAULT_D", INT2NUM(DHEAP_DEFAULT_D));
|
360
470
|
|
361
|
-
rb_define_singleton_method(rb_cDHeap, "heap_sift_down", dheap_sift_down_s, 3);
|
362
|
-
rb_define_singleton_method(rb_cDHeap, "heap_sift_up", dheap_sift_up_s, 3);
|
363
|
-
|
364
471
|
rb_define_method(rb_cDHeap, "initialize", dheap_initialize, -1);
|
365
472
|
rb_define_method(rb_cDHeap, "d", dheap_attr_d, 0);
|
366
473
|
rb_define_method(rb_cDHeap, "freeze", dheap_freeze, 0);
|
data/ext/d_heap/d_heap.h
CHANGED
@@ -13,46 +13,22 @@
|
|
13
13
|
|
14
14
|
VALUE rb_cDHeap;
|
15
15
|
|
16
|
-
|
17
|
-
#define CMP_LTE(a, b) (optimized_cmp(a, b) <= 0)
|
18
|
-
#define CMP_GT(a, b) (optimized_cmp(a, b) > 0)
|
19
|
-
#define CMP_GTE(a, b) (optimized_cmp(a, b) >= 0)
|
16
|
+
// copied from pg gem
|
20
17
|
|
21
|
-
|
22
|
-
|
18
|
+
#define UNUSED(x) ((void)(x))
|
19
|
+
|
20
|
+
#ifdef HAVE_RB_GC_MARK_MOVABLE
|
21
|
+
#define dheap_compact_callback(x) ((void (*)(void*))(x))
|
22
|
+
#define dheap_gc_location(x) x = rb_gc_location(x)
|
23
|
+
#else
|
24
|
+
#define rb_gc_mark_movable(x) rb_gc_mark(x)
|
25
|
+
#define dheap_compact_callback(x) {(x)}
|
26
|
+
#define dheap_gc_location(x) UNUSED(x)
|
27
|
+
#endif
|
23
28
|
|
24
29
|
// from internal/compar.h
|
25
30
|
#define STRING_P(s) (RB_TYPE_P((s), T_STRING) && CLASS_OF(s) == rb_cString)
|
26
31
|
|
27
|
-
/*
|
28
|
-
* short-circuit evaluation for a few basic types.
|
29
|
-
*
|
30
|
-
* Only Integer, Float, and String are optimized,
|
31
|
-
* and only when both arguments are the same type.
|
32
|
-
*/
|
33
|
-
static inline int
|
34
|
-
optimized_cmp(VALUE a, VALUE b) {
|
35
|
-
if (a == b) // Fixnum equality and object equality
|
36
|
-
return 0;
|
37
|
-
if (FIXNUM_P(a) && FIXNUM_P(b))
|
38
|
-
return (FIX2LONG(a) < FIX2LONG(b)) ? -1 : 1;
|
39
|
-
if (RB_FLOAT_TYPE_P(a) && RB_FLOAT_TYPE_P(b))
|
40
|
-
{
|
41
|
-
double x, y;
|
42
|
-
x = RFLOAT_VALUE(a);
|
43
|
-
y = RFLOAT_VALUE(b);
|
44
|
-
if (isnan(x) || isnan(y)) rb_cmperr(a, b); // raise ArgumentError
|
45
|
-
return (x < y) ? -1 : ((x == y) ? 0 : 1);
|
46
|
-
}
|
47
|
-
if (RB_TYPE_P(a, T_BIGNUM) && RB_TYPE_P(b, T_BIGNUM))
|
48
|
-
return FIX2INT(rb_big_cmp(a, b));
|
49
|
-
if (STRING_P(a) && STRING_P(b))
|
50
|
-
return rb_str_cmp(a, b);
|
51
|
-
|
52
|
-
// give up on an optimized version and just call (a <=> b)
|
53
|
-
return rb_cmpint(rb_funcallv(a, id_cmp, 1, &b), a, b);
|
54
|
-
}
|
55
|
-
|
56
32
|
#ifdef __D_HEAP_DEBUG
|
57
33
|
#define debug(v) { \
|
58
34
|
ID sym_puts = rb_intern("puts"); \
|
data/ext/d_heap/extconf.rb
CHANGED
data/lib/d_heap/version.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: d_heap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- nicholas a. evans
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-12-
|
12
|
-
dependencies:
|
11
|
+
date: 2020-12-29 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec-benchmark
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
13
27
|
description: |
|
14
28
|
A C extension implementation of a d-ary heap data structure, suitable for
|
15
29
|
use in e.g. priority queues or Djikstra's algorithm.
|