d_heap 0.2.0 → 0.5.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.
@@ -0,0 +1,392 @@
1
+ Profiling run at 2021-01-10 21:34:56 -0500
2
+ ruby v2.7.2, DHeap v0.3.0
3
+
4
+ ~~~~~~ filling @dheap_bm_random_vals with 1.0M ~~~~~~
5
+ ########################################################################
6
+ # Profile w/ N=5 (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 push and resort ---------------------------
12
+ Profiling push and resort ---------------------------
13
+ Measure Mode: wall_time
14
+ Thread ID: 1360
15
+ Fiber ID: 1340
16
+ Total: 2.639873
17
+ Sort by: self_time
18
+
19
+ %self total self wait child calls name location
20
+ 31.49 2.640 0.831 0.000 1.808 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
21
+ 25.69 1.219 0.678 0.000 0.541 1000000 DHeap::Benchmarks::Sorting#<< /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:35
22
+ 15.00 0.396 0.396 0.000 0.000 1000000 Array#sort!
23
+ 12.38 0.462 0.327 0.000 0.136 1000000 DHeap::Benchmarks::Sorting#pop /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:42
24
+ 5.50 0.145 0.145 0.000 0.000 1000000 Array#push
25
+ 5.13 0.136 0.136 0.000 0.000 1000000 Array#shift
26
+ 4.81 0.127 0.127 0.000 0.000 1000000 Array#fetch
27
+
28
+ * recursively called methods
29
+
30
+ Columns are:
31
+
32
+ %self - The percentage of time spent in this method, derived from self_time/total_time.
33
+ total - The time spent in this method and its children.
34
+ self - The time spent in this method.
35
+ wait - The amount of time this method waited for other threads.
36
+ child - The time spent in this method's children.
37
+ calls - The number of times this method was called.
38
+ name - The name of the method.
39
+ location - The location of the method.
40
+
41
+ The interpretation of method names is:
42
+
43
+ * MyObject#test - An instance method "test" of the class "MyObject"
44
+ * <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
45
+
46
+ Filling bsearch + insert ---------------------------
47
+ Profiling bsearch + insert ---------------------------
48
+ Measure Mode: wall_time
49
+ Thread ID: 1360
50
+ Fiber ID: 1340
51
+ Total: 2.634233
52
+ Sort by: self_time
53
+
54
+ %self total self wait child calls name location
55
+ 30.73 2.634 0.810 0.000 1.825 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
56
+ 25.94 1.228 0.683 0.000 0.545 1000000 DHeap::Benchmarks::BSearch#<< /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:61
57
+ 14.83 0.391 0.391 0.000 0.000 1000000 Array#bsearch_index
58
+ 12.79 0.462 0.337 0.000 0.125 1000000 DHeap::Benchmarks::BSearch#pop /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:70
59
+ 5.85 0.154 0.154 0.000 0.000 1000000 Array#insert
60
+ 5.10 0.134 0.134 0.000 0.000 1000000 Array#fetch
61
+ 4.76 0.125 0.125 0.000 0.000 1000000 Array#pop
62
+
63
+ * recursively called methods
64
+
65
+ Columns are:
66
+
67
+ %self - The percentage of time spent in this method, derived from self_time/total_time.
68
+ total - The time spent in this method and its children.
69
+ self - The time spent in this method.
70
+ wait - The amount of time this method waited for other threads.
71
+ child - The time spent in this method's children.
72
+ calls - The number of times this method was called.
73
+ name - The name of the method.
74
+ location - The location of the method.
75
+
76
+ The interpretation of method names is:
77
+
78
+ * MyObject#test - An instance method "test" of the class "MyObject"
79
+ * <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
80
+
81
+ Filling ruby binary heap ---------------------------
82
+ Profiling ruby binary heap ---------------------------
83
+ Measure Mode: wall_time
84
+ Thread ID: 1360
85
+ Fiber ID: 1340
86
+ Total: 4.940103
87
+ Sort by: self_time
88
+
89
+ %self total self wait child calls name location
90
+ 37.38 1.973 1.846 0.000 0.126 1000000 DHeap::Benchmarks::RbHeap#<< /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:80
91
+ 33.66 2.042 1.663 0.000 0.380 1000000 DHeap::Benchmarks::RbHeap#pop /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:96
92
+ 16.10 4.940 0.795 0.000 4.145 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
93
+ 2.64 0.130 0.130 0.000 0.000 1000000 Array#pop
94
+ 2.63 0.130 0.130 0.000 0.000 1000000 Array#first
95
+ 2.62 0.130 0.130 0.000 0.000 1000000 Array#fetch
96
+ 2.56 0.126 0.126 0.000 0.000 1000000 Array#push
97
+ 2.43 0.120 0.120 0.000 0.000 1000000 Array#last
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: 1360
121
+ Fiber ID: 1340
122
+ Total: 1.231464
123
+ Sort by: self_time
124
+
125
+ %self total self wait child calls name location
126
+ 60.13 1.231 0.741 0.000 0.491 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
127
+ 16.72 0.206 0.206 0.000 0.000 1000000 DHeap#<<
128
+ 12.85 0.158 0.158 0.000 0.000 1000000 DHeap#pop
129
+ 10.30 0.127 0.127 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=1365 (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 push and resort ---------------------------
156
+ Profiling push and resort ---------------------------
157
+ Measure Mode: wall_time
158
+ Thread ID: 1360
159
+ Fiber ID: 1340
160
+ Total: 41.950612
161
+ Sort by: self_time
162
+
163
+ %self total self wait child calls name location
164
+ 94.10 39.478 39.478 0.000 0.000 1000000 Array#sort!
165
+ 2.11 41.951 0.884 0.000 41.066 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
166
+ 1.68 40.328 0.707 0.000 39.621 1000000 DHeap::Benchmarks::Sorting#<< /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:35
167
+
168
+ * recursively called methods
169
+
170
+ Columns are:
171
+
172
+ %self - The percentage of time spent in this method, derived from self_time/total_time.
173
+ total - The time spent in this method and its children.
174
+ self - The time spent in this method.
175
+ wait - The amount of time this method waited for other threads.
176
+ child - The time spent in this method's children.
177
+ calls - The number of times this method was called.
178
+ name - The name of the method.
179
+ location - The location of the method.
180
+
181
+ The interpretation of method names is:
182
+
183
+ * MyObject#test - An instance method "test" of the class "MyObject"
184
+ * <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
185
+
186
+ Filling bsearch + insert ---------------------------
187
+ Profiling bsearch + insert ---------------------------
188
+ Measure Mode: wall_time
189
+ Thread ID: 1360
190
+ Fiber ID: 1340
191
+ Total: 3.559064
192
+ Sort by: self_time
193
+
194
+ %self total self wait child calls name location
195
+ 37.92 1.349 1.349 0.000 0.000 1000000 Array#bsearch_index
196
+ 22.76 3.559 0.810 0.000 2.749 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
197
+ 18.47 2.157 0.657 0.000 1.499 1000000 DHeap::Benchmarks::BSearch#<< /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:61
198
+ 9.45 0.462 0.336 0.000 0.125 1000000 DHeap::Benchmarks::BSearch#pop /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:70
199
+ 4.21 0.150 0.150 0.000 0.000 1000000 Array#insert
200
+ 3.67 0.131 0.131 0.000 0.000 1000000 Array#fetch
201
+ 3.53 0.125 0.125 0.000 0.000 1000000 Array#pop
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
+ Filling ruby binary heap ---------------------------
222
+ Profiling ruby binary heap ---------------------------
223
+ Measure Mode: wall_time
224
+ Thread ID: 1360
225
+ Fiber ID: 1340
226
+ Total: 11.581886
227
+ Sort by: self_time
228
+
229
+ %self total self wait child calls name location
230
+ 52.04 6.160 6.027 0.000 0.132 1000000 DHeap::Benchmarks::RbHeap#<< /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:80
231
+ 35.10 4.453 4.065 0.000 0.388 1000000 DHeap::Benchmarks::RbHeap#pop /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:96
232
+ 7.21 11.582 0.835 0.000 10.747 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
233
+ 1.16 0.134 0.134 0.000 0.000 1000000 Array#fetch
234
+ 1.14 0.132 0.132 0.000 0.000 1000000 Array#push
235
+ 1.14 0.132 0.132 0.000 0.000 1000000 Array#pop
236
+ 1.13 0.131 0.131 0.000 0.000 1000000 Array#first
237
+ 1.08 0.125 0.125 0.000 0.000 1000000 Array#last
238
+
239
+ * recursively called methods
240
+
241
+ Columns are:
242
+
243
+ %self - The percentage of time spent in this method, derived from self_time/total_time.
244
+ total - The time spent in this method and its children.
245
+ self - The time spent in this method.
246
+ wait - The amount of time this method waited for other threads.
247
+ child - The time spent in this method's children.
248
+ calls - The number of times this method was called.
249
+ name - The name of the method.
250
+ location - The location of the method.
251
+
252
+ The interpretation of method names is:
253
+
254
+ * MyObject#test - An instance method "test" of the class "MyObject"
255
+ * <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
256
+
257
+ Filling quaternary DHeap ---------------------------
258
+ Profiling quaternary DHeap ---------------------------
259
+ Measure Mode: wall_time
260
+ Thread ID: 1360
261
+ Fiber ID: 1340
262
+ Total: 1.431426
263
+ Sort by: self_time
264
+
265
+ %self total self wait child calls name location
266
+ 50.61 1.431 0.724 0.000 0.707 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
267
+ 21.61 0.309 0.309 0.000 0.000 1000000 DHeap#<<
268
+ 19.18 0.275 0.275 0.000 0.000 1000000 DHeap#pop
269
+ 8.59 0.123 0.123 0.000 0.000 1000000 Array#fetch
270
+
271
+ * recursively called methods
272
+
273
+ Columns are:
274
+
275
+ %self - The percentage of time spent in this method, derived from self_time/total_time.
276
+ total - The time spent in this method and its children.
277
+ self - The time spent in this method.
278
+ wait - The amount of time this method waited for other threads.
279
+ child - The time spent in this method's children.
280
+ calls - The number of times this method was called.
281
+ name - The name of the method.
282
+ location - The location of the method.
283
+
284
+ The interpretation of method names is:
285
+
286
+ * MyObject#test - An instance method "test" of the class "MyObject"
287
+ * <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
288
+
289
+ ########################################################################
290
+ # Profile w/ N=87381 (i=1000000)
291
+ # (n.b. RubyProf & tracepoint can change relative performance.
292
+ # A sampling profiler can provide more accurate relative metrics.
293
+ ########################################################################
294
+
295
+ Filling bsearch + insert ---------------------------
296
+ Profiling bsearch + insert ---------------------------
297
+ Measure Mode: wall_time
298
+ Thread ID: 1360
299
+ Fiber ID: 1340
300
+ Total: 5.894803
301
+ Sort by: self_time
302
+
303
+ %self total self wait child calls name location
304
+ 34.53 2.035 2.035 0.000 0.000 1000000 Array#bsearch_index
305
+ 30.22 1.782 1.782 0.000 0.000 1000000 Array#insert
306
+ 13.74 5.895 0.810 0.000 5.085 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
307
+ 11.52 4.496 0.679 0.000 3.817 1000000 DHeap::Benchmarks::BSearch#<< /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:61
308
+ 5.70 0.459 0.336 0.000 0.124 1000000 DHeap::Benchmarks::BSearch#pop /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:70
309
+ 2.20 0.130 0.130 0.000 0.000 1000000 Array#fetch
310
+ 2.10 0.124 0.124 0.000 0.000 1000000 Array#pop
311
+
312
+ * recursively called methods
313
+
314
+ Columns are:
315
+
316
+ %self - The percentage of time spent in this method, derived from self_time/total_time.
317
+ total - The time spent in this method and its children.
318
+ self - The time spent in this method.
319
+ wait - The amount of time this method waited for other threads.
320
+ child - The time spent in this method's children.
321
+ calls - The number of times this method was called.
322
+ name - The name of the method.
323
+ location - The location of the method.
324
+
325
+ The interpretation of method names is:
326
+
327
+ * MyObject#test - An instance method "test" of the class "MyObject"
328
+ * <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
329
+
330
+ Filling ruby binary heap ---------------------------
331
+ Profiling ruby binary heap ---------------------------
332
+ Measure Mode: wall_time
333
+ Thread ID: 1360
334
+ Fiber ID: 1340
335
+ Total: 16.514635
336
+ Sort by: self_time
337
+
338
+ %self total self wait child calls name location
339
+ 45.67 7.926 7.542 0.000 0.384 1000000 DHeap::Benchmarks::RbHeap#pop /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:96
340
+ 45.43 7.630 7.502 0.000 0.128 1000000 DHeap::Benchmarks::RbHeap#<< /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:80
341
+ 5.00 16.515 0.826 0.000 15.688 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
342
+
343
+ * recursively called methods
344
+
345
+ Columns are:
346
+
347
+ %self - The percentage of time spent in this method, derived from self_time/total_time.
348
+ total - The time spent in this method and its children.
349
+ self - The time spent in this method.
350
+ wait - The amount of time this method waited for other threads.
351
+ child - The time spent in this method's children.
352
+ calls - The number of times this method was called.
353
+ name - The name of the method.
354
+ location - The location of the method.
355
+
356
+ The interpretation of method names is:
357
+
358
+ * MyObject#test - An instance method "test" of the class "MyObject"
359
+ * <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
360
+
361
+ Filling quaternary DHeap ---------------------------
362
+ Profiling quaternary DHeap ---------------------------
363
+ Measure Mode: wall_time
364
+ Thread ID: 1360
365
+ Fiber ID: 1340
366
+ Total: 1.622729
367
+ Sort by: self_time
368
+
369
+ %self total self wait child calls name location
370
+ 44.66 1.623 0.725 0.000 0.898 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
371
+ 27.42 0.445 0.445 0.000 0.000 1000000 DHeap#pop
372
+ 20.41 0.331 0.331 0.000 0.000 1000000 DHeap#<<
373
+ 7.51 0.122 0.122 0.000 0.000 1000000 Array#fetch
374
+
375
+ * recursively called methods
376
+
377
+ Columns are:
378
+
379
+ %self - The percentage of time spent in this method, derived from self_time/total_time.
380
+ total - The time spent in this method and its children.
381
+ self - The time spent in this method.
382
+ wait - The amount of time this method waited for other threads.
383
+ child - The time spent in this method's children.
384
+ calls - The number of times this method was called.
385
+ name - The name of the method.
386
+ location - The location of the method.
387
+
388
+ The interpretation of method names is:
389
+
390
+ * MyObject#test - An instance method "test" of the class "MyObject"
391
+ * <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
392
+
@@ -1,327 +1,657 @@
1
+ #include <float.h>
1
2
  #include "d_heap.h"
2
3
 
3
- ID id_ivar_a;
4
- ID id_ivar_d;
5
-
6
- #define DHEAP_GET_A(self) rb_ivar_get(self, id_ivar_a)
7
- #define DHEAP_GET_D(self) rb_ivar_get(self, id_ivar_d)
8
-
9
- #define DHEAP_SIZE(ary) (RARRAY_LEN(ary) / 2)
10
- #define DHEAP_LAST_IDX(ary) (DHEAP_SIZE(ary) - 1)
11
- #define DHEAP_VALUE(ary, idx) rb_ary_entry(ary, idx * 2 + 1)
12
- #define DHEAP_SCORE(ary, idx) rb_ary_entry(ary, idx * 2)
13
- #define DHEAP_ASSIGN(ary, idx, scr, val) \
14
- rb_ary_store(ary, idx * 2, scr); \
15
- rb_ary_store(ary, idx * 2 + 1, val);
16
- #define DHEAP_APPEND(ary, scr, val) \
17
- rb_ary_push(ary, scr); \
18
- rb_ary_push(ary, val);
19
- #define DHEAP_DROP_LAST(ary) ( \
20
- rb_ary_pop(ary), \
21
- rb_ary_pop(ary) \
22
- ) // score, value
23
- #define IDX_PARENT(idx) ((idx - 1) / d)
24
- #define IDX_CHILD0(idx) ((idx * d) + 1)
25
-
26
- #define DHEAP_Check_d_size(d) \
27
- if (d < 2) { \
28
- rb_raise(rb_eIndexError, "DHeap d=%d is too small", d); \
29
- } \
30
- if (d > DHEAP_MAX_D) { \
31
- rb_raise(rb_eIndexError, "DHeap d=%d is too large", d); \
4
+ ID id_cmp; // <=>
5
+ ID id_abs; // abs
6
+
7
+ typedef struct dheap_struct {
8
+ int d;
9
+ long size;
10
+ long capa;
11
+ ENTRY *entries;
12
+ } dheap_t;
13
+
14
+ #define DHEAP_SCORE(heap, idx) ((heap)->entries[idx].score)
15
+ #define DHEAP_VALUE(heap, idx) ((heap)->entries[idx].value)
16
+ #define DHEAP_IDX_LAST(heap) (DHEAP_SIZE((heap)) - 1)
17
+ #define DHEAP_IDX_PARENT(heap, idx) (((idx) - 1) / (heap)->d)
18
+ #define DHEAP_IDX_CHILD_0(heap, idx) (((idx) * (heap)->d) + 1)
19
+ #define DHEAP_IDX_CHILD_D(heap, idx) (((idx) * (heap)->d) + (heap)->d)
20
+
21
+ #define DHEAP_SIZE(heap) ((heap)->size)
22
+
23
+ #define CMP_LT(a, b) ((a) < (b))
24
+ #define CMP_LTE(a, b) ((a) <= (b))
25
+ #define CMP_GT(a, b) ((a) > (b))
26
+ #define CMP_GTE(a, b) ((a) >= (b))
27
+
28
+ #if LDBL_MANT_DIG < SIZEOF_UNSIGNED_LONG_LONG * 8
29
+ #error 'unsigned long long' should fit into 'long double' mantissa
30
+ #endif
31
+
32
+ // copied and modified from ruby's object.c
33
+ #define FIX2SCORE(x) (long double)FIX2LONG(x)
34
+ // We could translate a much wider range of values to long double by
35
+ // implementing a new `rb_big2ldbl(x)` function. But requires reaching into
36
+ // T_BIGNUM internals.
37
+ static inline long double
38
+ BIG2SCORE(VALUE x)
39
+ {
40
+ if (RBIGNUM_POSITIVE_P(x)) {
41
+ unsigned long long ull = rb_big2ull(x);
42
+ return (long double)ull;
43
+ } else {
44
+ unsigned long long ull;
45
+ long double ldbl;
46
+ x = rb_funcall(x, id_abs, 0);
47
+ ull = rb_big2ull(x);
48
+ ldbl = (long double) ull;
49
+ return -ldbl;
32
50
  }
51
+ }
52
+ #define INT2SCORE(x) \
53
+ (FIXNUM_P(x) ? FIX2SCORE(x) : BIG2SCORE(x))
54
+ #define NUM2SCORE(x) \
55
+ (FIXNUM_P(x) ? FIX2SCORE(x) : \
56
+ RB_TYPE_P(x, T_BIGNUM) ? BIG2SCORE(x) : \
57
+ (Check_Type(x, T_FLOAT), (long double)RFLOAT_VALUE(x)))
58
+ static inline long double
59
+ RAT2SCORE(VALUE x)
60
+ {
61
+ VALUE num = rb_rational_num(x);
62
+ VALUE den = rb_rational_den(x);
63
+ return NUM2SCORE(num) / NUM2SCORE(den);
64
+ }
33
65
 
34
- #define DHEAP_Check_Sift_Idx(sift_index, last_index) \
35
- if (sift_index < 0) { \
36
- rb_raise(rb_eIndexError, "sift_index %ld too small", sift_index); \
37
- } \
38
- else if (last_index < sift_index) { \
39
- rb_raise(rb_eIndexError, "sift_index %ld too large", sift_index); \
66
+ /*
67
+ * Convert both T_FIXNUM and T_FLOAT (and sometimes T_BIGNUM, T_RATIONAL,
68
+ * String, etc) to SCORE
69
+ * * with no loss of precision (where possible for Integer and Float),
70
+ * * raises an exception if
71
+ * * a positive integer is too large for unsigned long long (should be 64bit)
72
+ * * a negative integer is too small for signed long long (should be 64bit)
73
+ * * reduced to long double (should be 80 or 128 bit) if it is Rational
74
+ * * reduced to double precision if the value is convertable by Float(x)
75
+ */
76
+ static inline long double
77
+ VAL2SCORE(VALUE score)
78
+ {
79
+ // assert that long double can hold 'unsigned long long':
80
+ // static_assert(sizeof(unsigned long long) * 8 <= LDBL_MANT_DIG);
81
+ // assert that long double can hold T_FLOAT
82
+ // static_assert(sizeof(double) <= sizeof(long double));
83
+
84
+ switch (TYPE(score)) {
85
+ case T_FIXNUM:
86
+ return FIX2SCORE(score);
87
+ case T_BIGNUM:
88
+ return BIG2SCORE(score);
89
+ case T_RATIONAL:
90
+ return RAT2SCORE(score);
91
+ default:
92
+ return (long double)(NUM2DBL(rb_Float(score)));
40
93
  }
94
+ }
41
95
 
42
- #define DHEAP_Check_Sift_Args(heap_array, d, sift_index) \
43
- DHEAP_Check_d_size(d); \
44
- Check_Type(heap_array, T_ARRAY); \
45
- long last_index = DHEAP_LAST_IDX(heap_array); \
46
- DHEAP_Check_Sift_Idx(sift_index, last_index); \
47
- \
48
- VALUE sift_value = DHEAP_VALUE(heap_array, sift_index); \
49
- VALUE sift_score = DHEAP_SCORE(heap_array, sift_index);
96
+ #ifdef ORIG_SCORE_CMP_CODE
50
97
 
51
- VALUE
52
- dheap_ary_sift_up(VALUE heap_array, int d, long sift_index) {
53
- DHEAP_Check_Sift_Args(heap_array, d, sift_index);
54
- struct cmp_opt_data cmp_opt = { 0, 0 };
55
- // sift it up to where it belongs
56
- for (long parent_index; 0 < sift_index; sift_index = parent_index) {
57
- // puts(rb_sprintf("sift up(%"PRIsVALUE", %d, %ld)", heap_array, d, sift_index));
58
- parent_index = IDX_PARENT(sift_index);
59
- VALUE parent_score = DHEAP_SCORE(heap_array, parent_index);
98
+ #define VAL2SCORE(score) (score)
60
99
 
61
- // parent is smaller: heap is restored
62
- if (CMP_LTE(parent_score, sift_score, cmp_opt)) break;
100
+ #define CMP_LT(a, b) (optimized_cmp(a, b) < 0)
101
+ #define CMP_LTE(a, b) (optimized_cmp(a, b) <= 0)
102
+ #define CMP_GT(a, b) (optimized_cmp(a, b) > 0)
103
+ #define CMP_GTE(a, b) (optimized_cmp(a, b) >= 0)
63
104
 
64
- // parent is larger: swap and continue sifting up
65
- VALUE parent_value = DHEAP_VALUE(heap_array, parent_index);
66
- DHEAP_ASSIGN(heap_array, sift_index, parent_score, parent_value);
67
- DHEAP_ASSIGN(heap_array, parent_index, sift_score, sift_value);
105
+ // from internal/compar.h
106
+ #define STRING_P(s) (RB_TYPE_P((s), T_STRING) && CLASS_OF(s) == rb_cString)
107
+ /*
108
+ * short-circuit evaluation for a few basic types.
109
+ *
110
+ * Only Integer, Float, and String are optimized,
111
+ * and only when both arguments are the same type.
112
+ */
113
+ static inline int
114
+ optimized_cmp(SCORE a, SCORE b) {
115
+ if (a == b) // Fixnum equality and object equality
116
+ return 0;
117
+ if (FIXNUM_P(a) && FIXNUM_P(b))
118
+ return (FIX2LONG(a) < FIX2LONG(b)) ? -1 : 1;
119
+ if (RB_FLOAT_TYPE_P(a) && RB_FLOAT_TYPE_P(b))
120
+ {
121
+ double x, y;
122
+ x = RFLOAT_VALUE(a);
123
+ y = RFLOAT_VALUE(b);
124
+ if (isnan(x) || isnan(y)) rb_cmperr(a, b); // raise ArgumentError
125
+ return (x < y) ? -1 : ((x == y) ? 0 : 1);
68
126
  }
69
- // puts(rb_sprintf("sifted (%"PRIsVALUE", %d, %ld)", heap_array, d, sift_index));
70
- return LONG2NUM(sift_index);
127
+ if (RB_TYPE_P(a, T_BIGNUM) && RB_TYPE_P(b, T_BIGNUM))
128
+ return FIX2INT(rb_big_cmp(a, b));
129
+ if (STRING_P(a) && STRING_P(b))
130
+ return rb_str_cmp(a, b);
131
+
132
+ // give up on an optimized version and just call (a <=> b)
133
+ return rb_cmpint(rb_funcallv(a, id_cmp, 1, &b), a, b);
71
134
  }
72
135
 
73
- VALUE
74
- dheap_ary_sift_down(VALUE heap_array, int d, long sift_index) {
75
- DHEAP_Check_Sift_Args(heap_array, d, sift_index);
76
- struct cmp_opt_data cmp_opt = { 0, 0 };
77
-
78
- // iteratively sift it down to where it belongs
79
- for (long child_index; sift_index < last_index; sift_index = child_index) {
80
- // puts(rb_sprintf("sift dn(%"PRIsVALUE", %d, %ld)", heap_array, d, sift_index));
81
- // find first child index, and break if we've reached the last layer
82
- long child_idx0 = child_index = IDX_CHILD0(sift_index);
83
- if (last_index < child_idx0) break;
84
-
85
- // find the min child (and its child_index)
86
- // requires "d" comparisons to find min child and compare to sift_score
87
- VALUE child_score = DHEAP_SCORE(heap_array, child_idx0);
88
- child_index = child_idx0;
89
- for (int i = 1; i < d; ++i) {
90
- long sibling_index = child_idx0 + i;
91
- if (last_index < sibling_index) break;
92
-
93
- VALUE sibling_score = DHEAP_SCORE(heap_array, sibling_index);
94
-
95
- if (CMP_LT(sibling_score, child_score, cmp_opt)) {
96
- child_score = sibling_score;
97
- child_index = sibling_index;
98
- }
99
- }
136
+ #endif
137
+
138
+ #define DHEAP_Check_d_size(d) do { \
139
+ if (d < 2) { \
140
+ rb_raise(rb_eArgError, "DHeap d=%d is too small", d); \
141
+ } \
142
+ if (d > DHEAP_MAX_D) { \
143
+ rb_raise(rb_eArgError, "DHeap d=%d is too large", d); \
144
+ } \
145
+ } while (0)
146
+
147
+ #ifdef __D_HEAP_DEBUG
148
+ #define ASSERT_DHEAP_INDEX(heap, index) do { \
149
+ if (index < 0) { \
150
+ rb_raise(rb_eIndexError, "DHeap index %ld too small", index); \
151
+ } \
152
+ else if (DHEAP_IDX_LAST(heap) < index) { \
153
+ rb_raise(rb_eIndexError, "DHeap index %ld too large", index); \
154
+ } \
155
+ } while (0)
156
+ #else
157
+ #define ASSERT_DHEAP_INDEX(heap, index)
158
+ #endif
159
+
160
+ static void
161
+ dheap_compact(void *ptr)
162
+ {
163
+ dheap_t *heap = ptr;
164
+ for (long i = 0; i < heap->size; ++i) {
165
+ if (DHEAP_VALUE(heap, i))
166
+ dheap_gc_location(DHEAP_VALUE(heap, i));
167
+ }
168
+ }
100
169
 
101
- // child is larger: heap is restored
102
- if (CMP_LTE(sift_score, child_score, cmp_opt)) break;
170
+ static void
171
+ dheap_mark(void *ptr)
172
+ {
173
+ dheap_t *heap = ptr;
174
+ for (long i = 0; i < heap->size; ++i) {
175
+ if (DHEAP_VALUE(heap, i))
176
+ rb_gc_mark_movable(DHEAP_VALUE(heap,i));
177
+ }
178
+ }
103
179
 
104
- // child is smaller: swap and continue sifting down
105
- VALUE child_value = DHEAP_VALUE(heap_array, child_index);
106
- DHEAP_ASSIGN(heap_array, sift_index, child_score, child_value);
107
- DHEAP_ASSIGN(heap_array, child_index, sift_score, sift_value);
180
+ static void
181
+ dheap_free(void *ptr)
182
+ {
183
+ dheap_t *heap = ptr;
184
+ heap->size = 0;
185
+ if (heap->entries) {
186
+ xfree(heap->entries);
187
+ heap->entries = NULL;
108
188
  }
109
- // puts(rb_sprintf("sifted (%"PRIsVALUE", %d, %ld)", heap_array, d, sift_index));
110
- return LONG2NUM(sift_index);
189
+ heap->capa = 0;
190
+ xfree(ptr);
111
191
  }
112
192
 
113
- #define DHEAP_Load_Sift_Vals(heap_array, dval, idxval) \
114
- Check_Type(dval, T_FIXNUM); \
115
- int d = FIX2INT(dval); \
116
- long sift_index = NUM2LONG(idxval);
193
+ static size_t
194
+ dheap_memsize(const void *ptr)
195
+ {
196
+ const dheap_t *heap = ptr;
197
+ size_t size = 0;
198
+ size += sizeof(*heap);
199
+ size += sizeof(ENTRY) * heap->capa;
200
+ return size;
201
+ }
202
+
203
+ static const rb_data_type_t dheap_data_type = {
204
+ "DHeap",
205
+ {
206
+ (void (*)(void*))dheap_mark,
207
+ (void (*)(void*))dheap_free,
208
+ (size_t (*)(const void *))dheap_memsize,
209
+ dheap_compact_callback(dheap_compact),
210
+ },
211
+ 0, 0,
212
+ RUBY_TYPED_FREE_IMMEDIATELY,
213
+ };
117
214
 
118
- /*
119
- * call-seq:
120
- * DHeap.array_sift_up(heap_array, d, sift_index)
121
- *
122
- * Treats +heap_array+ as a +d+-ary heap and sifts up from +sift_index+ to restore
123
- * the heap property.
124
- *
125
- * Time complexity: O(d log n / log d). If the average up shifted element sorts
126
- * into the bottom layer (e.g. new timers), this can avg O(1).
127
- *
128
- */
129
215
  static VALUE
130
- dheap_sift_up_s(VALUE unused, VALUE heap_array, VALUE dval, VALUE idxval) {
131
- DHEAP_Load_Sift_Vals(heap_array, dval, idxval);
132
- return dheap_ary_sift_up(heap_array, d, sift_index);
216
+ dheap_s_alloc(VALUE klass)
217
+ {
218
+ VALUE obj;
219
+ dheap_t *heap;
220
+
221
+ obj = TypedData_Make_Struct(klass, dheap_t, &dheap_data_type, heap);
222
+ heap->d = DHEAP_DEFAULT_D;
223
+
224
+ heap->size = 0;
225
+ heap->capa = 0;
226
+ heap->entries = NULL;
227
+
228
+ return obj;
229
+ }
230
+
231
+ static inline dheap_t *
232
+ get_dheap_struct(VALUE self)
233
+ {
234
+ dheap_t *heap;
235
+ TypedData_Get_Struct(self, dheap_t, &dheap_data_type, heap);
236
+ return heap;
237
+ }
238
+
239
+ void
240
+ dheap_set_capa(dheap_t *heap, long new_capa)
241
+ {
242
+ // Do nothing if we already have the capacity or are resizing too small
243
+ if (new_capa <= heap->capa || new_capa <= heap->size) return;
244
+
245
+ // allocate
246
+ if (heap->entries) {
247
+ RB_REALLOC_N(heap->entries, ENTRY, new_capa);
248
+ } else {
249
+ heap->entries = RB_ZALLOC_N(ENTRY, new_capa);
250
+ }
251
+ heap->capa = new_capa;
252
+ }
253
+
254
+ static void
255
+ dheap_ensure_room_for_push(dheap_t *heap, long incr_by)
256
+ {
257
+ long new_size = heap->size + incr_by;
258
+
259
+ // check for overflow of new_size
260
+ if (DHEAP_MAX_SIZE - incr_by < heap->size)
261
+ rb_raise(rb_eIndexError, "index %ld too big", new_size);
262
+
263
+ // if it existing capacity is too small
264
+ if (heap->capa < new_size) {
265
+ // double it...
266
+ long new_capa = new_size * 2;
267
+ if (DHEAP_CAPA_INCR_MAX < new_size)
268
+ new_size = new_size + DHEAP_CAPA_INCR_MAX;
269
+ // check for overflow of new_capa
270
+ if (DHEAP_MAX_SIZE / 2 < new_size) new_capa = DHEAP_MAX_SIZE;
271
+ // cap max incr_by
272
+ if (heap->capa + DHEAP_CAPA_INCR_MAX < new_capa)
273
+ new_capa = heap->capa + DHEAP_CAPA_INCR_MAX;
274
+
275
+ dheap_set_capa(heap, new_capa);
276
+ }
133
277
  }
134
278
 
135
279
  /*
136
- * call-seq:
137
- * DHeap.array_sift_down(heap_array, d, sift_index)
138
- *
139
- * Treats +heap_array+ as a +d+-ary heap and sifts down from +sift_index+ to
140
- * restore the heap property.
141
- *
142
- * Time complexity: O(d log n / log d). If the average down shifted element
143
- * sorts into the bottom layer (e.g. canceled timers), this can avg O(1).
280
+ * @overload initialize(d = DHeap::DEFAULT_D)
281
+ * Initialize a _d_-ary min-heap.
144
282
  *
283
+ * @param d [Integer] maximum number of children per parent
145
284
  */
146
- static VALUE
147
- dheap_sift_down_s(VALUE unused, VALUE heap_array, VALUE dval, VALUE idxval) {
148
- DHEAP_Load_Sift_Vals(heap_array, dval, idxval);
149
- return dheap_ary_sift_down(heap_array, d, sift_index);
150
- }
151
-
152
285
  static VALUE
153
286
  dheap_initialize(int argc, VALUE *argv, VALUE self) {
287
+ dheap_t *heap;
288
+ int d;
289
+
154
290
  rb_check_arity(argc, 0, 1);
155
- int d = DHEAP_DEFAULT_D;
156
- if (argc) {
157
- d = NUM2INT(argv[0]);
158
- }
291
+ TypedData_Get_Struct(self, dheap_t, &dheap_data_type, heap);
292
+
293
+ d = argc ? NUM2INT(argv[0]) : DHEAP_DEFAULT_D;
159
294
  DHEAP_Check_d_size(d);
160
- rb_ivar_set(self, id_ivar_d, INT2FIX(d));
161
- rb_ivar_set(self, id_ivar_a, rb_ary_new());
295
+ heap->d = d;
296
+
297
+ dheap_set_capa(heap, DHEAP_DEFAULT_SIZE);
298
+
162
299
  return self;
163
300
  }
164
301
 
165
- static VALUE dheap_size(VALUE self) {
166
- VALUE ary = DHEAP_GET_A(self);
167
- long size = DHEAP_SIZE(ary);
302
+ /* @!visibility private */
303
+ static VALUE
304
+ dheap_initialize_copy(VALUE copy, VALUE orig)
305
+ {
306
+ dheap_t *heap_copy;
307
+ dheap_t *heap_orig = get_dheap_struct(orig);
308
+
309
+ rb_check_frozen(copy);
310
+ TypedData_Get_Struct(copy, dheap_t, &dheap_data_type, heap_copy);
311
+
312
+ heap_copy->d = heap_orig->d;
313
+
314
+ dheap_set_capa(heap_copy, heap_orig->capa);
315
+ heap_copy->size = heap_orig->size;
316
+ if (heap_copy->size)
317
+ MEMCPY(heap_copy->entries, heap_orig->entries, ENTRY, heap_orig->size);
318
+
319
+ return copy;
320
+ }
321
+
322
+ VALUE
323
+ dheap_sift_up(dheap_t *heap, long index) {
324
+ ENTRY entry = heap->entries[index];
325
+
326
+ ASSERT_DHEAP_INDEX(heap, index);
327
+
328
+ // sift it up to where it belongs
329
+ for (long parent_index; 0 < index; index = parent_index) {
330
+ /* debug(rb_sprintf("sift up(%"PRIsVALUE", %d, %ld)", heap->values, heap->d, index)); */
331
+ parent_index = DHEAP_IDX_PARENT(heap, index);
332
+
333
+ // parent is smaller: heap is restored
334
+ if (CMP_LTE(DHEAP_SCORE(heap, parent_index), entry.score)) break;
335
+
336
+ // parent is larger: swap and continue sifting up
337
+ heap->entries[index] = heap->entries[parent_index];
338
+ }
339
+ heap->entries[index] = entry;
340
+ /* debug(rb_sprintf("sifted (%"PRIsVALUE", %d, %ld)", heap->values, heap->d, index)); */
341
+ return LONG2NUM(index);
342
+ }
343
+
344
+ /*
345
+ * this is a tiny bit more complicated than the binary heap version
346
+ */
347
+ static inline long
348
+ dheap_find_min_child(dheap_t *heap, long parent, long last_index) {
349
+ long min_child = DHEAP_IDX_CHILD_0(heap, parent);
350
+ long last_sib = DHEAP_IDX_CHILD_D(heap, parent);
351
+ if (last_index < last_sib) last_sib = last_index;
352
+
353
+ for (long sibidx = min_child + 1; sibidx <= last_sib; ++sibidx) {
354
+ if (CMP_LT(DHEAP_SCORE(heap, sibidx),
355
+ DHEAP_SCORE(heap, min_child))) {
356
+ min_child = sibidx;
357
+ }
358
+ }
359
+ return min_child;
360
+ }
361
+
362
+ void
363
+ dheap_sift_down(dheap_t *heap, long index, long last_index) {
364
+ if (last_index < 1 || DHEAP_IDX_PARENT(heap, last_index) < index) {
365
+ // short-circuit: no chance for a child
366
+ return;
367
+
368
+ } else {
369
+ ENTRY entry = heap->entries[index];
370
+
371
+ long last_parent = DHEAP_IDX_PARENT(heap, last_index);
372
+
373
+ ASSERT_DHEAP_INDEX(heap, index);
374
+
375
+ // iteratively sift it down to where it belongs
376
+ while (index <= last_parent) {
377
+ // find min child
378
+ long min_child = dheap_find_min_child(heap, index, last_index);
379
+
380
+ // child is larger: heap is restored
381
+ if (CMP_LTE(entry.score, DHEAP_SCORE(heap, min_child))) break;
382
+
383
+ // child is smaller: swap and continue sifting down
384
+ heap->entries[index] = heap->entries[min_child];
385
+ index = min_child;
386
+ }
387
+ heap->entries[index] = entry;
388
+ // debug(rb_sprintf("sifted (%"PRIsVALUE", %d, %ld)", heap->values, heap->d, index));
389
+ }
390
+ }
391
+
392
+ /*
393
+ * @return [Integer] the number of elements in the heap
394
+ */
395
+ static VALUE
396
+ dheap_size(VALUE self)
397
+ {
398
+ dheap_t *heap = get_dheap_struct(self);
399
+ long size = DHEAP_SIZE(heap);
168
400
  return LONG2NUM(size);
169
401
  }
170
402
 
171
- static VALUE dheap_empty_p(VALUE self) {
172
- VALUE ary = DHEAP_GET_A(self);
173
- long size = DHEAP_SIZE(ary);
403
+ /*
404
+ * @return [Boolean] if the heap is empty
405
+ */
406
+ static VALUE
407
+ dheap_empty_p(VALUE self)
408
+ {
409
+ dheap_t *heap = get_dheap_struct(self);
410
+ long size = DHEAP_SIZE(heap);
174
411
  return size == 0 ? Qtrue : Qfalse;
175
412
  }
176
413
 
177
- static VALUE dheap_attr_d(VALUE self) { return DHEAP_GET_D(self); }
178
-
414
+ /*
415
+ * @return [Integer] the maximum number of children per parent
416
+ */
179
417
  static VALUE
180
- dheap_freeze(VALUE self) {
181
- VALUE ary = DHEAP_GET_A(self);
182
- ID id_freeze = rb_intern("freeze");
183
- rb_funcall(ary, id_freeze, 0);
184
- return rb_call_super(0, NULL);
418
+ dheap_attr_d(VALUE self)
419
+ {
420
+ dheap_t *heap = get_dheap_struct(self);
421
+ return INT2FIX(heap->d);
185
422
  }
186
423
 
424
+ /* @!visibility private */
187
425
  static VALUE
188
- dheap_ary_push(VALUE ary, int d, VALUE val, VALUE scr)
426
+ dheap_init_clone(VALUE clone, VALUE orig, VALUE kwfreeze)
189
427
  {
190
- DHEAP_APPEND(ary, scr, val);
191
- long last_index = DHEAP_LAST_IDX(ary);
192
- return dheap_ary_sift_up(ary, d, last_index);
428
+ dheap_initialize_copy(clone, orig);
429
+ if (RTEST(kwfreeze) || (kwfreeze == Qnil && OBJ_FROZEN(orig))) {
430
+ rb_funcall(clone, rb_intern("freeze"), 0);
431
+ }
432
+ return clone;
433
+ }
434
+
435
+ static inline void
436
+ dheap_push_entry(dheap_t *heap, ENTRY *entry) {
437
+ dheap_ensure_room_for_push(heap, 1);
438
+ heap->entries[heap->size] = *entry;
439
+ ++heap->size;
440
+ dheap_sift_up(heap, heap->size - 1);
441
+ }
442
+
443
+ /*
444
+ * Inserts a value into the heap, using a score to determine sort-order.
445
+ *
446
+ * Score comes first, as an analogy with the +Array#insert+ index.
447
+ *
448
+ * Time complexity: <b>O(log n / log d)</b> <i>(worst-case)</i>
449
+ *
450
+ * @param score [Integer,Float,#to_f] a score to compare against other scores.
451
+ * @param value [Object] an object that is associated with the score.
452
+ *
453
+ * @return [self]
454
+ */
455
+ static VALUE
456
+ dheap_insert(VALUE self, VALUE score, VALUE value) {
457
+ ENTRY entry;
458
+ dheap_t *heap = get_dheap_struct(self);
459
+ rb_check_frozen(self);
460
+
461
+ entry.score = VAL2SCORE(score);
462
+ entry.value = value;
463
+ dheap_push_entry(heap, &entry);
464
+
465
+ return self;
193
466
  }
194
467
 
195
468
  /*
196
- * Push val onto the end of the heap, then sift up to maintain heap property.
469
+ * @overload push(value, score = value)
470
+ *
471
+ * Push a value onto heap, using a score to determine sort-order.
472
+ *
473
+ * Value comes first because the separate score is optional, and because it feels
474
+ * like a more natural variation on +Array#push+ or +Queue#enq+. If a score
475
+ * isn't provided, the value must be an Integer or can be cast with
476
+ * +Float(value)+.
197
477
  *
198
- * Returns the index of the value's final position.
478
+ * Time complexity: <b>O(log n / log d)</b> <i>(worst-case)</i>
199
479
  *
480
+ * @param value [Object] an object that is associated with the score.
481
+ * @param score [Integer,Float,#to_f] a score to compare against other scores.
482
+ *
483
+ * @return [self]
200
484
  */
201
485
  static VALUE
202
486
  dheap_push(int argc, VALUE *argv, VALUE self) {
203
- rb_check_arity(argc, 1, 2);
204
- VALUE scr = argv[0];
205
- VALUE val = argc < 2 ? scr : argv[1];
487
+ dheap_t *heap = get_dheap_struct(self);
488
+ ENTRY entry;
489
+ rb_check_frozen(self);
206
490
 
207
- VALUE ary = DHEAP_GET_A(self);
208
- VALUE dval = DHEAP_GET_D(self);
209
- int d = FIX2INT(dval);
491
+ rb_check_arity(argc, 1, 2);
492
+ entry.value = argv[0];
493
+ entry.score = VAL2SCORE(argc < 2 ? entry.value : argv[1]);
494
+ dheap_push_entry(heap, &entry);
210
495
 
211
- return dheap_ary_push(ary, d, val, scr);
496
+ return self;
212
497
  }
213
498
 
214
499
  /*
215
- * Push val onto the end of the heap, then sift up to maintain heap property.
500
+ * Pushes a value onto the heap.
216
501
  *
217
- * Time complexity: O(d log n / log d).
502
+ * The score will be derived from the value, by using the value itself if it is
503
+ * an Integer, otherwise by casting it with +Float(value)+.
218
504
  *
219
- * Returns +self+.
505
+ * Time complexity: <b>O(log n / log d)</b> <i>(worst-case)</i>
220
506
  *
507
+ * @param value [Integer,#to_f] a value with an intrinsic numeric score
508
+ * @return [self]
221
509
  */
222
510
  static VALUE
223
- dheap_left_shift(VALUE self, VALUE val) {
224
- dheap_push(1, &val, self);
511
+ dheap_left_shift(VALUE self, VALUE value) {
512
+ dheap_t *heap = get_dheap_struct(self);
513
+ ENTRY entry;
514
+ rb_check_frozen(self);
515
+
516
+ entry.score = VAL2SCORE(value);
517
+ entry.value = value;
518
+ dheap_push_entry(heap, &entry);
519
+
225
520
  return self;
226
521
  }
227
522
 
228
- #define DHEAP_Pop_Init(self) \
229
- VALUE ary = DHEAP_GET_A(self); \
230
- VALUE dval = DHEAP_GET_D(self); \
231
- long last_index = DHEAP_LAST_IDX(ary); \
523
+ static const ENTRY EmptyDheapEntry;
232
524
 
233
- #define DHEAP_Pop_SwapLastAndSiftDown(ary, dval, last_index, sift_value) \
234
- if (last_index == 0) { DHEAP_DROP_LAST(ary); return pop_value; } \
235
- VALUE sift_value = DHEAP_VALUE(ary, last_index); \
236
- VALUE sift_score = DHEAP_SCORE(ary, last_index); \
237
- DHEAP_ASSIGN(ary, 0, sift_score, sift_value); \
238
- DHEAP_DROP_LAST(ary); \
239
- dheap_ary_sift_down(ary, FIX2INT(dval), 0);
525
+ static inline VALUE
526
+ dheap_pop0(dheap_t *heap)
527
+ {
528
+ VALUE popped = DHEAP_VALUE(heap, 0);
529
+ if (0 < --heap->size) {
530
+ heap->entries[0] = heap->entries[heap->size];
531
+ heap->entries[heap->size] = EmptyDheapEntry; // unnecessary to zero?
532
+ dheap_sift_down(heap, 0, heap->size - 1);
533
+ }
534
+ return popped;
535
+ }
240
536
 
537
+ /*
538
+ * Clears all values from the heap, leaving it empty.
539
+ *
540
+ * @return [self]
541
+ */
241
542
  static VALUE
242
- dheap_peek(VALUE self) {
243
- VALUE ary = DHEAP_GET_A(self);
244
- return DHEAP_VALUE(ary, 0);
543
+ dheap_clear(VALUE self) {
544
+ dheap_t *heap = get_dheap_struct(self);
545
+ rb_check_frozen(self);
546
+ if (0 < DHEAP_SIZE(heap)) {
547
+ heap->size = 0;
548
+ }
549
+ return self;
245
550
  }
246
551
 
247
552
  /*
248
- * Pops the minimum value from the top of the heap, sifting down to maintain
249
- * heap property.
250
- *
251
- * Time complexity: O(d log n / log d).
553
+ * Returns the next value on the heap to be popped without popping it.
252
554
  *
555
+ * Time complexity: <b>O(1)</b> <i>(worst-case)</i>
556
+ * @return [Object] the next value to be popped without popping it.
253
557
  */
254
558
  static VALUE
255
- dheap_pop(VALUE self) {
256
- DHEAP_Pop_Init(self);
257
- if (last_index < 0) return Qnil;
258
- VALUE pop_value = DHEAP_VALUE(ary, 0);
259
-
260
- DHEAP_Pop_SwapLastAndSiftDown(ary, dval, last_index, sift_value);
261
- return pop_value;
559
+ dheap_peek(VALUE self)
560
+ {
561
+ dheap_t *heap = get_dheap_struct(self);
562
+ if (DHEAP_IDX_LAST(heap) < 0) return Qnil;
563
+ return DHEAP_VALUE(heap, 0);
262
564
  }
263
565
 
264
566
  /*
265
- * Pops the minimum value from the top of the heap, sifting down to maintain
266
- * heap property.
567
+ * Pops the minimum value from the top of the heap
267
568
  *
268
- * Time complexity: O(d log n / log d).
569
+ * Time complexity: <b>O(d log n / log d)</b> <i>(worst-case)</i>
269
570
  *
571
+ * @return [Object] the value with the minimum score
572
+ *
573
+ * @see #peek
574
+ * @see #pop_lt
575
+ * @see #pop_lte
270
576
  */
271
577
  static VALUE
272
- dheap_pop_lte(VALUE self, VALUE below_score) {
273
- DHEAP_Pop_Init(self);
274
- if (last_index < 0) return Qnil;
275
- VALUE pop_value = DHEAP_VALUE(ary, 0);
276
-
277
- VALUE pop_score = DHEAP_SCORE(ary, 0);
278
- struct cmp_opt_data cmp_opt = { 0, 0 };
279
- if (below_score && !CMP_LTE(pop_score, below_score, cmp_opt)) return Qnil;
280
-
281
- DHEAP_Pop_SwapLastAndSiftDown(ary, dval, last_index, sift_value);
282
- return pop_value;
578
+ dheap_pop(VALUE self)
579
+ {
580
+ dheap_t *heap = get_dheap_struct(self);
581
+ rb_check_frozen(self);
582
+ if (DHEAP_SIZE(heap) <= 0) return Qnil;
583
+ return dheap_pop0(heap);
283
584
  }
284
585
 
285
586
  /*
286
- * Pops the minimum value from the top of the heap, sifting down to maintain
287
- * heap property.
587
+ * Pops the minimum value only if it is less than or equal to a max score.
588
+ *
589
+ * @param max_score [Integer,#to_f] the maximum score to be popped
288
590
  *
289
- * Time complexity: O(d log n / log d).
591
+ * Time complexity: <b>O(d log n / log d)</b> <i>(worst-case)</i>
290
592
  *
593
+ * @return [Object] the value with the minimum score
594
+ *
595
+ * @see #peek
596
+ * @see #pop
597
+ * @see #pop_lt
291
598
  */
292
599
  static VALUE
293
- dheap_pop_lt(VALUE self, VALUE below_score) {
294
- DHEAP_Pop_Init(self);
295
- if (last_index < 0) return Qnil;
296
- VALUE pop_value = DHEAP_VALUE(ary, 0);
297
-
298
- VALUE pop_score = DHEAP_SCORE(ary, 0);
299
- struct cmp_opt_data cmp_opt = { 0, 0 };
300
- if (below_score && !CMP_LT(pop_score, below_score, cmp_opt)) return Qnil;
600
+ dheap_pop_lte(VALUE self, VALUE max_score)
601
+ {
602
+ dheap_t *heap = get_dheap_struct(self);
603
+ rb_check_frozen(self);
604
+ if (DHEAP_SIZE(heap) <= 0) return Qnil;
605
+ if (!CMP_LTE(DHEAP_SCORE(heap, 0), VAL2SCORE(max_score))) return Qnil;
606
+ return dheap_pop0(heap);
607
+ }
301
608
 
302
- DHEAP_Pop_SwapLastAndSiftDown(ary, dval, last_index, sift_value);
303
- return pop_value;
609
+ /*
610
+ * Pops the minimum value only if it is less than a max score.
611
+ *
612
+ * @param max_score [Integer,#to_f] the maximum score to be popped
613
+ *
614
+ * Time complexity: <b>O(d log n / log d)</b> <i>(worst-case)</i>
615
+ *
616
+ * @return [Object] the value with the minimum score
617
+ *
618
+ * @see #peek
619
+ * @see #pop
620
+ * @see #pop_lte
621
+ */
622
+ static VALUE
623
+ dheap_pop_lt(VALUE self, VALUE max_score)
624
+ {
625
+ dheap_t *heap = get_dheap_struct(self);
626
+ rb_check_frozen(self);
627
+ if (DHEAP_SIZE(heap) <= 0) return Qnil;
628
+ if (!CMP_LT(DHEAP_SCORE(heap, 0), VAL2SCORE(max_score))) return Qnil;
629
+ return dheap_pop0(heap);
304
630
  }
305
631
 
306
632
  void
307
633
  Init_d_heap(void)
308
634
  {
309
635
  id_cmp = rb_intern_const("<=>");
310
- id_ivar_a = rb_intern_const("ary");
311
- id_ivar_d = rb_intern_const("d");
636
+ id_abs = rb_intern_const("abs");
312
637
 
313
638
  rb_cDHeap = rb_define_class("DHeap", rb_cObject);
314
- rb_define_singleton_method(rb_cDHeap, "heap_sift_down", dheap_sift_down_s, 3);
315
- rb_define_singleton_method(rb_cDHeap, "heap_sift_up", dheap_sift_up_s, 3);
639
+ rb_define_alloc_func(rb_cDHeap, dheap_s_alloc);
316
640
 
317
- rb_define_method(rb_cDHeap, "initialize", dheap_initialize, -1);
318
- rb_define_method(rb_cDHeap, "d", dheap_attr_d, 0);
319
- rb_define_method(rb_cDHeap, "freeze", dheap_freeze, 0);
641
+ rb_define_const(rb_cDHeap, "MAX_D", INT2NUM(DHEAP_MAX_D));
642
+ rb_define_const(rb_cDHeap, "DEFAULT_D", INT2NUM(DHEAP_DEFAULT_D));
320
643
 
321
- rb_define_method(rb_cDHeap, "size", dheap_size, 0);
322
- rb_define_method(rb_cDHeap, "empty?", dheap_empty_p, 0);
644
+ rb_define_method(rb_cDHeap, "initialize", dheap_initialize, -1);
645
+ rb_define_method(rb_cDHeap, "initialize_copy", dheap_initialize_copy, 1);
646
+ rb_define_private_method(rb_cDHeap, "__init_clone__", dheap_init_clone, 2);
323
647
 
648
+ rb_define_method(rb_cDHeap, "d", dheap_attr_d, 0);
649
+ rb_define_method(rb_cDHeap, "size", dheap_size, 0);
650
+ rb_define_method(rb_cDHeap, "empty?", dheap_empty_p, 0);
324
651
  rb_define_method(rb_cDHeap, "peek", dheap_peek, 0);
652
+
653
+ rb_define_method(rb_cDHeap, "clear", dheap_clear, 0);
654
+ rb_define_method(rb_cDHeap, "insert", dheap_insert, 2);
325
655
  rb_define_method(rb_cDHeap, "push", dheap_push, -1);
326
656
  rb_define_method(rb_cDHeap, "<<", dheap_left_shift, 1);
327
657
  rb_define_method(rb_cDHeap, "pop", dheap_pop, 0);