nmatrix 0.0.2 → 0.0.3

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 (47) hide show
  1. data/Gemfile +1 -1
  2. data/History.txt +31 -3
  3. data/Manifest.txt +5 -0
  4. data/README.rdoc +29 -27
  5. data/ext/nmatrix/binary_format.txt +53 -0
  6. data/ext/nmatrix/data/data.cpp +18 -18
  7. data/ext/nmatrix/data/data.h +38 -7
  8. data/ext/nmatrix/data/rational.h +13 -0
  9. data/ext/nmatrix/data/ruby_object.h +10 -0
  10. data/ext/nmatrix/extconf.rb +2 -0
  11. data/ext/nmatrix/nmatrix.cpp +655 -103
  12. data/ext/nmatrix/nmatrix.h +26 -14
  13. data/ext/nmatrix/ruby_constants.cpp +4 -0
  14. data/ext/nmatrix/ruby_constants.h +2 -0
  15. data/ext/nmatrix/storage/dense.cpp +99 -41
  16. data/ext/nmatrix/storage/dense.h +3 -3
  17. data/ext/nmatrix/storage/list.cpp +36 -14
  18. data/ext/nmatrix/storage/list.h +4 -4
  19. data/ext/nmatrix/storage/storage.cpp +19 -19
  20. data/ext/nmatrix/storage/storage.h +11 -11
  21. data/ext/nmatrix/storage/yale.cpp +17 -20
  22. data/ext/nmatrix/storage/yale.h +13 -11
  23. data/ext/nmatrix/util/io.cpp +25 -23
  24. data/ext/nmatrix/util/io.h +5 -5
  25. data/ext/nmatrix/util/math.cpp +634 -17
  26. data/ext/nmatrix/util/math.h +958 -9
  27. data/ext/nmatrix/util/sl_list.cpp +7 -7
  28. data/ext/nmatrix/util/sl_list.h +2 -2
  29. data/lib/nmatrix.rb +9 -0
  30. data/lib/nmatrix/blas.rb +4 -4
  31. data/lib/nmatrix/io/market.rb +227 -0
  32. data/lib/nmatrix/io/mat_reader.rb +7 -7
  33. data/lib/nmatrix/lapack.rb +80 -0
  34. data/lib/nmatrix/nmatrix.rb +78 -52
  35. data/lib/nmatrix/shortcuts.rb +486 -0
  36. data/lib/nmatrix/version.rb +1 -1
  37. data/spec/2x2_dense_double.mat +0 -0
  38. data/spec/blas_spec.rb +59 -9
  39. data/spec/elementwise_spec.rb +25 -12
  40. data/spec/io_spec.rb +69 -1
  41. data/spec/lapack_spec.rb +53 -4
  42. data/spec/math_spec.rb +9 -0
  43. data/spec/nmatrix_list_spec.rb +95 -0
  44. data/spec/nmatrix_spec.rb +10 -53
  45. data/spec/nmatrix_yale_spec.rb +17 -15
  46. data/spec/shortcuts_spec.rb +154 -0
  47. metadata +22 -15
@@ -112,8 +112,8 @@
112
112
 
113
113
  #ifdef __cplusplus /* These are the C++ versions of the macros. */
114
114
 
115
- #define NM_DECL_ENUM(enum_type, name) enum_type name;
116
- #define NM_DECL_STRUCT(type, name) type name;
115
+ #define NM_DECL_ENUM(enum_type, name) nm::enum_type name
116
+ #define NM_DECL_STRUCT(type, name) type name;
117
117
 
118
118
  #define NM_DEF_STORAGE_ELEMENTS \
119
119
  NM_DECL_ENUM(dtype_t, dtype); \
@@ -135,13 +135,15 @@
135
135
  #define NM_DEF_STRUCT_POST(name) };
136
136
 
137
137
  #define NM_DEF_ENUM(name, ...) \
138
- enum name { \
139
- __VA_ARGS__ \
140
- };
138
+ namespace nm { \
139
+ enum name { \
140
+ __VA_ARGS__ \
141
+ }; \
142
+ } // end of namespace nm
141
143
 
142
144
  #else /* These are the C versions of the macros. */
143
145
 
