d_heap 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +19 -2
- data/Gemfile +4 -0
- data/Gemfile.lock +7 -1
- data/README.md +131 -107
- data/benchmarks/perf.rb +29 -0
- data/benchmarks/push_n.yml +6 -3
- data/benchmarks/push_n_pop_n.yml +4 -0
- data/benchmarks/push_pop.yml +6 -3
- data/benchmarks/stackprof.rb +31 -0
- data/docs/benchmarks-2.txt +63 -40
- data/docs/benchmarks-mem.txt +39 -0
- data/docs/benchmarks.txt +337 -265
- data/ext/d_heap/d_heap.c +202 -304
- data/ext/d_heap/d_heap.h +10 -4
- data/ext/d_heap/extconf.rb +8 -1
- data/lib/d_heap.rb +24 -0
- data/lib/d_heap/benchmarks.rb +2 -1
- data/lib/d_heap/benchmarks/benchmarker.rb +3 -0
- data/lib/d_heap/benchmarks/implementations.rb +86 -32
- data/lib/d_heap/version.rb +1 -1
- metadata +5 -2
data/ext/d_heap/d_heap.c
CHANGED
@@ -1,54 +1,29 @@
|
|
1
1
|
#include <float.h>
|
2
2
|
#include "d_heap.h"
|
3
3
|
|
4
|
-
#define SCORE_AS_LONG_DOUBLE 1
|
5
|
-
|
6
4
|
ID id_cmp; // <=>
|
7
5
|
ID id_abs; // abs
|
8
6
|
|
9
|
-
#ifdef SCORE_AS_LONG_DOUBLE
|
10
|
-
#define SCORE long double
|
11
|
-
#else
|
12
|
-
#define SCORE VALUE
|
13
|
-
#endif
|
14
|
-
|
15
7
|
typedef struct dheap_struct {
|
16
8
|
int d;
|
17
|
-
VALUE values;
|
18
|
-
#ifdef SCORE_AS_LONG_DOUBLE
|
19
9
|
long size;
|
20
10
|
long capa;
|
21
|
-
|
22
|
-
#else
|
23
|
-
VALUE scores; // T_ARRAY of comparable objects
|
24
|
-
#endif
|
11
|
+
ENTRY *entries;
|
25
12
|
} dheap_t;
|
26
13
|
|
27
|
-
#define
|
14
|
+
#define DHEAP_SCORE(heap, idx) ((heap)->entries[idx].score)
|
15
|
+
#define DHEAP_VALUE(heap, idx) ((heap)->entries[idx].value)
|
28
16
|
#define DHEAP_IDX_LAST(heap) (DHEAP_SIZE((heap)) - 1)
|
29
17
|
#define DHEAP_IDX_PARENT(heap, idx) (((idx) - 1) / (heap)->d)
|
30
|
-
#define
|
18
|
+
#define DHEAP_IDX_CHILD_0(heap, idx) (((idx) * (heap)->d) + 1)
|
19
|
+
#define DHEAP_IDX_CHILD_D(heap, idx) (((idx) * (heap)->d) + (heap)->d)
|
31
20
|
|
32
|
-
#
|
33
|
-
#define DHEAP_SIZE(heap) ((heap)->size)
|
34
|
-
#else
|
35
|
-
#define DHEAP_SIZE(heap) (RARRAY_LEN((heap)->scores))
|
36
|
-
#endif
|
21
|
+
#define DHEAP_SIZE(heap) ((heap)->size)
|
37
22
|
|
38
|
-
#
|
39
|
-
|
40
|
-
|
41
|
-
|
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)
|
23
|
+
#define CMP_LT(a, b) ((a) < (b))
|
24
|
+
#define CMP_LTE(a, b) ((a) <= (b))
|
25
|
+
#define CMP_GT(a, b) ((a) > (b))
|
26
|
+
#define CMP_GTE(a, b) ((a) >= (b))
|
52
27
|
|
53
28
|
#if LDBL_MANT_DIG < SIZEOF_UNSIGNED_LONG_LONG * 8
|
54
29
|
#error 'unsigned long long' should fit into 'long double' mantissa
|
@@ -118,7 +93,7 @@ VAL2SCORE(VALUE score)
|
|
118
93
|
}
|
119
94
|
}
|
120
95
|
|
121
|
-
#
|
96
|
+
#ifdef ORIG_SCORE_CMP_CODE
|
122
97
|
|
123
98
|
#define VAL2SCORE(score) (score)
|
124
99
|
|
@@ -169,47 +144,49 @@ optimized_cmp(SCORE a, SCORE b) {
|
|
169
144
|
} \
|
170
145
|
} while (0)
|
171
146
|
|
172
|
-
#
|
147
|
+
#ifdef __D_HEAP_DEBUG
|
148
|
+
#define ASSERT_DHEAP_INDEX(heap, index) do { \
|
173
149
|
if (index < 0) { \
|
174
150
|
rb_raise(rb_eIndexError, "DHeap index %ld too small", index); \
|
175
151
|
} \
|
176
|
-
else if (
|
152
|
+
else if (DHEAP_IDX_LAST(heap) < index) { \
|
177
153
|
rb_raise(rb_eIndexError, "DHeap index %ld too large", index); \
|
178
154
|
} \
|
179
155
|
} while (0)
|
156
|
+
#else
|
157
|
+
#define ASSERT_DHEAP_INDEX(heap, index)
|
158
|
+
#endif
|
180
159
|
|
181
160
|
static void
|
182
161
|
dheap_compact(void *ptr)
|
183
162
|
{
|
184
163
|
dheap_t *heap = ptr;
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
164
|
+
for (long i = 0; i < heap->size; ++i) {
|
165
|
+
if (DHEAP_VALUE(heap, i))
|
166
|
+
dheap_gc_location(DHEAP_VALUE(heap, i));
|
167
|
+
}
|
189
168
|
}
|
190
169
|
|
191
170
|
static void
|
192
171
|
dheap_mark(void *ptr)
|
193
172
|
{
|
194
173
|
dheap_t *heap = ptr;
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
174
|
+
for (long i = 0; i < heap->size; ++i) {
|
175
|
+
if (DHEAP_VALUE(heap, i))
|
176
|
+
rb_gc_mark_movable(DHEAP_VALUE(heap,i));
|
177
|
+
}
|
199
178
|
}
|
200
179
|
|
201
180
|
static void
|
202
181
|
dheap_free(void *ptr)
|
203
182
|
{
|
204
|
-
#ifdef SCORE_AS_LONG_DOUBLE
|
205
183
|
dheap_t *heap = ptr;
|
206
184
|
heap->size = 0;
|
207
|
-
if (heap->
|
208
|
-
|
209
|
-
heap->
|
185
|
+
if (heap->entries) {
|
186
|
+
xfree(heap->entries);
|
187
|
+
heap->entries = NULL;
|
210
188
|
}
|
211
189
|
heap->capa = 0;
|
212
|
-
#endif
|
213
190
|
xfree(ptr);
|
214
191
|
}
|
215
192
|
|
@@ -219,9 +196,7 @@ dheap_memsize(const void *ptr)
|
|
219
196
|
const dheap_t *heap = ptr;
|
220
197
|
size_t size = 0;
|
221
198
|
size += sizeof(*heap);
|
222
|
-
|
223
|
-
size += sizeof(long double) * heap->capa;
|
224
|
-
#endif
|
199
|
+
size += sizeof(ENTRY) * heap->capa;
|
225
200
|
return size;
|
226
201
|
}
|
227
202
|
|
@@ -245,15 +220,10 @@ dheap_s_alloc(VALUE klass)
|
|
245
220
|
|
246
221
|
obj = TypedData_Make_Struct(klass, dheap_t, &dheap_data_type, heap);
|
247
222
|
heap->d = DHEAP_DEFAULT_D;
|
248
|
-
heap->values = Qnil;
|
249
223
|
|
250
|
-
#ifdef SCORE_AS_LONG_DOUBLE
|
251
224
|
heap->size = 0;
|
252
225
|
heap->capa = 0;
|
253
|
-
heap->
|
254
|
-
#else
|
255
|
-
heap->scores = Qnil;
|
256
|
-
#endif
|
226
|
+
heap->entries = NULL;
|
257
227
|
|
258
228
|
return obj;
|
259
229
|
}
|
@@ -263,32 +233,21 @@ get_dheap_struct(VALUE self)
|
|
263
233
|
{
|
264
234
|
dheap_t *heap;
|
265
235
|
TypedData_Get_Struct(self, dheap_t, &dheap_data_type, heap);
|
266
|
-
Check_Type(heap->values, T_ARRAY); // ensure it's been initialized
|
267
236
|
return heap;
|
268
237
|
}
|
269
238
|
|
270
|
-
|
271
|
-
|
272
|
-
static void
|
239
|
+
void
|
273
240
|
dheap_set_capa(dheap_t *heap, long new_capa)
|
274
241
|
{
|
275
|
-
long double *new, *old;
|
276
242
|
// 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;
|
243
|
+
if (new_capa <= heap->capa || new_capa <= heap->size) return;
|
279
244
|
|
280
245
|
// allocate
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
if (old) {
|
286
|
-
MEMCPY(new, old, long double, heap->size);
|
287
|
-
ruby_xfree(old);
|
246
|
+
if (heap->entries) {
|
247
|
+
RB_REALLOC_N(heap->entries, ENTRY, new_capa);
|
248
|
+
} else {
|
249
|
+
heap->entries = RB_ZALLOC_N(ENTRY, new_capa);
|
288
250
|
}
|
289
|
-
|
290
|
-
// set vars
|
291
|
-
heap->cscores = new;
|
292
251
|
heap->capa = new_capa;
|
293
252
|
}
|
294
253
|
|
@@ -317,8 +276,6 @@ dheap_ensure_room_for_push(dheap_t *heap, long incr_by)
|
|
317
276
|
}
|
318
277
|
}
|
319
278
|
|
320
|
-
#endif
|
321
|
-
|
322
279
|
/*
|
323
280
|
* @overload initialize(d = DHeap::DEFAULT_D)
|
324
281
|
* Initialize a _d_-ary min-heap.
|
@@ -337,18 +294,12 @@ dheap_initialize(int argc, VALUE *argv, VALUE self) {
|
|
337
294
|
DHEAP_Check_d_size(d);
|
338
295
|
heap->d = d;
|
339
296
|
|
340
|
-
heap->values = rb_ary_new_capa(DHEAP_DEFAULT_SIZE);
|
341
|
-
|
342
|
-
#ifdef SCORE_AS_LONG_DOUBLE
|
343
297
|
dheap_set_capa(heap, DHEAP_DEFAULT_SIZE);
|
344
|
-
#else
|
345
|
-
heap->scores = rb_ary_new_capa(DHEAP_DEFAULT_SIZE);
|
346
|
-
#endif
|
347
298
|
|
348
299
|
return self;
|
349
300
|
}
|
350
301
|
|
351
|
-
/*
|
302
|
+
/* @!visibility private */
|
352
303
|
static VALUE
|
353
304
|
dheap_initialize_copy(VALUE copy, VALUE orig)
|
354
305
|
{
|
@@ -360,114 +311,82 @@ dheap_initialize_copy(VALUE copy, VALUE orig)
|
|
360
311
|
|
361
312
|
heap_copy->d = heap_orig->d;
|
362
313
|
|
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
314
|
dheap_set_capa(heap_copy, heap_orig->capa);
|
368
315
|
heap_copy->size = heap_orig->size;
|
369
316
|
if (heap_copy->size)
|
370
|
-
MEMCPY(
|
371
|
-
#else
|
372
|
-
heap_copy->scores = rb_ary_new();
|
373
|
-
rb_ary_replace(heap_copy->scores, heap_orig->scores);
|
374
|
-
#endif
|
317
|
+
MEMCPY(heap_copy->entries, heap_orig->entries, ENTRY, heap_orig->size);
|
375
318
|
|
376
319
|
return copy;
|
377
320
|
}
|
378
321
|
|
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
|
-
}
|
390
|
-
|
391
322
|
VALUE
|
392
|
-
|
393
|
-
|
394
|
-
SCORE sift_score;
|
323
|
+
dheap_sift_up(dheap_t *heap, long index) {
|
324
|
+
ENTRY entry = heap->entries[index];
|
395
325
|
|
396
|
-
|
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);
|
326
|
+
ASSERT_DHEAP_INDEX(heap, index);
|
401
327
|
|
402
328
|
// sift it up to where it belongs
|
403
|
-
for (long parent_index; 0 <
|
404
|
-
|
405
|
-
|
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);
|
329
|
+
for (long parent_index; 0 < index; index = parent_index) {
|
330
|
+
/* debug(rb_sprintf("sift up(%"PRIsVALUE", %d, %ld)", heap->values, heap->d, index)); */
|
331
|
+
parent_index = DHEAP_IDX_PARENT(heap, index);
|
410
332
|
|
411
333
|
// parent is smaller: heap is restored
|
412
|
-
if (CMP_LTE(
|
334
|
+
if (CMP_LTE(DHEAP_SCORE(heap, parent_index), entry.score)) break;
|
413
335
|
|
414
336
|
// parent is larger: swap and continue sifting up
|
415
|
-
|
416
|
-
dheap_assign(heap, sift_index, parent_score, parent_value);
|
417
|
-
dheap_assign(heap, parent_index, sift_score, sift_value);
|
337
|
+
heap->entries[index] = heap->entries[parent_index];
|
418
338
|
}
|
419
|
-
|
420
|
-
|
339
|
+
heap->entries[index] = entry;
|
340
|
+
/* debug(rb_sprintf("sifted (%"PRIsVALUE", %d, %ld)", heap->values, heap->d, index)); */
|
341
|
+
return LONG2NUM(index);
|
421
342
|
}
|
422
343
|
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
SCORE child_score;
|
437
|
-
VALUE child_value;
|
438
|
-
|
439
|
-
// find first child index, and break if we've reached the last layer
|
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;
|
458
|
-
}
|
344
|
+
/*
|
345
|
+
* this is a tiny bit more complicated than the binary heap version
|
346
|
+
*/
|
347
|
+
static inline long
|
348
|
+
dheap_find_min_child(dheap_t *heap, long parent, long last_index) {
|
349
|
+
long min_child = DHEAP_IDX_CHILD_0(heap, parent);
|
350
|
+
long last_sib = DHEAP_IDX_CHILD_D(heap, parent);
|
351
|
+
if (last_index < last_sib) last_sib = last_index;
|
352
|
+
|
353
|
+
for (long sibidx = min_child + 1; sibidx <= last_sib; ++sibidx) {
|
354
|
+
if (CMP_LT(DHEAP_SCORE(heap, sibidx),
|
355
|
+
DHEAP_SCORE(heap, min_child))) {
|
356
|
+
min_child = sibidx;
|
459
357
|
}
|
358
|
+
}
|
359
|
+
return min_child;
|
360
|
+
}
|
361
|
+
|
362
|
+
void
|
363
|
+
dheap_sift_down(dheap_t *heap, long index, long last_index) {
|
364
|
+
if (last_index < 1 || DHEAP_IDX_PARENT(heap, last_index) < index) {
|
365
|
+
// short-circuit: no chance for a child
|
366
|
+
return;
|
367
|
+
|
368
|
+
} else {
|
369
|
+
ENTRY entry = heap->entries[index];
|
370
|
+
|
371
|
+
long last_parent = DHEAP_IDX_PARENT(heap, last_index);
|
372
|
+
|
373
|
+
ASSERT_DHEAP_INDEX(heap, index);
|
374
|
+
|
375
|
+
// iteratively sift it down to where it belongs
|
376
|
+
while (index <= last_parent) {
|
377
|
+
// find min child
|
378
|
+
long min_child = dheap_find_min_child(heap, index, last_index);
|
460
379
|
|
461
|
-
|
462
|
-
|
380
|
+
// child is larger: heap is restored
|
381
|
+
if (CMP_LTE(entry.score, DHEAP_SCORE(heap, min_child))) break;
|
463
382
|
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
383
|
+
// child is smaller: swap and continue sifting down
|
384
|
+
heap->entries[index] = heap->entries[min_child];
|
385
|
+
index = min_child;
|
386
|
+
}
|
387
|
+
heap->entries[index] = entry;
|
388
|
+
// debug(rb_sprintf("sifted (%"PRIsVALUE", %d, %ld)", heap->values, heap->d, index));
|
468
389
|
}
|
469
|
-
debug(rb_sprintf("sifted (%"PRIsVALUE", %d, %ld)", heap->values, heap->d, sift_index));
|
470
|
-
return LONG2NUM(sift_index);
|
471
390
|
}
|
472
391
|
|
473
392
|
/*
|
@@ -482,7 +401,7 @@ dheap_size(VALUE self)
|
|
482
401
|
}
|
483
402
|
|
484
403
|
/*
|
485
|
-
* @return [Boolean]
|
404
|
+
* @return [Boolean] if the heap is empty
|
486
405
|
*/
|
487
406
|
static VALUE
|
488
407
|
dheap_empty_p(VALUE self)
|
@@ -502,24 +421,7 @@ dheap_attr_d(VALUE self)
|
|
502
421
|
return INT2FIX(heap->d);
|
503
422
|
}
|
504
423
|
|
505
|
-
/*
|
506
|
-
* Freezes the heap as well as its underlying array, but does <i>not</i>
|
507
|
-
* deep-freeze the elements in the heap.
|
508
|
-
*
|
509
|
-
* @return [self]
|
510
|
-
*/
|
511
|
-
static VALUE
|
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: */
|
424
|
+
/* @!visibility private */
|
523
425
|
static VALUE
|
524
426
|
dheap_init_clone(VALUE clone, VALUE orig, VALUE kwfreeze)
|
525
427
|
{
|
@@ -530,117 +432,119 @@ dheap_init_clone(VALUE clone, VALUE orig, VALUE kwfreeze)
|
|
530
432
|
return clone;
|
531
433
|
}
|
532
434
|
|
435
|
+
static inline void
|
436
|
+
dheap_push_entry(dheap_t *heap, ENTRY *entry) {
|
437
|
+
dheap_ensure_room_for_push(heap, 1);
|
438
|
+
heap->entries[heap->size] = *entry;
|
439
|
+
++heap->size;
|
440
|
+
dheap_sift_up(heap, heap->size - 1);
|
441
|
+
}
|
442
|
+
|
533
443
|
/*
|
534
|
-
*
|
444
|
+
* Inserts a value into the heap, using a score to determine sort-order.
|
445
|
+
*
|
446
|
+
* Score comes first, as an analogy with the +Array#insert+ index.
|
447
|
+
*
|
448
|
+
* Time complexity: <b>O(log n / log d)</b> <i>(worst-case)</i>
|
449
|
+
*
|
450
|
+
* @param score [Integer,Float,#to_f] a score to compare against other scores.
|
451
|
+
* @param value [Object] an object that is associated with the score.
|
452
|
+
*
|
453
|
+
* @return [self]
|
454
|
+
*/
|
455
|
+
static VALUE
|
456
|
+
dheap_insert(VALUE self, VALUE score, VALUE value) {
|
457
|
+
ENTRY entry;
|
458
|
+
dheap_t *heap = get_dheap_struct(self);
|
459
|
+
rb_check_frozen(self);
|
460
|
+
|
461
|
+
entry.score = VAL2SCORE(score);
|
462
|
+
entry.value = value;
|
463
|
+
dheap_push_entry(heap, &entry);
|
464
|
+
|
465
|
+
return self;
|
466
|
+
}
|
467
|
+
|
468
|
+
/*
|
469
|
+
* @overload push(value, score = value)
|
535
470
|
*
|
536
471
|
* Push a value onto heap, using a score to determine sort-order.
|
537
472
|
*
|
538
|
-
*
|
539
|
-
*
|
473
|
+
* Value comes first because the separate score is optional, and because it feels
|
474
|
+
* like a more natural variation on +Array#push+ or +Queue#enq+. If a score
|
475
|
+
* isn't provided, the value must be an Integer or can be cast with
|
476
|
+
* +Float(value)+.
|
540
477
|
*
|
541
478
|
* Time complexity: <b>O(log n / log d)</b> <i>(worst-case)</i>
|
542
479
|
*
|
543
|
-
* @param score [#<=>] a value that can be compared to other scores.
|
544
480
|
* @param value [Object] an object that is associated with the score.
|
481
|
+
* @param score [Integer,Float,#to_f] a score to compare against other scores.
|
545
482
|
*
|
546
|
-
* @return [
|
483
|
+
* @return [self]
|
547
484
|
*/
|
548
485
|
static VALUE
|
549
486
|
dheap_push(int argc, VALUE *argv, VALUE self) {
|
550
|
-
|
551
|
-
|
552
|
-
long last_index;
|
487
|
+
dheap_t *heap = get_dheap_struct(self);
|
488
|
+
ENTRY entry;
|
553
489
|
rb_check_frozen(self);
|
554
490
|
|
555
491
|
rb_check_arity(argc, 1, 2);
|
556
|
-
|
557
|
-
|
558
|
-
|
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);
|
492
|
+
entry.value = argv[0];
|
493
|
+
entry.score = VAL2SCORE(argc < 2 ? entry.value : argv[1]);
|
494
|
+
dheap_push_entry(heap, &entry);
|
573
495
|
|
574
|
-
return
|
496
|
+
return self;
|
575
497
|
}
|
576
498
|
|
577
499
|
/*
|
578
|
-
* Pushes a
|
500
|
+
* Pushes a value onto the heap.
|
579
501
|
*
|
580
|
-
* The
|
502
|
+
* The score will be derived from the value, by using the value itself if it is
|
503
|
+
* an Integer, otherwise by casting it with +Float(value)+.
|
581
504
|
*
|
582
505
|
* Time complexity: <b>O(log n / log d)</b> <i>(worst-case)</i>
|
583
506
|
*
|
584
|
-
* @param value [
|
507
|
+
* @param value [Integer,#to_f] a value with an intrinsic numeric score
|
585
508
|
* @return [self]
|
586
509
|
*/
|
587
510
|
static VALUE
|
588
511
|
dheap_left_shift(VALUE self, VALUE value) {
|
589
|
-
|
512
|
+
dheap_t *heap = get_dheap_struct(self);
|
513
|
+
ENTRY entry;
|
514
|
+
rb_check_frozen(self);
|
515
|
+
|
516
|
+
entry.score = VAL2SCORE(value);
|
517
|
+
entry.value = value;
|
518
|
+
dheap_push_entry(heap, &entry);
|
519
|
+
|
590
520
|
return self;
|
591
521
|
}
|
592
522
|
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
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)
|
523
|
+
static const ENTRY EmptyDheapEntry;
|
524
|
+
|
525
|
+
static inline VALUE
|
526
|
+
dheap_pop0(dheap_t *heap)
|
606
527
|
{
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
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);
|
528
|
+
VALUE popped = DHEAP_VALUE(heap, 0);
|
529
|
+
if (0 < --heap->size) {
|
530
|
+
heap->entries[0] = heap->entries[heap->size];
|
531
|
+
heap->entries[heap->size] = EmptyDheapEntry; // unnecessary to zero?
|
532
|
+
dheap_sift_down(heap, 0, heap->size - 1);
|
617
533
|
}
|
534
|
+
return popped;
|
618
535
|
}
|
619
536
|
|
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
|
631
|
-
|
632
537
|
/*
|
633
|
-
*
|
538
|
+
* Clears all values from the heap, leaving it empty.
|
634
539
|
*
|
635
|
-
*
|
636
|
-
* @return [Object] the next value to be popped without popping it.
|
540
|
+
* @return [self]
|
637
541
|
*/
|
638
542
|
static VALUE
|
639
543
|
dheap_clear(VALUE self) {
|
640
544
|
dheap_t *heap = get_dheap_struct(self);
|
641
545
|
rb_check_frozen(self);
|
642
546
|
if (0 < DHEAP_SIZE(heap)) {
|
643
|
-
|
547
|
+
heap->size = 0;
|
644
548
|
}
|
645
549
|
return self;
|
646
550
|
}
|
@@ -652,7 +556,8 @@ dheap_clear(VALUE self) {
|
|
652
556
|
* @return [Object] the next value to be popped without popping it.
|
653
557
|
*/
|
654
558
|
static VALUE
|
655
|
-
dheap_peek(VALUE self)
|
559
|
+
dheap_peek(VALUE self)
|
560
|
+
{
|
656
561
|
dheap_t *heap = get_dheap_struct(self);
|
657
562
|
if (DHEAP_IDX_LAST(heap) < 0) return Qnil;
|
658
563
|
return DHEAP_VALUE(heap, 0);
|
@@ -662,73 +567,66 @@ dheap_peek(VALUE self) {
|
|
662
567
|
* Pops the minimum value from the top of the heap
|
663
568
|
*
|
664
569
|
* Time complexity: <b>O(d log n / log d)</b> <i>(worst-case)</i>
|
570
|
+
*
|
571
|
+
* @return [Object] the value with the minimum score
|
572
|
+
*
|
573
|
+
* @see #peek
|
574
|
+
* @see #pop_lt
|
575
|
+
* @see #pop_lte
|
665
576
|
*/
|
666
577
|
static VALUE
|
667
|
-
dheap_pop(VALUE self)
|
668
|
-
|
578
|
+
dheap_pop(VALUE self)
|
579
|
+
{
|
669
580
|
dheap_t *heap = get_dheap_struct(self);
|
670
|
-
long last_index = DHEAP_IDX_LAST(heap);
|
671
581
|
rb_check_frozen(self);
|
672
|
-
|
673
|
-
|
674
|
-
pop_value = DHEAP_VALUE(heap, 0);
|
675
|
-
|
676
|
-
dheap_pop_swap_last_and_sift_down(heap, last_index);
|
677
|
-
return pop_value;
|
582
|
+
if (DHEAP_SIZE(heap) <= 0) return Qnil;
|
583
|
+
return dheap_pop0(heap);
|
678
584
|
}
|
679
585
|
|
680
586
|
/*
|
681
587
|
* Pops the minimum value only if it is less than or equal to a max score.
|
682
588
|
*
|
683
|
-
* @param max_score [
|
589
|
+
* @param max_score [Integer,#to_f] the maximum score to be popped
|
590
|
+
*
|
591
|
+
* Time complexity: <b>O(d log n / log d)</b> <i>(worst-case)</i>
|
592
|
+
*
|
593
|
+
* @return [Object] the value with the minimum score
|
684
594
|
*
|
595
|
+
* @see #peek
|
685
596
|
* @see #pop
|
597
|
+
* @see #pop_lt
|
686
598
|
*/
|
687
599
|
static VALUE
|
688
|
-
dheap_pop_lte(VALUE self, VALUE max_score)
|
689
|
-
|
600
|
+
dheap_pop_lte(VALUE self, VALUE max_score)
|
601
|
+
{
|
690
602
|
dheap_t *heap = get_dheap_struct(self);
|
691
|
-
long last_index = DHEAP_IDX_LAST(heap);
|
692
603
|
rb_check_frozen(self);
|
693
|
-
|
694
|
-
if (
|
695
|
-
|
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;
|
604
|
+
if (DHEAP_SIZE(heap) <= 0) return Qnil;
|
605
|
+
if (!CMP_LTE(DHEAP_SCORE(heap, 0), VAL2SCORE(max_score))) return Qnil;
|
606
|
+
return dheap_pop0(heap);
|
705
607
|
}
|
706
608
|
|
707
609
|
/*
|
708
610
|
* Pops the minimum value only if it is less than a max score.
|
709
611
|
*
|
710
|
-
* @param max_score [
|
612
|
+
* @param max_score [Integer,#to_f] the maximum score to be popped
|
711
613
|
*
|
712
614
|
* Time complexity: <b>O(d log n / log d)</b> <i>(worst-case)</i>
|
615
|
+
*
|
616
|
+
* @return [Object] the value with the minimum score
|
617
|
+
*
|
618
|
+
* @see #peek
|
619
|
+
* @see #pop
|
620
|
+
* @see #pop_lte
|
713
621
|
*/
|
714
622
|
static VALUE
|
715
|
-
dheap_pop_lt(VALUE self, VALUE max_score)
|
716
|
-
|
623
|
+
dheap_pop_lt(VALUE self, VALUE max_score)
|
624
|
+
{
|
717
625
|
dheap_t *heap = get_dheap_struct(self);
|
718
|
-
long last_index = DHEAP_IDX_LAST(heap);
|
719
626
|
rb_check_frozen(self);
|
720
|
-
|
721
|
-
if (
|
722
|
-
|
723
|
-
|
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);
|
729
|
-
|
730
|
-
dheap_pop_swap_last_and_sift_down(heap, last_index);
|
731
|
-
return pop_value;
|
627
|
+
if (DHEAP_SIZE(heap) <= 0) return Qnil;
|
628
|
+
if (!CMP_LT(DHEAP_SCORE(heap, 0), VAL2SCORE(max_score))) return Qnil;
|
629
|
+
return dheap_pop0(heap);
|
732
630
|
}
|
733
631
|
|
734
632
|
void
|
@@ -740,13 +638,12 @@ Init_d_heap(void)
|
|
740
638
|
rb_cDHeap = rb_define_class("DHeap", rb_cObject);
|
741
639
|
rb_define_alloc_func(rb_cDHeap, dheap_s_alloc);
|
742
640
|
|
743
|
-
rb_define_const(rb_cDHeap, "MAX_D",
|
641
|
+
rb_define_const(rb_cDHeap, "MAX_D", INT2NUM(DHEAP_MAX_D));
|
744
642
|
rb_define_const(rb_cDHeap, "DEFAULT_D", INT2NUM(DHEAP_DEFAULT_D));
|
745
643
|
|
746
644
|
rb_define_method(rb_cDHeap, "initialize", dheap_initialize, -1);
|
747
645
|
rb_define_method(rb_cDHeap, "initialize_copy", dheap_initialize_copy, 1);
|
748
646
|
rb_define_private_method(rb_cDHeap, "__init_clone__", dheap_init_clone, 2);
|
749
|
-
rb_define_method(rb_cDHeap, "freeze", dheap_freeze, 0);
|
750
647
|
|
751
648
|
rb_define_method(rb_cDHeap, "d", dheap_attr_d, 0);
|
752
649
|
rb_define_method(rb_cDHeap, "size", dheap_size, 0);
|
@@ -754,6 +651,7 @@ Init_d_heap(void)
|
|
754
651
|
rb_define_method(rb_cDHeap, "peek", dheap_peek, 0);
|
755
652
|
|
756
653
|
rb_define_method(rb_cDHeap, "clear", dheap_clear, 0);
|
654
|
+
rb_define_method(rb_cDHeap, "insert", dheap_insert, 2);
|
757
655
|
rb_define_method(rb_cDHeap, "push", dheap_push, -1);
|
758
656
|
rb_define_method(rb_cDHeap, "<<", dheap_left_shift, 1);
|
759
657
|
rb_define_method(rb_cDHeap, "pop", dheap_pop, 0);
|