How to generate tupled combinations of arbitrary number of vectors - c++

This question was kind of asked before, but I'm not sure a satisfactory response was really offered. For me, I'm not interested in landing in a std::vector of std::string, per se, but rather a std::tuple.
For instance, if I've got std::vector<A>, std::vector<B>, and std::vector<C>, then I expect perhaps std::vector<std::tuple<A, B, C>>. Or, even std::set<std::tuple<A, B, C>>, if that was more appropriate.
Now, I could encode nested for loops, however, I'd like to do this via functions, template functions if possible, then I suppose variadic would be necessary to accomplish the task.
There are no guarantees that A, B, or C have anything to do with each other, much less conversion to std::string, as were proposed in a couple of the responses.
I want to say there could be a variadic solution to that, but I'm not exactly sure how to compose the std::vector<T> or std::vector<T>::value_type definitions.

If you want to compute the Cartesian product of heterogeneous vectors, you may do something like:
template <std::size_t N>
bool increase(const std::array<std::size_t, N>& sizes, std::array<std::size_t, N>& it)
{
for (std::size_t i = 0; i != N; ++i) {
const std::size_t index = N - 1 - i;
++it[index];
if (it[index] >= sizes[index]) {
it[index] = 0;
} else {
return true;
}
}
return false;
}
template <typename F, std::size_t ... Is, std::size_t N, typename Tuple>
void apply_impl(F&& f,
std::index_sequence<Is...>,
const std::array<std::size_t, N>& it,
const Tuple& tuple)
{
f(std::get<Is>(tuple)[it[Is]]...);
}
template <typename F, typename ... Ts>
void iterate(F&& f, const std::vector<Ts>&... vs)
{
constexpr std::size_t N = sizeof...(Ts);
std::array<std::size_t, N> sizes{{vs.size()...}};
std::array<std::size_t, N> it{{(vs.size(), 0u)...}};
do {
apply_impl(f, std::index_sequence_for<Ts...>(), it, std::tie(vs...));
} while (increase(sizes, it));
}
Demo

Related

Expand two parameter packs

