#include <iostream>
#include <limits>

using namespace std;

#if defined(_MSC_VER) && _MSC_VER < 1900
#define CONST_EXPPR
#else
#define CONST_EXPPR constexpr
#endif

	/*
	 * Helper tempaltes which returns common minimum and maximum values for integer types
	 */
	template<typename DestinationType, typename SourceType, bool commonSignes, bool DestSourceEqual>
	struct HelperCommonLimits
	{
		static CONST_EXPPR SourceType max()
		{
			return static_cast<SourceType>(std::numeric_limits<DestinationType>::max());
		}
		static CONST_EXPPR SourceType min()
		{
			return static_cast<SourceType>(std::numeric_limits<DestinationType>::min());
		}
	};
	
	template<typename DestinationType, typename SourceType>
	struct HelperCommonLimits<DestinationType, SourceType, false, false>
	{
		static CONST_EXPPR SourceType max()
		{
			return static_cast<SourceType>(std::numeric_limits<DestinationType>::max());
		}
		static CONST_EXPPR SourceType min()
		{
			return 0;
		}
	};
	
	template<typename DestinationType, typename SourceType>
	struct HelperCommonLimits<DestinationType, SourceType, false, true>
	{
		static CONST_EXPPR SourceType max()
		{
			return static_cast<SourceType>(std::numeric_limits<typename std::make_signed<DestinationType>::type>::max());
		}
		static CONST_EXPPR SourceType min()
		{
			return 0;
		}
	};
	
	/*
	 * Helper template used to detect if size on destination type is bigger.
	 * If signs are same than simple casting is enough.
	 * if sings are different zero minimum is applied
	 */
	template<typename DestinationType, typename SourceType, bool singedToUnsigned, bool DestIsBigger>
	struct HelperNorrowCast
	{
		static CONST_EXPPR DestinationType Cast(SourceType arg)
		{
			using HelperType = HelperCommonLimits<DestinationType,
												SourceType,
												std::is_signed<SourceType>::value == std::is_signed<DestinationType>::value,
												(sizeof(DestinationType) == sizeof(SourceType))>;
			
			return static_cast<DestinationType>(
												HelperType::min() >= arg ? HelperType::min() :
												HelperType::max() <= arg ? HelperType::max() :
												arg);
		}
	};
	
	template<typename DestinationType, typename SourceType>
	struct HelperNorrowCast<DestinationType, SourceType, false, true>
	{
		static CONST_EXPPR DestinationType Cast(SourceType arg)
		{
			return static_cast<DestinationType>(arg);
		}
	};
	
	template<typename DestinationType, typename SourceType>
	struct HelperNorrowCast<DestinationType, SourceType, true, true>
	{
		static CONST_EXPPR DestinationType Cast(SourceType arg)
		{
			return static_cast<DestinationType>(arg >= 0 ? arg : 0);
		}
	};

	/**
	 * Used to do integer casting in such way that if argument value exceeds destination type range
	 * result value is returns respctivly maximum or minimum values of result type.
	 */
	template<typename DestinationType, typename SourceType>
	CONST_EXPPR DestinationType narrow_cast(SourceType arg)
	{
		static_assert(std::is_integral<DestinationType>::value, "Only integer types are supported");
		static_assert(std::is_integral<SourceType>::value, "Only integer types are supported");
		
		return HelperNorrowCast<DestinationType, SourceType,
				std::is_signed<SourceType>::value && std::is_unsigned<DestinationType>::value,
				(sizeof(DestinationType) > sizeof(SourceType))>::Cast(arg);
	}


#define EXPECT_EQ(x, y) if (x != y) cout << "Failed test for " << #x \
		<< "\nexpected: " << y \
		<< "\n  actual: " << x << endl; else

