d_heap 0.2.2 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f549e01dd83eb6b48c1190443495a628ed1dd64ead7eb94851e661aef2607e14
4
- data.tar.gz: 492ce5c17ace9ecc9deaccf8fcd47883835da28d4e5f32328aef60989186b2ff
3
+ metadata.gz: 5a20fe814944fa8945bc2cc0ce87f810c8bf8d21102b8c454ae5d639f0548576
4
+ data.tar.gz: 65a1af345ae84c7f6e5da8af89a00730ffc4cbdb6bb2cac59461c3728eb0ad28
5
5
  SHA512:
6
- metadata.gz: 85521dee7f2a9992980935756571e87afbe8ae13347b5b3fbad17b501b5709111972b98ad0c9e1fca6d318c4be20ce2983086dfd84f7c0e73636ac9e4f11f253
7
- data.tar.gz: e1daac5b02fcc817b3c6c6a99395e3ca0b92f42bb14bd813fedbb3037eed698bda335c2898d5b0131b48fecd73e4ccf1615943a52287c36f73764b08bf8b1969
6
+ metadata.gz: 27c240634013397033925ee258de08135587c31c7a046a902c028842680dadec20e2ccc0f76570e6f7db34d2292e5dae2ae7d17449436f890498697de4352c91
7
+ data.tar.gz: 8eb2cba120747cc7788c42f264d2109ef1afee8a28caa7effb8bcb1ff36ed7c99a0556b48b7d9e9479f6e8be8945eea3bf1ddb7f608c13c63252684643154179
@@ -44,6 +44,7 @@ Layout/EmptyLineBetweenDefs:
44
44
  Layout/EmptyLinesAroundAttributeAccessor:
45
45
  inherit_mode:
46
46
  merge:
47
+ - Exclude
47
48
  - AllowedMethods
48
49
  Enabled: true
49
50
  AllowedMethods:
@@ -139,8 +140,17 @@ Style/TernaryParentheses:
139
140
  Enabled: false
140
141
 
141
142
  Style/BlockDelimiters:
143
+ inherit_mode:
144
+ merge:
145
+ - Exclude
146
+ - ProceduralMethods
147
+ - IgnoredMethods
148
+ - FunctionalMethods
142
149
  EnforcedStyle: semantic
143
150
  AllowBracesOnProceduralOneLiners: true
151
+ IgnoredMethods:
152
+ - expect
153
+
144
154
 
145
155
  Style/FormatString:
146
156
  EnforcedStyle: percent
