///////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2018 martysama0134. All rights reserved.
//
// This code is licensed under the MIT License (MIT).
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
///////////////////////////////////////////////////////////////////////////////
#pragma once

#include <algorithm>
#include <vector>

namespace msl
{
//! @brief range_iterator used for range
template <class T> class range_iterator
{
	T m_value_{};

public:
	explicit range_iterator(T value) : m_value_{value} {}
	T operator*() { return m_value_; }
	range_iterator<T> & operator++()
	{
		++m_value_;
		return *this;
	}
	range_iterator<T> & operator--()
	{
		--m_value_;
		return *this;
	}
	range_iterator<T> operator++(int)
	{
		++m_value_;
		return *this;
	}
	range_iterator<T> operator--(int)
	{
		--m_value_;
		return *this;
	}
	bool operator!=(range_iterator<T> & r) { return m_value_ != *r; }
};

//! @brief range mostly used in for-range (no memory allocation)
template <class T> class range
{
	range_iterator<T> m_min_{0};
	range_iterator<T> m_max_;

public:
	explicit range(T max) : m_max_{max} {}
	range(T min, T max) : m_min_{min}, m_max_{max} {}
	range_iterator<T> begin()
	{
		if (*m_min_ == *m_max_)
			return m_max_;
		return m_min_;
	}
	range_iterator<T> end() { return m_max_; }
};

using crange = range<char>;
using irange = range<int>;
using llrange = range<long long>;
using frange = range<float>;
using drange = range<double>;

//! @brief xrange mostly used in for-range (it uses std::vector with n=((max-min)/diff) elements)
template <class T> class xrange
{
	std::vector<T> m_vec_;

public:
	explicit xrange(T max) : xrange(0, max, 1) {}
	xrange(T min, T max) : xrange(min, max, 1) {}
	xrange(T min, T max, T diff)
	{
		if (min >= max)
			throw std::runtime_error("xrange min >= max");
		if (diff <= 0)
			throw std::runtime_error("xrange diff <= 0");
		// 4x faster than emplace_back
		m_vec_.resize(static_cast<std::size_t>((max - min) / diff));
		for (auto & e : m_vec_)
		{
			e += min;
			min += diff;
		}
	}
	auto begin() { return std::begin(m_vec_); }
	auto end() { return std::end(m_vec_); }
};

using xcrange = xrange<char>;
using xirange = xrange<int>;
using xllrange = xrange<long long>;
using xfrange = xrange<float>;
using xdrange = xrange<double>;
} // namespace msl