nmatrix 0.0.1

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 (43) hide show
  1. data/.autotest +23 -0
  2. data/.gemtest +0 -0
  3. data/Gemfile +7 -0
  4. data/History.txt +6 -0
  5. data/LICENSE.txt +21 -0
  6. data/Manifest.txt +51 -0
  7. data/README.rdoc +63 -0
  8. data/Rakefile +154 -0
  9. data/ext/nmatrix/cblas.c +150 -0
  10. data/ext/nmatrix/dense.c +307 -0
  11. data/ext/nmatrix/dense/blas_header.template.c +52 -0
  12. data/ext/nmatrix/dense/elementwise.template.c +107 -0
  13. data/ext/nmatrix/dense/gemm.template.c +159 -0
  14. data/ext/nmatrix/dense/gemv.template.c +130 -0
  15. data/ext/nmatrix/dense/rationalmath.template.c +68 -0
  16. data/ext/nmatrix/depend +18 -0
  17. data/ext/nmatrix/extconf.rb +143 -0
  18. data/ext/nmatrix/generator.rb +594 -0
  19. data/ext/nmatrix/generator/syntax_tree.rb +481 -0
  20. data/ext/nmatrix/list.c +774 -0
  21. data/ext/nmatrix/nmatrix.c +1977 -0
  22. data/ext/nmatrix/nmatrix.h +912 -0
  23. data/ext/nmatrix/rational.c +98 -0
  24. data/ext/nmatrix/yale.c +726 -0
  25. data/ext/nmatrix/yale/complexmath.template.c +71 -0
  26. data/ext/nmatrix/yale/elementwise.template.c +46 -0
  27. data/ext/nmatrix/yale/elementwise_op.template.c +73 -0
  28. data/ext/nmatrix/yale/numbmm.template.c +94 -0
  29. data/ext/nmatrix/yale/smmp1.template.c +21 -0
  30. data/ext/nmatrix/yale/smmp1_header.template.c +38 -0
  31. data/ext/nmatrix/yale/smmp2.template.c +43 -0
  32. data/ext/nmatrix/yale/smmp2_header.template.c +46 -0
  33. data/ext/nmatrix/yale/sort_columns.template.c +56 -0
  34. data/ext/nmatrix/yale/symbmm.template.c +54 -0
  35. data/ext/nmatrix/yale/transp.template.c +68 -0
  36. data/lib/array.rb +67 -0
  37. data/lib/nmatrix.rb +263 -0
  38. data/lib/string.rb +65 -0
  39. data/spec/nmatrix_spec.rb +395 -0
  40. data/spec/nmatrix_yale_spec.rb +239 -0
  41. data/spec/nvector_spec.rb +43 -0
  42. data/spec/syntax_tree_spec.rb +46 -0
  43. metadata +150 -0
