ivl

ivl step 1 - arrays

ivl array is a one-dimensional data structure, a template container that uses contiguous memory to store data and provides constant-time random access. array comes in different flavors. The default is similar to std::vector in that it is allocated on heap and supports e.g. constant-time swap, hence efficient copy construction, and less efficient resizing. Another variant of fixed size is rather similar to C arrays, allocated on stack, with no space overhead and no resizing. Both provide STL-compatible iterators and powerful subscripting.

a first array

Let us begin with a source file, say array.cpp. We are including candidate.hpp because we will play with candidates once more. Of course, it is not a problem to include <ivl/ivl> twice.

Maybe the simplest way to construct an array with a few known elements is to write them down explicitly with function arr:

where by array<T> we say that T is the data type of the array [double in this case]. We can display the array at the standard output with streaming operator <<

operators

We can also declare an array without initialization, in which case it is left unallocated:

We can then assign [copy] an existing array into another, e.g. b = a; or we can form an expression first, e.g. by applying an operator between an array and a scalar or primitive type,

in which case we add 1 to each element of a. Similarly, we can add two arrays, element by element. The arrays do not even have to be of the same data type:

Whatever rule applies in C/C++ for the resulting type of the operator for primitive types [e.g., adding an int to a double yields a double], the same rule carries forward to arrays [adding an array<int> to an array<double> yields an array<double>].

We can also use arrays with compound assignment operators. In doing so, we can directly display the result of the expression without assigning to an array first:

Relational operators work as well, either between an array and a scalar, or between arrays, yielding arrays of bool:

type abstraction

All arithmetic, relational, logical, bitwise and compound assignment C++ operators are similarly extended for ivl arrays of any type. As long as an operator has been first defined for a data type, it carries forward to an array of this type, and this applies to user defined types as well.

For instance, consider a list of candidates:

Now, given a new candidate, Charlie,

how do candidates in our list compare to Charlie?

We will also see the names of these candidates in a while.

functions

There are array member functions, for instance size() returns the size of an array [synonym: length()]

Member functions can also modify an array. For instance, in the style of std::vector, let us add Charlie in our list, and confirm he is in the last position:

There also non-member functions yielding a scalar quantity, for instance

where we have disambiguated ivl::max in favor of std::max.

Or, functions may return a new array, for instance operating element-wise. Returning to numerics, all ivl math functions, including all standard C++ functions in <cmath>, are extended to arrays, e.g.

where floor() maps to the largest previous integer and extends [uses] its <cmath> counterpart, while round() maps to the nearest one and is new in ivl.

The new array may be of different data type. For instance, as in relational operators, it may be an array of bool:

ranges

ivl range is a special kind of array that represents a sequence of elements uniformly distributed in an interval, otherwise a uniform partition of the interval. A range behaves largely as an array but does not really store all elements. For instance,

constructs a range from 1 to 5 with step 1, only storing the two endpoints. On the other hand,

does the same for an array, now storing all five elements. Syntax (a,_,b) denotes a range from a to b and is equivalent to range<T>(a,b), where T is the type of a,b [or, if a,b are of different types, T is determined according to C/C++ rules for binary operators].

A range can also have an arbitrary step:

where shortcut (a,_,b,_c) denotes a range from a to c with step b and is equivalent to range<T>(a,b,c) [again with T determined automatically].

complex arrays

A complex array may constructed by explicitly giving its complex elements, or more conveniently from two existing real arrays:

Math functions apply equally to complex arrays, as defined for scalar quantities. For instance, the complex conjugate is found as

As an example on polar coordinates, let us distribute $n$ points uniformly on the unit circle. Since, according to Lagrange's trigonometric identities, \[ \sum_{k=0}^{n-1} e^{i2\pi k/n} = 0, \] the point coordinates should sum to zero [alternatively, being the vertices of a regular $n$-sided polygon inscribed in the unit circle, their centroid should lie on the origin]. Let us confirm this fact:

where we have used a real range in an expression constructing a complex array, exp() is the exponential function, and sum() is the sum over all elements of an array of any type, assuming operator + is defined.

expressions

We have already seen examples of expressions, where functions and operators are combined. One important lesson is that the form of an ivl expression does not necessarily imply anything on the sequence of operations in the underlying implementation. Of particular interest are expressions of ranges and expressions where all functions and operators involved are element-wise. We will focus on the latter.

Let us compute the density of a normal distribution \[ \mathcal{N}(x|\mu,\sigma^2) = \frac{1}{\sigma\sqrt{2\pi}} e^{-\frac{1}{2\sigma^2} (x-\mu)^2} \] for all elements of array a, given its mean $\mu$ and standard deviation $\sigma$:

First, observe that we enclose sigma in a scalar by _[] to apply the exponentiation operator, while (a-mu) is an array and exponentiation comes for free.

Second, think for a moment how this expression may be evaluated in practice. A naive approach would be to generate a new temporary array for each application of an operator or function. For instance,

This would require at least four temporary arrays, along with their allocation, copy, and de-allocation.

Make sure you check ivl step 2 - indexing to see how this is not the case in ivl!

done for now

This completes source file array.cpp: