d_heap 0.1.0 → 0.4.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,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
  }