nmatrix 0.0.2 → 0.0.3

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