nmatrix 0.0.8 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -8
  3. data/.rspec +1 -1
  4. data/.travis.yml +12 -0
  5. data/CONTRIBUTING.md +27 -12
  6. data/Gemfile +1 -0
  7. data/History.txt +38 -0
  8. data/Manifest.txt +15 -15
  9. data/README.rdoc +7 -6
  10. data/Rakefile +40 -5
  11. data/ext/nmatrix/data/data.cpp +2 -37
  12. data/ext/nmatrix/data/data.h +19 -121
  13. data/ext/nmatrix/data/meta.h +70 -0
  14. data/ext/nmatrix/extconf.rb +40 -12
  15. data/ext/nmatrix/math/math.h +13 -103
  16. data/ext/nmatrix/nmatrix.cpp +10 -2018
  17. data/ext/nmatrix/nmatrix.h +16 -13
  18. data/ext/nmatrix/ruby_constants.cpp +12 -1
  19. data/ext/nmatrix/ruby_constants.h +7 -1
  20. data/ext/nmatrix/ruby_nmatrix.c +2169 -0
  21. data/ext/nmatrix/storage/dense.cpp +123 -14
  22. data/ext/nmatrix/storage/dense.h +10 -4
  23. data/ext/nmatrix/storage/list.cpp +265 -48
  24. data/ext/nmatrix/storage/list.h +6 -9
  25. data/ext/nmatrix/storage/storage.cpp +44 -54
  26. data/ext/nmatrix/storage/storage.h +2 -2
  27. data/ext/nmatrix/storage/yale/class.h +1070 -0
  28. data/ext/nmatrix/storage/yale/iterators/base.h +142 -0
  29. data/ext/nmatrix/storage/yale/iterators/iterator.h +130 -0
  30. data/ext/nmatrix/storage/yale/iterators/row.h +449 -0
  31. data/ext/nmatrix/storage/yale/iterators/row_stored.h +139 -0
  32. data/ext/nmatrix/storage/yale/iterators/row_stored_nd.h +167 -0
  33. data/ext/nmatrix/storage/yale/iterators/stored_diagonal.h +123 -0
  34. data/ext/nmatrix/storage/yale/math/transpose.h +110 -0
  35. data/ext/nmatrix/storage/yale/yale.cpp +1785 -0
  36. data/ext/nmatrix/storage/{yale.h → yale/yale.h} +23 -55
  37. data/ext/nmatrix/types.h +2 -0
  38. data/ext/nmatrix/util/io.cpp +27 -45
  39. data/ext/nmatrix/util/io.h +0 -2
  40. data/ext/nmatrix/util/sl_list.cpp +169 -28
  41. data/ext/nmatrix/util/sl_list.h +9 -3
  42. data/lib/nmatrix/blas.rb +20 -20
  43. data/lib/nmatrix/enumerate.rb +1 -1
  44. data/lib/nmatrix/io/mat5_reader.rb +8 -14
  45. data/lib/nmatrix/lapack.rb +3 -3
  46. data/lib/nmatrix/math.rb +3 -3
  47. data/lib/nmatrix/nmatrix.rb +19 -5
  48. data/lib/nmatrix/nvector.rb +2 -0
  49. data/lib/nmatrix/shortcuts.rb +90 -125
  50. data/lib/nmatrix/version.rb +1 -1
  51. data/nmatrix.gemspec +7 -8
  52. data/spec/{nmatrix_spec.rb → 00_nmatrix_spec.rb} +45 -208
  53. data/spec/01_enum_spec.rb +184 -0
  54. data/spec/{slice_spec.rb → 02_slice_spec.rb} +55 -39
  55. data/spec/blas_spec.rb +22 -54
  56. data/spec/elementwise_spec.rb +9 -8
  57. data/spec/io_spec.rb +6 -4
  58. data/spec/lapack_spec.rb +26 -26
  59. data/spec/math_spec.rb +9 -5
  60. data/spec/nmatrix_yale_spec.rb +29 -61
  61. data/spec/shortcuts_spec.rb +34 -22
  62. data/spec/slice_set_spec.rb +157 -0
  63. data/spec/spec_helper.rb +42 -2
  64. data/spec/stat_spec.rb +192 -0
  65. metadata +52 -55
  66. data/ext/nmatrix/storage/yale.cpp +0 -2284
  67. data/spec/nmatrix_list_spec.rb +0 -113
  68. data/spec/nvector_spec.rb +0 -112
