#ifndef ARRAY_H
#define ARRAY_H
#include <iostream>
#include <algorithm>
#include <stdexcept>
#include <string.h>
#include <vector>
namespace neurophys {
class ArrayElementOutOfBoundsException: public std::exception {};
class ArraySizeMismatchException: public std::exception {};
// the difference between class member operators and external friends might be
// unnoetig here!
template <class T>
class Array {
size_t size_;
T* data_;
public:
explicit Array(const size_t size=0);
Array(const Array<T>& other);
Array(const std::vector<T>& vec);
#ifdef MOVE_SEMANTICS
Array(Array<T>&& other);
#endif
virtual ~Array();
Array<T>& operator=(Array<T> other);
const size_t size() const { return size_; }
const T* constdata() const { return data_; } // das geht bestimmt eleganter, oder?
T* data() { return data_; }
T& operator[](const int idx);
const T& operator[](const int idx) const;
Array<T>& operator*=(const T val);
Array<T>& operator*=(const Array<T>& other);
Array<T>& operator/=(const T val);
Array<T>& operator/=(const Array<T>& other);
Array<T>& operator+=(const T val);
Array<T>& operator+=(const Array<T>& other);
Array<T>& operator-=(const T val);
Array<T>& operator-=(const Array<T>& other);
Array<T> operator-() const;
template <class X>
friend void swap(Array<X>& first, Array<X>& second);
template <class X>
friend Array<X> operator*(const Array<X>& first,
const Array<X>& second);
template <class X, class Y>
friend Array<X> operator*(const Array<X>& array, const Y value);
template <class X, class Y>
friend Array<X> operator*(const Y value, const Array<X>& array);
template <class X, class Y>
friend Array<X> operator/(const Array<X>& array, const Y value);
template <class X, class Y>
friend Array<X> operator/(const Y value, const Array<X>& array);
template <class X, class Y>
friend Array<X> operator+(const Array<X>& array, const Y value);
template <class X, class Y>
friend Array<X> operator+(const Y value, const Array<X>& array);
template <class X, class Y>
friend Array<X> operator-(const Array<X>& array, const Y value);
template <class X, class Y>
friend Array<X> operator-(const Y value, const Array<X>& array);
template <class X>
friend Array<X> operator/(const Array<X>& first,
const Array<X>& second);
template <class X>
friend Array<X> operator+(const Array<X>& first,
const Array<X>& second);
template <class X>
friend Array<X> operator-(const Array<X>& first,
const Array<X>& second);
template <class X>
friend const bool operator==(const Array<X>& first,
const Array<X>& second);
};
template <class T>
Array<T>::Array(const size_t size):
size_(size), data_(size_ ? new T[size_] : 0)
{
}
// is it a good idea to have this and allow for implicit construction?
template <class T>
Array<T>::Array(const std::vector<T>& vec):
size_(vec.size()), data_(vec.size() ? new T[size_] : 0)
{
std::copy(vec.data(), vec.data() + vec.size(), data_);
}
template <class T>
Array<T>::Array(const Array<T>& other):
size_(other.size_), data_(size_ ? new T[size_] : 0)
{
//std::copy(other.data_, other.data_ + size_, data_);
for (int i = 0; i < size_; i++)
{
data_[i] = other.data_[i];
}
}
#ifdef MOVE_SEMANTICS
template <class T>
Array<T>::Array(Array<T>&& other):
size_(other.size_), data_(other.data_)
{
other.data_ = 0;
}
#endif
template <class T>
Array<T>::~Array()
{
delete[] data_;
}
template <class T>
Array<T>& Array<T>::operator=(Array other)
{
swap(*this, other);
return *this;
}
template <class T>
void swap(Array<T>& first, Array<T>& second)
{
using std::swap;
swap(first.size_, second.size_);
swap(first.data_, second.data_);
}
template <class T>
T& Array<T>::operator[](const int idx)
{
#ifndef NDEBUG
if ((idx < 0) || (idx >= size_))
throw ArrayElementOutOfBoundsException();
#endif // NDEBUG
return data_[idx];
}
template <class T>
const T& Array<T>::operator[](const int idx) const
{
return data_[idx];
}
template <class X, class Y>
Array<X> operator*(const Array<X>& array, const Y val)
{
Array<X> result(array.size_);
for (int i = 0; i < array.size_; i++)
{
result.data_[i] = array.data_[i] * val;
}
return result;
}
template <class X, class Y>
Array<X> operator*(const Y val, const Array<X>& array)
{
return array * val;
}
template <class X, class Y>
Array<X> operator/(const Array<X>& array, const Y val)
{
return array * (1./val);
}
template <class X, class Y>
Array<X> operator/(const Y val, const Array<X>& array)
{
Array<X> result(array.size_);
for (int i = 0; i < array.size_; i++)
{
result.data_[i] = val / array.data_[i];
}
return result;
}
template <class X, class Y>
Array<X> operator+(const Array<X>& array, const Y val)
{
Array<X> result(array.size_);
for (int i = 0; i < array.size_; i++)
{
result.data_[i] = array.data_[i] + val;
}
return result;
}
template <class X, class Y>
Array<X> operator+(const Y val, const Array<X>& array)
{
return array + val;
}
template <class X, class Y>
Array<X> operator-(const Array<X>& array, const Y val)
{
return array + (-val);
}
template <class X, class Y>
Array<X> operator-(const Y val, const Array<X>& array)
{
Array<X> result(array.size_);
for (int i = 0; i < array.size_; i++)
{
result.data_[i] = val - array.data_[i];
}
return result;
}
template <class T>
Array<T> Array<T>::operator-() const
{
return(*this * (-1));
}
template <class T>
Array<T>& Array<T>::operator*=(const T val)
{
for (int i = 0; i < size_; i++)
{
data_[i] = data_[i] * val;
}
return *this;
}
template <class T>
Array<T>& Array<T>::operator*=(const Array<T>& other)
{
if (size_ != other.size_)
throw ArraySizeMismatchException();
for (int i = 0; i < size_; i++)
{
data_[i] = data_[i] * other.data_[i];
}
return *this;
}
template <class T>
Array<T>& Array<T>::operator+=(const T val)
{
for (int i = 0; i < size_; i++)
{
data_[i] = data_[i] + val;
}
return *this;
}
template <class T>
Array<T>& Array<T>::operator+=(const Array<T>& other)
{
if (size_ != other.size_)
throw ArraySizeMismatchException();
for (int i = 0; i < size_; i++)
{
data_[i] = data_[i] + other.data_[i];
}
return *this;
}
template <class T>
Array<T>& Array<T>::operator/=(const T val)
{
return operator*=(1./val);
}
template <class T>
Array<T>& Array<T>::operator/=(const Array<T>& other)
{
if (size_ != other.size_)
throw ArraySizeMismatchException();
for (int i = 0; i < size_; i++)
{
data_[i] = data_[i] / other.data_[i];
}
return *this;
}
template <class T>
Array<T>& Array<T>::operator-=(const T val)
{
return operator+=(-val);
}
template <class T>
Array<T>& Array<T>::operator-=(const Array<T>& other)
{
if (size_ != other.size_)
throw ArraySizeMismatchException();
for (int i = 0; i < size_; i++)
{
data_[i] = data_[i] - other.data_[i];
}
return *this;
}
template <class T>
Array<T> operator*(const Array<T>& first, const Array<T>& second)
{
if (first.size_ != second.size_)
throw ArraySizeMismatchException();
Array<T> result(first.size_);
for (int i = 0; i < first.size_; i++)
{
result.data_[i] = first.data_[i] * second.data_[i];
}
return result;
}
template <class T>
Array<T> operator/(const Array<T>& first, const Array<T>& second)
{
if (first.size_ != second.size_)
throw ArraySizeMismatchException();
Array<T> result(first.size_);
for (int i = 0; i < first.size_; i++)
{
result.data_[i] = first.data_[i] / second.data_[i];
}
return result;
}
template <class T>
Array<T> operator+(const Array<T>& first, const Array<T>& second)
{
if (first.size_ != second.size_)
throw ArraySizeMismatchException();
Array<T> result(first.size_);
for (int i = 0; i < first.size_; i++)
{
result.data_[i] = first.data_[i] + second.data_[i];
}
return result;
}
template <class T>
Array<T> operator-(const Array<T>& first, const Array<T>& second)
{
if (first.size_ != second.size_)
throw ArraySizeMismatchException();
Array<T> result(first.size_);
for (int i = 0; i < first.size_; i++)
{
result.data_[i] = first.data_[i] - second.data_[i];
}
return result;
}
template <class T>
const bool operator==(const Array<T>& first, const Array<T>& second)
{
if (first.size_ != second.size_)
throw ArraySizeMismatchException();
for (int i = 0; i < first.size_; i++)
{
if (first.data_[i] != second.data_[i])
return false;
}
return true;
}
}
#endif /* ARRAY_H */