label(arrays-storage-detailed)

bzindex(Array!storage formats)
bzindex(storage of arrays)

Blitz++ is very flexible about the way arrays are stored in memory.  
Starting indices can be 0, 1, or arbitrary numbers; arrays
can be stored in row major, column major or an order based on
any permutation of the dimensions; each dimension can be stored
in either ascending or descending order.  An N dimensional
array can be stored in latexcommand($N! 2^N$)htmlcommand(N!2<sup>N</sup>)
possible ways.

Before getting into the messy details, a review of array storage formats is
useful.  If you're already familiar with strides and bases, you might
want to skip on to the next section.

bzsubsect(Fortran and C-style arrays)

Suppose we want to store this two-dimensional array in memory:

IFDEF(latex)
(latexcommand(
\begin{displaymath}
\left [
\begin{array}{ccc}
1 & 2 & 3 \\\
4 & 5 & 6 \\\
7 & 8 & 9
\end{array}
\right ]
\end{displaymath}
))
(bzverb(
[ 1 2 3 ]
[ 4 5 6 ]
[ 7 8 9 ]
))

nsubsubsect(Row major vs. column major)

To lay the array out in memory, it's necessary to map the indices (i,j)
into a one-dimensional block.  Here are two ways the array might appear
in memory:

bzverb(
[ 1 2 3 4 5 6 7 8 9 ]
[ 1 4 7 2 5 8 3 6 9 ]
)

The first order corresponds to a C or C++ style array, and is called
em(row-major ordering): the data is stored first by row, and then
by column.  The second order corresponds to a Fortran style array,
and is called em(column-major ordering): the data is stored first
by column, and then by row.

The simplest way of mapping the indices (i,j) into one-dimensional
memory is to take a linear combination.+footnote(Taking a linear
combination is sufficient for dense, asymmetric arrays, such as
are provided by the Blitz++ tt(Array) class.)  Here's the appropriate
linear combination for row major ordering:

bzverb(
memory offset = 3*i + 1*j
)

And for column major ordering:

bzverb(
memory offset = 1*i + 3*j
)

The coefficients of the (i,j) indices are called em(strides).  For a
row major storage of this array, the em(row stride) is 3 -- you have to skip
three memory locations to move down a row.  The em(column stride)
is 1 -- you move one memory location to move to the next column.
This is also known as em(unit stride).
For column major ordering, the row and column strides are 1 and 3,
respectively.



nsubsubsect(Bases)

To throw another complication into this scheme, C-style arrays
have indices which start at zero, and Fortran-style arrays
have indices which start at one.  The first valid index value
is called the em(base).  To account for a non-zero base, it's
necessary to include an offset term in addition to the linear
combination.
Here's
the mapping for a C-style array with i=0..3 and j=0..3:

bzverb(
memory offset =  0 + 3*i + 1*j 
)

No offset is necessary since the indices start at zero for C-style
arrays.  For a Fortran-style array with i=1..4 and j=1..4, the mapping 
would be:

bzverb(
memory offset = -4 + 3*i + 1*j
)

COMMENT(In practice, the offset term is included in the array base memory address
for efficiency.)

By default, Blitz++ creates arrays in the C-style storage format (base
zero, row major ordering).  To create a Fortran-style array, you can
use this syntax:

bzverb(
Array<int,2> A(3, 3, FortranArray<2>());
)

The third parameter, tt(FortranArray<2>()), tells the tt(Array) constructor
to use a storage format appropriate for two-dimensional Fortran arrays
(base one, column major ordering).

A similar object, tt(ColumnMajor<N>), tells the tt(Array) constructor
to use column major ordering, with base zero:

bzverb(
Array<int,2> B(3, 3, ColumnMajor<2>());
)

This creates a 3x3 array with indices i=0..2 and j=0..2.

In addition to supporting the 0 and 1 conventions for C and Fortran-style 
arrays, Blitz++ allows you to choose arbitrary bases, possibly different
for each dimension.  For example, this declaration creates an array
whose indices have ranges i=5..8 and j=2..5:

bzverb(
Array<int,2> A(Range(5,8), Range(2,5));
)

bzsubsect(Creating custom storage orders)

bzindex(storage order, creating your own)
bzindex(Array!storage order, creating your own)

All tt(Array) constructors take an optional parameter of type
tt(GeneralArrayStorage<N_rank>).  This parameter encapsulates
a complete description of the storage format.  If you want a
storage format other than C or Fortran-style, you have two
choices:

startit()

it()     You can create an object of type tt(GeneralArrayStorage<N_rank>),
         customize the storage format, and use the object as a argument
         for the tt(Array) constructor.

it()     You can create your own storage format object which inherits
         from tt(GeneralArrayStorage<N_rank>).  This is useful if you
         will be using the storage format many times.  This approach
         (inheriting from tt(GeneralArrayStorage<N_rank>)) was used
         to create the tt(FortranArray<N_rank>) objects.  If you want
         to take this approach, you can use the declaration of
         tt(FortranArray<N_rank>) in tt(<blitz/array.h>) as a guide.

endit()

The next sections describe how to modify a tt(GeneralArrayStorage<N_rank>)
object to suit your needs.

nsubsubsect(In higher dimensions)

In more than two dimensions, the choice of storage order becomes more
complicated.  Suppose we had a 3x3x3 array.  To map the indices (i,j,k)
into memory, we might choose one of these mappings:

bzverb(
memory offset = 9*i + 3*j + 1*k
memory offset = 1*i + 3*j + 9*k
)

The first corresponds to a C-style array, and the second to a Fortran-style
array.  But there are other choices; we can permute the strides (1,3,9)
any which way:

