/*
	This file is part of Fennix Kernel.

	Fennix Kernel is free software: you can redistribute it and/or
	modify it under the terms of the GNU General Public License as
	published by the Free Software Foundation, either version 3 of
	the License, or (at your option) any later version.

	Fennix Kernel is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/

#pragma once

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wredundant-decls"

#include <initializer_list>
#include <algorithm>
#include <stdexcept>
#include <convert.h>
#include <iterator>
#include <cstddef>
#include <compare>
#include <memory>
#include <ranges>

namespace std
{
	template <class CharT>
	class char_traits
	{
	public:
		typedef CharT char_type;
		typedef int int_type;

		static void assign(char_type &c1, const char_type &c2)
		{
			c1 = c2;
		}

		static char_type *assign(char_type *ptr, std::size_t count, char_type c2)
		{
			for (size_t i = 0; i < count; i++)
				ptr[i] = c2;
			return ptr;
		}

		static bool eq(char_type a, char_type b)
		{
			return a == b;
		}

		static bool lt(char_type a, char_type b)
		{
			return a < b;
		}

		static char_type *move(char_type *dest, const char_type *src, std::size_t count)
		{
			for (std::size_t i = 0; i < count; i++)
				dest[i] = src[i];
			return dest;
		}

		static char_type *copy(char_type *dest, const char_type *src, std::size_t count)
		{
			for (std::size_t i = 0; i < count; i++)
				dest[i] = src[i];
			return dest;
		}

		static int compare(const char_type *s1, const char_type *s2, std::size_t count)
		{
			for (std::size_t i = 0; i < count; i++)
			{
				if (s1[i] < s2[i])
					return -1;
				if (s1[i] > s2[i])
					return 1;
			}
			return 0;
		}

		static size_t length(const char_type *s)
		{
			size_t len = 0;
			while (s[len] != 0)
				len++;
			return len;
		}

		static const char_type *find(const char_type *ptr, std::size_t count, const char_type &ch)
		{
			for (std::size_t i = 0; i < count; i++)
			{
				if (ptr[i] == ch)
					return ptr + i;
			}
			return nullptr;
		}

		static char_type to_char_type(int_type c)
		{
			return static_cast<char_type>(c);
		}

		static int_type to_int_type(char_type c)
		{
			return static_cast<int_type>(c);
		}

		static bool eq_int_type(int_type c1, int_type c2)
		{
			return c1 == c2;
		}

		static int_type eof()
		{
			return static_cast<int_type>(-1);
		}

		static int_type not_eof(int_type e)
		{
			return e == eof() ? 0 : e;
		}
	};

	template <class CharT, class Traits = std::char_traits<CharT>>
	class basic_string_view
	{
	public:
		typedef Traits traits_type;
		typedef CharT value_type;
		typedef const CharT *pointer;
		typedef const CharT *const_pointer;
		typedef const CharT &reference;
		typedef const CharT &const_reference;
		typedef const_pointer iterator;
		typedef const_pointer const_iterator;
		// typedef std::reverse_iterator<const_iterator> reverse_iterator;
		// typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
		typedef std::size_t size_type;
		typedef std::ptrdiff_t difference_type;

		static const size_type npos = -1;

	private:
		const_pointer _data;
		size_type _size;

	public:
		constexpr basic_string_view()
			: _data(nullptr),
			  _size(0)
		{
		}

		constexpr basic_string_view(const basic_string_view &other)
			: _data(other._data),
			  _size(other._size)
		{
		}

		constexpr basic_string_view(const CharT *s)
			: _data(s),
			  _size(Traits::length(s))
		{
		}

		constexpr basic_string_view(const CharT *s, size_type count)
			: _data(s),
			  _size(count)
		{
		}

		constexpr basic_string_view &operator=(const basic_string_view &other)
		{
			_data = other._data;
			_size = other._size;
			return *this;
		}

		constexpr const_pointer data() const
		{
			return _data;
		}

		constexpr size_type size() const
		{
			return _size;
		}

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

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

		constexpr const_reference operator[](size_type pos) const
		{
			return _data[pos];
		}

		constexpr const_reference at(size_type pos) const
		{
			if (pos >= _size)
				throw std::out_of_range("basic_string_view::at");
			return _data[pos];
		}

		constexpr const_reference front() const
		{
			return _data[0];
		}

		constexpr const_reference back() const
		{
			return _data[_size - 1];
		}

		constexpr const_pointer begin() const
		{
			return _data;
		}

		constexpr const_pointer cbegin() const
		{
			return begin();
		}

		constexpr const_pointer end() const
		{
			return _data + _size;
		}

		constexpr const_pointer cend() const
		{
			return end();
		}

		constexpr void remove_prefix(size_type n)
		{
			_data += n;
			_size -= n;
		}

		constexpr void remove_suffix(size_type n)
		{
			_size -= n;
		}

		constexpr void swap(basic_string_view &other)
		{
			std::swap(_data, other._data);
			std::swap(_size, other._size);
		}

		constexpr size_type copy(CharT *dest, size_type count, size_type pos = 0) const
		{
			if (pos > _size)
				throw std::out_of_range("basic_string_view::copy");

			size_type rlen = std::min(count, _size - pos);
			std::copy(_data + pos, _data + pos + rlen, dest);
			return rlen;
		}

		constexpr basic_string_view substr(size_type pos = 0, size_type count = npos) const
		{
			if (pos > _size)
				throw std::out_of_range("basic_string_view::substr");

			return basic_string_view(_data + pos, std::min(count, _size - pos));
		}

		constexpr int compare(basic_string_view other) const
		{
			size_type rlen = std::min(_size, other._size);
			int res = Traits::compare(_data, other._data, rlen);
			if (res == 0)
			{
				if (_size < other._size)
					res = -1;
				else if (_size > other._size)
					res = 1;
			}
			return res;
		}

		constexpr int compare(size_type pos1, size_type count1, basic_string_view other) const
		{
			return substr(pos1, count1).compare(other);
		}

		constexpr int compare(size_type pos1, size_type count1, basic_string_view other, size_type pos2, size_type count2) const
		{
			return substr(pos1, count1).compare(other.substr(pos2, count2));
		}

		constexpr int compare(const CharT *s) const
		{
			return compare(basic_string_view(s));
		}

		constexpr int compare(size_type pos1, size_type count1, const CharT *s) const
		{
			return substr(pos1, count1).compare(basic_string_view(s));
		}

		constexpr int compare(size_type pos1, size_type count1, const CharT *s, size_type count2) const
		{
			return substr(pos1, count1).compare(basic_string_view(s, count2));
		}

		constexpr bool starts_with(basic_string_view x) const
		{
			return _size >= x._size && Traits::compare(_data, x._data, x._size) == 0;
		}

		constexpr bool starts_with(CharT x) const
		{
			return !empty() && Traits::eq(_data[0], x);
		}

		constexpr bool starts_with(const CharT *x) const
		{
			return starts_with(basic_string_view(x));
		}

		constexpr bool ends_with(basic_string_view x) const
		{
			return _size >= x._size && Traits::compare(_data + _size - x._size, x._data, x._size) == 0;
		}

		constexpr bool ends_with(CharT x) const
		{
			return !empty() && Traits::eq(_data[_size - 1], x);
		}

		constexpr bool ends_with(const CharT *x) const
		{
			return ends_with(basic_string_view(x));
		}

		constexpr size_type find(basic_string_view s, size_type pos = 0) const
		{
			if (pos > _size)
				return npos;

			const_pointer r = Traits::find(_data + pos, _size - pos, s[0]);
			if (r == nullptr)
				return npos;

			size_type index = r - _data;
			if (index + s.size() > _size)
				return npos;

			if (Traits::compare(r, s.data(), s.size()) == 0)
				return index;
			return npos;
		}

		constexpr size_type find(CharT c, size_type pos = 0) const
		{
			if (pos > _size)
				return npos;

			const_pointer r = Traits::find(_data + pos, _size - pos, c);
			if (r == nullptr)
				return npos;
			return r - _data;
		}

		constexpr size_type find(const CharT *s, size_type pos, size_type count) const
		{
			return find(basic_string_view(s, count), pos);
		}

		constexpr size_type find(const CharT *s, size_type pos = 0) const
		{
			return find(basic_string_view(s), pos);
		}

		constexpr size_type rfind(basic_string_view s, size_type pos = npos) const
		{
			if (s.size() > _size)
				return npos;

			if (pos == npos)
				pos = _size;
			else if (pos > _size)
				pos = _size;

			for (ssize_t i = (ssize_t)pos - s.size(); i >= 0; i--)
			{
				if (Traits::compare(_data + i, s.data(), s.size()) == 0)
					return i;
			}
			return npos;
		}

		constexpr size_type rfind(CharT c, size_type pos = npos) const
		{
			if (pos == npos)
				pos = _size;
			else if (pos > _size)
				pos = _size;

			for (ssize_t i = (ssize_t)pos - 1; i >= 0; i--)
			{
				if (Traits::eq(_data[i], c))
					return i;
			}
			return npos;
		}

		constexpr size_type rfind(const CharT *s, size_type pos, size_type count) const
		{
			return rfind(basic_string_view(s, count), pos);
		}

		constexpr size_type rfind(const CharT *s, size_type pos = npos) const
		{
			return rfind(basic_string_view(s), pos);
		}

		constexpr size_type find_first_of(basic_string_view s, size_type pos = 0) const
		{
			if (pos >= _size)
				return npos;

			for (size_type i = pos; i < _size; i++)
			{
				if (Traits::find(s.data(), s.size(), _data[i]) != nullptr)
					return i;
			}
			return npos;
		}

		constexpr size_type find_first_of(CharT c, size_type pos = 0) const
		{
			return find(c, pos);
		}

		constexpr size_type find_first_of(const CharT *s, size_type pos, size_type count) const
		{
			return find_first_of(basic_string_view(s, count), pos);
		}

		constexpr size_type find_first_of(const CharT *s, size_type pos = 0) const
		{
			return find_first_of(basic_string_view(s), pos);
		}

		constexpr size_type find_last_of(basic_string_view s, size_type pos = npos) const
		{
			if (pos == npos)
				pos = _size;
			else if (pos > _size)
				pos = _size;

			for (ssize_t i = (ssize_t)pos - 1; i >= 0; i--)
			{
				if (Traits::find(s.data(), s.size(), _data[i]) != nullptr)
					return i;
			}
			return npos;
		}

		constexpr size_type find_last_of(CharT c, size_type pos = npos) const
		{
			return rfind(c, pos);
		}

		constexpr size_type find_last_of(const CharT *s, size_type pos, size_type count) const
		{
			return find_last_of(basic_string_view(s, count), pos);
		}

		constexpr size_type find_last_of(const CharT *s, size_type pos = npos) const
		{
			return find_last_of(basic_string_view(s), pos);
		}

		constexpr size_type find_first_not_of(basic_string_view s, size_type pos = 0) const
		{
			if (pos >= _size)
				return npos;

			for (size_type i = pos; i < _size; i++)
			{
				if (Traits::find(s.data(), s.size(), _data[i]) == nullptr)
					return i;
			}
			return npos;
		}

		constexpr size_type find_first_not_of(CharT c, size_type pos = 0) const
		{
			if (pos >= _size)
				return npos;

			for (size_type i = pos; i < _size; i++)
			{
				if (!Traits::eq(_data[i], c))
					return i;
			}
			return npos;
		}

		constexpr size_type find_first_not_of(const CharT *s, size_type pos, size_type count) const
		{
			return find_first_not_of(basic_string_view(s, count), pos);
		}

		constexpr size_type find_first_not_of(const CharT *s, size_type pos = 0) const
		{
			return find_first_not_of(basic_string_view(s), pos);
		}

		constexpr size_type find_last_not_of(basic_string_view s, size_type pos = npos) const
		{
			if (pos == npos)
				pos = _size;
			else if (pos > _size)
				pos = _size;

			for (ssize_t i = (ssize_t)pos - 1; i >= 0; i--)
			{
				if (Traits::find(s.data(), s.size(), _data[i]) == nullptr)
					return i;
			}
			return npos;
		}

		constexpr size_type find_last_not_of(CharT c, size_type pos = npos) const
		{
			if (pos == npos)
				pos = _size;
			else if (pos > _size)
				pos = _size;

			for (ssize_t i = (ssize_t)pos - 1; i >= 0; i--)
			{
				if (!Traits::eq(_data[i], c))
					return i;
			}
			return npos;
		}

		constexpr size_type find_last_not_of(const CharT *s, size_type pos, size_type count) const
		{
			return find_last_not_of(basic_string_view(s, count), pos);
		}

		constexpr size_type find_last_not_of(const CharT *s, size_type pos = npos) const
		{
			return find_last_not_of(basic_string_view(s), pos);
		}
	};

	template <class CharT, class Traits>
	constexpr bool operator==(std::basic_string_view<CharT, Traits> lhs, std::type_identity_t<std::basic_string_view<CharT, Traits>> rhs)
	{
		return lhs.compare(rhs) == 0;
	}

	template <class CharT, class Traits = std::char_traits<CharT>, class Allocator = std::allocator<CharT>>
	class basic_string
	{
	public:
		typedef Traits traits_type;
		typedef CharT value_type;
		typedef Allocator allocator_type;
		typedef Allocator::size_type size_type;
		typedef Allocator::difference_type difference_type;
		typedef value_type &reference;
		typedef const value_type &const_reference;
		typedef Allocator::pointer pointer;
		typedef Allocator::const_pointer const_pointer;
		// typedef value_type iterator;			 /* FIXME: iterator */
		// typedef const value_type const_iterator; /* FIXME: iterator */
		// typedef std::reverse_iterator<iterator> reverse_iterator;
		// typedef std::reverse_iterator<const_iterator> const_reverse_iterator;

		static const size_type npos = -1;

		class iterator
		{
		public:
			using difference_type = typename basic_string::difference_type;
			using value_type = typename basic_string::value_type;
			using pointer = typename basic_string::pointer;
			using reference = typename basic_string::reference;
			using iterator_category = std::random_access_iterator_tag;

			iterator(CharT *ptr)
				: _ptr(ptr)
			{
			}

			CharT &operator*()
			{
				return *_ptr;
			}

			CharT *operator->()
			{
				return _ptr;
			}

			iterator &operator++()
			{
				_ptr++;
				return *this;
			}

			iterator operator++(int)
			{
				iterator tmp = *this;
				_ptr++;
				return tmp;
			}

			iterator &operator--()
			{
				_ptr--;
				return *this;
			}

			iterator operator--(int)
			{
				iterator tmp = *this;
				_ptr--;
				return tmp;
			}

			iterator &operator+=(difference_type n)
			{
				_ptr += n;
				return *this;
			}

			iterator operator+(difference_type n) const
			{
				return iterator(_ptr + n);
			}

			iterator &operator-=(difference_type n)
			{
				_ptr -= n;
				return *this;
			}

			iterator operator-(difference_type n) const
			{
				return iterator(_ptr - n);
			}

			difference_type operator-(const iterator &other) const
			{
				return _ptr - other._ptr;
			}

			CharT &operator[](difference_type n) const
			{
				return *(_ptr + n);
			}

			bool operator==(const iterator &other) const
			{
				return _ptr == other._ptr;
			}

			bool operator!=(const iterator &other) const
			{
				return _ptr != other._ptr;
			}

			bool operator<(const iterator &other) const
			{
				return _ptr < other._ptr;
			}

			bool operator>(const iterator &other) const
			{
				return _ptr > other._ptr;
			}

			bool operator<=(const iterator &other) const
			{
				return _ptr <= other._ptr;
			}

			bool operator>=(const iterator &other) const
			{
				return _ptr >= other._ptr;
			}

		private:
			CharT *_ptr;
		};

		class const_iterator
		{
		public:
			using difference_type = typename basic_string::difference_type;
			using value_type = typename basic_string::value_type;
			using pointer = typename basic_string::const_pointer;
			using reference = typename basic_string::const_reference;
			using iterator_category = std::random_access_iterator_tag;

			const_iterator(const CharT *ptr)
				: _ptr(ptr)
			{
			}

			const CharT &operator*() const
			{
				return *_ptr;
			}

			const CharT *operator->() const
			{
				return _ptr;
			}

			const_iterator &operator++()
			{
				_ptr++;
				return *this;
			}

			const_iterator operator++(int)
			{
				const_iterator tmp = *this;
				_ptr++;
				return tmp;
			}

			const_iterator &operator--()
			{
				_ptr--;
				return *this;
			}

			const_iterator operator--(int)
			{
				const_iterator tmp = *this;
				_ptr--;
				return tmp;
			}

			const_iterator &operator+=(difference_type n)
			{
				_ptr += n;
				return *this;
			}

			const_iterator operator+(difference_type n) const
			{
				return const_iterator(_ptr + n);
			}

			const_iterator &operator-=(difference_type n)
			{
				_ptr -= n;
				return *this;
			}

			const_iterator operator-(difference_type n) const
			{
				return const_iterator(_ptr - n);
			}

			difference_type operator-(const const_iterator &other) const
			{
				return _ptr - other._ptr;
			}

			const CharT &operator[](difference_type n) const
			{
				return *(_ptr + n);
			}

			bool operator==(const const_iterator &other) const
			{
				return _ptr == other._ptr;
			}

			bool operator!=(const const_iterator &other) const
			{
				return _ptr != other._ptr;
			}

			bool operator<(const const_iterator &other) const
			{
				return _ptr < other._ptr;
			}

			bool operator>(const const_iterator &other) const
			{
				return _ptr > other._ptr;
			}

			bool operator<=(const const_iterator &other) const
			{
				return _ptr <= other._ptr;
			}

			bool operator>=(const const_iterator &other) const
			{
				return _ptr >= other._ptr;
			}

		private:
			const CharT *_ptr;
		};

	private:
		allocator_type _alloc;
		CharT *_data;
		size_t _size;
		size_t _capacity;

	public:
#pragma region Constructors

		basic_string()
			: _alloc(Allocator()),
			  _data(nullptr),
			  _size(0),
			  _capacity(0)
		{
		}

		basic_string(size_type count, CharT ch, const Allocator &alloc = Allocator())
			: _alloc(alloc),
			  _size(count),
			  _capacity(count + 1)
		{
			_data = _alloc.allocate(_capacity);
			if (count > 0)
				memset(_data, ch, count);
			_data[count] = '\0';
		}

		basic_string(const basic_string &other, size_type pos, const Allocator &alloc = Allocator())
			: _alloc(alloc),
			  _size(other._size - pos),
			  _capacity(_size + 1)
		{
			_data = _alloc.allocate(_capacity);
			if (_size > 0)
				memcpy(_data, other._data + pos, _size);
			_data[_size] = '\0';
		}

		basic_string(const basic_string &other, size_type pos, size_type count, const Allocator &alloc = Allocator())
			: _alloc(alloc),
			  _size(count),
			  _capacity(count + 1)
		{
			if (count == npos)
			{
				_size = other._size - pos;
				_capacity = _size + 1;
			}

			_data = _alloc.allocate(_capacity);
			if (_size > 0)
				memcpy(_data, other._data + pos, _size);
			_data[_size] = '\0';
		}

		basic_string(const CharT *s, size_type count, const Allocator &alloc = Allocator())
			: _alloc(alloc),
			  _size(count),
			  _capacity(count + 1)
		{
			if (count == npos)
			{
				_size = Traits::length(s);
				_capacity = _size + 1;
			}

			_data = _alloc.allocate(_capacity);
			if (_size > 0)
				memcpy(_data, s, _size);
			_data[_size] = '\0';
		}

		basic_string(const CharT *s, const Allocator &alloc = Allocator())
			: _alloc(alloc),
			  _size(Traits::length(s)),
			  _capacity(_size + 1)
		{
			_data = _alloc.allocate(_capacity);
			if (_size > 0)
				memcpy(_data, s, _size);
			_data[_size] = '\0';
		}

		template <class InputIt>
		basic_string(InputIt first, InputIt last, const Allocator &alloc = Allocator())
			: _alloc(alloc),
			  _size(std::distance(first, last)),
			  _capacity(_size + 1)
		{
			_data = _alloc.allocate(_capacity);
			std::copy(first, last, _data);
			_data[_size] = '\0';
		}

		basic_string(const basic_string &other)
			: _alloc(other._alloc),
			  _size(other._size),
			  _capacity(_size + 1)
		{
			_data = _alloc.allocate(_capacity);
			if (_size > 0)
				memcpy(_data, other._data, _size);
			_data[_size] = '\0';
		}

		basic_string(const basic_string &other, const Allocator &alloc)
			: _alloc(alloc),
			  _size(other._size),
			  _capacity(_size + 1)
		{
			_data = _alloc.allocate(_capacity);
			if (_size > 0)
				memcpy(_data, other._data, _size);
			_data[_size] = '\0';
		}

		basic_string(std::initializer_list<CharT> ilist, const Allocator &alloc = Allocator())
			: _alloc(alloc),
			  _size(ilist._size),
			  _capacity(_size + 1)
		{
			_data = _alloc.allocate(_capacity);
			std::copy(ilist.begin(), ilist.end(), _data);
			_data[_size] = '\0';
		}

		template <class StringViewLike>
		basic_string(const StringViewLike &t, const Allocator &alloc = Allocator())
			: _alloc(alloc),
			  _size(strlen(t)),
			  _capacity(_size + 1)
		{
			_data = _alloc.allocate(_capacity);
			// std::copy(t.begin(), t.end(), _data);
			strncpy(_data, t, _size);
			_data[_size] = '\0';
		}

		template <class StringViewLike>
		basic_string(const StringViewLike &t, size_type pos, size_type n, const Allocator &alloc = Allocator())
			: _alloc(alloc),
			  _size(n),
			  _capacity(n + 1)
		{
			_data = _alloc.allocate(_capacity);
			std::copy(t.begin() + pos, t.begin() + pos + n, _data);
			_data[_size] = '\0';
		}

		basic_string(std::nullptr_t) = delete;

		~basic_string()
		{
			if (_data != nullptr)
			{
				_alloc.deallocate(_data, _capacity);
				_data = nullptr;
			}
		}

		basic_string &operator=(const basic_string &str)
		{
			if (this != &str)
			{
				if (_data != nullptr)
				{
					_alloc.deallocate(_data, _capacity);
					_data = nullptr;
				}

				_size = str._size;
				_capacity = str._capacity;
				_data = _alloc.allocate(_capacity);
				memcpy(_data, str._data, _capacity);
			}
			return *this;
		}

		basic_string &operator=(const CharT *s)
		{
			if (_data != nullptr)
			{
				_alloc.deallocate(_data, _capacity);
				_data = nullptr;
			}

			_size = Traits::length(s);
			_capacity = _size + 1;
			_data = _alloc.allocate(_capacity);
			memcpy(_data, s, _size > 0 ? _size : 1);
			_data[_size] = '\0';
			return *this;
		}

		basic_string &operator=(CharT ch)
		{
			if (_data != nullptr)
			{
				_alloc.deallocate(_data, _capacity);
				_data = nullptr;
			}

			_size = 1;
			_capacity = 2;
			_data = _alloc.allocate(_capacity);
			_data[0] = ch;
			_data[1] = '\0';
			return *this;
		}

		basic_string &operator=(std::initializer_list<CharT> ilist)
		{
			if (_data != nullptr)
			{
				_alloc.deallocate(_data, _capacity);
				_data = nullptr;
			}

			_size = ilist._size();
			_capacity = _size + 1;
			_data = _alloc.allocate(_capacity);
			std::copy(ilist.begin(), ilist.end(), _data);
			_data[_size] = '\0';
			return *this;
		}

		template <class StringViewLike>
		basic_string &operator=(const StringViewLike &t)
		{
			if (_data != nullptr)
			{
				_alloc.deallocate(_data, _capacity);
				_data = nullptr;
			}

			_size = t._size();
			_capacity = _size + 1;
			_data = _alloc.allocate(_capacity);
			std::copy(t.begin(), t.end(), _data);
			_data[_size] = '\0';
			return *this;
		}

		basic_string &operator=(std::nullptr_t) = delete;

		constexpr basic_string &assign(size_type count, CharT ch)
		{
			if (_data != nullptr)
			{
				_alloc.deallocate(_data, _capacity);
				_data = nullptr;
			}

			_size = count;
			_capacity = count + 1;
			_data = _alloc.allocate(_capacity);
			memset(_data, ch, count);
			_data[count] = '\0';
			return *this;
		}

		constexpr basic_string &assign(const basic_string &str)
		{
			if (_data != nullptr)
			{
				_alloc.deallocate(_data, _capacity);
				_data = nullptr;
			}

			_size = str._size;
			_capacity = str._capacity;
			_data = _alloc.allocate(_capacity);
			memcpy(_data, str._data, _size);
			_data[_size] = '\0';
			return *this;
		}

		constexpr basic_string &assign(const basic_string &str, size_type pos, size_type count = npos)
		{
			if (_data != nullptr)
			{
				_alloc.deallocate(_data, _capacity);
				_data = nullptr;
			}

			_size = count;
			_capacity = count + 1;
			_data = _alloc.allocate(_capacity);
			memcpy(_data, str._data + pos, _size);
			_data[_size] = '\0';
			return *this;
		}

		constexpr basic_string &assign(const CharT *s, size_type count)
		{
			if (_data != nullptr)
			{
				_alloc.deallocate(_data, _capacity);
				_data = nullptr;
			}

			_size = count;
			_capacity = count + 1;
			_data = _alloc.allocate(_capacity);
			memcpy(_data, s, _size);
			_data[_size] = '\0';
			return *this;
		}

		constexpr basic_string &assign(const CharT *s)
		{
			if (_data != nullptr)
			{
				_alloc.deallocate(_data, _capacity);
				_data = nullptr;
			}

			_size = Traits::length(s);
			_capacity = _size + 1;
			_data = _alloc.allocate(_capacity);
			memcpy(_data, s, _size);
			_data[_size] = '\0';
			return *this;
		}

		template <class InputIt>
		constexpr basic_string &assign(InputIt first, InputIt last)
		{
			if (_data != nullptr)
			{
				_alloc.deallocate(_data, _capacity);
				_data = nullptr;
			}

			_size = std::distance(first, last);
			_capacity = _size + 1;
			_data = _alloc.allocate(_capacity);
			std::copy(first, last, _data);
			_data[_size] = '\0';
			return *this;
		}

		constexpr basic_string &assign(std::initializer_list<CharT> ilist)
		{
			if (_data != nullptr)
			{
				_alloc.deallocate(_data, _capacity);
				_data = nullptr;
			}

			_size = ilist._size();
			_capacity = _size + 1;
			_data = _alloc.allocate(_capacity);
			std::copy(ilist.begin(), ilist.end(), _data);
			_data[_size] = '\0';
			return *this;
		}

		template <class StringViewLike>
		constexpr basic_string &assign(const StringViewLike &t)
		{
			if (_data != nullptr)
			{
				_alloc.deallocate(_data, _capacity);
				_data = nullptr;
			}

			_size = t._size();
			_capacity = _size + 1;
			_data = _alloc.allocate(_capacity);
			std::copy(t.begin(), t.end(), _data);
			_data[_size] = '\0';
			return *this;
		}

		template <class StringViewLike>
		constexpr basic_string &assign(const StringViewLike &t, size_type pos, size_type count = npos)
		{
			if (_data != nullptr)
			{
				_alloc.deallocate(_data, _capacity);
				_data = nullptr;
			}

			_size = count;
			_capacity = count + 1;
			_data = _alloc.allocate(_capacity);
			std::copy(t.begin() + pos, t.begin() + pos + count, _data);
			_data[_size] = '\0';
			return *this;
		}

		constexpr allocator_type get_allocator() const
		{
			return _alloc;
		}

#pragma endregion Constructors

#pragma region Element Access

		constexpr CharT &at(size_type pos)
		{
			if (pos >= _size)
				throw std::out_of_range("basic_string::at");
			return _data[pos];
		}

		constexpr const CharT &at(size_type pos) const
		{
			if (pos >= _size)
				throw std::out_of_range("basic_string::at");
			return _data[pos];
		}

		constexpr CharT &operator[](size_type pos)
		{
			return _data[pos];
		}

		constexpr const CharT &operator[](size_type pos) const
		{
			return _data[pos];
		}

		constexpr CharT &front()
		{
			return _data[0];
		}

		constexpr const CharT &front() const
		{
			return _data[0];
		}

		constexpr CharT &back()
		{
			return _data[_size - 1];
		}

		constexpr const CharT &back() const
		{
			return _data[_size - 1];
		}

		constexpr const CharT *data() const
		{
			return _data;
		}

		constexpr CharT *data()
		{
			return _data;
		}

		constexpr const CharT *c_str() const
		{
			return _data;
		}

		constexpr operator std::basic_string_view<CharT, Traits>() const
		{
			return std::basic_string_view<CharT, Traits>(_data, _size);
		}

#pragma endregion Element Access

#pragma region Iterators

		constexpr iterator begin()
		{
			return _data;
		}

		constexpr const_iterator begin() const
		{
			return _data;
		}

		constexpr const_iterator cbegin() const
		{
			return const_cast<const basic_string &>(*this).begin();
		}

		constexpr iterator end()
		{
			return _data + _size;
		}

		constexpr const_iterator end() const
		{
			return _data + _size;
		}

		constexpr const_iterator cend() const
		{
			return const_cast<const basic_string &>(*this).end();
		}

		// constexpr reverse_iterator rbegin()
		// {
		// 	return reverse_iterator(end());
		// }

		// constexpr const_reverse_iterator rbegin() const
		// {
		// 	return const_reverse_iterator(end());
		// }

		// constexpr const_reverse_iterator crbegin() const
		// {
		// 	return const_reverse_iterator(end());
		// }

		// constexpr reverse_iterator rend()
		// {
		// 	return reverse_iterator(begin());
		// }

		// constexpr const_reverse_iterator rend() const
		// {
		// 	return const_reverse_iterator(begin());
		// }

		// constexpr const_reverse_iterator crend() const
		// {
		// 	return const_reverse_iterator(begin());
		// }

#pragma endregion Iterators

#pragma region Capacity

		[[nodiscard]] constexpr bool empty() const
		{
			if (begin() == end())
				return true;
			return false;
		}

		constexpr size_type size() const
		{
			return _size;
		}

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

		constexpr size_type max_size() const
		{
			return numeric_limits<size_type>::max();
		}

		constexpr void reserve(size_type new_cap)
		{
			if (new_cap > _capacity)
			{
				if (_data == nullptr)
				{
					_data = _alloc.allocate(new_cap);
					_capacity = new_cap;
					return;
				}

				CharT *new_data = _alloc.allocate(new_cap);

				if (_size > 0)
					memcpy(new_data, _data, _size);

				if (_data != nullptr)
				{
					_alloc.deallocate(_data, _capacity);
					_data = nullptr;
				}

				_data = new_data;
				_capacity = new_cap;
			}
		}

		constexpr size_type capacity() const
		{
			return _capacity;
		}

		constexpr void shrink_to_fit()
		{
			if (_size < _capacity)
			{
				if (_size == 0)
					_size = Traits::length(_data);

				CharT *new_data = _alloc.allocate(_size);
				memcpy(new_data, _data, _size);

				if (_data != nullptr)
				{
					_alloc.deallocate(_data, _capacity);
					_data = nullptr;
				}

				_data = new_data;
				_capacity = _size;
			}
		}

#pragma endregion Capacity

#pragma region Modifiers

		constexpr void clear()
		{
			if (_data != nullptr)
			{
				_alloc.deallocate(_data, _capacity);
				_data = nullptr;
			}

			_size = 0;
			_capacity = 0;
		}

		constexpr basic_string &insert(size_type index, size_type count, CharT ch)
		{
			if (index > size())
				throw std::out_of_range("basic_string::insert");

			size_type new_size = _size + count;
			if (new_size > _capacity)
			{
				reserve(new_size);
			}

			std::copy_backward(begin() + index, end(), end() + count);
			std::fill_n(begin() + index, count, ch);
			_size = new_size;

			return *this;
		}

		constexpr basic_string &insert(size_type index, const CharT *s)
		{
			return insert(index, s, Traits::length(s));
		}

		constexpr basic_string &insert(size_type index, const CharT *s, size_type count)
		{
			if (index > size())
				throw std::out_of_range("basic_string::insert");

			size_type new_size = _size + count;
			if (new_size > _capacity)
				reserve(new_size);

			std::copy_backward(begin() + index, end(), end() + count);
			std::copy(s, s + count, begin() + index);
			_size = new_size;

			return *this;
		}

		constexpr basic_string &insert(size_type index, const basic_string &str)
		{
			return insert(index, str, 0, str.size());
		}

		constexpr basic_string &insert(size_type index, const basic_string &str, size_type s_index, size_type count = npos)
		{
			if (s_index > str.size())
				throw std::out_of_range("basic_string::insert");

			if (count == npos || s_index + count > str.size())
				count = str.size() - s_index;

			size_type new_size = _size + count;
			if (new_size > _capacity)
			{
				reserve(new_size);
			}

			std::copy_backward(begin() + index, end(), end() + count);
			std::copy(str.begin() + s_index, str.begin() + s_index + count, begin() + index);
			_size = new_size;

			return *this;
		}

		constexpr iterator insert(const_iterator pos, CharT ch)
		{
			size_type index = pos - begin();
			insert(index, 1, ch);
			return begin() + index;
		}

		constexpr iterator insert(const_iterator pos, size_type count, CharT ch)
		{
			size_type index = pos - begin();
			insert(index, count, ch);
			return begin() + index;
		}

		template <class InputIt>
		constexpr iterator insert(const_iterator pos, InputIt first, InputIt last)
		{
			size_type index = pos - begin();
			insert(index, first, last);
			return begin() + index;
		}

		constexpr iterator insert(const_iterator pos, std::initializer_list<CharT> ilist)
		{
			size_type index = pos - begin();
			insert(index, ilist);
			return begin() + index;
		}

		template <class StringViewLike>
		constexpr basic_string &insert(size_type index, const StringViewLike &t)
		{
			return insert(index, t, 0, t.size());
		}

		template <class StringViewLike>
		constexpr basic_string &insert(size_type index, const StringViewLike &t, size_type t_index, size_type count = npos)
		{
			if (t_index > t.size())
				throw std::out_of_range("basic_string::insert");

			if (count == npos || t_index + count > t.size())
				count = t.size() - t_index;

			size_type new_size = _size + count;
			if (new_size > _capacity)
			{
				reserve(new_size);
			}

			std::copy_backward(begin() + index, end(), end() + count);
			std::copy(t.begin() + t_index, t.begin() + t_index + count, begin() + index);
			_size = new_size;

			return *this;
		}

		constexpr basic_string &erase(size_type index = 0, size_type count = npos)
		{
			if (index > size())
				throw std::out_of_range("basic_string::erase");

			if (count == npos || index + count > size())
				count = size() - index;

			if (count > 0)
			{
				std::copy(begin() + index + count, end(), begin() + index);
				_size -= count;
				_data[_size] = '\0';
			}

			return *this;
		}

		constexpr iterator erase(const_iterator position)
		{
			size_type index = position - cbegin();
			erase(index, 1);
			return begin() + index;
		}

		constexpr iterator erase(const_iterator first, const_iterator last)
		{
			size_type index = first - cbegin();
			erase(index, last - first);
			return begin() + index;
		}

		constexpr void push_back(CharT ch)
		{
			if (_size == _capacity)
				reserve(_capacity == 0 ? 2 : _capacity * 2);

			_data[_size++] = ch;
			_data[_size] = 0;
		}

		constexpr void pop_back()
		{
			if (_size > 0)
			{
				_data[--_size] = 0;
			}
		}

		constexpr basic_string &append(size_type count, CharT ch)
		{
			if (count > 0)
			{
				size_type new_size = _size + count;
				if (new_size > _capacity)
				{
					reserve(new_size);
				}
				std::fill_n(_data + _size, count, ch);
				_size = new_size;
			}
			return *this;
		}

		constexpr basic_string &append(const basic_string &str)
		{
			return append(str.begin(), str.end());
		}

		constexpr basic_string &append(const basic_string &str, size_type pos, size_type count = npos)
		{
			return append(str.begin() + pos, str.begin() + pos + count);
		}

		constexpr basic_string &append(const CharT *s, size_type count)
		{
			return append(s, s + count);
		}

		constexpr basic_string &append(const CharT *s)
		{
			return append(s, s + Traits::length(s));
		}

		template <class InputIt>
		constexpr basic_string &append(InputIt first, InputIt last)
		{
			size_type count = last - first;
			size_type old_size = _size;
			size_type new_size = old_size + count;

			if (new_size + 1 > _capacity)
				reserve(new_size + 1);

			std::copy(first, last, _data + old_size);

			_size = new_size;
			_data[_size] = '\0';
			return *this;
		}

		constexpr basic_string &append(std::initializer_list<CharT> ilist)
		{
			return append(ilist.begin(), ilist.end());
		}

		template <class StringViewLike>
		constexpr basic_string &append(const StringViewLike &t)
		{
			return append(t.begin(), t.end());
		}

		template <class StringViewLike>
		constexpr basic_string &append(const StringViewLike &t, size_type pos, size_type count = npos)
		{
			return append(t.begin() + pos, t.begin() + pos + count);
		}

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

		constexpr basic_string &operator+=(CharT ch)
		{
			push_back(ch);
			return *this;
		}

		constexpr basic_string &operator+=(const CharT *s)
		{
			return append(s);
		}

		constexpr basic_string &operator+=(std::initializer_list<CharT> ilist)
		{
			return append(ilist);
		}

		template <class StringViewLike>
		constexpr basic_string &operator+=(const StringViewLike &t)
		{
			return append(t);
		}

		constexpr basic_string &replace(size_type pos, size_type count, const basic_string &str)
		{
			return replace(begin() + pos, begin() + pos + count, str.begin(), str.end());
		}

		constexpr basic_string &replace(const_iterator first, const_iterator last, const basic_string &str)
		{
			return replace(first, last, str.begin(), str.end());
		}

		constexpr basic_string &replace(size_type pos, size_type count, const basic_string &str, size_type pos2, size_type count2 = npos)
		{
			return replace(begin() + pos, begin() + pos + count, str.begin() + pos2, str.begin() + pos2 + count2);
		}

		constexpr basic_string &replace(size_type pos, size_type count, const CharT *cstr, size_type count2)
		{
			return replace(begin() + pos, begin() + pos + count, cstr, cstr + count2);
		}

		constexpr basic_string &replace(const_iterator first, const_iterator last, const CharT *cstr, size_type count2)
		{
			return replace(first, last, cstr, cstr + count2);
		}

		constexpr basic_string &replace(size_type pos, size_type count, const CharT *cstr)
		{
			if (pos > size())
				throw std::out_of_range("basic_string::replace");

			size_type new_cap = (_size - pos) + count;

			if (new_cap > _capacity)
				reserve(new_cap);

			std::copy(cstr, cstr + count, begin() + pos);
			return *this;
		}

		constexpr basic_string &replace(const_iterator first, const_iterator last, const CharT *cstr)
		{
			return replace(first, last, cstr, cstr + Traits::length(cstr));
		}

		constexpr basic_string &replace(size_type pos, size_type count, size_type count2, CharT ch)
		{
			return replace(begin() + pos, begin() + pos + count, count2, ch);
		}

		constexpr basic_string &replace(const_iterator first, const_iterator last, size_type count2, CharT ch)
		{
			return replace(first, last, count2, ch);
		}

		template <class InputIt>
		constexpr basic_string &replace(const_iterator first, const_iterator last, InputIt first2, InputIt last2)
		{
			size_type count = std::distance(first, last);
			size_type count2 = std::distance(first2, last2);
			if (count2 > count)
			{
				size_type new_size = _size + count2 - count;
				if (new_size > _capacity)
					reserve(new_size);

				std::copy_backward(last, end(), end() + count2 - count);
				std::copy(first2, last2, first);
				_size = new_size;
			}
			else
			{
				std::copy(first2, last2, first);
				std::copy(last, end(), first + count2);
				_size -= count - count2;
			}
			return *this;
		}

		constexpr basic_string &replace(const_iterator first, const_iterator last, std::initializer_list<CharT> ilist)
		{
			return replace(first, last, ilist.begin(), ilist.end());
		}

		template <class StringViewLike>
		constexpr basic_string &replace(size_type pos, size_type count, const StringViewLike &t)
		{
			return replace(begin() + pos, begin() + pos + count, t.begin(), t.end());
		}

		template <class StringViewLike>
		constexpr basic_string &replace(const_iterator first, const_iterator last, const StringViewLike &t)
		{
			return replace(first, last, t.begin(), t.end());
		}

		template <class StringViewLike>
		constexpr basic_string &replace(size_type pos, size_type count, const StringViewLike &t, size_type pos2, size_type count2 = npos)
		{
			return replace(begin() + pos, begin() + pos + count, t.begin() + pos2, t.begin() + pos2 + count2);
		}

		constexpr size_type copy(CharT *dest, size_type count, size_type pos = 0) const
		{
			if (pos > size())
				throw std::out_of_range("basic_string::copy");

			size_type len = std::min(count, size() - pos);
			std::copy(begin() + pos, begin() + pos + len, dest);
			return len;
		}

		constexpr void resize(size_type count)
		{
			if (_size == 0 && _data != nullptr)
				_size = Traits::length(_data);

			if (count < _size)
				erase(count);
			else if (count > _size)
				append(count - _size, CharT());
		}

		constexpr void resize(size_type count, CharT ch)
		{
			if (count < _size)
				erase(count);
			else if (count > _size)
				append(count - _size, ch);
		}

		constexpr void swap(basic_string &other)
		{
			fixme("The allocator won't be swapped");
			// std::swap(_alloc, other._alloc);
			std::swap(_data, other._data);
			std::swap(_size, other._size);
			std::swap(_capacity, other._capacity);
		}

#pragma endregion Modifiers

#pragma region Search

		constexpr size_type find(const basic_string &str, size_type pos = 0) const
		{
			return find(str.data(), pos, str.size());
		}

		constexpr size_type find(const CharT *s, size_type pos, size_type count) const
		{
			if (count == 0)
				return pos;
			if (pos >= _size)
				return npos;
			const_iterator it = std::search(begin() + pos, end(), s, s + count);
			return it == end() ? npos : std::distance(begin(), it);
		}

		constexpr size_type find(const CharT *s, size_type pos = 0) const
		{
			return find(s, pos, Traits::length(s));
		}

		constexpr size_type find(CharT ch, size_type pos = 0) const
		{
			const_iterator it = std::find(begin() + pos, end(), ch);
			return it == end() ? npos : std::distance(begin(), it);
		}

		template <class StringViewLike>
		constexpr size_type find(const StringViewLike &t, size_type pos = 0) const
		{
			return find(t.data(), pos, t.size());
		}

		constexpr size_type rfind(const basic_string &str, size_type pos = npos) const
		{
			return rfind(str.data(), pos, str.size());
		}

		constexpr size_type rfind(const CharT *s, size_type pos, size_type count) const
		{
			if (count == 0)
				return pos;
			if (pos >= _size)
				pos = _size;
			else
				pos = _size - pos;
			const_iterator it = std::find_end(begin(), begin() + pos, s, s + count);
			return it == begin() + pos ? npos : std::distance(begin(), it);
		}

		constexpr size_type rfind(const CharT *s, size_type pos = npos) const
		{
			return rfind(s, pos, Traits::length(s));
		}

		constexpr size_type rfind(CharT ch, size_type pos = npos) const
		{
			if (pos >= _size)
				pos = _size;
			else
				pos = _size - pos;
			const_iterator it = std::find(begin(), begin() + pos, ch);
			return it == begin() + pos ? npos : std::distance(begin(), it);
		}

		template <class StringViewLike>
		constexpr size_type rfind(const StringViewLike &t, size_type pos = npos) const
		{
			return rfind(t.data(), pos, t.size());
		}

		constexpr size_type find_first_of(const basic_string &str, size_type pos = 0) const
		{
			return find_first_of(str.data(), pos, str.size());
		}

		constexpr size_type find_first_of(const CharT *s, size_type pos, size_type count) const
		{
			if (count == 0)
				return npos;

			if (pos >= _size)
				return npos;

			const_iterator it = std::find_first_of(begin() + pos, end(), s, s + count);
			return it == end() ? npos : std::distance(begin(), it);
		}

		constexpr size_type find_first_of(const CharT *s, size_type pos = 0) const
		{
			return find_first_of(s, pos, Traits::length(s));
		}

		constexpr size_type find_first_of(CharT ch, size_type pos = 0) const
		{
			return find(ch, pos);
		}

		template <class StringViewLike>
		constexpr size_type find_first_of(const StringViewLike &t, size_type pos = 0) const
		{
			return find_first_of(t.data(), pos, t.size());
		}

		constexpr size_type find_first_not_of(const basic_string &str, size_type pos = 0) const
		{
			return find_first_not_of(str.data(), pos, str.size());
		}

		constexpr size_type find_first_not_of(const CharT *s, size_type pos, size_type count) const
		{
			if (count == 0)
				return pos;

			if (pos >= _size)
				return npos;

			const_iterator it = begin() + pos;
			while (it != end())
			{
				if (std::find(s, s + count, *it) == s + count)
					return std::distance(begin(), it);
				it++;
			}
			return npos;
		}

		constexpr size_type find_first_not_of(const CharT *s, size_type pos = 0) const
		{
			return find_first_not_of(s, pos, Traits::length(s));
		}

		constexpr size_type find_first_not_of(CharT ch, size_type pos = 0) const
		{
			const_iterator it = std::find_if(begin() + pos, end(), [ch](CharT c)
											 { return c != ch; });
			return it == end() ? npos : std::distance(begin(), it);
		}

		template <class StringViewLike>
		constexpr size_type find_first_not_of(const StringViewLike &t, size_type pos = 0) const
		{
			return find_first_not_of(t.data(), pos, t.size());
		}

		constexpr size_type find_last_of(const basic_string &str, size_type pos = npos) const
		{
			return find_last_of(str.data(), pos, str.size());
		}

		constexpr size_type find_last_of(const CharT *s, size_type pos, size_type count) const
		{
			if (count == 0)
				return npos;

			if (pos >= _size)
				pos = _size;
			else
				pos = _size - pos;

			const_iterator it = std::find_first_of(begin(), begin() + pos, s, s + count);
			return it == begin() + pos ? npos : std::distance(begin(), it);
		}

		constexpr size_type find_last_of(const CharT *s, size_type pos = npos) const
		{
			return find_last_of(s, pos, Traits::length(s));
		}

		constexpr size_type find_last_of(CharT ch, size_type pos = npos) const
		{
			if (pos >= _size)
				pos = _size;
			else
				pos = _size - pos;

			const_iterator it = std::find(begin(), begin() + pos, ch);
			return it == begin() + pos ? npos : std::distance(begin(), it);
		}

		template <class StringViewLike>
		constexpr size_type find_last_of(const StringViewLike &t, size_type pos = npos) const
		{
			return find_last_of(t.data(), pos, t.size());
		}

		constexpr size_type find_last_not_of(const basic_string &str, size_type pos = npos) const
		{
			return find_last_not_of(str.data(), pos, str.size());
		}

		constexpr size_type find_last_not_of(const CharT *s, size_type pos, size_type count) const
		{
			if (count == 0)
				return pos;

			if (pos >= _size)
				pos = _size;
			else
				pos = _size - pos;

			const_iterator it = begin() + pos;
			while (it != begin())
			{
				if (std::find(s, s + count, *it) == s + count)
					return std::distance(begin(), it);
				it--;
			}
			return npos;
		}

		constexpr size_type find_last_not_of(const CharT *s, size_type pos = npos) const
		{
			return find_last_not_of(s, pos, Traits::length(s));
		}

		constexpr size_type find_last_not_of(CharT ch, size_type pos = npos) const
		{
			if (pos >= _size)
				pos = _size;
			else
				pos = _size - pos;

			const_iterator it = std::find_if(begin(), begin() + pos, [ch](CharT c)
											 { return c != ch; });
			return it == begin() + pos ? npos : std::distance(begin(), it);
		}

		template <class StringViewLike>
		constexpr size_type find_last_not_of(const StringViewLike &t, size_type pos = npos) const
		{
			return find_last_not_of(t.data(), pos, t.size());
		}

#pragma endregion Search

#pragma region Operations

		constexpr int compare(const basic_string &str) const
		{
			return compare(0, npos, str);
		}

		constexpr int compare(size_type pos1, size_type count1, const basic_string &str) const
		{
			return compare(pos1, count1, str, 0, npos);
		}

		constexpr int compare(size_type pos1, size_type count1, const basic_string &str, size_type pos2, size_type count2 = npos) const
		{
			if (pos1 > _size)
				throw std::out_of_range("basic_string::compare");

			if (pos2 > str._size)
				throw std::out_of_range("basic_string::compare");

			size_type len1 = std::min(count1, _size - pos1);
			size_type len2 = std::min(count2, str._size - pos2);
			size_type len = std::min(len1, len2);

			int result = Traits::compare(data() + pos1, str.data() + pos2, len);
			if (result == 0)
			{
				if (len1 < len2)
					result = -1;
				else if (len1 > len2)
					result = 1;
			}
			return result;
		}

		constexpr int compare(const CharT *s) const
		{
			return compare(0, npos, s);
		}

		constexpr int compare(size_type pos1, size_type count1, const CharT *s) const
		{
			return compare(pos1, count1, s, Traits::length(s));
		}

		constexpr int compare(size_type pos1, size_type count1, const CharT *s, size_type count2) const
		{
			if (pos1 > size())
				throw std::out_of_range("basic_string::compare");

			size_type len1 = std::min(count1, size() - pos1);
			size_type len2 = std::min(count2, Traits::length(s));
			size_type len = std::min(len1, len2);

			int result = Traits::compare(data() + pos1, s, len);

			if (result == 0)
			{
				if (len1 < len2)
					result = -1;
				else if (len1 > len2)
					result = 1;
			}
			return result;
		}

		template <class StringViewLike>
		constexpr int compare(const StringViewLike &t) const
		{
			return compare(0, npos, t);
		}

		template <class StringViewLike>
		constexpr int compare(size_type pos1, size_type count1, const StringViewLike &t) const
		{
			return compare(pos1, count1, t, 0, npos);
		}

		template <class StringViewLike>
		constexpr int compare(size_type pos1, size_type count1, const StringViewLike &t, size_type pos2, size_type count2 = npos) const
		{
			if (pos1 > size())
				throw std::out_of_range("basic_string::compare");

			if (pos2 > t.size())
				throw std::out_of_range("basic_string::compare");

			size_type len1 = std::min(count1, size() - pos1);
			size_type len2 = std::min(count2, t.size() - pos2);
			size_type len = std::min(len1, len2);

			int result = Traits::compare(data() + pos1, t.data() + pos2, len);
			if (result == 0)
			{
				if (len1 < len2)
					result = -1;
				else if (len1 > len2)
					result = 1;
			}
			return result;
		}

		constexpr bool starts_with(std::basic_string_view<CharT, Traits> sv) const
		{
			return sv.size() <= size() && Traits::compare(data(), sv.data(), sv.size()) == 0;
		}

		constexpr bool starts_with(CharT ch) const
		{
			return !empty() && Traits::eq(front(), ch);
		}

		constexpr bool starts_with(const CharT *s) const
		{
			return starts_with(std::basic_string_view<CharT, Traits>(s));
		}

		constexpr bool ends_with(std::basic_string_view<CharT, Traits> sv) const
		{
			return sv.size() <= size() && Traits::compare(data() + size() - sv.size(), sv.data(), sv.size()) == 0;
		}

		constexpr bool ends_with(CharT ch) const
		{
			return !empty() && Traits::eq(back(), ch);
		}

		constexpr bool ends_with(const CharT *s) const
		{
			return ends_with(std::basic_string_view<CharT, Traits>(s));
		}

		constexpr bool contains(std::basic_string_view<CharT, Traits> sv) const
		{
			return find(sv) != npos;
		}

		constexpr bool contains(CharT ch) const
		{
			return find(ch) != npos;
		}

		constexpr bool contains(const CharT *s) const
		{
			return find(s) != npos;
		}

		constexpr basic_string substr(size_type pos = 0, size_type count = npos) const &
		{
			return basic_string(*this, pos, count);
		}

		constexpr basic_string substr(size_type pos = 0, size_type count = npos) &&
		{
			return basic_string(std::move(*this), pos, count);
		}

#pragma endregion Operations
	};

