data_structures_rmolinari 0.5.1 → 0.5.2

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: a41b03a0d46982d64fc1ec5648174df24c6c9b0593ff93354ad90fc65ff84e8e
4
- data.tar.gz: 544aa69540c9cef54c4eb6402b260bafbbeddf7dc5e770bf428976c11161c58e
3
+ metadata.gz: 5d10fb46bf10f119b95239cf8e3ed06585804d37ccb9b7b1da0c7139dfcb31b9
4
+ data.tar.gz: dc393fb3e3f597df278832b3c4faae08866ef93ad747ee0a30fca19345fa6c8a
5
5
  SHA512:
6
- metadata.gz: 19efca577bb0c3c9524cf09b6f63c0e06beab70bfd20d1dbafa5034009be9bcb965ac5b8cee0ba0a39ecdcb646c401a54b363852a4572613941f7a34da174593
7
- data.tar.gz: 05606e9c142a9bcb69a9fd555586b0ad8fe76bf7572af3dd82003de673d78b671b9a5edf1e5d4a2e4c7d5c6d5e41bef2ed1679573600725b74a25d4f3665c6ee
6
+ metadata.gz: 6ec16b3eb2a1f4deccf8a8c45cb20c9558a0267049236389de53444124feeefe282751f964f2cf1875e1fccd5fccc6ad5fea4edd0098fed7b358bc6051666b4e
7
+ data.tar.gz: 0435cf6031c7e40bf9706a7c8d7b493e7e31f7667131be682de59ad5dcd26a5151add0d03cee83b23da6fa6f0c132c964ef551341dc94ff5d17cffddc2b48f2a
data/README.md CHANGED
@@ -7,14 +7,11 @@ participating in the Advent of Code (https://adventofcode.com/).
7
7
  The implementations are based on the expository descriptions and pseudo-code I found as I read about each structure and so are not
8
8
  as fast as possible.
9
9
 
10
- The code is available as a gem: https://rubygems.org/gems/data_structures_rmolinari.
10
+ The code lives in the `DataStructuresRMolinari` module to avoid polluting the global namespace.
11
11
 
12
12
  It is distributed under the MIT license.
13
13
 
14
- ## Usage
15
-
16
- The right way to organize the code is not obvious to me. For now the data structures are all defined in the module
17
- `DataStructuresRMolinari` to avoid polluting the global namespace.
14
+ It is available as a gem: https://rubygems.org/gems/data_structures_rmolinari.
18
15
 
19
16
  # Implementations
20
17
 
@@ -210,8 +207,10 @@ The implementation uses the remarkable Convenient Containers library from Jackso
210
207
  the pure Ruby `SegmentTreeTemplate` class.
211
208
 
212
209
  A benchmark suggests that a long sequence of `max_on` operations against a max-val Segment Tree is about 4 times as fast with C as
213
- with Ruby. I'm a bit suprised the improvment isn't larger, but remember that the C code must still interact with the Ruby objects in
214
- the underlying data array, and must combine them, etc., via Ruby lambdas.
210
+ with Ruby. The speedup factor is lowered to a little over 2 when the Ruby code is run with YJIT (under Ruby 3.1.3).
211
+
212
+ I'm a bit suprised the improvement isn't larger, but remember that the C code must still interact with the Ruby objects
213
+ in the underlying data array, and must access and combine them via Ruby lambdas.
215
214
 
216
215
  # References
217
216
  - [Allan] Allan, J., _CC: Convenient Containers_, https://github.com/JacksonAllan/CC, (retrieved 2023-02-01).
@@ -69,6 +69,11 @@ typedef struct du_data {
69
69
  size_t subset_count;
70
70
  } disjoint_union_data;
71
71
 
72
+ /************************************************************
73
+ * Memory Management
74
+ *
75
+ */
76
+
72
77
  /*
73
78
  * Create one (on the heap).
74
79
  */
@@ -98,9 +103,76 @@ static void disjoint_union_free(void *ptr) {
98
103
  }
99
104
 
100
105
  /************************************************************
101
- * The disjoint union operations
106
+ * How much memory (roughly) does a disjoint_union_data instance consume?
107
+ *
108
+ * I guess the Ruby runtime can use this information when deciding how agressive to be during garbage collection and such.
109
+ */
110
+ static size_t disjoint_union_memsize(const void *ptr) {
111
+ if (ptr) {
112
+ const disjoint_union_data *du = ptr;
113
+
114
+ // See https://github.com/JacksonAllan/CC/issues/3
115
+ return sizeof( cc_vec_hdr_ty ) + cap( du->pairs ) * CC_EL_SIZE( *(du->pairs) );
116
+ } else {
117
+ return 0;
118
+ }
119
+ }
120
+
121
+ // There is no need for a mark() function as we don't hold any Ruby objects ourselves.
122
+
123
+ /*
124
+ * A configuration struct that tells the Ruby runtime how to deal with a disjoint_union_data object.
125
+ *
126
+ * https://docs.ruby-lang.org/en/master/extension_rdoc.html#label-Encapsulate+C+data+into+a+Ruby+object
127
+ */
128
+ static const rb_data_type_t disjoint_union_type = {
129
+ .wrap_struct_name = "disjoint_union",
130
+ { // help for the Ruby garbage collector
131
+ .dmark = NULL, // dmark, for marking other Ruby objects. We don't hold any other objects so this can be NULL
132
+ .dfree = disjoint_union_free, // how to free the memory associated with an object
133
+ .dsize = disjoint_union_memsize, // roughly how much space does the object consume?
134
+ },
135
+ .data = NULL, // a data field we could use for something here if we wanted. Ruby ignores it
136
+ .flags = 0 // GC-related flag values.
137
+ };
138
+
139
+ /*
140
+ * End memory management functions
102
141
  ************************************************************/
103
142
 
143
+ /************************************************************
144
+ * Wrapping and unwrapping things for the Ruby runtime
145
+ *
146
+ */
147
+
148
+ /*
149
+ * Unwrap a Ruby-side disjoint union object to get the C struct inside.
150
+ */
151
+ static disjoint_union_data *unwrapped(VALUE self) {
152
+ disjoint_union_data *disjoint_union;
153
+ TypedData_Get_Struct((self), disjoint_union_data, &disjoint_union_type, disjoint_union);
154
+ return disjoint_union;
155
+ }
156
+
157
+ /*
158
+ * This is for CDisjointUnion.allocate on the Ruby side
159
+ */
160
+ static VALUE disjoint_union_alloc(VALUE klass) {
161
+ // Get one on the heap
162
+ disjoint_union_data *disjoint_union = create_disjoint_union();
163
+ // Wrap it up into a Ruby object
164
+ return TypedData_Wrap_Struct(klass, &disjoint_union_type, disjoint_union);
165
+ }
166
+
167
+ /*
168
+ * End wrapping and unwrapping functions.
169
+ ************************************************************/
170
+
171
+ /************************************************************
172
+ * The Disjoint Union API here on the C side
173
+ *
174
+ */
175
+
104
176
  /*
105
177
  * Is the given element already a member of the universe?
106
178
  */
@@ -114,13 +186,6 @@ static int present_p(disjoint_union_data *disjoint_union, size_t element) {
114
186
  static void assert_membership(disjoint_union_data *disjoint_union, size_t element) {
115
187
  if (!present_p(disjoint_union, element)) {
116
188
  rb_raise(eSharedDataError, "Value %zu is not part of the universe", element);
117
- /* rb_raise( */
118
- /* eSharedDataError, */
119
- /* "Value %zu is not part of the universe, size = %zu, forest_val = %lu", */
120
- /* element, */
121
- /* size(disjoint_union->pairs), */
122
- /* get(disjoint_union->pairs, element)->parent */
123
- /* ); */
124
189
  }
125
190
  }
126
191
 
@@ -206,59 +271,15 @@ static void unite(disjoint_union_data *disjoint_union, size_t elt1, size_t elt2)
206
271
  link_roots(disjoint_union, root1, root2);
207
272
  }
208
273
 
209
-
210
- /**
211
- * Wrapping and unwrapping things for the Ruby runtime
212
- *
213
- */
214
-
215
- // How much memory (roughly) does a disjoint_union_data instance consume? I guess the Ruby runtime can use this information when
216
- // deciding how agressive to be during garbage collection and such.
217
- static size_t disjoint_union_memsize(const void *ptr) {
218
- if (ptr) {
219
- const disjoint_union_data *du = ptr;
220
-
221
- // See https://github.com/JacksonAllan/CC/issues/3
222
- return sizeof( cc_vec_hdr_ty ) + cap( du->pairs ) * CC_EL_SIZE( *(du->pairs) );
223
- } else {
224
- return 0;
225
- }
226
- }
227
-
228
- /*
229
- * A configuration struct that tells the Ruby runtime how to deal with a disjoint_union_data object.
230
- *
231
- * https://docs.ruby-lang.org/en/master/extension_rdoc.html#label-Encapsulate+C+data+into+a+Ruby+object
232
- */
233
- static const rb_data_type_t disjoint_union_type = {
234
- .wrap_struct_name = "disjoint_union",
235
- { // help for the Ruby garbage collector
236
- .dmark = NULL, // dmark, for marking other Ruby objects. We don't hold any other objects so this can be NULL
237
- .dfree = disjoint_union_free, // how to free the memory associated with an object
238
- .dsize = disjoint_union_memsize, // roughly how much space does the object consume?
239
- },
240
- .data = NULL, // a data field we could use for something here if we wanted. Ruby ignores it
241
- .flags = 0 // GC-related flag values.
242
- };
243
-
244
274
  /*
245
- * Unwrap a Ruby-side disjoint union object to get the C struct inside.
246
- */
247
- static disjoint_union_data *unwrapped(VALUE self) {
248
- disjoint_union_data *disjoint_union;
249
- TypedData_Get_Struct((self), disjoint_union_data, &disjoint_union_type, disjoint_union);
250
- return disjoint_union;
251
- }
275
+ * End C impelementaion of the Segment Tree API
276
+ ************************************************************/
252
277
 
253
- /*
254
- * This is for CDisjointUnion.allocate on the Ruby side
278
+ /************************************************************
279
+ * The wrappers around the C functionality.
280
+ *
281
+ * These become Ruby methods via rb_define_method() below.
255
282
  */
256
- static VALUE disjoint_union_alloc(VALUE klass) {
257
- // Get one on the heap
258
- disjoint_union_data *disjoint_union = create_disjoint_union();
259
- // Wrap it up into a Ruby object
260
- return TypedData_Wrap_Struct(klass, &disjoint_union_type, disjoint_union);
261
- }
262
283
 
263
284
  /*
264
285
  * A single parameter is optional. If given it should be a non-negative integer and specifies the initial size, s, of the universe
@@ -286,17 +307,6 @@ static VALUE disjoint_union_init(int argc, VALUE *argv, VALUE self) {
286
307
  return self;
287
308
  }
288
309
 
289
- /**
290
- * And now the simple wrappers around the Disjoint Union C functionality. In each case we
291
- * - unwrap a 'VALUE self',
292
- * - i.e., the CDisjointUnion instance on the Ruby side;
293
- * - munge any other arguments into longs;
294
- * - call the appropriate C function to act on the struct; and
295
- * - return an appropriate VALUE for the Ruby runtime can use.
296
- *
297
- * We make them into methods on CDisjointUnion in the Init_CDisjointUnion function, below.
298
- */
299
-
300
310
  /*
301
311
  * Add a new subset to the universe containing the element +new_v+.
302
312
  *
@@ -1,17 +1,5 @@
1
1
  require 'mkmf'
2
2
 
3
- abort 'missing malloc()' unless have_func "malloc"
4
- abort 'missing realloc()' unless have_func "realloc"
3
+ require_relative '../extconf_shared.rb'
5
4
 
6
- if try_cflags('-O3')
7
- append_cflags('-O3')
8
- end
9
-
10
- extension_name = "c_disjoint_union"
11
- dir_config(extension_name)
12
-
13
- $srcs = ["disjoint_union.c", "../shared.c"]
14
- $INCFLAGS << " -I$(srcdir)/.."
15
- $VPATH << "$(srcdir)/.."
16
-
17
- create_makefile("data_structures_rmolinari/c_disjoint_union")
5
+ generate_makefile('disjoint_union')
@@ -1,17 +1,4 @@
1
1
  require 'mkmf'
2
+ require_relative '../extconf_shared.rb'
2
3
 
3
- abort 'missing malloc()' unless have_func "malloc"
4
- abort 'missing realloc()' unless have_func "realloc"
5
-
6
- if try_cflags('-O3')
7
- append_cflags('-O3')
8
- end
9
-
10
- extension_name = "c_segment_tree_template"
11
- dir_config(extension_name)
12
-
13
- $srcs = ["segment_tree_template.c", "../shared.c"]
14
- $INCFLAGS << " -I$(srcdir)/.."
15
- $VPATH << "$(srcdir)/.."
16
-
17
- create_makefile("data_structures_rmolinari/c_segment_tree_template")
4
+ generate_makefile('segment_tree_template')
@@ -190,11 +190,11 @@ static void setup(segment_tree_data* seg_tree, VALUE combine, VALUE single_cell_
190
190
  VALUE idCall = rb_intern("call");
191
191
 
192
192
  if (!rb_obj_respond_to(combine, idCall, TRUE)) {
193
- rb_raise(rb_eArgError, "wrong type argument %"PRIsVALUE" (should be callable)", rb_obj_class(combine));
193
+ rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be callable)", rb_obj_class(combine));
194
194
  }
195
195
 
196
196
  if (!rb_obj_respond_to(single_cell_array_val, idCall, TRUE)) {
197
- rb_raise(rb_eArgError, "wrong type argument %"PRIsVALUE" (should be callable)", rb_obj_class(single_cell_array_val));
197
+ rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be callable)", rb_obj_class(single_cell_array_val));
198
198
  }
199
199
 
200
200
  seg_tree->combine_lambda = combine;
@@ -215,7 +215,6 @@ static void setup(segment_tree_data* seg_tree, VALUE combine, VALUE single_cell_
215
215
  build(seg_tree, TREE_ROOT, 0, seg_tree->size - 1);
216
216
  }
217
217
 
218
-
219
218
  /*
220
219
  * Determine the value for the subarray A(left, right).
221
220
  *
@@ -297,8 +296,10 @@ static void update_val_at(segment_tree_data *seg_tree, size_t idx, size_t tree_i
297
296
  * End C implementation of the Segment Tree API
298
297
  ************************************************************/
299
298
 
300
- /**
301
- * And now the wrappers around the C functionality.
299
+ /************************************************************
300
+ * The wrappers around the C functionality.
301
+ *
302
+ * These become Ruby methods via rb_define_method() below.
302
303
  */
303
304
 
304
305
  /*
data/ext/shared.c CHANGED
@@ -1,3 +1,4 @@
1
+ #include "ruby.h"
1
2
  #include "shared.h"
2
3
 
3
4
  /*
data/ext/shared.h CHANGED
@@ -2,7 +2,6 @@
2
2
  #define SHARED_H
3
3
 
4
4
  #include <stddef.h>
5
- #include "ruby.h"
6
5
 
7
6
  #define mShared rb_define_module("Shared")
8
7
  #define eSharedDataError rb_const_get(mShared, rb_intern_const("DataError"))
@@ -12,13 +11,11 @@
12
11
  //#define debug(...) printf(__VA_ARGS__)
13
12
  #define debug(...)
14
13
 
15
- /* What we might think of as vector[index]. It is assignable */
14
+ /* What we might think of as vector[index] for a CC vec(foo). It is assignable */
16
15
  #define lval(vector, index) (*get(vector, index))
17
16
 
18
17
  /*
19
18
  * Binary tree arithmetic for an implicit tree in an array, 1-based.
20
- *
21
- * TODO: into shared header
22
19
  */
23
20
  #define TREE_ROOT 1
24
21
  size_t midpoint(size_t left, size_t right);
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: data_structures_rmolinari
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rory Molinari
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-02-03 00:00:00.000000000 Z
11
+ date: 2023-02-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: must_be