@@ -1,12 +1,15 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- d_heap (0.2.2)
4
+ d_heap (0.3.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
9
  ast (2.4.1)
10
+ benchmark-malloc (0.2.0)
11
+ benchmark-perf (0.6.0)
12
+ benchmark-trend (0.4.0)
10
13
  diff-lcs (1.4.4)
11
14
  parallel (1.19.2)
12
15
  parser (2.7.2.0)
@@ -21,6 +24,11 @@ GEM
21
24
  rspec-core (~> 3.10.0)
22
25
  rspec-expectations (~> 3.10.0)
23
26
  rspec-mocks (~> 3.10.0)
27
+ rspec-benchmark (0.6.0)
28
+ benchmark-malloc (~> 0.2)
29
+ benchmark-perf (~> 0.6)
30
+ benchmark-trend (~> 0.4)
31
+ rspec (>= 3.0)
24
32
  rspec-core (3.10.0)
25
33
  rspec-support (~> 3.10.0)
26
34
  rspec-expectations (3.10.0)
@@ -52,6 +60,7 @@ DEPENDENCIES
52
60
  rake (~> 13.0)
53
61
  rake-compiler
54
62
  rspec (~> 3.10)
63
+ rspec-benchmark
55
64
  rubocop (~> 1.0)
56
65
 
57
66
  BUNDLED WITH
data/README.md CHANGED
@@ -14,6 +14,48 @@ worst-case time complexity. In the worst case, a _d_-ary heap requires only
14
14
  Although you should probably just stick with the default _d_ value of `4`, it
15
15
  may be worthwhile to benchmark your specific scenario.
16
16
 
17
+ ## Usage
18
+
19
+ The simplest way to use it is simply with `#push` and `#pop`. Push takes a
20
+ score and a value, and pop returns the value with the current minimum score.
21
+
22
+ ```ruby
23
+ require "d_heap"
24
+
25
+ heap = DHeap.new # defaults to a 4-ary heap
26
+
27
+ # storing [score, value] tuples
28
+ heap.push Time.now + 5*60, Task.new(1)
29
+ heap.push Time.now + 30, Task.new(2)
30
+ heap.push Time.now + 60, Task.new(3)
31
+ heap.push Time.now + 5, Task.new(4)
32
+
33
+ # peeking and popping (using last to get the task and ignore the time)
34
+ heap.pop.last # => Task[4]
35
+ heap.pop.last # => Task[2]
36
+ heap.peak.last # => Task[3], but don't pop it
37
+ heap.pop.last # => Task[3]
38
+ heap.pop.last # => Task[1]
39
+ ```
40
+
41
+ Read the `rdoc` for more detailed documentation and examples.
42
+
43
+ ## Installation
44
+
45
+ Add this line to your application's Gemfile:
46
+
47
+ ```ruby
48
+ gem 'd_heap'
49
+ ```
50
+
51
+ And then execute:
52
+
53
+ $ bundle install
54
+
55
+ Or install it yourself as:
56
+
57
+ $ gem install d_heap
58
+
17
59
  ## Motivation
18
60
 
19
61
  Sometimes you just need a priority queue, right? With a regular queue, you
@@ -49,47 +91,6 @@ unnecessary. This is definitely hotspot code, and the basic ruby implementation
49
91
  would work fine, if not for that `<=>` overhead. Until then... this gem gets
50
92
  the job done.
51
93
 
52
- ## Installation
53
-
54
- Add this line to your application's Gemfile:
55
-
56
- ```ruby
57
- gem 'd_heap'
58
- ```
59
-
60
- And then execute:
61
-
62
- $ bundle install
63
-
64
- Or install it yourself as:
65
-
66
- $ gem install d_heap
67
-
68
- ## Usage
69
-
70
- The simplest way to use it is simply with `#push` and `#pop`. Push will
71
-
72
- ```ruby
73
- require "d_heap"
74
-
75
- heap = DHeap.new # defaults to a 4-ary heap
76
-
77
- # storing [time, task] tuples
78
- heap << [Time.now + 5*60, Task.new(1)]
79
- heap << [Time.now + 30, Task.new(2)]
80
- heap << [Time.now + 60, Task.new(3)]
81
- heap << [Time.now + 5, Task.new(4)]
82
-
83
- # peeking and popping (using last to get the task and ignore the time)
84
- heap.pop.last # => Task[4]
85
- heap.pop.last # => Task[2]
86
- heap.peak.last # => Task[3]
87
- heap.pop.last # => Task[3]
88
- heap.pop.last # => Task[1]
89
- ```
90
-
91
- Read the `rdoc` for more detailed documentation and examples.
92
-
93
94
  ## TODOs...
94
95
 
95
96
  _TODO:_ In addition to a basic _d_-ary heap class (`DHeap`), this library
@@ -30,4 +30,6 @@ Gem::Specification.new do |spec|
30
30
  spec.executables = spec.files.grep(%r{^exe/}) {|f| File.basename(f) }
31
31
  spec.require_paths = ["lib"]
32
32
  spec.extensions = ["ext/d_heap/extconf.rb"]
33
+
34
+ spec.add_development_dependency "rspec-benchmark"
33
35
  end
@@ -1,27 +1,29 @@
1
1
  #include "d_heap.h"
2
2
 
3
- ID id_ivar_a;
3
+ ID id_cmp; // <=>
4
+ ID id_ivar_values;
5
+ ID id_ivar_scores;
4
6
  ID id_ivar_d;
5
7
 
6
- #define DHEAP_GET_A(self) rb_ivar_get(self, id_ivar_a)
7
- #define DHEAP_GET_D(self) rb_ivar_get(self, id_ivar_d)
8
-
9
- #define DHEAP_SIZE(ary) (RARRAY_LEN(ary) / 2)
10
- #define DHEAP_LAST_IDX(ary) (DHEAP_SIZE(ary) - 1)
11
- #define DHEAP_VALUE(ary, idx) rb_ary_entry(ary, idx * 2 + 1)
12
- #define DHEAP_SCORE(ary, idx) rb_ary_entry(ary, idx * 2)
13
- #define DHEAP_ASSIGN(ary, idx, scr, val) \
14
- rb_ary_store(ary, idx * 2, scr); \
15
- rb_ary_store(ary, idx * 2 + 1, val);
16
- #define DHEAP_APPEND(ary, scr, val) \
17
- rb_ary_push(ary, scr); \
18
- rb_ary_push(ary, val);
19
- #define DHEAP_DROP_LAST(ary) ( \
20
- rb_ary_pop(ary), \
21
- rb_ary_pop(ary) \
8
+ #define Get_DHeap(hobj, hstruct) ((hstruct) = get_dheap_struct(hobj))
9
+
10
+ #define DHEAP_IDX_LAST(heap) (DHEAP_SIZE(heap) - 1)
11
+ #define DHEAP_IDX_PARENT(heap, idx) ((idx - 1) / heap->d)
12
+ #define DHEAP_IDX_CHILD0(heap, idx) ((idx * heap->d) + 1)
13
+
14
+ #define DHEAP_SIZE(heap) (RARRAY_LEN((heap)->scores))
15
+ #define DHEAP_VALUE(heap, idx) RARRAY_AREF((heap)->values, idx)
16
+ #define DHEAP_SCORE(heap, idx) RARRAY_AREF((heap)->scores, idx)
17
+ #define DHEAP_ASSIGN(heap, idx, scr, val) \
18
+ rb_ary_store(heap->scores, idx, scr); \
19
+ rb_ary_store(heap->values, idx, val);
20
+ #define DHEAP_APPEND(heap, scr, val) \
21
+ rb_ary_push((heap)->scores, scr); \
22
+ rb_ary_push((heap)->values, val);
23
+ #define DHEAP_DROP_LAST(heap) ( \
24
+ rb_ary_pop(heap->scores), \
25
+ rb_ary_pop(heap->values) \
22
26
  ) // score, value
