nmatrix 0.0.1 → 0.0.2
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.
- data/.gitignore +27 -0
- data/.rspec +2 -0
- data/Gemfile +3 -5
- data/Guardfile +6 -0
- data/History.txt +33 -0
- data/Manifest.txt +41 -38
- data/README.rdoc +88 -11
- data/Rakefile +35 -53
- data/ext/nmatrix/data/complex.h +372 -0
- data/ext/nmatrix/data/data.cpp +275 -0
- data/ext/nmatrix/data/data.h +707 -0
- data/ext/nmatrix/data/rational.h +421 -0
- data/ext/nmatrix/data/ruby_object.h +446 -0
- data/ext/nmatrix/extconf.rb +101 -51
- data/ext/nmatrix/new_extconf.rb +56 -0
- data/ext/nmatrix/nmatrix.cpp +1609 -0
- data/ext/nmatrix/nmatrix.h +265 -849
- data/ext/nmatrix/ruby_constants.cpp +134 -0
- data/ext/nmatrix/ruby_constants.h +103 -0
- data/ext/nmatrix/storage/common.cpp +70 -0
- data/ext/nmatrix/storage/common.h +170 -0
- data/ext/nmatrix/storage/dense.cpp +665 -0
- data/ext/nmatrix/storage/dense.h +116 -0
- data/ext/nmatrix/storage/list.cpp +1088 -0
- data/ext/nmatrix/storage/list.h +129 -0
- data/ext/nmatrix/storage/storage.cpp +658 -0
- data/ext/nmatrix/storage/storage.h +99 -0
- data/ext/nmatrix/storage/yale.cpp +1601 -0
- data/ext/nmatrix/storage/yale.h +208 -0
- data/ext/nmatrix/ttable_helper.rb +126 -0
- data/ext/nmatrix/{yale/smmp1_header.template.c → types.h} +36 -9
- data/ext/nmatrix/util/io.cpp +295 -0
- data/ext/nmatrix/util/io.h +117 -0
- data/ext/nmatrix/util/lapack.h +1175 -0
- data/ext/nmatrix/util/math.cpp +557 -0
- data/ext/nmatrix/util/math.h +1363 -0
- data/ext/nmatrix/util/sl_list.cpp +475 -0
- data/ext/nmatrix/util/sl_list.h +255 -0
- data/ext/nmatrix/util/util.h +78 -0
- data/lib/nmatrix/blas.rb +70 -0
- data/lib/nmatrix/io/mat5_reader.rb +567 -0
- data/lib/nmatrix/io/mat_reader.rb +162 -0
- data/lib/{string.rb → nmatrix/monkeys.rb} +49 -2
- data/lib/nmatrix/nmatrix.rb +199 -0
- data/lib/nmatrix/nvector.rb +103 -0
- data/lib/nmatrix/version.rb +27 -0
- data/lib/nmatrix.rb +22 -230
- data/nmatrix.gemspec +59 -0
- data/scripts/mac-brew-gcc.sh +47 -0
- data/spec/4x4_sparse.mat +0 -0
- data/spec/4x5_dense.mat +0 -0
- data/spec/blas_spec.rb +47 -0
- data/spec/elementwise_spec.rb +164 -0
- data/spec/io_spec.rb +60 -0
- data/spec/lapack_spec.rb +52 -0
- data/spec/math_spec.rb +96 -0
- data/spec/nmatrix_spec.rb +93 -89
- data/spec/nmatrix_yale_spec.rb +52 -36
- data/spec/nvector_spec.rb +1 -1
- data/spec/slice_spec.rb +257 -0
- data/spec/spec_helper.rb +51 -0
- data/spec/utm5940.mtx +83844 -0
- metadata +113 -71
- data/.autotest +0 -23
- data/.gemtest +0 -0
- data/ext/nmatrix/cblas.c +0 -150
- data/ext/nmatrix/dense/blas_header.template.c +0 -52
- data/ext/nmatrix/dense/elementwise.template.c +0 -107
- data/ext/nmatrix/dense/gemm.template.c +0 -159
- data/ext/nmatrix/dense/gemv.template.c +0 -130
- data/ext/nmatrix/dense/rationalmath.template.c +0 -68
- data/ext/nmatrix/dense.c +0 -307
- data/ext/nmatrix/depend +0 -18
- data/ext/nmatrix/generator/syntax_tree.rb +0 -481
- data/ext/nmatrix/generator.rb +0 -594
- data/ext/nmatrix/list.c +0 -774
- data/ext/nmatrix/nmatrix.c +0 -1977
- data/ext/nmatrix/rational.c +0 -98
- data/ext/nmatrix/yale/complexmath.template.c +0 -71
- data/ext/nmatrix/yale/elementwise.template.c +0 -46
- data/ext/nmatrix/yale/elementwise_op.template.c +0 -73
- data/ext/nmatrix/yale/numbmm.template.c +0 -94
- data/ext/nmatrix/yale/smmp1.template.c +0 -21
- data/ext/nmatrix/yale/smmp2.template.c +0 -43
- data/ext/nmatrix/yale/smmp2_header.template.c +0 -46
- data/ext/nmatrix/yale/sort_columns.template.c +0 -56
- data/ext/nmatrix/yale/symbmm.template.c +0 -54
- data/ext/nmatrix/yale/transp.template.c +0 -68
- data/ext/nmatrix/yale.c +0 -726
- data/lib/array.rb +0 -67
- data/spec/syntax_tree_spec.rb +0 -46
|
@@ -0,0 +1,1088 @@
|
|
|
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
|
+
/*
|
|
30
|
+
* Standard Includes
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
#include <ruby.h>
|
|
34
|
+
#include <algorithm> // std::min
|
|
35
|
+
#include <iostream>
|
|
36
|
+
|
|
37
|
+
/*
|
|
38
|
+
* Project Includes
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
#include "types.h"
|
|
42
|
+
|
|
43
|
+
#include "data/data.h"
|
|
44
|
+
|
|
45
|
+
#include "common.h"
|
|
46
|
+
#include "list.h"
|
|
47
|
+
|
|
48
|
+
#include "util/math.h"
|
|
49
|
+
#include "util/sl_list.h"
|
|
50
|
+
|
|
51
|
+
/*
|
|
52
|
+
* Macros
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
/*
|
|
56
|
+
* Global Variables
|
|
57
|
+
*/
|
|
58
|
+
|
|
59
|
+
namespace nm { namespace list_storage {
|
|
60
|
+
|
|
61
|
+
/*
|
|
62
|
+
* Forward Declarations
|
|
63
|
+
*/
|
|
64
|
+
|
|
65
|
+
template <typename LDType, typename RDType>
|
|
66
|
+
static LIST_STORAGE* cast_copy(const LIST_STORAGE* rhs, dtype_t new_dtype);
|
|
67
|
+
|
|
68
|
+
template <typename LDType, typename RDType>
|
|
69
|
+
static bool eqeq(const LIST_STORAGE* left, const LIST_STORAGE* right);
|
|
70
|
+
|
|
71
|
+
template <ewop_t op, typename LDType, typename RDType>
|
|
72
|
+
static void* ew_op(LIST* dest, const LIST* left, const void* l_default, const LIST* right, const void* r_default, const size_t* shape, size_t dim);
|
|
73
|
+
|
|
74
|
+
template <ewop_t op, typename LDType, typename RDType>
|
|
75
|
+
static void ew_op_prime(LIST* dest, LDType d_default, const LIST* left, LDType l_default, const LIST* right, RDType r_default, const size_t* shape, size_t last_level, size_t level);
|
|
76
|
+
|
|
77
|
+
template <ewop_t op, typename LDType, typename RDType>
|
|
78
|
+
static void ew_comp_prime(LIST* dest, uint8_t d_default, const LIST* left, LDType l_default, const LIST* right, RDType r_default, const size_t* shape, size_t last_level, size_t level);
|
|
79
|
+
|
|
80
|
+
} // end of namespace list_storage
|
|
81
|
+
|
|
82
|
+
extern "C" {
|
|
83
|
+
|
|
84
|
+
/*
|
|
85
|
+
* Functions
|
|
86
|
+
*/
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
////////////////
|
|
90
|
+
// Lifecycle //
|
|
91
|
+
///////////////
|
|
92
|
+
|
|
93
|
+
/*
|
|
94
|
+
* Creates a list-of-lists(-of-lists-of-lists-etc) storage framework for a
|
|
95
|
+
* matrix.
|
|
96
|
+
*
|
|
97
|
+
* Note: The pointers you pass in for shape and init_val become property of our
|
|
98
|
+
* new storage. You don't need to free them, and you shouldn't re-use them.
|
|
99
|
+
*/
|
|
100
|
+
LIST_STORAGE* nm_list_storage_create(dtype_t dtype, size_t* shape, size_t dim, void* init_val) {
|
|
101
|
+
LIST_STORAGE* s;
|
|
102
|
+
|
|
103
|
+
s = ALLOC( LIST_STORAGE );
|
|
104
|
+
|
|
105
|
+
s->dim = dim;
|
|
106
|
+
s->shape = shape;
|
|
107
|
+
s->dtype = dtype;
|
|
108
|
+
|
|
109
|
+
s->offset = ALLOC_N(size_t, s->dim);
|
|
110
|
+
memset(s->offset, 0, s->dim * sizeof(size_t));
|
|
111
|
+
|
|
112
|
+
s->rows = list::create();
|
|
113
|
+
s->default_val = init_val;
|
|
114
|
+
s->count = 1;
|
|
115
|
+
s->src = s;
|
|
116
|
+
|
|
117
|
+
return s;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/*
|
|
121
|
+
* Documentation goes here.
|
|
122
|
+
*/
|
|
123
|
+
void nm_list_storage_delete(STORAGE* s) {
|
|
124
|
+
if (s) {
|
|
125
|
+
LIST_STORAGE* storage = (LIST_STORAGE*)s;
|
|
126
|
+
if (storage->count-- == 1) {
|
|
127
|
+
list::del( storage->rows, storage->dim - 1 );
|
|
128
|
+
|
|
129
|
+
free(storage->shape);
|
|
130
|
+
free(storage->offset);
|
|
131
|
+
free(storage->default_val);
|
|
132
|
+
free(s);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/*
|
|
138
|
+
* Documentation goes here.
|
|
139
|
+
*/
|
|
140
|
+
void nm_list_storage_delete_ref(STORAGE* s) {
|
|
141
|
+
if (s) {
|
|
142
|
+
LIST_STORAGE* storage = (LIST_STORAGE*)s;
|
|
143
|
+
|
|
144
|
+
nm_list_storage_delete( reinterpret_cast<STORAGE*>(storage->src ) );
|
|
145
|
+
free(storage->shape);
|
|
146
|
+
free(storage->offset);
|
|
147
|
+
free(s);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/*
|
|
152
|
+
* Documentation goes here.
|
|
153
|
+
*/
|
|
154
|
+
void nm_list_storage_mark(void* storage_base) {
|
|
155
|
+
LIST_STORAGE* storage = (LIST_STORAGE*)storage_base;
|
|
156
|
+
|
|
157
|
+
if (storage && storage->dtype == RUBYOBJ) {
|
|
158
|
+
rb_gc_mark(*((VALUE*)(storage->default_val)));
|
|
159
|
+
list::mark(storage->rows, storage->dim - 1);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
///////////////
|
|
164
|
+
// Accessors //
|
|
165
|
+
///////////////
|
|
166
|
+
|
|
167
|
+
/*
|
|
168
|
+
* Documentation goes here.
|
|
169
|
+
*/
|
|
170
|
+
NODE* list_storage_get_single_node(LIST_STORAGE* s, SLICE* slice)
|
|
171
|
+
{
|
|
172
|
+
size_t r;
|
|
173
|
+
LIST* l = s->rows;
|
|
174
|
+
NODE* n;
|
|
175
|
+
|
|
176
|
+
for (r = 0; r < s->dim; r++) {
|
|
177
|
+
n = list::find(l, s->offset[r] + slice->coords[r]);
|
|
178
|
+
if (n) l = reinterpret_cast<LIST*>(n->val);
|
|
179
|
+
else return NULL;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return n;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
static LIST* slice_copy(const LIST_STORAGE *src, LIST *src_rows, size_t *coords, size_t *lengths, size_t n)
|
|
187
|
+
{
|
|
188
|
+
NODE *src_node;
|
|
189
|
+
LIST *dst_rows = NULL;
|
|
190
|
+
void *val = NULL;
|
|
191
|
+
int key;
|
|
192
|
+
|
|
193
|
+
dst_rows = list::create();
|
|
194
|
+
src_node = src_rows->first;
|
|
195
|
+
|
|
196
|
+
while (src_node) {
|
|
197
|
+
key = src_node->key - (src->offset[n] + coords[n]);
|
|
198
|
+
|
|
199
|
+
if (key >= 0 && (size_t)key < lengths[n]) {
|
|
200
|
+
if (src->dim - n > 1) {
|
|
201
|
+
val = slice_copy(src,
|
|
202
|
+
reinterpret_cast<LIST*>(src_node->val),
|
|
203
|
+
coords,
|
|
204
|
+
lengths,
|
|
205
|
+
n + 1);
|
|
206
|
+
|
|
207
|
+
if (val)
|
|
208
|
+
list::insert_with_copy(dst_rows, key, val, sizeof(LIST));
|
|
209
|
+
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
list::insert_with_copy(dst_rows, key, src_node->val, DTYPE_SIZES[src->dtype]);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
src_node = src_node->next;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return dst_rows;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/*
|
|
223
|
+
* Documentation goes here.
|
|
224
|
+
*/
|
|
225
|
+
void* nm_list_storage_get(STORAGE* storage, SLICE* slice) {
|
|
226
|
+
LIST_STORAGE* s = (LIST_STORAGE*)storage;
|
|
227
|
+
LIST_STORAGE* ns = NULL;
|
|
228
|
+
NODE* n;
|
|
229
|
+
|
|
230
|
+
if (slice->single) {
|
|
231
|
+
n = list_storage_get_single_node(s, slice);
|
|
232
|
+
return (n ? n->val : s->default_val);
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
void *init_val = ALLOC_N(char, DTYPE_SIZES[s->dtype]);
|
|
236
|
+
memcpy(init_val, s->default_val, DTYPE_SIZES[s->dtype]);
|
|
237
|
+
|
|
238
|
+
size_t *shape = ALLOC_N(size_t, s->dim);
|
|
239
|
+
memcpy(shape, slice->lengths, sizeof(size_t) * s->dim);
|
|
240
|
+
|
|
241
|
+
ns = nm_list_storage_create(s->dtype, shape, s->dim, init_val);
|
|
242
|
+
|
|
243
|
+
ns->rows = slice_copy(s, s->rows, slice->coords, slice->lengths, 0);
|
|
244
|
+
return ns;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/*
|
|
249
|
+
* Get the contents of some set of coordinates. Note: Does not make a copy!
|
|
250
|
+
* Don't free!
|
|
251
|
+
*/
|
|
252
|
+
void* nm_list_storage_ref(STORAGE* storage, SLICE* slice) {
|
|
253
|
+
LIST_STORAGE* s = (LIST_STORAGE*)storage;
|
|
254
|
+
LIST_STORAGE* ns = NULL;
|
|
255
|
+
NODE* n;
|
|
256
|
+
|
|
257
|
+
//TODO: It needs a refactoring.
|
|
258
|
+
if (slice->single) {
|
|
259
|
+
n = list_storage_get_single_node(s, slice);
|
|
260
|
+
return (n ? n->val : s->default_val);
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
ns = ALLOC( LIST_STORAGE );
|
|
264
|
+
|
|
265
|
+
ns->dim = s->dim;
|
|
266
|
+
ns->dtype = s->dtype;
|
|
267
|
+
ns->offset = ALLOC_N(size_t, ns->dim);
|
|
268
|
+
ns->shape = ALLOC_N(size_t, ns->dim);
|
|
269
|
+
|
|
270
|
+
for (size_t i = 0; i < ns->dim; ++i) {
|
|
271
|
+
ns->offset[i] = slice->coords[i] + s->offset[i];
|
|
272
|
+
ns->shape[i] = slice->lengths[i];
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
ns->rows = s->rows;
|
|
276
|
+
ns->default_val = s->default_val;
|
|
277
|
+
|
|
278
|
+
s->src->count++;
|
|
279
|
+
ns->src = s->src;
|
|
280
|
+
|
|
281
|
+
return ns;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/*
|
|
286
|
+
* Documentation goes here.
|
|
287
|
+
*
|
|
288
|
+
* TODO: Allow this function to accept an entire row and not just one value -- for slicing
|
|
289
|
+
*/
|
|
290
|
+
void* nm_list_storage_insert(STORAGE* storage, SLICE* slice, void* val) {
|
|
291
|
+
LIST_STORAGE* s = (LIST_STORAGE*)storage;
|
|
292
|
+
// Pretend dims = 2
|
|
293
|
+
// Then coords is going to be size 2
|
|
294
|
+
// So we need to find out if some key already exists
|
|
295
|
+
size_t r;
|
|
296
|
+
NODE* n;
|
|
297
|
+
LIST* l = s->rows;
|
|
298
|
+
|
|
299
|
+
// drill down into the structure
|
|
300
|
+
for (r = s->dim; r > 1; --r) {
|
|
301
|
+
n = list::insert(l, false, s->offset[s->dim - r] + slice->coords[s->dim - r], list::create());
|
|
302
|
+
l = reinterpret_cast<LIST*>(n->val);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
n = list::insert(l, true, s->offset[s->dim - r] + slice->coords[s->dim - r], val);
|
|
306
|
+
return n->val;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/*
|
|
310
|
+
* Remove an item from list storage.
|
|
311
|
+
*/
|
|
312
|
+
void* nm_list_storage_remove(STORAGE* storage, SLICE* slice) {
|
|
313
|
+
LIST_STORAGE* s = (LIST_STORAGE*)storage;
|
|
314
|
+
void* rm = NULL;
|
|
315
|
+
|
|
316
|
+
// This returns a boolean, which will indicate whether s->rows is empty.
|
|
317
|
+
// We can safely ignore it, since we never want to delete s->rows until
|
|
318
|
+
// it's time to destroy the LIST_STORAGE object.
|
|
319
|
+
list::remove_recursive(s->rows, slice->coords, s->offset, 0, s->dim, rm);
|
|
320
|
+
|
|
321
|
+
return rm;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
///////////
|
|
325
|
+
// Tests //
|
|
326
|
+
///////////
|
|
327
|
+
|
|
328
|
+
/*
|
|
329
|
+
* Comparison of contents for list storage.
|
|
330
|
+
*/
|
|
331
|
+
bool nm_list_storage_eqeq(const STORAGE* left, const STORAGE* right) {
|
|
332
|
+
NAMED_LR_DTYPE_TEMPLATE_TABLE(ttable, nm::list_storage::eqeq, bool, const LIST_STORAGE* left, const LIST_STORAGE* right);
|
|
333
|
+
|
|
334
|
+
return ttable[left->dtype][right->dtype]((const LIST_STORAGE*)left, (const LIST_STORAGE*)right);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
//////////
|
|
338
|
+
// Math //
|
|
339
|
+
//////////
|
|
340
|
+
|
|
341
|
+
/*
|
|
342
|
+
* Element-wise operations for list storage.
|
|
343
|
+
*/
|
|
344
|
+
STORAGE* nm_list_storage_ew_op(nm::ewop_t op, const STORAGE* left, const STORAGE* right) {
|
|
345
|
+
rb_raise(rb_eNotImpError, "elementwise operations for list storage currently broken");
|
|
346
|
+
|
|
347
|
+
OP_LR_DTYPE_TEMPLATE_TABLE(nm::list_storage::ew_op, void*, LIST* dest, const LIST* left, const void* l_default, const LIST* right, const void* r_default, const size_t* shape, size_t dim);
|
|
348
|
+
|
|
349
|
+
// We may need to upcast our arguments to the same type.
|
|
350
|
+
dtype_t new_dtype = Upcast[left->dtype][right->dtype];
|
|
351
|
+
|
|
352
|
+
// Make sure we allocate a byte-storing matrix for comparison operations; otherwise, use the argument dtype (new_dtype)
|
|
353
|
+
dtype_t result_dtype = static_cast<uint8_t>(op) < NUM_NONCOMP_EWOPS ? new_dtype : BYTE;
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
const LIST_STORAGE* l = reinterpret_cast<const LIST_STORAGE*>(left),
|
|
358
|
+
* r = reinterpret_cast<const LIST_STORAGE*>(right);
|
|
359
|
+
|
|
360
|
+
LIST_STORAGE* new_l = NULL;
|
|
361
|
+
|
|
362
|
+
// Allocate a new shape array for the resulting matrix.
|
|
363
|
+
size_t* new_shape = (size_t*)calloc(l->dim, sizeof(size_t));
|
|
364
|
+
memcpy(new_shape, left->shape, sizeof(size_t) * l->dim);
|
|
365
|
+
|
|
366
|
+
// Create the result matrix.
|
|
367
|
+
LIST_STORAGE* result = nm_list_storage_create(result_dtype, new_shape, left->dim, NULL);
|
|
368
|
+
|
|
369
|
+
/*
|
|
370
|
+
* Call the templated elementwise multiplication function and set the default
|
|
371
|
+
* value for the resulting matrix.
|
|
372
|
+
*/
|
|
373
|
+
if (new_dtype != left->dtype) {
|
|
374
|
+
// Upcast the left-hand side if necessary.
|
|
375
|
+
new_l = reinterpret_cast<LIST_STORAGE*>(nm_list_storage_cast_copy(l, new_dtype));
|
|
376
|
+
|
|
377
|
+
result->default_val =
|
|
378
|
+
ttable[op][new_l->dtype][right->dtype](result->rows, new_l->rows, new_l->default_val, r->rows, r->default_val, result->shape, result->dim);
|
|
379
|
+
|
|
380
|
+
// Delete the temporary left-hand side matrix.
|
|
381
|
+
nm_list_storage_delete(reinterpret_cast<STORAGE*>(new_l));
|
|
382
|
+
|
|
383
|
+
} else {
|
|
384
|
+
result->default_val =
|
|
385
|
+
ttable[op][left->dtype][right->dtype](result->rows, l->rows, l->default_val, r->rows, r->default_val, result->shape, result->dim);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
return result;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
/*
|
|
393
|
+
* List storage matrix multiplication.
|
|
394
|
+
*/
|
|
395
|
+
STORAGE* nm_list_storage_matrix_multiply(const STORAGE_PAIR& casted_storage, size_t* resulting_shape, bool vector) {
|
|
396
|
+
free(resulting_shape);
|
|
397
|
+
rb_raise(rb_eNotImpError, "multiplication not implemented for list-of-list matrices");
|
|
398
|
+
return NULL;
|
|
399
|
+
//DTYPE_TEMPLATE_TABLE(dense_storage::matrix_multiply, NMATRIX*, STORAGE_PAIR, size_t*, bool);
|
|
400
|
+
|
|
401
|
+
//return ttable[reinterpret_cast<DENSE_STORAGE*>(casted_storage.left)->dtype](casted_storage, resulting_shape, vector);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
/*
|
|
406
|
+
* List storage to Hash conversion. Uses Hashes with default values, so you can continue to pretend
|
|
407
|
+
* it's a sparse matrix.
|
|
408
|
+
*/
|
|
409
|
+
VALUE nm_list_storage_to_hash(const LIST_STORAGE* s, const dtype_t dtype) {
|
|
410
|
+
|
|
411
|
+
// Get the default value for the list storage.
|
|
412
|
+
VALUE default_value = rubyobj_from_cval(s->default_val, dtype).rval;
|
|
413
|
+
|
|
414
|
+
// Recursively copy each dimension of the matrix into a nested hash.
|
|
415
|
+
return nm_list_copy_to_hash(s->rows, dtype, s->dim - 1, default_value);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/////////////
|
|
419
|
+
// Utility //
|
|
420
|
+
/////////////
|
|
421
|
+
|
|
422
|
+
/*
|
|
423
|
+
* Recursively count the non-zero elements in a list storage object.
|
|
424
|
+
*/
|
|
425
|
+
size_t nm_list_storage_count_elements_r(const LIST* l, size_t recursions) {
|
|
426
|
+
size_t count = 0;
|
|
427
|
+
NODE* curr = l->first;
|
|
428
|
+
|
|
429
|
+
if (recursions) {
|
|
430
|
+
while (curr) {
|
|
431
|
+
count += nm_list_storage_count_elements_r(reinterpret_cast<const LIST*>(curr->val), recursions - 1);
|
|
432
|
+
curr = curr->next;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
} else {
|
|
436
|
+
while (curr) {
|
|
437
|
+
++count;
|
|
438
|
+
curr = curr->next;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
return count;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/*
|
|
446
|
+
* Count non-diagonal non-zero elements.
|
|
447
|
+
*/
|
|
448
|
+
size_t nm_list_storage_count_nd_elements(const LIST_STORAGE* s) {
|
|
449
|
+
NODE *i_curr, *j_curr;
|
|
450
|
+
size_t count = 0;
|
|
451
|
+
|
|
452
|
+
if (s->dim != 2) {
|
|
453
|
+
rb_raise(rb_eNotImpError, "non-diagonal element counting only defined for dim = 2");
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
for (i_curr = s->rows->first; i_curr; i_curr = i_curr->next) {
|
|
457
|
+
int i = i_curr->key - s->offset[0];
|
|
458
|
+
if (i < 0 || i >= (int)s->shape[0]) continue;
|
|
459
|
+
|
|
460
|
+
for (j_curr = ((LIST*)(i_curr->val))->first; j_curr; j_curr = j_curr->next) {
|
|
461
|
+
int j = j_curr->key - s->offset[1];
|
|
462
|
+
if (j < 0 || j >= (int)s->shape[1]) continue;
|
|
463
|
+
|
|
464
|
+
if (i != j) ++count;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
return count;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/////////////////////////
|
|
472
|
+
// Copying and Casting //
|
|
473
|
+
/////////////////////////
|
|
474
|
+
//
|
|
475
|
+
/*
|
|
476
|
+
* List storage copy constructor C access.
|
|
477
|
+
*/
|
|
478
|
+
|
|
479
|
+
LIST_STORAGE* nm_list_storage_copy(const LIST_STORAGE* rhs)
|
|
480
|
+
{
|
|
481
|
+
size_t *shape = ALLOC_N(size_t, rhs->dim);
|
|
482
|
+
memcpy(shape, rhs->shape, sizeof(size_t) * rhs->dim);
|
|
483
|
+
|
|
484
|
+
void *init_val = ALLOC_N(char, DTYPE_SIZES[rhs->dtype]);
|
|
485
|
+
memcpy(init_val, rhs->default_val, DTYPE_SIZES[rhs->dtype]);
|
|
486
|
+
|
|
487
|
+
LIST_STORAGE* lhs = nm_list_storage_create(rhs->dtype, shape, rhs->dim, init_val);
|
|
488
|
+
|
|
489
|
+
lhs->rows = slice_copy(rhs, rhs->rows, lhs->offset, lhs->shape, 0);
|
|
490
|
+
|
|
491
|
+
return lhs;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/*
|
|
495
|
+
* List storage copy constructor C access with casting.
|
|
496
|
+
*/
|
|
497
|
+
STORAGE* nm_list_storage_cast_copy(const STORAGE* rhs, dtype_t new_dtype) {
|
|
498
|
+
NAMED_LR_DTYPE_TEMPLATE_TABLE(ttable, nm::list_storage::cast_copy, LIST_STORAGE*, const LIST_STORAGE* rhs, dtype_t new_dtype);
|
|
499
|
+
|
|
500
|
+
return (STORAGE*)ttable[new_dtype][rhs->dtype]((LIST_STORAGE*)rhs, new_dtype);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
/*
|
|
505
|
+
* List storage copy constructor for transposing.
|
|
506
|
+
*/
|
|
507
|
+
STORAGE* nm_list_storage_copy_transposed(const STORAGE* rhs_base) {
|
|
508
|
+
rb_raise(rb_eNotImpError, "list storage transpose not yet implemented");
|
|
509
|
+
return NULL;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
} // end of extern "C" block
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
/////////////////////////
|
|
517
|
+
// Templated Functions //
|
|
518
|
+
/////////////////////////
|
|
519
|
+
|
|
520
|
+
namespace list_storage {
|
|
521
|
+
|
|
522
|
+
/*
|
|
523
|
+
* List storage copy constructor for changing dtypes.
|
|
524
|
+
*/
|
|
525
|
+
template <typename LDType, typename RDType>
|
|
526
|
+
static LIST_STORAGE* cast_copy(const LIST_STORAGE* rhs, dtype_t new_dtype) {
|
|
527
|
+
|
|
528
|
+
// allocate and copy shape
|
|
529
|
+
size_t* shape = ALLOC_N(size_t, rhs->dim);
|
|
530
|
+
memcpy(shape, rhs->shape, rhs->dim * sizeof(size_t));
|
|
531
|
+
|
|
532
|
+
// copy default value
|
|
533
|
+
LDType* default_val = ALLOC_N(LDType, 1);
|
|
534
|
+
*default_val = *reinterpret_cast<RDType*>(rhs->default_val);
|
|
535
|
+
|
|
536
|
+
LIST_STORAGE* lhs = nm_list_storage_create(new_dtype, shape, rhs->dim, default_val);
|
|
537
|
+
lhs->rows = list::create();
|
|
538
|
+
|
|
539
|
+
// TODO: Needs optimization. When matrix is reference it is copped twice.
|
|
540
|
+
if (rhs->src == rhs)
|
|
541
|
+
list::cast_copy_contents<LDType, RDType>(lhs->rows, rhs->rows, rhs->dim - 1);
|
|
542
|
+
else {
|
|
543
|
+
LIST_STORAGE *tmp = nm_list_storage_copy(rhs);
|
|
544
|
+
list::cast_copy_contents<LDType, RDType>(lhs->rows, tmp->rows, rhs->dim - 1);
|
|
545
|
+
nm_list_storage_delete(tmp);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
return lhs;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
/*
|
|
553
|
+
* Do these two dense matrices of the same dtype have exactly the same
|
|
554
|
+
* contents?
|
|
555
|
+
*/
|
|
556
|
+
template <typename LDType, typename RDType>
|
|
557
|
+
bool eqeq(const LIST_STORAGE* left, const LIST_STORAGE* right) {
|
|
558
|
+
bool result;
|
|
559
|
+
|
|
560
|
+
// in certain cases, we need to keep track of the number of elements checked.
|
|
561
|
+
size_t num_checked = 0,
|
|
562
|
+
max_elements = nm_storage_count_max_elements(left);
|
|
563
|
+
LIST_STORAGE *tmp1 = NULL, *tmp2 = NULL;
|
|
564
|
+
|
|
565
|
+
if (!left->rows->first) {
|
|
566
|
+
// Easy: both lists empty -- just compare default values
|
|
567
|
+
if (!right->rows->first) {
|
|
568
|
+
return *reinterpret_cast<LDType*>(left->default_val) == *reinterpret_cast<RDType*>(right->default_val);
|
|
569
|
+
|
|
570
|
+
} else if (!list::eqeq_value<RDType,LDType>(right->rows, reinterpret_cast<LDType*>(left->default_val), left->dim-1, num_checked)) {
|
|
571
|
+
// Left empty, right not empty. Do all values in right == left->default_val?
|
|
572
|
+
return false;
|
|
573
|
+
|
|
574
|
+
} else if (num_checked < max_elements) {
|
|
575
|
+
// If the matrix isn't full, we also need to compare default values.
|
|
576
|
+
return *reinterpret_cast<LDType*>(left->default_val) == *reinterpret_cast<RDType*>(right->default_val);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
} else if (!right->rows->first) {
|
|
580
|
+
// fprintf(stderr, "!right->rows true\n");
|
|
581
|
+
// Right empty, left not empty. Do all values in left == right->default_val?
|
|
582
|
+
if (!list::eqeq_value<LDType,RDType>(left->rows, reinterpret_cast<RDType*>(right->default_val), left->dim-1, num_checked)) {
|
|
583
|
+
return false;
|
|
584
|
+
|
|
585
|
+
} else if (num_checked < max_elements) {
|
|
586
|
+
// If the matrix isn't full, we also need to compare default values.
|
|
587
|
+
return *reinterpret_cast<LDType*>(left->default_val) == *reinterpret_cast<RDType*>(right->default_val);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
} else {
|
|
591
|
+
// fprintf(stderr, "both matrices have entries\n");
|
|
592
|
+
// Hardest case. Compare lists node by node. Let's make it simpler by requiring that both have the same default value
|
|
593
|
+
|
|
594
|
+
// left is reference
|
|
595
|
+
if (left->src != left && right->src == right) {
|
|
596
|
+
tmp1 = nm_list_storage_copy(left);
|
|
597
|
+
result = list::eqeq<LDType,RDType>(tmp1->rows, right->rows, reinterpret_cast<LDType*>(left->default_val), reinterpret_cast<RDType*>(right->default_val), left->dim-1, num_checked);
|
|
598
|
+
nm_list_storage_delete(tmp1);
|
|
599
|
+
}
|
|
600
|
+
// right is reference
|
|
601
|
+
if (left->src == left && right->src != right) {
|
|
602
|
+
tmp2 = nm_list_storage_copy(right);
|
|
603
|
+
result = list::eqeq<LDType,RDType>(left->rows, tmp2->rows, reinterpret_cast<LDType*>(left->default_val), reinterpret_cast<RDType*>(right->default_val), left->dim-1, num_checked);
|
|
604
|
+
nm_list_storage_delete(tmp2);
|
|
605
|
+
}
|
|
606
|
+
// both are references
|
|
607
|
+
if (left->src != left && right->src != right) {
|
|
608
|
+
tmp1 = nm_list_storage_copy(left);
|
|
609
|
+
tmp2 = nm_list_storage_copy(right);
|
|
610
|
+
result = list::eqeq<LDType,RDType>(tmp1->rows, tmp2->rows, reinterpret_cast<LDType*>(left->default_val), reinterpret_cast<RDType*>(right->default_val), left->dim-1, num_checked);
|
|
611
|
+
nm_list_storage_delete(tmp1);
|
|
612
|
+
nm_list_storage_delete(tmp2);
|
|
613
|
+
}
|
|
614
|
+
// both are normal matricies
|
|
615
|
+
if (left->src == left && right->src == right)
|
|
616
|
+
result = list::eqeq<LDType,RDType>(left->rows, right->rows, reinterpret_cast<LDType*>(left->default_val), reinterpret_cast<RDType*>(right->default_val), left->dim-1, num_checked);
|
|
617
|
+
|
|
618
|
+
if (!result){
|
|
619
|
+
return result;
|
|
620
|
+
} else if (num_checked < max_elements) {
|
|
621
|
+
return *reinterpret_cast<LDType*>(left->default_val) == *reinterpret_cast<RDType*>(right->default_val);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
return true;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/*
|
|
629
|
+
* List storage element-wise operations (including comparisons).
|
|
630
|
+
*/
|
|
631
|
+
template <ewop_t op, typename LDType, typename RDType>
|
|
632
|
+
static void* ew_op(LIST* dest, const LIST* left, const void* l_default, const LIST* right, const void* r_default, const size_t* shape, size_t dim) {
|
|
633
|
+
|
|
634
|
+
if (static_cast<uint8_t>(op) < NUM_NONCOMP_EWOPS) {
|
|
635
|
+
|
|
636
|
+
/*
|
|
637
|
+
* Allocate space for, and calculate, the default value for the destination
|
|
638
|
+
* matrix.
|
|
639
|
+
*/
|
|
640
|
+
LDType* d_default_mem = ALLOC(LDType);
|
|
641
|
+
*d_default_mem = ew_op_switch<op, LDType, RDType>(*reinterpret_cast<const LDType*>(l_default), *reinterpret_cast<const RDType*>(r_default));
|
|
642
|
+
|
|
643
|
+
// Now that setup is done call the actual elementwise operation function.
|
|
644
|
+
ew_op_prime<op, LDType, RDType>(dest, *reinterpret_cast<const LDType*>(d_default_mem),
|
|
645
|
+
left, *reinterpret_cast<const LDType*>(l_default),
|
|
646
|
+
right, *reinterpret_cast<const RDType*>(r_default),
|
|
647
|
+
shape, dim - 1, 0);
|
|
648
|
+
|
|
649
|
+
// Return a pointer to the destination matrix's default value.
|
|
650
|
+
return d_default_mem;
|
|
651
|
+
|
|
652
|
+
} else { // Handle comparison operations in a similar manner.
|
|
653
|
+
/*
|
|
654
|
+
* Allocate a byte for default, and set default value to 0.
|
|
655
|
+
*/
|
|
656
|
+
uint8_t* d_default_mem = ALLOC(uint8_t);
|
|
657
|
+
*d_default_mem = 0;
|
|
658
|
+
switch (op) {
|
|
659
|
+
case EW_EQEQ:
|
|
660
|
+
*d_default_mem = *reinterpret_cast<const LDType*>(l_default) == *reinterpret_cast<const RDType*>(r_default);
|
|
661
|
+
break;
|
|
662
|
+
|
|
663
|
+
case EW_NEQ:
|
|
664
|
+
*d_default_mem = *reinterpret_cast<const LDType*>(l_default) != *reinterpret_cast<const RDType*>(r_default);
|
|
665
|
+
break;
|
|
666
|
+
|
|
667
|
+
case EW_LT:
|
|
668
|
+
*d_default_mem = *reinterpret_cast<const LDType*>(l_default) < *reinterpret_cast<const RDType*>(r_default);
|
|
669
|
+
break;
|
|
670
|
+
|
|
671
|
+
case EW_GT:
|
|
672
|
+
*d_default_mem = *reinterpret_cast<const LDType*>(l_default) > *reinterpret_cast<const RDType*>(r_default);
|
|
673
|
+
break;
|
|
674
|
+
|
|
675
|
+
case EW_LEQ:
|
|
676
|
+
*d_default_mem = *reinterpret_cast<const LDType*>(l_default) <= *reinterpret_cast<const RDType*>(r_default);
|
|
677
|
+
break;
|
|
678
|
+
|
|
679
|
+
case EW_GEQ:
|
|
680
|
+
*d_default_mem = *reinterpret_cast<const LDType*>(l_default) >= *reinterpret_cast<const RDType*>(r_default);
|
|
681
|
+
break;
|
|
682
|
+
|
|
683
|
+
default:
|
|
684
|
+
rb_raise(rb_eStandardError, "this should not happen");
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
// Now that setup is done call the actual elementwise comparison function.
|
|
688
|
+
ew_comp_prime<op, LDType, RDType>(dest, *reinterpret_cast<const uint8_t*>(d_default_mem),
|
|
689
|
+
left, *reinterpret_cast<const LDType*>(l_default),
|
|
690
|
+
right, *reinterpret_cast<const RDType*>(r_default),
|
|
691
|
+
shape, dim - 1, 0);
|
|
692
|
+
|
|
693
|
+
// Return a pointer to the destination matrix's default value.
|
|
694
|
+
return d_default_mem;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
|
|
699
|
+
/*
|
|
700
|
+
* List storage element-wise comparisons, recursive helper.
|
|
701
|
+
*/
|
|
702
|
+
template <ewop_t op, typename LDType, typename RDType>
|
|
703
|
+
static void ew_comp_prime(LIST* dest, uint8_t d_default, const LIST* left, LDType l_default, const LIST* right, RDType r_default, const size_t* shape, size_t last_level, size_t level) {
|
|
704
|
+
|
|
705
|
+
static LIST EMPTY_LIST = {NULL};
|
|
706
|
+
|
|
707
|
+
size_t index;
|
|
708
|
+
|
|
709
|
+
uint8_t tmp_result;
|
|
710
|
+
|
|
711
|
+
LIST* new_level = NULL;
|
|
712
|
+
|
|
713
|
+
NODE* l_node = left->first,
|
|
714
|
+
* r_node = right->first,
|
|
715
|
+
* dest_node = NULL;
|
|
716
|
+
|
|
717
|
+
for (index = 0; index < shape[level]; ++index) {
|
|
718
|
+
if (l_node == NULL and r_node == NULL) {
|
|
719
|
+
/*
|
|
720
|
+
* Both source lists are now empty. Because the default value of the
|
|
721
|
+
* destination is already set appropriately we can now return.
|
|
722
|
+
*/
|
|
723
|
+
|
|
724
|
+
return;
|
|
725
|
+
|
|
726
|
+
} else {
|
|
727
|
+
// At least one list still has entries.
|
|
728
|
+
|
|
729
|
+
if (l_node == NULL and r_node->key == index) {
|
|
730
|
+
/*
|
|
731
|
+
* One source list is empty, but the index has caught up to the key of
|
|
732
|
+
* the other list.
|
|
733
|
+
*/
|
|
734
|
+
|
|
735
|
+
if (level == last_level) {
|
|
736
|
+
switch (op) {
|
|
737
|
+
case EW_EQEQ:
|
|
738
|
+
tmp_result = (uint8_t)(l_default == *reinterpret_cast<RDType*>(r_node->val));
|
|
739
|
+
break;
|
|
740
|
+
|
|
741
|
+
case EW_NEQ:
|
|
742
|
+
tmp_result = (uint8_t)(l_default != *reinterpret_cast<RDType*>(r_node->val));
|
|
743
|
+
break;
|
|
744
|
+
|
|
745
|
+
case EW_LT:
|
|
746
|
+
tmp_result = (uint8_t)(l_default < *reinterpret_cast<RDType*>(r_node->val));
|
|
747
|
+
break;
|
|
748
|
+
|
|
749
|
+
case EW_GT:
|
|
750
|
+
tmp_result = (uint8_t)(l_default > *reinterpret_cast<RDType*>(r_node->val));
|
|
751
|
+
break;
|
|
752
|
+
|
|
753
|
+
case EW_LEQ:
|
|
754
|
+
tmp_result = (uint8_t)(l_default <= *reinterpret_cast<RDType*>(r_node->val));
|
|
755
|
+
break;
|
|
756
|
+
|
|
757
|
+
case EW_GEQ:
|
|
758
|
+
tmp_result = (uint8_t)(l_default >= *reinterpret_cast<RDType*>(r_node->val));
|
|
759
|
+
break;
|
|
760
|
+
|
|
761
|
+
default:
|
|
762
|
+
rb_raise(rb_eStandardError, "This should not happen.");
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
if (tmp_result != d_default) {
|
|
766
|
+
dest_node = nm::list::insert_helper(dest, dest_node, index, tmp_result);
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
} else {
|
|
770
|
+
new_level = nm::list::create();
|
|
771
|
+
dest_node = nm::list::insert_helper(dest, dest_node, index, new_level);
|
|
772
|
+
|
|
773
|
+
ew_comp_prime<op, LDType, RDType>(new_level, d_default, &EMPTY_LIST, l_default,
|
|
774
|
+
reinterpret_cast<LIST*>(r_node->val), r_default,
|
|
775
|
+
shape, last_level, level + 1);
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
r_node = r_node->next;
|
|
779
|
+
|
|
780
|
+
} else if (r_node == NULL and l_node->key == index) {
|
|
781
|
+
/*
|
|
782
|
+
* One source list is empty, but the index has caught up to the key of
|
|
783
|
+
* the other list.
|
|
784
|
+
*/
|
|
785
|
+
|
|
786
|
+
if (level == last_level) {
|
|
787
|
+
switch (op) {
|
|
788
|
+
case EW_EQEQ:
|
|
789
|
+
tmp_result = (uint8_t)(*reinterpret_cast<LDType*>(l_node->val) == r_default);
|
|
790
|
+
break;
|
|
791
|
+
|
|
792
|
+
case EW_NEQ:
|
|
793
|
+
tmp_result = (uint8_t)(*reinterpret_cast<LDType*>(l_node->val) != r_default);
|
|
794
|
+
break;
|
|
795
|
+
|
|
796
|
+
case EW_LT:
|
|
797
|
+
tmp_result = (uint8_t)(*reinterpret_cast<LDType*>(l_node->val) < r_default);
|
|
798
|
+
break;
|
|
799
|
+
|
|
800
|
+
case EW_GT:
|
|
801
|
+
tmp_result = (uint8_t)(*reinterpret_cast<LDType*>(l_node->val) > r_default);
|
|
802
|
+
break;
|
|
803
|
+
|
|
804
|
+
case EW_LEQ:
|
|
805
|
+
tmp_result = (uint8_t)(*reinterpret_cast<LDType*>(l_node->val) <= r_default);
|
|
806
|
+
break;
|
|
807
|
+
|
|
808
|
+
case EW_GEQ:
|
|
809
|
+
tmp_result = (uint8_t)(*reinterpret_cast<LDType*>(l_node->val) >= r_default);
|
|
810
|
+
break;
|
|
811
|
+
|
|
812
|
+
default:
|
|
813
|
+
rb_raise(rb_eStandardError, "this should not happen");
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
if (tmp_result != d_default) {
|
|
817
|
+
dest_node = nm::list::insert_helper(dest, dest_node, index, tmp_result);
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
} else {
|
|
821
|
+
new_level = nm::list::create();
|
|
822
|
+
dest_node = nm::list::insert_helper(dest, dest_node, index, new_level);
|
|
823
|
+
|
|
824
|
+
ew_comp_prime<op, LDType, RDType>(new_level, d_default,
|
|
825
|
+
reinterpret_cast<LIST*>(l_node->val), l_default,
|
|
826
|
+
&EMPTY_LIST, r_default,
|
|
827
|
+
shape, last_level, level + 1);
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
l_node = l_node->next;
|
|
831
|
+
|
|
832
|
+
} else if (l_node != NULL and r_node != NULL and index == std::min(l_node->key, r_node->key)) {
|
|
833
|
+
/*
|
|
834
|
+
* Neither list is empty and our index has caught up to one of the
|
|
835
|
+
* source lists.
|
|
836
|
+
*/
|
|
837
|
+
|
|
838
|
+
if (l_node->key == r_node->key) {
|
|
839
|
+
|
|
840
|
+
if (level == last_level) {
|
|
841
|
+
switch (op) {
|
|
842
|
+
case EW_EQEQ:
|
|
843
|
+
tmp_result = (uint8_t)(*reinterpret_cast<LDType*>(l_node->val) == *reinterpret_cast<RDType*>(r_node->val));
|
|
844
|
+
break;
|
|
845
|
+
|
|
846
|
+
case EW_NEQ:
|
|
847
|
+
tmp_result = (uint8_t)(*reinterpret_cast<LDType*>(l_node->val) != *reinterpret_cast<RDType*>(r_node->val));
|
|
848
|
+
break;
|
|
849
|
+
|
|
850
|
+
case EW_LT:
|
|
851
|
+
tmp_result = (uint8_t)(*reinterpret_cast<LDType*>(l_node->val) < *reinterpret_cast<RDType*>(r_node->val));
|
|
852
|
+
break;
|
|
853
|
+
|
|
854
|
+
case EW_GT:
|
|
855
|
+
tmp_result = (uint8_t)(*reinterpret_cast<LDType*>(l_node->val) > *reinterpret_cast<RDType*>(r_node->val));
|
|
856
|
+
break;
|
|
857
|
+
|
|
858
|
+
case EW_LEQ:
|
|
859
|
+
tmp_result = (uint8_t)(*reinterpret_cast<LDType*>(l_node->val) <= *reinterpret_cast<RDType*>(r_node->val));
|
|
860
|
+
break;
|
|
861
|
+
|
|
862
|
+
case EW_GEQ:
|
|
863
|
+
tmp_result = (uint8_t)(*reinterpret_cast<LDType*>(l_node->val) >= *reinterpret_cast<RDType*>(r_node->val));
|
|
864
|
+
break;
|
|
865
|
+
|
|
866
|
+
default:
|
|
867
|
+
rb_raise(rb_eStandardError, "this should not happen");
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
if (tmp_result != d_default) {
|
|
871
|
+
dest_node = nm::list::insert_helper(dest, dest_node, index, tmp_result);
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
} else {
|
|
875
|
+
new_level = nm::list::create();
|
|
876
|
+
dest_node = nm::list::insert_helper(dest, dest_node, index, new_level);
|
|
877
|
+
|
|
878
|
+
ew_comp_prime<op, LDType, RDType>(new_level, d_default,
|
|
879
|
+
reinterpret_cast<LIST*>(l_node->val), l_default,
|
|
880
|
+
reinterpret_cast<LIST*>(r_node->val), r_default,
|
|
881
|
+
shape, last_level, level + 1);
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
l_node = l_node->next;
|
|
885
|
+
r_node = r_node->next;
|
|
886
|
+
|
|
887
|
+
} else if (l_node->key < r_node->key) {
|
|
888
|
+
// Advance the left node knowing that the default value is OK.
|
|
889
|
+
|
|
890
|
+
l_node = l_node->next;
|
|
891
|
+
|
|
892
|
+
} else /* if (l_node->key > r_node->key) */ {
|
|
893
|
+
// Advance the right node knowing that the default value is OK.
|
|
894
|
+
|
|
895
|
+
r_node = r_node->next;
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
} else {
|
|
899
|
+
/*
|
|
900
|
+
* Our index needs to catch up but the default value is OK. This
|
|
901
|
+
* conditional is here only for documentation and should be optimized
|
|
902
|
+
* out.
|
|
903
|
+
*/
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
|
|
910
|
+
|
|
911
|
+
/*
|
|
912
|
+
* List storage element-wise operations, recursive helper.
|
|
913
|
+
*/
|
|
914
|
+
template <ewop_t op, typename LDType, typename RDType>
|
|
915
|
+
static void ew_op_prime(LIST* dest, LDType d_default, const LIST* left, LDType l_default, const LIST* right, RDType r_default, const size_t* shape, size_t last_level, size_t level) {
|
|
916
|
+
|
|
917
|
+
static LIST EMPTY_LIST = {NULL};
|
|
918
|
+
|
|
919
|
+
size_t index;
|
|
920
|
+
|
|
921
|
+
LDType tmp_result;
|
|
922
|
+
|
|
923
|
+
LIST* new_level = NULL;
|
|
924
|
+
|
|
925
|
+
NODE* l_node = left->first,
|
|
926
|
+
* r_node = right->first,
|
|
927
|
+
* dest_node = NULL;
|
|
928
|
+
|
|
929
|
+
for (index = 0; index < shape[level]; ++index) {
|
|
930
|
+
if (l_node == NULL and r_node == NULL) {
|
|
931
|
+
/*
|
|
932
|
+
* Both source lists are now empty. Because the default value of the
|
|
933
|
+
* destination is already set appropriately we can now return.
|
|
934
|
+
*/
|
|
935
|
+
|
|
936
|
+
return;
|
|
937
|
+
|
|
938
|
+
} else {
|
|
939
|
+
// At least one list still has entries.
|
|
940
|
+
|
|
941
|
+
if (op == EW_MUL) {
|
|
942
|
+
// Special cases for multiplication.
|
|
943
|
+
|
|
944
|
+
if (l_node == NULL and (l_default == 0 and d_default == 0)) {
|
|
945
|
+
/*
|
|
946
|
+
* The left hand list has run out of elements. We don't need to add new
|
|
947
|
+
* values to the destination if l_default and d_default are both 0.
|
|
948
|
+
*/
|
|
949
|
+
|
|
950
|
+
return;
|
|
951
|
+
|
|
952
|
+
} else if (r_node == NULL and (r_default == 0 and d_default == 0)) {
|
|
953
|
+
/*
|
|
954
|
+
* The right hand list has run out of elements. We don't need to add new
|
|
955
|
+
* values to the destination if r_default and d_default are both 0.
|
|
956
|
+
*/
|
|
957
|
+
|
|
958
|
+
return;
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
} else if (op == EW_DIV) {
|
|
962
|
+
// Special cases for division.
|
|
963
|
+
|
|
964
|
+
if (l_node == NULL and (l_default == 0 and d_default == 0)) {
|
|
965
|
+
/*
|
|
966
|
+
* The left hand list has run out of elements. We don't need to add new
|
|
967
|
+
* values to the destination if l_default and d_default are both 0.
|
|
968
|
+
*/
|
|
969
|
+
|
|
970
|
+
return;
|
|
971
|
+
|
|
972
|
+
} else if (r_node == NULL and (r_default == 0 and d_default == 0)) {
|
|
973
|
+
/*
|
|
974
|
+
* The right hand list has run out of elements. If the r_default
|
|
975
|
+
* value is 0 any further division will result in a SIGFPE.
|
|
976
|
+
*/
|
|
977
|
+
|
|
978
|
+
rb_raise(rb_eZeroDivError, "Cannot divide type by 0, would throw SIGFPE.");
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
// TODO: Add optimizations for addition and subtraction.
|
|
982
|
+
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
// We need to continue processing the lists.
|
|
986
|
+
|
|
987
|
+
if (l_node == NULL and r_node->key == index) {
|
|
988
|
+
/*
|
|
989
|
+
* One source list is empty, but the index has caught up to the key of
|
|
990
|
+
* the other list.
|
|
991
|
+
*/
|
|
992
|
+
|
|
993
|
+
if (level == last_level) {
|
|
994
|
+
tmp_result = ew_op_switch<op, LDType, RDType>(l_default, *reinterpret_cast<RDType*>(r_node->val));
|
|
995
|
+
std::cerr << "1. tmp_result = " << tmp_result << std::endl;
|
|
996
|
+
|
|
997
|
+
if (tmp_result != d_default) {
|
|
998
|
+
dest_node = nm::list::insert_helper(dest, dest_node, index, tmp_result);
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
} else {
|
|
1002
|
+
new_level = nm::list::create();
|
|
1003
|
+
dest_node = nm::list::insert_helper(dest, dest_node, index, new_level);
|
|
1004
|
+
|
|
1005
|
+
ew_op_prime<op, LDType, RDType>(new_level, d_default, &EMPTY_LIST, l_default,
|
|
1006
|
+
reinterpret_cast<LIST*>(r_node->val), r_default,
|
|
1007
|
+
shape, last_level, level + 1);
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
r_node = r_node->next;
|
|
1011
|
+
|
|
1012
|
+
} else if (r_node == NULL and l_node->key == index) {
|
|
1013
|
+
/*
|
|
1014
|
+
* One source list is empty, but the index has caught up to the key of
|
|
1015
|
+
* the other list.
|
|
1016
|
+
*/
|
|
1017
|
+
|
|
1018
|
+
if (level == last_level) {
|
|
1019
|
+
tmp_result = ew_op_switch<op, LDType, RDType>(*reinterpret_cast<LDType*>(l_node->val), r_default);
|
|
1020
|
+
std::cerr << "2. tmp_result = " << tmp_result << std::endl;
|
|
1021
|
+
|
|
1022
|
+
if (tmp_result != d_default) {
|
|
1023
|
+
dest_node = nm::list::insert_helper(dest, dest_node, index, tmp_result);
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
} else {
|
|
1027
|
+
new_level = nm::list::create();
|
|
1028
|
+
dest_node = nm::list::insert_helper(dest, dest_node, index, new_level);
|
|
1029
|
+
|
|
1030
|
+
ew_op_prime<op, LDType, RDType>(new_level, d_default, reinterpret_cast<LIST*>(l_node->val), l_default,
|
|
1031
|
+
&EMPTY_LIST, r_default, shape, last_level, level + 1);
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
l_node = l_node->next;
|
|
1035
|
+
|
|
1036
|
+
} else if (l_node != NULL and r_node != NULL and index == std::min(l_node->key, r_node->key)) {
|
|
1037
|
+
/*
|
|
1038
|
+
* Neither list is empty and our index has caught up to one of the
|
|
1039
|
+
* source lists.
|
|
1040
|
+
*/
|
|
1041
|
+
|
|
1042
|
+
if (l_node->key == r_node->key) {
|
|
1043
|
+
|
|
1044
|
+
if (level == last_level) {
|
|
1045
|
+
tmp_result = ew_op_switch<op, LDType, RDType>(*reinterpret_cast<LDType*>(l_node->val),*reinterpret_cast<RDType*>(r_node->val));
|
|
1046
|
+
std::cerr << "3. tmp_result = " << tmp_result << std::endl;
|
|
1047
|
+
|
|
1048
|
+
if (tmp_result != d_default) {
|
|
1049
|
+
dest_node = nm::list::insert_helper(dest, dest_node, index, tmp_result);
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
} else {
|
|
1053
|
+
new_level = nm::list::create();
|
|
1054
|
+
dest_node = nm::list::insert_helper(dest, dest_node, index, new_level);
|
|
1055
|
+
|
|
1056
|
+
ew_op_prime<op, LDType, RDType>(new_level, d_default,
|
|
1057
|
+
reinterpret_cast<LIST*>(l_node->val), l_default,
|
|
1058
|
+
reinterpret_cast<LIST*>(r_node->val), r_default,
|
|
1059
|
+
shape, last_level, level + 1);
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
l_node = l_node->next;
|
|
1063
|
+
r_node = r_node->next;
|
|
1064
|
+
|
|
1065
|
+
} else if (l_node->key < r_node->key) {
|
|
1066
|
+
// Advance the left node knowing that the default value is OK.
|
|
1067
|
+
|
|
1068
|
+
l_node = l_node->next;
|
|
1069
|
+
|
|
1070
|
+
} else /* if (l_node->key > r_node->key) */ {
|
|
1071
|
+
// Advance the right node knowing that the default value is OK.
|
|
1072
|
+
|
|
1073
|
+
r_node = r_node->next;
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
} else {
|
|
1077
|
+
/*
|
|
1078
|
+
* Our index needs to catch up but the default value is OK. This
|
|
1079
|
+
* conditional is here only for documentation and should be optimized
|
|
1080
|
+
* out.
|
|
1081
|
+
*/
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
}} // end of namespace nm::list_storage
|
|
1088
|
+
|