nmatrix 0.0.1

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