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
@@ -122,10 +122,12 @@ dir_config("atlas")
122
122
  # (substituting in the path of your cblas.h and clapack.h for the path I used). -- JW 8/27/12
123
123
 
124
124
  find_library("lapack", "clapack_dgetrf", "/usr/local/lib", "/usr/local/atlas/lib")
125
+ find_header("clapack.h", "/usr/local/atlas/include")
125
126
  have_header("clapack.h")
126
127
 
127
128
  find_library("cblas", "cblas_dgemm", "/usr/local/lib", "/usr/local/atlas/lib")
128
129
  find_library("atlas", "ATL_dgemmNN", "/usr/local/lib", "/usr/local/atlas/lib", "/usr/lib")
130
+ find_header("cblas.h", "/usr/local/atlas/include")
129
131
  have_header("cblas.h")
130
132
 
131
133
  # Order matters here: ATLAS has to go after LAPACK: http://mail.scipy.org/pipermail/scipy-user/2007-January/010717.html
@@ -39,6 +39,7 @@ extern "C" {
39
39
 
40
40
  #include <ruby.h>
41
41
  #include <algorithm> // std::min
42
+ #include <fstream>
42
43
 
43
44
  /*
44
45
  * Project Includes
@@ -77,6 +78,269 @@ extern "C" {
77
78
  */
78
79
 
79
80
 
81
+ namespace nm {
82
+
83
+ /*
84
+ * Read the shape from a matrix storage file, and ignore any padding.
85
+ *
86
+ * shape should already be allocated before calling this.
87
+ */
88
+ template <typename IType>
89
+ void read_padded_shape(std::ifstream& f, size_t dim, size_t* shape) {
90
+ size_t bytes_read = 0;
91
+
92
+ // Read shape
93
+ for (size_t i = 0; i < dim; ++i) {
94
+ IType s;
95
+ f.read(reinterpret_cast<char*>(&s), sizeof(IType));
96
+ shape[i] = s;
97
+
98
+ bytes_read += sizeof(IType);
99
+ }
100
+
101
+ // Ignore padding
102
+ f.ignore(bytes_read % 8);
103
+ }
104
+
105
+ template <typename IType>
106
+ void write_padded_shape(std::ofstream& f, size_t dim, size_t* shape) {
107
+ size_t bytes_written = 0;
108
+
109
+ // Write shape
110
+ for (size_t i = 0; i < dim; ++i) {
111
+ IType s = shape[i];
112
+ f.write(reinterpret_cast<const char*>(&s), sizeof(IType));
113
+
114
+ bytes_written += sizeof(IType);
115
+ }
116
+
117
+ // Pad with zeros
118
+ while (bytes_written % 8) {
119
+ IType zero = 0;
120
+ f.write(reinterpret_cast<const char*>(&zero), sizeof(IType));
121
+
122
+ bytes_written += sizeof(IType);
123
+ }
124
+ }
125
+
126
+ /*
127
+ * This function is pulled out separately so it can be called for hermitian matrix writing, which also uses it.
128
+ */
129
+ template <typename DType>
130
+ size_t write_padded_dense_elements_upper(std::ofstream& f, DENSE_STORAGE* storage, symm_t symm) {
131
+ // Write upper triangular portion. Assume 2D square matrix.
132
+ DType* elements = reinterpret_cast<DType*>(storage->elements);
133
+ size_t length = storage->shape[0];
134
+
135
+ size_t bytes_written = 0;
136
+
137
+ for (size_t i = 0; i < length; ++i) { // which row are we on?
138
+
139
+ f.write( reinterpret_cast<const char*>( &(elements[ i*(length + 1) ]) ),
140
+ (length - i) * sizeof(DType) );
141
+
142
+ bytes_written += (length - i) * sizeof(DType);
143
+ }
144
+ return bytes_written;
145
+ }
146
+
147
+
148
+ /*
149
+ * We need to specialize for Hermitian matrices. The next six functions accomplish that specialization, basically
150
+ * by ensuring that non-complex matrices cannot read or write hermitians (which would cause big problems).
151
+ */
152
+ template <typename DType>
153
+ size_t write_padded_dense_elements_herm(std::ofstream& f, DENSE_STORAGE* storage, symm_t symm) {
154
+ rb_raise(rb_eArgError, "cannot write a non-complex matrix as hermitian");
155
+ }
156
+
157
+ template <>
158
+ size_t write_padded_dense_elements_herm<Complex64>(std::ofstream& f, DENSE_STORAGE* storage, symm_t symm) {
159
+ return write_padded_dense_elements_upper<Complex64>(f, storage, symm);
160
+ }
161
+
162
+ template <>
163
+ size_t write_padded_dense_elements_herm<Complex128>(std::ofstream& f, DENSE_STORAGE* storage, symm_t symm) {
164
+ return write_padded_dense_elements_upper<Complex128>(f, storage, symm);
165
+ }
166
+
167
+ template <typename DType>
168
+ void read_padded_dense_elements_herm(DType* elements, size_t length) {
169
+ rb_raise(rb_eArgError, "cannot read a non-complex matrix as hermitian");
170
+ }
171
+
172
+ template <>
173
+ void read_padded_dense_elements_herm(Complex64* elements, size_t length) {
174
+ for (size_t i = 0; i < length; ++i) {
175
+ for (size_t j = i+1; j < length; ++j) {
176
+ elements[j * length + i] = elements[i * length + j].conjugate();
177
+ }
178
+ }
179
+ }
180
+
181
+ template <>
182
+ void read_padded_dense_elements_herm(Complex128* elements, size_t length) {
183
+ for (size_t i = 0; i < length; ++i) {
184
+ for (size_t j = i+1; j < length; ++j) {
185
+ elements[j * length + i] = elements[i * length + j].conjugate();
186
+ }
187
+ }
188
+ }
189
+
190
+
191
+ /*
192
+ * Read the elements of a dense storage matrix from a binary file, padded to 64-bits.
193
+ *
194
+ * storage should already be allocated. No initialization necessary.
195
+ */
196
+ template <typename DType>
197
+ void read_padded_dense_elements(std::ifstream& f, DENSE_STORAGE* storage, nm::symm_t symm) {
198
+ size_t bytes_read = 0;
199
+
200
+ if (symm == nm::NONSYMM) {
201
+ // Easy. Simply read the whole elements array.
202
+ size_t length = nm_storage_count_max_elements(reinterpret_cast<STORAGE*>(storage));
203
+ f.read(reinterpret_cast<char*>(storage->elements), length * sizeof(DType) );
204
+
205
+ bytes_read += length * sizeof(DType);
206
+ } else if (symm == LOWER) {
207
+
208
+ // Read lower triangular portion and initialize remainder to 0
209
+ DType* elements = reinterpret_cast<DType*>(storage->elements);
210
+ size_t length = storage->shape[0];
211
+
212
+ for (size_t i = 0; i < length; ++i) { // which row?
213
+
214
+ f.read( reinterpret_cast<char*>(&(elements[i * length])), (i + 1) * sizeof(DType) );
215
+
216
+ // need to zero-fill the rest of the row.
217
+ for (size_t j = i+1; j < length; ++j)
218
+ elements[i * length + j] = 0;
219
+
220
+ bytes_read += (i + 1) * sizeof(DType);
221
+ }
222
+ } else {
223
+
224
+ DType* elements = reinterpret_cast<DType*>(storage->elements);
225
+ size_t length = storage->shape[0];
226
+
227
+ for (size_t i = 0; i < length; ++i) { // which row?
228
+ f.read( reinterpret_cast<char*>(&(elements[i * (length + 1)])), (length - i) * sizeof(DType) );
229
+
230
+ bytes_read += (length - i) * sizeof(DType);
231
+ }
232
+
233
+ if (symm == SYMM) {
234
+ for (size_t i = 0; i < length; ++i) {
235
+ for (size_t j = i+1; j < length; ++j) {
236
+ elements[j * length + i] = elements[i * length + j];
237
+ }
238
+ }
239
+ } else if (symm == SKEW) {
240
+ for (size_t i = 0; i < length; ++i) {
241
+ for (size_t j = i+1; j < length; ++j) {
242
+ elements[j * length + i] = -elements[i * length + j];
243
+ }
244
+ }
245
+ } else if (symm == HERM) {
246
+ read_padded_dense_elements_herm<DType>(elements, length);
247
+
248
+ } else if (symm == UPPER) { // zero-fill the rest of the rows
249
+ for (size_t i = 0; i < length; ++i) {
250
+ for(size_t j = i+1; j < length; ++j) {
251
+ elements[j * length + i] = 0;
252
+ }
253
+ }
254
+ }
255
+
256
+ }
257
+
258
+ // Ignore any padding.
259
+ if (bytes_read % 8) f.ignore(bytes_read % 8);
260
+ }
261
+
262
+
263
+
264
+ template <typename DType, typename IType>
265
+ void write_padded_yale_elements(std::ofstream& f, YALE_STORAGE* storage, size_t length, nm::symm_t symm) {
266
+ if (symm != nm::NONSYMM) rb_raise(rb_eNotImpError, "Yale matrices can only be read/written in full form");
267
+
268
+ // Keep track of bytes written for each of A and IJA so we know how much padding to use.
269
+ size_t bytes_written = length * sizeof(DType);
270
+
271
+ // Write A array
272
+ f.write(reinterpret_cast<const char*>(storage->a), bytes_written);
273
+
274
+ // Padding
275
+ int64_t zero = 0;
276
+ f.write(reinterpret_cast<const char*>(&zero), bytes_written % 8);
277
+
278
+ bytes_written = length * sizeof(IType);
279
+ f.write(reinterpret_cast<const char*>(storage->ija), bytes_written);
280
+
281
+ // More padding
282
+ f.write(reinterpret_cast<const char*>(&zero), bytes_written % 8);
283
+ }
284
+
285
+
286
+ template <typename DType, typename IType>
287
+ void read_padded_yale_elements(std::ifstream& f, YALE_STORAGE* storage, size_t length, nm::symm_t symm) {
288
+ if (symm != NONSYMM) rb_raise(rb_eNotImpError, "Yale matrices can only be read/written in full form");
289
+
290
+ size_t bytes_read = length * sizeof(DType);
291
+ f.read(reinterpret_cast<char*>(storage->a), bytes_read);
292
+
293
+ int64_t padding = 0;
294
+ f.read(reinterpret_cast<char*>(&padding), bytes_read % 8);
295
+
296
+ bytes_read = length * sizeof(IType);
297
+ f.read(reinterpret_cast<char*>(storage->ija), bytes_read);
298
+
299
+ f.read(reinterpret_cast<char*>(&padding), bytes_read % 8);
300
+ }
301
+
302
+
303
+
304
+ /*
305
+ * Write the elements of a dense storage matrix to a binary file, padded to 64-bits.
306
+ */
307
+ template <typename DType>
308
+ void write_padded_dense_elements(std::ofstream& f, DENSE_STORAGE* storage, nm::symm_t symm) {
309
+ size_t bytes_written = 0;
310
+
311
+ if (symm == nm::NONSYMM) {
312
+ // Simply write the whole elements array.
313
+ size_t length = nm_storage_count_max_elements(storage);
314
+ f.write(reinterpret_cast<const char*>(storage->elements), length * sizeof(DType));
315
+
316
+ bytes_written += length * sizeof(DType);
317
+
318
+ } else if (symm == nm::LOWER) {
319
+
320
+ // Write lower triangular portion. Assume 2D square matrix.
321
+ DType* elements = reinterpret_cast<DType*>(storage->elements);
322
+ size_t length = storage->shape[0];
323
+ for (size_t i = 0; i < length; ++i) { // which row?
324
+
325
+ f.write( reinterpret_cast<const char*>( &(elements[i * length]) ),
326
+ (i + 1) * sizeof(DType) );
327
+
328
+ bytes_written += (i + 1) * sizeof(DType);
329
+ }
330
+ } else if (symm == nm::HERM) {
331
+ bytes_written += write_padded_dense_elements_herm<DType>(f, storage, symm);
332
+ } else { // HERM, UPPER, SYMM, SKEW
333
+ bytes_written += write_padded_dense_elements_upper<DType>(f, storage, symm);
334
+ }
335
+
336
+ // Padding
337
+ int64_t zero = 0;
338
+ f.write(reinterpret_cast<const char*>(&zero), bytes_written % 8);
339
+ }
340
+
341
+ } // end of namespace nm
342
+
343
+
80
344
  extern "C" {
81
345
 
82
346
  /*
@@ -87,6 +351,8 @@ static VALUE nm_init(int argc, VALUE* argv, VALUE nm);
87
351
  static VALUE nm_init_copy(VALUE copy, VALUE original);
88
352
  static VALUE nm_init_transposed(VALUE self);
89
353
  static VALUE nm_init_cast_copy(VALUE self, VALUE new_stype_symbol, VALUE new_dtype_symbol);
354
+ static VALUE nm_read(int argc, VALUE* argv, VALUE self);
355
+ static VALUE nm_write(int argc, VALUE* argv, VALUE self);
90
356
  static VALUE nm_to_hash(VALUE self);
91
357
  static VALUE nm_init_yale_from_old_yale(VALUE shape, VALUE dtype, VALUE ia, VALUE ja, VALUE a, VALUE from_dtype, VALUE nm);
92
358
  static VALUE nm_alloc(VALUE klass);
@@ -149,11 +415,10 @@ static VALUE nm_factorize_lu(VALUE self);
149
415
  static VALUE nm_det_exact(VALUE self);
150
416
  static VALUE nm_complex_conjugate_bang(VALUE self);
151
417
 
152
- static dtype_t dtype_guess(VALUE v);
153
- static dtype_t interpret_dtype(int argc, VALUE* argv, stype_t stype);
154
- static void* interpret_initial_value(VALUE arg, dtype_t dtype);
418
+ static nm::dtype_t interpret_dtype(int argc, VALUE* argv, nm::stype_t stype);
419
+ static void* interpret_initial_value(VALUE arg, nm::dtype_t dtype);
155
420
  static size_t* interpret_shape(VALUE arg, size_t* dim);
156
- static stype_t interpret_stype(VALUE arg);
421
+ static nm::stype_t interpret_stype(VALUE arg);
157
422
 
158
423
  /* Singleton methods */
159
424
  static VALUE nm_itype_by_shape(VALUE self, VALUE shape_arg);
@@ -197,13 +462,16 @@ void Init_nmatrix() {
197
462
 
198
463
  rb_define_singleton_method(cNMatrix, "upcast", (METHOD)nm_upcast, 2);
199
464
  rb_define_singleton_method(cNMatrix, "itype_by_shape", (METHOD)nm_itype_by_shape, 1);
200
-
465
+
201
466
  //////////////////////
202
467
  // Instance Methods //
203
468
  //////////////////////
204
469
 
205
470
  rb_define_method(cNMatrix, "initialize", (METHOD)nm_init, -1);
206
471
  rb_define_method(cNMatrix, "initialize_copy", (METHOD)nm_init_copy, 1);
472
+ rb_define_singleton_method(cNMatrix, "read", (METHOD)nm_read, -1);
473
+
474
+ rb_define_method(cNMatrix, "write", (METHOD)nm_write, -1);
207
475
 
208
476
  // Technically, the following function is a copy constructor.
209
477
  rb_define_method(cNMatrix, "transpose", (METHOD)nm_init_transposed, 0);
@@ -317,15 +585,15 @@ static VALUE nm_capacity(VALUE self) {
317
585
  VALUE cap;
318
586
 
319
587
  switch(NM_STYPE(self)) {
320
- case YALE_STORE:
588
+ case nm::YALE_STORE:
321
589
  cap = UINT2NUM(((YALE_STORAGE*)(NM_STORAGE(self)))->capacity);
322
590
  break;
323
591
 
324
- case DENSE_STORE:
592
+ case nm::DENSE_STORE:
325
593
  cap = UINT2NUM(nm_storage_count_max_elements( NM_STORAGE_DENSE(self) ));
326
594
  break;
327
595
 
328
- case LIST_STORE:
596
+ case nm::LIST_STORE:
329
597
  cap = UINT2NUM(nm_list_storage_count_elements( NM_STORAGE_LIST(self) ));
330
598
  break;
331
599
 
@@ -375,7 +643,7 @@ static VALUE nm_dtype(VALUE self) {
375
643
  * Get the index data type (dtype) of a matrix. Defined only for yale; others return nil.
376
644
  */
377
645
  static VALUE nm_itype(VALUE self) {
378
- if (NM_STYPE(self) == YALE_STORE) {
646
+ if (NM_STYPE(self) == nm::YALE_STORE) {
379
647
  ID itype = rb_intern(ITYPE_NAMES[NM_ITYPE(self)]);
380
648
  return ID2SYM(itype);
381
649
  }
@@ -391,8 +659,8 @@ static VALUE nm_itype_by_shape(VALUE self, VALUE shape_arg) {
391
659
  size_t dim;
392
660
  size_t* shape = interpret_shape(shape_arg, &dim);
393
661
 
394
- itype_t itype = nm_yale_storage_itype_by_shape(shape);
395
- ID itype_id = rb_intern(ITYPE_NAMES[itype]);
662
+ nm::itype_t itype = nm_yale_storage_itype_by_shape(shape);
663
+ ID itype_id = rb_intern(ITYPE_NAMES[itype]);
396
664
 
397
665
  return ID2SYM(itype_id);
398
666
  }
@@ -405,8 +673,8 @@ static VALUE nm_itype_by_shape(VALUE self, VALUE shape_arg) {
405
673
  */
406
674
  static VALUE nm_upcast(VALUE self, VALUE t1, VALUE t2) {
407
675
 
408
- dtype_t d1 = nm_dtype_from_rbsymbol(t1),
409
- d2 = nm_dtype_from_rbsymbol(t2);
676
+ nm::dtype_t d1 = nm_dtype_from_rbsymbol(t1),
677
+ d2 = nm_dtype_from_rbsymbol(t2);
410
678
 
411
679
  return ID2SYM(rb_intern( DTYPE_NAMES[ Upcast[d1][d2] ] ));
412
680
  }
@@ -453,7 +721,7 @@ static VALUE nm_dense_each_indirect(VALUE nm) {
453
721
  static VALUE nm_dense_each(VALUE nmatrix) {
454
722
  volatile VALUE nm = nmatrix; // Not sure this actually does anything.
455
723
 
456
- if (NM_DTYPE(nm) == RUBYOBJ) {
724
+ if (NM_DTYPE(nm) == nm::RUBYOBJ) {
457
725
 
458
726
  // matrix of Ruby objects -- yield those objects directly
459
727
  return nm_dense_each_direct(nm);
@@ -476,7 +744,7 @@ static VALUE nm_each(VALUE nmatrix) {
476
744
  volatile VALUE nm = nmatrix; // not sure why we do this, but it gets done in ruby's array.c.
477
745
 
478
746
  switch(NM_STYPE(nm)) {
479
- case DENSE_STORE:
747
+ case nm::DENSE_STORE:
480
748
  return nm_dense_each(nm);
481
749
  default:
482
750
  rb_raise(rb_eNotImpError, "only dense matrix's each method works right now");
@@ -508,13 +776,13 @@ static VALUE nm_eqeq(VALUE left, VALUE right) {
508
776
  bool result = false;
509
777
 
510
778
  switch(l->stype) {
511
- case DENSE_STORE:
779
+ case nm::DENSE_STORE:
512
780
  result = nm_dense_storage_eqeq(l->storage, r->storage);
513
781
  break;
514
- case LIST_STORE:
782
+ case nm::LIST_STORE:
515
783
  result = nm_list_storage_eqeq(l->storage, r->storage);
516
784
  break;
517
- case YALE_STORE:
785
+ case nm::YALE_STORE:
518
786
  result = nm_yale_storage_eqeq(l->storage, r->storage);
519
787
  break;
520
788
  }
@@ -558,12 +826,12 @@ static VALUE nm_complex_conjugate_bang(VALUE self) {
558
826
 
559
827
  UnwrapNMatrix(self, m);
560
828
 
561
- if (m->stype == DENSE_STORE) {
829
+ if (m->stype == nm::DENSE_STORE) {
562
830
 
563
831
  size = nm_storage_count_max_elements(NM_STORAGE(self));
564
832
  elem = NM_STORAGE_DENSE(self)->elements;
565
833
 
566
- } else if (m->stype == YALE_STORE) {
834
+ } else if (m->stype == nm::YALE_STORE) {
567
835
 
568
836
  size = nm_yale_storage_get_size(NM_STORAGE_YALE(self));
569
837
  elem = NM_STORAGE_YALE(self)->a;
@@ -573,13 +841,13 @@ static VALUE nm_complex_conjugate_bang(VALUE self) {
573
841
  }
574
842
 
575
843
  // Walk through and negate the imaginary component
576
- if (NM_DTYPE(self) == COMPLEX64) {
844
+ if (NM_DTYPE(self) == nm::COMPLEX64) {
577
845
 
578
846
  for (p = 0; p < size; ++p) {
579
847
  reinterpret_cast<nm::Complex64*>(elem)[p].i = -reinterpret_cast<nm::Complex64*>(elem)[p].i;
580
848
  }
581
849
 
582
- } else if (NM_DTYPE(self) == COMPLEX128) {
850
+ } else if (NM_DTYPE(self) == nm::COMPLEX128) {
583
851
 
584
852
  for (p = 0; p < size; ++p) {
585
853
  reinterpret_cast<nm::Complex128*>(elem)[p].i = -reinterpret_cast<nm::Complex128*>(elem)[p].i;
@@ -597,7 +865,7 @@ static VALUE nm_complex_conjugate_bang(VALUE self) {
597
865
  * Helper function for creating a matrix. You have to create the storage and pass it in, but you don't
598
866
  * need to worry about deleting it.
599
867
  */
600
- NMATRIX* nm_create(stype_t stype, STORAGE* storage) {
868
+ NMATRIX* nm_create(nm::stype_t stype, STORAGE* storage) {
601
869
  NMATRIX* mat = ALLOC(NMATRIX);
602
870
 
603
871
  mat->stype = stype;
@@ -646,11 +914,11 @@ static VALUE nm_init(int argc, VALUE* argv, VALUE nm) {
646
914
  }
647
915
 
648
916
  /* First, determine stype (dense by default) */
649
- stype_t stype;
917
+ nm::stype_t stype;
650
918
  size_t offset = 0;
651
919
 
652
920
  if (!SYMBOL_P(argv[0]) && TYPE(argv[0]) != T_STRING) {
653
- stype = DENSE_STORE;
921
+ stype = nm::DENSE_STORE;
654
922
 
655
923
  } else {
656
924
  // 0: String or Symbol
@@ -660,7 +928,7 @@ static VALUE nm_init(int argc, VALUE* argv, VALUE nm) {
660
928
 
661
929
  // If there are 7 arguments and Yale, refer to a different init function with fewer sanity checks.
662
930
  if (argc == 7) {
663
- if (stype == YALE_STORE) {
931
+ if (stype == nm::YALE_STORE) {
664
932
  return nm_init_yale_from_old_yale(argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], nm);
665
933
 
666
934
  } else {
@@ -673,14 +941,14 @@ static VALUE nm_init(int argc, VALUE* argv, VALUE nm) {
673
941
  size_t* shape = interpret_shape(argv[offset], &dim);
674
942
 
675
943
  // 2-3: dtype
676
- dtype_t dtype = interpret_dtype(argc-1-offset, argv+offset+1, stype);
944
+ nm::dtype_t dtype = interpret_dtype(argc-1-offset, argv+offset+1, stype);
677
945
 
678
946
  size_t init_cap = 0, init_val_len = 0;
679
947
  void* init_val = NULL;
680
948
  if (NM_RUBYVAL_IS_NUMERIC(argv[1+offset]) || TYPE(argv[1+offset]) == T_ARRAY) {
681
949
  // Initial value provided (could also be initial capacity, if yale).
682
950
 
683
- if (stype == YALE_STORE) {
951
+ if (stype == nm::YALE_STORE) {
684
952
  init_cap = FIX2UINT(argv[1+offset]);
685
953
 
686
954
  } else {
@@ -694,12 +962,12 @@ static VALUE nm_init(int argc, VALUE* argv, VALUE nm) {
694
962
  } else {
695
963
  // DType is RUBYOBJ.
696
964
 
697
- if (stype == DENSE_STORE) {
965
+ if (stype == nm::DENSE_STORE) {
698
966
  /*
699
967
  * No need to initialize dense with any kind of default value unless it's
700
968
  * an RUBYOBJ matrix.
701
969
  */
702
- if (dtype == RUBYOBJ) {
970
+ if (dtype == nm::RUBYOBJ) {
703
971
  // Pretend [nil] was passed for RUBYOBJ.
704
972
  init_val = ALLOC(VALUE);
705
973
  *(VALUE*)init_val = Qnil;
@@ -709,7 +977,7 @@ static VALUE nm_init(int argc, VALUE* argv, VALUE nm) {
709
977
  } else {
710
978
  init_val = NULL;
711
979
  }
712
- } else if (stype == LIST_STORE) {
980
+ } else if (stype == nm::LIST_STORE) {
713
981
  init_val = ALLOC_N(char, DTYPE_SIZES[dtype]);
714
982
  std::memset(init_val, 0, DTYPE_SIZES[dtype]);
715
983
  }
@@ -722,15 +990,15 @@ static VALUE nm_init(int argc, VALUE* argv, VALUE nm) {
722
990
  nmatrix->stype = stype;
723
991
 
724
992
  switch (stype) {
725
- case DENSE_STORE:
993
+ case nm::DENSE_STORE:
726
994
  nmatrix->storage = (STORAGE*)nm_dense_storage_create(dtype, shape, dim, init_val, init_val_len);
727
995
  break;
728
996
 
729
- case LIST_STORE:
997
+ case nm::LIST_STORE:
730
998
  nmatrix->storage = (STORAGE*)nm_list_storage_create(dtype, shape, dim, init_val);
731
999
  break;
732
1000
 
733
- case YALE_STORE:
1001
+ case nm::YALE_STORE:
734
1002
  nmatrix->storage = (STORAGE*)nm_yale_storage_create(dtype, shape, dim, init_cap);
735
1003
  nm_yale_storage_init((YALE_STORAGE*)(nmatrix->storage));
736
1004
  break;
@@ -746,7 +1014,7 @@ static VALUE nm_init(int argc, VALUE* argv, VALUE nm) {
746
1014
  * Currently only works for list storage.
747
1015
  */
748
1016
  static VALUE nm_to_hash(VALUE self) {
749
- if (NM_STYPE(self) != LIST_STORE) {
1017
+ if (NM_STYPE(self) != nm::LIST_STORE) {
750
1018
  rb_raise(rb_eNotImpError, "please cast to :list first");
751
1019
  }
752
1020
 
@@ -758,8 +1026,8 @@ static VALUE nm_to_hash(VALUE self) {
758
1026
  * Copy constructor for changing dtypes and stypes.
759
1027
  */
760
1028
  static VALUE nm_init_cast_copy(VALUE self, VALUE new_stype_symbol, VALUE new_dtype_symbol) {
761
- dtype_t new_dtype = nm_dtype_from_rbsymbol(new_dtype_symbol);
762
- stype_t new_stype = nm_stype_from_rbsymbol(new_stype_symbol);
1029
+ nm::dtype_t new_dtype = nm_dtype_from_rbsymbol(new_dtype_symbol);
1030
+ nm::stype_t new_stype = nm_stype_from_rbsymbol(new_stype_symbol);
763
1031
 
764
1032
  CheckNMatrixType(self);
765
1033
 
@@ -822,6 +1090,278 @@ static VALUE nm_init_copy(VALUE copy, VALUE original) {
822
1090
  }
823
1091
 
824
1092
 
1093
+ /*
1094
+ * Get major, minor, and release components of NMatrix::VERSION. Store in function parameters.
1095
+ */
1096
+ static void get_version_info(uint16_t& major, uint16_t& minor, uint16_t& release) {
1097
+ // Get VERSION and split it on periods. Result is an Array.
1098
+ VALUE version = rb_funcall(rb_const_get(cNMatrix, rb_intern("VERSION")), rb_intern("split"), 1, rb_str_new_cstr("."));
1099
+ VALUE* ary = RARRAY_PTR(version); // major, minor, and release
1100
+
1101
+ // Convert each to an integer
1102
+ VALUE maj = rb_funcall(ary[0], rb_intern("to_i"), 0);
1103
+ VALUE min = rb_funcall(ary[1], rb_intern("to_i"), 0);
1104
+ VALUE rel = rb_funcall(ary[2], rb_intern("to_i"), 0);
1105
+
1106
+ major = static_cast<uint16_t>(nm::RubyObject(maj));
1107
+ minor = static_cast<uint16_t>(nm::RubyObject(min));
1108
+ release = static_cast<uint16_t>(nm::RubyObject(rel));
1109
+ }
1110
+
1111
+
1112
+ /*
1113
+ * Interpret the NMatrix::write symmetry argument (which should be nil or a symbol). Return a symm_t (enum).
1114
+ */
1115
+ static nm::symm_t interpret_symm(VALUE symm) {
1116
+ if (symm == Qnil) return nm::NONSYMM;
1117
+
1118
+ ID rb_symm = rb_intern("symmetric"),
1119
+ rb_skew = rb_intern("skew"),
1120
+ rb_herm = rb_intern("hermitian");
1121
+ // nm_rb_upper, nm_rb_lower already set
1122
+
1123
+ ID symm_id = rb_to_id(symm);
1124
+
1125
+ if (symm_id == rb_symm) return nm::SYMM;
1126
+ else if (symm_id == rb_skew) return nm::SKEW;
1127
+ else if (symm_id == rb_herm) return nm::HERM;
1128
+ else if (symm_id == nm_rb_upper) return nm::UPPER;
1129
+ else if (symm_id == nm_rb_lower) return nm::LOWER;
1130
+ else rb_raise(rb_eArgError, "unrecognized symmetry argument");
1131
+
1132
+ return nm::NONSYMM;
1133
+ }
1134
+
1135
+
1136
+
1137
+ void read_padded_shape(std::ifstream& f, size_t dim, size_t* shape, nm::itype_t itype) {
1138
+ NAMED_ITYPE_TEMPLATE_TABLE(ttable, nm::read_padded_shape, void, std::ifstream&, size_t, size_t*)
1139
+
1140
+ ttable[itype](f, dim, shape);
1141
+ }
1142
+
1143
+
1144
+ void write_padded_shape(std::ofstream& f, size_t dim, size_t* shape, nm::itype_t itype) {
1145
+ NAMED_ITYPE_TEMPLATE_TABLE(ttable, nm::write_padded_shape, void, std::ofstream&, size_t, size_t*)
1146
+
1147
+ ttable[itype](f, dim, shape);
1148
+ }
1149
+
1150
+
1151
+ void read_padded_yale_elements(std::ifstream& f, YALE_STORAGE* storage, size_t length, nm::symm_t symm, nm::dtype_t dtype, nm::itype_t itype) {
1152
+ NAMED_LI_DTYPE_TEMPLATE_TABLE_NO_ROBJ(ttable, nm::read_padded_yale_elements, void, std::ifstream&, YALE_STORAGE*, size_t, nm::symm_t)
1153
+
1154
+ ttable[dtype][itype](f, storage, length, symm);
1155
+ }
1156
+
1157
+
1158
+ void write_padded_yale_elements(std::ofstream& f, YALE_STORAGE* storage, size_t length, nm::symm_t symm, nm::dtype_t dtype, nm::itype_t itype) {
1159
+ NAMED_LI_DTYPE_TEMPLATE_TABLE_NO_ROBJ(ttable, nm::write_padded_yale_elements, void, std::ofstream& f, YALE_STORAGE*, size_t, nm::symm_t)
1160
+
1161
+ ttable[dtype][itype](f, storage, length, symm);
1162
+ }
1163
+
1164
+
1165
+ void read_padded_dense_elements(std::ifstream& f, DENSE_STORAGE* storage, nm::symm_t symm, nm::dtype_t dtype) {
1166
+ NAMED_DTYPE_TEMPLATE_TABLE_NO_ROBJ(ttable, nm::read_padded_dense_elements, void, std::ifstream&, DENSE_STORAGE*, nm::symm_t)
1167
+
1168
+ ttable[dtype](f, storage, symm);
1169
+ }
1170
+
1171
+
1172
+ void write_padded_dense_elements(std::ofstream& f, DENSE_STORAGE* storage, nm::symm_t symm, nm::dtype_t dtype) {
1173
+ NAMED_DTYPE_TEMPLATE_TABLE_NO_ROBJ(ttable, nm::write_padded_dense_elements, void, std::ofstream& f, DENSE_STORAGE*, nm::symm_t)
1174
+
1175
+ ttable[dtype](f, storage, symm);
1176
+ }
1177
+
1178
+
1179
+
1180
+ /*
1181
+ * Binary file writer for NMatrix standard format. file should be a path, which we aren't going to
1182
+ * check very carefully (in other words, this function should generally be called from a Ruby
1183
+ * helper method). Function also takes a symmetry argument, which allows us to specify that we only want to
1184
+ * save the upper triangular portion of the matrix (or if the matrix is a lower triangular matrix, only
1185
+ * the lower triangular portion). nil means regular storage.
1186
+ */
1187
+ static VALUE nm_write(int argc, VALUE* argv, VALUE self) {
1188
+ using std::ofstream;
1189
+
1190
+ if (argc < 1 || argc > 2) {
1191
+ rb_raise(rb_eArgError, "Expected one or two arguments");
1192
+ }
1193
+ VALUE file = argv[0],
1194
+ symm = argc == 1 ? Qnil : argv[1];
1195
+
1196
+ NMATRIX* nmatrix;
1197
+ UnwrapNMatrix( self, nmatrix );
1198
+
1199
+ nm::symm_t symm_ = interpret_symm(symm);
1200
+ nm::itype_t itype = (nmatrix->stype == nm::YALE_STORE) ? reinterpret_cast<YALE_STORAGE*>(nmatrix->storage)->itype : nm::UINT32;
1201
+
1202
+ if (nmatrix->storage->dtype == nm::RUBYOBJ) {
1203
+ rb_raise(rb_eNotImpError, "Ruby Object writing is not implemented yet");
1204
+ }
1205
+
1206
+ // Get the dtype, stype, itype, and symm and ensure they're the correct number of bytes.
1207
+ uint8_t st = static_cast<uint8_t>(nmatrix->stype),
1208
+ dt = static_cast<uint8_t>(nmatrix->storage->dtype),
1209
+ sm = static_cast<uint8_t>(symm_),
1210
+ it = static_cast<uint8_t>(itype);
1211
+ uint16_t dim = nmatrix->storage->dim;
1212
+
1213
+ // Check arguments before starting to write.
1214
+ if (nmatrix->stype == nm::LIST_STORE) rb_raise(nm_eStorageTypeError, "cannot save list matrix; cast to yale or dense first");
1215
+ if (symm_ != nm::NONSYMM) {
1216
+ if (dim != 2) rb_raise(rb_eArgError, "symmetry/triangularity not defined for a non-2D matrix");
1217
+ if (nmatrix->storage->shape[0] != nmatrix->storage->shape[1])
1218
+ rb_raise(rb_eArgError, "symmetry/triangularity not defined for a non-square matrix");
1219
+ if (symm_ == nm::HERM &&
1220
+ dt != static_cast<uint8_t>(nm::COMPLEX64) && dt != static_cast<uint8_t>(nm::COMPLEX128) && dt != static_cast<uint8_t>(nm::RUBYOBJ))
1221
+ rb_raise(rb_eArgError, "cannot save a non-complex matrix as hermitian");
1222
+ }
1223
+
1224
+ ofstream f(RSTRING_PTR(file), std::ios::out | std::ios::binary);
1225
+
1226
+ // Get the NMatrix version information.
1227
+ uint16_t major, minor, release, null16 = 0;
1228
+ get_version_info(major, minor, release);
1229
+
1230
+ // WRITE FIRST 64-BIT BLOCK
1231
+ f.write(reinterpret_cast<const char*>(&major), sizeof(uint16_t));
1232
+ f.write(reinterpret_cast<const char*>(&minor), sizeof(uint16_t));
1233
+ f.write(reinterpret_cast<const char*>(&release), sizeof(uint16_t));
1234
+ f.write(reinterpret_cast<const char*>(&null16), sizeof(uint16_t));
1235
+
1236
+ // WRITE SECOND 64-BIT BLOCK
1237
+ f.write(reinterpret_cast<const char*>(&dt), sizeof(uint8_t));
1238
+ f.write(reinterpret_cast<const char*>(&st), sizeof(uint8_t));
1239
+ f.write(reinterpret_cast<const char*>(&it), sizeof(uint8_t));
1240
+ f.write(reinterpret_cast<const char*>(&sm), sizeof(uint8_t));
1241
+ f.write(reinterpret_cast<const char*>(&null16), sizeof(uint16_t));
1242
+ f.write(reinterpret_cast<const char*>(&dim), sizeof(uint16_t));
1243
+
1244
+ // Write shape (in 64-bit blocks)
1245
+ write_padded_shape(f, nmatrix->storage->dim, nmatrix->storage->shape, itype);
1246
+
1247
+ if (nmatrix->stype == nm::DENSE_STORE) {
1248
+ write_padded_dense_elements(f, reinterpret_cast<DENSE_STORAGE*>(nmatrix->storage), symm_, nmatrix->storage->dtype);
1249
+ } else if (nmatrix->stype == nm::YALE_STORE) {
1250
+ YALE_STORAGE* s = reinterpret_cast<YALE_STORAGE*>(nmatrix->storage);
1251
+ uint32_t ndnz = s->ndnz,
1252
+ length = nm_yale_storage_get_size(s);
1253
+ f.write(reinterpret_cast<const char*>(&ndnz), sizeof(uint32_t));
1254
+ f.write(reinterpret_cast<const char*>(&length), sizeof(uint32_t));
1255
+
1256
+ write_padded_yale_elements(f, s, length, symm_, s->dtype, itype);
1257
+ }
1258
+
1259
+ f.close();
1260
+
1261
+ return Qtrue;
1262
+ }
1263
+
1264
+
1265
+ /*
1266
+ * Binary file reader for NMatrix standard format. file should be a path, which we aren't going to
1267
+ * check very carefully (in other words, this function should generally be called from a Ruby
1268
+ * helper method).
1269
+ *
1270
+ * Note that currently, this function will by default refuse to read files that are newer than
1271
+ * your version of NMatrix. To force an override, set the second argument to anything other than nil.
1272
+ *
1273
+ * Returns an NMatrix Ruby object.
1274
+ */
1275
+ static VALUE nm_read(int argc, VALUE* argv, VALUE self) {
1276
+ using std::ifstream;
1277
+
1278
+ // Read the arguments
1279
+ if (argc < 1 || argc > 2) {
1280
+ rb_raise(rb_eArgError, "expected one or two arguments");
1281
+ }
1282
+ VALUE file = argv[0];
1283
+ bool force = argc == 1 ? false : argv[1];
1284
+
1285
+ // Open a file stream
1286
+ ifstream f(RSTRING_PTR(file), std::ios::in | std::ios::binary);
1287
+
1288
+ uint16_t major, minor, release;
1289
+ get_version_info(major, minor, release); // compare to NMatrix version
1290
+
1291
+ uint16_t fmajor, fminor, frelease, null16;
1292
+
1293
+ // READ FIRST 64-BIT BLOCK
1294
+ f.read(reinterpret_cast<char*>(&fmajor), sizeof(uint16_t));
1295
+ f.read(reinterpret_cast<char*>(&fminor), sizeof(uint16_t));
1296
+ f.read(reinterpret_cast<char*>(&frelease), sizeof(uint16_t));
1297
+ f.read(reinterpret_cast<char*>(&null16), sizeof(uint16_t));
1298
+
1299
+ int ver = major * 10000 + minor * 100 + release,
1300
+ fver = fmajor * 10000 + fminor * 100 + release;
1301
+ if (fver > ver && force == false) {
1302
+ rb_raise(rb_eIOError, "File was created in newer version of NMatrix than current");
1303
+ }
1304
+ if (null16 != 0) fprintf(stderr, "Warning: Expected zero padding was not zero\n");
1305
+
1306
+ uint8_t dt, st, it, sm;
1307
+ uint16_t dim;
1308
+
1309
+ // READ SECOND 64-BIT BLOCK
1310
+ f.read(reinterpret_cast<char*>(&dt), sizeof(uint8_t));
1311
+ f.read(reinterpret_cast<char*>(&st), sizeof(uint8_t));
1312
+ f.read(reinterpret_cast<char*>(&it), sizeof(uint8_t));
1313
+ f.read(reinterpret_cast<char*>(&sm), sizeof(uint8_t));
1314
+ f.read(reinterpret_cast<char*>(&null16), sizeof(uint16_t));
1315
+ f.read(reinterpret_cast<char*>(&dim), sizeof(uint16_t));
1316
+
1317
+ if (null16 != 0) fprintf(stderr, "Warning: Expected zero padding was not zero\n");
1318
+ nm::stype_t stype = static_cast<nm::stype_t>(st);
1319
+ nm::dtype_t dtype = static_cast<nm::dtype_t>(dt);
1320
+ nm::symm_t symm = static_cast<nm::symm_t>(sm);
1321
+ nm::itype_t itype = static_cast<nm::itype_t>(it);
1322
+
1323
+ // READ NEXT FEW 64-BIT BLOCKS
1324
+ size_t* shape = ALLOC_N(size_t, dim);
1325
+ read_padded_shape(f, dim, shape, itype);
1326
+
1327
+ VALUE klass = dim == 1 ? cNVector : cNMatrix;
1328
+
1329
+ STORAGE* s;
1330
+ if (stype == nm::DENSE_STORE) {
1331
+ s = nm_dense_storage_create(dtype, shape, dim, NULL, 0);
1332
+
1333
+ read_padded_dense_elements(f, reinterpret_cast<DENSE_STORAGE*>(s), symm, dtype);
1334
+
1335
+ } else if (stype == nm::YALE_STORE) {
1336
+ uint32_t ndnz, length;
1337
+
1338
+ // READ YALE-SPECIFIC 64-BIT BLOCK
1339
+ f.read(reinterpret_cast<char*>(&ndnz), sizeof(uint32_t));
1340
+ f.read(reinterpret_cast<char*>(&length), sizeof(uint32_t));
1341
+
1342
+ s = nm_yale_storage_create(dtype, shape, dim, length); // set length as init capacity
1343
+
1344
+ read_padded_yale_elements(f, reinterpret_cast<YALE_STORAGE*>(s), length, symm, dtype, itype);
1345
+ } else {
1346
+ rb_raise(nm_eStorageTypeError, "please convert to yale or dense before saving");
1347
+ }
1348
+
1349
+ NMATRIX* nm = nm_create(stype, s);
1350
+
1351
+ // Return the appropriate matrix object (Ruby VALUE)
1352
+ switch(stype) {
1353
+ case nm::DENSE_STORE:
1354
+ return Data_Wrap_Struct(klass, nm_dense_storage_mark, nm_delete, nm);
1355
+ case nm::YALE_STORE:
1356
+ return Data_Wrap_Struct(cNMatrix, nm_yale_storage_mark, nm_delete, nm);
1357
+ default:
1358
+ return Qnil;
1359
+ }
1360
+
1361
+ }
1362
+
1363
+
1364
+
825
1365
  /*
826
1366
  * Create a new NMatrix helper for handling internal ia, ja, and a arguments.
827
1367
  *
@@ -831,16 +1371,16 @@ static VALUE nm_init_copy(VALUE copy, VALUE original) {
831
1371
  static VALUE nm_init_yale_from_old_yale(VALUE shape, VALUE dtype, VALUE ia, VALUE ja, VALUE a, VALUE from_dtype, VALUE nm) {
832
1372
  size_t dim = 2;
833
1373
  size_t* shape_ = interpret_shape(shape, &dim);
834
- dtype_t dtype_ = nm_dtype_from_rbsymbol(dtype);
1374
+ nm::dtype_t dtype_ = nm_dtype_from_rbsymbol(dtype);
835
1375
  char *ia_ = RSTRING_PTR(ia),
836
1376
  *ja_ = RSTRING_PTR(ja),
837
1377
  *a_ = RSTRING_PTR(a);
838
- dtype_t from_dtype_ = nm_dtype_from_rbsymbol(from_dtype);
1378
+ nm::dtype_t from_dtype_ = nm_dtype_from_rbsymbol(from_dtype);
839
1379
  NMATRIX* nmatrix;
840
1380
 
841
1381
  UnwrapNMatrix( nm, nmatrix );
842
1382
 
843
- nmatrix->stype = YALE_STORE;
1383
+ nmatrix->stype = nm::YALE_STORE;
844
1384
  nmatrix->storage = (STORAGE*)nm_yale_storage_create_from_old_yale(dtype_, shape_, ia_, ja_, a_, from_dtype_);
845
1385
 
846
1386
  return nm;
@@ -851,11 +1391,11 @@ static VALUE nm_init_yale_from_old_yale(VALUE shape, VALUE dtype, VALUE ia, VALU
851
1391
  */
852
1392
  static VALUE nm_is_ref(VALUE self) {
853
1393
  // Refs only allowed for dense and list matrices.
854
- if (NM_STYPE(self) == DENSE_STORE) {
1394
+ if (NM_STYPE(self) == nm::DENSE_STORE) {
855
1395
  return (NM_DENSE_SRC(self) == NM_STORAGE(self)) ? Qfalse : Qtrue;
856
1396
  }
857
1397
 
858
- if (NM_STYPE(self) == LIST_STORE) {
1398
+ if (NM_STYPE(self) == nm::LIST_STORE) {
859
1399
  return (NM_LIST_SRC(self) == NM_STORAGE(self)) ? Qfalse : Qtrue;
860
1400
  }
861
1401
 
@@ -921,10 +1461,10 @@ static VALUE nm_mset(int argc, VALUE* argv, VALUE self) {
921
1461
  // FIXME: Can't use a function pointer table here currently because these functions have different
922
1462
  // signatures (namely the return type).
923
1463
  switch(NM_STYPE(self)) {
924
- case DENSE_STORE:
1464
+ case nm::DENSE_STORE:
925
1465
  nm_dense_storage_set(NM_STORAGE(self), slice, value);
926
1466
  break;
927
- case LIST_STORE:
1467
+ case nm::LIST_STORE:
928
1468
  // Remove if it's a zero, insert otherwise
929
1469
  if (!std::memcmp(value, NM_STORAGE_LIST(self)->default_val, DTYPE_SIZES[NM_DTYPE(self)])) {
930
1470
  free(value);
@@ -934,7 +1474,7 @@ static VALUE nm_mset(int argc, VALUE* argv, VALUE self) {
934
1474
  nm_list_storage_insert(NM_STORAGE(self), slice, value);
935
1475
  }
936
1476
  break;
937
- case YALE_STORE:
1477
+ case nm::YALE_STORE:
938
1478
  nm_yale_storage_set(NM_STORAGE(self), slice, value);
939
1479
  break;
940
1480
  }
@@ -988,6 +1528,8 @@ static VALUE nm_multiply(VALUE left_v, VALUE right_v) {
988
1528
  return Qnil;
989
1529
  }
990
1530
 
1531
+
1532
+
991
1533
  /*
992
1534
  * LU factorization of a matrix.
993
1535
  *
@@ -995,7 +1537,7 @@ static VALUE nm_multiply(VALUE left_v, VALUE right_v) {
995
1537
  * FIXME: result again. Ideally, this would be an in-place factorize instead, and would be called nm_factorize_lu_bang.
996
1538
  */
997
1539
  static VALUE nm_factorize_lu(VALUE self) {
998
- if (NM_STYPE(self) != DENSE_STORE) {
1540
+ if (NM_STYPE(self) != nm::DENSE_STORE) {
999
1541
  rb_raise(rb_eNotImpError, "only implemented for dense storage");
1000
1542
  }
1001
1543
 
@@ -1089,8 +1631,8 @@ static VALUE nm_xslice(int argc, VALUE* argv, void* (*slice_func)(STORAGE*, SLIC
1089
1631
  nm_yale_storage_ref
1090
1632
  };
1091
1633
 
1092
- if (NM_DTYPE(self) == RUBYOBJ) result = *reinterpret_cast<VALUE*>( ttable[NM_STYPE(self)](NM_STORAGE(self), slice) );
1093
- else result = rubyobj_from_cval( ttable[NM_STYPE(self)](NM_STORAGE(self), slice), NM_DTYPE(self) ).rval;
1634
+ if (NM_DTYPE(self) == nm::RUBYOBJ) result = *reinterpret_cast<VALUE*>( ttable[NM_STYPE(self)](NM_STORAGE(self), slice) );
1635
+ else result = rubyobj_from_cval( ttable[NM_STYPE(self)](NM_STORAGE(self), slice), NM_DTYPE(self) ).rval;
1094
1636
 
1095
1637
  } else {
1096
1638
  STYPE_MARK_TABLE(mark_table);
@@ -1119,54 +1661,64 @@ static VALUE nm_xslice(int argc, VALUE* argv, void* (*slice_func)(STORAGE*, SLIC
1119
1661
  static VALUE elementwise_op(nm::ewop_t op, VALUE left_val, VALUE right_val) {
1120
1662
  STYPE_MARK_TABLE(mark);
1121
1663
 
1122
- static STORAGE* (*ew_op[nm::NUM_STYPES])(nm::ewop_t, const STORAGE*, const STORAGE*) = {
1664
+ static STORAGE* (*ew_op[nm::NUM_STYPES])(nm::ewop_t, const STORAGE*, const STORAGE*, VALUE scalar) = {
1123
1665
  nm_dense_storage_ew_op,
1124
1666
  nm_list_storage_ew_op,
1125
1667
  nm_yale_storage_ew_op
1126
1668
  // NULL
1127
1669
  };
1128
1670
 
1129
- NMATRIX* result = ALLOC(NMATRIX);
1671
+ NMATRIX *result = ALLOC(NMATRIX), *left;
1130
1672
 
1131
1673
  CheckNMatrixType(left_val);
1132
- CheckNMatrixType(right_val);
1674
+ UnwrapNMatrix(left_val, left);
1133
1675
 
1134
- // Check that the left- and right-hand sides have the same dimensionality.
1135
- if (NM_DIM(left_val) != NM_DIM(right_val)) {
1136
- rb_raise(rb_eArgError, "The left- and right-hand sides of the operation must have the same dimensionality.");
1137
- }
1138
-
1139
- // Check that the left- and right-hand sides have the same shape.
1140
- if (memcmp(&NM_SHAPE(left_val, 0), &NM_SHAPE(right_val, 0), sizeof(size_t) * NM_DIM(left_val)) != 0) {
1141
- rb_raise(rb_eArgError, "The left- and right-hand sides of the operation must have the same shape.");
1142
- }
1676
+ if (TYPE(right_val) != T_DATA || (RDATA(right_val)->dfree != (RUBY_DATA_FUNC)nm_delete && RDATA(right_val)->dfree != (RUBY_DATA_FUNC)nm_delete_ref)) {
1677
+ // This is a matrix-scalar element-wise operation.
1143
1678
 
1144
- NMATRIX* left, * right;
1145
- UnwrapNMatrix(left_val, left);
1146
- UnwrapNMatrix(right_val, right);
1679
+ if (left->stype != nm::YALE_STORE) {
1680
+ result->storage = ew_op[left->stype](op, reinterpret_cast<STORAGE*>(left->storage), NULL, right_val);
1681
+ result->stype = left->stype;
1682
+ } else {
1683
+ rb_raise(rb_eNotImpError, "Scalar element-wise operations not implemented for Yale storage yet");
1684
+ }
1685
+
1686
+ } else {
1687
+
1688
+ // Check that the left- and right-hand sides have the same dimensionality.
1689
+ if (NM_DIM(left_val) != NM_DIM(right_val)) {
1690
+ rb_raise(rb_eArgError, "The left- and right-hand sides of the operation must have the same dimensionality.");
1691
+ }
1692
+
1693
+ // Check that the left- and right-hand sides have the same shape.
1694
+ if (memcmp(&NM_SHAPE(left_val, 0), &NM_SHAPE(right_val, 0), sizeof(size_t) * NM_DIM(left_val)) != 0) {
1695
+ rb_raise(rb_eArgError, "The left- and right-hand sides of the operation must have the same shape.");
1696
+ }
1697
+
1698
+ NMATRIX* right;
1699
+ UnwrapNMatrix(right_val, right);
1147
1700
 
1148
- if (left->stype == right->stype) {
1149
-
1150
- if (ew_op[left->stype] == NULL) {
1151
- rb_raise(rb_eArgError, "Element-wise operations are not currently supported for this data type.");
1152
- }
1153
-
1154
- result->storage = ew_op[left->stype](op, reinterpret_cast<STORAGE*>(left->storage), reinterpret_cast<STORAGE*>(right->storage));
1155
- result->stype = left->stype;
1156
-
1157
- } else {
1158
- rb_raise(rb_eArgError, "Element-wise operations are not currently supported between matrices with differing stypes.");
1159
- }
1701
+ if (left->stype == right->stype) {
1702
+
1703
+ result->storage = ew_op[left->stype](op, reinterpret_cast<STORAGE*>(left->storage), reinterpret_cast<STORAGE*>(right->storage), Qnil);
1704
+ result->stype = left->stype;
1705
+
1706
+ } else {
1707
+ rb_raise(rb_eArgError, "Element-wise operations are not currently supported between matrices with differing stypes.");
1708
+ }
1709
+ }
1160
1710
 
1161
1711
  return Data_Wrap_Struct(cNMatrix, mark[result->stype], nm_delete, result);
1162
1712
  }
1163
1713
 
1714
+
1715
+
1164
1716
  /*
1165
1717
  * Check to determine whether matrix is a reference to another matrix.
1166
1718
  */
1167
1719
  bool is_ref(const NMATRIX* matrix) {
1168
1720
  // FIXME: Needs to work for other types
1169
- if (matrix->stype != DENSE_STORE) {
1721
+ if (matrix->stype != nm::DENSE_STORE) {
1170
1722
  return false;
1171
1723
  }
1172
1724
 
@@ -1181,7 +1733,7 @@ static VALUE is_symmetric(VALUE self, bool hermitian) {
1181
1733
  UnwrapNMatrix(self, m);
1182
1734
 
1183
1735
  if (m->storage->shape[0] == m->storage->shape[1] and m->storage->dim == 2) {
1184
- if (NM_STYPE(self) == DENSE_STORE) {
1736
+ if (NM_STYPE(self) == nm::DENSE_STORE) {
1185
1737
  if (hermitian) {
1186
1738
  nm_dense_storage_is_hermitian((DENSE_STORAGE*)(m->storage), m->storage->shape[0]);
1187
1739
 
@@ -1209,15 +1761,15 @@ static VALUE is_symmetric(VALUE self, bool hermitian) {
1209
1761
  *
1210
1762
  * TODO: Probably needs some work for Bignum.
1211
1763
  */
1212
- static dtype_t dtype_guess(VALUE v) {
1764
+ nm::dtype_t nm_dtype_guess(VALUE v) {
1213
1765
  switch(TYPE(v)) {
1214
1766
  case T_TRUE:
1215
1767
  case T_FALSE:
1216
- return BYTE;
1768
+ return nm::BYTE;
1217
1769
 
1218
1770
  case T_STRING:
1219
1771
  if (RSTRING_LEN(v) == 1) {
1220
- return BYTE;
1772
+ return nm::BYTE;
1221
1773
 
1222
1774
  } else {
1223
1775
  rb_raise(rb_eArgError, "Strings of length > 1 may not be stored in a matrix.");
@@ -1225,45 +1777,45 @@ static dtype_t dtype_guess(VALUE v) {
1225
1777
 
1226
1778
  #if SIZEOF_INT == 8
1227
1779
  case T_FIXNUM:
1228
- return INT64;
1780
+ return nm::INT64;
1229
1781
 
1230
1782
  case T_RATIONAL:
1231
- return RATIONAL128;
1783
+ return nm::RATIONAL128;
1232
1784
 
1233
1785
  #else
1234
1786
  # if SIZEOF_INT == 4
1235
1787
  case T_FIXNUM:
1236
- return INT32;
1788
+ return nm::INT32;
1237
1789
 
1238
1790
  case T_RATIONAL:
1239
- return RATIONAL64;
1791
+ return nm::RATIONAL64;
1240
1792
 
1241
1793
  #else
1242
1794
  case T_FIXNUM:
1243
- return INT16;
1795
+ return nm::INT16;
1244
1796
 
1245
1797
  case T_RATIONAL:
1246
- return RATIONAL32;
1798
+ return nm::RATIONAL32;
1247
1799
  # endif
1248
1800
  #endif
1249
1801
 
1250
1802
  case T_BIGNUM:
1251
- return INT64;
1803
+ return nm::INT64;
1252
1804
 
1253
1805
  #if SIZEOF_FLOAT == 4
1254
1806
  case T_COMPLEX:
1255
- return COMPLEX128;
1807
+ return nm::COMPLEX128;
1256
1808
 
1257
1809
  case T_FLOAT:
1258
- return FLOAT64;
1810
+ return nm::FLOAT64;
1259
1811
 
1260
1812
  #else
1261
1813
  # if SIZEOF_FLOAT == 2
1262
1814
  case T_COMPLEX:
1263
- return COMPLEX64;
1815
+ return nm::COMPLEX64;
1264
1816
 
1265
1817
  case T_FLOAT:
1266
- return FLOAT32;
1818
+ return nm::FLOAT32;
1267
1819
  # endif
1268
1820
  #endif
1269
1821
 
@@ -1274,7 +1826,7 @@ static dtype_t dtype_guess(VALUE v) {
1274
1826
  * TODO: Look at entire array for most specific type.
1275
1827
  */
1276
1828
 
1277
- return dtype_guess(RARRAY_PTR(v)[0]);
1829
+ return nm_dtype_guess(RARRAY_PTR(v)[0]);
1278
1830
 
1279
1831
  case T_NIL:
1280
1832
  default:
@@ -1343,7 +1895,7 @@ static double get_time(void) {
1343
1895
  * initial or dtype. If 2, is initial and dtype. This function returns the
1344
1896
  * dtype.
1345
1897
  */
1346
- static dtype_t interpret_dtype(int argc, VALUE* argv, stype_t stype) {
1898
+ static nm::dtype_t interpret_dtype(int argc, VALUE* argv, nm::stype_t stype) {
1347
1899
  int offset;
1348
1900
 
1349
1901
  switch (argc) {
@@ -1366,18 +1918,18 @@ static dtype_t interpret_dtype(int argc, VALUE* argv, stype_t stype) {
1366
1918
  } else if (TYPE(argv[offset]) == T_STRING) {
1367
1919
  return nm_dtype_from_rbstring(StringValue(argv[offset]));
1368
1920
 
1369
- } else if (stype == YALE_STORE) {
1921
+ } else if (stype == nm::YALE_STORE) {
1370
1922
  rb_raise(rb_eArgError, "Yale storage class requires a dtype.");
1371
1923
 
1372
1924
  } else {
1373
- return dtype_guess(argv[0]);
1925
+ return nm_dtype_guess(argv[0]);
1374
1926
  }
1375
1927
  }
1376
1928
 
1377
1929
  /*
1378
1930
  * Convert an Ruby value or an array of Ruby values into initial C values.
1379
1931
  */
1380
- static void* interpret_initial_value(VALUE arg, dtype_t dtype) {
1932
+ static void* interpret_initial_value(VALUE arg, nm::dtype_t dtype) {
1381
1933
  unsigned int index;
1382
1934
  void* init_val;
1383
1935
 
@@ -1432,7 +1984,7 @@ static size_t* interpret_shape(VALUE arg, size_t* dim) {
1432
1984
  /*
1433
1985
  * Convert a Ruby symbol or string into an storage type.
1434
1986
  */
1435
- static stype_t interpret_stype(VALUE arg) {
1987
+ static nm::stype_t interpret_stype(VALUE arg) {
1436
1988
  if (SYMBOL_P(arg)) {
1437
1989
  return nm_stype_from_rbsymbol(arg);
1438
1990
 
@@ -1450,7 +2002,7 @@ static stype_t interpret_stype(VALUE arg) {
1450
2002
  //////////////////
1451
2003
 
1452
2004
 
1453
- STORAGE* matrix_storage_cast_alloc(NMATRIX* matrix, dtype_t new_dtype) {
2005
+ STORAGE* matrix_storage_cast_alloc(NMATRIX* matrix, nm::dtype_t new_dtype) {
1454
2006
  if (matrix->storage->dtype == new_dtype && !is_ref(matrix))
1455
2007
  return matrix->storage;
1456
2008
 
@@ -1461,7 +2013,7 @@ STORAGE* matrix_storage_cast_alloc(NMATRIX* matrix, dtype_t new_dtype) {
1461
2013
 
1462
2014
  STORAGE_PAIR binary_storage_cast_alloc(NMATRIX* left_matrix, NMATRIX* right_matrix) {
1463
2015
  STORAGE_PAIR casted;
1464
- dtype_t new_dtype = Upcast[left_matrix->storage->dtype][right_matrix->storage->dtype];
2016
+ nm::dtype_t new_dtype = Upcast[left_matrix->storage->dtype][right_matrix->storage->dtype];
1465
2017
 
1466
2018
  casted.left = matrix_storage_cast_alloc(left_matrix, new_dtype);
1467
2019
  casted.right = matrix_storage_cast_alloc(right_matrix, new_dtype);
@@ -1530,7 +2082,7 @@ static VALUE matrix_multiply(NMATRIX* left, NMATRIX* right) {
1530
2082
  * Note: Currently only implemented for 2x2 and 3x3 matrices.
1531
2083
  */
1532
2084
  static VALUE nm_det_exact(VALUE self) {
1533
- if (NM_STYPE(self) != DENSE_STORE) rb_raise(nm_eStorageTypeError, "can only calculate exact determinant for dense matrices");
2085
+ if (NM_STYPE(self) != nm::DENSE_STORE) rb_raise(nm_eStorageTypeError, "can only calculate exact determinant for dense matrices");
1534
2086
 
1535
2087
  if (NM_DIM(self) != 2 || NM_SHAPE0(self) != NM_SHAPE1(self)) return Qnil;
1536
2088
 
@@ -1558,7 +2110,7 @@ static VALUE nm_det_exact(VALUE self) {
1558
2110
  *
1559
2111
  * TODO: Add a column-major option for libraries that use column-major matrices.
1560
2112
  */
1561
- VALUE rb_nmatrix_dense_create(dtype_t dtype, size_t* shape, size_t dim, void* elements, size_t length) {
2113
+ VALUE rb_nmatrix_dense_create(nm::dtype_t dtype, size_t* shape, size_t dim, void* elements, size_t length) {
1562
2114
  NMATRIX* nm;
1563
2115
  VALUE klass;
1564
2116
  size_t nm_dim;
@@ -1584,7 +2136,7 @@ VALUE rb_nmatrix_dense_create(dtype_t dtype, size_t* shape, size_t dim, void* el
1584
2136
  memcpy(elements_copy, elements, DTYPE_SIZES[dtype]*length);
1585
2137
 
1586
2138
  // allocate and create the matrix and its storage
1587
- nm = nm_create(DENSE_STORE, nm_dense_storage_create(dtype, shape_copy, dim, elements_copy, length));
2139
+ nm = nm_create(nm::DENSE_STORE, nm_dense_storage_create(dtype, shape_copy, dim, elements_copy, length));
1588
2140
 
1589
2141
  // tell Ruby about the matrix and its storage, particularly how to garbage collect it.
1590
2142
  return Data_Wrap_Struct(klass, nm_dense_storage_mark, nm_dense_storage_delete, nm);
@@ -1600,7 +2152,7 @@ VALUE rb_nmatrix_dense_create(dtype_t dtype, size_t* shape, size_t dim, void* el
1600
2152
  *
1601
2153
  * TODO: Add a transpose option for setting the orientation of the vector?
1602
2154
  */
1603
- VALUE rb_nvector_dense_create(dtype_t dtype, void* elements, size_t length) {
2155
+ VALUE rb_nvector_dense_create(nm::dtype_t dtype, void* elements, size_t length) {
1604
2156
  size_t dim = 1, shape = length;
1605
2157
  return rb_nmatrix_dense_create(dtype, &shape, dim, elements, length);
1606
2158
  }