d_heap 0.3.0 → 0.7.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.
data/docs/profile.txt ADDED
@@ -0,0 +1,358 @@
1
+ Profiling run at 2021-02-01 00:43:34 -0500
2
+ ruby v2.7.2, DHeap v0.6.1
3
+
4
+ ~~~~~~ filling @dheap_bm_random_vals with 1.0M ~~~~~~
5
+ ########################################################################
6
+ # Profile w/ N=10 (i=1000000)
7
+ # (n.b. RubyProf & tracepoint can change relative performance.
8
+ # A sampling profiler can provide more accurate relative metrics.
9
+ ########################################################################
10
+
11
+ Filling ruby binary heap ---------------------------
12
+ Profiling ruby binary heap ---------------------------
13
+ Measure Mode: wall_time
14
+ Thread ID: 1400
15
+ Fiber ID: 1380
16
+ Total: 5.194751
17
+ Sort by: self_time
18
+
19
+ %self total self wait child calls name location
20
+ 41.50 2.156 2.156 0.000 0.000 1000000 DHeap::Benchmarks::RbHeap#<< /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:107
21
+ 36.37 2.137 1.889 0.000 0.247 1000000 DHeap::Benchmarks::RbHeap#pop /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:121
22
+ 14.92 5.195 0.775 0.000 4.420 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
23
+ 2.46 0.128 0.128 0.000 0.000 1000000 Array#fetch
24
+ 2.41 0.125 0.125 0.000 0.000 1000000 Array#pop
25
+ 2.35 0.122 0.122 0.000 0.000 1000000 Array#first
26
+
27
+ * recursively called methods
28
+
29
+ Columns are:
30
+
31
+ %self - The percentage of time spent in this method, derived from self_time/total_time.
32
+ total - The time spent in this method and its children.
33
+ self - The time spent in this method.
34
+ wait - The amount of time this method waited for other threads.
35
+ child - The time spent in this method's children.
36
+ calls - The number of times this method was called.
37
+ name - The name of the method.
38
+ location - The location of the method.
39
+
40
+ The interpretation of method names is:
41
+
42
+ * MyObject#test - An instance method "test" of the class "MyObject"
43
+ * <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
44
+
45
+ Filling quaternary DHeap ---------------------------
46
+ Profiling quaternary DHeap ---------------------------
47
+ Measure Mode: wall_time
48
+ Thread ID: 1400
49
+ Fiber ID: 1380
50
+ Total: 1.103473
51
+ Sort by: self_time
52
+
53
+ %self total self wait child calls name location
54
+ 64.92 1.103 0.716 0.000 0.387 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
55
+ 12.09 0.133 0.133 0.000 0.000 1000000 DHeap#<<
56
+ 11.82 0.130 0.130 0.000 0.000 1000000 DHeap#pop
57
+ 11.16 0.123 0.123 0.000 0.000 1000000 Array#fetch
58
+
59
+ * recursively called methods
60
+
61
+ Columns are:
62
+
63
+ %self - The percentage of time spent in this method, derived from self_time/total_time.
64
+ total - The time spent in this method and its children.
65
+ self - The time spent in this method.
66
+ wait - The amount of time this method waited for other threads.
67
+ child - The time spent in this method's children.
68
+ calls - The number of times this method was called.
69
+ name - The name of the method.
70
+ location - The location of the method.
71
+
72
+ The interpretation of method names is:
73
+
74
+ * MyObject#test - An instance method "test" of the class "MyObject"
75
+ * <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
76
+
77
+ ########################################################################
78
+ # Profile w/ N=100 (i=1000000)
79
+ # (n.b. RubyProf & tracepoint can change relative performance.
80
+ # A sampling profiler can provide more accurate relative metrics.
81
+ ########################################################################
82
+
83
+ Filling ruby binary heap ---------------------------
84
+ Profiling ruby binary heap ---------------------------
85
+ Measure Mode: wall_time
86
+ Thread ID: 1400
87
+ Fiber ID: 1380
88
+ Total: 7.422039
89
+ Sort by: self_time
90
+
91
+ %self total self wait child calls name location
92
+ 47.72 3.542 3.542 0.000 0.000 1000000 DHeap::Benchmarks::RbHeap#<< /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:107
93
+ 36.82 2.984 2.733 0.000 0.251 1000000 DHeap::Benchmarks::RbHeap#pop /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:121
94
+ 10.39 7.422 0.771 0.000 6.651 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
95
+ 1.71 0.127 0.127 0.000 0.000 1000000 Array#pop
96
+ 1.69 0.125 0.125 0.000 0.000 1000000 Array#fetch
97
+ 1.67 0.124 0.124 0.000 0.000 1000000 Array#first
98
+
99
+ * recursively called methods
100
+
101
+ Columns are:
102
+
103
+ %self - The percentage of time spent in this method, derived from self_time/total_time.
104
+ total - The time spent in this method and its children.
105
+ self - The time spent in this method.
106
+ wait - The amount of time this method waited for other threads.
107
+ child - The time spent in this method's children.
108
+ calls - The number of times this method was called.
109
+ name - The name of the method.
110
+ location - The location of the method.
111
+
112
+ The interpretation of method names is:
113
+
114
+ * MyObject#test - An instance method "test" of the class "MyObject"
115
+ * <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
116
+
117
+ Filling quaternary DHeap ---------------------------
118
+ Profiling quaternary DHeap ---------------------------
119
+ Measure Mode: wall_time
120
+ Thread ID: 1400
121
+ Fiber ID: 1380
122
+ Total: 1.163539
123
+ Sort by: self_time
124
+
125
+ %self total self wait child calls name location
126
+ 64.38 1.164 0.749 0.000 0.414 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
127
+ 12.52 0.146 0.146 0.000 0.000 1000000 DHeap#<<
128
+ 12.43 0.145 0.145 0.000 0.000 1000000 DHeap#pop
129
+ 10.67 0.124 0.124 0.000 0.000 1000000 Array#fetch
130
+
131
+ * recursively called methods
132
+
133
+ Columns are:
134
+
135
+ %self - The percentage of time spent in this method, derived from self_time/total_time.
136
+ total - The time spent in this method and its children.
137
+ self - The time spent in this method.
138
+ wait - The amount of time this method waited for other threads.
139
+ child - The time spent in this method's children.
140
+ calls - The number of times this method was called.
141
+ name - The name of the method.
142
+ location - The location of the method.
143
+
144
+ The interpretation of method names is:
145
+
146
+ * MyObject#test - An instance method "test" of the class "MyObject"
147
+ * <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
148
+
149
+ ########################################################################
150
+ # Profile w/ N=1000 (i=1000000)
151
+ # (n.b. RubyProf & tracepoint can change relative performance.
152
+ # A sampling profiler can provide more accurate relative metrics.
153
+ ########################################################################
154
+
155
+ Filling ruby binary heap ---------------------------
156
+ Profiling ruby binary heap ---------------------------
157
+ Measure Mode: wall_time
158
+ Thread ID: 1400
159
+ Fiber ID: 1380
160
+ Total: 9.693127
161
+ Sort by: self_time
162
+
163
+ %self total self wait child calls name location
164
+ 52.28 5.068 5.068 0.000 0.000 1000000 DHeap::Benchmarks::RbHeap#<< /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:107
165
+ 35.52 3.702 3.443 0.000 0.259 1000000 DHeap::Benchmarks::RbHeap#pop /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:121
166
+ 8.18 9.693 0.793 0.000 8.900 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
167
+ 1.36 0.131 0.131 0.000 0.000 1000000 Array#pop
168
+ 1.35 0.131 0.131 0.000 0.000 1000000 Array#fetch
169
+ 1.32 0.128 0.128 0.000 0.000 1000000 Array#first
170
+
171
+ * recursively called methods
172
+
173
+ Columns are:
174
+
175
+ %self - The percentage of time spent in this method, derived from self_time/total_time.
176
+ total - The time spent in this method and its children.
177
+ self - The time spent in this method.
178
+ wait - The amount of time this method waited for other threads.
179
+ child - The time spent in this method's children.
180
+ calls - The number of times this method was called.
181
+ name - The name of the method.
182
+ location - The location of the method.
183
+
184
+ The interpretation of method names is:
185
+
186
+ * MyObject#test - An instance method "test" of the class "MyObject"
187
+ * <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
188
+
189
+ Filling quaternary DHeap ---------------------------
190
+ Profiling quaternary DHeap ---------------------------
191
+ Measure Mode: wall_time
192
+ Thread ID: 1400
193
+ Fiber ID: 1380
194
+ Total: 1.125575
195
+ Sort by: self_time
196
+
197
+ %self total self wait child calls name location
198
+ 64.22 1.126 0.723 0.000 0.403 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
199
+ 13.16 0.148 0.148 0.000 0.000 1000000 DHeap#<<
200
+ 12.01 0.135 0.135 0.000 0.000 1000000 DHeap#pop
201
+ 10.62 0.119 0.119 0.000 0.000 1000000 Array#fetch
202
+
203
+ * recursively called methods
204
+
205
+ Columns are:
206
+
207
+ %self - The percentage of time spent in this method, derived from self_time/total_time.
208
+ total - The time spent in this method and its children.
209
+ self - The time spent in this method.
210
+ wait - The amount of time this method waited for other threads.
211
+ child - The time spent in this method's children.
212
+ calls - The number of times this method was called.
213
+ name - The name of the method.
214
+ location - The location of the method.
215
+
216
+ The interpretation of method names is:
217
+
218
+ * MyObject#test - An instance method "test" of the class "MyObject"
219
+ * <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
220
+
221
+ ########################################################################
222
+ # Profile w/ N=10000 (i=1000000)
223
+ # (n.b. RubyProf & tracepoint can change relative performance.
224
+ # A sampling profiler can provide more accurate relative metrics.
225
+ ########################################################################
226
+
227
+ Filling ruby binary heap ---------------------------
228
+ Profiling ruby binary heap ---------------------------
229
+ Measure Mode: wall_time
230
+ Thread ID: 1400
231
+ Fiber ID: 1380
232
+ Total: 13.737007
233
+ Sort by: self_time
234
+
235
+ %self total self wait child calls name location
236
+ 49.46 6.794 6.794 0.000 0.000 1000000 DHeap::Benchmarks::RbHeap#<< /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:107
237
+ 41.97 6.017 5.765 0.000 0.251 1000000 DHeap::Benchmarks::RbHeap#pop /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:121
238
+ 5.80 13.737 0.796 0.000 12.941 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
239
+
240
+ * recursively called methods
241
+
242
+ Columns are:
243
+
244
+ %self - The percentage of time spent in this method, derived from self_time/total_time.
245
+ total - The time spent in this method and its children.
246
+ self - The time spent in this method.
247
+ wait - The amount of time this method waited for other threads.
248
+ child - The time spent in this method's children.
249
+ calls - The number of times this method was called.
250
+ name - The name of the method.
251
+ location - The location of the method.
252
+
253
+ The interpretation of method names is:
254
+
255
+ * MyObject#test - An instance method "test" of the class "MyObject"
256
+ * <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
257
+
258
+ Filling quaternary DHeap ---------------------------
259
+ Profiling quaternary DHeap ---------------------------
260
+ Measure Mode: wall_time
261
+ Thread ID: 1400
262
+ Fiber ID: 1380
263
+ Total: 1.179968
264
+ Sort by: self_time
265
+
266
+ %self total self wait child calls name location
267
+ 62.48 1.180 0.737 0.000 0.443 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
268
+ 13.86 0.164 0.164 0.000 0.000 1000000 DHeap#<<
269
+ 13.46 0.159 0.159 0.000 0.000 1000000 DHeap#pop
270
+ 10.20 0.120 0.120 0.000 0.000 1000000 Array#fetch
271
+
272
+ * recursively called methods
273
+
274
+ Columns are:
275
+
276
+ %self - The percentage of time spent in this method, derived from self_time/total_time.
277
+ total - The time spent in this method and its children.
278
+ self - The time spent in this method.
279
+ wait - The amount of time this method waited for other threads.
280
+ child - The time spent in this method's children.
281
+ calls - The number of times this method was called.
282
+ name - The name of the method.
283
+ location - The location of the method.
284
+
285
+ The interpretation of method names is:
286
+
287
+ * MyObject#test - An instance method "test" of the class "MyObject"
288
+ * <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
289
+
290
+ ########################################################################
291
+ # Profile w/ N=100000 (i=1000000)
292
+ # (n.b. RubyProf & tracepoint can change relative performance.
293
+ # A sampling profiler can provide more accurate relative metrics.
294
+ ########################################################################
295
+
296
+ Filling ruby binary heap ---------------------------
297
+ Profiling ruby binary heap ---------------------------
298
+ Measure Mode: wall_time
299
+ Thread ID: 1400
300
+ Fiber ID: 1380
301
+ Total: 16.425915
302
+ Sort by: self_time
303
+
304
+ %self total self wait child calls name location
305
+ 50.99 8.623 8.376 0.000 0.247 1000000 DHeap::Benchmarks::RbHeap#pop /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:121
306
+ 41.82 6.869 6.869 0.000 0.000 1000000 DHeap::Benchmarks::RbHeap#<< /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:107
307
+ 4.89 16.426 0.803 0.000 15.623 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
308
+
309
+ * recursively called methods
310
+
311
+ Columns are:
312
+
313
+ %self - The percentage of time spent in this method, derived from self_time/total_time.
314
+ total - The time spent in this method and its children.
315
+ self - The time spent in this method.
316
+ wait - The amount of time this method waited for other threads.
317
+ child - The time spent in this method's children.
318
+ calls - The number of times this method was called.
319
+ name - The name of the method.
320
+ location - The location of the method.
321
+
322
+ The interpretation of method names is:
323
+
324
+ * MyObject#test - An instance method "test" of the class "MyObject"
325
+ * <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
326
+
327
+ Filling quaternary DHeap ---------------------------
328
+ Profiling quaternary DHeap ---------------------------
329
+ Measure Mode: wall_time
330
+ Thread ID: 1400
331
+ Fiber ID: 1380
332
+ Total: 1.205573
333
+ Sort by: self_time
334
+
335
+ %self total self wait child calls name location
336
+ 59.14 1.206 0.713 0.000 0.493 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
337
+ 16.93 0.204 0.204 0.000 0.000 1000000 DHeap#pop
338
+ 13.88 0.167 0.167 0.000 0.000 1000000 DHeap#<<
339
+ 10.05 0.121 0.121 0.000 0.000 1000000 Array#fetch
340
+
341
+ * recursively called methods
342
+
343
+ Columns are:
344
+
345
+ %self - The percentage of time spent in this method, derived from self_time/total_time.
346
+ total - The time spent in this method and its children.
347
+ self - The time spent in this method.
348
+ wait - The amount of time this method waited for other threads.
349
+ child - The time spent in this method's children.
350
+ calls - The number of times this method was called.
351
+ name - The name of the method.
352
+ location - The location of the method.
353
+
354
+ The interpretation of method names is:
355
+
356
+ * MyObject#test - An instance method "test" of the class "MyObject"
357
+ * <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
358
+
@@ -0,0 +1,7 @@
1
+ ---
2
+ inherit_from:
3
+ - "../../.rubocop.yml"
4
+
5
+ Style/GlobalVars:
6
+ AllowedVariables:
7
+ - "$defs"
data/ext/d_heap/d_heap.c CHANGED
@@ -1,99 +1,286 @@
1
- #include "d_heap.h"
2
-
3
- ID id_cmp; // <=>
4
- ID id_ivar_values;
5
- ID id_ivar_scores;
6
- ID id_ivar_d;
7
-
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) \
26
- ) // score, value
27
-
28
- #define DHEAP_Check_d_size(d) \
29
- if (d < 2) { \
30
- rb_raise(rb_eArgError, "DHeap d=%d is too small", d); \
31
- } \
32
- if (d > DHEAP_MAX_D) { \
33
- rb_raise(rb_eArgError, "DHeap d=%d is too large", d); \
34
- }
1
+ #include "ruby.h"
2
+ #include <float.h>
3
+ #include <math.h>
35
4
 