bzverb(
memory offset = 1*i + 9*j + 3*k
memory offset = 3*i + 1*j + 9*k
memory offset = 3*i + 9*j + 1*k
memory offset = 9*i + 1*j + 3*k
)

For an N dimensional array, there are N! such permutations.  Blitz++
allows you to select any permutation of the dimensions as a storage
order.  First you need to create an object of type 
tt(GeneralArrayStorage<N_rank>):

bzverb(
GeneralArrayStorage<3> storage;
)

tt(GeneralArrayStorage<N_rank>) contains a vector called tt(ordering)
which controls the order in which dimensions are stored in memory.
The tt(ordering) vector will contain a permutation of the numbers
0, 1, ...,  N_rank-1.  Since some people are used to the first
dimension being 1 rather than 0, a set of symbols (firstDim, secondDim,
..., eleventhDim) are provided which make the code more legible.  

The tt(ordering) vector lists the dimensions in increasing order of
stride.  You can access this vector using the member function
tt(ordering()).  A C-style array, the default, would have:

bzverb(
storage.ordering() = thirdDim, secondDim, firstDim;
)

meaning that the third index (k) is associated with the smallest
stride, and the first index (i) is associated with the largest
stride.  A Fortran-style array would have:

bzverb(
storage.ordering() = firstDim, secondDim, thirdDim;
)

nsubsubsect(Reversed dimensions)

To add yet another wrinkle, there are some applications where the
rows or columns need to be stored in reverse order.+footnote(For example,
certain bitmap formats store image rows from bottom to top rather than
top to bottom.)

Blitz++ allows you to store each dimension in either ascending or
descending order.  By default, arrays are always stored in ascending
order.  The tt(GeneralArrayStorage<N_rank>) object contains a
vector called tt(ascendingFlag) which indicates whether each
dimension is stored ascending (tt(true)) or descending (tt(false)).  
To alter the contents of this vector, use the tt(ascendingFlag()) method:

bzverb(
// Store the third dimension in descending order
storage.ascendingFlag() = true, true, false;

// Store all the dimensions in descending order
storage.ascendingFlag() = false, false, false;
)

nsubsubsect(Setting the base vector)

tt(GeneralArrayStorage<N_rank>) also has a tt(base) vector which
contains the base index value for each dimension.  By default,
the base vector is set to zero.  tt(FortranArray<N_rank>) sets
the base vector to one.  

To set your own set of bases, you have two choices:

startit()

it()      You can modify the tt(base) vector inside the
          tt(GeneralArrayStorage<N_rank>) object.  The method
          tt(base()) returns a mutable reference to the tt(base)
          vector which you can use to set the bases.

it()      You can provide a set of tt(Range) arguments to the
          tt(Array) constructor.

endit()

Here are some examples of the first approach:

bzverb(
// Set all bases equal to 5
storage.base() = 5;    

// Set the bases to [ 1 0 1 ]
storage.base() = 1, 0, 1;
)

And of the second approach:

bzverb(
// Have bases of 5, but otherwise C-style storage
Array<int,3> A(Range(5,7), Range(5,7), Range(5,7));

// Have bases of [ 1 0 1 ] and use a custom storage
Array<int,3> B(Range(1,4), Range(0,3), Range(1,4), storage);
)

nsubsubsect(Working simultaneously with different storage orders)

Once you have created an array object, you will probably never
have to worry about its storage order.  Blitz++ should handle
arrays of different storage orders transparently.  It's possible
to mix arrays of different storage orders in one expression,
and still get the correct result.

Note however, that mixing different storage orders in an
expression may incur a performance penalty, since Blitz++ will
have to pay more attention to differences in indexing than
it normally would.

You may not mix arrays with different domains in the
same expression.  For example, adding a base zero to
a base one array is a no-no.  The reason for this
restriction is that certain expressions become
ambiguous, for example:

bzverb(
Array<int,1> A(Range(0,5)), B(Range(1,6));
A=0;
B=0;
using namespace blitz::tensor;
int result = sum(A+B+i);
)

Should the index tt(i) take its domain from array tt(A)
or array tt(B)?  To avoid such ambiguities, users are
forbidden from mixing arrays with different domains
in an expression. 

nsubsubsect(Debug dumps of storage order information)

In debug mode (tt(-DBZ_DEBUG)), class tt(Array) provides a member
function tt(dumpStructureInformation()) which displays information
about the array storage:

bzverb(\
Array<float,4> A(3,7,8,2,FortranArray<4>());
A.dumpStructureInformation(cerr);
)

The optional argument is an tt(ostream) to dump information
to.  It defaults to tt(cout).  Here's the output:

bzverb(\
Dump of Array<float, 4>:
ordering_      = [          0         1         2         3 ]
ascendingFlag_ = [ 1111 ]
base_          = [          1         1         1         1 ]
length_        = [          3         7         8         2 ]
stride_        = [          1         3        21       168 ]
zeroOffset_    = -193
numElements()  = 336
storageContiguous = 1
)

nsubsubsect(A note about storage orders and initialization)

When initializing arrays with comma delimited lists, note
that the array is filled in storage order: from the first
memory location to the last memory location.  This won't
cause any problems if you stick with C-style arrays, but
it can be confusing for Fortran-style arrays:

bzverb(
Array<int,2> A(3, 3, FortranArray<2>());
A = 1, 2, 3,
    4, 5, 6,
    7, 8, 9;
cout << A << endl;
)

The output from this code excerpt will be:

bzverb(
A = 3 x 3
         1         4         7 
         2         5         8
         3         6         9
)

This is because Fortran-style arrays are stored in column
major order.

bzsubsect(Storage orders example)

bzexamplefile(examples/storage.cpp)

And the output:

bzexamplefile(examples/storage.out)

