d_heap 0.2.2 → 0.3.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 +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.
|