#ifndef INTERVAL_LIB_H
#define INTERVAL_LIB_H

#include <cmath>

template<class T>
class interval
{
	public:
		interval();
		interval(T const & v);
		interval(T const & l, T const & u);

		T const & lower() const;
		T const & upper() const;

	private:
		T low;
		T up;
};

// Constructors
template<class T> inline
interval<T>::interval()
{
}

template<class T> inline
interval<T>::interval(T const & l, T const & u) :
	low(l), up(u)
{
}

template<class T> inline
interval<T>::interval(T const & v) :
	low(v), up(v)
{
}


template<class T> inline
T const & interval<T>::lower() const
{
	return low;
}

template<class T> inline
T const & interval<T>::upper() const
{
	return up;
}

template<class T> inline
T width(interval<T> x)
{
	return x.upper() - x.lower();
}

// Arithmetic operations

// Unary operators
template<class T> inline 
interval<T> const & operator+(interval<T> const & x)
{
	return x;
}

template<class T> inline 
interval<T> operator-(interval<T> const & x)
{
	return interval<T>(-x.upper(), -x.lower());
}

// Binary operators
template<class T> inline
interval<T> operator+(interval<T> const & x, interval<T> const & y)
{
	return interval<T>(x.lower() + y.lower(), x.upper() + y.upper());
}

template<class T> inline 
interval<T> operator-(interval<T> const & x, interval<T> const & y)
{
	return interval<T>(x.lower() - y.upper(),
			x.upper() - y.lower());
}

/*
template<class T> inline
T min4(T a, T b, T c, T d)
{
	return fmin(fmin(a, b), fmin(c, d));
}

template<class T> inline
T max4(T a, T b, T c, T d)
{
	return fmax(fmax(a, b), fmax(c, d));
}
*/

template<class T> inline
T max4(T a, T b, T c, T d)
{
	if (a >= b && a >= c && a >= d)
		return a;

	if (b >= a && b >= c && b >= d)
		return b;

	if (c >= a && c >= b && c >= d)
		return c;

	return d;
}

template<class T> inline
T min4(T a, T b, T c, T d)
{
	if (a <= b && a <= c && a <= d)
		return a;

	if (b <= a && b <= c && b <= d)
		return b;

	if (c <= a && c <= b && c <= d)
		return c;

	return d;
}

template<class T> inline 
interval<T> operator*(interval<T> const & x, interval<T> const & y)
{
	return interval<T>(
			min4(
				x.lower() * y.lower(),
				x.lower() * y.upper(),
				x.upper() * y.lower(),
				x.upper() * y.upper()
				),
			max4(x.lower() * y.lower(),
				x.lower() * y.upper(),
				x.upper() * y.lower(),
				x.upper() * y.upper())
			);
}

#endif
