### 3.6 Index placeholders ¶

Blitz++ provides objects called index placeholders which represent array indices. They can be used directly in expressions.

There is a distinct index placeholder type associated with each dimension of an array. The types are called `firstIndex`, `secondIndex`, `thirdIndex`, ..., `tenthIndex`, `eleventhIndex`. Here’s an example of using an index placeholder:

    Array<float,1> A(10);
    firstIndex i;
    A = i;

This generates code which is similar to:

    for (int i=0; i < A.length(); ++i)
        A(i) = i;

Here’s an example which fills an array with a sampled sine wave:

    Array<float,1> A(16);
    firstIndex i;

    A = sin(2 * M_PI * i / 16.);

If your destination array has rank greater than 1, you may use multiple index placeholders:

    // Fill a two-dimensional array with a radially
    // symmetric, decaying sinusoid

    // Create the array
    int N = 64;
    Array<float,2> F(N,N);

    // Some parameters
    float midpoint = (N-1)/2.;
    int cycles = 3;
    float omega = 2.0 * M_PI * cycles / double(N);
    float tau = - 10.0 / N;

    // Index placeholders
    firstIndex i;
    secondIndex j;

    // Fill the array
    F = cos(omega * sqrt(pow2(i-midpoint) + pow2(j-midpoint)))
        * exp(tau * sqrt(pow2(i-midpoint) + pow2(j-midpoint)));

Here’s a plot of the resulting array:

Array filled using an index placeholder expression.

You can use index placeholder expressions in up to 11 dimensions. Here’s a three dimensional example:

    // Fill a three-dimensional array with a Gaussian function
    Array<float,3> A(16,16,16);
    firstIndex i;
    secondIndex j;
    thirdIndex k;
    float midpoint = 15/2.;
    float c = - 1/3.0;
    A = exp(c * (sqr(i-midpoint) + sqr(j-midpoint)
        + sqr(k-midpoint)));

You can mix array operands and index placeholders:

    Array<int,1> A(5), B(5);
    firstIndex i;

    A = 0, 1, 1, 0, 2;
    B = i * A;          // Results in [ 0, 1, 2, 0, 8 ]

For your convenience, there is a namespace within blitz called `tensor` which declares all the index placeholders:

    namespace blitz {
      namespace tensor {
        firstIndex i;
        secondIndex j;
        thirdIndex k;
         ...
        eleventhIndex t;
      }
    }

So instead of declaring your own index placeholder objects, you can just say

    namespace blitz::tensor;

when you would like to use them. Alternately, you can just preface all the index placeholders with `tensor::`, for example:

    A = sin(2 * M_PI * tensor::i / 16.);

This will make your code more readable, since it is immediately clear that `i` is an index placeholder, rather than a scalar value.

### 3.7 Type promotion ¶

When operands of different numeric types are used in an expression, the result gets promoted according to the usual C-style type promotion. For example, the result of adding an `Array<int>` to an `Arrray<float>` will be promoted to `float`. Generally, the result is promoted to whichever type has greater precision.

#### Type promotion for user-defined types ¶

The rules for type promotion of user-defined types (or types from another library) are a bit complicated. Here’s how a pair of operand types are promoted:

- If both types are intrinsic (e.g. bool, int, float) then type promotion follows the standard C rules. This generally means that the result will be promoted to whichever type has greater precision. In Blitz++, these rules have been extended to incorporate `complex<float>`, `complex<double>`, and `complex<long double>`.
- If one of the types is intrinsic (or complex), and the other is a user-defined type, then the result is promoted to the user-defined type.
- If both types are user-defined, then the result is promoted to whichever type requires more storage space (as determined by `sizeof()`). The rationale is that more storage space probably indicates more precision.

If you wish to alter the default type promotion rules above, you have two choices:

- If the type promotion behaviour isn’t dependent on the type of operation performed, then you can provide appropriate specializations for the class `promote_trait<A,B>` which is declared in `<blitz/promote.h>`.
- If type promotion does depend on the type of operation, then you will need to specialize the appropriate function objects in `<blitz/ops.h>`.

Note that you can do these specializations in your own header files (you don’t have to edit `promote.h` or `ops.h`).

#### Manual casts ¶

There are some inconvenient aspects of C-style type promotion. For example, when you divide two integers in C, the result gets truncated. The same problem occurs when dividing two integer arrays in Blitz++:

    Array<int,1> A(4), B(4);
    Array<float,1> C(4);

    A = 1, 2, 3, 5;
    B = 2, 2, 2, 7;

    C = A / B;      // Result:  [ 0  1  1  0 ]

The usual solution to this problem is to cast one of the operands to a floating type. For this purpose, Blitz++ provides a function `cast(expr,type)` which will cast the result of expr as type:

    C = A / cast(B, float());   // Result: [ 0.5  1  1.5  0.714 ]

The first argument to `cast()` is an array or expression. The second argument is a dummy object of the type to which you want to cast. Once compilers support templates more thoroughly, it will be possible to use this cast syntax:

    C = A / cast<float>(B);

But this is not yet supported.