nmatrix 0.0.8 → 0.0.9

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.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -8
  3. data/.rspec +1 -1
  4. data/.travis.yml +12 -0
  5. data/CONTRIBUTING.md +27 -12
  6. data/Gemfile +1 -0
  7. data/History.txt +38 -0
  8. data/Manifest.txt +15 -15
  9. data/README.rdoc +7 -6
  10. data/Rakefile +40 -5
  11. data/ext/nmatrix/data/data.cpp +2 -37
  12. data/ext/nmatrix/data/data.h +19 -121
  13. data/ext/nmatrix/data/meta.h +70 -0
  14. data/ext/nmatrix/extconf.rb +40 -12
  15. data/ext/nmatrix/math/math.h +13 -103
  16. data/ext/nmatrix/nmatrix.cpp +10 -2018
  17. data/ext/nmatrix/nmatrix.h +16 -13
  18. data/ext/nmatrix/ruby_constants.cpp +12 -1
  19. data/ext/nmatrix/ruby_constants.h +7 -1
  20. data/ext/nmatrix/ruby_nmatrix.c +2169 -0
  21. data/ext/nmatrix/storage/dense.cpp +123 -14
  22. data/ext/nmatrix/storage/dense.h +10 -4
  23. data/ext/nmatrix/storage/list.cpp +265 -48
  24. data/ext/nmatrix/storage/list.h +6 -9
  25. data/ext/nmatrix/storage/storage.cpp +44 -54
  26. data/ext/nmatrix/storage/storage.h +2 -2
  27. data/ext/nmatrix/storage/yale/class.h +1070 -0
  28. data/ext/nmatrix/storage/yale/iterators/base.h +142 -0
  29. data/ext/nmatrix/storage/yale/iterators/iterator.h +130 -0
  30. data/ext/nmatrix/storage/yale/iterators/row.h +449 -0
  31. data/ext/nmatrix/storage/yale/iterators/row_stored.h +139 -0
  32. data/ext/nmatrix/storage/yale/iterators/row_stored_nd.h +167 -0
  33. data/ext/nmatrix/storage/yale/iterators/stored_diagonal.h +123 -0
  34. data/ext/nmatrix/storage/yale/math/transpose.h +110 -0
  35. data/ext/nmatrix/storage/yale/yale.cpp +1785 -0
  36. data/ext/nmatrix/storage/{yale.h → yale/yale.h} +23 -55
  37. data/ext/nmatrix/types.h +2 -0
  38. data/ext/nmatrix/util/io.cpp +27 -45
  39. data/ext/nmatrix/util/io.h +0 -2
  40. data/ext/nmatrix/util/sl_list.cpp +169 -28
  41. data/ext/nmatrix/util/sl_list.h +9 -3
  42. data/lib/nmatrix/blas.rb +20 -20
  43. data/lib/nmatrix/enumerate.rb +1 -1
  44. data/lib/nmatrix/io/mat5_reader.rb +8 -14
  45. data/lib/nmatrix/lapack.rb +3 -3
  46. data/lib/nmatrix/math.rb +3 -3
  47. data/lib/nmatrix/nmatrix.rb +19 -5
  48. data/lib/nmatrix/nvector.rb +2 -0
  49. data/lib/nmatrix/shortcuts.rb +90 -125
  50. data/lib/nmatrix/version.rb +1 -1
  51. data/nmatrix.gemspec +7 -8
  52. data/spec/{nmatrix_spec.rb → 00_nmatrix_spec.rb} +45 -208
  53. data/spec/01_enum_spec.rb +184 -0
  54. data/spec/{slice_spec.rb → 02_slice_spec.rb} +55 -39
  55. data/spec/blas_spec.rb +22 -54
  56. data/spec/elementwise_spec.rb +9 -8
  57. data/spec/io_spec.rb +6 -4
  58. data/spec/lapack_spec.rb +26 -26
  59. data/spec/math_spec.rb +9 -5
  60. data/spec/nmatrix_yale_spec.rb +29 -61
  61. data/spec/shortcuts_spec.rb +34 -22
  62. data/spec/slice_set_spec.rb +157 -0
  63. data/spec/spec_helper.rb +42 -2
  64. data/spec/stat_spec.rb +192 -0
  65. metadata +52 -55
  66. data/ext/nmatrix/storage/yale.cpp +0 -2284
  67. data/spec/nmatrix_list_spec.rb +0 -113
  68. data/spec/nvector_spec.rb +0 -112