23
- #define IDX_PARENT(idx) ((idx - 1) / d)
24
- #define IDX_CHILD0(idx) ((idx * d) + 1)
25
27
 
26
28
  #define DHEAP_Check_d_size(d) \
27
29
  if (d < 2) { \
@@ -31,64 +33,219 @@ ID id_ivar_d;
31
33
  rb_raise(rb_eArgError, "DHeap d=%d is too large", d); \
32
34
  }
33
35
 
34
- #define DHEAP_Check_Sift_Idx(sift_index, last_index) \
35
- if (sift_index < 0) { \
36
- rb_raise(rb_eIndexError, "sift_index %ld too small", sift_index); \
36
+ #define DHEAP_Check_Index(index, last_index) \
37
+ if (index < 0) { \
38
+ rb_raise(rb_eIndexError, "DHeap index %ld too small", index); \
37
39
  } \
38
- else if (last_index < sift_index) { \
39
- rb_raise(rb_eIndexError, "sift_index %ld too large", sift_index); \
40
+ else if (last_index < index) { \
41
+ rb_raise(rb_eIndexError, "DHeap index %ld too large", index); \
40
42
  }
41
43
 
42
- #define DHEAP_Check_Sift_Args(heap_array, d, sift_index) \
43
- DHEAP_Check_d_size(d); \
44
- Check_Type(heap_array, T_ARRAY); \
45
- long last_index = DHEAP_LAST_IDX(heap_array); \
46
- DHEAP_Check_Sift_Idx(sift_index, last_index); \
47
- \
48
- VALUE sift_value = DHEAP_VALUE(heap_array, sift_index); \
49
- VALUE sift_score = DHEAP_SCORE(heap_array, sift_index);
44
+ struct dheap_struct {
45
+ int d;
46
+ VALUE scores;
47
+ VALUE values;
48
+ };
49
+ typedef struct dheap_struct dheap_t;
50
+
51
+ static void
52
+ dheap_compact(void *ptr)
53
+ {
54
+ dheap_t *heap = ptr;
55
+ if (heap->scores) dheap_gc_location( heap->scores );
56
+ if (heap->values) dheap_gc_location( heap->values );
57
+ }
58
+
59
+ static void
60
+ dheap_mark(void *ptr)
61
+ {
62
+ dheap_t *heap = ptr;
63
+ if (heap->scores) rb_gc_mark_movable(heap->scores);
64
+ if (heap->values) rb_gc_mark_movable(heap->values);
65
+ }
66
+
67
+ static size_t
68
+ dheap_memsize(const void *ptr)
69
+ {
70
+ const dheap_t *heap = ptr;
71
+ size_t size = sizeof(*heap);
72
+ return size;
73
+ }
74
+
75
+ static const rb_data_type_t dheap_data_type = {
76
+ "DHeap",
77
+ {
78
+ (void (*)(void*))dheap_mark,
79
+ (void (*)(void*))RUBY_DEFAULT_FREE,
80
+ (size_t (*)(const void *))dheap_memsize,
81
+ dheap_compact_callback(dheap_compact),
82
+ },
83
+ 0, 0,
84
+ RUBY_TYPED_FREE_IMMEDIATELY,
85
+ };
86
+
87
+ static VALUE
88
+ dheap_s_alloc(VALUE klass)
89
+ {
90
+ VALUE obj;
91
+ dheap_t *heap;
92
+
93
+ obj = TypedData_Make_Struct(klass, dheap_t, &dheap_data_type, heap);
94
+ heap->d = DHEAP_DEFAULT_D;
95
+ heap->scores = Qnil;
96
+ heap->values = Qnil;
97
+
98
+ return obj;
99
+ }
100
+
101
+ static inline dheap_t *
102
+ get_dheap_struct(VALUE self)
103
+ {
104
+ dheap_t *heap;
105
+ TypedData_Get_Struct(self, dheap_t, &dheap_data_type, heap);
106
+ Check_Type(heap->scores, T_ARRAY);
107
+ return heap;
108
+ }
109
+
110
+ /*
111
+ * @overload initialize(d = DHeap::DEFAULT_D)
112
+ * Initialize a _d_-ary min-heap.
113
+ *
114
+ * @param d [Integer] maximum number of children per parent
115
+ */
116
+ static VALUE
117
+ dheap_initialize(int argc, VALUE *argv, VALUE self) {
118
+ rb_check_arity(argc, 0, 1);
119
+ dheap_t *heap;
120
+ TypedData_Get_Struct(self, dheap_t, &dheap_data_type, heap);
121
+
122
+ int d = DHEAP_DEFAULT_D;
123
+ if (argc) {
124
+ d = NUM2INT(argv[0]);
125
+ }
126
+ DHEAP_Check_d_size(d);
127
+ heap->d = d;
128
+
129
+ heap->scores = rb_ary_new_capa(10000);
130
+ heap->values = rb_ary_new_capa(10000);
131
+
132
+ return self;
133
+ }
134
+
135
+ /*
136
+ static inline VALUE
137
+ make_dheap_element(VALUE score, VALUE value)
138
+ {
139
+ elem_t *elem;
140
+ VALUE obj = TypedData_Make_Struct(
141
+ rb_cObject,
142
+ elem_t,
143
+ &dheap_elem_type,
144
+ elem);
145
+ elem->score = score;
146
+ elem->value = value;
147
+ return obj;
148
+ }
149
+
150
+ #define IsDHeapElem(value) rb_typeddata_is_kind_of((value), &dheap_elem_type)
151
+ #define Get_DHeap_Elem(value) \
152
+ TypedData_Get_Struct((obj), elem_t, &dheap_elem_type, (elem))
153
+
154
+ static inline elem_t *
155
+ get_dheap_element(VALUE obj)
156
+ {
157
+ elem_t *elem;
158
+ TypedData_Get_Struct((obj), elem_t, &dheap_elem_type, (elem));
159
+ return elem;
160
+ }
161
+
162
+ */
163
+
164
+ #define CMP_LT(a, b) (optimized_cmp(a, b) < 0)
165
+ #define CMP_LTE(a, b) (optimized_cmp(a, b) <= 0)
166
+ #define CMP_GT(a, b) (optimized_cmp(a, b) > 0)
167
+ #define CMP_GTE(a, b) (optimized_cmp(a, b) >= 0)
168
+
169
+ /*
170
+ * short-circuit evaluation for a few basic types.
171
+ *
172
+ * Only Integer, Float, and String are optimized,
173
+ * and only when both arguments are the same type.
174
+ */
175
+ static inline int
176
+ optimized_cmp(VALUE a, VALUE b) {
177
+ if (a == b) // Fixnum equality and object equality
178
+ return 0;
179
+ if (FIXNUM_P(a) && FIXNUM_P(b))
180
+ return (FIX2LONG(a) < FIX2LONG(b)) ? -1 : 1;
181
+ if (RB_FLOAT_TYPE_P(a) && RB_FLOAT_TYPE_P(b))
182
+ {
183
+ double x, y;
184
+ x = RFLOAT_VALUE(a);
185
+ y = RFLOAT_VALUE(b);
186
+ if (isnan(x) || isnan(y)) rb_cmperr(a, b); // raise ArgumentError
187
+ return (x < y) ? -1 : ((x == y) ? 0 : 1);
188
+ }
189
+ if (RB_TYPE_P(a, T_BIGNUM) && RB_TYPE_P(b, T_BIGNUM))
190
+ return FIX2INT(rb_big_cmp(a, b));
191
+ if (STRING_P(a) && STRING_P(b))
192
+ return rb_str_cmp(a, b);
193
+
194
+ // give up on an optimized version and just call (a <=> b)
195
+ return rb_cmpint(rb_funcallv(a, id_cmp, 1, &b), a, b);
196
+ }
50
197
 
51
198
  VALUE
52
- dheap_ary_sift_up(VALUE heap_array, int d, long sift_index) {
53
- DHEAP_Check_Sift_Args(heap_array, d, sift_index);
199
+ dheap_ary_sift_up(dheap_t *heap, long sift_index) {
200
+ long last_index = DHEAP_IDX_LAST(heap);
201
+ DHEAP_Check_Index(sift_index, last_index);
202
+
203
+ VALUE sift_value = DHEAP_VALUE(heap, sift_index);
204
+ VALUE sift_score = DHEAP_SCORE(heap, sift_index);
205
+
54
206
  // sift it up to where it belongs
55
207
  for (long parent_index; 0 < sift_index; sift_index = parent_index) {
56
- debug(rb_sprintf("sift up(%"PRIsVALUE", %d, %ld)", heap_array, d, sift_index));
57
- parent_index = IDX_PARENT(sift_index);
58
- VALUE parent_score = DHEAP_SCORE(heap_array, parent_index);
208
+ debug(rb_sprintf("sift up(%"PRIsVALUE", %d, %ld)", heap->values, heap->d, sift_index));
209
+ parent_index = DHEAP_IDX_PARENT(heap, sift_index);
210
+ VALUE parent_score = DHEAP_SCORE(heap, parent_index);
59
211
 
60
212
  // parent is smaller: heap is restored
61
213
  if (CMP_LTE(parent_score, sift_score)) break;
62
214
 
63
215
  // parent is larger: swap and continue sifting up
64
- VALUE parent_value = DHEAP_VALUE(heap_array, parent_index);
65
- DHEAP_ASSIGN(heap_array, sift_index, parent_score, parent_value);
66
- DHEAP_ASSIGN(heap_array, parent_index, sift_score, sift_value);
216
+ VALUE parent_value = DHEAP_VALUE(heap, parent_index);
217
+ DHEAP_ASSIGN(heap, sift_index, parent_score, parent_value);
218
+ DHEAP_ASSIGN(heap, parent_index, sift_score, sift_value);
67
219
  }
68
- debug(rb_sprintf("sifted (%"PRIsVALUE", %d, %ld)", heap_array, d, sift_index));
220
+ debug(rb_sprintf("sifted (%"PRIsVALUE", %d, %ld)", heap->values, heap->d, sift_index));
69
221
  return LONG2NUM(sift_index);
70
222
  }
71
223
 
72
224
  VALUE
73
- dheap_ary_sift_down(VALUE heap_array, int d, long sift_index) {
74
- DHEAP_Check_Sift_Args(heap_array, d, sift_index);
225
+ dheap_ary_sift_down(dheap_t *heap, long sift_index) {
226
+ long last_index = DHEAP_IDX_LAST(heap);
227
+ DHEAP_Check_Index(sift_index, last_index);
228
+
229
+ VALUE sift_value = DHEAP_VALUE(heap, sift_index);
230
+ VALUE sift_score = DHEAP_SCORE(heap, sift_index);
75
231
 
76
232
  // iteratively sift it down to where it belongs
77
233
  for (long child_index; sift_index < last_index; sift_index = child_index) {
78
- debug(rb_sprintf("sift dn(%"PRIsVALUE", %d, %ld)", heap_array, d, sift_index));
234
+ debug(rb_sprintf("sift dn(%"PRIsVALUE", %d, %ld)", heap->values, heap->d, sift_index));
79
235
  // find first child index, and break if we've reached the last layer
80
- long child_idx0 = child_index = IDX_CHILD0(sift_index);
236
+ long child_idx0 = child_index = DHEAP_IDX_CHILD0(heap, sift_index);
81
237
  if (last_index < child_idx0) break;
82
238
 
83
239
  // find the min child (and its child_index)
84
240
  // requires "d" comparisons to find min child and compare to sift_score
85
- VALUE child_score = DHEAP_SCORE(heap_array, child_idx0);
241
+ long last_sibidx = child_idx0 + heap->d - 1;
242
+ if (last_index < last_sibidx) last_sibidx = last_index;
243
+ VALUE child_score = DHEAP_SCORE(heap, child_idx0);
86
244
  child_index = child_idx0;
87
- for (int i = 1; i < d; ++i) {
88
- long sibling_index = child_idx0 + i;
89
- if (last_index < sibling_index) break;
90
-
91
- VALUE sibling_score = DHEAP_SCORE(heap_array, sibling_index);
245
+ for (long sibling_index = child_idx0 + 1;
246
+ sibling_index <= last_sibidx;
247
+ ++sibling_index) {
248
+ VALUE sibling_score = DHEAP_SCORE(heap, sibling_index);
92
249
 
93
250
  if (CMP_LT(sibling_score, child_score)) {
94
251
  child_score = sibling_score;
@@ -100,106 +257,45 @@ dheap_ary_sift_down(VALUE heap_array, int d, long sift_index) {
100
257
  if (CMP_LTE(sift_score, child_score)) break;
101
258
 
102
259
  // child is smaller: swap and continue sifting down
103
- VALUE child_value = DHEAP_VALUE(heap_array, child_index);
104
- DHEAP_ASSIGN(heap_array, sift_index, child_score, child_value);
105
- DHEAP_ASSIGN(heap_array, child_index, sift_score, sift_value);
260
+ VALUE child_value = DHEAP_VALUE(heap, child_index);
261
+ DHEAP_ASSIGN(heap, sift_index, child_score, child_value);
262
+ DHEAP_ASSIGN(heap, child_index, sift_score, sift_value);
106
263
  }
107
- debug(rb_sprintf("sifted (%"PRIsVALUE", %d, %ld)", heap_array, d, sift_index));
264
+ debug(rb_sprintf("sifted (%"PRIsVALUE", %d, %ld)", heap->values, heap->d, sift_index));
108
265
  return LONG2NUM(sift_index);
109
266
  }
110
267
 
111
- #define DHEAP_Load_Sift_Vals(heap_array, dval, idxval) \
112
- Check_Type(dval, T_FIXNUM); \
113
- int dint = FIX2INT(dval); \
114
- long idx = NUM2LONG(idxval);
115
-
116
- /*
117
- * Treats a +heap_array+ as a +d+-ary heap and sifts up from +sift_index+ to
118
- * restore the heap property for all nodes between it and the root of the tree.
119
- *
120
- * The array is interpreted as holding two entries for each node, a score and a
121
- * value. The scores will held in every even-numbered array index and the
122
- * values in every odd numbered index. The array is flat, not an array of
123
- * length=2 arrays.
124
- *
125
- * Time complexity: <b>O(log n / log d)</b> <i>(worst-case)</i>
126
- *
127
- * @param heap_array [Array] the array which is treated a heap.
128
- * @param d [Integer] the maximum number of children per parent
129
- * @param sift_index [Integer] the index to start sifting from
130
- * @return [Integer] the new index for the object that starts at +sift_index+.
131
- */
132
- static VALUE
133
- dheap_sift_up_s(VALUE unused, VALUE heap_array, VALUE d, VALUE sift_index) {
134
- DHEAP_Load_Sift_Vals(heap_array, d, sift_index);
135
- return dheap_ary_sift_up(heap_array, dint, idx);
136
- }
137
-
138
- /*
139
- * Treats +heap_array+ as a +d+-ary heap and sifts down from +sift_index+ to
140
- * restore the heap property. If all _d_ subtrees below +sift_index+ are already
141
- * heaps, this method ensures the entire subtree rooted at +sift_index+ will be
142
- * a heap.
143
- *
144
- * The array is interpreted as holding two entries for each node, a score and a
145
- * value. The scores will held in every even-numbered array index and the
146
- * values in every odd numbered index. The array is flat, not an array of
147
- * length=2 arrays.
148
- *
149
- * Time complexity: <b>O(d log n / log d)</b> <i>(worst-case)</i>
150
- *
151
- * @param heap_array [Array] the array which is treated a heap.
152
- * @param d [Integer] the maximum number of children per parent
153
- * @param sift_index [Integer] the index to start sifting down from
154
- * @return [Integer] the new index for the object that starts at +sift_index+.
155
- */
156
- static VALUE
157
- dheap_sift_down_s(VALUE unused, VALUE heap_array, VALUE d, VALUE sift_index) {
158
- DHEAP_Load_Sift_Vals(heap_array, d, sift_index);
159
- return dheap_ary_sift_down(heap_array, dint, idx);
160
- }
161
-
162
- /*
163
- * @overload initialize(d = DHeap::DEFAULT_D)
164
- * Initialize a _d_-ary min-heap.
165
- *
166
- * @param d [Integer] maximum number of children per parent
167
- */
168
- static VALUE
169
- dheap_initialize(int argc, VALUE *argv, VALUE self) {
170
- rb_check_arity(argc, 0, 1);
171
- int d = DHEAP_DEFAULT_D;
172
- if (argc) {
173
- d = NUM2INT(argv[0]);
174
- }
175
- DHEAP_Check_d_size(d);
176
- rb_ivar_set(self, id_ivar_d, INT2FIX(d));
177
- rb_ivar_set(self, id_ivar_a, rb_ary_new());
178
- return self;
179
- }
180
-
181
268
  /*
182
269
  * @return [Integer] the number of elements in the heap
183
270
  */
184
- static VALUE dheap_size(VALUE self) {
185
- VALUE ary = DHEAP_GET_A(self);
186
- long size = DHEAP_SIZE(ary);
271
+ static VALUE
272
+ dheap_size(VALUE self)
273
+ {
274
+ dheap_t *heap = get_dheap_struct(self);
275
+ long size = DHEAP_SIZE(heap);
187
276
  return LONG2NUM(size);
188
277
  }
189
278
 
190
279
  /*
191
280
  * @return [Boolean] is the heap empty?
192
281
  */
193
- static VALUE dheap_empty_p(VALUE self) {
194
- VALUE ary = DHEAP_GET_A(self);
195
- long size = DHEAP_SIZE(ary);
282
+ static VALUE
283
+ dheap_empty_p(VALUE self)
284
+ {
285
+ dheap_t *heap = get_dheap_struct(self);
286
+ long size = DHEAP_SIZE(heap);
196
287
  return size == 0 ? Qtrue : Qfalse;
197
288
  }
198
289
 
199
290
  /*
200
291
  * @return [Integer] the maximum number of children per parent
201
292
  */
202
- static VALUE dheap_attr_d(VALUE self) { return DHEAP_GET_D(self); }
293
+ static VALUE
294
+ dheap_attr_d(VALUE self)
295
+ {
296
+ dheap_t *heap = get_dheap_struct(self);
297
+ return INT2FIX(heap->d);
298
+ }
203
299
 
204
300
  /*
205
301
  * Freezes the heap as well as its underlying array, but does <i>not</i>
@@ -209,20 +305,13 @@ static VALUE dheap_attr_d(VALUE self) { return DHEAP_GET_D(self); }
209
305
  */
210
306
  static VALUE
211
307
  dheap_freeze(VALUE self) {
212
- VALUE ary = DHEAP_GET_A(self);
308
+ dheap_t *heap = get_dheap_struct(self);
213
309
  ID id_freeze = rb_intern("freeze");
214
- rb_funcall(ary, id_freeze, 0);
310
+ rb_funcall(heap->scores, id_freeze, 0);
311
+ rb_funcall(heap->values, id_freeze, 0);
215
312
  return rb_call_super(0, NULL);
216
313
  }
217
314
 
218
- static VALUE
219
- dheap_ary_push(VALUE ary, int d, VALUE val, VALUE scr)
220
- {
221
- DHEAP_APPEND(ary, scr, val);
222
- long last_index = DHEAP_LAST_IDX(ary);
223
- return dheap_ary_sift_up(ary, d, last_index);
224
- }
225
-
226
315
  /*
227
316
  * @overload push(score, value = score)
228
317
  *
@@ -244,11 +333,11 @@ dheap_push(int argc, VALUE *argv, VALUE self) {
244
333
  VALUE scr = argv[0];
245
334
  VALUE val = argc < 2 ? scr : argv[1];
246
335
 
247
- VALUE ary = DHEAP_GET_A(self);
248
- VALUE dval = DHEAP_GET_D(self);
249
- int d = FIX2INT(dval);
336
+ dheap_t *heap = get_dheap_struct(self);
250
337
 
251
- return dheap_ary_push(ary, d, val, scr);
338
+ DHEAP_APPEND(heap, scr, val);
339
+ long last_index = DHEAP_IDX_LAST(heap);
340
+ return dheap_ary_sift_up(heap, last_index);
252
341
  }
253
342
 
254
343
  /*
@@ -267,18 +356,29 @@ dheap_left_shift(VALUE self, VALUE value) {
267
356
  return self;
268
357
  }
269
358
 
270
- #define DHEAP_Pop_Init(self) \
271
- VALUE ary = DHEAP_GET_A(self); \
272
- VALUE dval = DHEAP_GET_D(self); \
273
- long last_index = DHEAP_LAST_IDX(ary); \
274
-
275
- #define DHEAP_Pop_SwapLastAndSiftDown(ary, dval, last_index, sift_value) \
276
- if (last_index == 0) { DHEAP_DROP_LAST(ary); return pop_value; } \
277
- VALUE sift_value = DHEAP_VALUE(ary, last_index); \
278
- VALUE sift_score = DHEAP_SCORE(ary, last_index); \
279
- DHEAP_ASSIGN(ary, 0, sift_score, sift_value); \
280
- DHEAP_DROP_LAST(ary); \
281
- dheap_ary_sift_down(ary, FIX2INT(dval), 0);
359
+ /*
360
+ dheap_t hstruct; \
361
+ Get_DHeap(self, hstruct); \
362
+ VALUE values = hstruct.values; \
363
+ VALUE scores = hstruct.scores; \
364
+ VALUE dval = INT2FIX(hstruct.d); \
365
+ */
366
+
367
+ static inline void
368
+ dheap_pop_swap_last_and_sift_down(dheap_t *heap, long last_index)
369
+ {
370
+ if (last_index == 0) {
371
+ DHEAP_DROP_LAST(heap);
372
+ }
373
+ else
374
+ {
375
+ VALUE sift_value = DHEAP_VALUE(heap, last_index);
376
+ VALUE sift_score = DHEAP_SCORE(heap, last_index);
377
+ DHEAP_ASSIGN(heap, 0, sift_score, sift_value);
378
+ DHEAP_DROP_LAST(heap);
379
+ dheap_ary_sift_down(heap, 0);
380
+ }
381
+ }
282
382
 
283
383
  /*
284
384
  * Returns the next value on the heap to be popped without popping it.
@@ -288,8 +388,9 @@ dheap_left_shift(VALUE self, VALUE value) {
288
388
  */
289
389
  static VALUE
290
390
  dheap_peek(VALUE self) {
291
- VALUE ary = DHEAP_GET_A(self);
292
- return DHEAP_VALUE(ary, 0);
391
+ dheap_t *heap = get_dheap_struct(self);
392
+ if (DHEAP_IDX_LAST(heap) < 0) return Qnil;
393
+ return DHEAP_VALUE(heap, 0);
293
394
  }
294
395
 
295
396
  /*
@@ -299,11 +400,13 @@ dheap_peek(VALUE self) {
299
400
  */
300
401
  static VALUE
301
402
  dheap_pop(VALUE self) {
302
- DHEAP_Pop_Init(self);
303
- if (last_index < 0) return Qnil;
304
- VALUE pop_value = DHEAP_VALUE(ary, 0);
403
+ dheap_t *heap = get_dheap_struct(self);
404
+ long last_index = DHEAP_IDX_LAST(heap);
305
405
 
306
- DHEAP_Pop_SwapLastAndSiftDown(ary, dval, last_index, sift_value);
406
+ if (last_index < 0) return Qnil;
407
+ VALUE pop_value = DHEAP_VALUE(heap, 0);
408
+
409
+ dheap_pop_swap_last_and_sift_down(heap, last_index);
307
410
  return pop_value;
308
411
  }
309
412
 
@@ -316,14 +419,16 @@ dheap_pop(VALUE self) {
316
419
  */
317
420
  static VALUE
318
421
  dheap_pop_lte(VALUE self, VALUE max_score) {
319
- DHEAP_Pop_Init(self);
422
+ dheap_t *heap = get_dheap_struct(self);
423
+ long last_index = DHEAP_IDX_LAST(heap);
424
+
320
425
  if (last_index < 0) return Qnil;
321
- VALUE pop_value = DHEAP_VALUE(ary, 0);
426
+ VALUE pop_value = DHEAP_VALUE(heap, 0);
322
427
 
323
- VALUE pop_score = DHEAP_SCORE(ary, 0);
428
+ VALUE pop_score = DHEAP_SCORE(heap, 0);
324
429
  if (max_score && !CMP_LTE(pop_score, max_score)) return Qnil;
325
430
 
326
- DHEAP_Pop_SwapLastAndSiftDown(ary, dval, last_index, sift_value);
431
+ dheap_pop_swap_last_and_sift_down(heap, last_index);
327
432
  return pop_value;
328
433
  }
329
434
 
@@ -336,14 +441,16 @@ dheap_pop_lte(VALUE self, VALUE max_score) {
336
441
  */
337
442
  static VALUE
338
443
  dheap_pop_lt(VALUE self, VALUE max_score) {
339
- DHEAP_Pop_Init(self);
444
+ dheap_t *heap = get_dheap_struct(self);
445
+ long last_index = DHEAP_IDX_LAST(heap);
446
+
340
447
  if (last_index < 0) return Qnil;
341
- VALUE pop_value = DHEAP_VALUE(ary, 0);
448
+ VALUE pop_value = DHEAP_VALUE(heap, 0);
342
449
 
343
- VALUE pop_score = DHEAP_SCORE(ary, 0);
450
+ VALUE pop_score = DHEAP_SCORE(heap, 0);
344
451
  if (max_score && !CMP_LT(pop_score, max_score)) return Qnil;
345
452
 
346
- DHEAP_Pop_SwapLastAndSiftDown(ary, dval, last_index, sift_value);
453
+ dheap_pop_swap_last_and_sift_down(heap, last_index);
347
454
  return pop_value;
348
455
  }
349
456
 
@@ -351,16 +458,16 @@ void
351
458
  Init_d_heap(void)
352
459
  {
353
460
  id_cmp = rb_intern_const("<=>");
354
- id_ivar_a = rb_intern_const("ary");
461
+ id_ivar_values = rb_intern_const("values");
462
+ id_ivar_scores = rb_intern_const("scores");
355
463
  id_ivar_d = rb_intern_const("d");
356
464
 
357
465
  rb_cDHeap = rb_define_class("DHeap", rb_cObject);
466
+ rb_define_alloc_func(rb_cDHeap, dheap_s_alloc);
467
+
358
468
  rb_define_const(rb_cDHeap, "MAX_D", INT2NUM(DHEAP_MAX_D));
359
469
  rb_define_const(rb_cDHeap, "DEFAULT_D", INT2NUM(DHEAP_DEFAULT_D));
360
470
 
361
- rb_define_singleton_method(rb_cDHeap, "heap_sift_down", dheap_sift_down_s, 3);
362
- rb_define_singleton_method(rb_cDHeap, "heap_sift_up", dheap_sift_up_s, 3);
363
-
364
471
  rb_define_method(rb_cDHeap, "initialize", dheap_initialize, -1);
365
472
  rb_define_method(rb_cDHeap, "d", dheap_attr_d, 0);
366
473
  rb_define_method(rb_cDHeap, "freeze", dheap_freeze, 0);
@@ -13,46 +13,22 @@
13
13
 
14
14
  VALUE rb_cDHeap;
15
15
 
16
- #define CMP_LT(a, b) (optimized_cmp(a, b) < 0)
17
- #define CMP_LTE(a, b) (optimized_cmp(a, b) <= 0)
18
- #define CMP_GT(a, b) (optimized_cmp(a, b) > 0)
19
- #define CMP_GTE(a, b) (optimized_cmp(a, b) >= 0)
16
+ // copied from pg gem
20
17
 
21
- // <=>
22
- ID id_cmp;
18
+ #define UNUSED(x) ((void)(x))
19
+
20
+ #ifdef HAVE_RB_GC_MARK_MOVABLE
21
+ #define dheap_compact_callback(x) ((void (*)(void*))(x))
22
+ #define dheap_gc_location(x) x = rb_gc_location(x)
23
+ #else
24
+ #define rb_gc_mark_movable(x) rb_gc_mark(x)
25
+ #define dheap_compact_callback(x) {(x)}
26
+ #define dheap_gc_location(x) UNUSED(x)
27
+ #endif
23
28
 
24
29
  // from internal/compar.h
25
30
  #define STRING_P(s) (RB_TYPE_P((s), T_STRING) && CLASS_OF(s) == rb_cString)
26
31
 
27
- /*
28
- * short-circuit evaluation for a few basic types.
29
- *
30
- * Only Integer, Float, and String are optimized,
31
- * and only when both arguments are the same type.
32
- */
33
- static inline int
34
- optimized_cmp(VALUE a, VALUE b) {
35
- if (a == b) // Fixnum equality and object equality
36
- return 0;
37
- if (FIXNUM_P(a) && FIXNUM_P(b))
38
- return (FIX2LONG(a) < FIX2LONG(b)) ? -1 : 1;
39
- if (RB_FLOAT_TYPE_P(a) && RB_FLOAT_TYPE_P(b))
40
- {
41
- double x, y;
42
- x = RFLOAT_VALUE(a);
43
- y = RFLOAT_VALUE(b);
44
- if (isnan(x) || isnan(y)) rb_cmperr(a, b); // raise ArgumentError
45
- return (x < y) ? -1 : ((x == y) ? 0 : 1);
46
- }
47
- if (RB_TYPE_P(a, T_BIGNUM) && RB_TYPE_P(b, T_BIGNUM))
48
- return FIX2INT(rb_big_cmp(a, b));
49
- if (STRING_P(a) && STRING_P(b))
50
- return rb_str_cmp(a, b);
51
-
52
- // give up on an optimized version and just call (a <=> b)
53
- return rb_cmpint(rb_funcallv(a, id_cmp, 1, &b), a, b);
54
- }
55
-
56
32
  #ifdef __D_HEAP_DEBUG
57
33
  #define debug(v) { \
58
34
  ID sym_puts = rb_intern("puts"); \
@@ -6,4 +6,6 @@ require "mkmf"
6
6
  # $CFLAGS << " -D__D_HEAP_DEBUG"
7
7
  # end
8
8
 
9
+ have_func "rb_gc_mark_movable" # since ruby-2.7
10
+
9
11
  create_makefile("d_heap/d_heap")
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class DHeap
4
- VERSION = "0.2.2"
4
+ VERSION = "0.3.0"
5
5
 
6
6
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: d_heap
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - nicholas a. evans
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-12-27 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2020-12-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec-benchmark
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  description: |
14
28
  A C extension implementation of a d-ary heap data structure, suitable for
15
29
  use in e.g. priority queues or Djikstra's algorithm.