2 #ifndef SYDEVS_ARRAYND_BASE_H_
3 #define SYDEVS_ARRAYND_BASE_H_
24 template<
typename T,
int64 ndims>
32 std::array<int64, ndims>
dims()
const;
33 std::array<int64, ndims>
strides()
const;
39 const T*
data()
const;
46 const T&
operator()(
const std::array<int64, ndims>& indices)
const;
47 T&
operator()(
const std::array<int64, ndims>& indices);
52 template<
typename... Indices>
55 template<
typename... Indices>
58 void fill(
const T& value);
76 bool traverse(std::function<
bool(
const std::array<int64, ndims>& indices,
const T& value)> func)
const;
82 arraynd_base(
const std::array<int64, ndims>&
dims, std::function<T(
const std::array<int64, ndims>& indices)> func);
101 void compute_strides_and_size();
103 void fill_recursively(
const T& value, std::array<int64, ndims>& indices,
int64 idim,
int64 offset);
105 void assign_from_function_recursively(std::function<T(
const std::array<int64, ndims>& indices)> func, std::array<int64, ndims>& indices,
int64 idim,
int64 offset);
106 bool traverse_recursively(std::function<
bool(
const std::array<int64, ndims>& indices,
const T& value)> func, std::array<int64, ndims>& indices,
int64 idim,
int64 offset)
const;
108 std::array<int64, ndims> dims_;
109 std::array<int64, ndims> strides_;
115 std::shared_ptr<T> data_ptr_;
119 template<
typename T,
int64 ndims>
127 , is_readonly_(false)
130 static_assert(ndims >= 1,
"ndims must be at least 1");
131 for (
int idim = 0; idim < ndims; ++idim) {
134 compute_strides_and_size();
135 data_ptr_ = std::shared_ptr<T>(
new T[size_], std::default_delete<T[]>());
139 template<
typename T,
int64 ndims>
147 , is_readonly_(false)
150 static_assert(ndims >= 1,
"ndims must be at least 1");
151 compute_strides_and_size();
152 data_ptr_ = std::shared_ptr<T>(
new T[size_], std::default_delete<T[]>());
157 template<
typename T,
int64 ndims>
165 , is_readonly_(false)
168 static_assert(ndims >= 1,
"ndims must be at least 1");
169 compute_strides_and_size();
170 if (data.size() != size_)
throw std::domain_error(
"Length of multidimensional array data must match product of array dimensions");
171 data_ptr_ = std::shared_ptr<T>(
new T[size_], std::default_delete<T[]>());
172 for (
int64 i = 0; i < size_; ++i){
173 data_ptr_.get()[i] = data[i];
178 template<
typename T,
int64 ndims>
186 , is_readonly_(false)
189 static_assert(ndims >= 1,
"ndims must be at least 1");
190 compute_strides_and_size();
191 data_ptr_ = std::shared_ptr<T>(
new T[size_], std::default_delete<T[]>());
196 template<
typename T,
int64 ndims>
199 if (rotation_ > 0)
throw std::logic_error(
"Attempt to use a partially sliced multidimensional array (try completing the slicing operation by invoking operator[](range) as needed)");
204 template<
typename T,
int64 ndims>
207 if (rotation_ > 0)
throw std::logic_error(
"Attempt to use a partially sliced multidimensional array (try completing the slicing operation by invoking operator[](range) as needed)");
212 template<
typename T,
int64 ndims>
215 if (rotation_ > 0)
throw std::logic_error(
"Attempt to use a partially sliced multidimensional array (try completing the slicing operation by invoking operator[](range) as needed)");
220 template<
typename T,
int64 ndims>
223 if (rotation_ > 0)
throw std::logic_error(
"Attempt to use a partially sliced multidimensional array (try completing the slicing operation by invoking operator[](range) as needed)");
228 template<
typename T,
int64 ndims>
231 if (rotation_ > 0)
throw std::logic_error(
"Attempt to use a partially sliced multidimensional array (try completing the slicing operation by invoking operator[](range) as needed)");
236 template<
typename T,
int64 ndims>
239 if (rotation_ > 0)
throw std::logic_error(
"Attempt to use a partially sliced multidimensional array (try completing the slicing operation by invoking operator[](range) as needed)");
240 return data_ptr_.get();
244 template<
typename T,
int64 ndims>
247 if (is_readonly_)
throw std::logic_error(
"Attempt to obtain a non-const reference to readonly multidimensional array data");
248 if (rotation_ > 0)
throw std::logic_error(
"Attempt to use a partially sliced multidimensional array (try completing the slicing operation by invoking operator[](range) as needed)");
249 return data_ptr_.get();
253 template<
typename T,
int64 ndims>
256 if (rotation_ > 0)
throw std::logic_error(
"Attempt to use a partially sliced multidimensional array (try completing the slicing operation by invoking operator[](range) as needed)");
257 auto contiguous_so_far =
true;
259 for (
int64 idim = ndims - 1; contiguous_so_far && idim >= 0; --idim) {
260 if (strides_[idim] != size) {
261 contiguous_so_far =
false;
267 return contiguous_so_far;
271 template<
typename T,
int64 ndims>
274 if (rotation_ > 0)
throw std::logic_error(
"Attempt to use a partially sliced multidimensional array (try completing the slicing operation by invoking operator[](range) as needed)");
279 template<
typename T,
int64 ndims>
286 template<
typename T,
int64 ndims>
289 if (rotation_ > 0)
throw std::logic_error(
"Attempt to use a partially sliced multidimensional array (try completing the slicing operation by invoking operator[](range) as needed)");
290 int64 offset = offset_;
291 for (
int64 idim = 0; idim < ndims; ++idim) {
292 if (indices[idim] < 0 || indices[idim] >= dims_[idim])
throw std::out_of_range(
"Invalid multidimensional array index");
293 offset += indices[idim]*strides_[idim];
295 return data_ptr_.get()[offset];
299 template<
typename T,
int64 ndims>
302 if (is_readonly_)
throw std::logic_error(
"Attempt to obtain a non-const reference to readonly multidimensional array data");
303 if (rotation_ > 0)
throw std::logic_error(
"Attempt to use a partially sliced multidimensional array (try completing the slicing operation by invoking operator[](range) as needed)");
304 int64 offset = offset_;
305 for (
int64 idim = 0; idim < ndims; ++idim) {
306 if (indices[idim] < 0 || indices[idim] >= dims_[idim])
throw std::out_of_range(
"Invalid multidimensional array index");
307 offset += indices[idim]*strides_[idim];
309 return data_ptr_.get()[offset];
313 template<
typename T,
int64 ndims>
316 if (rotation_ > 0)
throw std::logic_error(
"Attempt to use a partially sliced multidimensional array (try completing the slicing operation by invoking operator[](range) as needed)");
317 if (indices.
size() != ndims)
throw std::logic_error(
"Size of indexing array does not match the number of multidimensional array dimensions");
318 int64 offset = offset_;
319 for (
int64 idim = 0; idim < ndims; ++idim) {
320 if (indices({idim}) < 0 || indices({idim}) >= dims_[idim])
throw std::out_of_range(
"Invalid multidimensional array index");
321 offset += indices({idim})*strides_[idim];
323 return data_ptr_.get()[offset];
327 template<
typename T,
int64 ndims>
330 if (is_readonly_)
throw std::logic_error(
"Attempt to obtain a non-const reference to readonly multidimensional array data");
331 if (rotation_ > 0)
throw std::logic_error(
"Attempt to use a partially sliced multidimensional array (try completing the slicing operation by invoking operator[](range) as needed)");
332 if (indices.
size() != ndims)
throw std::logic_error(
"Size of indexing array does not match the number of multidimensional array dimensions");
333 int64 offset = offset_;
334 for (
int64 idim = 0; idim < ndims; ++idim) {
335 if (indices({idim}) < 0 || indices({idim}) >= dims_[idim])
throw std::out_of_range(
"Invalid multidimensional array index");
336 offset += indices({idim})*strides_[idim];
338 return data_ptr_.get()[offset];
342 template<
typename T,
int64 ndims>
343 template<
typename... Indices>
346 return (*
this)({indices...});
350 template<
typename T,
int64 ndims>
351 template<
typename... Indices>
354 return (*
this)({indices...});
358 template<
typename T,
int64 ndims>
361 if (is_readonly_)
throw std::logic_error(
"Attempt to modify readonly multidimensional array data");
362 if (rotation_ > 0)
throw std::logic_error(
"Attempt to use a partially sliced multidimensional array (try completing the slicing operation by invoking operator[](range) as needed)");
363 auto indices = std::array<int64, ndims>();
364 fill_recursively(value, indices, 0, offset_);
368 template<
typename T,
int64 ndims>
371 if (is_readonly_)
throw std::logic_error(
"Attempt to modify readonly multidimensional array data");
372 if (rotation_ > 0)
throw std::logic_error(
"Attempt to use a partially sliced multidimensional array (try completing the slicing operation by invoking operator[](range) as needed)");
373 for (
int64 idim = 0; idim < ndims; ++idim) {
374 if (dims_[idim] != rhs.
dims()[idim])
throw std::logic_error(
"Multidimensional array dimensions do not match");
376 auto indices = std::array<int64, ndims>();
377 assign_recursively(rhs, indices, 0, offset_);
381 template<
typename T,
int64 ndims>
384 if (is_readonly_)
throw std::logic_error(
"Attempt to modify readonly multidimensional array data");
385 if (rotation_ > 0)
throw std::logic_error(
"Attempt to use a partially sliced multidimensional array (try completing the slicing operation by invoking operator[](range) as needed)");
386 auto indices = std::array<int64, ndims>();
387 assign_from_function_recursively(func, indices, 0, offset_);
391 template<
typename T,
int64 ndims>
394 if (rotation_ > 0)
throw std::logic_error(
"Attempt to use a partially sliced multidimensional array (try completing the slicing operation by invoking operator[](range) as needed)");
395 auto indices = std::array<int64, ndims>();
396 return traverse_recursively(func, indices, 0, offset_);
400 template<
typename T,
int64 ndims>
406 , rotation_(rhs.rotation_)
408 , is_readonly_(is_readonly)
409 , data_ptr_(rhs.data_ptr_)
411 if (index < 0 || index >= rhs.dims_[0])
throw std::out_of_range(
"Invalid multidimensional array index");
413 for (
int64 idim = 0; idim < ndims; ++idim) {
414 dims_[idim] = rhs.dims_[idim + 1];
415 strides_[idim] = rhs.strides_[idim + 1];
416 size_ *= dims_[idim];
418 offset_ = rhs.offset_ + index*rhs.strides_[0];
425 template<
typename T,
int64 ndims>
431 , rotation_(rhs.rotation_)
433 , is_readonly_(is_readonly)
434 , data_ptr_(rhs.data_ptr_)
436 if (r.
start() < 0 || r.
start() >= rhs.dims_[0])
throw std::out_of_range(
"Multidimensional array range must start inside array");
437 if (r.
stride() == 0)
throw std::invalid_argument(
"Multidimensional array range must have a nonzero stride");
439 for (
int64 idim = 0; idim < ndims - 1; ++idim) {
440 dims_[idim] = rhs.dims_[idim + 1];
441 strides_[idim] = rhs.strides_[idim + 1];
442 size_ *= dims_[idim];
445 (r.
stop() > rhs.dims_[0] - 1) ? rhs.dims_[0] - 1 :
448 dims_[ndims - 1] = 0;
449 if (gap == 0 || (gap > 0 && r.
stride() > 0) || (gap < 0 && r.
stride() < 0)) {
452 strides_[ndims - 1] = rhs.strides_[0]*r.
stride();
453 size_ *= dims_[ndims - 1];
454 offset_ = rhs.offset_ + r.
start()*rhs.strides_[0];
455 if (rotation_ == 0) {
456 rotation_ = ndims - 1;
464 template<
typename T,
int64 ndims>
467 , strides_(rhs.strides_)
469 , offset_(rhs.offset_)
470 , rotation_(rhs.rotation_)
472 , is_readonly_(is_readonly)
476 data_ptr_ = rhs.data_ptr_;
479 compute_strides_and_size();
482 is_readonly_ =
false;
483 data_ptr_ = std::shared_ptr<T>(
new T[size_], std::default_delete<T[]>());
489 template<
typename T,
int64 ndims>
492 , strides_(rhs.strides_)
494 , offset_(rhs.offset_)
495 , rotation_(rhs.rotation_)
496 , is_view_(rhs.is_view_)
497 , is_readonly_(rhs.is_readonly_)
501 data_ptr_ = rhs.data_ptr_;
504 compute_strides_and_size();
507 is_readonly_ =
false;
508 data_ptr_ = std::shared_ptr<T>(
new T[size_], std::default_delete<T[]>());
514 template<
typename T,
int64 ndims>
522 is_view_ = rhs.is_view_;
524 strides_ = rhs.strides_;
526 offset_ = rhs.offset_;
527 rotation_ = rhs.rotation_;
528 is_readonly_ = rhs.is_readonly_;
529 data_ptr_ = rhs.data_ptr_;
532 compute_strides_and_size();
535 is_readonly_ =
false;
536 data_ptr_ = std::shared_ptr<T>(
new T[size_], std::default_delete<T[]>());
544 template<
typename T,
int64 ndims>
547 if (arr.rotation_ > 0)
throw std::logic_error(
"Attempt to use a partially sliced multidimensional array (try completing the slicing operation by invoking operator[](range) as needed)");
548 std::reverse(arr.dims_.begin(), arr.dims_.end());
549 std::reverse(arr.strides_.begin(), arr.strides_.end());
553 template<
typename T,
int64 ndims>
556 if (arr.rotation_ > 0)
throw std::logic_error(
"Attempt to use a partially sliced multidimensional array (try completing the slicing operation by invoking operator[](range) as needed)");
557 if (idim0 < 0 || idim0 >= ndims || idim1 < 0 || idim1 >= ndims)
throw std::out_of_range(
"Invalid multidimensional array index");
558 std::swap(arr.dims_[idim0], arr.dims_[idim1]);
559 std::swap(arr.strides_[idim0], arr.strides_[idim1]);
563 template<
typename T,
int64 ndims>
566 if (arr0.rotation_ > 0)
throw std::logic_error(
"Attempt to use a partially sliced multidimensional array (try completing the slicing operation by invoking operator[](range) as needed)");
567 if (!arr0.
is_contiguous())
throw std::domain_error(
"Attempt to subdivide an axis of a non-contiguous multi-dimensional array");
568 if (idim < 0 || idim >= ndims)
throw std::out_of_range(
"Invalid multidimensional array index");
569 if (dims[0]*dims[1] != arr0.dims_[idim])
throw std::domain_error(
"Product of post-subdivision multidimensional array dimensions does not match pre-subdivision dimension");
570 for (
int64 i = 0; i < idim; ++i) {
571 arr.dims_[i] = arr0.dims_[i];
572 arr.strides_[i] = arr0.strides_[i];
574 arr.dims_[idim] = dims[0];
575 arr.dims_[idim + 1] = dims[1];
576 arr.strides_[idim] = arr0.strides_[idim]*dims[1];
577 arr.strides_[idim + 1] = arr0.strides_[idim];
578 for (
int64 i = idim + 2; i < ndims + 1; ++i) {
579 arr.dims_[i] = arr0.dims_[i - 1];
580 arr.strides_[i] = arr0.strides_[i - 1];
582 arr.size_ = arr0.size_;
583 arr.offset_ = arr0.offset_;
586 arr.is_readonly_ = arr0.is_readonly_;
587 arr.data_ptr_ = arr0.data_ptr_;
591 template<
typename T,
int64 ndims>
594 if (arr0.rotation_ > 0)
throw std::logic_error(
"Attempt to use a partially sliced multidimensional array (try completing the slicing operation by invoking operator[](range) as needed)");
595 if (!arr0.
is_contiguous())
throw std::domain_error(
"Attempt to absorb an axis of a non-contiguous multi-dimensional array");
596 if (idim < 0 || idim >= ndims)
throw std::out_of_range(
"Invalid multidimensional array index");
597 if (idim == 0)
throw std::domain_error(
"Attempt to absorb the first axis of a multidimensional array (the second axis must be absorbed into the first)");
598 for (
int64 i = 0; i < idim - 1; ++i) {
599 arr.dims_[i] = arr0.dims_[i];
600 arr.strides_[i] = arr0.strides_[i];
602 arr.dims_[idim - 1] = arr0.dims_[idim - 1]*arr0.dims_[idim];
603 arr.strides_[idim - 1] = arr0.strides_[idim];
604 for (
int64 i = idim; i < ndims - 1; ++i) {
605 arr.dims_[i] = arr0.dims_[i + 1];
606 arr.strides_[i] = arr0.strides_[i + 1];
608 arr.size_ = arr0.size_;
609 arr.offset_ = arr0.offset_;
612 arr.is_readonly_ = arr0.is_readonly_;
613 arr.data_ptr_ = arr0.data_ptr_;
617 template<
typename T,
int64 ndims>
621 for (
int64 idim = ndims - 1; idim >= 0; --idim) {
622 strides_[idim] = size_;
623 size_ *= dims_[idim];
628 template<
typename T,
int64 ndims>
629 void arraynd_base<T, ndims>::fill_recursively(
const T& value, std::array<int64, ndims>& indices,
int64 idim,
int64 offset)
631 if (idim == ndims - 1) {
632 for (
int64 i = 0; i < dims_[idim]; ++i) {
634 data_ptr_.get()[offset + i*strides_[idim]] = value;
638 for (
int64 i = 0; i < dims_[idim]; ++i) {
640 fill_recursively(value, indices, idim + 1, offset + i*strides_[idim]);
646 template<
typename T,
int64 ndims>
647 void arraynd_base<T, ndims>::assign_recursively(
const arraynd_base<T, ndims>& rhs, std::array<int64, ndims>& indices,
int64 idim,
int64 offset)
649 if (idim == ndims - 1) {
650 for (
int64 i = 0; i < dims_[idim]; ++i) {
652 data_ptr_.get()[offset + i*strides_[idim]] = rhs(indices);
656 for (
int64 i = 0; i < dims_[idim]; ++i) {
658 assign_recursively(rhs, indices, idim + 1, offset + i*strides_[idim]);
664 template<
typename T,
int64 ndims>
665 void arraynd_base<T, ndims>::assign_from_function_recursively(std::function<T(
const std::array<int64, ndims>& indices)> func, std::array<int64, ndims>& indices,
int64 idim,
int64 offset)
667 if (idim == ndims - 1) {
668 for (
int64 i = 0; i < dims_[idim]; ++i) {
670 data_ptr_.get()[offset + i*strides_[idim]] = func(indices);
674 for (
int64 i = 0; i < dims_[idim]; ++i) {
676 assign_from_function_recursively(func, indices, idim + 1, offset + i*strides_[idim]);
682 template<
typename T,
int64 ndims>
683 bool arraynd_base<T, ndims>::traverse_recursively(std::function<
bool(
const std::array<int64, ndims>& indices,
const T& value)> func, std::array<int64, ndims>& indices,
int64 idim,
int64 offset)
const
686 if (idim == ndims - 1) {
687 for (
int64 i = 0; proceed && i < dims_[idim]; ++i) {
689 proceed = func(indices, data_ptr_.get()[offset + i*strides_[idim]]);
693 for (
int64 i = 0; proceed && i < dims_[idim]; ++i) {
695 proceed = traverse_recursively(func, indices, idim + 1, offset + i*strides_[idim]);
void assign(const arraynd_base< T, ndims > &rhs)
Replaces every element with the corresponding value in rhs.
Definition: arraynd_base.h:369
bool is_readonly() const
Returns true if the data is readonly, in which case attempts to modify it raise a std::logic_error...
Definition: arraynd_base.h:280
A data type which represents a range of array indices along a single dimension.
Definition: range.h:51
std::array< int64, ndims > dims() const
Returns the lengths of each dimension.
Definition: arraynd_base.h:197
bool is_view() const
Returns true if this multidimensional array is a view of another, meaning that data is shared...
Definition: arraynd_base.h:272
std::array< int64, ndims > strides() const
Returns the number of element-size steps in memory between successive elements for each dimension...
Definition: arraynd_base.h:205
static void transpose(arraynd_base< T, ndims > &arr)
Definition: arraynd_base.h:545
const T * data() const
Returns a pointer to the element data (const).
Definition: arraynd_base.h:237
const T & operator()(const std::array< int64, ndims > &indices) const
Returns a reference to an element (const).
Definition: arraynd_base.h:287
bool traverse(std::function< bool(const std::array< int64, ndims > &indices, const T &value)> func) const
Traverses the multidimensional array in row-major order, calling func at every element.
Definition: arraynd_base.h:392
void assign_from_function(std::function< T(const std::array< int64, ndims > &indices)> func)
Replaces every element with the result of func evaluated at the cooresponding indices.
Definition: arraynd_base.h:382
void fill(const T &value)
Replaces every element with value.
Definition: arraynd_base.h:359
bool is_contiguous() const
Returns true if a row-major traversal of the multidimensional array accesses each element of data in ...
Definition: arraynd_base.h:254
A base class template for a multidimensional array with elements of type T arranged in a lattice of n...
Definition: arraynd_base.h:25
bool empty() const
Returns true if there is at least one element.
Definition: arraynd_base.h:213
int64 size() const
Returns the total number of elements.
Definition: arraynd_base.h:221
int64 offset() const
Returns the number of element-size steps in memory before the first element.
Definition: arraynd_base.h:229
arraynd_base()
Definition: arraynd_base.h:120
constexpr int64 stop() const
Returns the last possible index in the sequence.
Definition: range.h:117
static void subdivide_axis(const arraynd_base< T, ndims > &arr0, arraynd_base< T, ndims+1 > &arr, int64 idim, const std::array< int64, 2 > &dims)
Definition: arraynd_base.h:564
arraynd_base< T, ndims > & operator=(const arraynd_base< T, ndims > &rhs)
Definition: arraynd_base.h:515
static void swap_axes(arraynd_base< T, ndims > &arr, int64 idim0, int64 idim1)
Definition: arraynd_base.h:554
constexpr int64 stride() const
Returns the increment between sequence indices.
Definition: range.h:123
int64_t int64
Definition: number_types.h:14
constexpr int64 start() const
Returns the first index in the sequence.
Definition: range.h:111
~arraynd_base()=default
Destructor.
static void absorb_axis(const arraynd_base< T, ndims > &arr0, arraynd_base< T, ndims-1 > &arr, int64 idim)
Definition: arraynd_base.h:592