Accessing Vector/Matrix Element Values

Assign whole arrays at once. Element-by-element assignment is for the rare case where the values are computed one at a time — see the second half of this topic.

Assigning values in bulk. Allocate with Size — the users guide calls it the preferred method for memory allocation, because setting Rows and Cols separately can allocate an intermediate the final shape never needs — then assign the values:

Matrix a;
Vector v;
a = {{-3.0, -1.0}, {1.0, 2.0}};   // sizes rows and columns as it assigns
v = {1.0, 2.0, 3.0, 4.0};

A nested initializer list carries both dimensions, so it sizes the matrix as it assigns. A flat list carries no shape: assign one only to a matrix already sized with Size — on an unsized matrix it raises EMtxVecInvalidArgument: TMtx: Length can not be set. Use Size!. A Vector takes a flat list at any time, because a vector's length is its shape.

Do not use SetIt to populate an object. It encodes the complex flag as a positional argument (SetIt(2, 2, false, ...)), which says nothing at the call site about what the storage holds.

The values name their own precision. A vector or matrix holds one of four storage precisions, and the element type of the values says which one they are. The precision is never spelled out as a separate argument, and nothing converts silently:

In C++ a list assignment writes into the floating-point precision the object already has: a list of double assigned to a single-precision vector is stored as single, and a list of float assigned to a double-precision matrix is stored as double. The element type decides real versus complex — a list of TCplx makes the object complex, a list of double makes it real. Pick the precision with Size before assigning:

StorageSize withLiteral
doubleTMtxFloatPrecision::mvDouble{-3.0, 1.0}
singleTMtxFloatPrecision::mvSingle{-3.0, 1.0} or {-3.0f, 1.0f}
complex doubleTMtxFloatPrecision::mvDoubleComplex{Cplx(1, 2), Cplx(3, 4)}
complex singleTMtxFloatPrecision::mvSingleComplex{Cplx(1, 2), Cplx(3, 4)}
using namespace Dew::Math::Units::Math387;   // Cplx, CplxSingle, IntPower, ...

Matrix c;
Matrix s;
c = {{Cplx(1, 2), Cplx(3, 4)}, {Cplx(5, 6), Cplx(7, 8)}};
s.Size(2, 2, TMtxFloatPrecision::mvSingle);
s = {{-3.0, -1.0}, {1.0, 2.0}};   // written into single-precision storage

---

There are three ways to read/write individual elements, with different trade-offs.

Three access methods on TVec/TMtx (pool-managed types):

Access methodSpeedRange checkingSingle/Double conversion
Array field (Values1D[i], CValues1D[i], etc.)FastestLimitedNo
Array property (Values[i])MediumYes (if Assertions ON)No
Default array property (a[i], a[i,j])SlowestYes (if Assertions ON)Yes (automatic)

In C++ there are three ways to read and write elements:

Access methodHow it reaches the elementRange checkingSingle/Double conversion
Pointer (PValues1D(i), PSValues1D(i), PCValues1D(i))Plain pointer into the storageNoNo
Reference accessor (Values(i), Values(r, c), Values1D(i), SValues, CValues)Inline function returning a referenceWhen MTXVEC_RANGE_CHECKING is definedNo
Default accessor (a[i], a(r, c))Library call that branches on the precisionNoYes (automatic)

The reference accessors are inline functions in the headers: with MTXVEC_RANGE_CHECKING defined they check the index and the storage precision (see topic range-checking); without it they compile to a plain memory access. Vector and Matrix offer all three.

Default array properties are slower because they also support implicit single/double precision conversion. If the internal storage is single precision but you assign a double, the conversion happens automatically. This flexibility has a performance cost.

There is no default array property for complex data — an object can have only one, and it is the real one. So a[i, j] is a double even when the

In C++ the default accessor is a(i, j) on a matrix and a[i] on a vector; it converts to double whatever the storage, and complex elements are reached through CValues(i, j), which returns a TCplx&.

