/*
 * compare.hpp
 *
 * This file is part of Algorithms library toolkit.
 * Copyright (C) 2017 Jan Travnicek (jan.travnicek@fit.cvut.cz)

 * Algorithms library toolkit 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.

 * Algorithms library toolkit 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 Algorithms library toolkit.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Created on: Apr 1, 2013
 * Author: Jan Travnicek
 */

#ifndef __COMPARE_HPP_
#define __COMPARE_HPP_

#include <utility>

namespace ext {

class CompareOperatorsBase {
};

template < class T >
class CompareOperators : public CompareOperatorsBase {
public:
	/**
	 * Compares this object with other for inequality.
	 *
	 * \returns true if this and other objects are not equal, false othervise
	 */
	bool operator !=( const T & other ) const {
		return static_cast < const T & > ( * this ).compare ( other ) != 0;
	}

	/**
	 * Compares this object with other for equality.
	 *
	 * \returns true if this and other objects are equal, false othervise
	 */
	bool operator ==( const T & other ) const {
		return static_cast < const T & > ( * this ).compare ( other ) == 0;
	}

	/**
	 * Determines whether this object is strictly smaller in relation to the other object.
	 *
	 * \returns true if this object is strictly smaller than other object are not equal, false othervise
	 */
	bool operator <( const T & other ) const {
		return static_cast < const T & > ( * this ).compare ( other ) < 0;
	}

	/**
	 * Determines whether this object is smaller or equal in relation to the other object.
	 *
	 * \returns true if this object is smaller or equal than other object are not equal, false othervise
	 */
	bool operator <=( const T & other ) const {
		return static_cast < const T & > ( * this ).compare ( other ) <= 0;
	}

	/**
	 * Determines whether this object is strictly bigger in relation to the other object.
	 *
	 * \returns true if this object is strictly bigger than other object are not equal, false othervise
	 */
	bool operator >( const T & other ) const {
		return static_cast < const T & > ( * this ).compare ( other ) > 0;
	}

	/**
	 * Determines whether this object is bigger or equal in relation to the other object.
	 *
	 * \returns true if this object is bigger or equal than other object are not equal, false othervise
	 */
	bool operator >=( const T & other ) const {
		return static_cast < const T & > ( * this ).compare ( other ) >= 0;
	}

};

/**
 * \brief
 * Predefinition of the compare structure implementing the three-way comparison
 *
 * \tparam T the type of compared value
 * \tparam Enable the place for enable_if construct if needed
 */
template < typename T, typename Enable = void >
struct compare;

/**
 * \brief
 * Specialisation of the compare structure implementing the three-way comparison for bools
 */
template < >
struct compare < bool > {

	/**
	 * \brief
	 * Implementation of the three-way comparison
	 *
	 * \param first the left operand of the comparison
	 * \param second the right operand of the comparison
	 *
	 * \return negative value of left < right, positive value if left > right, zero if left == right
	 */
	int operator ( ) ( bool first, bool second ) const {
		return static_cast < int > ( first ) - static_cast < int > ( second );
	}
};

/**
 * \brief
 * Specialisation of the compare structure implementing the three-way comparison for unsigned chars
 */
template < >
struct compare < unsigned char > {

	/**
	 * \brief
	 * Implementation of the three-way comparison
	 *
	 * \param first the left operand of the comparison
	 * \param second the right operand of the comparison
	 *
	 * \return negative value of left < right, positive value if left > right, zero if left == right
	 */
	int operator ( ) ( unsigned char first, unsigned char second ) const {
		return first - second;
	}
};

/**
 * \brief
 * Specialisation of the compare structure implementing the three-way comparison for signed chars
 */
template < >
struct compare < signed char > {

	/**
	 * \brief
	 * Implementation of the three-way comparison
	 *
	 * \param first the left operand of the comparison
	 * \param second the right operand of the comparison
	 *
	 * \return negative value of left < right, positive value if left > right, zero if left == right
	 */
	int operator ( ) ( signed char first, signed char second ) const {
		return first - second;
	}
};

/**
 * \brief
 * Specialisation of the compare structure implementing the three-way comparison for chars
 */
template < >
struct compare < char > {

