#ifndef STD_ALGORITHM_H
#define STD_ALGORITHM_H

#include <utility>
#include <iterator>
#include <functional>

namespace std
{
template<typename InputIt, typename OutputIt>
constexpr OutputIt copy(InputIt first, InputIt last, OutputIt d_first)
{
	while(first != last)
	{
		*d_first++ = *first++;
	}
	return d_first;
}

template<typename BidirIt1, typename BidirIt2>
constexpr BidirIt2 copy_backward(BidirIt1 first, BidirIt1 last, BidirIt2 d_last)
{
	while(first != last)
	{
		*(--d_last) = *(--last);
	}
	return d_last;
}

template<typename InputIt, typename OutputIt, typename UnaryPredicate>
constexpr OutputIt copy_if(InputIt first, InputIt last, OutputIt d_first,
						   UnaryPredicate pred)
{
	while(first != last)
	{
		if(pred(*first)) *d_first++ = *first;
		first++;
	}
	return d_first;
}

template<typename InputIt, typename OutputIt>
constexpr OutputIt move(InputIt first, InputIt last, OutputIt d_first)
{
	while(first != last)
	{
		*d_first++ = std::move(*first++);
	}
	return d_first;
}

template<typename BidirIt1, typename BidirIt2>
constexpr BidirIt2 move_backward(BidirIt1 first, BidirIt1 last, BidirIt2 d_last)
{
	while(first != last)
	{
		*(--d_last) = std::move(*(--last));
	}
	return d_last;
}

template<typename ForwardIt, typename T>
constexpr void fill(ForwardIt first, ForwardIt last, const T& value)
{
	for(; first != last; ++first)
	{
		*first = value;
	}
}

template<typename InputIt1, typename InputIt2, typename BinaryPredicate>
constexpr bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2,
					 BinaryPredicate p)
{
	for(; first1 != last1; ++first1, ++first2)
	{
		if(!p(*first1, *first2))
		{
			return false;
		}
	}
	return true;
}

template<typename T>
constexpr const T& min(const T& a, const T& b)
{
	return (b < a) ? b : a;
}

template<typename T>
constexpr const T& max(const T& a, const T& b)
{
	return (b > a) ? b : a;
}

template<typename T, typename Compare>
constexpr const T& min(const T& a, const T& b, Compare comp)
{
	return (comp(b, a)) ? b : a;
}

template<typename T, typename Compare>
constexpr const T& clamp(const T& v, const T& lo, const T& hi, Compare cmp)
{
	return cmp(v, lo) ? lo : cmp(hi, v) ? hi : v;
}

template<typename T>
constexpr const T& clamp(const T& v, const T& lo, const T& hi)
{
	return clamp(v, lo, hi, less<T>{});
}

template<typename InputIt, typename T>
constexpr InputIt find(InputIt first, InputIt last, const T& value)
{
	for(; first != last; ++first)
	{
		if(*first == value)
		{
			return first;
		}
	}
	return last;
}

template<typename InputIt, typename UnaryPredicate>
constexpr InputIt find_if(InputIt first, InputIt last, UnaryPredicate p)
{
	for(; first != last; ++first)
	{
		if(p(*first))
		{
			return first;
		}
	}
	return last;
}

template<typename InputIt, typename UnaryPredicate>
constexpr InputIt find_if_not(InputIt first, InputIt last, UnaryPredicate q)
{
	for(; first != last; ++first)
	{
		if(!q(*first))
		{
			return first;
		}
	}
	return last;
}

template<typename ForwardIt>
constexpr ForwardIt max_element(ForwardIt first, ForwardIt last)
{
	if(first == last) return last;

	ForwardIt largest = first;
	++first;
	for(; first != last; ++first)
	{
		if(*largest < *first)
		{
			largest = first;
		}
	}
	return largest;
}

template<typename ForwardIt, typename Compare>
constexpr ForwardIt max_element(ForwardIt first, ForwardIt last, Compare comp)
{
	if(first == last) return last;

	ForwardIt largest = first;
	++first;
	for(; first != last; ++first)
	{
		if(comp(*largest, *first))
		{
			largest = first;
		}
	}
	return largest;
}

template<typename ForwardIt, typename T>
constexpr ForwardIt lower_bound(ForwardIt first, ForwardIt last, const T& value)
{
	ForwardIt it;
	typename iterator_traits<ForwardIt>::difference_type count, step;
	count = distance(first, last);

	while(count > 0)
	{
		it	 = first;
		step = count / 2;
		std::advance(it, step);
		if(*it < value)
		{
			first = ++it;
			count -= step + 1;
		}
		else
			count = step;
	}
	return first;
}

template<typename ForwardIt, typename T, typename Compare>
constexpr ForwardIt lower_bound(ForwardIt first, ForwardIt last, const T& value,
								Compare cmp)
{
	ForwardIt it;
	typename iterator_traits<ForwardIt>::difference_type count, step;
	count = distance(first, last);

	while(count > 0)
	{
		it	 = first;
		step = count / 2;
		advance(it, step);
		if(cmp(*it, value))
		{
			first = ++it;
			count -= step + 1;
		}
		else
			count = step;
	}
	return first;
}

template<typename ForwardIt1, typename ForwardIt2>
constexpr void iter_swap(ForwardIt1 a, ForwardIt2 b) 
{
	std::swap(*a, *b);
}

template<typename ForwardIt, typename UnaryPredicate>
constexpr ForwardIt partition(ForwardIt first, ForwardIt last, UnaryPredicate p)
{
	first = find_if_not(first, last, p);
	if(first == last) return first;

	for(ForwardIt i = next(first); i != last; ++i)
	{
		if(p(*i))
		{
			iter_swap(i, first);
			++first;
		}
	}
	return first;
}

namespace __pvt
{
template<typename It, typename Compare = less<>>
constexpr void quick_sort(It first, It last, Compare cmp = Compare{})
{
	auto N = distance(first, last);
	if(N <= 1) return;
	auto pivot = *next(first, N / 2);
	auto middle1 =
		partition(first, last,
				  [pivot, cmp](const auto& elem) { return cmp(elem, pivot); });
	auto middle2 =
		partition(middle1, last,
				  [pivot, cmp](const auto& elem) { return !cmp(pivot, elem); });
	quick_sort(first, middle1, cmp);
	quick_sort(middle2, last, cmp);
}
}
template<typename RandomIt, typename Compare>
constexpr void sort(RandomIt first, RandomIt last, Compare cmp)
{
	__pvt::quick_sort(first, last, cmp);
}

template<typename InputIt, typename T>
constexpr T accumulate(InputIt first, InputIt last, T init)
{
	for(; first != last; ++first)
	{
		init = std::move(init) + *first;
	}
	return init;
}

template<typename InputIt, typename T, typename BinaryOp>
constexpr T accumulate(InputIt first, InputIt last, T init, BinaryOp op)
{
	for(; first != last; ++first)
	{
		init = op(std::move(init), *first);
	}
	return init;
}

}


#endif