a.CValues(1, 0) = Cplx(2, 0);      // CValues returns TCplx&

Example — all access methods:

Matrix a;
Vector v;

a.Size(10, 10);
a(1, 0) = 2;            // default accessor (precision-converting)
a.Values(1, 0) = 2;     // Values: reference to a double element

v.Size(10);
v[1] = 2;               // default accessor
v.Values(1) = 2;        // Values

// Obtain the internal TMtx/TVec for the fastest access
TMtx* af = a;           // implicit conversion, no copy
TVec* vf = v;           // implicit conversion, no copy

af->Values1D(1) = 2;    // flat index into the matrix storage (fastest)
vf->Values(1) = 2;      // reference to the element

// The accessor must match the storage precision:
Matrix sa;
sa.Size(10, 10, TMtxFloatPrecision::mvSingle);
sa->SValues1D(1) = 2;   // single precision storage
Matrix ca;
ca.Size(10, 10, TMtxFloatPrecision::mvDoubleComplex);
ca->CValues1D(1) = Cplx(2, 0);   // complex storage

Recommended pattern for element traversal:

In C++, declare TVec* / TMtx* parameters: a Vector or Matrix argument converts to the pointer implicitly, and inside the function Values(i) is the inline reference accessor.

void DoValueTraversalMath(TMtx* a, TVec* v)
{
    for (int i = 0; i < v->GetLength(); i++)
        v->Values(i) = i;   // inline reference accessor — fast
}

void Example()
{
    Matrix a;
    Vector v;
    a.Size(10, 10);
    v.Size(10);
    DoValueTraversalMath(a, v);  // implicit Vector -> TVec*, Matrix -> TMtx* conversion
}

Why this pattern works:

  1. const saves a compiler var assignment and prevents accidental pointer modification (const applies to the pointer, not the data it points to — you can still write elements)
  2. Passing Vector/Matrix to TVec/TMtx const parameters triggers implicit conversion, returning the pointer to the internal storage object (zero-copy)

In C++ the pattern works because the conversion operator returns the internal TVec* / TMtx* (zero-copy), and Values(i) is an inline function returning a double& — a direct memory access, plus the checks when MTXVEC_RANGE_CHECKING is defined.

Two cardinal rules for element access:

  1. Allocate Vector/Matrix as local vars, but declare TVec/TMtx in parameter lists. This triggers implicit conversion and gives you fast array field access inside functions.

In C++, return results through a TVec* / TMtx* parameter (a Vector or Matrix argument converts to it), and the caller's object receives the result without an extra allocation.

C++ pointer access (zero-copy interop):

In C++ the pointer accessors hand the storage to any function that takes a pointer and a length — zero-copy. PValues1D, PSValues1D, PCValues1D and PSCValues1D return double*, float*, TCplx* and TSCplx*; call the one that matches the storage precision.

// Pass the storage to functions that take a pointer and a length
Vector v;
v.Size(1000);
ProcessData(v.PValues1D(0), v.GetLength());           // all 1000 elements

// Matrix: one flat, row-major block of all elements
Matrix m;
m.Size(10, 20);
ProcessData(m.PValues1D(0), m.GetLength());           // all 200 elements
ProcessData(m.PValues1D(3 * m.GetCols()), m.GetCols());   // row 3 only

// Create a Vector from an array (copies data IN)
DewArray<double> src = {1, 2, 3};
Vector fromArray;
fromArray.CopyFromArray(src);

The integer types have their own accessors (IValues, SValues, BValues on TVecInt / TMtxInt), described with those types.

Single/double precision in C++: a float constant is written 1.0f, and 1.0 is a double. With SValues/PSValues1D, use float constants and float variables, so no conversion is generated in the loop.

Important: In most cases, you should not access elements individually. Use the vectorized methods (Sin, Add, Mul, etc.) which operate on entire arrays using SIMD instructions and are orders of magnitude faster than element-by-element loops.