#ifndef STD_STRING_H
#define STD_STRING_H

#include <string.h>
#include <algorithm>
#include <type_traits>
#include <string_view>
#include <assert.h>
#include <char_traits.h>
#include <stddef.h>
#include <stdint.h>

namespace std {
template<typename C> class basic_string;

using string = basic_string<char>;
};

template<typename CharT>
class std::basic_string
{
public:
	using value_type = CharT;
	using size_type = size_t;
	using iterator = value_type*;
	using const_iterator = const value_type*;
	using traits_type = char_traits<value_type>;
	static const size_type npos = static_cast<size_type>(-1);

	constexpr basic_string() noexcept
	{
		__set_short_size(0);
		m_rep.s.data[0] = value_type();
		assert(!__is_long());
	}

	constexpr basic_string(const value_type* s) 
		: basic_string(s, traits_type::length(s))
	{}

	/*constexpr basic_string(const basic_string& other, size_type pos)
		: basic_string(other.data() + pos, other.size())
	{}

	constexpr basic_string(const basic_string& other, size_type pos,
						   size_type count)
		: basic_string(other.data() + pos, count)
	{}*/

	constexpr basic_string(const value_type* s, size_t len)
	{
		if(len > __min_cap)
		{
			const auto new_cap = len + 1;
			m_rep.l.size = len;
			m_rep.l.data = new value_type[new_cap];
			__set_long_cap(new_cap);
			memcpy(m_rep.l.data, s, len * sizeof(value_type));
			m_rep.l.data[len] = value_type();
			assert(__is_long());
		}
		else
		{
			__set_short_size(len);
			memcpy(&m_rep.s.data[0], s, len * sizeof(value_type));
			m_rep.s.data[len] = value_type();
			assert(!__is_long());
		}
	}

	constexpr basic_string(const basic_string& str) 
		: basic_string(str.data(), str.size())
	{}

	~basic_string()
	{
		if(__is_long())
			delete[] m_rep.l.data;
	}

	constexpr basic_string(basic_string&& str) noexcept
	{
		if(str.__is_long())
		{
			m_rep.l = str.m_rep.l;
			str.__set_short_size(0);
			str.m_rep.s.data[0] = value_type();
			assert(__is_long());
			assert(!str.__is_long());
		}
		else
		{
			m_rep.s = str.m_rep.s;
			assert(!__is_long());
		}
	}

	constexpr basic_string& assign(const value_type* s)
	{
		const auto new_size = traits_type::length(s);
		reserve(new_size);
		__copy_from_terminated_string(s, new_size);

		return *this;
	}

	constexpr basic_string& assign(const value_type* s, size_t n)
	{
		reserve(n);
		__copy_from_unterminated_string(s, n);

		return *this;
	}

	constexpr basic_string& assign(const basic_string& str)
	{
		const auto new_size = str.size();
		reserve(new_size);
		__copy_from_terminated_string(str.data(), new_size);

		return *this;
	}

	using __self_view = std::basic_string_view<CharT>;

	template <class Tp>
	struct __is_sv_convertible : public integral_constant<bool,
		is_convertible_v<const Tp&, __self_view> &&
		!is_convertible_v<const Tp&, const CharT*>>
	{};

	struct __sv_wrapper
	{
		explicit __sv_wrapper(__self_view __ssv) noexcept : _sv(__ssv) { }
		__self_view _sv;
	};

	constexpr explicit basic_string(__sv_wrapper __sv)
		: basic_string(__sv._sv.data(), __sv._sv.size())
	{}

	template <class T, class = typename enable_if<__is_sv_convertible<T>::value>::type>
	constexpr  explicit basic_string(const T& t) :
		basic_string(__sv_wrapper(t))
	{}

	template <class T, class = typename enable_if<__is_sv_convertible<T>::value>::type>
	constexpr basic_string& assign(const T& t)
	{
		__self_view sv = t;
		return assign(sv.data(), sv.size());
	}

	template <class T, class = typename enable_if<__is_sv_convertible<T>::value>::type>
	constexpr basic_string& operator=(const T& t)
	{
		__self_view sv = t;
		return assign(sv);
	}