36
- #define DHEAP_Check_Index(index, last_index) \
37
- if (index < 0) { \
38
- rb_raise(rb_eIndexError, "DHeap index %ld too small", index); \
39
- } \
40
- else if (last_index < index) { \
41
- rb_raise(rb_eIndexError, "DHeap index %ld too large", index); \
42
- }
5
+ #if CHAR_BIT != 8
6
+ # error "DHeap assumes 8-bit bytes"
7
+ #endif
8
+
9
+ #if SIZE_MAX != ULONG_MAX
10
+ # error "DHeap assumes 'size_t' is 'unsigned long'"
11
+ #endif
12
+
13
+ #if LDBL_MANT_DIG < SIZEOF_UNSIGNED_LONG_LONG * 8
14
+ # error "DHeap assumes 'long double' mantissa can store 'unsigned long long'"
15
+ #endif
16
+
17
+ // TODO: test this code on a 32 bit system (it MUST have some bugs!)
18
+ // TODO: perhaps just convert from "unsigned long long" to "uint64_t"!
19
+ #if SIZEOF_UNSIGNED_LONG_LONG * 8 != 64
20
+ # error "DHeap assumes 64-bit 'unsigned long long'"
21
+ #endif
22
+
23
+ /********************************************************************
24
+ *
25
+ * Type definitions
26
+ *
27
+ ********************************************************************/
43
28
 
44
- struct dheap_struct {
45
- int d;
46
- VALUE scores;
47
- VALUE values;
48
- };
49
29
  typedef struct dheap_struct dheap_t;
30
+ typedef struct dheap_entry ENTRY;
31
+
32
+ typedef double SCORE;
33
+
34
+ /********************************************************************
35
+ *
36
+ * Struct definitions
37
+ *
38
+ ********************************************************************/
39
+
40
+ struct dheap_struct
41
+ {
42
+ int d;
43
+ size_t size;
44
+ size_t capa;
45
+ ENTRY *entries;
46
+ #ifdef DHEAP_MAP
47
+ VALUE indexes; // Hash
48
+ #endif
49
+ };
50
+
51
+ struct dheap_entry
52
+ {
53
+ SCORE score;
54
+ VALUE value;
55
+ };
56
+
57
+ #define DHEAPMAP_P(heap) UNLIKELY(RTEST((heap)->indexes))
58
+
59
+ /********************************************************************
60
+ *
61
+ * Constant definitions
62
+ *
63
+ ********************************************************************/
64
+
65
+ #define DHEAP_DEFAULT_D 6
66
+ #define DHEAP_MAX_D INT_MAX
67
+
68
+ // sizeof(ENTRY) => 16 bytes, 128-bits
69
+ // one kilobyte = 32 * 32 bytes
70
+ #define DHEAP_DEFAULT_CAPA 32
71
+ #define DHEAP_MAX_CAPA (SIZE_MAX / (int)sizeof(ENTRY))
72
+ #define DHEAP_CAPA_INCR_MAX (10 * 1024 * 1024 / (int)sizeof(ENTRY))
73
+
74
+ static ID id_cmp; // <=>
75
+ static ID id_abs; // abs
76
+ static ID id_lshift; // <<
77
+ static ID id_uminus; // -@
78
+
79
+ static const rb_data_type_t dheap_data_type;
80
+
81
+ /********************************************************************
82
+ *
83
+ * Metaprogramming macros
84
+ *
85
+ ********************************************************************/
86
+
87
+ #define LIKELY RB_LIKELY
88
+ #define UNLIKELY RB_UNLIKELY
89
+
90
+ #ifdef DHEAP_MAP
91
+ # define DHEAP_DISPATCH_EXPR(func, heap, ...) \
92
+ (DHEAPMAP_P(heap) ? dheapmap_##func(heap, __VA_ARGS__) \
93
+ : dheap_##func(heap, __VA_ARGS__))
94
+ #else
95
+ # define DHEAP_DISPATCH_EXPR(func, heap, ...) \
96
+ dheap_##func(heap, __VA_ARGS__);
97
+ #endif
98
+
99
+ #ifdef DHEAP_MAP
100
+ # define DHEAP_DISPATCH_STMT(heap, macro, ...) \
101
+ do { \
102
+ if (DHEAPMAP_P(heap)) { \
103
+ macro(dheapmap, heap, __VA_ARGS__); \
104
+ } else { \
105
+ macro(dheap, heap, __VA_ARGS__); \
106
+ } \
107
+ } while (0)
108
+ #else
109
+ # define DHEAP_DISPATCH_STMT(heap, macro, ...) \
110
+ macro(dheap, heap, __VA_ARGS__)
111
+ #endif
112
+
113
+ /********************************************************************
114
+ *
115
+ * SCORE: casting to and from VALUE
116
+ * adapted from similar methods in ruby's object.c
117
+ *
118
+ ********************************************************************/
119
+
120
+ #define SCORE2NUM(score) rb_float_new(score)
121
+ #define VAL2SCORE(val) NUM2DBL(RB_FLOAT_TYPE_P(val) ? val : rb_Float(val))
122
+ #define CMP_LT(a, b) ((a) < (b))
123
+ #define CMP_LTE(a, b) ((a) <= (b))
124
+
125
+ /********************************************************************
126
+ *
127
+ * DHeap ENTRY accessors
128
+ *
129
+ ********************************************************************/
50
130
 
131
+ #define DHEAP_SCORE(heap, idx) (DHEAP_GET(heap, idx).score)
132
+ #define DHEAP_VALUE(heap, idx) (DHEAP_GET(heap, idx).value)
133
+
134
+ #define DHEAP_ENTRY_ARY(heap, idx) \
135
+ (((heap)->size <= (idx)) \
136
+ ? Qnil \
137
+ : rb_ary_new_from_args( \
138
+ 2, DHEAP_VALUE(heap, idx), SCORE2NUM(DHEAP_SCORE(heap, idx))))
139
+
140
+ #define DHEAP_GET(heap, idx) ((heap)->entries[idx])
141
+ #define DHEAP_SET(T, heap, index, entry) \
142
+ do { \
143
+ DHEAP_GET(heap, index) = (entry); \
144
+ DHEAP_SET_##T(heap, index, entry); \
145
+ } while (0)
146
+
147
+ #define DHEAP_SET_dheap(heap, index, entry) /* noop */
148
+
149
+ #ifdef DHEAP_MAP
150
+ # define DHEAP_SET_dheapmap(heap, index, entry) \
151
+ rb_hash_aset((heap)->indexes, (entry).value, ULONG2NUM(index))
152
+ #endif
153
+
154
+ /********************************************************************
155
+ *
156
+ * DHeap index math
157
+ *
158
+ ********************************************************************/
159
+
160
+ #define DHEAP_IDX_LAST(heap) ((heap)->size - 1)
161
+ #define DHEAP_IDX_PARENT(heap, idx) (((idx)-1) / (heap)->d)
162
+ #define DHEAP_IDX_CHILD_0(heap, idx) (((idx) * (heap)->d) + 1)
163
+ #define DHEAP_IDX_CHILD_D(heap, idx) (((idx) * (heap)->d) + (heap)->d)
164
+
165
+ #ifdef DEBUG
166
+ # define ASSERT_DHEAP_IDX_OK(heap, index) \
167
+ do { \
168
+ if (DHEAP_IDX_LAST(heap) < index) { \
169
+ rb_raise(rb_eIndexError, "DHeap index %ld too large", index); \
170
+ } \
171
+ } while (0)
172
+ #else
173
+ # define ASSERT_DHEAP_IDX_OK(heap, index)
174
+ #endif
175
+
176
+ /********************************************************************
177
+ *
178
+ * rb_data_type_t definitions
179
+ *
180
+ ********************************************************************/
181
+
182
+ #ifdef HAVE_RB_GC_MARK_MOVABLE
51
183
  static void
52
184
  dheap_compact(void *ptr)
53
185
  {
54
186
  dheap_t *heap = ptr;
55
- if (heap->scores) dheap_gc_location( heap->scores );
56
- if (heap->values) dheap_gc_location( heap->values );
187
+ for (size_t i = 0; i < heap->size; ++i) {
188
+ if (DHEAP_VALUE(heap, i))
189
+ DHEAP_VALUE(heap, i) = rb_gc_location(DHEAP_VALUE(heap, i));
190
+ }
191
+ # ifdef DHEAP_MAP
192
+ if (DHEAPMAP_P(heap)) heap->indexes = rb_gc_location(heap->indexes);
193
+ # endif
57
194
  }
195
+ #else
196
+ # define rb_gc_mark_movable(x) rb_gc_mark(x)
197
+ #endif
58
198
 
59
199
  static void
60
200
  dheap_mark(void *ptr)
61
201
  {
62
202
  dheap_t *heap = ptr;
63
- if (heap->scores) rb_gc_mark_movable(heap->scores);
64
- if (heap->values) rb_gc_mark_movable(heap->values);
203
+ for (size_t i = 0; i < heap->size; ++i) {
204
+ if (DHEAP_VALUE(heap, i)) rb_gc_mark_movable(DHEAP_VALUE(heap, i));
205
+ }
206
+ #ifdef DHEAP_MAP
207
+ if (DHEAPMAP_P(heap)) rb_gc_mark_movable(heap->indexes);
208
+ #endif
209
+ }
210
+
211
+ static void
212
+ dheap_free(void *ptr)
213
+ {
214
+ dheap_t *heap = ptr;
215
+ heap->size = 0;
216
+ if (heap->entries) {
217
+ xfree(heap->entries);
218
+ heap->entries = NULL;
219
+ }
220
+ heap->capa = 0;
221
+ xfree(ptr);
222
+ #ifdef DHEAP_MAP
223
+ heap->indexes = Qnil;
224
+ #endif
65
225
  }
66
226
 
67
227
  static size_t
68
228
  dheap_memsize(const void *ptr)
69
229
  {
70
230
  const dheap_t *heap = ptr;
71
- size_t size = sizeof(*heap);
231
+ size_t size = 0;
232
+ size += sizeof(*heap);
233
+ size += sizeof(ENTRY) * heap->capa;
72
234
  return size;
73
235
  }
74
236
 
75
237
  static const rb_data_type_t dheap_data_type = {
76
238
  "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),
239
+ { (void (*)(void *))dheap_mark,
240
+ (void (*)(void *))dheap_free,
241
+ (size_t(*)(const void *))dheap_memsize,
242
+ #ifdef HAVE_RB_GC_MARK_MOVABLE
243
+ (void (*)(void *))dheap_compact,
244
+ { 0 }
245
+ #else
246
+ { 0 }
247
+ #endif
82
248
  },
83
- 0, 0,
249
+ 0,
250
+ 0,
84
251
  RUBY_TYPED_FREE_IMMEDIATELY,
85
252
  };
86
253
 
254
+ /********************************************************************
255
+ *
256
+ * DHeap comparisons
257
+ *
258
+ ********************************************************************/
259
+
260
+ /********************************************************************
261
+ *
262
+ * DHeap allocation and initialization and resizing
263
+ *
264
+ ********************************************************************/
265
+
87
266
  static VALUE
88
267
  dheap_s_alloc(VALUE klass)
89
268
  {
90
- VALUE obj;
269
+ VALUE obj;
91
270
  dheap_t *heap;
92
271
 
272
+ #pragma GCC diagnostic push
273
+ #pragma GCC diagnostic ignored "-Wpedantic"
274
+ // TypedData_Make_Struct uses a non-std "statement expression"
93
275
  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;
276
+ #pragma GCC diagnostic pop
277
+ heap->d = DHEAP_DEFAULT_D;
278
+ heap->size = 0;
279
+ heap->capa = 0;
280
+ heap->entries = NULL;
281
+ #ifdef DHEAP_MAP
282
+ heap->indexes = Qnil;
283
+ #endif
97
284
 
98
285
  return obj;
99
286
  }
@@ -103,168 +290,183 @@ get_dheap_struct(VALUE self)
103
290
  {
104
291
  dheap_t *heap;
105
292
  TypedData_Get_Struct(self, dheap_t, &dheap_data_type, heap);
106
- Check_Type(heap->scores, T_ARRAY);
107
293
  return heap;
108
294
  }
109
295
 
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);
296
+ static inline dheap_t *
297
+ get_dheap_struct_unfrozen(VALUE self)
298
+ {
299
+ rb_check_frozen(self);
300
+ return get_dheap_struct(self);
301
+ }
121
302
 
122
- int d = DHEAP_DEFAULT_D;
123
- if (argc) {
124
- d = NUM2INT(argv[0]);
303
+ void
304
+ dheap_set_capa(dheap_t *heap, size_t new_capa)
305
+ {
306
+ // Do nothing if we already have the capacity or are resizing too small
307
+ if (new_capa <= heap->capa || new_capa <= heap->size) return;
308
+
309
+ // allocate
310
+ if (heap->entries) {
311
+ RB_REALLOC_N(heap->entries, ENTRY, new_capa);
312
+ } else {
313
+ heap->entries = RB_ZALLOC_N(ENTRY, new_capa);
125
314
  }
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);
315
+ heap->capa = new_capa;
316
+ }
131
317
 
132
- return self;
318
+ static void
319
+ dheap_incr_capa(dheap_t *heap, size_t new_size)
320
+ {
321
+ size_t new_capa = heap->capa;
322
+ while (new_capa < new_size) {
323
+ if (new_capa <= DHEAP_CAPA_INCR_MAX) {
324
+ // double it... up to DHEAP_CAPA_INCR_MAX
325
+ new_capa *= 2;
326
+ } else if (DHEAP_MAX_CAPA - DHEAP_CAPA_INCR_MAX < heap->capa) {
327
+ // avoid overflow
328
+ new_capa = DHEAP_MAX_CAPA;
329
+ } else {
330
+ new_capa += DHEAP_CAPA_INCR_MAX;
331
+ }
332
+ }
333
+ dheap_set_capa(heap, new_capa);
133
334
  }
134
335
 
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;
336
+ static void
337
+ dheap_ensure_room_for_push(dheap_t *heap, size_t incr_by)
338
+ {
339
+ // check for overflow of new_size
340
+ if (DHEAP_MAX_CAPA - incr_by < heap->size) {
341
+ rb_raise(rb_eIndexError,
342
+ "size increase overflow: %zu + %zu",
343
+ heap->size,
344
+ incr_by);
345
+ } else {
346
+ size_t new_size = heap->size + incr_by;
347
+ // if existing capacity is too small
348
+ if (heap->capa < new_size) dheap_incr_capa(heap, new_size);
349
+ }
148
350
  }
149
351
 
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))
352
+ static inline int
353
+ dheap_value_to_int_d(VALUE num)
354
+ {
355
+ int d = NUM2INT(num);
356
+ if (d < 2) rb_raise(rb_eArgError, "DHeap d=%u is too small", d);
357
+ if (d > DHEAP_MAX_D) rb_raise(rb_eArgError, "DHeap d=%u is too large", d);
358
+ return d;
359
+ }
153
360
 
154
- static inline elem_t *
155
- get_dheap_element(VALUE obj)
361
+ static inline size_t
362
+ dheap_value_to_capa(VALUE num)
156
363
  {
157
- elem_t *elem;
158
- TypedData_Get_Struct((obj), elem_t, &dheap_elem_type, (elem));
159
- return elem;
364
+ size_t capa = NUM2ULONG(num);
365
+ if (capa < 1) {
366
+ rb_raise(rb_eArgError, "DHeap capa=%zu must be positive", capa);
367
+ }
368
+ return capa;
160
369
  }
161
370
 
162
- */
371
+ static VALUE
372
+ dheap_init(VALUE self, VALUE d, VALUE capa, VALUE map)
373
+ {
374
+ dheap_t *heap = get_dheap_struct(self);
163
375
 
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)
376
+ if (heap->entries || heap->size || heap->capa)
377
+ rb_raise(rb_eScriptError, "DHeap already initialized.");
168
378
 
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);
379
+ heap->d = dheap_value_to_int_d(d);
380
+ dheap_set_capa(heap, dheap_value_to_capa(capa));
381
+ #ifdef DHEAP_MAP
382
+ if (RTEST(map)) heap->indexes = rb_hash_new();
383
+ #endif
193
384
 
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);
385
+ return self;
196
386
  }
197
387
 
198
- VALUE
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);
388
+ /* @!visibility private */
389
+ static VALUE
390
+ dheap_initialize_copy(VALUE copy, VALUE orig)
391
+ {
392
+ dheap_t *heap_copy = get_dheap_struct_unfrozen(copy);
393
+ dheap_t *heap_orig = get_dheap_struct(orig);
205
394
 
206
- // sift it up to where it belongs
207
- for (long parent_index; 0 < sift_index; sift_index = parent_index) {
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);
395
+ heap_copy->d = heap_orig->d;
211
396
 
212
- // parent is smaller: heap is restored
213
- if (CMP_LTE(parent_score, sift_score)) break;
397
+ dheap_set_capa(heap_copy, heap_orig->capa);
398
+ heap_copy->size = heap_orig->size;
399
+ if (heap_copy->size)
400
+ MEMCPY(heap_copy->entries, heap_orig->entries, ENTRY, heap_orig->size);
401
+ #ifdef DHEAP_MAP
402
+ if (RTEST(heap_orig->indexes))
403
+ heap_copy->indexes = rb_hash_dup(heap_orig->indexes);
404
+ #endif
214
405
 
215
- // parent is larger: swap and continue sifting up
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);
219
- }
220
- debug(rb_sprintf("sifted (%"PRIsVALUE", %d, %ld)", heap->values, heap->d, sift_index));
221
- return LONG2NUM(sift_index);
222
- }
223
-
224
- VALUE
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);
231
-
232
- // iteratively sift it down to where it belongs
233
- for (long child_index; sift_index < last_index; sift_index = child_index) {
234
- debug(rb_sprintf("sift dn(%"PRIsVALUE", %d, %ld)", heap->values, heap->d, sift_index));
235
- // find first child index, and break if we've reached the last layer
236
- long child_idx0 = child_index = DHEAP_IDX_CHILD0(heap, sift_index);
237
- if (last_index < child_idx0) break;
238
-
239
- // find the min child (and its child_index)
240
- // requires "d" comparisons to find min child and compare to sift_score
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);
244
- child_index = child_idx0;
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);
249
-
250
- if (CMP_LT(sibling_score, child_score)) {
251
- child_score = sibling_score;
252
- child_index = sibling_index;
253
- }
254
- }
406
+ return copy;
407
+ }
255
408
 
256
- // child is larger: heap is restored
257
- if (CMP_LTE(sift_score, child_score)) break;
409
+ /********************************************************************
410
+ *
411
+ * DHeap sift up/down
412
+ *
413
+ ********************************************************************/
414
+
415
+ #define DHEAP_SIFT_UP(T, heap, i) \
416
+ do { \
417
+ size_t sift_idx = i; \
418
+ ENTRY entry = DHEAP_GET(heap, sift_idx); \
419
+ ASSERT_DHEAP_IDX_OK(heap, sift_idx); \
420
+ for (size_t parent_idx; 0 < sift_idx; sift_idx = parent_idx) { \
421
+ parent_idx = DHEAP_IDX_PARENT(heap, sift_idx); \
422
+ if (CMP_LTE(DHEAP_SCORE((heap), parent_idx), entry.score)) break; \
423
+ DHEAP_SET(T, heap, sift_idx, DHEAP_GET(heap, parent_idx)); \
424
+ } \
425
+ DHEAP_SET(T, heap, sift_idx, entry); \
426
+ } while (0)
427
+
428
+ static inline size_t
429
+ dheap_min_child(dheap_t *heap, size_t parent, size_t last_index)
430
+ {
431
+ size_t min_child = DHEAP_IDX_CHILD_0(heap, parent);
432
+ size_t last_sib = DHEAP_IDX_CHILD_D(heap, parent);
433
+ if (UNLIKELY(last_index < last_sib)) last_sib = last_index;
258
434
 
259
- // child is smaller: swap and continue sifting down
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);
435
+ for (size_t sibidx = min_child + 1; sibidx <= last_sib; ++sibidx) {
436
+ if (CMP_LT(DHEAP_SCORE(heap, sibidx), DHEAP_SCORE(heap, min_child))) {
437
+ min_child = sibidx;
438
+ }
263
439
  }
264
- debug(rb_sprintf("sifted (%"PRIsVALUE", %d, %ld)", heap->values, heap->d, sift_index));
265
- return LONG2NUM(sift_index);
440
+ return min_child;
266
441
  }
267
442
 
443
+ #define DHEAP_CAN_SIFT_DOWN(heap, index, last_index) \
444
+ (LIKELY(1 <= last_index && index <= DHEAP_IDX_PARENT(heap, last_index)))
445
+
446
+ #define DHEAP_SIFT_DOWN(T, heap, i) \
447
+ do { \
448
+ size_t sift_idx = i; \
449
+ size_t last_idx = DHEAP_IDX_LAST(heap); \
450
+ ASSERT_DHEAP_IDX_OK(heap, sift_idx); \
451
+ if (DHEAP_CAN_SIFT_DOWN(heap, sift_idx, last_idx)) { \
452
+ ENTRY entry = heap->entries[sift_idx]; \
453
+ size_t last_parent = DHEAP_IDX_PARENT(heap, last_idx); \
454
+ while (sift_idx <= last_parent) { \
455
+ size_t min_child = dheap_min_child(heap, sift_idx, last_idx); \
456
+ if (CMP_LTE(entry.score, DHEAP_SCORE(heap, min_child))) break; \
457
+ DHEAP_SET(T, heap, sift_idx, (heap)->entries[min_child]); \
458
+ sift_idx = min_child; \
459
+ } \
460
+ DHEAP_SET(T, heap, sift_idx, entry); \
461
+ } \
462
+ } while (0)
463
+
464
+ /********************************************************************
465
+ *
466
+ * DHeap attributes
467
+ *
468
+ ********************************************************************/
469
+
268
470
  /*
269
471
  * @return [Integer] the number of elements in the heap
270
472
  */
@@ -272,19 +474,19 @@ static VALUE
272
474
  dheap_size(VALUE self)
273
475
  {
274
476
  dheap_t *heap = get_dheap_struct(self);
275
- long size = DHEAP_SIZE(heap);
276
- return LONG2NUM(size);
477
+ return ULONG2NUM(heap->size);
277
478
  }
278
479
 
480
+ #define DHEAP_EMPTY_P(heap) UNLIKELY((heap)->size <= 0)
481
+
279
482
  /*
280
- * @return [Boolean] is the heap empty?
483
+ * @return [Boolean] if the heap is empty
281
484
  */
282
485
  static VALUE
283
486
  dheap_empty_p(VALUE self)
284
487
  {
285
488
  dheap_t *heap = get_dheap_struct(self);
286
- long size = DHEAP_SIZE(heap);
287
- return size == 0 ? Qtrue : Qfalse;
489
+ return DHEAP_EMPTY_P(heap) ? Qtrue : Qfalse;
288
490
  }
289
491
 
290
492
  /*
@@ -297,188 +499,608 @@ dheap_attr_d(VALUE self)
297
499
  return INT2FIX(heap->d);
298
500
  }
299
501
 
502
+ /********************************************************************
503
+ *
504
+ * DHeap push
505
+ *
506
+ ********************************************************************/
507
+
508
+ #define DHEAP_PUSH(T, heap, entry) \
509
+ do { \
510
+ dheap_ensure_room_for_push(heap, 1); \
511
+ DHEAP_SET(T, heap, (heap)->size, *(entry)); \
512
+ ++heap->size; \
513
+ DHEAP_SIFT_UP(T, heap, DHEAP_IDX_LAST(heap)); \
514
+ } while (0)
515
+
516
+ static inline void
517
+ dheap_push_entry(VALUE self, ENTRY *entry)
518
+ {
519
+ dheap_t *heap = get_dheap_struct_unfrozen(self);
520
+ DHEAP_PUSH(dheap, heap, entry);
521
+ }
522
+
523
+ #ifdef DHEAP_MAP
524
+ static inline void
525
+ dheapmap_update_entry(dheap_t *heap, size_t index, ENTRY *entry)
526
+ {
527
+ SCORE prev = DHEAP_SCORE(heap, index);
528
+ DHEAP_SET(dheapmap, heap, index, *entry);
529
+ if (CMP_LT(prev, entry->score)) {
530
+ DHEAP_SIFT_DOWN(dheapmap, heap, index);
531
+ } else {
532
+ DHEAP_SIFT_UP(dheapmap, heap, index);
533
+ }
534
+ }
535
+
536
+ static inline void
537
+ dheapmap_push_entry(VALUE self, ENTRY *entry)
538
+ {
539
+ dheap_t *heap = get_dheap_struct_unfrozen(self);
540
+ VALUE idxval = rb_hash_lookup2(heap->indexes, entry->value, Qfalse);
541
+ if (idxval) {
542
+ size_t index = NUM2ULONG(idxval);
543
+ dheapmap_update_entry(heap, index, entry);
544
+ return;
545
+ }
546
+ DHEAP_PUSH(dheapmap, heap, entry);
547
+ }
548
+ #endif
549
+
300
550
  /*
301
- * Freezes the heap as well as its underlying array, but does <i>not</i>
302
- * deep-freeze the elements in the heap.
551
+ * Inserts a value into the heap, using a score to determine sort-order.
552
+ *
553
+ * Score comes first, as an analogy with the +Array#insert+ index.
554
+ *
555
+ * Time complexity: <b>O(log n / log d)</b> <i>(worst-case)</i>
556
+ *
557
+ * @param score [Integer,Float,#to_f] a score to compare against other scores.
558
+ * @param value [Object] an object that is associated with the score.
303
559
  *
304
560
  * @return [self]
305
561
  */
306
562
  static VALUE
