#ifndef STD_VECTOR_H
#define STD_VECTOR_H

extern "C" {
#include <string.h>
}
#include <algorithm>
#include <utility>
#include <new>
#include <assert.h>

namespace std {
	template<class T> class vector;
};

template<class T>
class std::vector {
public:
	using iterator = T*;
	using const_iterator = const T*;

	constexpr vector() 
		: m_begin(nullptr)
		, m_end(m_begin)
		, m_max(m_begin)
	{}

	constexpr vector(const T* s, size_t len)
	{
		m_begin = __allocate(len);
		m_end	= m_begin + len;
		m_max	= m_end;

		__copy_init_range(s, s + len, m_begin);
	}

	constexpr explicit vector(size_t count, const T& value = {})
	{
		m_begin = __allocate(count);
		m_end	= m_begin + count;
		m_max	= m_end;

		__value_init_range(m_begin, m_end, value);
	}

	constexpr ~vector()
	{
		__destruct_range(m_begin, m_end);
		__deallocate(m_begin);
	}

	constexpr iterator begin() noexcept
	{
		return m_begin;
	}

	constexpr iterator end() noexcept
	{
		return m_end;
	}

	constexpr const_iterator begin() const noexcept
	{
		return m_begin;
	}

	constexpr const_iterator end() const noexcept
	{
		return m_end;
	}

	constexpr const_iterator cbegin() const noexcept
	{
		return m_begin;
	}

	constexpr const_iterator cend() const noexcept
	{
		return m_end;
	}

	constexpr size_t size() const
	{
		return static_cast<size_t>(cend() - cbegin());
	}

	constexpr size_t length() const
	{
		return size();
	}

	constexpr size_t capacity() const
	{
		return static_cast<size_t>(m_max - m_begin);
	}

	constexpr T& operator[](size_t n)
	{
		return m_begin[n];
	}

	constexpr const T& operator[](size_t n) const
	{
		return m_begin[n];
	}

	constexpr void reserve(size_t n = 0)
	{
		if(n > capacity())
		{
			__reallocate(n);
		}
	}

	constexpr T* data()
	{
		return m_begin;
	}

	constexpr const T* data() const
	{
		return m_begin;
	}

	constexpr vector(const vector& v)
	{
		m_begin = __allocate(v.size());
		m_end	= m_begin + v.size();
		m_max	= m_end;

		__copy_init_range(v.cbegin(), v.cend(), m_begin);
	}

	constexpr vector(vector&& v) noexcept : vector()
	{
		swap(v);
	}

	constexpr vector& operator=(const vector& v)
	{
		clear();
		reserve(v.size());

		__copy_init_range(v.cbegin(), v.cend(), m_begin);

		m_end = m_begin + v.size();
		return *this;
	}

	constexpr vector& operator=(vector&& v) noexcept
	{
		swap(v);
		return *this;
	}

	constexpr void swap(vector& v) noexcept
	{
		std::swap(m_max, v.m_max);
		std::swap(m_begin, v.m_begin);
		std::swap(m_end, v.m_end);
	}

	constexpr void push_back(const T& c)
	{
		emplace_back(c);
	}

	constexpr void push_back(T&& c)
	{
		emplace_back(std::move(c));
	}

	template <typename... Args>
	constexpr T& emplace_back(Args&&... args)
	{
		if(m_end == m_max)
		{
			__reallocate((capacity() + 1) * 2);
		}
		new(m_end) T{std::forward<Args>(args)...};
		return *(m_end++);
	}

	constexpr bool empty() const
	{
		return size() == 0;
	}

	constexpr void pop_back()
	{
		back().~T();
		--m_end;
	}

	constexpr const T& front() const
	{
		return *m_begin;
	}

	constexpr T& front()
	{
		return *m_begin;
	}

	constexpr const T& back() const
	{
		assert(!empty());
		return *(m_end - 1);
	}

	constexpr T& back()
	{
		assert(!empty());
		return *(m_end - 1);
	}

	constexpr void clear()
	{
		__destruct_range(m_begin, m_end);
		m_end = m_begin;
	}

	constexpr iterator erase(const_iterator pos)
	{
		auto index = (pos - cbegin());

		std::move(begin() + index + 1, end(), begin() + index);
		pop_back();
		return begin() + index;
	}

	constexpr iterator insert(const_iterator pos, const T& value)
	{
		return emplace(pos, value);
	}

	constexpr iterator insert(const_iterator pos, T&& value)
	{
		return emplace(pos, std::move(value));
	}

	template<class... Args>
	constexpr iterator emplace(const_iterator pos, Args&&... args)
	{
		auto index = pos - begin();

		if(m_end == m_max)
		{
			__reallocate(size() + 1);
		}

		auto it = begin() + index;

		if(it == end())
		{
			new(m_end) T{std::forward<Args>(args)...};
			return m_end++;
		}
		else
		{
			new(m_end) T{std::move(back())};
			std::move_backward(it, end() - 1, end());
			++m_end;
			*it = T{std::forward<Args>(args)...};
		}

		return it;
	}

private:

	constexpr void __reallocate(size_t n)
	{
		T* new_ptr = __allocate(n);

		__move_init_range(m_begin, m_end, new_ptr);
		__destruct_range(m_begin, m_end);
		__deallocate(m_begin);

		auto sz = size();

		m_begin = new_ptr;
		m_end	= m_begin + sz;
		m_max	= m_begin + n;
	}

	constexpr static T* __allocate(size_t size)
	{
		return (T*)::operator new(sizeof(T) * size,
								  std::align_val_t{alignof(T)});
	}

	constexpr static void __deallocate(T* ptr)
	{
		::operator delete(ptr);
	}

	constexpr static void __destruct_range(iterator first, iterator last)
	{
		while(first != last)
			(first++)->~T();
	}

	constexpr static void __move_init_range(iterator first, iterator last,
											iterator d_first)
	{
		while(first != last)
			new(d_first++) T{std::move(*first++)};
	}

	constexpr static void __copy_init_range(const_iterator first,
											const_iterator last,
											iterator d_first)
	{
		while(first != last)
			new(d_first++) T{*first++};
	}

	constexpr static void __value_init_range(iterator first, iterator last,
											 const T& val)
	{
		while(first != last)
			new(first++) T{val};
	}

	T* m_begin;
	T* m_end;
	T* m_max;
};
#endif