//# ArrayAccessor.h: Fast 1D accessor/iterator for nD array classes //# Copyright (C) 2002,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library is distributed in the hope that it will be useful, but WITHOUT //# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or //# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef CASA_ARRAYACCESSOR_2_H #define CASA_ARRAYACCESSOR_2_H //# Includes #include "Array.h" namespace casacore { //#Begin casa namespace //# Hide simple Axis classes names from outside module namespace { // Class to enumerate compile-time axis numeration template struct Axis { enum { // Specify the constant axis N=AX }; }; // Class to specify run-time axis values struct AxisN { // Construct the run-time axis number explicit AxisN(const size_t n) : N(n) {} // Axis number size_t N; }; } // Axis independent base for the ArrayAccessor classes // // // The ArrayBaseAccessor class implements the axis independent parts of the // ArrayAccessor class. It can only be used from the ArrayAccessor class. // template class ArrayBaseAccessor { protected: //# Constructors // // Default constructor (for use in e.g. containers) ArrayBaseAccessor() : arrayPtr_p(0), axis_p(0), ptr_p(0), step_p(0), begin_p(0), end_p(0) {;} // Construct from an Array // explicit ArrayBaseAccessor(const Array &arr) : arrayPtr_p(&arr), axis_p(0), ptr_p(const_cast(arrayPtr_p->data())), step_p(0), begin_p(0), end_p(0) {;} ArrayBaseAccessor(const Array &arr, const size_t ax) : arrayPtr_p(&arr), axis_p(ax), ptr_p(const_cast(arrayPtr_p->data())), step_p(0), begin_p(0), end_p(0) {;} // // Copy constructor (copy semantics) // ArrayBaseAccessor(const ArrayBaseAccessor &other) : arrayPtr_p(other.arrayPtr_p), axis_p(other.axis_p), ptr_p(other.ptr_p), step_p(other.step_p), begin_p(other.begin_p), end_p(other.end_p) {;} ArrayBaseAccessor(const ArrayBaseAccessor &other, const size_t ax) : arrayPtr_p(other.arrayPtr_p), axis_p(ax), ptr_p(other.ptr_p), step_p(other.step_p), begin_p(other.begin_p), end_p(other.end_p) {;} // //# Destructor // Destructor ~ArrayBaseAccessor() {;} // // Assignment (copy semantics) ArrayBaseAccessor &operator=(const ArrayBaseAccessor &other) { if (&other != this) { arrayPtr_p = other.arrayPtr_p; ptr_p = other.ptr_p; }; return *this; } // (Re-)initialize from Array // void init(const Array &arr) { arrayPtr_p = &arr; ptr_p = const_cast(arrayPtr_p->data()); } void init(const Array &arr, const size_t ax) { arrayPtr_p = &arr; axis_p = ax; ptr_p = const_cast(arrayPtr_p->data()); } void init(const size_t ax) { arrayPtr_p = 0; axis_p = ax; ptr_p = 0; } // public: //# Operators // Iterator-like operations. // void operator+=(const size_t ix) { ptr_p += ix*step_p; } void operator-=(const size_t ix) { ptr_p -= ix*step_p; } void operator++() { ptr_p += step_p; } void operator++(int) { ptr_p += step_p; } void operator--() { ptr_p -= step_p; } void operator--(int) { ptr_p -= step_p; } // // Dereferencing. // const T &operator*() const { return *ptr_p; } T &operator*() { return *ptr_p; } T *data() { return ptr_p; } const Array &baseArray() { return *arrayPtr_p; } size_t step() { return step_p; } // // Index along current axis // const T &operator[](const int ix) const { return *(ptr_p + ix*step_p); }; T &operator[](const int ix) { return *(ptr_p + ix*step_p); } // // End of index on line // const T *end() { return end_p; } const T *end(const int n) { return end_p + n*step_p; } // // Start of index on line // const T *begin() { return begin_p; } const T *begin(const int n) { return begin_p + n*step_p; } // // End when reverse indexing // const T *rend() { return begin_p-step_p; } const T *rend(const int n) { return begin_p + (n-1)*step_p; } // // Begin when reverse indexing // const T *rbegin() { return end_p-step_p; } const T *rbegin(const int n) { return end_p + (n-1)*step_p; } // protected: //# Data // The pointer to belonging array const Array *arrayPtr_p; // Current run-time axis size_t axis_p; // Current access pointer T *ptr_p; // The increment to go from one point along an axis, to the next. int step_p; // The start element of array const T *begin_p; // The one element beyond last on line const T *end_p; }; // Fast 1D accessor/iterator for nD array classes // // // // //
  • Array indexing and access methods // (Array) // // // // Array and access, rather than Iterator, which would suggest more // standard-like interfaces // // // // Accessing a large multi-dimensional array by varying the indices of the // array can be a slow process. Timing indications are that for a cube // indexing with 3 indices was about seven times slower than using a // standard 1D C-like index into an array of basic int types. // Improvements have made this less, partly due to some pre-calculation // necessary for this class, but can still be a factor of more than 3 // slower. There are a variety of ways to access elements // cube(i,j,k): //
      //
    • Complete random access in all dimensions will need the // use of the indexing: cube(i,j,k); or // cube(IPosition(3)) as described in the // Array and // Cube classes //
    • Ordered access of all (or most) elements in an Array // (in memory order) can be best achieved by the use of Array's // STLIterator classes. // This is the fastest way for non-contiguous arrays, and only slightly // slower than the use of getStorage for contiguous arrays. //
    • Ordered access along memory order can also be achieved by the use // of the // // getStorage() method. // For contiguous arrays this could be slightly faster than the use of // the STLIterator (about 10% faster), but slower for // non-contiguous arrays. In addition it needs additional memory // resources, which will lead to extra overhead. The general use of // getStorage is discouraged with the introduction of the STLIterator. // It should only be used when an interface to routines in // other languages is needed (like Fortran), or when a large Array is // known to be contiguous, and the data have to be referenced many times. //
    • Access along one or more axes of a (large) multi-dimensional array // is best achieved using the ArrayAccessor class. Its total // access time is about 2 times faster than indexing (for cubes, // more for more indices), //
    • Special iteration (like in chunks) are catered for by the // ArrayIterator, // MatrixIterator, // VectorIterator classes. //
    // The ArrayAccessor class is an iterator like pointer to the data // in the array. It is a 1-dimensional accessor. It is created with either // a constant (at compile time) axis indicator, or with a run-time // axis selector. ArrayAccessor constructor accepts a const Array<>. // However, the underlying Array class can be modified at this moment. In // future a ConstArrayAccessor class is foreseen. // // Matrix mat(1000,500); // A 1000*500 matrix // // Fill Matrix ... // // Loop over index 1, than index 0: // for (ArrayAccessor > i(mat); i != i.end(); ++i) { // for (ArrayAccessor > j(i); j |= j.end(); ++j) { // // Actions on *j (which points to mat(j,i)) or j[n] // // (which points to mat(j+n,i)) // }} // // For run-time indices it would look like: // // Matrix mat(1000,500); // A 1000*500 matrix // // Fill Matrix ... // // Loop over index 1, than index 0: // for (ArrayAccessor i(mat, AxisN(1)); // i != i.end(); ++i) { // for (ArrayAccessor j(i,AxisN(0)); j |= j.end(); ++j) { // // Actions on *j (which points to mat(j,i)) or j[n] // // (which points to mat(j+n,i)) // }} // // Compile-time and run-time axes can be mixed in constructors and assignments. // // Like in all comparable situations, memory allocation // within a loop can slow down processes. For that reason the example above // can be better written (about 25% faster) as: // // Matrix mat(1000,500); // A 1000*500 matrix // ArrayAccessor > j; // accessor pre-allocated // // Fill Matrix ... // // Loop over index 1, than index 0: // for (ArrayAccessor > i(mat); i != i.end(); ++i) { // for (j=i; j |= j.end(); ++j) { // // Actions on *j (which points to mat(j,i)) or j[n] // // (which points to mat(j+n,i)) // }} // // // The underlying Array classes are structured with the // first index varying fastest. This means that in general (due to caching and // swapping) operations are fastest when Axis<0> > is in the // innermost loop (if possible of course). // // The demonstrator and test programs have more examples. // // The accessors can be dereferenced by the dereference operator (*) // and by the index operator ([int]), which can handle negative // values. // Points around the accessor in any axis direction can be addressed // along any axis by the templated methods next(), // prev() and index(int). Either run-time or // compile-time axes can be used (see example). // // An accessor can be re-initialized with the init() function. It can also // be reset() to any pointer value. Mthods end(), // begin(), rbegin() and rend() are available // for loop control (like in the STL iterators). In addition each of these // can have an optional integer argument, specifying an offset (in points // along the current axis). // // Operations ++ -- += -= are available. // // This class is available for Axis and AxisN // specializations only. //
    // // // // // get a cube and fill it // Cube cub(5,2,4); // indgen(cub); // // Loop over axes 2-0 and use index() over axis 1 // for (ArrayAccessor > i(cub); i != i.end() ; ++i) { // for (ArrayAccessor > j(i); // j != j.end(); ++j) { // // show result // cout << *j << ", " << j.index >(1) << endl; // }; // }; // // See the demonstrator program in // aips/implement/Arrays/test/dArrayAccessor.cc and the // test program tArrayAccessor for more examples. // // // // To speed up especially interpolation code // // // //
  • Any valid Array templating argument // // //
  • A class Axis //
  • Class AxisN // // // //
  • Exceptions created in the Array class //
  • Addressing errors // // // //
  • add a ConstArrayAccessor class // // // \see ArrayAccessor > // \see ArrayAccessor //# Next one suffices as declaration: only (part) specialisations allowed template class ArrayAccessor; // Specialization for compile-time axes. \see ArrayAccessor template class ArrayAccessor > : public ArrayBaseAccessor { public: // Constructors // // Default ctor. Note only available to accommodate containers of // ArrayAccessors. Use init() to initialize. ArrayAccessor() : ArrayBaseAccessor() {;} // Construct an accessor from specified Array along the selected axis. // The accessor will point to the first element along the axis (i.e. // at (0,0,...)). explicit ArrayAccessor(const Array &arr) : ArrayBaseAccessor(arr) { initStep(); } // Construct from an ArrayAccessor along same axis. The accessor will point // at the same element as the originator. ArrayAccessor(const ArrayAccessor > &other) : ArrayBaseAccessor(other) {;} // Construct from accessor along another (or run-time) axis. // The accessor will point to the same element (but will be oriented // along another axis). // template explicit ArrayAccessor(const ArrayAccessor > &other) : ArrayBaseAccessor(other) { initStep(); } explicit ArrayAccessor(const ArrayAccessor &other) : ArrayBaseAccessor(other) { initStep(); } // // Destructor ~ArrayAccessor() {;} // // Assignment (copy semantics) // // Assign from other compile-time accessor along same axis ArrayAccessor &operator=(const ArrayAccessor > &other) { if (&other != this) { ArrayBaseAccessor::operator=(other); this->step_p = other.step_p; this->begin_p = other.begin_p; this->end_p = other.end_p; }; return *this; } // Assign from other compile-time accessor along another axis template ArrayAccessor &operator=(const ArrayAccessor > &other) { ArrayBaseAccessor::operator=(other); initStep(); return *this; } // Assign from run-time accessor along any axis ArrayAccessor &operator=(const ArrayAccessor &other) { ArrayBaseAccessor::operator=(other); initStep(); return *this; } // // (Re-)initialization to start of array (i.e. element (0,0,0,...)) void init(const Array &arr) { ArrayBaseAccessor::init(arr); initStep(); } // Reset to start of dimension or to specified pointer // void reset() { this->ptr_p = const_cast(this->begin_p); } void reset(const T * p) { this->ptr_p = const_cast(p); initStep(); } // // Indexing operations along another axis than the one of the current // object. See for the indexing and iterator operations along the // object's axis ArrayBaseAccessor // // Get the value 'next' along the specified axis (e.g. with // a.next >()) // template const T &next() const { return *(this->ptr_p + this->arrayPtr_p->steps()[X::N]); } template T &next() { return *(this->ptr_p + this->arrayPtr_p->steps()[X::N]); } // // Get the value 'previous' along the specified axis (e.g. with // a.prev >()) // template const T &prev() const { return *(this->ptr_p - this->arrayPtr_p->steps()[X::N]); } template T &prev() { return *(this->ptr_p - this->arrayPtr_p->steps()[X::N]); } // // Get the next or previous along the specified run-time axis. E.g. // a.prev(AxisN(2)). // const T &next(const AxisN ax) const { return *(this->ptr_p + this->arrayPtr_p->steps()[ax.N]); } T &next(const AxisN ax) { return *(this->ptr_p + this->arrayPtr_p->steps()[ax.N]); } const T &prev(const AxisN ax) const { return *(this->ptr_p - this->arrayPtr_p->steps()[ax.N]); } T &prev(const AxisN ax) { return *(this->ptr_p - this->arrayPtr_p->steps()[ax.N]); } // // Give the value indexed with respect to the current accessor value // along the axis specified as either a compile-time or a run-time // axis. E.g. a.index >(5) or // a.index(5, AxisN(3)). // template const T &index(const int ix) const { return *(this->ptr_p + ix*this->arrayPtr_p->steps()[X::N]); } template T &index(const int ix) { return *(this->ptr_p + ix*this->arrayPtr_p->steps()[X::N]); } const T &index(const int ix, const AxisN ax) const { return *(this->ptr_p + ix*this->arrayPtr_p->steps()[ax.N]); } T &index(const int ix, const AxisN ax) { return *(this->ptr_p + ix*this->arrayPtr_p->steps()[ax.N]); } // // // Comparison. The comparisons are done for the accessor pointer // value. They can be used to control loops. // bool operator==(const ArrayAccessor > &other) const { return this->ptr_p == other.ptr_p; } bool operator!=(const ArrayAccessor > &other) const { return this->ptr_p != other.ptr_p; } bool operator==(const T *other) const { return this->ptr_p == other; } bool operator!=(const T *other) const { return this->ptr_p != other; } // private: // Get proper offset int initOff(int x, size_t ax) { size_t st = this->arrayPtr_p->steps()[ax]; return ((st) ? (ax == Axis::N ? x/st : initOff(x%st, ax-1)) : 0); } // Initialize some internal values void initStep() { this->step_p = this->arrayPtr_p->steps()[Axis::N]; this->begin_p = this->end_p = this->ptr_p - initOff(this->ptr_p - this->arrayPtr_p->data(), this->arrayPtr_p->ndim()-1)*this->step_p; this->end_p += this->arrayPtr_p->shape()[Axis::N]*this->step_p; } }; // Specialization for run-time axes // // // This class is a specialization for run-time axis selection within the // array accessor. The axis is specified in the constructors and in the // special indexing operators (prev, next, index) with // a parameter AxisN(n) in stead of a template parameter // >. // \see ArrayAccessor // // template class ArrayAccessor : public ArrayBaseAccessor { public: // Constructors // explicit ArrayAccessor(const AxisN ax=AxisN(0)) : ArrayBaseAccessor() { this->axis_p = ax.N; } explicit ArrayAccessor(Array &arr, const AxisN ax=AxisN(0)) : ArrayBaseAccessor(arr, ax.N) { initStep(); } ArrayAccessor(ArrayAccessor &other) : ArrayBaseAccessor(other) {;} explicit ArrayAccessor(ArrayAccessor &other, const AxisN ax) : ArrayBaseAccessor(other, ax.N) { initStep(); } template explicit ArrayAccessor(ArrayAccessor > &other, const AxisN ax=AxisN(0)) : ArrayBaseAccessor(other, ax.N) { initStep(); } ArrayAccessor &operator=(const ArrayAccessor &other) { if (&other != this) { ArrayBaseAccessor::operator=(other); initStep(); }; return *this; } template ArrayAccessor &operator=(const ArrayAccessor > &other) { ArrayBaseAccessor::operator=(other); initStep(); return *this; } // // Destructor ~ArrayAccessor() {;} // (Re-)initialization to start of array (i.e. element (0,0,0,...)) or // re-initialize to an axis. // void init(const Array &arr, const AxisN ax) { ArrayBaseAccessor::init(arr, ax.N); initStep(); } void init(const AxisN ax) { ArrayBaseAccessor::init(ax.N); } // // Reset to start of dimension or to specified pointer // void reset() { this->ptr_p = const_cast(this->begin_p); } void reset(const T *p) { this->ptr_p = const_cast(p); initStep(); } // // Indexing operations along another axis than the one of the current // object. See for the indexing and iterator operations along the // object's axis ArrayBaseAccessor // template const T &next() const { return *(this->ptr_p + this->arrayPtr_p->steps()[X::N]); } template T &next() { return *(this->ptr_p + this->arrayPtr_p->steps()[X::N]); } template const T &prev() const { return *(this->ptr_p - this->arrayPtr_p->steps()[X::N]); } template T &prev() { return *(this->ptr_p - this->arrayPtr_p->steps()[X::N]); } const T &next(const AxisN ax) const { return *(this->ptr_p + this->arrayPtr_p->steps()[ax.N]); } T &next(const AxisN ax) { return *(this->ptr_p + this->arrayPtr_p->steps()[ax.N]); } const T &prev(const AxisN ax) const { return *(this->ptr_p - this->arrayPtr_p->steps()[ax.N]); } T &prev(const AxisN ax) { return *(this->ptr_p - this->arrayPtr_p->steps()[ax.N]); } template const T &index(const int ix) const { return *(this->ptr_p + ix*this->arrayPtr_p->steps()[X::N]); } template T &index(const int ix) { return *(this->ptr_p + ix*this->arrayPtr_p->steps()[X::N]); } const T &index(const int ix, const AxisN(ax)) const { return *(this->ptr_p + ix*this->arrayPtr_p->steps()[ax.N]); } T &index(const int ix, const AxisN(ax)) { return *(this->ptr_p + ix*this->arrayPtr_p->steps()[ax.N]); } // // Comparisons // bool operator==(const ArrayAccessor &other) const { return this->ptr_p == other.ptr_p; } bool operator!=(const ArrayAccessor &other) const { return this->ptr_p != other.ptr_p; } bool operator==(const T *other) const { return this->ptr_p == other; } bool operator!=(const T *other) const { return this->ptr_p != other; } // private: // Get proper offset int initOff(int x, size_t ax) { size_t st = this->arrayPtr_p->steps()[ax]; return ((st) ? (ax == this->axis_p ? x/st : initOff(x%st, ax-1)) : 0); } // Initialize some internal values void initStep() { this->step_p = this->arrayPtr_p->steps()[this->axis_p]; this->begin_p = this->end_p = this->ptr_p - initOff(this->ptr_p - this->arrayPtr_p->data(), this->arrayPtr_p->ndim()-1)*this->step_p; this->end_p += this->arrayPtr_p->shape()[this->axis_p]*this->step_p; } }; } //#End casa namespace #endif