Created
April 22, 2019 17:00
-
-
Save johnmcfarlane/335cab3f9b70a12c81a7a9a12abcb518 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright John McFarlane 2018. | |
// Distributed under the Boost Software License, Version 1.0. | |
// (See accompanying file ../LICENSE_1_0.txt or copy at | |
// http://www.boost.org/LICENSE_1_0.txt) | |
// mechanically retrieved, single-header version of CNL library | |
// https://github.com/johnmcfarlane/cnl | |
#if ! defined(CNL_COMPLETE_H) | |
#define CNL_COMPLETE_H | |
#if (__cplusplus == 199711L) && defined(_MSC_VER) | |
#error Required Visual C++ compiler flags: /std:c++17 /Zc:__cplusplus /EHsc | |
#endif | |
#if (__cplusplus < 201703L) | |
#error This build of CNL requires C++17 or above. | |
#endif | |
#include <array> | |
#include <climits> | |
#include <cmath> | |
#include <cstdint> | |
#include <cstdio> | |
#include <exception> | |
#include <functional> | |
#include <istream> | |
#include <iterator> | |
#include <limits> | |
#include <numeric> | |
#include <ostream> | |
#include <stdexcept> | |
#include <string> | |
#include <system_error> | |
#include <tuple> | |
#include <type_traits> | |
#include <utility> | |
namespace cnl { | |
namespace _impl { | |
template<class T> | |
constexpr T max(T a, T b) | |
{ | |
return (a<b) ? b : a; | |
} | |
template<class T> | |
constexpr T min(T a, T b) | |
{ | |
return (a<b) ? a : b; | |
} | |
} | |
} | |
namespace cnl { | |
using int8 = std::int8_t; | |
using uint8 = std::uint8_t; | |
using int16 = std::int16_t; | |
using uint16 = std::uint16_t; | |
using int32 = std::int32_t; | |
using uint32 = std::uint32_t; | |
using int64 = std::int64_t; | |
using uint64 = std::uint64_t; | |
using int128 = __int128; | |
using uint128 = unsigned __int128; | |
using intmax = int128; | |
using uintmax = uint128; | |
namespace _cnlint_impl { | |
template<typename ParseDigit> | |
constexpr intmax parse(char const* s, int base, ParseDigit parse_digit, intmax value = 0) | |
{ | |
return (*s) ? parse(s+1, base, parse_digit, parse_digit(*s)+value*base) : value; | |
} | |
constexpr int parse_bin_char(char c) { | |
return (c == '0') ? 0 : (c == '1') ? 1 : int{}; | |
} | |
constexpr int parse_dec_char(char c) { | |
return (c >= '0' && c <= '9') ? c - '0' : int{}; | |
} | |
constexpr int parse_oct_char(char c) { | |
return (c >= '0' && c <= '7') ? c - '0' : int{}; | |
} | |
constexpr int parse_hex_char(char c) { | |
return (c >= '0' && c <= '9') | |
? c - '0' | |
: (c >= 'a' && c <= 'z') | |
? c + 10 - 'a' | |
: (c >= 'A' && c <= 'Z') | |
? c + 10 - 'A' | |
: int{}; | |
} | |
constexpr intmax parse_positive(char const* s) | |
{ | |
return (s[0]!='0') | |
? parse(s, 10, parse_dec_char) | |
: (s[1]=='x' || s[1]=='X') | |
? parse(s+2, 16, parse_hex_char) | |
: (s[1]=='b' || s[1]=='B') | |
? parse(s+2, 2, parse_bin_char) | |
: parse(s+1, 8, parse_oct_char); | |
} | |
template<int NumChars> | |
constexpr intmax parse(const char (& s)[NumChars]) | |
{ | |
return (s[0]=='-') | |
? -parse_positive(s+1) | |
: s[0]=='+' | |
? parse_positive(s+1) | |
: parse_positive(s); | |
} | |
template<char... Chars> | |
constexpr intmax parse() { | |
return parse<sizeof...(Chars) + 1>({Chars...,'\0'}); | |
} | |
} | |
} | |
namespace cnl { | |
template<class T> | |
struct numeric_limits : std::numeric_limits<T> {}; | |
template<> | |
struct numeric_limits<int128> : numeric_limits<long long> { | |
static int const digits = 8*sizeof(int128)-1; | |
static int const digits10 = 38; | |
struct _s { | |
constexpr _s(uint64 upper, uint64 lower) : value(lower + (int128{upper} << 64)) {} | |
constexpr operator int128() const { return value; } | |
int128 value; | |
}; | |
static constexpr int128 min() | |
{ | |
return _s(0x8000000000000000, 0x0000000000000000); | |
} | |
static constexpr int128 max() | |
{ | |
return _s(0x7fffffffffffffff, 0xffffffffffffffff); | |
} | |
static constexpr int128 lowest() | |
{ | |
return min(); | |
} | |
}; | |
template<> | |
struct numeric_limits<uint128> : numeric_limits<unsigned long long> { | |
static int const digits = 8*sizeof(int128); | |
static int const digits10 = 38; | |
struct _s { | |
constexpr _s(uint64 upper, uint64 lower) : value(lower + (uint128{upper} << 64)) {} | |
constexpr operator uint128() const { return value; } | |
uint128 value; | |
}; | |
static constexpr int128 min() | |
{ | |
return 0; | |
} | |
static constexpr uint128 max() | |
{ | |
return _s(0xffffffffffffffff, 0xffffffffffffffff); | |
} | |
static constexpr int128 lowest() | |
{ | |
return min(); | |
} | |
}; | |
} | |
namespace cnl { | |
template< ::cnl::intmax Value> | |
struct constant { | |
using value_type = ::cnl::intmax; | |
static constexpr value_type value = Value; | |
constexpr operator value_type() const | |
{ | |
return value; | |
} | |
}; | |
template< ::cnl::intmax Value> constexpr auto operator +(constant<Value>) noexcept -> constant<+ Value> { return constant<+ Value>{}; } | |
template< ::cnl::intmax Value> constexpr auto operator -(constant<Value>) noexcept -> constant<- Value> { return constant<- Value>{}; } | |
template< ::cnl::intmax LhsValue, ::cnl::intmax RhsValue> constexpr auto operator +(constant<LhsValue>, constant<RhsValue>) noexcept -> constant<(LhsValue + RhsValue)> { return constant<(LhsValue + RhsValue)>{}; } | |
template< ::cnl::intmax LhsValue, ::cnl::intmax RhsValue> constexpr auto operator -(constant<LhsValue>, constant<RhsValue>) noexcept -> constant<(LhsValue - RhsValue)> { return constant<(LhsValue - RhsValue)>{}; } | |
template< ::cnl::intmax LhsValue, ::cnl::intmax RhsValue> constexpr auto operator *(constant<LhsValue>, constant<RhsValue>) noexcept -> constant<(LhsValue * RhsValue)> { return constant<(LhsValue * RhsValue)>{}; } | |
template< ::cnl::intmax LhsValue, ::cnl::intmax RhsValue> constexpr auto operator /(constant<LhsValue>, constant<RhsValue>) noexcept -> constant<(LhsValue / RhsValue)> { return constant<(LhsValue / RhsValue)>{}; } | |
template< ::cnl::intmax LhsValue, ::cnl::intmax RhsValue> constexpr auto operator %(constant<LhsValue>, constant<RhsValue>) noexcept -> constant<(LhsValue % RhsValue)> { return constant<(LhsValue % RhsValue)>{}; } | |
template< ::cnl::intmax Value> constexpr auto operator ~(constant<Value>) noexcept -> constant<~ Value> { return constant<~ Value>{}; } | |
template< ::cnl::intmax LhsValue, ::cnl::intmax RhsValue> constexpr auto operator &(constant<LhsValue>, constant<RhsValue>) noexcept -> constant<(LhsValue & RhsValue)> { return constant<(LhsValue & RhsValue)>{}; } | |
template< ::cnl::intmax LhsValue, ::cnl::intmax RhsValue> constexpr auto operator |(constant<LhsValue>, constant<RhsValue>) noexcept -> constant<(LhsValue | RhsValue)> { return constant<(LhsValue | RhsValue)>{}; } | |
template< ::cnl::intmax LhsValue, ::cnl::intmax RhsValue> constexpr auto operator ^(constant<LhsValue>, constant<RhsValue>) noexcept -> constant<(LhsValue ^ RhsValue)> { return constant<(LhsValue ^ RhsValue)>{}; } | |
template< ::cnl::intmax LhsValue, ::cnl::intmax RhsValue> constexpr auto operator <<(constant<LhsValue>, constant<RhsValue>) noexcept -> constant<(LhsValue << RhsValue)> { return constant<(LhsValue << RhsValue)>{}; } | |
template< ::cnl::intmax LhsValue, ::cnl::intmax RhsValue> constexpr auto operator >>(constant<LhsValue>, constant<RhsValue>) noexcept -> constant<(LhsValue >> RhsValue)> { return constant<(LhsValue >> RhsValue)>{}; } | |
template< ::cnl::intmax Value> constexpr auto operator !(constant<Value>) noexcept -> constant<! Value> { return constant<! Value>{}; } | |
template< ::cnl::intmax LhsValue, ::cnl::intmax RhsValue> constexpr auto operator &&(constant<LhsValue>, constant<RhsValue>) noexcept -> constant<(LhsValue && RhsValue)> { return constant<(LhsValue && RhsValue)>{}; } | |
template< ::cnl::intmax LhsValue, ::cnl::intmax RhsValue> constexpr auto operator ||(constant<LhsValue>, constant<RhsValue>) noexcept -> constant<(LhsValue || RhsValue)> { return constant<(LhsValue || RhsValue)>{}; } | |
template< ::cnl::intmax LhsValue, ::cnl::intmax RhsValue> constexpr auto operator ==(constant<LhsValue>, constant<RhsValue>) noexcept -> constant<(LhsValue == RhsValue)> { return constant<(LhsValue == RhsValue)>{}; } | |
template< ::cnl::intmax LhsValue, ::cnl::intmax RhsValue> constexpr auto operator !=(constant<LhsValue>, constant<RhsValue>) noexcept -> constant<(LhsValue != RhsValue)> { return constant<(LhsValue != RhsValue)>{}; } | |
template< ::cnl::intmax LhsValue, ::cnl::intmax RhsValue> constexpr auto operator <(constant<LhsValue>, constant<RhsValue>) noexcept -> constant<(LhsValue < RhsValue)> { return constant<(LhsValue < RhsValue)>{}; } | |
template< ::cnl::intmax LhsValue, ::cnl::intmax RhsValue> constexpr auto operator >(constant<LhsValue>, constant<RhsValue>) noexcept -> constant<(LhsValue > RhsValue)> { return constant<(LhsValue > RhsValue)>{}; } | |
template< ::cnl::intmax LhsValue, ::cnl::intmax RhsValue> constexpr auto operator <=(constant<LhsValue>, constant<RhsValue>) noexcept -> constant<(LhsValue <= RhsValue)> { return constant<(LhsValue <= RhsValue)>{}; } | |
template< ::cnl::intmax LhsValue, ::cnl::intmax RhsValue> constexpr auto operator >=(constant<LhsValue>, constant<RhsValue>) noexcept -> constant<(LhsValue >= RhsValue)> { return constant<(LhsValue >= RhsValue)>{}; } | |
namespace _impl { | |
template<class T> | |
struct is_constant : std::false_type { | |
}; | |
template< ::cnl::intmax Value> | |
struct is_constant<::cnl::constant<Value>> : std::true_type { | |
}; | |
} | |
namespace literals { | |
template<char... Chars> | |
constexpr auto operator "" _c() | |
-> constant<_cnlint_impl::parse<Chars...,'\0'>()> | |
{ | |
return {}; | |
} | |
} | |
template< ::cnl::intmax Value> | |
struct numeric_limits<constant<Value>> : cnl::numeric_limits<typename constant<Value>::value_type> { | |
using _value_type = typename constant<Value>::value_type; | |
static constexpr _value_type min() | |
{ | |
return {}; | |
} | |
static constexpr _value_type max() | |
{ | |
return {}; | |
} | |
static constexpr _value_type lowest() | |
{ | |
return {}; | |
} | |
}; | |
} | |
namespace cnl { | |
namespace _impl { | |
template<bool IsSigned> | |
struct used_digits_signed; | |
template<> | |
struct used_digits_signed<false> { | |
template<class Integer> | |
constexpr int operator()(Integer const& value, int radix) const | |
{ | |
static_assert(cnl::numeric_limits<Integer>::is_integer, | |
"Integer parameter of used_digits_positive() must be a fundamental integer."); | |
return (value>0) ? 1+used_digits_signed<false>{}(value/radix, radix) : 0; | |
} | |
}; | |
template<> | |
struct used_digits_signed<true> { | |
template<class Integer> | |
constexpr int operator()(Integer const& value, int radix) const | |
{ | |
static_assert(cnl::numeric_limits<Integer>::is_integer, | |
"Integer parameter of used_digits_signed()() must be a fundamental integer."); | |
return (value<0) | |
? used_digits_signed<false>{}(Integer(-1)-value, radix) | |
: used_digits_signed<false>{}(value, radix); | |
} | |
}; | |
template<typename Integer> | |
constexpr int used_digits(Integer const& value, int radix = numeric_limits<Integer>::radix) | |
{ | |
static_assert(std::is_integral<Integer>::value | |
|| std::is_same<Integer, intmax>::value | |
|| std::is_same<Integer, uintmax>::value, "Integer must be a fundamental integral"); | |
return used_digits_signed<std::is_signed<Integer>::value>{}(value, radix); | |
} | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<bool C, class ... T> | |
using enable_if_t = typename std::enable_if<C, T ...>::type; | |
} | |
} | |
namespace cnl { | |
template<class T, class Enable = void> | |
struct is_composite : std::false_type { | |
static_assert(!std::is_const<T>::value, "T is const"); | |
static_assert(!std::is_volatile<T>::value, "T is volatile"); | |
}; | |
template<class T> | |
constexpr auto is_composite_v = is_composite<T>::value; | |
namespace _impl { | |
template<class ... Args> | |
struct are_composite; | |
template<> | |
struct are_composite<> : std::false_type { | |
}; | |
template<class ArgHead, class ... ArgTail> | |
struct are_composite<ArgHead, ArgTail...> | |
: std::integral_constant<bool, is_composite<typename std::decay<ArgHead>::type>::value || are_composite<ArgTail...>::value> { | |
}; | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<typename T> | |
using remove_cvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type; | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<class T> | |
struct is_integral : std::is_integral<T> { | |
}; | |
template<> | |
struct is_integral<int128> : std::integral_constant<bool, true> { | |
}; | |
template<> | |
struct is_integral<uint128> : std::integral_constant<bool, true> { | |
}; | |
template<class T> | |
constexpr int is_integral_v = is_integral<T>::value; | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<typename Number> | |
struct default_to_rep { | |
constexpr Number& operator()(Number& number) const { | |
return number; | |
}; | |
constexpr Number const& operator()(Number const& number) const { | |
return number; | |
}; | |
constexpr Number&& operator()(Number&& number) const { | |
return std::forward<Number>(number); | |
}; | |
}; | |
} | |
template<typename Number, class Enable = void> | |
struct to_rep; | |
template<typename Number> | |
struct to_rep<Number, _impl::enable_if_t< | |
_impl::is_integral<Number>::value | |
||std::is_floating_point<Number>::value | |
||_impl::is_constant<Number>::value>> | |
: _impl::default_to_rep<Number> { | |
}; | |
namespace _impl { | |
template<class Number> | |
constexpr auto to_rep(Number&& number) | |
-> decltype(cnl::to_rep<remove_cvref_t<Number>>()(std::forward<Number>(number))) { | |
return cnl::to_rep<remove_cvref_t<Number>>()(std::forward<Number>(number)); | |
} | |
template<class Number> | |
using to_rep_t = typename std::remove_reference<decltype(to_rep(std::declval<Number>()))>::type; | |
} | |
} | |
namespace cnl { | |
template<typename T, class Enable = void> | |
struct is_signed; | |
template<> | |
struct is_signed<char> : std::is_signed<char> { | |
}; | |
template<> | |
struct is_signed<signed char> : std::true_type { | |
}; | |
template<> | |
struct is_signed<unsigned char> : std::false_type { | |
}; | |
template<> | |
struct is_signed<wchar_t> : std::is_signed<char> { | |
}; | |
template<> | |
struct is_signed<char16_t> : std::is_signed<char16_t> { | |
}; | |
template<> | |
struct is_signed<char32_t> : std::is_signed<char32_t> { | |
}; | |
template<> | |
struct is_signed<signed short> : std::true_type { | |
}; | |
template<> | |
struct is_signed<unsigned short> : std::false_type { | |
}; | |
template<> | |
struct is_signed<signed int> : std::true_type { | |
}; | |
template<> | |
struct is_signed<unsigned int> : std::false_type { | |
}; | |
template<> | |
struct is_signed<signed long> : std::true_type { | |
}; | |
template<> | |
struct is_signed<unsigned long> : std::false_type { | |
}; | |
template<> | |
struct is_signed<signed long long> : std::true_type { | |
}; | |
template<> | |
struct is_signed<unsigned long long> : std::false_type { | |
}; | |
template<> | |
struct is_signed<int128> : std::true_type { | |
}; | |
template<> | |
struct is_signed<uint128> : std::false_type { | |
}; | |
template<> | |
struct is_signed<float> : std::true_type { | |
}; | |
template<> | |
struct is_signed<double> : std::true_type { | |
}; | |
template<> | |
struct is_signed<long double> : std::true_type { | |
}; | |
template< ::cnl::intmax Value> | |
struct is_signed<constant<Value>> : is_signed<decltype(Value)> { | |
}; | |
template<typename T> | |
struct is_signed<T, _impl::enable_if_t<is_composite<T>::value>> | |
: is_signed<_impl::to_rep_t<T>> { | |
}; | |
} | |
namespace cnl { | |
template<typename T, class Enable = void> | |
struct digits; | |
namespace _impl { | |
template<typename Integer> | |
struct fundamental_digits | |
: std::integral_constant<int, 8*sizeof(Integer)-is_signed<Integer>::value> { | |
}; | |
} | |
template<> | |
struct digits<char> : _impl::fundamental_digits<char> { | |
}; | |
template<> | |
struct digits<signed char> : _impl::fundamental_digits<signed char> { | |
}; | |
template<> | |
struct digits<unsigned char> : _impl::fundamental_digits<unsigned char> { | |
}; | |
template<> | |
struct digits<wchar_t> : _impl::fundamental_digits<char> { | |
}; | |
template<> | |
struct digits<char16_t> : _impl::fundamental_digits<char16_t> { | |
}; | |
template<> | |
struct digits<char32_t> : _impl::fundamental_digits<char32_t> { | |
}; | |
template<> | |
struct digits<signed short> : _impl::fundamental_digits<signed short> { | |
}; | |
template<> | |
struct digits<unsigned short> : _impl::fundamental_digits<unsigned short> { | |
}; | |
template<> | |
struct digits<signed int> : _impl::fundamental_digits<signed int> { | |
}; | |
template<> | |
struct digits<unsigned int> : _impl::fundamental_digits<unsigned int> { | |
}; | |
template<> | |
struct digits<signed long> : _impl::fundamental_digits<signed long> { | |
}; | |
template<> | |
struct digits<unsigned long> : _impl::fundamental_digits<unsigned long> { | |
}; | |
template<> | |
struct digits<signed long long> : _impl::fundamental_digits<signed long long> { | |
}; | |
template<> | |
struct digits<unsigned long long> : _impl::fundamental_digits<unsigned long long> { | |
}; | |
template<> | |
struct digits<int128> : _impl::fundamental_digits<int128> { | |
}; | |
template<> | |
struct digits<uint128> : _impl::fundamental_digits<uint128> { | |
}; | |
template< ::cnl::intmax Value> | |
struct digits<constant<Value>> : std::integral_constant< | |
int, | |
_impl::used_digits((Value<0) ? -Value : Value)> { | |
}; | |
template<class T> | |
constexpr int digits_v = digits<T>::value; | |
} | |
namespace cnl { | |
template<typename Number, typename Rep, class Enable = void> | |
struct from_rep; | |
template<typename Number, typename Rep> | |
struct from_rep<Number, Rep, _impl::enable_if_t<cnl::_impl::is_integral<Number>::value>> { | |
constexpr Number operator()(Rep const& rep) const { | |
return static_cast<Number>(rep); | |
} | |
}; | |
namespace _impl { | |
template<class Number, class Rep> | |
constexpr auto from_rep(Rep const& rep) | |
-> decltype(cnl::from_rep<Number, Rep>{}(rep)) | |
{ | |
return cnl::from_rep<Number, Rep>{}(rep); | |
} | |
template<class Number, class Rep> | |
using from_rep_t = decltype(from_rep<Number>(std::declval<Rep>())); | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<typename T> | |
struct type_identity { | |
using type = T; | |
}; | |
template<typename T> | |
using type_identity_t = typename type_identity<T>::type; | |
} | |
} | |
namespace cnl { | |
template<class T, class = void> | |
struct remove_signedness; | |
template<class T> | |
struct remove_signedness<T, _impl::enable_if_t<std::is_integral<T>::value>> : std::make_unsigned<T> { | |
}; | |
template<> | |
struct remove_signedness<int128> { | |
using type = uint128; | |
}; | |
template<> | |
struct remove_signedness<uint128> { | |
using type = uint128; | |
}; | |
template<class T> | |
using remove_signedness_t = typename remove_signedness<T>::type; | |
template<typename T> | |
struct remove_signedness<T, _impl::enable_if_t<is_composite<T>::value>> | |
: _impl::type_identity<_impl::from_rep_t<T, remove_signedness_t<_impl::to_rep_t<T>>>> { | |
}; | |
} | |
namespace cnl { | |
namespace _bit_impl { | |
template<typename T> | |
constexpr bool is_integral_unsigned() | |
{ | |
return numeric_limits<T>::is_integer && !is_signed<T>::value; | |
} | |
template<typename T> | |
constexpr bool is_integral_signed() | |
{ | |
return numeric_limits<T>::is_integer && is_signed<T>::value; | |
} | |
template<typename T> | |
constexpr T rotl(T x, unsigned int s, unsigned int width) noexcept | |
{ | |
static_assert(is_integral_unsigned<T>(), "T must be unsigned integer"); | |
return static_cast<T>((x << (s%width)) | (x >> (width-(s%width)))); | |
} | |
template<typename T> | |
constexpr T rotr(T x, unsigned int s, unsigned int width) noexcept | |
{ | |
static_assert(is_integral_unsigned<T>(), "T must be unsigned integer"); | |
return static_cast<T>((x >> (s%width)) | (x << (width-(s%width)))); | |
} | |
template<typename T> | |
constexpr int countr_zero(T x) noexcept | |
{ | |
static_assert(_bit_impl::is_integral_unsigned<T>(), "T must be unsigned integer"); | |
return (x & 1) ? 0 : countr_zero<T>(static_cast<T>(x >> 1))+1; | |
} | |
} | |
template<typename T> | |
constexpr T rotl(T x, unsigned int s) noexcept | |
{ | |
return _bit_impl::rotl(x, s, cnl::digits<T>::value); | |
} | |
template<typename T> | |
constexpr T rotr(T x, unsigned int s) noexcept | |
{ | |
return _bit_impl::rotr(x, s, cnl::digits<T>::value); | |
} | |
template<typename T> | |
constexpr int countl_zero(T x) noexcept; | |
template<> | |
constexpr int countl_zero(unsigned int x) noexcept | |
{ | |
return x ? __builtin_clz(x) : cnl::digits<unsigned int>::value; | |
} | |
template<> | |
constexpr int countl_zero(unsigned long x) noexcept | |
{ | |
return x ? __builtin_clzl(x) : cnl::digits<unsigned long>::value; | |
} | |
template<> | |
constexpr int countl_zero(unsigned long long x) noexcept | |
{ | |
return x ? __builtin_clzll(x) : cnl::digits<unsigned long long>::value; | |
} | |
template<typename T> | |
constexpr int countl_zero(T x) noexcept | |
{ | |
static_assert(_bit_impl::is_integral_unsigned<T>(), "T must be unsigned integer"); | |
return x ? countl_zero<T>(static_cast<T>(x >> 1))-1 : cnl::digits<T>::value; | |
} | |
template<typename T> | |
constexpr int countl_one(T x) noexcept; | |
template<> | |
constexpr int countl_one(unsigned int x) noexcept | |
{ | |
return ~x ? __builtin_clz(~x) : cnl::digits<unsigned int>::value; | |
} | |
template<> | |
constexpr int countl_one(unsigned long x) noexcept | |
{ | |
return ~x ? __builtin_clzl(~x) : cnl::digits<unsigned long>::value; | |
} | |
template<> | |
constexpr int countl_one(unsigned long long x) noexcept | |
{ | |
return ~x ? __builtin_clzll(~x) : cnl::digits<unsigned long long>::value; | |
} | |
template<typename T> | |
constexpr int countl_one(T x) noexcept | |
{ | |
static_assert(_bit_impl::is_integral_unsigned<T>(), "T must be unsigned integer"); | |
return (x & (T{1} << (cnl::digits<T>::value-1))) ? countl_one<T>(static_cast<T>(x << 1))+1 : 0; | |
} | |
template<typename T> | |
constexpr int countr_zero(T x) noexcept; | |
template<> | |
constexpr int countr_zero(unsigned int x) noexcept | |
{ | |
return __builtin_ctz(x); | |
} | |
template<> | |
constexpr int countr_zero(unsigned long x) noexcept | |
{ | |
return x ? __builtin_ctzl(x) : cnl::digits<unsigned long>::value; | |
} | |
template<> | |
constexpr int countr_zero(unsigned long long x) noexcept | |
{ | |
return x ? __builtin_ctzll(x) : cnl::digits<unsigned long long>::value; | |
} | |
template<typename T> | |
constexpr int countr_zero(T x) noexcept | |
{ | |
return x ? _bit_impl::countr_zero(x) : cnl::digits<T>::value; | |
} | |
template<typename T> | |
constexpr int countr_one(T x) noexcept; | |
template<> | |
constexpr int countr_one(unsigned int x) noexcept | |
{ | |
return countr_zero(~x); | |
} | |
template<typename T> | |
constexpr int countr_one(T x) noexcept | |
{ | |
return (x & T{1}) ? countr_one(x >> 1)+1 : 0; | |
} | |
template<typename T> | |
constexpr int popcount(T x) noexcept; | |
template<> | |
constexpr int popcount(unsigned int x) noexcept | |
{ | |
return __builtin_popcount(x); | |
} | |
template<> | |
constexpr int popcount(unsigned long x) noexcept | |
{ | |
return __builtin_popcountl(x); | |
} | |
template<> | |
constexpr int popcount(unsigned long long x) noexcept | |
{ | |
return __builtin_popcountll(x); | |
} | |
template<typename T> | |
constexpr int popcount(T x) noexcept | |
{ | |
return x ? popcount(x & (x-1))+1 : 0; | |
} | |
template<class T> | |
constexpr bool ispow2(T x) noexcept | |
{ | |
static_assert(_bit_impl::is_integral_unsigned<T>(), "T must be unsigned integer"); | |
return x && !(x & (x-1)); | |
} | |
template<class T> | |
constexpr T ceil2(T x) noexcept | |
{ | |
static_assert(_bit_impl::is_integral_unsigned<T>(), "T must be unsigned integer"); | |
return x ? static_cast<T>(T{1} << (digits<T>::value-countl_zero(T(x-T(1))))) : T{0}; | |
} | |
template<class T> | |
constexpr T floor2(T x) noexcept | |
{ | |
static_assert(_bit_impl::is_integral_unsigned<T>(), "T must be unsigned integer"); | |
return x ? static_cast<T>(T{1} << (digits<T>::value-1-countl_zero(x))) : T{0}; | |
} | |
template<class T> | |
constexpr int log2p1(T x) noexcept | |
{ | |
static_assert(_bit_impl::is_integral_unsigned<T>(), "T must be unsigned integer"); | |
return digits<T>::value-countl_zero(x); | |
} | |
template<typename T> | |
constexpr int countl_rsb(T x) noexcept; | |
template<> | |
constexpr int countl_rsb(int x) noexcept | |
{ | |
return __builtin_clrsb(x); | |
} | |
template<> | |
constexpr int countl_rsb(long x) noexcept | |
{ | |
return __builtin_clrsbl(x); | |
} | |
template<> | |
constexpr int countl_rsb(long long x) noexcept | |
{ | |
return __builtin_clrsbll(x); | |
} | |
template<typename T> | |
constexpr int countl_rsb(T x) noexcept | |
{ | |
static_assert(_bit_impl::is_integral_signed<T>(), "T must be signed integer"); | |
using unsigned_type = typename remove_signedness<T>::type; | |
return ((x<0) | |
? countl_one(static_cast<unsigned_type>(x)) | |
: countl_zero(static_cast<unsigned_type>(x))) - 1; | |
} | |
namespace _bit_impl { | |
template<bool IsSigned> | |
struct countl_rb { | |
template<class Integer> | |
constexpr int operator()(Integer const& value) const | |
{ | |
static_assert(_bit_impl::is_integral_unsigned<Integer>(), "T must be unsigned integer"); | |
return countl_zero(value); | |
} | |
}; | |
template<> | |
struct countl_rb<true> { | |
template<class Integer> | |
constexpr int operator()(Integer const& value) const | |
{ | |
static_assert(_bit_impl::is_integral_signed<Integer>(), "T must be signed integer"); | |
return countl_rsb(value); | |
} | |
}; | |
} | |
template<typename T> | |
constexpr int countl_rb(T x) noexcept | |
{ | |
return _bit_impl::countl_rb<is_signed<T>::value>()(x); | |
} | |
template<typename T> | |
constexpr int countr_used(T x) noexcept | |
{ | |
return digits<T>::value - countl_rb(x); | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<typename T> | |
constexpr auto abs(T const& value) | |
-> enable_if_t<is_signed<T>::value, T> | |
{ | |
static_assert( | |
std::is_same<decltype(+value), decltype(-value)>::value, | |
"cnl::abs only supports types with symetrically-typed unary operators"); | |
return static_cast<T>((value<0) ? -value : +value); | |
} | |
template<typename T> | |
constexpr auto abs(T const& value) | |
-> enable_if_t<!is_signed<T>::value, T> | |
{ | |
return value; | |
} | |
} | |
} | |
namespace cnl { | |
using _impl::abs; | |
template<typename T> | |
constexpr T sqrt(T arg) { | |
return std::sqrt(arg); | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<class Rep, bool IsSigned> | |
struct lowest; | |
template<class Rep> | |
struct lowest<Rep, true> { | |
constexpr Rep operator()(Rep const& max) const noexcept | |
{ | |
return static_cast<Rep>(-max); | |
} | |
}; | |
template<class Rep> | |
struct lowest<Rep, false> { | |
constexpr Rep operator()(Rep const&) const noexcept | |
{ | |
return 0; | |
} | |
}; | |
} | |
} | |
namespace cnl { | |
template<int NumDigits> | |
struct signed_integer_cannot_have { | |
template<int MaxNumDigits> | |
struct digits_because_maximum_is; | |
}; | |
template<int NumDigits> | |
struct unsigned_integer_cannot_have { | |
template<int MaxNumDigits> | |
struct digits_because_maximum_is; | |
}; | |
namespace _impl { | |
template<typename T, int digits> | |
struct narrower_than | |
: std::integral_constant< | |
bool, | |
std::is_same<T, void>::value ? true : numeric_limits<T>::digits<digits> { | |
}; | |
template<typename T, int digits> | |
struct no_narrower_than | |
: std::integral_constant< | |
bool, | |
std::is_same<T, void>::value ? true : numeric_limits<T>::digits>=digits> { | |
}; | |
template<int MinNumDigits, class Smaller, class T> | |
using enable_for_range_t = typename std::enable_if< | |
no_narrower_than<T, MinNumDigits>::value && narrower_than<Smaller, MinNumDigits>::value>::type; | |
template<int MinNumDigits, class Enable = void> | |
struct set_digits_signed; | |
template<int MinNumDigits> | |
struct set_digits_signed<MinNumDigits, enable_for_range_t<MinNumDigits, void, int8>> { | |
using type = int8; | |
}; | |
template<int MinNumDigits> | |
struct set_digits_signed<MinNumDigits, enable_for_range_t<MinNumDigits, int8, int16>> { | |
using type = int16; | |
}; | |
template<int MinNumDigits> | |
struct set_digits_signed<MinNumDigits, enable_for_range_t<MinNumDigits, int16, int32>> { | |
using type = int32; | |
}; | |
template<int MinNumDigits> | |
struct set_digits_signed<MinNumDigits, enable_for_range_t<MinNumDigits, int32, int64>> { | |
using type = int64; | |
}; | |
template<int MinNumDigits> | |
struct set_digits_signed<MinNumDigits, enable_for_range_t<MinNumDigits, int64, int128>> { | |
using type = int128; | |
}; | |
template<int MinNumDigits> | |
struct set_digits_signed<MinNumDigits, enable_for_range_t<MinNumDigits, intmax, void>> | |
: signed_integer_cannot_have<MinNumDigits>::template digits_because_maximum_is<numeric_limits<intmax>::digits> { | |
}; | |
template<int MinNumDigits, class Enable = void> | |
struct set_digits_unsigned; | |
template<int MinNumDigits> | |
struct set_digits_unsigned<MinNumDigits, enable_for_range_t<MinNumDigits, void, uint8>> { | |
using type = uint8; | |
}; | |
template<int MinNumDigits> | |
struct set_digits_unsigned<MinNumDigits, enable_for_range_t<MinNumDigits, uint8, uint16>> { | |
using type = uint16; | |
}; | |
template<int MinNumDigits> | |
struct set_digits_unsigned<MinNumDigits, enable_for_range_t<MinNumDigits, uint16, uint32>> { | |
using type = uint32; | |
}; | |
template<int MinNumDigits> | |
struct set_digits_unsigned<MinNumDigits, enable_for_range_t<MinNumDigits, uint32, uint64>> { | |
using type = uint64; | |
}; | |
template<int MinNumDigits> | |
struct set_digits_unsigned<MinNumDigits, enable_for_range_t<MinNumDigits, uint64, uint128>> { | |
using type = uint128; | |
}; | |
template<int MinNumDigits> | |
struct set_digits_unsigned<MinNumDigits, enable_for_range_t<MinNumDigits, uintmax, void>> | |
: unsigned_integer_cannot_have<MinNumDigits>::template digits_because_maximum_is<numeric_limits<uintmax>::digits> { | |
}; | |
template<class Integer, int MinNumDigits> | |
using set_digits_integer = typename std::conditional< | |
numeric_limits<Integer>::is_signed, | |
set_digits_signed<MinNumDigits>, | |
set_digits_unsigned<MinNumDigits>>::type; | |
} | |
template<class T, int Digits, class _Enable = void> | |
struct set_digits; | |
template<class T, int Digits> | |
struct set_digits<T, Digits, _impl::enable_if_t<_impl::is_integral<T>::value>> | |
: _impl::set_digits_integer<T, Digits> { | |
}; | |
template<int Digits> | |
struct set_digits<int128, Digits> | |
: _impl::set_digits_integer<signed, Digits> { | |
}; | |
template<int Digits> | |
struct set_digits<uint128, Digits> | |
: _impl::set_digits_integer<unsigned, Digits> { | |
}; | |
template<class T, int Digits> | |
using set_digits_t = typename set_digits<T, Digits>::type; | |
} | |
namespace cnl { | |
namespace _impl { | |
template<typename Mimic, typename Source> | |
struct adopt_digits : set_digits<Mimic, digits<Source>::value> { | |
}; | |
template<typename Mimic, typename Source> | |
using adopt_digits_t = typename adopt_digits<Mimic, Source>::type; | |
} | |
} | |
namespace cnl { | |
template<class T, class = void> | |
struct add_signedness; | |
template<class T> | |
struct add_signedness<T, _impl::enable_if_t<std::is_integral<T>::value>> : std::make_signed<T> { | |
}; | |
template<> | |
struct add_signedness<int128> { | |
using type = int128; | |
}; | |
template<> | |
struct add_signedness<uint128> { | |
using type = int128; | |
}; | |
template<class T> | |
using add_signedness_t = typename add_signedness<T>::type; | |
template<typename T> | |
struct add_signedness<T, _impl::enable_if_t<is_composite<T>::value>> | |
: _impl::type_identity<_impl::from_rep_t<T, add_signedness_t<_impl::to_rep_t<T>>>> { | |
}; | |
} | |
namespace cnl { | |
namespace _impl { | |
template<class T, bool IsSigned> | |
struct set_signedness; | |
template<class T> | |
struct set_signedness<T, true> : add_signedness<T> { | |
}; | |
template<class T> | |
struct set_signedness<T, false> : remove_signedness<T> { | |
}; | |
template<class T, bool IsSigned> | |
using set_signedness_t = typename set_signedness<T, IsSigned>::type; | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<typename Mimic, typename Source> | |
struct adopt_signedness : set_signedness<Mimic, is_signed<Source>::value> { | |
}; | |
template<typename Mimic, typename Source> | |
using adopt_signedness_t = typename adopt_signedness<Mimic, Source>::type; | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<typename Mimic, typename Source> | |
struct adopt : adopt_digits<adopt_signedness_t<Mimic, Source>, Source> { | |
}; | |
template<typename Mimic, typename Source> | |
using adopt_t = typename adopt<Mimic, Source>::type; | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template< | |
typename S, int Exponent, int Radix, | |
bool PositiveExponent = (0<Exponent), | |
bool OddExponent = ((Exponent & 1)!=0), | |
bool FloatingPointS = numeric_limits<S>::is_iec559> | |
struct default_power; | |
template<typename S, int Radix> | |
struct default_power<S, 0, Radix, false, false, false> { | |
constexpr S operator()() const | |
{ | |
return S{1}; | |
} | |
}; | |
template<typename S, int Exponent, bool OddExponent> | |
struct default_power<S, Exponent, 2, true, OddExponent, false> { | |
constexpr auto operator()() const | |
-> decltype(S{1} << constant<Exponent>{}) | |
{ | |
using result_numeric_limits = numeric_limits<decltype(S{1} << constant<Exponent>{})>; | |
static_assert(!std::is_integral<S>::value | |
|| !std::is_signed<S>::value | |
|| Exponent<result_numeric_limits::digits, "attempted operation will result in overflow"); | |
return S{1} << constant<Exponent>{}; | |
} | |
}; | |
template<typename S, int Exponent, int Radix, bool OddExponent> | |
struct default_power<S, Exponent, Radix, true, OddExponent, false> { | |
constexpr auto operator()() const | |
-> decltype(default_power<S, (Exponent-1), Radix>{}()*Radix) | |
{ | |
return default_power<S, (Exponent-1), Radix>{}()*Radix; | |
} | |
}; | |
template<typename S, int Exponent, int Radix, bool PositiveExponent, bool OddExponent> | |
struct default_power<S, Exponent, Radix, PositiveExponent, OddExponent, true> { | |
constexpr S operator()() const | |
{ | |
return Exponent | |
? S(1.)/default_power<S, -Exponent, Radix>{}() | |
: S{1.}; | |
} | |
}; | |
template<typename S, int Exponent, int Radix> | |
struct default_power<S, Exponent, Radix, true, false, true> { | |
constexpr static S square(S const& r) | |
{ | |
return r*r; | |
} | |
constexpr S operator()() const | |
{ | |
return square(default_power<S, Exponent/2, Radix>{}()); | |
} | |
}; | |
template<typename S, int Exponent, int Radix> | |
struct default_power<S, Exponent, Radix, true, true, true> { | |
constexpr static S square(S const& r) | |
{ | |
return r*r; | |
} | |
constexpr S operator()() const | |
{ | |
return S(Radix)*default_power<S, (Exponent-1), Radix>{}(); | |
} | |
}; | |
template<typename S, int Exponent, int Radix, class Enable = void> | |
struct power { | |
constexpr auto operator()() const | |
-> decltype(default_power<S, Exponent, Radix>{}()) { | |
return default_power<S, Exponent, Radix>{}(); | |
} | |
}; | |
} | |
template<typename S, int Exponent, int Radix> | |
constexpr auto power() | |
-> decltype(_impl::power<S, Exponent, Radix>{}()) | |
{ | |
return _impl::power<S, Exponent, Radix>{}(); | |
} | |
} | |
namespace cnl { | |
template<int Digits, int Radix, class S, class Enable = void> | |
struct scale; | |
namespace _impl { | |
template<int Digits, int Radix, typename S, class Enable = void> | |
struct default_scale; | |
template<int Digits, int Radix, typename S> | |
struct default_scale<Digits, Radix, S, _impl::enable_if_t<0<=Digits>> { | |
constexpr auto operator()(S const& s) const | |
-> decltype(s*cnl::power<S, Digits, Radix>()) | |
{ | |
return s*cnl::power<S, Digits, Radix>(); | |
} | |
}; | |
template<int Digits, int Radix, typename S> | |
struct default_scale<Digits, Radix, S, _impl::enable_if_t<Digits<0>> { | |
constexpr auto operator()(S const& s) const | |
-> decltype(s/cnl::power<S, -Digits, Radix>()) | |
{ | |
return s/cnl::power<S, -Digits, Radix>(); | |
} | |
}; | |
} | |
template<int Digits, int Radix, class S> | |
struct scale<Digits, Radix, S, _impl::enable_if_t<cnl::_impl::is_integral<S>::value>> | |
: _impl::default_scale<Digits, Radix, S> { | |
}; | |
namespace _impl { | |
template<int Digits, int Radix=2, class S> | |
constexpr auto scale(S const& s) | |
-> decltype(cnl::scale<Digits, Radix, S>{}(s)) | |
{ | |
return cnl::scale<Digits, Radix, S>{}(s); | |
} | |
} | |
} | |
namespace cnl { | |
template<int Digits, int Radix, class S, class Enable=void> | |
struct fixed_width_scale { | |
constexpr S operator()(S const& s) const | |
{ | |
static_assert( | |
Radix!=2||digits<S>::value>-Digits, | |
"this operation will flush the given value"); | |
return static_cast<S>(scale<Digits, Radix, S>()(s)); | |
} | |
}; | |
namespace _impl { | |
template<int Digits, class S=void> | |
constexpr S fixed_width_scale(S const& s) | |
{ | |
return cnl::fixed_width_scale<Digits, numeric_limits<S>::radix, S>()(s); | |
} | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<typename T> | |
struct width : std::integral_constant<int, digits<T>::value+is_signed<T>::value> { | |
}; | |
template<typename T, int Bits> | |
struct set_width : set_digits<T, Bits - is_signed<T>::value> { | |
}; | |
template<typename T, int Bits> | |
using set_width_t = typename set_width<T, Bits>::type; | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
struct convert_op { | |
template<class Destination, class Source> | |
constexpr auto operator()(Source const& source) const -> decltype(static_cast<Destination>(source)) | |
{ | |
return static_cast<Destination>(source); | |
} | |
}; | |
struct minus_op { | |
template<class Rhs> | |
constexpr auto operator()(Rhs const& rhs) const -> decltype(-rhs) | |
{ | |
return -rhs; | |
} | |
}; | |
struct plus_op { | |
template<class Rhs> | |
constexpr auto operator()(Rhs const& rhs) const -> decltype(+rhs) | |
{ | |
return +rhs; | |
} | |
}; | |
struct bitwise_not_op { | |
template<class Rhs> | |
constexpr auto operator()(Rhs const& rhs) const -> decltype(~std::declval<Rhs>()) | |
{ | |
return ~rhs; | |
} | |
}; | |
struct add_op { | |
template<class Lhs, class Rhs> | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const -> decltype(lhs+rhs) | |
{ | |
return lhs+rhs; | |
} | |
}; | |
struct subtract_op { | |
template<class Lhs, class Rhs> | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const -> decltype(lhs-rhs) | |
{ | |
return lhs-rhs; | |
} | |
}; | |
struct multiply_op { | |
template<class Lhs, class Rhs> | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const -> decltype(lhs*rhs) | |
{ | |
return lhs*rhs; | |
} | |
}; | |
struct divide_op { | |
template<class Lhs, class Rhs> | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const -> decltype(lhs/rhs) | |
{ | |
return lhs/rhs; | |
} | |
}; | |
struct modulo_op { | |
template<class Lhs, class Rhs> | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const -> decltype(lhs%rhs) | |
{ | |
return lhs%rhs; | |
} | |
}; | |
struct bitwise_or_op { | |
template<class Lhs, class Rhs> | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const -> decltype(lhs | rhs) | |
{ | |
return lhs | rhs; | |
} | |
}; | |
struct bitwise_and_op { | |
template<class Lhs, class Rhs> | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const -> decltype(lhs & rhs) | |
{ | |
return lhs & rhs; | |
} | |
}; | |
struct bitwise_xor_op { | |
template<class Lhs, class Rhs> | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const -> decltype(lhs ^ rhs) | |
{ | |
return lhs ^ rhs; | |
} | |
}; | |
struct shift_left_op { | |
template<class Lhs, class Rhs> | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const -> decltype(lhs << rhs) | |
{ | |
return lhs << rhs; | |
} | |
}; | |
struct shift_right_op { | |
template<class Lhs, class Rhs> | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const -> decltype(lhs >> rhs) | |
{ | |
return lhs >> rhs; | |
} | |
}; | |
struct equal_op { | |
template<class Lhs, class Rhs> | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const -> decltype(lhs==rhs) | |
{ | |
return lhs==rhs; | |
} | |
}; | |
struct not_equal_op { | |
template<class Lhs, class Rhs> | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const -> decltype(lhs!=rhs) | |
{ | |
return lhs!=rhs; | |
} | |
}; | |
struct less_than_op { | |
template<class Lhs, class Rhs> | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const -> decltype(lhs<rhs) | |
{ | |
return lhs<rhs; | |
} | |
}; | |
struct greater_than_op { | |
template<class Lhs, class Rhs> | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const -> decltype(lhs>rhs) | |
{ | |
return lhs>rhs; | |
} | |
}; | |
struct less_than_or_equal_op { | |
template<class Lhs, class Rhs> | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const -> decltype(lhs<=rhs) | |
{ | |
return lhs<=rhs; | |
} | |
}; | |
struct greater_than_or_equal_op { | |
template<class Lhs, class Rhs> | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const -> decltype(lhs>=rhs) | |
{ | |
return lhs>=rhs; | |
} | |
}; | |
struct pre_increment_op { | |
template<class Rhs> | |
constexpr auto operator()(Rhs& rhs) const -> decltype(++rhs) | |
{ | |
return ++rhs; | |
} | |
}; | |
struct pre_decrement_op { | |
template<class Rhs> | |
constexpr auto operator()(Rhs& rhs) const -> decltype(--rhs) | |
{ | |
return --rhs; | |
} | |
}; | |
struct post_increment_op { | |
template<class Lhs> | |
constexpr auto operator()(Lhs& lhs) const -> decltype(lhs++) | |
{ | |
return lhs++; | |
} | |
}; | |
struct post_decrement_op { | |
template<class Lhs> | |
constexpr auto operator()(Lhs& lhs) const -> decltype(lhs--) | |
{ | |
return lhs--; | |
} | |
}; | |
struct assign_add_op { | |
using binary = add_op; | |
template<class Lhs, class Rhs> | |
constexpr auto operator()(Lhs& lhs, Rhs const& rhs) const -> decltype(lhs += rhs) | |
{ | |
return lhs += rhs; | |
} | |
}; | |
struct assign_subtract_op { | |
using binary = subtract_op; | |
template<class Lhs, class Rhs> | |
constexpr auto operator()(Lhs& lhs, Rhs const& rhs) const -> decltype(lhs -= rhs) | |
{ | |
return lhs -= rhs; | |
} | |
}; | |
struct assign_multiply_op { | |
using binary = multiply_op; | |
template<class Lhs, class Rhs> | |
constexpr auto operator()(Lhs& lhs, Rhs const& rhs) const -> decltype(lhs *= rhs) | |
{ | |
return lhs *= rhs; | |
} | |
}; | |
struct assign_divide_op { | |
using binary = divide_op; | |
template<class Lhs, class Rhs> | |
constexpr auto operator()(Lhs& lhs, Rhs const& rhs) const -> decltype(lhs /= rhs) | |
{ | |
return lhs /= rhs; | |
} | |
}; | |
struct assign_modulo_op { | |
using binary = modulo_op; | |
template<class Lhs, class Rhs> | |
constexpr auto operator()(Lhs& lhs, Rhs const& rhs) const -> decltype(lhs %= rhs) | |
{ | |
return lhs %= rhs; | |
} | |
}; | |
struct assign_bitwise_or_op { | |
using binary = bitwise_or_op; | |
template<class Lhs, class Rhs> | |
constexpr auto operator()(Lhs& lhs, Rhs const& rhs) const -> decltype(lhs |= rhs) | |
{ | |
return lhs |= rhs; | |
} | |
}; | |
struct assign_bitwise_and_op { | |
using binary = bitwise_and_op; | |
template<class Lhs, class Rhs> | |
constexpr auto operator()(Lhs& lhs, Rhs const& rhs) const -> decltype(lhs &= rhs) | |
{ | |
return lhs &= rhs; | |
} | |
}; | |
struct assign_bitwise_xor_op { | |
using binary = bitwise_xor_op; | |
template<class Lhs, class Rhs> | |
constexpr auto operator()(Lhs& lhs, Rhs const& rhs) const -> decltype(lhs ^= rhs) | |
{ | |
return lhs ^= rhs; | |
} | |
}; | |
struct assign_shift_left_op { | |
using binary = shift_left_op; | |
template<class Lhs, class Rhs> | |
constexpr auto operator()(Lhs& lhs, Rhs const& rhs) const -> decltype(lhs <<= rhs) | |
{ | |
return lhs <<= rhs; | |
} | |
}; | |
struct assign_shift_right_op { | |
using binary = shift_right_op; | |
template<class Lhs, class Rhs> | |
constexpr auto operator()(Lhs& lhs, Rhs const& rhs) const -> decltype(lhs >>= rhs) | |
{ | |
return lhs >>= rhs; | |
} | |
}; | |
template<class Operator, class ... Operands> | |
using op_result = decltype(Operator()(std::declval<Operands>() ...)); | |
template<class Operator> | |
struct pre_to_assign; | |
template<> | |
struct pre_to_assign<pre_increment_op> : type_identity<assign_add_op> { | |
}; | |
template<> | |
struct pre_to_assign<pre_decrement_op> : type_identity<assign_subtract_op> { | |
}; | |
template<class Operator> | |
struct post_to_assign; | |
template<> | |
struct post_to_assign<post_increment_op> : type_identity<assign_add_op> { | |
}; | |
template<> | |
struct post_to_assign<post_decrement_op> : type_identity<assign_subtract_op> { | |
}; | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<class T, class Enable = void> | |
struct wants_generic_ops : std::false_type { | |
}; | |
template<class Operator, class Operand, class Enable = void> | |
struct unary_operator; | |
template<class Operator, class LhsOperand, class RhsOperand, class Enable = void> | |
struct binary_operator; | |
template<class Operator, class LhsOperand, class RhsOperand, class Enable = void> | |
struct comparison_operator; | |
template<class Operator, class RhsOperand, class Enable = void> | |
struct pre_operator; | |
template<class Operator, class LhsOperand, class Enable = void> | |
struct post_operator; | |
template<class Operator, class LhsOperand, class RhsOperand, class Enable = void> | |
struct compound_assignment_operator { | |
constexpr LhsOperand& operator()(LhsOperand& lhs, RhsOperand const& rhs) const | |
{ | |
return lhs = static_cast<LhsOperand>( | |
binary_operator<typename Operator::binary, LhsOperand, RhsOperand>()(lhs, rhs)); | |
} | |
}; | |
template<class Operand, class T> | |
using enable_unary_t = ::cnl::_impl::enable_if_t<_impl::wants_generic_ops<Operand>::value, T>; | |
template<class LhsOperand, class RhsOperand> | |
struct enable_binary; | |
template<class LhsOperand, int LhsSize, class RhsOperand> | |
struct enable_binary<LhsOperand[LhsSize], RhsOperand> : std::false_type { | |
}; | |
template<class LhsOperand, class RhsOperand, int RhsSize> | |
struct enable_binary<LhsOperand, RhsOperand[RhsSize]> : std::false_type { | |
}; | |
template<class LhsOperand, class RhsOperand> | |
struct enable_binary | |
: std::integral_constant< | |
bool, | |
(numeric_limits<LhsOperand>::is_specialized && numeric_limits<RhsOperand>::is_specialized) | |
&& (_impl::wants_generic_ops<LhsOperand>::value | |
|| _impl::wants_generic_ops<RhsOperand>::value)> { | |
}; | |
template<class LhsOperand, class RhsOperand, class T> | |
using enable_binary_t = _impl::enable_if_t<enable_binary<LhsOperand, RhsOperand>::value, T>; | |
template<class Operand> constexpr auto operator + (Operand const& operand) -> decltype(cnl::_impl::unary_operator<cnl::_impl::enable_unary_t< Operand, cnl::_impl::plus_op>, Operand>()(operand)) { return cnl::_impl::unary_operator<cnl::_impl::plus_op, Operand>()(operand); } | |
template<class Operand> constexpr auto operator - (Operand const& operand) -> decltype(cnl::_impl::unary_operator<cnl::_impl::enable_unary_t< Operand, cnl::_impl::minus_op>, Operand>()(operand)) { return cnl::_impl::unary_operator<cnl::_impl::minus_op, Operand>()(operand); } | |
template<class Operand> constexpr auto operator ~ (Operand const& operand) -> decltype(cnl::_impl::unary_operator<cnl::_impl::enable_unary_t< Operand, cnl::_impl::bitwise_not_op>, Operand>()(operand)) { return cnl::_impl::unary_operator<cnl::_impl::bitwise_not_op, Operand>()(operand); } | |
template<class LhsOperand, class RhsOperand> constexpr auto operator + (LhsOperand const& lhs, RhsOperand const& rhs) -> decltype(cnl::_impl::binary_operator<cnl::_impl::enable_binary_t< LhsOperand, RhsOperand, cnl::_impl::add_op>, LhsOperand, RhsOperand>()(lhs, rhs)) { return cnl::_impl::binary_operator<cnl::_impl::add_op, LhsOperand, RhsOperand>()(lhs, rhs); } | |
template<class LhsOperand, class RhsOperand> constexpr auto operator - (LhsOperand const& lhs, RhsOperand const& rhs) -> decltype(cnl::_impl::binary_operator<cnl::_impl::enable_binary_t< LhsOperand, RhsOperand, cnl::_impl::subtract_op>, LhsOperand, RhsOperand>()(lhs, rhs)) { return cnl::_impl::binary_operator<cnl::_impl::subtract_op, LhsOperand, RhsOperand>()(lhs, rhs); } | |
template<class LhsOperand, class RhsOperand> constexpr auto operator * (LhsOperand const& lhs, RhsOperand const& rhs) -> decltype(cnl::_impl::binary_operator<cnl::_impl::enable_binary_t< LhsOperand, RhsOperand, cnl::_impl::multiply_op>, LhsOperand, RhsOperand>()(lhs, rhs)) { return cnl::_impl::binary_operator<cnl::_impl::multiply_op, LhsOperand, RhsOperand>()(lhs, rhs); } | |
template<class LhsOperand, class RhsOperand> constexpr auto operator / (LhsOperand const& lhs, RhsOperand const& rhs) -> decltype(cnl::_impl::binary_operator<cnl::_impl::enable_binary_t< LhsOperand, RhsOperand, cnl::_impl::divide_op>, LhsOperand, RhsOperand>()(lhs, rhs)) { return cnl::_impl::binary_operator<cnl::_impl::divide_op, LhsOperand, RhsOperand>()(lhs, rhs); } | |
template<class LhsOperand, class RhsOperand> constexpr auto operator % (LhsOperand const& lhs, RhsOperand const& rhs) -> decltype(cnl::_impl::binary_operator<cnl::_impl::enable_binary_t< LhsOperand, RhsOperand, cnl::_impl::modulo_op>, LhsOperand, RhsOperand>()(lhs, rhs)) { return cnl::_impl::binary_operator<cnl::_impl::modulo_op, LhsOperand, RhsOperand>()(lhs, rhs); } | |
template<class LhsOperand, class RhsOperand> constexpr auto operator | (LhsOperand const& lhs, RhsOperand const& rhs) -> decltype(cnl::_impl::binary_operator<cnl::_impl::enable_binary_t< LhsOperand, RhsOperand, cnl::_impl::bitwise_or_op>, LhsOperand, RhsOperand>()(lhs, rhs)) { return cnl::_impl::binary_operator<cnl::_impl::bitwise_or_op, LhsOperand, RhsOperand>()(lhs, rhs); } | |
template<class LhsOperand, class RhsOperand> constexpr auto operator & (LhsOperand const& lhs, RhsOperand const& rhs) -> decltype(cnl::_impl::binary_operator<cnl::_impl::enable_binary_t< LhsOperand, RhsOperand, cnl::_impl::bitwise_and_op>, LhsOperand, RhsOperand>()(lhs, rhs)) { return cnl::_impl::binary_operator<cnl::_impl::bitwise_and_op, LhsOperand, RhsOperand>()(lhs, rhs); } | |
template<class LhsOperand, class RhsOperand> constexpr auto operator ^ (LhsOperand const& lhs, RhsOperand const& rhs) -> decltype(cnl::_impl::binary_operator<cnl::_impl::enable_binary_t< LhsOperand, RhsOperand, cnl::_impl::bitwise_xor_op>, LhsOperand, RhsOperand>()(lhs, rhs)) { return cnl::_impl::binary_operator<cnl::_impl::bitwise_xor_op, LhsOperand, RhsOperand>()(lhs, rhs); } | |
template<class LhsOperand, class RhsOperand> constexpr auto operator << (LhsOperand const& lhs, RhsOperand const& rhs) -> decltype(cnl::_impl::binary_operator<cnl::_impl::enable_binary_t< LhsOperand, RhsOperand, cnl::_impl::shift_left_op>, LhsOperand, RhsOperand>()(lhs, rhs)) { return cnl::_impl::binary_operator<cnl::_impl::shift_left_op, LhsOperand, RhsOperand>()(lhs, rhs); } | |
template<class LhsOperand, class RhsOperand> constexpr auto operator >> (LhsOperand const& lhs, RhsOperand const& rhs) -> decltype(cnl::_impl::binary_operator<cnl::_impl::enable_binary_t< LhsOperand, RhsOperand, cnl::_impl::shift_right_op>, LhsOperand, RhsOperand>()(lhs, rhs)) { return cnl::_impl::binary_operator<cnl::_impl::shift_right_op, LhsOperand, RhsOperand>()(lhs, rhs); } | |
template<class LhsOperand, class RhsOperand> constexpr auto operator == (LhsOperand const& lhs, RhsOperand const& rhs) -> decltype(cnl::_impl::comparison_operator<cnl::_impl::enable_binary_t< LhsOperand, RhsOperand, cnl::_impl::equal_op>, LhsOperand, RhsOperand>()(lhs, rhs)) { return cnl::_impl::comparison_operator<cnl::_impl::equal_op, LhsOperand, RhsOperand>()(lhs, rhs); } | |
template<class LhsOperand, class RhsOperand> constexpr auto operator != (LhsOperand const& lhs, RhsOperand const& rhs) -> decltype(cnl::_impl::comparison_operator<cnl::_impl::enable_binary_t< LhsOperand, RhsOperand, cnl::_impl::not_equal_op>, LhsOperand, RhsOperand>()(lhs, rhs)) { return cnl::_impl::comparison_operator<cnl::_impl::not_equal_op, LhsOperand, RhsOperand>()(lhs, rhs); } | |
template<class LhsOperand, class RhsOperand> constexpr auto operator < (LhsOperand const& lhs, RhsOperand const& rhs) -> decltype(cnl::_impl::comparison_operator<cnl::_impl::enable_binary_t< LhsOperand, RhsOperand, cnl::_impl::less_than_op>, LhsOperand, RhsOperand>()(lhs, rhs)) { return cnl::_impl::comparison_operator<cnl::_impl::less_than_op, LhsOperand, RhsOperand>()(lhs, rhs); } | |
template<class LhsOperand, class RhsOperand> constexpr auto operator > (LhsOperand const& lhs, RhsOperand const& rhs) -> decltype(cnl::_impl::comparison_operator<cnl::_impl::enable_binary_t< LhsOperand, RhsOperand, cnl::_impl::greater_than_op>, LhsOperand, RhsOperand>()(lhs, rhs)) { return cnl::_impl::comparison_operator<cnl::_impl::greater_than_op, LhsOperand, RhsOperand>()(lhs, rhs); } | |
template<class LhsOperand, class RhsOperand> constexpr auto operator <= (LhsOperand const& lhs, RhsOperand const& rhs) -> decltype(cnl::_impl::comparison_operator<cnl::_impl::enable_binary_t< LhsOperand, RhsOperand, cnl::_impl::less_than_or_equal_op>, LhsOperand, RhsOperand>()(lhs, rhs)) { return cnl::_impl::comparison_operator<cnl::_impl::less_than_or_equal_op, LhsOperand, RhsOperand>()(lhs, rhs); } | |
template<class LhsOperand, class RhsOperand> constexpr auto operator >= (LhsOperand const& lhs, RhsOperand const& rhs) -> decltype(cnl::_impl::comparison_operator<cnl::_impl::enable_binary_t< LhsOperand, RhsOperand, cnl::_impl::greater_than_or_equal_op>, LhsOperand, RhsOperand>()(lhs, rhs)) { return cnl::_impl::comparison_operator<cnl::_impl::greater_than_or_equal_op, LhsOperand, RhsOperand>()(lhs, rhs); } | |
template<class RhsOperand> constexpr auto operator ++ (RhsOperand& rhs) -> decltype(cnl::_impl::pre_operator<cnl::_impl::pre_increment_op, RhsOperand>()(rhs)) { return cnl::_impl::pre_operator<cnl::_impl::pre_increment_op, RhsOperand>()(rhs); } | |
template<class RhsOperand> constexpr auto operator -- (RhsOperand& rhs) -> decltype(cnl::_impl::pre_operator<cnl::_impl::pre_decrement_op, RhsOperand>()(rhs)) { return cnl::_impl::pre_operator<cnl::_impl::pre_decrement_op, RhsOperand>()(rhs); } | |
template<class LhsOperand> constexpr auto operator ++ (LhsOperand& lhs, int) -> decltype(cnl::_impl::post_operator<cnl::_impl::post_increment_op, LhsOperand>()(lhs)) { return cnl::_impl::post_operator<cnl::_impl::post_increment_op, LhsOperand>()(lhs); } | |
template<class LhsOperand> constexpr auto operator -- (LhsOperand& lhs, int) -> decltype(cnl::_impl::post_operator<cnl::_impl::post_decrement_op, LhsOperand>()(lhs)) { return cnl::_impl::post_operator<cnl::_impl::post_decrement_op, LhsOperand>()(lhs); } | |
template<class LhsOperand, class RhsOperand> constexpr auto operator += (LhsOperand& lhs, RhsOperand const& rhs) -> cnl::_impl::enable_binary_t<LhsOperand, RhsOperand, decltype( cnl::_impl::compound_assignment_operator<cnl::_impl::assign_add_op, LhsOperand, RhsOperand>()(lhs, rhs))> { return cnl::_impl::compound_assignment_operator<cnl::_impl::assign_add_op, LhsOperand, RhsOperand>()(lhs, rhs); } | |
template<class LhsOperand, class RhsOperand> constexpr auto operator -= (LhsOperand& lhs, RhsOperand const& rhs) -> cnl::_impl::enable_binary_t<LhsOperand, RhsOperand, decltype( cnl::_impl::compound_assignment_operator<cnl::_impl::assign_subtract_op, LhsOperand, RhsOperand>()(lhs, rhs))> { return cnl::_impl::compound_assignment_operator<cnl::_impl::assign_subtract_op, LhsOperand, RhsOperand>()(lhs, rhs); } | |
template<class LhsOperand, class RhsOperand> constexpr auto operator *= (LhsOperand& lhs, RhsOperand const& rhs) -> cnl::_impl::enable_binary_t<LhsOperand, RhsOperand, decltype( cnl::_impl::compound_assignment_operator<cnl::_impl::assign_multiply_op, LhsOperand, RhsOperand>()(lhs, rhs))> { return cnl::_impl::compound_assignment_operator<cnl::_impl::assign_multiply_op, LhsOperand, RhsOperand>()(lhs, rhs); } | |
template<class LhsOperand, class RhsOperand> constexpr auto operator /= (LhsOperand& lhs, RhsOperand const& rhs) -> cnl::_impl::enable_binary_t<LhsOperand, RhsOperand, decltype( cnl::_impl::compound_assignment_operator<cnl::_impl::assign_divide_op, LhsOperand, RhsOperand>()(lhs, rhs))> { return cnl::_impl::compound_assignment_operator<cnl::_impl::assign_divide_op, LhsOperand, RhsOperand>()(lhs, rhs); } | |
template<class LhsOperand, class RhsOperand> constexpr auto operator %= (LhsOperand& lhs, RhsOperand const& rhs) -> cnl::_impl::enable_binary_t<LhsOperand, RhsOperand, decltype( cnl::_impl::compound_assignment_operator<cnl::_impl::assign_modulo_op, LhsOperand, RhsOperand>()(lhs, rhs))> { return cnl::_impl::compound_assignment_operator<cnl::_impl::assign_modulo_op, LhsOperand, RhsOperand>()(lhs, rhs); } | |
template<class LhsOperand, class RhsOperand> constexpr auto operator |= (LhsOperand& lhs, RhsOperand const& rhs) -> cnl::_impl::enable_binary_t<LhsOperand, RhsOperand, decltype( cnl::_impl::compound_assignment_operator<cnl::_impl::assign_bitwise_or_op, LhsOperand, RhsOperand>()(lhs, rhs))> { return cnl::_impl::compound_assignment_operator<cnl::_impl::assign_bitwise_or_op, LhsOperand, RhsOperand>()(lhs, rhs); } | |
template<class LhsOperand, class RhsOperand> constexpr auto operator &= (LhsOperand& lhs, RhsOperand const& rhs) -> cnl::_impl::enable_binary_t<LhsOperand, RhsOperand, decltype( cnl::_impl::compound_assignment_operator<cnl::_impl::assign_bitwise_and_op, LhsOperand, RhsOperand>()(lhs, rhs))> { return cnl::_impl::compound_assignment_operator<cnl::_impl::assign_bitwise_and_op, LhsOperand, RhsOperand>()(lhs, rhs); } | |
template<class LhsOperand, class RhsOperand> constexpr auto operator ^= (LhsOperand& lhs, RhsOperand const& rhs) -> cnl::_impl::enable_binary_t<LhsOperand, RhsOperand, decltype( cnl::_impl::compound_assignment_operator<cnl::_impl::assign_bitwise_xor_op, LhsOperand, RhsOperand>()(lhs, rhs))> { return cnl::_impl::compound_assignment_operator<cnl::_impl::assign_bitwise_xor_op, LhsOperand, RhsOperand>()(lhs, rhs); } | |
template<class LhsOperand, class RhsOperand> constexpr auto operator <<= (LhsOperand& lhs, RhsOperand const& rhs) -> cnl::_impl::enable_binary_t<LhsOperand, RhsOperand, decltype( cnl::_impl::compound_assignment_operator<cnl::_impl::assign_shift_left_op, LhsOperand, RhsOperand>()(lhs, rhs))> { return cnl::_impl::compound_assignment_operator<cnl::_impl::assign_shift_left_op, LhsOperand, RhsOperand>()(lhs, rhs); } | |
template<class LhsOperand, class RhsOperand> constexpr auto operator >>= (LhsOperand& lhs, RhsOperand const& rhs) -> cnl::_impl::enable_binary_t<LhsOperand, RhsOperand, decltype( cnl::_impl::compound_assignment_operator<cnl::_impl::assign_shift_right_op, LhsOperand, RhsOperand>()(lhs, rhs))> { return cnl::_impl::compound_assignment_operator<cnl::_impl::assign_shift_right_op, LhsOperand, RhsOperand>()(lhs, rhs); } | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<typename Result, typename Value> | |
struct from_value_simple { | |
constexpr Result operator()(Value const& value) const { | |
return value; | |
} | |
}; | |
} | |
template<typename Number, typename Value, class Enable = void> | |
struct from_value : _impl::from_value_simple<void, Value> { | |
void operator()(Value const &) const; | |
}; | |
template<class Number, class Value> | |
struct from_value< | |
Number, Value, _impl::enable_if_t<_impl::is_integral<Number>::value && _impl::is_integral<Value>::value>> | |
: _impl::from_value_simple<Value, Value> { | |
}; | |
template<class Number, ::cnl::intmax Value> | |
struct from_value<Number, constant<Value>, _impl::enable_if_t<_impl::is_integral<Number>::value>> { | |
private: | |
using _result_type = set_digits_t< | |
add_signedness_t<Number>, | |
_impl::max(digits<int>::value, _impl::used_digits(Value))>; | |
public: | |
constexpr _result_type operator()(constant<Value> const &value) const { | |
return _result_type(value); | |
} | |
}; | |
namespace _impl { | |
template<typename Number, typename Value> | |
constexpr auto from_number(Value const& value) | |
-> decltype(cnl::from_value<Number, Value>{}(value)) | |
{ | |
return cnl::from_value<Number, Value>{}(value); | |
} | |
} | |
template<typename Number, typename Value> | |
using from_value_t = decltype(_impl::from_number<Number>(std::declval<Value>())); | |
} | |
namespace cnl { | |
namespace _impl { | |
template<class Derived, class Rep> | |
class number_base; | |
template<class Derived, class Rep> | |
class number_base { | |
public: | |
using rep = Rep; | |
explicit constexpr operator bool() const | |
{ | |
return static_cast<bool>(_rep); | |
} | |
protected: | |
static_assert(numeric_limits<Rep>::is_integer, "number_base must be specialized with integer Rep type template parameter"); | |
number_base() = default; | |
explicit constexpr number_base(rep const& r) | |
: _rep(r) { } | |
template<class T> | |
constexpr number_base& operator=(T const& r) { | |
_rep = r; | |
return static_cast<Derived&>(*this); | |
} | |
friend struct cnl::to_rep<number_base>; | |
private: | |
rep _rep; | |
}; | |
template<class Derived, class Enable = void> | |
struct is_class_derived_from_number_base : std::false_type {}; | |
template<class Derived> | |
struct is_class_derived_from_number_base<const Derived> : is_class_derived_from_number_base<Derived> {}; | |
template<class Derived> | |
struct is_class_derived_from_number_base< | |
Derived, | |
enable_if_t<std::is_base_of<number_base<Derived, typename Derived::rep>, Derived>::value>> | |
: std::true_type {}; | |
template<class T, class Enable = void> | |
struct is_derived_from_number_base : std::false_type {}; | |
template<class Derived> | |
struct is_derived_from_number_base<Derived, enable_if_t<std::is_class<Derived>::value>> | |
: is_class_derived_from_number_base<Derived> { }; | |
template<class Wrapper, bool IsComposite = is_composite<Wrapper>::value> | |
struct depth; | |
template<class Wrapper> | |
struct depth<Wrapper, true> { | |
using _rep = _impl::to_rep_t<Wrapper>; | |
static constexpr auto value = depth<_rep>::value + 1; | |
}; | |
template<class T> | |
struct depth<T, false> : std::integral_constant<int, 0> {}; | |
template<class Rep, class Wrapper> | |
struct is_wrappable; | |
template<class Rep, int RepN, class Wrapper> | |
struct is_wrappable<Rep[RepN], Wrapper> : std::false_type {}; | |
template<class Rep, class Wrapper, int WrapperN> | |
struct is_wrappable<Rep, Wrapper[WrapperN]> : std::false_type {}; | |
template<class Rep, class Wrapper> | |
struct is_wrappable | |
: std::integral_constant<bool, cnl::numeric_limits<Rep>::is_specialized | |
&& !std::is_floating_point<Rep>::value | |
&& !std::is_same<from_value_t<Rep, int>, from_value_t<Wrapper, int>>::value | |
&& (depth<Rep>::value < depth<Wrapper>::value)> {}; | |
template<class Operator, class Lhs, class Rhs> | |
struct binary_operator< | |
Operator, Lhs, Rhs, | |
enable_if_t<std::is_floating_point<Lhs>::value && is_derived_from_number_base<Rhs>::value>> { | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const | |
-> decltype(Operator()(lhs, static_cast<Lhs>(rhs))) | |
{ | |
return Operator()(lhs, static_cast<Lhs>(rhs)); | |
} | |
}; | |
template<class Operator, class Lhs, class Rhs> | |
struct binary_operator< | |
Operator, Lhs, Rhs, | |
enable_if_t<is_derived_from_number_base<Lhs>::value && std::is_floating_point<Rhs>::value>> { | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const | |
-> decltype(Operator()(static_cast<Rhs>(lhs), rhs)) | |
{ | |
return Operator()(static_cast<Rhs>(lhs), rhs); | |
} | |
}; | |
template<class Operator, class Lhs, class Rhs> | |
struct binary_operator< | |
Operator, Lhs, Rhs, | |
enable_if_t<is_wrappable<Lhs, Rhs>::value && is_derived_from_number_base<Rhs>::value>> { | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const | |
-> decltype(Operator()(from_number<Rhs>(lhs), rhs)) | |
{ | |
return Operator()(from_number<Rhs>(lhs), rhs); | |
} | |
}; | |
template<class Operator, class Lhs, class Rhs> | |
struct binary_operator< | |
Operator, Lhs, Rhs, | |
enable_if_t<is_derived_from_number_base<Lhs>::value && is_wrappable<Rhs, Lhs>::value>> { | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const | |
-> decltype(Operator()(lhs, from_number<Lhs>(rhs))) | |
{ | |
return Operator()(lhs, from_number<Lhs>(rhs)); | |
} | |
}; | |
template<class Operator, class Lhs, class Rhs> | |
struct comparison_operator< | |
Operator, Lhs, Rhs, | |
enable_if_t<std::is_floating_point<Lhs>::value && is_derived_from_number_base<Rhs>::value>> { | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const | |
-> decltype(Operator()(lhs, static_cast<Lhs>(rhs))) | |
{ | |
return Operator()(lhs, static_cast<Lhs>(rhs)); | |
} | |
}; | |
template<class Operator, class Lhs, class Rhs> | |
struct comparison_operator< | |
Operator, Lhs, Rhs, | |
enable_if_t<is_derived_from_number_base<Lhs>::value && std::is_floating_point<Rhs>::value>> { | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const | |
-> decltype(Operator()(static_cast<Rhs>(lhs), rhs)) | |
{ | |
return Operator()(static_cast<Rhs>(lhs), rhs); | |
} | |
}; | |
template<class Operator, class Lhs, class Rhs> | |
struct comparison_operator< | |
Operator, Lhs, Rhs, | |
enable_if_t<is_wrappable<Lhs, Rhs>::value && is_derived_from_number_base<Rhs>::value>> { | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const | |
-> decltype(Operator()(from_number<Rhs>(lhs), rhs)) | |
{ | |
return Operator()(from_number<Rhs>(lhs), rhs); | |
} | |
}; | |
template<class Operator, class Lhs, class Rhs> | |
struct comparison_operator< | |
Operator, Lhs, Rhs, | |
enable_if_t<is_derived_from_number_base<Lhs>::value && is_wrappable<Rhs, Lhs>::value>> { | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const | |
-> decltype(Operator()(lhs, from_number<Lhs>(rhs))) | |
{ | |
return Operator()(lhs, from_number<Lhs>(rhs)); | |
} | |
}; | |
template<class Operator, class Derived, typename Rep> | |
struct pre_operator<Operator, number_base<Derived, Rep>> { | |
constexpr Derived& operator()(Derived& rhs) const | |
{ | |
Operator()(_impl::to_rep(rhs)); | |
return rhs; | |
} | |
}; | |
template<class Operator, class Derived, typename Rep> | |
struct post_operator<Operator, number_base<Derived, Rep>> { | |
constexpr Derived operator()(Derived& lhs) const | |
{ | |
auto copy = lhs; | |
Operator()(_impl::to_rep(lhs)); | |
return copy; | |
} | |
}; | |
template<class Number> | |
struct wants_generic_ops<Number, _impl::enable_if_t<_impl::is_derived_from_number_base<Number>::value>> : std::true_type { | |
}; | |
} | |
template<class Number> | |
struct is_composite<Number, _impl::enable_if_t<_impl::is_derived_from_number_base<Number>::value>> : std::true_type { | |
}; | |
template<class Derived, class Rep> | |
struct to_rep<_impl::number_base<Derived, Rep>> { | |
constexpr Rep& operator()(Derived& number) const { | |
return number._rep; | |
} | |
constexpr Rep const& operator()(Derived const& number) const { | |
return number._rep; | |
} | |
constexpr Rep&& operator()(Derived&& number) const { | |
return std::forward<Rep>(number._rep); | |
} | |
}; | |
template<class Derived> | |
struct to_rep<Derived, _impl::enable_if_t<_impl::is_derived_from_number_base<Derived>::value>> | |
: to_rep<_impl::number_base<Derived, typename Derived::rep>> { | |
}; | |
template<int Digits, int Radix, class Derived> | |
struct scale<Digits, Radix, _impl::number_base<Derived, typename Derived::rep>> { | |
using _scalar_type = _impl::number_base<Derived, typename Derived::rep>; | |
constexpr auto operator()(Derived const &s) const | |
-> decltype(_impl::from_rep<Derived>(_impl::scale<Digits, Radix>(_impl::to_rep(s)))) | |
{ | |
return _impl::from_rep<Derived>(_impl::scale<Digits, Radix>(_impl::to_rep(s))); | |
} | |
}; | |
template<class Derived, class Rep> | |
struct numeric_limits<cnl::_impl::number_base<Derived, Rep>> | |
: numeric_limits<Rep> { | |
using _value_type = Derived; | |
using _rep = typename _value_type::rep; | |
using _rep_numeric_limits = numeric_limits<_rep>; | |
static constexpr _value_type min() noexcept | |
{ | |
return _impl::from_rep<_value_type>(_rep_numeric_limits::min()); | |
} | |
static constexpr _value_type max() noexcept | |
{ | |
return _impl::from_rep<_value_type>(_rep_numeric_limits::max()); | |
} | |
static constexpr _value_type lowest() noexcept | |
{ | |
return _impl::from_rep<_value_type>(_rep_numeric_limits::lowest()); | |
} | |
static constexpr _value_type epsilon() noexcept | |
{ | |
return _impl::from_rep<_value_type>(_rep_numeric_limits::round_error()); | |
} | |
static constexpr _value_type round_error() noexcept | |
{ | |
return static_cast<_value_type>(_rep_numeric_limits::round_error()); | |
} | |
static constexpr _value_type infinity() noexcept | |
{ | |
return static_cast<_value_type>(_rep_numeric_limits::infinity()); | |
} | |
static constexpr _value_type quiet_NaN() noexcept | |
{ | |
return static_cast<_value_type>(_rep_numeric_limits::quiet_NaN()); | |
} | |
static constexpr _value_type signaling_NaN() noexcept | |
{ | |
return static_cast<_value_type>(_rep_numeric_limits::signaling_NaN()); | |
} | |
static constexpr _value_type denorm_min() noexcept | |
{ | |
return static_cast<_value_type>(_rep_numeric_limits::denorm_min()); | |
} | |
}; | |
} | |
namespace cnl { | |
namespace _impl { | |
template<class ... T> | |
using common_type_t = typename std::common_type<T ...>::type; | |
} | |
} | |
namespace cnl { | |
template<int Digits, class Narrowest> | |
class elastic_integer; | |
namespace _elastic_integer_impl { | |
template<class ElasticInteger> | |
struct is_elastic_integer : std::false_type { | |
}; | |
template<int Digits, class Narrowest> | |
struct is_elastic_integer<elastic_integer<Digits, Narrowest>> : std::true_type { | |
}; | |
template<int Digits, class Narrowest> | |
using rep_t = typename set_digits<Narrowest, _impl::max(cnl::digits<Narrowest>::value, Digits)>::type; | |
template<int Digits, class Narrowest> | |
using base_class_t = _impl::number_base< | |
elastic_integer<Digits, Narrowest>, | |
_elastic_integer_impl::rep_t<Digits, Narrowest>>; | |
} | |
template<int Digits, class Narrowest> | |
struct digits<elastic_integer<Digits, Narrowest>> : std::integral_constant<int, Digits> { | |
}; | |
template<int Digits, class Narrowest, int MinNumBits> | |
struct set_digits<elastic_integer<Digits, Narrowest>, MinNumBits> { | |
using type = elastic_integer<MinNumBits, Narrowest>; | |
}; | |
namespace _impl { | |
template<class T1, class T2> | |
struct common_signedness { | |
static constexpr bool _are_signed = numeric_limits<T1>::is_signed | numeric_limits<T2>::is_signed; | |
using type = typename std::common_type<set_signedness_t<T1, _are_signed>, | |
set_signedness_t<T2, _are_signed>>::type; | |
}; | |
template<class T1, class T2> | |
using common_signedness_t = typename common_signedness<T1, T2>::type; | |
} | |
template<int Digits, typename Narrowest, typename Rep> | |
struct from_rep<elastic_integer<Digits, Narrowest>, Rep> { | |
constexpr auto operator()(Rep const& r) const | |
-> elastic_integer<Digits, cnl::_impl::adopt_signedness_t<Narrowest, Rep>> | |
{ | |
return r; | |
} | |
}; | |
template<int Digits, class Narrowest, class Value> | |
struct from_value<elastic_integer<Digits, Narrowest>, Value> | |
: _impl::from_value_simple< | |
elastic_integer< | |
cnl::digits<Value>::value, | |
_impl::set_width_t<Value, _impl::width<Narrowest>::value>>, | |
Value> { | |
}; | |
template<int Digits, typename Narrowest, int ValueDigits, typename ValueNarrowest> | |
struct from_value<elastic_integer<Digits, Narrowest>, elastic_integer<ValueDigits, ValueNarrowest>> | |
: _impl::from_value_simple< | |
elastic_integer<_impl::max(Digits, ValueDigits), Narrowest>, | |
elastic_integer<ValueDigits, Narrowest>> { | |
}; | |
template<int Digits, class Narrowest, ::cnl::intmax Value> | |
struct from_value<elastic_integer<Digits, Narrowest>, constant<Value>> : _impl::from_value_simple< | |
elastic_integer<digits<constant<Value>>::value, int>, | |
constant<Value>> { | |
}; | |
template<int ScalePower, int ScaleRadix, int ScalarDigits, class ScalarNarrowest> | |
struct fixed_width_scale< | |
ScalePower, ScaleRadix, elastic_integer<ScalarDigits, ScalarNarrowest>> { | |
constexpr auto operator()(elastic_integer<ScalarDigits, ScalarNarrowest> const& s) const | |
-> elastic_integer<ScalarDigits, ScalarNarrowest> | |
{ | |
using result_type = elastic_integer<ScalarDigits, ScalarNarrowest>; | |
using result_rep = typename result_type::rep; | |
return fixed_width_scale<ScalePower, ScaleRadix, result_rep>()(_impl::to_rep(s)); | |
} | |
}; | |
template<int ShiftDigits, int ScaleRadix, int ScalarDigits, class ScalarNarrowest> | |
struct scale<ShiftDigits, ScaleRadix, elastic_integer<ScalarDigits, ScalarNarrowest>, | |
_impl::enable_if_t<(0<=ShiftDigits)>> { | |
constexpr auto operator()(elastic_integer<ScalarDigits, ScalarNarrowest> const& s) const | |
-> elastic_integer<ShiftDigits+ScalarDigits, ScalarNarrowest> | |
{ | |
using result_type = elastic_integer<ShiftDigits+ScalarDigits, ScalarNarrowest>; | |
using result_rep = typename result_type::rep; | |
return _impl::from_rep<result_type>( | |
scale<ShiftDigits, ScaleRadix, result_rep>()(_impl::to_rep(s))); | |
} | |
}; | |
template<int ShiftDigits, int ScalarDigits, class ScalarNarrowest> | |
struct scale<ShiftDigits, 2, elastic_integer<ScalarDigits, ScalarNarrowest>, | |
_impl::enable_if_t<(ShiftDigits<0)>> { | |
constexpr auto operator()(elastic_integer<ScalarDigits, ScalarNarrowest> const& s) const | |
-> elastic_integer<ShiftDigits+ScalarDigits, ScalarNarrowest> | |
{ | |
using divisor_type = elastic_integer<1-ShiftDigits, ScalarNarrowest>; | |
using divisor_rep = typename divisor_type::rep; | |
return _impl::to_rep(s) / (divisor_rep{1} << -ShiftDigits); | |
} | |
}; | |
template<int Digits = digits<int>::value, class Narrowest = int> | |
class elastic_integer : public _elastic_integer_impl::base_class_t<Digits, Narrowest> { | |
public: | |
using _base = _elastic_integer_impl::base_class_t<Digits, Narrowest>; | |
static_assert(!_elastic_integer_impl::is_elastic_integer<typename _base::rep>::value, | |
"elastic_integer of elastic_integer is not a supported"); | |
using rep = typename _base::rep; | |
elastic_integer() = default; | |
template<class Number, _impl::enable_if_t<numeric_limits<Number>::is_specialized, int> = 0> | |
constexpr elastic_integer(Number n) | |
: _base(static_cast<rep>(n)) | |
{ | |
} | |
template<int FromWidth, class FromNarrowest> | |
explicit constexpr elastic_integer(elastic_integer<FromWidth, FromNarrowest> const& rhs) | |
:_base(static_cast<rep>(_impl::to_rep(rhs))) | |
{ | |
} | |
template< ::cnl::intmax Value> | |
constexpr elastic_integer(constant<Value>) | |
: _base(static_cast<rep>(Value)) | |
{ | |
} | |
template<class S, _impl::enable_if_t<std::is_floating_point<S>::value, int> = 0> | |
elastic_integer& operator=(S s) | |
{ | |
_base::operator=(floating_point_to_rep(s)); | |
return *this; | |
} | |
template<class S> | |
explicit constexpr operator S() const | |
{ | |
return static_cast<S>(_impl::to_rep(*this)); | |
} | |
}; | |
namespace _elastic_integer_impl { | |
template<bool Signed> | |
struct machine_digits { | |
static constexpr int value = | |
cnl::digits<typename std::conditional<Signed, signed, unsigned>::type>::value; | |
}; | |
template<typename S> | |
using narrowest = set_digits_t<S, machine_digits<is_signed<S>::value>::value>; | |
} | |
template<class S> | |
elastic_integer(S const& s) | |
-> elastic_integer<digits_v<S>, _elastic_integer_impl::narrowest<S>>; | |
template< ::cnl::intmax Value> | |
elastic_integer(constant<Value>) | |
-> elastic_integer<digits_v<constant<Value>>>; | |
template< ::cnl::intmax Value> | |
constexpr auto make_elastic_integer(constant<Value>) | |
-> elastic_integer<digits<constant<Value>>::value> | |
{ | |
return elastic_integer<digits<constant<Value>>::value>{Value}; | |
} | |
namespace _elastic_integer_impl { | |
template<class Narrowest, class Integral> | |
struct make_narrowest { | |
using type = Narrowest; | |
}; | |
template<class Integral> | |
struct make_narrowest<void, Integral> { | |
using type = narrowest<Integral>; | |
}; | |
template<class Narrowest, class Integral> | |
using make_narrowest_t = typename make_narrowest<Narrowest, Integral>::type; | |
template<class Narrowest, class Integral> | |
using make_type = elastic_integer<cnl::digits<Integral>::value, make_narrowest_t<Narrowest, Integral>>; | |
} | |
template<class Narrowest = void, class Integral, _impl::enable_if_t<!_impl::is_constant<Integral>::value, int> = 0> | |
constexpr auto make_elastic_integer(Integral const& value) | |
-> _elastic_integer_impl::make_type<Narrowest, Integral> | |
{ | |
return _elastic_integer_impl::make_type<Narrowest, Integral>{value}; | |
} | |
template<int RhsDigits, class RhsNarrowest> | |
constexpr auto operator~(elastic_integer<RhsDigits, RhsNarrowest> const& rhs) | |
-> elastic_integer<RhsDigits, RhsNarrowest> | |
{ | |
using elastic_integer = elastic_integer<RhsDigits, RhsNarrowest>; | |
using rep = typename elastic_integer::rep; | |
return _impl::from_rep<elastic_integer>( | |
static_cast<rep>( | |
_impl::to_rep(rhs) | |
^ ((static_cast<rep>(~0)) >> (numeric_limits<rep>::digits - RhsDigits)))); | |
} | |
namespace _impl { | |
template<int FromDigits, class FromNarrowest, int OtherDigits, class OtherNarrowest, | |
_impl::enable_if_t<FromDigits!=OtherDigits || !std::is_same<FromNarrowest, OtherNarrowest>::value, std::nullptr_t> = nullptr> | |
constexpr auto cast_to_common_type( | |
elastic_integer<FromDigits, FromNarrowest> const& from, | |
elastic_integer<OtherDigits, OtherNarrowest> const&) | |
-> decltype(static_cast<_impl::common_type_t< | |
elastic_integer<FromDigits, FromNarrowest>, | |
elastic_integer<OtherDigits, OtherNarrowest>>>(from)) { | |
return static_cast<_impl::common_type_t< | |
elastic_integer<FromDigits, FromNarrowest>, | |
elastic_integer<OtherDigits, OtherNarrowest>>>(from); | |
} | |
template<class Operator, int LhsDigits, class LhsNarrowest, int RhsDigits, class RhsNarrowest> | |
struct comparison_operator<Operator, | |
elastic_integer<LhsDigits, LhsNarrowest>, elastic_integer<RhsDigits, RhsNarrowest>> { | |
constexpr auto operator()( | |
elastic_integer<LhsDigits, LhsNarrowest> const& lhs, | |
elastic_integer<RhsDigits, RhsNarrowest> const& rhs) const | |
-> decltype(Operator()(cast_to_common_type(lhs, rhs), cast_to_common_type(rhs, lhs))) | |
{ | |
return Operator()(cast_to_common_type(lhs, rhs), cast_to_common_type(rhs, lhs)); | |
} | |
}; | |
template<class Operator, int Digits, class Narrowest> | |
struct comparison_operator<Operator, elastic_integer<Digits, Narrowest>, elastic_integer<Digits, Narrowest>> { | |
constexpr auto operator()( | |
elastic_integer<Digits, Narrowest> const& lhs, | |
elastic_integer<Digits, Narrowest> const& rhs) const | |
-> decltype(Operator()(_impl::to_rep(lhs), _impl::to_rep(rhs))) | |
{ | |
return Operator()(_impl::to_rep(lhs), _impl::to_rep(rhs)); | |
} | |
}; | |
template<class Operation, class LhsTraits, class RhsTraits> | |
struct policy; | |
template<class LhsTraits, class RhsTraits> | |
struct policy<_impl::add_op, LhsTraits, RhsTraits> { | |
static constexpr int digits = _impl::max(LhsTraits::digits, RhsTraits::digits)+1; | |
static constexpr bool is_signed = LhsTraits::is_signed || RhsTraits::is_signed; | |
}; | |
template<class LhsTraits, class RhsTraits> | |
struct policy<_impl::subtract_op, LhsTraits, RhsTraits> { | |
static constexpr int digits = _impl::max(LhsTraits::digits, RhsTraits::digits) + (LhsTraits::is_signed | RhsTraits::is_signed); | |
static constexpr bool is_signed = true; | |
}; | |
template<class LhsTraits, class RhsTraits> | |
struct policy<_impl::multiply_op, LhsTraits, RhsTraits> { | |
static constexpr int contribution(int operand_digits) { return operand_digits == 1 ? 0 : operand_digits; } | |
static constexpr int digits = max(1, contribution(LhsTraits::digits)+contribution(RhsTraits::digits)); | |
static constexpr bool is_signed = LhsTraits::is_signed || RhsTraits::is_signed; | |
}; | |
template<class LhsTraits, class RhsTraits> | |
struct policy<_impl::divide_op, LhsTraits, RhsTraits> { | |
static constexpr int digits = LhsTraits::digits; | |
static constexpr bool is_signed = LhsTraits::is_signed || RhsTraits::is_signed; | |
}; | |
template<class LhsTraits, class RhsTraits> | |
struct policy<_impl::modulo_op, LhsTraits, RhsTraits> { | |
static constexpr int digits = LhsTraits::digits; | |
static constexpr bool is_signed = LhsTraits::is_signed || RhsTraits::is_signed; | |
}; | |
template<class LhsTraits, class RhsTraits> | |
struct policy<_impl::bitwise_or_op, LhsTraits, RhsTraits> { | |
static constexpr int digits = _impl::max(LhsTraits::digits, RhsTraits::digits); | |
static constexpr bool is_signed = LhsTraits::is_signed || RhsTraits::is_signed; | |
}; | |
template<class LhsTraits, class RhsTraits> | |
struct policy<_impl::bitwise_and_op, LhsTraits, RhsTraits> { | |
static constexpr int digits = _impl::min(LhsTraits::digits, RhsTraits::digits); | |
static constexpr bool is_signed = LhsTraits::is_signed || RhsTraits::is_signed; | |
}; | |
template<class LhsTraits, class RhsTraits> | |
struct policy<_impl::bitwise_xor_op, LhsTraits, RhsTraits> { | |
static constexpr int digits = _impl::max(LhsTraits::digits, RhsTraits::digits); | |
static constexpr bool is_signed = LhsTraits::is_signed || RhsTraits::is_signed; | |
}; | |
template<class LhsTraits, class RhsTraits> | |
struct policy<_impl::shift_left_op, LhsTraits, RhsTraits> { | |
static constexpr int digits = LhsTraits::digits; | |
static constexpr bool is_signed = LhsTraits::is_signed; | |
}; | |
template<class LhsTraits, class RhsTraits> | |
struct policy<_impl::shift_right_op, LhsTraits, RhsTraits> { | |
static constexpr int digits = LhsTraits::digits; | |
static constexpr bool is_signed = LhsTraits::is_signed; | |
}; | |
template<class OperationTag, int LhsDigits, class LhsNarrowest, int RhsDigits, class RhsNarrowest> | |
struct operate_params { | |
using lhs = elastic_integer<LhsDigits, LhsNarrowest>; | |
using rhs = elastic_integer<RhsDigits, RhsNarrowest>; | |
using lhs_traits = numeric_limits<lhs>; | |
using rhs_traits = numeric_limits<rhs>; | |
using policy = typename _impl::policy<OperationTag, lhs_traits, rhs_traits>; | |
using lhs_rep = typename lhs::rep; | |
using rhs_rep = typename rhs::rep; | |
using rep_result = typename _impl::op_result<OperationTag, lhs_rep, rhs_rep>; | |
static constexpr int narrowest_width = _impl::max( | |
width<LhsNarrowest>::value, | |
width<RhsNarrowest>::value); | |
using narrowest = set_digits_t< | |
_impl::set_signedness_t<rep_result, policy::is_signed>, | |
narrowest_width-policy::is_signed>; | |
using result_type = elastic_integer<policy::digits, narrowest>; | |
}; | |
template<class Operator, int LhsDigits, class LhsNarrowest, int RhsDigits, class RhsNarrowest> | |
struct binary_operator<Operator, | |
elastic_integer<LhsDigits, LhsNarrowest>, elastic_integer<RhsDigits, RhsNarrowest>> { | |
constexpr auto operator()( | |
elastic_integer<LhsDigits, LhsNarrowest> const& lhs, | |
elastic_integer<RhsDigits, RhsNarrowest> const& rhs) const | |
-> typename operate_params<Operator, LhsDigits, LhsNarrowest, RhsDigits, RhsNarrowest>::result_type | |
{ | |
using result_type = typename operate_params<Operator, LhsDigits, LhsNarrowest, RhsDigits, RhsNarrowest>::result_type; | |
return from_rep<result_type>( | |
static_cast<typename result_type::rep>(Operator()( | |
to_rep(static_cast<result_type>(lhs)), | |
to_rep(static_cast<result_type>(rhs))))); | |
} | |
}; | |
template<class Operator, int Digits, typename Narrowest> | |
struct pre_operator<Operator, elastic_integer<Digits, Narrowest>> | |
: pre_operator<Operator, typename elastic_integer<Digits, Narrowest>::_base> { | |
}; | |
template<class Operator, int Digits, typename Narrowest> | |
struct post_operator<Operator, elastic_integer<Digits, Narrowest>> | |
: post_operator<Operator, typename elastic_integer<Digits, Narrowest>::_base> { | |
}; | |
} | |
template<int LhsDigits, class LhsNarrowest, ::cnl::intmax RhsValue> | |
constexpr auto operator<<(elastic_integer<LhsDigits, LhsNarrowest> const& lhs, constant<RhsValue>) | |
-> decltype(_impl::from_rep<elastic_integer<LhsDigits + static_cast<int>(RhsValue), LhsNarrowest>>( | |
_impl::to_rep(static_cast<elastic_integer<LhsDigits + static_cast<int>(RhsValue), LhsNarrowest>>(lhs)) << RhsValue)) { | |
using result_type = elastic_integer<LhsDigits + static_cast<int>(RhsValue), LhsNarrowest>; | |
return _impl::from_rep<result_type>(_impl::to_rep(static_cast<result_type>(lhs)) << RhsValue); | |
} | |
template<int LhsDigits, class LhsNarrowest, ::cnl::intmax RhsValue> | |
constexpr auto operator>>(elastic_integer<LhsDigits, LhsNarrowest> const& lhs, constant<RhsValue>) | |
-> decltype (_impl::from_rep<elastic_integer<LhsDigits - static_cast<int>(RhsValue), LhsNarrowest>>( | |
_impl::to_rep(lhs) >> RhsValue)) { | |
return _impl::from_rep<elastic_integer<LhsDigits - static_cast<int>(RhsValue), LhsNarrowest>>( | |
_impl::to_rep(lhs) >> RhsValue); | |
} | |
template<int RhsDigits, class RhsNarrowest> | |
constexpr auto operator-(elastic_integer<RhsDigits, RhsNarrowest> const& rhs) | |
-> decltype(_impl::from_rep<elastic_integer<RhsDigits, typename add_signedness<RhsNarrowest>::type>>( | |
-_impl::to_rep(static_cast<elastic_integer<RhsDigits, typename add_signedness<RhsNarrowest>::type>>(rhs)))) | |
{ | |
using result_type = elastic_integer<RhsDigits, typename add_signedness<RhsNarrowest>::type>; | |
return _impl::from_rep<result_type>(-_impl::to_rep(static_cast<result_type>(rhs))); | |
} | |
template<int RhsDigits, class RhsNarrowest> | |
constexpr auto operator+(elastic_integer<RhsDigits, RhsNarrowest> const& rhs) | |
-> decltype(_impl::from_rep<elastic_integer<RhsDigits, RhsNarrowest>>( | |
+_impl::to_rep(static_cast<elastic_integer<RhsDigits, RhsNarrowest>>(rhs)))) | |
{ | |
return _impl::from_rep<elastic_integer<RhsDigits, RhsNarrowest>>( | |
+_impl::to_rep(static_cast<elastic_integer<RhsDigits, RhsNarrowest>>(rhs))); | |
} | |
} | |
namespace std { | |
template<int LhsDigits, class LhsNarrowest, int RhsDigits, class RhsNarrowest> | |
struct common_type<cnl::elastic_integer<LhsDigits, LhsNarrowest>, cnl::elastic_integer<RhsDigits, RhsNarrowest>> { | |
using type = cnl::elastic_integer< | |
cnl::_impl::max(LhsDigits, RhsDigits), | |
cnl::_impl::common_signedness_t<LhsNarrowest, RhsNarrowest>>; | |
}; | |
template<int LhsDigits, class LhsNarrowest, class Rhs> | |
struct common_type<cnl::elastic_integer<LhsDigits, LhsNarrowest>, Rhs> | |
: common_type<cnl::elastic_integer<LhsDigits, LhsNarrowest>, cnl::elastic_integer<numeric_limits<Rhs>::digits, Rhs>> { | |
}; | |
template<class Lhs, int RhsDigits, class RhsNarrowest> | |
struct common_type<Lhs, cnl::elastic_integer<RhsDigits, RhsNarrowest>> | |
: common_type<cnl::elastic_integer<numeric_limits<Lhs>::digits, Lhs>, cnl::elastic_integer<RhsDigits, RhsNarrowest>> { | |
}; | |
} | |
namespace cnl { | |
template<int Digits, class Narrowest> | |
struct numeric_limits<elastic_integer<Digits, Narrowest>> | |
: numeric_limits<Narrowest> { | |
using _narrowest_numeric_limits = numeric_limits<Narrowest>; | |
using _value_type = elastic_integer<Digits, Narrowest>; | |
using _rep = typename _value_type::rep; | |
using _rep_numeric_limits = numeric_limits<_rep>; | |
static constexpr _rep _rep_max() noexcept | |
{ | |
return static_cast<_rep>(_rep_numeric_limits::max() >> (_rep_numeric_limits::digits-digits)); | |
} | |
static constexpr int digits = Digits; | |
static constexpr _value_type min() noexcept | |
{ | |
return _impl::from_rep<_value_type>(1); | |
} | |
static constexpr _value_type max() noexcept | |
{ | |
return _impl::from_rep<_value_type>(_rep_max()); | |
} | |
static constexpr _value_type lowest() noexcept | |
{ | |
return _impl::lowest<_rep, _narrowest_numeric_limits::is_signed>()(_rep_max()); | |
} | |
}; | |
template<int Digits, class Narrowest> | |
struct numeric_limits<elastic_integer<Digits, Narrowest> const> | |
: numeric_limits<elastic_integer<Digits, Narrowest>> { | |
}; | |
template<int Digits, class Narrowest> | |
std::ostream &operator<<(std::ostream &o, elastic_integer<Digits, Narrowest> const &i) { | |
return o << _impl::to_rep(i); | |
} | |
} | |
namespace cnl { | |
template<typename Rep = int, int Exponent = 0, int Radix = cnl::numeric_limits<Rep>::radix> | |
class fixed_point; | |
} | |
namespace cnl { | |
namespace _impl { | |
template<typename Number, typename Enable = void> | |
struct unwrap; | |
template<typename Number> | |
struct unwrap<Number, enable_if_t<!is_composite<Number>::value>> { | |
constexpr Number operator()(Number const& number) const | |
{ | |
return number; | |
} | |
}; | |
template<typename Number> | |
struct unwrap<Number, enable_if_t<is_composite<Number>::value>> { | |
constexpr auto operator()(Number const& number) const | |
-> decltype(unwrap<to_rep_t<Number>>{}(to_rep(number))) | |
{ | |
return unwrap<to_rep_t<Number>>{}(to_rep(number)); | |
} | |
}; | |
} | |
template<typename Number> | |
constexpr auto unwrap(Number const& number) | |
-> decltype(_impl::unwrap<Number>{}(number)) | |
{ | |
return _impl::unwrap<Number>{}(number); | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<typename T> | |
constexpr T deleted_fn() = delete; | |
} | |
template<typename T> inline constexpr T e{_impl::deleted_fn<T>()}; | |
template<typename T> inline constexpr T log2e{_impl::deleted_fn<T>()}; | |
template<typename T> inline constexpr T log10e{_impl::deleted_fn<T>()}; | |
template<typename T> inline constexpr T pi{_impl::deleted_fn<T>()}; | |
template<typename T> inline constexpr T invpi{_impl::deleted_fn<T>()}; | |
template<typename T> inline constexpr T invsqrtpi{_impl::deleted_fn<T>()}; | |
template<typename T> inline constexpr T ln2{_impl::deleted_fn<T>()}; | |
template<typename T> inline constexpr T ln10{_impl::deleted_fn<T>()}; | |
template<typename T> inline constexpr T sqrt2{_impl::deleted_fn<T>()}; | |
template<typename T> inline constexpr T sqrt3{_impl::deleted_fn<T>()}; | |
template<typename T> inline constexpr T invsqrt2{sqrt2<T>/2}; | |
template<typename T> inline constexpr T invsqrt3{_impl::deleted_fn<T>()}; | |
template<typename T> inline constexpr T radian{_impl::deleted_fn<T>()}; | |
template<typename T> inline constexpr T egamma{_impl::deleted_fn<T>()}; | |
template<typename T> inline constexpr T phi{_impl::deleted_fn<T>()}; | |
template<typename T> inline constexpr T catalan{_impl::deleted_fn<T>()}; | |
template<typename T> inline constexpr T apery{_impl::deleted_fn<T>()}; | |
template<typename T> inline constexpr T glaisher{_impl::deleted_fn<T>()}; | |
template<> inline constexpr long double e<long double>{2.718281828459045235360287471352662498L}; | |
template<> inline constexpr long double log2e<long double>{1.442695040888963407359924681001892137L}; | |
template<> inline constexpr long double log10e<long double>{0.434294481903251827651128918916605082L}; | |
template<> inline constexpr long double pi<long double>{3.141592653589793238462643383279502884L}; | |
template<> inline constexpr long double invpi<long double>{0.318309886183790671537767526745028724L}; | |
template<> inline constexpr long double invsqrtpi<long double>{ | |
0.564189583547756286948079451560772585844050629329L}; | |
template<> inline constexpr long double ln2<long double>{0.693147180559945309417232121458176568L}; | |
template<> inline constexpr long double ln10<long double>{2.302585092994045684017991454684364208L}; | |
template<> inline constexpr long double sqrt2<long double>{1.414213562373095048801688724209698079L}; | |
template<> inline constexpr long double sqrt3<long double>{ | |
1.73205080756887729352744634150587236694280525381038062805580L}; | |
template<> inline constexpr long double invsqrt3<long double>{ | |
0.57735026918962576450914878050195745564760175127013L}; | |
template<> inline constexpr long double radian<long double>{ | |
57.295779513082320876798154814105170332405472466564L}; | |
template<> inline constexpr long double egamma<long double>{0.5772156649015328606065120900824024L}; | |
template<> inline constexpr long double phi<long double>{1.6180339887498948482045868343656381L}; | |
template<> inline constexpr long double catalan<long double>{0.915965594177219015054603514932384110774L}; | |
template<> inline constexpr long double apery<long double>{1.202056903159594285399738161511449990L}; | |
template<> inline constexpr long double glaisher<long double>{1.282427129100622636875342568869791727L}; | |
template<> inline constexpr double e<double>{2.7182818284590452354}; | |
template<> inline constexpr double log2e<double>{1.4426950408889634074}; | |
template<> inline constexpr double log10e<double>{0.43429448190325182765}; | |
template<> inline constexpr double pi<double>{3.14159265358979323846}; | |
template<> inline constexpr double invpi<double>{0.31830988618379067154}; | |
template<> inline constexpr double invsqrtpi<double>{0.564189583547756286948079451560772585844050629329}; | |
template<> inline constexpr double ln2<double>{0.69314718055994530942}; | |
template<> inline constexpr double ln10<double>{2.30258509299404568402}; | |
template<> inline constexpr double sqrt2<double>{1.41421356237309504880}; | |
template<> inline constexpr double sqrt3<double>{ | |
1.73205080756887729352744634150587236694280525381038062805580}; | |
template<> inline constexpr double invsqrt3<double>{0.57735026918962576450914878050195745564760175127013}; | |
template<> inline constexpr double radian<double>{57.295779513082320876798154814105170332405472466564}; | |
template<> inline constexpr double egamma<double>{0.5772156649015328606065120900824024}; | |
template<> inline constexpr double phi<double>{1.6180339887498948482045868343656381}; | |
template<> inline constexpr double catalan<double>{0.915965594177219015054603514932384110774}; | |
template<> inline constexpr double apery<double>{1.202056903159594285399738161511449990}; | |
template<> inline constexpr double glaisher<double>{1.282427129100622636875342568869791727}; | |
template<> inline constexpr float e<float>{2.7182818284590452354f}; | |
template<> inline constexpr float log2e<float>{1.4426950408889634074f}; | |
template<> inline constexpr float log10e<float>{0.43429448190325182765f}; | |
template<> inline constexpr float pi<float>{3.14159265358979323846f}; | |
template<> inline constexpr float invpi<float>{0.31830988618379067154f}; | |
template<> inline constexpr float invsqrtpi<float>{0.564189583547756286948079451560772585844050629329f}; | |
template<> inline constexpr float ln2<float>{0.69314718055994530942f}; | |
template<> inline constexpr float ln10<float>{2.30258509299404568402f}; | |
template<> inline constexpr float sqrt2<float>{1.41421356237309504880f}; | |
template<> inline constexpr float sqrt3<float>{1.73205080756887729352744634150587236694280525381038062805580f}; | |
template<> inline constexpr float invsqrt3<float>{0.57735026918962576450914878050195745564760175127013f}; | |
template<> inline constexpr float radian<float>{57.295779513082320876798154814105170332405472466564f}; | |
template<> inline constexpr float egamma<float>{0.5772156649015328606065120900824024f}; | |
template<> inline constexpr float phi<float>{1.6180339887498948482045868343656381f}; | |
template<> inline constexpr float catalan<float>{0.915965594177219015054603514932384110774f}; | |
template<> inline constexpr float apery<float>{1.202056903159594285399738161511449990f}; | |
template<> inline constexpr float glaisher<float>{1.282427129100622636875342568869791727f}; | |
namespace _numeric_impl { | |
template<class Integer, bool IsSigned> | |
struct trailing_bits { | |
constexpr int operator()(Integer const& integer) const noexcept | |
{ | |
return countr_zero(integer); | |
} | |
}; | |
template<class Integer> | |
struct trailing_bits<Integer, true> { | |
constexpr int operator()(Integer const& integer) const noexcept | |
{ | |
using unsigned_type = remove_signedness_t<Integer>; | |
return countr_zero(static_cast<unsigned_type>(integer)); | |
} | |
}; | |
} | |
template<class Integer> | |
constexpr int trailing_bits(Integer const& value) | |
{ | |
return value ? _numeric_impl::trailing_bits<Integer, is_signed<Integer>::value>()(value) : 0; | |
} | |
template<typename Integer> | |
constexpr int used_digits(Integer const& value, int radix = numeric_limits<Integer>::radix) | |
{ | |
return _impl::used_digits_signed<is_signed<Integer>::value>{}(unwrap(value), radix); | |
} | |
template<class Integer> | |
constexpr int leading_bits(Integer const& value) | |
{ | |
return digits<Integer>::value-cnl::used_digits(value); | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<class T> | |
struct is_fixed_point | |
: public std::false_type { | |
}; | |
template<typename Rep, int Exponent, int Radix> | |
struct is_fixed_point<fixed_point<Rep, Exponent, Radix>> | |
: public std::true_type { | |
}; | |
} | |
} | |
namespace cnl { | |
template<typename Numerator, typename Denominator> | |
struct fraction; | |
template<typename Rep, int Exponent, int Radix> | |
class fixed_point | |
: public _impl::number_base<fixed_point<Rep, Exponent, Radix>, Rep> { | |
static_assert(Radix>=2, "Radix must be two or greater"); | |
static_assert(!_impl::is_fixed_point<Rep>::value, | |
"fixed_point of fixed_point is not a supported"); | |
using _base = _impl::number_base<fixed_point<Rep, Exponent, Radix>, Rep>; | |
public: | |
using rep = Rep; | |
constexpr static int exponent = Exponent; | |
constexpr static int radix = Radix; | |
private: | |
constexpr fixed_point(rep r, int) | |
:_base(r) | |
{ | |
} | |
public: | |
fixed_point() = default; | |
template<class FromRep, int FromExponent> | |
constexpr fixed_point(fixed_point<FromRep, FromExponent, Radix> const& rhs) | |
: _base( | |
static_cast<Rep>(_impl::scale<FromExponent-exponent, Radix>( | |
_impl::from_number<Rep>(cnl::_impl::to_rep(rhs))))) | |
{ | |
} | |
template< ::cnl::intmax Value> | |
constexpr fixed_point(constant<Value> rhs) | |
: fixed_point(_impl::from_rep<fixed_point<typename decltype(rhs)::value_type, 0>>(Value)) | |
{ | |
} | |
template<class S, _impl::enable_if_t<numeric_limits<S>::is_integer, int> Dummy = 0> | |
constexpr fixed_point(S const& s) | |
: _base(static_cast<Rep>(_impl::scale<-exponent, Radix>(_impl::from_number<Rep>(s)))) | |
{ | |
} | |
template<class S, _impl::enable_if_t<numeric_limits<S>::is_iec559, int> Dummy = 0> | |
constexpr fixed_point(S s) | |
:_base(floating_point_to_rep(s)) | |
{ | |
} | |
template<typename Numerator, typename Denominator> | |
constexpr fixed_point(fraction<Numerator, Denominator> const& f); | |
template<class S, _impl::enable_if_t<numeric_limits<S>::is_iec559, int> Dummy = 0> | |
constexpr fixed_point& operator=(S s) | |
{ | |
_base::operator=(floating_point_to_rep(s)); | |
return *this; | |
} | |
template<class FromRep, int FromExponent> | |
constexpr fixed_point& operator=(fixed_point<FromRep, FromExponent, Radix> const& rhs) | |
{ | |
_base::operator=(fixed_point_to_rep(rhs)); | |
return *this; | |
} | |
template<typename Numerator, typename Denominator> | |
constexpr fixed_point& operator=(fraction<Numerator, Denominator> const& f); | |
template<class S, _impl::enable_if_t<numeric_limits<S>::is_integer, int> Dummy = 0> | |
explicit constexpr operator S() const | |
{ | |
return static_cast<S>(_impl::scale<exponent>(_impl::to_rep(*this))); | |
} | |
template<class S, _impl::enable_if_t<numeric_limits<S>::is_iec559, int> Dummy = 0> | |
explicit constexpr operator S() const | |
{ | |
static_assert(numeric_limits<S>::is_iec559, "S must be floating-point type"); | |
return S(_impl::to_rep(*this))*inverse_one<S>(); | |
} | |
template<typename, typename, typename> | |
friend struct from_rep; | |
private: | |
template<class S> | |
static constexpr _impl::enable_if_t<numeric_limits<S>::is_iec559, S> one(); | |
template<class S> | |
static constexpr _impl::enable_if_t<numeric_limits<S>::is_integer, S> one(); | |
template<class S> | |
static constexpr S inverse_one(); | |
template<class S> | |
static constexpr S rep_to_integral(rep r); | |
template<class S> | |
static constexpr rep floating_point_to_rep(S s); | |
template<class S> | |
static constexpr S rep_to_floating_point(rep r); | |
template<class FromRep, int FromExponent> | |
static constexpr rep fixed_point_to_rep(fixed_point<FromRep, FromExponent, Radix> const& rhs); | |
}; | |
template<typename Rep, int Exponent, int Radix> | |
constexpr int fixed_point<Rep, Exponent, Radix>::exponent; | |
template< ::cnl::intmax Value> | |
fixed_point(::cnl::constant<Value>) | |
-> fixed_point< | |
set_digits_t<int, _impl::max(digits_v<int>, _impl::used_digits(Value)-trailing_bits(Value))>, | |
trailing_bits(Value)>; | |
template<class Integer> | |
fixed_point(Integer) | |
-> fixed_point<Integer, 0>; | |
template<typename Rep, int Exponent, int Radix> | |
template<class S> | |
constexpr auto fixed_point<Rep, Exponent, Radix>::one() | |
-> _impl::enable_if_t<numeric_limits<S>::is_iec559, S> | |
{ | |
return power<S, -exponent, Radix>(); | |
} | |
template<typename Rep, int Exponent, int Radix> | |
template<class S> | |
constexpr auto fixed_point<Rep, Exponent, Radix>::one() | |
-> _impl::enable_if_t<numeric_limits<S>::is_integer, S> | |
{ | |
return _impl::from_rep<fixed_point<S, 0>>(1); | |
} | |
template<typename Rep, int Exponent, int Radix> | |
template<class S> | |
constexpr S fixed_point<Rep, Exponent, Radix>::inverse_one() | |
{ | |
static_assert(numeric_limits<S>::is_iec559, "S must be floating-point type"); | |
return power<S, exponent, Radix>(); | |
} | |
template<typename Rep, int Exponent, int Radix> | |
template<class S> | |
constexpr typename fixed_point<Rep, Exponent, Radix>::rep | |
fixed_point<Rep, Exponent, Radix>::floating_point_to_rep(S s) | |
{ | |
static_assert(numeric_limits<S>::is_iec559, "S must be floating-point type"); | |
return static_cast<rep>(s*one<S>()); | |
} | |
template<typename Rep, int Exponent, int Radix> | |
template<class FromRep, int FromExponent> | |
constexpr typename fixed_point<Rep, Exponent, Radix>::rep | |
fixed_point<Rep, Exponent, Radix>::fixed_point_to_rep(fixed_point<FromRep, FromExponent, Radix> const& rhs) | |
{ | |
return _impl::scale<FromExponent-exponent>(_impl::to_rep(rhs)); | |
} | |
} | |
namespace cnl { | |
template<typename ArchetypeRep, int Exponent, int Radix, typename Rep> | |
struct from_rep<fixed_point<ArchetypeRep, Exponent, Radix>, Rep> { | |
constexpr auto operator()(Rep const& r) const | |
-> fixed_point<Rep, Exponent, Radix> | |
{ | |
return fixed_point<Rep, Exponent, Radix>(r, 0); | |
} | |
}; | |
} | |
namespace cnl { | |
template<typename Rep, int Exponent, int Radix> | |
struct digits<fixed_point<Rep, Exponent, Radix>> : digits<Rep> { | |
}; | |
template<typename Rep, int Exponent, int Radix, int MinNumBits> | |
struct set_digits<fixed_point<Rep, Exponent, Radix>, MinNumBits> { | |
using type = fixed_point<set_digits_t<Rep, MinNumBits>, Exponent, Radix>; | |
}; | |
template<typename Rep, int Exponent, int Radix, typename Value> | |
struct from_value<fixed_point<Rep, Exponent, Radix>, Value> | |
: _impl::from_value_simple<fixed_point<Value, 0, Radix>, Value> { | |
}; | |
template<typename Rep, int Exponent, int Radix, typename ValueRep, int ValueExponent> | |
struct from_value<fixed_point<Rep, Exponent, Radix>, fixed_point<ValueRep, ValueExponent>> : _impl::from_value_simple< | |
fixed_point<from_value_t<Rep, ValueRep>, ValueExponent>, | |
fixed_point<ValueRep, ValueExponent>> { | |
}; | |
template<typename Rep, int Exponent, int Radix, typename Numerator, typename Denominator> | |
struct from_value<fixed_point<Rep, Exponent, Radix>, fraction<Numerator, Denominator>> { | |
constexpr auto operator()(fraction<Numerator, Denominator> const& value) const | |
-> decltype(quotient(value.numerator, value.denominator)) { | |
return quotient(value.numerator, value.denominator); | |
} | |
}; | |
template<typename Rep, int Exponent, int Radix, ::cnl::intmax Value> | |
struct from_value<fixed_point<Rep, Exponent, Radix>, constant<Value>> : _impl::from_value_simple< | |
fixed_point< | |
set_digits_t<int, _impl::max(digits<int>::value, _impl::used_digits(Value)-trailing_bits(Value))>, | |
trailing_bits(Value)>, | |
constant<Value>> { | |
}; | |
namespace _impl { | |
template <class T> | |
struct fractional_digits : std::integral_constant<int, 0> { | |
}; | |
template<typename Rep, int Exponent, int Radix> | |
struct fractional_digits<fixed_point<Rep, Exponent, Radix>> : std::integral_constant<int, -Exponent> { | |
}; | |
template <class T> | |
struct integer_digits : std::integral_constant<int, digits<T>::value - fractional_digits<T>::value> { | |
}; | |
} | |
} | |
namespace cnl { | |
template<typename Value> | |
constexpr auto make_fixed_point(Value const& value) | |
-> cnl::from_value_t<fixed_point<Value, 0>, Value> | |
{ | |
return _impl::from_number<fixed_point<Value, 0>>(value); | |
} | |
namespace _impl { | |
template<typename Number> | |
struct fixed_point_rep { | |
using type = Number; | |
}; | |
template<typename Rep, int Exponent, int Radix> | |
struct fixed_point_rep<fixed_point<Rep, Exponent, Radix>> : fixed_point_rep<Rep> { | |
}; | |
template<typename Number> | |
constexpr Number not_fixed_point(Number const& number) | |
{ | |
return number; | |
} | |
template<typename Rep, int Exponent, int Radix> | |
constexpr Rep not_fixed_point(fixed_point<Rep, Exponent, Radix> const& f) | |
{ | |
return _impl::to_rep(f); | |
} | |
template<typename Number> | |
struct exponent : constant<0> {}; | |
template<typename Rep, int Exponent, int Radix> | |
struct exponent<fixed_point<Rep, Exponent, Radix>> : constant<Exponent> { | |
}; | |
template<class Quotient, class Dividend, class Divisor> | |
struct exponent_shift : std::integral_constant< | |
int, | |
_impl::exponent<Dividend>::value | |
-_impl::exponent<Divisor>::value | |
-_impl::exponent<Quotient>::value> { | |
}; | |
struct default_quotient_tag {}; | |
template<class Quotient, class Dividend, class Divisor> | |
struct result; | |
template<typename Rep, int Exponent, int Radix, typename Dividend, typename Divisor> | |
struct result<fixed_point<Rep, Exponent, Radix>, Dividend, Divisor> { | |
using type = fixed_point<Rep, Exponent, Radix>; | |
}; | |
template<class Dividend, class Divisor> | |
struct result<default_quotient_tag, Dividend, Divisor> { | |
using natural_result = _impl::op_result<_impl::divide_op, Dividend, Divisor>; | |
static constexpr int integer_digits = | |
_impl::integer_digits<Dividend>::value+_impl::fractional_digits<Divisor>::value; | |
static constexpr int fractional_digits = | |
_impl::fractional_digits<Dividend>::value+_impl::integer_digits<Divisor>::value; | |
static constexpr auto necessary_digits = integer_digits+fractional_digits; | |
static constexpr auto natural_digits = digits<natural_result>::value; | |
static constexpr auto result_digits = _impl::max(necessary_digits, natural_digits); | |
using rep_type = set_digits_t<natural_result, result_digits>; | |
static constexpr int rep_exponent = -fractional_digits; | |
using type = fixed_point<typename fixed_point_rep<rep_type>::type, rep_exponent>; | |
}; | |
} | |
template< | |
class Quotient = _impl::default_quotient_tag, | |
class Dividend, | |
class Divisor> | |
constexpr auto quotient(Dividend const& dividend, Divisor const& divisor) | |
-> typename _impl::result<Quotient, Dividend, Divisor>::type { | |
using result_type = typename _impl::result<Quotient, Dividend, Divisor>::type; | |
using result_rep = typename result_type::rep; | |
return _impl::from_rep<result_type>( | |
static_cast<result_rep>(_impl::fixed_width_scale<_impl::exponent_shift<result_type, Dividend, Divisor>::value>( | |
static_cast<result_rep>(_impl::not_fixed_point(dividend))) | |
/_impl::not_fixed_point(divisor))); | |
} | |
} | |
namespace cnl { | |
template<typename Numerator = int, typename Denominator = Numerator> | |
struct fraction { | |
static_assert( | |
_impl::is_integral<Numerator>::value==_impl::is_integral<Denominator>::value, | |
"ill-formed if only one template parameter is floating-point"); | |
using numerator_type = Numerator; | |
using denominator_type = Denominator; | |
explicit constexpr fraction(Numerator const& n, Denominator const& d) | |
: numerator{n}, denominator{d} {} | |
explicit constexpr fraction(Numerator const& n) | |
: numerator{n}, denominator{1} {} | |
template<typename RhsNumerator, typename RhsDenominator> | |
constexpr fraction(fraction<RhsNumerator, RhsDenominator> const& f) | |
: numerator(f.numerator), denominator(f.denominator) { } | |
template<typename Scalar, _impl::enable_if_t<std::is_floating_point<Scalar>::value, int> = 0> | |
explicit constexpr operator Scalar() const | |
{ | |
return static_cast<Scalar>(numerator)/static_cast<Scalar>(denominator); | |
} | |
numerator_type numerator; | |
denominator_type denominator = 1; | |
}; | |
} | |
namespace cnl { | |
template<typename Rep, int Exponent, int Radix> | |
template<typename Numerator, typename Denominator> | |
constexpr fixed_point<Rep, Exponent, Radix>::fixed_point(fraction<Numerator, Denominator> const& f) | |
: fixed_point(quotient<fixed_point>(f.numerator, f.denominator)) | |
{ | |
} | |
template<typename Rep, int Exponent, int Radix> | |
template<typename Numerator, typename Denominator> | |
constexpr fixed_point<Rep, Exponent, Radix>& | |
fixed_point<Rep, Exponent, Radix>::operator=(fraction<Numerator, Denominator> const& f) | |
{ | |
return operator=(quotient<fixed_point>(f.numerator, f.denominator)); | |
} | |
template<typename Numerator, typename Denominator> | |
fixed_point(fraction<Numerator, Denominator>) | |
-> fixed_point< | |
typename decltype(quotient(std::declval<Numerator>(), std::declval<Denominator>()))::rep, | |
decltype(quotient(std::declval<Numerator>(), std::declval<Denominator>()))::exponent>; | |
} | |
namespace cnl { | |
namespace _impl { | |
template<typename Rep, int Exponent> | |
constexpr auto pi(int const max_iterations) { | |
using fp = fixed_point<Rep, Exponent>; | |
constexpr auto four = fixed_point<Rep, 3 - digits_v<Rep>>{4.}; | |
auto previous = fp{3.}; | |
for(auto n = 2; n != (max_iterations << 1); n += 4) { | |
auto const addend = four / ((n+0L) * (n+1L) * (n+2L)); | |
auto const subtrahend = four / ((n+2L) * (n+3L) * (n+4L)); | |
auto next = fp{previous + addend - subtrahend}; | |
if (next == previous) { | |
return next; | |
} | |
previous = next; | |
} | |
return previous; | |
} | |
template<typename Rep, int Exponent> | |
constexpr auto pi() { | |
return pi<Rep, Exponent>(0); | |
} | |
template<typename Rep, int Exponent> | |
constexpr auto e(int const max_iterations) { | |
using fp = fixed_point<Rep, Exponent>; | |
constexpr auto one = fixed_point<Rep, 2 - digits_v<Rep>>{1.}; | |
auto previous = fp{2.}; | |
auto factor = 2; | |
for (auto n = 2; n != max_iterations; ++ n, factor *= n) { | |
auto const addend = one / factor; | |
auto next = fp{previous + addend}; | |
if (next == previous) { | |
return next; | |
} | |
previous = next; | |
} | |
return previous; | |
} | |
template<typename Rep, int Exponent> | |
constexpr auto e() { | |
return e<Rep, Exponent>(0); | |
} | |
template<typename Float, typename Rep, int Exponent> | |
constexpr auto constant_with_fallback(Float constant, fixed_point<Rep, Exponent>(*procedure)()) { | |
using fp = fixed_point<Rep, Exponent>; | |
auto const required_integer_digits = used_digits(static_cast<int>(constant)); | |
constexpr auto fixed_fractional_digits = fractional_digits<fp>::value; | |
constexpr auto float_digits = std::numeric_limits<Float>::digits; | |
auto const float_fractional_digits = float_digits - required_integer_digits; | |
return (float_fractional_digits >= fixed_fractional_digits) | |
? fp{constant} | |
: procedure(); | |
} | |
} | |
template<typename Rep, int Exponent> inline constexpr fixed_point<Rep, Exponent> e<fixed_point<Rep, Exponent>> { | |
_impl::constant_with_fallback<long double, Rep, Exponent>(e<long double>, _impl::e<Rep, Exponent>) | |
}; | |
template<typename Rep, int Exponent> inline constexpr fixed_point<Rep, Exponent> log2e<fixed_point<Rep, Exponent>> { | |
fixed_point<Rep, Exponent>{ log2e<long double> } | |
}; | |
template<typename Rep, int Exponent> inline constexpr fixed_point<Rep, Exponent> log10e<fixed_point<Rep, Exponent>> { | |
fixed_point<Rep, Exponent>{ log10e<long double> } | |
}; | |
template<typename Rep, int Exponent> inline constexpr fixed_point<Rep, Exponent> pi<fixed_point<Rep, Exponent>>{ | |
_impl::constant_with_fallback<long double, Rep, Exponent>(pi<long double>, _impl::pi<Rep, Exponent>) | |
}; | |
template<typename Rep, int Exponent> inline constexpr fixed_point<Rep, Exponent> invpi<fixed_point<Rep, Exponent>> { | |
fixed_point<Rep, Exponent>{ invpi<long double> } | |
}; | |
template<typename Rep, int Exponent> inline constexpr fixed_point<Rep, Exponent> invsqrtpi<fixed_point<Rep, Exponent>> { | |
fixed_point<Rep, Exponent>{ invsqrtpi<long double> } | |
}; | |
template<typename Rep, int Exponent> inline constexpr fixed_point<Rep, Exponent> ln2<fixed_point<Rep, Exponent>> { | |
fixed_point<Rep, Exponent>{ ln2<long double> } | |
}; | |
template<typename Rep, int Exponent> inline constexpr fixed_point<Rep, Exponent> ln10<fixed_point<Rep, Exponent>> { | |
fixed_point<Rep, Exponent>{ ln10<long double> } | |
}; | |
template<typename Rep, int Exponent> inline constexpr fixed_point<Rep, Exponent> sqrt2<fixed_point<Rep, Exponent>> { | |
fixed_point<Rep, Exponent>{ sqrt2<long double> } | |
}; | |
template<typename Rep, int Exponent> inline constexpr fixed_point<Rep, Exponent> sqrt3<fixed_point<Rep, Exponent>> { | |
fixed_point<Rep, Exponent>{ sqrt3<long double> } | |
}; | |
template<typename Rep, int Exponent> inline constexpr fixed_point<Rep, Exponent> invsqrt2<fixed_point<Rep, Exponent>> { | |
fixed_point<Rep, Exponent>{ invsqrt2<long double> } | |
}; | |
template<typename Rep, int Exponent> inline constexpr fixed_point<Rep, Exponent> invsqrt3<fixed_point<Rep, Exponent>> { | |
fixed_point<Rep, Exponent>{ invsqrt3<long double> } | |
}; | |
template<typename Rep, int Exponent> inline constexpr fixed_point<Rep, Exponent> radian<fixed_point<Rep, Exponent>> { | |
fixed_point<Rep, Exponent>{ radian<long double> } | |
}; | |
template<typename Rep, int Exponent> inline constexpr fixed_point<Rep, Exponent> egamma<fixed_point<Rep, Exponent>> { | |
fixed_point<Rep, Exponent>{ egamma<long double> } | |
}; | |
template<typename Rep, int Exponent> inline constexpr fixed_point<Rep, Exponent> phi<fixed_point<Rep, Exponent>> { | |
fixed_point<Rep, Exponent>{ phi<long double> } | |
}; | |
template<typename Rep, int Exponent> inline constexpr fixed_point<Rep, Exponent> catalan<fixed_point<Rep, Exponent>> { | |
fixed_point<Rep, Exponent>{ catalan<long double> } | |
}; | |
template<typename Rep, int Exponent> inline constexpr fixed_point<Rep, Exponent> apery<fixed_point<Rep, Exponent>> { | |
fixed_point<Rep, Exponent>{ apery<long double> } | |
}; | |
template<typename Rep, int Exponent> inline constexpr fixed_point<Rep, Exponent> glaisher<fixed_point<Rep, Exponent>> { | |
fixed_point<Rep, Exponent>{ glaisher<long double> } | |
}; | |
} | |
namespace cnl { | |
struct nearest_rounding_tag { | |
}; | |
} | |
template<typename Type> | |
struct CNL_ERROR___cannot_use { | |
struct as_a_tag; | |
}; | |
namespace cnl { | |
namespace _impl { | |
struct native_tag {}; | |
template<class Tag, typename Destination, typename Source, typename Enabled=void> | |
struct tagged_convert_operator : public CNL_ERROR___cannot_use<Tag>::as_a_tag { | |
}; | |
template<typename Destination, typename Source> | |
struct tagged_convert_operator<native_tag, Destination, Source> : convert_op { | |
}; | |
template<class Tag, class Operator> | |
struct tagged_unary_operator : public CNL_ERROR___cannot_use<Tag>::as_a_tag { | |
}; | |
template<class Operator> | |
struct tagged_unary_operator<native_tag, Operator> : Operator { | |
}; | |
template<class Tag, class Operator> | |
struct tagged_binary_operator : public CNL_ERROR___cannot_use<Tag>::as_a_tag { | |
}; | |
template<class Operator> | |
struct tagged_binary_operator<native_tag, Operator> : Operator {}; | |
template<> | |
struct tagged_binary_operator<native_tag, shift_left_op> { | |
template<class Lhs, class Rhs> | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const | |
-> decltype(lhs<<rhs) | |
{ | |
using result_type = decltype(lhs<<rhs); | |
return static_cast<result_type>(static_cast<cnl::remove_signedness_t<result_type>>(lhs) << rhs); | |
} | |
}; | |
} | |
} | |
namespace cnl { | |
struct native_rounding_tag : public _impl::native_tag { | |
}; | |
} | |
namespace cnl { | |
template<typename Number, class Enable = void> | |
struct rounding; | |
template<typename Number> | |
struct rounding<Number&&> : rounding<Number> {}; | |
template<typename Number> | |
struct rounding<Number, _impl::enable_if_t<_impl::is_integral<Number>::value>> | |
: _impl::type_identity<native_rounding_tag> { | |
}; | |
template<typename Number> | |
using rounding_t = typename rounding<Number>::type; | |
} | |
namespace cnl { | |
template<typename Number, class RoundingTag, class Enable = void> | |
struct set_rounding; | |
template<typename Number> | |
struct set_rounding<Number, rounding_t<Number>, _impl::enable_if_t<!is_composite<Number>::value>> | |
: _impl::type_identity<Number> { | |
}; | |
template<typename Number, class RoundingTag> | |
struct set_rounding<Number const&, RoundingTag> | |
: set_rounding<_impl::remove_cvref_t<Number>, RoundingTag> { | |
}; | |
template<typename Number, class RoundingTag> | |
struct set_rounding<Number&, RoundingTag> | |
: set_rounding<Number, RoundingTag> { | |
}; | |
template<typename Number, class RoundingTag> | |
struct set_rounding<Number&&, RoundingTag> | |
: set_rounding<Number, RoundingTag> { | |
}; | |
template<typename Number, class RoundingTag> | |
using set_rounding_t = typename set_rounding<Number, RoundingTag>::type; | |
} | |
namespace cnl { | |
namespace _impl { | |
template<class N> | |
struct is_arithmetic_or_integer | |
: std::integral_constant<bool, std::is_floating_point<N>::value || numeric_limits<N>::is_integer> { | |
}; | |
template<class N1, class N2> | |
struct are_arithmetic_or_integer | |
: std::integral_constant<bool, | |
is_arithmetic_or_integer<N1>::value && is_arithmetic_or_integer<N2>::value> { | |
}; | |
template<typename Destination, typename Source> | |
struct tagged_convert_operator< | |
native_rounding_tag, Destination, Source> { | |
constexpr Destination operator()(Source const& from) const | |
{ | |
return static_cast<Destination>(from); | |
} | |
}; | |
template<typename Destination, typename Source> | |
struct tagged_convert_operator< | |
nearest_rounding_tag, Destination, Source, | |
enable_if_t<are_arithmetic_or_integer<Destination, Source>::value>> { | |
constexpr Destination operator()(Source const& from) const | |
{ | |
return numeric_limits<Destination>::is_integer && std::is_floating_point<Source>::value | |
? static_cast<Destination>(from+((from >= 0) ? .5 : -.5)) | |
: static_cast<Destination>(from); | |
} | |
}; | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<class Operator> | |
struct tagged_binary_operator<native_rounding_tag, Operator> | |
: tagged_binary_operator<native_tag, Operator> { | |
}; | |
template<class Operator> | |
struct tagged_binary_operator<nearest_rounding_tag, Operator> : Operator { | |
}; | |
template<> | |
struct tagged_binary_operator<nearest_rounding_tag, divide_op> { | |
template<class Lhs, class Rhs> | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const | |
-> decltype(lhs/rhs) | |
{ | |
return (((lhs<0) ^ (rhs<0)) | |
? lhs-(rhs/2) | |
: lhs+(rhs/2))/rhs; | |
} | |
}; | |
template<class RoundingTag, class Lhs, class Rhs> | |
struct divide { | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const | |
-> decltype(lhs/rhs) | |
{ | |
return tagged_binary_operator<RoundingTag, divide_op>{}(cnl::unwrap(lhs), cnl::unwrap(rhs)); | |
} | |
}; | |
template<class RoundingTag, class Lhs, class Rhs> | |
struct shift_right { | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const | |
-> decltype(lhs >> rhs) | |
{ | |
return tagged_binary_operator<RoundingTag, shift_right_op>{}(lhs, rhs); | |
} | |
}; | |
} | |
} | |
namespace cnl { | |
template<class Tag, typename Result, typename Input> | |
constexpr auto convert(Input const& from) | |
-> decltype(cnl::_impl::tagged_convert_operator<Tag, Result, Input>{}(from)) | |
{ | |
return cnl::_impl::tagged_convert_operator<Tag, Result, Input>{}(from); | |
} | |
template<class Tag, typename Lhs, typename Rhs> | |
constexpr auto add(Lhs const& lhs, Rhs const& rhs) | |
-> decltype(_impl::tagged_binary_operator<Tag, _impl::add_op>{}(lhs, rhs)) | |
{ | |
return _impl::tagged_binary_operator<Tag, _impl::add_op>{}(lhs, rhs); | |
} | |
template<class Tag, typename Lhs, typename Rhs> | |
constexpr auto subtract(Lhs const& lhs, Rhs const& rhs) | |
-> decltype(_impl::tagged_binary_operator<Tag, _impl::subtract_op>{}(lhs, rhs)) | |
{ | |
return _impl::tagged_binary_operator<Tag, _impl::subtract_op>{}(lhs, rhs); | |
} | |
template<class Tag, typename Lhs, typename Rhs> | |
constexpr auto multiply(Lhs const& lhs, Rhs const& rhs) | |
-> decltype(_impl::tagged_binary_operator<Tag, _impl::multiply_op>{}(lhs, rhs)) | |
{ | |
return _impl::tagged_binary_operator<Tag, _impl::multiply_op>{}(lhs, rhs); | |
} | |
template<class Tag, typename Lhs, typename Rhs> | |
constexpr auto divide(Lhs const& lhs, Rhs const& rhs) | |
-> decltype(cnl::_impl::tagged_binary_operator<Tag, _impl::divide_op>{}.template operator()<Lhs, Rhs>(lhs, rhs)) | |
{ | |
return cnl::_impl::tagged_binary_operator<Tag, _impl::divide_op>{}.template operator()<Lhs, Rhs>(lhs, rhs); | |
} | |
template<class Tag, typename Lhs, typename Rhs> | |
constexpr auto shift_left(Lhs const& lhs, Rhs const& rhs) | |
-> decltype(_impl::tagged_binary_operator<Tag, _impl::shift_left_op>{}(lhs, rhs)) | |
{ | |
return _impl::tagged_binary_operator<Tag, _impl::shift_left_op>{}(lhs, rhs); | |
} | |
template<class Tag, typename Lhs, typename Rhs> | |
constexpr auto shift_right(Lhs const& lhs, Rhs const& rhs) | |
-> decltype(cnl::_impl::tagged_binary_operator<Tag, _impl::shift_right_op>{}.template operator()<Lhs, Rhs>(lhs, rhs)) | |
{ | |
return cnl::_impl::tagged_binary_operator<Tag, _impl::shift_right_op>{}.template operator()<Lhs, Rhs>(lhs, rhs); | |
} | |
} | |
namespace cnl { | |
template<class Rep = int, class RoundingTag = nearest_rounding_tag> | |
class rounding_integer; | |
namespace _impl { | |
template<class T> | |
struct is_rounding_integer : std::false_type { | |
}; | |
template<class Rep, class RoundingTag> | |
struct is_rounding_integer<rounding_integer<Rep, RoundingTag>> : std::true_type { | |
}; | |
} | |
template<typename Number> | |
struct rounding< | |
Number, | |
_impl::enable_if_t< | |
is_composite<Number>::value | |
&&!_impl::is_rounding_integer<Number>::value>> | |
: rounding<_impl::to_rep_t<Number>> { | |
}; | |
template<typename Rep, class RoundingTag> | |
struct rounding<rounding_integer<Rep, RoundingTag>> | |
: _impl::type_identity<RoundingTag> { | |
}; | |
template<typename Number, class RoundingTag> | |
struct set_rounding< | |
Number, | |
RoundingTag, | |
_impl::enable_if_t< | |
is_composite<Number>::value | |
&& !_impl::is_rounding_integer<Number>::value>> | |
: _impl::type_identity<_impl::from_rep_t< | |
Number, | |
typename set_rounding<_impl::to_rep_t<Number>, RoundingTag>::type>> { | |
}; | |
template<typename InputRep, class InputRoundingTag, class OutputRoundingTag> | |
struct set_rounding<rounding_integer<InputRep, InputRoundingTag>, OutputRoundingTag> | |
: _impl::type_identity<rounding_integer<InputRep, OutputRoundingTag>> { | |
}; | |
template<class Rep, class RoundingTag> | |
class rounding_integer : public _impl::number_base<rounding_integer<Rep, RoundingTag>, Rep> { | |
static_assert(!_impl::is_rounding_integer<Rep>::value, | |
"rounding_integer of rounding_integer is not a supported"); | |
static_assert(std::is_same<native_rounding_tag, rounding_t<Rep>>::value, | |
"rounding_integer requires a Rep type that rounds natively"); | |
public: | |
using rounding = RoundingTag; | |
using _base = _impl::number_base<rounding_integer<Rep, RoundingTag>, Rep>; | |
rounding_integer() = default; | |
template<class T> | |
constexpr rounding_integer(T const& v) | |
: _base(convert<rounding, Rep>(v)) { } | |
template<class T> | |
constexpr explicit operator T() const | |
{ | |
return static_cast<T>(_impl::to_rep(*this)); | |
} | |
}; | |
template<class Rep, class RoundingTag> | |
struct digits<rounding_integer<Rep, RoundingTag>> : digits<Rep> { | |
}; | |
template<class Rep, class RoundingTag, int MinNumBits> | |
struct set_digits<rounding_integer<Rep, RoundingTag>, MinNumBits> { | |
using type = rounding_integer<set_digits_t<Rep, MinNumBits>, RoundingTag>; | |
}; | |
template<typename ArchetypeRep, class RoundingTag, typename Rep> | |
struct from_rep<rounding_integer<ArchetypeRep, RoundingTag>, Rep> { | |
constexpr auto operator()(Rep const& r) const | |
-> rounding_integer<Rep, RoundingTag> | |
{ | |
return r; | |
} | |
}; | |
template<class Rep, class RoundingTag, class Value> | |
struct from_value<rounding_integer<Rep, RoundingTag>, Value> : _impl::from_value_simple< | |
rounding_integer<Value, RoundingTag>, Value> { | |
}; | |
template<class Rep, class RoundingTag, class ValueRep, class ValueRoundingTag> | |
struct from_value<rounding_integer<Rep, RoundingTag>, rounding_integer<ValueRep, ValueRoundingTag>> | |
: _impl::from_value_simple< | |
rounding_integer<ValueRep, RoundingTag>, rounding_integer<ValueRep, ValueRoundingTag>> { | |
}; | |
template<class Rep, class RoundingTag, ::cnl::intmax Value> | |
struct from_value<rounding_integer<Rep, RoundingTag>, constant<Value>> : _impl::from_value_simple< | |
rounding_integer<typename std::conditional< | |
digits<int>::value<_impl::used_digits(Value), | |
decltype(Value), | |
int>::type, RoundingTag>, | |
constant<Value>> { | |
}; | |
template<int Digits, class Rep, class RoundingTag> | |
struct scale<Digits, 2, rounding_integer<Rep, RoundingTag>, | |
_impl::enable_if_t<Digits<0>> | |
: _impl::default_scale<Digits, 2, rounding_integer<Rep, RoundingTag>> { | |
}; | |
template<int Digits, int Radix, class Rep, class RoundingTag> | |
struct scale<Digits, Radix, rounding_integer<Rep, RoundingTag>, | |
_impl::enable_if_t<0<=Digits>> { | |
constexpr auto operator()(rounding_integer<Rep, RoundingTag> const& s) const | |
-> decltype(_impl::from_rep<rounding_integer<Rep, RoundingTag>>( | |
scale<Digits, Radix, Rep>{}(_impl::to_rep(s)))) | |
{ | |
return _impl::from_rep<rounding_integer<Rep, RoundingTag>>( | |
scale<Digits, Radix, Rep>{}(_impl::to_rep(s))); | |
} | |
}; | |
template<int Digits, class Rep, class RoundingTag> | |
struct fixed_width_scale<Digits, 2, rounding_integer<Rep, RoundingTag>, _impl::enable_if_t<0<=Digits>> { | |
constexpr auto operator()(rounding_integer<Rep, RoundingTag> const& s) const | |
-> decltype(_impl::from_rep<rounding_integer<Rep, RoundingTag>>( | |
fixed_width_scale<Digits, 2, Rep>{}(_impl::to_rep(s)))) | |
{ | |
return _impl::from_rep<rounding_integer<Rep, RoundingTag>>( | |
fixed_width_scale<Digits, 2, Rep>{}(_impl::to_rep(s))); | |
} | |
}; | |
template<class RoundingTag, class Rep> | |
constexpr auto make_rounding_integer(Rep const& value) | |
-> rounding_integer<Rep, RoundingTag> | |
{ | |
return value; | |
} | |
namespace _impl { | |
template<class Operator, class Rep, class RoundingTag> | |
struct unary_operator<Operator, rounding_integer<Rep, RoundingTag>> { | |
constexpr auto operator()(rounding_integer<Rep, RoundingTag> const& operand) const | |
-> decltype(from_rep<rounding_integer<decltype(Operator()(_impl::to_rep(operand))), RoundingTag>>( | |
Operator()(_impl::to_rep(operand)))) | |
{ | |
using result_type = rounding_integer<decltype(Operator()(_impl::to_rep(operand))), RoundingTag>; | |
return from_rep<result_type>(Operator()(_impl::to_rep(operand))); | |
} | |
}; | |
template<class Operator, class LhsRep, class RhsRep, class RoundingTag> | |
struct binary_operator<Operator, | |
rounding_integer<LhsRep, RoundingTag>, rounding_integer<RhsRep, RoundingTag>> { | |
constexpr auto operator()( | |
rounding_integer<LhsRep, RoundingTag> const& lhs, | |
rounding_integer<RhsRep, RoundingTag> const& rhs) const | |
-> decltype(make_rounding_integer<RoundingTag>(tagged_binary_operator<RoundingTag, Operator>()( | |
_impl::to_rep(lhs), _impl::to_rep(rhs)))) | |
{ | |
return make_rounding_integer<RoundingTag>( | |
tagged_binary_operator<RoundingTag, Operator>()(_impl::to_rep(lhs), _impl::to_rep(rhs))); | |
} | |
}; | |
template<class LhsRep, class RhsRep, class RoundingTag> | |
struct binary_operator<shift_right_op, | |
rounding_integer<LhsRep, RoundingTag>, rounding_integer<RhsRep, RoundingTag>> { | |
constexpr auto operator()( | |
rounding_integer<LhsRep, RoundingTag> const& lhs, | |
rounding_integer<RhsRep, RoundingTag> const& rhs) const | |
-> decltype(from_rep<rounding_integer<decltype(_impl::to_rep(lhs)>>_impl::to_rep(rhs)), RoundingTag>>( | |
_impl::to_rep(lhs)>>_impl::to_rep(rhs))) | |
{ | |
return from_rep<rounding_integer<decltype(_impl::to_rep(lhs)>>_impl::to_rep(rhs)), RoundingTag>>( | |
_impl::to_rep(lhs)>>_impl::to_rep(rhs)); | |
} | |
}; | |
template<class Operator, class LhsRep, class RhsRep, class RoundingTag> | |
struct comparison_operator<Operator, rounding_integer<LhsRep, RoundingTag>, rounding_integer<RhsRep, RoundingTag>> { | |
constexpr auto operator()( | |
rounding_integer<LhsRep, RoundingTag> const& lhs, | |
rounding_integer<RhsRep, RoundingTag> const& rhs) const | |
-> decltype(Operator()(_impl::to_rep(lhs), _impl::to_rep(rhs))) | |
{ | |
return Operator()(_impl::to_rep(lhs), _impl::to_rep(rhs)); | |
} | |
}; | |
template<class Operator, class LhsRep, class LhsRoundingTag, class RhsRep, class RhsRoundingTag> | |
struct comparison_operator<Operator, | |
rounding_integer<LhsRep, LhsRoundingTag>, rounding_integer<RhsRep, RhsRoundingTag>> { | |
constexpr auto operator()( | |
rounding_integer<LhsRep, LhsRoundingTag> const& lhs, | |
rounding_integer<RhsRep, RhsRoundingTag> const& rhs) const | |
-> decltype(comparison_operator<Operator, rounding_integer<LhsRep, common_type_t<LhsRoundingTag, RhsRoundingTag>>, rounding_integer<LhsRep, common_type_t<LhsRoundingTag, RhsRoundingTag>>>()(lhs, rhs)) | |
{ | |
using common_tag = common_type_t<LhsRoundingTag, RhsRoundingTag>; | |
return comparison_operator<Operator, rounding_integer<LhsRep, common_tag>, rounding_integer<LhsRep, common_tag>>()(lhs, rhs); | |
} | |
}; | |
template<class Operator, typename Rep, class RoundingTag> | |
struct pre_operator<Operator, rounding_integer<Rep, RoundingTag>> | |
: pre_operator<Operator, typename rounding_integer<Rep, RoundingTag>::_base> { | |
}; | |
template<class Operator, typename Rep, class RoundingTag> | |
struct post_operator<Operator, rounding_integer<Rep, RoundingTag>> | |
: post_operator<Operator, typename rounding_integer<Rep, RoundingTag>::_base> { | |
}; | |
} | |
template<class Rep, class RoundingTag> | |
struct numeric_limits<rounding_integer<Rep, RoundingTag>> | |
: numeric_limits<_impl::number_base<rounding_integer<Rep, RoundingTag>, Rep>> { | |
static constexpr bool is_integer = true; | |
}; | |
template<class Rep, class RoundingTag> | |
struct numeric_limits<rounding_integer<Rep, RoundingTag> const> | |
: numeric_limits<_impl::number_base<rounding_integer<Rep, RoundingTag>, Rep>> { | |
static constexpr bool is_integer = true; | |
}; | |
} | |
namespace std { | |
template<class Rep, class RoundingTag> | |
struct numeric_limits<cnl::rounding_integer<Rep, RoundingTag>> | |
: cnl::numeric_limits<cnl::rounding_integer<Rep, RoundingTag>> { | |
}; | |
template<class Rep, class RoundingTag> | |
struct numeric_limits<cnl::rounding_integer<Rep, RoundingTag> const> | |
: cnl::numeric_limits<cnl::rounding_integer<Rep, RoundingTag>> { | |
}; | |
} | |
namespace cnl { | |
namespace _impl { | |
template<class Result> | |
[[noreturn]] constexpr Result terminate(char const* message) noexcept | |
{ | |
std::fputs(message, | |
stderr | |
); | |
std::fputc('\n', | |
stderr | |
); | |
std::terminate(); | |
} | |
} | |
} | |
namespace cnl { | |
struct to_chars_result { | |
char* ptr; | |
std::errc ec; | |
}; | |
namespace _impl { | |
template<typename Scalar, int Base=10> | |
struct max_to_chars_chars; | |
template<typename Scalar> | |
char itoc(Scalar value) noexcept | |
{ | |
static_assert( | |
std::is_same< | |
typename rounding<Scalar>::type, | |
native_rounding_tag>::value, | |
"wrong rounding type"); | |
auto c = '0'+static_cast<int>(value); | |
return static_cast<char>(c); | |
} | |
template<class Integer> | |
char* to_chars_natural(char* const ptr, char* const last, Integer const& value) | |
{ | |
auto const quotient = value/10; | |
auto const next_ptr = quotient | |
? to_chars_natural(ptr, last, quotient) | |
: ptr; | |
if (next_ptr==last || next_ptr==nullptr) { | |
return nullptr; | |
} | |
auto const remainder = value-(quotient*10); | |
*next_ptr = itoc(remainder); | |
return next_ptr+1; | |
} | |
template<typename Number, bool is_signed = is_signed<Number>::value> | |
struct to_chars_non_zero; | |
template<typename Number> | |
struct to_chars_non_zero<Number, false> { | |
to_chars_result operator()(char* const first, char* const last, Number const& value) const | |
{ | |
return to_chars_positive(first, last, value); | |
} | |
}; | |
template<typename Number> | |
struct to_chars_non_zero<Number, true> { | |
to_chars_result operator()(char* const first, char* const last, Number const& value) const | |
{ | |
if (value>0.) { | |
return to_chars_positive(first, last, value); | |
} | |
else { | |
auto const destination_length = std::distance(first, last); | |
if (destination_length<2) { | |
return to_chars_result{last, std::errc::value_too_large}; | |
} | |
*first = '-'; | |
return to_chars_positive(first+1, last, -value); | |
} | |
} | |
}; | |
} | |
template<typename Number> | |
std::array< | |
char, | |
_impl::max_to_chars_chars<Number>::value+1> | |
to_chars(Number const& value) | |
{ | |
constexpr auto max_num_chars = _impl::max_to_chars_chars<Number>::value; | |
std::array<char, max_num_chars+1> chars; | |
auto result = to_chars(chars.data(), chars.data()+max_num_chars, value); | |
((result.ptr>chars.data()) ? static_cast<void>(0) : __builtin_unreachable()); | |
((result.ptr<=chars.data()+max_num_chars) ? static_cast<void>(0) : __builtin_unreachable()); | |
((result.ec==std::errc{}) ? static_cast<void>(0) : __builtin_unreachable()); | |
*result.ptr = '\0'; | |
return chars; | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<typename Rep, int Exponent> | |
struct max_to_chars_chars<fixed_point<Rep, Exponent>> { | |
using _scalar = cnl::fixed_point<Rep, Exponent>; | |
static constexpr auto _fractional_digits = cnl::_impl::fractional_digits<_scalar>::value; | |
static constexpr auto _sign_chars = static_cast<int>(cnl::is_signed<_scalar>::value); | |
static constexpr auto _integer_chars = ((cnl::_impl::integer_digits<_scalar>::value + 2) / 3); | |
static constexpr auto _radix_chars = static_cast<int>(_fractional_digits > 0); | |
static constexpr auto _fractional_chars = max(0, _fractional_digits); | |
static constexpr auto value = _sign_chars + _integer_chars + _radix_chars + _fractional_chars; | |
}; | |
template<typename Rep, int Exponent, int Radix, bool Flushed = (Radix==2 && Exponent<=-digits<Rep>::value)> | |
struct split; | |
template<typename Rep, int Exponent, int Radix> | |
struct split<Rep, Exponent, Radix, false> { | |
private: | |
using value_type = fixed_point<Rep, Exponent, Radix>; | |
constexpr auto integral(value_type const& scalar) const | |
-> decltype(scale<Exponent, Radix>(to_rep(scalar))) | |
{ | |
return scale<Exponent, Radix>(to_rep(scalar)); | |
} | |
template<typename Integral> | |
static auto from_integral_and_value(Integral const& integral, value_type const& value) | |
-> decltype(std::make_pair(integral, value_type{value-integral})) | |
{ | |
return std::make_pair(integral, value_type{value-integral}); | |
} | |
public: | |
constexpr auto operator()(value_type const& value) const | |
-> decltype(from_integral_and_value(integral(value), value)) | |
{ | |
return from_integral_and_value(integral(value), value); | |
} | |
}; | |
template<typename Rep, int Exponent, int Radix> | |
struct split<Rep, Exponent, Radix, true> { | |
using value_type = fixed_point<Rep, Exponent, Radix>; | |
constexpr auto operator()(value_type const& value) const | |
-> decltype(std::make_pair(Rep{}, value)) | |
{ | |
return std::make_pair(Rep{}, value); | |
} | |
}; | |
template<typename Rep, int Exponent, int Radix> | |
auto to_chars_fractional_specialized( | |
char* first, | |
char const* const last, | |
fixed_point<Rep, Exponent, Radix> value) noexcept | |
-> enable_if_t<integer_digits<fixed_point<Rep, Exponent, Radix>>::value>=4, char*> | |
{ | |
do { | |
using fixed_point = fixed_point<Rep, Exponent, Radix>; | |
((value<=numeric_limits<fixed_point>::max()/Rep{10}) ? static_cast<void>(0) : __builtin_unreachable()); | |
value = from_rep<fixed_point>( | |
cnl::fixed_width_scale<1, 10, Rep>{}(to_rep(value))); | |
auto const split = _impl::split<Rep, Exponent, Radix>{}(value); | |
*first = itoc(split.first); | |
++first; | |
value = split.second; | |
if (!value) { | |
break; | |
} | |
} | |
while (first!=last); | |
return first; | |
} | |
template<typename Rep, int Exponent, int Radix> | |
auto to_chars_fractional_specialized( | |
char* const first, | |
char* last, | |
fixed_point<Rep, Exponent, Radix> const& value) noexcept | |
-> enable_if_t<integer_digits<fixed_point<Rep, Exponent, Radix>>::value<4, char*> | |
{ | |
std::fill<char*>(first, last, '0'); | |
auto const digits = std::distance(first, last); | |
std::array<char, (Exponent*-302LL)/100> bit{ }; | |
((std::ptrdiff_t(bit.size())>=digits) ? static_cast<void>(0) : __builtin_unreachable()); | |
bit[0] = 5; | |
for (auto mask = fixed_point<Rep, Exponent, Radix>{ .5 };;) { | |
if (value & mask) { | |
auto carry = 0; | |
for (auto pos = digits-1; pos>=0; --pos) { | |
*(first+pos) = char(*(first+pos)+bit[pos]+carry); | |
if (*(first+pos)>'9') { | |
*(first+pos) = char(*(first+pos)-10); | |
carry = 1; | |
} | |
else { | |
carry = 0; | |
} | |
} | |
((carry==0) ? static_cast<void>(0) : __builtin_unreachable()); | |
} | |
mask >>= 1; | |
if (!mask) { | |
break; | |
} | |
for (auto digit = std::end(bit)-1; digit>std::begin(bit); --digit) { | |
auto const before = digit[-1]; | |
digit[-1] = char(before >> 1); | |
if (before & 1) { | |
digit[0] = char(digit[0]+5); | |
((digit[0]<10) ? static_cast<void>(0) : __builtin_unreachable()); | |
} | |
} | |
} | |
return last; | |
} | |
template<typename Rep, int Exponent, int Radix> | |
auto to_chars_fractional( | |
char* first, | |
char* last, | |
fixed_point<Rep, Exponent, Radix> const& value) noexcept | |
-> to_chars_result | |
{ | |
auto const destination_length = std::distance(first, last); | |
if (destination_length<2) { | |
return to_chars_result{first, std::errc{}}; | |
} | |
*first = '.'; | |
first++; | |
last = to_chars_fractional_specialized(first, last, value); | |
for (;;) { | |
auto const prev = last[-1]; | |
if (prev!='0') { | |
if (prev=='.') { | |
--last; | |
} | |
break; | |
} | |
--last; | |
} | |
return to_chars_result{last, std::errc{}}; | |
} | |
template<typename Rep, int Exponent, int Radix> | |
to_chars_result to_chars_positive( | |
char* const first, | |
char* const last, | |
fixed_point<Rep, Exponent, Radix> const& value) noexcept | |
{ | |
auto const split = _impl::split<Rep, Exponent, Radix>{}(value); | |
auto const natural_last = to_chars_natural(first, last, split.first); | |
if (!natural_last) { | |
return to_chars_result{last, std::errc::value_too_large}; | |
} | |
if (!split.second) { | |
return to_chars_result{natural_last, std::errc{}}; | |
} | |
return to_chars_fractional(natural_last, last, split.second); | |
} | |
} | |
template<typename Rep, int Exponent, int Radix> | |
to_chars_result to_chars( | |
char* const first, | |
char* const last, | |
cnl::fixed_point<Rep, Exponent, Radix> const& value) | |
{ | |
if (!value) { | |
if (first==last) { | |
return to_chars_result{last, std::errc::value_too_large}; | |
} | |
*first = '0'; | |
return to_chars_result{first+1, std::errc{}}; | |
} | |
using native_rounding_type = set_rounding_t<decltype(value), native_rounding_tag>; | |
auto const& native_rounding_value = static_cast<native_rounding_type>(value); | |
return _impl::to_chars_non_zero<native_rounding_type>{}( | |
first, last, native_rounding_value); | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<class Result> | |
constexpr Result unreachable(char const* ) noexcept | |
{ | |
__builtin_unreachable(); | |
} | |
} | |
} | |
namespace cnl { | |
template<typename Rep, int Exponent, int Radix> | |
constexpr auto abs(fixed_point<Rep, Exponent, Radix> const& x) noexcept | |
-> decltype(-x) | |
{ | |
return (x>=fixed_point<Rep, Exponent, Radix>{}) ? static_cast<decltype(-x)>(x) : -x; | |
} | |
namespace _impl { | |
template<class Rep> | |
constexpr Rep sqrt_solve3( | |
Rep n, | |
Rep bit, | |
Rep result) | |
{ | |
return (bit!=Rep{0}) | |
? (n>=result+bit) | |
? sqrt_solve3<Rep>( | |
static_cast<Rep>(n-(result+bit)), | |
static_cast<Rep>(bit >> 2), | |
static_cast<Rep>((result >> 1)+bit)) | |
: sqrt_solve3<Rep>( | |
n, | |
static_cast<Rep>(bit >> 2), | |
static_cast<Rep>(result >> 1)) | |
: result; | |
} | |
template<int Exponent> | |
struct sqrt_solve1 { | |
template<class Rep> | |
constexpr Rep operator()(Rep n) const | |
{ | |
using widened_rep = _impl::set_width_t<Rep, _impl::width<Rep>::value*2>; | |
return static_cast<Rep>(sqrt_solve3<widened_rep>( | |
_impl::fixed_width_scale<-Exponent>(static_cast<widened_rep>(n)), | |
widened_rep((widened_rep{1}<<((countr_used(n)+1-Exponent)&~1))>>2), | |
widened_rep{0})); | |
} | |
}; | |
} | |
template<typename Rep, int Exponent, int Radix> | |
constexpr auto | |
sqrt(fixed_point<Rep, Exponent, Radix> const& x) | |
-> fixed_point <Rep, Exponent, Radix> | |
{ | |
using type = fixed_point<Rep, Exponent, Radix>; | |
return _impl::to_rep(x)<0 | |
? _impl::unreachable<type>("negative value passed to cnl::sqrt") | |
: type{_impl::from_rep<type>(_impl::sqrt_solve1<Exponent>{}(unwrap(x)))}; | |
} | |
template<class Rep, int Exponent, int Radix, | |
_impl::enable_if_t<(Exponent<0), int> dummy = 0> | |
constexpr auto floor(fixed_point<Rep, Exponent, Radix> const& x) | |
-> decltype(_impl::from_rep<fixed_point<Rep, 0, Radix>>(_impl::to_rep(x)>>constant<-Exponent>())) { | |
static_assert( | |
Radix==2, | |
"cnl::floor(fixed_point<Rep, Exponent, Radix>) not implemented for Exponent<0 && Radix!=2"); | |
return _impl::from_rep<fixed_point<Rep, 0, Radix>>(_impl::to_rep(x)>>constant<-Exponent>()); | |
} | |
template<class Rep, int Exponent, int Radix> | |
constexpr auto floor(fixed_point<Rep, Exponent, Radix> const& x) | |
-> _impl::enable_if_t<Exponent>=0, fixed_point<Rep, Exponent, Radix>> { | |
return x; | |
} | |
namespace _impl { | |
template<int NumBits, class Enable = void> | |
struct float_of_size; | |
template<int NumBits> | |
struct float_of_size<NumBits, enable_if_t<NumBits <= sizeof(float)*8>> { | |
using type = float; | |
}; | |
template<int NumBits> | |
struct float_of_size<NumBits, enable_if_t<sizeof(float)*8 < NumBits && NumBits <= sizeof(double)*8>> { | |
using type = double; | |
}; | |
template<int NumBits> | |
struct float_of_size<NumBits, enable_if_t<sizeof(double)*8 < NumBits && NumBits <= sizeof(long double)*8>> { | |
using type = long double; | |
}; | |
template<class T> | |
using float_of_same_size = typename float_of_size<_impl::width<T>::value>::type; | |
template<typename Rep, int Exponent, int Radix, _impl::float_of_same_size<Rep>(* F)( | |
_impl::float_of_same_size<Rep>)> | |
constexpr fixed_point <Rep, Exponent, Radix> | |
crib(fixed_point<Rep, Exponent, Radix> const& x) noexcept | |
{ | |
using floating_point = _impl::float_of_same_size<Rep>; | |
return static_cast<fixed_point<Rep, Exponent, Radix>>(F(static_cast<floating_point>(x))); | |
} | |
} | |
template<typename Rep, int Exponent, int Radix> | |
constexpr fixed_point <Rep, Exponent, Radix> | |
sin(fixed_point<Rep, Exponent, Radix> const& x) noexcept | |
{ | |
return _impl::crib<Rep, Exponent, Radix, std::sin>(x); | |
} | |
template<typename Rep, int Exponent, int Radix> | |
constexpr fixed_point <Rep, Exponent, Radix> | |
cos(fixed_point<Rep, Exponent, Radix> const& x) noexcept | |
{ | |
return _impl::crib<Rep, Exponent, Radix, std::cos>(x); | |
} | |
template<typename Rep, int Exponent, int Radix> | |
constexpr fixed_point <Rep, Exponent, Radix> | |
exp(fixed_point<Rep, Exponent, Radix> const& x) noexcept | |
{ | |
return _impl::crib<Rep, Exponent, Radix, std::exp>(x); | |
} | |
template<typename Rep, int Exponent, int Radix> | |
constexpr fixed_point <Rep, Exponent, Radix> | |
pow(fixed_point<Rep, Exponent, Radix> const& x) noexcept | |
{ | |
return _impl::crib<Rep, Exponent, Radix, std::pow>(x); | |
} | |
template<typename Rep, int Exponent, int Radix> | |
::std::ostream& operator<<(::std::ostream& out, fixed_point<Rep, Exponent, Radix> const& fp) | |
{ | |
return out << to_chars(fp).data(); | |
} | |
template<typename Rep, int Exponent, int Radix> | |
::std::istream& operator>>(::std::istream& in, fixed_point <Rep, Exponent, Radix>& fp) | |
{ | |
long double ld; | |
in >> ld; | |
fp = ld; | |
return in; | |
} | |
} | |
namespace cnl { | |
template<typename Rep, int Exponent, int Radix> | |
struct numeric_limits<cnl::fixed_point<Rep, Exponent, Radix>> | |
: numeric_limits<cnl::_impl::number_base<cnl::fixed_point<Rep, Exponent, Radix>, Rep>> { | |
using _value_type = cnl::fixed_point<Rep, Exponent, Radix>; | |
using _rep = typename _value_type::rep; | |
using _rep_numeric_limits = numeric_limits<_rep>; | |
static constexpr _value_type min() noexcept | |
{ | |
return _impl::from_rep<_value_type>(_rep{1}); | |
} | |
static constexpr _value_type max() noexcept | |
{ | |
return _impl::from_rep<_value_type>(_rep_numeric_limits::max()); | |
} | |
static constexpr _value_type lowest() noexcept | |
{ | |
return _impl::from_rep<_value_type>(_rep_numeric_limits::lowest()); | |
} | |
static constexpr bool is_integer = false; | |
static constexpr _value_type epsilon() noexcept | |
{ | |
return _impl::from_rep<_value_type>(_rep{1}); | |
} | |
static constexpr _value_type round_error() noexcept | |
{ | |
return _impl::from_rep<_value_type>(_rep{0}); | |
} | |
static constexpr _value_type infinity() noexcept | |
{ | |
return _impl::from_rep<_value_type>(_rep{0}); | |
} | |
static constexpr _value_type quiet_NaN() noexcept | |
{ | |
return _impl::from_rep<_value_type>(_rep{0}); | |
} | |
static constexpr _value_type signaling_NaN() noexcept | |
{ | |
return _impl::from_rep<_value_type>(_rep{0}); | |
} | |
static constexpr _value_type denorm_min() noexcept | |
{ | |
return _impl::from_rep<_value_type>(_rep{1}); | |
} | |
}; | |
} | |
namespace std { | |
template<typename Rep, int Exponent, int Radix> | |
struct numeric_limits<cnl::fixed_point<Rep, Exponent, Radix>> | |
: cnl::numeric_limits<cnl::fixed_point<Rep, Exponent, Radix>> { | |
}; | |
template<typename Rep, int Exponent, int Radix> | |
struct numeric_limits<cnl::fixed_point<Rep, Exponent, Radix> const> | |
: cnl::numeric_limits<cnl::fixed_point<Rep, Exponent, Radix>> { | |
}; | |
} | |
namespace cnl { | |
namespace _impl { | |
template<typename Operator, typename Rep, int Exponent, int Radix> | |
struct unary_operator<Operator, fixed_point<Rep, Exponent, Radix>> { | |
constexpr auto operator()(fixed_point<Rep, Exponent, Radix> const& rhs) const | |
-> decltype(from_rep<fixed_point<decltype(Operator()(_impl::to_rep(rhs))), Exponent, Radix>>(Operator()(_impl::to_rep(rhs)))) | |
{ | |
return from_rep<fixed_point<decltype(Operator()(_impl::to_rep(rhs))), Exponent, Radix>>(Operator()(_impl::to_rep(rhs))); | |
} | |
}; | |
template<typename Operator, typename LhsRep, typename RhsRep, int Exponent, int Radix> | |
struct comparison_operator<Operator, | |
fixed_point<LhsRep, Exponent, Radix>, | |
fixed_point<RhsRep, Exponent, Radix>> { | |
constexpr auto operator()( | |
fixed_point<LhsRep, Exponent, Radix> const& lhs, | |
fixed_point<RhsRep, Exponent, Radix> const& rhs) const | |
-> decltype(Operator{}(_impl::to_rep(lhs), _impl::to_rep(rhs))) | |
{ | |
return Operator{}(_impl::to_rep(lhs), _impl::to_rep(rhs)); | |
} | |
}; | |
template<typename Operator, typename LhsRep, int LhsExponent, typename RhsRep, int RhsExponent, int Radix> | |
struct comparison_operator<Operator, | |
fixed_point<LhsRep, LhsExponent, Radix>, | |
fixed_point<RhsRep, RhsExponent, Radix>, | |
enable_if_t<(LhsExponent<RhsExponent)>> { | |
static constexpr int shiftage = RhsExponent - LhsExponent; | |
using lhs_type = fixed_point<LhsRep, LhsExponent, Radix>; | |
using rhs_type = fixed_point<decltype(std::declval<RhsRep>()<<constant<shiftage>()), LhsExponent, Radix>; | |
using operator_type = comparison_operator<Operator, lhs_type, rhs_type>; | |
constexpr auto operator()( | |
fixed_point<LhsRep, LhsExponent, Radix> const& lhs, | |
fixed_point<RhsRep, RhsExponent, Radix> const& rhs) const | |
-> decltype(operator_type{}(lhs, rhs)) | |
{ | |
return operator_type{}(lhs, rhs); | |
} | |
}; | |
template<typename Operator, typename LhsRep, int LhsExponent, typename RhsRep, int RhsExponent, int Radix> | |
struct comparison_operator<Operator, | |
fixed_point<LhsRep, LhsExponent, Radix>, | |
fixed_point<RhsRep, RhsExponent, Radix>, | |
enable_if_t<(RhsExponent<LhsExponent)>> { | |
static constexpr int shiftage = LhsExponent - RhsExponent; | |
using lhs_type = fixed_point<decltype(std::declval<LhsRep>()<<constant<shiftage>()), RhsExponent, Radix>; | |
using rhs_type = fixed_point<RhsRep, RhsExponent, Radix>; | |
using operator_type = comparison_operator<Operator, lhs_type, rhs_type>; | |
constexpr auto operator()( | |
fixed_point<LhsRep, LhsExponent, Radix> const& lhs, | |
fixed_point<RhsRep, RhsExponent, Radix> const& rhs) const | |
-> decltype(operator_type{}(lhs, rhs)) | |
{ | |
return operator_type{}(lhs, rhs); | |
} | |
}; | |
template<typename LhsRep, int LhsExponent, typename RhsRep, int RhsExponent, int Radix> | |
struct binary_operator<multiply_op, fixed_point<LhsRep, LhsExponent, Radix>, fixed_point<RhsRep, RhsExponent, Radix>> { | |
constexpr auto operator()( | |
fixed_point<LhsRep, LhsExponent, Radix> const& lhs, | |
fixed_point<RhsRep, RhsExponent, Radix> const& rhs) const | |
-> decltype(from_rep<fixed_point< | |
decltype(_impl::to_rep(lhs)*_impl::to_rep(rhs)), | |
LhsExponent+RhsExponent, | |
Radix>>(_impl::to_rep(lhs)*_impl::to_rep(rhs))) | |
{ | |
return from_rep<fixed_point< | |
decltype(_impl::to_rep(lhs)*_impl::to_rep(rhs)), | |
LhsExponent+RhsExponent, | |
Radix>>(_impl::to_rep(lhs)*_impl::to_rep(rhs)); | |
} | |
}; | |
template<typename LhsRep, int LhsExponent, typename RhsRep, int RhsExponent, int Radix> | |
struct binary_operator<divide_op, fixed_point<LhsRep, LhsExponent, Radix>, fixed_point<RhsRep, RhsExponent, Radix>> { | |
constexpr auto operator()( | |
fixed_point<LhsRep, LhsExponent, Radix> const& lhs, | |
fixed_point<RhsRep, RhsExponent, Radix> const& rhs) const | |
-> decltype(from_rep<fixed_point< | |
decltype(_impl::to_rep(lhs)/_impl::to_rep(rhs)), | |
LhsExponent-RhsExponent, | |
Radix>>(_impl::to_rep(lhs)/_impl::to_rep(rhs))) | |
{ | |
return from_rep<fixed_point< | |
decltype(_impl::to_rep(lhs)/_impl::to_rep(rhs)), | |
LhsExponent-RhsExponent, | |
Radix>>(_impl::to_rep(lhs)/_impl::to_rep(rhs)); | |
} | |
}; | |
template<typename LhsRep, int LhsExponent, typename RhsRep, int RhsExponent, int Radix> | |
struct binary_operator<modulo_op, fixed_point<LhsRep, LhsExponent, Radix>, fixed_point<RhsRep, RhsExponent, Radix>> { | |
constexpr auto operator()( | |
fixed_point<LhsRep, LhsExponent, Radix> const& lhs, | |
fixed_point<RhsRep, RhsExponent, Radix> const& rhs) const | |
-> decltype(from_rep<fixed_point< | |
decltype(_impl::to_rep(lhs)%_impl::to_rep(rhs)), | |
LhsExponent, | |
Radix>>(_impl::to_rep(lhs)%_impl::to_rep(rhs))) | |
{ | |
return from_rep<fixed_point< | |
decltype(_impl::to_rep(lhs)%_impl::to_rep(rhs)), | |
LhsExponent, | |
Radix>>(_impl::to_rep(lhs)%_impl::to_rep(rhs)); | |
} | |
}; | |
template<class Operator> struct is_zero_degree : std::true_type {}; | |
template<> struct is_zero_degree<multiply_op> : std::false_type {}; | |
template<> struct is_zero_degree<divide_op> : std::false_type {}; | |
template<> struct is_zero_degree<modulo_op> : std::false_type {}; | |
template<> struct is_zero_degree<shift_left_op> : std::false_type {}; | |
template<> struct is_zero_degree<shift_right_op> : std::false_type {}; | |
template<class Operator, class LhsRep, class RhsRep, int Exponent, int Radix> | |
struct binary_operator<Operator, fixed_point<LhsRep, Exponent, Radix>, fixed_point<RhsRep, Exponent, Radix>, | |
enable_if_t<is_zero_degree<Operator>::value>> { | |
constexpr auto operator()( | |
fixed_point<LhsRep, Exponent, Radix> const& lhs, fixed_point<RhsRep, Exponent, Radix> const& rhs) const | |
-> decltype(from_rep<fixed_point< | |
decltype(Operator()(_impl::to_rep(lhs), _impl::to_rep(rhs))), | |
Exponent, | |
Radix>>(Operator()(_impl::to_rep(lhs), _impl::to_rep(rhs)))) | |
{ | |
return from_rep<fixed_point< | |
decltype(Operator()(_impl::to_rep(lhs), _impl::to_rep(rhs))), | |
Exponent, | |
Radix>>(Operator()(_impl::to_rep(lhs), _impl::to_rep(rhs))); | |
} | |
}; | |
template<typename Operator, typename LhsRep, int LhsExponent, typename RhsRep, int RhsExponent, int Radix> | |
struct binary_operator<Operator, fixed_point<LhsRep, LhsExponent, Radix>, fixed_point<RhsRep, RhsExponent, Radix>, | |
enable_if_t<is_zero_degree<Operator>::value>> { | |
private: | |
static constexpr int _common_exponent = min(LhsExponent, RhsExponent); | |
static constexpr int _lhs_left_shift = LhsExponent-_common_exponent; | |
static constexpr int _rhs_left_shift = RhsExponent-_common_exponent; | |
public: | |
constexpr auto operator()( | |
fixed_point<LhsRep, LhsExponent, Radix> const& lhs, fixed_point<RhsRep, RhsExponent, Radix> const& rhs) const | |
-> decltype(Operator{}( | |
from_rep<fixed_point<LhsRep, _common_exponent, Radix>>( | |
_impl::scale<_lhs_left_shift, Radix>(_impl::to_rep(lhs))), | |
from_rep<fixed_point<RhsRep, _common_exponent, Radix>>( | |
_impl::scale<_rhs_left_shift, Radix>(_impl::to_rep(rhs))))) | |
{ | |
return Operator{}( | |
from_rep<fixed_point<LhsRep, _common_exponent, Radix>>( | |
_impl::scale<_lhs_left_shift, Radix>(_impl::to_rep(lhs))), | |
from_rep<fixed_point<RhsRep, _common_exponent, Radix>>( | |
_impl::scale<_rhs_left_shift, Radix>(_impl::to_rep(rhs)))); | |
} | |
}; | |
template<typename Operator, typename Rep, int Exponent, int Radix> | |
struct pre_operator<Operator, fixed_point<Rep, Exponent, Radix>> { | |
constexpr auto operator()(fixed_point<Rep, Exponent, Radix>& rhs) const | |
-> decltype(typename pre_to_assign<Operator>::type{}(rhs, 1)) | |
{ | |
return typename pre_to_assign<Operator>::type{}(rhs, 1); | |
} | |
}; | |
template<typename Operator, typename Rep, int Exponent, int Radix> | |
struct post_operator<Operator, fixed_point<Rep, Exponent, Radix>> { | |
constexpr fixed_point<Rep, Exponent, Radix> operator()(fixed_point<Rep, Exponent, Radix>& rhs) const | |
{ | |
auto copy = rhs; | |
typename post_to_assign<Operator>::type{}(rhs, 1); | |
return copy; | |
} | |
}; | |
} | |
template<typename LhsRep, int LhsExponent, int LhsRadix, typename Rhs> | |
constexpr auto operator<<(fixed_point<LhsRep, LhsExponent, LhsRadix> const& lhs, Rhs const& rhs) | |
-> decltype(_impl::from_rep<fixed_point<decltype(_impl::to_rep(lhs) << int(rhs)), LhsExponent, LhsRadix>>( | |
_impl::to_rep(lhs) << int(rhs))) | |
{ | |
return _impl::from_rep<fixed_point<decltype(_impl::to_rep(lhs) << int(rhs)), LhsExponent, LhsRadix>>( | |
_impl::to_rep(lhs) << int(rhs)); | |
} | |
template<typename LhsRep, int LhsExponent, int LhsRadix, typename Rhs> | |
constexpr auto operator>>(fixed_point<LhsRep, LhsExponent, LhsRadix> const& lhs, Rhs const& rhs) | |
-> decltype(_impl::from_rep<fixed_point< | |
decltype(_impl::to_rep(lhs) >> int(rhs)), | |
LhsExponent, | |
LhsRadix>>(_impl::to_rep(lhs) >> int(rhs))) | |
{ | |
return _impl::from_rep<fixed_point< | |
decltype(_impl::to_rep(lhs) >> int(rhs)), | |
LhsExponent, | |
LhsRadix>>(_impl::to_rep(lhs) >> int(rhs)); | |
} | |
template<typename LhsRep, int LhsExponent, int LhsRadix, ::cnl::intmax RhsValue> | |
constexpr fixed_point<LhsRep, LhsExponent+static_cast<int>(RhsValue), LhsRadix> | |
operator<<(fixed_point<LhsRep, LhsExponent, LhsRadix> const& lhs, constant<RhsValue>) | |
{ | |
return _impl::from_rep<fixed_point<LhsRep, LhsExponent+static_cast<int>(RhsValue), LhsRadix>>( | |
_impl::to_rep(lhs)); | |
} | |
template<typename LhsRep, int LhsExponent, int LhsRadix, ::cnl::intmax RhsValue> | |
constexpr fixed_point<LhsRep, LhsExponent-static_cast<int>(RhsValue), LhsRadix> | |
operator>>(fixed_point<LhsRep, LhsExponent, LhsRadix> const& lhs, constant<RhsValue>) | |
{ | |
return _impl::from_rep<fixed_point<LhsRep, LhsExponent-static_cast<int>(RhsValue), LhsRadix>>( | |
_impl::to_rep(lhs)); | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
enum class polarity { | |
negative = -1, | |
neutral = 0, | |
positive = 1 | |
}; | |
constexpr polarity operator-(polarity const& p) | |
{ | |
return static_cast<polarity>(-static_cast<int>(p)); | |
} | |
constexpr polarity operator*(polarity const& lhs, polarity const& rhs) | |
{ | |
return static_cast<polarity>(static_cast<int>(lhs)*static_cast<int>(rhs)); | |
} | |
template<typename T> | |
constexpr polarity measure_polarity(T const& value) | |
{ | |
return (value>T{}) | |
?polarity::positive | |
:(value<T{}) | |
?polarity::negative | |
:polarity::neutral; | |
} | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<typename Operator, typename OverflowTag, polarity Polarity> | |
struct overflow_operator; | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template< | |
typename ResultRep, int ResultExponent, | |
typename InputRep, int InputExponent, | |
int Radix> | |
struct tagged_convert_operator< | |
nearest_rounding_tag, | |
fixed_point<ResultRep, ResultExponent, Radix>, | |
fixed_point<InputRep, InputExponent, Radix>, | |
_impl::enable_if_t<(ResultExponent <= InputExponent)>> | |
: tagged_convert_operator< | |
native_rounding_tag, | |
fixed_point<ResultRep, ResultExponent, Radix>, | |
fixed_point<InputRep, InputExponent, Radix>> { | |
}; | |
template< | |
typename ResultRep, int ResultExponent, | |
typename InputRep, int InputExponent, | |
int Radix> | |
struct tagged_convert_operator< | |
nearest_rounding_tag, | |
fixed_point<ResultRep, ResultExponent, Radix>, | |
fixed_point<InputRep, InputExponent, Radix>, | |
_impl::enable_if_t<!(ResultExponent<=InputExponent)>> { | |
private: | |
using _result = fixed_point<ResultRep, ResultExponent, Radix>; | |
using _input = fixed_point<InputRep, InputExponent, Radix>; | |
static constexpr _input half() | |
{ | |
return static_cast<_input>(_impl::from_rep<_result>(1))/2; | |
} | |
public: | |
constexpr _result operator()(_input const& from) const | |
{ | |
return static_cast<_result>(from+((from >= 0) ? half() : -half())); | |
} | |
}; | |
template< | |
typename ResultRep, int ResultExponent, int ResultRadix, | |
typename Input> | |
struct tagged_convert_operator< | |
nearest_rounding_tag, | |
fixed_point<ResultRep, ResultExponent, ResultRadix>, | |
Input, | |
_impl::enable_if_t<std::is_floating_point<Input>::value>> { | |
private: | |
using _result = fixed_point<ResultRep, ResultExponent, ResultRadix>; | |
static constexpr Input half() | |
{ | |
return cnl::power<Input, ResultExponent-1, ResultRadix>(); | |
} | |
public: | |
constexpr _result operator()(Input const& from) const | |
{ | |
return static_cast<_result>(from+((from >= 0) ? half() : -half())); | |
} | |
}; | |
template< | |
typename ResultRep, int ResultExponent, int ResultRadix, | |
typename Input> | |
struct tagged_convert_operator< | |
nearest_rounding_tag, | |
fixed_point<ResultRep, ResultExponent, ResultRadix>, | |
Input, | |
_impl::enable_if_t<cnl::numeric_limits<Input>::is_integer>> | |
: tagged_convert_operator< | |
nearest_rounding_tag, | |
fixed_point<ResultRep, ResultExponent, ResultRadix>, | |
fixed_point<Input>> { | |
}; | |
template< | |
typename Result, | |
typename InputRep, int InputExponent, int InputRadix> | |
struct tagged_convert_operator< | |
nearest_rounding_tag, | |
Result, | |
fixed_point<InputRep, InputExponent, InputRadix>, | |
_impl::enable_if_t<cnl::numeric_limits<Result>::is_integer>> { | |
using _input = fixed_point<InputRep, InputExponent, InputRadix>; | |
constexpr Result operator()(_input const& from) const | |
{ | |
return _impl::to_rep( | |
_impl::tagged_convert_operator<nearest_rounding_tag, fixed_point<Result>, _input>{}( | |
from)); | |
} | |
}; | |
} | |
} | |
namespace cnl { | |
using std::to_string; | |
template<typename Rep, int Exponent> | |
std::string to_string(cnl::fixed_point<Rep, Exponent> const& value) | |
{ | |
auto chars = to_chars(value); | |
return chars.data(); | |
} | |
} | |
namespace cnl { | |
template<int Digits, int Exponent = 0, class Narrowest = signed> | |
using elastic_number = fixed_point<elastic_integer<Digits, Narrowest>, Exponent>; | |
template< | |
typename Narrowest = int, | |
::cnl::intmax Value = 0> | |
constexpr auto | |
make_elastic_number(constant<Value>) | |
-> elastic_number< | |
_impl::max(digits<constant<Value>>::value-trailing_bits(Value), 1), | |
trailing_bits(Value), | |
Narrowest> | |
{ | |
return Value; | |
} | |
template<typename Narrowest = void, typename Integral = int> | |
constexpr auto | |
make_elastic_number(Integral const& value) | |
-> elastic_number< | |
numeric_limits<Integral>::digits, | |
0, | |
typename std::conditional< | |
std::is_same<void, Narrowest>::value, | |
_impl::adopt_signedness_t<int, Integral>, | |
Narrowest>::type> | |
{ | |
return {value}; | |
} | |
template<typename Narrowest = void, typename Rep = int, int Exponent = 0, int Radix = 2> | |
constexpr auto | |
make_elastic_number(fixed_point<Rep, Exponent, Radix> const& value) | |
-> elastic_number< | |
numeric_limits<Rep>::digits, | |
Exponent, | |
typename std::conditional< | |
std::is_same<void, Narrowest>::value, | |
_impl::adopt_signedness_t<int, Rep>, | |
Narrowest>::type> | |
{ | |
return {value}; | |
} | |
namespace literals { | |
template<char... Chars> | |
constexpr auto operator "" _elastic() | |
-> decltype(make_elastic_number<int>( | |
constant<_cnlint_impl::parse<sizeof...(Chars)+1>({Chars..., '\0'})>{})) | |
{ | |
return make_elastic_number<int>( | |
constant<_cnlint_impl::parse<sizeof...(Chars)+1>({Chars..., '\0'})>{}); | |
} | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<typename Numerator, typename Denominator> | |
constexpr fraction<Numerator, Denominator> make_fraction(Numerator const& n, Denominator const& d) | |
{ | |
return fraction<Numerator, Denominator>{n, d}; | |
} | |
template<typename Numerator> | |
constexpr fraction<Numerator, Numerator> make_fraction(Numerator const& n) | |
{ | |
return fraction<Numerator, Numerator>{n, 1}; | |
} | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<typename N, typename D> | |
constexpr auto abs(fraction<N, D> const& f) | |
-> decltype(make_fraction(abs(f.numerator), abs(f.denominator))) | |
{ | |
return make_fraction(abs(f.numerator), abs(f.denominator)); | |
} | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<typename Numerator, typename Denominator> | |
constexpr auto gcd(fraction<Numerator, Denominator> const& f) { | |
using std::gcd; | |
return gcd(f.numerator, f.denominator); | |
} | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<typename Numerator, typename Denominator, typename Gcd> | |
constexpr auto reduce_from_gcd(fraction<Numerator, Denominator> const& f, Gcd const& gcd) | |
{ | |
return make_fraction(f.numerator/gcd, f.denominator/gcd); | |
} | |
template<typename Numerator, typename Denominator> | |
constexpr auto reduce(fraction<Numerator, Denominator> const& f) | |
{ | |
return reduce_from_gcd(f, gcd(f)); | |
} | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<typename Numerator, typename Denominator> | |
constexpr auto canonical_from_reduce(fraction<Numerator, Denominator> const& f) | |
-> decltype(-f) | |
{ | |
return (f.denominator<Denominator(0.)) ? -f : f; | |
} | |
template<typename Numerator, typename Denominator> | |
constexpr auto canonical(fraction<Numerator, Denominator> const& f) | |
{ | |
return canonical_from_reduce(reduce(f)); | |
} | |
} | |
} | |
namespace std { | |
template<typename Numerator, typename Denominator> | |
struct hash<cnl::fraction<Numerator, Denominator>> { | |
static_assert( | |
cnl::_impl::is_integral_v<Numerator>&&cnl::_impl::is_integral_v<Numerator>, | |
"std::hash<cnl::fractional<T>> - T must be an integer"); | |
constexpr size_t operator()(cnl::fraction<Numerator, Denominator> const& value) const | |
{ | |
return from_canonical(cnl::_impl::canonical(value)); | |
} | |
private: | |
static constexpr size_t from_canonical(cnl::fraction<Numerator, Denominator> const& value) | |
{ | |
return from_canonical_hashes( | |
hash<Numerator>{}(value.numerator), | |
hash<Denominator>{}(value.denominator)); | |
} | |
static constexpr size_t from_canonical_hashes(size_t const n, size_t const d) | |
{ | |
return n ^ cnl::rotl(d, cnl::_impl::width<size_t>::value / 2); | |
} | |
}; | |
} | |
namespace cnl { | |
template<typename RhsNumerator, typename RhsDenominator> | |
constexpr auto operator+(fraction<RhsNumerator, RhsDenominator> const& rhs) | |
-> decltype(_impl::make_fraction(+rhs.numerator, +rhs.denominator)) | |
{ | |
return _impl::make_fraction(+rhs.numerator, +rhs.denominator); | |
} | |
template<typename RhsNumerator, typename RhsDenominator> | |
constexpr auto operator-(fraction<RhsNumerator, RhsDenominator> const& rhs) | |
-> decltype(_impl::make_fraction(-rhs.numerator, -rhs.denominator)) | |
{ | |
return _impl::make_fraction(-rhs.numerator, -rhs.denominator); | |
} | |
template<typename LhsNumerator, typename LhsDenominator, typename RhsNumerator, typename RhsDenominator> | |
constexpr auto operator+( | |
fraction<LhsNumerator, LhsDenominator> const& lhs, | |
fraction<RhsNumerator, RhsDenominator> const& rhs) | |
-> decltype(_impl::make_fraction( | |
lhs.numerator*rhs.denominator+rhs.numerator*lhs.denominator, lhs.denominator*rhs.denominator)) | |
{ | |
return _impl::make_fraction( | |
lhs.numerator*rhs.denominator+rhs.numerator*lhs.denominator, lhs.denominator*rhs.denominator); | |
} | |
template<typename LhsNumerator, typename LhsDenominator, typename RhsNumerator, typename RhsDenominator> | |
constexpr auto operator-( | |
fraction<LhsNumerator, LhsDenominator> const& lhs, | |
fraction<RhsNumerator, RhsDenominator> const& rhs) | |
-> decltype(_impl::make_fraction( | |
lhs.numerator*rhs.denominator-rhs.numerator*lhs.denominator, lhs.denominator*rhs.denominator)) | |
{ | |
return _impl::make_fraction( | |
lhs.numerator*rhs.denominator-rhs.numerator*lhs.denominator, lhs.denominator*rhs.denominator); | |
} | |
template<typename LhsNumerator, typename LhsDenominator, typename RhsNumerator, typename RhsDenominator> | |
constexpr auto operator*( | |
fraction<LhsNumerator, LhsDenominator> const& lhs, | |
fraction<RhsNumerator, RhsDenominator> const& rhs) | |
-> decltype(_impl::make_fraction(lhs.numerator*rhs.numerator, lhs.denominator*rhs.denominator)) | |
{ | |
return _impl::make_fraction(lhs.numerator*rhs.numerator, lhs.denominator*rhs.denominator); | |
} | |
template<typename LhsNumerator, typename LhsDenominator, typename RhsNumerator, typename RhsDenominator> | |
constexpr auto operator/( | |
fraction<LhsNumerator, LhsDenominator> const& lhs, | |
fraction<RhsNumerator, RhsDenominator> const& rhs) | |
-> decltype(_impl::make_fraction(lhs.numerator*rhs.denominator, lhs.denominator*rhs.numerator)) | |
{ | |
return _impl::make_fraction(lhs.numerator*rhs.denominator, lhs.denominator*rhs.numerator); | |
} | |
template<typename LhsNumerator, typename LhsDenominator, typename RhsNumerator, typename RhsDenominator> | |
constexpr auto operator==( | |
fraction<LhsNumerator, LhsDenominator> const& lhs, | |
fraction<RhsNumerator, RhsDenominator> const& rhs) | |
-> decltype(lhs.numerator*rhs.denominator==rhs.numerator*lhs.denominator) | |
{ | |
return lhs.numerator*rhs.denominator==rhs.numerator*lhs.denominator; | |
} | |
template<typename LhsNumerator, typename LhsDenominator, typename RhsNumerator, typename RhsDenominator> | |
constexpr auto operator!=( | |
fraction<LhsNumerator, LhsDenominator> const& lhs, | |
fraction<RhsNumerator, RhsDenominator> const& rhs) | |
-> decltype(lhs.numerator*rhs.denominator!=rhs.numerator*lhs.denominator) | |
{ | |
return lhs.numerator*rhs.denominator!=rhs.numerator*lhs.denominator; | |
} | |
template<typename LhsNumerator, typename LhsDenominator, typename RhsNumerator, typename RhsDenominator> | |
constexpr auto operator<( | |
fraction<LhsNumerator, LhsDenominator> const& lhs, | |
fraction<RhsNumerator, RhsDenominator> const& rhs) | |
-> decltype(lhs.numerator*rhs.denominator<rhs.numerator*lhs.denominator) | |
{ | |
return lhs.numerator*rhs.denominator<rhs.numerator*lhs.denominator; | |
} | |
template<typename LhsNumerator, typename LhsDenominator, typename RhsNumerator, typename RhsDenominator> | |
constexpr auto operator>( | |
fraction<LhsNumerator, LhsDenominator> const& lhs, | |
fraction<RhsNumerator, RhsDenominator> const& rhs) | |
-> decltype(lhs.numerator*rhs.denominator>rhs.numerator*lhs.denominator) | |
{ | |
return lhs.numerator*rhs.denominator>rhs.numerator*lhs.denominator; | |
} | |
template<typename LhsNumerator, typename LhsDenominator, typename RhsNumerator, typename RhsDenominator> | |
constexpr auto operator<=( | |
fraction<LhsNumerator, LhsDenominator> const& lhs, | |
fraction<RhsNumerator, RhsDenominator> const& rhs) | |
-> decltype(lhs.numerator*rhs.denominator<=rhs.numerator*lhs.denominator) | |
{ | |
return lhs.numerator*rhs.denominator<=rhs.numerator*lhs.denominator; | |
} | |
template<typename LhsNumerator, typename LhsDenominator, typename RhsNumerator, typename RhsDenominator> | |
constexpr auto operator>=( | |
fraction<LhsNumerator, LhsDenominator> const& lhs, | |
fraction<RhsNumerator, RhsDenominator> const& rhs) | |
-> decltype(lhs.numerator*rhs.denominator>=rhs.numerator*lhs.denominator) | |
{ | |
return lhs.numerator*rhs.denominator>=rhs.numerator*lhs.denominator; | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
using std::to_string; | |
template<typename N, typename D> | |
std::string to_string(fraction<N, D> const& f) | |
{ | |
auto const numerator_string = to_string(f.numerator); | |
auto const denominator_string = to_string(f.denominator); | |
auto const total_length = numerator_string.length()+1+denominator_string.length(); | |
std::string fraction_string; | |
fraction_string.reserve(total_length); | |
fraction_string = numerator_string; | |
fraction_string.push_back('/'); | |
fraction_string += denominator_string; | |
return fraction_string; | |
} | |
} | |
} | |
namespace cnl { | |
using _impl::abs; | |
using _impl::make_fraction; | |
using _impl::to_string; | |
using _impl::reduce; | |
using _impl::canonical; | |
} | |
namespace cnl { | |
namespace _impl { | |
template<typename Number, typename Rep, typename Enable = void> | |
struct wrap; | |
template<typename Number, typename Rep> | |
struct wrap<Number, Rep, enable_if_t<!is_composite<Number>::value>> { | |
constexpr Number operator()(Rep const& number) const | |
{ | |
return number; | |
} | |
}; | |
template<typename Number, typename Rep> | |
struct wrap<Number, Rep, enable_if_t<is_composite<Number>::value>> { | |
constexpr auto operator()(Rep const& rep) const | |
-> decltype(from_rep<Number>(wrap<to_rep_t<Number>, Rep>{}(rep))) | |
{ | |
return from_rep<Number>(wrap<to_rep_t<Number>, Rep>{}(rep)); | |
} | |
}; | |
} | |
template<typename Number, typename Rep> | |
constexpr auto wrap(Rep const& rep) | |
-> decltype(_impl::wrap<Number, Rep>{}(rep)) | |
{ | |
return _impl::wrap<Number, Rep>{}(rep); | |
} | |
} | |
namespace cnl { | |
using _impl::is_integral; | |
using _impl::is_integral_v; | |
} | |
namespace cnl { | |
namespace _impl { | |
template<class Operator> | |
struct overflow_polarity; | |
template<> | |
struct overflow_polarity<convert_op> { | |
template<typename Destination, typename Source> | |
constexpr polarity operator()(Source const& from) const | |
{ | |
return measure_polarity(from); | |
} | |
}; | |
template<> | |
struct overflow_polarity<minus_op> { | |
template<typename Rhs> | |
constexpr polarity operator()(Rhs const& rhs) const | |
{ | |
return -measure_polarity(rhs); | |
} | |
}; | |
template<> | |
struct overflow_polarity<add_op> { | |
template<typename Lhs, typename Rhs> | |
constexpr polarity operator()(Lhs const&, Rhs const& rhs) const | |
{ | |
return measure_polarity(rhs); | |
} | |
}; | |
template<> | |
struct overflow_polarity<subtract_op> { | |
template<typename Lhs, typename Rhs> | |
constexpr polarity operator()(Lhs const&, Rhs const& rhs) const | |
{ | |
return -measure_polarity(rhs); | |
} | |
}; | |
template<> | |
struct overflow_polarity<multiply_op> { | |
template<typename Lhs, typename Rhs> | |
constexpr polarity operator()(Lhs const& lhs, Rhs const& rhs) const | |
{ | |
return measure_polarity(lhs)*measure_polarity(rhs); | |
} | |
}; | |
enum class builtin_overflow_result { | |
overflow, | |
ok, | |
inconclusive | |
}; | |
template<class Operator, typename Lhs, typename Rhs, typename Result> | |
constexpr builtin_overflow_result builtin_overflow(Operator, Lhs const&, Rhs const&, Result&) | |
{ | |
return builtin_overflow_result::inconclusive; | |
} | |
template<typename Lhs, typename Rhs, typename Result> | |
constexpr auto builtin_overflow(add_op, Lhs const& lhs, Rhs const& rhs, Result& result) | |
-> enable_if_t< | |
is_integral<Lhs>::value && is_integral<Rhs>::value && is_integral<Result>::value, | |
builtin_overflow_result> | |
{ | |
return __builtin_add_overflow(lhs, rhs, &result) | |
? builtin_overflow_result::overflow | |
: builtin_overflow_result::ok; | |
} | |
template<typename Lhs, typename Rhs, typename Result> | |
constexpr auto builtin_overflow(subtract_op, Lhs const& lhs, Rhs const& rhs, Result& result) | |
-> enable_if_t< | |
is_integral<Lhs>::value && is_integral<Rhs>::value && is_integral<Result>::value, | |
builtin_overflow_result> | |
{ | |
return __builtin_sub_overflow(lhs, rhs, &result) | |
? builtin_overflow_result::overflow | |
: builtin_overflow_result::ok; | |
} | |
template<typename Lhs, typename Rhs, typename Result> | |
constexpr auto builtin_overflow(multiply_op, Lhs const& lhs, Rhs const& rhs, Result& result) | |
-> enable_if_t< | |
is_integral<Lhs>::value && is_integral<Rhs>::value && is_integral<Result>::value, | |
builtin_overflow_result> | |
{ | |
return __builtin_mul_overflow(lhs, rhs, &result) | |
? builtin_overflow_result::overflow | |
: builtin_overflow_result::ok; | |
} | |
template<class OverflowTag, class Operator, class Lhs, class Rhs> | |
constexpr auto builtin_tagged_binary_overflow_operator(Lhs const& lhs, Rhs const& rhs) | |
-> op_result<Operator, Lhs, Rhs> | |
{ | |
using Result = op_result<Operator, Lhs, Rhs>; | |
Result result{}; | |
switch (builtin_overflow(Operator{}, lhs, rhs, result)) | |
{ | |
case builtin_overflow_result::ok: | |
return result; | |
case builtin_overflow_result::overflow: | |
switch (overflow_polarity<Operator>{}(lhs, rhs)) { | |
case polarity::positive: | |
return overflow_operator<Operator, OverflowTag, polarity::positive>{}(lhs, rhs); | |
case polarity::negative: | |
return overflow_operator<Operator, OverflowTag, polarity::negative>{}(lhs, rhs); | |
case polarity::neutral: | |
return unreachable<Result>("CNL internal error"); | |
} | |
case builtin_overflow_result::inconclusive: | |
break; | |
} | |
return unreachable<Result>("CNL internal error"); | |
} | |
template<class Operator, typename Lhs, typename Rhs> | |
constexpr bool builtin_overflow_supported() | |
{ | |
using Result = op_result<Operator, Lhs, Rhs>; | |
Result result{}; | |
return builtin_overflow(Operator{}, Lhs{}, Rhs{}, result)!=builtin_overflow_result::inconclusive; | |
} | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<class T, polarity> | |
struct overflow_digits; | |
template<class T> | |
struct overflow_digits<T, polarity::positive> | |
: public std::integral_constant<int, numeric_limits<T>::digits> { | |
}; | |
template<class T> | |
struct overflow_digits<T, polarity::negative> | |
: public std::integral_constant<int, cnl::is_signed<T>::value ? digits<T>::value : 0> { | |
}; | |
template<typename Operand, bool IsSigned = is_signed<Operand>::value> | |
struct has_most_negative_number : std::false_type { | |
}; | |
template<typename Operand> | |
struct has_most_negative_number<Operand, true> : std::integral_constant<bool, | |
numeric_limits<Operand>::lowest() < -numeric_limits<Operand>::max()> { | |
}; | |
template<polarity Polarity, bool DestinationIsFloat, bool SourceIsFloat> | |
struct is_overflow_convert; | |
template<> | |
struct is_overflow_convert<polarity::positive, false, false> { | |
template<typename Destination, typename Source> | |
constexpr bool operator()(Source const &rhs) const { | |
return overflow_digits<Destination, polarity::positive>::value< | |
overflow_digits<Source, polarity::positive>::value | |
&& rhs> | |
static_cast<Source>(numeric_limits<Destination>::max()); | |
} | |
}; | |
template<> | |
struct is_overflow_convert<polarity::positive, false, true> { | |
template<typename Destination, typename Source> | |
constexpr bool operator()(Source const &rhs) const | |
{ | |
return rhs > static_cast<Source>(numeric_limits<Destination>::max()); | |
} | |
}; | |
template<> | |
struct is_overflow_convert<polarity::positive, true, false> { | |
template<typename Destination, typename Source> | |
constexpr bool operator()(Source const& rhs) const | |
{ | |
return static_cast<Destination>(rhs) > numeric_limits<Destination>::max(); | |
} | |
}; | |
template<> | |
struct is_overflow_convert<polarity::positive, true, true> { | |
template<typename Destination, typename Source> | |
constexpr bool operator()(Source const&) const | |
{ | |
return false; | |
} | |
}; | |
template<> | |
struct is_overflow_convert<polarity::negative, false, false> { | |
template<typename Destination, typename Source> | |
constexpr bool operator()(Source const &rhs) const { | |
return overflow_digits<Destination, polarity::negative>::value< | |
overflow_digits<Source, polarity::negative>::value | |
&& rhs < static_cast<Source>(numeric_limits<Destination>::lowest()); | |
} | |
}; | |
template<> | |
struct is_overflow_convert<polarity::negative, false, true> { | |
template<typename Destination, typename Source> | |
constexpr bool operator()(Source const &rhs) const | |
{ | |
return rhs < static_cast<Source>(numeric_limits<Destination>::lowest()); | |
} | |
}; | |
template<> | |
struct is_overflow_convert<polarity::negative, true, false> { | |
template<typename Destination, typename Source> | |
constexpr bool operator()(Source const& rhs) const | |
{ | |
return static_cast<Destination>(rhs) < numeric_limits<Destination>::lowest(); | |
} | |
}; | |
template<> | |
struct is_overflow_convert<polarity::negative, true, true> { | |
template<typename Destination, typename Source> | |
constexpr bool operator()(Source const&) const | |
{ | |
return false; | |
} | |
}; | |
template<class Operator, typename ... Operands> | |
struct operator_overflow_traits { | |
using result = op_result<Operator, Operands...>; | |
using numeric_limits = cnl::numeric_limits<result>; | |
static constexpr int positive_digits = _impl::overflow_digits<result, polarity::positive>::value; | |
static constexpr int negative_digits = _impl::overflow_digits<result, polarity::negative>::value; | |
static constexpr result lowest() | |
{ | |
return numeric_limits::lowest(); | |
} | |
static constexpr result max() | |
{ | |
return numeric_limits::max(); | |
} | |
template<typename Operand> | |
static constexpr int leading_bits(Operand const& operand) | |
{ | |
return cnl::leading_bits(static_cast<result>(operand)); | |
} | |
}; | |
template<typename Operator, polarity Polarity> | |
struct is_overflow { | |
template<typename ... Operands> | |
constexpr bool operator()(Operands const& ...) const | |
{ | |
return false; | |
} | |
}; | |
template<polarity Polarity> | |
struct is_overflow<convert_op, Polarity> { | |
template<typename Destination, typename Source> | |
constexpr bool operator()(Source const& from) const | |
{ | |
using is_overflow_convert = cnl::_impl::is_overflow_convert< | |
Polarity, | |
std::is_floating_point<Destination>::value, | |
std::is_floating_point<Source>::value>; | |
return is_overflow_convert{}.template operator()<Destination>(from); | |
} | |
}; | |
template<> | |
struct is_overflow<minus_op, polarity::positive> { | |
template<typename Rhs> | |
constexpr bool operator()(Rhs const& rhs) const | |
{ | |
return has_most_negative_number<Rhs>::value && rhs < -numeric_limits<Rhs>::max(); | |
} | |
}; | |
template<> | |
struct is_overflow<minus_op, polarity::negative> { | |
template<typename Rhs> | |
constexpr bool operator()(Rhs const& rhs) const | |
{ | |
return !is_signed<Rhs>::value && rhs; | |
} | |
}; | |
template<> | |
struct is_overflow<add_op, polarity::positive> { | |
template<typename Lhs, typename Rhs> | |
constexpr bool operator()(Lhs const& lhs, Rhs const& rhs) const | |
{ | |
using traits = operator_overflow_traits<add_op, Lhs, Rhs>; | |
return (max(overflow_digits<Lhs, polarity::positive>::value, | |
overflow_digits<Rhs, polarity::positive>::value)+1 | |
>traits::positive_digits) | |
&& lhs>Lhs{0} | |
&& rhs>Rhs{0} | |
&& typename traits::result(lhs)>traits::max()-rhs; | |
} | |
}; | |
template<> | |
struct is_overflow<add_op, polarity::negative> { | |
template<typename Lhs, typename Rhs> | |
constexpr bool operator()(Lhs const& lhs, Rhs const& rhs) const | |
{ | |
using traits = operator_overflow_traits<add_op, Lhs, Rhs>; | |
return (max(overflow_digits<Lhs, polarity::positive>::value, | |
overflow_digits<Rhs, polarity::positive>::value)+1 | |
>traits::positive_digits) | |
&& lhs<Lhs{0} | |
&& rhs<Rhs{0} | |
&& typename traits::result(lhs)<traits::lowest()-rhs; | |
} | |
}; | |
template<> | |
struct is_overflow<subtract_op, polarity::positive> { | |
template<typename Lhs, typename Rhs> | |
constexpr bool operator()(Lhs const& lhs, Rhs const& rhs) const | |
{ | |
using traits = operator_overflow_traits<subtract_op, Lhs, Rhs>; | |
return (max(overflow_digits<Lhs, polarity::positive>::value, | |
overflow_digits<Rhs, polarity::positive>::value)+1 | |
>traits::positive_digits) | |
&& rhs<Rhs{0} | |
&& lhs>traits::max()+rhs; | |
} | |
}; | |
template<> | |
struct is_overflow<subtract_op, polarity::negative> { | |
template<typename Lhs, typename Rhs> | |
constexpr bool operator()(Lhs const& lhs, Rhs const& rhs) const | |
{ | |
using traits = operator_overflow_traits<subtract_op, Lhs, Rhs>; | |
return (max(overflow_digits<Lhs, polarity::positive>::value, | |
overflow_digits<Rhs, polarity::positive>::value)+1 | |
>traits::positive_digits) | |
&& (rhs>=0) | |
&& lhs<traits::lowest()+rhs; | |
} | |
}; | |
template<> | |
struct is_overflow<multiply_op, polarity::positive> { | |
template<typename Lhs, typename Rhs> | |
constexpr bool operator()(Lhs const& lhs, Rhs const& rhs) const | |
{ | |
using traits = operator_overflow_traits<multiply_op, Lhs, Rhs>; | |
return (overflow_digits<Lhs, polarity::positive>::value+overflow_digits<Rhs, polarity::positive>::value | |
> traits::positive_digits) | |
&& ((lhs>Lhs{0}) | |
? (rhs>Rhs{0}) && (traits::max()/rhs)<lhs | |
: (rhs<Rhs{0}) && (traits::max()/rhs)>lhs); | |
} | |
}; | |
template<> | |
struct is_overflow<multiply_op, polarity::negative> { | |
template<typename Lhs, typename Rhs> | |
constexpr bool operator()(Lhs const& lhs, Rhs const& rhs) const | |
{ | |
using traits = operator_overflow_traits<multiply_op, Lhs, Rhs>; | |
return (overflow_digits<Lhs, polarity::positive>::value+overflow_digits<Rhs, polarity::positive>::value | |
> traits::positive_digits) | |
&& ((lhs<Lhs{0}) | |
? (rhs>Rhs{0}) && (traits::lowest()/rhs)>lhs | |
: (rhs<Rhs{0}) && (traits::lowest()/rhs)<lhs); | |
} | |
}; | |
template<> | |
struct is_overflow<divide_op, polarity::positive> { | |
template<typename Lhs, typename Rhs> | |
constexpr bool operator()(Lhs const& lhs, Rhs const& rhs) const | |
{ | |
using traits = operator_overflow_traits<divide_op, Lhs, Rhs>; | |
return (has_most_negative_number<Lhs>::value) | |
? rhs == -1 && lhs == traits::lowest() | |
: false; | |
} | |
}; | |
template<> | |
struct is_overflow<shift_left_op, polarity::negative> { | |
template<typename Lhs, typename Rhs> | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const | |
-> enable_if_t<is_signed<Lhs>::value, bool> | |
{ | |
using traits = operator_overflow_traits<shift_left_op, Lhs, Rhs>; | |
return lhs<0 | |
? rhs>0 | |
? rhs<traits::positive_digits | |
? (lhs >> (traits::positive_digits-rhs))!=-1 | |
: true | |
: false | |
: false; | |
} | |
template<typename Lhs, typename Rhs> | |
constexpr auto operator()(Lhs const&, Rhs const&) const | |
-> enable_if_t<!is_signed<Lhs>::value, bool> | |
{ | |
return false; | |
} | |
}; | |
template<> | |
struct is_overflow<shift_left_op, polarity::positive> { | |
template<typename Lhs, typename Rhs> | |
constexpr bool operator()(Lhs const& lhs, Rhs const& rhs) const | |
{ | |
using traits = operator_overflow_traits<shift_left_op, Lhs, Rhs>; | |
return lhs>0 | |
? rhs>0 | |
? rhs<traits::positive_digits | |
? (lhs >> (traits::positive_digits-rhs))!=0 | |
: true | |
: false | |
: false; | |
} | |
}; | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<typename Result, class OverflowTag> | |
struct positive_overflow_result; | |
template<typename Result, class OverflowTag> | |
struct negative_overflow_result; | |
template<class OverflowTag, typename Destination, typename Source> | |
struct tagged_convert_overflow_operator { | |
constexpr Destination operator()(Source const& from) const | |
{ | |
return is_overflow<convert_op, polarity::positive>{}.template operator()<Destination>(from) | |
? overflow_operator<convert_op, OverflowTag, polarity::positive>{}.template operator()< | |
Destination>(from) | |
: is_overflow<convert_op, polarity::negative>{}.template operator()<Destination>(from) | |
? overflow_operator<convert_op, OverflowTag, polarity::negative>{}.template operator()< | |
Destination>(from) | |
: static_cast<Destination>(from); | |
} | |
}; | |
template<class OverflowTag, class Operator> | |
struct tagged_unary_overflow_operator { | |
template<typename Operand> | |
constexpr auto operator()(Operand const& operand) const | |
-> op_result<Operator, Operand> | |
{ | |
return is_overflow<Operator, polarity::positive>{}(operand) | |
? overflow_operator<Operator, OverflowTag, polarity::positive>{}(operand) | |
: is_overflow<Operator, polarity::negative>{}(operand) | |
? overflow_operator<Operator, OverflowTag, polarity::negative>{}(operand) | |
: Operator{}(operand); | |
} | |
}; | |
template<class OverflowTag, class Operator> | |
struct tagged_binary_overflow_operator { | |
template<class Lhs, class Rhs> | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const | |
-> enable_if_t<builtin_overflow_supported<Operator, Lhs, Rhs>(), op_result<Operator, Lhs, Rhs>> | |
{ | |
return builtin_tagged_binary_overflow_operator<OverflowTag, Operator>(lhs, rhs); | |
} | |
template<class Lhs, class Rhs> | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const | |
-> enable_if_t<!builtin_overflow_supported<Operator, Lhs, Rhs>(), op_result<Operator, Lhs, Rhs>> | |
{ | |
return is_overflow<Operator, polarity::positive>{}(lhs, rhs) | |
? overflow_operator<Operator, OverflowTag, polarity::positive>{}(lhs, rhs) | |
: is_overflow<Operator, polarity::negative>{}(lhs, rhs) | |
? overflow_operator<Operator, OverflowTag, polarity::negative>{}(lhs, rhs) | |
: tagged_binary_operator<native_tag, Operator>{}(lhs, rhs); | |
} | |
}; | |
} | |
} | |
namespace cnl { | |
struct native_overflow_tag : _impl::native_tag { | |
}; | |
namespace _impl { | |
template<typename Operator, polarity Polarity> | |
struct overflow_operator<Operator, native_overflow_tag, Polarity> : Operator { | |
}; | |
template<typename Destination, typename Source> | |
struct tagged_convert_operator<native_overflow_tag, Destination, Source> | |
: tagged_convert_overflow_operator<native_overflow_tag, Destination, Source> { | |
}; | |
template<class Operator> | |
struct tagged_unary_operator<native_overflow_tag, Operator> | |
: tagged_unary_overflow_operator<native_overflow_tag, Operator> { | |
}; | |
template<class Operator> | |
struct tagged_binary_operator<native_overflow_tag, Operator> | |
: tagged_binary_operator<_impl::native_tag, Operator> { | |
}; | |
} | |
} | |
namespace cnl { | |
struct saturated_overflow_tag { | |
}; | |
namespace _impl { | |
template<typename Operator> | |
struct overflow_operator<Operator, saturated_overflow_tag, polarity::positive> { | |
template<typename Destination, typename Source> | |
constexpr Destination operator()(Source const&) const | |
{ | |
return numeric_limits<Destination>::max(); | |
} | |
template<class ... Operands> | |
constexpr op_result<Operator, Operands...> operator()(Operands const& ...) const | |
{ | |
return numeric_limits<op_result<Operator, Operands...>>::max(); | |
} | |
}; | |
template<typename Operator> | |
struct overflow_operator<Operator, saturated_overflow_tag, polarity::negative> { | |
template<typename Destination, typename Source> | |
constexpr Destination operator()(Source const&) const | |
{ | |
return numeric_limits<Destination>::lowest(); | |
} | |
template<class ... Operands> | |
constexpr op_result<Operator, Operands...> operator()(Operands const& ...) const | |
{ | |
return numeric_limits<op_result<Operator, Operands...>>::lowest(); | |
} | |
}; | |
template<typename Destination, typename Source> | |
struct tagged_convert_operator<saturated_overflow_tag, Destination, Source> | |
: tagged_convert_overflow_operator<saturated_overflow_tag, Destination, Source> { | |
}; | |
template<class Operator> | |
struct tagged_unary_operator<saturated_overflow_tag, Operator> | |
: tagged_unary_overflow_operator<saturated_overflow_tag, Operator> { | |
}; | |
template<class Operator> | |
struct tagged_binary_operator<saturated_overflow_tag, Operator> | |
: tagged_binary_overflow_operator<saturated_overflow_tag, Operator> { | |
}; | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<typename Result, class Exception> | |
Result throw_exception(char const* message) | |
{ | |
return true ? throw Exception(message) : Result{}; | |
} | |
} | |
} | |
namespace cnl { | |
struct throwing_overflow_tag { | |
}; | |
namespace _impl { | |
template<typename Operator> | |
struct overflow_operator<Operator, throwing_overflow_tag, polarity::positive> { | |
template<typename Destination, typename Source> | |
constexpr Destination operator()(Source const&) const | |
{ | |
return throw_exception<Destination, std::overflow_error>("positive overflow"); | |
} | |
template<class ... Operands> | |
constexpr op_result<Operator, Operands...> operator()(Operands const& ...) const | |
{ | |
return throw_exception<op_result<Operator, Operands...>, std::overflow_error>("positive overflow"); | |
} | |
}; | |
template<typename Operator> | |
struct overflow_operator<Operator, throwing_overflow_tag, polarity::negative> { | |
template<typename Destination, typename Source> | |
constexpr Destination operator()(Source const&) const | |
{ | |
return throw_exception<Destination, std::overflow_error>("negative overflow"); | |
} | |
template<class ... Operands> | |
constexpr op_result<Operator, Operands...> operator()(Operands const& ...) const | |
{ | |
return throw_exception<op_result<Operator, Operands...>, std::overflow_error>("negative overflow"); | |
} | |
}; | |
template<typename Result> | |
struct negative_overflow_result<Result, throwing_overflow_tag> { | |
constexpr Result operator()() const | |
{ | |
return throw_exception<Result, std::overflow_error>("negative overflow"); | |
} | |
}; | |
template<typename Destination, typename Source> | |
struct tagged_convert_operator<throwing_overflow_tag, Destination, Source> | |
: tagged_convert_overflow_operator<throwing_overflow_tag, Destination, Source> { | |
}; | |
template<class Operator> | |
struct tagged_unary_operator<throwing_overflow_tag, Operator> | |
: tagged_unary_overflow_operator<throwing_overflow_tag, Operator> { | |
}; | |
template<class Operator> | |
struct tagged_binary_operator<throwing_overflow_tag, Operator> | |
: tagged_binary_overflow_operator<throwing_overflow_tag, Operator> { | |
}; | |
} | |
} | |
namespace cnl { | |
struct trapping_overflow_tag { | |
}; | |
namespace _impl { | |
template<typename Operator> | |
struct overflow_operator<Operator, trapping_overflow_tag, polarity::positive> { | |
template<typename Destination, typename Source> | |
constexpr Destination operator()(Source const&) const | |
{ | |
return terminate<Destination>("positive overflow"); | |
} | |
template<class ... Operands> | |
constexpr op_result<Operator, Operands...> operator()(Operands const&...) const | |
{ | |
return terminate<op_result<Operator, Operands...>>("positive overflow"); | |
} | |
}; | |
template<typename Operator> | |
struct overflow_operator<Operator, trapping_overflow_tag, polarity::negative> { | |
template<typename Destination, typename Source> | |
constexpr Destination operator()(Source const&) const | |
{ | |
return terminate<Destination>("negative overflow"); | |
} | |
template<class ... Operands> | |
constexpr op_result<Operator, Operands...> operator()(Operands const&...) const | |
{ | |
return terminate<op_result<Operator, Operands...>>("negative overflow"); | |
} | |
}; | |
template<typename Destination, typename Source> | |
struct tagged_convert_operator<trapping_overflow_tag, Destination, Source> | |
: tagged_convert_overflow_operator<trapping_overflow_tag, Destination, Source> { | |
}; | |
template<class Operator> | |
struct tagged_unary_operator<trapping_overflow_tag, Operator> | |
: tagged_unary_overflow_operator<trapping_overflow_tag, Operator> { | |
}; | |
template<class Operator> | |
struct tagged_binary_operator<trapping_overflow_tag, Operator> | |
: tagged_binary_overflow_operator<trapping_overflow_tag, Operator> { | |
}; | |
} | |
} | |
namespace cnl { | |
struct undefined_overflow_tag { | |
}; | |
namespace _impl { | |
template<typename Operator> | |
struct overflow_operator<Operator, undefined_overflow_tag, polarity::positive> { | |
template<typename Destination, typename Source> | |
constexpr Destination operator()(Source const&) const | |
{ | |
return unreachable<Destination>("positive overflow"); | |
} | |
template<class ... Operands> | |
constexpr op_result<Operator, Operands...> operator()(Operands const& ...) const | |
{ | |
return unreachable<op_result<Operator, Operands...>>("positive overflow"); | |
} | |
}; | |
template<typename Operator> | |
struct overflow_operator<Operator, undefined_overflow_tag, polarity::negative> { | |
template<typename Destination, typename Source> | |
constexpr Destination operator()(Source const&) const | |
{ | |
return unreachable<Destination>("negative overflow"); | |
} | |
template<class ... Operands> | |
constexpr op_result<Operator, Operands...> operator()(Operands const& ...) const | |
{ | |
return unreachable<op_result<Operator, Operands...>>("negative overflow"); | |
} | |
}; | |
template<typename Destination, typename Source> | |
struct tagged_convert_operator<undefined_overflow_tag, Destination, Source> | |
: tagged_convert_overflow_operator<undefined_overflow_tag, Destination, Source> { | |
}; | |
template<class Operator> | |
struct tagged_unary_operator<undefined_overflow_tag, Operator> | |
: tagged_unary_overflow_operator<undefined_overflow_tag, Operator> { | |
}; | |
template<class Operator> | |
struct tagged_binary_operator<undefined_overflow_tag, Operator> | |
: tagged_binary_overflow_operator<undefined_overflow_tag, Operator> { | |
}; | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
inline std::ostream& operator<<(std::ostream& out, int128 const n) | |
{ | |
std::array<char, 41> line; | |
return out << cnl::_impl::to_chars_natural(std::begin(line), std::end(line), n); | |
} | |
inline std::ostream& operator<<(std::ostream& out, uint128 const n) | |
{ | |
std::array<char, 40> line; | |
return out << cnl::_impl::to_chars_natural(std::begin(line), std::end(line), n); | |
} | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<class A, class B> | |
constexpr bool identical(A const& a, B const& b) | |
{ | |
static_assert(std::is_same<A, B>::value, "different types"); | |
return a==b; | |
} | |
} | |
} | |
namespace cnl { | |
template<class Rep, class OverflowTag> | |
class overflow_integer; | |
namespace _integer_impl { | |
template<class T> | |
struct is_overflow_integer | |
: std::false_type { | |
}; | |
template<class Rep, class OverflowTag> | |
struct is_overflow_integer<overflow_integer<Rep, OverflowTag>> | |
: std::true_type { | |
}; | |
} | |
template<class Rep = int, class OverflowTag = undefined_overflow_tag> | |
class overflow_integer : public _impl::number_base<overflow_integer<Rep, OverflowTag>, Rep> { | |
static_assert(!_integer_impl::is_overflow_integer<Rep>::value, | |
"overflow_integer of overflow_integer is not a supported"); | |
public: | |
using rep = Rep; | |
using overflow_tag = OverflowTag; | |
using _base = _impl::number_base<overflow_integer<Rep, OverflowTag>, Rep>; | |
overflow_integer() = default; | |
template<class RhsRep, class RhsOverflowTag> | |
constexpr overflow_integer(overflow_integer<RhsRep, RhsOverflowTag> const& rhs) | |
:overflow_integer(_impl::to_rep(rhs)) | |
{ | |
} | |
template<class Rhs, _impl::enable_if_t<!_integer_impl::is_overflow_integer<Rhs>::value, int> dummy = 0> | |
constexpr overflow_integer(Rhs const& rhs) | |
:_base(convert<overflow_tag, rep>(rhs)) | |
{ | |
} | |
template< ::cnl::intmax Value> | |
constexpr overflow_integer(constant<Value>) | |
: _base(static_cast<rep>(Value)) | |
{ | |
static_assert(Value <= numeric_limits<rep>::max(), "initialization by out-of-range value"); | |
static_assert(!numeric_limits<decltype(Value)>::is_signed || Value >= numeric_limits<rep>::lowest(), "initialization by out-of-range value"); | |
} | |
template<class T> | |
constexpr explicit operator T() const | |
{ | |
return convert<overflow_tag, T>(_impl::to_rep(*this)); | |
} | |
}; | |
template<class Rep, class OverflowTag> | |
struct digits<overflow_integer<Rep, OverflowTag>> : digits<Rep> { | |
}; | |
template<class Rep, class OverflowTag, int MinNumBits> | |
struct set_digits<overflow_integer<Rep, OverflowTag>, MinNumBits> { | |
using type = overflow_integer<set_digits_t<Rep, MinNumBits>, OverflowTag>; | |
}; | |
template<int Digits, int Radix, typename Rep, class OverflowTag> | |
struct scale<Digits, Radix, overflow_integer<Rep, OverflowTag>> { | |
using _value_type = overflow_integer<Rep, OverflowTag>; | |
constexpr auto operator()(_value_type const& s) const | |
-> decltype(_impl::from_rep<_value_type>(_impl::scale<Digits, Radix>(_impl::to_rep(s)))) | |
{ | |
return _impl::default_scale<Digits, Radix, _value_type>{}(s); | |
} | |
}; | |
template<typename ArchetypeRep, class OverflowTag, typename Rep> | |
struct from_rep<overflow_integer<ArchetypeRep, OverflowTag>, Rep> { | |
constexpr auto operator()(Rep const& r) const | |
-> overflow_integer<Rep, OverflowTag> | |
{ | |
return r; | |
} | |
}; | |
template<class Rep, class OverflowTag, class Value> | |
struct from_value<overflow_integer<Rep, OverflowTag>, Value> | |
: _impl::from_value_simple<overflow_integer<Value, OverflowTag>, Value> { | |
}; | |
template<class Rep, class OverflowTag, class ValueRep, class ValueOverflowTag> | |
struct from_value<overflow_integer<Rep, OverflowTag>, overflow_integer<ValueRep, ValueOverflowTag>> | |
: _impl::from_value_simple< | |
overflow_integer< | |
from_value_t<Rep, ValueRep>, | |
_impl::common_type_t<OverflowTag, ValueOverflowTag>>, | |
overflow_integer<ValueRep, ValueOverflowTag>> { | |
}; | |
template<class Rep, class OverflowTag, ::cnl::intmax Value> | |
struct from_value<overflow_integer<Rep, OverflowTag>, constant<Value>> | |
: _impl::from_value_simple<overflow_integer< | |
typename std::conditional< | |
digits<int>::value<_impl::used_digits(Value), decltype(Value), int>::type, OverflowTag>, | |
constant<Value>>{ | |
}; | |
template<class OverflowTag, class Rep> | |
constexpr auto make_overflow_int(Rep const& value) | |
-> overflow_integer<Rep, OverflowTag> | |
{ | |
return value; | |
} | |
namespace _impl { | |
template<class Operator, class Rep, class OverflowTag> | |
struct unary_operator<Operator, overflow_integer<Rep, OverflowTag>> { | |
constexpr auto operator()(overflow_integer<Rep, OverflowTag> const& operand) const | |
-> decltype(overflow_integer<decltype(Operator()(_impl::to_rep(operand))), OverflowTag>(Operator()(_impl::to_rep(operand)))) | |
{ | |
return from_rep<overflow_integer<op_result<Operator, Rep>, OverflowTag>>( | |
_impl::tagged_unary_operator<OverflowTag, Operator>{}(_impl::to_rep(operand))); | |
} | |
}; | |
template<class Operator, class LhsRep, class RhsRep, class OverflowTag> | |
struct binary_operator<Operator, | |
overflow_integer<LhsRep, OverflowTag>, overflow_integer<RhsRep, OverflowTag>> { | |
constexpr auto operator()( | |
overflow_integer<LhsRep, OverflowTag> const& lhs, | |
overflow_integer<RhsRep, OverflowTag> const& rhs) const | |
-> overflow_integer<op_result<Operator, LhsRep, RhsRep>, OverflowTag> | |
{ | |
return from_rep<overflow_integer<op_result<Operator, LhsRep, RhsRep>, OverflowTag>>( | |
_impl::tagged_binary_operator<OverflowTag, Operator>{}(_impl::to_rep(lhs), _impl::to_rep(rhs))); | |
} | |
}; | |
template<class Operator, class LhsRep, class RhsRep, class OverflowTag> | |
struct comparison_operator<Operator, | |
overflow_integer<LhsRep, OverflowTag>, overflow_integer<RhsRep, OverflowTag>> { | |
constexpr auto operator()( | |
overflow_integer<LhsRep, OverflowTag> const& lhs, | |
overflow_integer<RhsRep, OverflowTag> const& rhs) const | |
-> decltype(Operator()(_impl::to_rep(lhs), _impl::to_rep(rhs))) | |
{ | |
return Operator()(_impl::to_rep(lhs), _impl::to_rep(rhs)); | |
} | |
}; | |
template<class Operator, class LhsRep, class LhsTag, class RhsRep, class RhsTag> | |
struct comparison_operator<Operator, | |
overflow_integer<LhsRep, LhsTag>, overflow_integer<RhsRep, RhsTag>> { | |
using common_tag = cnl::_impl::common_type_t<LhsTag, RhsTag>; | |
using operator_type = comparison_operator< | |
Operator, | |
overflow_integer<LhsRep, common_tag>, | |
overflow_integer<RhsRep, common_tag>>; | |
constexpr auto operator()( | |
const overflow_integer<LhsRep, LhsTag>& lhs, | |
const overflow_integer<RhsRep, RhsTag>& rhs) const | |
-> decltype(operator_type{}(lhs, rhs)) | |
{ | |
return operator_type{}(lhs, rhs); | |
} | |
}; | |
template<class Operator, typename Rep, class OverflowTag> | |
struct pre_operator<Operator, overflow_integer<Rep, OverflowTag>> { | |
constexpr auto operator()(overflow_integer<Rep, OverflowTag>& rhs) const | |
-> decltype(typename pre_to_assign<Operator>::type{}(rhs, 1)) | |
{ | |
return typename pre_to_assign<Operator>::type{}(rhs, 1); | |
} | |
}; | |
template<class Operator, typename Rep, class OverflowTag> | |
struct post_operator<Operator, overflow_integer<Rep, OverflowTag>> { | |
constexpr auto operator()(overflow_integer<Rep, OverflowTag>& rhs) const | |
-> overflow_integer<Rep, OverflowTag> | |
{ | |
auto copy = rhs; | |
typename post_to_assign<Operator>::type{}(rhs, 1); | |
return copy; | |
} | |
}; | |
} | |
template<class Rep, class OverflowTag> | |
struct numeric_limits<overflow_integer<Rep, OverflowTag>> | |
: numeric_limits<_impl::number_base<overflow_integer<Rep, OverflowTag>, Rep>> { | |
static constexpr bool is_integer = true; | |
}; | |
template<class Rep, class OverflowTag> | |
struct numeric_limits<overflow_integer<Rep, OverflowTag> const> | |
: numeric_limits<_impl::number_base<overflow_integer<Rep, OverflowTag>, Rep>> { | |
static constexpr bool is_integer = true; | |
}; | |
template<class Rep, class OverflowTag> | |
std::ostream &operator<<(std::ostream &o, overflow_integer<Rep, OverflowTag> const &i) { | |
return o << _impl::to_rep(i); | |
} | |
} | |
namespace std { | |
template<class Rep, class OverflowTag> | |
struct numeric_limits<cnl::overflow_integer<Rep, OverflowTag>> | |
: cnl::numeric_limits<cnl::overflow_integer<Rep, OverflowTag>> { | |
}; | |
template<class Rep, class OverflowTag> | |
struct numeric_limits<cnl::overflow_integer<Rep, OverflowTag> const> | |
: cnl::numeric_limits<cnl::overflow_integer<Rep, OverflowTag>> { | |
}; | |
} | |
namespace cnl { | |
namespace _impl { | |
template<int Digits = digits<int>::value, typename Narrowest = int> | |
class wide_integer; | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<bool Signed> | |
struct max_digits_fundamental; | |
template<> | |
struct max_digits_fundamental<true> : digits<intmax> { | |
}; | |
template<> | |
struct max_digits_fundamental<false> : digits<uintmax> { | |
}; | |
template<typename T, class Enable = void> | |
struct max_digits; | |
template<typename T> | |
struct max_digits<T, enable_if_t<is_integral<T>::value>> | |
: max_digits_fundamental<is_signed<T>::value> { | |
}; | |
template<typename T> | |
struct max_digits<T, enable_if_t<is_composite<T>::value>> | |
: max_digits<to_rep_t<T>> { | |
}; | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<typename Upper, typename Lower> | |
class duplex_integer; | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<typename Word, int NumWords> | |
struct multiword_integer; | |
template<typename Word> | |
struct multiword_integer<Word, 1> { | |
using type = Word; | |
}; | |
template<typename Word> | |
struct multiword_integer<Word, 2> { | |
using upper = Word; | |
using lower = set_signedness_t<upper, false>; | |
using type = duplex_integer<upper, lower>; | |
}; | |
template<typename Word, int NumWords> | |
struct multiword_integer { | |
private: | |
static_assert(NumWords>2, ""); | |
static constexpr auto num_words = NumWords; | |
static constexpr auto num_words_rounded_up = (1 << used_digits(num_words-1)); | |
static constexpr auto upper_num_words = num_words_rounded_up/2; | |
static constexpr auto lower_num_words = num_words-upper_num_words; | |
using upper = typename multiword_integer<Word, upper_num_words>::type; | |
using lower = typename multiword_integer<set_signedness_t<Word, false>, lower_num_words>::type; | |
public: | |
using type = duplex_integer<upper, lower>; | |
}; | |
template<typename Word, int NumWords> | |
using multiword_integer_t = typename multiword_integer<Word, NumWords>::type; | |
template<int N, class Enable = void> | |
struct is_power_of_two; | |
template<int N> | |
struct is_power_of_two<N, enable_if_t<(N>0)>> | |
: std::integral_constant<bool, !(N & (N-1))> { | |
}; | |
template<typename Word, typename Signedness = set_signedness_t<int, is_signed<Word>::value>> | |
struct optimal_duplex; | |
template<typename Narrowest> | |
struct optimal_duplex<Narrowest, unsigned> { | |
static constexpr auto double_word_digits = max_digits<Narrowest>::value; | |
static_assert(double_word_digits>=2 && is_power_of_two<double_word_digits>::value, | |
"invalid integer type, Narrowest"); | |
static constexpr auto word_digits = double_word_digits/2; | |
using word = set_digits_t<Narrowest, word_digits>; | |
static_assert(digits<word>::value==word_digits, "failed to half a double-width word"); | |
using type = word; | |
}; | |
template<typename Narrowest> | |
struct optimal_duplex<Narrowest, signed> { | |
using unsiged_narrowest = remove_signedness_t<Narrowest>; | |
using unsigned_multiword_integer = optimal_duplex<unsiged_narrowest, unsigned>; | |
using type = add_signedness_t<typename unsigned_multiword_integer::type>; | |
}; | |
template<int Digits, typename Narrowest> | |
struct instantiate_duplex_integer { | |
using word = typename optimal_duplex<Narrowest>::type; | |
static constexpr auto num_sign_bits = is_signed<word>::value; | |
static constexpr auto word_digits = digits<word>::value+num_sign_bits; | |
static constexpr auto required_num_words = (Digits+num_sign_bits+word_digits-1)/word_digits; | |
static constexpr auto plural_num_words = max(2, required_num_words); | |
using type = multiword_integer_t<word, plural_num_words>; | |
}; | |
template<int Digits, typename Narrowest> | |
using instantiate_duplex_integer_t = typename instantiate_duplex_integer<Digits, Narrowest>::type; | |
} | |
} | |
namespace cnl { | |
template<typename Upper, typename Lower> | |
struct remove_signedness<_impl::duplex_integer<Upper, Lower>> | |
: _impl::type_identity<_impl::duplex_integer<remove_signedness_t<Upper>, Lower>> { | |
}; | |
} | |
namespace cnl { | |
namespace _impl { | |
template<typename Result, typename Lhs> | |
constexpr auto sensible_right_shift(Lhs const& lhs, int rhs) | |
-> enable_if_t<digits<Result>::value <= digits<decltype(lhs>>rhs)>::value, Result> | |
{ | |
((rhs>=0) ? static_cast<void>(0) : __builtin_unreachable()); | |
using promoted_type = decltype(lhs >> rhs); | |
return static_cast<Result>((rhs>=digits<promoted_type>::value) | |
? lhs >> (digits<Lhs>::value-1) >> 1 | |
: (lhs >> rhs) & static_cast<promoted_type>(~Result{})); | |
} | |
template<typename Result, typename Lhs> | |
constexpr auto sensible_right_shift(Lhs const& lhs, int rhs) | |
-> enable_if_t<(digits<Result>::value > digits<decltype(lhs>>rhs)>::value), Result> | |
{ | |
((rhs>=0) ? static_cast<void>(0) : __builtin_unreachable()); | |
using promoted_type = decltype(lhs >> rhs); | |
return (rhs>=digits<promoted_type>::value) | |
? Result{} | |
: static_cast<Result>(lhs >> rhs); | |
} | |
template<typename Result, typename Lhs> | |
constexpr auto sensible_left_shift(Lhs const& lhs, int rhs) | |
-> enable_if_t<digits<Result>::value <= digits<decltype(lhs<<rhs)>::value, Result> | |
{ | |
((rhs>=0) ? static_cast<void>(0) : __builtin_unreachable()); | |
using promoted_type = decltype(lhs << rhs); | |
using unsigned_type = remove_signedness_t<decltype(lhs & lhs)>; | |
return (rhs>=digits<promoted_type>::value) | |
? Result{} | |
: static_cast<Result>( | |
static_cast<unsigned_type>(lhs & sensible_right_shift<Lhs>(~Result{}, rhs)) << rhs); | |
} | |
template<typename Result, typename Lhs> | |
constexpr auto sensible_left_shift(Lhs const& lhs, int rhs) | |
-> enable_if_t<(digits<Result>::value > digits<decltype(lhs<<rhs)>::value), Result> | |
{ | |
return sensible_left_shift<Result>(static_cast<Result>(lhs), rhs); | |
} | |
template<typename Result, typename Lhs> | |
constexpr auto extra_sensible_right_shift(Lhs const& lhs, int rhs) -> Result | |
{ | |
return (rhs<0) | |
? sensible_left_shift<Result>(lhs, -rhs) | |
: sensible_right_shift<Result>(lhs, rhs); | |
} | |
} | |
template<typename Upper, typename Lower> | |
struct digits<_impl::duplex_integer<Upper, Lower>> | |
: std::integral_constant<int, digits<Upper>::value+digits<Lower>::value> { | |
}; | |
} | |
namespace cnl { | |
template<typename Upper, typename Lower> | |
struct is_signed<_impl::duplex_integer<Upper, Lower>> | |
: std::integral_constant<bool, is_signed<Upper>::value> { | |
}; | |
} | |
namespace cnl { | |
namespace _impl { | |
template<typename Integer> | |
constexpr bool is_flushed(Integer const& value) | |
{ | |
return value==0 || value==static_cast<Integer>(~Integer{}); | |
} | |
template<typename Result, typename Upper, typename Lower> | |
constexpr auto upper_value(Upper const& upper) -> Result | |
{ | |
return (digits<Result>::value<=digits<Lower>::value) | |
? !is_flushed(upper) | |
? unreachable<Result>("overflow in narrowing conversion") | |
: Result{} | |
: Result(sensible_left_shift<Result>(upper, digits<Lower>::value)); | |
} | |
template<typename Upper, typename Lower> | |
class duplex_integer { | |
static_assert(!is_signed<Lower>::value, "Lower component must be unsigned."); | |
using upper_type = Upper; | |
using lower_type = Lower; | |
static constexpr int lower_width = width<lower_type>::value; | |
public: | |
duplex_integer() = default; | |
constexpr duplex_integer(upper_type const& u, lower_type const& l); | |
template<typename Integer, _impl::enable_if_t<(numeric_limits<Integer>::is_integer), int> Dummy = 0> | |
constexpr duplex_integer(Integer const& i); | |
template<typename Number, _impl::enable_if_t<(numeric_limits<Number>::is_iec559), int> Dummy = 0> | |
constexpr duplex_integer(Number const& i); | |
constexpr auto upper() const -> upper_type const& | |
{ | |
return _upper; | |
} | |
constexpr auto upper() -> upper_type& | |
{ | |
return _upper; | |
} | |
constexpr auto lower() const -> lower_type const& | |
{ | |
return _lower; | |
} | |
constexpr auto lower() -> lower_type& | |
{ | |
return _lower; | |
} | |
explicit constexpr operator bool() const { return _lower || _upper; } | |
template<typename Integer, _impl::enable_if_t<numeric_limits<Integer>::is_integer, int> = 0> | |
explicit constexpr operator Integer() const | |
{ | |
return upper_value<Integer, Upper, Lower>(_upper) | static_cast<Integer>(_lower); | |
} | |
template<typename Number, _impl::enable_if_t<numeric_limits<Number>::is_iec559, int> = 0> | |
explicit constexpr operator Number() const | |
{ | |
return static_cast<Number>(_upper)*cnl::power<Number, lower_width, 2>() | |
+static_cast<Number>(_lower); | |
} | |
private: | |
upper_type _upper; | |
lower_type _lower; | |
}; | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<typename T> | |
struct is_duplex_integer : std::false_type {}; | |
template<typename Upper, typename Lower> | |
struct is_duplex_integer<duplex_integer<Upper, Lower>> : std::true_type {}; | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<typename Upper, typename Lower> | |
struct wants_generic_ops<duplex_integer<Upper, Lower>> : std::true_type { | |
}; | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<typename Operator, typename Upper, typename Lower> | |
struct comparison_operator<Operator, duplex_integer<Upper, Lower>, duplex_integer<Upper, Lower>> { | |
constexpr auto operator()( | |
duplex_integer<Upper, Lower> const& lhs, | |
duplex_integer<Upper, Lower> const& rhs) const -> bool | |
{ | |
using tuple = std::tuple<Upper const&, Lower const&>; | |
return Operator{}(tuple(lhs.upper(), lhs.lower()), tuple(rhs.upper(), rhs.lower())); | |
} | |
}; | |
template<class Operator, typename Lhs, typename Rhs> | |
struct comparison_operator<Operator, Lhs, Rhs, | |
enable_if_t<is_duplex_integer<Lhs>::value!=is_duplex_integer<Rhs>::value>> { | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const -> bool | |
{ | |
using common_type = common_type_t<Lhs, Rhs>; | |
return comparison_operator<Operator, common_type, common_type>{}(lhs, rhs); | |
} | |
}; | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<int Digits, typename Narrowest, typename Enable = void> | |
struct wide_integer_rep; | |
template<int Digits, typename Narrowest> | |
struct wide_integer_rep<Digits, Narrowest, enable_if_t<(max_digits<Narrowest>::value>=Digits)>> | |
: set_digits<Narrowest, max(Digits, digits<Narrowest>::value)> { | |
}; | |
template<int Digits, typename Narrowest> | |
struct wide_integer_rep<Digits, Narrowest, enable_if_t<(max_digits<Narrowest>::value<Digits)>> | |
: instantiate_duplex_integer<Digits, Narrowest> { | |
}; | |
template<int Digits, typename Narrowest> | |
using wide_integer_rep_t = typename wide_integer_rep<Digits, Narrowest>::type; | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<typename Lhs, typename Rhs> | |
struct heterogeneous_duplex_divide_operator { | |
using common_type = wide_integer_rep_t< | |
max(digits<Lhs>::value, digits<Rhs>::value), | |
set_signedness_t<int, is_signed<Lhs>::value|is_signed<Rhs>::value>>; | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const -> Lhs | |
{ | |
return static_cast<Lhs>(static_cast<common_type>(lhs) / static_cast<common_type>(rhs)); | |
} | |
}; | |
template<typename Upper, typename Lower> | |
struct binary_operator<divide_op, duplex_integer<Upper, Lower>, duplex_integer<Upper, Lower>> { | |
using _duplex_integer = duplex_integer<Upper, Lower>; | |
using _unsigned_duplex_integer = remove_signedness_t<_duplex_integer>; | |
constexpr auto operator()(_duplex_integer const& lhs, _duplex_integer const& rhs) const | |
-> _duplex_integer | |
{ | |
return (lhs<_duplex_integer{0}) | |
? (rhs<_duplex_integer{0}) | |
? non_negative_division(-lhs, -rhs) | |
: -non_negative_division(-lhs, rhs) | |
: (rhs<_duplex_integer{0}) | |
? -non_negative_division(lhs, -rhs) | |
: non_negative_division(lhs, rhs); | |
} | |
static constexpr auto non_negative_division( | |
_unsigned_duplex_integer const& dividend, | |
_unsigned_duplex_integer const& divisor) | |
-> _unsigned_duplex_integer | |
{ | |
auto const high = divisor.upper(); | |
if (!high) { | |
return div_by_lower(dividend, divisor.lower()); | |
} | |
int n = fls(high); | |
auto quot = div_by_lower(dividend >> n, (divisor >> n).lower()); | |
if (quot) { | |
--quot; | |
} | |
if ((dividend-quot*divisor)>=divisor) { | |
++quot; | |
} | |
return quot; | |
} | |
static constexpr auto fls(Upper n) -> int | |
{ | |
auto half_digits = numeric_limits<_duplex_integer>::digits/2; | |
if (!n) { | |
return 0; | |
} | |
auto const msd = Upper{1} << (half_digits-1); | |
for (int r = half_digits;; n <<= 1, r--) { | |
if (n & msd) { | |
return r; | |
} | |
} | |
}; | |
static constexpr auto | |
div_by_lower(_unsigned_duplex_integer const& dividend, Lower const& divisor) | |
-> _unsigned_duplex_integer | |
{ | |
_unsigned_duplex_integer rem = dividend; | |
_unsigned_duplex_integer b = divisor; | |
_unsigned_duplex_integer d = 1; | |
using unsigned_upper = set_signedness_t<Upper, false>; | |
auto high = rem.upper(); | |
_unsigned_duplex_integer quot = 0; | |
if (static_cast<unsigned_upper>(high)>=divisor) { | |
high /= divisor; | |
quot = _unsigned_duplex_integer{high, 0}; | |
rem -= _unsigned_duplex_integer(high*divisor, 0); | |
} | |
while (b<rem) { | |
b <<= 1; | |
d <<= 1; | |
} | |
do { | |
if (rem>=b) { | |
rem -= b; | |
quot += d; | |
} | |
b >>= 1; | |
d >>= 1; | |
} | |
while (d); | |
return quot; | |
}; | |
}; | |
template<typename LhsUpper, typename LhsLower, typename RhsUpper, typename RhsLower> | |
struct binary_operator<divide_op, duplex_integer<LhsUpper, LhsLower>, duplex_integer<RhsUpper, RhsLower>> | |
: heterogeneous_duplex_divide_operator< | |
duplex_integer<LhsUpper, LhsLower>, duplex_integer<RhsUpper, RhsLower>> { | |
}; | |
template<typename Lhs, typename RhsUpper, typename RhsLower> | |
struct binary_operator<divide_op, Lhs, duplex_integer<RhsUpper, RhsLower>> | |
: heterogeneous_duplex_divide_operator<Lhs, duplex_integer<RhsUpper, RhsLower>> { | |
}; | |
template<typename LhsUpper, typename LhsLower, typename Rhs> | |
struct binary_operator<divide_op, duplex_integer<LhsUpper, LhsLower>, Rhs> | |
: heterogeneous_duplex_divide_operator<duplex_integer<LhsUpper, LhsLower>, Rhs> { | |
}; | |
} | |
} | |
namespace cnl { | |
template<typename Upper, typename Lower> | |
struct add_signedness<_impl::duplex_integer < Upper, Lower>> | |
: _impl::type_identity<_impl::duplex_integer<add_signedness_t < Upper>, Lower>> { | |
}; | |
} | |
namespace cnl { | |
namespace _impl { | |
template<typename Lhs, typename Rhs> | |
struct heterogeneous_duplex_modulo_operator { | |
using common_type = wide_integer_rep_t< | |
max(digits<Lhs>::value, digits<Rhs>::value), | |
set_signedness_t<int, is_signed<Lhs>::value|is_signed<Rhs>::value>>; | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const -> Lhs | |
{ | |
return static_cast<Lhs>(static_cast<common_type>(lhs) / static_cast<common_type>(rhs)); | |
} | |
}; | |
template<typename Upper, typename Lower> | |
struct binary_operator<modulo_op, duplex_integer<Upper, Lower>, duplex_integer<Upper, Lower>> { | |
using _duplex_integer = duplex_integer<Upper, Lower>; | |
using _unsigned_duplex_integer = remove_signedness_t<_duplex_integer>; | |
constexpr auto operator()(_duplex_integer const& lhs, _duplex_integer const& rhs) const | |
-> _duplex_integer | |
{ | |
return lhs - rhs*(lhs/rhs); | |
} | |
}; | |
template<typename LhsUpper, typename LhsLower, typename RhsUpper, typename RhsLower> | |
struct binary_operator<modulo_op, duplex_integer<LhsUpper, LhsLower>, duplex_integer<RhsUpper, RhsLower>> | |
: heterogeneous_duplex_modulo_operator< | |
duplex_integer<LhsUpper, LhsLower>, duplex_integer<RhsUpper, RhsLower>> { | |
}; | |
template<typename Lhs, typename RhsUpper, typename RhsLower> | |
struct binary_operator<modulo_op, Lhs, duplex_integer<RhsUpper, RhsLower>> | |
: heterogeneous_duplex_modulo_operator<Lhs, duplex_integer<RhsUpper, RhsLower>> { | |
}; | |
template<typename LhsUpper, typename LhsLower, typename Rhs> | |
struct binary_operator<modulo_op, duplex_integer<LhsUpper, LhsLower>, Rhs> | |
: heterogeneous_duplex_modulo_operator<duplex_integer<LhsUpper, LhsLower>, Rhs> { | |
}; | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<typename Upper, typename Lower, int Digits> | |
struct set_width<_impl::duplex_integer<Upper, Lower>, Digits> { | |
using word = _impl::to_rep_t<Upper>; | |
using type = typename _impl::instantiate_duplex_integer<Digits+!is_signed<word>::value, word>::type; | |
}; | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template< | |
int Value, | |
typename Positive, typename Zero, typename Negative, | |
polarity Polarity = measure_polarity(Value)> | |
struct conditional3; | |
template<int Value, typename Positive, typename Zero, typename Negative> | |
struct conditional3<Value, Positive, Zero, Negative, polarity::neutral> : type_identity<Zero> { | |
}; | |
template<int Value, typename Positive, typename Zero, typename Negative> | |
struct conditional3<Value, Positive, Zero, Negative, polarity::negative> : type_identity<Negative> { | |
}; | |
template<int Value, typename Positive, typename Zero, typename Negative> | |
struct conditional3<Value, Positive, Zero, Negative, polarity::positive> : type_identity<Positive> { | |
}; | |
template<int Value, typename Positive, typename Zero, typename Negative> | |
using conditional3_t = typename conditional3<Value, Positive, Zero, Negative>::type; | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<typename Lhs, typename Rhs> | |
struct heterogeneous_duplex_multiply_operator { | |
using common_type = conditional3_t< | |
width<Lhs>::value-width<Rhs>::value, | |
Lhs, | |
conditional3_t< | |
(is_signed<Lhs>::value-is_signed<Rhs>::value), | |
Lhs, | |
void, | |
Rhs>, | |
Rhs>; | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const | |
-> decltype(std::declval<common_type>()*std::declval<common_type>()) | |
{ | |
return static_cast<common_type>(lhs)*static_cast<common_type>(rhs); | |
} | |
}; | |
template<typename T> | |
struct long_multiply; | |
template<typename Word> | |
struct long_multiply { | |
template<typename Lhs, typename Rhs> | |
using result_type = set_width_t<Word, width<Lhs>::value+width<Rhs>::value>; | |
template<typename Lhs, typename Rhs> | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const -> result_type<Lhs, Rhs> | |
{ | |
using result_type = result_type<Lhs, Rhs>; | |
return static_cast<result_type>(lhs)*static_cast<result_type>(rhs); | |
} | |
}; | |
template<typename Upper, typename Lower> | |
struct long_multiply<duplex_integer<Upper, Lower>> { | |
using result_type = duplex_integer<duplex_integer<Upper, Lower>, duplex_integer<Lower, Lower>>; | |
template<typename LhsUpper, typename LhsLower, typename RhsUpper, typename RhsLower> | |
constexpr auto operator()( | |
duplex_integer<LhsUpper, LhsLower> const& lhs, | |
duplex_integer<RhsUpper, RhsLower> const& rhs) const | |
-> result_type | |
{ | |
return multiply_components(lhs.upper(), lhs.lower(), rhs.upper(), rhs.lower()); | |
} | |
template<typename Lhs, typename RhsUpper, typename RhsLower> | |
constexpr auto operator()( | |
Lhs const& lhs, | |
duplex_integer<RhsUpper, RhsLower> const& rhs) const | |
-> result_type | |
{ | |
return multiply_components(0, lhs, rhs.upper(), rhs.lower()); | |
} | |
template<typename LhsUpper, typename LhsLower, typename Rhs> | |
constexpr auto operator()( | |
duplex_integer<LhsUpper, LhsLower> const& lhs, | |
Rhs const& rhs) const | |
-> result_type | |
{ | |
return multiply_components(lhs.upper(), lhs.lower(), 0, rhs); | |
} | |
template<typename LhsUpper, typename LhsLower, typename RhsUpper, typename RhsLower> | |
static constexpr auto multiply_components( | |
LhsUpper const& lhs_upper, LhsLower const& lhs_lower, | |
RhsUpper const& rhs_upper, RhsLower const& rhs_lower) | |
-> result_type | |
{ | |
return ((result_type{_impl::long_multiply<Upper>{}(lhs_upper, rhs_upper)}) | |
<< (digits<Lower>::value+digits<Upper>::value)) | |
+((result_type{_impl::long_multiply<Upper>{}(lhs_lower, rhs_upper)} | |
+result_type{_impl::long_multiply<Upper>{}(lhs_upper, rhs_lower)}) | |
<< digits<Lower>::value) | |
+((result_type{_impl::long_multiply<Lower>{}(lhs_lower, rhs_lower)})); | |
} | |
}; | |
template<typename Upper, typename Lower> | |
struct binary_operator<multiply_op, duplex_integer<Upper, Lower>, duplex_integer<Upper, Lower>> { | |
using _duplex_integer = duplex_integer<Upper, Lower>; | |
constexpr auto operator()(_duplex_integer const& lhs, _duplex_integer const& rhs) const | |
-> _duplex_integer | |
{ | |
return multiply_components(lhs.upper(), lhs.lower(), rhs.upper(), rhs.lower()); | |
} | |
static constexpr auto multiply_components( | |
Upper const& lhs_upper, | |
Lower const& lhs_lower, | |
Upper const& rhs_upper, | |
Lower const& rhs_lower) | |
-> _duplex_integer | |
{ | |
using common_result_type = decltype(long_multiply<Upper>{}(lhs_upper, rhs_upper)); | |
return (long_multiply<Upper>{}(lhs_upper, rhs_upper) << digits<Upper>::value) | |
+((long_multiply<Upper>{}(lhs_upper, rhs_lower)+long_multiply<Upper>{}(lhs_lower, rhs_upper)) | |
<< digits<Lower>::value) | |
+static_cast<common_result_type>(long_multiply<Lower>{}(lhs_lower, rhs_lower)); | |
} | |
}; | |
template<typename LhsUpper, typename LhsLower, typename RhsUpper, typename RhsLower> | |
struct binary_operator<multiply_op, duplex_integer<LhsUpper, LhsLower>, duplex_integer<RhsUpper, RhsLower>> | |
: heterogeneous_duplex_multiply_operator< | |
duplex_integer<LhsUpper, LhsLower>, duplex_integer<RhsUpper, RhsLower>> { | |
}; | |
template<typename LhsUpper, typename LhsLower, typename Rhs> | |
struct binary_operator<multiply_op, duplex_integer<LhsUpper, LhsLower>, Rhs> | |
: heterogeneous_duplex_multiply_operator< | |
duplex_integer<LhsUpper, LhsLower>, Rhs> { | |
}; | |
template<typename Lhs, typename RhsUpper, typename RhsLower> | |
struct binary_operator<multiply_op, Lhs, duplex_integer<RhsUpper, RhsLower>> | |
: heterogeneous_duplex_multiply_operator< | |
Lhs, duplex_integer<RhsUpper, RhsLower>> { | |
}; | |
} | |
} | |
namespace cnl { | |
template<typename Upper, typename Lower> | |
struct to_rep<_impl::duplex_integer<Upper, Lower>> { | |
constexpr auto operator()(_impl::duplex_integer<Upper, Lower> const& number) const | |
-> _impl::to_rep_t<Upper> | |
{ | |
return _impl::to_rep(Upper(number)); | |
} | |
}; | |
} | |
namespace cnl { | |
template<typename Upper, typename Lower, int Digits> | |
struct set_digits<_impl::duplex_integer<Upper, Lower>, Digits> | |
: _impl::instantiate_duplex_integer<Digits, _impl::to_rep_t<Upper>> { | |
}; | |
} | |
namespace cnl { | |
namespace _impl { | |
template<typename Upper, typename Lower, typename Rhs> | |
struct binary_operator<shift_left_op, duplex_integer<Upper, Lower>, Rhs> { | |
using _duplex_integer = duplex_integer<Upper, Lower>; | |
constexpr auto operator()(_duplex_integer const& lhs, Rhs const& rhs) const | |
-> _duplex_integer | |
{ | |
return with_int(lhs, static_cast<int>(rhs)); | |
} | |
private: | |
constexpr auto with_int(_duplex_integer const& lhs, int const& rhs) const | |
-> _duplex_integer | |
{ | |
return _duplex_integer( | |
sensible_left_shift<Upper>(lhs.upper(), rhs) | |
| extra_sensible_right_shift<Upper>(lhs.lower(), width<Lower>::value-rhs), | |
sensible_left_shift<Lower>(lhs.lower(), rhs)); | |
} | |
}; | |
template<typename Upper, typename Lower, typename Rhs> | |
struct binary_operator<shift_right_op, duplex_integer<Upper, Lower>, Rhs> { | |
using _duplex_integer = duplex_integer<Upper, Lower>; | |
constexpr auto operator()(_duplex_integer const& lhs, Rhs const& rhs) const | |
-> _duplex_integer | |
{ | |
return with_int(lhs, static_cast<int>(rhs)); | |
} | |
private: | |
constexpr auto with_int(_duplex_integer const& lhs, int rhs) const | |
-> _duplex_integer | |
{ | |
return _duplex_integer(calculate_upper(lhs, rhs), calculate_lower(lhs, rhs)); | |
} | |
constexpr auto calculate_upper(_duplex_integer const& lhs, int rhs) const | |
-> Upper | |
{ | |
return sensible_right_shift<Upper>(lhs.upper(), rhs); | |
} | |
constexpr auto calculate_lower(_duplex_integer const& lhs, int rhs) const | |
-> Lower | |
{ | |
return static_cast<Lower>( | |
sensible_right_shift<Lower>(lhs.lower(), rhs) | |
| extra_sensible_right_shift<Lower>(lhs.upper(), rhs-width<Lower>::value)); | |
} | |
}; | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<typename Upper, typename Lower> | |
struct max_to_chars_chars<duplex_integer<Upper, Lower>> { | |
using _scalar = duplex_integer<Upper, Lower>; | |
static constexpr auto _sign_chars = static_cast<int>(cnl::is_signed<_scalar>::value); | |
static constexpr auto _integer_chars = ((cnl::digits<_scalar>::value+2)/3); | |
static constexpr auto value = _sign_chars+_integer_chars; | |
}; | |
template<typename Upper, typename Lower> | |
to_chars_result to_chars_positive( | |
char* const first, char* const last, duplex_integer<Upper, Lower> const& value) noexcept | |
{ | |
auto const natural_last = to_chars_natural(first, last, value); | |
return to_chars_result{natural_last, natural_last?std::errc{}:std::errc::value_too_large}; | |
} | |
template<typename Upper, typename Lower> | |
to_chars_result to_chars( | |
char* const first, | |
char* const last, | |
_impl::duplex_integer<Upper, Lower> const& value) | |
{ | |
if (!value) { | |
if (first==last) { | |
return to_chars_result{last, std::errc::value_too_large}; | |
} | |
*first = '0'; | |
return to_chars_result{first+1, std::errc{}}; | |
} | |
using native_rounding_type = set_rounding_t<decltype(value), native_rounding_tag>; | |
auto const& native_rounding_value = static_cast<native_rounding_type>(value); | |
return _impl::to_chars_non_zero<native_rounding_type>{}( | |
first, last, native_rounding_value); | |
} | |
} | |
using _impl::to_chars; | |
} | |
namespace cnl { | |
template<int Digits, typename Narrowest, typename Rep> | |
struct from_rep<_impl::wide_integer<Digits, Narrowest>, Rep> { | |
constexpr auto operator()(Rep const& rep) const | |
-> _impl::wide_integer< | |
Digits, | |
_impl::adopt_signedness_t<Narrowest, Rep>> | |
{ | |
return rep; | |
} | |
}; | |
} | |
namespace cnl { | |
template<int Digits, class Narrowest, class Value> | |
struct from_value<_impl::wide_integer<Digits, Narrowest>, Value> | |
: _impl::from_value_simple< | |
_impl::wide_integer< | |
cnl::digits<Value>::value, | |
_impl::adopt_signedness_t<Narrowest, Value>>, | |
Value> { | |
}; | |
} | |
namespace cnl { | |
namespace _impl { | |
template<typename Rep> | |
constexpr auto make_wide_integer(Rep const& rep) | |
-> wide_integer< | |
digits<Rep>::value, | |
adopt_signedness_t<int, Rep>> { | |
return rep; | |
} | |
} | |
} | |
namespace cnl { | |
template<int Digits, typename Narrowest> | |
struct numeric_limits<_impl::wide_integer<Digits, Narrowest>> | |
: numeric_limits<typename _impl::wide_integer<Digits, Narrowest>::rep> { | |
static constexpr bool is_integer = true; | |
using _narrowest_numeric_limits = numeric_limits<Narrowest>; | |
using _value_type = _impl::wide_integer<Digits, Narrowest>; | |
using _rep = typename _value_type::rep; | |
using _rep_numeric_limits = numeric_limits<_rep>; | |
static constexpr int digits = Digits; | |
static constexpr _value_type min() noexcept | |
{ | |
return _impl::from_rep<_value_type>(1); | |
} | |
static constexpr _value_type max() noexcept | |
{ | |
return static_cast<_rep>(_rep_numeric_limits::max() >> (_rep_numeric_limits::digits-digits)); | |
} | |
static constexpr _value_type lowest() noexcept | |
{ | |
return static_cast<_rep>(_rep_numeric_limits::lowest() >> (_rep_numeric_limits::digits-digits)); | |
} | |
}; | |
template<int Digits, typename Narrowest> | |
struct numeric_limits<_impl::wide_integer<Digits, Narrowest> const> | |
: numeric_limits<_impl::wide_integer<Digits, Narrowest>> { | |
static constexpr bool is_integer = true; | |
}; | |
} | |
namespace cnl { | |
namespace _impl { | |
template<int LhsDigits, class LhsNarrowest, class Rhs> | |
constexpr auto operator<<(wide_integer<LhsDigits, LhsNarrowest> const& lhs, Rhs const& rhs) | |
-> wide_integer<LhsDigits, LhsNarrowest> | |
{ | |
return wide_integer<LhsDigits, LhsNarrowest>( | |
to_rep(lhs) << set_signedness_t<int, is_signed<Rhs>::value>(rhs)); | |
} | |
template<int LhsDigits, class LhsNarrowest, class Rhs> | |
constexpr auto operator>>(wide_integer<LhsDigits, LhsNarrowest> const& lhs, Rhs const& rhs) | |
-> wide_integer<LhsDigits, LhsNarrowest> | |
{ | |
return wide_integer<LhsDigits, LhsNarrowest>( | |
to_rep(lhs) >> set_signedness_t<int, is_signed<Rhs>::value>(rhs)); | |
} | |
template<typename Operator, int Digits, typename Narrowest> | |
struct unary_operator<Operator, wide_integer<Digits, Narrowest>> { | |
constexpr auto operator()(wide_integer<Digits, Narrowest> const& rhs) const -> wide_integer<Digits, Narrowest> | |
{ | |
return Operator()(to_rep(rhs)); | |
} | |
}; | |
template<class Operator, int LhsDigits, typename LhsNarrowest, int RhsDigits, typename RhsNarrowest> | |
struct binary_operator< | |
Operator, | |
wide_integer<LhsDigits, LhsNarrowest>, wide_integer<RhsDigits, RhsNarrowest>> { | |
using _lhs = wide_integer<LhsDigits, LhsNarrowest>; | |
using _rhs = wide_integer<RhsDigits, RhsNarrowest>; | |
using _result = typename std::common_type<_lhs, _rhs>::type; | |
constexpr auto operator()(_lhs const& lhs, _rhs const& rhs) const -> _result | |
{ | |
return Operator{}(to_rep(lhs), to_rep(rhs)); | |
} | |
}; | |
template<class Operator, int LhsDigits, typename LhsNarrowest, int RhsDigits, typename RhsNarrowest> | |
struct comparison_operator< | |
Operator, | |
wide_integer<LhsDigits, LhsNarrowest>, wide_integer<RhsDigits, RhsNarrowest>> { | |
constexpr auto operator()( | |
wide_integer<LhsDigits, LhsNarrowest> const& lhs, | |
wide_integer<RhsDigits, RhsNarrowest> const& rhs) const | |
-> decltype(Operator()(to_rep(lhs), to_rep(rhs))) | |
{ | |
return Operator()(to_rep(lhs), to_rep(rhs)); | |
} | |
}; | |
template<class Operator, int Digits, typename Narrowest> | |
struct pre_operator<Operator, wide_integer<Digits, Narrowest>> { | |
constexpr auto operator()(wide_integer<Digits, Narrowest>& rhs) const | |
-> wide_integer<Digits, Narrowest>& | |
{ | |
Operator()(_impl::to_rep(rhs)); | |
return rhs; | |
} | |
}; | |
template<class Operator, int Digits, typename Narrowest> | |
struct post_operator<Operator, wide_integer<Digits, Narrowest>> { | |
constexpr auto operator()(wide_integer<Digits, Narrowest>& lhs) const | |
-> wide_integer<Digits, Narrowest> | |
{ | |
auto copy = lhs; | |
Operator()(_impl::to_rep(lhs)); | |
return copy; | |
} | |
}; | |
template<int Digits, typename Narrowest> | |
::std::ostream& operator<<(::std::ostream& out, wide_integer<Digits, Narrowest> const& value) | |
{ | |
return out << to_rep(value); | |
} | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<class Operator, typename Upper, typename Lower> | |
struct default_binary_operator { | |
using _duplex_integer = duplex_integer<Upper, Lower>; | |
constexpr auto operator()(_duplex_integer const& lhs, _duplex_integer const& rhs) const | |
-> _duplex_integer | |
{ | |
return _duplex_integer( | |
static_cast<Upper>(Operator{}(lhs.upper(), rhs.upper())), | |
static_cast<Lower>(Operator{}(lhs.lower(), rhs.lower()))); | |
} | |
}; | |
template<class Operator, typename Upper, typename Lower> | |
struct first_degree_binary_operator { | |
using _duplex_integer = duplex_integer<Upper, Lower>; | |
static constexpr auto lower_digits = digits<Lower>::value; | |
using wide_lower = set_digits_t<set_signedness_t<Lower, true>, lower_digits+1>; | |
constexpr auto operator()(_duplex_integer const& lhs, _duplex_integer const& rhs) const | |
-> _duplex_integer | |
{ | |
return from_sums( | |
static_cast<Upper>(Operator{}(lhs.upper(), rhs.upper())), | |
wide_lower(Operator{}(wide_lower{lhs.lower()}, wide_lower{rhs.lower()}))); | |
} | |
static constexpr auto from_sums(Upper const& upper_sum, wide_lower const& lower_sum) | |
-> _duplex_integer | |
{ | |
return _duplex_integer{ | |
static_cast<Upper>(upper_sum+static_cast<Upper>(lower_sum >> constant<lower_digits>{})), | |
static_cast<Lower>(lower_sum)}; | |
} | |
}; | |
template<typename Upper, typename Lower> | |
struct unary_operator<bitwise_not_op, duplex_integer<Upper, Lower>> { | |
constexpr auto operator()(duplex_integer<Upper, Lower> const& rhs) const | |
-> duplex_integer<Upper, Lower> | |
{ | |
return duplex_integer<Upper, Lower>(~rhs.upper(), ~rhs.lower()); | |
} | |
}; | |
template<typename Upper, typename Lower> | |
struct unary_operator<minus_op, duplex_integer<Upper, Lower>> { | |
constexpr auto operator()(duplex_integer<Upper, Lower> const& rhs) const | |
-> duplex_integer<Upper, Lower> | |
{ | |
return unary_operator<bitwise_not_op, duplex_integer<Upper, Lower>>{}( | |
rhs-duplex_integer<Upper, Lower>{1}); | |
} | |
}; | |
template<typename Upper, typename Lower> | |
struct unary_operator<plus_op, duplex_integer<Upper, Lower>> { | |
constexpr auto operator()(duplex_integer<Upper, Lower> const& rhs) const | |
-> duplex_integer<Upper, Lower> | |
{ | |
return duplex_integer<Upper, Lower>(+rhs.upper(), +rhs.lower()); | |
} | |
}; | |
template<class Operator, typename Upper, typename Lower, typename Rhs> | |
struct binary_operator<Operator, duplex_integer<Upper, Lower>, Rhs> | |
: binary_operator<Operator, duplex_integer<Upper, Lower>, duplex_integer<Upper, Lower>> { | |
}; | |
template<typename Upper, typename Lower> | |
struct binary_operator<add_op, duplex_integer<Upper, Lower>, duplex_integer<Upper, Lower>> | |
: first_degree_binary_operator<add_op, Upper, Lower> { | |
}; | |
template<typename Upper, typename Lower> | |
struct binary_operator<subtract_op, duplex_integer<Upper, Lower>, duplex_integer<Upper, Lower>> | |
: first_degree_binary_operator<subtract_op, Upper, Lower> { | |
}; | |
template<typename Upper, typename Lower> | |
struct binary_operator<bitwise_or_op, duplex_integer<Upper, Lower>, duplex_integer<Upper, Lower>> | |
: default_binary_operator<bitwise_or_op, Upper, Lower> { | |
}; | |
template<typename Upper, typename Lower> | |
struct binary_operator<bitwise_and_op, duplex_integer<Upper, Lower>, duplex_integer<Upper, Lower>> | |
: default_binary_operator<bitwise_and_op, Upper, Lower> { | |
}; | |
template<typename Upper, typename Lower> | |
struct binary_operator<bitwise_xor_op, duplex_integer<Upper, Lower>, duplex_integer<Upper, Lower>> | |
: default_binary_operator<bitwise_xor_op, Upper, Lower> { | |
}; | |
template<typename Operator, typename LhsUpper, typename LhsLower, typename RhsUpper, typename RhsLower> | |
struct comparison_operator<Operator, duplex_integer<LhsUpper, LhsLower>, duplex_integer<RhsUpper, RhsLower>> { | |
constexpr auto operator()( | |
duplex_integer<LhsUpper, LhsLower> const& lhs, | |
duplex_integer<RhsUpper, RhsLower> const& rhs) const -> bool | |
{ | |
using common_type = duplex_integer< | |
common_type_t<LhsUpper, RhsUpper>, | |
common_type_t<LhsLower, RhsLower>>; | |
return comparison_operator<Operator, common_type, common_type>{}(lhs, rhs); | |
} | |
}; | |
template<typename Upper, typename Lower> | |
struct pre_operator<pre_increment_op, duplex_integer<Upper, Lower>> { | |
constexpr auto operator()(duplex_integer<Upper, Lower>& rhs) const | |
-> duplex_integer<Upper, Lower> | |
{ | |
return __builtin_expect(!!(rhs.lower()==numeric_limits<Lower>::max()), 0) | |
? duplex_integer<Upper, Lower>{++rhs.upper(), numeric_limits<Lower>::lowest()} | |
: duplex_integer<Upper, Lower>{rhs.upper(), ++rhs.lower()}; | |
} | |
}; | |
template<typename Upper, typename Lower> | |
struct pre_operator<pre_decrement_op, duplex_integer<Upper, Lower>> { | |
constexpr auto operator()(duplex_integer<Upper, Lower>& rhs) const | |
-> duplex_integer<Upper, Lower> | |
{ | |
return __builtin_expect(!!(rhs.lower()==numeric_limits<Lower>::lowest()), 0) | |
? duplex_integer<Upper, Lower>{static_cast<Upper>(--rhs.upper()), numeric_limits<Lower>::max()} | |
: duplex_integer<Upper, Lower>{rhs.upper(), --rhs.lower()}; | |
} | |
}; | |
template<typename Upper, typename Lower> | |
::std::ostream& operator<<(::std::ostream& out, duplex_integer<Upper, Lower> const& value) | |
{ | |
return out << cnl::to_chars(value).data(); | |
} | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<typename Lower, typename Integer> | |
constexpr auto calculate_lower(Integer const& input) | |
-> enable_if_t<digits<Lower>::value>=digits<Integer>::value, Lower> | |
{ | |
return Lower(input) & numeric_limits<Lower>::max(); | |
} | |
template<typename Lower, typename Integer> | |
constexpr auto calculate_lower(Integer const& input) | |
-> enable_if_t<digits<Lower>::value<digits<Integer>::value, Lower> | |
{ | |
return static_cast<Lower>(input & static_cast<Integer>(numeric_limits<Lower>::max())); | |
} | |
template<typename Upper, typename Lower, typename Integer> | |
constexpr auto calculate_upper(Integer const& input) | |
-> enable_if_t<digits<Lower>::value>=digits<Integer>::value, Upper> | |
{ | |
return static_cast<Upper>((input >> (digits<Integer>::value-1)) >> 1); | |
} | |
template<typename Upper, typename Lower, typename Integer> | |
constexpr auto calculate_upper(Integer const& input) | |
-> enable_if_t<digits<Lower>::value<digits<Integer>::value, Upper> | |
{ | |
return sensible_right_shift<Upper>(input, digits<Lower>::value); | |
} | |
template<typename Upper, typename Lower> | |
constexpr int duplex_integer<Upper, Lower>::lower_width; | |
template<typename Upper, typename Lower> | |
constexpr duplex_integer<Upper, Lower>::duplex_integer(upper_type const& u, lower_type const& l) | |
:_upper(u), _lower(l) { } | |
template<typename Upper, typename Lower> | |
template<typename Integer, _impl::enable_if_t<(numeric_limits<Integer>::is_integer), int> Dummy> | |
constexpr duplex_integer<Upper, Lower>::duplex_integer(Integer const& i) | |
: _upper(calculate_upper<Upper, Lower>(i)), | |
_lower(calculate_lower<Lower>(i)) | |
{ | |
} | |
template<typename Upper, typename Lower> | |
template<typename Number, _impl::enable_if_t<(numeric_limits<Number>::is_iec559), int> Dummy> | |
constexpr duplex_integer<Upper, Lower>::duplex_integer(Number const& n) | |
: _upper(Upper(n / cnl::power<Number, lower_width, 2>())), | |
_lower(Lower(std::fmod(n, cnl::power<Number, lower_width, 2>()))) | |
{ | |
} | |
} | |
} | |
namespace cnl { | |
template<typename Upper, typename Lower> | |
struct numeric_limits<_impl::duplex_integer<Upper, Lower>> | |
: numeric_limits<Upper> { | |
static constexpr bool is_integer = true; | |
using _lower_numeric_limits = numeric_limits<Lower>; | |
using _upper_numeric_limits = numeric_limits<Upper>; | |
using _value_type = _impl::duplex_integer<Upper, Lower>; | |
static constexpr int digits = _lower_numeric_limits::digits+_upper_numeric_limits::digits; | |
static constexpr _value_type lowest() noexcept | |
{ | |
return _value_type{ | |
numeric_limits<Upper>::lowest(), | |
numeric_limits<Lower>::lowest()}; | |
} | |
static constexpr _value_type min() noexcept | |
{ | |
return _value_type{ | |
numeric_limits<Upper>::min(), | |
numeric_limits<Lower>::min()}; | |
} | |
static constexpr _value_type max() noexcept | |
{ | |
return _value_type{ | |
numeric_limits<Upper>::max(), | |
numeric_limits<Lower>::max()}; | |
} | |
}; | |
} | |
namespace cnl { | |
template<typename Upper, typename Lower> | |
struct rounding<_impl::duplex_integer<Upper, Lower>> | |
: _impl::type_identity<native_rounding_tag> { | |
static_assert( | |
std::is_same<rounding_t<Upper>, native_rounding_tag>::value, | |
"This type can only be specialized with integers that have int-like rounding behavior."); | |
static_assert( | |
std::is_same<rounding_t<Lower>, native_rounding_tag>::value, | |
"This type can only be specialized with integers that have int-like rounding behavior."); | |
}; | |
} | |
namespace cnl { | |
namespace _impl { | |
template<typename Rep=int> | |
class integer : public number_base<integer<Rep>, Rep> { | |
using _base = number_base<integer<Rep>, Rep>; | |
public: | |
integer() = default; | |
template<typename T> | |
constexpr integer(T const& value) | |
: _base(static_cast<Rep>(value)) { } | |
template<class S> | |
explicit constexpr operator S() const | |
{ | |
return static_cast<S>(_impl::to_rep(*this)); | |
} | |
}; | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<int Digits, typename Narrowest> | |
class wide_integer | |
: public number_base< | |
wide_integer<Digits, Narrowest>, | |
wide_integer_rep_t<Digits, Narrowest>> { | |
using _base = number_base< | |
wide_integer<Digits, Narrowest>, | |
wide_integer_rep_t<Digits, Narrowest>>; | |
public: | |
using rep = typename _base::rep; | |
wide_integer() = default; | |
template<typename T> | |
constexpr wide_integer(T const& value) | |
: _base(static_cast<rep>(value)) { } | |
template<class S> | |
explicit constexpr operator S() const | |
{ | |
return static_cast<S>(_impl::to_rep(*this)); | |
} | |
}; | |
} | |
} | |
namespace std { | |
template<int Digits1, typename Narrowest1, int Digits2, typename Narrowest2> | |
struct common_type<cnl::_impl::wide_integer<Digits1, Narrowest1>, cnl::_impl::wide_integer<Digits2, Narrowest2>> { | |
static constexpr auto _max_digits = cnl::_impl::max(Digits1, Digits2); | |
static constexpr auto _are_signed = cnl::is_signed<Narrowest1>::value || cnl::is_signed<Narrowest2>::value; | |
using _common_type = typename std::common_type<Narrowest1, Narrowest2>::type; | |
using _narrowest = cnl::_impl::set_signedness_t<_common_type, _are_signed>; | |
using type = cnl::_impl::wide_integer<cnl::_impl::max(Digits1, Digits2), _narrowest>; | |
}; | |
} | |
namespace cnl { | |
template<int Digits, typename Narrowest> | |
struct digits<_impl::wide_integer<Digits, Narrowest>> | |
: std::integral_constant<int, Digits> { | |
}; | |
} | |
namespace cnl { | |
namespace _impl { | |
template<int Base, typename ParseDigit, typename Integer> | |
constexpr auto wide_integer_parse(char const* s, ParseDigit parse_digit, Integer const& value) -> Integer | |
{ | |
return *s | |
? wide_integer_parse<Base>( | |
s+1, | |
parse_digit, | |
Integer{parse_digit(*s)}+value*Base) | |
: value; | |
} | |
constexpr int parse_dec_char(char c) | |
{ | |
return (c>='0' && c<='9') ? c-'0' : unreachable<int>("invalid decimal digits"); | |
} | |
template<int NumChars> | |
constexpr auto decimal_wide_integer_parse(const char (& s)[NumChars]) | |
-> wide_integer<(NumChars-1)*3322/1000+1> | |
{ | |
using result = wide_integer<(NumChars-1)*3322/1000+1>; | |
return result(wide_integer_parse<10>(s, parse_dec_char, typename result::rep{})); | |
} | |
template<char ... Chars> | |
struct wide_integer_parser { | |
constexpr auto operator()() const | |
-> decltype(decimal_wide_integer_parse<sizeof...(Chars)+1>({Chars..., '\0'})) | |
{ | |
return decimal_wide_integer_parse<sizeof...(Chars)+1>({Chars..., '\0'}); | |
} | |
}; | |
constexpr int parse_oct_char(char c) | |
{ | |
return (c>='0' && c<='7') ? c-'0' : unreachable<int>("invalid octal digits"); | |
} | |
template<int NumChars> | |
constexpr auto octal_wide_integer_parse(const char (& s)[NumChars]) | |
-> wide_integer<(NumChars-1)*3> | |
{ | |
using result = wide_integer<(NumChars-1)*3>; | |
return result(wide_integer_parse<8>(s, parse_oct_char, typename result::rep{})); | |
} | |
template<char ... Chars> | |
struct wide_integer_parser<'0', Chars...> { | |
constexpr auto operator()() const | |
-> decltype(octal_wide_integer_parse<sizeof...(Chars)+1>({Chars..., '\0'})) | |
{ | |
return octal_wide_integer_parse<sizeof...(Chars)+1>({Chars..., '\0'}); | |
} | |
}; | |
constexpr int parse_bin_char(char c) | |
{ | |
return (c=='0') ? 0 : (c=='1') ? 1 : unreachable<int>("invalid hexadecimal digits"); | |
} | |
template<int NumChars> | |
constexpr auto binary_wide_integer_parse(const char (& s)[NumChars]) | |
-> wide_integer<NumChars-1> | |
{ | |
using result = wide_integer<NumChars-1>; | |
return result(wide_integer_parse<2>(s, parse_bin_char, typename result::rep{})); | |
} | |
template<char ... Chars> | |
struct wide_integer_parser<'0', 'B', Chars...> { | |
constexpr auto operator()() const | |
-> decltype(binary_wide_integer_parse<sizeof...(Chars)+1>({Chars..., '\0'})) | |
{ | |
return binary_wide_integer_parse<sizeof...(Chars)+1>({Chars..., '\0'}); | |
} | |
}; | |
template<char ... Chars> | |
struct wide_integer_parser<'0', 'b', Chars...> { | |
constexpr auto operator()() const | |
-> decltype(binary_wide_integer_parse<sizeof...(Chars)+1>({Chars..., '\0'})) | |
{ | |
return binary_wide_integer_parse<sizeof...(Chars)+1>({Chars..., '\0'}); | |
} | |
}; | |
constexpr int parse_hex_char(char c) | |
{ | |
return (c>='0' && c<='9') ? c-'0' : (c>='a' && c<='z') ? c+10-'a' : (c>='A' && c<='Z') ? c | |
+10-'A' : unreachable<int>("invalid hexadecimal digits"); | |
} | |
template<int NumChars> | |
constexpr auto hexadecimal_wide_integer_parse(const char (& s)[NumChars]) | |
-> wide_integer<(NumChars-1)*4> | |
{ | |
using result = wide_integer<(NumChars-1)*4>; | |
return result(wide_integer_parse<16>(s, parse_hex_char, typename result::rep{})); | |
} | |
template<char ... Chars> | |
struct wide_integer_parser<'0', 'X', Chars...> { | |
constexpr auto operator()() const | |
-> decltype(hexadecimal_wide_integer_parse<sizeof...(Chars)+1>({Chars..., '\0'})) | |
{ | |
return hexadecimal_wide_integer_parse<sizeof...(Chars)+1>({Chars..., '\0'}); | |
} | |
}; | |
template<char ... Chars> | |
struct wide_integer_parser<'0', 'x', Chars...> { | |
constexpr auto operator()() const | |
-> decltype(hexadecimal_wide_integer_parse<sizeof...(Chars)+1>({Chars..., '\0'})) | |
{ | |
return hexadecimal_wide_integer_parse<sizeof...(Chars)+1>({Chars..., '\0'}); | |
} | |
}; | |
template<char ... Chars> | |
constexpr auto operator "" _wide() | |
-> decltype(_impl::wide_integer_parser<Chars...>{}()) | |
{ | |
return _impl::wide_integer_parser<Chars...>{}(); | |
} | |
} | |
} | |
namespace cnl { | |
template<int Digits, int Radix, int WideIntegerDigits, typename WideIntegerNarrowest> | |
struct scale<Digits, Radix, _impl::wide_integer<WideIntegerDigits, WideIntegerNarrowest>> | |
: _impl::default_scale<Digits, Radix, _impl::wide_integer<WideIntegerDigits, WideIntegerNarrowest>> { | |
}; | |
} | |
namespace cnl { | |
template<int FromDigits, class Rep, int ToDigits> | |
struct set_digits<_impl::wide_integer<FromDigits, Rep>, ToDigits> | |
: _impl::type_identity<_impl::wide_integer<ToDigits, Rep>> { | |
}; | |
} | |
namespace cnl { | |
template<int Digits = digits<int>::value, typename Narrowest = int> | |
using wide_integer = _impl::wide_integer<Digits, Narrowest>; | |
namespace literals { | |
using _impl::operator "" _wide; | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template< | |
int Digits = digits<int>::value, | |
class RoundingTag = nearest_rounding_tag, | |
class OverflowTag = undefined_overflow_tag, | |
class Narrowest = int> | |
using static_integer = rounding_integer< | |
overflow_integer< | |
elastic_integer< | |
Digits, | |
wide_integer< | |
digits<Narrowest>::value, | |
Narrowest>>, | |
OverflowTag>, | |
RoundingTag>; | |
template< | |
class RoundingTag = nearest_rounding_tag, | |
class OverflowTag = undefined_overflow_tag, | |
class Narrowest = int, | |
class Input = int> | |
_impl::enable_if_t<!_impl::is_constant<Input>::value, | |
static_integer< | |
numeric_limits<Input>::digits, | |
RoundingTag, OverflowTag, | |
Narrowest>> | |
constexpr make_static_integer(Input const& input) | |
{ | |
return input; | |
} | |
template< | |
class RoundingTag = nearest_rounding_tag, | |
class OverflowTag = undefined_overflow_tag, | |
class Narrowest = int, | |
::cnl::intmax InputValue = 0> | |
static_integer< | |
used_digits(InputValue), | |
RoundingTag, OverflowTag, | |
Narrowest> | |
constexpr make_static_integer(constant<InputValue>) | |
{ | |
return InputValue; | |
} | |
} | |
} | |
namespace cnl { | |
template< | |
int Digits = digits<int>::value, | |
class RoundingTag = nearest_rounding_tag, | |
class OverflowTag = undefined_overflow_tag, | |
class Narrowest = int> | |
using static_integer = _impl::static_integer<Digits, RoundingTag, OverflowTag, Narrowest>; | |
using _impl::make_static_integer; | |
} | |
namespace cnl { | |
template< | |
int Digits, | |
int Exponent = 0, | |
class RoundingTag = nearest_rounding_tag, | |
class OverflowTag = undefined_overflow_tag, | |
class Narrowest = signed> | |
using static_number = fixed_point< | |
_impl::static_integer<Digits, RoundingTag, OverflowTag, Narrowest>, | |
Exponent>; | |
template< | |
class RoundingTag = nearest_rounding_tag, | |
class OverflowTag = undefined_overflow_tag, | |
class Narrowest = signed, | |
class Input = int> | |
constexpr auto make_static_number(Input const& input) | |
-> static_number< | |
numeric_limits<Input>::digits, 0, | |
RoundingTag, OverflowTag, | |
Narrowest> | |
{ | |
return input; | |
} | |
template< | |
class RoundingTag = rounding_integer<>::rounding, | |
class OverflowTag = overflow_integer<>::overflow_tag, | |
class Narrowest = int, | |
class Input = int, | |
::cnl::intmax Value> | |
constexpr auto make_static_number(constant<Value> const&) | |
-> static_number< | |
_impl::used_digits(Value)-trailing_bits(Value), trailing_bits(Value), | |
RoundingTag, OverflowTag, | |
Narrowest> | |
{ | |
return constant<Value>{}; | |
} | |
} | |
namespace cnl { | |
using _impl::common_type_t; | |
using _impl::enable_if_t; | |
using _impl::is_integral; | |
using _impl::is_integral_v; | |
using _impl::remove_cvref_t; | |
using _impl::type_identity; | |
using _impl::type_identity_t; | |
} | |
#endif // CNL_COMPLETE_H |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment