#pragma once #include #include #include #include #include #include #include #include "Platform.h" using schar = signed char; using uchar = unsigned char; using ushort = unsigned short; using uint = unsigned int; using ulong = unsigned long; using ullong = unsigned long long; using llong = long long; using u8 = std::uint8_t; using u16 = std::uint16_t; using u32 = std::uint32_t; using u64 = std::uint64_t; using s8 = std::int8_t; using s16 = std::int16_t; using s32 = std::int32_t; using s64 = std::int64_t; // Specialization with static constexpr pair map[] member expected template struct bijective; template struct atomic_storage; template struct atomic_add; template struct atomic_sub; template struct atomic_and; template struct atomic_or; template struct atomic_xor; template struct atomic_pre_inc; template struct atomic_post_inc; template struct atomic_pre_dec; template struct atomic_post_dec; template struct atomic_test_and_set; template struct atomic_test_and_reset; template struct atomic_test_and_complement; template class atomic_t; namespace fmt { template struct unveil; } // TODO: replace with std::void_t when available namespace void_details { template struct make_void { using type = void; }; } template using void_t = typename void_details::make_void::type; // Extract T::simple_type if available, remove cv qualifiers template struct simple_type_helper { using type = typename std::remove_cv::type; }; template struct simple_type_helper> { using type = typename T::simple_type; }; template using simple_t = typename simple_type_helper::type; // Bool type equivalent class b8 { std::uint8_t m_value; public: b8() = default; constexpr b8(bool value) : m_value(value) { } constexpr operator bool() const { return m_value != 0; } }; // Bool wrapper for restricting bool result conversions struct explicit_bool_t { const bool value; constexpr explicit_bool_t(bool value) : value(value) { } explicit constexpr operator bool() const { return value; } }; #ifndef _MSC_VER using u128 = __uint128_t; using s128 = __int128_t; #else #include "intrin.h" // Unsigned 128-bit integer implementation (TODO) struct alignas(16) u128 { std::uint64_t lo, hi; u128() = default; constexpr u128(std::uint64_t l) : lo(l) , hi(0) { } friend u128 operator +(const u128& l, const u128& r) { u128 value; _addcarry_u64(_addcarry_u64(0, r.lo, l.lo, &value.lo), r.hi, l.hi, &value.hi); return value; } friend u128 operator +(const u128& l, std::uint64_t r) { u128 value; _addcarry_u64(_addcarry_u64(0, r, l.lo, &value.lo), l.hi, 0, &value.hi); return value; } friend u128 operator +(std::uint64_t l, const u128& r) { u128 value; _addcarry_u64(_addcarry_u64(0, r.lo, l, &value.lo), 0, r.hi, &value.hi); return value; } friend u128 operator -(const u128& l, const u128& r) { u128 value; _subborrow_u64(_subborrow_u64(0, r.lo, l.lo, &value.lo), r.hi, l.hi, &value.hi); return value; } friend u128 operator -(const u128& l, std::uint64_t r) { u128 value; _subborrow_u64(_subborrow_u64(0, r, l.lo, &value.lo), 0, l.hi, &value.hi); return value; } friend u128 operator -(std::uint64_t l, const u128& r) { u128 value; _subborrow_u64(_subborrow_u64(0, r.lo, l, &value.lo), r.hi, 0, &value.hi); return value; } u128 operator +() const { return *this; } u128 operator -() const { u128 value; _subborrow_u64(_subborrow_u64(0, lo, 0, &value.lo), hi, 0, &value.hi); return value; } u128& operator ++() { _addcarry_u64(_addcarry_u64(0, 1, lo, &lo), 0, hi, &hi); return *this; } u128 operator ++(int) { u128 value = *this; _addcarry_u64(_addcarry_u64(0, 1, lo, &lo), 0, hi, &hi); return value; } u128& operator --() { _subborrow_u64(_subborrow_u64(0, 1, lo, &lo), 0, hi, &hi); return *this; } u128 operator --(int) { u128 value = *this; _subborrow_u64(_subborrow_u64(0, 1, lo, &lo), 0, hi, &hi); return value; } u128 operator ~() const { u128 value; value.lo = ~lo; value.hi = ~hi; return value; } friend u128 operator &(const u128& l, const u128& r) { u128 value; value.lo = l.lo & r.lo; value.hi = l.hi & r.hi; return value; } friend u128 operator |(const u128& l, const u128& r) { u128 value; value.lo = l.lo | r.lo; value.hi = l.hi | r.hi; return value; } friend u128 operator ^(const u128& l, const u128& r) { u128 value; value.lo = l.lo ^ r.lo; value.hi = l.hi ^ r.hi; return value; } u128& operator +=(const u128& r) { _addcarry_u64(_addcarry_u64(0, r.lo, lo, &lo), r.hi, hi, &hi); return *this; } u128& operator +=(uint64_t r) { _addcarry_u64(_addcarry_u64(0, r, lo, &lo), 0, hi, &hi); return *this; } u128& operator &=(const u128& r) { lo &= r.lo; hi &= r.hi; return *this; } u128& operator |=(const u128& r) { lo |= r.lo; hi |= r.hi; return *this; } u128& operator ^=(const u128& r) { lo ^= r.lo; hi ^= r.hi; return *this; } }; // Signed 128-bit integer implementation (TODO) struct alignas(16) s128 { std::uint64_t lo; std::int64_t hi; s128() = default; constexpr s128(std::int64_t l) : hi(l >> 63) , lo(l) { } constexpr s128(std::uint64_t l) : hi(0) , lo(l) { } }; #endif namespace std { /* Let's hack. */ template<> struct is_integral : true_type { }; template<> struct is_integral : true_type { }; template<> struct make_unsigned { using type = u128; }; template<> struct make_unsigned { using type = u128; }; template<> struct make_signed { using type = s128; }; template<> struct make_signed { using type = s128; }; } static_assert(std::is_arithmetic::value && std::is_integral::value && alignof(u128) == 16 && sizeof(u128) == 16, "Wrong u128 implementation"); static_assert(std::is_arithmetic::value && std::is_integral::value && alignof(s128) == 16 && sizeof(s128) == 16, "Wrong s128 implementation"); union alignas(2) f16 { u16 _u16; u8 _u8[2]; explicit f16(u16 raw) { _u16 = raw; } explicit operator float() const { // See http://stackoverflow.com/a/26779139 // The conversion doesn't handle NaN/Inf u32 raw = ((_u16 & 0x8000) << 16) | // Sign (just moved) (((_u16 & 0x7c00) + 0x1C000) << 13) | // Exponent ( exp - 15 + 127) ((_u16 & 0x03FF) << 13); // Mantissa return (float&)raw; } }; using f32 = float; using f64 = double; struct ignore { template ignore(T) { } }; // Allows to define integer convertible to multiple enum types template struct multicast : multicast { static_assert(std::is_enum::value, "multicast<> error: invalid conversion type (enum type expected)"); multicast() = default; template constexpr multicast(const UT& value) : multicast(value) , m_value{ value } // Forbid narrowing { } constexpr operator T() const { // Cast to enum type return static_cast(m_value); } private: std::underlying_type_t m_value; }; // Recursion terminator template<> struct multicast { multicast() = default; template constexpr multicast(const UT& value) { } }; // Small bitset for enum class types with available values [0, bitsize). // T must be either enum type or convertible to (registered with via simple_t). // Internal representation is single value of type T. template struct mset { using type = simple_t; using under = std::underlying_type_t; static constexpr auto bitsize = sizeof(type) * CHAR_BIT; mset() = default; constexpr mset(type _enum_const) : m_value(static_cast(shift(_enum_const))) { } constexpr mset(under raw_value, const std::nothrow_t&) : m_value(static_cast(raw_value)) { } // Get underlying value constexpr under _value() const { return static_cast(m_value); } explicit constexpr operator bool() const { return _value() ? true : false; } mset& operator +=(mset rhs) { return *this = { _value() | rhs._value(), std::nothrow }; } mset& operator -=(mset rhs) { return *this = { _value() & ~rhs._value(), std::nothrow }; } mset& operator &=(mset rhs) { return *this = { _value() & rhs._value(), std::nothrow }; } mset& operator ^=(mset rhs) { return *this = { _value() ^ rhs._value(), std::nothrow }; } friend constexpr mset operator +(mset lhs, mset rhs) { return{ lhs._value() | rhs._value(), std::nothrow }; } friend constexpr mset operator -(mset lhs, mset rhs) { return{ lhs._value() & ~rhs._value(), std::nothrow }; } friend constexpr mset operator &(mset lhs, mset rhs) { return{ lhs._value() & rhs._value(), std::nothrow }; } friend constexpr mset operator ^(mset lhs, mset rhs) { return{ lhs._value() ^ rhs._value(), std::nothrow }; } bool test(mset rhs) const { const under v = _value(); const under s = rhs._value(); return (v & s) != 0; } bool test_and_set(mset rhs) { const under v = _value(); const under s = rhs._value(); *this = { v | s, std::nothrow }; return (v & s) != 0; } bool test_and_reset(mset rhs) { const under v = _value(); const under s = rhs._value(); *this = { v & ~s, std::nothrow }; return (v & s) != 0; } bool test_and_complement(mset rhs) { const under v = _value(); const under s = rhs._value(); *this = { v ^ s, std::nothrow }; return (v & s) != 0; } private: [[noreturn]] static under xrange() { throw std::out_of_range("mset<>: bit out of range"); } static constexpr under shift(const T& value) { return static_cast(value) < bitsize ? static_cast(1) << static_cast(value) : xrange(); } T m_value; }; template constexpr RT to_mset() { return RT{}; } // Fold enum constants into mset<> template::value, mset, T>> constexpr RT to_mset(Arg&& _enum_const, Args&&... args) { return RT{ std::forward(_enum_const) } + to_mset(std::forward(args)...); } template struct atomic_add, CT, std::enable_if_t::value>> { using under = typename mset::under; static force_inline mset op1(mset& left, mset right) { return{ atomic_storage::fetch_or(reinterpret_cast(left), right._value()), std::nothrow }; } static constexpr auto fetch_op = &op1; static force_inline mset op2(mset& left, mset right) { return{ atomic_storage::or_fetch(reinterpret_cast(left), right._value()), std::nothrow }; } static constexpr auto op_fetch = &op2; static constexpr auto atomic_op = &op2; }; template struct atomic_sub, CT, std::enable_if_t::value>> { using under = typename mset::under; static force_inline mset op1(mset& left, mset right) { return{ atomic_storage::fetch_and(reinterpret_cast(left), ~right._value()), std::nothrow }; } static constexpr auto fetch_op = &op1; static force_inline mset op2(mset& left, mset right) { return{ atomic_storage::and_fetch(reinterpret_cast(left), ~right._value()), std::nothrow }; } static constexpr auto op_fetch = &op2; static constexpr auto atomic_op = &op2; }; template struct atomic_and, CT, std::enable_if_t::value>> { using under = typename mset::under; static force_inline mset op1(mset& left, mset right) { return{ atomic_storage::fetch_and(reinterpret_cast(left), right._value()), std::nothrow }; } static constexpr auto fetch_op = &op1; static force_inline mset op2(mset& left, mset right) { return{ atomic_storage::and_fetch(reinterpret_cast(left), right._value()), std::nothrow }; } static constexpr auto op_fetch = &op2; static constexpr auto atomic_op = &op2; }; template struct atomic_xor, CT, std::enable_if_t::value>> { using under = typename mset::under; static force_inline mset op1(mset& left, mset right) { return{ atomic_storage::fetch_xor(reinterpret_cast(left), right._value()), std::nothrow }; } static constexpr auto fetch_op = &op1; static force_inline mset op2(mset& left, mset right) { return{ atomic_storage::xor_fetch(reinterpret_cast(left), right._value()), std::nothrow }; } static constexpr auto op_fetch = &op2; static constexpr auto atomic_op = &op2; }; template struct atomic_test_and_set, T, std::enable_if_t::value>> { using under = typename mset::under; static force_inline bool _op(mset& left, const T& value) { return atomic_storage::bts(reinterpret_cast(left), static_cast(value)); } static constexpr auto atomic_op = &_op; }; template struct atomic_test_and_reset, T, std::enable_if_t::value>> { using under = typename mset::under; static force_inline bool _op(mset& left, const T& value) { return atomic_storage::btr(reinterpret_cast(left), static_cast(value)); } static constexpr auto atomic_op = &_op; }; template struct atomic_test_and_complement, T, std::enable_if_t::value>> { using under = typename mset::under; static force_inline bool _op(mset& left, const T& value) { return atomic_storage::btc(reinterpret_cast(left), static_cast(value)); } static constexpr auto atomic_op = &_op; }; template T2 bijective_find(const T& left, const DT& def = {}) { for (const auto& pair : bijective::map) { if (pair.first == left) { return pair.second; } } return def; } template struct size2_base { T width, height; constexpr size2_base() : width{}, height{} { } constexpr size2_base(T width, T height) : width{ width }, height{ height } { } constexpr size2_base(const size2_base& rhs) : width{ rhs.width }, height{ rhs.height } { } constexpr size2_base operator -(const size2_base& rhs) const { return{ width - rhs.width, height - rhs.height }; } constexpr size2_base operator -(T rhs) const { return{ width - rhs, height - rhs }; } constexpr size2_base operator +(const size2_base& rhs) const { return{ width + rhs.width, height + rhs.height }; } constexpr size2_base operator +(T rhs) const { return{ width + rhs, height + rhs }; } constexpr size2_base operator /(const size2_base& rhs) const { return{ width / rhs.width, height / rhs.height }; } constexpr size2_base operator /(T rhs) const { return{ width / rhs, height / rhs }; } constexpr size2_base operator *(const size2_base& rhs) const { return{ width * rhs.width, height * rhs.height }; } constexpr size2_base operator *(T rhs) const { return{ width * rhs, height * rhs }; } size2_base& operator -=(const size2_base& rhs) { width -= rhs.width; height -= rhs.height; return *this; } size2_base& operator -=(T rhs) { width -= rhs; height -= rhs; return *this; } size2_base& operator +=(const size2_base& rhs) { width += rhs.width; height += rhs.height; return *this; } size2_base& operator +=(T rhs) { width += rhs; height += rhs; return *this; } size2_base& operator /=(const size2_base& rhs) { width /= rhs.width; height /= rhs.height; return *this; } size2_base& operator /=(T rhs) { width /= rhs; height /= rhs; return *this; } size2_base& operator *=(const size2_base& rhs) { width *= rhs.width; height *= rhs.height; return *this; } size2_base& operator *=(T rhs) { width *= rhs; height *= rhs; return *this; } constexpr bool operator == (const size2_base& rhs) const { return width == rhs.width && height == rhs.height; } constexpr bool operator != (const size2_base& rhs) const { return width != rhs.width || height != rhs.height; } template constexpr operator size2_base() const { return{ (NT)width, (NT)height }; } }; template struct position1_base { T x; position1_base operator -(const position1_base& rhs) const { return{ x - rhs.x }; } position1_base operator -(T rhs) const { return{ x - rhs }; } position1_base operator +(const position1_base& rhs) const { return{ x + rhs.x }; } position1_base operator +(T rhs) const { return{ x + rhs }; } template position1_base operator *(RhsT rhs) const { return{ T(x * rhs) }; } position1_base operator *(const position1_base& rhs) const { return{ T(x * rhs.x) }; } template position1_base operator /(RhsT rhs) const { return{ x / rhs }; } position1_base operator /(const position1_base& rhs) const { return{ x / rhs.x }; } position1_base& operator -=(const position1_base& rhs) { x -= rhs.x; return *this; } position1_base& operator -=(T rhs) { x -= rhs; return *this; } position1_base& operator +=(const position1_base& rhs) { x += rhs.x; return *this; } position1_base& operator +=(T rhs) { x += rhs; return *this; } template position1_base& operator *=(RhsT rhs) const { x *= rhs; return *this; } position1_base& operator *=(const position1_base& rhs) const { x *= rhs.x; return *this; } template position1_base& operator /=(RhsT rhs) const { x /= rhs; return *this; } position1_base& operator /=(const position1_base& rhs) const { x /= rhs.x; return *this; } bool operator ==(const position1_base& rhs) const { return x == rhs.x; } bool operator ==(T rhs) const { return x == rhs; } bool operator !=(const position1_base& rhs) const { return !(*this == rhs); } bool operator !=(T rhs) const { return !(*this == rhs); } template operator position1_base() const { return{ (NT)x }; } double distance(const position1_base& to) { return abs(x - to.x); } }; template struct position2_base { T x, y; constexpr position2_base() : x{}, y{} { } constexpr position2_base(T x, T y) : x{ x }, y{ y } { } constexpr position2_base(const position2_base& rhs) : x{ rhs.x }, y{ rhs.y } { } constexpr bool operator >(const position2_base& rhs) const { return x > rhs.x && y > rhs.y; } constexpr bool operator >(T rhs) const { return x > rhs && y > rhs; } constexpr bool operator <(const position2_base& rhs) const { return x < rhs.x && y < rhs.y; } constexpr bool operator <(T rhs) const { return x < rhs && y < rhs; } constexpr bool operator >=(const position2_base& rhs) const { return x >= rhs.x && y >= rhs.y; } constexpr bool operator >=(T rhs) const { return x >= rhs && y >= rhs; } constexpr bool operator <=(const position2_base& rhs) const { return x <= rhs.x && y <= rhs.y; } constexpr bool operator <=(T rhs) const { return x <= rhs && y <= rhs; } constexpr position2_base operator -(const position2_base& rhs) const { return{ x - rhs.x, y - rhs.y }; } constexpr position2_base operator -(T rhs) const { return{ x - rhs, y - rhs }; } constexpr position2_base operator +(const position2_base& rhs) const { return{ x + rhs.x, y + rhs.y }; } constexpr position2_base operator +(T rhs) const { return{ x + rhs, y + rhs }; } template constexpr position2_base operator *(RhsT rhs) const { return{ T(x * rhs), T(y * rhs) }; } constexpr position2_base operator *(const position2_base& rhs) const { return{ T(x * rhs.x), T(y * rhs.y) }; } template constexpr position2_base operator /(RhsT rhs) const { return{ x / rhs, y / rhs }; } constexpr position2_base operator /(const position2_base& rhs) const { return{ x / rhs.x, y / rhs.y }; } constexpr position2_base operator /(const size2_base& rhs) const { return{ x / rhs.width, y / rhs.height }; } position2_base& operator -=(const position2_base& rhs) { x -= rhs.x; y -= rhs.y; return *this; } position2_base& operator -=(T rhs) { x -= rhs; y -= rhs; return *this; } position2_base& operator +=(const position2_base& rhs) { x += rhs.x; y += rhs.y; return *this; } position2_base& operator +=(T rhs) { x += rhs; y += rhs; return *this; } template position2_base& operator *=(RhsT rhs) { x *= rhs; y *= rhs; return *this; } position2_base& operator *=(const position2_base& rhs) { x *= rhs.x; y *= rhs.y; return *this; } template position2_base& operator /=(RhsT rhs) { x /= rhs; y /= rhs; return *this; } position2_base& operator /=(const position2_base& rhs) { x /= rhs.x; y /= rhs.y; return *this; } constexpr bool operator ==(const position2_base& rhs) const { return x == rhs.x && y == rhs.y; } constexpr bool operator ==(T rhs) const { return x == rhs && y == rhs; } constexpr bool operator !=(const position2_base& rhs) const { return !(*this == rhs); } constexpr bool operator !=(T rhs) const { return !(*this == rhs); } template constexpr operator position2_base() const { return{ (NT)x, (NT)y }; } double distance(const position2_base& to) const { return std::sqrt(double((x - to.x) * (x - to.x) + (y - to.y) * (y - to.y))); } }; template struct position3_base { T x, y, z; /* position3_base() : x{}, y{}, z{} { } position3_base(T x, T y, T z) : x{ x }, y{ y }, z{ z } { } */ position3_base operator -(const position3_base& rhs) const { return{ x - rhs.x, y - rhs.y, z - rhs.z }; } position3_base operator -(T rhs) const { return{ x - rhs, y - rhs, z - rhs }; } position3_base operator +(const position3_base& rhs) const { return{ x + rhs.x, y + rhs.y, z + rhs.z }; } position3_base operator +(T rhs) const { return{ x + rhs, y + rhs, z + rhs }; } position3_base& operator -=(const position3_base& rhs) { x -= rhs.x; y -= rhs.y; z -= rhs.z; return *this; } position3_base& operator -=(T rhs) { x -= rhs; y -= rhs; z -= rhs; return *this; } position3_base& operator +=(const position3_base& rhs) { x += rhs.x; y += rhs.y; z += rhs.z; return *this; } position3_base& operator +=(T rhs) { x += rhs; y += rhs; z += rhs; return *this; } bool operator ==(const position3_base& rhs) const { return x == rhs.x && y == rhs.y && z == rhs.z; } bool operator ==(T rhs) const { return x == rhs && y == rhs && z == rhs; } bool operator !=(const position3_base& rhs) const { return !(*this == rhs); } bool operator !=(T rhs) const { return !(*this == rhs); } template operator position3_base() const { return{ (NT)x, (NT)y, (NT)z }; } }; template struct position4_base { T x, y, z, w; constexpr position4_base() : x{}, y{}, z{}, w{} { } constexpr position4_base(T x, T y = {}, T z = {}, T w = {T(1)}) : x{ x }, y{ y }, z{ z }, w{ w } { } constexpr position4_base operator -(const position4_base& rhs) const { return{ x - rhs.x, y - rhs.y, z - rhs.z, w - rhs.w }; } constexpr position4_base operator -(T rhs) const { return{ x - rhs, y - rhs, z - rhs, w - rhs }; } constexpr position4_base operator +(const position4_base& rhs) const { return{ x + rhs.x, y + rhs.y, z + rhs.z, w + rhs.w }; } constexpr position4_base operator +(T rhs) const { return{ x + rhs, y + rhs, z + rhs, w + rhs }; } position4_base& operator -=(const position4_base& rhs) { x -= rhs.x; y -= rhs.y; z -= rhs.z; w -= rhs.w; return *this; } position4_base& operator -=(T rhs) { x -= rhs; y -= rhs; z -= rhs; w -= rhs; return *this; } position4_base& operator +=(const position4_base& rhs) { x += rhs.x; y += rhs.y; z += rhs.z; w += rhs.w; return *this; } position4_base& operator +=(T rhs) { x += rhs; y += rhs; z += rhs; w += rhs; return *this; } constexpr bool operator ==(const position4_base& rhs) const { return x == rhs.x && y == rhs.y && z == rhs.z && w == rhs.w; } constexpr bool operator ==(T rhs) const { return x == rhs && y == rhs && z == rhs && w == rhs; } constexpr bool operator !=(const position4_base& rhs) const { return !(*this == rhs); } constexpr bool operator !=(T rhs) const { return !(*this == rhs); } template constexpr operator position4_base() const { return{ (NT)x, (NT)y, (NT)z, (NT)w }; } }; template using position_base = position2_base; template struct coord_base { union { position_base position; struct { T x, y; }; }; union { size2_base size; struct { T width, height; }; }; constexpr coord_base() : position{}, size{} #ifdef _MSC_VER //compiler error , x{}, y{}, width{}, height{} #endif { } constexpr coord_base(const position_base& position, const size2_base& size) : position{ position }, size{ size } #ifdef _MSC_VER , x{ position.x }, y{ position.y }, width{ size.width }, height{ size.height } #endif { } constexpr coord_base(T x, T y, T width, T height) : x{ x }, y{ y }, width{ width }, height{ height } { } constexpr bool test(const position_base& position) const { if (position.x < x || position.x >= x + width) return false; if (position.y < y || position.y >= y + height) return false; return true; } constexpr bool operator == (const coord_base& rhs) const { return position == rhs.position && size == rhs.size; } constexpr bool operator != (const coord_base& rhs) const { return position != rhs.position || size != rhs.size; } template constexpr operator coord_base() const { return{ (NT)x, (NT)y, (NT)width, (NT)height }; } }; template struct area_base { T x1, x2; T y1, y2; constexpr area_base() : x1{}, x2{}, y1{}, y2{} { } constexpr area_base(T x1, T y1, T x2, T y2) : x1{ x1 }, x2{ x2 }, y1{ y1 }, y2{ y2 } { } constexpr area_base(const coord_base& coord) : x1{ coord.x }, x2{ coord.x + coord.width }, y1{ coord.y }, y2{ coord.y + coord.height } { } constexpr operator coord_base() const { return{ x1, y1, x2 - x1, y2 - y1 }; } void flip_vertical() { std::swap(y1, y2); } void flip_horizontal() { std::swap(x1, x2); } constexpr area_base flipped_vertical() const { return{ x1, y2, x2, y1 }; } constexpr area_base flipped_horizontal() const { return{ x2, y1, x1, y2 }; } constexpr bool operator == (const area_base& rhs) const { return x1 == rhs.x1 && x2 == rhs.x2 && y1 == rhs.y1 && y2 == rhs.y2; } constexpr bool operator != (const area_base& rhs) const { return !(*this == rhs); } constexpr area_base operator - (const size2_base& size) const { return{ x1 - size.width, y1 - size.height, x2 - size.width, y2 - size.height }; } constexpr area_base operator - (const T& value) const { return{ x1 - value, y1 - value, x2 - value, y2 - value }; } constexpr area_base operator + (const size2_base& size) const { return{ x1 + size.width, y1 + size.height, x2 + size.width, y2 + size.height }; } constexpr area_base operator + (const T& value) const { return{ x1 + value, y1 + value, x2 + value, y2 + value }; } constexpr area_base operator / (const size2_base& size) const { return{ x1 / size.width, y1 / size.height, x2 / size.width, y2 / size.height }; } constexpr area_base operator / (const T& value) const { return{ x1 / value, y1 / value, x2 / value, y2 / value }; } constexpr area_base operator * (const size2_base& size) const { return{ x1 * size.width, y1 * size.height, x2 * size.width, y2 * size.height }; } constexpr area_base operator * (const T& value) const { return{ x1 * value, y1 * value, x2 * value, y2 * value }; } template constexpr operator area_base() const { return{(NT)x1, (NT)y1, (NT)x2, (NT)y2}; } }; template struct size3_base { T width, height, depth; /* size3_base() : width{}, height{}, depth{} { } size3_base(T width, T height, T depth) : width{ width }, height{ height }, depth{ depth } { } */ }; template struct coord3_base { union { position3_base position; struct { T x, y, z; }; }; union { size3_base size; struct { T width, height, depth; }; }; constexpr coord3_base() : position{}, size{} { } constexpr coord3_base(const position3_base& position, const size3_base& size) : position{ position }, size{ size } { } constexpr coord3_base(T x, T y, T z, T width, T height, T depth) : x{ x }, y{ y }, z{ z }, width{ width }, height{ height }, depth{ depth } { } constexpr bool test(const position3_base& position) const { if (position.x < x || position.x >= x + width) return false; if (position.y < y || position.y >= y + height) return false; if (position.z < z || position.z >= z + depth) return false; return true; } template constexpr operator coord3_base() const { return{ (NT)x, (NT)y, (NT)z, (NT)width, (NT)height, (NT)depth }; } }; template struct color4_base { union { struct { T r, g, b, a; }; struct { T x, y, z, w; }; T rgba[4]; T xyzw[4]; }; color4_base() : x{} , y{} , z{} , w{ T(1) } { } color4_base(T x, T y = {}, T z = {}, T w = {}) : x(x) , y(y) , z(z) , w(w) { } bool operator == (const color4_base& rhs) const { return r == rhs.r && g == rhs.g && b == rhs.b && a == rhs.a; } bool operator != (const color4_base& rhs) const { return !(*this == rhs); } template operator color4_base() const { return{ (NT)x, (NT)y, (NT)z, (NT)w }; } }; template struct color3_base { union { struct { T r, g, b; }; struct { T x, y, z; }; T rgb[3]; T xyz[3]; }; constexpr color3_base(T x = {}, T y = {}, T z = {}) : x(x) , y(y) , z(z) { } constexpr bool operator == (const color3_base& rhs) const { return r == rhs.r && g == rhs.g && b == rhs.b; } constexpr bool operator != (const color3_base& rhs) const { return !(*this == rhs); } template constexpr operator color3_base() const { return{ (NT)x, (NT)y, (NT)z }; } }; template struct color2_base { union { struct { T r, g; }; struct { T x, y; }; T rg[2]; T xy[2]; }; constexpr color2_base(T x = {}, T y = {}) : x(x) , y(y) { } constexpr bool operator == (const color2_base& rhs) const { return r == rhs.r && g == rhs.g; } constexpr bool operator != (const color2_base& rhs) const { return !(*this == rhs); } template constexpr operator color2_base() const { return{ (NT)x, (NT)y }; } }; template struct color1_base { union { T r; T x; }; constexpr color1_base(T x = {}) : x(x) { } constexpr bool operator == (const color1_base& rhs) const { return r == rhs.r; } constexpr bool operator != (const color1_base& rhs) const { return !(*this == rhs); } template constexpr operator color1_base() const { return{ (NT)x }; } }; //specializations using positioni = position_base; using positionf = position_base; using positiond = position_base; using coordi = coord_base; using coordf = coord_base; using coordd = coord_base; using areai = area_base; using areaf = area_base; using aread = area_base; using position1i = position1_base; using position1f = position1_base; using position1d = position1_base; using position2i = position2_base; using position2f = position2_base; using position2d = position2_base; using position3i = position3_base; using position3f = position3_base; using position3d = position3_base; using position4i = position4_base; using position4f = position4_base; using position4d = position4_base; using size2i = size2_base; using size2f = size2_base; using size2d = size2_base; using sizei = size2i; using sizef = size2f; using sized = size2d; using size3i = size3_base; using size3f = size3_base; using size3d = size3_base; using coord3i = coord3_base; using coord3f = coord3_base; using coord3d = coord3_base; using color4i = color4_base; using color4f = color4_base; using color4d = color4_base; using color3i = color3_base; using color3f = color3_base; using color3d = color3_base; using color2i = color2_base; using color2f = color2_base; using color2d = color2_base; using color1i = color1_base; using color1f = color1_base; using color1d = color1_base;