data_structures_rmolinari 0.5.1 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
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