#ifndef ROW_HPP
#define ROW_HPP

#include <cstdint>
#include <stdexcept>
#include <iostream>

template<typename T>
class row {
private:
	T*       p; // pointer to a dynamically 
	uint32_t n; // number of elements
public:
	// constructor
	row(T* src, uint32_t size)
		: p(new T[size]), n(size)
	{
		for (uint32_t i=0; i<n; ++i)
			p[i] = src[i];
	}

	// destructor
	~row()
	{
		delete[] p;
	}

	// copy constructor
	row(const row& other)
		: p(new T[other.n]), n(other.n)
	{
		for (uint32_t i=0;i<n; ++i)
			p[i] = other.p[i];
	}

	// assignment operator (deep copy)
	// row<int> x(b, 10);
	// row<int> y(c, 10);
	// x = y;  // calls the assignment operator
	row& 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;
	}

	// size()
	uint32_t size() const { return n; }
	
	// operator []
	T& operator[](uint32_t idx) 
	{return p[idx];}

	const T& operator[](uint32_t idx) const 
	{return p[idx];}

	// operator at
	T& at(uint32_t idx)
	{
		if (idx >= n)
			throw std::out_of_range("*row:at - index out of range");
		return p[idx];
	}

	const T& at(uint32_t idx) const
	{
		if (idx >= n)
			throw std::out_of_range("*row:at - index out of range");
		return p[idx];
	}

	// std::ostream operator <<
	friend std::ostream& operator<<(std::ostream& os, const row<T>& r)
	{
		os << "[ ";
		for (uint32_t i=0; i<r.n; ++i)
		{
			os << r.p[i];
			if (i + 1 < r.n) os << " ";
		}
		os << " ]";
		return os;
	}

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

#endif






