d_heap 0.1.0 → 0.4.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,219 +1,762 @@
1
+ #include <float.h>
1
2
  #include "d_heap.h"
2
3
 
3
- ID id_ivar_a;
4
- ID id_ivar_d;
4
+ #define SCORE_AS_LONG_DOUBLE 1
5
5
 
6
- #define DHEAP_Check_d_size(d) \
7
- if (d < 2) { \
8
- rb_raise(rb_eIndexError, "DHeap d=%d is too small", d); \
9
- } \
10
- if (d > DHEAP_MAX_D) { \
11
- rb_raise(rb_eIndexError, "DHeap d=%d is too large", d); \
6
+ ID id_cmp; // <=>
7
+ ID id_abs; // abs
8
+
9
+ #ifdef SCORE_AS_LONG_DOUBLE
10
+ #define SCORE long double
11
+ #else
12
+ #define SCORE VALUE
13
+ #endif
14
+
15
+ typedef struct dheap_struct {
16
+ int d;
17
+ VALUE values;
18
+ #ifdef SCORE_AS_LONG_DOUBLE
19
+ long size;
20
+ long capa;
21
+ SCORE *cscores;
22
+ #else
23
+ VALUE scores; // T_ARRAY of comparable objects
24
+ #endif
25
+ } dheap_t;
26
+
27
+ #define DHEAP_VALUE(heap, idx) RARRAY_AREF((heap)->values, idx)
28
+ #define DHEAP_IDX_LAST(heap) (DHEAP_SIZE((heap)) - 1)
29
+ #define DHEAP_IDX_PARENT(heap, idx) (((idx) - 1) / (heap)->d)
30
+ #define DHEAP_IDX_CHILD0(heap, idx) (((idx) * (heap)->d) + 1)
31
+
32
+ #ifdef SCORE_AS_LONG_DOUBLE
33
+ #define DHEAP_SIZE(heap) ((heap)->size)
34
+ #else
35
+ #define DHEAP_SIZE(heap) (RARRAY_LEN((heap)->scores))
36
+ #endif
37
+
38
+ #ifdef SCORE_AS_LONG_DOUBLE
39
+ #define DHEAP_SCORE(heap, idx) \
40
+ (idx < 0 || heap->size <= idx ? (SCORE)0 : \
41
+ ((heap)->cscores[idx]))
42
+ #else
43
+ #define DHEAP_SCORE(heap, idx) RARRAY_AREF((heap)->scores, idx)
44
+ #endif
45
+
46
+ #ifdef SCORE_AS_LONG_DOUBLE
47
+
48
+ #define CMP_LT(a, b) (a < b)
49
+ #define CMP_LTE(a, b) (a <= b)
50
+ #define CMP_GT(a, b) (a > b)
51
+ #define CMP_GTE(a, b) (a >= b)
52
+
53
+ #if LDBL_MANT_DIG < SIZEOF_UNSIGNED_LONG_LONG * 8
54
+ #error 'unsigned long long' should fit into 'long double' mantissa
55
+ #endif
56
+
57
+ // copied and modified from ruby's object.c
58
+ #define FIX2SCORE(x) (long double)FIX2LONG(x)
59
+ // We could translate a much wider range of values to long double by
60
+ // implementing a new `rb_big2ldbl(x)` function. But requires reaching into
61
+ // T_BIGNUM internals.
62
+ static inline long double
63
+ BIG2SCORE(VALUE x)
64
+ {
65
+ if (RBIGNUM_POSITIVE_P(x)) {
66
+ unsigned long long ull = rb_big2ull(x);
67
+ return (long double)ull;
68
+ } else {
69
+ unsigned long long ull;
70
+ long double ldbl;
71
+ x = rb_funcall(x, id_abs, 0);
72
+ ull = rb_big2ull(x);
73
+ ldbl = (long double) ull;
74
+ return -ldbl;
75
+ }
76
+ }
77
+ #define INT2SCORE(x) \
78
+ (FIXNUM_P(x) ? FIX2SCORE(x) : BIG2SCORE(x))
79
+ #define NUM2SCORE(x) \
80
+ (FIXNUM_P(x) ? FIX2SCORE(x) : \
81
+ RB_TYPE_P(x, T_BIGNUM) ? BIG2SCORE(x) : \
82
+ (Check_Type(x, T_FLOAT), (long double)RFLOAT_VALUE(x)))
83
+ static inline long double
84
+ RAT2SCORE(VALUE x)
85
+ {
86
+ VALUE num = rb_rational_num(x);
87
+ VALUE den = rb_rational_den(x);
88
+ return NUM2SCORE(num) / NUM2SCORE(den);
89
+ }
90
+
91
+ /*
92
+ * Convert both T_FIXNUM and T_FLOAT (and sometimes T_BIGNUM, T_RATIONAL,
93
+ * String, etc) to SCORE
94
+ * * with no loss of precision (where possible for Integer and Float),
95
+ * * raises an exception if
96
+ * * a positive integer is too large for unsigned long long (should be 64bit)
97
+ * * a negative integer is too small for signed long long (should be 64bit)
98
+ * * reduced to long double (should be 80 or 128 bit) if it is Rational
99
+ * * reduced to double precision if the value is convertable by Float(x)
100
+ */
101
+ static inline long double
102
+ VAL2SCORE(VALUE score)
103
+ {
104
+ // assert that long double can hold 'unsigned long long':
105
+ // static_assert(sizeof(unsigned long long) * 8 <= LDBL_MANT_DIG);
106
+ // assert that long double can hold T_FLOAT
107
+ // static_assert(sizeof(double) <= sizeof(long double));
108
+
109
+ switch (TYPE(score)) {
110
+ case T_FIXNUM:
111
+ return FIX2SCORE(score);
112
+ case T_BIGNUM:
113
+ return BIG2SCORE(score);
114
+ case T_RATIONAL:
115
+ return RAT2SCORE(score);
116
+ default:
117
+ return (long double)(NUM2DBL(rb_Float(score)));
118
+ }
119
+ }
120
+
121
+ #else
122
+
123
+ #define VAL2SCORE(score) (score)
124
+
125
+ #define CMP_LT(a, b) (optimized_cmp(a, b) < 0)
126
+ #define CMP_LTE(a, b) (optimized_cmp(a, b) <= 0)
127
+ #define CMP_GT(a, b) (optimized_cmp(a, b) > 0)
128
+ #define CMP_GTE(a, b) (optimized_cmp(a, b) >= 0)
129
+
130
+ // from internal/compar.h
131
+ #define STRING_P(s) (RB_TYPE_P((s), T_STRING) && CLASS_OF(s) == rb_cString)
132
+ /*
133
+ * short-circuit evaluation for a few basic types.
134
+ *
135
+ * Only Integer, Float, and String are optimized,
136
+ * and only when both arguments are the same type.
137
+ */
138
+ static inline int
139
+ optimized_cmp(SCORE a, SCORE b) {
140
+ if (a == b) // Fixnum equality and object equality
141
+ return 0;
142
+ if (FIXNUM_P(a) && FIXNUM_P(b))
143
+ return (FIX2LONG(a) < FIX2LONG(b)) ? -1 : 1;
144
+ if (RB_FLOAT_TYPE_P(a) && RB_FLOAT_TYPE_P(b))
145
+ {
146
+ double x, y;
147
+ x = RFLOAT_VALUE(a);
148
+ y = RFLOAT_VALUE(b);
149
+ if (isnan(x) || isnan(y)) rb_cmperr(a, b); // raise ArgumentError
150
+ return (x < y) ? -1 : ((x == y) ? 0 : 1);
151
+ }
152
+ if (RB_TYPE_P(a, T_BIGNUM) && RB_TYPE_P(b, T_BIGNUM))
153
+ return FIX2INT(rb_big_cmp(a, b));
154
+ if (STRING_P(a) && STRING_P(b))
155
+ return rb_str_cmp(a, b);
156
+
157
+ // give up on an optimized version and just call (a <=> b)
158
+ return rb_cmpint(rb_funcallv(a, id_cmp, 1, &b), a, b);
159
+ }
160
+
161
+ #endif
162
+
163
+ #define DHEAP_Check_d_size(d) do { \
164
+ if (d < 2) { \
165
+ rb_raise(rb_eArgError, "DHeap d=%d is too small", d); \
166
+ } \
167
+ if (d > DHEAP_MAX_D) { \
168
+ rb_raise(rb_eArgError, "DHeap d=%d is too large", d); \
169
+ } \
170
+ } while (0)
171
+
172
+ #define DHEAP_Check_Index(index, last_index) do { \
173
+ if (index < 0) { \
174
+ rb_raise(rb_eIndexError, "DHeap index %ld too small", index); \
175
+ } \
176
+ else if (last_index < index) { \
177
+ rb_raise(rb_eIndexError, "DHeap index %ld too large", index); \
178
+ } \
179
+ } while (0)
180
+
181
+ static void
182
+ dheap_compact(void *ptr)
183
+ {
184
+ dheap_t *heap = ptr;
185
+ #ifndef SCORE_AS_LONG_DOUBLE
186
+ if (heap->scores) dheap_gc_location( heap->scores );
187
+ #endif
188
+ if (heap->values) dheap_gc_location( heap->values );
189
+ }
190
+
191
+ static void
192
+ dheap_mark(void *ptr)
193
+ {
194
+ dheap_t *heap = ptr;
195
+ #ifndef SCORE_AS_LONG_DOUBLE
196
+ if (heap->scores) rb_gc_mark_movable(heap->scores);
197
+ #endif
198
+ if (heap->values) rb_gc_mark_movable(heap->values);
199
+ }
200
+
201
+ static void
202
+ dheap_free(void *ptr)
203
+ {
204
+ #ifdef SCORE_AS_LONG_DOUBLE
205
+ dheap_t *heap = ptr;
206
+ heap->size = 0;
207
+ if (heap->cscores) {
208
+ ruby_xfree(heap->cscores);
209
+ heap->cscores = NULL;
210
+ }
211
+ heap->capa = 0;
212
+ #endif
213
+ xfree(ptr);
214
+ }
215
+
216
+ static size_t
217
+ dheap_memsize(const void *ptr)
218
+ {
219
+ const dheap_t *heap = ptr;
220
+ size_t size = 0;
221
+ size += sizeof(*heap);
222
+ #ifdef SCORE_AS_LONG_DOUBLE
223
+ size += sizeof(long double) * heap->capa;
224
+ #endif
225
+ return size;
226
+ }
227
+
228
+ static const rb_data_type_t dheap_data_type = {
229
+ "DHeap",
230
+ {
231
+ (void (*)(void*))dheap_mark,
232
+ (void (*)(void*))dheap_free,
233
+ (size_t (*)(const void *))dheap_memsize,
234
+ dheap_compact_callback(dheap_compact),
235
+ },
236
+ 0, 0,
237
+ RUBY_TYPED_FREE_IMMEDIATELY,
238
+ };
239
+
240
+ static VALUE
241
+ dheap_s_alloc(VALUE klass)
242
+ {
243
+ VALUE obj;
244
+ dheap_t *heap;
245
+
246
+ obj = TypedData_Make_Struct(klass, dheap_t, &dheap_data_type, heap);
247
+ heap->d = DHEAP_DEFAULT_D;
248
+ heap->values = Qnil;
249
+
250
+ #ifdef SCORE_AS_LONG_DOUBLE
251
+ heap->size = 0;
252
+ heap->capa = 0;
253
+ heap->cscores = NULL;
254
+ #else
255
+ heap->scores = Qnil;
256
+ #endif
257
+
258
+ return obj;
259
+ }
260
+
261
+ static inline dheap_t *
262
+ get_dheap_struct(VALUE self)
263
+ {
264
+ dheap_t *heap;
265
+ TypedData_Get_Struct(self, dheap_t, &dheap_data_type, heap);
266
+ Check_Type(heap->values, T_ARRAY); // ensure it's been initialized
267
+ return heap;
268
+ }
269
+
270
+ #ifdef SCORE_AS_LONG_DOUBLE
271
+
272
+ static void
273
+ dheap_set_capa(dheap_t *heap, long new_capa)
274
+ {
275
+ long double *new, *old;
276
+ // Do nothing if we already have the capacity or are resizing too small
277
+ if (new_capa <= heap->capa) return;
278
+ if (new_capa <= heap->size) return;
279
+
280
+ // allocate
281
+ new = ruby_xcalloc(new_capa, sizeof(long double));
282
+ old = heap->cscores;
283
+
284
+ // copy contents
285
+ if (old) {
286
+ MEMCPY(new, old, long double, heap->size);
287
+ ruby_xfree(old);
12
288
  }
13
289
 
14
- #define DHEAP_Check_Sift_Idx(sift_idx, last_idx) \
15
- if (sift_idx < 0) { \
16
- rb_raise(rb_eIndexError, "sift_idx %ld too small", sift_idx); \
17
- } \
18
- else if (last_idx < sift_idx) { \
19
- rb_raise(rb_eIndexError, "sift_idx %ld too large", sift_idx); \
290
+ // set vars
291
+ heap->cscores = new;
292
+ heap->capa = new_capa;
293
+ }
294
+
295
+ static void
296
+ dheap_ensure_room_for_push(dheap_t *heap, long incr_by)
297
+ {
298
+ long new_size = heap->size + incr_by;
299
+
300
+ // check for overflow of new_size
301
+ if (DHEAP_MAX_SIZE - incr_by < heap->size)
302
+ rb_raise(rb_eIndexError, "index %ld too big", new_size);
303
+
304
+ // if it existing capacity is too small
305
+ if (heap->capa < new_size) {
306
+ // double it...
307
+ long new_capa = new_size * 2;
308
+ if (DHEAP_CAPA_INCR_MAX < new_size)
309
+ new_size = new_size + DHEAP_CAPA_INCR_MAX;
310
+ // check for overflow of new_capa
311
+ if (DHEAP_MAX_SIZE / 2 < new_size) new_capa = DHEAP_MAX_SIZE;
312
+ // cap max incr_by
313
+ if (heap->capa + DHEAP_CAPA_INCR_MAX < new_capa)
314
+ new_capa = heap->capa + DHEAP_CAPA_INCR_MAX;
315
+
316
+ dheap_set_capa(heap, new_capa);
20
317
  }
318
+ }
319
+
320
+ #endif
321
+
322
+ /*
323
+ * @overload initialize(d = DHeap::DEFAULT_D)
324
+ * Initialize a _d_-ary min-heap.
325
+ *
326
+ * @param d [Integer] maximum number of children per parent
327
+ */
328
+ static VALUE
329
+ dheap_initialize(int argc, VALUE *argv, VALUE self) {
330
+ dheap_t *heap;
331
+ int d;
332
+
333
+ rb_check_arity(argc, 0, 1);
334
+ TypedData_Get_Struct(self, dheap_t, &dheap_data_type, heap);
335
+
336
+ d = argc ? NUM2INT(argv[0]) : DHEAP_DEFAULT_D;
337
+ DHEAP_Check_d_size(d);
338
+ heap->d = d;
339
+
340
+ heap->values = rb_ary_new_capa(DHEAP_DEFAULT_SIZE);
341
+
342
+ #ifdef SCORE_AS_LONG_DOUBLE
343
+ dheap_set_capa(heap, DHEAP_DEFAULT_SIZE);
344
+ #else
345
+ heap->scores = rb_ary_new_capa(DHEAP_DEFAULT_SIZE);
346
+ #endif
347
+
348
+ return self;
349
+ }
350
+
351
+ /* :nodoc: */
352
+ static VALUE
353
+ dheap_initialize_copy(VALUE copy, VALUE orig)
354
+ {
355
+ dheap_t *heap_copy;
356
+ dheap_t *heap_orig = get_dheap_struct(orig);
357
+
358
+ rb_check_frozen(copy);
359
+ TypedData_Get_Struct(copy, dheap_t, &dheap_data_type, heap_copy);
21
360
 
22
- #define DHEAP_Load_Sift_Vals(heap_array, dval, idxval) \
23
- Check_Type(dval, T_FIXNUM); \
24
- int d = FIX2INT(dval); \
25
- long sift_idx = NUM2LONG(idxval);
361
+ heap_copy->d = heap_orig->d;
26
362
 
27
- #define DHEAP_Check_Sift_Args(heap_array, d, sift_idx) \
28
- DHEAP_Check_d_size(d); \
29
- Check_Type(heap_array, T_ARRAY); \
30
- long last_idx = RARRAY_LEN(heap_array) - 1; \
31
- DHEAP_Check_Sift_Idx(sift_idx, last_idx); \
32
- \
33
- VALUE sift_val = rb_ary_entry(heap_array, sift_idx);
363
+ heap_copy->values = rb_ary_new();
364
+ rb_ary_replace(heap_copy->values, heap_orig->values);
365
+
366
+ #ifdef SCORE_AS_LONG_DOUBLE
367
+ dheap_set_capa(heap_copy, heap_orig->capa);
368
+ heap_copy->size = heap_orig->size;
369
+ if (heap_copy->size)
370
+ MEMCPY(heap_orig->cscores, heap_copy->cscores, long double, heap_orig->size);
371
+ #else
372
+ heap_copy->scores = rb_ary_new();
373
+ rb_ary_replace(heap_copy->scores, heap_orig->scores);
374
+ #endif
375
+
376
+ return copy;
377
+ }
378
+
379
+ static inline void
380
+ dheap_assign(dheap_t *heap, long idx, SCORE score, VALUE value)
381
+ {
382
+ #ifdef SCORE_AS_LONG_DOUBLE
383
+ heap->cscores[idx] = score;
384
+ rb_ary_store(heap->values, idx, value);
385
+ #else
386
+ rb_ary_store(heap->scores, idx, score);
387
+ rb_ary_store(heap->values, idx, value);
388
+ #endif
389
+ }
34
390
 
35
391
  VALUE
36
- dheap_ary_sift_up(VALUE heap_array, int d, long sift_idx) {
37
- DHEAP_Check_Sift_Args(heap_array, d, sift_idx);
392
+ dheap_ary_sift_up(dheap_t *heap, long sift_index) {
393
+ VALUE sift_value;
394
+ SCORE sift_score;
395
+
396
+ long last_index = DHEAP_IDX_LAST(heap);
397
+ DHEAP_Check_Index(sift_index, last_index);
398
+
399
+ sift_value = DHEAP_VALUE(heap, sift_index);
400
+ sift_score = DHEAP_SCORE(heap, sift_index);
401
+
38
402
  // sift it up to where it belongs
39
- for (long parent_idx; 0 < sift_idx; sift_idx = parent_idx) {
40
- parent_idx = (sift_idx - 1) / d;
41
- VALUE parent_val = rb_ary_entry(heap_array, parent_idx);
403
+ for (long parent_index; 0 < sift_index; sift_index = parent_index) {
404
+ SCORE parent_score;
405
+ VALUE parent_value;
406
+
407
+ debug(rb_sprintf("sift up(%"PRIsVALUE", %d, %ld)", heap->values, heap->d, sift_index));
408
+ parent_index = DHEAP_IDX_PARENT(heap, sift_index);
409
+ parent_score = DHEAP_SCORE(heap, parent_index);
42
410
 
43
411
  // parent is smaller: heap is restored
44
- if (CMP_LTE(parent_val, sift_val)) break;
412
+ if (CMP_LTE(parent_score, sift_score)) break;
45
413
 
46
414
  // parent is larger: swap and continue sifting up
47
- rb_ary_store(heap_array, sift_idx, parent_val);
48
- rb_ary_store(heap_array, parent_idx, sift_val);
415
+ parent_value = DHEAP_VALUE(heap, parent_index);
416
+ dheap_assign(heap, sift_index, parent_score, parent_value);
417
+ dheap_assign(heap, parent_index, sift_score, sift_value);
49
418
  }
50
- return LONG2NUM(sift_idx);
419
+ debug(rb_sprintf("sifted (%"PRIsVALUE", %d, %ld)", heap->values, heap->d, sift_index));
420
+ return LONG2NUM(sift_index);
51
421
  }
52
422
 
53
-
54
423
  VALUE
55
- dheap_ary_sift_down(VALUE heap_array, int d, long sift_idx) {
56
- DHEAP_Check_Sift_Args(heap_array, d, sift_idx);
424
+ dheap_ary_sift_down(dheap_t *heap, long sift_index) {
425
+ VALUE sift_value;
426
+ SCORE sift_score;
427
+ long last_index = DHEAP_IDX_LAST(heap);
428
+ DHEAP_Check_Index(sift_index, last_index);
429
+
430
+ sift_value = DHEAP_VALUE(heap, sift_index);
431
+ sift_score = DHEAP_SCORE(heap, sift_index);
432
+
57
433
  // iteratively sift it down to where it belongs
58
- for (long child_idx; sift_idx < last_idx; sift_idx = child_idx) {
434
+ for (long child_index; sift_index < last_index; sift_index = child_index) {
435
+ long child_idx0, last_sibidx;
436
+ SCORE child_score;
437
+ VALUE child_value;
438
+
59
439
  // find first child index, and break if we've reached the last layer
60
- long child_idx0 = sift_idx * d + 1;
61
- child_idx = child_idx0;
62
- if (last_idx < child_idx0) break;
63
-
64
- // find the min child (and its child_idx)
65
- // requires "d" comparisons. (d - 1 to find min child; + 1 vs sift_val)
66
- VALUE child_val = rb_ary_entry(heap_array, child_idx0);
67
- for (int i = 1; i < d; i++) {
68
- long sibling_idx = child_idx0 + i;
69
- if (last_idx < sibling_idx) break;
70
-
71
- VALUE sibling_val = rb_ary_entry(heap_array, sibling_idx);
72
-
73
- if (CMP_LT(sibling_val, child_val)) {
74
- child_val = sibling_val;
75
- child_idx = sibling_idx;
440
+ child_idx0 = child_index = DHEAP_IDX_CHILD0(heap, sift_index);
441
+ debug(rb_sprintf("sift dn(%"PRIsVALUE", %d, %ld)", heap->values, heap->d, sift_index));
442
+ if (last_index < child_idx0) break;
443
+
444
+ // find the min child (and its child_index)
445
+ // requires "d" comparisons to find min child and compare to sift_score
446
+ last_sibidx = child_idx0 + heap->d - 1;
447
+ if (last_index < last_sibidx) last_sibidx = last_index;
448
+ child_score = DHEAP_SCORE(heap, child_idx0);
449
+ child_index = child_idx0;
450
+ for (long sibling_index = child_idx0 + 1;
451
+ sibling_index <= last_sibidx;
452
+ ++sibling_index) {
453
+ SCORE sibling_score = DHEAP_SCORE(heap, sibling_index);
454
+
455
+ if (CMP_LT(sibling_score, child_score)) {
456
+ child_score = sibling_score;
457
+ child_index = sibling_index;
76
458
  }
77
459
  }
78
460
 
79
461
  // child is larger: heap is restored
80
- if (CMP_GT(child_val, sift_val)) break;
462
+ if (CMP_LTE(sift_score, child_score)) break;
81
463
 
82
464
  // child is smaller: swap and continue sifting down
83
- rb_ary_store(heap_array, sift_idx, child_val);
84
- rb_ary_store(heap_array, child_idx, sift_val);
465
+ child_value = DHEAP_VALUE(heap, child_index);
466
+ dheap_assign(heap, sift_index, child_score, child_value);
467
+ dheap_assign(heap, child_index, sift_score, sift_value);
85
468
  }
86
- return LONG2NUM(sift_idx);
469
+ debug(rb_sprintf("sifted (%"PRIsVALUE", %d, %ld)", heap->values, heap->d, sift_index));
470
+ return LONG2NUM(sift_index);
87
471
  }
88
472
 
89
473
  /*
90
- * call-seq:
91
- * DHeap.array_sift_up(heap_array, d, sift_idx)
92
- *
93
- * Treats +heap_array+ as a +d+-ary heap and sifts up from +sift_idx+ to restore
94
- * the heap property.
95
- *
96
- * Time complexity: O(d log n / log d). If the average up shifted element sorts
97
- * into the bottom layer (e.g. new timers), this can avg O(1).
474
+ * @return [Integer] the number of elements in the heap
475
+ */
476
+ static VALUE
477
+ dheap_size(VALUE self)
478
+ {
479
+ dheap_t *heap = get_dheap_struct(self);
480
+ long size = DHEAP_SIZE(heap);
481
+ return LONG2NUM(size);
482
+ }
483
+
484
+ /*
485
+ * @return [Boolean] is the heap empty?
486
+ */
487
+ static VALUE
488
+ dheap_empty_p(VALUE self)
489
+ {
490
+ dheap_t *heap = get_dheap_struct(self);
491
+ long size = DHEAP_SIZE(heap);
492
+ return size == 0 ? Qtrue : Qfalse;
493
+ }
494
+
495
+ /*
496
+ * @return [Integer] the maximum number of children per parent
497
+ */
498
+ static VALUE
499
+ dheap_attr_d(VALUE self)
500
+ {
501
+ dheap_t *heap = get_dheap_struct(self);
502
+ return INT2FIX(heap->d);
503
+ }
504
+
505
+ /*
506
+ * Freezes the heap as well as its underlying array, but does <i>not</i>
507
+ * deep-freeze the elements in the heap.
98
508
  *
509
+ * @return [self]
99
510
  */
100
511
  static VALUE
101
- dheap_sift_up_s(VALUE unused, VALUE heap_array, VALUE dval, VALUE idxval) {
102
- DHEAP_Load_Sift_Vals(heap_array, dval, idxval);
103
- return dheap_ary_sift_up(heap_array, d, sift_idx);
512
+ dheap_freeze(VALUE self) {
513
+ dheap_t *heap = get_dheap_struct(self);
514
+ ID id_freeze = rb_intern("freeze");
515
+ rb_funcall(heap->values, id_freeze, 0);
516
+ #ifndef SCORE_AS_LONG_DOUBLE
517
+ rb_funcall(heap->scores, id_freeze, 0);
518
+ #endif
519
+ return rb_call_super(0, NULL);
520
+ }
521
+
522
+ /* :nodoc: */
523
+ static VALUE
524
+ dheap_init_clone(VALUE clone, VALUE orig, VALUE kwfreeze)
525
+ {
526
+ dheap_initialize_copy(clone, orig);
527
+ if (RTEST(kwfreeze) || (kwfreeze == Qnil && OBJ_FROZEN(orig))) {
528
+ rb_funcall(clone, rb_intern("freeze"), 0);
529
+ }
530
+ return clone;
104
531
  }
105
532
 
106
533
  /*
107
- * call-seq:
108
- * DHeap.array_sift_down(heap_array, d, sift_idx)
534
+ * @overload push(score, value = score)
535
+ *
536
+ * Push a value onto heap, using a score to determine sort-order.
109
537
  *
110
- * Treats +heap_array+ as a +d+-ary heap and sifts down from +sift_idx+ to
111
- * restore the heap property.
538
+ * Ideally, the score should be a frozen value that can be efficiently compared
539
+ * to other scores, e.g. an Integer or Float or (maybe) a String
112
540
  *
113
- * Time complexity: O(d log n / log d). If the average down shifted element
114
- * sorts into the bottom layer (e.g. canceled timers), this can avg O(1).
541
+ * Time complexity: <b>O(log n / log d)</b> <i>(worst-case)</i>
115
542
  *
543
+ * @param score [#<=>] a value that can be compared to other scores.
544
+ * @param value [Object] an object that is associated with the score.
545
+ *
546
+ * @return [Integer] the index of the value's final position.
116
547
  */
117
548
  static VALUE
118
- dheap_sift_down_s(VALUE unused, VALUE heap_array, VALUE dval, VALUE idxval) {
119
- DHEAP_Load_Sift_Vals(heap_array, dval, idxval);
120
- return dheap_ary_sift_down(heap_array, d, sift_idx);
549
+ dheap_push(int argc, VALUE *argv, VALUE self) {
550
+ VALUE scr, val;
551
+ dheap_t *heap;
552
+ long last_index;
553
+ rb_check_frozen(self);
554
+
555
+ rb_check_arity(argc, 1, 2);
556
+ heap = get_dheap_struct(self);
557
+ scr = argv[0];
558
+ val = argc < 2 ? scr : argv[1];
559
+
560
+ #ifdef SCORE_AS_LONG_DOUBLE
561
+ do {
562
+ long double score_as_ldbl = VAL2SCORE(scr);
563
+ dheap_ensure_room_for_push(heap, 1);
564
+ ++heap->size;
565
+ last_index = DHEAP_IDX_LAST(heap);
566
+ heap->cscores[last_index] = score_as_ldbl;
567
+ } while (0);
568
+ #else
569
+ rb_ary_push((heap)->scores, scr);
570
+ last_index = DHEAP_IDX_LAST(heap);
571
+ #endif
572
+ rb_ary_push((heap)->values, val);
573
+
574
+ return dheap_ary_sift_up(heap, last_index);
121
575
  }
122
576
 
577
+ /*
578
+ * Pushes a comparable value onto the heap.
579
+ *
580
+ * The value will be its own score.
581
+ *
582
+ * Time complexity: <b>O(log n / log d)</b> <i>(worst-case)</i>
583
+ *
584
+ * @param value [#<=>] a value that can be compared to other heap members.
585
+ * @return [self]
586
+ */
123
587
  static VALUE
124
- dheap_initialize(int argc, VALUE *argv, VALUE self) {
125
- rb_check_arity(argc, 0, 1);
126
- int d = DHEAP_DEFAULT_D;
127
- if (argc) {
128
- d = NUM2INT(argv[0]);
129
- }
130
- DHEAP_Check_d_size(d);
131
- rb_ivar_set(self, id_ivar_d, INT2FIX(d));
132
- rb_ivar_set(self, id_ivar_a, rb_ary_new());
588
+ dheap_left_shift(VALUE self, VALUE value) {
589
+ dheap_push(1, &value, self);
133
590
  return self;
134
591
  }
135
592
 
136
- static inline VALUE dheap_get_a(VALUE dheap) { return rb_ivar_get(dheap, id_ivar_a); }
137
- static inline VALUE dheap_get_d(VALUE dheap) { return rb_ivar_get(dheap, id_ivar_d); }
593
+ #ifdef SCORE_AS_LONG_DOUBLE
594
+ #define DHEAP_DROP_LAST(heap) do { \
595
+ rb_ary_pop(heap->values); \
596
+ --heap->size; \
597
+ } while (0)
598
+ #else
599
+ #define DHEAP_DROP_LAST(heap) do { \
600
+ rb_ary_pop(heap->values); \
601
+ rb_ary_pop(heap->scores); \
602
+ } while (0)
603
+ #endif
604
+ static inline void
605
+ dheap_pop_swap_last_and_sift_down(dheap_t *heap, long last_index)
606
+ {
607
+ if (last_index == 0) {
608
+ DHEAP_DROP_LAST(heap);
609
+ }
610
+ else
611
+ {
612
+ VALUE sift_value = DHEAP_VALUE(heap, last_index);
613
+ SCORE sift_score = DHEAP_SCORE(heap, last_index);
614
+ dheap_assign(heap, 0, sift_score, sift_value);
615
+ DHEAP_DROP_LAST(heap);
616
+ dheap_ary_sift_down(heap, 0);
617
+ }
618
+ }
138
619
 
139
- static VALUE dheap_a(VALUE dheap) { return dheap_get_a(dheap); }
140
- static VALUE dheap_d(VALUE dheap) { return dheap_get_d(dheap); }
620
+ #ifdef SCORE_AS_LONG_DOUBLE
621
+ #define DHEAP_CLEAR(heap) do { \
622
+ rb_ary_clear(heap->values); \
623
+ heap->size = 0; \
624
+ } while (0)
625
+ #else
626
+ #define DHEAP_CLEAR(heap) do { \
627
+ rb_ary_clear(heap->values); \
628
+ rb_ary_clear(heap->scores); \
629
+ } while (0)
630
+ #endif
141
631
 
142
632
  /*
143
- * Push val onto the end of the heap, then sift up to maintain heap property.
633
+ * Returns the next value on the heap to be popped without popping it.
144
634
  *
145
- * Returns the index of the value's final position.
635
+ * Time complexity: <b>O(1)</b> <i>(worst-case)</i>
636
+ * @return [Object] the next value to be popped without popping it.
637
+ */
638
+ static VALUE
639
+ dheap_clear(VALUE self) {
640
+ dheap_t *heap = get_dheap_struct(self);
641
+ rb_check_frozen(self);
642
+ if (0 < DHEAP_SIZE(heap)) {
643
+ DHEAP_CLEAR(heap);
644
+ }
645
+ return self;
646
+ }
647
+
648
+ /*
649
+ * Returns the next value on the heap to be popped without popping it.
146
650
  *
651
+ * Time complexity: <b>O(1)</b> <i>(worst-case)</i>
652
+ * @return [Object] the next value to be popped without popping it.
147
653
  */
148
654
  static VALUE
149
- dheap_push(VALUE dheap, VALUE val) {
150
- VALUE heap_a = dheap_get_a(dheap);
151
- VALUE heap_d = dheap_get_d(dheap);
152
- rb_ary_push(heap_a, val);
153
- long last_idx = RARRAY_LEN(heap_a) - 1;
154
- return dheap_sift_up_s(Qnil, heap_a, heap_d, LONG2NUM(last_idx));
655
+ dheap_peek(VALUE self) {
656
+ dheap_t *heap = get_dheap_struct(self);
657
+ if (DHEAP_IDX_LAST(heap) < 0) return Qnil;
658
+ return DHEAP_VALUE(heap, 0);
155
659
  }
156
660
 
157
661
  /*
158
- * Push val onto the end of the heap, then sift up to maintain heap property.
662
+ * Pops the minimum value from the top of the heap
159
663
  *
160
- * Time complexity: O(d log n / log d).
664
+ * Time complexity: <b>O(d log n / log d)</b> <i>(worst-case)</i>
665
+ */
666
+ static VALUE
667
+ dheap_pop(VALUE self) {
668
+ VALUE pop_value;
669
+ dheap_t *heap = get_dheap_struct(self);
670
+ long last_index = DHEAP_IDX_LAST(heap);
671
+ rb_check_frozen(self);
672
+
673
+ if (last_index < 0) return Qnil;
674
+ pop_value = DHEAP_VALUE(heap, 0);
675
+
676
+ dheap_pop_swap_last_and_sift_down(heap, last_index);
677
+ return pop_value;
678
+ }
679
+
680
+ /*
681
+ * Pops the minimum value only if it is less than or equal to a max score.
161
682
  *
162
- * Returns +self+.
683
+ * @param max_score [#to_f] the maximum score to be popped
163
684
  *
685
+ * @see #pop
164
686
  */
165
687
  static VALUE
166
- dheap_left_shift(VALUE dheap, VALUE val) {
167
- dheap_push(dheap, val);
168
- return dheap;
688
+ dheap_pop_lte(VALUE self, VALUE max_score) {
689
+ VALUE pop_value;
690
+ dheap_t *heap = get_dheap_struct(self);
691
+ long last_index = DHEAP_IDX_LAST(heap);
692
+ rb_check_frozen(self);
693
+
694
+ if (last_index < 0) return Qnil;
695
+ pop_value = DHEAP_VALUE(heap, 0);
696
+
697
+ do {
698
+ SCORE max = VAL2SCORE(max_score);
699
+ SCORE pop_score = DHEAP_SCORE(heap, 0);
700
+ if (max && !CMP_LTE(pop_score, max)) return Qnil;
701
+ } while (0);
702
+
703
+ dheap_pop_swap_last_and_sift_down(heap, last_index);
704
+ return pop_value;
169
705
  }
170
706
 
171
707
  /*
172
- * Pops the minimum value from the top of the heap, sifting down to maintain
173
- * heap property.
708
+ * Pops the minimum value only if it is less than a max score.
174
709
  *
175
- * Time complexity: O(log n / log d).
710
+ * @param max_score [#to_f] the maximum score to be popped
176
711
  *
712
+ * Time complexity: <b>O(d log n / log d)</b> <i>(worst-case)</i>
177
713
  */
178
714
  static VALUE
179
- dheap_pop(VALUE dheap) {
180
- VALUE heap_a = dheap_get_a(dheap);
181
- VALUE heap_d = dheap_get_d(dheap);
182
- long last_idx = RARRAY_LEN(heap_a) - 1;
715
+ dheap_pop_lt(VALUE self, VALUE max_score) {
716
+ VALUE pop_value;
717
+ dheap_t *heap = get_dheap_struct(self);
718
+ long last_index = DHEAP_IDX_LAST(heap);
719
+ rb_check_frozen(self);
183
720
 
184
- /*
185
- * short-circuit empty or nearly empty
186
- */
187
- if (last_idx <= 0) return rb_ary_pop(heap_a);
721
+ if (last_index < 0) return Qnil;
722
+ pop_value = DHEAP_VALUE(heap, 0);
188
723
 
189
- /*
190
- * swap with last, then sift down
191
- */
192
- VALUE pop_val = rb_ary_entry(heap_a, 0);
193
- VALUE sift_val = rb_ary_entry(heap_a, last_idx);
194
- rb_ary_store(heap_a, 0, sift_val);
195
- rb_ary_pop(heap_a);
196
- dheap_sift_down_s(Qnil, heap_a, heap_d, LONG2NUM(0));
724
+ do {
725
+ SCORE max = VAL2SCORE(max_score);
726
+ SCORE pop_score = DHEAP_SCORE(heap, 0);
727
+ if (max && !CMP_LT(pop_score, max)) return Qnil;
728
+ } while (0);
197
729
 
198
- return pop_val;
730
+ dheap_pop_swap_last_and_sift_down(heap, last_index);
731
+ return pop_value;
199
732
  }
200
733
 
201
734
  void
202
735
  Init_d_heap(void)
203
736
  {
204
737
  id_cmp = rb_intern_const("<=>");
205
- id_ivar_a = rb_intern_const("ary");
206
- id_ivar_d = rb_intern_const("d");
738
+ id_abs = rb_intern_const("abs");
207
739
 
208
740
  rb_cDHeap = rb_define_class("DHeap", rb_cObject);
741
+ rb_define_alloc_func(rb_cDHeap, dheap_s_alloc);
742
+
743
+ rb_define_const(rb_cDHeap, "MAX_D", INT2NUM(DHEAP_MAX_D));
744
+ rb_define_const(rb_cDHeap, "DEFAULT_D", INT2NUM(DHEAP_DEFAULT_D));
745
+
209
746
  rb_define_method(rb_cDHeap, "initialize", dheap_initialize, -1);
210
- rb_define_method(rb_cDHeap, "d", dheap_d, 0);
211
- rb_define_private_method(rb_cDHeap, "_ary_", dheap_a, 0);
747
+ rb_define_method(rb_cDHeap, "initialize_copy", dheap_initialize_copy, 1);
748
+ rb_define_private_method(rb_cDHeap, "__init_clone__", dheap_init_clone, 2);
749
+ rb_define_method(rb_cDHeap, "freeze", dheap_freeze, 0);
212
750
 
213
- rb_define_singleton_method(rb_cDHeap, "heap_sift_down", dheap_sift_down_s, 3);
214
- rb_define_singleton_method(rb_cDHeap, "heap_sift_up", dheap_sift_up_s, 3);
751
+ rb_define_method(rb_cDHeap, "d", dheap_attr_d, 0);
752
+ rb_define_method(rb_cDHeap, "size", dheap_size, 0);
753
+ rb_define_method(rb_cDHeap, "empty?", dheap_empty_p, 0);
754
+ rb_define_method(rb_cDHeap, "peek", dheap_peek, 0);
215
755
 
216
- rb_define_method(rb_cDHeap, "push", dheap_push, 1);
217
- rb_define_method(rb_cDHeap, "<<", dheap_left_shift, 1);
218
- rb_define_method(rb_cDHeap, "pop", dheap_pop, 0);
756
+ rb_define_method(rb_cDHeap, "clear", dheap_clear, 0);
757
+ rb_define_method(rb_cDHeap, "push", dheap_push, -1);
758
+ rb_define_method(rb_cDHeap, "<<", dheap_left_shift, 1);
759
+ rb_define_method(rb_cDHeap, "pop", dheap_pop, 0);
760
+ rb_define_method(rb_cDHeap, "pop_lt", dheap_pop_lt, 1);
761
+ rb_define_method(rb_cDHeap, "pop_lte", dheap_pop_lte, 1);
219
762
  }