	constexpr operator basic_string_view<CharT>() const noexcept
	{
		return basic_string_view<CharT>(data(), size());
	}

	constexpr basic_string& operator=(const value_type* str)
	{
		return assign(str);
	}

	constexpr basic_string& operator=(const basic_string& str)
	{
		return assign(str);
	}

	constexpr basic_string& operator=(basic_string&& str) noexcept
	{
		if(!str.__is_long())
		{
			__copy_from_terminated_string(str.data(), str.size());
		}
		else if(__is_long())
		{
			std::swap(m_rep.l, str.m_rep.l);
		}
		else if(&str != this)
		{
			m_rep.l = str.m_rep.l;
			str.__set_short_size(0);
			str.m_rep.s.data[0] = value_type();
			assert(__is_long());
		}
		return *this;
	}

	constexpr iterator begin() noexcept
	{
		if(__is_long())
			return m_rep.l.data;
		else
			return &m_rep.s.data[0];
	}

	constexpr iterator end() noexcept
	{
		if(__is_long())
			return __long_end();
		else
			return __short_end();
	}

	constexpr const_iterator begin() const noexcept
	{
		if(__is_long())
			return m_rep.l.data;
		else
			return &m_rep.s.data[0];
	}

	constexpr const_iterator end() const noexcept
	{
		if(__is_long())
			return __long_end();
		else
			return __short_end();
	}

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

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

	constexpr size_type size() const noexcept
	{
		if(__is_long())
			return m_rep.l.size;
		else
			return __get_short_size();
	}

	constexpr size_type length() const noexcept
	{
		return size();
	}

	constexpr size_type capacity() const noexcept
	{
		if(__is_long())
			return __get_long_cap() - 1;
		else
			return __min_cap;
	}

	constexpr value_type& operator[](size_type n)
	{
		return begin()[n];
	}

	constexpr const value_type& operator[](size_type n) const
	{
		return cbegin()[n];
	}

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

	constexpr const value_type* data() const noexcept
	{
		return cbegin();
	}

	constexpr const value_type* c_str() const noexcept
	{
		return cbegin();
	}

	constexpr void swap(basic_string& s) noexcept
	{
		std::swap(m_rep, s.m_rep);
	}

	constexpr void push_back(value_type c)
	{
		if(size() != capacity()) [[likely]]
		{
			if(__is_long())
			{
				auto end   = __long_end();
				*end	   = c;
				*(end + 1) = value_type();
				++m_rep.l.size;
			}
			else
			{
				//we must decrement inv_sz before we null terminate
				//because the termination could overwrite inv_sz
				//which is fine if its 0
				auto end   = __short_end();
				--m_rep.s.inv_sz;

				*end	   = c;
				*(end + 1) = value_type();
			}
		}
		else
		{
			__push_back_and_reallocate(c);
		}
	}

	constexpr basic_string& append(const value_type* str, size_type count)
	{
		const auto new_size = size() + count;
		if(new_size <= capacity())
		{
			if(__is_long()) [[likely]]
			{
				memcpy(__long_end(), str, count * sizeof(value_type));
				m_rep.l.size		   = new_size;
				m_rep.l.data[new_size] = value_type();
				assert(__is_long());
			}
			else
			{
				memcpy(__short_end(), str, count * sizeof(value_type));
				__set_short_size(new_size);
				m_rep.s.data[new_size] = value_type();
				assert(!__is_long());
			}
		}
		else
		{
			__append_and_reallocate(str, count);
		}
		return *this;
	}

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

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

	constexpr basic_string& insert(size_t index, const basic_string& s)
	{
		return insert(index, s.c_str(), s.size());
	}

	constexpr basic_string& insert(size_t index, const CharT* s,
								   size_type count)
	{
		const auto new_size = size() + count;
		if(new_size <= capacity()) [[likely]]
		{
			std::copy_backward(begin() + index, end(), end() + count);
			memcpy(begin() + index, s, count);

			if(__is_long())
			{
				m_rep.l.size = new_size;
				assert(__is_long());
			}
			else
			{
				__set_short_size(new_size);
				assert(!__is_long());
			}
		}
		else
		{
			__insert_and_reallocate(index, s, size(), count);
		}

		return *this;
	}

	constexpr iterator insert(const_iterator pos, CharT ch)
	{
		insert(static_cast<size_t>(pos - begin()), &ch, 1);
		return begin() + (pos - cbegin());
	}

	constexpr basic_string& append(const value_type* str)
	{
		return append(str, traits_type::length(str));
	}

	constexpr basic_string& append(const basic_string& str)
	{
		return append(str.data(), str.size());
	}

	template<class StringViewLike>
	constexpr basic_string& append(const StringViewLike& str) 
		requires(__is_sv_convertible<StringViewLike>::value)
	{
		__self_view sv = str;
		return append(sv.data(), sv.size());
	}

	constexpr basic_string& operator+=(const value_type* str)
	{
		return append(str);
	}

	constexpr basic_string& operator+=(const basic_string& str)
	{
		return append(str.data(), str.size());
	}

	constexpr basic_string& operator+=(value_type c)
	{
		push_back(c);
		return *this;
	}

	template <class StringViewLike>
	constexpr basic_string& operator+=(const StringViewLike& str) 
		requires(__is_sv_convertible<StringViewLike>::value)
	{
		__self_view sv = str;
		return append(sv.data(), sv.size());
	}

	constexpr int compare(const value_type* s) const noexcept
	{
		return strcmp(c_str(), s);
	}

	constexpr int compare(const basic_string& s) const noexcept
	{
		size_type lhs_sz = size();
		size_type rhs_sz = s.size();
		int result = traits_type::compare(data(), s.data(), min(lhs_sz, rhs_sz));
		if(result != 0)
			return result;
		if(lhs_sz < rhs_sz)
			return -1;
		if(lhs_sz > rhs_sz)
			return 1;
		return 0;
	}

	[[nodiscard]] constexpr bool empty() const noexcept
	{
		return size() == 0;
	}

	constexpr void pop_back()
	{
		if(__is_long())
		{
			--m_rep.l.size;
		}
		else
		{
			m_rep.s.inv_sz++;
		}
		*end() = value_type();
	}

	constexpr bool starts_with(value_type c) const noexcept
	{
		return !empty() && front() == c;
	}

	constexpr bool ends_with(value_type c) const noexcept
	{
		return !empty() && back() == c;
	}

	constexpr basic_string substr(size_t pos = 0, size_t count = npos) const
	{
		return basic_string{ cbegin() + pos,
							 std::min(size() - pos, count) };
	}

	template<class StringViewLike>
	constexpr size_t find_first_of(const StringViewLike& str,
								   size_t pos = 0) const
		requires(__is_sv_convertible<StringViewLike>::value)
	{
		return __find_first_of(str.cbegin(), str.cend(), pos);
	}

	constexpr size_t find_first_of(const basic_string& str,
								   size_t pos = 0) const
	{
		return __find_first_of(str.cbegin(), str.cend(), pos);
	}

	constexpr size_t find(value_type c, size_t pos = 0) const noexcept
	{
		for(auto it = cbegin() + pos; it < cend(); it++)
		{
			if(c == *it) 
				return static_cast<size_t>(it - cbegin());
		}

		return npos;
	}

	constexpr size_t find_first_of(value_type c, size_t pos = 0) const noexcept
	{
		return find(c, pos);
	}

	constexpr size_type find_last_of(value_type c,
									 size_type pos = npos) const noexcept
	{
		for(auto it = cend() - 1; it >= cbegin() + pos; --it)
		{
			if(c == *it) 
				return static_cast<size_t>(it - cbegin());
		}

		return npos;
	}

	constexpr value_type& front()
	{
		return *begin();
	}

	constexpr const value_type& front() const
	{
		return *cbegin();
	}

	constexpr const value_type& back() const noexcept
	{
		return *(cend() - 1);
	}

	constexpr value_type& back() noexcept
	{
		return *(end() - 1);
	}

	constexpr void clear() noexcept
	{
		if(__is_long())
		{
			m_rep.l.size = 0;
			m_rep.l.data[0] = value_type();
		}
		else
		{
			__set_short_size(0);			
			m_rep.s.data[0] = value_type();
		}
	}