144
- #define NM_DECL_ENUM(enum_type, name) nm_ ## enum_type name;
146
+ #define NM_DECL_ENUM(enum_type, name) nm_ ## enum_type name
145
147
  #define NM_DECL_STRUCT(type, name) NM_ ## type name;
146
148
 
147
149
  #define NM_DEF_STORAGE_ELEMENTS \
@@ -160,7 +162,6 @@
160
162
  typedef struct NM_STORAGE { \
161
163
  NM_DEF_STORAGE_ELEMENTS; \
162
164
  } NM_STORAGE;
163
- >>>>>>> compile_point
164
165
 
165
166
  #define NM_DEF_STRUCT_PRE(name) typedef struct NM_ ## name {
166
167
  #define NM_DEF_STRUCT_POST(name) } NM_ ## name;
@@ -181,11 +182,9 @@
181
182
  #define NM_NUM_ITYPES 4 // data/data.h
182
183
  #define NM_NUM_STYPES 3 // storage/storage.h
183
184
 
184
- #ifndef __cplusplus
185
+ //#ifdef __cplusplus
185
186
  //namespace nm {
186
- #else
187
-
188
- #endif
187
+ //#endif
189
188
 
190
189
  /* Storage Type -- Dense or Sparse */
191
190
  NM_DEF_ENUM(stype_t, DENSE_STORE = 0,
@@ -213,6 +212,17 @@ NM_DEF_ENUM(itype_t, UINT8 = 0,
213
212
  UINT32 = 2,
214
213
  UINT64 = 3);
215
214
 
215
+ NM_DEF_ENUM(symm_t, NONSYMM = 0,
216
+ SYMM = 1,
217
+ SKEW = 2,
218
+ HERM = 3,
219
+ UPPER = 4,
220
+ LOWER = 5);
221
+
222
+ //#ifdef __cplusplus
223
+ //}; // end of namespace nm
224
+ //#endif
225
+
216
226
  /* struct STORAGE */
217
227
  NM_DEF_STORAGE_STRUCT;
218
228
 
@@ -227,7 +237,7 @@ NM_DEF_STORAGE_CHILD_STRUCT_PRE(YALE_STORAGE);
227
237
  void* a; // should go first
228
238
  size_t ndnz; // Strictly non-diagonal non-zero count!
229
239
  size_t capacity;
230
- itype_t itype;
240
+ NM_DECL_ENUM(itype_t, itype);
231
241
  void* ija;
232
242
  NM_DEF_STORAGE_STRUCT_POST(YALE_STORAGE);
233
243
 
@@ -321,8 +331,10 @@ extern "C" {
321
331
  void Init_nmatrix();
322
332
 
323
333
  // External API
324
- VALUE rb_nmatrix_dense_create(dtype_t dtype, size_t* shape, size_t dim, void* elements, size_t length);
325
- VALUE rb_nvector_dense_create(dtype_t dtype, void* elements, size_t length);
334
+ VALUE rb_nmatrix_dense_create(NM_DECL_ENUM(dtype_t, dtype), size_t* shape, size_t dim, void* elements, size_t length);
335
+ VALUE rb_nvector_dense_create(NM_DECL_ENUM(dtype_t, dtype), void* elements, size_t length);
336
+
337
+ NM_DECL_ENUM(dtype_t, nm_dtype_guess(VALUE)); // (This is a function)
326
338
  }
327
339
 
328
340
  #endif // NMATRIX_H
@@ -68,6 +68,8 @@ ID nm_rb_real,
68
68
  nm_rb_mul,
69
69
  nm_rb_div,
70
70
 
71
+ nm_rb_negate,
72
+
71
73
  nm_rb_percent,
72
74
  nm_rb_gt,
73
75
  nm_rb_lt,
@@ -116,6 +118,8 @@ void nm_init_ruby_constants(void) {
116
118
  nm_rb_mul = rb_intern("*");
117
119
  nm_rb_div = rb_intern("/");
118
120
 
121
+ nm_rb_negate = rb_intern("-@");
122
+
119
123
  nm_rb_percent = rb_intern("%");
120
124
  nm_rb_gt = rb_intern(">");
121
125
  nm_rb_lt = rb_intern("<");
@@ -74,6 +74,8 @@ extern ID nm_rb_real,
74
74
  nm_rb_sub,
75
75
  nm_rb_mul,
76
76
  nm_rb_div,
77
+
78
+ nm_rb_negate,
77
79
 
78
80
  nm_rb_percent,
79
81
  nm_rb_gt,
@@ -56,13 +56,13 @@
56
56
  namespace nm { namespace dense_storage {
57
57
 
58
58
  template <typename LDType, typename RDType>
59
- DENSE_STORAGE* cast_copy(const DENSE_STORAGE* rhs, dtype_t new_dtype);
59
+ DENSE_STORAGE* cast_copy(const DENSE_STORAGE* rhs, nm::dtype_t new_dtype);
60
60
 
61
61
  template <typename LDType, typename RDType>
62
62
  bool eqeq(const DENSE_STORAGE* left, const DENSE_STORAGE* right);
63
63
 
64
64
  template <ewop_t op, typename LDType, typename RDType>
65
- static DENSE_STORAGE* ew_op(const DENSE_STORAGE* left, const DENSE_STORAGE* right);
65
+ static DENSE_STORAGE* ew_op(const DENSE_STORAGE* left, const DENSE_STORAGE* right, const void* rscalar);
66
66
 
67
67
  template <typename DType>
68
68
  static DENSE_STORAGE* matrix_multiply(const STORAGE_PAIR& casted_storage, size_t* resulting_shape, bool vector);
@@ -95,7 +95,7 @@ static void slice_copy(DENSE_STORAGE *dest, const DENSE_STORAGE *src, size_t* le
95
95
  * will be concatenated over and over again into a new elements array. If
96
96
  * elements is NULL, the new elements array will not be initialized.
97
97
  */
98
- DENSE_STORAGE* nm_dense_storage_create(dtype_t dtype, size_t* shape, size_t dim, void* elements, size_t elements_length) {
98
+ DENSE_STORAGE* nm_dense_storage_create(nm::dtype_t dtype, size_t* shape, size_t dim, void* elements, size_t elements_length) {
99
99
  DENSE_STORAGE* s = ALLOC( DENSE_STORAGE );
100
100
 
101
101
  s->dim = dim;
@@ -175,7 +175,7 @@ void nm_dense_storage_delete_ref(STORAGE* s) {
175
175
  void nm_dense_storage_mark(void* storage_base) {
176
176
  DENSE_STORAGE* storage = (DENSE_STORAGE*)storage_base;
177
177
 
178
- if (storage && storage->dtype == RUBYOBJ) {
178
+ if (storage && storage->dtype == nm::RUBYOBJ) {
179
179
  VALUE* els = reinterpret_cast<VALUE*>(storage->elements);
180
180
 
181
181
  for (size_t index = nm_storage_count_max_elements(storage); index-- > 0;) {
@@ -281,10 +281,10 @@ bool nm_dense_storage_eqeq(const STORAGE* left, const STORAGE* right) {
281
281
  * dtype of Complex64 or Complex128 this is the same as testing for symmetry.
282
282
  */
283
283
  bool nm_dense_storage_is_hermitian(const DENSE_STORAGE* mat, int lda) {
284
- if (mat->dtype == COMPLEX64) {
284
+ if (mat->dtype == nm::COMPLEX64) {
285
285
  return nm::dense_storage::is_hermitian<nm::Complex64>(mat, lda);
286
286
 
287
- } else if (mat->dtype == COMPLEX128) {
287
+ } else if (mat->dtype == nm::COMPLEX128) {
288
288
  return nm::dense_storage::is_hermitian<nm::Complex128>(mat, lda);
289
289
 
290
290
  } else {
@@ -306,12 +306,22 @@ bool nm_dense_storage_is_symmetric(const DENSE_STORAGE* mat, int lda) {
306
306
  //////////
307
307
 
308
308
  /*
309
- * Dense element-wise operations.
309
+ * Dense matrix-matrix and matrix-scalar element-wise operations.
310
+ *
311
+ * right or rscalar should be NULL; they should not both be initialized. If right is NULL, it'll use the scalar value instead.
310
312
  */
311
- STORAGE* nm_dense_storage_ew_op(nm::ewop_t op, const STORAGE* left, const STORAGE* right) {
312
- OP_LR_DTYPE_TEMPLATE_TABLE(nm::dense_storage::ew_op, DENSE_STORAGE*, const DENSE_STORAGE* left, const DENSE_STORAGE* right);
313
+ STORAGE* nm_dense_storage_ew_op(nm::ewop_t op, const STORAGE* left, const STORAGE* right, VALUE scalar) {
314
+ OP_LR_DTYPE_TEMPLATE_TABLE(nm::dense_storage::ew_op, DENSE_STORAGE*, const DENSE_STORAGE* left, const DENSE_STORAGE* right, const void*);
315
+
316
+ if (right)
317
+ return ttable[op][left->dtype][right->dtype](reinterpret_cast<const DENSE_STORAGE*>(left), reinterpret_cast<const DENSE_STORAGE*>(right), NULL);
318
+ else {
319
+ nm::dtype_t r_dtype = nm_dtype_guess(scalar);
320
+ void* r_scalar = ALLOCA_N(char, DTYPE_SIZES[r_dtype]);
321
+ rubyval_to_cval(scalar, r_dtype, r_scalar);
313
322
 
314
- return ttable[op][left->dtype][right->dtype](reinterpret_cast<const DENSE_STORAGE*>(left), reinterpret_cast<const DENSE_STORAGE*>(right));
323
+ return ttable[op][left->dtype][r_dtype](reinterpret_cast<const DENSE_STORAGE*>(left), NULL, r_scalar);
324
+ }
315
325
  }
316
326
 
317
327
  /*
@@ -383,8 +393,8 @@ static void slice_copy(DENSE_STORAGE *dest, const DENSE_STORAGE *src, size_t* le
383
393
  /*
384
394
  * Copy dense storage, changing dtype if necessary.
385
395
  */
386
- STORAGE* nm_dense_storage_cast_copy(const STORAGE* rhs, dtype_t new_dtype) {
387
- NAMED_LR_DTYPE_TEMPLATE_TABLE(ttable, nm::dense_storage::cast_copy, DENSE_STORAGE*, const DENSE_STORAGE* rhs, dtype_t new_dtype);
396
+ STORAGE* nm_dense_storage_cast_copy(const STORAGE* rhs, nm::dtype_t new_dtype) {
397
+ NAMED_LR_DTYPE_TEMPLATE_TABLE(ttable, nm::dense_storage::cast_copy, DENSE_STORAGE*, const DENSE_STORAGE* rhs, nm::dtype_t new_dtype);
388
398
 
389
399
  return (STORAGE*)ttable[new_dtype][rhs->dtype]((DENSE_STORAGE*)rhs, new_dtype);
390
400
  }
@@ -572,7 +582,7 @@ bool is_symmetric(const DENSE_STORAGE* mat, int lda) {
572
582
  * Templated dense storage element-wise operations which return the same DType.
573
583
  */
574
584
  template <ewop_t op, typename LDType, typename RDType>
575
- static DENSE_STORAGE* ew_op(const DENSE_STORAGE* left, const DENSE_STORAGE* right) {
585
+ static DENSE_STORAGE* ew_op(const DENSE_STORAGE* left, const DENSE_STORAGE* right, const void* rscalar) {
576
586
  unsigned int count;
577
587
 
578
588
  size_t* new_shape = (size_t*)calloc(left->dim, sizeof(size_t));
@@ -585,46 +595,94 @@ static DENSE_STORAGE* ew_op(const DENSE_STORAGE* left, const DENSE_STORAGE* righ
585
595
  DENSE_STORAGE* result = nm_dense_storage_create(new_dtype, new_shape, left->dim, NULL, 0);
586
596
 
587
597
  LDType* l_elems = reinterpret_cast<LDType*>(left->elements);
588
- RDType* r_elems = reinterpret_cast<RDType*>(right->elements);
589
598
 
590
- if (static_cast<uint8_t>(op) < NUM_NONCOMP_EWOPS) { // use left-dtype
599
+ if (right) { // matrix-matrix operation
600
+ RDType* r_elems = reinterpret_cast<RDType*>(right->elements);
601
+
602
+ if (static_cast<uint8_t>(op) < NUM_NONCOMP_EWOPS) { // use left-dtype
603
+
604
+ for (count = nm_storage_count_max_elements(result); count-- > 0;) {
605
+ reinterpret_cast<LDType*>(result->elements)[count] = ew_op_switch<op,LDType,RDType>(l_elems[count], r_elems[count]);
606
+ }
607
+
608
+ } else { // new_dtype is BYTE: comparison operators
609
+ uint8_t* res_elems = reinterpret_cast<uint8_t*>(result->elements);
610
+
611
+ for (count = nm_storage_count_max_elements(result); count-- > 0;) {
612
+ switch (op) {
613
+ case EW_EQEQ:
614
+ res_elems[count] = l_elems[count] == r_elems[count];
615
+ break;
616
+
617
+ case EW_NEQ:
618
+ res_elems[count] = l_elems[count] != r_elems[count];
619
+ break;
620
+
621
+ case EW_LT:
622
+ res_elems[count] = l_elems[count] < r_elems[count];
623
+ break;
624
+
625
+ case EW_GT:
626
+ res_elems[count] = l_elems[count] > r_elems[count];
627
+ break;
591
628
 
592
- for (count = nm_storage_count_max_elements(result); count-- > 0;) {
593
- reinterpret_cast<LDType*>(result->elements)[count] = ew_op_switch<op,LDType,RDType>(l_elems[count], r_elems[count]);
629
+ case EW_LEQ:
630
+ res_elems[count] = l_elems[count] <= r_elems[count];
631
+ break;
632
+
633
+ case EW_GEQ:
634
+ res_elems[count] = l_elems[count] >= r_elems[count];
635
+ break;
636
+
637
+ default:
638
+ rb_raise(rb_eStandardError, "this should not happen");
639
+ }
640
+ }
594
641
  }
595
642
 
596
- } else { // new_dtype is BYTE: comparison operators
597
- uint8_t* res_elems = reinterpret_cast<uint8_t*>(result->elements);
643
+ } else { // matrix-scalar operation
644
+ const RDType* r_elem = reinterpret_cast<const RDType*>(rscalar);
598
645
 
599
- for (count = nm_storage_count_max_elements(result); count-- > 0;) {
600
- switch (op) {
601
- case EW_EQEQ:
602
- res_elems[count] = l_elems[count] == r_elems[count];
603
- break;
646
+ if (static_cast<uint8_t>(op) < NUM_NONCOMP_EWOPS) { // use left-dtype
604
647
 
605
- case EW_NEQ:
606
- res_elems[count] = l_elems[count] != r_elems[count];
607
- break;
648
+ for (count = nm_storage_count_max_elements(result); count-- > 0;) {
649
+ reinterpret_cast<LDType*>(result->elements)[count] = ew_op_switch<op,LDType,RDType>(l_elems[count], *r_elem);
650
+ }
608
651
 
609
- case EW_LT:
610
- res_elems[count] = l_elems[count] < r_elems[count];
611
- break;
652
+ } else {
653
+ uint8_t* res_elems = reinterpret_cast<uint8_t*>(result->elements);
612
654
 
613
- case EW_GT:
614
- res_elems[count] = l_elems[count] > r_elems[count];
615
- break;
655
+ for (count = nm_storage_count_max_elements(result); count-- > 0;) {
656
+ switch (op) {
657
+ case EW_EQEQ:
658
+ res_elems[count] = l_elems[count] == *r_elem;
659
+ break;
616
660
 
617
- case EW_LEQ:
618
- res_elems[count] = l_elems[count] <= r_elems[count];
619
- break;
661
+ case EW_NEQ:
662
+ res_elems[count] = l_elems[count] != *r_elem;
663
+ break;
620
664
 
621
- case EW_GEQ:
622
- res_elems[count] = l_elems[count] >= r_elems[count];
623
- break;
665
+ case EW_LT:
666
+ res_elems[count] = l_elems[count] < *r_elem;
667
+ break;
624
668
 
625
- default:
626
- rb_raise(rb_eStandardError, "this should not happen");
669
+ case EW_GT:
670
+ res_elems[count] = l_elems[count] > *r_elem;
671
+ break;
672
+
673
+ case EW_LEQ:
674
+ res_elems[count] = l_elems[count] <= *r_elem;
675
+ break;
676
+
677
+ case EW_GEQ:
678
+ res_elems[count] = l_elems[count] >= *r_elem;
679
+ break;
680
+
681
+ default:
682
+ rb_raise(rb_eStandardError, "this should not happen");
683
+ }
627
684
  }
685
+
628
686
  }
629
687
  }
630
688
 
@@ -69,7 +69,7 @@ extern "C" {
69
69
  // Lifecycle //
70
70
  ///////////////
71
71
 
72
- DENSE_STORAGE* nm_dense_storage_create(dtype_t dtype, size_t* shape, size_t dim, void* elements, size_t elements_length);
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
75
  void nm_dense_storage_mark(void*);
@@ -94,7 +94,7 @@ bool nm_dense_storage_is_hermitian(const DENSE_STORAGE* mat, int lda);
94
94
  // Math //
95
95
  //////////
96
96
 
97
- STORAGE* nm_dense_storage_ew_op(nm::ewop_t op, const STORAGE* left, const STORAGE* right);
97
+ STORAGE* nm_dense_storage_ew_op(nm::ewop_t op, const STORAGE* left, const STORAGE* right, VALUE scalar);
98
98
  STORAGE* nm_dense_storage_matrix_multiply(const STORAGE_PAIR& casted_storage, size_t* resulting_shape, bool vector);
99
99
 
100
100
  /////////////
@@ -109,7 +109,7 @@ size_t nm_dense_storage_pos(const DENSE_STORAGE* s, const size_t* coords);
109
109
 
110
110
  DENSE_STORAGE* nm_dense_storage_copy(const DENSE_STORAGE* rhs);
111
111
  STORAGE* nm_dense_storage_copy_transposed(const STORAGE* rhs_base);
112
- STORAGE* nm_dense_storage_cast_copy(const STORAGE* rhs, dtype_t new_dtype);
112
+ STORAGE* nm_dense_storage_cast_copy(const STORAGE* rhs, nm::dtype_t new_dtype);
113
113
 
114
114
  } // end of extern "C" block
115
115
 
@@ -340,27 +340,44 @@ bool nm_list_storage_eqeq(const STORAGE* left, const STORAGE* right) {
340
340
 
341
341
  /*
342
342
  * Element-wise operations for list storage.
343
+ *
344
+ * If a scalar is given, a temporary matrix is created with that scalar as a default value.
343
345
  */
344
- STORAGE* nm_list_storage_ew_op(nm::ewop_t op, const STORAGE* left, const STORAGE* right) {
345
- rb_raise(rb_eNotImpError, "elementwise operations for list storage currently broken");
346
+ STORAGE* nm_list_storage_ew_op(nm::ewop_t op, const STORAGE* left, const STORAGE* right, VALUE scalar) {
347
+ // rb_raise(rb_eNotImpError, "elementwise operations for list storage currently broken");
346
348
 
347
- OP_LR_DTYPE_TEMPLATE_TABLE(nm::list_storage::ew_op, void*, LIST* dest, const LIST* left, const void* l_default, const LIST* right, const void* r_default, const size_t* shape, size_t dim);
349
+ bool cleanup = false;
350
+ LIST_STORAGE *r, *new_l;
351
+ const LIST_STORAGE* l = reinterpret_cast<const LIST_STORAGE*>(left);
352
+
353
+ if (!right) { // need to build a right-hand matrix temporarily, with default value of 'scalar'
354
+
355
+ dtype_t scalar_dtype = nm_dtype_guess(scalar);
356
+ void* scalar_init = rubyobj_to_cval(scalar, scalar_dtype);
357
+
358
+ size_t* shape = ALLOC_N(size_t, l->dim);
359
+ memcpy(shape, left->shape, sizeof(size_t) * l->dim);
360
+
361
+ r = nm_list_storage_create(scalar_dtype, shape, l->dim, scalar_init);
362
+
363
+ cleanup = true;
364
+
365
+ } else {
366
+
367
+ r = reinterpret_cast<LIST_STORAGE*>(const_cast<STORAGE*>(right));
368
+
369
+ }
348
370
 
349
371
  // We may need to upcast our arguments to the same type.
350
- dtype_t new_dtype = Upcast[left->dtype][right->dtype];
372
+ dtype_t new_dtype = Upcast[left->dtype][r->dtype];
351
373
 
352
374
  // Make sure we allocate a byte-storing matrix for comparison operations; otherwise, use the argument dtype (new_dtype)
353
375
  dtype_t result_dtype = static_cast<uint8_t>(op) < NUM_NONCOMP_EWOPS ? new_dtype : BYTE;
354
376
 
355
-
356
-
357
- const LIST_STORAGE* l = reinterpret_cast<const LIST_STORAGE*>(left),
358
- * r = reinterpret_cast<const LIST_STORAGE*>(right);
359
-
360
- LIST_STORAGE* new_l = NULL;
377
+ OP_LR_DTYPE_TEMPLATE_TABLE(nm::list_storage::ew_op, void*, LIST* dest, const LIST* left, const void* l_default, const LIST* right, const void* r_default, const size_t* shape, size_t dim);
361
378
 
362
379
  // Allocate a new shape array for the resulting matrix.
363
- size_t* new_shape = (size_t*)calloc(l->dim, sizeof(size_t));
380
+ size_t* new_shape = ALLOC_N(size_t, l->dim);
364
381
  memcpy(new_shape, left->shape, sizeof(size_t) * l->dim);
365
382
 
366
383
  // Create the result matrix.
@@ -375,14 +392,19 @@ STORAGE* nm_list_storage_ew_op(nm::ewop_t op, const STORAGE* left, const STORAGE
375
392
  new_l = reinterpret_cast<LIST_STORAGE*>(nm_list_storage_cast_copy(l, new_dtype));
376
393
 
377
394
  result->default_val =
378
- ttable[op][new_l->dtype][right->dtype](result->rows, new_l->rows, new_l->default_val, r->rows, r->default_val, result->shape, result->dim);
395
+ ttable[op][new_l->dtype][r->dtype](result->rows, new_l->rows, new_l->default_val, r->rows, r->default_val, result->shape, result->dim);
379
396
 
380
397
  // Delete the temporary left-hand side matrix.
381
398
  nm_list_storage_delete(reinterpret_cast<STORAGE*>(new_l));
382
-
399
+
383
400
  } else {
384
401
  result->default_val =
385
- ttable[op][left->dtype][right->dtype](result->rows, l->rows, l->default_val, r->rows, r->default_val, result->shape, result->dim);
402
+ ttable[op][left->dtype][r->dtype](result->rows, l->rows, l->default_val, r->rows, r->default_val, result->shape, result->dim);
403
+ }
404
+
405
+ // If we created a temporary scalar matrix (for matrix-scalar operations), we now need to delete it.
406
+ if (cleanup) {
407
+ nm_list_storage_delete(reinterpret_cast<STORAGE*>(r));
386
408
  }
387
409
 
388
410
  return result;
@@ -73,7 +73,7 @@ extern "C" {
73
73
  // Lifecycle //
74
74
  ///////////////
75
75
 
76
- LIST_STORAGE* nm_list_storage_create(dtype_t dtype, size_t* shape, size_t dim, void* init_val);
76
+ LIST_STORAGE* nm_list_storage_create(nm::dtype_t dtype, size_t* shape, size_t dim, void* init_val);
77
77
  void nm_list_storage_delete(STORAGE* s);
78
78
  void nm_list_storage_delete_ref(STORAGE* s);
79
79
  void nm_list_storage_mark(void*);
@@ -97,7 +97,7 @@ extern "C" {
97
97
  // Math //
98
98
  //////////
99
99
 
100
- STORAGE* nm_list_storage_ew_op(nm::ewop_t op, const STORAGE* left, const STORAGE* right);
100
+ STORAGE* nm_list_storage_ew_op(nm::ewop_t op, const STORAGE* left, const STORAGE* right, VALUE scalar);
101
101
  STORAGE* nm_list_storage_matrix_multiply(const STORAGE_PAIR& casted_storage, size_t* resulting_shape, bool vector);
102
102
 
103
103
 
@@ -121,8 +121,8 @@ extern "C" {
121
121
 
122
122
  LIST_STORAGE* nm_list_storage_copy(const LIST_STORAGE* rhs);
123
123
  STORAGE* nm_list_storage_copy_transposed(const STORAGE* rhs_base);
124
- STORAGE* nm_list_storage_cast_copy(const STORAGE* rhs, dtype_t new_dtype);
125
- VALUE nm_list_storage_to_hash(const LIST_STORAGE* s, const dtype_t dtype);
124
+ STORAGE* nm_list_storage_cast_copy(const STORAGE* rhs, nm::dtype_t new_dtype);
125
+ VALUE nm_list_storage_to_hash(const LIST_STORAGE* s, const nm::dtype_t dtype);
126
126
 
127
127
  } // end of extern "C" block
128
128