307
- dheap_freeze(VALUE self) {
308
- dheap_t *heap = get_dheap_struct(self);
309
- ID id_freeze = rb_intern("freeze");
310
- rb_funcall(heap->scores, id_freeze, 0);
311
- rb_funcall(heap->values, id_freeze, 0);
312
- return rb_call_super(0, NULL);
563
+ dheap_insert(VALUE self, VALUE score, VALUE value)
564
+ {
565
+ ENTRY entry = { VAL2SCORE(score), value };
566
+ dheap_push_entry(self, &entry);
567
+ return self;
568
+ }
569
+
570
+ #ifdef DHEAP_MAP
571
+ /* (see DHeap#insert) */
572
+ static VALUE
573
+ dheapmap_insert(VALUE self, VALUE score, VALUE value)
574
+ {
575
+ ENTRY entry = { VAL2SCORE(score), value };
576
+ dheapmap_push_entry(self, &entry);
577
+ return self;
578
+ }
579
+ #endif
580
+
581
+ static inline ENTRY
582
+ dheap_push_args_to_entry(int argc, VALUE *argv)
583
+ {
584
+ ENTRY entry;
585
+ rb_check_arity(argc, 1, 2);
586
+ entry.value = argv[0];
587
+ entry.score = VAL2SCORE(argc < 2 ? entry.value : argv[1]);
588
+ return entry;
313
589
  }
314
590
 
315
591
  /*
316
- * @overload push(score, value = score)
592
+ * @overload push(value, score = value)
317
593
  *
318
594
  * Push a value onto heap, using a score to determine sort-order.
319
595
  *
320
- * Ideally, the score should be a frozen value that can be efficiently compared
321
- * to other scores, e.g. an Integer or Float or (maybe) a String
596
+ * Value comes first because the separate score is optional, and because it
597
+ * feels like a more natural variation on +Array#push+ or +Queue#enq+. If a
598
+ * score isn't provided, the value must be an Integer or can be cast with
599
+ * +Float(value)+.
322
600
  *
323
601
  * Time complexity: <b>O(log n / log d)</b> <i>(worst-case)</i>
324
602
  *
325
- * @param score [#<=>] a value that can be compared to other scores.
326
603
  * @param value [Object] an object that is associated with the score.
604
+ * @param score [Integer,Float,#to_f] a score to compare against other scores.
327
605
  *
328
- * @return [Integer] the index of the value's final position.
606
+ * @return [self]
329
607
  */
330
608
  static VALUE
331
- dheap_push(int argc, VALUE *argv, VALUE self) {
332
- rb_check_arity(argc, 1, 2);
333
- VALUE scr = argv[0];
334
- VALUE val = argc < 2 ? scr : argv[1];
335
-
336
- dheap_t *heap = get_dheap_struct(self);
609
+ dheap_push(int argc, VALUE *argv, VALUE self)
610
+ {
611
+ ENTRY entry = dheap_push_args_to_entry(argc, argv);
612
+ dheap_push_entry(self, &entry);
613
+ return self;
614
+ }
337
615
 
338
- DHEAP_APPEND(heap, scr, val);
339
- long last_index = DHEAP_IDX_LAST(heap);
340
- return dheap_ary_sift_up(heap, last_index);
616
+ #ifdef DHEAP_MAP
617
+ /* (see DHeap#push) */
618
+ static VALUE
619
+ dheapmap_push(int argc, VALUE *argv, VALUE self)
620
+ {
621
+ ENTRY entry = dheap_push_args_to_entry(argc, argv);
622
+ dheapmap_push_entry(self, &entry);
623
+ return self;
341
624
  }
625
+ #endif
342
626
 
343
627
  /*
344
- * Pushes a comparable value onto the heap.
628
+ * Pushes a value onto the heap.
345
629
  *
346
- * The value will be its own score.
630
+ * The score will be derived from the value, by using the value itself if it is
631
+ * an Integer, otherwise by casting it with +Float(value)+.
347
632
  *
348
633
  * Time complexity: <b>O(log n / log d)</b> <i>(worst-case)</i>
349
634
  *
350
- * @param value [#<=>] a value that can be compared to other heap members.
635
+ * @param value [Integer,#to_f] a value with an intrinsic numeric score
351
636
  * @return [self]
352
637
  */
353
638
  static VALUE
354
- dheap_left_shift(VALUE self, VALUE value) {
355
- dheap_push(1, &value, self);
639
+ dheap_lshift(VALUE self, VALUE value)
640
+ {
641
+ ENTRY entry = { VAL2SCORE(value), value };
642
+ dheap_push_entry(self, &entry);
643
+ return self;
644
+ }
645
+
646
+ #ifdef DHEAP_MAP
647
+ /* (see DHeap#<<) */
648
+ static VALUE
649
+ dheapmap_lshift(VALUE self, VALUE value)
650
+ {
651
+ ENTRY entry = { VAL2SCORE(value), value };
652
+ dheapmap_push_entry(self, &entry);
356
653
  return self;
357
654
  }
655
+ #endif
656
+
657
+ /********************************************************************
658
+ *
659
+ * DHeap pop and peek
660
+ *
661
+ ********************************************************************/
662
+
663
+ #define PEEK_VALUE(heap) DHEAP_VALUE(heap, 0)
664
+ #define PEEK_SCORE(heap) DHEAP_SCORE(heap, 0)
665
+ #define PEEK_WITH_SCORE(heap) DHEAP_ENTRY_ARY(heap, 0)
666
+ #define PEEK_LT_P(heap, max_score) _PEEK_CMP_P(heap, CMP_LT, max_score)
667
+ #define PEEK_LTE_P(heap, max_score) _PEEK_CMP_P(heap, CMP_LTE, max_score)
668
+ #define _PEEK_CMP_P(heap, cmp, score) \
669
+ ((!DHEAP_EMPTY_P(heap) && cmp(PEEK_SCORE(heap), (score))) \
670
+ ? PEEK_VALUE(heap) \
671
+ : 0)
358
672
 
359
673
  /*
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
- */
674
+ * Returns the next value on the heap, and its score, without popping it
675
+ *
676
+ * Time complexity: <b>O(1)</b> <i>(worst-case)</i>
677
+ * @return [nil,Array<(Object, Numeric)>] the next value and its score
678
+ *
679
+ * @see #peek
680
+ * @see #peek_score
681
+ */
682
+ static VALUE
683
+ dheap_peek_with_score(VALUE self)
684
+ {
685
+ dheap_t *heap = get_dheap_struct(self);
686
+ return PEEK_WITH_SCORE(heap);
687
+ }
366
688
 
367
- static inline void
368
- dheap_pop_swap_last_and_sift_down(dheap_t *heap, long last_index)
689
+ /*
690
+ * Returns the next score on the heap, without the value and without popping
691
+ * it.
692
+ *
693
+ * Time complexity: <b>O(1)</b> <i>(worst-case)</i>
694
+ * @return [nil, Numeric] the next score, if there is one
695
+ *
696
+ * @see #peek
697
+ * @see #peek_with_score
698
+ */
699
+ static VALUE
700
+ dheap_peek_score(VALUE self)
369
701
  {
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
- }
702
+ dheap_t *heap = get_dheap_struct(self);
703
+ if (DHEAP_EMPTY_P(heap)) return Qnil;
704
+ return SCORE2NUM(PEEK_SCORE(heap));
381
705
  }
382
706
 
383
707
  /*
384
708
  * Returns the next value on the heap to be popped without popping it.
385
709
  *
386
710
  * Time complexity: <b>O(1)</b> <i>(worst-case)</i>
387
- * @return [Object] the next value to be popped without popping it.
711
+ * @return [nil, Object] the next value to be popped without popping it.
712
+ *
713
+ * @see #peek_score
714
+ * @see #peek_with_score
388
715
  */
389
716
  static VALUE
390
- dheap_peek(VALUE self) {
717
+ dheap_peek(VALUE self)
718
+ {
391
719
  dheap_t *heap = get_dheap_struct(self);
392
- if (DHEAP_IDX_LAST(heap) < 0) return Qnil;
393
- return DHEAP_VALUE(heap, 0);
720
+ if (DHEAP_EMPTY_P(heap)) return Qnil;
721
+ return PEEK_VALUE(heap);
394
722
  }
395
723
 
724
+ #define DHEAP_POP_COMMON_INIT(self) \
725
+ dheap_t *heap = get_dheap_struct_unfrozen(self); \
726
+ if (DHEAP_EMPTY_P(heap)) return Qnil;
727
+
728
+ #define DHEAP_DELETE_0(T, heap) \
729
+ do { \
730
+ _DELETE_ENTRY(T, heap, 0); \
731
+ if (0 < --(heap)->size) { \
732
+ DHEAP_SET(T, (heap), 0, (heap)->entries[(heap)->size]); \
733
+ DHEAP_SIFT_DOWN(T, (heap), 0); \
734
+ } \
735
+ } while (0)
736
+
737
+ #define _DELETE_ENTRY(T, heap, idx) _DELETE_ENTRY_##T(heap, idx)
738
+ #define _DELETE_ENTRY_dheap(heap, idx) /* noop */
739
+ #define _DELETE_ENTRY_dheapmap(heap, idx) \
740
+ rb_hash_delete(heap->indexes, DHEAP_VALUE(heap, idx))
741
+
742
+ #define POP(T, heap, popped) _POP(T, VALUE, heap, popped)
743
+ #define POP_WITH_SCORE(T, heap, popped) _POP(T, WITH_SCORE, heap, popped)
744
+ #define POP_LT(T, heap, max, popped) _POP_CMP(T, heap, LT, max, popped)
745
+ #define POP_LTE(T, heap, max, popped) _POP_CMP(T, heap, LTE, max, popped)
746
+
747
+ #define _POP_CMP(T, heap, cmp, cmp_score, popped) \
748
+ do { \
749
+ if ((*(popped) = PEEK_##cmp##_P(heap, cmp_score))) { \
750
+ DHEAP_DELETE_0(T, heap); \
751
+ } else { \
752
+ *(popped) = Qnil; \
753
+ } \
754
+ } while (0)
755
+
756
+ #define _POP(T, peek_type, heap, popped) \
757
+ do { \
758
+ if (DHEAP_EMPTY_P(heap)) { \
759
+ *(popped) = Qnil; \
760
+ } else { \
761
+ *(popped) = PEEK_##peek_type(heap); \
762
+ DHEAP_DELETE_0(T, heap); \
763
+ } \
764
+ } while (0)
765
+
396
766
  /*
397
767
  * Pops the minimum value from the top of the heap
398
768
  *
399
769
  * Time complexity: <b>O(d log n / log d)</b> <i>(worst-case)</i>
770
+ *
771
+ * @return [Object] the value with the minimum score
772
+ *
773
+ * @see #peek
774
+ * @see #pop_lt
775
+ * @see #pop_lte
776
+ * @see #pop_with_score
400
777
  */
401
778
  static VALUE
402
- dheap_pop(VALUE self) {
403
- dheap_t *heap = get_dheap_struct(self);
404
- long last_index = DHEAP_IDX_LAST(heap);
405
-
406
- if (last_index < 0) return Qnil;
407
- VALUE pop_value = DHEAP_VALUE(heap, 0);
779
+ dheap_pop(VALUE self)
780
+ {
781
+ dheap_t *heap = get_dheap_struct_unfrozen(self);
782
+ VALUE popped;
783
+ POP(dheap, heap, &popped);
784
+ return popped;
785
+ }
408
786
 
409
- dheap_pop_swap_last_and_sift_down(heap, last_index);
410
- return pop_value;
787
+ #ifdef DHEAP_MAP
788
+ /* (see DHeap#pop) */
789
+ static VALUE
790
+ dheapmap_pop(VALUE self)
791
+ {
792
+ dheap_t *heap = get_dheap_struct_unfrozen(self);
793
+ VALUE popped;
794
+ POP(dheapmap, heap, &popped);
795
+ return popped;
411
796
  }
797
+ #endif
412
798
 
413
799
  /*
414
- * Pops the minimum value only if it is less than or equal to a max score.
800
+ * Pops the minimum value from the top of the heap, along with its score.
801
+ *
802
+ * Time complexity: <b>O(d log n / log d)</b> <i>(worst-case)</i>
415
803
  *
416
- * @param max_score [#<=>] the maximum score to be popped
804
+ * @return [nil,Array<(Object, Numeric)>] the next value and its score
417
805
  *
418
806
  * @see #pop
807
+ * @see #peek_with_score
419
808
  */
420
809
  static VALUE
421
- dheap_pop_lte(VALUE self, VALUE max_score) {
422
- dheap_t *heap = get_dheap_struct(self);
423
- long last_index = DHEAP_IDX_LAST(heap);
810
+ dheap_pop_with_score(VALUE self)
811
+ {
812
+ dheap_t *heap = get_dheap_struct_unfrozen(self);
813
+ VALUE popped;
814
+ POP_WITH_SCORE(dheap, heap, &popped);
815
+ return popped;
816
+ }
424
817
 
425
- if (last_index < 0) return Qnil;
426
- VALUE pop_value = DHEAP_VALUE(heap, 0);
818
+ #ifdef DHEAP_MAP
819
+ /* (see DHeap#pop_with_score) */
820
+ static VALUE
821
+ dheapmap_pop_with_score(VALUE self)
822
+ {
823
+ dheap_t *heap = get_dheap_struct_unfrozen(self);
824
+ VALUE popped;
825
+ POP_WITH_SCORE(dheapmap, heap, &popped);
826
+ return popped;
827
+ }
828
+ #endif
427
829
 
428
- VALUE pop_score = DHEAP_SCORE(heap, 0);
429
- if (max_score && !CMP_LTE(pop_score, max_score)) return Qnil;
830
+ #define DHEAP_POP_IF(heap, cmp, max_score) \
831
+ if (!cmp(PEEK_SCORE(heap), VAL2SCORE(max_score))) return Qnil
832
+
833
+ /*
834
+ * Pops the minimum value only if it is less than or equal to a max score.
835
+ *
836
+ * @param max_score [Integer,#to_f] the maximum score to be popped
837
+ *
838
+ * Time complexity: <b>O(d log n / log d)</b> <i>(worst-case)</i>
839
+ *
840
+ * @return [Object] the value with the minimum score
841
+ *
842
+ * @see #peek
843
+ * @see #pop
844
+ * @see #pop_lt
845
+ * @see #pop_all_below
846
+ */
847
+ static VALUE
848
+ dheap_pop_lte(VALUE self, VALUE max_score)
849
+ {
850
+ dheap_t *heap = get_dheap_struct_unfrozen(self);
851
+ VALUE popped;
852
+ POP_LTE(dheap, heap, VAL2SCORE(max_score), &popped);
853
+ return popped;
854
+ }
430
855
 
431
- dheap_pop_swap_last_and_sift_down(heap, last_index);
432
- return pop_value;
856
+ #ifdef DHEAP_MAP
857
+ /* (see DHeap#pop_lte) */
858
+ static VALUE
859
+ dheapmap_pop_lte(VALUE self, VALUE max_score)
860
+ {
861
+ dheap_t *heap = get_dheap_struct_unfrozen(self);
862
+ VALUE popped;
863
+ POP_LTE(dheapmap, heap, VAL2SCORE(max_score), &popped);
864
+ return popped;
433
865
  }
866
+ #endif
434
867
 
435
868
  /*
436
869
  * Pops the minimum value only if it is less than a max score.
437
870
  *
438
- * @param max_score [#<=>] the maximum score to be popped
871
+ * @param max_score [Integer,#to_f] the maximum score to be popped
439
872
  *
440
873
  * Time complexity: <b>O(d log n / log d)</b> <i>(worst-case)</i>
874
+ *
875
+ * @return [Object] the value with the minimum score
876
+ *
877
+ * @see #peek
878
+ * @see #pop
879
+ * @see #pop_lte
880
+ * @see #pop_all_below
441
881
  */
442
882
  static VALUE
443
- dheap_pop_lt(VALUE self, VALUE max_score) {
444
- dheap_t *heap = get_dheap_struct(self);
445
- long last_index = DHEAP_IDX_LAST(heap);
883
+ dheap_pop_lt(VALUE self, VALUE max_score)
884
+ {
885
+ dheap_t *heap = get_dheap_struct_unfrozen(self);
886
+ VALUE popped;
887
+ POP_LT(dheap, heap, VAL2SCORE(max_score), &popped);
888
+ return popped;
889
+ }
446
890
 
447
- if (last_index < 0) return Qnil;
448
- VALUE pop_value = DHEAP_VALUE(heap, 0);
891
+ #ifdef DHEAP_MAP
892
+ /* (see DHeap#pop_lt) */
893
+ static VALUE
894
+ dheapmap_pop_lt(VALUE self, VALUE max_score)
895
+ {
896
+ dheap_t *heap = get_dheap_struct_unfrozen(self);
897
+ VALUE popped;
898
+ POP_LT(dheapmap, heap, VAL2SCORE(max_score), &popped);
899
+ return popped;
900
+ }
901
+ #endif
902
+
903
+ #define POP_ALL_BELOW(T, heap, max_score, array) \
904
+ do { \
905
+ VALUE val = Qnil; \
906
+ if (RB_TYPE_P(array, T_ARRAY)) { \
907
+ while ((val = PEEK_LT_P(heap, max_score))) { \
908
+ DHEAP_DELETE_0(T, heap); \
909
+ rb_ary_push(array, val); \
910
+ } \
911
+ } else { \
912
+ while ((val = PEEK_LT_P(heap, max_score))) { \
913
+ DHEAP_DELETE_0(T, heap); \
914
+ rb_funcall(array, id_lshift, 1, val); \
915
+ } \
916
+ } \
917
+ } while (0)
918
+
919
+ /*
920
+ * @overload pop_all_below(max_score, receiver = [])
921
+ *
922
+ * Pops all value with score less than max score.
923
+ *
924
+ * Time complexity: <b>O(m * d log n / log d)</b>, <i>m = number popped</i>
925
+ *
926
+ * @param max_score [Integer,#to_f] the maximum score to be popped
927
+ * @param receiver [Array,#<<] object onto which the values will be pushed,
928
+ * in order by score.
929
+ *
930
+ * @return [Object] the object onto which the values were pushed
931
+ *
932
+ * @see #pop
933
+ * @see #pop_lt
934
+ */
935
+ static VALUE
936
+ dheap_pop_all_below(int argc, VALUE *argv, VALUE self)
937
+ {
938
+ dheap_t *heap = get_dheap_struct_unfrozen(self);
939
+ SCORE max_score = (argc) ? VAL2SCORE(argv[0]) : 0.0;
940
+ VALUE array = (argc == 1) ? rb_ary_new() : argv[1];
941
+ rb_check_arity(argc, 1, 2);
942
+ DHEAP_DISPATCH_STMT(heap, POP_ALL_BELOW, max_score, array);
943
+ return array;
944
+ }
449
945
 
450
- VALUE pop_score = DHEAP_SCORE(heap, 0);
451
- if (max_score && !CMP_LT(pop_score, max_score)) return Qnil;
946
+ /********************************************************************
947
+ *
948
+ * DHeap, misc methods
949
+ *
950
+ ********************************************************************/
452
951
 
453
- dheap_pop_swap_last_and_sift_down(heap, last_index);
454
- return pop_value;
952
+ static VALUE
953
+ dheap_to_a(VALUE self)
954
+ {
955
+ dheap_t *heap = get_dheap_struct(self);
956
+ VALUE array = rb_ary_new_capa(heap->size);
957
+ for (size_t i = 0; i < heap->size; i++) {
958
+ rb_ary_push(array, DHEAP_ENTRY_ARY(heap, i));
959
+ }
960
+ return array;
455
961
  }
456
962
 
963
+ /*
964
+ * Clears all values from the heap, leaving it empty.
965
+ *
966
+ * @return [self]
967
+ */
968
+ static VALUE
969
+ dheap_clear(VALUE self)
970
+ {
971
+ dheap_t *heap = get_dheap_struct_unfrozen(self);
972
+ if (!DHEAP_EMPTY_P(heap)) {
973
+ heap->size = 0;
974
+ #ifdef DHEAP_MAP
975
+ if (DHEAPMAP_P(heap)) rb_hash_clear(heap->indexes);
976
+ #endif
977
+ }
978
+ return self;
979
+ }
980
+
981
+ /********************************************************************
982
+ *
983
+ * DHeap::Map methods
984
+ *
985
+ ********************************************************************/
986
+
987
+ #ifdef DHEAP_MAP
988
+
989
+ /*
990
+ * Retrieves the score that has been assigned to a heap member.
991
+ *
992
+ * Time complexity: <b>O(1)</b>
993
+ *
994
+ * @param object [Object] an object to lookup
995
+ * @return [Float,Integer,nil] the score associated with the object,
996
+ * or nil if the object isn't a member
997
+ */
998
+ static VALUE
999
+ dheapmap_aref(VALUE self, VALUE object)
1000
+ {
1001
+ dheap_t *heap = get_dheap_struct(self);
1002
+ VALUE idxval = rb_hash_lookup2(heap->indexes, object, Qfalse);
1003
+ if (idxval) {
1004
+ size_t index = NUM2ULONG(idxval);
1005
+ return SCORE2NUM(DHEAP_SCORE(heap, index));
1006
+ }
1007
+ return Qnil;
1008
+ }
1009
+
1010
+ /*
1011
+ * Assign a score to an object, adding it to the heap or updating as necessary.
1012
+ *
1013
+ * Time complexity: <b>O(log n)</b> (score decrease will be faster than
1014
+ * score increase)
1015
+ *
1016
+ * @param object [Object] an object to lookup
1017
+ * @param score [Integer,#to_f] the score to set
1018
+ * @return [Float,Integer] the score
1019
+ */
1020
+ static VALUE
1021
+ dheapmap_aset(VALUE self, VALUE object, VALUE score)
1022
+ {
1023
+ dheapmap_insert(self, score, object);
1024
+ return score;
1025
+ }
1026
+
1027
+ #endif
1028
+
1029
+ /********************************************************************
1030
+ *
1031
+ * DHeap setup
1032
+ *
1033
+ ********************************************************************/
1034
+
457
1035
  void
458
1036
  Init_d_heap(void)
459
1037
  {
460
- id_cmp = rb_intern_const("<=>");
461
- id_ivar_values = rb_intern_const("values");
462
- id_ivar_scores = rb_intern_const("scores");
463
- id_ivar_d = rb_intern_const("d");
1038
+ VALUE rb_cDHeap = rb_define_class("DHeap", rb_cObject);
1039
+ #ifdef DHEAP_MAP
1040
+ VALUE rb_cDHeapMap = rb_define_class_under(rb_cDHeap, "Map", rb_cDHeap);
1041
+ #endif
1042
+
1043
+ id_cmp = rb_intern_const("<=>");
1044
+ id_abs = rb_intern_const("abs");
1045
+ id_lshift = rb_intern_const("<<");
1046
+ id_uminus = rb_intern_const("-@");
464
1047
 
465
- rb_cDHeap = rb_define_class("DHeap", rb_cObject);
466
1048
  rb_define_alloc_func(rb_cDHeap, dheap_s_alloc);
467
1049
 
1050
+ #ifdef DHEAP_MAP
1051
+ # define def_override_inherited(rb_name, c_name, argc) \
1052
+ rb_define_method(rb_cDHeap, rb_name, dheap_##c_name, argc); \
1053
+ rb_define_method(rb_cDHeapMap, rb_name, dheapmap_##c_name, argc);
1054
+ #else
1055
+ # define def_override_inherited(rb_name, c_name, argc) \
1056
+ rb_define_method(rb_cDHeap, rb_name, dheap_##c_name, argc);
1057
+ #endif
1058
+
1059
+ /*
1060
+ * This is based on INT_MAX. But it is very very unlikely you will want a
1061
+ * large value for d. The tradeoff is that higher d values give faster push
1062
+ * and slower pop. If you expect pushes and pops to be balanced, then just
1063
+ * stick with the default. If you expect more pushes than pops, it might be
1064
+ * worthwhile to increase d.
1065
+ */
468
1066
  rb_define_const(rb_cDHeap, "MAX_D", INT2NUM(DHEAP_MAX_D));
1067
+
1068
+ /*
1069
+ * d=4 uses the fewest comparisons for (worst case) insert + delete-min.
1070
+ */
469
1071
  rb_define_const(rb_cDHeap, "DEFAULT_D", INT2NUM(DHEAP_DEFAULT_D));
470
1072
 
471
- rb_define_method(rb_cDHeap, "initialize", dheap_initialize, -1);
472
- rb_define_method(rb_cDHeap, "d", dheap_attr_d, 0);
473
- rb_define_method(rb_cDHeap, "freeze", dheap_freeze, 0);
1073
+ /*
1074
+ * The default heap capacity. The heap grows automatically as necessary, so
1075
+ * you shouldn't need to worry about this.
1076
+ */
1077
+ rb_define_const(rb_cDHeap, "DEFAULT_CAPA", INT2NUM(DHEAP_DEFAULT_CAPA));
1078
+
1079
+ rb_define_private_method(rb_cDHeap, "__init_without_kw__", dheap_init, 3);
1080
+ rb_define_method(rb_cDHeap, "initialize_copy", dheap_initialize_copy, 1);
474
1081
 
1082
+ rb_define_method(rb_cDHeap, "d", dheap_attr_d, 0);
475
1083
  rb_define_method(rb_cDHeap, "size", dheap_size, 0);
476
1084
  rb_define_method(rb_cDHeap, "empty?", dheap_empty_p, 0);
477
-
478
- rb_define_method(rb_cDHeap, "peek", dheap_peek, 0);
479
- rb_define_method(rb_cDHeap, "push", dheap_push, -1);
480
- rb_define_method(rb_cDHeap, "<<", dheap_left_shift, 1);
481
- rb_define_method(rb_cDHeap, "pop", dheap_pop, 0);
482
- rb_define_method(rb_cDHeap, "pop_lt", dheap_pop_lt, 1);
483
- rb_define_method(rb_cDHeap, "pop_lte", dheap_pop_lte, 1);
1085
+ rb_define_method(rb_cDHeap, "to_a", dheap_to_a, 0);
1086
+
1087
+ rb_define_method(rb_cDHeap, "clear", dheap_clear, 0);
1088
+ rb_define_method(rb_cDHeap, "peek", dheap_peek, 0);
1089
+ rb_define_method(rb_cDHeap, "peek_score", dheap_peek_score, 0);
1090
+ rb_define_method(rb_cDHeap, "peek_with_score", dheap_peek_with_score, 0);
1091
+ rb_define_method(rb_cDHeap, "pop_all_below", dheap_pop_all_below, -1);
1092
+
1093
+ def_override_inherited("insert", insert, 2);
1094
+ def_override_inherited("push", push, -1);
1095
+ def_override_inherited("<<", lshift, 1);
1096
+
1097
+ def_override_inherited("pop", pop, 0);
1098
+ def_override_inherited("pop_lt", pop_lt, 1);
1099
+ def_override_inherited("pop_lte", pop_lte, 1);
1100
+ def_override_inherited("pop_with_score", pop_with_score, 0);
1101
+
1102
+ #ifdef DHEAP_MAP
1103
+ rb_define_method(rb_cDHeapMap, "[]", dheapmap_aref, 1);
1104
+ rb_define_method(rb_cDHeapMap, "[]=", dheapmap_aset, 2);
1105
+ #endif
484
1106
  }