int main() {
	// to smaller type signed to signed
	EXPECT_EQ(narrow_cast<short>(100000), 32767);
	EXPECT_EQ(narrow_cast<short>(-100000), -32768);
	EXPECT_EQ(narrow_cast<short>(0), 0);
	EXPECT_EQ(narrow_cast<short>(-1), -1);
	
	// to smaller type unsigned to unsigned
	EXPECT_EQ(narrow_cast<unsigned short>(100000u), 65535u);
	EXPECT_EQ(narrow_cast<unsigned short>(60000u),  60000u);
	EXPECT_EQ(narrow_cast<unsigned short>(0u), 0u);
	EXPECT_EQ(narrow_cast<unsigned short>(1u), 1u);
	
	// to smaller type signed to signed to unsigned
	EXPECT_EQ(narrow_cast<unsigned short>(100000), 65535);
	EXPECT_EQ(narrow_cast<unsigned short>(-100000), 0);
	EXPECT_EQ(narrow_cast<unsigned short>(0), 0);
	EXPECT_EQ(narrow_cast<unsigned short>(-1), 0);
	
	// to smaller type unsigned to signed
	EXPECT_EQ(narrow_cast<short>(100000u), 32767);
	EXPECT_EQ(narrow_cast<short>(60000u), 32767);
	EXPECT_EQ(narrow_cast<short>(0u), 0);
	EXPECT_EQ(narrow_cast<short>(1u), 1);
	
	// to same size type signed to signed ////
	EXPECT_EQ(narrow_cast<int>(0x7fffffff), 0x7fffffff);
	EXPECT_EQ(narrow_cast<int>(-2147483648), -2147483648);
	EXPECT_EQ(narrow_cast<int>(0), 0);
	EXPECT_EQ(narrow_cast<int>(-1), -1);
	
	// to same size type unsigned to unsigned
	EXPECT_EQ(narrow_cast<unsigned int>(0xffffffffu), 0xffffffffu);
	EXPECT_EQ(narrow_cast<unsigned int>(60000u),  60000u);
	EXPECT_EQ(narrow_cast<unsigned int>(0u), 0u);
	EXPECT_EQ(narrow_cast<unsigned int>(1u), 1u);
	
	// to same size type signed to signed to unsigned
	EXPECT_EQ(narrow_cast<unsigned int>(0x7fffffff), 0x7fffffffu);
	EXPECT_EQ(narrow_cast<unsigned int>(-2147483648), 0u);
	EXPECT_EQ(narrow_cast<unsigned int>(0), 0u);
	EXPECT_EQ(narrow_cast<unsigned int>(-1), 0u);
	
	// to same size type unsigned to signed
	EXPECT_EQ(narrow_cast<int>(0xffffffffu), 0x7fffffff);
	EXPECT_EQ(narrow_cast<int>(0x7fffffffu), 0x7fffffff);
	EXPECT_EQ(narrow_cast<int>(0x80000000u), 0x7fffffff);
	EXPECT_EQ(narrow_cast<int>(0u), 0);
	EXPECT_EQ(narrow_cast<int>(1u), 1);
	
	// to bigger size type signed to signed ////
	EXPECT_EQ(narrow_cast<int64_t>(0x7fffffff), 0x7fffffffll);
	EXPECT_EQ(narrow_cast<int64_t>(-2147483648), -2147483648ll);
	EXPECT_EQ(narrow_cast<int64_t>(0), 0);
	EXPECT_EQ(narrow_cast<int64_t>(-1), -1ll);
	
	// to bigger size type unsigned to unsigned
	EXPECT_EQ(narrow_cast<uint64_t>(0xffffffffu), 0xffffffffull);
	EXPECT_EQ(narrow_cast<uint64_t>(60000u),  60000ull);
	EXPECT_EQ(narrow_cast<uint64_t>(0u), 0ull);
	EXPECT_EQ(narrow_cast<uint64_t>(1u), 1ull);
	
	// to bigger size type signed to signed to unsigned
	EXPECT_EQ(narrow_cast<uint64_t>(0x7fffffff), 0x7fffffffull);
	EXPECT_EQ(narrow_cast<uint64_t>(-2147483648), 0ull);
	EXPECT_EQ(narrow_cast<uint64_t>(0), 0ull);
	EXPECT_EQ(narrow_cast<uint64_t>(-1), 0ull);
	
	// to bigger size type unsigned to signed
	EXPECT_EQ(narrow_cast<int64_t>(0xffffffffu), 0xffffffffll);
	EXPECT_EQ(narrow_cast<int64_t>(0x7fffffffu), 0x7fffffffll);
	EXPECT_EQ(narrow_cast<int64_t>(0x80000000u), 0x80000000ll);
	EXPECT_EQ(narrow_cast<int64_t>(0u), 0ll);
	EXPECT_EQ(narrow_cast<int64_t>(1u), 1ll);

	return 0;
}