#include "row.hpp"

/* =========================
   CONSTRUCTORS
   ========================= */

template<typename T>
row<T>::row(T* data, uint32_t size) : n(size)
{
    p = new T[n];
    for (uint32_t i = 0; i < n; ++i)
        p[i] = data[i];
}

template<typename T>
row<T>::row(const row& other) : n(other.n)
{
    p = new T[n];
    for (uint32_t i = 0; i < n; ++i)
        p[i] = other.p[i];
}

template<typename T>
row<T>& row<T>::operator=(const row& other)
{
    if (this == &other)
        return *this;

    delete[] p;
    n = other.n;

    p = new T[n];
    for (uint32_t i = 0; i < n; ++i)
        p[i] = other.p[i];

    return *this;
}

template<typename T>
row<T>::~row()
{
    delete[] p;
}

/* =========================
   MEMBER FUNCTIONS
   ========================= */

template<typename T>
uint32_t row<T>::size() const
{
    return n;
}

template<typename T>
T& row<T>::operator[](uint32_t index)
{
    if (index >= n) {
        std::cerr << "Index out of bounds\n";
        std::abort();
    }
    return p[index];
}

template<typename T>
const T& row<T>::operator[](uint32_t index) const
{
    if (index >= n) {
        std::cerr << "Index out of bounds\n";
        std::abort();
    }
    return p[index];
}

template<typename T>
T& row<T>::at(uint32_t index)
{
    if (index >= n) {
        std::cerr << "Index out of bounds in at()\n";
        std::abort();
    }
    return p[index];
}

template<typename T>
const T& row<T>::at(uint32_t index) const
{
    if (index >= n) {
        std::cerr << "Index out of bounds in at()\n";
        std::abort();
    }
    return p[index];
}

/* =========================
   STREAM OPERATORS
   ========================= */

template<typename T>
std::ostream& operator<<(std::ostream& os, const row<T>& obj)
{
    for (uint32_t i = 0; i < obj.n; ++i)
        os << obj.p[i] << " ";
    return os;
}

template<typename T>
std::istream& operator>>(std::istream& is, row<T>& obj)
{
    for (uint32_t i = 0; i < obj.n; ++i)
        is >> obj.p[i];
    return is;
}


/* =========================
   EXPLICIT INSTANTIATION Needed for templates because are instantiated in runtime
   ========================= */

// Add the types you want to support
//g++ main.cpp row.cpp -o main
template class row<int>;
template class row<long int>;
template class row<double>;
template class row<float>;
template class row<uint8_t>;

template std::ostream& operator<<(std::ostream&, const row<int>&);
template std::ostream& operator<<(std::ostream&, const row<long int>&);
template std::ostream& operator<<(std::ostream&, const row<float>&);
template std::ostream& operator<<(std::ostream&, const row<double>&);
template std::ostream& operator<<(std::ostream&, const row<uint8_t>&);

template std::istream& operator>>(std::istream&, row<int>&);
template std::istream& operator>>(std::istream&, row<long int>&);
template std::istream& operator>>(std::istream&, row<float>&);
template std::istream& operator>>(std::istream&, row<double>&);
template std::istream& operator>>(std::istream&, row<uint8_t>&);
