diff --git a/boost/safe_numerics/CMakeLists.txt b/boost/safe_numerics/CMakeLists.txt new file mode 100644 index 0000000..cd94725 --- /dev/null +++ b/boost/safe_numerics/CMakeLists.txt @@ -0,0 +1,13 @@ +#################### +# add include headers to IDE + +file(GLOB include_files + RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" + "*.hpp" +) +add_custom_target(safe_numerics SOURCES ${include_files}) + +add_subdirectory("concept") + +# end headers in IDE +#################### diff --git a/boost/safe_numerics/automatic.hpp b/boost/safe_numerics/automatic.hpp new file mode 100644 index 0000000..df63bf8 --- /dev/null +++ b/boost/safe_numerics/automatic.hpp @@ -0,0 +1,479 @@ +#ifndef BOOST_NUMERIC_AUTOMATIC_HPP +#define BOOST_NUMERIC_AUTOMATIC_HPP + +// Copyright (c) 2012 Robert Ramey +// +// 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) + +// policy which creates expanded results types designed +// to avoid overflows. + +#include +#include // (u)intmax_t, +#include // conditional +#include + +#include "safe_common.hpp" +#include "checked_result.hpp" +#include "checked_default.hpp" +#include "checked_integer.hpp" +#include "checked_result_operations.hpp" +#include "interval.hpp" +#include "utility.hpp" + +namespace boost { +namespace safe_numerics { + +struct automatic { +private: + // the following returns the "true" type. After calculating the new max and min + // these return the minimum size type which can hold the expected result. + struct defer_stored_signed_lazily { + template + using type = utility::signed_stored_type; + }; + + struct defer_stored_unsigned_lazily { + template + using type = utility::unsigned_stored_type; + }; + + template + struct result_type { + using type = typename std::conditional< + std::numeric_limits::is_signed, + defer_stored_signed_lazily, + defer_stored_unsigned_lazily + >::type::template type; + }; + +public: + /////////////////////////////////////////////////////////////////////// + template + struct addition_result { + using temp_base_type = typename std::conditional< + // if both arguments are unsigned + ! std::numeric_limits::is_signed + && ! std::numeric_limits::is_signed, + // result is unsigned + std::uintmax_t, + // otherwise result is signed + std::intmax_t + >::type; + + using r_type = checked_result; + using r_interval_type = interval; + + constexpr static const r_interval_type t_interval{ + checked::cast(base_value(std::numeric_limits::min())), + checked::cast(base_value(std::numeric_limits::max())) + }; + + constexpr static const r_interval_type u_interval{ + checked::cast(base_value(std::numeric_limits::min())), + checked::cast(base_value(std::numeric_limits::max())) + }; + + constexpr static const r_interval_type r_interval = t_interval + u_interval; + + constexpr static auto rl = r_interval.l; + constexpr static auto ru = r_interval.u; + + using type = typename result_type< + temp_base_type, + rl.exception() + ? std::numeric_limits::min() + : static_cast(rl), + ru.exception() + ? std::numeric_limits::max() + : static_cast(ru) + >::type; + }; + + /////////////////////////////////////////////////////////////////////// + template + struct subtraction_result { + // result of subtraction are always signed. + using temp_base_type = intmax_t; + + using r_type = checked_result; + using r_interval_type = interval; + + constexpr static const r_interval_type t_interval{ + checked::cast(base_value(std::numeric_limits::min())), + checked::cast(base_value(std::numeric_limits::max())) + }; + + constexpr static const r_interval_type u_interval{ + checked::cast(base_value(std::numeric_limits::min())), + checked::cast(base_value(std::numeric_limits::max())) + }; + + constexpr static const r_interval_type r_interval = t_interval - u_interval; + + constexpr static auto rl = r_interval.l; + constexpr static auto ru = r_interval.u; + + using type = typename result_type< + temp_base_type, + rl.exception() + ? std::numeric_limits::min() + : static_cast(rl), + ru.exception() + ? std::numeric_limits::max() + : static_cast(ru) + >::type; + }; + + /////////////////////////////////////////////////////////////////////// + template + struct multiplication_result { + using temp_base_type = typename std::conditional< + // if both arguments are unsigned + ! std::numeric_limits::is_signed + && ! std::numeric_limits::is_signed, + // result is unsigned + std::uintmax_t, + // otherwise result is signed + std::intmax_t + >::type; + + using r_type = checked_result; + using r_interval_type = interval; + + constexpr static const r_interval_type t_interval{ + checked::cast(base_value(std::numeric_limits::min())), + checked::cast(base_value(std::numeric_limits::max())) + }; + + constexpr static const r_interval_type u_interval{ + checked::cast(base_value(std::numeric_limits::min())), + checked::cast(base_value(std::numeric_limits::max())) + }; + + constexpr static const r_interval_type r_interval = t_interval * u_interval; + + constexpr static auto rl = r_interval.l; + constexpr static auto ru = r_interval.u; + + using type = typename result_type< + temp_base_type, + rl.exception() + ? std::numeric_limits::min() + : static_cast(rl), + ru.exception() + ? std::numeric_limits::max() + : static_cast(ru) + >::type; + }; + + /////////////////////////////////////////////////////////////////////// + template + struct division_result { + using temp_base_type = typename std::conditional< + // if both arguments are unsigned + ! std::numeric_limits::is_signed + && ! std::numeric_limits::is_signed, + // result is unsigned + std::uintmax_t, + // otherwise result is signed + std::intmax_t + >::type; + + using r_type = checked_result; + using r_interval_type = interval; + + constexpr static const r_interval_type t_interval{ + checked::cast(base_value(std::numeric_limits::min())), + checked::cast(base_value(std::numeric_limits::max())) + }; + + constexpr static const r_interval_type u_interval{ + checked::cast(base_value(std::numeric_limits::min())), + checked::cast(base_value(std::numeric_limits::max())) + }; + + constexpr static const r_interval_type rx(){ + if(u_interval.u < r_type(0) + || u_interval.l > r_type(0)) + return t_interval / u_interval; + return utility::minmax( + std::initializer_list { + t_interval.l / u_interval.l, + t_interval.l / r_type(-1), + t_interval.l / r_type(1), + t_interval.l / u_interval.u, + t_interval.u / u_interval.l, + t_interval.u / r_type(-1), + t_interval.u / r_type(1), + t_interval.u / u_interval.u, + } + ); + } + + constexpr static const r_interval_type r_interval = rx(); + + constexpr static auto rl = r_interval.l; + constexpr static auto ru = r_interval.u; + + using type = typename result_type< + temp_base_type, + rl.exception() + ? std::numeric_limits::min() + : static_cast(rl), + ru.exception() + ? std::numeric_limits::max() + : static_cast(ru) + >::type; + }; + + /////////////////////////////////////////////////////////////////////// + template + struct modulus_result { + using temp_base_type = typename std::conditional< + // if both arguments are unsigned + ! std::numeric_limits::is_signed + && ! std::numeric_limits::is_signed, + // result is unsigned + std::uintmax_t, + // otherwise result is signed + std::intmax_t + >::type; + + using r_type = checked_result; + using r_interval_type = interval; + + constexpr static const r_interval_type t_interval{ + checked::cast(base_value(std::numeric_limits::min())), + checked::cast(base_value(std::numeric_limits::max())) + }; + + constexpr static const r_interval_type u_interval{ + checked::cast(base_value(std::numeric_limits::min())), + checked::cast(base_value(std::numeric_limits::max())) + }; + + constexpr static const r_interval_type rx(){ + if(u_interval.u < r_type(0) + || u_interval.l > r_type(0)) + return t_interval / u_interval; + return utility::minmax( + std::initializer_list { + t_interval.l % u_interval.l, + t_interval.l % r_type(-1), + t_interval.l % r_type(1), + t_interval.l % u_interval.u, + t_interval.u % u_interval.l, + t_interval.u % r_type(-1), + t_interval.u % r_type(1), + t_interval.u % u_interval.u, + } + ); + } + + constexpr static const r_interval_type r_interval = rx(); + + constexpr static auto rl = r_interval.l; + constexpr static auto ru = r_interval.u; + + using type = typename result_type< + temp_base_type, + rl.exception() + ? std::numeric_limits::min() + : static_cast(rl), + ru.exception() + ? std::numeric_limits::max() + : static_cast(ru) + >::type; + }; + + /////////////////////////////////////////////////////////////////////// + // note: comparison_result (<, >, ...) is special. + // The return value is always a bool. The type returned here is + // the intermediate type applied to make the values comparable. + template + struct comparison_result { + using temp_base_type = typename std::conditional< + // if both arguments are unsigned + ! std::numeric_limits::is_signed + && ! std::numeric_limits::is_signed, + // result is unsigned + std::uintmax_t, + // otherwise result is signed + std::intmax_t + >::type; + + using r_type = checked_result; + using r_interval_type = interval; + + constexpr static const r_interval_type t_interval{ + checked::cast(base_value(std::numeric_limits::min())), + checked::cast(base_value(std::numeric_limits::max())) + }; + + constexpr static const r_interval_type u_interval{ + checked::cast(base_value(std::numeric_limits::min())), + checked::cast(base_value(std::numeric_limits::max())) + }; + + // workaround some microsoft problem + #if 0 + constexpr static r_type min(const r_type & t, const r_type & u){ + // assert(! u.exception()); + // assert(! t.exception()); + return static_cast(t < u) ? t : u; + } + + constexpr static r_type max(const r_type & t, const r_type & u){ + // assert(! u.exception()); + // assert(! t.exception()); + return static_cast(t < u) ? u : t; + } + #endif + + // union of two intervals + // note: we can't use t_interval | u_interval because it + // depends on max and min which in turn depend on < which in turn + // depends on implicit conversion of tribool to bool + constexpr static r_interval_type union_interval( + const r_interval_type & t, + const r_interval_type & u + ){ + //const r_type & rl = min(t.l, u.l); + const r_type & rmin = static_cast(t.l < u.l) ? t.l : u.l; + //const r_type & ru = max(t.u, u.u); + const r_type & rmax = static_cast(t.u < u.u) ? u.u : t.u; + return r_interval_type(rmin, rmax); + } + + constexpr static const r_interval_type r_interval = + union_interval(t_interval, u_interval); + + constexpr static auto rl = r_interval.l; + constexpr static auto ru = r_interval.u; + + using type = typename result_type< + temp_base_type, + rl.exception() + ? std::numeric_limits::min() + : static_cast(rl), + ru.exception() + ? std::numeric_limits::max() + : static_cast(ru) + >::type; + }; + + /////////////////////////////////////////////////////////////////////// + // shift operations + template + struct left_shift_result { + using temp_base_type = typename std::conditional< + std::numeric_limits::is_signed, + std::intmax_t, + std::uintmax_t + >::type; + + using r_type = checked_result; + using r_interval_type = interval; + + constexpr static const r_interval_type t_interval{ + checked::cast(base_value(std::numeric_limits::min())), + checked::cast(base_value(std::numeric_limits::max())) + }; + + constexpr static const r_interval_type u_interval{ + checked::cast(base_value(std::numeric_limits::min())), + checked::cast(base_value(std::numeric_limits::max())) + }; + + constexpr static const r_interval_type r_interval = + t_interval << u_interval; + + constexpr static auto rl = r_interval.l; + constexpr static auto ru = r_interval.u; + + using type = typename result_type< + temp_base_type, + rl.exception() + ? std::numeric_limits::min() + : static_cast(rl), + ru.exception() + ? std::numeric_limits::max() + : static_cast(ru) + >::type; + }; + + /////////////////////////////////////////////////////////////////////// + template + struct right_shift_result { + using temp_base_type = typename std::conditional< + std::numeric_limits::is_signed, + std::intmax_t, + std::uintmax_t + >::type; + + using r_type = checked_result; + using r_interval_type = interval; + + constexpr static const r_interval_type t_interval{ + checked::cast(base_value(std::numeric_limits::min())), + checked::cast(base_value(std::numeric_limits::max())) + }; + + constexpr static const r_type u_min + = checked::cast(base_value(std::numeric_limits::min())); + + constexpr static const r_interval_type u_interval{ + u_min.exception() + ? r_type(0) + : u_min, + checked::cast(base_value(std::numeric_limits::max())) + }; + + constexpr static const r_interval_type r_interval = t_interval >> u_interval; + + constexpr static auto rl = r_interval.l; + constexpr static auto ru = r_interval.u; + + using type = typename result_type< + temp_base_type, + rl.exception() + ? std::numeric_limits::min() + : static_cast(rl), + ru.exception() + ? std::numeric_limits::max() + : static_cast(ru) + >::type; + + }; + + /////////////////////////////////////////////////////////////////////// + template + struct bitwise_and_result { + using type = decltype( + typename base_type::type() + & typename base_type::type() + ); + }; + template + struct bitwise_or_result { + using type = decltype( + typename base_type::type() + | typename base_type::type() + ); + }; + template + struct bitwise_xor_result { + using type = decltype( + typename base_type::type() + ^ typename base_type::type() + ); + }; +}; + +} // safe_numerics +} // boost + +#endif // BOOST_NUMERIC_AUTOMATIC_HPP diff --git a/boost/safe_numerics/checked_default.hpp b/boost/safe_numerics/checked_default.hpp new file mode 100644 index 0000000..1b13fa5 --- /dev/null +++ b/boost/safe_numerics/checked_default.hpp @@ -0,0 +1,217 @@ +#ifndef BOOST_NUMERIC_CHECKED_DEFAULT_HPP +#define BOOST_NUMERIC_CHECKED_DEFAULT_HPP + +// Copyright (c) 2017 Robert Ramey +// +// 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) + +// contains operation implementation of arithmetic operators +// on built-in types. The default implementation is to just +// invoke the operation with no checking. These are overloaded +// for specific types such as integer, etc. + +// implement the equivant of template partial specialization for functions + +// what we need is +// a) a default implementation of add, subtract, etc which just +// implements the standard operations and returns the result +// b) specific implementations to be called from safe implementation +// such as safe ... and someday maybe money ... +// +// What we need is partial function specialization - but this doesn't +// exist in C++ (yet?). But particial specialization of structures DOES +// exist. So put our functions into a class which can then be +// partially specialized. Finally. add a function interface to so that +// data types can be deduced from the function call. We now have +// the equivalent of partial function template specialization. + +// usage example: checked::add(t, u) ... + +#include +#include "checked_result.hpp" + +namespace boost { +namespace safe_numerics { + +// main function object which contains functions which handle +// primitives which haven't been overriden. For now, these +// implement the default operation. But I see this as an indicator +// that there is more work to be done. For example float * int should +// never be called because promotions on operands should occur before +// the operation is invoked. So rather than returning the default operation +// it should trap with a static_assert. This occurs at compile time while +// calculating result interval. This needs more investigation. + +template< + typename R, + R Min, + R Max, + typename T, + class F = make_checked_result, + class Default = void +> +struct heterogeneous_checked_operation { + constexpr static checked_result + cast(const T & t) /* noexcept */ { + return static_cast(t); + } +}; + +template< + typename R, + class F = make_checked_result, + class Default = void +> +struct checked_operation{ + constexpr static checked_result + minus(const R & t) noexcept { + return - t; + } + constexpr static checked_result + add(const R & t, const R & u) noexcept { + return t + u; + } + constexpr static checked_result + subtract(const R & t, const R & u) noexcept { + return t - u; + } + constexpr static checked_result + multiply(const R & t, const R & u) noexcept { + return t * u; + } + constexpr static checked_result + divide(const R & t, const R & u) noexcept { + return t / u; + } + constexpr static checked_result + modulus(const R & t, const R & u) noexcept { + return t % u; + } + constexpr static boost::logic::tribool + less_than(const R & t, const R & u) noexcept { + return t < u; + } + constexpr static boost::logic::tribool + greater_than(const R & t, const R & u) noexcept { + return t > u; + } + constexpr static boost::logic::tribool + equal(const R & t, const R & u) noexcept { + return t < u; + } + constexpr static checked_result + left_shift(const R & t, const R & u) noexcept { + return t << u; + } + constexpr static checked_result + right_shift(const R & t, const R & u) noexcept { + return t >> u; + } + constexpr static checked_result + bitwise_or(const R & t, const R & u) noexcept { + return t | u; + } + constexpr static checked_result + bitwise_xor(const R & t, const R & u) noexcept { + return t ^ u; + } + constexpr static checked_result + bitwise_and(const R & t, const R & u) noexcept { + return t & u; + } + constexpr static checked_result + bitwise_not(const R & t) noexcept { + return ~t; + } +}; + +namespace checked { + +// implement function call interface so that types other than +// the result type R can be deduced from the function parameters. + +template +constexpr checked_result cast(const T & t) /* noexcept */ { + return heterogeneous_checked_operation< + R, + std::numeric_limits::min(), + std::numeric_limits::max(), + T + >::cast(t); +} +template +constexpr checked_result minus(const R & t) noexcept { + return checked_operation::minus(t); +} +template +constexpr checked_result add(const R & t, const R & u) noexcept { + return checked_operation::add(t, u); +} +template +constexpr checked_result subtract(const R & t, const R & u) noexcept { + return checked_operation::subtract(t, u); +} +template +constexpr checked_result multiply(const R & t, const R & u) noexcept { + return checked_operation::multiply(t, u); +} +template +constexpr checked_result divide(const R & t, const R & u) noexcept { + return checked_operation::divide(t, u); +} +template +constexpr checked_result modulus(const R & t, const R & u) noexcept { + return checked_operation::modulus(t, u); +} +template +constexpr checked_result less_than(const R & t, const R & u) noexcept { + return checked_operation::less_than(t, u); +} +template +constexpr checked_result greater_than_equal(const R & t, const R & u) noexcept { + return ! checked_operation::less_than(t, u); +} +template +constexpr checked_result greater_than(const R & t, const R & u) noexcept { + return checked_operation::greater_than(t, u); +} +template +constexpr checked_result less_than_equal(const R & t, const R & u) noexcept { + return ! checked_operation::greater_than(t, u); +} +template +constexpr checked_result equal(const R & t, const R & u) noexcept { + return checked_operation::equal(t, u); +} +template +constexpr checked_result left_shift(const R & t, const R & u) noexcept { + return checked_operation::left_shift(t, u); +} +template +constexpr checked_result right_shift(const R & t, const R & u) noexcept { + return checked_operation::right_shift(t, u); +} +template +constexpr checked_result bitwise_or(const R & t, const R & u) noexcept { + return checked_operation::bitwise_or(t, u); +} +template +constexpr checked_result bitwise_xor(const R & t, const R & u) noexcept { + return checked_operation::bitwise_xor(t, u); +} +template +constexpr checked_result bitwise_and(const R & t, const R & u) noexcept { + return checked_operation::bitwise_and(t, u); +} +template +constexpr checked_result bitwise_not(const R & t) noexcept { + return checked_operation::bitwise_not(t); +} + +} // checked +} // safe_numerics +} // boost + +#endif // BOOST_NUMERIC_CHECKED_DEFAULT_HPP diff --git a/boost/safe_numerics/checked_float.hpp b/boost/safe_numerics/checked_float.hpp new file mode 100644 index 0000000..bbd7d86 --- /dev/null +++ b/boost/safe_numerics/checked_float.hpp @@ -0,0 +1,214 @@ +#ifndef BOOST_NUMERIC_CHECKED_FLOAT_HPP +#define BOOST_NUMERIC_CHECKED_FLOAT_HPP + +// Copyright (c) 2017 Robert Ramey +// +// 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) + +// contains operation implementation of arithmetic operators +// on built-in floating point types. The default implementation is to just +// invoke the operation with no checking. These are overloaded +// for specific types such as integer, etc. + +#include // std::is_floating_point, make_unsigned + +namespace boost { +namespace safe_numerics { +namespace checked { + +//////////////////////////////////////////////////// +// layer 0 - implement safe operations for floating + +template< + typename R, + R Min, + R Max, + typename T, + class F +> +struct heterogeneous_checked_operation< + R, + Min, + Max, + T, + F, + typename std::enable_if< + std::is_floating_point::value + && std::is_floating_point::value + >::type +>{ + constexpr static checked_result + cast(const T & t) noexcept { + return t; + }; +}; // checked_unary_operation + +template< + typename R, + R Min, + R Max, + typename T, + class + F +> +struct heterogeneous_checked_operation< + R, + Min, + Max, + T, + F, + typename std::enable_if< + std::is_floating_point::value + && std::is_integralt::value + >::type +>{ + constexpr static checked_result + cast(const T & t) noexcept { + return t; + }; +}; // checked_unary_operation + +template +struct checked_operation::value + >::type +>{ + constexpr static checked_result cast(const T & t) { + return + cast_impl_detail::cast_impl( + t, + std::is_signed(), + std::is_signed() + ); + } + constexpr static checked_result add(const T & t, const U & u) { + return t + u; + } + + constexpr static checked_result subtract( + const T & t, + const U & u + ) { + return t - u; + } + + constexpr static checked_result multiply( + const T & t, + const U & u + ) noexcept { + return t * u; + } + + constexpr static checked_result divide( + const T & t, + const U & u + ) noexcept { + return t / u; + } + + constexpr static checked_result modulus( + const T & t, + const U & u + ) noexcept { + return t % u; + } + + constexpr static bool less_than(const T & t, const U & u) noexcept { + return t < u; + } + + constexpr static bool greater_than(const T & t, const U & u) noexcept { + return t > u; + } + + constexpr static bool equal(const T & t, const U & u) noexcept { + return t < u; + } + +}; // checked_binary_operation +template +typename std::enable_if< + std::is_floating_point::value + && std::is_floating_point::value + && std::is_floating_point::value, + checked_result +>::type +constexpr bool less_than(const T & t, const U & u) noexcept { + return t < u; +} + +template +typename std::enable_if< + std::is_floating_point::value + && std::is_floating_point::value + && std::is_floating_point::value, + checked_result +>::type +constexpr bool equal(const T & t, const U & u) noexcept { + return t < u; +} + +template +typename std::enable_if< + std::is_floating_point::value + && std::is_floating_point::value + && std::is_floating_point::value, + checked_result +>::type +constexpr checked_result left_shift(const T & t, const U & u) noexcept { + return t << u; +} + +template +typename std::enable_if< + std::is_floating_point::value + && std::is_floating_point::value + && std::is_floating_point::value, + checked_result +>::type +constexpr checked_result right_shift(const T & t, const U & u) noexcept { + return t >> u; +} + +template +typename std::enable_if< + std::is_floating_point::value + && std::is_floating_point::value + && std::is_floating_point::value, + checked_result +>::type +constexpr checked_result bitwise_or(const T & t, const U & u) noexcept { + return t | u; +} + +template +typename std::enable_if< + std::is_floating_point::value + && std::is_floating_point::value + && std::is_floating_point::value, + checked_result +>::type +constexpr checked_result bitwise_xor(const T & t, const U & u) noexcept { + return t ^ u; +} + +template +typename std::enable_if< + std::is_floating_point::value + && std::is_floating_point::value + && std::is_floating_point::value, + checked_result +>::type +constexpr checked_result bitwise_and(const T & t, const U & u) noexcept { + return t & u; +} + +} // checked +} // safe_numerics +} // boost + +#endif // BOOST_NUMERIC_CHECKED_DEFAULT_HPP + diff --git a/boost/safe_numerics/checked_integer.hpp b/boost/safe_numerics/checked_integer.hpp new file mode 100644 index 0000000..c6e0790 --- /dev/null +++ b/boost/safe_numerics/checked_integer.hpp @@ -0,0 +1,819 @@ +#ifndef BOOST_NUMERIC_CHECKED_INTEGER_HPP +#define BOOST_NUMERIC_CHECKED_INTEGER_HPP + +// Copyright (c) 2012 Robert Ramey +// +// 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) + +// contains operations for doing checked aritmetic on NATIVE +// C++ types. + +#include +#include // is_integral, make_unsigned, enable_if +#include // std::max + +#include "checked_result.hpp" +#include "checked_default.hpp" +#include "safe_compare.hpp" +#include "utility.hpp" +#include "exception.hpp" + +namespace boost { +namespace safe_numerics { + +// utility + +template +using bool_type = typename std::conditional::type; + +//////////////////////////////////////////////////// +// layer 0 - implement safe operations for intrinsic integers +// Note presumption of twos complement integer arithmetic + +// convert an integral value to some other integral type +template< + typename R, + R Min, + R Max, + typename T, + class F +> +struct heterogeneous_checked_operation< + R, + Min, + Max, + T, + F, + typename std::enable_if< + std::is_integral::value + && std::is_integral::value + >::type +>{ + //////////////////////////////////////////////////// + // safe casting on primitive types + + struct cast_impl_detail { + constexpr static checked_result + cast_impl( + const T & t, + std::true_type, // R is signed + std::true_type // T is signed + ){ + // INT32-C Ensure that operations on signed + // integers do not overflow + return + boost::safe_numerics::safe_compare::greater_than( + t, + Max + ) ? + F::template invoke( + "converted signed value too large" + ) + : + boost::safe_numerics::safe_compare::less_than( + t, + Min + ) ? + F::template invoke( + "converted signed value too small" + ) + : + checked_result(static_cast(t)) + ; + } + constexpr static checked_result + cast_impl( + const T & t, + std::true_type, // R is signed + std::false_type // T is unsigned + ){ + // INT30-C Ensure that unsigned integer operations + // do not wrap + return + boost::safe_numerics::safe_compare::greater_than( + t, + Max + ) ? + F::template invoke( + "converted unsigned value too large" + ) + : + checked_result(static_cast(t)) + ; + } + constexpr static checked_result + cast_impl( + const T & t, + std::false_type, // R is unsigned + std::false_type // T is unsigned + ){ + // INT32-C Ensure that operations on unsigned + // integers do not overflow + return + boost::safe_numerics::safe_compare::greater_than( + t, + Max + ) ? + F::template invoke( + "converted unsigned value too large" + ) + : + checked_result(static_cast(t)) + ; + } + constexpr static checked_result + cast_impl( + const T & t, + std::false_type, // R is unsigned + std::true_type // T is signed + ){ + return + boost::safe_numerics::safe_compare::less_than(t, 0) ? + F::template invoke( + "converted negative value to unsigned" + ) + : + boost::safe_numerics::safe_compare::greater_than( + t, + Max + ) ? + F::template invoke( + "converted signed value too large" + ) + : + checked_result(static_cast(t)) + ; + } + }; // cast_impl_detail + + constexpr static checked_result + cast(const T & t){ + return + cast_impl_detail::cast_impl( + t, + std::is_signed(), + std::is_signed() + ); + } +}; + +// converting floating point value to integral type +template< + typename R, + R Min, + R Max, + typename T, + class F +> +struct heterogeneous_checked_operation< + R, + Min, + Max, + T, + F, + typename std::enable_if< + std::is_integral::value + && std::is_floating_point::value + >::type +>{ + constexpr static checked_result + cast(const T & t){ + return static_cast(t); + } +}; + +// converting integral value to floating point type + +// INT35-C. Use correct integer precisions +template< + typename R, + R Min, + R Max, + typename T, + class F +> +struct heterogeneous_checked_operation< + R, + Min, + Max, + T, + F, + typename std::enable_if< + std::is_floating_point::value + && std::is_integral::value + >::type + >{ + constexpr static checked_result + cast(const T & t){ + if(std::numeric_limits::digits < std::numeric_limits::digits){ + if(utility::significant_bits(t) > std::numeric_limits::digits){ + return F::invoke( + safe_numerics_error::precision_overflow_error, + "keep precision" + ); + } + } + return t; + } +}; + +// binary operations on primitive integer types + +template< + typename R, + class F +> +struct checked_operation::value + >::type +>{ + //////////////////////////////////////////////////// + // safe addition on primitive types + + struct add_impl_detail { + // result unsigned + constexpr static checked_result add( + const R t, + const R u, + std::false_type // R unsigned + ){ + return + // INT30-C. Ensure that unsigned integer operations do not wrap + std::numeric_limits::max() - u < t ? + F::template invoke( + "addition result too large" + ) + : + checked_result(t + u) + ; + } + + // result signed + constexpr static checked_result add( + const R t, + const R u, + std::true_type // R signed + ){ + // INT32-C. Ensure that operations on signed integers do not result in overflow + return + // INT32-C. Ensure that operations on signed integers do not result in overflow + ((u > 0) && (t > (std::numeric_limits::max() - u))) ? + F::template invoke( + "addition result too large" + ) + : + ((u < 0) && (t < (std::numeric_limits::min() - u))) ? + F::template invoke( + "addition result too low" + ) + : + checked_result(t + u) + ; + } + }; // add_impl_detail + + constexpr static checked_result + add(const R & t, const R & u){ + return add_impl_detail::add(t, u, std::is_signed()); + } + + //////////////////////////////////////////////////// + // safe subtraction on primitive types + struct subtract_impl_detail { + + // result unsigned + constexpr static checked_result subtract( + const R t, + const R u, + std::false_type // R is unsigned + ){ + // INT30-C. Ensure that unsigned integer operations do not wrap + return + t < u ? + F::template invoke( + "subtraction result cannot be negative" + ) + : + checked_result(t - u) + ; + } + + // result signed + constexpr static checked_result subtract( + const R t, + const R u, + std::true_type // R is signed + ){ // INT32-C + return + // INT32-C. Ensure that operations on signed integers do not result in overflow + ((u > 0) && (t < (std::numeric_limits::min() + u))) ? + F::template invoke( + "subtraction result overflows result type" + ) + : + ((u < 0) && (t > (std::numeric_limits::max() + u))) ? + F::template invoke( + "subtraction result overflows result type" + ) + : + checked_result(t - u) + ; + } + + }; // subtract_impl_detail + + constexpr static checked_result subtract(const R & t, const R & u){ + return subtract_impl_detail::subtract(t, u, std::is_signed()); + } + + //////////////////////////////////////////////////// + // safe minus on primitive types + struct minus_impl_detail { + + // result unsigned + constexpr static checked_result minus( + const R t, + std::false_type // R is unsigned + ){ + return t > 0 ? + F::template invoke( + "minus unsigned would be negative" + ) + : + // t == 0 + checked_result(0) + ; + } + + // result signed + constexpr static checked_result minus( + const R t, + std::true_type // R is signed + ){ // INT32-C + return t == std::numeric_limits::min() ? + F::template invoke( + "subtraction result overflows result type" + ) + : + checked_result(-t) + ; + } + + }; // minus_impl_detail + + constexpr static checked_result minus(const R & t){ + return minus_impl_detail::minus(t, std::is_signed()); + } + + //////////////////////////////////////////////////// + // safe multiplication on primitive types + + struct multiply_impl_detail { + + // result unsigned + constexpr static checked_result multiply( + const R t, + const R u, + std::false_type, // R is unsigned + std::false_type // !(sizeochecked_resultR) > sizeochecked_resultstd::uintmax_t) / 2) + + ){ + // INT30-C + // fast method using intermediate result guaranteed not to overflow + // todo - replace std::uintmax_t with a size double the size of R + using i_type = std::uintmax_t; + return + static_cast(t) * static_cast(u) + > std::numeric_limits::max() ? + F::template invoke( + "multiplication overflow" + ) + : + checked_result(t * u) + ; + } + constexpr static checked_result multiply( + const R t, + const R u, + std::false_type, // R is unsigned + std::true_type // (sizeochecked_resultR) > sizeochecked_resultstd::uintmax_t) / 2) + + ){ + // INT30-C + return + u > 0 && t > std::numeric_limits::max() / u ? + F::template invoke( + "multiplication overflow" + ) + : + checked_result(t * u) + ; + } + + // result signed + constexpr static checked_result multiply( + const R t, + const R u, + std::true_type, // R is signed + std::false_type // ! (sizeochecked_resultR) > (sizeochecked_resultstd::intmax_t) / 2)) + + ){ + // INT30-C + // fast method using intermediate result guaranteed not to overflow + // todo - replace std::intmax_t with a size double the size of R + using i_type = std::intmax_t; + return + ( + static_cast(t) * static_cast(u) + > static_cast(std::numeric_limits::max()) + ) ? + F::template invoke( + "multiplication overflow" + ) + : + ( + static_cast(t) * static_cast(u) + < static_cast(std::numeric_limits::min()) + ) ? + F::template invoke( + "multiplication overflow" + ) + : + checked_result(t * u) + ; + } + constexpr static checked_result multiply( + const R t, + const R u, + std::true_type, // R is signed + std::true_type // (sizeochecked_resultR) > (sizeochecked_resultstd::intmax_t) / 2)) + ){ // INT32-C + return t > 0 ? + u > 0 ? + t > std::numeric_limits::max() / u ? + F::template invoke( + "multiplication overflow" + ) + : + checked_result(t * u) + : // u <= 0 + u < std::numeric_limits::min() / t ? + F::template invoke( + "multiplication overflow" + ) + : + checked_result(t * u) + : // t <= 0 + u > 0 ? + t < std::numeric_limits::min() / u ? + F::template invoke( + "multiplication overflow" + ) + : + checked_result(t * u) + : // u <= 0 + t != 0 && u < std::numeric_limits::max() / t ? + F::template invoke( + "multiplication overflow" + ) + : + checked_result(t * u) + ; + } + }; // multiply_impl_detail + + constexpr static checked_result multiply(const R & t, const R & u){ + return multiply_impl_detail::multiply( + t, + u, + std::is_signed(), + std::integral_constant< + bool, + (sizeof(R) > sizeof(std::uintmax_t) / 2) + >() + ); + } + + //////////////////////////////// + // safe division on unsafe types + + struct divide_impl_detail { + constexpr static checked_result divide( + const R & t, + const R & u, + std::false_type // R is unsigned + ){ + return t / u; + } + + constexpr static checked_result divide( + const R & t, + const R & u, + std::true_type // R is signed + ){ + return + (u == -1 && t == std::numeric_limits::min()) ? + F::template invoke( + "result cannot be represented" + ) + : + checked_result(t / u) + ; + } + }; // divide_impl_detail + + // note that we presume that the size of R >= size of T + constexpr static checked_result divide(const R & t, const R & u){ + if(u == 0){ + return F::template invoke( + "divide by zero" + ); + } + return divide_impl_detail::divide(t, u, std::is_signed()); + } + + //////////////////////////////// + // safe modulus on unsafe types + + struct modulus_impl_detail { + constexpr static checked_result modulus( + const R & t, + const R & u, + std::false_type // R is unsigned + ){ + return t % u; + } + + constexpr static checked_result modulus( + const R & t, + const R & u, + std::true_type // R is signed + ){ + if(u >= 0) + return t % u; + checked_result ux = checked::minus(u); + if(ux.exception()) + return t; + return t % static_cast(ux); + } + }; // modulus_impl_detail + + constexpr static checked_result modulus(const R & t, const R & u){ + if(0 == u) + return F::template invoke( + "denominator is zero" + ); + + // why to we need abs here? the sign of the modulus is the sign of the + // dividend. Consider -128 % -1 The result of this operation should be -1 + // but if I use t % u the x86 hardware uses the divide instruction + // capturing the modulus as a side effect. When it does this, it + // invokes the operation -128 / -1 -> 128 which overflows a signed type + // and provokes a hardware exception. We can fix this using abs() + // since -128 % -1 = -128 % 1 = 0 + return modulus_impl_detail::modulus(t, u, typename std::is_signed::type()); + } + + /////////////////////////////////// + // shift operations + + struct left_shift_integer_detail { + + #if 0 + // todo - optimize for gcc to exploit builtin + /* for gcc compilers + int __builtin_clz (unsigned int x) + Returns the number of leading 0-bits in x, starting at the + most significant bit position. If x is 0, the result is undefined. + */ + + #ifndef __has_feature // Optional of course. + #define __has_feature(x) 0 // Compatibility with non-clang compilers. + #endif + + template + constexpr unsigned int leading_zeros(const T & t){ + if(0 == t) + return 0; + #if __has_feature(builtin_clz) + return __builtin_clz(t); + #else + #endif + } + #endif + + // INT34-C C++ + + // standard paragraph 5.8 / 2 + // The value of E1 << E2 is E1 left-shifted E2 bit positions; + // vacated bits are zero-filled. + constexpr static checked_result left_shift( + const R & t, + const R & u, + std::false_type // R is unsigned + ){ + // the value of the result is E1 x 2^E2, reduced modulo one more than + // the maximum value representable in the result type. + + // see 5.8 & 1 + // if right operand is + // greater than or equal to the length in bits of the promoted left operand. + if( + safe_compare::greater_than( + u, + std::numeric_limits::digits - utility::significant_bits(t) + ) + ){ + // behavior is undefined + return F::template invoke( + "shifting left more bits than available is undefined behavior" + ); + } + return t << u; + } + + constexpr static checked_result left_shift( + const R & t, + const R & u, + std::true_type // R is signed + ){ + // and [E1] has a non-negative value + if(t >= 0){ + // and E1 x 2^E2 is representable in the corresponding + // unsigned type of the result type, + + // see 5.8 & 1 + // if right operand is + // greater than or equal to the length in bits of the promoted left operand. + if( + safe_compare::greater_than( + u, + std::numeric_limits::digits - utility::significant_bits(t) + ) + ){ + // behavior is undefined + return F::template invoke( + "shifting left more bits than available" + ); + } + else{ + return t << u; + } + } + // otherwise, the behavior is undefined. + return F::template invoke( + "shifting a negative value" + ); + } + + }; // left_shift_integer_detail + + constexpr static checked_result left_shift( + const R & t, + const R & u + ){ + // INT34-C - Do not shift an expression by a negative number of bits + + // standard paragraph 5.8 & 1 + // if the right operand is negative + if(u == 0){ + return t; + } + if(u < 0){ + return F::template invoke( + "shifting negative amount" + ); + } + if(u > std::numeric_limits::digits){ + // behavior is undefined + return F::template invoke( + "shifting more bits than available" + ); + } + return left_shift_integer_detail::left_shift(t, u, std::is_signed()); + } + +// right shift + + struct right_shift_integer_detail { + + // INT34-C C++ + + // standard paragraph 5.8 / 3 + // The value of E1 << E2 is E1 left-shifted E2 bit positions; + // vacated bits are zero-filled. + constexpr static checked_result right_shift( + const R & t, + const R & u, + std::false_type // T is unsigned + ){ + // the value of the result is the integral part of the + // quotient of E1/2E2 + return t >> u; + } + + constexpr static checked_result right_shift( + const R & t, + const R & u, + std::true_type // T is signed; + ){ + if(t < 0){ + // note that the C++ standard considers this case is "implemenation + // defined" rather than "undefined". + return F::template invoke( + "shifting a negative value" + ); + } + + // the value is the integral part of E1 / 2^E2, + return t >> u; + } + }; // right_shift_integer_detail + +constexpr static checked_result right_shift( + const R & t, + const R & u +){ + // INT34-C - Do not shift an expression by a negative number of bits + + // standard paragraph 5.8 & 1 + // if the right operand is negative + if(u < 0){ + return F::template invoke( + "shifting negative amount" + ); + } + if(u > std::numeric_limits::digits){ + // behavior is undefined + return F::template invoke( + "shifting more bits than available" + ); + } + return right_shift_integer_detail::right_shift(t, u ,std::is_signed()); +} + +/////////////////////////////////// +// bitwise operations + +// INT13-C Note: We don't enforce recommendation as acually written +// as it would break too many programs. Specifically, we permit signed +// integer operands. + +constexpr static checked_result bitwise_or(const R & t, const R & u){ + using namespace boost::safe_numerics::utility; + const unsigned int result_size + = std::max(significant_bits(t), significant_bits(u)); + + if(result_size > bits_type::value){ + return F::template invoke( + "result type too small to hold bitwise or" + ); + } + return t | u; +} + +constexpr static checked_result bitwise_xor(const R & t, const R & u){ + using namespace boost::safe_numerics::utility; + const unsigned int result_size + = std::max(significant_bits(t), significant_bits(u)); + + if(result_size > bits_type::value){ + return F::template invoke( + "result type too small to hold bitwise or" + ); + } + return t ^ u; +} + +constexpr static checked_result bitwise_and(const R & t, const R & u){ + using namespace boost::safe_numerics::utility; + const unsigned int result_size + = std::min(significant_bits(t), significant_bits(u)); + + if(result_size > bits_type::value){ + return F::template invoke( + "result type too small to hold bitwise and" + ); + } + return t & u; +} + +constexpr static checked_result bitwise_not(const R & t){ + using namespace boost::safe_numerics::utility; + + if(significant_bits(t) > bits_type::value){ + return F::template invoke( + "result type too small to hold bitwise inverse" + ); + } + return ~t; +} + +}; // checked_operation +} // safe_numerics +} // boost + +#endif // BOOST_NUMERIC_CHECKED_INTEGER_HPP diff --git a/boost/safe_numerics/checked_result.hpp b/boost/safe_numerics/checked_result.hpp new file mode 100644 index 0000000..10bd7a6 --- /dev/null +++ b/boost/safe_numerics/checked_result.hpp @@ -0,0 +1,146 @@ +#ifndef BOOST_NUMERIC_CHECKED_RESULT +#define BOOST_NUMERIC_CHECKED_RESULT + +// Copyright (c) 2012 Robert Ramey +// +// 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) + +// contains operations for doing checked aritmetic on NATIVE +// C++ types. +#include +#include // is_convertible +#include "exception.hpp" + +namespace boost { +namespace safe_numerics { + +template +struct checked_result { + const safe_numerics_error m_e; + const union { + R m_r; + char const * m_msg; + }; + + // don't permit construction without initial value; + checked_result() = delete; + + // note: I implemented the following non-default copy and move + // constructors because I thought I needed to do this in order + // to make them constexpr. Turns out though that doing this creates + // a syntax error because the assignment results in error due + // to assignment "outside of object lifetime". I think this could + // be addressed by replacing the anonymous union above with a + // named union. This would create some syntax changes which would + // ripple through some parts of th program. So for now, we'll just + // rely on the default copy and move constructors. + #if 0 + // copy constructor + constexpr /*explicit*/ checked_result(const checked_result & r) noexpect : + m_e(r.m_e) + { + if(safe_numerics_error::success == r.m_e) + m_r = r.m_r; + else + m_msg = r.m_msg; + } + + // move constructor + constexpr /*explicit*/ checked_result(checked_result && r) noexcept : + m_e(r.m_e) + { + if(safe_numerics_error::success == r.m_e) + m_r = r.m_r; + else + m_msg = r.m_msg; + } + #endif + checked_result(const checked_result & r) = default; + checked_result(checked_result && r) = default; + + constexpr /*explicit*/ checked_result(const R & r) : + m_e(safe_numerics_error::success), + m_r(r) + {} + #if 0 + template + constexpr /*explicit*/ checked_result(const T & t) noexcept : + m_e(safe_numerics_error::success), + m_r(t) + {} + #endif + constexpr /*explicit*/ checked_result( + const safe_numerics_error & e, + const char * msg = "" + ) noexcept : + m_e(e), + m_msg(msg) + { + assert(m_e != safe_numerics_error::success); + } + // permit construct from another checked result type + template + constexpr /*explicit*/ checked_result(const checked_result & t) noexcept : + m_e(t.m_e) + { + static_assert( + std::is_convertible::value, + "T must be convertible to R" + ); + if(safe_numerics_error::success == t.m_e) + m_r = t.m_r; + else + m_msg = t.m_msg; + } + constexpr bool exception() const { + return m_e != safe_numerics_error::success; + } + + // accesors + constexpr operator R() const noexcept{ + // don't assert here. Let the library catch these errors + assert(! exception()); + return m_r; + } + + constexpr operator safe_numerics_error () const noexcept{ + // note that this is a legitimate operation even when + // the operation was successful - it will return success + return m_e; + } + constexpr operator const char *() const noexcept{ + assert(exception()); + return m_msg; + } + + // disallow assignment + checked_result & operator=(const checked_result &) = delete; +}; + +#if 0 +template +constexpr checked_result make_checked_result( + const safe_numerics_error & e, + char const * const & m +) noexcept { + return checked_result(e, m); +} +#endif + +template +class make_checked_result { +public: + template + constexpr static checked_result invoke( + char const * const & m + ) noexcept { + return checked_result(E, m); + } +}; + +} // safe_numerics +} // boost + +#endif // BOOST_NUMERIC_CHECKED_RESULT diff --git a/boost/safe_numerics/checked_result_operations.hpp b/boost/safe_numerics/checked_result_operations.hpp new file mode 100644 index 0000000..0492cab --- /dev/null +++ b/boost/safe_numerics/checked_result_operations.hpp @@ -0,0 +1,1217 @@ +#ifndef BOOST_NUMERIC_CHECKED_RESULT_OPERATIONS +#define BOOST_NUMERIC_CHECKED_RESULT_OPERATIONS + +// Copyright (c) 2012 Robert Ramey +// +// 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) + +// Implemenation of arithmetic on "extended" integers. +// Extended integers are defined in terms of C++ primitive integers as +// a) an interger range +// b) extra elements +inf, -inf, indeterminate +// +// Integer operations are closed on the set of extended integers +// but operations are not necessarily associative when they result in the +// extensions +inf, -inf, and indeterminate +// +// in this code, the type "checked_result" where T is some +// integer type is an "extended" integer. + +#include + +#include + +#include "checked_result.hpp" +#include "checked_integer.hpp" + +////////////////////////////////////////////////////////////////////////// +// the following idea of "value_type" is used by several of the operations +// defined by checked_result arithmetic. + +namespace boost { +namespace safe_numerics { + +template +constexpr void display(const boost::safe_numerics::checked_result & c){ + switch(c.m_e){ + case safe_numerics_error::success: + std::terminate(); + case safe_numerics_error::positive_overflow_error: // result is above representational maximum + std::terminate(); + case safe_numerics_error::negative_overflow_error: // result is below representational minimum + std::terminate(); + case safe_numerics_error::domain_error: // one operand is out of valid range + std::terminate(); + case safe_numerics_error::range_error: // result cannot be produced for this operation + std::terminate(); + case safe_numerics_error::precision_overflow_error: // result lost precision + std::terminate(); + case safe_numerics_error::underflow_error: // result is too small to be represented + std::terminate(); + case safe_numerics_error::negative_value_shift: // negative value in shift operator + std::terminate(); + case safe_numerics_error::negative_shift: // shift a negative value + std::terminate(); + case safe_numerics_error::shift_too_large: // l/r shift exceeds variable size + std::terminate(); + case safe_numerics_error::uninitialized_value: // creating of uninitialized value + std::terminate(); + } +} + +////////////////////////////////////////////////////////////////////////// +// implement C++ operators for check_result + +struct sum_value_type { + // characterization of various values + const enum flag { + known_value = 0, + less_than_min, + greater_than_max, + indeterminate, + count + } m_flag; + template + constexpr flag to_flag(const checked_result & t) const { + switch(static_cast(t)){ + case safe_numerics_error::success: + return known_value; + case safe_numerics_error::negative_overflow_error: + // result is below representational minimum + return less_than_min; + case safe_numerics_error::positive_overflow_error: + // result is above representational maximum + return greater_than_max; + default: + return indeterminate; + } + } + template + constexpr sum_value_type(const checked_result & t) : + m_flag(to_flag(t)) + {} + constexpr operator std::uint8_t () const { + return static_cast(m_flag); + } +}; + +// integers addition +template +typename std::enable_if< + std::is_integral::value, + checked_result +>::type +constexpr inline operator+( + const checked_result & t, + const checked_result & u +){ + using value_type = sum_value_type; + const std::uint8_t order = static_cast(value_type::count); + + // note major pain. Clang constexpr multi-dimensional array is fine. + // but gcc doesn't permit a multi-dimensional array to be be constexpr. + // so we need to some ugly gymnastics to make our system work for all + // all systems. + const enum safe_numerics_error result[order * order] = { + // t == known_value + //{ + // u == ... + safe_numerics_error::success, // known_value, + safe_numerics_error::negative_overflow_error, // less_than_min, + safe_numerics_error::positive_overflow_error, // greater_than_max, + safe_numerics_error::range_error, // indeterminate, + //}, + // t == less_than_min, + //{ + // u == ... + safe_numerics_error::negative_overflow_error, // known_value, + safe_numerics_error::negative_overflow_error, // less_than_min, + safe_numerics_error::range_error, // greater_than_max, + safe_numerics_error::range_error, // indeterminate, + //}, + // t == greater_than_max, + //{ + // u == ... + safe_numerics_error::positive_overflow_error, // known_value, + safe_numerics_error::range_error, // less_than_min, + safe_numerics_error::positive_overflow_error, // greater_than_max, + safe_numerics_error::range_error, // indeterminate, + //}, + // t == indeterminate, + //{ + // u == ... + safe_numerics_error::range_error, // known_value, + safe_numerics_error::range_error, // less_than_min, + safe_numerics_error::range_error, // greater_than_max, + safe_numerics_error::range_error, // indeterminate, + //}, + }; + + const value_type tx(t); + const value_type ux(u); + + const safe_numerics_error e = result[tx * order + ux]; + if(safe_numerics_error::success == e) + return checked::add(t, u); + return checked_result(e, "addition result"); +} + +// unary + +template +typename std::enable_if< + std::is_integral::value, + checked_result +>::type +constexpr inline operator+( + const checked_result & t +){ + return t; +} + +// integers subtraction +template +typename std::enable_if< + std::is_integral::value, + checked_result +>::type +constexpr inline operator-( + const checked_result & t, + const checked_result & u +){ + using value_type = sum_value_type; + constexpr const std::uint8_t order = static_cast(value_type::count); + + constexpr const enum safe_numerics_error result[order * order] = { + // t == known_value + //{ + // u == ... + safe_numerics_error::success, // known_value, + safe_numerics_error::positive_overflow_error, // less_than_min, + safe_numerics_error::negative_overflow_error, // greater_than_max, + safe_numerics_error::range_error, // indeterminate, + //}, + // t == less_than_min, + //{ + // u == ... + safe_numerics_error::negative_overflow_error, // known_value, + safe_numerics_error::range_error, // less_than_min, + safe_numerics_error::negative_overflow_error, // greater_than_max, + safe_numerics_error::range_error, // indeterminate, + //}, + // t == greater_than_max, + //{ + // u == ... + safe_numerics_error::positive_overflow_error, // known_value, + safe_numerics_error::positive_overflow_error, // less_than_min, + safe_numerics_error::range_error, // greater_than_max, + safe_numerics_error::range_error, // indeterminate, + //}, + // t == indeterminate, + //{ + // u == ... + safe_numerics_error::range_error, // known_value, + safe_numerics_error::range_error, // less_than_min, + safe_numerics_error::range_error, // greater_than_max, + safe_numerics_error::range_error, // indeterminate, + //}, + }; + + const value_type tx(t); + const value_type ux(u); + + const safe_numerics_error e = result[tx * order + ux]; + if(safe_numerics_error::success == e) + return checked::subtract(t, u); + return checked_result(e, "subtraction result"); +} + +// unary - +template +typename std::enable_if< + std::is_integral::value, + checked_result +>::type +constexpr inline operator-( + const checked_result & t +){ +// assert(false); + return checked_result(0) - t; +} + +struct product_value_type { + // characterization of various values + const enum flag { + less_than_min = 0, + less_than_zero, + zero, + greater_than_zero, + greater_than_max, + indeterminate, + // count of number of cases for values + count, + // temporary values for special cases + t_value, + u_value, + z_value + } m_flag; + template + constexpr flag to_flag(const checked_result & t) const { + switch(static_cast(t)){ + case safe_numerics_error::success: + return (t < checked_result(0)) + ? less_than_zero + : (t > checked_result(0)) + ? greater_than_zero + : zero; + case safe_numerics_error::negative_overflow_error: + // result is below representational minimum + return less_than_min; + case safe_numerics_error::positive_overflow_error: + // result is above representational maximum + return greater_than_max; + default: + return indeterminate; + } + } + template + constexpr product_value_type(const checked_result & t) : + m_flag(to_flag(t)) + {} + constexpr operator std::uint8_t () const { + return static_cast(m_flag); + } +}; + +// integers multiplication +template +typename std::enable_if< + std::is_integral::value, + checked_result +>::type +constexpr inline operator*( + const checked_result & t, + const checked_result & u +){ + using value_type = product_value_type; + const std::uint8_t order = static_cast(value_type::count); + + constexpr const enum value_type::flag result[order * order] = { + // t == less_than_min + //{ + // u == ... + value_type::greater_than_max, // less_than_min, + value_type::greater_than_max, // less_than_zero, + value_type::zero, // zero, + value_type::less_than_min, // greater_than_zero, + value_type::less_than_min, // greater than max, + value_type::indeterminate, // indeterminate, + //}, + // t == less_than_zero, + //{ + // u == ... + value_type::greater_than_max, // less_than_min, + value_type::greater_than_zero, // less_than_zero, + value_type::zero, // zero, + value_type::less_than_zero, // greater_than_zero, + value_type::less_than_min, // greater than max, + value_type::indeterminate, // indeterminate, + //}, + // t == zero, + //{ + // u == ... + value_type::zero, // less_than_min, + value_type::zero, // less_than_zero, + value_type::zero, // zero, + value_type::zero, // greater_than_zero, + value_type::zero, // greater than max, + value_type::indeterminate, // indeterminate, + //}, + // t == greater_than_zero, + //{ + // u == ... + value_type::less_than_min, // less_than_min, + value_type::less_than_zero, // less_than_zero, + value_type::zero, // zero, + value_type::greater_than_zero, // greater_than_zero, + value_type::greater_than_max, // greater than max, + value_type::indeterminate, // indeterminate, + //}, + // t == greater_than_max + //{ + value_type::less_than_min, // less_than_min, + value_type::less_than_min, // less_than_zero, + value_type::zero, // zero, + value_type::greater_than_max, // greater_than_zero, + value_type::greater_than_max, // greater than max, + value_type::indeterminate, // indeterminate, + //}, + // t == indeterminate + //{ + value_type::indeterminate, // less_than_min, + value_type::indeterminate, // less_than_zero, + value_type::indeterminate, // zero, + value_type::indeterminate, // greater_than_zero, + value_type::indeterminate, // greater than max, + value_type::indeterminate, // indeterminate, + //} + }; + + const value_type tx(t); + const value_type ux(u); + + switch(result[tx * order + ux]){ + case value_type::less_than_min: + return safe_numerics_error::negative_overflow_error; + case value_type::zero: + return T(0); + case value_type::greater_than_max: + return safe_numerics_error::positive_overflow_error; + case value_type::less_than_zero: + case value_type::greater_than_zero: + return checked::multiply(t, u); + case value_type::indeterminate: + return safe_numerics_error::range_error; + default: + assert(false); + } + return checked_result(0); // to suppress msvc warning +} + +// integers division +template +typename std::enable_if< + std::is_integral::value, + checked_result +>::type +constexpr inline operator/( + const checked_result & t, + const checked_result & u +){ + using value_type = product_value_type; + const std::uint8_t order = static_cast(value_type::count); + + constexpr const enum value_type::flag result[order * order] = { + // t == less_than_min + //{ + // u == ... + value_type::indeterminate, // less_than_min, + value_type::greater_than_max, // less_than_zero, + value_type::less_than_min, // zero, + value_type::less_than_min, // greater_than_zero, + value_type::less_than_min, // greater than max, + value_type::indeterminate, // indeterminate, + //}, + // t == less_than_zero, + //{ + // u == ... + value_type::zero, // less_than_min, + value_type::greater_than_zero, // less_than_zero, + value_type::less_than_min, // zero, + value_type::less_than_zero, // greater_than_zero, + value_type::zero, // greater than max, + value_type::indeterminate, // indeterminate, + //}, + // t == zero, + //{ + // u == ... + value_type::zero, // less_than_min, + value_type::zero, // less_than_zero, + value_type::indeterminate, // zero, + value_type::zero, // greater_than_zero, + value_type::zero, // greater than max, + value_type::indeterminate, // indeterminate, + //}, + // t == greater_than_zero, + //{ + // u == ... + value_type::zero, // less_than_min, + value_type::less_than_zero, // less_than_zero, + value_type::greater_than_max, // zero, + value_type::greater_than_zero, // greater_than_zero, + value_type::zero, // greater than max, + value_type::indeterminate, // indeterminate, + //}, + // t == greater_than_max + //{ + value_type::less_than_min, // less_than_min, + value_type::less_than_min, // less_than_zero, + value_type::greater_than_max, // zero, + value_type::greater_than_max, // greater_than_zero, + value_type::indeterminate, // greater than max, + value_type::indeterminate, // indeterminate, + //}, + // t == indeterminate + //{ + value_type::indeterminate, // less_than_min, + value_type::indeterminate, // less_than_zero, + value_type::indeterminate, // zero, + value_type::indeterminate, // greater_than_zero, + value_type::indeterminate, // greater than max, + value_type::indeterminate, // indeterminate, + //} + }; + + const value_type tx(t); + const value_type ux(u); + + switch(result[tx * order + ux]){ + case value_type::less_than_min: + return safe_numerics_error::negative_overflow_error; + case value_type::zero: + return 0; + case value_type::greater_than_max: + return safe_numerics_error::positive_overflow_error; + case value_type::less_than_zero: + case value_type::greater_than_zero: + return checked::divide(t, u); + case value_type::indeterminate: + return safe_numerics_error::range_error; + default: + assert(false); + } + return checked_result(0); // to suppress msvc warning +} + +// integers modulus +template +typename std::enable_if< + std::is_integral::value, + checked_result +>::type +constexpr inline operator%( + const checked_result & t, + const checked_result & u +){ + using value_type = product_value_type; + const std::uint8_t order = static_cast(value_type::count); + + constexpr const enum value_type::flag result[order * order] = { + // t == less_than_min + //{ + // u == ... + value_type::indeterminate, // less_than_min, + value_type::z_value, // less_than_zero, + value_type::indeterminate, // zero, + value_type::z_value, // greater_than_zero, + value_type::indeterminate, // greater than max, + value_type::indeterminate, // indeterminate, + //}, + // t == less_than_zero, + //{ + // u == ... + value_type::t_value, // less_than_min, + value_type::greater_than_zero, // less_than_zero, + value_type::indeterminate, // zero, + value_type::less_than_zero, // greater_than_zero, + value_type::t_value, // greater than max, + value_type::indeterminate, // indeterminate, + //}, + // t == zero, + //{ + // u == ... + value_type::zero, // less_than_min, + value_type::zero, // less_than_zero, + value_type::indeterminate, // zero, + value_type::zero, // greater_than_zero, + value_type::zero, // greater than max, + value_type::indeterminate, // indeterminate, + //}, + // t == greater_than_zero, + //{ + // u == ... + value_type::t_value, // less_than_min, + value_type::less_than_zero, // less_than_zero, + value_type::indeterminate, // zero, + value_type::greater_than_zero, // greater_than_zero, + value_type::t_value, // greater than max, + value_type::indeterminate, // indeterminate, + //}, + // t == greater_than_max + //{ + value_type::indeterminate, // less_than_min, + value_type::u_value, // less_than_zero, + value_type::indeterminate, // zero, + value_type::u_value, // greater_than_zero, + value_type::indeterminate, // greater than max, + value_type::indeterminate, // indeterminate, + //}, + // t == indeterminate + //{ + value_type::indeterminate, // less_than_min, + value_type::indeterminate, // less_than_zero, + value_type::indeterminate, // zero, + value_type::indeterminate, // greater_than_zero, + value_type::indeterminate, // greater than max, + value_type::indeterminate, // indeterminate, + //} + }; + + const value_type tx(t); + const value_type ux(u); + + switch(result[tx * order + ux]){ + case value_type::zero: + return 0; + case value_type::less_than_zero: + case value_type::greater_than_zero: + return checked::modulus(t, u); + case value_type::indeterminate: + return safe_numerics_error::range_error; + case value_type::t_value: + return t; + case value_type::u_value: + return checked::subtract(u, 1); + case value_type::z_value: + return checked::subtract(1, u); + case value_type::greater_than_max: + case value_type::less_than_min: + default: + assert(false); + } + // suppress msvc warning + return checked_result(0); +} + +// comparison operators + +template +constexpr boost::logic::tribool operator<( + const checked_result & t, + const checked_result & u +){ + using value_type = sum_value_type; + constexpr const std::uint8_t order = static_cast(value_type::count); + + // the question arises about how to order values of type greater_than_min. + // that is: what should greater_than_min < greater_than_min return. + // + // a) return indeterminate because we're talking about the "true" values for + // which greater_than_min is a placholder. + // + // b) return false because the two values are "equal" + // + // for our purposes, a) seems the better interpretation. + + enum class result_type : std::uint8_t { + runtime, + false_value, + true_value, + indeterminate, + }; + constexpr const result_type resultx[order * order]{ + // t == known_value + //{ + // u == ... + result_type::runtime, // known_value, + result_type::false_value, // less_than_min, + result_type::true_value, // greater_than_max, + result_type::indeterminate, // indeterminate, + //}, + // t == less_than_min + //{ + // u == ... + result_type::true_value, // known_value, + result_type::indeterminate, // less_than_min, see above argument + result_type::true_value, // greater_than_max, + result_type::indeterminate, // indeterminate, + //}, + // t == greater_than_max + //{ + // u == ... + result_type::false_value, // known_value, + result_type::false_value, // less_than_min, + result_type::indeterminate, // greater_than_max, see above argument + result_type::indeterminate, // indeterminate, + //}, + // t == indeterminate + //{ + // u == ... + result_type::indeterminate, // known_value, + result_type::indeterminate, // less_than_min, + result_type::indeterminate, // greater_than_max, + result_type::indeterminate, // indeterminate, + //}, + }; + + const value_type tx(t); + const value_type ux(u); + + switch(resultx[tx * order + ux]){ + case result_type::runtime: + return static_cast(t) < static_cast(u); + case result_type::false_value: + return false; + case result_type::true_value: + return true; + case result_type::indeterminate: + return boost::logic::indeterminate; + default: + assert(false); + } + return true; +} + +template +constexpr boost::logic::tribool +operator>=( + const checked_result & t, + const checked_result & u +){ + return !(t < u); +} + +template +constexpr boost::logic::tribool +operator>( + const checked_result & t, + const checked_result & u +){ + return u < t; +} + +template +constexpr boost::logic::tribool +operator<=( + const checked_result & t, + const checked_result & u +){ + return !(u < t); +} + +template +constexpr boost::logic::tribool +operator==( + const checked_result & t, + const checked_result & u +){ + using value_type = sum_value_type; + constexpr const std::uint8_t order = static_cast(value_type::count); + + enum class result_type : std::uint8_t { + runtime, + false_value, + true_value, + indeterminate, + }; + + constexpr const result_type result[order * order]{ + // t == known_value + //{ + // u == ... + result_type::runtime, // known_value, + result_type::false_value, // less_than_min, + result_type::false_value, // greater_than_max, + result_type::indeterminate, // indeterminate, + //}, + // t == less_than_min + //{ + // u == ... + result_type::false_value, // known_value, + result_type::indeterminate, // less_than_min, + result_type::false_value, // greater_than_max, + result_type::indeterminate, // indeterminate, + //}, + // t == greater_than_max + //{ + // u == ... + result_type::false_value, // known_value, + result_type::false_value, // less_than_min, + result_type::indeterminate, // greater_than_max, + result_type::indeterminate, // indeterminate, + //}, + // t == indeterminate + //{ + // u == ... + result_type::indeterminate, // known_value, + result_type::indeterminate, // less_than_min, + result_type::indeterminate, // greater_than_max, + result_type::indeterminate, // indeterminate, + //}, + }; + + const value_type tx(t); + const value_type ux(u); + + switch(result[tx * order + ux]){ + case result_type::runtime: + return static_cast(t) == static_cast(u); + case result_type::false_value: + return false; + case result_type::true_value: + return true; + case result_type::indeterminate: + return boost::logic::indeterminate; + default: + assert(false); + } + // suppress msvc warning - not all control paths return a value + return false; +} + +template +constexpr boost::logic::tribool +operator!=( + const checked_result & t, + const checked_result & u +){ + return ! (t == u); +} + +template +typename std::enable_if< + std::is_integral::value, + checked_result +>::type +constexpr inline operator>>( + const checked_result & t, + const checked_result & u +); + +template +typename std::enable_if< + std::is_integral::value, + checked_result +>::type +constexpr inline operator~( + const checked_result & t +){ +// assert(false); + return ~t.m_r; +} + +template +typename std::enable_if< + std::is_integral::value, + checked_result +>::type +constexpr inline operator<<( + const checked_result & t, + const checked_result & u +){ + using value_type = product_value_type; + const std::uint8_t order = static_cast(value_type::count); + + constexpr const std::uint8_t result[order * order] = { + // t == less_than_min + //{ + // u == ... + 1, // -1, // less_than_min, + 2, // safe_numerics_error::negative_overflow_error, // less_than_zero, + 2, // safe_numerics_error::negative_overflow_error, // zero, + 2, // safe_numerics_error::negative_overflow_error, // greater_than_zero, + 2, // safe_numerics_error::negative_overflow_error, // greater than max, + 1, // safe_numerics_error::range_error, // indeterminate, + //}, + // t == less_than_zero, + //{ + // u == ... + 3, // -1, // less_than_min, + 4, // - (-t >> -u), // less_than_zero, + 5, // safe_numerics_error::negative_overflow_error, // zero, + 6, // - (-t << u), // greater_than_zero, + 2, // safe_numerics_error::negative_overflow_error, // greater than max, + 1, // safe_numerics_error::range_error, // indeterminate, + //}, + // t == zero, + //{ + // u == ... + 3, // 0 // less_than_min, + 3, // 0 // less_than_zero, + 3, // 0, // zero, + 3, // 0, // greater_than_zero, + 3, // 0, // greater than max, + 3, // safe_numerics_error::range_error, // indeterminate, + //}, + // t == greater_than_zero, + //{ + // u == ... + 3, // 0, // less_than_min, + 7, // t << -u, // less_than_zero, + 5, // t, // zero, + 8, // t << u // greater_than_zero, + 9, // safe_numerics_error::positive_overflow_error, // greater than max, + 1, // safe_numerics_error::range_error, // indeterminate, + //}, + // t == greater_than_max + //{ + // u == ... + 1, // safe_numerics_error::range_error, // less_than_min, + 9, // safe_numerics_error::positive_overflow_error), // less_than_zero, + 9, // safe_numerics_error::positive_overflow_error, // zero, + 9, // safe_numerics_error::positive_overflow_error), // greater_than_zero, + 9, // safe_numerics_error::positive_overflow_error, // greater than max, + 1, // safe_numerics_error::range_error, // indeterminate, + //}, + // t == indeterminate + //{ + 1, // safe_numerics_error::range_error, // indeterminate, + 1, // safe_numerics_error::range_error, // indeterminate, + 1, // safe_numerics_error::range_error, // indeterminate, + 1, // safe_numerics_error::range_error, // indeterminate, + 1, // safe_numerics_error::range_error, // indeterminate, + 1, // safe_numerics_error::range_error, // indeterminate, + //} + }; + + const value_type tx(t); + const value_type ux(u); + assert(tx * order + ux < order * order); + + // I had a switch(i) statment here - but it results in an ICE + // on multiple versions of gcc. So make the equivalent in + // nested if statments - should be the same (more or less) + // performancewise. + const unsigned int i = result[tx * order + ux]; + assert(i <= 9); + if(1 == i){ + return safe_numerics_error::range_error; + } + else + if(2 == i){ + return safe_numerics_error::negative_overflow_error; + } + else + if(3 == i){ + return checked_result(0); + // the following gymnastics are to handle the case where + // a value is changed from a negative to a positive number. + // For example, and 8 bit number t == -128. Then -t also + // equals -128 since 128 cannot be held in an 8 bit signed + // integer. + } + else + if(4 == i){ // - (-t >> -u) + assert(static_cast(t < checked_result(0))); + assert(static_cast(u < checked_result(0))); + return t >> -u; + } + else + if(5 == i){ + return t; + } + else + if(6 == i){ // - (-t << u) + assert(static_cast(t < checked_result(0))); + assert(static_cast(u > checked_result(0))); + const checked_result temp_t = t * checked_result(2); + const checked_result temp_u = u - checked_result(1); + return - (-temp_t << temp_u); + } + else + if(7 == i){ // t >> -u + assert(static_cast(t > checked_result(0))); + assert(static_cast(u < checked_result(0))); + return t >> -u; + } + else + if(8 == i){ // t << u + assert(static_cast(t > checked_result(0))); + assert(static_cast(u > checked_result(0))); + checked_result r = checked::left_shift(t, u); + return (r.m_e == safe_numerics_error::shift_too_large) + ? checked_result(safe_numerics_error::positive_overflow_error) + : r; + } + else + if(9 == i){ + return safe_numerics_error::positive_overflow_error; + } + else{ + assert(false); + }; + return checked_result(0); // to suppress msvc warning +} + +template +typename std::enable_if< + std::is_integral::value, + checked_result +>::type +constexpr inline operator>>( + const checked_result & t, + const checked_result & u +){ + using value_type = product_value_type; + const std::uint8_t order = static_cast(value_type::count); + + const std::uint8_t result[order * order] = { + // t == less_than_min + //{ + // u == ... + 2, // safe_numerics_error::negative_overflow_error, // less_than_min, + 2, // safe_numerics_error::negative_overflow_error, // less_than_zero, + 2, // safe_numerics_error::negative_overflow_error, // zero, + 2, // safe_numerics_error::negative_overflow_error, // greater_than_zero, + 1, // safe_numerics_error::range_error, // greater than max, + 1, // safe_numerics_error::range_error, // indeterminate, + //}, + // t == less_than_zero, + //{ + // u == ... + 2, // safe_numerics_error::negative_overflow_error // less_than_min, + 4, // - (-t << -u), // less_than_zero, + 5, // safe_numerics_error::negative_overflow_error. // zero, + 6, // - (-t >> u), // greater_than_zero, + 3, // 0, ? or -1 // greater than max, + 1, // safe_numerics_error::range_error, // indeterminate, + //}, + // t == zero, + //{ + // u == ... + 3, // 0 // less_than_min, + 3, // 0 // less_than_zero, + 3, // 0, // zero, + 3, // 0, // greater_than_zero, + 3, // 0, // greater than max, + 3, // safe_numerics_error::range_error, // indeterminate, + //}, + // t == greater_than_zero, + //{ + // u == ... + 9, // safe_numerics_error::positive_overflow_error // less_than_min, + 7, // t << -u, // less_than_zero, + 5, // t, // zero, + 8, // t >> u // greater_than_zero, + 3, // 0, // greater than max, + 1, // safe_numerics_error::range_error, // indeterminate, + //}, + // t == greater_than_max + //{ + // u == ... + 9, // safe_numerics_error::positive_overflow_error, // less_than_min, + 9, // safe_numerics_error::positive_overflow_error, // less_than_zero, + 9, // safe_numerics_error::positive_overflow_error, // zero, + 9, // safe_numerics_error::positive_overflow_error, // greater_than_zero, + 1, // safe_numerics_error::range_error, // greater than max, + 1, // safe_numerics_error::range_error, // indeterminate, + //}, + // t == indeterminate + //{ + 1, // safe_numerics_error::range_error, // indeterminate, + 1, // safe_numerics_error::range_error, // indeterminate, + 1, // safe_numerics_error::range_error, // indeterminate, + 1, // safe_numerics_error::range_error, // indeterminate, + 1, // safe_numerics_error::range_error, // indeterminate, + 1, // safe_numerics_error::range_error, // indeterminate, + //} + }; + + const value_type tx(t); + const value_type ux(u); + assert(tx * order + ux < order * order); + + // I had a switch(i) statment here - but it results in an ICE + // on multiple versions of gcc. So make the equivalent in + // nested if statments - should be the same (more or less) + // performancewise. + const unsigned int i = result[tx * order + ux]; + assert(i <= 9); + if(1 == i){ + return safe_numerics_error::range_error; + } + else + if(2 == i){ + return safe_numerics_error::negative_overflow_error; + } + else + if(3 == i){ + return checked_result(0); + } + else + if(4 == i){ // - (-t << -u) + assert(static_cast(t < checked_result(0))); + assert(static_cast(u < checked_result(0))); + return t << -u; + } + else + if(5 == i){ + return t; + } + else + if(6 == i){ // - (-t >> u) + assert(static_cast(t < checked_result(0))); + assert(static_cast(u > checked_result(0))); + const checked_result temp_t = t / checked_result(2); + const checked_result temp_u = u - checked_result(1); + return - (-temp_t >> temp_u); + } + else + if(7 == i){ // t << -u, + assert(static_cast(t > checked_result(0))); + assert(static_cast(u < checked_result(0))); + return t << -u; + } + else + if(8 == i){ // t >> u + assert(static_cast(t > checked_result(0))); + assert(static_cast(u > checked_result(0))); + checked_result r = checked::right_shift(t, u); + return (r.m_e == safe_numerics_error::shift_too_large) + ? checked_result(0) + : r; + } + else + if(9 == i){ + return safe_numerics_error::positive_overflow_error; + } + else{ + assert(false); + }; + return checked_result(0); // to suppress msvc warning +} + +template +typename std::enable_if< + std::is_integral::value, + checked_result +>::type +constexpr inline operator|( + const checked_result & t, + const checked_result & u +){ + return + t.exception() || u.exception() + ? checked_result(safe_numerics_error::range_error) + : checked::bitwise_or( + static_cast(t), + static_cast(u) + ); +} +template +typename std::enable_if< + std::is_integral::value, + checked_result +>::type +constexpr inline operator^( + const checked_result & t, + const checked_result & u +){ + return + t.exception() || u.exception() + ? checked_result(safe_numerics_error::range_error) + : checked::bitwise_xor( + static_cast(t), + static_cast(u) + ); +} + +template +typename std::enable_if< + std::is_integral::value, + checked_result +>::type +constexpr inline operator&( + const checked_result & t, + const checked_result & u +){ + return + t.exception() || u.exception() + ? checked_result(safe_numerics_error::range_error) + : checked::bitwise_and( + static_cast(t), + static_cast(u) + ); +} + +} // safe_numerics +} // boost + +#include + +namespace std { + +template +inline std::basic_ostream & operator<<( + std::basic_ostream & os, + const boost::safe_numerics::checked_result & r +){ + if(!r.exception()) + os << static_cast(r); + else + os << std::error_code(r.m_e).message() << ':' << r.m_msg; + return os; +} + +template +inline std::basic_ostream & operator<<( + std::basic_ostream & os, + const boost::safe_numerics::checked_result & r +){ + if(! r.exception()) + os << static_cast(r); + else + os << std::error_code(r.m_e).message() << ':' << r.m_msg; + return os; +} + +template +inline std::basic_ostream & operator<<( + std::basic_ostream & os, + const boost::safe_numerics::checked_result & r +){ + if(! r.exception()) + os << static_cast(r); + else + os << std::error_code(r.m_e).message() << ':' << r.m_msg; + return os; +} + +template +inline std::basic_istream & operator>>( + std::basic_istream & is, + boost::safe_numerics::checked_result & r +){ + is >> r.m_r; + return is; +} + +template +inline std::basic_istream & operator>>( + std::basic_istream & is, + boost::safe_numerics::checked_result & r +){ + std::int16_t i; + is >> i; + r.m_r = i; + return is; +} + +template +inline std::basic_istream & operator>>( + std::basic_istream & is, + boost::safe_numerics::checked_result & r +){ + std::uint16_t i; + is >> i; + r.m_r = i; + return is; +} + +} // std + +///////////////////////////////////////////////////////////////// +// numeric limits for checked + +#include + +namespace std { + +template +class numeric_limits > + : public std::numeric_limits +{ + using this_type = boost::safe_numerics::checked_result; +public: + constexpr static this_type min() noexcept { + return this_type(std::numeric_limits::min()); + } + constexpr static this_type max() noexcept { + return this_type(std::numeric_limits::max()); + } +}; + +} // std + +#endif // BOOST_NUMERIC_CHECKED_RESULT_OPERATIONS diff --git a/boost/safe_numerics/concept/CMakeLists.txt b/boost/safe_numerics/concept/CMakeLists.txt new file mode 100644 index 0000000..dd2832b --- /dev/null +++ b/boost/safe_numerics/concept/CMakeLists.txt @@ -0,0 +1,14 @@ +#################### +# add include headers to IDE + +set(USE_FOLDERS TRUE) + +file(GLOB include_files + RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" + "${CMAKE_CURRENT_SOURCE_DIR}/*.hpp" +) +add_custom_target(concepts SOURCES ${include_files}) +set_target_properties(concepts PROPERTIES FOLDER "safe_numerics") + +# end headers in IDE +#################### diff --git a/boost/safe_numerics/concept/exception_policy.hpp b/boost/safe_numerics/concept/exception_policy.hpp new file mode 100644 index 0000000..134ed25 --- /dev/null +++ b/boost/safe_numerics/concept/exception_policy.hpp @@ -0,0 +1,29 @@ +#ifndef BOOST_NUMERIC_CONCEPT_EXCEPTION_POLICY_HPP +#define BOOST_NUMERIC_CONCEPT_EXCEPTION_POLICY_HPP + +// Copyright (c) 2015 Robert Ramey +// +// 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) + +namespace boost { +namespace safe_numerics { + +template +struct ExceptionPolicy { + const char * message; + /* + BOOST_CONCEPT_USAGE(ExceptionPolicy){ + EP::on_arithmetic_error(e, message); + EP::on_undefined_behavior(e, message) + EP::on_implementation_defined_behavior(e, message) + EP::on_uninitialized_value(e, message) + } + */ +}; + +} // safe_numerics +} // boost + +#endif // BOOST_NUMERIC_CONCEPT_EXCEPTION_POLICY_HPP diff --git a/boost/safe_numerics/concept/integer.hpp b/boost/safe_numerics/concept/integer.hpp new file mode 100644 index 0000000..79d0791 --- /dev/null +++ b/boost/safe_numerics/concept/integer.hpp @@ -0,0 +1,27 @@ +#ifndef BOOST_NUMERIC_CONCEPT_INTEGER_HPP +#define BOOST_NUMERIC_CONCEPT_INTEGER_HPP + +// Copyright (c) 2012 Robert Ramey +// +// 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) + +#include "numeric.hpp" + +namespace boost { +namespace safe_numerics { + +template +class Integer : public Numeric { + // integer types must have the corresponding numeric trait. + static_assert( + std::numeric_limits::is_integer, + "Fails to fulfill requirements for an integer type" + ); +}; + +} // safe_numerics +} // boost + +#endif // BOOST_NUMERIC_CONCEPT_INTEGER_HPP diff --git a/boost/safe_numerics/concept/numeric.hpp b/boost/safe_numerics/concept/numeric.hpp new file mode 100644 index 0000000..d7f0f34 --- /dev/null +++ b/boost/safe_numerics/concept/numeric.hpp @@ -0,0 +1,29 @@ +#ifndef BOOST_NUMERIC_CONCEPT_NUMERIC_HPP +#define BOOST_NUMERIC_CONCEPT_NUMERIC_HPP + +// Copyright (c) 2012 Robert Ramey +// +// 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) + +#include + +namespace boost { +namespace safe_numerics { + +template +struct Numeric { + // if your program traps here, you need to create a + // std::numeric_limits class for your type T. see + // see C++ standard 18.3.2.2 + static_assert( + std::numeric_limits::is_specialized, + "std::numeric_limits has not been specialized for this type" + ); +}; + +} // safe_numerics +} // boost + +#endif // BOOST_NUMERIC_CONCEPT_NUMERIC_HPP diff --git a/boost/safe_numerics/concept/promotion_policy.hpp b/boost/safe_numerics/concept/promotion_policy.hpp new file mode 100644 index 0000000..14bbb3c --- /dev/null +++ b/boost/safe_numerics/concept/promotion_policy.hpp @@ -0,0 +1,33 @@ +#ifndef BOOST_NUMERIC_CONCEPT_PROMOTION_POLICY_HPP +#define BOOST_NUMERIC_CONCEPT_PROMOTION_POLICY_HPP + +// Copyright (c) 2015 Robert Ramey +// +// 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) + +namespace boost { +namespace safe_numerics { + +template +struct PromotionPolicy { + using T = int; + using U = int; + using a_type = typename PP::template addition_result; + using s_type = typename PP::template subtraction_result; + using m_type = typename PP::template multiplication_result; + using d_type = typename PP::template division_result; + using mod_type = typename PP::template modulus_result; + using ls_type = typename PP::template left_shift_result; + using rs_type = typename PP::template right_shift_result; + using cc_type = typename PP::template comparison_result; + using baw_type = typename PP::template bitwise_and_result; + using bow_type = typename PP::template bitwise_or_result; + using bxw_type = typename PP::template bitwise_xor_result; +}; + +} // safe_numerics +} // boost + +#endif // BOOST_NUMERIC_CONCEPT_EXCEPTION_POLICY_HPP diff --git a/boost/safe_numerics/concept/safe_numeric.hpp b/boost/safe_numerics/concept/safe_numeric.hpp new file mode 100644 index 0000000..0b6ac12 --- /dev/null +++ b/boost/safe_numerics/concept/safe_numeric.hpp @@ -0,0 +1,34 @@ +#ifndef BOOST_NUMERIC_CONCEPT_SAFE_NUMERIC_HPP +#define BOOST_NUMERIC_CONCEPT_SAFE_NUMERIC_HPP + +// Copyright (c) 2015 Robert Ramey +// +// 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) + +#include +#include +#include +#include "concept/numeric.hpp" + +namespace boost { +namespace safe_numerics { + +template +struct SafeNumeric : public Numeric { + static_assert( + is_safe::value, + "std::numeric_limits has not been specialized for this type" + ); + BOOST_CONCEPT_USAGE(SafeNumeric){ + using t1 = get_exception_policy; + using t2 = get_promotion_policy; + using t3 = base_type; + } +}; + +} // safe_numerics +} // boost + +#endif // BOOST_NUMERIC_CONCEPT_SAFE_NUMERIC_HPP diff --git a/boost/safe_numerics/cpp.hpp b/boost/safe_numerics/cpp.hpp new file mode 100644 index 0000000..077fbfc --- /dev/null +++ b/boost/safe_numerics/cpp.hpp @@ -0,0 +1,197 @@ +#ifndef BOOST_NUMERIC_CPP_HPP +#define BOOST_NUMERIC_CPP_HPP + +// Copyright (c) 2012 Robert Ramey +// +// 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) + +// policy which creates results types equal to that of C++ promotions. +// Using the policy will permit the program to build and run in release +// mode which is identical to that in debug mode except for the fact +// that errors aren't trapped. + +#include // integral constant, remove_cv, conditional +#include +#include // integer type selection + +#include "safe_common.hpp" +#include "checked_result.hpp" + +namespace boost { +namespace safe_numerics { + +// in C++ the following rules govern integer arithmetic + +// This policy is use to emulate another compiler/machine architecture +// For example, a Z80 has 8 bit char, 16 bit short, 16 bit int, 32 bit long. So one +// would use cpp<8, 16, 16, 32, 32> to test programs destined to run on a Z80 + +// Follow section 5 of the standard. +template< + int CharBits, + int ShortBits, + int IntBits, + int LongBits, + int LongLongBits +> +struct cpp { +public: + using local_char_type = typename boost::int_t::exact; + using local_short_type = typename boost::int_t::exact; + using local_int_type = typename boost::int_t::exact; + using local_long_type = typename boost::int_t::exact; + using local_long_long_type = typename boost::int_t::exact; + + template + using rank = + typename std::conditional< + std::is_same::type>::value, + std::integral_constant, + typename std::conditional< + std::is_same::type>::value, + std::integral_constant, + typename std::conditional< + std::is_same::type>::value, + std::integral_constant, + typename std::conditional< + std::is_same::type>::value, + std::integral_constant, + typename std::conditional< + std::is_same::type>::value, + std::integral_constant, + std::integral_constant // catch all - never promote integral + >::type >::type >::type >::type >::type; + + // section 4.5 integral promotions + + // convert smaller of two types to the size of the larger + template + using higher_ranked_type = typename std::conditional< + (rank::value < rank::value), + U, + T + >::type; + + template + using copy_sign = typename std::conditional< + std::is_signed::value, + typename std::make_signed::type, + typename std::make_unsigned::type + >::type; + + template + using integral_promotion = copy_sign< + higher_ranked_type, + T + >; + + // note presumption that T & U don't have he same sign + // if that's not true, these won't work + template + using select_signed = typename std::conditional< + std::numeric_limits::is_signed, + T, + U + >::type; + + template + using select_unsigned = typename std::conditional< + std::numeric_limits::is_signed, + U, + T + >::type; + + // section 5 clause 11 - usual arithmetic conversions + template + using usual_arithmetic_conversions = + // clause 0 - if both operands have the same type + typename std::conditional< + std::is_same::value, + // no further conversion is needed + T, + // clause 1 - otherwise if both operands have the same sign + typename std::conditional< + std::numeric_limits::is_signed + == std::numeric_limits::is_signed, + // convert to the higher ranked type + higher_ranked_type, + // clause 2 - otherwise if the rank of he unsigned type exceeds + // the rank of the of the signed type + typename std::conditional< + rank>::value + >= rank< select_signed>::value, + // use unsigned type + select_unsigned, + // clause 3 - otherwise if the type of the signed integer type can + // represent all the values of the unsigned type + typename std::conditional< + std::numeric_limits< select_signed>::digits >= + std::numeric_limits< select_unsigned>::digits, + // use signed type + select_signed, + // clause 4 - otherwise use unsigned version of the signed type + std::make_signed< select_signed> + >::type >::type >::type + >; + + template + using result_type = typename usual_arithmetic_conversions< + integral_promotion::type>, + integral_promotion::type> + >::type; +public: + template + struct addition_result { + using type = result_type; + }; + template + struct subtraction_result { + using type = result_type; + }; + template + struct multiplication_result { + using type = result_type; + }; + template + struct division_result { + using type = result_type; + }; + template + struct modulus_result { + using type = result_type; + }; + // note: comparison_result (<, >, ...) is special. + // The return value is always a bool. The type returned here is + // the intermediate type applied to make the values comparable. + template + struct comparison_result { + using type = result_type; + }; + template + struct left_shift_result { + using type = result_type; + }; + template + struct right_shift_result { + using type = result_type; + }; + template + struct bitwise_and_result { + using type = result_type; + }; + template + struct bitwise_or_result { + using type = result_type; + }; + template + struct bitwise_xor_result { + using type = result_type; + }; +}; + +} // safe_numerics +} // boost + +#endif // BOOST_NUMERIC_cpp_HPP diff --git a/boost/safe_numerics/exception.hpp b/boost/safe_numerics/exception.hpp new file mode 100644 index 0000000..d7da5c2 --- /dev/null +++ b/boost/safe_numerics/exception.hpp @@ -0,0 +1,190 @@ +#ifndef BOOST_NUMERIC_EXCEPTION +#define BOOST_NUMERIC_EXCEPTION + +// Copyright (c) 2012 Robert Ramey +// +// 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) + +// contains error indicators for results of doing checked +// arithmetic on native C++ types + +#include +#include // error_code, system_error +#include +#include +#include // std::uint8_t + +// Using the system_error code facility. This facility is more complex +// than meets the eye. To fully understand what out intent here is, +// review http://blog.think-async.com/2010/04/system-error-support-in-c0x-part-5.html +// "Giving context-specific meaning to generic error codes" + +namespace boost { +namespace safe_numerics { + +// errors codes for safe numerics + +// in spite of the similarity, this list is distinct from the exceptions +// listed in documentation for std::exception. + +// note: Don't reorder these. Code in the file checked_result_operations.hpp +// depends upon this order !!! +enum class safe_numerics_error : std::uint8_t { + success = 0, + positive_overflow_error, // result is above representational maximum + negative_overflow_error, // result is below representational minimum + domain_error, // one operand is out of valid range + range_error, // result cannot be produced for this operation + precision_overflow_error, // result lost precision + underflow_error, // result is too small to be represented + negative_value_shift, // negative value in shift operator + negative_shift, // shift a negative value + shift_too_large, // l/r shift exceeds variable size + uninitialized_value // creating of uninitialized value +}; + +const std::uint8_t safe_numerics_casting_error_count = + static_cast(safe_numerics_error::domain_error) + 1; + +const std::uint8_t safe_numerics_error_count = + static_cast(safe_numerics_error::uninitialized_value) + 1; + +} // safe_numerics +} // boost + +namespace std { + template <> + struct is_error_code_enum + : public true_type {}; +} // std + +namespace boost { +namespace safe_numerics { + +const class : public std::error_category { +public: + virtual const char* name() const noexcept{ + return "safe numerics error"; + } + virtual std::string message(int ev) const { + switch(static_cast(ev)){ + case safe_numerics_error::success: + return "success"; + case safe_numerics_error::positive_overflow_error: + return "positive overflow error"; + case safe_numerics_error::negative_overflow_error: + return "negative overflow error"; + case safe_numerics_error::underflow_error: + return "underflow error"; + case safe_numerics_error::range_error: + return "range error"; + case safe_numerics_error::precision_overflow_error: + return "precision_overflow_error"; + case safe_numerics_error::domain_error: + return "domain error"; + case safe_numerics_error::negative_shift: + return "negative shift"; + case safe_numerics_error::negative_value_shift: + return "negative value shift"; + case safe_numerics_error::shift_too_large: + return "shift too large"; + case safe_numerics_error::uninitialized_value: + return "uninitialized value"; + default: + assert(false); + } + return ""; // suppress bogus warning + } +} safe_numerics_error_category {}; + +// constexpr - damn, can't use constexpr due to std::error_code +inline std::error_code make_error_code(const safe_numerics_error & e){ + return std::error_code(static_cast(e), safe_numerics_error_category); +} + +// actions for error_codes for safe numerics. I've leveraged on +// error_condition in order to do this. I'm not sure this is a good +// idea or not. + +enum class safe_numerics_actions { + no_action = 0, + uninitialized_value, + arithmetic_error, + implementation_defined_behavior, + undefined_behavior +}; + +} // safe_numerics +} // boost + +namespace std { + template <> + struct is_error_condition_enum + : public true_type {}; +} // std + +namespace boost { +namespace safe_numerics { + +const class : public std::error_category { +public: + virtual const char* name() const noexcept { + return "safe numerics error group"; + } + virtual std::string message(int) const { + return "safe numerics error group"; + } + // return true if a given error code corresponds to a + // given safe numeric action + virtual bool equivalent( + const std::error_code & code, + int condition + ) const noexcept { + if(code.category() != safe_numerics_error_category) + return false; + switch (static_cast(condition)){ + case safe_numerics_actions::no_action: + return code == safe_numerics_error::success; + case safe_numerics_actions::uninitialized_value: + return code == safe_numerics_error::uninitialized_value; + case safe_numerics_actions::arithmetic_error: + return code == safe_numerics_error::positive_overflow_error + || code == safe_numerics_error::negative_overflow_error + || code == safe_numerics_error::underflow_error + || code == safe_numerics_error::range_error + || code == safe_numerics_error::domain_error; + case safe_numerics_actions::implementation_defined_behavior: + return code == safe_numerics_error::negative_value_shift + || code == safe_numerics_error::negative_shift + || code == safe_numerics_error::shift_too_large; + case safe_numerics_actions::undefined_behavior: + return false; + default: + ; + } + // should never arrive here + assert(false); + // suppress bogus warning + return false; + } +} safe_numerics_actions_category {}; + +// the following function is used to "finish" implementation of conversion +// of safe_numerics_error to std::error_condition. At least for now, this +// isn't being used and defining here it can lead duplicate symbol errors +// depending on the compiler. So suppress it until further notice +#if 0 +std::error_condition make_error_condition(const safe_numerics_error & e) { + return std::error_condition( + static_cast(e), + safe_numerics_error_category + ); +} +#endif + +} // safe_numerics +} // boost + +#endif // BOOST_NUMERIC_CHECKED_RESULT diff --git a/boost/safe_numerics/exception_policies.hpp b/boost/safe_numerics/exception_policies.hpp new file mode 100644 index 0000000..e07f7e6 --- /dev/null +++ b/boost/safe_numerics/exception_policies.hpp @@ -0,0 +1,230 @@ +#ifndef BOOST_NUMERIC_EXCEPTION_POLICIES_HPP +#define BOOST_NUMERIC_EXCEPTION_POLICIES_HPP + +// Copyright (c) 2015 Robert Ramey +// +// 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) + +#include +#include // BOOST_NO_EXCEPTIONS +#include "exception.hpp" + +namespace boost { +namespace safe_numerics { + +template< + typename AE, + typename IDB, + typename UB, + typename UV +> +struct exception_policy { + static constexpr void on_arithmetic_error( + const safe_numerics_error & e, + const char * msg + ){ + AE(e, msg); + } + static constexpr void on_implementation_defined_behavior( + const safe_numerics_error & e, + const char * msg + ){ + IDB(e, msg); + } + static constexpr void on_undefined_behavior( + const safe_numerics_error & e, + const char * msg + ){ + UB(e, msg); + } + static constexpr void on_uninitialized_value( + const safe_numerics_error & e, + const char * msg + ){ + UV(e, msg); + } +}; + +//////////////////////////////////////////////////////////////////////////////// +// pre-made error action handers + +// ignore any error and just return. +struct ignore_exception { + constexpr ignore_exception(const safe_numerics_error &, const char * ){} +}; + +// emit compile time error if this is invoked. +struct trap_exception { +#if 1 + //constexpr trap_exception(const safe_numerics_error & e, const char * ); + trap_exception() = delete; + trap_exception(const trap_exception &) = delete; + trap_exception(trap_exception &&) = delete; +#endif +}; + +// If an exceptional condition is detected at runtime throw the exception. +struct throw_exception { + #ifndef BOOST_NO_EXCEPTIONS + throw_exception(const safe_numerics_error & e, const char * message){ + throw std::system_error(std::error_code(e), message); + } + #else + trap_exception(const safe_numerics_error & e, const char * message); + #endif +}; + +// given an error code - return the action code which it corresponds to. +constexpr safe_numerics_actions +make_safe_numerics_action(const safe_numerics_error & e){ + // we can't use standard algorithms since we want this to be constexpr + // this brute force solution is simple and pretty fast anyway + switch(e){ + case safe_numerics_error::negative_overflow_error: + case safe_numerics_error::underflow_error: + case safe_numerics_error::range_error: + case safe_numerics_error::domain_error: + case safe_numerics_error::positive_overflow_error: + case safe_numerics_error::precision_overflow_error: + return safe_numerics_actions::arithmetic_error; + + case safe_numerics_error::negative_value_shift: + case safe_numerics_error::negative_shift: + case safe_numerics_error::shift_too_large: + return safe_numerics_actions::implementation_defined_behavior; + + case safe_numerics_error::uninitialized_value: + return safe_numerics_actions::uninitialized_value; + + case safe_numerics_error::success: + return safe_numerics_actions::no_action; + default: + assert(false); + } + // should never arrive here + //include to suppress bogus warning + return safe_numerics_actions::no_action; +} + +//////////////////////////////////////////////////////////////////////////////// +// compile time error dispatcher + +// note slightly baroque implementation of a compile time switch statement +// which instatiates only those cases which are actually invoked. This is +// motivated to implement the "trap" functionality which will generate a syntax +// error if and only a function which might fail is called. + +namespace dispatch_switch { + + template + struct dispatch_case {}; + + template + struct dispatch_case { + constexpr static void invoke(const safe_numerics_error & e, const char * msg){ + EP::on_uninitialized_value(e, msg); + } + }; + template + struct dispatch_case { + constexpr static void invoke(const safe_numerics_error & e, const char * msg){ + EP::on_arithmetic_error(e, msg); + } + }; + template + struct dispatch_case { + constexpr static void invoke(const safe_numerics_error & e, const char * msg){ + EP::on_implementation_defined_behavior(e, msg); + } + }; + template + struct dispatch_case { + constexpr static void invoke(const safe_numerics_error & e, const char * msg){ + EP::on_undefined_behavior(e, msg); + } + }; + +} // dispatch_switch + +template +constexpr void +dispatch(const char * msg){ + constexpr safe_numerics_actions a = make_safe_numerics_action(E); + dispatch_switch::dispatch_case::invoke(E, msg); +} + +template +class dispatch_and_return { +public: + template + constexpr static checked_result invoke( + char const * const & msg + ) { + dispatch(msg); + return checked_result(E, msg); + } +}; + +//////////////////////////////////////////////////////////////////////////////// +// pre-made error policy classes + +// loose exception +// - throw on arithmetic errors +// - ignore other errors. +// Some applications ignore these issues and still work and we don't +// want to update them. +using loose_exception_policy = exception_policy< + throw_exception, // arithmetic error + ignore_exception, // implementation defined behavior + ignore_exception, // undefined behavior + ignore_exception // uninitialized value +>; + +// loose trap +// same as above in that it doesn't check for various undefined behaviors +// but traps at compile time for hard arithmetic errors. This policy +// would be suitable for older embedded systems which depend on +// bit manipulation operations to work. +using loose_trap_policy = exception_policy< + trap_exception, // arithmetic error + ignore_exception, // implementation defined behavior + ignore_exception, // undefined behavior + ignore_exception // uninitialized value +>; + +// strict exception +// - throw at runtime on any kind of error +// recommended for new code. Check everything at compile time +// if possible and runtime if necessary. Trap or Throw as +// appropriate. Should guarantee code to be portable across +// architectures. +using strict_exception_policy = exception_policy< + throw_exception, + throw_exception, + throw_exception, + ignore_exception +>; + +// strict trap +// Same as above but requires code to be written in such a way as to +// make it impossible for errors to occur. This naturally will require +// extra coding effort but might be justified for embedded and/or +// safety critical systems. +using strict_trap_policy = exception_policy< + trap_exception, + trap_exception, + trap_exception, + trap_exception +>; + +// default policy +// One would use this first. After experimentation, one might +// replace some actions with ignore_exception +using default_exception_policy = strict_exception_policy; + +} // namespace safe_numerics +} // namespace boost + +#endif // BOOST_NUMERIC_EXCEPTION_POLICIES_HPP diff --git a/boost/safe_numerics/interval.hpp b/boost/safe_numerics/interval.hpp new file mode 100644 index 0000000..4c8fc3c --- /dev/null +++ b/boost/safe_numerics/interval.hpp @@ -0,0 +1,310 @@ +#ifndef BOOST_NUMERIC_INTERVAL_HPP +#define BOOST_NUMERIC_INTERVAL_HPP + +// Copyright (c) 2012 Robert Ramey +// +// 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) + +#include +#include +#include +#include +#include // minmax, min, max + +#include + +#include "utility.hpp" // log + +// from stack overflow +// http://stackoverflow.com/questions/23815138/implementing-variadic-min-max-functions + +namespace boost { +namespace safe_numerics { + +template +struct interval { + const R l; + const R u; + + template + constexpr interval(const T & lower, const T & upper) : + l(lower), + u(upper) + { + // assert(static_cast(l <= u)); + } + template + constexpr interval(const std::pair & p) : + l(p.first), + u(p.second) + {} + template + constexpr interval(const interval & rhs) : + l(rhs.l), + u(rhs.u) + {} + + constexpr interval(); + + // return true if this interval contains the given point + constexpr tribool includes(const R & t) const { + return l <= t && t <= u; + } + // if this interval contains every point found in some other inteval t + // return true + // otherwise + // return false or indeterminate + constexpr tribool includes(const interval & t) const { + return u >= t.u && l <= t.l; + } + + // return true if this interval contains the given point + constexpr tribool excludes(const R & t) const { + return t < l || t > u; + } + // if this interval excludes every point found in some other inteval t + // return true + // otherwise + // return false or indeterminate + constexpr tribool excludes(const interval & t) const { + return t.u < l || u < t.l; + } + +}; + +template +constexpr interval make_interval(){ + return interval(); +} +template +constexpr interval make_interval(const R & r){ + return interval(r, r); +} +template +constexpr interval::interval() : + l(std::numeric_limits::lowest()), + u(std::numeric_limits::max()) +{} +// account for the fact that for floats and doubles +// the most negative value is called "lowest" rather +// than min +template<> +constexpr interval::interval() : + l(std::numeric_limits::lowest()), + u(std::numeric_limits::max()) +{} +template<> +constexpr interval::interval() : + l(std::numeric_limits::lowest()), + u(std::numeric_limits::max()) +{} + +template +constexpr interval operator+(const interval & t, const interval & u){ + // adapted from https://en.wikipedia.org/wiki/Interval_arithmetic + return {t.l + u.l, t.u + u.u}; +} + +template +constexpr interval operator-(const interval & t, const interval & u){ + // adapted from https://en.wikipedia.org/wiki/Interval_arithmetic + return {t.l - u.u, t.u - u.l}; +} + +template +constexpr interval operator*(const interval & t, const interval & u){ + // adapted from https://en.wikipedia.org/wiki/Interval_arithmetic + return utility::minmax( + std::initializer_list { + t.l * u.l, + t.l * u.u, + t.u * u.l, + t.u * u.u + } + ); +} + +// interval division +// note: presumes 0 is not included in the range of the denominator +template +constexpr interval operator/(const interval & t, const interval & u){ + assert(static_cast(u.excludes(T(0)))); + return utility::minmax( + std::initializer_list { + t.l / u.l, + t.l / u.u, + t.u / u.l, + t.u / u.u + } + ); +} + +// modulus of two intervals. This will give a new range of for the modulus. +// note: presumes 0 is not included in the range of the denominator +template +constexpr interval operator%(const interval & t, const interval & u){ + assert(static_cast(u.excludes(T(0)))); + return utility::minmax( + std::initializer_list { + t.l % u.l, + t.l % u.u, + t.u % u.l, + t.u % u.u + } + ); +} + +template +constexpr interval operator<<(const interval & t, const interval & u){ +// static_assert(std::is_integral::value, "left shift only defined for integral type"); + //return interval{t.l << u.l, t.u << u.u}; + return utility::minmax( + std::initializer_list { + t.l << u.l, + t.l << u.u, + t.u << u.l, + t.u << u.u + } + ); +} + +template +constexpr interval operator>>(const interval & t, const interval & u){ +// static_assert(std::is_integral::value, "right shift only defined for integral type"); + //return interval{t.l >> u.u, t.u >> u.l}; + return utility::minmax( + std::initializer_list { + t.l >> u.l, + t.l >> u.u, + t.u >> u.l, + t.u >> u.u + } + ); +} + +// union of two intervals +template +constexpr interval operator|(const interval & t, const interval & u){ + const T & rl = std::min(t.l, u.l); + const T & ru = std::max(t.u, u.u); + return interval(rl, ru); +} + +// intersection of two intervals +template +constexpr interval operator&(const interval & t, const interval & u){ + const T & rl = std::max(t.l, u.l); + const T & ru = std::min(t.u, u.u); + return interval(rl, ru); +} + +// determine whether two intervals intersect +template +constexpr boost::logic::tribool intersect(const interval & t, const interval & u){ + return t.u >= u.l || t.l <= u.u; +} + +template +constexpr boost::logic::tribool operator<( + const interval & t, + const interval & u +){ + return + // if every element in t is less than every element in u + t.u < u.l ? boost::logic::tribool(true): + // if every element in t is greater than every element in u + t.l > u.u ? boost::logic::tribool(false): + // otherwise some element(s) in t are greater than some element in u + boost::logic::indeterminate + ; +} + +template +constexpr boost::logic::tribool operator>( + const interval & t, + const interval & u +){ + return + // if every element in t is greater than every element in u + t.l > u.u ? boost::logic::tribool(true) : + // if every element in t is less than every element in u + t.u < u.l ? boost::logic::tribool(false) : + // otherwise some element(s) in t are greater than some element in u + boost::logic::indeterminate + ; +} + +template +constexpr bool operator==( + const interval & t, + const interval & u +){ + // intervals have the same limits + return t.l == u.l && t.u == u.u; +} + +template +constexpr bool operator!=( + const interval & t, + const interval & u +){ + return ! (t == u); +} + +template +constexpr boost::logic::tribool operator<=( + const interval & t, + const interval & u +){ + return ! (t > u); +} + +template +constexpr boost::logic::tribool operator>=( + const interval & t, + const interval & u +){ + return ! (t < u); +} + +} // safe_numerics +} // boost + +#include + +namespace std { + +template +inline std::basic_ostream & +operator<<( + std::basic_ostream & os, + const boost::safe_numerics::interval & i +){ + return os << '[' << i.l << ',' << i.u << ']'; +} +template +inline std::basic_ostream & +operator<<( + std::basic_ostream & os, + const boost::safe_numerics::interval & i +){ + os << "[" << (unsigned)i.l << "," << (unsigned)i.u << "]"; + return os; +} + +template +inline std::basic_ostream & +operator<<( + std::basic_ostream & os, + const boost::safe_numerics::interval & i +){ + os << "[" << (int)i.l << "," << (int)i.u << "]"; + return os; +} + +} // std + + +#endif // BOOST_NUMERIC_INTERVAL_HPP diff --git a/boost/safe_numerics/native.hpp b/boost/safe_numerics/native.hpp new file mode 100644 index 0000000..78d34fd --- /dev/null +++ b/boost/safe_numerics/native.hpp @@ -0,0 +1,110 @@ +#ifndef BOOST_NUMERIC_NATIVE_HPP +#define BOOST_NUMERIC_NATIVE_HPP + +// Copyright (c) 2012 Robert Ramey +// +// 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) + +#include +#include + +// policy which creates results types and values equal to that of C++ promotions. +// When used in conjunction with a desired exception policy, traps errors but +// does not otherwise alter the results produced by the program using it. +namespace boost { +namespace safe_numerics { + +struct native { +public: + // arithmetic operators + template + struct addition_result { + using type = decltype( + typename base_type::type() + + typename base_type::type() + ); + }; + template + struct subtraction_result { + using type = decltype( + typename base_type::type() + - typename base_type::type() + ); + }; + template + struct multiplication_result { + using type = decltype( + typename base_type::type() + * typename base_type::type() + ); + }; + template + struct division_result { + using type = decltype( + typename base_type::type() + / typename base_type::type() + ); + }; + template + struct modulus_result { + using type = decltype( + typename base_type::type() + % typename base_type::type() + ); + }; + // note: comparison_result (<, >, ...) is special. + // The return value is always a bool. The type returned here is + // the intermediate type applied to make the values comparable. + template + struct comparison_result { + using type = decltype( + typename base_type::type() + + typename base_type::type() + ); + }; + + // shift operators + template + struct left_shift_result { + using type = decltype( + typename base_type::type() + << typename base_type::type() + ); + }; + template + struct right_shift_result { + using type = decltype( + typename base_type::type() + >> typename base_type::type() + ); + }; + // bitwise operators + template + struct bitwise_or_result { + using type = decltype( + typename base_type::type() + | typename base_type::type() + ); + }; + template + struct bitwise_and_result { + using type = decltype( + typename base_type::type() + & typename base_type::type() + ); + }; + template + struct bitwise_xor_result { + using type = decltype( + typename base_type::type() + ^ typename base_type::type() + ); + }; +}; + +} // safe_numerics +} // boost + +#endif // BOOST_NUMERIC_NATIVE_HPP diff --git a/boost/safe_numerics/range_value.hpp b/boost/safe_numerics/range_value.hpp new file mode 100644 index 0000000..ceef19e --- /dev/null +++ b/boost/safe_numerics/range_value.hpp @@ -0,0 +1,71 @@ +#ifndef BOOST_RANGE_VALUE_HPP +#define BOOST_RANGE_VALUE_HPP + +// Copyright (c) 2015 Robert Ramey +// +// 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) + +#include + +// print range and value to a standard stream +// make a simple wrapper +template +struct range_value { + // type requirement - a numeric type + const T & m_t; + + constexpr range_value(const T & t) + : m_t(t) + {} + +}; + +template +constexpr range_value make_range_value(const T & t){ + return range_value(t); +} + +#include "interval.hpp" + +template< + class CharT, + class Traits, + class T +> +std::basic_ostream & operator<<( + std::basic_ostream & os, + const range_value & t +){ + return os + << boost::safe_numerics::make_interval() + << t.m_t; +} + +template +struct result_display { + const T & m_t; + result_display(const T & t) : + m_t(t) + {} +}; + +template +result_display make_result_display(const T & t){ + return result_display(t); +} + +template +inline std::basic_ostream & +operator<<( + std::basic_ostream & os, + const result_display & r +){ + return os + << std::hex + << make_range_value(r.m_t) + << "(" << std::dec << r.m_t << ")"; +} + +#endif // BOOST_RANGE_VALUE_HPP diff --git a/boost/safe_numerics/safe_base.hpp b/boost/safe_numerics/safe_base.hpp new file mode 100644 index 0000000..55508d0 --- /dev/null +++ b/boost/safe_numerics/safe_base.hpp @@ -0,0 +1,349 @@ +#ifndef BOOST_NUMERIC_SAFE_BASE_HPP +#define BOOST_NUMERIC_SAFE_BASE_HPP + +// Copyright (c) 2012 Robert Ramey +// +// 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) + +#include +#include // is_integral, enable_if, conditional +#include // BOOST_CLANG +#include "concept/exception_policy.hpp" +#include "concept/promotion_policy.hpp" + +#include "safe_common.hpp" +#include "exception_policies.hpp" + +#include "boost/concept/assert.hpp" + +namespace boost { +namespace safe_numerics { + +///////////////////////////////////////////////////////////////// +// forward declarations to support friend function declarations +// in safe_base + +template< + class Stored, + Stored Min, + Stored Max, + class P, // promotion polic + class E // exception policy +> +class safe_base; + +template< + class T, + T Min, + T Max, + class P, + class E +> +struct is_safe > : public std::true_type +{}; + +template< + class T, + T Min, + T Max, + class P, + class E +> +struct get_promotion_policy > { + using type = P; +}; + +template< + class T, + T Min, + T Max, + class P, + class E +> +struct get_exception_policy > { + using type = E; +}; + +template< + class T, + T Min, + T Max, + class P, + class E +> +struct base_type > { + using type = T; +}; + +template< + class T, + T Min, + T Max, + class P, + class E +> +constexpr T base_value( + const safe_base & st +) { + return static_cast(st); +} + +template< + typename T, + T N, + class P, // promotion policy + class E // exception policy +> +class safe_literal_impl; + +// works for both GCC and clang +#if BOOST_CLANG==1 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmismatched-tags" +#endif + +///////////////////////////////////////////////////////////////// +// Main implementation + +template< + class Stored, + Stored Min, + Stored Max, + class P, // promotion polic + class E // exception policy +> +class safe_base { +private: + BOOST_CONCEPT_ASSERT((PromotionPolicy

)); + BOOST_CONCEPT_ASSERT((ExceptionPolicy)); + Stored m_t; + + template< + class StoredX, + StoredX MinX, + StoredX MaxX, + class PX, // promotion polic + class EX // exception policy + > + friend class safe_base; + + friend class std::numeric_limits; + + template + constexpr Stored validated_cast(const T & t) const; + + template + constexpr Stored validated_cast( + const safe_literal_impl & t + ) const; + + // stream support + + template + void output(std::basic_ostream & os) const; + + // note usage of friend declaration to mark function as + // a global function rather than a member function. If + // this is not done, the compiler will confuse this with + // a member operator overload on the << operator. Weird + // I know. But it's documented here + // http://en.cppreference.com/w/cpp/language/friend + // under the heading "Template friend operators" + template + friend std::basic_ostream & + operator<<( + std::basic_ostream & os, + const safe_base & t + ){ + t.output(os); + return os; + } + + template + void input(std::basic_istream & is); + + // see above + template + friend inline std::basic_istream & + operator>>( + std::basic_istream & is, + safe_base & t + ){ + t.input(is); + return is; + } + +public: + //////////////////////////////////////////////////////////// + // constructors + + struct skip_validation{}; + + constexpr explicit safe_base(const Stored & rhs, skip_validation); + + constexpr safe_base(); + + // construct an instance of a safe type + // from an instance of a convertible underlying type. + + template + constexpr /*explicit*/ safe_base( + const T & t, + typename std::enable_if< + is_safe::value, + bool + >::type = true + ); + + template + constexpr /*explicit*/ safe_base( + const T & t, + typename std::enable_if< + std::is_integral::value, + bool + >::type = true + ); + + template + constexpr /*explicit*/ safe_base( + const std::integral_constant & + ); + + // note: Rule of Five. Supply all or none of the following + // a) user-defined destructor + ~safe_base() = default; + // b) copy-constructor + constexpr safe_base(const safe_base &) = default; + // c) copy-assignment + constexpr safe_base & operator=(const safe_base &) = default; + // d) move constructor + constexpr safe_base(safe_base &&) = default; + // e) move assignment operator + constexpr safe_base & operator=(safe_base &&) = default; + + ///////////////////////////////////////////////////////////////// + // casting operators for intrinsic integers + // convert to any type which is not safe. safe types need to be + // excluded to prevent ambiguous function selection which + // would otherwise occur. validity of safe types is checked in + // the constructor of safe types + template< + class R, + typename std::enable_if< + ! boost::safe_numerics::is_safe::value, + int + >::type = 0 + > + constexpr /*explicit*/ operator R () const; + + constexpr /*explicit*/ operator Stored () const; + + ///////////////////////////////////////////////////////////////// + // modification binary operators + template + constexpr safe_base & + operator=(const T & rhs){ + m_t = validated_cast(rhs); + return *this; + } + + // required to passify VS2017 + constexpr safe_base & + operator=(const Stored & rhs){ + m_t = validated_cast(rhs); + return *this; + } + + // mutating unary operators + safe_base & operator++(){ // pre increment + return *this = *this + 1; + } + safe_base & operator--(){ // pre decrement + return *this = *this - 1; + } + safe_base operator++(int){ // post increment + safe_base old_t = *this; + ++(*this); + return old_t; + } + safe_base operator--(int){ // post decrement + safe_base old_t = *this; + --(*this); + return old_t; + } + // non mutating unary operators + constexpr auto operator+() const { // unary plus + return *this; + } + // after much consideration, I've permited the resulting value of a unary + // - to change the type. The C++ standard does invoke integral promotions + // so it's changing the type as well. + + /* section 5.3.1 &8 of the C++ standard + The operand of the unary - operator shall have arithmetic or unscoped + enumeration type and the result is the negation of its operand. Integral + promotion is performed on integral or enumeration operands. The negative + of an unsigned quantity is computed by subtracting its value from 2n, + where n is the number of bits in the promoted operand. The type of the + result is the type of the promoted operand. + */ + constexpr auto operator-() const { // unary minus + // if this is a unsigned type and the promotion policy is native + // the result will be unsigned. But then the operation will fail + // according to the requirements of arithmetic correctness. + // if this is an unsigned type and the promotion policy is automatic. + // the result will be signed. + return 0 - *this; + } + /* section 5.3.1 &10 of the C++ standard + The operand of ~ shall have integral or unscoped enumeration type; + the result is the ones’ complement of its operand. Integral promotions + are performed. The type of the result is the type of the promoted operand. + */ + constexpr auto operator~() const { // complement + return ~Stored(0u) ^ *this; + } +}; + +} // safe_numerics +} // boost + +///////////////////////////////////////////////////////////////// +// numeric limits for safe etc. + +#include + +namespace std { + +template< + class T, + T Min, + T Max, + class P, + class E +> +class numeric_limits > + : public std::numeric_limits +{ + using SB = boost::safe_numerics::safe_base; +public: + constexpr static SB lowest() noexcept { + return SB(Min, typename SB::skip_validation()); + } + constexpr static SB min() noexcept { + return SB(Min, typename SB::skip_validation()); + } + constexpr static SB max() noexcept { + return SB(Max, typename SB::skip_validation()); + } +}; + +} // std + +#if BOOST_CLANG==1 +#pragma GCC diagnostic pop +#endif + +#endif // BOOST_NUMERIC_SAFE_BASE_HPP diff --git a/boost/safe_numerics/safe_base_operations.hpp b/boost/safe_numerics/safe_base_operations.hpp new file mode 100644 index 0000000..d97ac92 --- /dev/null +++ b/boost/safe_numerics/safe_base_operations.hpp @@ -0,0 +1,1745 @@ +#ifndef BOOST_NUMERIC_SAFE_BASE_OPERATIONS_HPP +#define BOOST_NUMERIC_SAFE_BASE_OPERATIONS_HPP + +// Copyright (c) 2012 Robert Ramey +// +// 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) + +#include +#include // is_base_of, is_same, is_floating_point, conditional +#include // max +#include + +#include + +#include // lazy_enable_if +#include +#include + +#include "checked_integer.hpp" +#include "checked_result.hpp" +#include "safe_base.hpp" + +#include "interval.hpp" +#include "utility.hpp" + +namespace boost { +namespace safe_numerics { + +///////////////////////////////////////////////////////////////// +// validation + +template +struct validate_detail { + using r_type = checked_result; + + struct exception_possible { + template + constexpr static R return_value( + const T & t + ){ + // INT08-C + const r_type rx = heterogeneous_checked_operation< + R, + Min, + Max, + typename base_type::type, + dispatch_and_return + >::cast(t); + + return rx; + } + }; + struct exception_not_possible { + template + constexpr static R return_value( + const T & t + ){ + return static_cast(base_value(t)); + } + }; + + template + constexpr static R return_value(const T & t){ + + constexpr const interval t_interval{ + checked::cast(base_value(std::numeric_limits::min())), + checked::cast(base_value(std::numeric_limits::max())) + }; + constexpr const interval r_interval{r_type(Min), r_type(Max)}; + + static_assert( + true != static_cast(r_interval.excludes(t_interval)), + "can't cast from ranges that don't overlap" + ); + + return std::conditional< + static_cast(r_interval.includes(t_interval)), + exception_not_possible, + exception_possible + >::type::return_value(t); + } +}; + +template +template +constexpr Stored safe_base:: +validated_cast(const T & t) const { + return validate_detail::return_value(t); +} + +template +template +constexpr Stored safe_base:: +validated_cast(const safe_literal_impl &) const { + constexpr const interval this_interval{}; + // if static values don't overlap, the program can never function + static_assert( + this_interval.includes(N), + "safe type cannot be constructed from this value" + ); + return static_cast(N); +} + +///////////////////////////////////////////////////////////////// +// constructors + +template + constexpr /*explicit*/ safe_base::safe_base( + const Stored & rhs, + skip_validation +) : + m_t(rhs) +{} + +template +constexpr /*explicit*/ safe_base::safe_base(){ + dispatch( + "safe values must be initialized" + ); +} + +// construct an instance of a safe type +// from an instance of a convertible underlying type. +template +template +constexpr /*explicit*/ safe_base::safe_base( + const T & t, + typename std::enable_if< + is_safe::value, + bool + >::type +) : + m_t(validated_cast(t)) +{} + +template +template +constexpr /*explicit*/ safe_base::safe_base( + const T & t, + typename std::enable_if< + std::is_integral::value, + bool + >::type +) : + m_t(validated_cast(t)) +{} + +template +template +constexpr /*explicit*/ safe_base::safe_base( + + const std::integral_constant & +) : + m_t(validated_cast(value)) +{} + +///////////////////////////////////////////////////////////////// +// casting operators + +// cast to a builtin type from a safe type +template< class Stored, Stored Min, Stored Max, class P, class E> +template< + class R, + typename std::enable_if< + ! boost::safe_numerics::is_safe::value, + int + >::type +> +constexpr safe_base:: +operator R () const { + // if static values don't overlap, the program can never function + #if 1 + constexpr const interval r_interval; + constexpr const interval this_interval(Min, Max); + static_assert( + ! r_interval.excludes(this_interval), + "safe type cannot be constructed with this type" + ); + #endif + + return validate_detail< + R, + std::numeric_limits::min(), + std::numeric_limits::max(), + E + >::return_value(*this); +} + +// cast to the underlying builtin type from a safe type +template +constexpr safe_base:: +operator Stored () const { + return m_t; +} + +///////////////////////////////////////////////////////////////// +// binary operators + +template +struct common_exception_policy { + static_assert(is_safe::value || is_safe::value, + "at least one type must be a safe type" + ); + + using t_exception_policy = typename get_exception_policy::type; + using u_exception_policy = typename get_exception_policy::type; + + static_assert( + std::is_same::value + || std::is_same::value + || std::is_same::value, + "if the exception policies are different, one must be void!" + ); + + static_assert( + ! (std::is_same::value + && std::is_same::value), + "at least one exception policy must not be void" + ); + + using type = + typename std::conditional< + !std::is_same::value, + u_exception_policy, + typename std::conditional< + !std::is_same::value, + t_exception_policy, + // + void + >::type >::type; + + static_assert( + !std::is_same::value, + "exception_policy is void" + ); +}; + +template +struct common_promotion_policy { + static_assert(is_safe::value || is_safe::value, + "at least one type must be a safe type" + ); + using t_promotion_policy = typename get_promotion_policy::type; + using u_promotion_policy = typename get_promotion_policy::type; + static_assert( + std::is_same::value + ||std::is_same::value + ||std::is_same::value, + "if the promotion policies are different, one must be void!" + ); + static_assert( + ! (std::is_same::value + && std::is_same::value), + "at least one promotion policy must not be void" + ); + + using type = + typename std::conditional< + ! std::is_same::value, + u_promotion_policy, + typename std::conditional< + ! std::is_same::value, + t_promotion_policy, + // + void + >::type >::type; + + static_assert( + ! std::is_same::value, + "promotion_policy is void" + ); + +}; + +// give the resultant base type, figure out what the final result +// type will be. Note we currently need this because we support +// return of only safe integer types. Someday ..., we'll support +// all other safe types including float and user defined ones. + +// helper - cast arguments to binary operators to a specified +// result type + +template +std::pair +constexpr static casting_helper(const T & t, const U & u){ + using r_type = checked_result; + const r_type tx = heterogeneous_checked_operation< + R, + std::numeric_limits::min(), + std::numeric_limits::max(), + typename base_type::type, + dispatch_and_return + >::cast(base_value(t)); + const R tr = tx.exception() + ? static_cast(t) + : tx.m_r; + + const r_type ux = heterogeneous_checked_operation< + R, + std::numeric_limits::min(), + std::numeric_limits::max(), + typename base_type::type, + dispatch_and_return + >::cast(base_value(u)); + const R ur = ux.exception() + ? static_cast(u) + : ux.m_r; + return std::pair(tr, ur); +} + +// Note: the following global operators will be found via +// argument dependent lookup. + +///////////////////////////////////////////////////////////////// +// addition + +template +struct addition_result { +private: + using promotion_policy = typename common_promotion_policy::type; + using result_base_type = + typename promotion_policy::template addition_result::type; + + // if exception not possible + constexpr static result_base_type + return_value(const T & t, const U & u, std::false_type){ + return + static_cast(base_value(t)) + + static_cast(base_value(u)); + } + + // if exception possible + using exception_policy = typename common_exception_policy::type; + + using r_type = checked_result; + + constexpr static result_base_type + return_value(const T & t, const U & u, std::true_type){ + const std::pair r = casting_helper< + exception_policy, + result_base_type + >(t, u); + + const r_type rx = checked_operation< + result_base_type, + dispatch_and_return + >::add(r.first, r.second); + + return + rx.exception() + ? r.first + r.second + : rx.m_r; + } + + using r_type_interval_t = interval; + + constexpr static const r_type_interval_t get_r_type_interval(){ + constexpr const r_type_interval_t t_interval{ + checked::cast(base_value(std::numeric_limits::min())), + checked::cast(base_value(std::numeric_limits::max())) + }; + constexpr const r_type_interval_t u_interval{ + checked::cast(base_value(std::numeric_limits::min())), + checked::cast(base_value(std::numeric_limits::max())) + }; + return t_interval + u_interval; + } + constexpr static const r_type_interval_t r_type_interval = get_r_type_interval(); + + constexpr static const interval return_interval{ + r_type_interval.l.exception() + ? std::numeric_limits::min() + : static_cast(r_type_interval.l), + r_type_interval.u.exception() + ? std::numeric_limits::max() + : static_cast(r_type_interval.u) + }; + + constexpr static bool exception_possible(){ + if(r_type_interval.l.exception()) + return true; + if(r_type_interval.u.exception()) + return true; + if(! return_interval.includes(r_type_interval)) + return true; + return false; + } + + constexpr static auto rl = return_interval.l; + constexpr static auto ru = return_interval.u; + +public: + using type = + safe_base< + result_base_type, + rl, + ru, + promotion_policy, + exception_policy + >; + + constexpr static type return_value(const T & t, const U & u){ + return type( + return_value( + t, + u, + std::integral_constant() + ), + typename type::skip_validation() + ); + } +}; + +template +typename boost::lazy_enable_if_c< + is_safe::value || is_safe::value, + addition_result +>::type +constexpr operator+(const T & t, const U & u){ + return addition_result::return_value(t, u); +} + +template +typename std::enable_if< + is_safe::value || is_safe::value, + T +>::type +constexpr operator+=(T & t, const U & u){ + t = static_cast(t + u); + return t; +} + +///////////////////////////////////////////////////////////////// +// subtraction + +template +struct subtraction_result { +private: + using promotion_policy = typename common_promotion_policy::type; + using result_base_type = + typename promotion_policy::template subtraction_result::type; + + // if exception not possible + constexpr static result_base_type + return_value(const T & t, const U & u, std::false_type){ + return + static_cast(base_value(t)) + - static_cast(base_value(u)); + } + + // if exception possible + using exception_policy = typename common_exception_policy::type; + + using r_type = checked_result; + + constexpr static result_base_type + return_value(const T & t, const U & u, std::true_type){ + const std::pair r = casting_helper< + exception_policy, + result_base_type + >(t, u); + + const r_type rx = checked_operation< + result_base_type, + dispatch_and_return + >::subtract(r.first, r.second); + + return + rx.exception() + ? r.first + r.second + : rx.m_r; + } + using r_type_interval_t = interval; + + constexpr static const r_type_interval_t get_r_type_interval(){ + constexpr const r_type_interval_t t_interval{ + checked::cast(base_value(std::numeric_limits::min())), + checked::cast(base_value(std::numeric_limits::max())) + }; + + constexpr const r_type_interval_t u_interval{ + checked::cast(base_value(std::numeric_limits::min())), + checked::cast(base_value(std::numeric_limits::max())) + }; + + return t_interval - u_interval; + } + static constexpr const r_type_interval_t r_type_interval = get_r_type_interval(); + + constexpr static const interval return_interval{ + r_type_interval.l.exception() + ? std::numeric_limits::min() + : static_cast(r_type_interval.l), + r_type_interval.u.exception() + ? std::numeric_limits::max() + : static_cast(r_type_interval.u) + }; + + constexpr static bool exception_possible(){ + if(r_type_interval.l.exception()) + return true; + if(r_type_interval.u.exception()) + return true; + if(! return_interval.includes(r_type_interval)) + return true; + return false; + } + +public: + constexpr static auto rl = return_interval.l; + constexpr static auto ru = return_interval.u; + + using type = + safe_base< + result_base_type, + rl, + ru, + promotion_policy, + exception_policy + >; + + constexpr static type return_value(const T & t, const U & u){ + return type( + return_value( + t, + u, + std::integral_constant() + ), + typename type::skip_validation() + ); + } +}; + +template +typename boost::lazy_enable_if_c< + is_safe::value || is_safe::value, + subtraction_result +>::type +constexpr operator-(const T & t, const U & u){ + return subtraction_result::return_value(t, u); +} + +template +typename std::enable_if< + is_safe::value || is_safe::value, + T +>::type +constexpr operator-=(T & t, const U & u){ + t = static_cast(t - u); + return t; +} + +///////////////////////////////////////////////////////////////// +// multiplication + +template +struct multiplication_result { +private: + using promotion_policy = typename common_promotion_policy::type; + using result_base_type = + typename promotion_policy::template multiplication_result::type; + + // if exception not possible + constexpr static result_base_type + return_value(const T & t, const U & u, std::false_type){ + return + static_cast(base_value(t)) + * static_cast(base_value(u)); + } + + // if exception possible + using exception_policy = typename common_exception_policy::type; + + using r_type = checked_result; + + constexpr static result_base_type + return_value(const T & t, const U & u, std::true_type){ + const std::pair r = casting_helper< + exception_policy, + result_base_type + >(t, u); + + const r_type rx = checked_operation< + result_base_type, + dispatch_and_return + >::multiply(r.first, r.second); + + return + rx.exception() + ? r.first * r.second + : rx.m_r; + } + + using r_type_interval_t = interval; + + constexpr static r_type_interval_t get_r_type_interval(){ + constexpr const r_type_interval_t t_interval{ + checked::cast(base_value(std::numeric_limits::min())), + checked::cast(base_value(std::numeric_limits::max())) + }; + + constexpr const r_type_interval_t u_interval{ + checked::cast(base_value(std::numeric_limits::min())), + checked::cast(base_value(std::numeric_limits::max())) + }; + + return t_interval * u_interval; + } + + static constexpr const r_type_interval_t r_type_interval = get_r_type_interval(); + + constexpr static const interval return_interval{ + r_type_interval.l.exception() + ? std::numeric_limits::min() + : static_cast(r_type_interval.l), + r_type_interval.u.exception() + ? std::numeric_limits::max() + : static_cast(r_type_interval.u) + }; + + constexpr static bool exception_possible(){ + if(r_type_interval.l.exception()) + return true; + if(r_type_interval.u.exception()) + return true; + if(! return_interval.includes(r_type_interval)) + return true; + return false; + } + + constexpr static auto rl = return_interval.l; + constexpr static auto ru = return_interval.u; + +public: + using type = + safe_base< + result_base_type, + rl, + ru, + promotion_policy, + exception_policy + >; + + constexpr static type return_value(const T & t, const U & u){ + return type( + return_value( + t, + u, + std::integral_constant() + ), + typename type::skip_validation() + ); + } +}; + +template +typename boost::lazy_enable_if_c< + is_safe::value || is_safe::value, + multiplication_result +>::type +constexpr operator*(const T & t, const U & u){ + // argument dependent lookup should guarentee that we only get here + return multiplication_result::return_value(t, u); +} + +template +typename std::enable_if< + is_safe::value || is_safe::value, + T +>::type +constexpr operator*=(T & t, const U & u){ + t = static_cast(t * u); + return t; +} + +///////////////////////////////////////////////////////////////// +// division + +// key idea here - result will never be larger than T +template +struct division_result { +private: + using promotion_policy = typename common_promotion_policy::type; + using result_base_type = + typename promotion_policy::template division_result::type; + + // if exception not possible + constexpr static result_base_type + return_value(const T & t, const U & u, std::false_type){ + return + static_cast(base_value(t)) + / static_cast(base_value(u)); + } + + // if exception possible + using exception_policy = typename common_exception_policy::type; + + constexpr static int bits = std::min( + std::numeric_limits::digits, + std::max(std::initializer_list{ + std::numeric_limits::digits, + std::numeric_limits::type>::digits, + std::numeric_limits::type>::digits + }) + (std::numeric_limits::is_signed ? 1 : 0) + ); + + using r_type = checked_result; + + constexpr static result_base_type + return_value(const T & t, const U & u, std::true_type){ + using temp_base = typename std::conditional< + std::numeric_limits::is_signed, + typename boost::int_t::least, + typename boost::uint_t::least + >::type; + using t_type = checked_result; + + const std::pair r = casting_helper< + exception_policy, + temp_base + >(t, u); + + const t_type rx = checked_operation< + temp_base, + dispatch_and_return + >::divide(r.first, r.second); + + return + rx.exception() + ? r.first / r.second + : rx; + } + using r_type_interval_t = interval; + + constexpr static r_type_interval_t t_interval(){ + return r_type_interval_t{ + checked::cast(base_value(std::numeric_limits::min())), + checked::cast(base_value(std::numeric_limits::max())) + }; + }; + + constexpr static r_type_interval_t u_interval(){ + return r_type_interval_t{ + checked::cast(base_value(std::numeric_limits::min())), + checked::cast(base_value(std::numeric_limits::max())) + }; + }; + + constexpr static r_type_interval_t get_r_type_interval(){ + constexpr const r_type_interval_t t = t_interval(); + constexpr const r_type_interval_t u = u_interval(); + + if(u.u < r_type(0) || u.l > r_type(0)) + return t / u; + return utility::minmax( + std::initializer_list { + t.l / u.l, + t.l / r_type(-1), + t.l / r_type(1), + t.l / u.u, + t.u / u.l, + t.u / r_type(-1), + t.u / r_type(1), + t.u / u.u, + } + ); + } + + static constexpr const r_type_interval_t r_type_interval = get_r_type_interval(); + + constexpr static const interval return_interval{ + r_type_interval.l.exception() + ? std::numeric_limits::min() + : static_cast(r_type_interval.l), + r_type_interval.u.exception() + ? std::numeric_limits::max() + : static_cast(r_type_interval.u) + }; + + constexpr static bool exception_possible(){ + constexpr const r_type_interval_t ri = get_r_type_interval(); + constexpr const r_type_interval_t ui = u_interval(); + return + static_cast(ui.includes(r_type(0))) + || ri.l.exception() + || ri.u.exception(); + } + + constexpr static auto rl = return_interval.l; + constexpr static auto ru = return_interval.u; + +public: + using type = + safe_base< + result_base_type, + rl, + ru, + promotion_policy, + exception_policy + >; + + constexpr static type return_value(const T & t, const U & u){ + return type( + return_value( + t, + u, + std::integral_constant() + ), + typename type::skip_validation() + ); + } +}; + +template +typename boost::lazy_enable_if_c< + is_safe::value || is_safe::value, + division_result +>::type +constexpr operator/(const T & t, const U & u){ + return division_result::return_value(t, u); +} + +template +typename std::enable_if< + is_safe::value || is_safe::value, + T +>::type +constexpr operator/=(T & t, const U & u){ + t = static_cast(t / u); + return t; +} + +///////////////////////////////////////////////////////////////// +// modulus + +template +struct modulus_result { +private: + using promotion_policy = typename common_promotion_policy::type; + using result_base_type = typename promotion_policy::template modulus_result::type; + + // if exception not possible + constexpr static result_base_type + return_value(const T & t, const U & u, std::false_type){ + return + static_cast(base_value(t)) + % static_cast(base_value(u)); + } + + // if exception possible + using exception_policy = typename common_exception_policy::type; + + constexpr static int bits = std::min( + std::numeric_limits::digits, + std::max(std::initializer_list{ + std::numeric_limits::digits, + std::numeric_limits::type>::digits, + std::numeric_limits::type>::digits + }) + (std::numeric_limits::is_signed ? 1 : 0) + ); + + using r_type = checked_result; + + constexpr static result_base_type + return_value(const T & t, const U & u, std::true_type){ + using temp_base = typename std::conditional< + std::numeric_limits::is_signed, + typename boost::int_t::least, + typename boost::uint_t::least + >::type; + using t_type = checked_result; + + const std::pair r = casting_helper< + exception_policy, + temp_base + >(t, u); + + const t_type rx = checked_operation< + temp_base, + dispatch_and_return + >::modulus(r.first, r.second); + + return + rx.exception() + ? r.first % r.second + : rx; + } + + using r_type_interval_t = interval; + + constexpr static const r_type_interval_t t_interval(){ + return r_type_interval_t{ + checked::cast(base_value(std::numeric_limits::min())), + checked::cast(base_value(std::numeric_limits::max())) + }; + }; + + constexpr static const r_type_interval_t u_interval(){ + return r_type_interval_t{ + checked::cast(base_value(std::numeric_limits::min())), + checked::cast(base_value(std::numeric_limits::max())) + }; + }; + + constexpr static const r_type_interval_t get_r_type_interval(){ + constexpr const r_type_interval_t t = t_interval(); + constexpr const r_type_interval_t u = u_interval(); + + if(u.u < r_type(0) + || u.l > r_type(0)) + return t % u; + return utility::minmax( + std::initializer_list { + t.l % u.l, + t.l % r_type(-1), + t.l % r_type(1), + t.l % u.u, + t.u % u.l, + t.u % r_type(-1), + t.u % r_type(1), + t.u % u.u, + } + ); + } + + static constexpr const r_type_interval_t r_type_interval = get_r_type_interval(); + + constexpr static const interval return_interval{ + r_type_interval.l.exception() + ? std::numeric_limits::min() + : static_cast(r_type_interval.l), + r_type_interval.u.exception() + ? std::numeric_limits::max() + : static_cast(r_type_interval.u) + }; + + constexpr static bool exception_possible(){ + constexpr const r_type_interval_t ri = get_r_type_interval(); + constexpr const r_type_interval_t ui = u_interval(); + return + static_cast(ui.includes(r_type(0))) + || ri.l.exception() + || ri.u.exception(); + } + + constexpr static auto rl = return_interval.l; + constexpr static auto ru = return_interval.u; + +public: + using type = + safe_base< + result_base_type, + rl, + ru, + promotion_policy, + exception_policy + >; + + constexpr static type return_value(const T & t, const U & u){ + return type( + return_value( + t, + u, + std::integral_constant() + ), + typename type::skip_validation() + ); + } +}; + +template +typename boost::lazy_enable_if_c< + is_safe::value || is_safe::value, + modulus_result +>::type +constexpr operator%(const T & t, const U & u){ + // see https://en.wikipedia.org/wiki/Modulo_operation + return modulus_result::return_value(t, u); +} + +template +typename std::enable_if< + is_safe::value || is_safe::value, + T +>::type +constexpr operator%=(T & t, const U & u){ + t = static_cast(t % u); + return t; +} + +///////////////////////////////////////////////////////////////// +// comparison + +// less than + +template +struct less_than_result { +private: + using promotion_policy = typename common_promotion_policy::type; + + using result_base_type = + typename promotion_policy::template comparison_result::type; + + // if exception not possible + constexpr static bool + return_value(const T & t, const U & u, std::false_type){ + return + static_cast(base_value(t)) + < static_cast(base_value(u)); + } + + using exception_policy = typename common_exception_policy::type; + + using r_type = checked_result; + + // if exception possible + constexpr static bool + return_value(const T & t, const U & u, std::true_type){ + const std::pair r = casting_helper< + exception_policy, + result_base_type + >(t, u); + + return safe_compare::less_than(r.first, r.second); + } + + using r_type_interval_t = interval; + + constexpr static bool interval_open(const r_type_interval_t & t){ + return t.l.exception() || t.u.exception(); + } + +public: + constexpr static bool + return_value(const T & t, const U & u){ + constexpr const r_type_interval_t t_interval{ + checked::cast(base_value(std::numeric_limits::min())), + checked::cast(base_value(std::numeric_limits::max())) + }; + constexpr const r_type_interval_t u_interval{ + checked::cast(base_value(std::numeric_limits::min())), + checked::cast(base_value(std::numeric_limits::max())) + }; + + if(t_interval < u_interval) + return true; + if(t_interval > u_interval) + return false; + + constexpr bool exception_possible + = interval_open(t_interval) || interval_open(u_interval); + + return return_value( + t, + u, + std::integral_constant() + ); + } +}; + +template +typename std::enable_if< + is_safe::value || is_safe::value, + bool +>::type +constexpr operator<(const T & lhs, const U & rhs) { + return less_than_result::return_value(lhs, rhs); +} + +template +typename std::enable_if< + is_safe::value || is_safe::value, + bool +>::type +constexpr operator>(const T & lhs, const U & rhs) { + return rhs < lhs; +} + +template +typename std::enable_if< + is_safe::value || is_safe::value, + bool +>::type +constexpr operator>=(const T & lhs, const U & rhs) { + return ! ( lhs < rhs ); +} + +template +typename std::enable_if< + is_safe::value || is_safe::value, + bool +>::type +constexpr operator<=(const T & lhs, const U & rhs) { + return ! ( lhs > rhs ); +} + +// equal + +template +struct equal_result { +private: + using promotion_policy = typename common_promotion_policy::type; + + using result_base_type = + typename promotion_policy::template comparison_result::type; + + // if exception not possible + constexpr static bool + return_value(const T & t, const U & u, std::false_type){ + return + static_cast(base_value(t)) + == static_cast(base_value(u)); + } + + using exception_policy = typename common_exception_policy::type; + + using r_type = checked_result; + + // exception possible + constexpr static bool + return_value(const T & t, const U & u, std::true_type){ + const std::pair r = casting_helper< + exception_policy, + result_base_type + >(t, u); + + return safe_compare::equal(r.first, r.second); + } + + using r_type_interval = interval; + + constexpr static bool interval_open(const r_type_interval & t){ + return t.l.exception() || t.u.exception(); + } + +public: + constexpr static bool + return_value(const T & t, const U & u){ + constexpr const r_type_interval t_interval{ + checked::cast(base_value(std::numeric_limits::min())), + checked::cast(base_value(std::numeric_limits::max())) + }; + + constexpr const r_type_interval u_interval{ + checked::cast(base_value(std::numeric_limits::min())), + checked::cast(base_value(std::numeric_limits::max())) + }; + + if(! intersect(t_interval, u_interval)) + return false; + + constexpr bool exception_possible + = interval_open(t_interval) || interval_open(u_interval); + + return return_value( + t, + u, + std::integral_constant() + ); + } +}; + +template +typename std::enable_if< + is_safe::value || is_safe::value, + bool +>::type +constexpr operator==(const T & lhs, const U & rhs) { + return equal_result::return_value(lhs, rhs); +} + +template +typename std::enable_if< + is_safe::value || is_safe::value, + bool +>::type +constexpr operator!=(const T & lhs, const U & rhs) { + return ! (lhs == rhs); +} + +///////////////////////////////////////////////////////////////// +// shift operators + +// left shift +template +struct left_shift_result { +private: + using promotion_policy = typename common_promotion_policy::type; + using result_base_type = + typename promotion_policy::template left_shift_result::type; + + // if exception not possible + constexpr static result_base_type + return_value(const T & t, const U & u, std::false_type){ + return + static_cast(base_value(t)) + << static_cast(base_value(u)); + } + + // exception possible + using exception_policy = typename common_exception_policy::type; + + using r_type = checked_result; + + constexpr static result_base_type + return_value(const T & t, const U & u, std::true_type){ + const std::pair r = casting_helper< + exception_policy, + result_base_type + >(t, u); + + const r_type rx = checked_operation< + result_base_type, + dispatch_and_return + >::left_shift(r.first, r.second); + + return + rx.exception() + ? r.first << r.second + : rx.m_r; + } + + using r_type_interval_t = interval; + + constexpr static r_type_interval_t get_r_type_interval(){ + constexpr const r_type_interval_t t_interval{ + checked::cast(base_value(std::numeric_limits::min())), + checked::cast(base_value(std::numeric_limits::max())) + }; + + constexpr const r_type_interval_t u_interval{ + checked::cast(base_value(std::numeric_limits::min())), + checked::cast(base_value(std::numeric_limits::max())) + }; + return (t_interval << u_interval); + } + + static constexpr const r_type_interval_t r_type_interval = get_r_type_interval(); + + constexpr static const interval return_interval{ + r_type_interval.l.exception() + ? std::numeric_limits::min() + : static_cast(r_type_interval.l), + r_type_interval.u.exception() + ? std::numeric_limits::max() + : static_cast(r_type_interval.u) + }; + + constexpr static bool exception_possible(){ + if(r_type_interval.l.exception()) + return true; + if(r_type_interval.u.exception()) + return true; + if(! return_interval.includes(r_type_interval)) + return true; + return false; + } + + constexpr static auto rl = return_interval.l; + constexpr static auto ru = return_interval.u; + +public: + using type = + safe_base< + result_base_type, + rl, + ru, + promotion_policy, + exception_policy + >; + + constexpr static type return_value(const T & t, const U & u){ + return type( + return_value( + t, + u, + std::integral_constant() + ), + typename type::skip_validation() + ); + } +}; + +template +typename boost::lazy_enable_if_c< + // handle safe << int, int << safe, safe << safe + // exclude std::ostream << ... + (! std::is_base_of::value) + && (is_safe::value || is_safe::value), + left_shift_result +>::type +constexpr operator<<(const T & t, const U & u){ + // INT13-CPP + // C++ standards document N4618 & 5.8.2 + static_assert( + std::numeric_limits::is_integer, "shifted value must be an integer" + ); + static_assert( + std::numeric_limits::is_integer, "shift amount must be an integer" + ); + return left_shift_result::return_value(t, u); +} + +template +typename std::enable_if< + is_safe::value || is_safe::value, + T +>::type +constexpr operator<<=(T & t, const U & u){ + t = static_cast(t << u); + return t; +} + +// right shift +template +struct right_shift_result { + using promotion_policy = typename common_promotion_policy::type; + using result_base_type = + typename promotion_policy::template right_shift_result::type; + + // if exception not possible + constexpr static result_base_type + return_value(const T & t, const U & u, std::false_type){ + return + static_cast(base_value(t)) + >> static_cast(base_value(u)); + } + + // exception possible + using exception_policy = typename common_exception_policy::type; + + using r_type = checked_result; + + constexpr static result_base_type + return_value(const T & t, const U & u, std::true_type){ + const std::pair r = casting_helper< + exception_policy, + result_base_type + >(t, u); + + const r_type rx = checked_operation< + result_base_type, + dispatch_and_return + >::right_shift(r.first, r.second); + + return + rx.exception() + ? r.first >> r.second + : rx.m_r; + } + + using r_type_interval_t = interval; + + constexpr static r_type_interval_t t_interval(){ + return r_type_interval_t( + checked::cast(base_value(std::numeric_limits::min())), + checked::cast(base_value(std::numeric_limits::max())) + ); + }; + + constexpr static r_type_interval_t u_interval(){ + return r_type_interval_t( + checked::cast(base_value(std::numeric_limits::min())), + checked::cast(base_value(std::numeric_limits::max())) + ); + } + constexpr static r_type_interval_t get_r_type_interval(){; + return (t_interval() >> u_interval()); + } + + static constexpr const r_type_interval_t r_type_interval = get_r_type_interval(); + + constexpr static const interval return_interval{ + r_type_interval.l.exception() + ? std::numeric_limits::min() + : static_cast(r_type_interval.l), + r_type_interval.u.exception() + ? std::numeric_limits::max() + : static_cast(r_type_interval.u) + }; + + constexpr static bool exception_possible(){ + constexpr const r_type_interval_t ri = r_type_interval; + constexpr const r_type_interval_t ti = t_interval(); + constexpr const r_type_interval_t ui = u_interval(); + return static_cast( + // note undesirable coupling with checked::shift right here ! + ui.u > checked_result( + std::numeric_limits::digits + ) + || ti.l < checked_result(0) + || ui.l < checked_result(0) + || ri.l.exception() + || ri.u.exception() + ); + } + + constexpr static auto rl = return_interval.l; + constexpr static auto ru = return_interval.u; + +public: + using type = + safe_base< + result_base_type, + rl, + ru, + promotion_policy, + exception_policy + >; + + constexpr static type return_value(const T & t, const U & u){ + return type( + return_value( + t, + u, + std::integral_constant() + ), + typename type::skip_validation() + ); + } +}; + +template +typename boost::lazy_enable_if_c< + (! std::is_base_of::value) + && (is_safe::value || is_safe::value), + right_shift_result +>::type +constexpr operator>>(const T & t, const U & u){ + // INT13-CPP + static_assert( + std::numeric_limits::is_integer, "shifted value must be an integer" + ); + static_assert( + std::numeric_limits::is_integer, "shift amount must be an integer" + ); + return right_shift_result::return_value(t, u); +} + +template +typename std::enable_if< + is_safe::value || is_safe::value, + T +>::type +constexpr operator>>=(T & t, const U & u){ + t = static_cast(t >> u); + return t; +} + +///////////////////////////////////////////////////////////////// +// bitwise operators + +// operator | +template +struct bitwise_or_result { +private: + using promotion_policy = typename common_promotion_policy::type; + using result_base_type = + typename promotion_policy::template bitwise_or_result::type; + + // according to the C++ standard, the bitwise operators are executed as if + // the operands are consider a logical array of bits. That is, there is no + // sense that these are signed numbers. + + using r_type = typename std::make_unsigned::type; + using r_type_interval_t = interval; + + #if 0 + // breaks compilation for earlier versions of clant + constexpr static const r_type_interval_t r_interval{ + r_type(0), + utility::round_out( + std::max( + static_cast(base_value(std::numeric_limits::max())), + static_cast(base_value(std::numeric_limits::max())) + ) + ) + }; + #endif + + using exception_policy = typename common_exception_policy::type; + +public: + // lazy_enable_if_c depends on this + using type = safe_base< + result_base_type, + //r_interval.l, + r_type(0), + //r_interval.u, + utility::round_out( + std::max( + static_cast(base_value(std::numeric_limits::max())), + static_cast(base_value(std::numeric_limits::max())) + ) + ), + promotion_policy, + exception_policy + >; + + constexpr static type return_value(const T & t, const U & u){ + return type( + static_cast(base_value(t)) + | static_cast(base_value(u)), + typename type::skip_validation() + ); + } +}; + +template +typename boost::lazy_enable_if_c< + is_safe::value || is_safe::value, + bitwise_or_result +>::type +constexpr operator|(const T & t, const U & u){ + return bitwise_or_result::return_value(t, u); +} + +template +typename std::enable_if< + is_safe::value || is_safe::value, + T +>::type +constexpr operator|=(T & t, const U & u){ + t = static_cast(t | u); + return t; +} + +// operator & +template +struct bitwise_and_result { +private: + using promotion_policy = typename common_promotion_policy::type; + using result_base_type = + typename promotion_policy::template bitwise_and_result::type; + + // according to the C++ standard, the bitwise operators are executed as if + // the operands are consider a logical array of bits. That is, there is no + // sense that these are signed numbers. + + using r_type = typename std::make_unsigned::type; + using r_type_interval_t = interval; + + #if 0 + // breaks compilation for earlier versions of clant + constexpr static const r_type_interval_t r_interval{ + r_type(0), + utility::round_out( + std::min( + static_cast(base_value(std::numeric_limits::max())), + static_cast(base_value(std::numeric_limits::max())) + ) + ) + }; + #endif + using exception_policy = typename common_exception_policy::type; + +public: + // lazy_enable_if_c depends on this + using type = safe_base< + result_base_type, + //r_interval.l, + r_type(0), + //r_interval.u, + utility::round_out( + std::min( + static_cast(base_value(std::numeric_limits::max())), + static_cast(base_value(std::numeric_limits::max())) + ) + ), + promotion_policy, + exception_policy + >; + + constexpr static type return_value(const T & t, const U & u){ + return type( + static_cast(base_value(t)) + & static_cast(base_value(u)), + typename type::skip_validation() + ); + } +}; + +template +typename boost::lazy_enable_if_c< + is_safe::value || is_safe::value, + bitwise_and_result +>::type +constexpr operator&(const T & t, const U & u){ + return bitwise_and_result::return_value(t, u); +} + +template +typename std::enable_if< + is_safe::value || is_safe::value, + T +>::type +constexpr operator&=(T & t, const U & u){ + t = static_cast(t & u); + return t; +} + +// operator ^ +template +struct bitwise_xor_result { + using promotion_policy = typename common_promotion_policy::type; + using result_base_type = + typename promotion_policy::template bitwise_xor_result::type; + + // according to the C++ standard, the bitwise operators are executed as if + // the operands are consider a logical array of bits. That is, there is no + // sense that these are signed numbers. + + using r_type = typename std::make_unsigned::type; + using r_type_interval_t = interval; + + #if 0 + // breaks compilation for earlier versions of clant + constexpr static const r_type_interval_t r_interval{ + r_type(0), + utility::round_out( + std::max( + static_cast(base_value(std::numeric_limits::max())), + static_cast(base_value(std::numeric_limits::max())) + ) + ) + }; + #endif + + using exception_policy = typename common_exception_policy::type; + +public: + // lazy_enable_if_c depends on this + using type = safe_base< + result_base_type, + //r_interval.l, + r_type(0), + //r_interval.u, + utility::round_out( + std::max( + static_cast(base_value(std::numeric_limits::max())), + static_cast(base_value(std::numeric_limits::max())) + ) + ), + promotion_policy, + exception_policy + >; + + constexpr static type return_value(const T & t, const U & u){ + return type( + static_cast(base_value(t)) + ^ static_cast(base_value(u)), + typename type::skip_validation() + ); + } +}; + +template +typename boost::lazy_enable_if_c< + is_safe::value || is_safe::value, + bitwise_xor_result +>::type +constexpr operator^(const T & t, const U & u){ + return bitwise_xor_result::return_value(t, u); +} + +template +typename std::enable_if< + is_safe::value || is_safe::value, + T +>::type +constexpr operator^=(T & t, const U & u){ + t = static_cast(t ^ u); + return t; +} + +///////////////////////////////////////////////////////////////// +// stream helpers + +template< + class T, + T Min, + T Max, + class P, // promotion polic + class E // exception policy +> +template< + class CharT, + class Traits +> +void safe_base::output( + std::basic_ostream & os +) const { + os << ( + (std::is_same::value + || std::is_same::value + || std::is_same::value + ) ? + static_cast(m_t) + : + m_t + ); +} + +template< + class T, + T Min, + T Max, + class P, // promotion polic + class E // exception policy +> +template< + class CharT, + class Traits +> +void safe_base::input( + std::basic_istream & is +){ + if(std::is_same::value + || std::is_same::value + || std::is_same::value + ){ + int x; + is >> x; + m_t = validated_cast(x); + } + else{ + is >> m_t; + validated_cast(m_t); + } + if(is.fail()){ + boost::safe_numerics::dispatch< + E, + boost::safe_numerics::safe_numerics_error::domain_error + >( + "error in file input" + ); + } +} + +} // safe_numerics +} // boost + +#endif // BOOST_NUMERIC_SAFE_BASE_OPERATIONS_HPP diff --git a/boost/safe_numerics/safe_common.hpp b/boost/safe_numerics/safe_common.hpp new file mode 100644 index 0000000..71a00ad --- /dev/null +++ b/boost/safe_numerics/safe_common.hpp @@ -0,0 +1,44 @@ +#ifndef BOOST_NUMERIC_SAFE_COMMON_HPP +#define BOOST_NUMERIC_SAFE_COMMON_HPP + +// Copyright (c) 2012 Robert Ramey +// +// 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) + +#include + +namespace boost { +namespace safe_numerics { + +// default implementations for required meta-functions +template +struct is_safe : public std::false_type +{}; + +template +struct base_type { + using type = T; +}; + +template +constexpr const typename base_type::type & base_value(const T & t) { + return static_cast::type & >(t); +} + +template +struct get_promotion_policy { + using type = void; +}; + +template +struct get_exception_policy { + using type = void; +}; + + +} // safe_numerics +} // boost + +#endif // BOOST_NUMERIC_SAFE_COMMON_HPP diff --git a/boost/safe_numerics/safe_compare.hpp b/boost/safe_numerics/safe_compare.hpp new file mode 100644 index 0000000..2b52c2d --- /dev/null +++ b/boost/safe_numerics/safe_compare.hpp @@ -0,0 +1,180 @@ +#ifndef BOOST_NUMERIC_SAFE_COMPARE_HPP +#define BOOST_NUMERIC_SAFE_COMPARE_HPP + +// Copyright (c) 2012 Robert Ramey +// +// 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) + +#include +#include + +namespace boost { +namespace safe_numerics { +namespace safe_compare { + +//////////////////////////////////////////////////// +// safe comparison on primitive integral types +namespace safe_compare_detail { + template + using make_unsigned = typename std::conditional< + std::is_signed::value, + std::make_unsigned, + T + >::type; + + // both arguments unsigned or signed + template + struct less_than { + template + constexpr static bool invoke(const T & t, const U & u){ + return t < u; + } + }; + + // T unsigned, U signed + template<> + struct less_than { + template + constexpr static bool invoke(const T & t, const U & u){ + return + (u < 0) ? + false + : + less_than::invoke( + t, + static_cast::type &>(u) + ) + ; + } + }; + // T signed, U unsigned + template<> + struct less_than { + template + constexpr static bool invoke(const T & t, const U & u){ + return + (t < 0) ? + true + : + less_than::invoke( + static_cast::type &>(t), + u + ) + ; + } + }; +} // safe_compare_detail + +template +typename std::enable_if< + std::is_integral::value && std::is_integral::value, + bool +>::type +constexpr less_than(const T & lhs, const U & rhs) { + return safe_compare_detail::less_than< + std::is_signed::value, + std::is_signed::value + >::template invoke(lhs, rhs); +} + +template +typename std::enable_if< + std::is_floating_point::value && std::is_floating_point::value, + bool +>::type +constexpr less_than(const T & lhs, const U & rhs) { + return lhs < rhs; +} + +template +constexpr bool greater_than(const T & lhs, const U & rhs) { + return less_than(rhs, lhs); +} + +template +constexpr bool less_than_equal(const T & lhs, const U & rhs) { + return ! greater_than(lhs, rhs); +} + +template +constexpr bool greater_than_equal(const T & lhs, const U & rhs) { + return ! less_than(lhs, rhs); +} + +namespace safe_compare_detail { + // both arguments unsigned or signed + template + struct equal { + template + constexpr static bool invoke(const T & t, const U & u){ + return t == u; + } + }; + + // T unsigned, U signed + template<> + struct equal { + template + constexpr static bool invoke(const T & t, const U & u){ + return + (u < 0) ? + false + : + equal::invoke( + t, + static_cast::type &>(u) + ) + ; + } + }; + // T signed, U unsigned + template<> + struct equal { + template + constexpr static bool invoke(const T & t, const U & u){ + return + (t < 0) ? + false + : + equal::invoke( + static_cast::type &>(t), + u + ) + ; + } + }; +} // safe_compare_detail + +template +typename std::enable_if< + std::is_integral::value && std::is_integral::value, + bool +>::type +constexpr equal(const T & lhs, const U & rhs) { + return safe_compare_detail::equal< + std::numeric_limits::is_signed, + std::numeric_limits::is_signed + >::template invoke(lhs, rhs); +} + +template +typename std::enable_if< + std::is_floating_point::value && std::is_floating_point::value, + bool +>::type +constexpr equal(const T & lhs, const U & rhs) { + return lhs == rhs; +} + +template +constexpr bool not_equal(const T & lhs, const U & rhs) { + return ! equal(lhs, rhs); +} + +} // safe_compare +} // safe_numerics +} // boost + +#endif // BOOST_NUMERIC_SAFE_COMPARE_HPP diff --git a/boost/safe_numerics/safe_integer.hpp b/boost/safe_numerics/safe_integer.hpp new file mode 100644 index 0000000..a6f1983 --- /dev/null +++ b/boost/safe_numerics/safe_integer.hpp @@ -0,0 +1,42 @@ +#ifndef BOOST_NUMERIC_SAFE_INTEGER_HPP +#define BOOST_NUMERIC_SAFE_INTEGER_HPP + +// Copyright (c) 2012 Robert Ramey +// +// 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) + +// not actually used here - but needed for integer arithmetic +// so this is a good place to include it +#include "checked_integer.hpp" +#include "checked_result_operations.hpp" + +#include "safe_base.hpp" +#include "safe_base_operations.hpp" + +#include "native.hpp" +#include "exception_policies.hpp" + +// specialization for meta functions with safe argument +namespace boost { +namespace safe_numerics { + +template < + class T, + class P = native, + class E = default_exception_policy +> +using safe = safe_base< + T, + ::std::numeric_limits::min(), + ::std::numeric_limits::max(), + P, + E +>; + +} // safe_numerics +} // boost + + +#endif // BOOST_NUMERIC_SAFE_INTEGER_HPP diff --git a/boost/safe_numerics/safe_integer_literal.hpp b/boost/safe_numerics/safe_integer_literal.hpp new file mode 100644 index 0000000..1e0922c --- /dev/null +++ b/boost/safe_numerics/safe_integer_literal.hpp @@ -0,0 +1,260 @@ +#ifndef BOOST_NUMERIC_SAFE_INTEGER_LITERAL_HPP +#define BOOST_NUMERIC_SAFE_INTEGER_LITERAL_HPP + +// Copyright (c) 2012 Robert Ramey +// +// 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) + +#include // for intmax_t/uintmax_t +#include +#include // conditional, enable_if +#include + +#include "utility.hpp" +#include "safe_integer.hpp" +#include "checked_integer.hpp" + +namespace boost { +namespace safe_numerics { + +template +class safe_literal_impl; + +template +struct is_safe > : public std::true_type +{}; + +template +struct get_promotion_policy > { + using type = P; +}; + +template +struct get_exception_policy > { + using type = E; +}; +template +struct base_type > { + using type = T; +}; + +template +constexpr T base_value( + const safe_literal_impl & +) { + return N; +} + +template +inline std::basic_ostream & operator<<( + std::basic_ostream & os, + const safe_literal_impl & +){ + return os << ( + (std::is_same::value + || std::is_same::value + ) ? + static_cast(N) + : + N + ); +} + +template +class safe_literal_impl { + + template< + class CharT, + class Traits + > + friend std::basic_ostream & operator<<( + std::basic_ostream & os, + const safe_literal_impl & + ){ + return os << ( + (::std::is_same::value + || ::std::is_same::value + || ::std::is_same::value + ) ? + static_cast(N) + : + N + ); + }; + +public: + + //////////////////////////////////////////////////////////// + // constructors + // default constructor + constexpr safe_literal_impl(){} + + ///////////////////////////////////////////////////////////////// + // casting operators for intrinsic integers + // convert to any type which is not safe. safe types need to be + // excluded to prevent ambiguous function selection which + // would otherwise occur + template< + class R, + typename std::enable_if< + ! boost::safe_numerics::is_safe::value, + int + >::type = 0 + > + constexpr operator R () const { + // if static values don't overlap, the program can never function + #if 1 + constexpr const interval r_interval; + static_assert( + ! r_interval.excludes(N), + "safe type cannot be constructed with this type" + ); + #endif + + return validate_detail< + R, + std::numeric_limits::min(), + std::numeric_limits::max(), + E + >::return_value(*this); + } + + // non mutating unary operators + constexpr safe_literal_impl operator+() const { // unary plus + return safe_literal_impl(); + } + // after much consideration, I've permitted the resulting value of a unary + // - to change the type in accordance with the promotion policy. + // The C++ standard does invoke integral promotions so it's changing the type as well. + + /* section 5.3.1 &8 of the C++ standard + The operand of the unary - operator shall have arithmetic or unscoped + enumeration type and the result is the negation of its operand. Integral + promotion is performed on integral or enumeration operands. The negative + of an unsigned quantity is computed by subtracting its value from 2n, + where n is the number of bits in the promoted operand. The type of the + result is the type of the promoted operand. + */ + template< + typename Tx, Tx Nx, typename = std::enable_if_t + > + constexpr auto minus_helper() const { + return safe_literal_impl(); + } + + constexpr auto operator-() const { // unary minus + return minus_helper(); + } + + /* section 5.3.1 &10 of the C++ standard + The operand of ~ shall have integral or unscoped enumeration type; + the result is the ones’ complement of its operand. Integral promotions + are performed. The type of the result is the type of the promoted operand. + constexpr safe_literal_impl operator~() const { // invert bits + return safe_literal_impl(); + } + */ + template< + typename Tx, Tx Nx, typename = std::enable_if_t + > + constexpr auto not_helper() const { + return safe_literal_impl(); + } + + constexpr auto operator~() const { // unary minus + return not_helper(); + } +}; + +template< + std::intmax_t N, + class P = void, + class E = void +> +using safe_signed_literal = safe_literal_impl< + typename utility::signed_stored_type, + N, + P, + E +>; + +template< + std::uintmax_t N, + class P = void, + class E = void +> +using safe_unsigned_literal = safe_literal_impl< + typename utility::unsigned_stored_type, + N, + P, + E +>; + +template< + class T, + T N, + class P = void, + class E = void, + typename std::enable_if< + std::is_signed::value, + int + >::type = 0 +> +constexpr auto make_safe_literal_impl() { + return boost::safe_numerics::safe_signed_literal(); +} + +template< + class T, + T N, + class P = void, + class E = void, + typename std::enable_if< + ! std::is_signed::value, + int + >::type = 0 +> +constexpr auto make_safe_literal_impl() { + return boost::safe_numerics::safe_unsigned_literal(); +} + +} // safe_numerics +} // boost + +#define make_safe_literal(n, P, E) \ + boost::safe_numerics::make_safe_literal_impl() + +///////////////////////////////////////////////////////////////// +// numeric limits for safe_literal etc. + +#include + +namespace std { + +template< + typename T, + T N, + class P, + class E +> +class numeric_limits > + : public std::numeric_limits +{ + using SL = boost::safe_numerics::safe_literal_impl; +public: + constexpr static SL lowest() noexcept { + return SL(); + } + constexpr static SL min() noexcept { + return SL(); + } + constexpr static SL max() noexcept { + return SL(); + } +}; + +} // std + +#endif // BOOST_NUMERIC_SAFE_INTEGER_LITERAL_HPP diff --git a/boost/safe_numerics/safe_integer_range.hpp b/boost/safe_numerics/safe_integer_range.hpp new file mode 100644 index 0000000..e9fce84 --- /dev/null +++ b/boost/safe_numerics/safe_integer_range.hpp @@ -0,0 +1,60 @@ +#ifndef BOOST_NUMERIC_SAFE_INTEGER_RANGE_HPP +#define BOOST_NUMERIC_SAFE_INTEGER_RANGE_HPP + +// Copyright (c) 2012 Robert Ramey +// +// 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) + +#include // intmax_t, uintmax_t + +#include "utility.hpp" +#include "safe_integer.hpp" +#include "native.hpp" +#include "exception_policies.hpp" + +///////////////////////////////////////////////////////////////// +// higher level types implemented in terms of safe_base + +namespace boost { +namespace safe_numerics { + +///////////////////////////////////////////////////////////////// +// safe_signed_range + +template < + std::intmax_t Min, + std::intmax_t Max, + class P = native, + class E = default_exception_policy +> +using safe_signed_range = safe_base< + typename utility::signed_stored_type, + static_cast >(Min), + static_cast >(Max), + P, + E +>; + +///////////////////////////////////////////////////////////////// +// safe_unsigned_range + +template < + std::uintmax_t Min, + std::uintmax_t Max, + class P = native, + class E = default_exception_policy +> +using safe_unsigned_range = safe_base< + typename utility::unsigned_stored_type, + static_cast >(Min), + static_cast >(Max), + P, + E +>; + +} // safe_numerics +} // boost + +#endif // BOOST_NUMERIC_SAFE_RANGE_HPP diff --git a/boost/safe_numerics/utility.hpp b/boost/safe_numerics/utility.hpp new file mode 100644 index 0000000..c0cc2dd --- /dev/null +++ b/boost/safe_numerics/utility.hpp @@ -0,0 +1,280 @@ +#ifndef BOOST_NUMERIC_UTILITY_HPP +#define BOOST_NUMERIC_UTILITY_HPP + +// Copyright (c) 2015 Robert Ramey +// +// 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) + +#include // intmax_t, uintmax_t, uint8_t, ... +#include +#include // conditional +#include +#include +#include // pair + +#include // (u)int_t<>::least, exact + +namespace boost { +namespace safe_numerics { +namespace utility { + +/////////////////////////////////////////////////////////////////////////////// +// used for debugging + +// provokes warning message with names of type T +// usage - print_types; +// see https://cukic.co/2019/02/19/tmp-testing-and-debugging-templates + +/* +template +using print_type = typename Tx::error_message; +*/ +template +struct [[deprecated]] print_types {}; + +// display value of constexpr during compilation +// usage print_value(N) pn; +template +struct print_value +{ + enum test : char { + value = N < 0 ? N - 256 : N + 256 + }; +}; + +#if 0 +// static warning - same as static_assert but doesn't +// stop compilation. +template +struct static_test{}; + +template <> +struct static_test{ + [[deprecated]] static_test(){} +}; + +template +constexpr void static_warning(const T){ + //using x = static_test; + const static_test x; +} + +#endif + +/* +// can be called by constexpr to produce a compile time +// trap of parameter passed is false. +// usage constexpr_assert(bool) +constexpr int constexpr_assert(const bool tf){ + return 1 / tf; +} +*/ + +/////////////////////////////////////////////////////////////////////////////// +// return an integral constant equal to the the number of bits +// held by some integer type (including the sign bit) + +template +using bits_type = std::integral_constant< + int, + std::numeric_limits::digits + + (std::numeric_limits::is_signed ? 1 : 0) +>; + +/* +From http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogObvious +Find the log base 2 of an integer with a lookup table + + static const char LogTable256[256] = + { + #define LT(n) n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n + -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, + LT(4), LT(5), LT(5), LT(6), LT(6), LT(6), LT(6), + LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7) + }; + + unsigned int v; // 32-bit word to find the log of + unsigned r; // r will be lg(v) + register unsigned int t, tt; // temporaries + + if (tt = v >> 16) + { + r = (t = tt >> 8) ? 24 + LogTable256[t] : 16 + LogTable256[tt]; + } + else + { + r = (t = v >> 8) ? 8 + LogTable256[t] : LogTable256[v]; + } + +The lookup table method takes only about 7 operations to find the log of a 32-bit value. +If extended for 64-bit quantities, it would take roughly 9 operations. Another operation +can be trimmed off by using four tables, with the possible additions incorporated into each. +Using int table elements may be faster, depending on your architecture. +*/ + +namespace ilog2_detail { + + // I've "improved" the above and recast as C++ code which depends upon + // the optimizer to minimize the operations. This should result in + // nine operations to calculate the position of the highest order + // bit in a 64 bit number. RR + + constexpr static unsigned int ilog2(const boost::uint_t<8>::exact & t){ + #define LT(n) n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n + const char LogTable256[256] = { + static_cast(-1), 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, + LT(4), LT(5), LT(5), LT(6), LT(6), LT(6), LT(6), + LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7) + }; + return LogTable256[t]; + } + constexpr static unsigned int ilog2(const boost::uint_t<16>::exact & t){ + const boost::uint_t<8>::exact upper_half = (t >> 8); + return upper_half == 0 + ? ilog2(static_cast::exact>(t)) + : 8 + ilog2(upper_half); + } + constexpr static unsigned int ilog2(const boost::uint_t<32>::exact & t){ + const boost::uint_t<16>::exact upper_half = (t >> 16); + return upper_half == 0 + ? ilog2(static_cast::exact>(t)) + : 16 + ilog2(upper_half); + } + constexpr static unsigned int ilog2(const boost::uint_t<64>::exact & t){ + const boost::uint_t<32>::exact upper_half = (t >> 32); + return upper_half == 0 + ? ilog2(static_cast::exact>(t)) + : 32 + ilog2(upper_half); + } + +} // ilog2_detail + +template +constexpr unsigned int ilog2(const T & t){ +// log not defined for negative numbers +// assert(t > 0); + if(t == 0) + return 0; + return ilog2_detail::ilog2( + static_cast< + typename boost::uint_t< + bits_type::value + >::least + >(t) + ); +} + +// the number of bits required to render the value in x +// including sign bit +template +constexpr unsigned int significant_bits(const T & t){ + return 1 + ((t < 0) ? ilog2(~t) : ilog2(t)); +} + +/* +// give the value t, return the number which corresponds +// to all 1's which is higher than that number +template +constexpr unsigned int bits_value(const T & t){ + const unsigned int sb = significant_bits(t); + const unsigned int sb_max = significant_bits(std::numeric_limits::max()); + return sb < sb_max ? ((sb << 1) - 1) : std::numeric_limits::max(); +} +*/ + +/////////////////////////////////////////////////////////////////////////////// +// meta functions returning types + +// If we use std::max in here we get internal compiler errors +// with MSVC (tested VC2017) ... + +// Notes from https://en.cppreference.com/w/cpp/algorithm/max +// Capturing the result of std::max by reference if one of the parameters +// is rvalue produces a dangling reference if that parameter is returned. + +template +// turns out this problem crashes all versions of gcc compilers. So +// make sure we return by value +//constexpr const T & max( +constexpr T max( + const T & lhs, + const T & rhs +){ + return lhs > rhs ? lhs : rhs; +} + +// given a signed range, return type required to hold all the values +// in the range +template< + std::intmax_t Min, + std::intmax_t Max +> +using signed_stored_type = typename boost::int_t< + max( + significant_bits(Min), + significant_bits(Max) + ) + 1 +>::least ; + +// given an unsigned range, return type required to hold all the values +// in the range +template< + std::uintmax_t Min, + std::uintmax_t Max +> +// unsigned range +using unsigned_stored_type = typename boost::uint_t< + significant_bits(Max) +>::least; + +/////////////////////////////////////////////////////////////////////////////// +// constexpr functions + +// need our own version because official version +// a) is not constexpr +// b) is not guarenteed to handle non-assignable types +template +constexpr std::pair +minmax(const std::initializer_list l){ + assert(l.size() > 0); + const T * minimum = l.begin(); + const T * maximum = l.begin(); + for(const T * i = l.begin(); i != l.end(); ++i){ + if(*i < * minimum) + minimum = i; + else + if(* maximum < *i) + maximum = i; + } + return std::pair{* minimum, * maximum}; +} + +// for any given t +// a) figure number of significant bits +// b) return a value with all significant bits set +// so for example: +// 3 == round_out(2) because +// 2 == 10 and 3 == 11 +template +constexpr T round_out(const T & t){ + if(t >= 0){ + const std::uint8_t sb = utility::significant_bits(t); + return (sb < sizeof(T) * 8) + ? ((T)1 << sb) - 1 + : std::numeric_limits::max(); + } + else{ + const std::uint8_t sb = utility::significant_bits(~t); + return (sb < sizeof(T) * 8) + ? ~(((T)1 << sb) - 1) + : std::numeric_limits::min(); + } +} + +} // utility +} // safe_numerics +} // boost + +#endif // BOOST_NUMERIC_UTILITY_HPP