Consider following piece of code:
static constexpr size_t Num {2};
struct S {
std::array<size_t, Num> get () { return {1, 2}; }
};
struct S1 : S {};
struct S2 : S {};
struct M {
template <typename T>
typename std::enable_if<std::is_same<T, S1>::value, S1>::type get () const {
return S1 {};
}
template <typename T>
typename std::enable_if<std::is_same<T, S2>::value, S2>::type get () const {
return S2 {};
}
};
I want to have a function which merges two or more std::arrays making one std::array.
So far I ended with something like this:
template <typename Mode, typename... Rs, size_t... Ns>
std::array<size_t, sizeof... (Rs)*Num> get_array (const Mode& mode, Sequence::Sequence<Ns...>) {
return {std::get<Ns> (mode.template get<Rs...> ().get ())...};
}
I want to have that the following code
M m;
auto x = get_array<M, S1, S2> (m, Sequence::Make<2> {});
produces std::array<size_t, 4> filled with {1, 2, 1, 2}.
Where Sequence::Sequence and Sequence::Make are described here.
I know that placing ... of Rs is incorrect in this context (If sizeof... (Rs) is 1 then it is fine, std::array<size_t, 2> with {1, 2} is returned) but I have no idea where to put it to make expansion which looks like this:
std::get<0> (mode.template get<Rs[0]> ().get ()),
std::get<1> (mode.template get<Rs[0]> ().get ()),
std::get<0> (mode.template get<Rs[1]> ().get ()),
std::get<1> (mode.template get<Rs[1]> ().get ());
Of course Rs[0] I mean first type from parameter pack.
Is it even possible?
Assuming that we're using Xeo's index sequence implementation, we can do something like this:
First create a function for concatenating two arrays. It receives the arrays, plus an index sequence for each one (detail::seq is the index_sequence type)
template<class T, size_t N, size_t M, size_t... I, size_t... J>
std::array<T, N + M> concat(const std::array<T, N>& arr1, const std::array<T, M>& arr2, detail::seq<I...>, detail::seq<J...>)
{
return {arr1[I]..., arr2[J]...};
}
Next, call this function from your get_array function, except we're going to double the seq that we received from the call in main:
template<class MODE, class... T, size_t... I>
auto get_array(MODE m, detail::seq<I...>) ->decltype(concat(m.template get<T>().get()..., detail::seq<I...>{}, detail::seq<I...>{})){
return concat(m.template get<T>().get()..., detail::seq<I...>{}, detail::seq<I...>{});
}
The call in main looks just like it did in your code:
M m;
auto x = get_array<M, S1, S2>(m, detail::gen_seq<2>{});
Where detail::gen_seq is the implementation of make_index_sequence that Xeo had.
Live Demo
Note that I replaced unsigned with size_t in Xeo's index sequence impl.
In C++14 we don't need to implement seq or gen_seq, and we also wouldn't need a trailing -> decltype() after our function.
In C++17 it would be even easier to generalize our concatenation for an arbitrary number of arrays, using fold expressions.
Yes, this can be done, with the standard index_sequence tricks:
template <class T, std::size_t N1, std::size_t N2, std::size_t ... Is, std::size_t ... Js>
std::array<T, N1 + N2> merge_impl(const std::array<T, N1>& a1,
const std::array<T, N2>& a2,
std::index_sequence<Is...>,
std::index_sequence<Js...>) {
return {a1[Is]..., a2[Js]...};
}
template <class T, std::size_t N1, std::size_t N2>
std::array<T, N1 + N2> merge(const std::array<T, N1>& a1, const std::array<T, N2>& a2) {
return merge_impl(a1, a2,
std::make_index_sequence<N1>{},
std::make_index_sequence<N2>{});
}
index_sequence is only in the 14 standard, but can be easily implemented in 11; there are many resources (including on SO) that describe how to do so (edit: it's basically equivalent to your Sequence stuff, may as well get used to the standard names for them). Live example: http://coliru.stacked-crooked.com/a/54dce4a695357359.
To start with, this is basically asking to concatenate an arbitrary number of arrays. Which is very similar to concatenate an arbitrary number of tuples, for which there is a standard library function, even in C++11: std::tuple_cat(). That gets us almost there:
template <class... Ts, class M>
auto get_array(M m) -> decltype(std::tuple_cat(m.template get<Ts>()...)) {
return std::tuple_cat(m.template get<Ts>()...);
}
Note that I flipped the template parameters, so this is just get_array<T1, T2>(m) instead of having to write get_array<M, T1, T2>(m).
Now the question is, how do we write array_cat? We'll just use tuple_cat and convert the resulting tuple to an array. Assume an implementation of index_sequence is available (which is something you'll want in your collection anyway):
template <class T, class... Ts, size_t... Is>
std::array<T, sizeof...(Ts)+1> to_array_impl(std::tuple<T, Ts...>&& tup,
std::index_sequence<Is...> ) {
return {{std::get<Is>(std::move(tup))...}};
}
template <class T, class... Ts>
std::array<T, sizeof...(Ts)+1> to_array(std::tuple<T, Ts...>&& tup) {
return to_array_impl(std::move(tup), std::index_sequence_for<T, Ts...>());
}
template <class... Tuples>
auto array_cat(Tuples&&... tuples) -> decltype(to_array(std::tuple_cat(std::forward<Tuples>(tuples)...))) {
return to_array(std::tuple_cat(std::forward<Tuples>(tuples)...));
}
And that gives you:
template <class... Ts, class M>
auto get_array(M m) -> decltype(array_cat(m.template get<Ts>()...)) {
return array_cat(m.template get<Ts>()...);
}
which handles arbitrarily many types.
So here's for an arbitrary number of same-type arrays. We are basically implementing a highly restrictive version of tuple_cat, made substantially easier because the number of elements in the arrays is the same. I make use of a couple C++14 and 17 library features that are all readily implementable in C++11.
template<class, size_t> struct div_sequence;
template<size_t...Is, size_t Divisor>
struct div_sequence<std::index_sequence<Is...>, Divisor>
{
using quot = std::index_sequence<Is / Divisor...>;
using rem = std::index_sequence<Is % Divisor...>;
};
template<class T, size_t...Ns, size_t...Is, class ToA>
std::array<T, sizeof...(Ns)> array_cat_impl(std::index_sequence<Ns...>,
std::index_sequence<Is...>,
ToA&& t)
{
// NB: get gives you perfect forwarding; [] doesn't.
return {std::get<Is>(std::get<Ns>(std::forward<ToA>(t)))... };
}
template<class Array, class... Arrays,
class VT = typename std::decay_t<Array>::value_type,
size_t S = std::tuple_size<std::decay_t<Array>>::value,
size_t N = S * (1 + sizeof...(Arrays))>
std::array<VT, N> array_cat(Array&& a1, Arrays&&... as)
{
static_assert(std::conjunction_v<std::is_same<std::decay_t<Array>,
std::decay_t<Arrays>>...
>, "Array type mismatch");
using ind_seq = typename div_sequence<std::make_index_sequence<N>, S>::rem;
using arr_seq = typename div_sequence<std::make_index_sequence<N>, S>::quot;
return array_cat_impl<VT>(arr_seq(), ind_seq(),
std::forward_as_tuple(std::forward<Array>(a1),
std::forward<Arrays>(as)...)
);
}
We can also reuse the tuple_cat machinery, as in #Barry's answer. To sidestep potential QoI issues, avoid depending on extensions and also extra moves, we don't want to tuple_cat std::arrays directly. Instead, we transform the array into a tuple of references first.
template<class TupleLike, size_t... Is>
auto as_tuple_ref(TupleLike&& t, std::index_sequence<Is...>)
-> decltype(std::forward_as_tuple(std::get<Is>(std::forward<TupleLike>(t))...))
{
return std::forward_as_tuple(std::get<Is>(std::forward<TupleLike>(t))...);
}
template<class TupleLike,
size_t S = std::tuple_size<std::decay_t<TupleLike>>::value >
auto as_tuple_ref(TupleLike&& t)
-> decltype(as_tuple_ref(std::forward<TupleLike>(t), std::make_index_sequence<S>()))
{
return as_tuple_ref(std::forward<TupleLike>(t), std::make_index_sequence<S>());
}
We can then transform the tuple_cat'd references back into an array:
template <class R1, class...Rs, size_t... Is>
std::array<std::decay_t<R1>, sizeof...(Is)>
to_array(std::tuple<R1, Rs...> t, std::index_sequence<Is...>)
{
return { std::get<Is>(std::move(t))... };
}
template <class R1, class...Rs>
std::array<std::decay_t<R1>, sizeof...(Rs) + 1> to_array(std::tuple<R1, Rs...> t)
{
static_assert(std::conjunction_v<std::is_same<std::decay_t<R1>, std::decay_t<Rs>>...>,
"Array element type mismatch");
return to_array(t, std::make_index_sequence<sizeof...(Rs) + 1>());
}
Finally, array_cat itself is just
template <class... Arrays>
auto array_cat(Arrays&&... arrays)
-> decltype(to_array(std::tuple_cat(as_tuple_ref(std::forward<Arrays>(arrays))...)))
{
return to_array(std::tuple_cat(as_tuple_ref(std::forward<Arrays>(arrays))...));
}
Any decent optimizer should have little difficulty optimizing the intermediate tuples of references away.

