Last active
August 4, 2018 19:03
-
-
Save johnmcfarlane/d2d51e122d72c5a235127e437a9e3be7 to your computer and use it in GitHub Desktop.
This file contains 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 <algorithm> | |
#include <array> | |
#include <climits> | |
#include <cmath> | |
#include <cstdint> | |
#include <istream> | |
#include <limits> | |
#include <numeric> | |
#include <stdexcept> | |
#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 intmax = std::intmax_t; | |
using uintmax = std::uintmax_t; | |
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{}; | |
} | |
template<int NumChars> | |
constexpr intmax parse(const char (& s)[NumChars]) | |
{ | |
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<char... Chars> | |
constexpr intmax parse() { | |
return parse<sizeof...(Chars) + 1>({Chars...,'\0'}); | |
} | |
} | |
} | |
namespace cnl { | |
template<class T> | |
struct numeric_limits : std::numeric_limits<T> {}; | |
} | |
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 { | |
template<typename T> | |
struct digits : std::integral_constant<int, numeric_limits<T>::digits> { | |
static_assert(numeric_limits<T>::is_specialized, "cnl::digits is not correctly specialized for T"); | |
}; | |
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<class T> | |
struct is_signed : std::integral_constant<bool, numeric_limits<T>::is_signed> { | |
}; | |
} | |
namespace cnl { | |
namespace _impl { | |
template<class ... T> | |
using common_type_t = typename std::common_type<T ...>::type; | |
template<bool C, class ... T> | |
using enable_if_t = typename std::enable_if<C, T ...>::type; | |
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; | |
} | |
template<typename Expected, typename Actual> | |
struct assert_same; | |
template<typename T> | |
struct assert_same<T, T> : std::true_type { | |
}; | |
template<typename T> | |
using remove_cvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type; | |
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, class = void> | |
struct make_unsigned; | |
template<class T> | |
struct make_unsigned<T, _impl::enable_if_t<std::is_integral<T>::value>> : std::make_unsigned<T> { | |
}; | |
template<class T> | |
using make_unsigned_t = typename make_unsigned<T>::type; | |
} | |
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<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<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<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<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<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 make_unsigned<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 { | |
template<typename T> | |
constexpr T sqrt(T arg) { | |
return std::sqrt(arg); | |
} | |
} | |
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> | |
struct default_power<S, 0, 2, false, false, false> { | |
constexpr int operator()() const | |
{ | |
return 1; | |
} | |
}; | |
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 { | |
namespace _impl { | |
template<class T> | |
struct is_integral : std::is_integral<T> { | |
}; | |
} | |
} | |
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 | |
{ | |
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 { | |
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> | |
constexpr bool narrower_than(int digits) | |
{ | |
return std::is_same<T, void>::value ? true : numeric_limits<T>::digits<digits; | |
} | |
template<typename T> | |
constexpr bool no_narrower_than(int digits) | |
{ | |
return 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) && narrower_than<Smaller>(MinNumDigits)>::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, 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, 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<class T, int Digits> | |
using set_digits_t = typename set_digits<T, Digits>::type; | |
} | |
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 { | |
template<class Number, class Enable = void> | |
struct from_rep; | |
template<class Number> | |
struct from_rep<Number, _impl::enable_if_t<cnl::_impl::is_integral<Number>::value>> { | |
template<class Rep> | |
constexpr Number operator()(Rep const& rep) const { | |
return static_cast<Number>(rep); | |
} | |
}; | |
} | |
namespace cnl { | |
template<class, class = void> | |
struct make_signed; | |
template<class T> | |
struct make_signed<T, _impl::enable_if_t<std::is_integral<T>::value>> : std::make_signed<T> { | |
}; | |
template<class T> | |
using make_signed_t = typename make_signed<T>::type; | |
} | |
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< | |
make_signed_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 make_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::make_number<Number>(std::declval<Value>())); | |
} | |
namespace cnl { | |
template<class T, class Enable = void> | |
struct is_composite : std::false_type { | |
static_assert(!std::is_reference<T>::value, "T is a reference"); | |
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 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 = decltype(to_rep(std::declval<Number>())); | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<class T, class Enable = void> | |
struct wants_generic_ops : std::false_type { | |
}; | |
struct binary_op { | |
using is_not_comparison = void; | |
}; | |
struct comparison_op { | |
using is_comparison = void; | |
}; | |
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 add_op : binary_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 : binary_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 : binary_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 : binary_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 : binary_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 : binary_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 : binary_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 : binary_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 : binary_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 : binary_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 : comparison_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 : comparison_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 : comparison_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 : comparison_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 : comparison_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 : comparison_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> {}; | |
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 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)); | |
} | |
}; | |
} | |
namespace _operators_impl { | |
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::_operators_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::_operators_impl::enable_unary_t< Operand, cnl::_impl::minus_op>, Operand>()(operand)) { return cnl::_impl::unary_operator<cnl::_impl::minus_op, Operand>()(operand); } | |
template<class LhsOperand, class RhsOperand> constexpr auto operator + (LhsOperand const& lhs, RhsOperand const& rhs) -> decltype(cnl::_impl::binary_operator<cnl::_operators_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::_operators_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::_operators_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::_operators_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::_operators_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::_operators_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::_operators_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::_operators_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::_operators_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::_operators_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::binary_operator<cnl::_operators_impl::enable_binary_t< LhsOperand, RhsOperand, cnl::_impl::equal_op>, LhsOperand, RhsOperand>()(lhs, rhs)) { return cnl::_impl::binary_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::binary_operator<cnl::_operators_impl::enable_binary_t< LhsOperand, RhsOperand, cnl::_impl::not_equal_op>, LhsOperand, RhsOperand>()(lhs, rhs)) { return cnl::_impl::binary_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::binary_operator<cnl::_operators_impl::enable_binary_t< LhsOperand, RhsOperand, cnl::_impl::less_than_op>, LhsOperand, RhsOperand>()(lhs, rhs)) { return cnl::_impl::binary_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::binary_operator<cnl::_operators_impl::enable_binary_t< LhsOperand, RhsOperand, cnl::_impl::greater_than_op>, LhsOperand, RhsOperand>()(lhs, rhs)) { return cnl::_impl::binary_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::binary_operator<cnl::_operators_impl::enable_binary_t< LhsOperand, RhsOperand, cnl::_impl::less_than_or_equal_op>, LhsOperand, RhsOperand>()(lhs, rhs)) { return cnl::_impl::binary_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::binary_operator<cnl::_operators_impl::enable_binary_t< LhsOperand, RhsOperand, cnl::_impl::greater_than_or_equal_op>, LhsOperand, RhsOperand>()(lhs, rhs)) { return cnl::_impl::binary_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::_operators_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::_operators_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::_operators_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::_operators_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::_operators_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::_operators_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::_operators_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::_operators_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::_operators_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::_operators_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<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 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, class Rep = _impl::remove_cvref_t<_impl::to_rep_t<Wrapper>>> | |
struct depth { | |
static constexpr auto value = depth<Rep>::value + 1; | |
}; | |
template<class T> | |
struct depth<T, T> : 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()(make_number<Rhs>(lhs), rhs)) { | |
return Operator()(make_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, make_number<Lhs>(rhs))) | |
{ | |
return Operator()(lhs, make_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 { | |
}; | |
namespace _impl { | |
template<class Number> | |
struct get_rep; | |
template<class Number> | |
using get_rep_t = typename get_rep<Number>::type; | |
template<class Number, class NewRep, class Enable = void> | |
struct set_rep; | |
template<class Number, class NewRep> | |
using set_rep_t = typename set_rep<Number, NewRep>::type; | |
} | |
template<class Number> | |
struct make_signed<Number, _impl::enable_if_t<_impl::is_derived_from_number_base<Number>::value>> { | |
using type = _impl::set_rep_t<Number, make_signed_t<_impl::get_rep_t<Number>>>; | |
}; | |
template<class Number> | |
struct make_unsigned<Number, _impl::enable_if_t<_impl::is_derived_from_number_base<Number>::value>> { | |
using type = _impl::set_rep_t<Number, make_unsigned_t<_impl::get_rep_t<Number>>>; | |
}; | |
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(from_rep<Derived>{}(_impl::scale<Digits, Radix>(_impl::to_rep(s)))) | |
{ | |
return 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 from_rep<_value_type>{}(_rep_numeric_limits::min()); | |
} | |
static constexpr _value_type max() noexcept | |
{ | |
return from_rep<_value_type>{}(_rep_numeric_limits::max()); | |
} | |
static constexpr _value_type lowest() noexcept | |
{ | |
return from_rep<_value_type>{}(_rep_numeric_limits::lowest()); | |
} | |
static constexpr _value_type epsilon() noexcept | |
{ | |
return 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 { | |
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<int Digits, class Narrowest> | |
struct get_rep<elastic_integer<Digits, Narrowest>> { | |
using type = Narrowest; | |
}; | |
template<int Digits, class OldNarrowest, class NewNarrowest> | |
struct set_rep<elastic_integer<Digits, OldNarrowest>, NewNarrowest> { | |
using type = elastic_integer<Digits, NewNarrowest>; | |
}; | |
template<class T, bool IsSigned = true> | |
struct set_signedness; | |
template<class T> | |
struct set_signedness<T, true> : ::cnl::make_signed<T> { | |
}; | |
template<class T> | |
struct set_signedness<T, false> : ::cnl::make_unsigned<T> { | |
}; | |
template<class T, bool IsSigned> | |
using set_signedness_t = typename set_signedness<T, IsSigned>::type; | |
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, class Narrowest> | |
struct from_rep<elastic_integer<Digits, Narrowest>> { | |
template<typename Rep> | |
constexpr auto operator()(Rep const& r) const | |
-> elastic_integer<Digits, cnl::_impl::set_signedness_t<Narrowest, cnl::is_signed<Rep>::value>> | |
{ | |
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, | |
cnl::_impl::set_signedness_t<Narrowest, cnl::is_signed<Value>::value>>, | |
Value> { | |
}; | |
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 ScalarDigits, class ScalarNarrowest> | |
struct fixed_width_scale<ScalePower, 2, 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 _impl::to_rep(s) * (result_rep{1} << ScalePower); | |
} | |
}; | |
template<int ShiftDigits, int ScalarDigits, class ScalarNarrowest> | |
struct scale<ShiftDigits, 2, 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::to_rep(s) * (result_rep{1} << ShiftDigits); | |
} | |
}; | |
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> Dummy = 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> Dummy = 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> Dummy = 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 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> Dummy = 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 binary_operator<Operator, | |
elastic_integer<LhsDigits, LhsNarrowest>, elastic_integer<RhsDigits, RhsNarrowest>, | |
typename Operator::is_comparison> { | |
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 binary_operator<Operator, elastic_integer<Digits, Narrowest>, elastic_integer<Digits, Narrowest>, | |
typename Operator::is_comparison> { | |
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::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>, | |
typename Operator::is_not_comparison> { | |
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(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 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 (from_rep<elastic_integer<LhsDigits - static_cast<int>(RhsValue), LhsNarrowest>>{}(_impl::to_rep(lhs) >> RhsValue)) { | |
return 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(from_rep<elastic_integer<RhsDigits, typename make_signed<RhsNarrowest>::type>>{}(-_impl::to_rep(static_cast<elastic_integer<RhsDigits, typename make_signed<RhsNarrowest>::type>>(rhs)))) | |
{ | |
using result_type = elastic_integer<RhsDigits, typename make_signed<RhsNarrowest>::type>; | |
return 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(from_rep<elastic_integer<RhsDigits, RhsNarrowest>>{}( | |
+_impl::to_rep(static_cast<elastic_integer<RhsDigits, RhsNarrowest>>(rhs)))) | |
{ | |
return 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 { | |
namespace _elastic_integer_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; | |
} | |
}; | |
} | |
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 from_rep<_value_type>{}(1); | |
} | |
static constexpr _value_type max() noexcept | |
{ | |
return from_rep<_value_type>{}(_rep_max()); | |
} | |
static constexpr _value_type lowest() noexcept | |
{ | |
return _elastic_integer_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>> { | |
}; | |
} | |
namespace cnl { | |
namespace _impl { | |
template<class Result, class F, class ... Args, | |
_impl::enable_if_t<!_impl::are_composite<Args ...>::value, int> dummy = 0> | |
constexpr Result for_rep(F f, Args &&...args) { | |
return f(std::forward<Args>(args)...); | |
} | |
template<class Result, class F, class ... Args, | |
_impl::enable_if_t<_impl::are_composite<Args ...>::value, int> dummy = 0> | |
constexpr Result for_rep(F f, Args &&...args) { | |
return for_rep<Result>(f, _impl::to_rep(std::forward<Args>(args))...); | |
} | |
} | |
} | |
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 = make_unsigned_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::for_rep<int>(_impl::used_digits_signed<is_signed<Integer>::value>(), value, radix); | |
} | |
template<class Integer> | |
constexpr int leading_bits(Integer const& value) | |
{ | |
return digits<Integer>::value-cnl::used_digits(value); | |
} | |
} | |
namespace cnl { | |
template<typename Rep = int, int Exponent = 0, int Radix = cnl::numeric_limits<Rep>::radix> | |
class fixed_point; | |
template<typename Numerator, typename Denominator> | |
struct fractional; | |
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 fp { | |
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> | |
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"); | |
public: | |
using rep = Rep; | |
using _base = _impl::number_base<fixed_point<Rep, Exponent, Radix>, 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::make_number<Rep>(cnl::_impl::to_rep(rhs))))) | |
{ | |
} | |
template< ::cnl::intmax Value> | |
constexpr fixed_point(constant<Value> rhs) | |
: fixed_point(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::make_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(fractional<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=(fractional<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<class, class> | |
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 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 { | |
namespace _impl { | |
template<typename Rep, int Exponent, int Radix> | |
struct get_rep<fixed_point<Rep, Exponent, Radix>> { | |
using type = Rep; | |
}; | |
template <class OldRep, int Exponent, class NewRep> | |
struct set_rep<fixed_point<OldRep, Exponent>, NewRep> { | |
using type = fixed_point<NewRep, Exponent>; | |
}; | |
} | |
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 ArchetypeRep, int Exponent, int Radix> | |
struct from_rep<fixed_point<ArchetypeRep, Exponent, Radix>> { | |
template<typename Rep> | |
constexpr auto operator()(Rep const& r) const | |
-> fixed_point<Rep, Exponent, Radix> | |
{ | |
return fixed_point<Rep, Exponent, Radix>(r, 0); | |
} | |
}; | |
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>, fractional<Numerator, Denominator>> { | |
constexpr auto operator()(fractional<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::make_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 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, typename Denominator = int> | |
struct fractional { | |
using numerator_type = Numerator; | |
using denominator_type = Denominator; | |
explicit constexpr fractional(Numerator const& n, Denominator const& d) | |
: numerator{n}, denominator{d} {} | |
explicit constexpr fractional(Numerator const& n) | |
: numerator{n}, denominator{1} {} | |
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; | |
}; | |
template<typename Numerator, typename Denominator> | |
constexpr fractional<Numerator, Denominator> make_fractional(Numerator const& n, Denominator const& d) | |
{ | |
return fractional<Numerator, Denominator>{n, d}; | |
} | |
template<typename Numerator> | |
constexpr fractional<Numerator, Numerator> make_fractional(Numerator const& n) | |
{ | |
return fractional<Numerator, Numerator>{n, 1}; | |
} | |
template<typename LhsNumerator, typename LhsDenominator, typename RhsNumerator, typename RhsDenominator> | |
constexpr auto operator+( | |
fractional<LhsNumerator, LhsDenominator> const& lhs, | |
fractional<RhsNumerator, RhsDenominator> const& rhs) | |
-> decltype(make_fractional( | |
lhs.numerator*rhs.denominator+rhs.numerator*lhs.denominator, lhs.denominator*rhs.denominator)) | |
{ | |
return make_fractional( | |
lhs.numerator*rhs.denominator+rhs.numerator*lhs.denominator, lhs.denominator*rhs.denominator); | |
} | |
template<typename LhsNumerator, typename LhsDenominator, typename RhsNumerator, typename RhsDenominator> | |
constexpr auto operator-( | |
fractional<LhsNumerator, LhsDenominator> const& lhs, | |
fractional<RhsNumerator, RhsDenominator> const& rhs) | |
-> decltype(make_fractional( | |
lhs.numerator*rhs.denominator-rhs.numerator*lhs.denominator, lhs.denominator*rhs.denominator)) | |
{ | |
return make_fractional( | |
lhs.numerator*rhs.denominator-rhs.numerator*lhs.denominator, lhs.denominator*rhs.denominator); | |
} | |
template<typename LhsNumerator, typename LhsDenominator, typename RhsNumerator, typename RhsDenominator> | |
constexpr auto operator*( | |
fractional<LhsNumerator, LhsDenominator> const& lhs, | |
fractional<RhsNumerator, RhsDenominator> const& rhs) | |
-> decltype(make_fractional(lhs.numerator*rhs.numerator, lhs.denominator*rhs.denominator)) | |
{ | |
return make_fractional(lhs.numerator*rhs.numerator, lhs.denominator*rhs.denominator); | |
} | |
template<typename LhsNumerator, typename LhsDenominator, typename RhsNumerator, typename RhsDenominator> | |
constexpr auto operator/( | |
fractional<LhsNumerator, LhsDenominator> const& lhs, | |
fractional<RhsNumerator, RhsDenominator> const& rhs) | |
-> decltype(make_fractional(lhs.numerator*rhs.denominator, lhs.denominator*rhs.numerator)) | |
{ | |
return make_fractional(lhs.numerator*rhs.denominator, lhs.denominator*rhs.numerator); | |
} | |
namespace _fractional_impl { | |
template<typename Numerator, typename Denominator> | |
constexpr auto one(fractional<Numerator, Denominator> const& f) | |
-> decltype(f.numerator==f.denominator) | |
{ | |
return f.numerator==f.denominator; | |
} | |
} | |
template<typename LhsNumerator, typename LhsDenominator, typename RhsNumerator, typename RhsDenominator> | |
constexpr auto operator==( | |
fractional<LhsNumerator, LhsDenominator> const& lhs, | |
fractional<RhsNumerator, RhsDenominator> const& rhs) | |
-> decltype(_fractional_impl::one(lhs/rhs)) | |
{ | |
return _fractional_impl::one(lhs/rhs); | |
} | |
template<typename LhsNumerator, typename LhsDenominator, typename RhsNumerator, typename RhsDenominator> | |
constexpr auto operator!=( | |
fractional<LhsNumerator, LhsDenominator> const& lhs, | |
fractional<RhsNumerator, RhsDenominator> const& rhs) | |
-> decltype(!(lhs==rhs)) | |
{ | |
return !(lhs==rhs); | |
} | |
} | |
namespace cnl { | |
template<typename Rep, int Exponent, int Radix> | |
template<typename Numerator, typename Denominator> | |
constexpr fixed_point<Rep, Exponent, Radix>::fixed_point(fractional<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=(fractional<Numerator, Denominator> const& f) | |
{ | |
return operator=(quotient<fixed_point>(f.numerator, f.denominator)); | |
} | |
template<typename Numerator, typename Denominator> | |
fixed_point(fractional<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, 1 - 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 { | |
namespace _fixed_point_operators_impl { | |
template<class Lhs, class Rhs> | |
constexpr bool is_heterogeneous() { | |
return (!std::is_same<Lhs, Rhs>::value) && | |
(_impl::is_fixed_point<Lhs>::value || _impl::is_fixed_point<Rhs>::value); | |
} | |
} | |
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 binary_operator<Operator, | |
fixed_point<LhsRep, Exponent, Radix>, | |
fixed_point<RhsRep, Exponent, Radix>, | |
typename Operator::is_comparison> { | |
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 binary_operator<Operator, | |
fixed_point<LhsRep, LhsExponent, Radix>, | |
fixed_point<RhsRep, RhsExponent, Radix>, | |
enable_if_t<LhsExponent<RhsExponent, typename Operator::is_comparison>> { | |
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 = binary_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 binary_operator<Operator, | |
fixed_point<LhsRep, LhsExponent, Radix>, | |
fixed_point<RhsRep, RhsExponent, Radix>, | |
enable_if_t<RhsExponent<LhsExponent, typename Operator::is_comparison>> { | |
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 = binary_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, typename Operator::is_not_comparison>> { | |
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, typename Operator::is_not_comparison>> { | |
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(from_rep<fixed_point<decltype(_impl::to_rep(lhs) << int(rhs)), LhsExponent, LhsRadix>>{}(_impl::to_rep(lhs) << int(rhs))) | |
{ | |
return 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(from_rep<fixed_point<decltype(_impl::to_rep(lhs) >> int(rhs)), LhsExponent, LhsRadix>>{}(_impl::to_rep(lhs) >> int(rhs))) | |
{ | |
return 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 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 from_rep<fixed_point<LhsRep, LhsExponent-static_cast<int>(RhsValue), LhsRadix>>{}(_impl::to_rep(lhs)); | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<class Result> | |
[[noreturn]] constexpr Result terminate(char const* message) noexcept | |
{ | |
std::fprintf( | |
stderr | |
, "%s\n", message); | |
std::terminate(); | |
} | |
} | |
} | |
namespace cnl { | |
template<typename Rep, int Exponent, int Radix> | |
constexpr auto abs(fixed_point<Rep, Exponent, Radix> const& x) noexcept | |
-> decltype(-x) | |
{ | |
return (x>=Rep{0}) ? 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 fixed_point <Rep, Exponent, Radix> | |
sqrt(fixed_point<Rep, Exponent, Radix> const& x) | |
{ | |
using type = fixed_point<Rep, Exponent, Radix>; | |
return _impl::to_rep(x)<0 | |
? _impl::terminate<type>("negative value passed to cnl::sqrt") | |
: type{from_rep<type>{}(_impl::for_rep<Rep>(_impl::sqrt_solve1<Exponent>(), 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(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 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 { | |
namespace fp { | |
namespace extras { | |
template<typename Rep, int Exponent, int Radix, _impl::fp::float_of_same_size<Rep>(* F)( | |
_impl::fp::float_of_same_size<Rep>)> | |
constexpr fixed_point <Rep, Exponent, Radix> | |
crib(fixed_point<Rep, Exponent, Radix> const& x) noexcept | |
{ | |
using floating_point = _impl::fp::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::fp::extras::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::fp::extras::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::fp::extras::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::fp::extras::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 << static_cast<long double>(fp); | |
} | |
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 from_rep<_value_type>{}(_rep{1}); | |
} | |
static constexpr _value_type max() noexcept | |
{ | |
return from_rep<_value_type>{}(_rep_numeric_limits::max()); | |
} | |
static constexpr _value_type lowest() noexcept | |
{ | |
return from_rep<_value_type>{}(_rep_numeric_limits::lowest()); | |
} | |
static constexpr bool is_integer = false; | |
static constexpr _value_type epsilon() noexcept | |
{ | |
return from_rep<_value_type>{}(_rep{1}); | |
} | |
static constexpr _value_type round_error() noexcept | |
{ | |
return from_rep<_value_type>{}(_rep{0}); | |
} | |
static constexpr _value_type infinity() noexcept | |
{ | |
return from_rep<_value_type>{}(_rep{0}); | |
} | |
static constexpr _value_type quiet_NaN() noexcept | |
{ | |
return from_rep<_value_type>{}(_rep{0}); | |
} | |
static constexpr _value_type signaling_NaN() noexcept | |
{ | |
return from_rep<_value_type>{}(_rep{0}); | |
} | |
static constexpr _value_type denorm_min() noexcept | |
{ | |
return 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 { | |
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 elastic_number< | |
_impl::max(digits<constant<Value>>::value-trailing_bits(Value), 1), | |
trailing_bits(Value), | |
Narrowest> | |
make_elastic_number(constant<Value>) | |
{ | |
return Value; | |
} | |
template<typename Narrowest = void, typename Integral = int> | |
constexpr elastic_number< | |
numeric_limits<Integral>::digits, | |
0, | |
typename std::conditional< | |
std::is_same<void, Narrowest>::value, | |
typename std::conditional<is_signed<Integral>::value, signed, unsigned>::type, | |
Narrowest>::type> | |
make_elastic_number(Integral const& value) | |
{ | |
return {value}; | |
} | |
template<typename Narrowest = void, typename Rep = int, int Exponent = 0, int Radix = 2> | |
constexpr elastic_number< | |
numeric_limits<Rep>::digits, | |
Exponent, | |
typename std::conditional< | |
std::is_same<void, Narrowest>::value, | |
typename std::conditional<is_signed<Rep>::value, signed, unsigned>::type, | |
Narrowest>::type> | |
make_elastic_number(fixed_point<Rep, Exponent, Radix> const& value) | |
{ | |
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 { | |
using _impl::is_integral; | |
} | |
template<typename Type> | |
struct CNL_ERROR___cannot_use { | |
struct as_a_tag; | |
}; | |
namespace cnl { | |
namespace _impl { | |
struct native_tag {}; | |
template<class Tag, typename Result> | |
struct convert : public CNL_ERROR___cannot_use<Tag>::as_a_tag { | |
}; | |
template<typename Result> | |
struct convert<native_tag, Result> { | |
template<typename Input> | |
constexpr Result operator()(Input const& rhs) const | |
{ | |
return static_cast<Result>(rhs); | |
} | |
}; | |
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::make_unsigned_t<result_type>>(lhs)<<rhs); | |
} | |
}; | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<class T> | |
struct positive_digits : public std::integral_constant<int, numeric_limits<T>::digits> { | |
}; | |
template<class T> | |
struct negative_digits | |
: public std::integral_constant<int, cnl::is_signed<T>::value ? digits<T>::value : 0> { | |
}; | |
template<typename Destination, typename Source> | |
struct convert_test { | |
static constexpr bool positive(Source const& rhs) | |
{ | |
return positive_digits<Destination>::value | |
<positive_digits<Source>::value | |
&& rhs>static_cast<Source>(numeric_limits<Destination>::max()); | |
} | |
static constexpr bool negative(Source const& rhs) | |
{ | |
return negative_digits<Destination>::value | |
<negative_digits<Source>::value | |
&& rhs<static_cast<Source>(numeric_limits<Destination>::lowest()); | |
} | |
}; | |
template<class Operator, typename ... Operands> | |
class operator_overflow_traits { | |
using result = op_result<Operator, Operands...>; | |
using numeric_limits = cnl::numeric_limits<result>; | |
public: | |
static constexpr int positive_digits = _impl::positive_digits<result>::value; | |
static constexpr int negative_digits = _impl::negative_digits<result>::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 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() + 1) == numeric_limits<Operand>::max()> { | |
}; | |
template<class Operator, typename ... Operands> | |
struct overflow_test_base { | |
static constexpr bool positive(Operands const &...) | |
{ | |
return false; | |
} | |
static constexpr bool negative(Operands const &...) | |
{ | |
return false; | |
} | |
}; | |
template<class Operator, typename ... Operands> | |
struct overflow_test : overflow_test_base<Operator, Operands...> { | |
}; | |
template<typename Rhs> | |
struct overflow_test<minus_op, Rhs> : overflow_test_base<minus_op, Rhs> { | |
using traits = operator_overflow_traits<minus_op, Rhs>; | |
static constexpr bool positive(Rhs const &rhs) { | |
return has_most_negative_number<Rhs>::value | |
? (-traits::max()) > rhs | |
: 0; | |
} | |
static constexpr bool negative(Rhs const &rhs) { | |
return !is_signed<Rhs>::value && rhs; | |
} | |
}; | |
template<typename Lhs, typename Rhs> | |
struct overflow_test<add_op, Lhs, Rhs> : overflow_test_base<add_op, Lhs, Rhs> { | |
using traits = operator_overflow_traits<add_op, Lhs, Rhs>; | |
static constexpr bool positive(Lhs const& lhs, Rhs const& rhs) | |
{ | |
return (max(positive_digits<Lhs>::value, positive_digits<Rhs>::value)+1 | |
>traits::positive_digits) | |
&& lhs>Lhs{0} | |
&& rhs>Rhs{0} | |
&& lhs>traits::max()-rhs; | |
} | |
static constexpr bool negative(Lhs const& lhs, Rhs const& rhs) | |
{ | |
return (max(positive_digits<Lhs>::value, positive_digits<Rhs>::value)+1 | |
>traits::positive_digits) | |
&& lhs<Lhs{0} | |
&& rhs<Rhs{0} | |
&& lhs<traits::lowest()-rhs; | |
} | |
}; | |
template<typename Lhs, typename Rhs> | |
struct overflow_test<subtract_op, Lhs, Rhs> : overflow_test_base<subtract_op, Lhs, Rhs> { | |
using traits = operator_overflow_traits<subtract_op, Lhs, Rhs>; | |
static constexpr bool positive(Lhs const& lhs, Rhs const& rhs) | |
{ | |
return (max(positive_digits<Lhs>::value, positive_digits<Rhs>::value)+1 | |
>traits::positive_digits) | |
&& lhs>Lhs{0} | |
&& rhs<Rhs{0} | |
&& lhs>traits::max()+rhs; | |
} | |
static constexpr bool negative(Lhs const& lhs, Rhs const& rhs) | |
{ | |
return (max(positive_digits<Lhs>::value, positive_digits<Rhs>::value)+1 | |
>traits::positive_digits) | |
&& (rhs>=0) | |
&& lhs<traits::lowest()+rhs; | |
} | |
}; | |
template<typename Lhs, typename Rhs> | |
struct overflow_test<multiply_op, Lhs, Rhs> : overflow_test_base<multiply_op, Lhs, Rhs> { | |
using traits = operator_overflow_traits<multiply_op, Lhs, Rhs>; | |
static constexpr bool positive(Lhs const& lhs, Rhs const& rhs) | |
{ | |
return (positive_digits<Lhs>::value+positive_digits<Rhs>::value>traits::positive_digits) | |
&& ((lhs>Lhs{0}) | |
? (rhs>Rhs{0}) && (traits::max()/rhs)<lhs | |
: (rhs<Rhs{0}) && (traits::max()/rhs)>lhs); | |
} | |
static constexpr bool negative(Lhs const& lhs, Rhs const& rhs) | |
{ | |
return (positive_digits<Lhs>::value+positive_digits<Rhs>::value>traits::positive_digits) | |
&& ((lhs<Lhs{0}) | |
? (rhs>Rhs{0}) && (traits::lowest()/rhs)>lhs | |
: (rhs<Rhs{0}) && (traits::lowest()/rhs)<lhs); | |
} | |
}; | |
template<typename Lhs, typename Rhs> | |
struct overflow_test<divide_op, Lhs, Rhs> | |
: overflow_test_base<divide_op, Lhs, Rhs> { | |
using traits = operator_overflow_traits<divide_op, Lhs, Rhs>; | |
static constexpr bool positive(Lhs const &lhs, Rhs const &rhs) { | |
return (has_most_negative_number<Lhs>::value && has_most_negative_number<Rhs>::value) | |
? rhs == -1 && lhs == traits::lowest() | |
: false; | |
} | |
}; | |
template<typename Lhs, typename Rhs, bool IsSigned> | |
struct shift_left_overflow_test; | |
template<typename Lhs, typename Rhs> | |
struct shift_left_overflow_test<Lhs, Rhs, false> | |
: overflow_test_base<Lhs, Rhs> { | |
using traits = operator_overflow_traits<shift_left_op, Lhs, Rhs>; | |
static constexpr bool negative(Lhs const&, Rhs const&) | |
{ | |
return false; | |
} | |
}; | |
template<typename Lhs, typename Rhs> | |
struct shift_left_overflow_test<Lhs, Rhs, true> | |
: overflow_test_base<Lhs, Rhs> { | |
using traits = operator_overflow_traits<shift_left_op, Lhs, Rhs>; | |
static constexpr bool negative(Lhs const& lhs, Rhs const& rhs) | |
{ | |
return lhs<0 | |
? rhs>0 | |
? rhs<traits::positive_digits | |
? (lhs >> (traits::positive_digits-rhs))!=-1 | |
: true | |
: false | |
: false; | |
} | |
}; | |
template<typename Lhs, typename Rhs> | |
struct overflow_test<shift_left_op, Lhs, Rhs> | |
: shift_left_overflow_test<Lhs, Rhs, is_signed<Lhs>::value> { | |
using traits = operator_overflow_traits<shift_left_op, Lhs, Rhs>; | |
static constexpr bool positive(Lhs const& lhs, Rhs const& rhs) | |
{ | |
return lhs>0 | |
? rhs>0 | |
? rhs<traits::positive_digits | |
? (lhs >> (traits::positive_digits-rhs))!=0 | |
: true | |
: false | |
: false; | |
} | |
}; | |
template<typename Result, class OverflowTag> | |
struct positive_overflow_result; | |
template<typename Result, class OverflowTag> | |
struct negative_overflow_result; | |
template<class OverflowTag, typename Result> | |
struct overflow_convert { | |
template<typename Input> | |
constexpr Result operator()(Input const& rhs) const | |
{ | |
using test = convert_test<Result, Input>; | |
return test::positive(rhs) | |
? positive_overflow_result<Result, OverflowTag>{}() | |
: test::negative(rhs) | |
? negative_overflow_result<Result, OverflowTag>{}() | |
: static_cast<Result>(rhs); | |
} | |
}; | |
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 | |
-> op_result<Operator, Lhs, Rhs> | |
{ | |
using overflow_test = overflow_test<Operator, Lhs, Rhs>; | |
using result_type = op_result<Operator, Lhs, Rhs>; | |
return overflow_test::positive(lhs, rhs) | |
? positive_overflow_result<result_type, OverflowTag>{}() | |
: overflow_test::negative(lhs, rhs) | |
? negative_overflow_result<result_type, OverflowTag>{}() | |
: tagged_binary_operator<native_tag, Operator>{}(lhs, rhs); | |
} | |
}; | |
} | |
} | |
namespace cnl { | |
struct native_overflow_tag : _impl::native_tag { | |
}; | |
static constexpr native_overflow_tag native_overflow{}; | |
namespace _impl { | |
template<typename Result> | |
struct convert<native_overflow_tag, Result> | |
: convert<_impl::native_tag, Result> { | |
}; | |
} | |
namespace _impl { | |
template<class Operator> | |
struct tagged_binary_operator<native_overflow_tag, Operator> | |
: tagged_binary_operator<_impl::native_tag, Operator> { | |
}; | |
} | |
} | |
namespace cnl { | |
static constexpr struct saturated_overflow_tag { | |
} saturated_overflow{}; | |
namespace _impl { | |
template<typename Result> | |
struct positive_overflow_result<Result, saturated_overflow_tag> { | |
constexpr Result operator()() const | |
{ | |
return numeric_limits<Result>::max(); | |
} | |
}; | |
template<typename Result> | |
struct negative_overflow_result<Result, saturated_overflow_tag> { | |
constexpr Result operator()() const | |
{ | |
return numeric_limits<Result>::lowest(); | |
} | |
}; | |
template<typename Result> | |
struct convert<saturated_overflow_tag, Result> : overflow_convert<saturated_overflow_tag, Result> { | |
}; | |
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 { | |
static constexpr struct throwing_overflow_tag { | |
} throwing_overflow{}; | |
namespace _impl { | |
template<typename Result> | |
struct positive_overflow_result<Result, throwing_overflow_tag> { | |
constexpr Result operator()() const | |
{ | |
return throw_exception<Result, std::overflow_error>("positive 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 Result> | |
struct convert<throwing_overflow_tag, Result> : overflow_convert<throwing_overflow_tag, Result> { | |
}; | |
template<class Operator> | |
struct tagged_binary_operator<throwing_overflow_tag, Operator> | |
: tagged_binary_overflow_operator<throwing_overflow_tag, Operator> { | |
}; | |
} | |
} | |
namespace cnl { | |
static constexpr struct trapping_overflow_tag { | |
} trapping_overflow{}; | |
namespace _impl { | |
template<typename Result> | |
struct positive_overflow_result<Result, trapping_overflow_tag> { | |
constexpr Result operator()() const | |
{ | |
return terminate<Result>("positive overflow"); | |
} | |
}; | |
template<typename Result> | |
struct negative_overflow_result<Result, trapping_overflow_tag> { | |
constexpr Result operator()() const | |
{ | |
return terminate<Result>("negative overflow"); | |
} | |
}; | |
template<typename Result> | |
struct convert<trapping_overflow_tag, Result> : overflow_convert<trapping_overflow_tag, Result> { | |
}; | |
template<class Operator> | |
struct tagged_binary_operator<trapping_overflow_tag, Operator> | |
: tagged_binary_overflow_operator<trapping_overflow_tag, Operator> { | |
}; | |
} | |
} | |
namespace cnl { | |
namespace _impl { | |
template<class Result> | |
constexpr Result unreachable(char const* ) noexcept | |
{ | |
__builtin_unreachable(); | |
} | |
} | |
} | |
namespace cnl { | |
static constexpr struct undefined_overflow_tag { | |
} undefined_overflow{}; | |
namespace _impl { | |
template<typename Result> | |
struct positive_overflow_result<Result, undefined_overflow_tag> { | |
constexpr Result operator()() const | |
{ | |
return unreachable<Result>("positive overflow"); | |
} | |
}; | |
template<typename Result> | |
struct negative_overflow_result<Result, undefined_overflow_tag> { | |
constexpr Result operator()() const | |
{ | |
return unreachable<Result>("negative overflow"); | |
} | |
}; | |
template<typename Result> | |
struct convert<undefined_overflow_tag, Result> : overflow_convert<undefined_overflow_tag, Result> { | |
}; | |
template<class Operator> | |
struct tagged_binary_operator<undefined_overflow_tag, Operator> | |
: tagged_binary_overflow_operator<undefined_overflow_tag, Operator> { | |
}; | |
} | |
} | |
namespace cnl { | |
using _impl::convert; | |
template<class OverflowTag, class Lhs, class Rhs> | |
constexpr auto add(OverflowTag, Lhs const& lhs, Rhs const& rhs) | |
-> decltype(lhs+rhs) | |
{ | |
return _impl::tagged_binary_operator<OverflowTag, _impl::add_op>{}(lhs, rhs); | |
} | |
template<class OverflowTag, class Lhs, class Rhs> | |
constexpr auto subtract(OverflowTag, Lhs const& lhs, Rhs const& rhs) | |
-> decltype(lhs-rhs) | |
{ | |
return _impl::tagged_binary_operator<OverflowTag, _impl::subtract_op>{}(lhs, rhs); | |
} | |
template<class OverflowTag, class Lhs, class Rhs> | |
constexpr auto multiply(OverflowTag, Lhs const& lhs, Rhs const& rhs) | |
-> decltype(lhs*rhs) | |
{ | |
return _impl::tagged_binary_operator<OverflowTag, _impl::multiply_op>{}(lhs, rhs); | |
} | |
template<class OverflowTag, class Lhs, class Rhs> | |
constexpr auto shift_left(OverflowTag, Lhs const& lhs, Rhs const& rhs) | |
-> decltype(lhs<<rhs) | |
{ | |
return _impl::for_rep<decltype(lhs << rhs)>( | |
_impl::tagged_binary_operator<OverflowTag, _impl::shift_left_op>(), lhs, rhs); | |
} | |
} | |
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() = delete; | |
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 static_cast<T>(_impl::to_rep(*this)); | |
} | |
}; | |
namespace _impl { | |
template<class Rep, class OverflowTag> | |
struct get_rep<overflow_integer<Rep, OverflowTag>> { | |
using type = Rep; | |
}; | |
template<class OldRep, class OverflowTag, class NewRep> | |
struct set_rep<overflow_integer<OldRep, OverflowTag>, NewRep> { | |
using type = overflow_integer<NewRep, OverflowTag>; | |
}; | |
} | |
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<class ArchetypeRep, class OverflowTag> | |
struct from_rep<overflow_integer<ArchetypeRep, OverflowTag>> { | |
template<typename 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<int Digits, class Rep, class OverflowTag> | |
struct scale<Digits, 2, overflow_integer<Rep, OverflowTag>, | |
_impl::enable_if_t<(Digits>=0)>> { | |
using _value_type = overflow_integer<Rep, OverflowTag>; | |
constexpr auto operator()(_value_type const& s) const | |
-> decltype(from_rep<_value_type>{}(shift_left(OverflowTag{}, _impl::to_rep(s), constant<Digits>{}))) | |
{ | |
return from_rep<_value_type>{}(shift_left(OverflowTag{}, _impl::to_rep(s), constant<Digits>{})); | |
} | |
}; | |
template<int Digits, int Radix, class Rep, class OverflowTag> | |
struct scale<Digits, Radix, overflow_integer<Rep, OverflowTag>, | |
_impl::enable_if_t<(Digits<0||Radix!=2)>> | |
: scale<Digits, Radix, _impl::number_base<overflow_integer<Rep, OverflowTag>, Rep>> { | |
}; | |
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 overflow_integer<decltype(Operator()(_impl::to_rep(operand))), 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>, | |
typename Operator::is_not_comparison> { | |
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 binary_operator<Operator, | |
overflow_integer<LhsRep, OverflowTag>, overflow_integer<RhsRep, OverflowTag>, | |
typename Operator::is_comparison> { | |
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 binary_operator<Operator, | |
overflow_integer<LhsRep, LhsTag>, overflow_integer<RhsRep, RhsTag>, | |
typename Operator::is_comparison> { | |
using common_tag = cnl::_impl::common_type_t<LhsTag, RhsTag>; | |
using operator_type = binary_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; | |
} | |
}; | |
} | |
} | |
namespace cnl { | |
template<class Rep, class OverflowTag> | |
struct numeric_limits<cnl::overflow_integer<Rep, OverflowTag>> | |
: numeric_limits<cnl::_impl::number_base<cnl::overflow_integer<Rep, OverflowTag>, Rep>> { | |
static constexpr bool is_integer = true; | |
}; | |
template<class Rep, class OverflowTag> | |
struct numeric_limits<cnl::overflow_integer<Rep, OverflowTag> const> | |
: numeric_limits<cnl::_impl::number_base<cnl::overflow_integer<Rep, OverflowTag>, Rep>> { | |
static constexpr bool is_integer = true; | |
}; | |
} | |
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 { | |
struct native_rounding_tag : public native_tag { | |
}; | |
struct nearest_rounding_tag { | |
}; | |
template<typename Result> | |
struct convert<native_rounding_tag, Result> : convert<native_tag, Result> { | |
}; | |
template<typename Result> | |
struct convert<nearest_rounding_tag, Result> { | |
template<typename Input> | |
constexpr Result operator()(Input const& from) const | |
{ | |
return static_cast<Result>(from+((from>=0) ? .5 : -.5)); | |
} | |
}; | |
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<> | |
struct tagged_binary_operator<nearest_rounding_tag, shift_right_op> { | |
template<class Lhs, class Rhs> | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const | |
-> decltype(lhs >> rhs) | |
{ | |
return (lhs + ((static_cast<from_value_t<Lhs, constant<1>>>(constant<1>{}) << rhs) >> constant<1>{})) | |
>> rhs; | |
} | |
}; | |
template<> | |
struct tagged_binary_operator<nearest_rounding_tag, shift_left_op> { | |
template<class Lhs, class Rhs> | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const | |
-> decltype(lhs << rhs) | |
{ | |
return lhs << rhs; | |
} | |
}; | |
template<class RoundingTag, class Lhs, class Rhs> | |
struct divide { | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const | |
-> decltype(lhs/rhs) | |
{ | |
return for_rep<decltype(lhs/rhs)>( | |
tagged_binary_operator<RoundingTag, divide_op>(), lhs, 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 { | |
using _impl::nearest_rounding_tag; | |
using _impl::native_rounding_tag; | |
using _impl::convert; | |
using _impl::divide; | |
using _impl::shift_right; | |
} | |
namespace cnl { | |
using _impl::native_rounding_tag; | |
using _impl::nearest_rounding_tag; | |
template<class Rep = int, class RoundingTag = _impl::nearest_rounding_tag> | |
class rounding_integer; | |
namespace _rounding_integer_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<class Rep, class RoundingTag> | |
class rounding_integer : public _impl::number_base<rounding_integer<Rep, RoundingTag>, Rep> { | |
static_assert(!_rounding_integer_impl::is_rounding_integer<Rep>::value, | |
"rounding_integer of rounding_integer is not a supported"); | |
using super = _impl::number_base<rounding_integer<Rep, RoundingTag>, Rep>; | |
public: | |
using rounding = RoundingTag; | |
using _base = _impl::number_base<rounding_integer<Rep, RoundingTag>, Rep>; | |
rounding_integer() = default; | |
template<class T, _impl::enable_if_t<numeric_limits<T>::is_integer, int> Dummy = 0> | |
constexpr rounding_integer(T const& v) | |
: super(static_cast<Rep>(v)) { } | |
template<class T, _impl::enable_if_t<!numeric_limits<T>::is_integer, int> Dummy = 0> | |
constexpr rounding_integer(T const& v) | |
: super(_impl::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<class ArchetypeRep, class RoundingTag> | |
struct from_rep<rounding_integer<ArchetypeRep, RoundingTag>> { | |
template<typename Rep> | |
constexpr auto operator()(Rep const& r) const | |
-> rounding_integer<Rep, RoundingTag> | |
{ | |
return r; | |
} | |
}; | |
namespace _impl { | |
template<class Rep, class RoundingTag> | |
struct get_rep<rounding_integer<Rep, RoundingTag>> { | |
using type = Rep; | |
}; | |
template<class OldRep, class RoundingTag, class NewRep> | |
struct set_rep<rounding_integer<OldRep, RoundingTag>, NewRep> { | |
using type = rounding_integer<NewRep, RoundingTag>; | |
}; | |
} | |
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(from_rep<rounding_integer<Rep, RoundingTag>>{}(scale<Digits, Radix, Rep>{}(_impl::to_rep(s)))) | |
{ | |
return 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(from_rep<rounding_integer<Rep, RoundingTag>>{}( | |
fixed_width_scale<Digits, 2, Rep>{}(_impl::to_rep(s)))) | |
{ | |
return 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>, | |
typename Operator::is_not_comparison> { | |
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 binary_operator<Operator, rounding_integer<LhsRep, RoundingTag>, rounding_integer<RhsRep, RoundingTag>, | |
typename Operator::is_comparison> { | |
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 binary_operator<Operator, | |
rounding_integer<LhsRep, LhsRoundingTag>, rounding_integer<RhsRep, RhsRoundingTag>, | |
typename Operator::is_comparison> { | |
constexpr auto operator()( | |
rounding_integer<LhsRep, LhsRoundingTag> const& lhs, | |
rounding_integer<RhsRep, RhsRoundingTag> const& rhs) const | |
-> decltype(binary_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 binary_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<cnl::rounding_integer<Rep, RoundingTag>> | |
: numeric_limits<cnl::_impl::number_base<cnl::rounding_integer<Rep, RoundingTag>, Rep>> { | |
static constexpr bool is_integer = true; | |
}; | |
template<class Rep, class RoundingTag> | |
struct numeric_limits<cnl::rounding_integer<Rep, RoundingTag> const> | |
: numeric_limits<cnl::_impl::number_base<cnl::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< | |
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, | |
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> | |
static_number< | |
numeric_limits<Input>::digits, 0, | |
RoundingTag, OverflowTag, | |
Narrowest> | |
constexpr make_static_number(Input const& input) | |
{ | |
return input; | |
} | |
template< | |
class RoundingTag = rounding_integer<>::rounding, | |
class OverflowTag = overflow_integer<>::overflow_tag, | |
class Narrowest = int, | |
class Input = int, | |
::cnl::intmax Value> | |
static_number< | |
_impl::used_digits(Value)-trailing_bits(Value), trailing_bits(Value), | |
RoundingTag, OverflowTag, | |
Narrowest> | |
constexpr make_static_number(constant<Value> const&) | |
{ | |
return constant<Value>{}; | |
} | |
} | |
#endif // CNL_COMPLETE_H |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment