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