@@ -43,18 +43,16 @@
43
43
  */
44
44
 
45
45
  #include <limits> // for std::numeric_limits<T>::max()
46
+ #include <stdexcept>
46
47
 
47
48
  /*
48
49
  * Project Includes
49
50
  */
50
51
 
51
- #include "types.h"
52
-
53
- #include "data/data.h"
54
-
55
- #include "common.h"
56
-
57
- #include "nmatrix.h"
52
+ #include "../../types.h"
53
+ #include "../../data/data.h"
54
+ #include "../common.h"
55
+ #include "../../nmatrix.h"
58
56
 
59
57
  extern "C" {
60
58
 
@@ -62,8 +60,6 @@ extern "C" {
62
60
  * Macros
63
61
  */
64
62
 
65
- #define NM_YALE_MINIMUM(sptr) (((YALE_STORAGE*)(sptr))->shape[0]*2 + 1) // arbitrarily defined
66
-
67
63
  #ifndef NM_CHECK_ALLOC
68
64
  #define NM_CHECK_ALLOC(x) if (!x) rb_raise(rb_eNoMemError, "insufficient memory");
69
65
  #endif
@@ -86,13 +82,13 @@ extern "C" {
86
82
  // Lifecycle //
87
83
  ///////////////
88
84
 
89
- YALE_STORAGE* nm_yale_storage_create(nm::dtype_t dtype, size_t* shape, size_t dim, size_t init_capacity, nm::itype_t itype);
90
- YALE_STORAGE* nm_yale_storage_create_from_old_yale(nm::dtype_t dtype, size_t* shape, void* ia, void* ja, void* a, nm::dtype_t from_dtype);
85
+ YALE_STORAGE* nm_yale_storage_create(nm::dtype_t dtype, size_t* shape, size_t dim, size_t init_capacity);
86
+ YALE_STORAGE* nm_yale_storage_create_from_old_yale(nm::dtype_t dtype, size_t* shape, char* ia, char* ja, char* a, nm::dtype_t from_dtype);
91
87
  YALE_STORAGE* nm_yale_storage_create_merged(const YALE_STORAGE* merge_template, const YALE_STORAGE* other);
92
88
  void nm_yale_storage_delete(STORAGE* s);
93
89
  void nm_yale_storage_delete_ref(STORAGE* s);
94
90
  void nm_yale_storage_init(YALE_STORAGE* s, void* default_val);
95
- void nm_yale_storage_mark(void*);
91
+ void nm_yale_storage_mark(STORAGE*);
96
92
 
97
93
  ///////////////
98
94
  // Accessors //
@@ -100,9 +96,12 @@ extern "C" {
100
96
 
101
97
  VALUE nm_yale_each_with_indices(VALUE nmatrix);
102
98
  VALUE nm_yale_each_stored_with_indices(VALUE nmatrix);
103
- void* nm_yale_storage_get(STORAGE* s, SLICE* slice);
104
- void* nm_yale_storage_ref(STORAGE* s, SLICE* slice);
105
- char nm_yale_storage_set(STORAGE* storage, SLICE* slice, void* v);
99
+ VALUE nm_yale_stored_diagonal_each_with_indices(VALUE nmatrix);
100
+ VALUE nm_yale_stored_nondiagonal_each_with_indices(VALUE nmatrix);
101
+ VALUE nm_yale_each_ordered_stored_with_indices(VALUE nmatrix);
102
+ void* nm_yale_storage_get(const STORAGE* s, SLICE* slice);
103
+ void* nm_yale_storage_ref(const STORAGE* s, SLICE* slice);
104
+ void nm_yale_storage_set(VALUE left, SLICE* slice, VALUE right);
106
105
 
107
106
  //char nm_yale_storage_vector_insert(YALE_STORAGE* s, size_t pos, size_t* js, void* vals, size_t n, bool struct_only, nm::dtype_t dtype, nm::itype_t itype);
108
107
  //void nm_yale_storage_increment_ia_after(YALE_STORAGE* s, size_t ija_size, size_t i, size_t n);
@@ -128,39 +127,6 @@ extern "C" {
128
127
  // Utility //
129
128
  /////////////
130
129
 
131
- /*
132
- * Calculates the itype a YALE_STORAGE object would need without actually needing
133
- * to see the YALE_STORAGE object. Does this just by looking at the shape.
134
- *
135
- * Useful for creating Yale Storage by other means than NMatrix.new(:yale, ...),
136
- * e.g., from a MATLAB v5 .mat file.
137
- */
138
- inline nm::itype_t nm_yale_storage_itype_by_shape(const size_t* shape) {
139
- uint64_t yale_max_size = shape[0] * (shape[1]+1);
140
-
141
- if (yale_max_size < static_cast<uint64_t>(std::numeric_limits<uint8_t>::max()) - 2) {
142
- return nm::UINT8;
143
-
144
- } else if (yale_max_size < static_cast<uint64_t>(std::numeric_limits<uint16_t>::max()) - 2) {
145
- return nm::UINT16;
146
-
147
- } else if (yale_max_size < std::numeric_limits<uint32_t>::max() - 2) {
148
- return nm::UINT32;
149
-
150
- } else {
151
- return nm::UINT64;
152
- }
153
- }
154
-
155
- /*
156
- * Determine the index dtype (which will be used for the ija vector). This is
157
- * determined by matrix shape, not IJA/A vector capacity. Note that it's MAX-2
158
- * because UINTX_MAX and UINTX_MAX-1 are both reserved for sparse matrix
159
- * multiplication.
160
- */
161
- inline nm::itype_t nm_yale_storage_default_itype(const YALE_STORAGE* s) {
162
- return nm_yale_storage_itype_by_shape(s->shape);
163
- }
164
130
 
165
131
 
166
132
  /////////////////////////
@@ -179,19 +145,21 @@ extern "C" {
179
145
 
180
146
  } // end of extern "C" block
181
147
 
182
- namespace nm { namespace yale_storage {
148
+ namespace nm {
149
+
150
+ namespace yale_storage {
183
151
 
184
152
  /*
185
- * Constants
153
+ * Typedefs
186
154
  */
187
- const float GROWTH_CONSTANT = 1.5;
155
+
156
+ typedef size_t IType;
188
157
 
189
158
 
190
159
  /*
191
160
  * Templated Functions
192
161
  */
193
162
 
194
- template <typename IType>
195
163
  int binary_search(YALE_STORAGE* s, IType left, IType right, IType key);
196
164
 
197
165
  /*
@@ -217,14 +185,14 @@ namespace nm { namespace yale_storage {
217
185
  }
218
186
  }
219
187
 
220
- template <typename DType, typename IType>
188
+ template <typename DType>
221
189
  void init(YALE_STORAGE* s, void* init_val);
222
190
 
223
- template <typename IType>
224
191
  size_t get_size(const YALE_STORAGE* storage);
225
192
 
226
- template <typename IType>
227
193
  IType binary_search_left_boundary(const YALE_STORAGE* s, IType left, IType right, IType bound);
194
+
195
+
228
196
  }} // end of namespace nm::yale_storage
229
197
 
230
198
  #endif // YALE_H
@@ -49,4 +49,6 @@
49
49
  typedef float float32_t;
50
50
  typedef double float64_t;
51
51
 
52
+ typedef size_t IType;
53
+
52
54
  #endif
@@ -74,8 +74,8 @@ namespace nm { namespace io {
74
74
  template <typename DType, typename MDType>
75
75
  char* matlab_cstring_to_dtype_string(size_t& result_len, const char* str, size_t bytes) {
76
76
 
77
- result_len = bytes / sizeof(DType);
78
- char* result = ALLOC_N(char, bytes / sizeof(DType));
77
+ result_len = sizeof(DType) * bytes / sizeof(MDType);
78
+ char* result = ALLOC_N(char, result_len);
79
79
 
80
80
  if (bytes % sizeof(MDType) != 0) {
81
81
  rb_raise(rb_eArgError, "the given string does not divide evenly for the given MATLAB dtype");
@@ -109,7 +109,7 @@ nm::dtype_t nm_dtype_from_rbstring(VALUE str) {
109
109
  }
110
110
  }
111
111
 
112
- rb_raise(rb_eArgError, "Invalid data type string specified.");
112
+ rb_raise(rb_eArgError, "invalid data type string (%s) specified", RSTRING_PTR(str));
113
113
  }
114
114
 
115
115
 
@@ -117,31 +117,19 @@ nm::dtype_t nm_dtype_from_rbstring(VALUE str) {
117
117
  * Converts a symbol to a data type.
118
118
  */
119
119
  nm::dtype_t nm_dtype_from_rbsymbol(VALUE sym) {
120
+ ID sym_id = SYM2ID(sym);
120
121
 
121
122
  for (size_t index = 0; index < NM_NUM_DTYPES; ++index) {
122
- if (SYM2ID(sym) == rb_intern(DTYPE_NAMES[index])) {
123
+ if (sym_id == rb_intern(DTYPE_NAMES[index])) {
123
124
  return static_cast<nm::dtype_t>(index);
124
125
  }
125
126
  }
126
127
 
127
- rb_raise(rb_eArgError, "Invalid data type symbol specified.");
128
+ VALUE str = rb_any_to_s(sym);
129
+ rb_raise(rb_eArgError, "invalid data type symbol (:%s) specified", RSTRING_PTR(str));
128
130
  }
129
131
 
130
132
 
131
- /*
132
- * Converts a symbol to an index type.
133
- */
134
- nm::itype_t nm_itype_from_rbsymbol(VALUE sym) {
135
-
136
- for (size_t index = 0; index < NM_NUM_ITYPES; ++index) {
137
- if (SYM2ID(sym) == rb_intern(ITYPE_NAMES[index])) {
138
- return static_cast<nm::itype_t>(index);
139
- }
140
- }
141
-
142
- rb_raise(rb_eArgError, "Invalid index type specified.");
143
- }
144
-
145
133
  /*
146
134
  * Converts a string to a storage type. Only looks at the first three
147
135
  * characters.
@@ -169,7 +157,8 @@ nm::stype_t nm_stype_from_rbsymbol(VALUE sym) {
169
157
  }
170
158
  }
171
159
 
172
- rb_raise(rb_eArgError, "Invalid storage type symbol specified");
160
+ VALUE str = rb_any_to_s(sym);
161
+ rb_raise(rb_eArgError, "invalid storage type symbol (:%s) specified", RSTRING_PTR(str));
173
162
  return nm::DENSE_STORE;
174
163
  }
175
164
 
@@ -197,35 +186,28 @@ static nm::io::matlab_dtype_t matlab_dtype_from_rbsymbol(VALUE sym) {
197
186
  * Arguments:
198
187
  * * str :: the data
199
188
  * * from :: symbol representing MATLAB data type (e.g., :miINT8)
200
- * * options :: hash, give either :itype => some_itype or :dtype => some_dtype, tells function
201
- * what to give as output.
189
+ * * type :: either :itype or some dtype symbol (:byte, :uint32, etc)
202
190
  */
203
- static VALUE nm_rbstring_matlab_repack(VALUE self, VALUE str, VALUE from, VALUE options) {
191
+ static VALUE nm_rbstring_matlab_repack(VALUE self, VALUE str, VALUE from, VALUE type) {
204
192
  nm::io::matlab_dtype_t from_type = matlab_dtype_from_rbsymbol(from);
205
193
  uint8_t to_type;
206
194
 
207
- if (TYPE(options) != T_HASH) {
208
- rb_raise(rb_eArgError, "third argument to repack must be an options hash");
209
- }
210
-
211
- if (RB_HASH_HAS_SYMBOL_KEY(options, "dtype")) { // Hash#has_key?(:dtype)
212
-
213
- nm::dtype_t to_dtype = nm_dtype_from_rbsymbol(rb_hash_aref(options, ID2SYM(rb_intern("dtype"))));
214
- to_type = static_cast<int8_t>(to_dtype);
215
-
216
- } else if (RB_HASH_HAS_SYMBOL_KEY(options, "itype")) {
217
-
218
- nm::itype_t to_itype = nm_itype_from_rbsymbol(rb_hash_aref(options, ID2SYM(rb_intern("itype"))));
219
-
220
- // we're going to cheat and use the DTYPE template table. To do this, we just act like uint8_t
221
- // is a dtype (both are 0, itype and dtype), or we add 1 to the other itypes and treat them as
222
- // signed.
223
- to_type = static_cast<uint8_t>(to_itype);
224
- if (to_itype != nm::UINT8) to_type += 1;
225
-
226
-
195
+ if (SYMBOL_P(type)) {
196
+ if (rb_to_id(type) == rb_intern("itype")) {
197
+ if (sizeof(size_t) == sizeof(int64_t)) {
198
+ to_type = static_cast<int8_t>(nm::INT64);
199
+ } else if (sizeof(size_t) == sizeof(int32_t)) {
200
+ to_type = static_cast<int8_t>(nm::INT32);
201
+ } else if (sizeof(size_t) == sizeof(int16_t)) {
202
+ to_type = static_cast<int8_t>(nm::INT16);
203
+ } else {
204
+ rb_raise(rb_eStandardError, "unhandled size_t definition");
205
+ }
206
+ } else {
207
+ to_type = static_cast<uint8_t>(nm_dtype_from_rbsymbol(type));
208
+ }
227
209
  } else {
228
- rb_raise(rb_eArgError, "third argument must have either :itype or :dtype as a key");
210
+ rb_raise(rb_eArgError, "expected symbol for third argument");
229
211
  }
230
212
 
231
213
  // For next few lines, see explanation above NM_MATLAB_DTYPE_TEMPLATE_TABLE definition in io.h.
@@ -241,7 +223,7 @@ static VALUE nm_rbstring_matlab_repack(VALUE self, VALUE str, VALUE from, VALUE
241
223
 
242
224
  // Encode as 8-bit ASCII with a length -- don't want to hiccup on \0
243
225
  VALUE result = rb_str_new(repacked_data, repacked_data_length);
244
- free(repacked_data); // Don't forget to free what we allocated!
226
+ xfree(repacked_data); // Don't forget to free what we allocated!
245
227
 
246
228
  return result;
247
229
  }
@@ -41,7 +41,6 @@
41
41
  * Extern Types
42
42
  */
43
43
  extern const char* const DTYPE_NAMES[nm::NUM_DTYPES];
44
- extern const char* const ITYPE_NAMES[nm::NUM_ITYPES];
45
44
 
46
45
  namespace nm { namespace io {
47
46
  /*
@@ -77,7 +76,6 @@ extern "C" {
77
76
  nm::dtype_t nm_dtype_from_rbstring(VALUE str);
78
77
  nm::stype_t nm_stype_from_rbsymbol(VALUE sym);
79
78
  nm::stype_t nm_stype_from_rbstring(VALUE str);
80
- nm::itype_t nm_itype_from_rbsymbol(VALUE sym);
81
79
 
82
80
  void nm_init_io(void);
83
81
 
@@ -77,18 +77,18 @@ void del(LIST* list, size_t recursions) {
77
77
 
78
78
  if (recursions == 0) {
79
79
  //fprintf(stderr, " free_val: %p\n", curr->val);
80
- free(curr->val);
80
+ xfree(curr->val);
81
81
 
82
82
  } else {
83
83
  //fprintf(stderr, " free_list: %p\n", list);
84
84
  del((LIST*)curr->val, recursions - 1);
85
85
  }
86
86
 
87
- free(curr);
87
+ xfree(curr);
88
88
  curr = next;
89
89
  }
90
90
  //fprintf(stderr, " free_list: %p\n", list);
91
- free(list);
91
+ xfree(list);
92
92
  }
93
93
 
94
94
  /*
@@ -116,6 +116,37 @@ void mark(LIST* list, size_t recursions) {
116
116
  // Accessors //
117
117
  ///////////////
118
118
 
119
+
120
+ /*
121
+ * Given a list, insert key/val as the first entry in the list. Does not do any
122
+ * checks, just inserts.
123
+ */
124
+ NODE* insert_first_node(LIST* list, size_t key, void* val, size_t val_size) {
125
+ NODE* ins = ALLOC(NODE);
126
+ ins->next = list->first;
127
+
128
+ void* val_copy = ALLOC_N(char, val_size);
129
+ memcpy(val_copy, val, val_size);
130
+
131
+ ins->val = reinterpret_cast<void*>(val_copy);
132
+ ins->key = key;
133
+ list->first = ins;
134
+
135
+ return ins;
136
+ }
137
+
138
+ NODE* insert_first_list(LIST* list, size_t key, LIST* l) {
139
+ NODE* ins = ALLOC(NODE);
140
+ ins->next = list->first;
141
+
142
+ ins->val = reinterpret_cast<void*>(l);
143
+ ins->key = key;
144
+ list->first = ins;
145
+
146
+ return ins;
147
+ }
148
+
149
+
119
150
  /*
120
151
  * Given a list and a key/value-ptr pair, create a node (and return that node).
121
152
  * If NULL is returned, it means insertion failed.
@@ -156,11 +187,11 @@ NODE* insert(LIST* list, bool replace, size_t key, void* val) {
156
187
  if (ins->key == key) {
157
188
  // key already exists
158
189
  if (replace) {
159
- free(ins->val);
190
+ xfree(ins->val);
160
191
  ins->val = val;
161
192
 
162
193
  } else {
163
- free(val);
194
+ xfree(val);
164
195
  }
165
196
 
166
197
  return ins;
@@ -170,14 +201,14 @@ NODE* insert(LIST* list, bool replace, size_t key, void* val) {
170
201
  }
171
202
  }
172
203
 
204
+
205
+
173
206
  /*
174
207
  * Documentation goes here.
175
208
  */
176
209
  NODE* insert_after(NODE* node, size_t key, void* val) {
177
- NODE* ins;
178
-
179
210
  //if (!(ins = malloc(sizeof(NODE)))) return NULL;
180
- ins = ALLOC(NODE);
211
+ NODE* ins = ALLOC(NODE);
181
212
 
182
213
  // insert 'ins' between 'node' and 'node->next'
183
214
  ins->next = node->next;
@@ -190,22 +221,69 @@ NODE* insert_after(NODE* node, size_t key, void* val) {
190
221
  return ins;
191
222
  }
192
223
 
224
+
193
225
  /*
194
- * Analog functions list_insert but this insert copy of value.
226
+ * Insert a new node immediately after +node+, or replace the existing one if its key is a match.
195
227
  */
196
- NODE* insert_with_copy(LIST *list, size_t key, void *val, size_t size) {
228
+ NODE* replace_insert_after(NODE* node, size_t key, void* val, bool copy, size_t copy_size) {
229
+ if (node->next && node->next->key == key) {
230
+
231
+ // Should we copy into the current one or free and insert?
232
+ if (copy) memcpy(node->next->val, val, copy_size);
233
+ else {
234
+ xfree(node->next->val);
235
+ node->next->val = val;
236
+ }
237
+
238
+ return node->next;
239
+
240
+ } else { // no next node, or if there is one, it's greater than the current key
241
+
242
+ if (copy) {
243
+ void* val_copy = ALLOC_N(char, copy_size);
244
+ memcpy(val_copy, val, copy_size);
245
+ return insert_after(node, key, val_copy);
246
+ } else {
247
+ return insert_after(node, key, val);
248
+ }
249
+
250
+ }
251
+ }
252
+
253
+
254
+
255
+ /*
256
+ * Functions analogously to list::insert but this inserts a copy of the value instead of the original.
257
+ */
258
+ NODE* insert_copy(LIST *list, bool replace, size_t key, void *val, size_t size) {
197
259
  void *copy_val = ALLOC_N(char, size);
198
260
  memcpy(copy_val, val, size);
199
261
 
262
+ return insert(list, replace, key, copy_val);
263
+ }
264
+
200
265
 
201
- return insert(list, false, key, copy_val);
266
+ /*
267
+ * Returns the value pointer for some key. Doesn't free the memory for that value. Doesn't require a find operation,
268
+ * assumes finding has already been done. If rm is the first item in the list, prev should be NULL.
269
+ */
270
+ void* remove_by_node(LIST* list, NODE* prev, NODE* rm) {
271
+ if (!prev) list->first = rm->next;
272
+ else prev->next = rm->next;
273
+
274
+ void* val = rm->val;
275
+ xfree(rm);
276
+
277
+ return val;
202
278
  }
279
+
280
+
203
281
  /*
204
282
  * Returns the value pointer (not the node) for some key. Note that it doesn't
205
283
  * free the memory for the value stored in the node -- that pointer gets
206
284
  * returned! Only the node is destroyed.
207
285
  */
208
- void* remove(LIST* list, size_t key) {
286
+ void* remove_by_key(LIST* list, size_t key) {
209
287
  NODE *f, *rm;
210
288
  void* val;
211
289
 
@@ -218,12 +296,12 @@ void* remove(LIST* list, size_t key) {
218
296
  rm = list->first;
219
297
 
220
298
  list->first = rm->next;
221
- free(rm);
299
+ xfree(rm);
222
300
 
223
301
  return val;
224
302
  }
225
303
 
226
- f = find_preceding_from(list->first, key);
304
+ f = find_preceding_from_node(list->first, key);
227
305
  if (!f || !f->next) { // not found, end of list
228
306
  return NULL;
229
307
  }
@@ -235,7 +313,7 @@ void* remove(LIST* list, size_t key) {
235
313
 
236
314
  // get the value and free the memory for the node
237
315
  val = rm->val;
238
- free(rm);
316
+ xfree(rm);
239
317
 
240
318
  return val;
241
319
  }
@@ -244,29 +322,56 @@ void* remove(LIST* list, size_t key) {
244
322
  }
245
323
 
246
324
 
325
+ bool node_is_within_slice(NODE* n, size_t coord, size_t len) {
326
+ if (!n) return false;
327
+ if (n->key >= coord && n->key < coord + len) return true;
328
+ else return false;
329
+ }
330
+
331
+
247
332
  /*
248
333
  * Recursive removal of lists that may contain sub-lists. Stores the value ultimately removed in rm.
249
- *
250
- * FIXME: Could be made slightly faster by using a variety of find which also returns the previous node. This way,
251
- * FIXME: we can remove directly instead of calling remove() and doing the search over again.
252
334
  */
253
- bool remove_recursive(LIST* list, const size_t* coords, const size_t* offset, size_t r, const size_t& dim, void* rm) {
335
+ bool remove_recursive(LIST* list, const size_t* coords, const size_t* offsets, const size_t* lengths, size_t r, const size_t& dim) {
336
+ // std::cerr << "remove_recursive: " << r << std::endl;
337
+ // find the current coordinates in the list
338
+ NODE* prev = find_preceding_from_list(list, coords[r] + offsets[r]);
339
+ NODE* n;
340
+ if (prev) n = prev->next && node_is_within_slice(prev->next, coords[r] + offsets[r], lengths[r]) ? prev->next : NULL;
341
+ else n = node_is_within_slice(list->first, coords[r] + offsets[r], lengths[r]) ? list->first : NULL;
254
342
 
255
343
  if (r < dim-1) { // nodes here are lists
256
- // find the current coordinates in the list
257
- NODE* n = find(list, coords[r] + offset[r]);
258
344
 
259
- if (n) {
345
+ while (n) {
260
346
  // from that sub-list, call remove_recursive.
261
- bool remove_parent = remove_recursive(reinterpret_cast<LIST*>(n->val), coords, offset, r+1, dim, rm);
347
+ bool remove_parent = remove_recursive(reinterpret_cast<LIST*>(n->val), coords, offsets, lengths, r+1, dim);
262
348
 
263
349
  if (remove_parent) { // now empty -- so remove the sub-list
264
- free(remove(list, coords[r] + offset[r]));
350
+ // std::cerr << r << ": removing parent list at " << n->key << std::endl;
351
+ xfree(remove_by_node(list, prev, n));
352
+
353
+ if (prev) n = prev->next && node_is_within_slice(prev->next, coords[r] + offsets[r], lengths[r]) ? prev->next : NULL;
354
+ else n = node_is_within_slice(list->first, coords[r] + offsets[r], lengths[r]) ? list->first : NULL;
355
+ } else {
356
+ // Move forward to next node (list at n still exists)
357
+ prev = n;
358
+ n = prev->next && node_is_within_slice(prev->next, coords[r] + offsets[r], lengths[r]) ? prev->next : NULL;
265
359
  }
360
+
361
+ // Iterate to next one.
362
+ if (prev) n = prev->next && node_is_within_slice(prev->next, coords[r] + offsets[r], lengths[r]) ? prev->next : NULL;
363
+ else n = node_is_within_slice(list->first, coords[r] + offsets[r], lengths[r]) ? list->first : NULL;
266
364
  }
267
365
 
268
366
  } else { // nodes here are not lists, but actual values
269
- rm = remove(list, coords[r] + offset[r]);
367
+
368
+ while (n) {
369
+ // std::cerr << r << ": removing node at " << n->key << std::endl;
370
+ xfree(remove_by_node(list, prev, n));
371
+
372
+ if (prev) n = prev->next && node_is_within_slice(prev->next, coords[r] + offsets[r], lengths[r]) ? prev->next : NULL;
373
+ else n = node_is_within_slice(list->first, coords[r] + offsets[r], lengths[r]) ? list->first : NULL;
374
+ }
270
375
  }
271
376
 
272
377
  if (!list->first) return true; // if current list is now empty, signal its removal
@@ -303,21 +408,57 @@ NODE* find(LIST* list, size_t key) {
303
408
  return NULL;
304
409
  }
305
410
 
411
+
412
+
413
+ /*
414
+ * Find some element in the list and return the node ptr for that key.
415
+ */
416
+ NODE* find_with_preceding(LIST* list, size_t key, NODE*& prev) {
417
+ if (!prev) prev = list->first;
418
+ if (!prev) return NULL; // empty list, does not exist
419
+
420
+ if (prev->key == key) {
421
+ NODE* n = prev;
422
+ prev = NULL;
423
+ return n;
424
+ }
425
+
426
+ while (prev->next && prev->next->key < key) {
427
+ prev = prev->next;
428
+ }
429
+
430
+ return prev->next;
431
+ }
432
+
433
+
434
+
435
+
306
436
  /*
307
437
  * Finds the node that should go before whatever key we request, whether or not
308
438
  * that key is present.
309
439
  */
310
- NODE* find_preceding_from(NODE* prev, size_t key) {
440
+ NODE* find_preceding_from_node(NODE* prev, size_t key) {
311
441
  NODE* curr = prev->next;
312
442
 
313
443
  if (!curr || key <= curr->key) {
314
444
  return prev;
315
445
 
316
446
  } else {
317
- return find_preceding_from(curr, key);
447
+ return find_preceding_from_node(curr, key);
318
448
  }
319
449
  }
320
450
 
451
+
452
+ /*
453
+ * Returns NULL if the key being sought is first in the list or *should* be first in the list but is absent. Otherwise
454
+ * returns the previous node to where that key is or should be.
455
+ */
456
+ NODE* find_preceding_from_list(LIST* l, size_t key) {
457
+ NODE* n = l->first;
458
+ if (!n || n->key >= key) return NULL;
459
+ else return find_preceding_from_node(n, key);
460
+ }
461
+
321
462
  /*
322
463
  * Finds the node or, if not present, the node that it should follow. NULL
323
464
  * indicates no preceding node.
@@ -336,7 +477,7 @@ NODE* find_nearest_from(NODE* prev, size_t key) {
336
477
  return prev;
337
478
  }
338
479
 
339
- f = find_preceding_from(prev, key);
480
+ f = find_preceding_from_node(prev, key);
340
481
 
341
482
  if (!f->next) { // key exceeds final node; return final node.
342
483
  return f;