	/**
	 * \brief
	 * Implementation of the three-way comparison
	 *
	 * \param first the left operand of the comparison
	 * \param second the right operand of the comparison
	 *
	 * \return negative value of left < right, positive value if left > right, zero if left == right
	 */
	int operator ( ) ( char first, char second ) const {
		return first - second;
	}
};

/**
 * \brief
 * Specialisation of the compare structure implementing the three-way comparison for unsigned shorts
 */
template < >
struct compare < unsigned short > {

	/**
	 * \brief
	 * Implementation of the three-way comparison
	 *
	 * \param first the left operand of the comparison
	 * \param second the right operand of the comparison
	 *
	 * \return negative value of left < right, positive value if left > right, zero if left == right
	 */
	int operator ( ) ( unsigned short first, unsigned short second ) const {
		return first - second;
	}
};

/**
 * \brief
 * Specialisation of the compare structure implementing the three-way comparison for signed shorts
 */
template < >
struct compare < signed short > {

	/**
	 * \brief
	 * Implementation of the three-way comparison
	 *
	 * \param first the left operand of the comparison
	 * \param second the right operand of the comparison
	 *
	 * \return negative value of left < right, positive value if left > right, zero if left == right
	 */
	int operator()(signed short first, signed short second) const {
		return first - second;
	}
};

/**
 * \brief
 * Specialisation of the compare structure implementing the three-way comparison for unsigned ints
 */
template < >
struct compare < unsigned int > {

	/**
	 * \brief
	 * Implementation of the three-way comparison
	 *
	 * \param first the left operand of the comparison
	 * \param second the right operand of the comparison
	 *
	 * \return negative value of left < right, positive value if left > right, zero if left == right
	 */
	int operator()(unsigned int first, unsigned int second) const {
		return first - second;
	}
};

/**
 * \brief
 * Specialisation of the compare structure implementing the three-way comparison for signed ints
 */
template < >
struct compare < signed int > {

	/**
	 * \brief
	 * Implementation of the three-way comparison
	 *
	 * \param first the left operand of the comparison
	 * \param second the right operand of the comparison
	 *
	 * \return negative value of left < right, positive value if left > right, zero if left == right
	 */
	int operator()(signed int first, signed int second) const {
		return first - second;
	}
};

/**
 * \brief
 * Specialisation of the compare structure implementing the three-way comparison for unsigned longs
 */
template < >
struct compare < unsigned long > {

	/**
	 * \brief
	 * Implementation of the three-way comparison
	 *
	 * \param first the left operand of the comparison
	 * \param second the right operand of the comparison
	 *
	 * \return negative value of left < right, positive value if left > right, zero if left == right
	 */
	int operator ( ) ( unsigned long first, unsigned long second ) const {
		if ( first < second )
			return -1;
		else if ( first > second )
			return 1;
		else
			return 0;
	}
};

/**
 * \brief
 * Specialisation of the compare structure implementing the three-way comparison for signed longs
 */
template < >
struct compare < signed long > {

	/**
	 * \brief
	 * Implementation of the three-way comparison
	 *
	 * \param first the left operand of the comparison
	 * \param second the right operand of the comparison
	 *
	 * \return negative value of left < right, positive value if left > right, zero if left == right
	 */
	int operator ( ) ( signed long first, signed long second ) const {
		if ( first < second )
			return -1;
		else if ( first > second )
			return 1;
		else
			return 0;
	}
};

/**
 * \brief
 * Specialisation of the compare structure implementing the three-way comparison for unsigned long longs
 */
template < >
struct compare < unsigned long long > {

	/**
	 * \brief
	 * Implementation of the three-way comparison
	 *
	 * \param first the left operand of the comparison
	 * \param second the right operand of the comparison
	 *
	 * \return negative value of left < right, positive value if left > right, zero if left == right
	 */
	int operator ( ) ( unsigned long long first, unsigned long long second ) const {
		if ( first < second )
			return -1;
		else if ( first > second )
			return 1;
		else
			return 0;
	}
};

/**
 * \brief
 * Specialisation of the compare structure implementing the three-way comparison for signed long longs
 */
template < >
struct compare < signed long long > {

	/**
	 * \brief
	 * Implementation of the three-way comparison
	 *
	 * \param first the left operand of the comparison
	 * \param second the right operand of the comparison
	 *
	 * \return negative value of left < right, positive value if left > right, zero if left == right
	 */
	int operator ( ) ( signed long long first, signed long long second ) const {
		if ( first < second )
			return -1;
		else if ( first > second )
			return 1;
		else
			return 0;
	}
};

/**
 * \brief
 * Specialisation of the compare structure implementing the three-way comparison for floats
 */
template < >
struct compare < float > {

	/**
	 * \brief
	 * Implementation of the three-way comparison
	 *
	 * \param first the left operand of the comparison
	 * \param second the right operand of the comparison
	 *
	 * \return negative value of left < right, positive value if left > right, zero if left == right
	 */
	int operator ( ) ( float first, float second ) const {
		if ( first < second )
			return -1;
		else if ( first > second )
			return 1;
		else
			return 0;
	}
};

/**
 * \brief
 * Specialisation of the compare structure implementing the three-way comparison for doubles
 */
template < >
struct compare < double > {

	/**
	 * \brief
	 * Implementation of the three-way comparison
	 *
	 * \param first the left operand of the comparison
	 * \param second the right operand of the comparison
	 *
	 * \return negative value of left < right, positive value if left > right, zero if left == right
	 */
	int operator ( ) ( double first, double second ) const {
		if ( first < second )
			return -1;
		else if ( first > second )
			return 1;
		else
			return 0;
	}
};

/**
 * \brief
 * Specialisation of the compare structure implementing the three-way comparison for long doubles
 */
template < >
struct compare < long double > {

	/**
	 * \brief
	 * Implementation of the three-way comparison
	 *
	 * \param first the left operand of the comparison
	 * \param second the right operand of the comparison
	 *
	 * \return negative value of left < right, positive value if left > right, zero if left == right
	 */
	int operator ( ) ( long double first, long double second ) const {
		if ( first < second )
			return -1;
		else if ( first > second )
			return 1;
		else
			return 0;
	}
};

/**
 * \brief
 * Specialisation of the compare structure implementing the three-way comparison for pointers
 *
 * \tparam T the type of compared pointer
 */
template < class T >
struct compare < T * > {

	/**
	 * \brief
	 * Implementation of the three-way comparison
	 *
	 * \param first the left operand of the comparison
	 * \param second the right operand of the comparison
	 *
	 * \return negative value of left < right, positive value if left > right, zero if left == right
	 */
	int operator ( ) ( T * const first, T * const second ) const {
		static compare < typename std::decay < T >::type > comp;
		return comp ( * first, * second );
	}
};

/**
 * \brief
 * Specialisation of the compare structure implementing the three-way comparison for lvalue references
 *
 * \tparam T the type of compared reference
 */
template < class T >
struct compare < T & > {

	/**
	 * \brief
	 * Implementation of the three-way comparison
	 *
	 * \param first the left operand of the comparison
	 * \param second the right operand of the comparison
	 *
	 * \return negative value of left < right, positive value if left > right, zero if left == right
	 */
	int operator ( ) ( const T & first, const T & second ) const {
		static compare < typename std::decay < T >::type > comp;
		return comp ( first, second );
	}
};

/**
 * \brief
 * Specialisation of the compare structure implementing the three-way comparison for rvalue references
 *
 * \tparam T the type of compared reference
 */
template < class T >
struct compare < T && > {

	/**
	 * \brief
	 * Implementation of the three-way comparison
	 *
	 * \param first the left operand of the comparison
	 * \param second the right operand of the comparison
	 *
	 * \return negative value of left < right, positive value if left > right, zero if left == right
	 */
	int operator ( ) ( const T & first, const T & second ) const {
		static compare < typename std::decay < T >::type > comp;
		return comp ( first, second );
	}
};

/**
 * Helper for comparing everything that inherits from CommonBaseBase.
 *
 * \tparam T the type to compare
 */
template < class T >
struct compare < T, typename std::enable_if < std::is_base_of < CompareOperatorsBase, typename std::decay < T >::type >::value >::type > {

	/**
	 * Compare operator that determines relation between first and second object.
	 *
	 * \returns value < 0 if first < second
	 *          value == 0 if first == second
	 *          value > 0 if first > second
	 */
	int operator()(const T& first, const T& second) const {
		return first.compare(second);
	}
};

} /* namespace ext */

#endif /* __COMPARE_HPP_ */