d_heap 0.3.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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
  }