d_heap 0.5.0 → 0.6.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/.github/workflows/main.yml +2 -2
- data/.gitignore +1 -0
- data/.rubocop.yml +1 -1
- data/.yardopts +10 -0
- data/CHANGELOG.md +19 -6
- data/Gemfile +4 -0
- data/Gemfile.lock +10 -1
- data/N +7 -0
- data/README.md +185 -231
- data/benchmarks/push_n.yml +10 -6
- data/benchmarks/push_n_pop_n.yml +27 -10
- data/benchmarks/push_pop.yml +5 -0
- data/bin/bench_charts +13 -0
- data/d_heap.gemspec +1 -1
- data/ext/d_heap/d_heap.c +435 -140
- data/ext/d_heap/extconf.rb +3 -4
- data/images/push_n.png +0 -0
- data/images/push_n_pop_n.png +0 -0
- data/images/push_pop.png +0 -0
- data/images/wikipedia-min-heap.png +0 -0
- data/lib/benchmark_driver/runner/ips_zero_fail.rb +89 -51
- data/lib/d_heap.rb +81 -18
- data/lib/d_heap/benchmarks/implementations.rb +30 -28
- data/lib/d_heap/benchmarks/rspec_matchers.rb +29 -51
- data/lib/d_heap/version.rb +1 -1
- metadata +10 -4
- data/ext/d_heap/d_heap.h +0 -50
data/benchmarks/push_n.yml
CHANGED
@@ -5,27 +5,31 @@ prelude: |
|
|
5
5
|
include DHeap::Benchmarks
|
6
6
|
fill_random_vals
|
7
7
|
|
8
|
-
|
8
|
+
N = ENV.fetch("BENCH_N", 1000).to_i
|
9
9
|
|
10
10
|
benchmark:
|
11
11
|
- script: &script |
|
12
|
-
|
13
|
-
|
14
|
-
while i < n
|
15
|
-
q << random_val
|
16
|
-
i += 1
|
12
|
+
if __bmdv_i % N == 0
|
13
|
+
q.clear
|
17
14
|
end
|
15
|
+
|
16
|
+
q << random_val
|
18
17
|
name: "push N (findmin)"
|
19
18
|
prelude: "q = initq FindMin"
|
19
|
+
loop_count: 24000000
|
20
20
|
- script: *script
|
21
21
|
name: "push N (bsearch)"
|
22
22
|
prelude: "q = initq BSearch"
|
23
|
+
loop_count: 2300000
|
23
24
|
- script: *script
|
24
25
|
name: "push N (rb_heap)"
|
25
26
|
prelude: "q = initq RbHeap"
|
27
|
+
loop_count: 9800000
|
26
28
|
- script: *script
|
27
29
|
name: "push N (c++ stl)"
|
28
30
|
prelude: "q = initq CppSTL"
|
31
|
+
loop_count: 18700000
|
29
32
|
- script: *script
|
30
33
|
name: "push N (c_dheap)"
|
31
34
|
prelude: "q = initq DHeap"
|
35
|
+
loop_count: 25100000
|
data/benchmarks/push_n_pop_n.yml
CHANGED
@@ -5,31 +5,48 @@ prelude: |
|
|
5
5
|
include DHeap::Benchmarks
|
6
6
|
fill_random_vals
|
7
7
|
|
8
|
-
|
8
|
+
N = ENV.fetch("BENCH_N", 1000).to_i
|
9
|
+
N2 = N * 2
|
9
10
|
|
10
|
-
i = 0
|
11
|
+
i = j = 0
|
11
12
|
|
12
13
|
benchmark:
|
13
14
|
- script: &script |
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
15
|
+
if i < N
|
16
|
+
q.clear if __bmdv_i == 0
|
17
|
+
q << random_val
|
18
|
+
i += 1
|
19
|
+
|
20
|
+
elsif j < N
|
21
|
+
q.pop
|
22
|
+
j += 1
|
23
|
+
|
24
|
+
elsif q.empty?
|
25
|
+
i = 1
|
26
|
+
j = 0
|
27
|
+
q.clear
|
28
|
+
q << random_val
|
29
|
+
|
30
|
+
else
|
31
|
+
raise "q not empty!"
|
32
|
+
end
|
33
|
+
|
22
34
|
name: "push N + pop N (findmin)"
|
23
35
|
prelude: "q = initq FindMin"
|
36
|
+
loop_count: 200000
|
24
37
|
- script: *script
|
25
38
|
name: "push N + pop N (bsearch)"
|
26
39
|
prelude: "q = initq BSearch"
|
40
|
+
loop_count: 4000000
|
27
41
|
- script: *script
|
28
42
|
name: "push N + pop N (rb_heap)"
|
29
43
|
prelude: "q = initq RbHeap"
|
44
|
+
loop_count: 4000000
|
30
45
|
- script: *script
|
31
46
|
name: "push N + pop N (c++ stl)"
|
32
47
|
prelude: "q = initq CppSTL"
|
48
|
+
loop_count: 16000000
|
33
49
|
- script: *script
|
34
50
|
name: "push N + pop N (c_dheap)"
|
35
51
|
prelude: "q = initq DHeap"
|
52
|
+
loop_count: 16000000
|
data/benchmarks/push_pop.yml
CHANGED
@@ -13,15 +13,20 @@ benchmark:
|
|
13
13
|
q.pop
|
14
14
|
name: "push + pop (findmin)"
|
15
15
|
prelude: "q = FindMin.new(n) { random_val }"
|
16
|
+
loop_count: 250000
|
16
17
|
- script: *script
|
17
18
|
name: "push + pop (bsearch)"
|
18
19
|
prelude: "q = BSearch.new(n) { random_val }"
|
20
|
+
loop_count: 5000000
|
19
21
|
- script: *script
|
20
22
|
name: "push + pop (rb_heap)"
|
21
23
|
prelude: "q = RbHeap.new(n) { random_val }"
|
24
|
+
loop_count: 2000000
|
22
25
|
- script: *script
|
23
26
|
name: "push + pop (c++ stl)"
|
24
27
|
prelude: "q = initq CppSTL, n"
|
28
|
+
loop_count: 13000000
|
25
29
|
- script: *script
|
26
30
|
name: "push + pop (c_dheap)"
|
27
31
|
prelude: "q = initq DHeap, n"
|
32
|
+
loop_count: 20000000
|
data/bin/bench_charts
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
set -eu
|
3
|
+
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" > /dev/null; pwd -P)
|
4
|
+
PROJECT_DIR=$(cd "$SCRIPT_DIR" > /dev/null; cd .. > /dev/null; pwd -P)
|
5
|
+
cd "$PROJECT_DIR"
|
6
|
+
|
7
|
+
opts=( --bundle -e 'N 100000;N 10000;N 1000;N 100;N 10' -o gruff )
|
8
|
+
|
9
|
+
for bm in push_n push_n_pop_n push_pop; do
|
10
|
+
bin/benchmark-driver "${opts[@]}" "benchmarks/${bm}.yml"
|
11
|
+
mv graph.png "images/${bm}.png"
|
12
|
+
done
|
13
|
+
|
data/d_heap.gemspec
CHANGED
@@ -15,7 +15,7 @@ Gem::Specification.new do |spec|
|
|
15
15
|
DESC
|
16
16
|
spec.homepage = "https://github.com/nevans/#{spec.name}"
|
17
17
|
spec.license = "MIT"
|
18
|
-
spec.required_ruby_version = Gem::Requirement.new(">= 2.
|
18
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.4.0")
|
19
19
|
|
20
20
|
spec.metadata["homepage_uri"] = spec.homepage
|
21
21
|
spec.metadata["source_code_uri"] = spec.homepage
|
data/ext/d_heap/d_heap.c
CHANGED
@@ -1,36 +1,96 @@
|
|
1
1
|
#include <float.h>
|
2
|
-
#include
|
2
|
+
#include <math.h>
|
3
|
+
#include "ruby.h"
|
3
4
|
|
4
|
-
|
5
|
-
|
5
|
+
/********************************************************************
|
6
|
+
*
|
7
|
+
* Type definitions
|
8
|
+
*
|
9
|
+
********************************************************************/
|
10
|
+
|
11
|
+
typedef struct dheap_struct dheap_t;
|
12
|
+
typedef struct dheap_entry ENTRY;
|
13
|
+
|
14
|
+
// TODO: convert SCORE to a union, and use an ENTRY flag for its type
|
15
|
+
typedef long double SCORE;
|
6
16
|
|
7
|
-
|
17
|
+
#if LDBL_MANT_DIG < SIZEOF_UNSIGNED_LONG_LONG * 8
|
18
|
+
#error 'unsigned long long' must fit into 'long double' mantissa
|
19
|
+
#endif
|
20
|
+
|
21
|
+
// TODO: test this code on a 32 bit system (it MUST have some bugs!)
|
22
|
+
#if SIZEOF_UNSIGNED_LONG_LONG * 8 != 64
|
23
|
+
#error 'unsigned long long' must be 64bits
|
24
|
+
#endif
|
25
|
+
|
26
|
+
/********************************************************************
|
27
|
+
*
|
28
|
+
* Struct definitions
|
29
|
+
*
|
30
|
+
********************************************************************/
|
31
|
+
|
32
|
+
struct dheap_struct {
|
8
33
|
int d;
|
9
34
|
long size;
|
10
35
|
long capa;
|
11
36
|
ENTRY *entries;
|
12
|
-
}
|
37
|
+
};
|
13
38
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
#define DHEAP_IDX_CHILD_0(heap, idx) (((idx) * (heap)->d) + 1)
|
19
|
-
#define DHEAP_IDX_CHILD_D(heap, idx) (((idx) * (heap)->d) + (heap)->d)
|
39
|
+
struct dheap_entry {
|
40
|
+
SCORE score;
|
41
|
+
VALUE value;
|
42
|
+
};
|
20
43
|
|
21
|
-
|
44
|
+
/********************************************************************
|
45
|
+
*
|
46
|
+
* Constant definitions
|
47
|
+
*
|
48
|
+
********************************************************************/
|
22
49
|
|
23
|
-
#define
|
24
|
-
#define
|
25
|
-
#define CMP_GT(a, b) ((a) > (b))
|
26
|
-
#define CMP_GTE(a, b) ((a) >= (b))
|
50
|
+
#define DHEAP_DEFAULT_D 4
|
51
|
+
#define DHEAP_MAX_D INT_MAX
|
27
52
|
|
28
|
-
|
29
|
-
|
30
|
-
#
|
53
|
+
// sizeof(ENTRY) => 32 bytes
|
54
|
+
// one kilobyte = 32 * 32 bytes
|
55
|
+
#define DHEAP_DEFAULT_CAPA 32
|
56
|
+
#define DHEAP_MAX_CAPA (LONG_MAX / (int)sizeof(ENTRY))
|
57
|
+
#define DHEAP_CAPA_INCR_MAX (10 * 1024 * 1024 / (int)sizeof(ENTRY))
|
58
|
+
|
59
|
+
static ID id_cmp; // <=>
|
60
|
+
static ID id_abs; // abs
|
61
|
+
static ID id_lshift; // <<
|
62
|
+
static ID id_unary_minus; // -@
|
63
|
+
static const ENTRY EmptyDheapEntry; // 0 value for safety overwrite after pop
|
64
|
+
static const rb_data_type_t dheap_data_type;
|
65
|
+
|
66
|
+
/********************************************************************
|
67
|
+
*
|
68
|
+
* SCORE: casting to and from VALUE
|
69
|
+
* adapted from similar methods in ruby's object.c
|
70
|
+
*
|
71
|
+
********************************************************************/
|
72
|
+
|
73
|
+
// ruby doesn't have a LDBL2NUM. :(
|
74
|
+
// So this only accomplishes a subset of what that ought to do.
|
75
|
+
static inline VALUE
|
76
|
+
SCORE2NUM(SCORE s)
|
77
|
+
{
|
78
|
+
if (floorl((long double) s) == s) {
|
79
|
+
if (s < 0) {
|
80
|
+
unsigned long long ull = (unsigned long long)(-s);
|
81
|
+
return rb_funcall(ULL2NUM(ull), id_unary_minus, 0, Qundef);
|
82
|
+
}
|
83
|
+
return ULL2NUM((unsigned long long)(s));
|
84
|
+
}
|
85
|
+
return rb_float_new((double)(s));
|
86
|
+
}
|
87
|
+
|
88
|
+
static inline SCORE
|
89
|
+
FIX2SCORE(VALUE x)
|
90
|
+
{
|
91
|
+
return (long double)FIX2LONG(x);
|
92
|
+
}
|
31
93
|
|
32
|
-
// copied and modified from ruby's object.c
|
33
|
-
#define FIX2SCORE(x) (long double)FIX2LONG(x)
|
34
94
|
// We could translate a much wider range of values to long double by
|
35
95
|
// implementing a new `rb_big2ldbl(x)` function. But requires reaching into
|
36
96
|
// T_BIGNUM internals.
|
@@ -43,18 +103,27 @@ BIG2SCORE(VALUE x)
|
|
43
103
|
} else {
|
44
104
|
unsigned long long ull;
|
45
105
|
long double ldbl;
|
46
|
-
x = rb_funcall(x, id_abs, 0);
|
106
|
+
x = rb_funcall(x, id_abs, 0, Qundef);
|
47
107
|
ull = rb_big2ull(x);
|
48
108
|
ldbl = (long double) ull;
|
49
109
|
return -ldbl;
|
50
110
|
}
|
51
111
|
}
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
112
|
+
|
113
|
+
static inline SCORE
|
114
|
+
INT2SCORE(VALUE x)
|
115
|
+
{
|
116
|
+
return (FIXNUM_P(x) ? FIX2SCORE(x) : BIG2SCORE(x));
|
117
|
+
}
|
118
|
+
|
119
|
+
static inline SCORE
|
120
|
+
NUM2SCORE(VALUE x)
|
121
|
+
{
|
122
|
+
return (FIXNUM_P(x) ? FIX2SCORE(x) :
|
123
|
+
RB_TYPE_P(x, T_BIGNUM) ? BIG2SCORE(x) :
|
124
|
+
(Check_Type(x, T_FLOAT), (long double)RFLOAT_VALUE(x)));
|
125
|
+
}
|
126
|
+
|
58
127
|
static inline long double
|
59
128
|
RAT2SCORE(VALUE x)
|
60
129
|
{
|
@@ -73,14 +142,13 @@ RAT2SCORE(VALUE x)
|
|
73
142
|
* * reduced to long double (should be 80 or 128 bit) if it is Rational
|
74
143
|
* * reduced to double precision if the value is convertable by Float(x)
|
75
144
|
*/
|
76
|
-
static inline
|
145
|
+
static inline SCORE
|
77
146
|
VAL2SCORE(VALUE score)
|
78
147
|
{
|
79
148
|
// assert that long double can hold 'unsigned long long':
|
80
149
|
// static_assert(sizeof(unsigned long long) * 8 <= LDBL_MANT_DIG);
|
81
150
|
// assert that long double can hold T_FLOAT
|
82
151
|
// static_assert(sizeof(double) <= sizeof(long double));
|
83
|
-
|
84
152
|
switch (TYPE(score)) {
|
85
153
|
case T_FIXNUM:
|
86
154
|
return FIX2SCORE(score);
|
@@ -93,59 +161,37 @@ VAL2SCORE(VALUE score)
|
|
93
161
|
}
|
94
162
|
}
|
95
163
|
|
96
|
-
|
97
|
-
|
98
|
-
#define VAL2SCORE(score) (score)
|
99
|
-
|
100
|
-
#define CMP_LT(a, b) (optimized_cmp(a, b) < 0)
|
101
|
-
#define CMP_LTE(a, b) (optimized_cmp(a, b) <= 0)
|
102
|
-
#define CMP_GT(a, b) (optimized_cmp(a, b) > 0)
|
103
|
-
#define CMP_GTE(a, b) (optimized_cmp(a, b) >= 0)
|
104
|
-
|
105
|
-
// from internal/compar.h
|
106
|
-
#define STRING_P(s) (RB_TYPE_P((s), T_STRING) && CLASS_OF(s) == rb_cString)
|
107
|
-
/*
|
108
|
-
* short-circuit evaluation for a few basic types.
|
164
|
+
/********************************************************************
|
109
165
|
*
|
110
|
-
*
|
111
|
-
*
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
return 0;
|
117
|
-
if (FIXNUM_P(a) && FIXNUM_P(b))
|
118
|
-
return (FIX2LONG(a) < FIX2LONG(b)) ? -1 : 1;
|
119
|
-
if (RB_FLOAT_TYPE_P(a) && RB_FLOAT_TYPE_P(b))
|
120
|
-
{
|
121
|
-
double x, y;
|
122
|
-
x = RFLOAT_VALUE(a);
|
123
|
-
y = RFLOAT_VALUE(b);
|
124
|
-
if (isnan(x) || isnan(y)) rb_cmperr(a, b); // raise ArgumentError
|
125
|
-
return (x < y) ? -1 : ((x == y) ? 0 : 1);
|
126
|
-
}
|
127
|
-
if (RB_TYPE_P(a, T_BIGNUM) && RB_TYPE_P(b, T_BIGNUM))
|
128
|
-
return FIX2INT(rb_big_cmp(a, b));
|
129
|
-
if (STRING_P(a) && STRING_P(b))
|
130
|
-
return rb_str_cmp(a, b);
|
166
|
+
* DHeap ENTRY accessors
|
167
|
+
*
|
168
|
+
********************************************************************/
|
169
|
+
|
170
|
+
#define DHEAP_SCORE(heap, idx) ((heap)->entries[idx].score)
|
171
|
+
#define DHEAP_VALUE(heap, idx) ((heap)->entries[idx].value)
|
131
172
|
|
132
|
-
|
133
|
-
|
173
|
+
static inline VALUE
|
174
|
+
DHEAP_ENTRY_ARY(dheap_t *heap, long idx)
|
175
|
+
{
|
176
|
+
if (idx < 0 || heap->size <= idx) { return Qnil; }
|
177
|
+
return rb_ary_new_from_args(2,
|
178
|
+
DHEAP_VALUE(heap, 0),
|
179
|
+
SCORE2NUM(DHEAP_SCORE(heap, 0)));
|
134
180
|
}
|
135
181
|
|
136
|
-
|
182
|
+
/********************************************************************
|
183
|
+
*
|
184
|
+
* DHeap index math
|
185
|
+
*
|
186
|
+
********************************************************************/
|
137
187
|
|
138
|
-
#define
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
if (d > DHEAP_MAX_D) { \
|
143
|
-
rb_raise(rb_eArgError, "DHeap d=%d is too large", d); \
|
144
|
-
} \
|
145
|
-
} while (0)
|
188
|
+
#define DHEAP_IDX_LAST(heap) ((heap)->size - 1)
|
189
|
+
#define DHEAP_IDX_PARENT(heap, idx) (((idx) - 1) / (heap)->d)
|
190
|
+
#define DHEAP_IDX_CHILD_0(heap, idx) (((idx) * (heap)->d) + 1)
|
191
|
+
#define DHEAP_IDX_CHILD_D(heap, idx) (((idx) * (heap)->d) + (heap)->d)
|
146
192
|
|
147
193
|
#ifdef __D_HEAP_DEBUG
|
148
|
-
#define
|
194
|
+
#define ASSERT_DHEAP_IDX_OK(heap, index) do { \
|
149
195
|
if (index < 0) { \
|
150
196
|
rb_raise(rb_eIndexError, "DHeap index %ld too small", index); \
|
151
197
|
} \
|
@@ -154,18 +200,28 @@ optimized_cmp(SCORE a, SCORE b) {
|
|
154
200
|
} \
|
155
201
|
} while (0)
|
156
202
|
#else
|
157
|
-
#define
|
203
|
+
#define ASSERT_DHEAP_IDX_OK(heap, index)
|
158
204
|
#endif
|
159
205
|
|
206
|
+
/********************************************************************
|
207
|
+
*
|
208
|
+
* rb_data_type_t definitions
|
209
|
+
*
|
210
|
+
********************************************************************/
|
211
|
+
|
212
|
+
#ifdef HAVE_RB_GC_MARK_MOVABLE
|
160
213
|
static void
|
161
214
|
dheap_compact(void *ptr)
|
162
215
|
{
|
163
216
|
dheap_t *heap = ptr;
|
164
217
|
for (long i = 0; i < heap->size; ++i) {
|
165
218
|
if (DHEAP_VALUE(heap, i))
|
166
|
-
|
219
|
+
rb_gc_location(DHEAP_VALUE(heap, i));
|
167
220
|
}
|
168
221
|
}
|
222
|
+
#else
|
223
|
+
#define rb_gc_mark_movable(x) rb_gc_mark(x)
|
224
|
+
#endif
|
169
225
|
|
170
226
|
static void
|
171
227
|
dheap_mark(void *ptr)
|
@@ -200,25 +256,88 @@ dheap_memsize(const void *ptr)
|
|
200
256
|
return size;
|
201
257
|
}
|
202
258
|
|
259
|
+
|
203
260
|
static const rb_data_type_t dheap_data_type = {
|
204
261
|
"DHeap",
|
205
262
|
{
|
206
263
|
(void (*)(void*))dheap_mark,
|
207
264
|
(void (*)(void*))dheap_free,
|
208
265
|
(size_t (*)(const void *))dheap_memsize,
|
209
|
-
|
266
|
+
#ifdef HAVE_RB_GC_MARK_MOVABLE
|
267
|
+
(void (*)(void*))dheap_compact, {0}
|
268
|
+
#else
|
269
|
+
{0}
|
270
|
+
#endif
|
210
271
|
},
|
211
272
|
0, 0,
|
212
273
|
RUBY_TYPED_FREE_IMMEDIATELY,
|
213
274
|
};
|
214
275
|
|
276
|
+
/********************************************************************
|
277
|
+
*
|
278
|
+
* DHeap comparisons
|
279
|
+
* TODO: bring back comparisons for score types other than `long double`.
|
280
|
+
*
|
281
|
+
********************************************************************/
|
282
|
+
|
283
|
+
#define CMP_LT(a, b) ((a) < (b))
|
284
|
+
#define CMP_LTE(a, b) ((a) <= (b))
|
285
|
+
|
286
|
+
/* #ifdef ORIG_SCORE_CMP_CODE */
|
287
|
+
|
288
|
+
/* #define CMP_LT(a, b) (optimized_cmp(a, b) < 0) */
|
289
|
+
/* #define CMP_LTE(a, b) (optimized_cmp(a, b) <= 0) */
|
290
|
+
|
291
|
+
/* // from internal/compar.h */
|
292
|
+
/* #define STRING_P(s) (RB_TYPE_P((s), T_STRING) && CLASS_OF(s) == rb_cString) */
|
293
|
+
/*
|
294
|
+
* short-circuit evaluation for a few basic types.
|
295
|
+
*
|
296
|
+
* Only Integer, Float, and String are optimized,
|
297
|
+
* and only when both arguments are the same type.
|
298
|
+
*/
|
299
|
+
/* static inline int */
|
300
|
+
/* optimized_cmp(SCORE a, SCORE b) { */
|
301
|
+
/* if (a == b) // Fixnum equality and object equality */
|
302
|
+
/* return 0; */
|
303
|
+
/* if (FIXNUM_P(a) && FIXNUM_P(b)) */
|
304
|
+
/* return (FIX2LONG(a) < FIX2LONG(b)) ? -1 : 1; */
|
305
|
+
/* if (RB_FLOAT_TYPE_P(a) && RB_FLOAT_TYPE_P(b)) */
|
306
|
+
/* { */
|
307
|
+
/* double x, y; */
|
308
|
+
/* x = RFLOAT_VALUE(a); */
|
309
|
+
/* y = RFLOAT_VALUE(b); */
|
310
|
+
/* if (isnan(x) || isnan(y)) rb_cmperr(a, b); // raise ArgumentError */
|
311
|
+
/* return (x < y) ? -1 : ((x == y) ? 0 : 1); */
|
312
|
+
/* } */
|
313
|
+
/* if (RB_TYPE_P(a, T_BIGNUM) && RB_TYPE_P(b, T_BIGNUM)) */
|
314
|
+
/* return FIX2INT(rb_big_cmp(a, b)); */
|
315
|
+
/* if (STRING_P(a) && STRING_P(b)) */
|
316
|
+
/* return rb_str_cmp(a, b); */
|
317
|
+
|
318
|
+
/* // give up on an optimized version and just call (a <=> b) */
|
319
|
+
/* return rb_cmpint(rb_funcallv(a, id_cmp, 1, &b), a, b); */
|
320
|
+
/* } */
|
321
|
+
|
322
|
+
/* #endif */
|
323
|
+
|
324
|
+
/********************************************************************
|
325
|
+
*
|
326
|
+
* DHeap allocation and initialization and resizing
|
327
|
+
*
|
328
|
+
********************************************************************/
|
329
|
+
|
215
330
|
static VALUE
|
216
331
|
dheap_s_alloc(VALUE klass)
|
217
332
|
{
|
218
333
|
VALUE obj;
|
219
334
|
dheap_t *heap;
|
220
335
|
|
336
|
+
// TypedData_Make_Struct uses a non-std "statement expression"
|
337
|
+
#pragma GCC diagnostic push
|
338
|
+
#pragma GCC diagnostic ignored "-Wpedantic"
|
221
339
|
obj = TypedData_Make_Struct(klass, dheap_t, &dheap_data_type, heap);
|
340
|
+
#pragma GCC diagnostic pop
|
222
341
|
heap->d = DHEAP_DEFAULT_D;
|
223
342
|
|
224
343
|
heap->size = 0;
|
@@ -257,7 +376,7 @@ dheap_ensure_room_for_push(dheap_t *heap, long incr_by)
|
|
257
376
|
long new_size = heap->size + incr_by;
|
258
377
|
|
259
378
|
// check for overflow of new_size
|
260
|
-
if (
|
379
|
+
if (DHEAP_MAX_CAPA - incr_by < heap->size)
|
261
380
|
rb_raise(rb_eIndexError, "index %ld too big", new_size);
|
262
381
|
|
263
382
|
// if it existing capacity is too small
|
@@ -267,7 +386,7 @@ dheap_ensure_room_for_push(dheap_t *heap, long incr_by)
|
|
267
386
|
if (DHEAP_CAPA_INCR_MAX < new_size)
|
268
387
|
new_size = new_size + DHEAP_CAPA_INCR_MAX;
|
269
388
|
// check for overflow of new_capa
|
270
|
-
if (
|
389
|
+
if (DHEAP_MAX_CAPA / 2 < new_size) new_capa = DHEAP_MAX_CAPA;
|
271
390
|
// cap max incr_by
|
272
391
|
if (heap->capa + DHEAP_CAPA_INCR_MAX < new_capa)
|
273
392
|
new_capa = heap->capa + DHEAP_CAPA_INCR_MAX;
|
@@ -276,25 +395,38 @@ dheap_ensure_room_for_push(dheap_t *heap, long incr_by)
|
|
276
395
|
}
|
277
396
|
}
|
278
397
|
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
398
|
+
static inline int
|
399
|
+
dheap_value_to_int_d(VALUE num)
|
400
|
+
{
|
401
|
+
int d = NUM2INT(num);
|
402
|
+
if (d < 2) {
|
403
|
+
rb_raise(rb_eArgError, "DHeap d=%u is too small", d);
|
404
|
+
}
|
405
|
+
if (d > DHEAP_MAX_D) {
|
406
|
+
rb_raise(rb_eArgError, "DHeap d=%u is too large", d);
|
407
|
+
}
|
408
|
+
return d;
|
409
|
+
}
|
289
410
|
|
290
|
-
|
291
|
-
|
411
|
+
static inline long
|
412
|
+
dheap_value_to_long_capa(VALUE num)
|
413
|
+
{
|
414
|
+
long capa = NUM2LONG(num);
|
415
|
+
if (capa < 1) {
|
416
|
+
rb_raise(rb_eArgError, "DHeap capa=%lu must be positive", capa);
|
417
|
+
}
|
418
|
+
return capa;
|
419
|
+
}
|
292
420
|
|
293
|
-
|
294
|
-
|
295
|
-
heap
|
421
|
+
static VALUE
|
422
|
+
dheap_initialize(VALUE self, VALUE d, VALUE capa) {
|
423
|
+
dheap_t *heap = get_dheap_struct(self);
|
424
|
+
|
425
|
+
if(heap->entries || heap->size || heap->capa)
|
426
|
+
rb_raise(rb_eScriptError, "DHeap already initialized.");
|
296
427
|
|
297
|
-
|
428
|
+
heap->d = dheap_value_to_int_d(d);
|
429
|
+
dheap_set_capa(heap, dheap_value_to_long_capa(capa));
|
298
430
|
|
299
431
|
return self;
|
300
432
|
}
|
@@ -319,11 +451,17 @@ dheap_initialize_copy(VALUE copy, VALUE orig)
|
|
319
451
|
return copy;
|
320
452
|
}
|
321
453
|
|
454
|
+
/********************************************************************
|
455
|
+
*
|
456
|
+
* DHeap sift up/down
|
457
|
+
*
|
458
|
+
********************************************************************/
|
459
|
+
|
322
460
|
VALUE
|
323
461
|
dheap_sift_up(dheap_t *heap, long index) {
|
324
462
|
ENTRY entry = heap->entries[index];
|
325
463
|
|
326
|
-
|
464
|
+
ASSERT_DHEAP_IDX_OK(heap, index);
|
327
465
|
|
328
466
|
// sift it up to where it belongs
|
329
467
|
for (long parent_index; 0 < index; index = parent_index) {
|
@@ -370,7 +508,7 @@ dheap_sift_down(dheap_t *heap, long index, long last_index) {
|
|
370
508
|
|
371
509
|
long last_parent = DHEAP_IDX_PARENT(heap, last_index);
|
372
510
|
|
373
|
-
|
511
|
+
ASSERT_DHEAP_IDX_OK(heap, index);
|
374
512
|
|
375
513
|
// iteratively sift it down to where it belongs
|
376
514
|
while (index <= last_parent) {
|
@@ -389,6 +527,12 @@ dheap_sift_down(dheap_t *heap, long index, long last_index) {
|
|
389
527
|
}
|
390
528
|
}
|
391
529
|
|
530
|
+
/********************************************************************
|
531
|
+
*
|
532
|
+
* DHeap attributes
|
533
|
+
*
|
534
|
+
********************************************************************/
|
535
|
+
|
392
536
|
/*
|
393
537
|
* @return [Integer] the number of elements in the heap
|
394
538
|
*/
|
@@ -396,8 +540,7 @@ static VALUE
|
|
396
540
|
dheap_size(VALUE self)
|
397
541
|
{
|
398
542
|
dheap_t *heap = get_dheap_struct(self);
|
399
|
-
|
400
|
-
return LONG2NUM(size);
|
543
|
+
return LONG2NUM(heap->size);
|
401
544
|
}
|
402
545
|
|
403
546
|
/*
|
@@ -407,8 +550,7 @@ static VALUE
|
|
407
550
|
dheap_empty_p(VALUE self)
|
408
551
|
{
|
409
552
|
dheap_t *heap = get_dheap_struct(self);
|
410
|
-
|
411
|
-
return size == 0 ? Qtrue : Qfalse;
|
553
|
+
return heap->size ? Qfalse : Qtrue;
|
412
554
|
}
|
413
555
|
|
414
556
|
/*
|
@@ -421,16 +563,11 @@ dheap_attr_d(VALUE self)
|
|
421
563
|
return INT2FIX(heap->d);
|
422
564
|
}
|
423
565
|
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
if (RTEST(kwfreeze) || (kwfreeze == Qnil && OBJ_FROZEN(orig))) {
|
430
|
-
rb_funcall(clone, rb_intern("freeze"), 0);
|
431
|
-
}
|
432
|
-
return clone;
|
433
|
-
}
|
566
|
+
/********************************************************************
|
567
|
+
*
|
568
|
+
* DHeap push
|
569
|
+
*
|
570
|
+
********************************************************************/
|
434
571
|
|
435
572
|
static inline void
|
436
573
|
dheap_push_entry(dheap_t *heap, ENTRY *entry) {
|
@@ -520,47 +657,77 @@ dheap_left_shift(VALUE self, VALUE value) {
|
|
520
657
|
return self;
|
521
658
|
}
|
522
659
|
|
523
|
-
|
660
|
+
/********************************************************************
|
661
|
+
*
|
662
|
+
* DHeap pop and peek
|
663
|
+
*
|
664
|
+
********************************************************************/
|
524
665
|
|
525
|
-
static inline
|
526
|
-
|
666
|
+
static inline void
|
667
|
+
dheap_del0(dheap_t *heap)
|
527
668
|
{
|
528
|
-
VALUE popped = DHEAP_VALUE(heap, 0);
|
529
669
|
if (0 < --heap->size) {
|
530
670
|
heap->entries[0] = heap->entries[heap->size];
|
531
671
|
heap->entries[heap->size] = EmptyDheapEntry; // unnecessary to zero?
|
532
672
|
dheap_sift_down(heap, 0, heap->size - 1);
|
533
673
|
}
|
674
|
+
}
|
675
|
+
|
676
|
+
static inline VALUE
|
677
|
+
dheap_pop0(dheap_t *heap)
|
678
|
+
{
|
679
|
+
VALUE popped = DHEAP_VALUE(heap, 0);
|
680
|
+
dheap_del0(heap);
|
534
681
|
return popped;
|
535
682
|
}
|
536
683
|
|
537
684
|
/*
|
538
|
-
*
|
685
|
+
* Returns the next value on the heap, and its score, without popping it
|
539
686
|
*
|
540
|
-
*
|
687
|
+
* Time complexity: <b>O(1)</b> <i>(worst-case)</i>
|
688
|
+
* @return [nil,Array<(Object, Numeric)>] the next value and its score
|
689
|
+
*
|
690
|
+
* @see #peek
|
691
|
+
* @see #peek_score
|
541
692
|
*/
|
542
693
|
static VALUE
|
543
|
-
|
694
|
+
dheap_peek_with_score(VALUE self)
|
695
|
+
{
|
544
696
|
dheap_t *heap = get_dheap_struct(self);
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
697
|
+
return DHEAP_ENTRY_ARY(heap, 0);
|
698
|
+
}
|
699
|
+
|
700
|
+
/*
|
701
|
+
* Returns the next score on the heap, without the value and without popping it.
|
702
|
+
*
|
703
|
+
* Time complexity: <b>O(1)</b> <i>(worst-case)</i>
|
704
|
+
* @return [nil, Numeric] the next score, if there is one
|
705
|
+
*
|
706
|
+
* @see #peek
|
707
|
+
* @see #peek_with_score
|
708
|
+
*/
|
709
|
+
static VALUE
|
710
|
+
dheap_peek_score(VALUE self)
|
711
|
+
{
|
712
|
+
dheap_t *heap = get_dheap_struct(self);
|
713
|
+
if (DHEAP_IDX_LAST(heap) < 0) return Qnil;
|
714
|
+
return SCORE2NUM(DHEAP_SCORE(heap, 0));
|
550
715
|
}
|
551
716
|
|
552
717
|
/*
|
553
718
|
* Returns the next value on the heap to be popped without popping it.
|
554
719
|
*
|
555
720
|
* Time complexity: <b>O(1)</b> <i>(worst-case)</i>
|
556
|
-
* @return [Object] the next value to be popped without popping it.
|
721
|
+
* @return [nil, Object] the next value to be popped without popping it.
|
722
|
+
*
|
723
|
+
* @see #peek_score
|
724
|
+
* @see #peek_with_score
|
557
725
|
*/
|
558
726
|
static VALUE
|
559
727
|
dheap_peek(VALUE self)
|
560
728
|
{
|
561
729
|
dheap_t *heap = get_dheap_struct(self);
|
562
|
-
|
563
|
-
return DHEAP_VALUE(heap, 0);
|
730
|
+
return heap->size ? DHEAP_VALUE(heap, 0) : Qnil;
|
564
731
|
}
|
565
732
|
|
566
733
|
/*
|
@@ -573,14 +740,34 @@ dheap_peek(VALUE self)
|
|
573
740
|
* @see #peek
|
574
741
|
* @see #pop_lt
|
575
742
|
* @see #pop_lte
|
743
|
+
* @see #pop_with_score
|
576
744
|
*/
|
577
745
|
static VALUE
|
578
746
|
dheap_pop(VALUE self)
|
579
747
|
{
|
580
748
|
dheap_t *heap = get_dheap_struct(self);
|
581
749
|
rb_check_frozen(self);
|
582
|
-
|
583
|
-
|
750
|
+
return heap->size ? dheap_pop0(heap) : Qnil;
|
751
|
+
}
|
752
|
+
|
753
|
+
/*
|
754
|
+
* Pops the minimum value from the top of the heap, along with its score.
|
755
|
+
*
|
756
|
+
* Time complexity: <b>O(d log n / log d)</b> <i>(worst-case)</i>
|
757
|
+
*
|
758
|
+
* @return [nil,Array<(Object, Numeric)>] the next value and its score
|
759
|
+
*
|
760
|
+
* @see #pop
|
761
|
+
* @see #peek_with_score
|
762
|
+
*/
|
763
|
+
static VALUE
|
764
|
+
dheap_pop_with_score(VALUE self)
|
765
|
+
{
|
766
|
+
dheap_t *heap = get_dheap_struct(self);
|
767
|
+
VALUE ary = DHEAP_ENTRY_ARY(heap, 0);
|
768
|
+
rb_check_frozen(self);
|
769
|
+
if (ary != Qnil) { dheap_pop0(heap); }
|
770
|
+
return ary;
|
584
771
|
}
|
585
772
|
|
586
773
|
/*
|
@@ -595,13 +782,14 @@ dheap_pop(VALUE self)
|
|
595
782
|
* @see #peek
|
596
783
|
* @see #pop
|
597
784
|
* @see #pop_lt
|
785
|
+
* @see #pop_all_below
|
598
786
|
*/
|
599
787
|
static VALUE
|
600
788
|
dheap_pop_lte(VALUE self, VALUE max_score)
|
601
789
|
{
|
602
790
|
dheap_t *heap = get_dheap_struct(self);
|
603
791
|
rb_check_frozen(self);
|
604
|
-
if (
|
792
|
+
if (heap->size <= 0) return Qnil;
|
605
793
|
if (!CMP_LTE(DHEAP_SCORE(heap, 0), VAL2SCORE(max_score))) return Qnil;
|
606
794
|
return dheap_pop0(heap);
|
607
795
|
}
|
@@ -618,32 +806,132 @@ dheap_pop_lte(VALUE self, VALUE max_score)
|
|
618
806
|
* @see #peek
|
619
807
|
* @see #pop
|
620
808
|
* @see #pop_lte
|
809
|
+
* @see #pop_all_below
|
621
810
|
*/
|
622
811
|
static VALUE
|
623
812
|
dheap_pop_lt(VALUE self, VALUE max_score)
|
624
813
|
{
|
625
814
|
dheap_t *heap = get_dheap_struct(self);
|
626
815
|
rb_check_frozen(self);
|
627
|
-
if (
|
816
|
+
if (heap->size <= 0) return Qnil;
|
628
817
|
if (!CMP_LT(DHEAP_SCORE(heap, 0), VAL2SCORE(max_score))) return Qnil;
|
629
818
|
return dheap_pop0(heap);
|
630
819
|
}
|
631
820
|
|
821
|
+
#define DHEAP_PEEK_LT_P(heap, max_score) \
|
822
|
+
(heap->size && CMP_LT(DHEAP_SCORE(heap, 0), max_score))
|
823
|
+
|
824
|
+
static VALUE
|
825
|
+
dheap_pop_all_below0(dheap_t *heap, SCORE max_score, VALUE array)
|
826
|
+
{
|
827
|
+
if (!RTEST(array)) { array = rb_ary_new(); }
|
828
|
+
if (RB_TYPE_P(array, T_ARRAY)) {
|
829
|
+
while (DHEAP_PEEK_LT_P(heap, max_score)) {
|
830
|
+
rb_ary_push(array, dheap_pop0(heap));
|
831
|
+
}
|
832
|
+
} else {
|
833
|
+
while (DHEAP_PEEK_LT_P(heap, max_score)) {
|
834
|
+
rb_funcall(array, id_lshift, 1, dheap_pop0(heap));
|
835
|
+
}
|
836
|
+
}
|
837
|
+
return array;
|
838
|
+
}
|
839
|
+
|
840
|
+
/*
|
841
|
+
* @overload pop_all_below(max_score, receiver = [])
|
842
|
+
*
|
843
|
+
* Pops all value with score less than max score.
|
844
|
+
*
|
845
|
+
* Time complexity: <b>O(m * d log n / log d)</b>, <i>m = number popped</i>
|
846
|
+
*
|
847
|
+
* @param max_score [Integer,#to_f] the maximum score to be popped
|
848
|
+
* @param receiver [Array,#<<] object onto which the values will be pushed,
|
849
|
+
* in order by score.
|
850
|
+
*
|
851
|
+
* @return [Object] the object onto which the values were pushed
|
852
|
+
*
|
853
|
+
* @see #pop
|
854
|
+
* @see #pop_lt
|
855
|
+
*/
|
856
|
+
static VALUE
|
857
|
+
dheap_pop_all_below(int argc, VALUE *argv, VALUE self)
|
858
|
+
{
|
859
|
+
dheap_t *heap = get_dheap_struct(self);
|
860
|
+
SCORE max_score;
|
861
|
+
VALUE array;
|
862
|
+
rb_check_frozen(self);
|
863
|
+
rb_check_arity(argc, 1, 2);
|
864
|
+
max_score = VAL2SCORE(argv[0]);
|
865
|
+
if (argc == 1) {
|
866
|
+
array = rb_ary_new();
|
867
|
+
} else {
|
868
|
+
array = argv[1];
|
869
|
+
}
|
870
|
+
return dheap_pop_all_below0(heap, max_score, array);
|
871
|
+
}
|
872
|
+
|
873
|
+
/********************************************************************
|
874
|
+
*
|
875
|
+
* DHeap, misc methods
|
876
|
+
*
|
877
|
+
********************************************************************/
|
878
|
+
|
879
|
+
/*
|
880
|
+
* Clears all values from the heap, leaving it empty.
|
881
|
+
*
|
882
|
+
* @return [self]
|
883
|
+
*/
|
884
|
+
static VALUE
|
885
|
+
dheap_clear(VALUE self)
|
886
|
+
{
|
887
|
+
dheap_t *heap = get_dheap_struct(self);
|
888
|
+
rb_check_frozen(self);
|
889
|
+
if (0 < heap->size) {
|
890
|
+
heap->size = 0;
|
891
|
+
}
|
892
|
+
return self;
|
893
|
+
}
|
894
|
+
|
895
|
+
/********************************************************************
|
896
|
+
*
|
897
|
+
* DHeap setup
|
898
|
+
*
|
899
|
+
********************************************************************/
|
900
|
+
|
632
901
|
void
|
633
902
|
Init_d_heap(void)
|
634
903
|
{
|
904
|
+
VALUE rb_cDHeap = rb_define_class("DHeap", rb_cObject);
|
905
|
+
|
635
906
|
id_cmp = rb_intern_const("<=>");
|
636
907
|
id_abs = rb_intern_const("abs");
|
908
|
+
id_lshift = rb_intern_const("<<");
|
909
|
+
id_unary_minus = rb_intern_const("-@");
|
637
910
|
|
638
|
-
rb_cDHeap = rb_define_class("DHeap", rb_cObject);
|
639
911
|
rb_define_alloc_func(rb_cDHeap, dheap_s_alloc);
|
640
912
|
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
913
|
+
/*
|
914
|
+
* This is based on INT_MAX. But it is very very unlikely you will want a
|
915
|
+
* large value for d. The tradeoff is that higher d values give faster push
|
916
|
+
* and slower pop. If you expect pushes and pops to be balanced, then just
|
917
|
+
* stick with the default. If you expect more pushes than pops, it might be
|
918
|
+
* worthwhile to increase d.
|
919
|
+
*/
|
920
|
+
rb_define_const(rb_cDHeap, "MAX_D", INT2NUM(DHEAP_MAX_D));
|
921
|
+
|
922
|
+
/*
|
923
|
+
* d=4 uses the fewest comparisons for (worst case) insert + delete-min.
|
924
|
+
*/
|
925
|
+
rb_define_const(rb_cDHeap, "DEFAULT_D", INT2NUM(DHEAP_DEFAULT_D));
|
926
|
+
|
927
|
+
/*
|
928
|
+
* The default heap capacity. The heap grows automatically as necessary, so
|
929
|
+
* you shouldn't need to worry about this.
|
930
|
+
*/
|
931
|
+
rb_define_const(rb_cDHeap, "DEFAULT_CAPA", INT2NUM(DHEAP_DEFAULT_CAPA));
|
932
|
+
|
933
|
+
rb_define_private_method(rb_cDHeap, "__init_without_kw__", dheap_initialize, 2);
|
645
934
|
rb_define_method(rb_cDHeap, "initialize_copy", dheap_initialize_copy, 1);
|
646
|
-
rb_define_private_method(rb_cDHeap, "__init_clone__", dheap_init_clone, 2);
|
647
935
|
|
648
936
|
rb_define_method(rb_cDHeap, "d", dheap_attr_d, 0);
|
649
937
|
rb_define_method(rb_cDHeap, "size", dheap_size, 0);
|
@@ -655,6 +943,13 @@ Init_d_heap(void)
|
|
655
943
|
rb_define_method(rb_cDHeap, "push", dheap_push, -1);
|
656
944
|
rb_define_method(rb_cDHeap, "<<", dheap_left_shift, 1);
|
657
945
|
rb_define_method(rb_cDHeap, "pop", dheap_pop, 0);
|
658
|
-
|
659
|
-
rb_define_method(rb_cDHeap, "
|
946
|
+
|
947
|
+
rb_define_method(rb_cDHeap, "pop_lt", dheap_pop_lt, 1);
|
948
|
+
rb_define_method(rb_cDHeap, "pop_lte", dheap_pop_lte, 1);
|
949
|
+
rb_define_method(rb_cDHeap, "pop_all_below", dheap_pop_all_below, -1);
|
950
|
+
|
951
|
+
rb_define_method(rb_cDHeap, "peek_score", dheap_peek_score, 0);
|
952
|
+
rb_define_method(rb_cDHeap, "peek_with_score", dheap_peek_with_score, 0);
|
953
|
+
rb_define_method(rb_cDHeap, "pop_with_score", dheap_pop_with_score, 0);
|
954
|
+
|
660
955
|
}
|