	constexpr void resize(size_t n)
	{
		if(n > size())
		{
			reserve(n);
			memset(end(), value_type(), (n - size()) * sizeof(value_type));
		}

		__terminate_at(n);
	}

private:

	constexpr const_iterator __long_end() const
	{
		return m_rep.l.data + m_rep.l.size;
	}

	constexpr const_iterator __short_end() const
	{
		return &m_rep.s.data[0] + __get_short_size();
	}

	constexpr iterator __long_end()
	{
		return m_rep.l.data + m_rep.l.size;
	}

	constexpr iterator __short_end()
	{
		return &m_rep.s.data[0] + __get_short_size();
	}

	constexpr void __push_back_and_reallocate(CharT c)
	{
		auto old_size = size();
		auto new_size = old_size + 1;
		auto new_cap  = new_size * 2 + 1;
		__replace_buffer(__duplicate_buffer(old_size, new_cap), new_cap,
						 new_size);
		m_rep.l.data[m_rep.l.size - 1] = c;
		m_rep.l.data[m_rep.l.size]	   = value_type();
	}

	constexpr void __append_and_reallocate(const CharT* s, size_t count)
	{
		auto old_size = size();
		auto new_size = old_size + count;
		auto new_cap  = new_size * 2 + 1;
		__replace_buffer(__duplicate_buffer(old_size, new_cap), new_cap,
						 new_size);

		memcpy(m_rep.l.data + old_size, s, count * sizeof(value_type));

		m_rep.l.data[m_rep.l.size] = value_type();
	}

	constexpr void __insert_and_reallocate(size_t index, const CharT* s,
										   size_type old_size, size_type count)
	{
		auto new_size = old_size + count;
		auto cap	  = new_size * 2 + 1;
		auto buf	  = __duplicate_buffer(index, cap);

		memcpy(buf + index, s, count);
		//the additional +1 is for the null terminator
		memcpy(buf + index + count, begin() + index, (old_size - index) + 1);

		__replace_buffer(buf, cap, new_size);
	}

	template<class Iterator>
	constexpr size_t __find_first_of(Iterator begin, Iterator end,
									 size_t pos) const
	{
		for(auto it = cbegin() + pos; it < cend(); it++)
		{
			for(auto c = begin; c < end; c++)
			{
				if(*c == *it) return static_cast<size_t>(it - cbegin());
			}
		}

		return npos;
	}

	constexpr void __replace_buffer(value_type* new_buffer, size_t new_cap, size_t new_size)
	{
		if(__is_long())
		{
			delete[] m_rep.l.data;
		}

		assert(new_size <= new_cap);

		m_rep.l.data = new_buffer;
		m_rep.l.size = new_size;
		__set_long_cap(new_cap);
		assert(__is_long());
	}

	constexpr value_type* __duplicate_buffer(size_t size, size_t capacity)
	{
		value_type* new_ptr = new value_type[capacity];
		memcpy(new_ptr, cbegin(), size * sizeof(value_type));
		return new_ptr;
	}

	constexpr void __reallocate(size_t n) __attribute__((noinline))
	{
		const auto new_cap = n + 1;

		__replace_buffer(__duplicate_buffer(size() + 1, new_cap), new_cap,
						 size());
	}

	constexpr void __copy_from_terminated_string(const value_type* str,
												 size_type new_size) noexcept
	{
		assert(capacity() >= new_size);
		value_type* const dst = begin();
		if(__is_long())
		{
			m_rep.l.size = new_size;
			assert(__is_long());
		}
		else
		{
			__set_short_size(new_size);
			assert(!__is_long());
		}
		memcpy(dst, str, (new_size + 1) * sizeof(value_type));
	}

	constexpr void __copy_from_unterminated_string(const value_type* str,
												   size_type new_size) noexcept
	{
		assert(capacity() >= new_size);
		value_type* const dst = begin();
		__terminate_at(new_size);
		memcpy(dst, str, new_size * sizeof(value_type));
	}

	constexpr void __terminate_at(size_t n)
	{
		if(__is_long())
		{
			m_rep.l.size	= n;
			m_rep.l.data[n] = value_type();
			assert(__is_long());
		}
		else
		{
			__set_short_size(n);
			m_rep.s.data[n] = value_type();
			assert(!__is_long());
		}
	}