c++ variadic template argument iterating

I'm pretty inexperienced in such things, but I'm trying to create a template function that evaluates a n-variable function at "rotated" argument (see example below) and returns a vector of all these values.
For example for n=3 with a function f(x,y,z) the returned triple\vector should be
< f(x,0,0), f(0,x,0), f(0,0,x) >
The naive version of what I need could look like the following (not necessary correct\working)
typedef FunctionSignature Function;
template<class Function, size_t Dimensions>
std::array<Function::Out,Dimensions> F(Function::InComponent x)
{
std::array<Function::Out,Dimensions> Result;
for (i=0; i<Dimensions; i++)
Result[i] = Function::f("rotate((x,0,...,0),i)");
return Result;
}
But how to make the rotate thing.
I also hope that the run-time for could be somehow be eliminated since n is well known in time of compilation.
template<class Function, size_t... Is, size_t... Js>
typename Function::Out call_f(typename Function::InComponent x,
std::index_sequence<Is...>,
std::index_sequence<Js...>) {
return Function::f((void(Is), 0)..., x, (void(Js), 0)...);
}
template<class Function, size_t Dimensions, size_t... Is>
std::array<typename Function::Out, Dimensions> F(typename Function::InComponent x,
std::index_sequence<Is...>)
{
return {{ call_f<Function>(x, std::make_index_sequence<Is>(),
std::make_index_sequence<Dimensions - Is - 1>())... }};
}
template<class Function, size_t Dimensions>
std::array<typename Function::Out,Dimensions> F(typename Function::InComponent x)
{
return F<Function, Dimensions>(x, std::make_index_sequence<Dimensions>());
}
For C++11, search on SO for an implementation of make_index_sequence.
Demo.

Spliting a std::array into a tuple of smaller sized std::array

I'm trying to split a std::array<T, N> into a tuple of smaller arrays, like std::tuple<std::array<T, N1>, std::array<T, N2>, ...> where N1 + N2 + ... = N.
namespace detail {
// Summation of the given values
template <class T>
constexpr T sum(const T& x) { return x; }
template <class T, class ...Args>
constexpr auto sum(const T& x, Args&&... args)
{ return x + sum(std::forward<Args>(args)...); }
}
template <class T, std::size_t... Ns>
constexpr
std::tuple<std::array<T, Ns>...>
f(const std::array<T, detail::sum(Ns...)>& x)
{
// How do I implement this function?
}
int main()
{
constexpr std::array<Foo, 5> arr = { ... };
constexpr auto t = f<Foo, 2,3>(arr);
}
Actually I already implemented f but it's based on a loop which simply creates an empty array and copies the elements of the given array, but it doesn't work if T is not default_constructible.
I tried to utilize std::integer_sequence and std::make_index_sequence, but I think i'm totally lost with no clue.
Can anyone help me implement the function please?
Write
template<class T,size_t...Is,size_t N>
std::array<T,sizeof...(Is)>
extract(std::array<T,N>const&,std::index_sequence<Is...>){
return {{arr[Is]...}};
}
now we just need to turn {1,2,3} into {{0},{1,2},{3,4,5}} roughly, with everything being C++ index sequences (so syntax).
Map {3,4,0} to {0,1,2} -- a count of indexes to subarrays. Then map {3,4,0} x 1 to {3,4,5,6} and similar for the others. That gives us the indexes inside the subarrays, which we feed to extract and bob is your uncle.
template<size_t n, size_t...counts>
constexpr auto
foo( std::index_sequence<counts...> )
-> offset_seq<
sum_n<n>(counts...),
std::make_index_sequence<get_n<n,counts...> >
>{ return {}; }
with various helpers to be written is the {3,4,0} x 1 to {3,4,5,6} portion, for example.

Should std::hash<T> work when T is std::pair<two simpler types also supported by std::hash>?

