nmatrix 0.0.8 → 0.0.9

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