@@ -0,0 +1,774 @@
1
+ /////////////////////////////////////////////////////////////////////
2
+ // = NMatrix
3
+ //
4
+ // A linear algebra library for scientific computation in Ruby.
5
+ // NMatrix is part of SciRuby.
6
+ //
7
+ // NMatrix was originally inspired by and derived from NArray, by
8
+ // Masahiro Tanaka: http://narray.rubyforge.org
9
+ //
10
+ // == Copyright Information
11
+ //
12
+ // SciRuby is Copyright (c) 2010 - 2012, Ruby Science Foundation
13
+ // NMatrix is Copyright (c) 2012, Ruby Science Foundation
14
+ //
15
+ // Please see LICENSE.txt for additional copyright notices.
16
+ //
17
+ // == Contributing
18
+ //
19
+ // By contributing source code to SciRuby, you agree to be bound by
20
+ // our Contributor Agreement:
21
+ //
22
+ // * https://github.com/SciRuby/sciruby/wiki/Contributor-Agreement
23
+ //
24
+ // == list.c
25
+ //
26
+ // List-of-lists n-dimensional matrix storage. Uses singly-linked
27
+ // lists.
28
+
29
+ #ifndef LIST_C
30
+ # define LIST_C
31
+
32
+ #include <ruby.h>
33
+
34
+ #include "nmatrix.h"
35
+
36
+ extern VALUE nm_eStorageTypeError;
37
+
38
+
39
+ /* Calculate the max number of elements in the list storage structure, based on shape and rank */
40
+ inline size_t count_storage_max_elements(const STORAGE* s) {
41
+ return count_dense_storage_elements((DENSE_STORAGE*)s);
42
+ }
43
+
44
+ static size_t count_list_storage_elements_r(const LIST* l, size_t recursions) {
45
+ size_t count = 0;
46
+ NODE* curr = l->first;
47
+ if (recursions) {
48
+ while (curr) {
49
+ count += count_list_storage_elements_r(curr->val, recursions-1);
50
+ curr = curr->next;
51
+ }
52
+ } else {
53
+ while (curr) {
54
+ ++count;
55
+ curr = curr->next;
56
+ }
57
+ }
58
+ return count;
59
+ }
60
+
61
+
62
+ // Count non-zero elements. See also count_list_storage_nd_elements.
63
+ size_t count_list_storage_elements(const LIST_STORAGE* s) {
64
+ return count_list_storage_elements_r(s->rows, s->rank-1);
65
+ }
66
+
67
+
68
+ // Count non-diagonal non-zero elements
69
+ size_t count_list_storage_nd_elements(const LIST_STORAGE* s) {
70
+ NODE *i_curr, *j_curr;
71
+ size_t count = 0;
72
+ if (s->rank != 2) rb_raise(rb_eNotImpError, "non-diagonal element counting only defined for rank = 2");
73
+
74
+ for (i_curr = s->rows->first; i_curr; i_curr = i_curr->next) {
75
+ for (j_curr = ((LIST*)(i_curr->val))->first; j_curr; j_curr = j_curr->next) {
76
+ if (i_curr->key != j_curr->key) ++count;
77
+ }
78
+ }
79
+ return count;
80
+ }
81
+
82
+
83
+ /* Finds the node that should go before whatever key we request, whether or not that key is present */
84
+ static NODE* list_find_preceding_from(NODE* prev, size_t key) {
85
+ NODE* curr = prev->next;
86
+
87
+ if (!curr || key <= curr->key) return prev;
88
+ return list_find_preceding_from(curr, key);
89
+ }
90
+
91
+
92
+ /* Finds a node or the one immediately preceding it if it doesn't exist */
93
+ static NODE* list_find_nearest_from(NODE* prev, size_t key) {
94
+ NODE* f;
95
+
96
+ if (prev && prev->key == key) return prev;
97
+
98
+ f = list_find_preceding_from(prev, key);
99
+
100
+ if (!f->next) return f;
101
+ else if (key == f->next->key) return f->next;
102
+ else return prev;
103
+ }
104
+
105
+
106
+ /* Finds the node or, if not present, the node that it should follow.
107
+ * NULL indicates no preceding node. */
108
+ //static NODE* list_find_nearest(LIST* list, size_t key) {
109
+ // return list_find_nearest_from(list->first, key);
110
+ //}
111
+
112
+
113
+ /* Find some element in the list and return the node ptr for that key. */
114
+ static NODE* list_find(LIST* list, size_t key) {
115
+ NODE* f;
116
+ if (!list->first) return NULL; // empty list -- does not exist
117
+
118
+ // see if we can find it.
119
+ f = list_find_nearest_from(list->first, key);
120
+ if (!f || f->key == key) return f;
121
+ return NULL;
122
+ }
123
+
124
+
125
+
126
+ /* Get the contents of some set of coordinates. Note: Does not make a copy! Don't free! */
127
+ void* list_storage_get(LIST_STORAGE* s, size_t* coords) {
128
+ //LIST_STORAGE* s = (LIST_STORAGE*)(t);
129
+ size_t r;
130
+ NODE* n;
131
+ LIST* l = s->rows;
132
+
133
+ for (r = s->rank; r > 1; --r) {
134
+ n = list_find(l, coords[s->rank - r]);
135
+ if (n) l = n->val;
136
+ else return s->default_val;
137
+ }
138
+
139
+ n = list_find(l, coords[s->rank - r]);
140
+ if (n) return n->val;
141
+ else return s->default_val;
142
+ }
143
+
144
+
145
+ /* Returns the value pointer (not the node) for some key. Note that it doesn't free the memory
146
+ * for the value stored in the node -- that pointer gets returned! Only the node is destroyed.
147
+ */
148
+ static void* list_remove(LIST* list, size_t key) {
149
+ NODE *f, *rm;
150
+ void* val;
151
+
152
+ if (!list->first || list->first->key > key) return NULL; // empty list or def. not present
153
+
154
+ if (list->first->key == key) {
155
+ val = list->first->val;
156
+ rm = list->first;
157
+ list->first = rm->next;
158
+ free(rm);
159
+ return val;
160
+ }
161
+
162
+ f = list_find_preceding_from(list->first, key);
163
+ if (!f || !f->next) return NULL; // not found, end of list
164
+
165
+ if (f->next->key == key) {
166
+ // remove the node
167
+ rm = f->next;
168
+ f->next = rm->next;
169
+
170
+ // get the value and free the memory for the node
171
+ val = rm->val;
172
+ free(rm);
173
+ return val;
174
+ }
175
+
176
+ return NULL; // not found, middle of list
177
+ }
178
+
179
+
180
+ /// TODO: Speed up removal.
181
+ void* list_storage_remove(LIST_STORAGE* s, size_t* coords) {
182
+ int r;
183
+ NODE *n = NULL;
184
+ LIST* l = s->rows;
185
+ void* rm = NULL;
186
+
187
+ // keep track of where we are in the traversals
188
+ NODE** stack = ALLOCA_N( NODE*, s->rank - 1 );
189
+
190
+ for (r = (int)(s->rank); r > 1; --r) {
191
+ n = list_find(l, coords[s->rank - r]); // does this row exist in the matrix?
192
+
193
+ if (!n) { // not found
194
+ free(stack);
195
+ return NULL;
196
+ } else { // found
197
+ stack[s->rank - r] = n;
198
+ l = n->val;
199
+ }
200
+ }
201
+
202
+ rm = list_remove(l, coords[s->rank - r]);
203
+
204
+ // if we removed something, we may now need to remove parent lists
205
+ if (rm) {
206
+ for (r = (int)(s->rank) - 2; r >= 0; --r) { // walk back down the stack
207
+ if (((LIST*)(stack[r]->val))->first == NULL)
208
+ free(list_remove(stack[r]->val, coords[r]));
209
+ else
210
+ break; // no need to continue unless we just deleted one.
211
+ }
212
+ }
213
+
214
+ return rm;
215
+ }
216
+
217
+
218
+ /* Creates an empty linked list */
219
+ static LIST* create_list() {
220
+ LIST* list;
221
+ //if (!(list = malloc(sizeof(LIST)))) return NULL;
222
+ list = ALLOC( LIST );
223
+
224
+ //fprintf(stderr, " create_list LIST: %p\n", list);
225
+
226
+ list->first = NULL;
227
+ return list;
228
+ }
229
+
230
+
231
+ static NODE* list_insert_after(NODE* node, size_t key, void* val) {
232
+ NODE* ins;
233
+
234
+ //if (!(ins = malloc(sizeof(NODE)))) return NULL;
235
+ ins = ALLOC(NODE);
236
+
237
+ // insert 'ins' between 'node' and 'node->next'
238
+ ins->next = node->next;
239
+ node->next = ins;
240
+
241
+ // initialize our new node
242
+ ins->key = key;
243
+ ins->val = val;
244
+
245
+ return ins;
246
+ }
247
+
248
+
249
+
250
+ /* Given a list and a key/value-ptr pair, create a node (and return that node).
251
+ * If NULL is returned, it means insertion failed.
252
+ * If the key already exists in the list, replace tells it to delete the old value
253
+ * and put in your new one. !replace means delete the new value.
254
+ */
255
+ static NODE* list_insert(LIST* list, bool replace, size_t key, void* val) {
256
+ NODE *ins;
257
+
258
+ if (list->first == NULL) { // List is empty
259
+ //if (!(ins = malloc(sizeof(NODE)))) return NULL;
260
+ ins = ALLOC(NODE);
261
+ ins->next = NULL;
262
+ ins->val = val;
263
+ ins->key = key;
264
+ list->first = ins;
265
+ return ins;
266
+
267
+ } else if (key < list->first->key) { // Goes at the beginning of the list
268
+ //if (!(ins = malloc(sizeof(NODE)))) return NULL;
269
+ ins = ALLOC(NODE);
270
+ ins->next = list->first;
271
+ ins->val = val;
272
+ ins->key = key;
273
+ list->first = ins;
274
+ return ins;
275
+ }
276
+
277
+ // Goes somewhere else in the list.
278
+ ins = list_find_nearest_from(list->first, key);
279
+
280
+ if (ins->key == key) {
281
+ // key already exists
282
+ if (replace) {
283
+ free(ins->val);
284
+ ins->val = val;
285
+ } else free(val);
286
+ return ins;
287
+
288
+ } else return list_insert_after(ins, key, val);
289
+
290
+ }
291
+
292
+
293
+
294
+ // TODO: Allow this function to accept an entire row and not just one value -- for slicing
295
+ void* list_storage_insert(LIST_STORAGE* s, size_t* coords, void* val) {
296
+ // Pretend ranks = 2
297
+ // Then coords is going to be size 2
298
+ // So we need to find out if some key already exists
299
+ size_t r;
300
+ NODE* n;
301
+ LIST* l = s->rows;
302
+
303
+ // drill down into the structure
304
+ for (r = s->rank; r > 1; --r) {
305
+ n = list_insert(l, false, coords[s->rank - r], create_list());
306
+ l = n->val;
307
+ }
308
+
309
+ n = list_insert(l, true, coords[s->rank - r], val);
310
+ return n->val;
311
+ }
312
+
313
+ // Creates a list-of-lists(-of-lists-of-lists-etc) storage framework for a matrix.
314
+ //
315
+ // Note: The pointers you pass in for shape and init_val become property of our new
316
+ // storage. You don't need to free them, and you shouldn't re-use them.
317
+ LIST_STORAGE* create_list_storage(int8_t dtype, size_t* shape, size_t rank, void* init_val) {
318
+ LIST_STORAGE* s;
319
+
320
+ s = ALLOC( LIST_STORAGE );
321
+
322
+ s->rank = rank;
323
+ s->shape = shape;
324
+ s->dtype = dtype;
325
+
326
+ s->rows = create_list();
327
+
328
+ s->default_val = init_val;
329
+
330
+ return s;
331
+ }
332
+
333
+
334
+ static void cast_copy_list_contents(LIST* lhs, LIST* rhs, int8_t lhs_dtype, int8_t rhs_dtype, size_t recursions) {
335
+ NODE *lcurr, *rcurr;
336
+
337
+ if (rhs->first) {
338
+ // copy head node
339
+ rcurr = rhs->first;
340
+ lcurr = lhs->first = ALLOC( NODE );
341
+
342
+ while (rcurr) {
343
+ lcurr->key = rcurr->key;
344
+
345
+ if (recursions == 0) { // contents is some kind of value
346
+ lcurr->val = ALLOC_N(char, nm_sizeof[lhs_dtype]);
347
+ //fprintf(stderr, " create_val: %p\n", lcurr->val);
348
+
349
+ if (lhs_dtype == rhs_dtype) memcpy(lcurr->val, rcurr->val, nm_sizeof[lhs_dtype]);
350
+ else SetFuncs[lhs_dtype][rhs_dtype](1, lcurr->val, 0, rcurr->val, 0);
351
+
352
+ } else { // contents is a list
353
+ lcurr->val = ALLOC( LIST );
354
+ //fprintf(stderr, " create_list: %p\n", lcurr->val);
355
+
356
+ cast_copy_list_contents(lcurr->val, rcurr->val, lhs_dtype, rhs_dtype, recursions-1);
357
+ }
358
+ if (rcurr->next) lcurr->next = ALLOC( NODE );
359
+ else lcurr->next = NULL;
360
+
361
+ lcurr = lcurr->next;
362
+ rcurr = rcurr->next;
363
+ }
364
+ } else {
365
+ lhs->first = NULL;
366
+ }
367
+ }
368
+
369
+
370
+ /* Deletes the linked list and all of its contents. If you want to delete a list inside of a list,
371
+ * set recursions to 1. For lists inside of lists inside of the list, set it to 2; and so on.
372
+ * Setting it to 0 is for no recursions.
373
+ */
374
+ static void delete_list(LIST* list, size_t recursions) {
375
+ NODE* next;
376
+ NODE* curr = list->first;
377
+
378
+ while (curr != NULL) {
379
+ next = curr->next;
380
+
381
+ if (recursions == 0) {
382
+ //fprintf(stderr, " free_val: %p\n", curr->val);
383
+ free(curr->val);
384
+ } else {
385
+ //fprintf(stderr, " free_list: %p\n", list);
386
+ delete_list(curr->val, recursions-1);
387
+ }
388
+
389
+ free(curr);
390
+ curr = next;
391
+ }
392
+ //fprintf(stderr, " free_list: %p\n", list);
393
+ free(list);
394
+ }
395
+
396
+
397
+ // Copy dense into lists recursively
398
+ //
399
+ // TODO: This works, but could probably be cleaner (do we really need to pass coords around?)
400
+ static bool cast_copy_list_contents_dense(LIST* lhs, const char* rhs, void* zero, int8_t l_dtype, int8_t r_dtype, size_t* pos, size_t* coords, const size_t* shape, size_t rank, size_t recursions) {
401
+ NODE *prev;
402
+ LIST *sub_list;
403
+ bool added = false, added_list = false;
404
+ void* insert_value;
405
+
406
+ for (coords[rank-1-recursions] = 0; coords[rank-1-recursions] < shape[rank-1-recursions]; ++coords[rank-1-recursions], ++(*pos)) {
407
+ //fprintf(stderr, "(%u)\t<%u, %u>: ", recursions, coords[0], coords[1]);
408
+
409
+ if (recursions == 0) { // create nodes
410
+ if (memcmp((char*)rhs + (*pos)*nm_sizeof[r_dtype], zero, nm_sizeof[r_dtype])) { // is not zero
411
+ //fprintf(stderr, "inserting value\n");
412
+
413
+ // Create a copy of our value that we will insert in the list
414
+ insert_value = ALLOC_N(char, nm_sizeof[l_dtype]);
415
+ cast_copy_value_single(insert_value, rhs + (*pos)*nm_sizeof[r_dtype], l_dtype, r_dtype);
416
+
417
+ if (!lhs->first) prev = list_insert(lhs, false, coords[rank-1-recursions], insert_value);
418
+ else prev = list_insert_after(prev, coords[rank-1-recursions], insert_value);
419
+ added = true;
420
+ } //else fprintf(stderr, "zero\n");
421
+ // no need to do anything if the element is zero
422
+ } else { // create lists
423
+ //fprintf(stderr, "inserting list\n");
424
+ // create a list as if there's something in the row in question, and then delete it if nothing turns out to be there
425
+ sub_list = create_list();
426
+
427
+ added_list = cast_copy_list_contents_dense(sub_list, rhs, zero, l_dtype, r_dtype, pos, coords, shape, rank, recursions-1);
428
+
429
+ if (!added_list) { delete_list(sub_list, recursions-1); fprintf(stderr, "deleting list\n"); }// nothing added
430
+ else if (!lhs->first) prev = list_insert(lhs, false, coords[rank-1-recursions], sub_list);
431
+ else prev = list_insert_after(prev, coords[rank-1-recursions], sub_list);
432
+
433
+ // added = (added || added_list);
434
+
435
+ }
436
+ }
437
+
438
+ coords[rank-1-recursions] = 0;
439
+ --(*pos);
440
+
441
+ return added;
442
+ }
443
+
444
+
445
+
446
+ LIST_STORAGE* copy_list_storage(LIST_STORAGE* rhs) {
447
+ LIST_STORAGE* lhs;
448
+ size_t* shape;
449
+ void* default_val = ALLOC_N(char, nm_sizeof[rhs->dtype]);
450
+
451
+ //fprintf(stderr, "copy_list_storage\n");
452
+
453
+ // allocate and copy shape
454
+ shape = ALLOC_N(size_t, rhs->rank);
455
+ memcpy(shape, rhs->shape, rhs->rank * sizeof(size_t));
456
+ memcpy(default_val, rhs->default_val, nm_sizeof[rhs->dtype]);
457
+
458
+ lhs = create_list_storage(rhs->dtype, shape, rhs->rank, default_val);
459
+
460
+ if (lhs) {
461
+ lhs->rows = create_list();
462
+ cast_copy_list_contents(lhs->rows, rhs->rows, rhs->dtype, rhs->dtype, rhs->rank - 1);
463
+ } else free(shape);
464
+
465
+ return lhs;
466
+ }
467
+
468
+
469
+ LIST_STORAGE* cast_copy_list_storage(LIST_STORAGE* rhs, int8_t new_dtype) {
470
+ LIST_STORAGE* lhs;
471
+ size_t* shape;
472
+ void* default_val = ALLOC_N(char, nm_sizeof[rhs->dtype]);
473
+
474
+ //fprintf(stderr, "copy_list_storage\n");
475
+
476
+ // allocate and copy shape
477
+ shape = ALLOC_N(size_t, rhs->rank);
478
+ memcpy(shape, rhs->shape, rhs->rank * sizeof(size_t));
479
+
480
+ // copy default value
481
+ if (new_dtype == rhs->dtype) memcpy(default_val, rhs->default_val, nm_sizeof[rhs->dtype]);
482
+ else SetFuncs[new_dtype][rhs->dtype](1, default_val, 0, rhs->default_val, 0);
483
+
484
+ lhs = create_list_storage(new_dtype, shape, rhs->rank, default_val);
485
+
486
+ lhs->rows = create_list();
487
+ cast_copy_list_contents(lhs->rows, rhs->rows, new_dtype, rhs->dtype, rhs->rank - 1);
488
+
489
+ return lhs;
490
+ }
491
+
492
+
493
+
494
+ LIST_STORAGE* scast_copy_list_dense(const DENSE_STORAGE* rhs, int8_t l_dtype) {
495
+ LIST_STORAGE* lhs;
496
+ size_t pos = 0;
497
+ void* l_default_val = ALLOC_N(char, nm_sizeof[l_dtype]);
498
+ void* r_default_val = ALLOCA_N(char, nm_sizeof[rhs->dtype]); // clean up when finished with this function
499
+
500
+ // allocate and copy shape and coords
501
+ size_t *shape = ALLOC_N(size_t, rhs->rank), *coords = ALLOC_N(size_t, rhs->rank);
502
+ memcpy(shape, rhs->shape, rhs->rank * sizeof(size_t));
503
+ memset(coords, 0, rhs->rank * sizeof(size_t));
504
+
505
+ // set list default_val to 0
506
+ if (l_dtype == NM_ROBJ) *(VALUE*)l_default_val = INT2FIX(0);
507
+ else memset(l_default_val, 0, nm_sizeof[l_dtype]);
508
+
509
+ // need test default value for comparing to elements in dense matrix
510
+ if (rhs->dtype == l_dtype) r_default_val = l_default_val;
511
+ else if (rhs->dtype == NM_ROBJ) *(VALUE*)r_default_val = INT2FIX(0);
512
+ else memset(r_default_val, 0, nm_sizeof[rhs->dtype]);
513
+
514
+ lhs = create_list_storage(l_dtype, shape, rhs->rank, l_default_val);
515
+
516
+ lhs->rows = create_list();
517
+ cast_copy_list_contents_dense(lhs->rows, rhs->elements, r_default_val, l_dtype, rhs->dtype, &pos, coords, rhs->shape, rhs->rank, rhs->rank - 1);
518
+
519
+ return lhs;
520
+ }
521
+
522
+
523
+ LIST_STORAGE* scast_copy_list_yale(const YALE_STORAGE* rhs, int8_t l_dtype) {
524
+ LIST_STORAGE* lhs;
525
+ NODE *last_added, *last_row_added = NULL;
526
+ LIST* curr_row;
527
+ y_size_t ija, ija_next, i, jj;
528
+ bool add_diag;
529
+ void* default_val = ALLOC_N(char, nm_sizeof[l_dtype]);
530
+ void* R_ZERO = (char*)(rhs->a) + rhs->shape[0]*nm_sizeof[rhs->dtype];
531
+ void* insert_val;
532
+
533
+ // allocate and copy shape
534
+ size_t *shape = ALLOC_N(size_t, rhs->rank);
535
+ shape[0] = rhs->shape[0]; shape[1] = rhs->shape[1];
536
+
537
+ // copy default value from the zero location in the Yale matrix
538
+ SetFuncs[l_dtype][rhs->dtype](1, default_val, 0, R_ZERO, 0);
539
+
540
+ lhs = create_list_storage(l_dtype, shape, rhs->rank, default_val);
541
+
542
+ if (rhs->rank != 2)
543
+ rb_raise(nm_eStorageTypeError, "can only convert matrices of rank 2 from yale");
544
+
545
+ // Walk through rows and columns as if RHS were a dense matrix
546
+ for (i = 0; i < rhs->shape[0]; ++i) {
547
+
548
+ // Get boundaries of beginning and end of row
549
+ YaleGetIJA(ija, rhs, i);
550
+ YaleGetIJA(ija_next, rhs, i+1);
551
+
552
+ // Are we going to need to add a diagonal for this row?
553
+ if (!memcmp((char*)(rhs->a) + i*nm_sizeof[rhs->dtype], R_ZERO, nm_sizeof[rhs->dtype])) add_diag = false; // zero
554
+ else add_diag = true; // nonzero diagonal
555
+
556
+
557
+ if (ija < ija_next || add_diag) {
558
+
559
+ curr_row = create_list();
560
+ last_added = NULL;
561
+
562
+ while (ija < ija_next) {
563
+ YaleGetIJA(jj, rhs, ija); // what column number is this?
564
+
565
+ // Is there a nonzero diagonal item between the previously added item and the current one?
566
+ if (jj > i && add_diag) {
567
+ // Allocate and copy insertion value
568
+ insert_val = ALLOC_N(char, nm_sizeof[l_dtype]);
569
+ SetFuncs[l_dtype][rhs->dtype](1, insert_val, 0, (char*)(rhs->a) + i*nm_sizeof[rhs->dtype], 0);
570
+
571
+ // insert the item in the list at the appropriate location
572
+ if (last_added) last_added = list_insert_after(last_added, i, insert_val);
573
+ else last_added = list_insert(curr_row, false, i, insert_val);
574
+
575
+ add_diag = false; // don't add again!
576
+ }
577
+
578
+ // now allocate and add the current item
579
+ insert_val = ALLOC_N(char, nm_sizeof[l_dtype]);
580
+ SetFuncs[l_dtype][rhs->dtype](1, insert_val, 0, (char*)(rhs->a) + ija*nm_sizeof[rhs->dtype], 0);
581
+
582
+ if (last_added) last_added = list_insert_after(last_added, jj, insert_val);
583
+ else last_added = list_insert(curr_row, false, jj, insert_val);
584
+
585
+ ++ija; // move to next entry in Yale matrix
586
+ }
587
+
588
+ if (add_diag) { // still haven't added the diagonal.
589
+ insert_val = ALLOC_N(char, nm_sizeof[l_dtype]);
590
+ SetFuncs[l_dtype][rhs->dtype](1, insert_val, 0, (char*)(rhs->a) + i*nm_sizeof[rhs->dtype], 0);
591
+
592
+ // insert the item in the list at the appropriate location
593
+ if (last_added) last_added = list_insert_after(last_added, i, insert_val);
594
+ else last_added = list_insert(curr_row, false, i, insert_val);
595
+ }
596
+
597
+ // Now add the list at the appropriate location
598
+ if (last_row_added) last_row_added = list_insert_after(last_row_added, i, curr_row);
599
+ else last_row_added = list_insert(lhs->rows, false, i, curr_row);
600
+ }
601
+
602
+ } // end of walk through rows
603
+
604
+ return lhs;
605
+ }
606
+
607
+
608
+
609
+ // Do all values in a list == some value?
610
+ static bool list_eqeq_value(const LIST* l, const void* v, size_t value_size, size_t recursions, size_t* checked) {
611
+ NODE *next, *curr = l->first;
612
+
613
+ while (curr) {
614
+ next = curr->next;
615
+
616
+ if (recursions == 0) {
617
+ ++(*checked);
618
+ if (memcmp(curr->val, v, value_size)) return false;
619
+ } else if (!list_eqeq_value(curr->val, v, value_size, recursions-1, checked))
620
+ return false;
621
+
622
+ curr = next;
623
+ }
624
+ return true;
625
+ }
626
+
627
+
628
+ // Are all values in the two lists equal? If one is missing a value, but the other isn't, does the value in the list match
629
+ // the default value?
630
+ static bool list_eqeq_list(const LIST* left, const LIST* right, const void* left_val, const void* right_val, size_t value_size, size_t recursions, size_t* checked) {
631
+ NODE *lnext, *lcurr = left->first, *rnext, *rcurr = right->first;
632
+
633
+ //fprintf(stderr, "list_eqeq_list: recursions=%d\n", recursions);
634
+
635
+ if (lcurr) lnext = lcurr->next;
636
+ if (rcurr) rnext = rcurr->next;
637
+
638
+ while (lcurr && rcurr) {
639
+
640
+ if (lcurr->key == rcurr->key) { // MATCHING KEYS
641
+ if (recursions == 0) {
642
+ ++(*checked);
643
+ if (memcmp(lcurr->val, rcurr->val, value_size)) return false;
644
+ } else if (!list_eqeq_list(lcurr->val, rcurr->val, left_val, right_val, value_size, recursions-1, checked))
645
+ return false;
646
+
647
+ // increment both iterators
648
+ rcurr = rnext;
649
+ if (rcurr) rnext = rcurr->next;
650
+ lcurr = lnext;
651
+ if (lcurr) lnext = lcurr->next;
652
+
653
+ } else if (lcurr->key < rcurr->key) { // NON-MATCHING KEYS
654
+
655
+ if (recursions == 0) {
656
+ // compare left entry to right default value
657
+ ++(*checked);
658
+ if (memcmp(lcurr->val, right_val, value_size)) return false;
659
+ } else if (!list_eqeq_value(lcurr->val, right_val, value_size, recursions-1, checked))
660
+ return false;
661
+
662
+ // increment left iterator
663
+ lcurr = lnext;
664
+ if (lcurr) lnext = lcurr->next;
665
+
666
+ } else { // if (rcurr->key < lcurr->key)
667
+
668
+ if (recursions == 0) {
669
+ // compare right entry to left default value
670
+ ++(*checked);
671
+ if (memcmp(rcurr->val, left_val, value_size)) return false;
672
+ } else if (!list_eqeq_value(rcurr->val, left_val, value_size, recursions-1, checked))
673
+ return false;
674
+
675
+ // increment right iterator
676
+ rcurr = rnext;
677
+ if (rcurr) rnext = rcurr->next;
678
+ }
679
+
680
+ }
681
+
682
+ // One final check, in case we get to the end of one list but not the other one.
683
+ if (lcurr) { // nothing left in right-hand list
684
+ if (memcmp(lcurr->val, right_val, value_size)) return false;
685
+ } else if (rcurr) { // nothing left in left-hand list
686
+ if (memcmp(rcurr->val, left_val, value_size)) return false;
687
+ }
688
+
689
+ // Nothing different between the two lists -- but make sure after this return that you compare the default values themselves,
690
+ // if we haven't visited every value in the two matrices.
691
+ return true;
692
+ }
693
+
694
+
695
+ // Do these two dense matrices of the same dtype have exactly the same contents?
696
+ bool list_storage_eqeq(const LIST_STORAGE* left, const LIST_STORAGE* right) {
697
+
698
+ // in certain cases, we need to keep track of the number of elements checked.
699
+ size_t num_checked = 0,
700
+ max_elements = count_storage_max_elements((STORAGE*)left),
701
+ sz = nm_sizeof[left->dtype];
702
+
703
+ if (!left->rows->first) {
704
+ // fprintf(stderr, "!left->rows true\n");
705
+ // Easy: both lists empty -- just compare default values
706
+ if (!right->rows->first) return !memcmp(left->default_val, right->default_val, sz);
707
+
708
+ // Left empty, right not empty. Do all values in right == left->default_val?
709
+ if (!list_eqeq_value(right->rows, left->default_val, sz, left->rank-1, &num_checked)) return false;
710
+
711
+ // If the matrix isn't full, we also need to compare default values.
712
+ if (num_checked < max_elements) return !memcmp(left->default_val, right->default_val, sz);
713
+
714
+ } else if (!right->rows->first) {
715
+ // fprintf(stderr, "!right->rows true\n");
716
+ // Right empty, left not empty. Do all values in left == right->default_val?
717
+ if (!list_eqeq_value(left->rows, right->default_val, sz, left->rank-1, &num_checked)) return false;
718
+
719
+ // If the matrix isn't full, we also need to compare default values.
720
+ if (num_checked < max_elements) return !memcmp(left->default_val, right->default_val, sz);
721
+
722
+ } else {
723
+ // fprintf(stderr, "both matrices have entries\n");
724
+ // Hardest case. Compare lists node by node. Let's make it simpler by requiring that both have the same default value
725
+ if (!list_eqeq_list(left->rows, right->rows, left->default_val, right->default_val, sz, left->rank-1, &num_checked)) return false;
726
+ if (num_checked < max_elements) return !memcmp(left->default_val, right->default_val, sz);
727
+ }
728
+
729
+ return true;
730
+ }
731
+
732
+
733
+ void delete_list_storage(LIST_STORAGE* s) {
734
+ if (s) {
735
+ //fprintf(stderr, "* Deleting list storage rows at %p\n", s->rows);
736
+ delete_list( s->rows, s->rank - 1 );
737
+
738
+ //fprintf(stderr, " Deleting list storage shape at %p\n", s->shape);
739
+ free(s->shape);
740
+ //fprintf(stderr, " Deleting list storage default_val at %p\n", s->default_val);
741
+ free(s->default_val);
742
+ //fprintf(stderr, " Deleting list storage at %p\n", s);
743
+ free(s);
744
+ }
745
+ }
746
+
747
+
748
+ static void mark_list(LIST* list, size_t recursions) {
749
+ NODE* next;
750
+ NODE* curr = list->first;
751
+
752
+ while (curr != NULL) {
753
+ next = curr->next;
754
+ if (recursions == 0) rb_gc_mark(*((VALUE*)(curr->val)));
755
+ else mark_list(curr->val, recursions-1);
756
+ curr = next;
757
+ }
758
+ }
759
+
760
+
761
+ void mark_list_storage(void* m) {
762
+ LIST_STORAGE* storage;
763
+
764
+ if (m) {
765
+ storage = (LIST_STORAGE*)(((NMATRIX*)m)->storage);
766
+ if (storage && storage->dtype == NM_ROBJ) {
767
+ rb_gc_mark(*((VALUE*)(storage->default_val)));
768
+ mark_list(storage->rows, storage->rank - 1);
769
+ }
770
+ }
771
+ }
772
+
773
+
774
+ #endif