#pragma region Additional Operations

	template <class CharT, class Traits, class Alloc>
	constexpr bool operator==(const std::basic_string<CharT, Traits, Alloc> &lhs, const std::basic_string<CharT, Traits, Alloc> &rhs)
	{
		return lhs.compare(rhs) == 0;
	}

	template <class CharT, class Traits, class Alloc>
	constexpr auto operator<=>(const std::basic_string<CharT, Traits, Alloc> &lhs, const std::basic_string<CharT, Traits, Alloc> &rhs) noexcept -> decltype(static_cast<weak_ordering>(0 <=> 0))
	{
		return static_cast<weak_ordering>(lhs.compare(rhs) <=> 0);
	}

	template <class CharT, class Traits, class Alloc>
	constexpr bool operator==(const std::basic_string<CharT, Traits, Alloc> &lhs, const CharT *rhs)
	{
		return lhs.compare(rhs) == 0;
	}

	template <class CharT, class Traits, class Alloc>
	constexpr auto operator<=>(const std::basic_string<CharT, Traits, Alloc> &lhs, const CharT *rhs) -> decltype(static_cast<weak_ordering>(0 <=> 0))
	{
		return static_cast<weak_ordering>(lhs.compare(rhs) <=> 0);
	}

	template <class CharT, class Traits, class Alloc>
	std::basic_string<CharT, Traits, Alloc> constexpr operator+(const std::basic_string<CharT, Traits, Alloc> &lhs, const std::basic_string<CharT, Traits, Alloc> &rhs)
	{
		std::basic_string<CharT, Traits, Alloc> result(lhs);
		result += rhs;
		return result;
	}

	template <class CharT, class Traits, class Alloc>
	std::basic_string<CharT, Traits, Alloc> constexpr operator+(const std::basic_string<CharT, Traits, Alloc> &lhs, const CharT *rhs)
	{
		std::basic_string<CharT, Traits, Alloc> result(lhs);
		result += rhs;
		return result;
	}

	template <class CharT, class Traits, class Alloc>
	std::basic_string<CharT, Traits, Alloc> constexpr operator+(const std::basic_string<CharT, Traits, Alloc> &lhs, CharT rhs)
	{
		std::basic_string<CharT, Traits, Alloc> result(lhs);
		result += rhs;
		return result;
	}

	template <class CharT, class Traits, class Alloc>
	std::basic_string<CharT, Traits, Alloc> constexpr operator+(const CharT *lhs, const std::basic_string<CharT, Traits, Alloc> &rhs)
	{
		std::basic_string<CharT, Traits, Alloc> result(lhs);
		result += rhs;
		return result;
	}

	template <class CharT, class Traits, class Alloc>
	std::basic_string<CharT, Traits, Alloc> constexpr operator+(CharT lhs, const std::basic_string<CharT, Traits, Alloc> &rhs)
	{
		std::basic_string<CharT, Traits, Alloc> result(1, lhs);
		result += rhs;
		return result;
	}

	template <class CharT, class Traits, class Alloc>
	std::basic_string<CharT, Traits, Alloc> constexpr operator+(std::basic_string<CharT, Traits, Alloc> &&lhs, std::basic_string<CharT, Traits, Alloc> &&rhs)
	{
		lhs.append(rhs);
		return std::move(lhs);
	}

	template <class CharT, class Traits, class Alloc>
	std::basic_string<CharT, Traits, Alloc> constexpr operator+(std::basic_string<CharT, Traits, Alloc> &&lhs, const std::basic_string<CharT, Traits, Alloc> &rhs)
	{
		lhs.append(rhs);
		return std::move(lhs);
	}

	template <class CharT, class Traits, class Alloc>
	std::basic_string<CharT, Traits, Alloc> constexpr operator+(std::basic_string<CharT, Traits, Alloc> &&lhs, const CharT *rhs)
	{
		lhs.append(rhs);
		return std::move(lhs);
	}

	template <class CharT, class Traits, class Alloc>
	std::basic_string<CharT, Traits, Alloc> constexpr operator+(std::basic_string<CharT, Traits, Alloc> &&lhs, CharT rhs)
	{
		lhs.push_back(rhs);
		return std::move(lhs);
	}

	template <class CharT, class Traits, class Alloc>
	std::basic_string<CharT, Traits, Alloc> constexpr operator+(const std::basic_string<CharT, Traits, Alloc> &lhs, std::basic_string<CharT, Traits, Alloc> &&rhs)
	{
		std::basic_string<CharT, Traits, Alloc> result(lhs);
		result.append(rhs);
		return result;
	}

	template <class CharT, class Traits, class Alloc>
	std::basic_string<CharT, Traits, Alloc> constexpr operator+(const CharT *lhs, std::basic_string<CharT, Traits, Alloc> &&rhs)
	{
		std::basic_string<CharT, Traits, Alloc> result(lhs);
		result.append(rhs);
		return result;
	}

	template <class CharT, class Traits, class Alloc>
	std::basic_string<CharT, Traits, Alloc> constexpr operator+(CharT lhs, std::basic_string<CharT, Traits, Alloc> &&rhs)
	{
		std::basic_string<CharT, Traits, Alloc> result(1, lhs);
		result.append(rhs);
		return result;
	}

#pragma endregion Additional Operations

	typedef basic_string<char> string;
	typedef basic_string<wchar_t> wstring;
	typedef basic_string<char8_t> u8string;
	typedef basic_string<char16_t> u16string;
	typedef basic_string<char32_t> u32string;

#pragma region To String

	int sprintf(char *s, const char *format, ...) __attribute__((format(__printf__, (2), (3))));
	int snprintf(char *s, size_t count, const char *format, ...) __attribute__((format(__printf__, (3), (4))));

	inline string to_string(int value)
	{
		char buffer[128];
		snprintf(buffer, sizeof(buffer), "%d", value);
		return {buffer};
	}

	inline string to_string(long value)
	{
		char buffer[128];
		snprintf(buffer, sizeof(buffer), "%ld", value);
		return {buffer};
	}

	inline string to_string(long long value)
	{
		char buffer[128];
		snprintf(buffer, sizeof(buffer), "%lld", value);
		return {buffer};
	}

	inline string to_string(unsigned value)
	{
		char buffer[128];
		snprintf(buffer, sizeof(buffer), "%u", value);
		return {buffer};
	}

	inline string to_string(unsigned long value)
	{
		char buffer[128];
		snprintf(buffer, sizeof(buffer), "%lu", value);
		return {buffer};
	}

	inline string to_string(unsigned long long value)
	{
		char buffer[128];
		snprintf(buffer, sizeof(buffer), "%llu", value);
		return {buffer};
	}

	inline string to_string(float value)
	{
		char buffer[128];
		snprintf(buffer, sizeof(buffer), "%f", value);
		return {buffer};
	}

	inline string to_string(double value)
	{
		char buffer[128];
		snprintf(buffer, sizeof(buffer), "%f", value);
		return {buffer};
	}

	inline string to_string(long double value)
	{
		char buffer[128];
		snprintf(buffer, sizeof(buffer), "%Lf", value);
		return {buffer};
	}

#pragma endregion To String

	/* FIXME: there's no swprintf implemented yet */
	constexpr std::wstring to_wstring(int value);
	constexpr std::wstring to_wstring(long value);
	constexpr std::wstring to_wstring(long long value);
	constexpr std::wstring to_wstring(unsigned value);
	constexpr std::wstring to_wstring(unsigned long value);
	constexpr std::wstring to_wstring(unsigned long long value);
	constexpr std::wstring to_wstring(float value);
	constexpr std::wstring to_wstring(double value);
	constexpr std::wstring to_wstring(long double value);

	inline namespace literals
	{
		inline namespace string_literals
		{
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wliteral-suffix"
			inline std::string operator""s(const char *str, std::size_t len)
			{
				return std::string{str, len};
			}

			inline std::u8string operator""s(const char8_t *str, std::size_t len)
			{
				return std::u8string{str, len};
			}

			inline std::u16string operator""s(const char16_t *str, std::size_t len)
			{
				return std::u16string{str, len};
			}

			inline std::u32string operator""s(const char32_t *str, std::size_t len)
			{
				return std::u32string{str, len};
			}

			inline std::wstring operator""s(const wchar_t *str, std::size_t len)
			{
				return std::wstring{str, len};
			}
#pragma GCC diagnostic pop
		}
	}
}

#pragma GCC diagnostic pop
