d_heap 0.2.0 → 0.5.0

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