@@ -99,6 +99,69 @@ namespace nm { namespace dense_storage {
99
99
 
100
100
  }
101
101
 
102
+ /*
103
+ * Recursive function, sets multiple values in a matrix from a single source value. Same basic pattern as slice_copy.
104
+ */
105
+ template <typename D>
106
+ static void slice_set(DENSE_STORAGE* dest, size_t* lengths, size_t pdest, size_t rank, D* const v, size_t v_size, size_t& v_offset) {
107
+ if (dest->dim - rank > 1) {
108
+ for (size_t i = 0; i < lengths[rank]; ++i) {
109
+ slice_set<D>(dest, lengths, pdest + dest->stride[rank] * i, rank + 1, v, v_size, v_offset);
110
+ }
111
+ } else {
112
+ for (size_t p = 0; p < lengths[rank]; ++p, ++v_offset) {
113
+ if (v_offset >= v_size) v_offset %= v_size;
114
+
115
+ D* elem = reinterpret_cast<D*>(dest->elements);
116
+ elem[p + pdest] = v[v_offset];
117
+ }
118
+ }
119
+ }
120
+
121
+
122
+ /*
123
+ * Dense storage set/slice-set function, templated version.
124
+ */
125
+ template <typename D>
126
+ void set(VALUE left, SLICE* slice, VALUE right) {
127
+ DENSE_STORAGE* s = NM_STORAGE_DENSE(left);
128
+
129
+ std::pair<NMATRIX*,bool> nm_and_free =
130
+ interpret_arg_as_dense_nmatrix(right, NM_DTYPE(left));
131
+
132
+ // Map the data onto D* v.
133
+ D* v;
134
+ size_t v_size = 1;
135
+
136
+ if (nm_and_free.first) {
137
+ DENSE_STORAGE* t = reinterpret_cast<DENSE_STORAGE*>(nm_and_free.first->storage);
138
+ v = reinterpret_cast<D*>(t->elements);
139
+ v_size = nm_storage_count_max_elements(t);
140
+
141
+ } else if (TYPE(right) == T_ARRAY) {
142
+ v_size = RARRAY_LEN(right);
143
+ v = ALLOC_N(D, v_size);
144
+ for (size_t m = 0; m < v_size; ++m) {
145
+ rubyval_to_cval(rb_ary_entry(right, m), s->dtype, &(v[m]));
146
+ }
147
+ } else {
148
+ v = reinterpret_cast<D*>(rubyobj_to_cval(right, NM_DTYPE(left)));
149
+ }
150
+
151
+ if (slice->single) {
152
+ reinterpret_cast<D*>(s->elements)[nm_dense_storage_pos(s, slice->coords)] = *v;
153
+ } else {
154
+ size_t v_offset = 0;
155
+ slice_set(s, slice->lengths, nm_dense_storage_pos(s, slice->coords), 0, v, v_size, v_offset);
156
+ }
157
+
158
+ // Only free v if it was allocated in this function.
159
+ if (nm_and_free.first && nm_and_free.second)
160
+ nm_delete(nm_and_free.first);
161
+ else
162
+ xfree(v);
163
+ }
164
+
102
165
  }} // end of namespace nm::dense_storage
103
166
 
104
167
 
@@ -216,15 +279,18 @@ void nm_dense_storage_delete_ref(STORAGE* s) {
216
279
  /*
217
280
  * Mark values in a dense matrix for garbage collection. This may not be necessary -- further testing required.
218
281
  */
219
- void nm_dense_storage_mark(void* storage_base) {
282
+ void nm_dense_storage_mark(STORAGE* storage_base) {
283
+
220
284
  DENSE_STORAGE* storage = (DENSE_STORAGE*)storage_base;
221
285
 
222
286
  if (storage && storage->dtype == nm::RUBYOBJ) {
223
287
  VALUE* els = reinterpret_cast<VALUE*>(storage->elements);
224
288
 
225
- for (size_t index = nm_storage_count_max_elements(storage); index-- > 0;) {
226
- rb_gc_mark(els[index]);
227
- }
289
+ rb_gc_mark_locations(els, els + nm_storage_count_max_elements(storage) * sizeof(VALUE));
290
+
291
+ //for (size_t index = nm_storage_count_max_elements(storage); index-- > 0;) {
292
+ // rb_gc_mark(els[index]);
293
+ //}
228
294
  }
229
295
  }
230
296
 
@@ -254,6 +320,8 @@ VALUE nm_dense_map_pair(VALUE self, VALUE right) {
254
320
  DENSE_STORAGE* result = nm_dense_storage_create(nm::RUBYOBJ, shape_copy, s->dim, NULL, 0);
255
321
  VALUE* result_elem = reinterpret_cast<VALUE*>(result->elements);
256
322
 
323
+ nm_register_values(result_elem, count);
324
+
257
325
  for (size_t k = 0; k < count; ++k) {
258
326
  nm_dense_storage_coords(result, k, coords);
259
327
  size_t s_index = nm_dense_storage_pos(s, coords),
@@ -266,10 +334,13 @@ VALUE nm_dense_map_pair(VALUE self, VALUE right) {
266
334
  }
267
335
 
268
336
  NMATRIX* m = nm_create(nm::DENSE_STORE, reinterpret_cast<STORAGE*>(result));
337
+ VALUE rb_nm = Data_Wrap_Struct(CLASS_OF(self), nm_mark, nm_delete, m);
269
338
 
270
- return Data_Wrap_Struct(CLASS_OF(self), nm_dense_storage_mark, nm_delete, m);
271
- }
339
+ nm_unregister_values(result_elem, count);
272
340
 
341
+ return rb_nm;
342
+
343
+ }
273
344
 
274
345
  /*
275
346
  * map enumerator for dense matrices.
@@ -290,6 +361,8 @@ VALUE nm_dense_map(VALUE self) {
290
361
  DENSE_STORAGE* result = nm_dense_storage_create(nm::RUBYOBJ, shape_copy, s->dim, NULL, 0);
291
362
  VALUE* result_elem = reinterpret_cast<VALUE*>(result->elements);
292
363
 
364
+ nm_register_values(result_elem, count);
365
+
293
366
  for (size_t k = 0; k < count; ++k) {
294
367
  nm_dense_storage_coords(result, k, coords);
295
368
  size_t s_index = nm_dense_storage_pos(s, coords);
@@ -298,8 +371,12 @@ VALUE nm_dense_map(VALUE self) {
298
371
  }
299
372
 
300
373
  NMATRIX* m = nm_create(nm::DENSE_STORE, reinterpret_cast<STORAGE*>(result));
374
+ VALUE rb_nm = Data_Wrap_Struct(CLASS_OF(self), nm_mark, nm_delete, m);
375
+
376
+ nm_unregister_values(result_elem, count);
377
+
378
+ return rb_nm;
301
379
 
302
- return Data_Wrap_Struct(CLASS_OF(self), nm_dense_storage_mark, nm_delete, m);
303
380
  }
304
381
 
305
382
 
@@ -408,7 +485,7 @@ static void slice_copy(DENSE_STORAGE *dest, const DENSE_STORAGE *src, size_t* le
408
485
  *
409
486
  * FIXME: Template the first condition.
410
487
  */
411
- void* nm_dense_storage_get(STORAGE* storage, SLICE* slice) {
488
+ void* nm_dense_storage_get(const STORAGE* storage, SLICE* slice) {
412
489
  DENSE_STORAGE* s = (DENSE_STORAGE*)storage;
413
490
 
414
491
  if (slice->single)
@@ -437,7 +514,7 @@ void* nm_dense_storage_get(STORAGE* storage, SLICE* slice) {
437
514
  *
438
515
  * FIXME: Template the first condition.
439
516
  */
440
- void* nm_dense_storage_ref(STORAGE* storage, SLICE* slice) {
517
+ void* nm_dense_storage_ref(const STORAGE* storage, SLICE* slice) {
441
518
  DENSE_STORAGE* s = (DENSE_STORAGE*)storage;
442
519
 
443
520
  if (slice->single)
@@ -466,14 +543,18 @@ void* nm_dense_storage_ref(STORAGE* storage, SLICE* slice) {
466
543
  }
467
544
 
468
545
 
546
+
547
+
469
548
  /*
470
- * Does not free passed-in value! Different from list_storage_insert.
549
+ * Set a value or values in a dense matrix. Requires that right be either a single value or an NMatrix (ref or real).
471
550
  */
472
- void nm_dense_storage_set(STORAGE* storage, SLICE* slice, void* val) {
473
- DENSE_STORAGE* s = (DENSE_STORAGE*)storage;
474
- memcpy((char*)(s->elements) + nm_dense_storage_pos(s, slice->coords) * DTYPE_SIZES[s->dtype], val, DTYPE_SIZES[s->dtype]);
551
+ void nm_dense_storage_set(VALUE left, SLICE* slice, VALUE right) {
552
+ NAMED_DTYPE_TEMPLATE_TABLE(ttable, nm::dense_storage::set, void, VALUE, SLICE*, VALUE)
553
+
554
+ ttable[NM_DTYPE(left)](left, slice, right);
475
555
  }
476
556
 
557
+
477
558
  ///////////
478
559
  // Tests //
479
560
  ///////////
@@ -678,7 +759,35 @@ STORAGE* nm_dense_storage_copy_transposed(const STORAGE* rhs_base) {
678
759
 
679
760
  } // end of extern "C" block
680
761
 
681
- namespace nm { namespace dense_storage {
762
+ namespace nm {
763
+
764
+ /*
765
+ * Used for slice setting. Takes the right-hand of the equal sign, a single VALUE, and massages
766
+ * it into the correct form if it's not already there (dtype, non-ref, dense). Returns a pair of the NMATRIX* and a
767
+ * boolean. If the boolean is true, the calling function is responsible for calling nm_delete on the NMATRIX*.
768
+ * Otherwise, the NMATRIX* still belongs to Ruby and Ruby will free it.
769
+ */
770
+ std::pair<NMATRIX*,bool> interpret_arg_as_dense_nmatrix(VALUE right, nm::dtype_t dtype) {
771
+ if (TYPE(right) == T_DATA && (RDATA(right)->dfree == (RUBY_DATA_FUNC)nm_delete || RDATA(right)->dfree == (RUBY_DATA_FUNC)nm_delete_ref)) {
772
+ NMATRIX *r;
773
+ if (NM_STYPE(right) != DENSE_STORE || NM_DTYPE(right) != dtype || NM_SRC(right) != NM_STORAGE(right)) {
774
+ UnwrapNMatrix( right, r );
775
+ NMATRIX* ldtype_r = nm_cast_with_ctype_args(r, nm::DENSE_STORE, dtype, NULL);
776
+ return std::make_pair(ldtype_r,true);
777
+ } else { // simple case -- right-hand matrix is dense and is not a reference and has same dtype
778
+ UnwrapNMatrix( right, r );
779
+ return std::make_pair(r, false);
780
+ }
781
+ // Do not set v_alloc = true for either of these. It is the responsibility of r/ldtype_r
782
+ } else if (TYPE(right) == T_DATA) {
783
+ rb_raise(rb_eTypeError, "unrecognized type for slice assignment");
784
+ }
785
+
786
+ return std::make_pair<NMATRIX*,bool>(NULL, false);
787
+ }
788
+
789
+
790
+ namespace dense_storage {
682
791
 
683
792
  /////////////////////////
684
793
  // Templated Functions //
@@ -72,7 +72,9 @@ extern "C" {
72
72
  DENSE_STORAGE* nm_dense_storage_create(nm::dtype_t dtype, size_t* shape, size_t dim, void* elements, size_t elements_length);
73
73
  void nm_dense_storage_delete(STORAGE* s);
74
74
  void nm_dense_storage_delete_ref(STORAGE* s);
75
- void nm_dense_storage_mark(void*);
75
+ void nm_dense_storage_mark(STORAGE*);
76
+ void nm_dense_storage_register_values(VALUE* values, size_t n);
77
+ void nm_dense_storage_unregister_values(VALUE* values, size_t n);
76
78
 
77
79
  ///////////////
78
80
  // Accessors //
@@ -83,9 +85,9 @@ VALUE nm_dense_map_pair(VALUE self, VALUE right);
83
85
  VALUE nm_dense_map(VALUE self);
84
86
  VALUE nm_dense_each(VALUE nmatrix);
85
87
  VALUE nm_dense_each_with_indices(VALUE nmatrix);
86
- void* nm_dense_storage_get(STORAGE* s, SLICE* slice);
87
- void* nm_dense_storage_ref(STORAGE* s, SLICE* slice);
88
- void nm_dense_storage_set(STORAGE* s, SLICE* slice, void* val);
88
+ void* nm_dense_storage_get(const STORAGE* s, SLICE* slice);
89
+ void* nm_dense_storage_ref(const STORAGE* s, SLICE* slice);
90
+ void nm_dense_storage_set(VALUE left, SLICE* slice, VALUE right);
89
91
 
90
92
  ///////////
91
93
  // Tests //
@@ -118,4 +120,8 @@ STORAGE* nm_dense_storage_cast_copy(const STORAGE* rhs, nm::dtype_t new_d
118
120
 
119
121
  } // end of extern "C" block
120
122
 
123
+ namespace nm {
124
+ std::pair<NMATRIX*,bool> interpret_arg_as_dense_nmatrix(VALUE right, nm::dtype_t dtype);
125
+ } // end of namespace nm
126
+
121
127
  #endif // DENSE_H
@@ -43,6 +43,7 @@
43
43
 
44
44
  #include "data/data.h"
45
45
 
46
+ #include "dense.h"
46
47
  #include "common.h"
47
48
  #include "list.h"
48
49
 
@@ -57,6 +58,11 @@
57
58
  * Global Variables
58
59
  */
59
60
 
61
+
62
+ extern "C" {
63
+ static void slice_set_single(LIST_STORAGE* dest, LIST* l, void* val, size_t* coords, size_t* lengths, size_t n);
64
+ }
65
+
60
66
  namespace nm { namespace list_storage {
61
67
 
62
68
  /*
@@ -126,7 +132,7 @@ protected:
126
132
 
127
133
 
128
134
  template <typename LDType, typename RDType>
129
- static LIST_STORAGE* cast_copy(const LIST_STORAGE* rhs, dtype_t new_dtype);
135
+ static LIST_STORAGE* cast_copy(const LIST_STORAGE* rhs, nm::dtype_t new_dtype);
130
136
 
131
137
  template <typename LDType, typename RDType>
132
138
  static bool eqeq_r(RecurseData& left, RecurseData& right, const LIST* l, const LIST* r, size_t rec);
@@ -246,8 +252,171 @@ static void map_merged_stored_r(RecurseData& result, RecurseData& left, RecurseD
246
252
  }
247
253
  }
248
254
 
255
+ /*
256
+ * Recursive function, sets multiple values in a matrix from multiple source values. Also handles removal; returns true
257
+ * if the recursion results in an empty list at that level (which signals that the current parent should be removed).
258
+ */
259
+ template <typename D>
260
+ static bool slice_set(LIST_STORAGE* dest, LIST* l, size_t* coords, size_t* lengths, size_t n, D* v, size_t v_size, size_t& v_offset) {
261
+ using nm::list::node_is_within_slice;
262
+ using nm::list::remove_by_node;
263
+ using nm::list::find_preceding_from_list;
264
+ using nm::list::insert_first_list;
265
+ using nm::list::insert_first_node;
266
+ using nm::list::insert_after;
267
+ size_t* offsets = dest->offset;
268
+
269
+ // drill down into the structure
270
+ NODE* prev = find_preceding_from_list(l, coords[n] + offsets[n]);
271
+ NODE* node = NULL;
272
+ if (prev) node = prev->next && node_is_within_slice(prev->next, coords[n] + offsets[n], lengths[n]) ? prev->next : NULL;
273
+ else node = node_is_within_slice(l->first, coords[n] + offsets[n], lengths[n]) ? l->first : NULL;
274
+
275
+ if (dest->dim - n > 1) {
276
+ size_t i = 0;
277
+ size_t key = i + offsets[n] + coords[n];
278
+
279
+ // Make sure we have an element to work with
280
+ if (!node) {
281
+ if (!prev) {
282
+ node = insert_first_list(l, key, nm::list::create());
283
+ } else {
284
+ node = insert_after(prev, key, nm::list::create());
285
+ }
286
+ }
287
+
288
+ // At this point, it's guaranteed that there is a list here matching key.
289
+
290
+ while (node) {
291
+ // Recurse down into the list. If it returns true, it's empty, so we need to delete it.
292
+ bool remove_parent = slice_set(dest, reinterpret_cast<LIST*>(node->val), coords, lengths, n+1, v, v_size, v_offset);
293
+
294
+ if (remove_parent) {
295
+ xfree(remove_by_node(l, prev, node));
296
+ if (prev) node = prev->next ? prev->next : NULL;
297
+ else node = l->first ? l->first : NULL;
298
+ } else { // move forward
299
+ prev = node;
300
+ node = node_is_within_slice(prev->next, key-i, lengths[n]) ? prev->next : NULL;
301
+ }
302
+
303
+ ++i; ++key;
304
+
305
+ if (i >= lengths[n]) break;
306
+
307
+ // Now do we need to insert another node here? Or is there already one?
308
+ if (!node) {
309
+ if (!prev) {
310
+ node = insert_first_list(l, key, nm::list::create());
311
+ } else {
312
+ node = insert_after(prev, key, nm::list::create());
313
+ }
314
+ }
315
+ }
316
+
317
+ } else {
318
+
319
+ size_t i = 0;
320
+ size_t key = i + offsets[n] + coords[n];
321
+
322
+ while (i < lengths[n]) {
323
+ // Make sure we have an element to work with
324
+ if (v_offset >= v_size) v_offset %= v_size;
325
+
326
+ if (node) {
327
+ if (node->key == key) {
328
+ if (v[v_offset] == *reinterpret_cast<D*>(dest->default_val)) { // remove zero value
329
+
330
+ xfree(remove_by_node(l, (prev ? prev : l->first), node));
331
+
332
+ if (prev) node = prev->next ? prev->next : NULL;
333
+ else node = l->first ? l->first : NULL;
334
+
335
+ } else { // edit directly
336
+ *reinterpret_cast<D*>(node->val) = v[v_offset];
337
+ prev = node;
338
+ node = node->next ? node->next : NULL;
339
+ }
340
+ } else if (node->key > key) {
341
+ D* nv = ALLOC(D); *nv = v[v_offset];
342
+ if (prev) node = insert_after(prev, key, nv);
343
+ else node = insert_first_node(l, key, nv, sizeof(D));
344
+
345
+ prev = node;
346
+ node = prev->next ? prev->next : NULL;
347
+ }
348
+ } else { // no node -- insert a new one
349
+ D* nv = ALLOC(D); *nv = v[v_offset];
350
+ if (prev) node = insert_after(prev, key, nv);
351
+ else node = insert_first_node(l, key, nv, sizeof(D));
352
+
353
+ prev = node;
354
+ node = prev->next ? prev->next : NULL;
355
+ }
356
+
357
+ ++i; ++key;
358
+ }
359
+ }
360
+
361
+ return (l->first) ? false : true;
362
+ }
363
+
364
+
365
+ template <typename D>
366
+ void set(VALUE left, SLICE* slice, VALUE right) {
367
+ LIST_STORAGE* s = NM_STORAGE_LIST(left);
368
+
369
+ std::pair<NMATRIX*,bool> nm_and_free =
370
+ interpret_arg_as_dense_nmatrix(right, NM_DTYPE(left));
371
+
372
+ // Map the data onto D* v.
373
+ D* v;
374
+ size_t v_size = 1;
375
+
376
+ if (nm_and_free.first) {
377
+ DENSE_STORAGE* t = reinterpret_cast<DENSE_STORAGE*>(nm_and_free.first->storage);
378
+ v = reinterpret_cast<D*>(t->elements);
379
+ v_size = nm_storage_count_max_elements(t);
380
+
381
+ } else if (TYPE(right) == T_ARRAY) {
382
+ v_size = RARRAY_LEN(right);
383
+ v = ALLOC_N(D, v_size);
384
+ for (size_t m = 0; m < v_size; ++m) {
385
+ rubyval_to_cval(rb_ary_entry(right, m), s->dtype, &(v[m]));
386
+ }
387
+ } else {
388
+ v = reinterpret_cast<D*>(rubyobj_to_cval(right, NM_DTYPE(left)));
389
+ }
390
+
391
+ if (v_size == 1 && *v == *reinterpret_cast<D*>(s->default_val)) {
392
+ nm::list::remove_recursive(s->rows, slice->coords, s->offset, slice->lengths, 0, s->dim);
393
+ } else if (slice->single) {
394
+ slice_set_single(s, s->rows, reinterpret_cast<void*>(v), slice->coords, slice->lengths, 0);
395
+ } else {
396
+ size_t v_offset = 0;
397
+ slice_set<D>(s, s->rows, slice->coords, slice->lengths, 0, v, v_size, v_offset);
398
+ }
399
+
400
+
401
+ // Only free v if it was allocated in this function.
402
+ if (nm_and_free.first) {
403
+ if (nm_and_free.second) {
404
+ nm_delete(nm_and_free.first);
405
+ }
406
+ } else xfree(v);
407
+ }
408
+
409
+ /*
410
+ * Used only to set a default initial value.
411
+ */
412
+ template <typename D>
413
+ void init_default(LIST_STORAGE* s) {
414
+ s->default_val = ALLOC(D);
415
+ *reinterpret_cast<D*>(s->default_val) = 0;
416
+ }
417
+
249
418
 
250
- } // end of namespace list_storage
419
+ }} // end of namespace list_storage
251
420
 
252
421
  extern "C" {
253
422
 
@@ -260,6 +429,7 @@ extern "C" {
260
429
  // Lifecycle //
261
430
  ///////////////
262
431
 
432
+
263
433
  /*
264
434
  * Creates a list-of-lists(-of-lists-of-lists-etc) storage framework for a
265
435
  * matrix.
@@ -267,7 +437,7 @@ extern "C" {
267
437
  * Note: The pointers you pass in for shape and init_val become property of our
268
438
  * new storage. You don't need to free them, and you shouldn't re-use them.
269
439
  */
270
- LIST_STORAGE* nm_list_storage_create(dtype_t dtype, size_t* shape, size_t dim, void* init_val) {
440
+ LIST_STORAGE* nm_list_storage_create(nm::dtype_t dtype, size_t* shape, size_t dim, void* init_val) {
271
441
  LIST_STORAGE* s = ALLOC( LIST_STORAGE );
272
442
 
273
443
  s->dim = dim;
@@ -277,8 +447,13 @@ LIST_STORAGE* nm_list_storage_create(dtype_t dtype, size_t* shape, size_t dim, v
277
447
  s->offset = ALLOC_N(size_t, s->dim);
278
448
  memset(s->offset, 0, s->dim * sizeof(size_t));
279
449
 
280
- s->rows = list::create();
281
- s->default_val = init_val;
450
+ s->rows = nm::list::create();
451
+ if (init_val)
452
+ s->default_val = init_val;
453
+ else {
454
+ DTYPE_TEMPLATE_TABLE(nm::list_storage::init_default, void, LIST_STORAGE*)
455
+ ttable[dtype](s);
456
+ }
282
457
  s->count = 1;
283
458
  s->src = s;
284
459
 
@@ -292,7 +467,7 @@ void nm_list_storage_delete(STORAGE* s) {
292
467
  if (s) {
293
468
  LIST_STORAGE* storage = (LIST_STORAGE*)s;
294
469
  if (storage->count-- == 1) {
295
- list::del( storage->rows, storage->dim - 1 );
470
+ nm::list::del( storage->rows, storage->dim - 1 );
296
471
 
297
472
  xfree(storage->shape);
298
473
  xfree(storage->offset);
@@ -319,12 +494,12 @@ void nm_list_storage_delete_ref(STORAGE* s) {
319
494
  /*
320
495
  * Documentation goes here.
321
496
  */
322
- void nm_list_storage_mark(void* storage_base) {
497
+ void nm_list_storage_mark(STORAGE* storage_base) {
323
498
  LIST_STORAGE* storage = (LIST_STORAGE*)storage_base;
324
499
 
325
- if (storage && storage->dtype == RUBYOBJ) {
500
+ if (storage && storage->dtype == nm::RUBYOBJ) {
326
501
  rb_gc_mark(*((VALUE*)(storage->default_val)));
327
- list::mark(storage->rows, storage->dim - 1);
502
+ nm::list::mark(storage->rows, storage->dim - 1);
328
503
  }
329
504
  }
330
505
 
@@ -342,7 +517,7 @@ static NODE* list_storage_get_single_node(LIST_STORAGE* s, SLICE* slice)
342
517
  NODE* n;
343
518
 
344
519
  for (r = 0; r < s->dim; r++) {
345
- n = list::find(l, s->offset[r] + slice->coords[r]);
520
+ n = nm::list::find(l, s->offset[r] + slice->coords[r]);
346
521
  if (n) l = reinterpret_cast<LIST*>(n->val);
347
522
  else return NULL;
348
523
  }
@@ -537,41 +712,36 @@ VALUE nm_list_map_merged_stored(VALUE left, VALUE right, VALUE init) {
537
712
  // If we are working with a scalar operation
538
713
  if (scalar) nm_list_storage_delete(t);
539
714
 
540
- return Data_Wrap_Struct(CLASS_OF(left), nm_list_storage_mark, nm_delete, result);
715
+ return Data_Wrap_Struct(CLASS_OF(left), nm_mark, nm_delete, result);
541
716
  }
542
717
 
543
718
 
544
719
  /*
545
720
  * Copy a slice of a list matrix into a regular list matrix.
546
721
  */
547
- static LIST* slice_copy(const LIST_STORAGE *src, LIST *src_rows, size_t *coords, size_t *lengths, size_t n) {
722
+ static LIST* slice_copy(const LIST_STORAGE* src, LIST* src_rows, size_t* coords, size_t* lengths, size_t n) {
548
723
 
549
- NODE *src_node;
550
- LIST *dst_rows = NULL;
551
724
  void *val = NULL;
552
725
  int key;
553
726
 
554
- dst_rows = list::create();
555
- src_node = src_rows->first;
727
+ LIST* dst_rows = nm::list::create();
728
+ NODE* src_node = src_rows->first;
556
729
 
557
730
  while (src_node) {
558
731
  key = src_node->key - (src->offset[n] + coords[n]);
559
732
 
560
733
  if (key >= 0 && (size_t)key < lengths[n]) {
561
734
  if (src->dim - n > 1) {
562
- val = slice_copy(src,
563
- reinterpret_cast<LIST*>(src_node->val),
564
- coords,
565
- lengths,
566
- n + 1);
567
-
568
- if (val)
569
- list::insert_with_copy(dst_rows, key, val, sizeof(LIST));
570
-
571
- }
572
- else {
573
- list::insert_with_copy(dst_rows, key, src_node->val, DTYPE_SIZES[src->dtype]);
735
+ val = slice_copy( src,
736
+ reinterpret_cast<LIST*>(src_node->val),
737
+ coords,
738
+ lengths,
739
+ n + 1 );
740
+
741
+ if (val) { nm::list::insert_copy(dst_rows, false, key, val, sizeof(LIST)); }
574
742
  }
743
+
744
+ else nm::list::insert_copy(dst_rows, false, key, src_node->val, DTYPE_SIZES[src->dtype]);
575
745
  }
576
746
 
577
747
  src_node = src_node->next;
@@ -583,7 +753,7 @@ static LIST* slice_copy(const LIST_STORAGE *src, LIST *src_rows, size_t *coords,
583
753
  /*
584
754
  * Documentation goes here.
585
755
  */
586
- void* nm_list_storage_get(STORAGE* storage, SLICE* slice) {
756
+ void* nm_list_storage_get(const STORAGE* storage, SLICE* slice) {
587
757
  LIST_STORAGE* s = (LIST_STORAGE*)storage;
588
758
  LIST_STORAGE* ns = NULL;
589
759
  NODE* n;
@@ -609,7 +779,7 @@ void* nm_list_storage_get(STORAGE* storage, SLICE* slice) {
609
779
  * Get the contents of some set of coordinates. Note: Does not make a copy!
610
780
  * Don't free!
611
781
  */
612
- void* nm_list_storage_ref(STORAGE* storage, SLICE* slice) {
782
+ void* nm_list_storage_ref(const STORAGE* storage, SLICE* slice) {
613
783
  LIST_STORAGE* s = (LIST_STORAGE*)storage;
614
784
  LIST_STORAGE* ns = NULL;
615
785
  NODE* n;
@@ -642,12 +812,63 @@ void* nm_list_storage_ref(STORAGE* storage, SLICE* slice) {
642
812
  }
643
813
  }
644
814
 
815
+
645
816
  /*
646
- * Documentation goes here.
817
+ * Recursive function, sets multiple values in a matrix from a single source value.
818
+ */
819
+ static void slice_set_single(LIST_STORAGE* dest, LIST* l, void* val, size_t* coords, size_t* lengths, size_t n) {
820
+
821
+ // drill down into the structure
822
+ NODE* node = NULL;
823
+ if (dest->dim - n > 1) {
824
+ for (size_t i = 0; i < lengths[n]; ++i) {
825
+
826
+ size_t key = i + dest->offset[n] + coords[n];
827
+
828
+ if (!node) {
829
+ node = nm::list::insert(l, false, key, nm::list::create()); // try to insert list
830
+ } else if (!node->next || (node->next && node->next->key > key)) {
831
+ node = nm::list::insert_after(node, key, nm::list::create());
832
+ } else {
833
+ node = node->next; // correct rank already exists.
834
+ }
835
+
836
+ // cast it to a list and recurse
837
+ slice_set_single(dest, reinterpret_cast<LIST*>(node->val), val, coords, lengths, n + 1);
838
+ }
839
+ } else {
840
+ for (size_t i = 0; i < lengths[n]; ++i) {
841
+
842
+ size_t key = i + dest->offset[n] + coords[n];
843
+
844
+ if (!node) {
845
+ node = nm::list::insert_copy(l, true, key, val, DTYPE_SIZES[dest->dtype]);
846
+ } else {
847
+ node = nm::list::replace_insert_after(node, key, val, true, DTYPE_SIZES[dest->dtype]);
848
+ }
849
+ }
850
+ }
851
+ }
852
+
853
+
854
+
855
+ /*
856
+ * Set a value or values in a list matrix.
857
+ */
858
+ void nm_list_storage_set(VALUE left, SLICE* slice, VALUE right) {
859
+ NAMED_DTYPE_TEMPLATE_TABLE(ttable, nm::list_storage::set, void, VALUE, SLICE*, VALUE)
860
+ ttable[NM_DTYPE(left)](left, slice, right);
861
+ }
862
+
863
+
864
+ /*
865
+ * Insert an entry directly in a row (not using copy! don't free after).
866
+ *
867
+ * Returns a pointer to the insertion location.
647
868
  *
648
869
  * TODO: Allow this function to accept an entire row and not just one value -- for slicing
649
870
  */
650
- void* nm_list_storage_insert(STORAGE* storage, SLICE* slice, void* val) {
871
+ NODE* nm_list_storage_insert(STORAGE* storage, SLICE* slice, void* val) {
651
872
  LIST_STORAGE* s = (LIST_STORAGE*)storage;
652
873
  // Pretend dims = 2
653
874
  // Then coords is going to be size 2
@@ -658,27 +879,23 @@ void* nm_list_storage_insert(STORAGE* storage, SLICE* slice, void* val) {
658
879
 
659
880
  // drill down into the structure
660
881
  for (r = s->dim; r > 1; --r) {
661
- n = list::insert(l, false, s->offset[s->dim - r] + slice->coords[s->dim - r], list::create());
882
+ n = nm::list::insert(l, false, s->offset[s->dim - r] + slice->coords[s->dim - r], nm::list::create());
662
883
  l = reinterpret_cast<LIST*>(n->val);
663
884
  }
664
885
 
665
- n = list::insert(l, true, s->offset[s->dim - r] + slice->coords[s->dim - r], val);
666
- return n->val;
886
+ return nm::list::insert(l, true, s->offset[s->dim - r] + slice->coords[s->dim - r], val);
667
887
  }
668
888
 
669
889
  /*
670
- * Remove an item from list storage.
890
+ * Remove an item or slice from list storage.
671
891
  */
672
- void* nm_list_storage_remove(STORAGE* storage, SLICE* slice) {
892
+ void nm_list_storage_remove(STORAGE* storage, SLICE* slice) {
673
893
  LIST_STORAGE* s = (LIST_STORAGE*)storage;
674
- void* rm = NULL;
675
894
 
676
895
  // This returns a boolean, which will indicate whether s->rows is empty.
677
896
  // We can safely ignore it, since we never want to delete s->rows until
678
897
  // it's time to destroy the LIST_STORAGE object.
679
- list::remove_recursive(s->rows, slice->coords, s->offset, 0, s->dim, rm);
680
-
681
- return rm;
898
+ nm::list::remove_recursive(s->rows, slice->coords, s->offset, slice->lengths, 0, s->dim);
682
899
  }
683
900
 
684
901
  ///////////
@@ -719,7 +936,7 @@ STORAGE* nm_list_storage_matrix_multiply(const STORAGE_PAIR& casted_storage, siz
719
936
  * List storage to Hash conversion. Uses Hashes with default values, so you can continue to pretend
720
937
  * it's a sparse matrix.
721
938
  */
722
- VALUE nm_list_storage_to_hash(const LIST_STORAGE* s, const dtype_t dtype) {
939
+ VALUE nm_list_storage_to_hash(const LIST_STORAGE* s, const nm::dtype_t dtype) {
723
940
 
724
941
  // Get the default value for the list storage.
725
942
  VALUE default_value = rubyobj_from_cval(s->default_val, dtype).rval;
@@ -807,8 +1024,8 @@ LIST_STORAGE* nm_list_storage_copy(const LIST_STORAGE* rhs)
807
1024
  /*
808
1025
  * List storage copy constructor C access with casting.
809
1026
  */
810
- STORAGE* nm_list_storage_cast_copy(const STORAGE* rhs, dtype_t new_dtype, void* dummy) {
811
- NAMED_LR_DTYPE_TEMPLATE_TABLE(ttable, nm::list_storage::cast_copy, LIST_STORAGE*, const LIST_STORAGE* rhs, dtype_t new_dtype);
1027
+ STORAGE* nm_list_storage_cast_copy(const STORAGE* rhs, nm::dtype_t new_dtype, void* dummy) {
1028
+ NAMED_LR_DTYPE_TEMPLATE_TABLE(ttable, nm::list_storage::cast_copy, LIST_STORAGE*, const LIST_STORAGE* rhs, nm::dtype_t new_dtype);
812
1029
 
813
1030
  return (STORAGE*)ttable[new_dtype][rhs->dtype]((LIST_STORAGE*)rhs, new_dtype);
814
1031
  }
@@ -831,7 +1048,7 @@ STORAGE* nm_list_storage_copy_transposed(const STORAGE* rhs_base) {
831
1048
  /////////////////////////
832
1049
 
833
1050
 
834
-
1051
+ namespace nm {
835
1052
  namespace list_storage {
836
1053
 
837
1054
 
@@ -850,14 +1067,14 @@ static LIST_STORAGE* cast_copy(const LIST_STORAGE* rhs, dtype_t new_dtype) {
850
1067
  *default_val = *reinterpret_cast<RDType*>(rhs->default_val);
851
1068
 
852
1069
  LIST_STORAGE* lhs = nm_list_storage_create(new_dtype, shape, rhs->dim, default_val);
853
- //lhs->rows = list::create();
1070
+ //lhs->rows = nm::list::create();
854
1071
 
855
1072
  // TODO: Needs optimization. When matrix is reference it is copped twice.
856
1073
  if (rhs->src == rhs)
857
- list::cast_copy_contents<LDType, RDType>(lhs->rows, rhs->rows, rhs->dim - 1);
1074
+ nm::list::cast_copy_contents<LDType, RDType>(lhs->rows, rhs->rows, rhs->dim - 1);
858
1075
  else {
859
1076
  LIST_STORAGE *tmp = nm_list_storage_copy(rhs);
860
- list::cast_copy_contents<LDType, RDType>(lhs->rows, tmp->rows, rhs->dim - 1);
1077
+ nm::list::cast_copy_contents<LDType, RDType>(lhs->rows, tmp->rows, rhs->dim - 1);
861
1078
  nm_list_storage_delete(tmp);
862
1079
  }
863
1080