#ifndef STD_STRING_VIEW_H
#define STD_STRING_VIEW_H

#include <string.h>
#include <algorithm>
#include <char_traits.h>

namespace std {
	template<typename C> class basic_string_view;

	using string_view = basic_string_view<char>;
};

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

	constexpr basic_string_view() noexcept :
		m_data(nullptr),
		m_size(0)
	{}

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

	constexpr basic_string_view(const value_type* s, size_t len) :
		m_data(s),
		m_size(len)
	{}

	constexpr basic_string_view(const basic_string_view& other) noexcept
		= default;

	constexpr basic_string_view& operator=(const basic_string_view& view) noexcept 
		= default;

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

	constexpr const_iterator cend() const noexcept
	{
		return cbegin() + size();
	}

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

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

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

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

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

	constexpr const_pointer data() const noexcept
	{
		return cbegin();
	}

	constexpr void swap(basic_string_view& v) noexcept
	{
		std::swap(m_data, v.m_data);
		std::swap(m_size, v.m_size);
	}

	constexpr int compare(size_type pos, size_type count,
						  const basic_string_view& str) const
	{
		return substr(pos, pos + count).compare(str);
	}

	constexpr int compare(const value_type* s) const
	{
		return compare(string_view(s));
	}

	constexpr int compare(const basic_string_view& 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;
	}

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

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

	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 bool ends_with(basic_string_view sv) const noexcept
	{
		return size() >= sv.size() &&
			   compare(size() - sv.size(), npos, sv) == 0;
	}

	constexpr size_type find_first_of( basic_string_view str, 
									  size_type pos = 0 ) const noexcept
	{
		for(auto it = cbegin() + pos; it < cend(); it++)
		{
			for(auto c = str.cbegin(); c < str.cend(); c++)
			{
				if(*c == *it) 
					return static_cast<size_t>(it - cbegin());
			}
		}

		return npos;
	}

	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_not_of(value_type c, size_type 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 const_reference back() const
	{
		return *(cend() - 1);
	}

	constexpr const_reference front() const
	{
		return *cbegin();
	}

	constexpr size_type max_size() const noexcept
	{
		return ~((size_type)0) / sizeof(value_type);
	}

private:

	const_pointer m_data;
	size_type m_size;
};

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

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

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


template<class C>
constexpr bool operator!=(std::type_identity_t<std::basic_string_view<C>> lhs,
						  std::basic_string_view<C> rhs) noexcept {
	if(lhs.size() != rhs.size())
		return true;
	return lhs.compare(rhs) != 0;
}

template<class C>
constexpr bool operator==(std::type_identity_t<std::basic_string_view<C>> lhs,
						  std::basic_string_view<C> rhs) noexcept {
	if(lhs.size() != rhs.size())
		return false;
	return lhs.compare(rhs) == 0;
}

template<class C>
constexpr bool operator!=(std::basic_string_view<C> lhs,
						  std::type_identity_t<std::basic_string_view<C>> rhs) noexcept {
	if(lhs.size() != rhs.size())
		return true;
	return lhs.compare(rhs) != 0;
}

template<class C>
constexpr bool operator==(std::basic_string_view<C> lhs,
						  std::type_identity_t<std::basic_string_view<C>> rhs) noexcept {
	if(lhs.size() != rhs.size())
		return false;
	return lhs.compare(rhs) == 0;
}

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

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

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

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

inline namespace literals {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wuser-defined-literals"
constexpr basic_string_view<char> operator "" sv(const char* str, size_t len) noexcept
{
	return basic_string_view<char>(str, len);
}
#pragma clang diagnostic pop
};
}
#endif