I was using an ordered set declared as so:
std::set<std::pair<const std::string, const myClass *> > myset;
After doing some analysis of the way I was using the set, I concluded that an unordered_set would be a smarter choice. But when I changed std::set to std::unordered_set, I got a vast spew of error messages from my compiler (g++ 4.8.1) complaining of an
invalid use of incomplete type struct std::hash<std::pair<const std::basic_string<char>, const myClass * > >
I figured out that std::hash didn't know how to deal with a type which was std::pair, despite the fact that the two types that made up the pair were each hashable. I think error for hash function of pair of ints contains relevant information about the C++11 standard that explains why things went awry. (There's no good explanation for the impenetrable wall of error text that g++ emits for this.)
It would seem to me that
std::hash<std::pair<T1, T2>> hasher(make_pair(x,y))
= some_func(std::hash<T1>hasher(x), std::hash<T2>hasher(y) )
where some_func() could be as simple as XOR (or not; see Why is XOR the default way to combine hashes?)
Is there a good reason for the standard to not require std::hash to know how to construct a hash value for an object which is a pair of types that are each hashable?
The reason is simple, it was not added to the standard. The same is true of hashing other structures like tuple.
Things tend to be added to the standard when they are good enough, not when they are perfect, as perfection is the enemy of the good. More specializations of std::hash are not things that will break code (that often), so adding new ones is relatively harmless.
In any case, to that end, we can write our own hash extenders. As an example:
namespace hashers {
constexpr size_t hash_combine( size_t, size_t ); // steal from boost, or write your own
constexpr size_t hash_combine( size_t a ) { return a; }
constexpr size_t hash_combine() { return 0; }
template<class...Sizes>
constexpr size_t hash_combine( size_t a, size_t b, Sizes... sizes ) {
return hash_combine( hash_combine(a,b), sizes... );
}
template<class T=void> struct hash;
template<class A, class B>
constexpr size_t custom_hash( std::pair<A,B> const& p ) {
return hash_combine( hash<size_t>{}(2), hash<std::decay_t<A>>{}(p.first), hash<std::decay_t<B>>{}(p.second) );
}
template<class...Ts, size_t...Is>
constexpr size_t custom_hash( std::index_sequence<Is...>, std::tuple<Ts...> const& p ) {
return hash_combine( hash<size_t>{}(sizeof...(Ts)), hash<std::decay_t<Ts>>{}(std::get<Is>(p))... );
}
template<class...Ts>
constexpr size_t custom_hash( std::tuple<Ts...> const& p ) {
return custom_hash( std::index_sequence_for<Ts...>{}, p );
}
template<class T0, class C>
constexpr size_t custom_hash_container( size_t n, C const& c) {
size_t retval = hash<size_t>{}(n);
for( auto&& x : c)
retval = hash_combine( retval, hash<T>{}(x) );
return retval;
}
template<class T0, class C>
constexpr size_t custom_hash_container( C const& c) {
return custom_hash_container( c.size(), c );
}
template<class T, class...Ts>
size_t custom_hash( std::vector<T, Ts...> const& v ) {
return custom_hash_container<T>(v);
}
template<class T, class...Ts>
size_t custom_hash( std::basic_string<T, Ts...> const& v ) {
return custom_hash_container<T>(v);
}
template<class T, size_t n>
constexpr size_t custom_hash( std::array<T, n> const& v ) {
return custom_hash_container<T>(n, v);
}
template<class T, size_t n>
constexpr size_t custom_hash( T (const& v)[n] ) {
return custom_hash_container<T>(n, v);
}
// etc -- list, deque, map, unordered map, whatever you want to support
namespace details {
template<class T, class=void>
struct hash : std::hash<T> {};
using hashers::custom_hash;
template<class T>
struct hash<T,decltype(void(
custom_hash(declval<T const&>())
)) {
constexpr size_t operator()(T const& t)const {
return custom_hash(t);
}
};
}
template<class T>
struct hash : details::hash<T> {};
template<>
struct hash<void> {
template<class T>
constexpr size_t operator()(T const& t)const { return hash<T>{}(t); }
}
}
and now hashers::hash<T> will recursively use either an ADL-looked up custom_hash function, or std::hash if that fails, to hash T and its components, and hashers::hash<> is a universal hasher that tries to hash anything passed to it.
Code may not compile as shown.
I chose to hash all containers and tuples as hash their length, followed by hashing the combination of their contents. As a side effect, array<int, 3> hashes the same as tuple<int,int,int>, and tuple<int,int> hashes the same as pair<int,int>, and std::vector<char>{'a','b','c', '\0'} hashes the same as "abc", which I think is a nice property. The empty array/tuple/vector/etc hashes like size_t(0).
You can extend the above system for your own types by simply overriding custom_hash in the namespace of the type in question, or specializing either std::hash<X> or hashers::hash<X> to do your custom hash (I would go with std::hash for the principle of least surprise myself). For advanced use, you can specialize hashers::details::hash<X,void> with SFINAE, but I'd say do it for custom_hash instead.

5-dimensional vector declaration

I am looking for an elegant way to declare a 5-dimensional array in C++.
Each nested vectors have known sizes so I started doing :
std::vector<std::vector<std::vector<std::vector<double>>>> myDblVec;
Then assuming I know all dimension sizes :
myDblVec.resize(dim1);
for (int d1 = 0; d1 != dim1; d1++) {
myDblVec[d1].resize[dim2];
for (int d2 = 0; d2 != dim2; d2++) {
myDblVec[d1][d2].resize(dim3)
for (int d3 = 0; d3 != dim3; d3++) {
myDblVec[d1][d2][d3].resize(dim4);
}
}
}
I am looking for a 1-liner or something less 'heavy' to declare this array.
If you get the dimensions run-time, like
myDblVec = std::vector<std::vector<std::vector<std::vector<double>>>>(dim1,
std::vector<std::vector<std::vector<double>>>(dim2,
std::vector<std::vector<double>>(dim3,
std::vector<double>>(dim4, 0.0))));
You could use std::array, assuming the sizes are known at compile time:
std::array<std::array<std::array<std::array<double, dim4>, dim3>, dim2>, dim1> myDblArray;
If you aren't too attached to pre-C++11, you could write a simple variadic template :
template <typename T, size_t... N> struct NestedArray;
template <typename T, size_t N> struct NestedArray<T, N> {
using type = array<T, N>;
};
template <typename T, size_t N, size_t... Rest>
struct NestedArray<T, N, Rest...> {
using type = array<typename NestedArray<T, Rest...>::type, N>;
};
Now, you can define your array as NestedArray<double, dim1, dim2, dim3, dim4>::type.
std::vector<T> takes the size as first constructor argument. You could take advantage of that and use somethign along the lines of
make_vector_t<double, 5> myDblVec(init_vector<double, 5>(dim1, dim2, dim3, dim4, dim5));
That'll rquire a bit of infrastructure to create the types and initialize the elements. Of course, this infrastructure is reasonable straight forward (although I haven't compiled it - currently I'm just using a mobile device, i.e., there is almost certainly a typo somewheter but the overall approach should owrk):
template <typename T, int Dim> struct make_vector;
template <typename T, int Dim>
using make_vector_t = typename make_vector<T, Dim>::type;
template <typename T>
struct make_vector<T, 0> { using type = T; }
template <typename T, int Dim>
struct make_vector { using type = std::vector<make_vector_t<T, Dim-1>>; }
template <typename T, int Dim, typename Arg, typename... Args>
auto init_vector(Arg size, Args... sizes) -> make_vector_t<T, Dim-1> {
return make_vector_t<T, Dim>(size, init_vector<T, Dim-1>(sizes...);
}
If you are in heavy need for multidimensional arrays, consider moving the stuff that has to deal with them into a separate source file that uses C instead of C++. You can interface quite easily between the languages, and allocating a 5D array of dynamical size in C is as simple as
double (*fiveDArray)[dim2][dim3][dim4][dim5] = malloc(dim1 * sizeof(*fiveDArray));
Usage is virtually the same as with your nested std::vector<>s, all you need to remember is the call to free() when you are done with your array. If you need a zero initialized array, replace malloc() by calloc(). As an added bonus, the contiguous nature of a C multidimensional array is nicer on the CPU caches than use of std::vector<>, and indexing is quicker since there is only one pointer to chase.
C++ does not allow this, because this language restricts array dimensions to be compile time constants. C, on the other hand, has lifted this restriction in the C99 standard, and even allows dynamic array sizes in a typedef:
void foo(size_t dim1, size_t dim2, size_t dim3, size_t dim4, size_t dim5) {
typedef double FourDSlice[dim2][dim3][dim4][dim5];
FourDSlice *fiveDArray = malloc(dim1 * sizeof(*fiveDArray));
...
}
is perfectly legal C99, and impossible to do in C++.
well... vector<vector> is it really a good thing? are you thinking to a matrix whose rows can have independent length? If the answer is "no", then consider the idea that all you need is a plain vector, whose size is the product of the 5 dimensions, and whose elements are located at
size_t at(size_t a, size_t b, size_t c, size_t d, size_t e, size_t A, size_t B, size_t C, size_t D, size_t E)
{ return e+d*E+c*D*E+b*C*D*E+a*B*C*D*E; }
(note: capital letters are the sizes).
You can easily generalize this to whatever number of dimensions by using varadics:
template<class... I>
size_t at(size_t r, size_t R, size_t c, size_t C, I... i)
{ return size_at(r*C+c,i...); }
size_t at(size_t c, size_t C)
{ return c; }
And you can also embed all this into a recurring class
template<class T, size_t Rank>
class grid
{
grid<T,Rank-1> m; size_t C;
public:
template<class... I>
grid(size_t r, size_t c, I... i) :m(r*c,i...) :C(c) {}
template<class... I>
T& operator()(size_t r, size_t c, I... i)
{ return m(r*C+c,i...); }
template<class... I>
const T& operator()(size_t r, size_t c, I... i) const
{ return m(r*C+c,i...); }
};
template<class T>
class grid<T,1>
{
std::vector<T> m;
public:
explicit grid(size_t n) :m(n) {}
T& operator()(size_t i) { return m[i]; }
const T& operator()(size_t i) const { return m[i]; }
};
You can just declare a grid<5> a(3,2,4,5,3);
and access its elements as a(x,y,z,w,t); whatever x in 0..2, y in 0..1, z in 0..3 w in 0..4 and t in 0..2;