#ifndef ROW_HPP
#define ROW_HPP

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

//#include "sorting_algorithms.hpp"

template<typename T>

class row {

private:
	T* p;
	uint32_t n;
public:
	row():p(nullptr), n(0){}

	row(const T* src, uint32_t size):p(nullptr), n(size){
		if(n==0){
		p=nullptr;
		return;
		}
		p = new T[n];
		for (uint32_t i=0; i<n; i++)
			p[i]=src[i];
	}

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


	~row() {
		delete[] p;
		p = nullptr;
		n = 0;
	}

	//ASSIGNMENT OPERATOR (DEEP COPY)

	row& operator=(const row& other) {
		if (this == &other) return *this;

		delete[] p;

		n = other.n;
		if (n==0) {
			p = nullptr;
		} else {
			p = new T[n];
			for (uint32_t i = 0; i<n; i++)
				p[i] = other.p[i];
		}

		return *this;
	}

	// ======== ACCESSORS ============

	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]; }

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


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

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

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

#endif