	constexpr size_type __get_short_size() const noexcept
	{
		return __min_cap - m_rep.s.inv_sz;
	}

	constexpr void __set_short_size(size_type s) noexcept
	{
		m_rep.s.inv_sz = static_cast<uint8_t>(__min_cap - s);
	}

	constexpr size_type __get_long_cap() const noexcept
	{
		return m_rep.l.cap & (size_type)(~__long_mask);
	}

	constexpr void __set_long_cap(size_type s) noexcept
	{
		m_rep.l.cap = __long_mask | s;
	}

	constexpr bool __is_long() const noexcept
	{
		return !!(m_rep.s.inv_sz & __short_mask);
	}

	struct __long
	{
		value_type* data;
		size_type size;
		size_type cap;
	};

	//needs to be little endian
	static const size_type __short_mask = 0x80;
	static const size_type __long_mask = ~(size_type(~0) >> 1);

	constexpr static 
	size_t __min_cap =	(sizeof(__long) - 1) / sizeof(value_type) > 2 ?
						(sizeof(__long) - 1) / sizeof(value_type) : 2;

	struct __short
	{
		union {
			value_type data[__min_cap + 1];
			struct
			{
				value_type padding[__min_cap];
				uint8_t p[sizeof(value_type) - 1];
				uint8_t inv_sz;
			};
		};
	};

	struct __rep
	{
		union
		{
			__long  l;
			__short s;
		};
	};

	__rep m_rep;
};

namespace std
{

template<typename C>
constexpr basic_string<C> operator+(std::basic_string<C>&& lhs,
									std::basic_string<C>&& rhs)
{
	return std::move(lhs.append(rhs));
}

template<typename C>
constexpr std::basic_string<C> operator+(const std::basic_string<C>& lhs,
										 const std::basic_string<C>& rhs)
{
	auto r = lhs;
	r += rhs;
	return r;
}

template<typename C>
constexpr bool operator==(const std::basic_string<C>& lhs,
						  const std::basic_string<C>& rhs)
{
	if(lhs.size() != rhs.size()) return false;
	return lhs.compare(rhs) == 0;
}

template<typename C>
constexpr bool operator!=(const std::basic_string<C>& lhs,
						  const std::basic_string<C>& rhs)
{
	if(lhs.size() != rhs.size()) return true;
	return lhs.compare(rhs) != 0;
}

template<typename C>
constexpr bool operator==(const std::basic_string<C>& lhs,
						  const typename std::basic_string<C>::value_type* rhs)
{
	return lhs.compare(rhs) == 0;
}

template<typename C>
constexpr bool operator!=(const std::basic_string<C>& lhs,
						  const typename std::basic_string<C>::value_type* rhs)
{
	return lhs.compare(rhs) != 0;
}

template<typename C>
constexpr bool operator==(const typename std::basic_string<C>::value_type* lhs,
						  const std::basic_string<C>& rhs)
{
	return rhs.compare(lhs) == 0;
}

template<typename C>
constexpr bool operator!=(const typename std::basic_string<C>::value_type* lhs,
						  const std::basic_string<C>& rhs)
{
	return rhs.compare(lhs) != 0;
}

template<typename C>
constexpr std::basic_string<C>
operator+(const std::basic_string<C>& lhs,
		  const typename std::basic_string<C>::value_type* rhs)
{
	auto new_str = lhs;
	return new_str.append(rhs);
}

template<typename C>
constexpr std::basic_string<C>
operator+(const std::basic_string<C>& lhs,
		  const typename std::basic_string<C>::value_type rhs)
{
	auto new_str = lhs;
	new_str.push_back(rhs);
	return new_str;
}

template<typename C>
constexpr std::basic_string<C>
operator+(std::basic_string<C>&& lhs,
		  const typename std::basic_string<C>::value_type rhs)
{
	lhs.push_back(rhs);
	return std::move(lhs);
}

std::string to_string(int value);
std::string to_string(long value);
std::string to_string(long long value);
std::string to_string(unsigned value);
std::string to_string(unsigned long value);
std::string to_string(unsigned long long value);

}
#endif

