diff --git a/include/boost/math/big_number.hpp b/include/boost/math/big_number.hpp index b140dbf7..082c95b6 100644 --- a/include/boost/math/big_number.hpp +++ b/include/boost/math/big_number.hpp @@ -17,6 +17,9 @@ #include #include #include +#include +#include +#include #include namespace boost{ namespace math{ @@ -201,6 +204,65 @@ public: } return *this; } + // + // These operators are *not* proto-ized. + // The issue is that the increment/decrement must happen + // even if the result of the operator *is never used*. + // Possibly we could modify our expression wrapper to + // execute the increment/decrement on destruction, but + // correct implemetation will be tricky, so defered for now... + // + big_number& operator++() + { + BOOST_STATIC_ASSERT_MSG(is_extended_integer::value, "The increment operation is only valid for integer types"); + using big_num_default_ops::increment; + increment(m_backend); + return *this; + } + + big_number& operator--() + { + BOOST_STATIC_ASSERT_MSG(is_extended_integer::value, "The increment operation is only valid for integer types"); + using big_num_default_ops::decrement; + decrement(m_backend); + return *this; + } + + big_number operator++(int) + { + BOOST_STATIC_ASSERT_MSG(is_extended_integer::value, "The increment operation is only valid for integer types"); + using big_num_default_ops::increment; + self_type temp(*this); + increment(m_backend); + return temp; + } + + big_number operator--(int) + { + BOOST_STATIC_ASSERT_MSG(is_extended_integer::value, "The increment operation is only valid for integer types"); + using big_num_default_ops::decrement; + self_type temp(*this); + decrement(m_backend); + return temp; + } + + template + typename enable_if, big_number&>::type operator <<= (V val) + { + BOOST_STATIC_ASSERT_MSG(is_extended_integer::value, "The left-shift operation is only valid for integer types"); + check_shift_range(val, mpl::bool_<(sizeof(V) > sizeof(std::size_t))>(), is_signed()); + left_shift(m_backend, canonical_value(val)); + return *this; + } + + template + typename enable_if, big_number&>::type operator >>= (V val) + { + BOOST_STATIC_ASSERT_MSG(is_extended_integer::value, "The right-shift operation is only valid for integer types"); + check_shift_range(val, mpl::bool_<(sizeof(V) > sizeof(std::size_t))>(), is_signed()); + right_shift(m_backend, canonical_value(val)); + return *this; + } template typename enable_if, big_number& >::type @@ -237,6 +299,14 @@ public: return *this; } + // + // swap: + // + void swap(self_type& other) + { + m_backend.swap(other.backend()); + } + // // String conversion functions: // @@ -284,6 +354,29 @@ public: return m_backend; } private: + template + void check_shift_range(V val, const mpl::true_&, const mpl::true_&) + { + if(val > std::numeric_limits::max()) + BOOST_THROW_EXCEPTION(std::out_of_range("Can not shift by a value greater than std::numeric_limits::max().")); + if(val < 0) + BOOST_THROW_EXCEPTION(std::out_of_range("Can not shift by a negative value.")); + } + template + void check_shift_range(V val, const mpl::false_&, const mpl::true_&) + { + if(val < 0) + BOOST_THROW_EXCEPTION(std::out_of_range("Can not shift by a negative value.")); + } + template + void check_shift_range(V val, const mpl::true_&, const mpl::false_&) + { + if(val > std::numeric_limits::max()) + BOOST_THROW_EXCEPTION(std::out_of_range("Can not shift by a value greater than std::numeric_limits::max().")); + } + template + void check_shift_range(V val, const mpl::false_&, const mpl::false_&){} + template void do_assign(const Exp& e, const detail::add_immediates&) { @@ -363,6 +456,7 @@ private: template void do_assign(const Exp& e, const detail::modulus_immediates&) { + BOOST_STATIC_ASSERT_MSG(is_extended_integer::value, "The modulus operation is only valid for integer types"); using big_num_default_ops::modulus; typedef typename proto::tag_of::type>::type left_tag; typedef typename proto::tag_of::type>::type right_tag; @@ -575,6 +669,64 @@ private: typedef typename proto::arity_of::type tag_type; do_assign_function(e, tag_type()); } + template + void do_assign(const Exp& e, const proto::tag::shift_left&) + { + // We can only shift by an integer value, not an arbitrary expression: + typedef typename proto::result_of::left::type left_type; + typedef typename proto::result_of::right::type right_type; + typedef typename proto::arity_of::type right_arity; + BOOST_STATIC_ASSERT_MSG(right_arity::value == 0, "The left shift operator requires an integer value for the shift operand."); + typedef typename proto::result_of::value::type right_value_type; + BOOST_STATIC_ASSERT_MSG(is_integral::value, "The left shift operator requires an integer value for the shift operand."); + typedef typename proto::tag_of::type tag_type; + do_assign_left_shift(proto::left(e), canonical_value(proto::value(proto::right(e))), tag_type()); + } + + template + void do_assign(const Exp& e, const proto::tag::shift_right&) + { + // We can only shift by an integer value, not an arbitrary expression: + typedef typename proto::result_of::left::type left_type; + typedef typename proto::result_of::right::type right_type; + typedef typename proto::arity_of::type right_arity; + BOOST_STATIC_ASSERT_MSG(right_arity::value == 0, "The left shift operator requires an integer value for the shift operand."); + typedef typename proto::result_of::value::type right_value_type; + BOOST_STATIC_ASSERT_MSG(is_integral::value, "The left shift operator requires an integer value for the shift operand."); + typedef typename proto::tag_of::type tag_type; + do_assign_right_shift(proto::left(e), canonical_value(proto::value(proto::right(e))), tag_type()); + } + + template + void do_assign_right_shift(const Exp& e, const Val& val, const proto::tag::terminal&) + { + using big_num_default_ops::right_shift; + right_shift(m_backend, canonical_value(proto::value(e)), val); + } + + template + void do_assign_left_shift(const Exp& e, const Val& val, const proto::tag::terminal&) + { + using big_num_default_ops::left_shift; + left_shift(m_backend, canonical_value(proto::value(e)), val); + } + + template + void do_assign_right_shift(const Exp& e, const Val& val, const Tag&) + { + using big_num_default_ops::right_shift; + self_type temp(e); + right_shift(m_backend, temp.backend(), val); + } + + template + void do_assign_left_shift(const Exp& e, const Val& val, const Tag&) + { + using big_num_default_ops::left_shift; + self_type temp(e); + left_shift(m_backend, temp.backend(), val); + } + template void do_assign_function(const Exp& e, const mpl::long_<1>&) { @@ -804,6 +956,7 @@ private: template void do_modulus(const Exp& e, const proto::tag::terminal&) { + BOOST_STATIC_ASSERT_MSG(is_extended_integer::value, "The modulus operation is only valid for integer types"); using big_num_default_ops::modulus; modulus(m_backend, canonical_value(proto::value(e))); } @@ -811,6 +964,7 @@ private: template void do_modulus(const Exp& e, const Unknown&) { + BOOST_STATIC_ASSERT_MSG(is_extended_integer::value, "The modulus operation is only valid for integer types"); using big_num_default_ops::modulus; self_type temp(e); modulus(m_backend, canonical_value(proto::value(temp))); diff --git a/include/boost/math/big_number/big_number_base.hpp b/include/boost/math/big_number/big_number_base.hpp index b3caed30..5a75e71d 100644 --- a/include/boost/math/big_number/big_number_base.hpp +++ b/include/boost/math/big_number/big_number_base.hpp @@ -29,6 +29,8 @@ struct big_number_grammar , proto::unary_plus< big_number_grammar > , proto::negate< big_number_grammar > , proto::modulus + , proto::shift_left + , proto::shift_right > {}; diff --git a/include/boost/math/big_number/default_ops.hpp b/include/boost/math/big_number/default_ops.hpp index 86a2aa09..c5b3f581 100644 --- a/include/boost/math/big_number/default_ops.hpp +++ b/include/boost/math/big_number/default_ops.hpp @@ -146,6 +146,34 @@ inline void modulus(T& t, const U& u, const V& v) } } +template +inline void increment(T& val) +{ + typedef typename mpl::front::type ui_type; + add(val, static_cast(1u)); +} + +template +inline void decrement(T& val) +{ + typedef typename mpl::front::type ui_type; + subtract(val, static_cast(1u)); +} + +template +inline void left_shift(T& result, const T& arg, const V val) +{ + result = arg; + left_shift(result, val); +} + +template +inline void right_shift(T& result, const T& arg, const V val) +{ + result = arg; + right_shift(result, val); +} + // // Functions: // diff --git a/include/boost/math/big_number/gmp.hpp b/include/boost/math/big_number/gmp.hpp index b137cf52..6397d354 100644 --- a/include/boost/math/big_number/gmp.hpp +++ b/include/boost/math/big_number/gmp.hpp @@ -862,6 +862,26 @@ inline void divide(gmp_int& t, long i) if(i < 0) mpz_neg(t.data(), t.data()); } +template +inline void left_shift(gmp_int& t, UI i) +{ + mpz_mul_2exp(t.data(), t.data(), static_cast(i)); +} +template +inline void right_shift(gmp_int& t, UI i) +{ + mpz_fdiv_q_2exp(t.data(), t.data(), static_cast(i)); +} +template +inline void left_shift(gmp_int& t, const gmp_int& v, UI i) +{ + mpz_mul_2exp(t.data(), v.data(), static_cast(i)); +} +template +inline void right_shift(gmp_int& t, const gmp_int& v, UI i) +{ + mpz_fdiv_q_2exp(t.data(), v.data(), static_cast(i)); +} inline void add(gmp_int& t, const gmp_int& p, const gmp_int& o) { @@ -1087,8 +1107,8 @@ private: { std::numeric_limits > >::epsilon(); std::numeric_limits > >::round_error(); - std::numeric_limits > >::min(); - std::numeric_limits > >::max(); + (std::numeric_limits > >::min)(); + (std::numeric_limits > >::max)(); } void do_nothing()const{} }; diff --git a/include/boost/math/big_number/mpfr.hpp b/include/boost/math/big_number/mpfr.hpp index 1aa60cae..380e4eaa 100644 --- a/include/boost/math/big_number/mpfr.hpp +++ b/include/boost/math/big_number/mpfr.hpp @@ -741,8 +741,8 @@ private: { std::numeric_limits > >::epsilon(); std::numeric_limits > >::round_error(); - std::numeric_limits > >::min(); - std::numeric_limits > >::max(); + (std::numeric_limits > >::min)(); + (std::numeric_limits > >::max)(); std::numeric_limits > >::infinity(); std::numeric_limits > >::quiet_NaN(); } diff --git a/math/test/test_arithmetic.cpp b/math/test/test_arithmetic.cpp index 8df99829..70c44787 100644 --- a/math/test/test_arithmetic.cpp +++ b/math/test/test_arithmetic.cpp @@ -36,6 +36,11 @@ #include #endif +#define BOOST_TEST_THROW(x, EX)\ + try { x; BOOST_ERROR("Expected exception not thrown"); } \ + catch(const EX&){}\ + catch(...){ BOOST_ERROR("Incorrect exception type thrown"); } + template void test_integer_ops(const boost::mpl::false_&){} @@ -104,6 +109,41 @@ void test_integer_ops(const boost::mpl::true_&) a %= -7LL; BOOST_TEST(a == -20 % -7); #endif + a = 20; + BOOST_TEST(++a == 21); + BOOST_TEST(--a == 20); + BOOST_TEST(a++ == 20); + BOOST_TEST(a == 21); + BOOST_TEST(a-- == 21); + BOOST_TEST(a == 20); + a = 2000; + a <<= 20; + BOOST_TEST(a == 2000L << 20); + a >>= 20; + BOOST_TEST(a == 2000); + a <<= 20u; + BOOST_TEST(a == 2000L << 20); + a >>= 20u; + BOOST_TEST(a == 2000); + BOOST_TEST_THROW(a <<= -20, std::out_of_range); + BOOST_TEST_THROW(a >>= -20, std::out_of_range); +#ifndef BOOST_NO_LONG_LONG + if(sizeof(long long) > sizeof(std::size_t)) + { + // extreme values should trigger an exception: + BOOST_TEST_THROW(a >>= (1uLL << (sizeof(long long) * CHAR_BIT - 2)), std::out_of_range); + BOOST_TEST_THROW(a <<= (1uLL << (sizeof(long long) * CHAR_BIT - 2)), std::out_of_range); + } +#endif + a = 20; + b = a << 20; + BOOST_TEST(b == (20 << 20)); + b = a >> 2; + BOOST_TEST(b == (20 >> 2)); + b = (a + 2) << 10; + BOOST_TEST(b == (22 << 10)); + b = (a + 3) >> 3; + BOOST_TEST(b == (23 >> 3)); // // Non-member functions: //