#include <array>
#include <cstddef>
#include <iostream>
#include <iterator>
enum class ArithmeticOperator {
addition,
subtraction,
multiplication,
division
};
namespace _arithmeticoperationresult
{
template<ArithmeticOperator T_operator, typename T1, typename T2>
struct BinaryResult;
template<typename T1, typename T2>
struct BinaryResult<ArithmeticOperator::addition, T1, T2>
{ using type = decltype(std::declval<T1>() + std::declval<T2>()); };
template<typename T1, typename T2>
struct BinaryResult<ArithmeticOperator::subtraction, T1, T2>
{ using type = decltype(std::declval<T1>() - std::declval<T2>()); };
template<typename T1, typename T2>
struct BinaryResult<ArithmeticOperator::multiplication, T1, T2>
{ using type = decltype(std::declval<T1>() * std::declval<T2>()); };
template<typename T1, typename T2>
struct BinaryResult<ArithmeticOperator::division, T1, T2>
{ using type = decltype(std::declval<T1>() / std::declval<T2>()); };
}
template<
ArithmeticOperator T_operator,
typename T_First,
typename ... T_Others
>
struct ArithmeticOperationResult;
template<
ArithmeticOperator T_operator,
typename T_First,
typename T_Second,
typename ... T_Others
>
struct ArithmeticOperationResult<
T_operator,
T_First,
T_Second,
T_Others ...
>
{
using type = typename ArithmeticOperationResult<
T_operator,
typename _arithmeticoperationresult::BinaryResult<
T_operator,
T_First,
T_Second
>::type,
T_Others ...
>::type;
};
template<
ArithmeticOperator T_operator,
typename T_Unique
>
struct ArithmeticOperationResult<T_operator, T_Unique>
{
using type = T_Unique;
};
template<typename ... T_Values>
using MultiplicationResult =
ArithmeticOperationResult<ArithmeticOperator::multiplication, T_Values ...>;
template<typename ... T_Values>
using MultiplicationResultT =
typename MultiplicationResult<T_Values ...>::type;
template<typename T_Unique>
constexpr T_Unique
product(T_Unique unique) noexcept
{
return unique;
}
template<typename T_First, typename T_Second, typename ... T_Others>
constexpr MultiplicationResultT<T_First, T_Second, T_Others ...>
product(T_First first, T_Second second, T_Others ... others) noexcept
{
return product(first * second, others ...);
}
enum class StorageOrder {
rowMajor,
columnMajor
};
template<StorageOrder> struct StorageOrderTag {};
using RowMajorStorageOrderTag = StorageOrderTag<StorageOrder::rowMajor>;
using ColumnMajorStorageOrderTag = StorageOrderTag<StorageOrder::columnMajor>;
// - Converts a list of indices for a specific shape array into a 1-D index.
template<typename T_Shape>
std::size_t storageIndex(const T_Shape &indices, const T_Shape &shape)
{
std::size_t i = 0;
std::size_t out = indices[i];
while (i++ < indices.size() - 1) {
out = indices[i] + shape[i] * out;
}
return out;
}
//- Element-wise iterator.
template<
typename T,
typename T_Data,
StorageOrder T_storageOrder,
std::size_t T_dimensionality
>
class ElementWiseIterator
: public std::iterator<std::bidirectional_iterator_tag, T>
{
private:
using Shape = std::array<std::size_t, T_dimensionality>;
public:
T & operator*() const
{ return *_currentElement; }
ElementWiseIterator & operator++()
{
std::size_t i = _shape.size();
if ( T_storageOrder == StorageOrder::columnMajor ) {
std::swap(_shape[T_dimensionality-1], _shape[T_dimensionality-2]);
std::swap(_currentIndices[T_dimensionality-1], _currentIndices[T_dimensionality-2]);
}
// For each dimension
while (i-- > 0) {
// Find the lowest index one can increase
// Indeces are stored from top to bottom
if (_currentIndices[i] < _shape[i] - 1) {
++_currentIndices[i];
break;
}
}
bool endOfContainer = ( i == -1 );
// Then we reset all the others, since they could not be increased.
for (++i; i < _currentIndices.size(); ++i) {
_currentIndices[i] = 0;
}
if ( T_storageOrder == StorageOrder::columnMajor ) {
std::swap(_shape[T_dimensionality-1], _shape[T_dimensionality-2]);
std::swap(_currentIndices[T_dimensionality-1], _currentIndices[T_dimensionality-2]);
}
if ( endOfContainer )
_currentIndices[0] = _shape[0];
setCurrentElement();
return *this;
}
friend bool operator==(const ElementWiseIterator &iterator1, const ElementWiseIterator &iterator2)
{ return iterator1._currentElement == iterator2._currentElement; }
friend bool operator!=(const ElementWiseIterator &iterator1, const ElementWiseIterator &iterator2)
{ return !(iterator1 == iterator2); }
private:
ElementWiseIterator(T_Data *data, const Shape &indices, const Shape &shape)
: _currentElement(nullptr),
_data(data),
_currentIndices(indices),
_shape(shape)
{
setCurrentElement();
}
void setCurrentElement()
{
std::size_t index = storageIndex(
_currentIndices,
_shape
);
_currentElement = &(*_data)[index];
}
T *_currentElement;
T_Data *_data;
Shape _currentIndices;
Shape _shape;
template<typename, StorageOrder, std::size_t ...> friend class Array;
};
//- Array class.
template<typename T, StorageOrder T_storageOrder, std::size_t ... T_dimensions>
class Array
{
public:
static constexpr std::size_t size()
{ return product(T_dimensions ...); }
using Shape = std::array<std::size_t, sizeof ... (T_dimensions)>;
static constexpr Shape shape()
{ return {T_dimensions ...}; }
protected:
using Storage = std::array<T, size()>;
public:
using Iterator = typename Storage::iterator;
using EWiseIterator = ElementWiseIterator<
T,
Storage,
T_storageOrder,
sizeof ... (T_dimensions)
>;
Iterator begin()
{ return _data.begin(); }
Iterator end()
{ return _data.end(); }
EWiseIterator elementWiseBegin()
{ // Passes all zeros, N dimension, starting index.
return EWiseIterator(&_data, {0}, shape());
}
EWiseIterator elementWiseEnd()
{
// Set the current iterator indices to the first out of range element.
// Ie: for an a 2x3 array, that would be {2, 0}.
Shape shape = this->shape();
//if ( T_storageOrder == StorageOrder::columnMajor )
// std::cout << "Making end: ";
return EWiseIterator(&_data, {shape[0]}, shape);
}
private:
Storage _data;
};
int main(int argc, char **argv)
{
const int width = 3, height = 2;
int i;
Array<int, StorageOrder::rowMajor, height, width> rowArray;
i = 0;
for (auto it = rowArray.elementWiseBegin(); it != rowArray.elementWiseEnd(); ++it) {
*it = i++;
}
Array<int, StorageOrder::columnMajor, height, width> columnArray;
i = 0;
for (auto it = columnArray.elementWiseBegin(); it != columnArray.elementWiseEnd(); ++it) {
*it = i++;
}
// Show internal storage order for row
for (auto &value : rowArray) {
std::cout << value << " ";
}
std::cout << std::endl;
// Show internal storage order for column
for (auto &value : columnArray) {
std::cout << value << " ";
}
std::cout << std::endl;
auto cit = columnArray.elementWiseBegin();
auto rit = rowArray.elementWiseBegin();
// Compare elements
for (int i = 0; i < height * width; ++i) {
std::cout << *cit << " == " << *rit << "\n";
++cit; ++rit;
}
std::cout << std::endl;
return 0;
}