#include <iostream>
#include <limits>

using namespace std;

template<typename Desired, typename Given>
constexpr Desired narrow_cast(Given arg) {
	static_assert(std::is_integral<Desired>::value, "Only integer types are supported");
	static_assert(std::is_integral<Given>::value, "Only integer types are supported");
	
	auto min = std::numeric_limits<Desired>::min();
	auto max = std::numeric_limits<Desired>::max();
	
	auto different_signs = std::is_signed<Given>::value xor std::is_signed<Desired>::value;
	if(sizeof(Desired) == sizeof(Given) && different_signs && arg < 0) {
		return 0;
	}
	
	return 
		min > arg? min:
		max < arg? max: 
		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;
}