nmatrix 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. data/.gitignore +27 -0
  2. data/.rspec +2 -0
  3. data/Gemfile +3 -5
  4. data/Guardfile +6 -0
  5. data/History.txt +33 -0
  6. data/Manifest.txt +41 -38
  7. data/README.rdoc +88 -11
  8. data/Rakefile +35 -53
  9. data/ext/nmatrix/data/complex.h +372 -0
  10. data/ext/nmatrix/data/data.cpp +275 -0
  11. data/ext/nmatrix/data/data.h +707 -0
  12. data/ext/nmatrix/data/rational.h +421 -0
  13. data/ext/nmatrix/data/ruby_object.h +446 -0
  14. data/ext/nmatrix/extconf.rb +101 -51
  15. data/ext/nmatrix/new_extconf.rb +56 -0
  16. data/ext/nmatrix/nmatrix.cpp +1609 -0
  17. data/ext/nmatrix/nmatrix.h +265 -849
  18. data/ext/nmatrix/ruby_constants.cpp +134 -0
  19. data/ext/nmatrix/ruby_constants.h +103 -0
  20. data/ext/nmatrix/storage/common.cpp +70 -0
  21. data/ext/nmatrix/storage/common.h +170 -0
  22. data/ext/nmatrix/storage/dense.cpp +665 -0
  23. data/ext/nmatrix/storage/dense.h +116 -0
  24. data/ext/nmatrix/storage/list.cpp +1088 -0
  25. data/ext/nmatrix/storage/list.h +129 -0
  26. data/ext/nmatrix/storage/storage.cpp +658 -0
  27. data/ext/nmatrix/storage/storage.h +99 -0
  28. data/ext/nmatrix/storage/yale.cpp +1601 -0
  29. data/ext/nmatrix/storage/yale.h +208 -0
  30. data/ext/nmatrix/ttable_helper.rb +126 -0
  31. data/ext/nmatrix/{yale/smmp1_header.template.c → types.h} +36 -9
  32. data/ext/nmatrix/util/io.cpp +295 -0
  33. data/ext/nmatrix/util/io.h +117 -0
  34. data/ext/nmatrix/util/lapack.h +1175 -0
  35. data/ext/nmatrix/util/math.cpp +557 -0
  36. data/ext/nmatrix/util/math.h +1363 -0
  37. data/ext/nmatrix/util/sl_list.cpp +475 -0
  38. data/ext/nmatrix/util/sl_list.h +255 -0
  39. data/ext/nmatrix/util/util.h +78 -0
  40. data/lib/nmatrix/blas.rb +70 -0
  41. data/lib/nmatrix/io/mat5_reader.rb +567 -0
  42. data/lib/nmatrix/io/mat_reader.rb +162 -0
  43. data/lib/{string.rb → nmatrix/monkeys.rb} +49 -2
  44. data/lib/nmatrix/nmatrix.rb +199 -0
  45. data/lib/nmatrix/nvector.rb +103 -0
  46. data/lib/nmatrix/version.rb +27 -0
  47. data/lib/nmatrix.rb +22 -230
  48. data/nmatrix.gemspec +59 -0
  49. data/scripts/mac-brew-gcc.sh +47 -0
  50. data/spec/4x4_sparse.mat +0 -0
  51. data/spec/4x5_dense.mat +0 -0
  52. data/spec/blas_spec.rb +47 -0
  53. data/spec/elementwise_spec.rb +164 -0
  54. data/spec/io_spec.rb +60 -0
  55. data/spec/lapack_spec.rb +52 -0
  56. data/spec/math_spec.rb +96 -0
  57. data/spec/nmatrix_spec.rb +93 -89
  58. data/spec/nmatrix_yale_spec.rb +52 -36
  59. data/spec/nvector_spec.rb +1 -1
  60. data/spec/slice_spec.rb +257 -0
  61. data/spec/spec_helper.rb +51 -0
  62. data/spec/utm5940.mtx +83844 -0
  63. metadata +113 -71
  64. data/.autotest +0 -23
  65. data/.gemtest +0 -0
  66. data/ext/nmatrix/cblas.c +0 -150
  67. data/ext/nmatrix/dense/blas_header.template.c +0 -52
  68. data/ext/nmatrix/dense/elementwise.template.c +0 -107
  69. data/ext/nmatrix/dense/gemm.template.c +0 -159
  70. data/ext/nmatrix/dense/gemv.template.c +0 -130
  71. data/ext/nmatrix/dense/rationalmath.template.c +0 -68
  72. data/ext/nmatrix/dense.c +0 -307
  73. data/ext/nmatrix/depend +0 -18
  74. data/ext/nmatrix/generator/syntax_tree.rb +0 -481
  75. data/ext/nmatrix/generator.rb +0 -594
  76. data/ext/nmatrix/list.c +0 -774
  77. data/ext/nmatrix/nmatrix.c +0 -1977
  78. data/ext/nmatrix/rational.c +0 -98
  79. data/ext/nmatrix/yale/complexmath.template.c +0 -71
  80. data/ext/nmatrix/yale/elementwise.template.c +0 -46
  81. data/ext/nmatrix/yale/elementwise_op.template.c +0 -73
  82. data/ext/nmatrix/yale/numbmm.template.c +0 -94
  83. data/ext/nmatrix/yale/smmp1.template.c +0 -21
  84. data/ext/nmatrix/yale/smmp2.template.c +0 -43
  85. data/ext/nmatrix/yale/smmp2_header.template.c +0 -46
  86. data/ext/nmatrix/yale/sort_columns.template.c +0 -56
  87. data/ext/nmatrix/yale/symbmm.template.c +0 -54
  88. data/ext/nmatrix/yale/transp.template.c +0 -68
  89. data/ext/nmatrix/yale.c +0 -726
  90. data/lib/array.rb +0 -67
  91. data/spec/syntax_tree_spec.rb +0 -46
@@ -0,0 +1,1601 @@
1
+ /////////////////////////////////////////////////////////////////////
2
+ // = NMatrix
3
+ //
4
+ // A linear algebra library for scientific computation in Ruby.
5
+ // NMatrix is part of SciRuby.
6
+ //
7
+ // NMatrix was originally inspired by and derived from NArray, by
8
+ // Masahiro Tanaka: http://narray.rubyforge.org
9
+ //
10
+ // == Copyright Information
11
+ //
12
+ // SciRuby is Copyright (c) 2010 - 2012, Ruby Science Foundation
13
+ // NMatrix is Copyright (c) 2012, Ruby Science Foundation
14
+ //
15
+ // Please see LICENSE.txt for additional copyright notices.
16
+ //
17
+ // == Contributing
18
+ //
19
+ // By contributing source code to SciRuby, you agree to be bound by
20
+ // our Contributor Agreement:
21
+ //
22
+ // * https://github.com/SciRuby/sciruby/wiki/Contributor-Agreement
23
+ //
24
+ // == yale.c
25
+ //
26
+ // "new yale" storage format for 2D matrices (like yale, but with
27
+ // the diagonal pulled out for O(1) access).
28
+ //
29
+ // Specifications:
30
+ // * dtype and index dtype must necessarily differ
31
+ // * index dtype is defined by whatever unsigned type can store
32
+ // max(rows,cols)
33
+ // * that means vector ija stores only index dtype, but a stores
34
+ // dtype
35
+ // * vectors must be able to grow as necessary
36
+ // * maximum size is rows*cols+1
37
+
38
+ /*
39
+ * Standard Includes
40
+ */
41
+
42
+ #include <ruby.h>
43
+ #include <algorithm> // std::min
44
+ #include <cstdio> // std::fprintf
45
+
46
+ /*
47
+ * Project Includes
48
+ */
49
+
50
+ // #include "types.h"
51
+ #include "util/math.h"
52
+
53
+ #include "data/data.h"
54
+
55
+ #include "common.h"
56
+ #include "yale.h"
57
+
58
+ #include "nmatrix.h"
59
+ #include "ruby_constants.h"
60
+
61
+ /*
62
+ * Macros
63
+ */
64
+ #ifndef NM_MAX
65
+ #define NM_MAX(a,b) (((a)>(b))?(a):(b))
66
+ #define NM_MIN(a,b) (((a)<(b))?(a):(b))
67
+ #endif
68
+
69
+
70
+ /*
71
+ * Global Variables
72
+ */
73
+
74
+ /*
75
+ * Forward Declarations
76
+ */
77
+
78
+ extern "C" {
79
+ static YALE_STORAGE* nm_copy_alloc_struct(const YALE_STORAGE* rhs, const dtype_t new_dtype, const size_t new_capacity, const size_t new_size);
80
+ static YALE_STORAGE* alloc(dtype_t dtype, size_t* shape, size_t dim);
81
+
82
+ /* Ruby-accessible functions */
83
+ static VALUE nm_size(VALUE self);
84
+ static VALUE nm_a(VALUE self);
85
+ static VALUE nm_d(VALUE self);
86
+ static VALUE nm_lu(VALUE self);
87
+ static VALUE nm_ia(VALUE self);
88
+ static VALUE nm_ja(VALUE self);
89
+ static VALUE nm_ija(VALUE self);
90
+
91
+ } // end extern "C" block
92
+
93
+
94
+
95
+ namespace nm { namespace yale_storage {
96
+
97
+ template <typename DType, typename IType>
98
+ static bool ndrow_is_empty(const YALE_STORAGE* s, IType ija, const IType ija_next);
99
+
100
+ template <typename LDType, typename RDType, typename IType>
101
+ static bool ndrow_eqeq_ndrow(const YALE_STORAGE* l, const YALE_STORAGE* r, IType l_ija, const IType l_ija_next, IType r_ija, const IType r_ija_next);
102
+
103
+ template <typename LDType, typename RDType, typename IType>
104
+ static bool eqeq(const YALE_STORAGE* left, const YALE_STORAGE* right);
105
+
106
+ template <typename IType>
107
+ static YALE_STORAGE* copy_alloc_struct(const YALE_STORAGE* rhs, const dtype_t new_dtype, const size_t new_capacity, const size_t new_size);
108
+
109
+ template <typename IType>
110
+ static void increment_ia_after(YALE_STORAGE* s, IType ija_size, IType i, IType n);
111
+
112
+ template <typename IType>
113
+ static IType insert_search(YALE_STORAGE* s, IType left, IType right, IType key, bool* found);
114
+
115
+ template <typename IType>
116
+ static inline size_t get_size(const YALE_STORAGE* storage);
117
+
118
+ template <typename DType, typename IType>
119
+ static char vector_insert(YALE_STORAGE* s, size_t pos, size_t* j, DType* val, size_t n, bool struct_only);
120
+
121
+ template <typename DType, typename IType>
122
+ static char vector_insert_resize(YALE_STORAGE* s, size_t current_size, size_t pos, size_t* j, size_t n, bool struct_only);
123
+
124
+ template <typename nm::ewop_t op, typename IType, typename DType>
125
+ YALE_STORAGE* ew_op(const YALE_STORAGE* left, const YALE_STORAGE* right, dtype_t dtype);
126
+
127
+ /*
128
+ * Functions
129
+ */
130
+
131
+ /*
132
+ * Create Yale storage from IA, JA, and A vectors given in Old Yale format (probably from a file, since NMatrix only uses
133
+ * new Yale for its storage).
134
+ *
135
+ * This function is needed for Matlab .MAT v5 IO.
136
+ */
137
+ template <typename LDType, typename RDType, typename IType>
138
+ YALE_STORAGE* create_from_old_yale(dtype_t dtype, size_t* shape, void* r_ia, void* r_ja, void* r_a) {
139
+ IType* ir = reinterpret_cast<IType*>(r_ia);
140
+ IType* jr = reinterpret_cast<IType*>(r_ja);
141
+ RDType* ar = reinterpret_cast<RDType*>(r_a);
142
+
143
+ // Read through ia and ja and figure out the ndnz (non-diagonal non-zeros) count.
144
+ size_t ndnz = 0, i, p, p_next;
145
+
146
+ for (i = 0; i < shape[0]; ++i) { // Walk down rows
147
+ for (p = ir[i], p_next = ir[i+1]; p < p_next; ++p) { // Now walk through columns
148
+
149
+ if (i != jr[p]) ++ndnz; // entry is non-diagonal and probably nonzero
150
+
151
+ }
152
+ }
153
+
154
+ // Having walked through the matrix, we now go about allocating the space for it.
155
+ YALE_STORAGE* s = alloc(dtype, shape, 2);
156
+
157
+ s->capacity = shape[0] + ndnz + 1;
158
+ s->ndnz = ndnz;
159
+
160
+ // Setup IJA and A arrays
161
+ s->ija = ALLOC_N( IType, s->capacity );
162
+ s->a = ALLOC_N( LDType, s->capacity );
163
+ IType* ijl = reinterpret_cast<IType*>(s->ija);
164
+ LDType* al = reinterpret_cast<LDType*>(s->a);
165
+
166
+ // set the diagonal to zero -- this prevents uninitialized values from popping up.
167
+ for (size_t index = 0; index < shape[0]; ++index) {
168
+ al[index] = 0;
169
+ }
170
+
171
+ // Figure out where to start writing JA in IJA:
172
+ size_t pp = s->shape[0]+1;
173
+
174
+ // Find beginning of first row
175
+ p = ir[0];
176
+
177
+ // Now fill the arrays
178
+ for (i = 0; i < s->shape[0]; ++i) {
179
+
180
+ // Set the beginning of the row (of output)
181
+ ijl[i] = pp;
182
+
183
+ // Now walk through columns, starting at end of row (of input)
184
+ for (size_t p_next = ir[i+1]; p < p_next; ++p, ++pp) {
185
+
186
+ if (i == jr[p]) { // diagonal
187
+
188
+ al[i] = ar[p];
189
+ --pp;
190
+
191
+ } else { // nondiagonal
192
+
193
+ ijl[pp] = jr[p];
194
+ al[pp] = ar[p];
195
+
196
+ }
197
+ }
198
+ }
199
+
200
+ ijl[i] = pp; // Set the end of the last row
201
+
202
+ // Set the zero position for our output matrix
203
+ al[i] = 0;
204
+
205
+ return s;
206
+ }
207
+
208
+
209
+ /*
210
+ * Take two Yale storages and merge them into a new Yale storage.
211
+ *
212
+ * Uses the left as a template for the creation of a new one.
213
+ */
214
+ template <typename DType, typename IType>
215
+ YALE_STORAGE* create_merged(const YALE_STORAGE* left, const YALE_STORAGE* right) {
216
+ char ins_type;
217
+
218
+ size_t size = get_size<IType>(left);
219
+
220
+ // s represents the resulting storage
221
+ YALE_STORAGE* s = copy_alloc_struct<IType>(left, left->dtype, NM_MAX(left->capacity, right->capacity), size);
222
+
223
+ IType* sija = reinterpret_cast<IType*>(s->ija);
224
+ IType* rija = reinterpret_cast<IType*>(right->ija);
225
+
226
+ // set the element between D and LU (the boundary in A), which should be 0.
227
+ reinterpret_cast<DType*>(s->a)[s->shape[0]] = reinterpret_cast<DType*>(left->a)[left->shape[0]];
228
+
229
+ if (right && right != left) {
230
+ // some operations are unary and don't need this; others are x+x and don't need this
231
+
232
+ for (IType i = 0; i < s->shape[0]; ++i) {
233
+
234
+ IType ija = sija[i];
235
+ IType ija_next = sija[i+1];
236
+
237
+ for (IType r_ija = rija[i]; r_ija < rija[i+1]; ++r_ija) {
238
+
239
+ size_t ja = sija[ija]; // insert expects a size_t
240
+
241
+ if (ija == ija_next) {
242
+ // destination row is empty
243
+ ins_type = vector_insert<DType,IType>(s, ija, &ja, NULL, 1, true);
244
+ increment_ia_after<IType>(s, s->shape[0], i, 1);
245
+ ++(s->ndnz);
246
+ ++ija;
247
+
248
+ if (ins_type == 'i') ++ija_next;
249
+
250
+ } else {
251
+ bool found;
252
+
253
+ // merge positions into destination row
254
+ IType pos = insert_search<IType>(s, ija, ija_next-1, sija[ija], &found);
255
+
256
+ if (!found) {
257
+ vector_insert<DType,IType>(s, pos, &ja, NULL, 1, true);
258
+ increment_ia_after<IType>(s, s->shape[0], i, 1);
259
+ ++(s->ndnz);
260
+
261
+ if (ins_type == 'i') ++ija_next;
262
+ }
263
+
264
+ // can now set a left boundary for the next search
265
+ ija = pos + 1;
266
+ }
267
+ }
268
+ }
269
+ }
270
+
271
+ return s;
272
+ }
273
+
274
+
275
+ /*
276
+ * Empty the matrix by initializing the IJA vector and setting the diagonal to 0.
277
+ *
278
+ * Called when most YALE_STORAGE objects are created.
279
+ */
280
+ template <typename DType, typename IType>
281
+ void init(YALE_STORAGE* s) {
282
+ IType IA_INIT = s->shape[0] + 1;
283
+
284
+ IType* ija = reinterpret_cast<IType*>(s->ija);
285
+ // clear out IJA vector
286
+ for (IType i = 0; i < IA_INIT; ++i) {
287
+ ija[i] = IA_INIT; // set initial values for IJA
288
+ }
289
+
290
+ clear_diagonal_and_zero<DType>(s);
291
+ }
292
+
293
+ size_t max_size(YALE_STORAGE* s) {
294
+ size_t result = s->shape[0]*s->shape[1] + 1;
295
+ if (s->shape[0] > s->shape[1])
296
+ result += s->shape[0] - s->shape[1];
297
+
298
+ return result;
299
+ }
300
+ ///////////////
301
+ // Accessors //
302
+ ///////////////
303
+
304
+ /*
305
+ * Returns a slice of YALE_STORAGE object by coppy
306
+ *
307
+ * Slicing-related.
308
+ */
309
+ template <typename DType,typename IType>
310
+ void* get(YALE_STORAGE* storage, SLICE* slice) {
311
+
312
+ size_t *offset = slice->coords;
313
+ // Copy shape for yale construction
314
+ size_t* shape = ALLOC_N(size_t, 2);
315
+ shape[0] = slice->lengths[0];
316
+ shape[1] = slice->lengths[1];
317
+
318
+ IType *src_ija = reinterpret_cast<IType*>(storage->ija);
319
+ DType *src_a = reinterpret_cast<DType*>(storage->a);
320
+
321
+ // Calc ndnz
322
+ size_t ndnz = 0;
323
+ size_t i,j; // indexes of destination matrix
324
+ size_t k,l; // indexes of source matrix
325
+ for (i = 0; i < shape[0]; i++) {
326
+ k = i + offset[0];
327
+ for (j = 0; j < shape[1]; j++) {
328
+ l = j + offset[1];
329
+
330
+ if (j == i) continue;
331
+
332
+ if (k == l && src_a[k] != 0) ndnz++; // for diagonal element of source
333
+ else { // for non-diagonal element
334
+ for (size_t c = src_ija[k]; c < src_ija[k+1]; c++)
335
+ if (src_ija[c] == l) { ndnz++; break; }
336
+ }
337
+
338
+ }
339
+ }
340
+
341
+ size_t request_capacity = shape[0] + ndnz + 1;
342
+ YALE_STORAGE* ns = nm_yale_storage_create(storage->dtype, shape, 2, request_capacity);
343
+
344
+ if (ns->capacity < request_capacity)
345
+ rb_raise(nm_eStorageTypeError, "conversion failed; capacity of %ld requested, max allowable is %ld", request_capacity, ns->capacity);
346
+
347
+ // Initialize the A and IJA arrays
348
+ init<DType,IType>(ns);
349
+ IType *dst_ija = reinterpret_cast<IType*>(ns->ija);
350
+ DType *dst_a = reinterpret_cast<DType*>(ns->a);
351
+
352
+ size_t ija = shape[0] + 1;
353
+ DType val;
354
+ for (i = 0; i < shape[0]; ++i) {
355
+ k = i + offset[0];
356
+ for (j = 0; j < shape[1]; ++j) {
357
+ l = j + offset[1];
358
+
359
+ // Get value from source matrix
360
+ if (k == l) val = src_a[k];
361
+ else {
362
+ // copy non-diagonal element
363
+ for (size_t c = src_ija[k]; c < src_ija[k+1]; ++c) {
364
+ if (src_ija[c] == l) val = src_a[c];
365
+ }
366
+ }
367
+
368
+ // Set value to destination matrix
369
+ if (i == j) dst_a[i] = val;
370
+ else {
371
+ // copy non-diagonal element
372
+ dst_ija[ija] = j;
373
+ dst_a[ija] = val;
374
+
375
+ ++ija;
376
+ for (size_t c = i + 1; c <= shape[0]; ++c) {
377
+ dst_ija[c] = ija;
378
+ }
379
+ }
380
+ }
381
+ }
382
+
383
+ dst_ija[shape[0]] = ija; // indicate the end of the last row
384
+ ns->ndnz = ndnz;
385
+ return ns;
386
+ }
387
+ /*
388
+ * Returns a pointer to the correct location in the A vector of a YALE_STORAGE object, given some set of coordinates
389
+ * (the coordinates are stored in slice).
390
+ */
391
+ template <typename DType,typename IType>
392
+ void* ref(YALE_STORAGE* storage, SLICE* slice) {
393
+ size_t* coords = slice->coords;
394
+
395
+ if (!slice->single) rb_raise(rb_eNotImpError, "This type slicing not supported yet.");
396
+
397
+ DType* a = reinterpret_cast<DType*>(storage->a);
398
+ IType* ija = reinterpret_cast<IType*>(storage->ija);
399
+
400
+ if (coords[0] == coords[1])
401
+ return &(a[ coords[0] ]); // return diagonal entry
402
+
403
+ if (ija[coords[0]] == ija[coords[0]+1])
404
+ return &(a[ storage->shape[0] ]); // return zero pointer
405
+
406
+ // binary search for the column's location
407
+ int pos = binary_search<IType>(storage,
408
+ ija[coords[0]],
409
+ ija[coords[0]+1]-1,
410
+ coords[1]);
411
+
412
+ if (pos != -1 && ija[pos] == coords[1])
413
+ return &(a[pos]); // found exact value
414
+
415
+ return &(a[ storage->shape[0] ]); // return a pointer that happens to be zero
416
+ }
417
+
418
+ /*
419
+ * Attempt to set some cell in a YALE_STORAGE object. Must supply coordinates and a pointer to a value (which will be
420
+ * copied into the storage object).
421
+ */
422
+ template <typename DType, typename IType>
423
+ char set(YALE_STORAGE* storage, SLICE* slice, void* value) {
424
+ DType* v = reinterpret_cast<DType*>(value);
425
+ size_t* coords = slice->coords;
426
+
427
+ bool found = false;
428
+ char ins_type;
429
+
430
+ if (coords[0] == coords[1]) {
431
+ reinterpret_cast<DType*>(storage->a)[coords[0]] = *v; // set diagonal
432
+ return 'r';
433
+ }
434
+
435
+ // Get IJA positions of the beginning and end of the row
436
+ if (reinterpret_cast<IType*>(storage->ija)[coords[0]] == reinterpret_cast<IType*>(storage->ija)[coords[0]+1]) {
437
+ // empty row
438
+ ins_type = vector_insert<DType,IType>(storage, reinterpret_cast<IType*>(storage->ija)[coords[0]], &(coords[1]), v, 1, false);
439
+ increment_ia_after<IType>(storage, storage->shape[0], coords[0], 1);
440
+ storage->ndnz++;
441
+
442
+ return ins_type;
443
+ }
444
+
445
+ // non-empty row. search for coords[1] in the IJA array, between ija and ija_next
446
+ // (including ija, not including ija_next)
447
+ //ija_size = get_size<IType>(storage);
448
+
449
+ // Do a binary search for the column
450
+ size_t pos = insert_search<IType>(storage,
451
+ reinterpret_cast<IType*>(storage->ija)[coords[0]],
452
+ reinterpret_cast<IType*>(storage->ija)[coords[0]+1]-1,
453
+ coords[1], &found);
454
+
455
+ if (found) { // replace
456
+ reinterpret_cast<IType*>(storage->ija)[pos] = coords[1];
457
+ reinterpret_cast<DType*>(storage->a)[pos] = *v;
458
+ return 'r';
459
+ }
460
+
461
+ ins_type = vector_insert<DType,IType>(storage, pos, &(coords[1]), v, 1, false);
462
+ increment_ia_after<IType>(storage, storage->shape[0], coords[0], 1);
463
+ storage->ndnz++;
464
+
465
+ return ins_type;
466
+ }
467
+
468
+ ///////////
469
+ // Tests //
470
+ ///////////
471
+
472
+ /*
473
+ * Yale eql? -- for whole-matrix comparison returning a single value.
474
+ */
475
+ template <typename LDType, typename RDType, typename IType>
476
+ static bool eqeq(const YALE_STORAGE* left, const YALE_STORAGE* right) {
477
+ LDType* la = reinterpret_cast<LDType*>(left->a);
478
+ RDType* ra = reinterpret_cast<RDType*>(right->a);
479
+
480
+ // Compare the diagonals first.
481
+ for (size_t index = 0; index < left->shape[0]; ++index) {
482
+ if (la[index] != ra[index]) return false;
483
+ }
484
+
485
+ IType* lij = reinterpret_cast<IType*>(left->ija);
486
+ IType* rij = reinterpret_cast<IType*>(right->ija);
487
+
488
+ for (IType i = 0; i < left->shape[0]; ++i) {
489
+
490
+ // Get start and end positions of row
491
+ IType l_ija = lij[i],
492
+ l_ija_next = lij[i+1],
493
+ r_ija = rij[i],
494
+ r_ija_next = rij[i+1];
495
+
496
+ // Check to see if one row is empty and the other isn't.
497
+ if (ndrow_is_empty<LDType,IType>(left, l_ija, l_ija_next)) {
498
+ if (!ndrow_is_empty<RDType,IType>(right, r_ija, r_ija_next)) {
499
+ return false;
500
+ }
501
+
502
+ } else if (ndrow_is_empty<RDType,IType>(right, r_ija, r_ija_next)) {
503
+ // one is empty but the other isn't
504
+ return false;
505
+
506
+ } else if (!ndrow_eqeq_ndrow<LDType,RDType,IType>(left, right, l_ija, l_ija_next, r_ija, r_ija_next)) {
507
+ // Neither row is empty. Must compare the rows directly.
508
+ return false;
509
+ }
510
+
511
+ }
512
+
513
+ return true;
514
+ }
515
+
516
+ /*
517
+ * Are two non-diagonal rows the same? We already know.
518
+ */
519
+ template <typename LDType, typename RDType, typename IType>
520
+ static bool ndrow_eqeq_ndrow(const YALE_STORAGE* l, const YALE_STORAGE* r, IType l_ija, const IType l_ija_next, IType r_ija, const IType r_ija_next) {
521
+ bool l_no_more = false, r_no_more = false;
522
+
523
+ IType *lij = reinterpret_cast<IType*>(l->ija),
524
+ *rij = reinterpret_cast<IType*>(r->ija);
525
+
526
+ LDType* la = reinterpret_cast<LDType*>(l->a);
527
+ RDType* ra = reinterpret_cast<RDType*>(r->a);
528
+
529
+ IType l_ja = lij[l_ija],
530
+ r_ja = rij[r_ija];
531
+
532
+ IType ja = std::min(l_ja, r_ja);
533
+
534
+ while (!(l_no_more && r_no_more)) {
535
+ if (l_ja == r_ja) {
536
+
537
+ if (ra[r_ija] != la[l_ija]) return false; // Direct comparison
538
+
539
+ ++l_ija;
540
+ ++r_ija;
541
+
542
+ if (l_ija < l_ija_next) {
543
+ l_ja = lij[l_ija];
544
+
545
+ } else {
546
+ l_no_more = true;
547
+ }
548
+
549
+ if (r_ija < r_ija_next) {
550
+ r_ja = rij[r_ija];
551
+
552
+ } else {
553
+ r_no_more = true;
554
+ }
555
+
556
+ ja = std::min(l_ja, r_ja);
557
+
558
+ } else if (l_no_more || ja < l_ja) {
559
+
560
+ if (ra[r_ija] != 0) return false;
561
+
562
+ ++r_ija;
563
+ if (r_ija < r_ija_next) {
564
+ // get next column
565
+ r_ja = rij[r_ija];
566
+ ja = std::min(l_ja, r_ja);
567
+
568
+ } else {
569
+ l_no_more = true;
570
+ }
571
+
572
+ } else if (r_no_more || ja < r_ja) {
573
+
574
+ if (la[l_ija] != 0) return false;
575
+
576
+ ++l_ija;
577
+ if (l_ija < l_ija_next) {
578
+ // get next column
579
+ l_ja = lij[l_ija];
580
+ ja = std::min(l_ja, r_ja);
581
+ } else {
582
+ l_no_more = true;
583
+ }
584
+
585
+ } else {
586
+ std::fprintf(stderr, "Unhandled in eqeq: l_ja=%d, r_ja=%d\n", (int)l_ja, (int)r_ja);
587
+ }
588
+ }
589
+
590
+ // every item matched
591
+ return true;
592
+ }
593
+
594
+ /*
595
+ * Is the non-diagonal portion of the row empty?
596
+ */
597
+ template <typename DType, typename IType>
598
+ static bool ndrow_is_empty(const YALE_STORAGE* s, IType ija, const IType ija_next) {
599
+ if (ija == ija_next) return true;
600
+
601
+ DType* a = reinterpret_cast<DType*>(s->a);
602
+
603
+ // do all the entries = zero?
604
+ for (; ija < ija_next; ++ija) {
605
+ if (a[ija] != 0) return false;
606
+ }
607
+
608
+ return true;
609
+ }
610
+
611
+ //////////
612
+ // Math //
613
+ //////////
614
+
615
+ #define YALE_IA(s) (reinterpret_cast<IType*>(s->ija))
616
+ #define YALE_IJ(s) (reinterpret_cast<IType*>(s->ija) + s->shape[0] + 1)
617
+ #define YALE_COUNT(yale) (yale->ndnz + yale->shape[0])
618
+
619
+ template <typename nm::ewop_t op, typename IType, typename DType>
620
+ YALE_STORAGE* ew_op(const YALE_STORAGE* left, const YALE_STORAGE* right, dtype_t dtype) {
621
+ size_t init_capacity;
622
+ size_t* new_shape;
623
+
624
+ unsigned int da_index,
625
+ la_index,
626
+ ra_index,
627
+
628
+ a_index_offset,
629
+
630
+ la_row_max,
631
+ ra_row_max,
632
+
633
+ row_index;
634
+
635
+ DType tmp_result;
636
+
637
+ DType * la = reinterpret_cast<DType*> (left->a),
638
+ * ra = reinterpret_cast<DType*>(right->a),
639
+ * da;
640
+
641
+ YALE_STORAGE* dest;
642
+
643
+ new_shape = reinterpret_cast<size_t*>(calloc(2, sizeof(size_t)));
644
+ new_shape[0] = left->shape[0];
645
+ new_shape[1] = left->shape[1];
646
+
647
+ init_capacity = std::min(left->ndnz + right->ndnz + new_shape[0], new_shape[0] * new_shape[1]);
648
+
649
+ dest = nm_yale_storage_create(dtype, new_shape, 2, init_capacity);
650
+ da = reinterpret_cast<DType*>(dest->a);
651
+
652
+ // Calculate diagonal values.
653
+ for (da_index = 0; da_index < dest->shape[0]; ++da_index) {
654
+ da[da_index] = ew_op_switch<op, DType, DType>(la[da_index], ra[da_index]);
655
+ }
656
+
657
+ // Set the zero representation seperator.
658
+ da[da_index] = typeid(DType) == typeid(RubyObject) ? INT2FIX(0) : 0;
659
+
660
+ /*
661
+ * Calculate the offset between start of the A arrays and the non-diagonal
662
+ * entries.
663
+ */
664
+ a_index_offset = dest->shape[0] + 1;
665
+
666
+ // Re-base the A arrays.
667
+ la = la + a_index_offset;
668
+ ra = ra + a_index_offset;
669
+ da = da + a_index_offset;
670
+
671
+ // Initialize our A array indices.
672
+ la_index = ra_index = da_index = 0;
673
+
674
+ // Calculate the non-diagonal values.
675
+ for (row_index = 0; row_index < dest->shape[0]; ++row_index) {
676
+ /*
677
+ * Each row.
678
+ */
679
+
680
+ printf("Row %d\n", row_index);
681
+
682
+ // Get row bounds.
683
+ la_row_max = YALE_IA( left)[row_index + 1] - a_index_offset;
684
+ ra_row_max = YALE_IA(right)[row_index + 1] - a_index_offset;
685
+
686
+ printf("Left : Row Start: %d - Row End %d\n", la_index + a_index_offset, la_row_max + a_index_offset);
687
+ printf("Right : Row Start: %d - Row End %d\n", ra_index + a_index_offset, ra_row_max + a_index_offset);
688
+
689
+ /*
690
+ * Set this row's left bound (which is also the previous row's right
691
+ * bound).
692
+ */
693
+ YALE_IA(dest)[row_index] = da_index + a_index_offset;
694
+
695
+ printf("Left bound of row %d in destination: %d\n", (int)row_index, (int)YALE_IA(dest)[row_index]);
696
+
697
+ // Iterate over non-diagonal entries in this row.
698
+ while (la_index < la_row_max and ra_index < ra_row_max) {
699
+ /*
700
+ * Elements are present on both the left- and right-hand side.
701
+ */
702
+
703
+ printf("Marker 0\n");
704
+
705
+ if (YALE_IJ(left)[la_index] == YALE_IJ(right)[ra_index]) {
706
+ /*
707
+ * Current left- and right-hand values are in the same row and
708
+ * column.
709
+ */
710
+
711
+ printf("Calculating value for [%d, %d].\n", (int)row_index, (int)YALE_IJ(left)[la_index]);
712
+
713
+ tmp_result = ew_op_switch<op, DType, DType>(la[la_index], ra[ra_index]);
714
+
715
+ if (tmp_result != 0) {
716
+ printf("Setting value for [%d, %d] at index %d in destination's A array.\n", (int)row_index, (int)YALE_IJ(left)[la_index], (int)(da_index + a_index_offset));
717
+
718
+ da[da_index] = tmp_result;
719
+ YALE_IJ(dest)[da_index] = YALE_IJ(left)[la_index];
720
+
721
+ ++da_index;
722
+
723
+ } else {
724
+ printf("Result was 0. Skipping.\n");
725
+ }
726
+
727
+ ++la_index;
728
+ ++ra_index;
729
+
730
+ } else if (YALE_IJ(left)[la_index] < YALE_IJ(right)[ra_index]) {
731
+ /*
732
+ * The right-hand index is ahead of the left-hand index.
733
+ */
734
+
735
+ if (op != EW_MUL) {
736
+ // If this is multiplion there is no point in doing the operation.
737
+
738
+ tmp_result = ew_op_switch<op, DType, DType>(la[la_index], typeid(DType) == typeid(RubyObject) ? INT2FIX(0) : 0);
739
+
740
+ printf("Setting value for [%d, %d].\n", (int)row_index, (int)YALE_IJ(left)[la_index]);
741
+
742
+ if (tmp_result != 0) {
743
+ da[da_index] = tmp_result;
744
+ YALE_IJ(dest)[da_index] = YALE_IJ(left)[la_index];
745
+
746
+ ++da_index;
747
+ }
748
+ }
749
+
750
+ ++la_index;
751
+
752
+ } else {
753
+ /*
754
+ * The left-hand index is ahead of the right-hand index.
755
+ */
756
+
757
+ if (op != EW_MUL) {
758
+ // If this is multiplion there is no point in doing the operation.
759
+
760
+ tmp_result = ew_op_switch<op, DType, DType>(typeid(DType) == typeid(RubyObject) ? INT2FIX(0) : 0, ra[ra_index]);
761
+
762
+ printf("Setting value for [%d, %d].\n", (int)row_index, (int)YALE_IJ(right)[ra_index]);
763
+
764
+ if (tmp_result != 0) {
765
+ da[da_index] = tmp_result;
766
+ YALE_IJ(dest)[da_index] = YALE_IJ(right)[ra_index];
767
+
768
+ ++da_index;
769
+ }
770
+ }
771
+
772
+ ++ra_index;
773
+ }
774
+ }
775
+
776
+ if (op != EW_MUL) {
777
+ /*
778
+ * Process the remaining elements on the left- or right-hand side. One or
779
+ * the other, or neither, of the following loops may execute, but not
780
+ * both.
781
+ *
782
+ * If we are doing multiplication this is unnecessary as all remaining
783
+ * operations will produce a zero value.
784
+ */
785
+
786
+ while (la_index < la_row_max) {
787
+ /*
788
+ * Process the remaining elements on the left-hand side.
789
+ */
790
+
791
+ printf("Marker 1\n");
792
+
793
+ tmp_result = ew_op_switch<op, DType, DType>(la[la_index], typeid(DType) == typeid(RubyObject) ? INT2FIX(0) : 0);
794
+
795
+ printf("Setting value for [%d, %d].\n", (int)row_index, (int)YALE_IJ(left)[la_index]);
796
+
797
+ if (tmp_result != 0) {
798
+ da[da_index] = tmp_result;
799
+ YALE_IJ(dest)[da_index] = YALE_IJ(left)[la_index];
800
+
801
+ ++da_index;
802
+ }
803
+
804
+ ++la_index;
805
+ }
806
+
807
+ while (ra_index < ra_row_max) {
808
+ /*
809
+ * Process the remaining elements on the right-hand side.
810
+ */
811
+
812
+ printf("Marker 2\n");
813
+
814
+ tmp_result = ew_op_switch<op, DType, DType>(typeid(DType) == typeid(RubyObject) ? INT2FIX(0) : 0, ra[ra_index]);
815
+
816
+ printf("Setting value for [%d, %d].\n", (int)row_index, (int)YALE_IJ(right)[ra_index]);
817
+
818
+ if (tmp_result != 0) {
819
+ da[da_index] = tmp_result;
820
+ YALE_IJ(dest)[da_index] = YALE_IJ(right)[ra_index];
821
+
822
+ ++da_index;
823
+ }
824
+
825
+ ++ra_index;
826
+ }
827
+ }
828
+
829
+ // Advance the row indices.
830
+ la_index = la_row_max;
831
+ ra_index = ra_row_max;
832
+
833
+ printf("End of row %d\n\n", row_index);
834
+ }
835
+
836
+ // Set the last row's right bound.
837
+ YALE_IA(dest)[row_index] = da_index + a_index_offset;
838
+
839
+ printf("Right bound of row %d in destination: %d\n", row_index - 1, da_index + a_index_offset);
840
+
841
+ // Set the number of non-diagonal non-zero entries in the destination matrix.
842
+ dest->ndnz = da_index;
843
+
844
+ printf("Number of non-diagonal non-zero entires: %ld\n\n", (unsigned long)(dest->ndnz));
845
+
846
+ // Set the capacity of the destination matrix.
847
+ dest->capacity = dest->shape[0] + dest->ndnz + 1;
848
+
849
+ // Resize the destination matrix.
850
+ dest->a = realloc(dest->a, sizeof(DType) * dest->capacity);
851
+ dest->ija = realloc(dest->ija, sizeof(IType) * dest->capacity);
852
+
853
+ return dest;
854
+ }
855
+
856
+ /////////////
857
+ // Utility //
858
+ /////////////
859
+
860
+ /*
861
+ * Binary search for returning stored values. Returns a non-negative position, or -1 for not found.
862
+ */
863
+ template <typename IType>
864
+ int binary_search(YALE_STORAGE* s, IType left, IType right, IType key) {
865
+
866
+ if (left > right) return -1;
867
+
868
+ IType* ija = reinterpret_cast<IType*>(s->ija);
869
+
870
+ IType mid = (left + right)/2;
871
+ IType mid_j = ija[mid];
872
+
873
+ if (mid_j == key)
874
+ return mid;
875
+
876
+ else if (mid_j > key)
877
+ return binary_search<IType>(s, left, mid - 1, key);
878
+
879
+ else
880
+ return binary_search<IType>(s, mid + 1, right, key);
881
+ }
882
+
883
+
884
+
885
+ /*
886
+ * Resize yale storage vectors A and IJA in preparation for an insertion.
887
+ */
888
+ template <typename DType, typename IType>
889
+ static char vector_insert_resize(YALE_STORAGE* s, size_t current_size, size_t pos, size_t* j, size_t n, bool struct_only) {
890
+ // Determine the new capacity for the IJA and A vectors.
891
+ size_t new_capacity = s->capacity * GROWTH_CONSTANT;
892
+ size_t max_capacity = max_size(s);
893
+
894
+ if (new_capacity > max_capacity) {
895
+ new_capacity = max_capacity;
896
+
897
+ if (current_size + n > max_capacity) rb_raise(rb_eNoMemError, "insertion size exceeded maximum yale matrix size");
898
+ }
899
+
900
+ if (new_capacity < current_size + n)
901
+ new_capacity = current_size + n;
902
+
903
+ // Allocate the new vectors.
904
+ IType* new_ija = ALLOC_N( IType, new_capacity );
905
+ NM_CHECK_ALLOC(new_ija);
906
+
907
+ DType* new_a = ALLOC_N( DType, new_capacity );
908
+ NM_CHECK_ALLOC(new_a);
909
+
910
+ IType* old_ija = reinterpret_cast<IType*>(s->ija);
911
+ DType* old_a = reinterpret_cast<DType*>(s->a);
912
+
913
+ // Copy all values prior to the insertion site to the new IJA and new A
914
+ if (struct_only) {
915
+ for (size_t i = 0; i < pos; ++i) {
916
+ new_ija[i] = old_ija[i];
917
+ }
918
+ } else {
919
+ for (size_t i = 0; i < pos; ++i) {
920
+ new_ija[i] = old_ija[i];
921
+ new_a[i] = old_a[i];
922
+ }
923
+ }
924
+
925
+
926
+ // Copy all values subsequent to the insertion site to the new IJA and new A, leaving room (size n) for insertion.
927
+ if (struct_only) {
928
+ for (size_t i = pos; i < current_size - pos + n - 1; ++i) {
929
+ new_ija[i+n] = old_ija[i];
930
+ }
931
+ } else {
932
+ for (size_t i = pos; i < current_size - pos + n - 1; ++i) {
933
+ new_ija[i+n] = old_ija[i];
934
+ new_a[i+n] = old_a[i];
935
+ }
936
+ }
937
+
938
+ s->capacity = new_capacity;
939
+
940
+ free(s->ija);
941
+ free(s->a);
942
+
943
+ s->ija = reinterpret_cast<void*>(new_ija);
944
+ s->a = reinterpret_cast<void*>(new_a);
945
+
946
+ return 'i';
947
+ }
948
+
949
+ /*
950
+ * Insert a value or contiguous values in the ija and a vectors (after ja and
951
+ * diag). Does not free anything; you are responsible!
952
+ *
953
+ * TODO: Improve this so it can handle non-contiguous element insertions
954
+ * efficiently. For now, we can just sort the elements in the row in
955
+ * question.)
956
+ */
957
+ template <typename DType, typename IType>
958
+ static char vector_insert(YALE_STORAGE* s, size_t pos, size_t* j, DType* val, size_t n, bool struct_only) {
959
+ if (pos < s->shape[0]) {
960
+ rb_raise(rb_eArgError, "vector insert pos is before beginning of ja; this should not happen");
961
+ }
962
+
963
+ size_t size = get_size<IType>(s);
964
+
965
+ IType* ija = reinterpret_cast<IType*>(s->ija);
966
+ DType* a = reinterpret_cast<DType*>(s->a);
967
+
968
+ if (size + n > s->capacity) {
969
+ vector_insert_resize<DType,IType>(s, size, pos, j, n, struct_only);
970
+
971
+ // Need to get the new locations for ija and a.
972
+ ija = reinterpret_cast<IType*>(s->ija);
973
+ a = reinterpret_cast<DType*>(s->a);
974
+
975
+ } else {
976
+ /*
977
+ * No resize required:
978
+ * easy (but somewhat slow), just copy elements to the tail, starting at
979
+ * the end, one element at a time.
980
+ *
981
+ * TODO: This can be made slightly more efficient, but only after the tests
982
+ * are written.
983
+ */
984
+
985
+ if (struct_only) {
986
+ for (size_t i = 0; i < size - pos; ++i) {
987
+ ija[size+n-1-i] = ija[size-1-i];
988
+ }
989
+ } else {
990
+ for (size_t i = 0; i < size - pos; ++i) {
991
+ ija[size+n-1-i] = ija[size-1-i];
992
+ a[size+n-1-i] = a[size-1-i];
993
+ }
994
+ }
995
+ }
996
+
997
+ // Now insert the new values.
998
+ if (struct_only) {
999
+ for (size_t i = 0; i < n; ++i) {
1000
+ ija[pos+i] = j[i];
1001
+ }
1002
+ } else {
1003
+ for (size_t i = 0; i < n; ++i) {
1004
+ ija[pos+i] = j[i];
1005
+ a[pos+i] = val[i];
1006
+ }
1007
+ }
1008
+
1009
+ return 'i';
1010
+ }
1011
+
1012
+ /*
1013
+ * If we add n items to row i, we need to increment ija[i+1] and onward.
1014
+ */
1015
+ template <typename IType>
1016
+ static void increment_ia_after(YALE_STORAGE* s, IType ija_size, IType i, IType n) {
1017
+ IType* ija = reinterpret_cast<IType*>(s->ija);
1018
+
1019
+ ++i;
1020
+ for (; i <= ija_size; ++i) {
1021
+ ija[i] += n;
1022
+ }
1023
+ }
1024
+
1025
+ /*
1026
+ * Binary search for returning insertion points.
1027
+ */
1028
+ template <typename IType>
1029
+ static IType insert_search(YALE_STORAGE* s, IType left, IType right, IType key, bool* found) {
1030
+
1031
+ if (left > right) {
1032
+ *found = false;
1033
+ return left;
1034
+ }
1035
+
1036
+ IType* ija = reinterpret_cast<IType*>(s->ija);
1037
+ IType mid = (left + right)/2;
1038
+ IType mid_j = ija[mid];
1039
+
1040
+ if (mid_j == key) {
1041
+ *found = true;
1042
+ return mid;
1043
+
1044
+ } else if (mid_j > key) {
1045
+ return insert_search<IType>(s, left, mid-1, key, found);
1046
+
1047
+ } else {
1048
+ return insert_search<IType>(s, mid+1, right, key, found);
1049
+ }
1050
+ }
1051
+
1052
+ /////////////////////////
1053
+ // Copying and Casting //
1054
+ /////////////////////////
1055
+
1056
+ /*
1057
+ * Templated copy constructor for changing dtypes.
1058
+ */
1059
+ template <typename LDType, typename RDType, typename IType>
1060
+ YALE_STORAGE* cast_copy(const YALE_STORAGE* rhs, dtype_t new_dtype) {
1061
+
1062
+ // Allocate a new structure
1063
+ size_t size = get_size<IType>(rhs);
1064
+ YALE_STORAGE* lhs = copy_alloc_struct<IType>(rhs, new_dtype, rhs->capacity, size);
1065
+
1066
+ if (rhs->dtype == new_dtype) { // FIXME: Test if this condition is actually faster; second condition should work just as well.
1067
+
1068
+ memcpy(lhs->a, rhs->a, size * DTYPE_SIZES[new_dtype]);
1069
+
1070
+ } else {
1071
+
1072
+ LDType* la = reinterpret_cast<LDType*>(lhs->a);
1073
+ RDType* ra = reinterpret_cast<RDType*>(rhs->a);
1074
+
1075
+ for (size_t index = 0; index < size; ++index) {
1076
+ la[index] = ra[index];
1077
+ }
1078
+
1079
+ }
1080
+
1081
+ return lhs;
1082
+ }
1083
+
1084
+ /*
1085
+ * Template access for getting the size of Yale storage.
1086
+ */
1087
+ template <typename IType>
1088
+ static inline size_t get_size(const YALE_STORAGE* storage) {
1089
+ return static_cast<size_t>(reinterpret_cast<IType*>(storage->ija)[ storage->shape[0] ]);
1090
+ }
1091
+
1092
+ /*
1093
+ * Allocate for a copy or copy-cast operation, and copy the IJA portion of the
1094
+ * matrix (the structure).
1095
+ */
1096
+ template <typename IType>
1097
+ static YALE_STORAGE* copy_alloc_struct(const YALE_STORAGE* rhs, const dtype_t new_dtype, const size_t new_capacity, const size_t new_size) {
1098
+ YALE_STORAGE* lhs = ALLOC( YALE_STORAGE );
1099
+ lhs->dim = rhs->dim;
1100
+ lhs->shape = ALLOC_N( size_t, lhs->dim );
1101
+ memcpy(lhs->shape, rhs->shape, lhs->dim * sizeof(size_t));
1102
+ lhs->itype = rhs->itype;
1103
+ lhs->capacity = new_capacity;
1104
+ lhs->dtype = new_dtype;
1105
+ lhs->ndnz = rhs->ndnz;
1106
+
1107
+ lhs->ija = ALLOC_N( IType, lhs->capacity );
1108
+ lhs->a = ALLOC_N( char, DTYPE_SIZES[new_dtype] * lhs->capacity );
1109
+
1110
+ // Now copy the contents -- but only within the boundaries set by the size. Leave
1111
+ // the rest uninitialized.
1112
+ for (size_t i = 0; i < get_size<IType>(rhs); ++i)
1113
+ reinterpret_cast<IType*>(lhs->ija)[i] = reinterpret_cast<IType*>(rhs->ija)[i]; // copy indices
1114
+
1115
+ return lhs;
1116
+ }
1117
+
1118
+ template <typename DType, typename IType>
1119
+ static STORAGE* matrix_multiply(const STORAGE_PAIR& casted_storage, size_t* resulting_shape, bool vector) {
1120
+ YALE_STORAGE *left = (YALE_STORAGE*)(casted_storage.left),
1121
+ *right = (YALE_STORAGE*)(casted_storage.right);
1122
+
1123
+ // We can safely get dtype from the casted matrices; post-condition of binary_storage_cast_alloc is that dtype is the
1124
+ // same for left and right.
1125
+ // int8_t dtype = left->dtype;
1126
+
1127
+ // Create result storage.
1128
+ YALE_STORAGE* result = nm_yale_storage_create(left->dtype, resulting_shape, 2, left->capacity + right->capacity);
1129
+ init<DType,IType>(result);
1130
+
1131
+ IType* ijl = reinterpret_cast<IType*>(left->ija);
1132
+ IType* ijr = reinterpret_cast<IType*>(right->ija);
1133
+ IType* ija = reinterpret_cast<IType*>(result->ija);
1134
+
1135
+ // Symbolic multiplication step (build the structure)
1136
+ nm::math::symbmm<IType>(result->shape[0], result->shape[1], ijl, ijl, true, ijr, ijr, true, ija, true);
1137
+
1138
+ // Numeric multiplication step (fill in the elements)
1139
+ nm::math::numbmm<DType,IType>(result->shape[0], result->shape[1],
1140
+ ijl, ijl, reinterpret_cast<DType*>(left->a), true,
1141
+ ijr, ijr, reinterpret_cast<DType*>(right->a), true,
1142
+ ija, ija, reinterpret_cast<DType*>(result->a), true);
1143
+
1144
+ // Sort the columns
1145
+ nm::math::smmp_sort_columns<DType,IType>(result->shape[0], ija, ija, reinterpret_cast<DType*>(result->a));
1146
+
1147
+ return reinterpret_cast<STORAGE*>(result);
1148
+ }
1149
+
1150
+ }} // end of namespace nm::yale_storage.
1151
+
1152
+ ///////////////////
1153
+ // Ruby Bindings //
1154
+ ///////////////////
1155
+
1156
+ /* These bindings are mostly only for debugging Yale. They are called from Init_nmatrix. */
1157
+
1158
+ extern "C" {
1159
+
1160
+ void nm_init_yale_functions() {
1161
+ cNMatrix_YaleFunctions = rb_define_module_under(cNMatrix, "YaleFunctions");
1162
+
1163
+ rb_define_method(cNMatrix_YaleFunctions, "yale_ija", (METHOD)nm_ija, 0);
1164
+ rb_define_method(cNMatrix_YaleFunctions, "yale_a", (METHOD)nm_a, 0);
1165
+ rb_define_method(cNMatrix_YaleFunctions, "yale_size", (METHOD)nm_size, 0);
1166
+ rb_define_method(cNMatrix_YaleFunctions, "yale_ia", (METHOD)nm_ia, 0);
1167
+ rb_define_method(cNMatrix_YaleFunctions, "yale_ja", (METHOD)nm_ja, 0);
1168
+ rb_define_method(cNMatrix_YaleFunctions, "yale_d", (METHOD)nm_d, 0);
1169
+ rb_define_method(cNMatrix_YaleFunctions, "yale_lu", (METHOD)nm_lu, 0);
1170
+ rb_define_const(cNMatrix_YaleFunctions, "YALE_GROWTH_CONSTANT", rb_float_new(nm::yale_storage::GROWTH_CONSTANT));
1171
+ }
1172
+
1173
+
1174
+ /////////////////
1175
+ // C ACCESSORS //
1176
+ /////////////////
1177
+
1178
+ /*
1179
+ * C accessor for inserting some value in a matrix (or replacing an existing cell).
1180
+ */
1181
+ char nm_yale_storage_set(STORAGE* storage, SLICE* slice, void* v) {
1182
+ NAMED_LI_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::set, char, YALE_STORAGE* storage, SLICE* slice, void* value);
1183
+
1184
+ YALE_STORAGE* casted_storage = (YALE_STORAGE*)storage;
1185
+
1186
+ return ttable[casted_storage->dtype][casted_storage->itype](casted_storage, slice, v);
1187
+ }
1188
+
1189
+ /*
1190
+ * C accessor for yale_storage::get, which returns a slice of YALE_STORAGE object by coppy
1191
+ *
1192
+ * Slicing-related.
1193
+ */
1194
+ void* nm_yale_storage_get(STORAGE* storage, SLICE* slice) {
1195
+ NAMED_LI_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::get, void*, YALE_STORAGE* storage, SLICE* slice);
1196
+ YALE_STORAGE* s = (YALE_STORAGE*)storage;
1197
+
1198
+
1199
+ YALE_STORAGE* casted_storage = (YALE_STORAGE*)storage;
1200
+ return ttable[casted_storage->dtype][casted_storage->itype](casted_storage, slice);
1201
+ }
1202
+
1203
+ /*
1204
+ * C accessor for yale_storage::ref, which returns a pointer to the correct location in a YALE_STORAGE object
1205
+ * for some set of coordinates.
1206
+ */
1207
+ void* nm_yale_storage_ref(STORAGE* storage, SLICE* slice) {
1208
+ NAMED_LI_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::ref, void*, YALE_STORAGE* storage, SLICE* slice);
1209
+
1210
+ YALE_STORAGE* casted_storage = (YALE_STORAGE*)storage;
1211
+ return ttable[casted_storage->dtype][casted_storage->itype](casted_storage, slice);
1212
+ }
1213
+
1214
+ /*
1215
+ * C accessor for determining whether two YALE_STORAGE objects have the same contents.
1216
+ *
1217
+ * FIXME: Is this for element-wise or whole-matrix equality?
1218
+ */
1219
+ bool nm_yale_storage_eqeq(const STORAGE* left, const STORAGE* right) {
1220
+ NAMED_LRI_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::eqeq, bool, const YALE_STORAGE* left, const YALE_STORAGE* right);
1221
+
1222
+ const YALE_STORAGE* casted_left = reinterpret_cast<const YALE_STORAGE*>(left);
1223
+
1224
+ return ttable[casted_left->dtype][right->dtype][casted_left->itype](casted_left, (const YALE_STORAGE*)right);
1225
+ }
1226
+
1227
+ /*
1228
+ * Copy constructor for changing dtypes. (C accessor)
1229
+ */
1230
+ STORAGE* nm_yale_storage_cast_copy(const STORAGE* rhs, dtype_t new_dtype) {
1231
+ NAMED_LRI_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::cast_copy, YALE_STORAGE*, const YALE_STORAGE* rhs, dtype_t new_dtype);
1232
+
1233
+ const YALE_STORAGE* casted_rhs = reinterpret_cast<const YALE_STORAGE*>(rhs);
1234
+
1235
+ return (STORAGE*)ttable[new_dtype][casted_rhs->dtype][casted_rhs->itype](casted_rhs, new_dtype);
1236
+ }
1237
+
1238
+ /*
1239
+ * Returns size of Yale storage as a size_t (no matter what the itype is). (C accessor)
1240
+ */
1241
+ inline size_t nm_yale_storage_get_size(const YALE_STORAGE* storage) {
1242
+ NAMED_ITYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::get_size, size_t, const YALE_STORAGE* storage);
1243
+
1244
+ return ttable[storage->itype](storage);
1245
+ }
1246
+
1247
+ /*
1248
+ * C accessor for allocating a yale storage object for cast-copying. Copies the IJA vector, does not copy the A vector.
1249
+ */
1250
+ static YALE_STORAGE* nm_copy_alloc_struct(const YALE_STORAGE* rhs, const dtype_t new_dtype, const size_t new_capacity, const size_t new_size) {
1251
+ NAMED_ITYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::copy_alloc_struct, YALE_STORAGE*, const YALE_STORAGE* rhs, const dtype_t new_dtype, const size_t new_capacity, const size_t new_size);
1252
+
1253
+ return ttable[rhs->itype](rhs, new_dtype, new_capacity, new_size);
1254
+ }
1255
+
1256
+ /*
1257
+ * Transposing copy constructor.
1258
+ */
1259
+ STORAGE* nm_yale_storage_copy_transposed(const STORAGE* rhs_base) {
1260
+ YALE_STORAGE* rhs = (YALE_STORAGE*)rhs_base;
1261
+
1262
+ size_t* shape = ALLOC_N(size_t, 2);
1263
+ shape[0] = rhs->shape[1];
1264
+ shape[1] = rhs->shape[0];
1265
+
1266
+ size_t size = nm_yale_storage_get_size(rhs);
1267
+
1268
+ YALE_STORAGE* lhs = nm_yale_storage_create(rhs->dtype, shape, 2, size);
1269
+ nm_yale_storage_init(lhs);
1270
+
1271
+ NAMED_LI_DTYPE_TEMPLATE_TABLE(transp, nm::math::transpose_yale, void, const size_t n, const size_t m, const void* ia_, const void* ja_, const void* a_, const bool diaga, void* ib_, void* jb_, void* b_, const bool move);
1272
+
1273
+ transp[lhs->dtype][lhs->itype](rhs->shape[0], rhs->shape[1], rhs->ija, rhs->ija, rhs->a, true, lhs->ija, lhs->ija, lhs->a, true);
1274
+
1275
+ return (STORAGE*)lhs;
1276
+ }
1277
+
1278
+ /*
1279
+ * C accessor for multiplying two YALE_STORAGE matrices, which have already been casted to the same dtype.
1280
+ *
1281
+ * FIXME: What happens if the two matrices have different itypes?
1282
+ */
1283
+ STORAGE* nm_yale_storage_matrix_multiply(const STORAGE_PAIR& casted_storage, size_t* resulting_shape, bool vector) {
1284
+ LI_DTYPE_TEMPLATE_TABLE(nm::yale_storage::matrix_multiply, STORAGE*, const STORAGE_PAIR& casted_storage, size_t* resulting_shape, bool vector);
1285
+
1286
+ YALE_STORAGE* storage_access = (YALE_STORAGE*)(casted_storage.left);
1287
+
1288
+ return ttable[storage_access->dtype][storage_access->itype](casted_storage, resulting_shape, vector);
1289
+ }
1290
+
1291
+ /*
1292
+ * Documentation goes here.
1293
+ */
1294
+ STORAGE* nm_yale_storage_ew_op(nm::ewop_t op, const STORAGE* left, const STORAGE* right) {
1295
+ OP_ITYPE_DTYPE_TEMPLATE_TABLE(nm::yale_storage::ew_op, YALE_STORAGE*, const YALE_STORAGE*, const YALE_STORAGE*, dtype_t);
1296
+
1297
+ YALE_STORAGE* new_l = NULL, * new_r = NULL;
1298
+ YALE_STORAGE* result;
1299
+
1300
+ const YALE_STORAGE* casted_l, * casted_r;
1301
+
1302
+ dtype_t new_dtype;
1303
+
1304
+ if (left->dtype != right->dtype) {
1305
+
1306
+ new_dtype = Upcast[left->dtype][right->dtype];
1307
+
1308
+ if (left->dtype != new_dtype) {
1309
+ new_l = reinterpret_cast<YALE_STORAGE*>(nm_yale_storage_cast_copy( left, new_dtype));
1310
+ }
1311
+
1312
+ if (right->dtype != new_dtype) {
1313
+ new_r = reinterpret_cast<YALE_STORAGE*>(nm_yale_storage_cast_copy(right, new_dtype));
1314
+ }
1315
+
1316
+ if (static_cast<uint8_t>(op) < nm::NUM_NONCOMP_EWOPS) {
1317
+ result = ttable[op][new_l->itype][new_dtype]( left->dtype == new_dtype ?
1318
+ reinterpret_cast<const YALE_STORAGE*>( left) :
1319
+ reinterpret_cast<const YALE_STORAGE*>(new_l),
1320
+
1321
+ right->dtype == new_dtype ?
1322
+ reinterpret_cast<const YALE_STORAGE*>(right) :
1323
+ reinterpret_cast<const YALE_STORAGE*>(new_r),
1324
+
1325
+ new_dtype);
1326
+
1327
+ } else {
1328
+ rb_raise(rb_eNotImpError, "Elementwise comparison is not yet implemented for the Yale storage class.");
1329
+ }
1330
+
1331
+ if (new_l != NULL) {
1332
+ nm_yale_storage_delete(new_l);
1333
+ }
1334
+
1335
+ if (new_r != NULL) {
1336
+ nm_yale_storage_delete(new_r);
1337
+ }
1338
+
1339
+ return result;
1340
+
1341
+ } else {
1342
+
1343
+ casted_l = reinterpret_cast<const YALE_STORAGE*>( left);
1344
+ casted_r = reinterpret_cast<const YALE_STORAGE*>(right);
1345
+
1346
+ if (static_cast<uint8_t>(op) < nm::NUM_NONCOMP_EWOPS) {
1347
+
1348
+ return ttable[op][casted_l->itype][casted_l->dtype](casted_l, casted_r, casted_l->dtype);
1349
+
1350
+ } else {
1351
+ rb_raise(rb_eNotImpError, "Elementwise comparison is not yet implemented for the Yale storage class.");
1352
+ }
1353
+ }
1354
+ }
1355
+
1356
+ ///////////////
1357
+ // Lifecycle //
1358
+ ///////////////
1359
+
1360
+ /*
1361
+ * C accessor function for creating a YALE_STORAGE object. Prior to calling this function, you MUST
1362
+ * allocate shape (should be size_t * 2) -- don't use use a regular size_t array!
1363
+ *
1364
+ * For this type, dim must always be 2. The final argument is the initial capacity with which to
1365
+ * create the storage.
1366
+ */
1367
+
1368
+ YALE_STORAGE* nm_yale_storage_create(dtype_t dtype, size_t* shape, size_t dim, size_t init_capacity) {
1369
+ YALE_STORAGE* s;
1370
+ size_t max_capacity;
1371
+
1372
+ // FIXME: This error should be handled in the nmatrix.c file.
1373
+ if (dim != 2) {
1374
+ rb_raise(rb_eNotImpError, "Can only support 2D matrices");
1375
+ }
1376
+
1377
+ s = alloc(dtype, shape, dim);
1378
+ max_capacity = nm::yale_storage::max_size(s);
1379
+
1380
+ // Set matrix capacity (and ensure its validity)
1381
+ if (init_capacity < NM_YALE_MINIMUM(s)) {
1382
+ s->capacity = NM_YALE_MINIMUM(s);
1383
+
1384
+ } else if (init_capacity > max_capacity) {
1385
+ // Don't allow storage to be created larger than necessary
1386
+ s->capacity = max_capacity;
1387
+
1388
+ } else {
1389
+ s->capacity = init_capacity;
1390
+
1391
+ }
1392
+
1393
+ s->ija = ALLOC_N( char, ITYPE_SIZES[s->itype] * s->capacity );
1394
+ s->a = ALLOC_N( char, DTYPE_SIZES[s->dtype] * s->capacity );
1395
+
1396
+ return s;
1397
+ }
1398
+
1399
+ /*
1400
+ * Destructor for yale storage (C-accessible).
1401
+ */
1402
+ void nm_yale_storage_delete(STORAGE* s) {
1403
+ if (s) {
1404
+ YALE_STORAGE* storage = (YALE_STORAGE*)s;
1405
+ free(storage->shape);
1406
+ free(storage->ija);
1407
+ free(storage->a);
1408
+ free(storage);
1409
+ }
1410
+ }
1411
+
1412
+ /*
1413
+ * C accessor for yale_storage::init, a templated function.
1414
+ *
1415
+ * Initializes the IJA vector of the YALE_STORAGE matrix.
1416
+ */
1417
+ void nm_yale_storage_init(YALE_STORAGE* s) {
1418
+ NAMED_LI_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::init, void, YALE_STORAGE* s);
1419
+
1420
+ ttable[s->dtype][s->itype](s);
1421
+ }
1422
+
1423
+ /*
1424
+ * Ruby GC mark function for YALE_STORAGE. C accessible.
1425
+ */
1426
+ void nm_yale_storage_mark(void* storage_base) {
1427
+ YALE_STORAGE* storage = (YALE_STORAGE*)storage_base;
1428
+ size_t i;
1429
+
1430
+ if (storage && storage->dtype == RUBYOBJ) {
1431
+ for (i = storage->capacity; i-- > 0;) {
1432
+ rb_gc_mark(*((VALUE*)((char*)(storage->a) + i*DTYPE_SIZES[RUBYOBJ])));
1433
+ }
1434
+ }
1435
+ }
1436
+
1437
+ /*
1438
+ * Allocates and initializes the basic struct (but not the IJA or A vectors).
1439
+ */
1440
+ static YALE_STORAGE* alloc(dtype_t dtype, size_t* shape, size_t dim) {
1441
+ YALE_STORAGE* s;
1442
+
1443
+ s = ALLOC( YALE_STORAGE );
1444
+
1445
+ s->ndnz = 0;
1446
+ s->dtype = dtype;
1447
+ s->shape = shape;
1448
+ s->dim = dim;
1449
+ s->itype = nm_yale_storage_itype_by_shape(shape);
1450
+
1451
+ return s;
1452
+ }
1453
+
1454
+ YALE_STORAGE* nm_yale_storage_create_from_old_yale(dtype_t dtype, size_t* shape, void* ia, void* ja, void* a, dtype_t from_dtype) {
1455
+
1456
+ NAMED_LRI_DTYPE_TEMPLATE_TABLE(ttable, nm::yale_storage::create_from_old_yale, YALE_STORAGE*, dtype_t dtype, size_t* shape, void* r_ia, void* r_ja, void* r_a);
1457
+
1458
+ // With C++ templates, we don't want to have a 4-parameter template. That would be LDType, RDType, LIType, RIType.
1459
+ // We can prevent that by copying ia and ja into the correct itype (if necessary) before passing them to the yale
1460
+ // copy constructor.
1461
+ itype_t to_itype = nm_yale_storage_itype_by_shape(shape);
1462
+
1463
+ return ttable[dtype][from_dtype][to_itype](dtype, shape, ia, ja, a);
1464
+
1465
+ }
1466
+
1467
+ //////////////////////////////////////////////
1468
+ // YALE-SPECIFIC FUNCTIONS (RUBY ACCESSORS) //
1469
+ //////////////////////////////////////////////
1470
+
1471
+ /*
1472
+ * Get the size of a Yale matrix (the number of elements actually stored).
1473
+ *
1474
+ * For capacity (the maximum number of elements that can be stored without a resize), use capacity instead.
1475
+ */
1476
+ static VALUE nm_size(VALUE self) {
1477
+ YALE_STORAGE* s = (YALE_STORAGE*)NM_STORAGE(self);
1478
+
1479
+ return rubyobj_from_cval_by_itype((char*)(s->ija) + ITYPE_SIZES[s->itype]*(s->shape[0]), s->itype).rval;
1480
+ }
1481
+
1482
+
1483
+ /*
1484
+ * Get the A array of a Yale matrix (which stores the diagonal and the LU portions of the matrix).
1485
+ */
1486
+ static VALUE nm_a(VALUE self) {
1487
+ YALE_STORAGE* s = NM_STORAGE_YALE(self);
1488
+
1489
+ size_t size = nm_yale_storage_get_size(s);
1490
+ VALUE* vals = ALLOCA_N(VALUE, size);
1491
+
1492
+ for (size_t i = 0; i < size; ++i) {
1493
+ vals[i] = rubyobj_from_cval((char*)(s->a) + DTYPE_SIZES[s->dtype]*i, s->dtype).rval;
1494
+ }
1495
+ VALUE ary = rb_ary_new4(size, vals);
1496
+
1497
+ for (size_t i = size; i < s->capacity; ++i)
1498
+ rb_ary_push(ary, Qnil);
1499
+
1500
+ return ary;
1501
+ }
1502
+
1503
+
1504
+ /*
1505
+ * Get the diagonal ("D") portion of the A array of a Yale matrix.
1506
+ */
1507
+ static VALUE nm_d(VALUE self) {
1508
+ YALE_STORAGE* s = NM_STORAGE_YALE(self);
1509
+
1510
+ VALUE* vals = ALLOCA_N(VALUE, s->shape[0]);
1511
+
1512
+ for (size_t i = 0; i < s->shape[0]; ++i) {
1513
+ vals[i] = rubyobj_from_cval((char*)(s->a) + DTYPE_SIZES[s->dtype]*i, s->dtype).rval;
1514
+ }
1515
+ return rb_ary_new4(s->shape[0], vals);
1516
+ }
1517
+
1518
+ /*
1519
+ * Get the non-diagonal ("LU") portion of the A array of a Yale matrix.
1520
+ */
1521
+ static VALUE nm_lu(VALUE self) {
1522
+ YALE_STORAGE* s = NM_STORAGE_YALE(self);
1523
+
1524
+ size_t size = nm_yale_storage_get_size(s);
1525
+
1526
+ VALUE* vals = ALLOCA_N(VALUE, size - s->shape[0] - 1);
1527
+
1528
+ for (size_t i = 0; i < size - s->shape[0] - 1; ++i) {
1529
+ vals[i] = rubyobj_from_cval((char*)(s->a) + DTYPE_SIZES[s->dtype]*(s->shape[0] + 1 + i), s->dtype).rval;
1530
+ }
1531
+
1532
+ VALUE ary = rb_ary_new4(size - s->shape[0] - 1, vals);
1533
+
1534
+ for (size_t i = size; i < s->capacity; ++i)
1535
+ rb_ary_push(ary, Qnil);
1536
+
1537
+ return ary;
1538
+ }
1539
+
1540
+ /*
1541
+ * Get the IA portion of the IJA array of a Yale matrix. This gives the start and end positions of rows in the
1542
+ * JA and LU portions of the IJA and A arrays, respectively.
1543
+ */
1544
+ static VALUE nm_ia(VALUE self) {
1545
+ YALE_STORAGE* s = NM_STORAGE_YALE(self);
1546
+
1547
+ VALUE* vals = ALLOCA_N(VALUE, s->shape[0] + 1);
1548
+
1549
+ for (size_t i = 0; i < s->shape[0] + 1; ++i) {
1550
+ vals[i] = rubyobj_from_cval_by_itype((char*)(s->ija) + ITYPE_SIZES[s->itype]*i, s->itype).rval;
1551
+ }
1552
+
1553
+ return rb_ary_new4(s->shape[0]+1, vals);
1554
+ }
1555
+
1556
+ /*
1557
+ * Get the JA portion of the IJA array of a Yale matrix. This gives the column indices for entries in corresponding
1558
+ * positions in the LU portion of the A array.
1559
+ */
1560
+ static VALUE nm_ja(VALUE self) {
1561
+ YALE_STORAGE* s = NM_STORAGE_YALE(self);
1562
+
1563
+ size_t size = nm_yale_storage_get_size(s);
1564
+
1565
+ VALUE* vals = ALLOCA_N(VALUE, size - s->shape[0] - 1);
1566
+
1567
+ for (size_t i = 0; i < size - s->shape[0] - 1; ++i) {
1568
+ vals[i] = rubyobj_from_cval_by_itype((char*)(s->ija) + ITYPE_SIZES[s->itype]*(s->shape[0] + 1 + i), s->itype).rval;
1569
+ }
1570
+
1571
+ VALUE ary = rb_ary_new4(size - s->shape[0] - 1, vals);
1572
+
1573
+ for (size_t i = size; i < s->capacity; ++i)
1574
+ rb_ary_push(ary, Qnil);
1575
+
1576
+ return ary;
1577
+ }
1578
+
1579
+ /*
1580
+ * Get the IJA array of a Yale matrix.
1581
+ */
1582
+ static VALUE nm_ija(VALUE self) {
1583
+ YALE_STORAGE* s = NM_STORAGE_YALE(self);
1584
+
1585
+ size_t size = nm_yale_storage_get_size(s);
1586
+
1587
+ VALUE* vals = ALLOCA_N(VALUE, size);
1588
+
1589
+ for (size_t i = 0; i < size; ++i) {
1590
+ vals[i] = rubyobj_from_cval_by_itype((char*)(s->ija) + ITYPE_SIZES[s->itype]*i, s->itype).rval;
1591
+ }
1592
+
1593
+ VALUE ary = rb_ary_new4(size, vals);
1594
+
1595
+ for (size_t i = size; i < s->capacity; ++i)
1596
+ rb_ary_push(ary, Qnil);
1597
+
1598
+ return ary;
1599
+ }
1600
+
1601
+ } // end of extern "C" block