From 827f114525f7c0872ed1477252717f088a318e90 Mon Sep 17 00:00:00 2001 From: Francesco Biscani Date: Sat, 27 Feb 2016 00:24:54 +0100 Subject: [PATCH 01/17] Attempt another coverage service. [skip appveyor] --- .travis.yml | 46 ++++++++++++++++++++--------------------- tools/install_travis.sh | 2 +- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/.travis.yml b/.travis.yml index 564f716d3..01ec09263 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,30 +36,30 @@ env: matrix: include: - - compiler: gcc - env: BUILD_TYPE="Release" - - compiler: gcc - env: BUILD_TYPE="Debug" SPLIT_TEST_NUM="0" - - compiler: gcc - env: BUILD_TYPE="Debug" SPLIT_TEST_NUM="1" - - compiler: gcc - env: BUILD_TYPE="Debug" SPLIT_TEST_NUM="2" - - compiler: clang - env: BUILD_TYPE="Release" + #- compiler: gcc + #env: BUILD_TYPE="Release" + #- compiler: gcc + #env: BUILD_TYPE="Debug" SPLIT_TEST_NUM="0" + #- compiler: gcc + #env: BUILD_TYPE="Debug" SPLIT_TEST_NUM="1" + #- compiler: gcc + #env: BUILD_TYPE="Debug" SPLIT_TEST_NUM="2" + #- compiler: clang + #env: BUILD_TYPE="Release" - compiler: clang env: BUILD_TYPE="Debug" SPLIT_TEST_NUM="0" - compiler: clang env: BUILD_TYPE="Debug" SPLIT_TEST_NUM="1" - compiler: clang env: BUILD_TYPE="Debug" SPLIT_TEST_NUM="2" - - compiler: clang - env: BUILD_TYPE="Python2" - - compiler: clang - env: BUILD_TYPE="Python3" - - compiler: clang - env: BUILD_TYPE="Tutorial" - - compiler: gcc - env: BUILD_TYPE="Doxygen" + #- compiler: clang + #env: BUILD_TYPE="Python2" + #- compiler: clang + #env: BUILD_TYPE="Python3" + #- compiler: clang + #env: BUILD_TYPE="Tutorial" + #- compiler: gcc + #env: BUILD_TYPE="Doxygen" install: - if [[ "${CC}" == "gcc" ]]; then @@ -76,10 +76,10 @@ script: - mkdir build - cd build - source ../tools/install_travis.sh -#after_success: - #- if [[ "${BUILD_TYPE}" == "Debug" && "${PIRANHA_COMPILER}" == "clang" ]]; then - #cd tests; - #/home/travis/.local/bin/coveralls --gcov-options '\-lp'; - #fi +after_success: + - if [[ "${BUILD_TYPE}" == "Debug" && "${PIRANHA_COMPILER}" == "clang" ]]; then + cd tests; + bash <(curl -s https://codecov.io/bash); + fi notifications: email: false diff --git a/tools/install_travis.sh b/tools/install_travis.sh index 75e30f714..7e25865d3 100644 --- a/tools/install_travis.sh +++ b/tools/install_travis.sh @@ -11,7 +11,7 @@ if [[ "${BUILD_TYPE}" == "Debug" ]]; then make; ctest -E "thread|memory" -V; elif [[ "${PIRANHA_COMPILER}" == "clang" ]]; then - cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_TUTORIAL=yes -DPIRANHA_TEST_SPLIT=yes -DPIRANHA_TEST_SPLIT_NUM=${SPLIT_TEST_NUM} ../; + cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="-coverage" -DBUILD_TUTORIAL=yes -DPIRANHA_TEST_SPLIT=yes -DPIRANHA_TEST_SPLIT_NUM=${SPLIT_TEST_NUM} ../; make; ctest -E "thread" -V; fi From 26ce4567a238996ad45980343929349d4359950d Mon Sep 17 00:00:00 2001 From: Francesco Biscani Date: Sat, 27 Feb 2016 00:36:10 +0100 Subject: [PATCH 02/17] mp_integer: fix clang warning in the test. --- tests/mp_integer_03.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/mp_integer_03.cpp b/tests/mp_integer_03.cpp index 30fbc19d9..964ddedc2 100644 --- a/tests/mp_integer_03.cpp +++ b/tests/mp_integer_03.cpp @@ -651,6 +651,7 @@ struct divexact_tester void operator()(const T &) { using int_type = mp_integer; + { // A few simple checks. int_type out, n1{1}, n2{1}; int_type::_divexact(out,n1,n2); @@ -678,6 +679,9 @@ struct divexact_tester int_type::_divexact(n1,n1,n1); BOOST_CHECK(n1.is_static()); BOOST_CHECK_EQUAL(n1,1); + // Try the exception. + BOOST_CHECK_THROW(int_type::_divexact(n1,n1,int_type{}),zero_division_error); + } // Random testing. std::uniform_int_distribution int_dist(std::numeric_limits::min(),std::numeric_limits::max()); std::uniform_int_distribution p_dist(0,1); @@ -714,8 +718,6 @@ struct divexact_tester int_type::_divexact(out,int_type{},n1n2); BOOST_CHECK_EQUAL(out,0); } - // Try the exception. - BOOST_CHECK_THROW(int_type::_divexact(n1,n1,int_type{}),zero_division_error); } }; From e5d8efce9d444612319eaf000c629152d41a2ce7 Mon Sep 17 00:00:00 2001 From: Francesco Biscani Date: Sat, 27 Feb 2016 01:03:20 +0100 Subject: [PATCH 03/17] Revert "Attempt another coverage service." This reverts commit 827f114525f7c0872ed1477252717f088a318e90. --- .travis.yml | 46 ++++++++++++++++++++--------------------- tools/install_travis.sh | 2 +- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/.travis.yml b/.travis.yml index 01ec09263..564f716d3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,30 +36,30 @@ env: matrix: include: - #- compiler: gcc - #env: BUILD_TYPE="Release" - #- compiler: gcc - #env: BUILD_TYPE="Debug" SPLIT_TEST_NUM="0" - #- compiler: gcc - #env: BUILD_TYPE="Debug" SPLIT_TEST_NUM="1" - #- compiler: gcc - #env: BUILD_TYPE="Debug" SPLIT_TEST_NUM="2" - #- compiler: clang - #env: BUILD_TYPE="Release" + - compiler: gcc + env: BUILD_TYPE="Release" + - compiler: gcc + env: BUILD_TYPE="Debug" SPLIT_TEST_NUM="0" + - compiler: gcc + env: BUILD_TYPE="Debug" SPLIT_TEST_NUM="1" + - compiler: gcc + env: BUILD_TYPE="Debug" SPLIT_TEST_NUM="2" + - compiler: clang + env: BUILD_TYPE="Release" - compiler: clang env: BUILD_TYPE="Debug" SPLIT_TEST_NUM="0" - compiler: clang env: BUILD_TYPE="Debug" SPLIT_TEST_NUM="1" - compiler: clang env: BUILD_TYPE="Debug" SPLIT_TEST_NUM="2" - #- compiler: clang - #env: BUILD_TYPE="Python2" - #- compiler: clang - #env: BUILD_TYPE="Python3" - #- compiler: clang - #env: BUILD_TYPE="Tutorial" - #- compiler: gcc - #env: BUILD_TYPE="Doxygen" + - compiler: clang + env: BUILD_TYPE="Python2" + - compiler: clang + env: BUILD_TYPE="Python3" + - compiler: clang + env: BUILD_TYPE="Tutorial" + - compiler: gcc + env: BUILD_TYPE="Doxygen" install: - if [[ "${CC}" == "gcc" ]]; then @@ -76,10 +76,10 @@ script: - mkdir build - cd build - source ../tools/install_travis.sh -after_success: - - if [[ "${BUILD_TYPE}" == "Debug" && "${PIRANHA_COMPILER}" == "clang" ]]; then - cd tests; - bash <(curl -s https://codecov.io/bash); - fi +#after_success: + #- if [[ "${BUILD_TYPE}" == "Debug" && "${PIRANHA_COMPILER}" == "clang" ]]; then + #cd tests; + #/home/travis/.local/bin/coveralls --gcov-options '\-lp'; + #fi notifications: email: false diff --git a/tools/install_travis.sh b/tools/install_travis.sh index 7e25865d3..75e30f714 100644 --- a/tools/install_travis.sh +++ b/tools/install_travis.sh @@ -11,7 +11,7 @@ if [[ "${BUILD_TYPE}" == "Debug" ]]; then make; ctest -E "thread|memory" -V; elif [[ "${PIRANHA_COMPILER}" == "clang" ]]; then - cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="-coverage" -DBUILD_TUTORIAL=yes -DPIRANHA_TEST_SPLIT=yes -DPIRANHA_TEST_SPLIT_NUM=${SPLIT_TEST_NUM} ../; + cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_TUTORIAL=yes -DPIRANHA_TEST_SPLIT=yes -DPIRANHA_TEST_SPLIT_NUM=${SPLIT_TEST_NUM} ../; make; ctest -E "thread" -V; fi From df1d17b586ad31195aee945a6954cf7e5b8ccadf Mon Sep 17 00:00:00 2001 From: Francesco Biscani Date: Sat, 27 Feb 2016 01:16:03 +0100 Subject: [PATCH 04/17] Another try. --- .travis.yml | 55 +++++++++++++++++++++-------------------- tools/install_travis.sh | 2 +- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/.travis.yml b/.travis.yml index 564f716d3..f33e9235e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,30 +36,30 @@ env: matrix: include: - - compiler: gcc - env: BUILD_TYPE="Release" - - compiler: gcc - env: BUILD_TYPE="Debug" SPLIT_TEST_NUM="0" - - compiler: gcc - env: BUILD_TYPE="Debug" SPLIT_TEST_NUM="1" - - compiler: gcc - env: BUILD_TYPE="Debug" SPLIT_TEST_NUM="2" - - compiler: clang - env: BUILD_TYPE="Release" + #- compiler: gcc + #env: BUILD_TYPE="Release" + #- compiler: gcc + #env: BUILD_TYPE="Debug" SPLIT_TEST_NUM="0" + #- compiler: gcc + #env: BUILD_TYPE="Debug" SPLIT_TEST_NUM="1" + #- compiler: gcc + #env: BUILD_TYPE="Debug" SPLIT_TEST_NUM="2" + #- compiler: clang + #env: BUILD_TYPE="Release" - compiler: clang env: BUILD_TYPE="Debug" SPLIT_TEST_NUM="0" - - compiler: clang - env: BUILD_TYPE="Debug" SPLIT_TEST_NUM="1" - - compiler: clang - env: BUILD_TYPE="Debug" SPLIT_TEST_NUM="2" - - compiler: clang - env: BUILD_TYPE="Python2" - - compiler: clang - env: BUILD_TYPE="Python3" - - compiler: clang - env: BUILD_TYPE="Tutorial" - - compiler: gcc - env: BUILD_TYPE="Doxygen" + #- compiler: clang + #env: BUILD_TYPE="Debug" SPLIT_TEST_NUM="1" + #- compiler: clang + #env: BUILD_TYPE="Debug" SPLIT_TEST_NUM="2" + #- compiler: clang + #env: BUILD_TYPE="Python2" + #- compiler: clang + #env: BUILD_TYPE="Python3" + #- compiler: clang + #env: BUILD_TYPE="Tutorial" + #- compiler: gcc + #env: BUILD_TYPE="Doxygen" install: - if [[ "${CC}" == "gcc" ]]; then @@ -76,10 +76,11 @@ script: - mkdir build - cd build - source ../tools/install_travis.sh -#after_success: - #- if [[ "${BUILD_TYPE}" == "Debug" && "${PIRANHA_COMPILER}" == "clang" ]]; then - #cd tests; - #/home/travis/.local/bin/coveralls --gcov-options '\-lp'; - #fi +after_success: + - if [[ "${BUILD_TYPE}" == "Debug" && "${PIRANHA_COMPILER}" == "clang" ]]; then + pip install --user cpp-coveralls; + cd tests; + coveralls --gcov llvm-cov --encoding iso-8859-1 || echo 'Coveralls upload failed.'; + fi notifications: email: false diff --git a/tools/install_travis.sh b/tools/install_travis.sh index 75e30f714..d61a16c9b 100644 --- a/tools/install_travis.sh +++ b/tools/install_travis.sh @@ -11,7 +11,7 @@ if [[ "${BUILD_TYPE}" == "Debug" ]]; then make; ctest -E "thread|memory" -V; elif [[ "${PIRANHA_COMPILER}" == "clang" ]]; then - cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_TUTORIAL=yes -DPIRANHA_TEST_SPLIT=yes -DPIRANHA_TEST_SPLIT_NUM=${SPLIT_TEST_NUM} ../; + cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="--coverage" -DBUILD_TUTORIAL=yes -DPIRANHA_TEST_SPLIT=yes -DPIRANHA_TEST_SPLIT_NUM=${SPLIT_TEST_NUM} ../; make; ctest -E "thread" -V; fi From d3d4acf6f290e8998da376feeb3a50bfb2c6b0e3 Mon Sep 17 00:00:00 2001 From: Francesco Biscani Date: Sat, 27 Feb 2016 01:18:30 +0100 Subject: [PATCH 05/17] Add the ternary form for division and implement it for mp_integer. --- src/math.hpp | 91 +++++++++++++++++++++++++++++++++++++++++ src/mp_integer.hpp | 81 ++++++++++++++++++++++++++++++++++++ tests/math.cpp | 19 +++++++++ tests/mp_integer_03.cpp | 61 +++++++++++++++++++++++++++ 4 files changed, 252 insertions(+) diff --git a/src/math.hpp b/src/math.hpp index 70b7b18cd..c130a08fb 100644 --- a/src/math.hpp +++ b/src/math.hpp @@ -2320,6 +2320,79 @@ inline auto mul3(T &a, const T &b, const T &c) -> decltype(mul3_impl()(a,b,c) return mul3_impl()(a,b,c); } +/// Default functor for the implementation of piranha::math::div3(). +/** + * This functor should be specialised via the \p std::enable_if mechanism. + */ +template +struct div3_impl +{ + /// Call operator. + /** + * \note + * This operator is enabled only if the expression a = b / c is well-formed. + * + * This operator will return the result of the expression a = b / c. + * + * @param[out] a the return value. + * @param[in] b the first operand. + * @param[in] c the second operand. + * + * @return a = b / c. + * + * @throws unspecified any exception thrown by the invoked binary and/or assignment operators + * of \p U. + */ + template + auto operator()(U &a, const U &b, const U &c) const -> decltype(a = b / c) + { + return a = b / c; + } +}; + +/// Specialisation of the piranha::math::div3() functor for integral types. +/** + * This specialisation is activated when \p T is an integral type. + */ +template +struct div3_impl::value>::type> +{ + /// Call operator. + /** + * This operator will return the expression a = static_cast(b / c), with b / c + * forcibly cast back to \p T in order to avoid compiler warnings with short integral types. + * + * @param[out] a the return value. + * @param[in] b the first operand. + * @param[in] c the second operand. + * + * @return a = static_cast(b / c). + */ + T &operator()(T &a, const T &b, const T &c) const + { + return a = static_cast(b / c); + } +}; + +/// Ternary division. +/** + * This function will set \p a to b / c. The actual implementation of this function is in the piranha::math::div3_impl functor's + * call operator. + * + * @param[out] a the return value. + * @param[in] b the first operand. + * @param[in] c the second operand. + * + * @return the value returned by the call operator of piranha::math::div3_impl. + * + * @throws unspecified any exception thrown by the call operator of the piranha::math::div3_impl functor. + */ +template +inline auto div3(T &a, const T &b, const T &c) -> decltype(div3_impl()(a,b,c)) +{ + return div3_impl()(a,b,c); +} + } /// Detect piranha::math::add3(). @@ -2379,6 +2452,24 @@ class has_mul3: detail::sfinae_types template const bool has_mul3::value; +/// Detect piranha::math::div3(). +/** + * The type trait will be \p true if piranha::math::div3() can be used on instances of type \p T, + * \p false otherwise. + */ +template +class has_div3: detail::sfinae_types +{ + template + static auto test(const T1 &) -> decltype(math::div3(std::declval(),std::declval(),std::declval()),void(),yes()); + static no test(...); + public: + /// Value of the type trait. + static const bool value = std::is_same())),yes>::value; +}; + +template +const bool has_div3::value; } diff --git a/src/mp_integer.hpp b/src/mp_integer.hpp index 9289a012c..6c616dd79 100644 --- a/src/mp_integer.hpp +++ b/src/mp_integer.hpp @@ -3218,6 +3218,66 @@ class mp_integer } return binary_div(x,y); } + /// Division in ternary form. + /** + * Sets \p this to n1 / n2. This form can be more efficient than the corresponding binary operator. + * + * @param[in] n1 first argument. + * @param[in] n2 second argument. + * + * @throws piranha::zero_division_error if \p n2 is zero. + * + * @return reference to \p this. + */ + mp_integer &div(const mp_integer &n1, const mp_integer &n2) + { + if (unlikely(math::is_zero(n2))) { + piranha_throw(zero_division_error,"division by zero"); + } + bool s0 = is_static(), s1 = n1.is_static(), s2 = n2.is_static(); + if (s1 && s2) { + if (!s0) { + // NOTE: this is safe, as this is not static while n1 and n2 are, + // and thus this cannot overlap with n1 or n2. + *this = mp_integer{}; + } + mp_integer r; + // NOTE: the static division routine here does both division and remainder, we + // need to check if there's performance to be gained by doing only the division. + m_int.g_st().div(m_int.g_st(),r.m_int.g_st(),n1.m_int.g_st(),n2.m_int.g_st()); + return *this; + } + // this will have to be mpz in any case, promote it if needed and re-check the + // static flags in case this coincides with n1 and/or n2. + if (s0) { + m_int.promote(); + s1 = n1.is_static(); + s2 = n2.is_static(); + } + // NOTE: here the 0 flag means that the operand is static and needs to be promoted, + // 1 means that it is dynamic already. + const unsigned mask = static_cast(!s1) + (static_cast(!s2) << 1u); + piranha_assert(mask > 0u); + switch (mask) { + // NOTE: case 0 here is not possible as it would mean that n1 and n2 are both static, + // but we handled the case above. + case 1u: + { + auto v2 = n2.m_int.g_st().get_mpz_view(); + ::mpz_tdiv_q(&m_int.g_dy(),&n1.m_int.g_dy(),v2); + break; + } + case 2u: + { + auto v1 = n1.m_int.g_st().get_mpz_view(); + ::mpz_tdiv_q(&m_int.g_dy(),v1,&n2.m_int.g_dy()); + break; + } + case 3u: + ::mpz_tdiv_q(&m_int.g_dy(),&n1.m_int.g_dy(),&n2.m_int.g_dy()); + } + return *this; + } /// In-place modulo operation. /** * \note @@ -4209,6 +4269,27 @@ struct mul3_impl::value>::typ } }; +/// Specialisation of the piranha::math::div3() functor for piranha::mp_integer. +/** + * This specialisation is activated when \p T is an instance of piranha::mp_integer. + */ +template +struct div3_impl::value>::type> +{ + /// Call operator. + /** + * @param[out] out the output value. + * @param[in] a the first operand. + * @param[in] b the second operand. + * + * @return the output of piranha::mp_integer::div(). + */ + auto operator()(T &out, const T &a, const T &b) const -> decltype(out.div(a,b)) + { + return out.div(a,b); + } +}; + } namespace detail diff --git a/tests/math.cpp b/tests/math.cpp index 136245dba..0854e4a06 100644 --- a/tests/math.cpp +++ b/tests/math.cpp @@ -827,4 +827,23 @@ BOOST_AUTO_TEST_CASE(math_ternary_ops_test) BOOST_CHECK(!has_mul3>::value); BOOST_CHECK(!has_mul3::value); } + { + // Division. + BOOST_CHECK(has_div3::value); + int i1 = 0; + math::div3(i1,6,3); + BOOST_CHECK_EQUAL(i1,2); + BOOST_CHECK(has_div3::value); + short s1 = -8; + math::div3(s1,short(-8),short(2)); + BOOST_CHECK_EQUAL(s1,-4); + BOOST_CHECK(has_div3::value); + BOOST_CHECK(has_div3::value); + float f1 = 1.234f; + math::div3(f1,3.456f,8.145f); + BOOST_CHECK_EQUAL(f1,3.456f / 8.145f); + BOOST_CHECK(!has_div3::value); + BOOST_CHECK(!has_div3>::value); + BOOST_CHECK(!has_div3::value); + } } diff --git a/tests/mp_integer_03.cpp b/tests/mp_integer_03.cpp index 964ddedc2..7ad701c1b 100644 --- a/tests/mp_integer_03.cpp +++ b/tests/mp_integer_03.cpp @@ -638,6 +638,66 @@ struct ternary_tester BOOST_CHECK_EQUAL(out,n1 * n1); } } + { + // Division. + BOOST_CHECK(has_div3::value); + int_type a, b, c; + BOOST_CHECK_THROW(a.div(b,c),zero_division_error); + BOOST_CHECK_EQUAL(a,0); + BOOST_CHECK(a.is_static()); + a = 1; + b = -4; + c = 2; + a.div(b,c); + BOOST_CHECK_EQUAL(a,-2); + BOOST_CHECK(a.is_static()); + // Try with promotion. + b = int_type(1) << (2u * limb_bits); + c = 2; + a.div(b,c); + BOOST_CHECK_EQUAL(a,int_type(1) << (2u * limb_bits - 1u)); + BOOST_CHECK(!a.is_static()); + // Same with overlapping operands. + a = int_type(1) << (2u * limb_bits); + BOOST_CHECK(!a.is_static()); + a.div(a,int_type(2)); + BOOST_CHECK_EQUAL(a,int_type(1) << (2u * limb_bits - 1u)); + BOOST_CHECK(!a.is_static()); + // Random testing. + std::uniform_int_distribution int_dist(std::numeric_limits::min(),std::numeric_limits::max()); + std::uniform_int_distribution p_dist(0,1); + for (int i = 0; i < ntries; ++i) { + int_type n1(int_dist(rng)), n2(int_dist(rng)), out; + if (n1 == 0 || n2 == 0) { + continue; + } + // Promote randomly. + if (p_dist(rng) && n1.is_static()) { + n1.promote(); + } + if (p_dist(rng) && n2.is_static()) { + n2.promote(); + } + if (p_dist(rng)) { + out.promote(); + } + out.div(n1,n2); + BOOST_CHECK_EQUAL(out,n1 / n2); + // Try the math:: counterpart. + math::div3(out,n1,n2); + BOOST_CHECK_EQUAL(out,n1 / n2); + // Try with overlapping operands. + out = n1; + out.div(out,n2); + BOOST_CHECK_EQUAL(out,n1 / n2); + out = n2; + out.div(n1,out); + BOOST_CHECK_EQUAL(out,n1 / n2); + out = n1; + out.div(out,out); + BOOST_CHECK_EQUAL(out,n1 / n1); + } + } } }; @@ -645,6 +705,7 @@ BOOST_AUTO_TEST_CASE(mp_integer_ternary_test) { boost::mpl::for_each(ternary_tester()); } + struct divexact_tester { template From f2044133231637b7d19f0148b394c73567821fdb Mon Sep 17 00:00:00 2001 From: Francesco Biscani Date: Sat, 27 Feb 2016 02:45:06 +0100 Subject: [PATCH 06/17] Another try. [skip appveyor] --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f33e9235e..ca2a3a409 100644 --- a/.travis.yml +++ b/.travis.yml @@ -80,7 +80,7 @@ after_success: - if [[ "${BUILD_TYPE}" == "Debug" && "${PIRANHA_COMPILER}" == "clang" ]]; then pip install --user cpp-coveralls; cd tests; - coveralls --gcov llvm-cov --encoding iso-8859-1 || echo 'Coveralls upload failed.'; + coveralls --gcov llvm-cov --gcov-options gcov || echo 'Coveralls upload failed.'; fi notifications: email: false From a270c0c21b9a80052d4dd5d5772919807ec3570e Mon Sep 17 00:00:00 2001 From: Francesco Biscani Date: Sun, 28 Feb 2016 12:20:26 +0100 Subject: [PATCH 07/17] mp_integer: small doc addition. --- src/mp_integer.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mp_integer.hpp b/src/mp_integer.hpp index 6c616dd79..4468389f2 100644 --- a/src/mp_integer.hpp +++ b/src/mp_integer.hpp @@ -3666,6 +3666,8 @@ class mp_integer } /// GCD. /** + * The returned value is guaranteed to be non-negative if both arguments are non-negative. + * * @param[in] a first argument * @param[in] b second argument. * From e51766c7674bf288e6b46384ac0df9c61c304a55 Mon Sep 17 00:00:00 2001 From: Francesco Biscani Date: Sun, 28 Feb 2016 12:24:15 +0100 Subject: [PATCH 08/17] bsm: use the ternary multiplication and the _divexact() method when dealing with rational coefficients. --- src/base_series_multiplier.hpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/base_series_multiplier.hpp b/src/base_series_multiplier.hpp index 2f6f0354e..cb5f44897 100644 --- a/src/base_series_multiplier.hpp +++ b/src/base_series_multiplier.hpp @@ -49,6 +49,7 @@ see https://www.gnu.org/licenses/. */ #include "detail/gcd.hpp" #include "exceptions.hpp" #include "key_is_multipliable.hpp" +#include "math.hpp" #include "mp_integer.hpp" #include "mp_rational.hpp" #include "safe_cast.hpp" @@ -163,11 +164,16 @@ struct base_series_multiplier_implm_cf.den()) / gcd(m_lcm,it->m_cf.den()); + // NOTE: if we implement it, we should use the ternary form of GCD here. + const auto g = gcd(m_lcm,it->m_cf.den()); + math::mul3(m_lcm,m_lcm,it->m_cf.den()); + int_type::_divexact(m_lcm,m_lcm,g); } it_f = c2.end(); for (auto it = c2.begin(); it != it_f; ++it) { - m_lcm = (m_lcm * it->m_cf.den()) / gcd(m_lcm,it->m_cf.den()); + const auto g = gcd(m_lcm,it->m_cf.den()); + math::mul3(m_lcm,m_lcm,it->m_cf.den()); + int_type::_divexact(m_lcm,m_lcm,g); } // All these computations involve only positive numbers, // the GCD must always be positive. From 07d782eb4ab2e8e7bae92eaaefba48857b66dd40 Mon Sep 17 00:00:00 2001 From: Francesco Biscani Date: Sun, 28 Feb 2016 12:24:56 +0100 Subject: [PATCH 09/17] Revert "Another try." This reverts commit f2044133231637b7d19f0148b394c73567821fdb. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ca2a3a409..f33e9235e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -80,7 +80,7 @@ after_success: - if [[ "${BUILD_TYPE}" == "Debug" && "${PIRANHA_COMPILER}" == "clang" ]]; then pip install --user cpp-coveralls; cd tests; - coveralls --gcov llvm-cov --gcov-options gcov || echo 'Coveralls upload failed.'; + coveralls --gcov llvm-cov --encoding iso-8859-1 || echo 'Coveralls upload failed.'; fi notifications: email: false From b148e48b0c326ccd474993155d1831b5b542fca7 Mon Sep 17 00:00:00 2001 From: Francesco Biscani Date: Sun, 28 Feb 2016 12:25:23 +0100 Subject: [PATCH 10/17] Revert "Another try." This reverts commit df1d17b586ad31195aee945a6954cf7e5b8ccadf. --- .travis.yml | 55 ++++++++++++++++++++--------------------- tools/install_travis.sh | 2 +- 2 files changed, 28 insertions(+), 29 deletions(-) diff --git a/.travis.yml b/.travis.yml index f33e9235e..564f716d3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,30 +36,30 @@ env: matrix: include: - #- compiler: gcc - #env: BUILD_TYPE="Release" - #- compiler: gcc - #env: BUILD_TYPE="Debug" SPLIT_TEST_NUM="0" - #- compiler: gcc - #env: BUILD_TYPE="Debug" SPLIT_TEST_NUM="1" - #- compiler: gcc - #env: BUILD_TYPE="Debug" SPLIT_TEST_NUM="2" - #- compiler: clang - #env: BUILD_TYPE="Release" + - compiler: gcc + env: BUILD_TYPE="Release" + - compiler: gcc + env: BUILD_TYPE="Debug" SPLIT_TEST_NUM="0" + - compiler: gcc + env: BUILD_TYPE="Debug" SPLIT_TEST_NUM="1" + - compiler: gcc + env: BUILD_TYPE="Debug" SPLIT_TEST_NUM="2" + - compiler: clang + env: BUILD_TYPE="Release" - compiler: clang env: BUILD_TYPE="Debug" SPLIT_TEST_NUM="0" - #- compiler: clang - #env: BUILD_TYPE="Debug" SPLIT_TEST_NUM="1" - #- compiler: clang - #env: BUILD_TYPE="Debug" SPLIT_TEST_NUM="2" - #- compiler: clang - #env: BUILD_TYPE="Python2" - #- compiler: clang - #env: BUILD_TYPE="Python3" - #- compiler: clang - #env: BUILD_TYPE="Tutorial" - #- compiler: gcc - #env: BUILD_TYPE="Doxygen" + - compiler: clang + env: BUILD_TYPE="Debug" SPLIT_TEST_NUM="1" + - compiler: clang + env: BUILD_TYPE="Debug" SPLIT_TEST_NUM="2" + - compiler: clang + env: BUILD_TYPE="Python2" + - compiler: clang + env: BUILD_TYPE="Python3" + - compiler: clang + env: BUILD_TYPE="Tutorial" + - compiler: gcc + env: BUILD_TYPE="Doxygen" install: - if [[ "${CC}" == "gcc" ]]; then @@ -76,11 +76,10 @@ script: - mkdir build - cd build - source ../tools/install_travis.sh -after_success: - - if [[ "${BUILD_TYPE}" == "Debug" && "${PIRANHA_COMPILER}" == "clang" ]]; then - pip install --user cpp-coveralls; - cd tests; - coveralls --gcov llvm-cov --encoding iso-8859-1 || echo 'Coveralls upload failed.'; - fi +#after_success: + #- if [[ "${BUILD_TYPE}" == "Debug" && "${PIRANHA_COMPILER}" == "clang" ]]; then + #cd tests; + #/home/travis/.local/bin/coveralls --gcov-options '\-lp'; + #fi notifications: email: false diff --git a/tools/install_travis.sh b/tools/install_travis.sh index d61a16c9b..75e30f714 100644 --- a/tools/install_travis.sh +++ b/tools/install_travis.sh @@ -11,7 +11,7 @@ if [[ "${BUILD_TYPE}" == "Debug" ]]; then make; ctest -E "thread|memory" -V; elif [[ "${PIRANHA_COMPILER}" == "clang" ]]; then - cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="--coverage" -DBUILD_TUTORIAL=yes -DPIRANHA_TEST_SPLIT=yes -DPIRANHA_TEST_SPLIT_NUM=${SPLIT_TEST_NUM} ../; + cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_TUTORIAL=yes -DPIRANHA_TEST_SPLIT=yes -DPIRANHA_TEST_SPLIT_NUM=${SPLIT_TEST_NUM} ../; make; ctest -E "thread" -V; fi From 9b6a370e64438015ae9c53d66411cdc3aa707442 Mon Sep 17 00:00:00 2001 From: Francesco Biscani Date: Sun, 28 Feb 2016 15:54:17 +0100 Subject: [PATCH 11/17] Tentatively enable the -Werror debug flag for clang. This is a bit risky as it will be brittle with respect to changes in compiler behaviour. Let's see how it goes. --- cmake_modules/PiranhaCompilerLinkerSettings.cmake | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cmake_modules/PiranhaCompilerLinkerSettings.cmake b/cmake_modules/PiranhaCompilerLinkerSettings.cmake index ac62f140e..c1f4f6fdc 100644 --- a/cmake_modules/PiranhaCompilerLinkerSettings.cmake +++ b/cmake_modules/PiranhaCompilerLinkerSettings.cmake @@ -152,3 +152,10 @@ if(CMAKE_COMPILER_IS_CLANGXX OR CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_IN PIRANHA_CHECK_ENABLE_DEBUG_CXX_FLAG(-ftemplate-backtrace-limit=0) PIRANHA_CHECK_ENABLE_DEBUG_CXX_FLAG(-fstack-protector-all) endif() + +# Clang specific configuration. +if(CMAKE_COMPILER_IS_CLANGXX) + # Clang is better at this flag than GCC, which emits a questionable warning when compiling + # the Python bindings. + PIRANHA_CHECK_ENABLE_DEBUG_CXX_FLAG(-Werror) +endif() From 5bf13ac2fb82772bf1ab1571ea44811799cae515 Mon Sep 17 00:00:00 2001 From: Francesco Biscani Date: Sun, 28 Feb 2016 18:24:39 +0100 Subject: [PATCH 12/17] Move the GCD implementation in the public namespace, implement its ternary form and start using it. --- src/CMakeLists.txt | 1 - src/base_series_multiplier.hpp | 7 +- src/detail/gcd.hpp | 58 ---------- src/divisor.hpp | 4 +- src/divisor_series.hpp | 3 +- src/math.hpp | 189 ++++++++++++++++++++++++++++++++ src/mp_integer.hpp | 194 +++++++++++++++++++++++---------- src/mp_rational.hpp | 5 +- src/piranha.hpp | 2 + src/poisson_series.hpp | 3 +- tests/CMakeLists.txt | 1 - tests/gcd.cpp | 83 -------------- tests/math.cpp | 106 ++++++++++++++++++ tests/mp_integer_02.cpp | 57 ---------- tests/mp_integer_03.cpp | 137 +++++++++++++++++++++++ tests/mp_rational.cpp | 5 +- 16 files changed, 580 insertions(+), 275 deletions(-) delete mode 100644 src/detail/gcd.hpp delete mode 100644 tests/gcd.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f06e9430f..620eb2a64 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -75,7 +75,6 @@ SET(DETAIL_HEADERS_LIST detail/is_digit.hpp detail/config_intel.hpp detail/type_in_tuple.hpp - detail/gcd.hpp detail/vector_merge_args.hpp detail/divisor_series_fwd.hpp detail/cf_mult_impl.hpp diff --git a/src/base_series_multiplier.hpp b/src/base_series_multiplier.hpp index cb5f44897..b5f17562b 100644 --- a/src/base_series_multiplier.hpp +++ b/src/base_series_multiplier.hpp @@ -46,7 +46,6 @@ see https://www.gnu.org/licenses/. */ #include "config.hpp" #include "detail/atomic_utils.hpp" -#include "detail/gcd.hpp" #include "exceptions.hpp" #include "key_is_multipliable.hpp" #include "math.hpp" @@ -163,15 +162,15 @@ struct base_series_multiplier_implm_cf.den()); + math::gcd3(g,m_lcm,it->m_cf.den()); math::mul3(m_lcm,m_lcm,it->m_cf.den()); int_type::_divexact(m_lcm,m_lcm,g); } it_f = c2.end(); for (auto it = c2.begin(); it != it_f; ++it) { - const auto g = gcd(m_lcm,it->m_cf.den()); + math::gcd3(g,m_lcm,it->m_cf.den()); math::mul3(m_lcm,m_lcm,it->m_cf.den()); int_type::_divexact(m_lcm,m_lcm,g); } diff --git a/src/detail/gcd.hpp b/src/detail/gcd.hpp deleted file mode 100644 index ee708d12a..000000000 --- a/src/detail/gcd.hpp +++ /dev/null @@ -1,58 +0,0 @@ -/* Copyright 2009-2016 Francesco Biscani (bluescarni@gmail.com) - -This file is part of the Piranha library. - -The Piranha library is free software; you can redistribute it and/or modify -it under the terms of either: - - * the GNU Lesser General Public License as published by the Free - Software Foundation; either version 3 of the License, or (at your - option) any later version. - -or - - * the GNU General Public License as published by the Free Software - Foundation; either version 3 of the License, or (at your option) any - later version. - -or both in parallel, as here. - -The Piranha library is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -for more details. - -You should have received copies of the GNU General Public License and the -GNU Lesser General Public License along with the Piranha library. If not, -see https://www.gnu.org/licenses/. */ - -#ifndef PIRANHA_DETAIL_GCD_HPP -#define PIRANHA_DETAIL_GCD_HPP - -#include - -#include "../mp_integer.hpp" - -namespace piranha -{ - -namespace detail -{ - -template ::value,int>::type = 0> -inline T gcd(const T &a, const T &b) -{ - return gcd_euclidean(a,b); -} - -template ::value,int>::type = 0> -inline T gcd(const T &a, const T &b) -{ - return T::gcd(a,b); -} - -} - -} - -#endif diff --git a/src/divisor.hpp b/src/divisor.hpp index 8fe954587..d0a94ac10 100644 --- a/src/divisor.hpp +++ b/src/divisor.hpp @@ -44,7 +44,6 @@ see https://www.gnu.org/licenses/. */ #include "config.hpp" #include "detail/cf_mult_impl.hpp" #include "detail/divisor_series_fwd.hpp" -#include "detail/gcd.hpp" #include "detail/prepare_for_print.hpp" #include "detail/series_fwd.hpp" #include "detail/vector_merge_args.hpp" @@ -52,6 +51,7 @@ see https://www.gnu.org/licenses/. */ #include "hash_set.hpp" #include "is_cf.hpp" #include "is_key.hpp" +#include "math.hpp" #include "mp_integer.hpp" #include "pow.hpp" #include "safe_cast.hpp" @@ -162,7 +162,7 @@ class divisor first_nonzero_found = true; } // NOTE: gcd(0,n) == n (or +-n, in our case) for all n, zero included. - cd = detail::gcd(cd,n); + math::gcd3(cd,cd,n); } if (cd != 1 && cd != -1) { return false; diff --git a/src/divisor_series.hpp b/src/divisor_series.hpp index 9e84b8d11..219131c86 100644 --- a/src/divisor_series.hpp +++ b/src/divisor_series.hpp @@ -40,7 +40,6 @@ see https://www.gnu.org/licenses/. */ #include "config.hpp" #include "detail/divisor_series_fwd.hpp" #include "detail/polynomial_fwd.hpp" -#include "detail/gcd.hpp" #include "divisor.hpp" #include "exceptions.hpp" #include "forwarding.hpp" @@ -319,7 +318,7 @@ class divisor_series: public power_series decltype(div3_impl()(a,b,c) } +namespace detail +{ + +// Greatest common divisor using the euclidean algorithm. +// NOTE: this can yield negative values, depending on the signs +// of a and b. Supports C++ integrals and mp_integer. +// NOTE: using this with C++ integrals unchecked on ranges can result in undefined +// behaviour. +template +inline T gcd_euclidean(T a, T b) +{ + while (true) { + if (math::is_zero(a)) { + return b; + } + b %= a; + if (math::is_zero(b)) { + return a; + } + a %= b; + } +} + +} + +namespace math +{ + +/// Default functor for the implementation of piranha::math::gcd(). +/** + * This functor should be specialised via the \p std::enable_if mechanism. Default implementation will not define + * the call operator, and will hence result in a compilation error when used. + */ +template +struct gcd_impl {}; + +/// Implementation of piranha::math::gcd() for integral types. +/** + * This specialisation is enabled when \p T and \p U are C++ integral types. + */ +template +struct gcd_impl::value && std::is_integral::value>::type> +{ + /// The promoted type of T and U. + using p_type = decltype(std::declval() + std::declval()); + /// Call operator. + /** + * The GCD will be computed via the euclidean algorithm. No overflow check is performed during + * the computation. + * + * @param[in] a the first operand. + * @param[in] b the second operand. + * + * @return the GCD of \p a and \p b. + */ + p_type operator()(const T &a, const U &b) const + { + return detail::gcd_euclidean(static_cast(a),static_cast(b)); + } +}; + +/// GCD. +/** + * This function will return the GCD of \p a and \p b. The actual implementation of this function is in the piranha::math::gcd_impl functor's + * call operator. + * + * @param[in] a the first operand. + * @param[in] b the second operand. + * + * @return the value returned by the call operator of piranha::math::gcd_impl. + * + * @throws unspecified any exception thrown by the call operator of the piranha::math::gcd_impl functor. + */ +template +inline auto gcd(const T &a, const U &b) -> decltype(gcd_impl()(a,b)) +{ + return gcd_impl()(a,b); +} + +/// Default functor for the implementation of piranha::math::gcd3(). +/** + * This functor should be specialised via the \p std::enable_if mechanism. + */ +template +struct gcd3_impl +{ + /// Call operator. + /** + * \note + * This operator is enabled only if the expression out = math::gcd(a,b) is well-formed. + * + * @param[out] out the output value. + * @param[in] a the first operand. + * @param[in] b the second operand. + * + * @return out = math::gcd(a,b). + * + * @throws unspecified any exception thrown by piranha::math::gcd() or the invoked + * assignment operator. + */ + template + auto operator()(T1 &out, const T1 &a, const T1 &b) const -> decltype(out = math::gcd(a,b)) + { + return out = math::gcd(a,b); + } +}; + +/// Specialisation of the piranha::math::gcd3() functor for integral types. +/** + * This specialisation is enabled when \p T is a C++ integral type. + */ +template +struct gcd3_impl::value>::type> +{ + /// Call operator. + /** + * This call operator will forcibly cast back to \p T the result of piranha::math::gcd(). + * + * @param[out] out the output value. + * @param[in] a the first operand. + * @param[in] b the second operand. + * + * @return out = static_cast(math::gcd(a,b)). + */ + T &operator()(T &out, const T &a, const T &b) const + { + return out = static_cast(math::gcd(a,b)); + } +}; + +/// Ternary GCD. +/** + * This function will write the GCD of \p a and \p b into \p out. The actual implementation of this function is in the piranha::math::gcd3_impl functor's + * call operator. + * + * @param[out] out the output value. + * @param[in] a the first operand. + * @param[in] b the second operand. + * + * @return the value returned by the call operator of piranha::math::gcd3_impl. + * + * @throws unspecified any exception thrown by the call operator of the piranha::math::gcd3_impl functor. + */ +template +inline auto gcd3(T &out, const T &a, const T &b) -> decltype(gcd3_impl()(out,a,b)) +{ + return gcd3_impl()(out,a,b); +} + +} + /// Detect piranha::math::add3(). /** * The type trait will be \p true if piranha::math::add3() can be used on instances of type \p T, @@ -2471,6 +2622,44 @@ class has_div3: detail::sfinae_types template const bool has_div3::value; +/// Detect piranha::math::gcd(). +/** + * The type trait will be \p true if piranha::math::gcd() can be used on instances of type \p T and \p U, + * \p false otherwise. + */ +template +class has_gcd: detail::sfinae_types +{ + template + static auto test(const T1 &a, const U1 &b) -> decltype(math::gcd(a,b),void(),yes()); + static no test(...); + public: + /// Value of the type trait. + static const bool value = std::is_same(),std::declval())),yes>::value; +}; + +template +const bool has_gcd::value; + +/// Detect piranha::math::gcd3(). +/** + * The type trait will be \p true if piranha::math::gcd3() can be used on instances of type \p T, + * \p false otherwise. + */ +template +class has_gcd3: detail::sfinae_types +{ + template + static auto test(const T1 &) -> decltype(math::gcd3(std::declval(),std::declval(),std::declval()),void(),yes()); + static no test(...); + public: + /// Value of the type trait. + static const bool value = std::is_same())),yes>::value; +}; + +template +const bool has_gcd3::value; + } #endif diff --git a/src/mp_integer.hpp b/src/mp_integer.hpp index 4468389f2..978f5bd9c 100644 --- a/src/mp_integer.hpp +++ b/src/mp_integer.hpp @@ -64,47 +64,6 @@ see https://www.gnu.org/licenses/. */ namespace piranha { namespace detail { -// Fwd declaration of is_mp_integer. -template -struct is_mp_integer; - -// GCD utils. -template ::value,int>::type = 0> -inline void gcd_mod(T &a, const T &b) -{ - a %= b; -} - -template ::value,int>::type = 0> -inline void gcd_mod(T &a, const T &b) -{ - a = static_cast(a % b); -} - -// Greatest common divisor using the euclidean algorithm. -// NOTE: this can yield negative values, depending on the signs -// of a and b. Supports C++ integrals and mp_integer. -// NOTE: using this with C++ integrals unchecked on ranges can result in undefined -// behaviour. -template -inline T gcd_euclidean(T a, T b) -{ - while (true) { - if (math::is_zero(a)) { - return b; - } - // NOTE: the difference in implementation here is because - // we want to prevent compiler warnings when T is a short int, - // hence the static cast. For mp_integer, the in-place version - // might be faster. - gcd_mod(b,a); - if (math::is_zero(b)) { - return a; - } - gcd_mod(a,b); - } -} - // Small utility function to clear the upper n bits of an unsigned type. // The static_casts are needed to work around integer promotions when // operating on types smaller than unsigned int. @@ -3666,30 +3625,52 @@ class mp_integer } /// GCD. /** + * This method will write to \p out the GCD of \p n1 and \p n2. * The returned value is guaranteed to be non-negative if both arguments are non-negative. * - * @param[in] a first argument - * @param[in] b second argument. - * - * @return a greatest common divisor of \p a and \p b. + * @param[out] out the output value. + * @param[in] n1 first argument + * @param[in] n2 second argument. */ - static mp_integer gcd(const mp_integer &a, const mp_integer &b) + static void gcd(mp_integer &out, const mp_integer &n1, const mp_integer &n2) { - const bool s1 = a.is_static(), s2 = b.is_static(); - mp_integer retval; + // NOTE: this function would be a good candidate for demotion. + bool s0 = out.is_static(), s1 = n1.is_static(), s2 = n2.is_static(); if (s1 && s2) { - retval = detail::gcd_euclidean(a,b); - } else if (s1) { - retval.promote(); - ::mpz_gcd(&retval.m_int.g_dy(),a.get_mpz_view(),&b.m_int.g_dy()); - } else if (s2) { - retval.promote(); - ::mpz_gcd(&retval.m_int.g_dy(),&a.m_int.g_dy(),b.get_mpz_view()); - } else { - retval.promote(); - ::mpz_gcd(&retval.m_int.g_dy(),&a.m_int.g_dy(),&b.m_int.g_dy()); + // Go with the euclidean computation if both are statics. This will result + // in a static out as well. + out = detail::gcd_euclidean(n1,n2); + return; + } + // We will set out to mpz in any case, promote it if needed and re-check the + // static flags in case this coincides with n1 and/or n2. + if (s0) { + out.m_int.promote(); + s1 = n1.is_static(); + s2 = n2.is_static(); + } + // NOTE: here the 0 flag means that the operand is static and needs to be promoted, + // 1 means that it is dynamic already. + const unsigned mask = static_cast(!s1) + (static_cast(!s2) << 1u); + piranha_assert(mask > 0u); + switch (mask) { + // NOTE: case 0 here is not possible as it would mean that n1 and n2 are both static, + // but we handled the case above. + case 1u: + { + auto v2 = n2.m_int.g_st().get_mpz_view(); + ::mpz_gcd(&out.m_int.g_dy(),&n1.m_int.g_dy(),v2); + break; + } + case 2u: + { + auto v1 = n1.m_int.g_st().get_mpz_view(); + ::mpz_gcd(&out.m_int.g_dy(),v1,&n2.m_int.g_dy()); + break; + } + case 3u: + ::mpz_gcd(&out.m_int.g_dy(),&n1.m_int.g_dy(),&n2.m_int.g_dy()); } - return retval; } private: struct hash_checks @@ -4297,6 +4278,101 @@ struct div3_impl::value>::typ namespace detail { +// Enabler for the GCD specialisation. +template +using mp_integer_gcd_enabler = typename std::enable_if< + (std::is_integral::value && is_mp_integer::value) || + (std::is_integral::value && is_mp_integer::value) || + (is_mp_integer::value && is_mp_integer::value) +>::type; + +} + +namespace math +{ + +/// Implementation of piranha::math::gcd() for piranha::mp_integer. +/** + * This specialisation is enabled when: + * - \p T and \p U are both instances of piranha::mp_integer, + * - \p T is an instance of piranha::mp_integer and \p U is an integral type, + * - \p U is an instance of piranha::mp_integer and \p T is an integral type. + * + * The result will be calculated via piranha::mp_integer::gcd(), after any necessary type conversion. + */ +template +struct gcd_impl> +{ + /// Call operator, piranha::mp_integer - piranha::mp_integer overload. + /** + * @param[in] a first argument. + * @param[in] b second argument. + * + * @return the GCD of \p a and \p b. + */ + template + mp_integer operator()(const mp_integer &a, const mp_integer &b) const + { + mp_integer retval; + mp_integer::gcd(retval,a,b); + return retval; + } + /// Call operator, piranha::mp_integer - integral overload. + /** + * @param[in] a first argument. + * @param[in] b second argument. + * + * @return the GCD of \p a and \p b. + */ + template + mp_integer operator()(const mp_integer &a, const T1 &b) const + { + return operator()(a,mp_integer(b)); + } + /// Call operator, integral - piranha::mp_integer overload. + /** + * @param[in] a first argument. + * @param[in] b second argument. + * + * @return the GCD of \p a and \p b. + */ + template + mp_integer operator()(const T1 &a, const mp_integer &b) const + { + return operator()(b,a); + } +}; + +/// Implementation of piranha::math::gcd3() for piranha::mp_integer. +/** + * This specialisation is enabled when \p T is an instances of piranha::mp_integer. + */ +template +struct gcd3_impl::value>::type> +{ + /// Call operator. + /** + * This call operator will use internally piranha::mp_integer::gcd(). + * + * @param[out] out return value. + * @param[in] a first argument. + * @param[in] b second argument. + * + * @return a reference to \p out. + */ + template + mp_integer &operator()(mp_integer &out, const mp_integer &a, const mp_integer &b) const + { + mp_integer::gcd(out,a,b); + return out; + } +}; + +} + +namespace detail +{ + // Enabler for the overload below. template using ipow_subs_int_enabler = typename std::enable_if::value,int>::type; diff --git a/src/mp_rational.hpp b/src/mp_rational.hpp index ddf2e840d..1c117ed58 100644 --- a/src/mp_rational.hpp +++ b/src/mp_rational.hpp @@ -45,7 +45,6 @@ see https://www.gnu.org/licenses/. */ #include "binomial.hpp" #include "config.hpp" -#include "detail/gcd.hpp" #include "detail/mp_rational_fwd.hpp" #include "exceptions.hpp" #include "math.hpp" @@ -939,7 +938,7 @@ class mp_rational // not throw either. // NOTE: there should be no way to set a negative denominator, so no check is performed. // The condition is checked in the dtor. - const auto gcd = detail::gcd(m_num,m_den); + const auto gcd = math::gcd(m_num,m_den); return (m_num.sign() != 0 && (gcd == 1 || gcd == -1)) || (m_num.sign() == 0 && m_den == 1); } @@ -958,7 +957,7 @@ class mp_rational } // NOTE: here we can avoid the further division by gcd if it is one or -one. // Consider this as a possible optimisation in the future. - const int_type gcd = detail::gcd(m_num,m_den); + const int_type gcd = math::gcd(m_num,m_den); piranha_assert(!math::is_zero(gcd)); int_type::_divexact(m_num,m_num,gcd); int_type::_divexact(m_den,m_den,gcd); diff --git a/src/piranha.hpp b/src/piranha.hpp index 1eac0646f..832aabfa6 100644 --- a/src/piranha.hpp +++ b/src/piranha.hpp @@ -180,6 +180,8 @@ see https://www.gnu.org/licenses/. */ * \todo in order to circumvent the problem of the lack of thread local storage on osx, we should probably just create a local variable ad-hoc. * It will be suboptimal but at least it should work on osx. * \todo the multiplication of a series by single coefficient can probably be handled in the binary_mul_impl() method. + * \todo we need to review the documentation/implementation of type traits were we strip away cv qualifications vs, e.g., implementing the test() method + * in terms of const references. I think in some cases it should be made more explicit and consistent across the type traits. */ namespace piranha { diff --git a/src/poisson_series.hpp b/src/poisson_series.hpp index 5d60dcb78..d3e3c700b 100644 --- a/src/poisson_series.hpp +++ b/src/poisson_series.hpp @@ -40,7 +40,6 @@ see https://www.gnu.org/licenses/. */ #include "base_series_multiplier.hpp" #include "config.hpp" -#include "detail/gcd.hpp" #include "detail/divisor_series_fwd.hpp" #include "detail/poisson_series_fwd.hpp" #include "detail/polynomial_fwd.hpp" @@ -429,7 +428,7 @@ class poisson_series: bool first_nonzero_found = false; for (auto it2 = tmp_int.begin(); it2 != tmp_int.end(); ++it2) { // NOTE: gcd is safe, operating on integers. - cd = detail::gcd(cd,*it2); + cd = math::gcd(cd,*it2); if (!first_nonzero_found && !math::is_zero(*it2)) { piranha_assert(*it2 > 0); first_nonzero_found = true; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 94ec123a6..3de486eb2 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -54,7 +54,6 @@ ADD_PIRANHA_TESTCASE(divisor_series) ADD_PIRANHA_TESTCASE(dynamic_aligning_allocator) ADD_PIRANHA_TESTCASE(environment) ADD_PIRANHA_TESTCASE(exceptions) -ADD_PIRANHA_TESTCASE(gcd) ADD_PIRANHA_TESTCASE(hash_set) ADD_PIRANHA_TESTCASE(invert) ADD_PIRANHA_TESTCASE(ipow_substitutable_series) diff --git a/tests/gcd.cpp b/tests/gcd.cpp deleted file mode 100644 index 6b4080401..000000000 --- a/tests/gcd.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/* Copyright 2009-2016 Francesco Biscani (bluescarni@gmail.com) - -This file is part of the Piranha library. - -The Piranha library is free software; you can redistribute it and/or modify -it under the terms of either: - - * the GNU Lesser General Public License as published by the Free - Software Foundation; either version 3 of the License, or (at your - option) any later version. - -or - - * the GNU General Public License as published by the Free Software - Foundation; either version 3 of the License, or (at your option) any - later version. - -or both in parallel, as here. - -The Piranha library is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -for more details. - -You should have received copies of the GNU General Public License and the -GNU Lesser General Public License along with the Piranha library. If not, -see https://www.gnu.org/licenses/. */ - -#include "../src/detail/gcd.hpp" - -#define BOOST_TEST_MODULE gcd_test -#include - -#include "../src/environment.hpp" -#include "../src/mp_integer.hpp" - -using namespace piranha; -using namespace piranha::detail; - -BOOST_AUTO_TEST_CASE(gcd_test_00) -{ - environment env; - BOOST_CHECK_EQUAL(gcd(4,3),1); - BOOST_CHECK_EQUAL(gcd(3,4),1); - BOOST_CHECK_EQUAL(gcd(4,6),2); - BOOST_CHECK_EQUAL(gcd(6,4),2); - BOOST_CHECK_EQUAL(gcd(4,25),1); - BOOST_CHECK_EQUAL(gcd(25,4),1); - BOOST_CHECK_EQUAL(gcd(27,54),27); - BOOST_CHECK_EQUAL(gcd(54,27),27); - BOOST_CHECK_EQUAL(gcd(1,54),1); - BOOST_CHECK_EQUAL(gcd(54,1),1); - BOOST_CHECK_EQUAL(gcd(36,24),12); - BOOST_CHECK_EQUAL(gcd(24,36),12); - // Check compiler warnings with short ints. - BOOST_CHECK_EQUAL(gcd(short(54),short(27)),short(27)); - BOOST_CHECK_EQUAL(gcd(short(27),short(53)),short(1)); - BOOST_CHECK(gcd(short(27),short(-54)) == short(27) || gcd(short(27),short(-54)) == short(-27)); - BOOST_CHECK(gcd(short(-54),short(27)) == short(27) || gcd(short(-54),short(27)) == short(-27)); - // Check with different signs. - BOOST_CHECK(gcd(27,-54) == 27 || gcd(27,-54) == -27); - BOOST_CHECK(gcd(-54,27) == 27 || gcd(-54,27) == -27); - BOOST_CHECK(gcd(4,-25) == 1 || gcd(4,-25) == -1); - BOOST_CHECK(gcd(-25,4) == 1 || gcd(-25,4) == -1); - BOOST_CHECK(gcd(-25,1) == -1 || gcd(-25,1) == 1); - BOOST_CHECK(gcd(25,-1) == -1 || gcd(25,-1) == 1); - BOOST_CHECK(gcd(-24,36) == -12 || gcd(-24,36) == 12); - BOOST_CHECK(gcd(24,-36) == -12 || gcd(24,-36) == 12); - // Check with integer. - BOOST_CHECK(gcd(27_z,-54_z) == 27_z || gcd(27_z,-54_z) == -27_z); - BOOST_CHECK(gcd(-54_z,27_z) == 27_z || gcd(-54_z,27_z) == -27_z); - BOOST_CHECK(gcd(4_z,-25_z) == 1_z || gcd(4_z,-25_z) == -1_z); - BOOST_CHECK(gcd(-25_z,4_z) == 1_z || gcd(-25_z,4_z) == -1_z); - BOOST_CHECK(gcd(-24_z,36_z) == -12_z || gcd(-24_z,36_z) == 12_z); - BOOST_CHECK(gcd(24_z,-36_z) == -12_z || gcd(24_z,-36_z) == 12_z); - // Check with zeroes. - BOOST_CHECK_EQUAL(gcd(54,0),54); - BOOST_CHECK_EQUAL(gcd(0,54),54); - BOOST_CHECK(gcd(-25,0) == -25 || gcd(-25,0) == 25); - BOOST_CHECK(gcd(-25_z,0_z) == -25_z || gcd(-25_z,0_z) == 25_z); - BOOST_CHECK_EQUAL(gcd(0,0),0); - BOOST_CHECK_EQUAL(gcd(0_z,0_z),0_z); -} diff --git a/tests/math.cpp b/tests/math.cpp index 0854e4a06..2f3085026 100644 --- a/tests/math.cpp +++ b/tests/math.cpp @@ -65,6 +65,7 @@ see https://www.gnu.org/licenses/. */ #include "../src/pow.hpp" #include "../src/real.hpp" #include "../src/symbol_set.hpp" +#include "../src/type_traits.hpp" using namespace piranha; @@ -847,3 +848,108 @@ BOOST_AUTO_TEST_CASE(math_ternary_ops_test) BOOST_CHECK(!has_div3::value); } } + +// A fake GCD-enabled type. +struct mock_type +{}; + +namespace piranha { namespace math { + +template +struct gcd_impl::value>::type> +{ + mock_type operator()(const mock_type &, const mock_type &) const + { + return mock_type {}; + } +}; + +}} + +BOOST_AUTO_TEST_CASE(math_gcd_test) +{ + using math::gcd; + using math::gcd3; + BOOST_CHECK_EQUAL(gcd(0,0),0); + BOOST_CHECK_EQUAL(gcd(0,12),12); + BOOST_CHECK_EQUAL(gcd(14,0),14); + BOOST_CHECK_EQUAL(gcd(4,3),1); + BOOST_CHECK_EQUAL(gcd(3,4),1); + BOOST_CHECK_EQUAL(gcd(4,6),2); + BOOST_CHECK_EQUAL(gcd(6,4),2); + BOOST_CHECK_EQUAL(gcd(4,25),1); + BOOST_CHECK_EQUAL(gcd(25,4),1); + BOOST_CHECK_EQUAL(gcd(27,54),27); + BOOST_CHECK_EQUAL(gcd(54,27),27); + BOOST_CHECK_EQUAL(gcd(1,54),1); + BOOST_CHECK_EQUAL(gcd(54,1),1); + BOOST_CHECK_EQUAL(gcd(36,24),12); + BOOST_CHECK_EQUAL(gcd(24,36),12); + // Check compiler warnings with short ints. + BOOST_CHECK((!std::is_same::value)); + BOOST_CHECK((!std::is_same::value)); + BOOST_CHECK_EQUAL(gcd(short(54),short(27)),short(27)); + BOOST_CHECK_EQUAL(gcd(short(27),short(53)),short(1)); + BOOST_CHECK(gcd(short(27),short(-54)) == short(27) || gcd(short(27),short(-54)) == short(-27)); + BOOST_CHECK(gcd(short(-54),short(27)) == short(27) || gcd(short(-54),short(27)) == short(-27)); + // Check with different signs. + BOOST_CHECK(gcd(27,-54) == 27 || gcd(27,-54) == -27); + BOOST_CHECK(gcd(-54,27) == 27 || gcd(-54,27) == -27); + BOOST_CHECK(gcd(4,-25) == 1 || gcd(4,-25) == -1); + BOOST_CHECK(gcd(-25,4) == 1 || gcd(-25,4) == -1); + BOOST_CHECK(gcd(-25,1) == -1 || gcd(-25,1) == 1); + BOOST_CHECK(gcd(25,-1) == -1 || gcd(25,-1) == 1); + BOOST_CHECK(gcd(-24,36) == -12 || gcd(-24,36) == 12); + BOOST_CHECK(gcd(24,-36) == -12 || gcd(24,-36) == 12); + // Check with zeroes. + BOOST_CHECK_EQUAL(gcd(54,0),54); + BOOST_CHECK_EQUAL(gcd(0,54),54); + BOOST_CHECK_EQUAL(gcd(0,0),0); + // The ternary form, check particularily with respect to short ints. + int out; + gcd3(out,12,9); + BOOST_CHECK_EQUAL(out,3); + short s_out; + gcd3(s_out,short(12),short(9)); + char c_out; + gcd3(c_out,char(12),char(9)); + // Random testing. + std::uniform_int_distribution int_dist(-detail::safe_abs_sint::value,detail::safe_abs_sint::value); + for (int i = 0; i < ntries; ++i) { + int a(int_dist(rng)), b(int_dist(rng)); + int c; + int g = gcd(a,b); + gcd3(c,a,b); + if (g == 0) { + continue; + } + BOOST_CHECK_EQUAL(c,g); + BOOST_CHECK_EQUAL(a % g,0); + BOOST_CHECK_EQUAL(b % g,0); + } + // Check the type traits. + BOOST_CHECK((has_gcd::value)); + BOOST_CHECK((has_gcd::value)); + BOOST_CHECK((has_gcd::value)); + BOOST_CHECK((!has_gcd::value)); + BOOST_CHECK((!has_gcd::value)); + BOOST_CHECK((!has_gcd::value)); + BOOST_CHECK((!has_gcd::value)); + BOOST_CHECK(has_gcd3::value); + BOOST_CHECK(has_gcd3::value); + BOOST_CHECK(has_gcd3::value); + BOOST_CHECK(has_gcd3::value); + BOOST_CHECK(has_gcd3::value); + BOOST_CHECK(has_gcd3::value); + BOOST_CHECK(!has_gcd3::value); + BOOST_CHECK(!has_gcd3::value); + BOOST_CHECK(!has_gcd3::value); + // Try the mock type. + BOOST_CHECK(has_gcd::value); + BOOST_CHECK((!has_gcd::value)); + BOOST_CHECK((!has_gcd::value)); + BOOST_CHECK(has_gcd3::value); + BOOST_CHECK_NO_THROW(gcd(mock_type{},mock_type{})); + mock_type m0; + BOOST_CHECK_NO_THROW(gcd3(m0,mock_type{},mock_type{})); +} diff --git a/tests/mp_integer_02.cpp b/tests/mp_integer_02.cpp index a2c5ef4f2..e8cadb68d 100644 --- a/tests/mp_integer_02.cpp +++ b/tests/mp_integer_02.cpp @@ -2769,60 +2769,3 @@ BOOST_AUTO_TEST_CASE(mp_integer_get_mpz_ptr_test) { boost::mpl::for_each(get_mpz_ptr_tester()); } - -struct gcd_tester -{ - template - void operator()(const T &) - { - typedef mp_integer int_type; - int_type a, b; - // Check with two zeroes. - BOOST_CHECK_EQUAL(int_type::gcd(a,b),0); - a.promote(); - BOOST_CHECK_EQUAL(int_type::gcd(a,b),0); - BOOST_CHECK_EQUAL(int_type::gcd(b,a),0); - b.promote(); - a = 0; - BOOST_CHECK_EQUAL(int_type::gcd(a,b),0); - BOOST_CHECK_EQUAL(int_type::gcd(b,a),0); - a.promote(); - BOOST_CHECK_EQUAL(int_type::gcd(a,b),0); - BOOST_CHECK_EQUAL(int_type::gcd(b,a),0); - // Only one zero. - a = 0; - b = 1; - BOOST_CHECK_EQUAL(int_type::gcd(a,b),1); - a.promote(); - BOOST_CHECK_EQUAL(int_type::gcd(a,b),1); - BOOST_CHECK_EQUAL(int_type::gcd(b,a),1); - b.promote(); - a = 0; - BOOST_CHECK_EQUAL(int_type::gcd(a,b),1); - BOOST_CHECK_EQUAL(int_type::gcd(b,a),1); - a.promote(); - BOOST_CHECK_EQUAL(int_type::gcd(a,b),1); - BOOST_CHECK_EQUAL(int_type::gcd(b,a),1); - // Randomised testing. - std::uniform_int_distribution pdist(0,1); - std::uniform_int_distribution ndist(std::numeric_limits::min(),std::numeric_limits::max()); - for (int i = 0; i < ntries; ++i) { - a = int_type(ndist(rng)); - b = int_type(ndist(rng)); - if (pdist(rng) && a.is_static()) { - a.promote(); - } - if (pdist(rng) && b.is_static()) { - b.promote(); - } - auto gcd = int_type::gcd(a,b).abs(); - BOOST_CHECK_EQUAL(a % gcd,0); - BOOST_CHECK_EQUAL(b % gcd,0); - } - } -}; - -BOOST_AUTO_TEST_CASE(mp_integer_gcd_test) -{ - boost::mpl::for_each(gcd_tester()); -} diff --git a/tests/mp_integer_03.cpp b/tests/mp_integer_03.cpp index 7ad701c1b..f4c547972 100644 --- a/tests/mp_integer_03.cpp +++ b/tests/mp_integer_03.cpp @@ -786,3 +786,140 @@ BOOST_AUTO_TEST_CASE(mp_integer_divexact_test) { boost::mpl::for_each(divexact_tester()); } + +struct gcd_tester +{ + template + void operator()(const T &) + { + typedef mp_integer int_type; + int_type a, b, out; + // Check with two zeroes. + int_type::gcd(out,a,b); + BOOST_CHECK_EQUAL(out,0); + BOOST_CHECK(out.is_static()); + a.promote(); + int_type::gcd(out,a,b); + BOOST_CHECK_EQUAL(out,0); + int_type::gcd(out,b,a); + BOOST_CHECK_EQUAL(out,0); + BOOST_CHECK(!out.is_static()); + b.promote(); + a = 0; + out = 2; + int_type::gcd(out,a,b); + BOOST_CHECK_EQUAL(out,0); + int_type::gcd(out,b,a); + BOOST_CHECK_EQUAL(out,0); + BOOST_CHECK(!out.is_static()); + a.promote(); + out = 1; + int_type::gcd(out,a,b); + BOOST_CHECK_EQUAL(out,0); + int_type::gcd(out,b,a); + BOOST_CHECK_EQUAL(out,0); + BOOST_CHECK(!out.is_static()); + // Only one zero. + a = 0; + b = 1; + out = 2; + int_type::gcd(out,a,b); + BOOST_CHECK_EQUAL(out,1); + int_type::gcd(out,b,a); + BOOST_CHECK_EQUAL(out,1); + BOOST_CHECK(out.is_static()); + a.promote(); + int_type::gcd(out,a,b); + BOOST_CHECK_EQUAL(out,1); + int_type::gcd(out,b,a); + BOOST_CHECK_EQUAL(out,1); + BOOST_CHECK(!out.is_static()); + b.promote(); + a = 0; + out = 0; + int_type::gcd(out,a,b); + BOOST_CHECK_EQUAL(out,1); + int_type::gcd(out,b,a); + BOOST_CHECK_EQUAL(out,1); + BOOST_CHECK(!out.is_static()); + a.promote(); + out = 0; + int_type::gcd(out,a,b); + BOOST_CHECK_EQUAL(out,1); + int_type::gcd(out,b,a); + BOOST_CHECK_EQUAL(out,1); + BOOST_CHECK(!out.is_static()); + // Randomised testing. + std::uniform_int_distribution pdist(0,1); + std::uniform_int_distribution ndist(std::numeric_limits::min(),std::numeric_limits::max()); + for (int i = 0; i < ntries; ++i) { + auto aint = ndist(rng); + auto bint = ndist(rng); + a = int_type(aint); + b = int_type(bint); + int_type out; + if (pdist(rng) && a.is_static()) { + a.promote(); + } + if (pdist(rng) && b.is_static()) { + b.promote(); + } + if (pdist(rng)) { + out.promote(); + } + int_type::gcd(out,a,b); + if (out == 0) { + continue; + } + BOOST_CHECK_EQUAL(a % out.abs(),0); + BOOST_CHECK_EQUAL(b % out.abs(),0); + const auto out_copy(out); + int_type::gcd(out,b,a); + BOOST_CHECK_EQUAL(out,out_copy); + // Check the math overload. + math::gcd3(out,a,b); + BOOST_CHECK_EQUAL(out,out_copy); + math::gcd3(out,out,out); + BOOST_CHECK_EQUAL(out,out_copy); + // Some tests with overlapping arguments. + int_type old_a(a), old_b(b); + int_type::gcd(a,a,b); + BOOST_CHECK_EQUAL(a,out_copy); + a = old_a; + math::gcd3(a,a,b); + BOOST_CHECK_EQUAL(a,out_copy); + a = old_a; + int_type::gcd(b,a,b); + BOOST_CHECK_EQUAL(b,out_copy); + b = old_b; + math::gcd3(b,a,b); + BOOST_CHECK_EQUAL(b,out_copy); + b = old_b; + int_type::gcd(a,a,a); + BOOST_CHECK_EQUAL(a.abs(),old_a.abs()); + a = old_a; + math::gcd3(a,a,a); + BOOST_CHECK_EQUAL(a.abs(),old_a.abs()); + a = old_a; + // Check the math overloads. + BOOST_CHECK((std::is_same::value)); + BOOST_CHECK((std::is_same::value)); + BOOST_CHECK((std::is_same::value)); + BOOST_CHECK_EQUAL(math::gcd(a,b).abs(),out.abs()); + BOOST_CHECK_EQUAL(math::gcd(aint,b).abs(),out.abs()); + BOOST_CHECK_EQUAL(math::gcd(a,bint).abs(),out.abs()); + } + // Check the math type traits. + BOOST_CHECK((has_gcd::value)); + BOOST_CHECK((has_gcd::value)); + BOOST_CHECK((has_gcd::value)); + BOOST_CHECK((!has_gcd::value)); + BOOST_CHECK((!has_gcd::value)); + BOOST_CHECK((has_gcd3::value)); + } +}; + +BOOST_AUTO_TEST_CASE(mp_integer_gcd_test) +{ + boost::mpl::for_each(gcd_tester()); +} diff --git a/tests/mp_rational.cpp b/tests/mp_rational.cpp index 789079aa6..985e1b6b9 100644 --- a/tests/mp_rational.cpp +++ b/tests/mp_rational.cpp @@ -54,7 +54,6 @@ see https://www.gnu.org/licenses/. */ #include "../src/binomial.hpp" #include "../src/config.hpp" -#include "../src/detail/gcd.hpp" #include "../src/exceptions.hpp" #include "../src/environment.hpp" #include "../src/math.hpp" @@ -478,7 +477,7 @@ struct plus_tester template void operator()(const T &) { - using detail::gcd; + using math::gcd; using q_type = mp_rational; using int_type = typename q_type::int_type; { @@ -713,7 +712,7 @@ struct minus_tester template void operator()(const T &) { - using detail::gcd; + using math::gcd; using q_type = mp_rational; using int_type = typename q_type::int_type; { From f88b1391c298f3de4dc9336de4fa987ffc482835 Mon Sep 17 00:00:00 2001 From: Francesco Biscani Date: Sun, 28 Feb 2016 19:31:49 +0100 Subject: [PATCH 13/17] mp_integer: fix clang warning in the test. --- tests/mp_integer_03.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/mp_integer_03.cpp b/tests/mp_integer_03.cpp index f4c547972..c5d65b76b 100644 --- a/tests/mp_integer_03.cpp +++ b/tests/mp_integer_03.cpp @@ -793,6 +793,7 @@ struct gcd_tester void operator()(const T &) { typedef mp_integer int_type; + { int_type a, b, out; // Check with two zeroes. int_type::gcd(out,a,b); @@ -849,14 +850,15 @@ struct gcd_tester int_type::gcd(out,b,a); BOOST_CHECK_EQUAL(out,1); BOOST_CHECK(!out.is_static()); + } // Randomised testing. std::uniform_int_distribution pdist(0,1); std::uniform_int_distribution ndist(std::numeric_limits::min(),std::numeric_limits::max()); for (int i = 0; i < ntries; ++i) { auto aint = ndist(rng); auto bint = ndist(rng); - a = int_type(aint); - b = int_type(bint); + int_type a(aint); + int_type b(bint); int_type out; if (pdist(rng) && a.is_static()) { a.promote(); From e546e5e505f9afa5a14f77de3cddd59b78e32938 Mon Sep 17 00:00:00 2001 From: Francesco Biscani Date: Mon, 29 Feb 2016 00:24:54 +0100 Subject: [PATCH 14/17] mp_integer: add an implementation of division with remainder. --- src/mp_integer.hpp | 72 +++++++++++++++++++++++++++ tests/mp_integer_03.cpp | 108 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+) diff --git a/src/mp_integer.hpp b/src/mp_integer.hpp index 978f5bd9c..a976341fe 100644 --- a/src/mp_integer.hpp +++ b/src/mp_integer.hpp @@ -3237,6 +3237,78 @@ class mp_integer } return *this; } + /// Division with remainder. + /** + * This method will set \p q to the quotient and \p r to the remainder of n1 / n2. + * The sign of the remainder will be the sign of the numerator \p n1. + * + * @param[out] q the quotient. + * @param[out] r the remainder. + * @param[in] n1 the numerator. + * @param[in] n2 the denominator. + * + * @throws piranha::zero_division_error if \p n2 is zero. + * @throws std::invalid_argument if \p q and \p r are the same object. + */ + static void divrem(mp_integer &q, mp_integer &r, const mp_integer &n1, const mp_integer &n2) + { + if (unlikely(math::is_zero(n2))) { + piranha_throw(zero_division_error,"division by zero"); + } + if (unlikely(&q == &r)) { + piranha_throw(std::invalid_argument,"quotient and remainder cannot be the same object"); + } + // First we make sure that both q and r have the same storage. + bool s0 = q.is_static(); + if (s0 != r.is_static()) { + if (s0) { + // q is static, r is dynamic. Promote q. + q.promote(); + s0 = false; + } else { + // q is dynamic, r static. Promote r. + r.promote(); + } + } + piranha_assert(s0 == q.is_static() || q.is_static() == r.is_static()); + bool s1 = n1.is_static(), s2 = n2.is_static(); + // If n1 and n2 are both statics, we can carry the computation completely in static storage. + if (s1 && s2) { + if (!s0) { + q = mp_integer{}; + r = mp_integer{}; + } + q.m_int.g_st().div(q.m_int.g_st(),r.m_int.g_st(),n1.m_int.g_st(),n2.m_int.g_st()); + return; + } + // If either n1 or n2 are static, we need to promote the return values and re-check n1/n2. + if (s0) { + q.promote(); + r.promote(); + s1 = n1.is_static(); + s2 = n2.is_static(); + } + const unsigned mask = static_cast(!s1) + (static_cast(!s2) << 1u); + piranha_assert(mask > 0u); + switch (mask) { + // NOTE: case 0 here is not possible as it would mean that n1 and n2 are both static, + // but we handled the case above. + case 1u: + { + auto v2 = n2.m_int.g_st().get_mpz_view(); + ::mpz_tdiv_qr(&q.m_int.g_dy(),&r.m_int.g_dy(),&n1.m_int.g_dy(),v2); + break; + } + case 2u: + { + auto v1 = n1.m_int.g_st().get_mpz_view(); + ::mpz_tdiv_qr(&q.m_int.g_dy(),&r.m_int.g_dy(),v1,&n2.m_int.g_dy()); + break; + } + case 3u: + ::mpz_tdiv_qr(&q.m_int.g_dy(),&r.m_int.g_dy(),&n1.m_int.g_dy(),&n2.m_int.g_dy()); + } + } /// In-place modulo operation. /** * \note diff --git a/tests/mp_integer_03.cpp b/tests/mp_integer_03.cpp index c5d65b76b..57266f274 100644 --- a/tests/mp_integer_03.cpp +++ b/tests/mp_integer_03.cpp @@ -925,3 +925,111 @@ BOOST_AUTO_TEST_CASE(mp_integer_gcd_test) { boost::mpl::for_each(gcd_tester()); } + +struct divrem_tester +{ + template + void operator()(const T &) + { + typedef mp_integer int_type; + { + // Check throwing conditions + int_type q, r; + BOOST_CHECK_THROW(int_type::divrem(q,r,int_type(1),int_type(0)),zero_division_error); + BOOST_CHECK_THROW(int_type::divrem(q,q,int_type(1),int_type(1)),std::invalid_argument); + BOOST_CHECK_THROW(int_type::divrem(r,r,int_type(1),int_type(1)),std::invalid_argument); + } + // Randomised testing. + std::uniform_int_distribution pdist(0,1); + std::uniform_int_distribution ndist(std::numeric_limits::min(),std::numeric_limits::max()); + for (int i = 0; i < ntries; ++i) { + int_type a(ndist(rng)), b(ndist(rng)); + int_type q, r; + if (b == 0) { + continue; + } + if (pdist(rng) && a.is_static()) { + a.promote(); + } + if (pdist(rng) && b.is_static()) { + b.promote(); + } + if (pdist(rng)) { + q.promote(); + } + if (pdist(rng)) { + r.promote(); + } + int_type::divrem(q,r,a,b); + BOOST_CHECK_EQUAL(q,a / b); + BOOST_CHECK_EQUAL(r,a % b); + BOOST_CHECK(r.sign() == a.sign() || r.sign() == 0); + int_type::divrem(q,r,a*b,b); + BOOST_CHECK_EQUAL(q,a); + BOOST_CHECK_EQUAL(r,0); + int_type::divrem(q,r,a*a*b,b); + BOOST_CHECK_EQUAL(q,a*a); + BOOST_CHECK_EQUAL(r,0); + int_type::divrem(q,r,a*b + 1,b); + BOOST_CHECK_EQUAL(q,(a*b + 1) / b); + BOOST_CHECK_EQUAL(r,(a*b + 1) % b); + BOOST_CHECK(r.sign() == (a*b + 1).sign() || r.sign() == 0); + int_type::divrem(q,r,a*a*b + 1,b); + BOOST_CHECK_EQUAL(q,(a*a*b + 1) / b); + BOOST_CHECK_EQUAL(r,(a*a*b + 1) % b); + BOOST_CHECK(r.sign() == (a*a*b + 1).sign() || r.sign() == 0); + int_type::divrem(q,r,a,int_type(1)); + BOOST_CHECK_EQUAL(q,a); + BOOST_CHECK_EQUAL(r,0); + int_type::divrem(q,r,a,int_type(-1)); + BOOST_CHECK_EQUAL(q,-a); + BOOST_CHECK_EQUAL(r,0); + // Tests with overlapping arguments. + auto old_a(a); + int_type::divrem(a,r,a,b); + BOOST_CHECK_EQUAL(a,old_a / b); + BOOST_CHECK_EQUAL(r,old_a % b); + a = old_a; + auto old_b(b); + int_type::divrem(b,r,a,b); + BOOST_CHECK_EQUAL(b,a / old_b); + BOOST_CHECK_EQUAL(r,a % old_b); + b = old_b; + old_a = a; + int_type::divrem(q,a,a,b); + BOOST_CHECK_EQUAL(q,old_a / b); + BOOST_CHECK_EQUAL(a,old_a % b); + a = old_a; + old_b = b; + int_type::divrem(q,b,a,b); + BOOST_CHECK_EQUAL(q,a / old_b); + BOOST_CHECK_EQUAL(b,a % old_b); + b = old_b; + old_a = a; + int_type::divrem(q,a,a,a); + BOOST_CHECK_EQUAL(q,1); + BOOST_CHECK_EQUAL(a,0); + a = old_a; + old_b = b; + int_type::divrem(q,b,b,b); + BOOST_CHECK_EQUAL(q,1); + BOOST_CHECK_EQUAL(b,0); + b = old_b; + old_a = a; + int_type::divrem(a,r,a,a); + BOOST_CHECK_EQUAL(a,1); + BOOST_CHECK_EQUAL(r,0); + a = old_a; + old_b = b; + int_type::divrem(b,r,b,b); + BOOST_CHECK_EQUAL(b,1); + BOOST_CHECK_EQUAL(r,0); + b = old_b; + } + } +}; + +BOOST_AUTO_TEST_CASE(mp_integer_divrem_test) +{ + boost::mpl::for_each(divrem_tester()); +} From 197fba118ee53ad172a904a3b5960acba50a83ef Mon Sep 17 00:00:00 2001 From: Francesco Biscani Date: Mon, 29 Feb 2016 01:10:38 +0100 Subject: [PATCH 15/17] mp_integer: move some tests from 02 to 03, as the latest travis build timed out. --- tests/mp_integer_02.cpp | 285 ------------------------------------ tests/mp_integer_03.cpp | 309 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 309 insertions(+), 285 deletions(-) diff --git a/tests/mp_integer_02.cpp b/tests/mp_integer_02.cpp index e8cadb68d..88608babf 100644 --- a/tests/mp_integer_02.cpp +++ b/tests/mp_integer_02.cpp @@ -51,7 +51,6 @@ see https://www.gnu.org/licenses/. */ #include #include -#include "../src/binomial.hpp" #include "../src/config.hpp" #include "../src/debug_access.hpp" #include "../src/environment.hpp" @@ -2129,290 +2128,6 @@ BOOST_AUTO_TEST_CASE(mp_integer_hash_test) boost::mpl::for_each(hash_tester()); } -struct next_prime_tester -{ - template - void operator()(const T &) - { - typedef mp_integer int_type; - int_type n; - BOOST_CHECK_EQUAL(n.nextprime(),2); - n = 2; - BOOST_CHECK_EQUAL(n.nextprime(),3); - n = 3; - BOOST_CHECK_EQUAL(n.nextprime(),5); - n = 7901; - BOOST_CHECK_EQUAL(n.nextprime(),7907); - n = -1; - BOOST_CHECK_THROW(n.nextprime(),std::invalid_argument); - // Random tests. - std::uniform_int_distribution ud(std::numeric_limits::lowest(),std::numeric_limits::max()); - std::uniform_int_distribution promote_dist(0,1); - mpz_raii m; - for (int i = 0; i < ntries; ++i) { - auto tmp = ud(rng); - n = tmp; - if (promote_dist(rng) && n.is_static()) { - n.promote(); - } - if (tmp < 0) { - BOOST_CHECK_THROW(n.nextprime(),std::invalid_argument); - continue; - } - ::mpz_set_si(&m.m_mpz,static_cast(tmp)); - ::mpz_nextprime(&m.m_mpz,&m.m_mpz); - BOOST_CHECK_EQUAL(boost::lexical_cast(n.nextprime()),mpz_lexcast(m)); - } - } -}; - -BOOST_AUTO_TEST_CASE(mp_integer_next_prime_test) -{ - boost::mpl::for_each(next_prime_tester()); -} - -struct probab_prime_p_tester -{ - template - void operator()(const T &) - { - typedef mp_integer int_type; - int_type n; - BOOST_CHECK(n.probab_prime_p() == 0); - n = 1; - BOOST_CHECK(n.probab_prime_p() == 0); - n = 2; - BOOST_CHECK(n.probab_prime_p() != 0); - n = 3; - BOOST_CHECK(n.probab_prime_p() != 0); - n = 5; - BOOST_CHECK(n.probab_prime_p() != 0); - n = 11; - BOOST_CHECK(n.probab_prime_p() != 0); - n = 16; - BOOST_CHECK(n.probab_prime_p() != 2); - n = 7901; - BOOST_CHECK(n.probab_prime_p() != 0); - n = 7907; - BOOST_CHECK(n.probab_prime_p(5) != 0); - n = -1; - BOOST_CHECK_THROW(n.probab_prime_p(),std::invalid_argument); - n = 5; - BOOST_CHECK_THROW(n.probab_prime_p(0),std::invalid_argument); - BOOST_CHECK_THROW(n.probab_prime_p(-1),std::invalid_argument); - } -}; - -BOOST_AUTO_TEST_CASE(mp_integer_probab_prime_p_test) -{ - boost::mpl::for_each(probab_prime_p_tester()); -} - -struct integer_sqrt_tester -{ - template - void operator()(const T &) - { - typedef mp_integer int_type; - int_type n; - BOOST_CHECK(n.sqrt() == 0); - n = 1; - BOOST_CHECK(n.sqrt() == 1); - n = 2; - BOOST_CHECK(n.sqrt() == 1); - n = 3; - BOOST_CHECK(n.sqrt() == 1); - n = 4; - BOOST_CHECK(n.sqrt() == 2); - n = 5; - BOOST_CHECK(n.sqrt() == 2); - // Random tests. - std::uniform_int_distribution ud(std::numeric_limits::lowest(),std::numeric_limits::max()); - std::uniform_int_distribution promote_dist(0,1); - mpz_raii m; - for (int i = 0; i < ntries; ++i) { - auto tmp = ud(rng); - n = tmp; - if (promote_dist(rng) && n.is_static()) { - n.promote(); - } - if (tmp < 0) { - BOOST_CHECK_THROW(n.sqrt(),std::invalid_argument); - continue; - } - ::mpz_set_si(&m.m_mpz,static_cast(tmp)); - ::mpz_sqrt(&m.m_mpz,&m.m_mpz); - BOOST_CHECK_EQUAL(boost::lexical_cast(n.sqrt()),mpz_lexcast(m)); - } - } -}; - -BOOST_AUTO_TEST_CASE(mp_integer_integer_sqrt_test) -{ - boost::mpl::for_each(integer_sqrt_tester()); -} - -struct factorial_tester -{ - template - void operator()(const T &) - { - typedef mp_integer int_type; - int_type n; - BOOST_CHECK(n.factorial() == 1); - n = 1; - BOOST_CHECK(n.factorial() == 1); - n = 2; - BOOST_CHECK(n.factorial() == 2); - n = 3; - BOOST_CHECK(n.factorial() == 6); - n = 4; - BOOST_CHECK(n.factorial() == 24); - n = 5; - BOOST_CHECK(n.factorial() == 24 * 5); - // Random tests. - std::uniform_int_distribution ud(-1000,1000); - std::uniform_int_distribution promote_dist(0,1); - mpz_raii m; - for (int i = 0; i < ntries; ++i) { - auto tmp = ud(rng); - n = tmp; - if (promote_dist(rng) && n.is_static()) { - n.promote(); - } - if (tmp < 0) { - BOOST_CHECK_THROW(n.factorial(),std::invalid_argument); - continue; - } - ::mpz_set_si(&m.m_mpz,static_cast(tmp)); - ::mpz_fac_ui(&m.m_mpz,static_cast(tmp)); - BOOST_CHECK_EQUAL(boost::lexical_cast(n.factorial()),mpz_lexcast(m)); - BOOST_CHECK_EQUAL(boost::lexical_cast(math::factorial(n)),mpz_lexcast(m)); - } - } -}; - -BOOST_AUTO_TEST_CASE(mp_integer_factorial_test) -{ - boost::mpl::for_each(factorial_tester()); -} - -struct binomial_tester -{ - template - void operator()(const T &) - { - typedef mp_integer int_type; - BOOST_CHECK((has_binomial::value)); - BOOST_CHECK((has_binomial::value)); - BOOST_CHECK((has_binomial::value)); - BOOST_CHECK((has_binomial::value)); - BOOST_CHECK((has_binomial::value)); - int_type n; - BOOST_CHECK(n.binomial(0) == 1); - BOOST_CHECK(n.binomial(1) == 0); - n = 1; - BOOST_CHECK(n.binomial(1) == 1); - n = 5; - BOOST_CHECK(n.binomial(3) == 10); - n = -5; - BOOST_CHECK(n.binomial(int_type(4)) == 70); - BOOST_CHECK((has_binomial::value)); - BOOST_CHECK((has_binomial::value)); - BOOST_CHECK((std::is_same::value)); - BOOST_CHECK((std::is_same::value)); - BOOST_CHECK((has_binomial::value)); - BOOST_CHECK((has_binomial::value)); - BOOST_CHECK((std::is_same::value)); - BOOST_CHECK((std::is_same::value)); - BOOST_CHECK((has_binomial::value)); - BOOST_CHECK((std::is_same::value)); - // Random tests. - std::uniform_int_distribution ud(-1000,1000); - std::uniform_int_distribution promote_dist(0,1); - mpz_raii m; - for (int i = 0; i < ntries; ++i) { - auto tmp1 = ud(rng), tmp2 = ud(rng); - n = tmp1; - if (promote_dist(rng) && n.is_static()) { - n.promote(); - } - if (tmp2 < 0) { - // NOTE: we cannot check this case with GMP, defer to some tests below. - BOOST_CHECK_NO_THROW(n.binomial(tmp2)); - continue; - } - ::mpz_set_si(&m.m_mpz,static_cast(tmp1)); - ::mpz_bin_ui(&m.m_mpz,&m.m_mpz,static_cast(tmp2)); - BOOST_CHECK_EQUAL(boost::lexical_cast(n.binomial(tmp2)),mpz_lexcast(m)); - // int -- integral. - BOOST_CHECK_EQUAL(math::binomial(n,tmp2),n.binomial(tmp2)); - // integral -- int. - BOOST_CHECK_EQUAL(math::binomial(tmp2,n),int_type(tmp2).binomial(n)); - // integral -- integral. - BOOST_CHECK_EQUAL(math::binomial(tmp2,tmp1),integer(tmp2).binomial(tmp1)); - // int -- double. - BOOST_CHECK_EQUAL(math::binomial(n,static_cast(tmp2)),math::binomial(double(n),static_cast(tmp2))); - // double -- int. - BOOST_CHECK_EQUAL(math::binomial(static_cast(tmp2),n),math::binomial(static_cast(tmp2),double(n))); - BOOST_CHECK_EQUAL(n.binomial(tmp2),n.binomial(int_type(tmp2))); - BOOST_CHECK_EQUAL(n.binomial(long(tmp2)),n.binomial(int_type(tmp2))); - BOOST_CHECK_EQUAL(n.binomial((long long)(tmp2)),n.binomial(int_type(tmp2))); - BOOST_CHECK_EQUAL(n.binomial((unsigned long)(tmp2)),n.binomial(int_type(tmp2))); - BOOST_CHECK_EQUAL(n.binomial((unsigned long long)(tmp2)),n.binomial(int_type(tmp2))); - } - BOOST_CHECK_THROW(n.binomial(std::numeric_limits::max() + int_type(1)),std::invalid_argument); - // Negative k. - BOOST_CHECK_EQUAL(int_type{-3}.binomial(-4),-3); - BOOST_CHECK_EQUAL(int_type{-3}.binomial(-10),-36); - BOOST_CHECK_EQUAL(int_type{-3}.binomial(-1),0); - BOOST_CHECK_EQUAL(int_type{3}.binomial(-1),0); - BOOST_CHECK_EQUAL(int_type{10}.binomial(-1),0); - BOOST_CHECK_EQUAL(int_type{-3}.binomial(-3),1); - BOOST_CHECK_EQUAL(int_type{-1}.binomial(-1),1); - } -}; - -BOOST_AUTO_TEST_CASE(mp_integer_binomial_test) -{ - boost::mpl::for_each(binomial_tester()); - // Check the ints. - using int_type = integer; - BOOST_CHECK((has_binomial::value)); - BOOST_CHECK_EQUAL(math::binomial(4,2),math::binomial(int_type(4),2)); - BOOST_CHECK((has_binomial::value)); - BOOST_CHECK_EQUAL(math::binomial(char(4),2u),math::binomial(int_type(4),2)); - BOOST_CHECK((has_binomial::value)); - BOOST_CHECK_EQUAL(math::binomial(7ll,4),math::binomial(int_type(7),4)); - BOOST_CHECK((std::is_same::value)); - BOOST_CHECK_EQUAL(math::binomial(-7ll,4u),math::binomial(int_type(-7),4)); - // Different bits sizes. - BOOST_CHECK((!has_binomial,mp_integer<32>>::value)); - BOOST_CHECK((!has_binomial,mp_integer<16>>::value)); -} - -struct sin_cos_tester -{ - template - void operator()(const T &) - { - typedef mp_integer int_type; - BOOST_CHECK_EQUAL(math::sin(int_type()),0); - BOOST_CHECK_EQUAL(math::cos(int_type()),1); - BOOST_CHECK_THROW(math::sin(int_type(1)),std::invalid_argument); - BOOST_CHECK_THROW(math::cos(int_type(1)),std::invalid_argument); - BOOST_CHECK((std::is_same::value)); - BOOST_CHECK((std::is_same::value)); - BOOST_CHECK(has_sine::value); - BOOST_CHECK(has_cosine::value); - } -}; - -BOOST_AUTO_TEST_CASE(mp_integer_sin_cos_test) -{ - boost::mpl::for_each(sin_cos_tester()); -} - struct partial_tester { template diff --git a/tests/mp_integer_03.cpp b/tests/mp_integer_03.cpp index 57266f274..65e83637a 100644 --- a/tests/mp_integer_03.cpp +++ b/tests/mp_integer_03.cpp @@ -37,12 +37,18 @@ see https://www.gnu.org/licenses/. */ #include #include #include +#include +#include +#include #include #include +#include #include #include #include +#include +#include "../src/binomial.hpp" #include "../src/config.hpp" #include "../src/environment.hpp" #include "../src/exceptions.hpp" @@ -61,6 +67,25 @@ using size_types = boost::mpl::vector,std::integra static std::mt19937 rng; constexpr int ntries = 1000; +using mpz_raii = detail::mpz_raii; + +static inline std::string mpz_lexcast(const mpz_raii &m) +{ + std::ostringstream os; + const std::size_t size_base10 = ::mpz_sizeinbase(&m.m_mpz,10); + if (unlikely(size_base10 > std::numeric_limits::max() - static_cast(2))) { + piranha_throw(std::overflow_error,"number of digits is too large"); + } + const auto total_size = size_base10 + 2u; + std::vector tmp; + tmp.resize(static_cast::size_type>(total_size)); + if (unlikely(tmp.size() != total_size)) { + piranha_throw(std::overflow_error,"number of digits is too large"); + } + os << ::mpz_get_str(&tmp[0u],10,&m.m_mpz); + return os.str(); +} + struct static_lshift_tester { template @@ -1033,3 +1058,287 @@ BOOST_AUTO_TEST_CASE(mp_integer_divrem_test) { boost::mpl::for_each(divrem_tester()); } + +struct next_prime_tester +{ + template + void operator()(const T &) + { + typedef mp_integer int_type; + int_type n; + BOOST_CHECK_EQUAL(n.nextprime(),2); + n = 2; + BOOST_CHECK_EQUAL(n.nextprime(),3); + n = 3; + BOOST_CHECK_EQUAL(n.nextprime(),5); + n = 7901; + BOOST_CHECK_EQUAL(n.nextprime(),7907); + n = -1; + BOOST_CHECK_THROW(n.nextprime(),std::invalid_argument); + // Random tests. + std::uniform_int_distribution ud(std::numeric_limits::lowest(),std::numeric_limits::max()); + std::uniform_int_distribution promote_dist(0,1); + mpz_raii m; + for (int i = 0; i < ntries; ++i) { + auto tmp = ud(rng); + n = tmp; + if (promote_dist(rng) && n.is_static()) { + n.promote(); + } + if (tmp < 0) { + BOOST_CHECK_THROW(n.nextprime(),std::invalid_argument); + continue; + } + ::mpz_set_si(&m.m_mpz,static_cast(tmp)); + ::mpz_nextprime(&m.m_mpz,&m.m_mpz); + BOOST_CHECK_EQUAL(boost::lexical_cast(n.nextprime()),mpz_lexcast(m)); + } + } +}; + +BOOST_AUTO_TEST_CASE(mp_integer_next_prime_test) +{ + boost::mpl::for_each(next_prime_tester()); +} + +struct probab_prime_p_tester +{ + template + void operator()(const T &) + { + typedef mp_integer int_type; + int_type n; + BOOST_CHECK(n.probab_prime_p() == 0); + n = 1; + BOOST_CHECK(n.probab_prime_p() == 0); + n = 2; + BOOST_CHECK(n.probab_prime_p() != 0); + n = 3; + BOOST_CHECK(n.probab_prime_p() != 0); + n = 5; + BOOST_CHECK(n.probab_prime_p() != 0); + n = 11; + BOOST_CHECK(n.probab_prime_p() != 0); + n = 16; + BOOST_CHECK(n.probab_prime_p() != 2); + n = 7901; + BOOST_CHECK(n.probab_prime_p() != 0); + n = 7907; + BOOST_CHECK(n.probab_prime_p(5) != 0); + n = -1; + BOOST_CHECK_THROW(n.probab_prime_p(),std::invalid_argument); + n = 5; + BOOST_CHECK_THROW(n.probab_prime_p(0),std::invalid_argument); + BOOST_CHECK_THROW(n.probab_prime_p(-1),std::invalid_argument); + } +}; + +BOOST_AUTO_TEST_CASE(mp_integer_probab_prime_p_test) +{ + boost::mpl::for_each(probab_prime_p_tester()); +} + +struct integer_sqrt_tester +{ + template + void operator()(const T &) + { + typedef mp_integer int_type; + int_type n; + BOOST_CHECK(n.sqrt() == 0); + n = 1; + BOOST_CHECK(n.sqrt() == 1); + n = 2; + BOOST_CHECK(n.sqrt() == 1); + n = 3; + BOOST_CHECK(n.sqrt() == 1); + n = 4; + BOOST_CHECK(n.sqrt() == 2); + n = 5; + BOOST_CHECK(n.sqrt() == 2); + // Random tests. + std::uniform_int_distribution ud(std::numeric_limits::lowest(),std::numeric_limits::max()); + std::uniform_int_distribution promote_dist(0,1); + mpz_raii m; + for (int i = 0; i < ntries; ++i) { + auto tmp = ud(rng); + n = tmp; + if (promote_dist(rng) && n.is_static()) { + n.promote(); + } + if (tmp < 0) { + BOOST_CHECK_THROW(n.sqrt(),std::invalid_argument); + continue; + } + ::mpz_set_si(&m.m_mpz,static_cast(tmp)); + ::mpz_sqrt(&m.m_mpz,&m.m_mpz); + BOOST_CHECK_EQUAL(boost::lexical_cast(n.sqrt()),mpz_lexcast(m)); + } + } +}; + +BOOST_AUTO_TEST_CASE(mp_integer_integer_sqrt_test) +{ + boost::mpl::for_each(integer_sqrt_tester()); +} + +struct factorial_tester +{ + template + void operator()(const T &) + { + typedef mp_integer int_type; + int_type n; + BOOST_CHECK(n.factorial() == 1); + n = 1; + BOOST_CHECK(n.factorial() == 1); + n = 2; + BOOST_CHECK(n.factorial() == 2); + n = 3; + BOOST_CHECK(n.factorial() == 6); + n = 4; + BOOST_CHECK(n.factorial() == 24); + n = 5; + BOOST_CHECK(n.factorial() == 24 * 5); + // Random tests. + std::uniform_int_distribution ud(-1000,1000); + std::uniform_int_distribution promote_dist(0,1); + mpz_raii m; + for (int i = 0; i < ntries; ++i) { + auto tmp = ud(rng); + n = tmp; + if (promote_dist(rng) && n.is_static()) { + n.promote(); + } + if (tmp < 0) { + BOOST_CHECK_THROW(n.factorial(),std::invalid_argument); + continue; + } + ::mpz_set_si(&m.m_mpz,static_cast(tmp)); + ::mpz_fac_ui(&m.m_mpz,static_cast(tmp)); + BOOST_CHECK_EQUAL(boost::lexical_cast(n.factorial()),mpz_lexcast(m)); + BOOST_CHECK_EQUAL(boost::lexical_cast(math::factorial(n)),mpz_lexcast(m)); + } + } +}; + +BOOST_AUTO_TEST_CASE(mp_integer_factorial_test) +{ + boost::mpl::for_each(factorial_tester()); +} + +struct binomial_tester +{ + template + void operator()(const T &) + { + typedef mp_integer int_type; + BOOST_CHECK((has_binomial::value)); + BOOST_CHECK((has_binomial::value)); + BOOST_CHECK((has_binomial::value)); + BOOST_CHECK((has_binomial::value)); + BOOST_CHECK((has_binomial::value)); + int_type n; + BOOST_CHECK(n.binomial(0) == 1); + BOOST_CHECK(n.binomial(1) == 0); + n = 1; + BOOST_CHECK(n.binomial(1) == 1); + n = 5; + BOOST_CHECK(n.binomial(3) == 10); + n = -5; + BOOST_CHECK(n.binomial(int_type(4)) == 70); + BOOST_CHECK((has_binomial::value)); + BOOST_CHECK((has_binomial::value)); + BOOST_CHECK((std::is_same::value)); + BOOST_CHECK((std::is_same::value)); + BOOST_CHECK((has_binomial::value)); + BOOST_CHECK((has_binomial::value)); + BOOST_CHECK((std::is_same::value)); + BOOST_CHECK((std::is_same::value)); + BOOST_CHECK((has_binomial::value)); + BOOST_CHECK((std::is_same::value)); + // Random tests. + std::uniform_int_distribution ud(-1000,1000); + std::uniform_int_distribution promote_dist(0,1); + mpz_raii m; + for (int i = 0; i < ntries; ++i) { + auto tmp1 = ud(rng), tmp2 = ud(rng); + n = tmp1; + if (promote_dist(rng) && n.is_static()) { + n.promote(); + } + if (tmp2 < 0) { + // NOTE: we cannot check this case with GMP, defer to some tests below. + BOOST_CHECK_NO_THROW(n.binomial(tmp2)); + continue; + } + ::mpz_set_si(&m.m_mpz,static_cast(tmp1)); + ::mpz_bin_ui(&m.m_mpz,&m.m_mpz,static_cast(tmp2)); + BOOST_CHECK_EQUAL(boost::lexical_cast(n.binomial(tmp2)),mpz_lexcast(m)); + // int -- integral. + BOOST_CHECK_EQUAL(math::binomial(n,tmp2),n.binomial(tmp2)); + // integral -- int. + BOOST_CHECK_EQUAL(math::binomial(tmp2,n),int_type(tmp2).binomial(n)); + // integral -- integral. + BOOST_CHECK_EQUAL(math::binomial(tmp2,tmp1),integer(tmp2).binomial(tmp1)); + // int -- double. + BOOST_CHECK_EQUAL(math::binomial(n,static_cast(tmp2)),math::binomial(double(n),static_cast(tmp2))); + // double -- int. + BOOST_CHECK_EQUAL(math::binomial(static_cast(tmp2),n),math::binomial(static_cast(tmp2),double(n))); + BOOST_CHECK_EQUAL(n.binomial(tmp2),n.binomial(int_type(tmp2))); + BOOST_CHECK_EQUAL(n.binomial(long(tmp2)),n.binomial(int_type(tmp2))); + BOOST_CHECK_EQUAL(n.binomial((long long)(tmp2)),n.binomial(int_type(tmp2))); + BOOST_CHECK_EQUAL(n.binomial((unsigned long)(tmp2)),n.binomial(int_type(tmp2))); + BOOST_CHECK_EQUAL(n.binomial((unsigned long long)(tmp2)),n.binomial(int_type(tmp2))); + } + BOOST_CHECK_THROW(n.binomial(std::numeric_limits::max() + int_type(1)),std::invalid_argument); + // Negative k. + BOOST_CHECK_EQUAL(int_type{-3}.binomial(-4),-3); + BOOST_CHECK_EQUAL(int_type{-3}.binomial(-10),-36); + BOOST_CHECK_EQUAL(int_type{-3}.binomial(-1),0); + BOOST_CHECK_EQUAL(int_type{3}.binomial(-1),0); + BOOST_CHECK_EQUAL(int_type{10}.binomial(-1),0); + BOOST_CHECK_EQUAL(int_type{-3}.binomial(-3),1); + BOOST_CHECK_EQUAL(int_type{-1}.binomial(-1),1); + } +}; + +BOOST_AUTO_TEST_CASE(mp_integer_binomial_test) +{ + boost::mpl::for_each(binomial_tester()); + // Check the ints. + using int_type = integer; + BOOST_CHECK((has_binomial::value)); + BOOST_CHECK_EQUAL(math::binomial(4,2),math::binomial(int_type(4),2)); + BOOST_CHECK((has_binomial::value)); + BOOST_CHECK_EQUAL(math::binomial(char(4),2u),math::binomial(int_type(4),2)); + BOOST_CHECK((has_binomial::value)); + BOOST_CHECK_EQUAL(math::binomial(7ll,4),math::binomial(int_type(7),4)); + BOOST_CHECK((std::is_same::value)); + BOOST_CHECK_EQUAL(math::binomial(-7ll,4u),math::binomial(int_type(-7),4)); + // Different bits sizes. + BOOST_CHECK((!has_binomial,mp_integer<32>>::value)); + BOOST_CHECK((!has_binomial,mp_integer<16>>::value)); +} + +struct sin_cos_tester +{ + template + void operator()(const T &) + { + typedef mp_integer int_type; + BOOST_CHECK_EQUAL(math::sin(int_type()),0); + BOOST_CHECK_EQUAL(math::cos(int_type()),1); + BOOST_CHECK_THROW(math::sin(int_type(1)),std::invalid_argument); + BOOST_CHECK_THROW(math::cos(int_type(1)),std::invalid_argument); + BOOST_CHECK((std::is_same::value)); + BOOST_CHECK((std::is_same::value)); + BOOST_CHECK(has_sine::value); + BOOST_CHECK(has_cosine::value); + } +}; + +BOOST_AUTO_TEST_CASE(mp_integer_sin_cos_test) +{ + boost::mpl::for_each(sin_cos_tester()); +} From 2926ddddb1c420c9eaa4fee01b1555c8f4efe424 Mon Sep 17 00:00:00 2001 From: Francesco Biscani Date: Mon, 29 Feb 2016 02:20:22 +0100 Subject: [PATCH 16/17] mp_integer: minor internal doc fix. [skip ci] --- src/mp_integer.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mp_integer.hpp b/src/mp_integer.hpp index a976341fe..617b74c46 100644 --- a/src/mp_integer.hpp +++ b/src/mp_integer.hpp @@ -3995,7 +3995,7 @@ class mp_integer return; } // out will have to be mpz in any case, promote it if needed and re-check the - // static flags in case this coincides with n1 and/or n2. + // static flags in case out coincides with n1 and/or n2. if (s0) { out.m_int.promote(); s1 = n1.is_static(); From 278aa52af9e5f20ee7ed5822384370b4edd94ab5 Mon Sep 17 00:00:00 2001 From: Francesco Biscani Date: Mon, 29 Feb 2016 13:09:42 +0100 Subject: [PATCH 17/17] Add a public function for exact division and implement the integer/rational specialisations for it. --- src/math.hpp | 46 +++++++++++++++++++++++++++++++++++++++++ src/mp_integer.hpp | 32 ++++++++++++++++++++++++++++ src/mp_rational.hpp | 26 +++++++++++++++++++++++ tests/math.cpp | 8 +++++++ tests/mp_integer_03.cpp | 22 ++++++++++++++++++++ tests/mp_rational.cpp | 23 +++++++++++++++++++++ 6 files changed, 157 insertions(+) diff --git a/src/math.hpp b/src/math.hpp index 0a040457d..a37cbd7ed 100644 --- a/src/math.hpp +++ b/src/math.hpp @@ -2393,6 +2393,33 @@ inline auto div3(T &a, const T &b, const T &c) -> decltype(div3_impl()(a,b,c) return div3_impl()(a,b,c); } +/// Default functor for the implementation of piranha::math::divexact(). +/** + * This functor should be specialised via the \p std::enable_if mechanism. Default implementation will not define + * the call operator, and will hence result in a compilation error when used. + */ +template +struct divexact_impl {}; + +/// Exact division. +/** + * This method will write into \p a the exact result of b / c. The actual implementation of this function is in the piranha::math::divexact_impl functor's + * call operator. + * + * @param[out] a the return value. + * @param[in] b the first operand. + * @param[in] c the second operand. + * + * @return the value returned by the call operator of piranha::math::divexact_impl. + * + * @throws unspecified any exception thrown by the call operator of the piranha::math::divexact_impl functor. + */ +template +inline auto divexact(T &a, const T &b, const T &c) -> decltype(divexact_impl()(a,b,c)) +{ + return divexact_impl()(a,b,c); +} + } namespace detail @@ -2660,6 +2687,25 @@ class has_gcd3: detail::sfinae_types template const bool has_gcd3::value; +/// Detect piranha::math::divexact(). +/** + * The type trait will be \p true if piranha::math::divexact() can be used on instances of type \p T, + * \p false otherwise. + */ +template +class has_divexact: detail::sfinae_types +{ + template + static auto test(const T1 &) -> decltype(math::divexact(std::declval(),std::declval(),std::declval()),void(),yes()); + static no test(...); + public: + /// Value of the type trait. + static const bool value = std::is_same())),yes>::value; +}; + +template +const bool has_divexact::value; + } #endif diff --git a/src/mp_integer.hpp b/src/mp_integer.hpp index 617b74c46..9c01138c4 100644 --- a/src/mp_integer.hpp +++ b/src/mp_integer.hpp @@ -4345,6 +4345,38 @@ struct div3_impl::value>::typ } }; +/// Implementation of piranha::math::divexact() for piranha::mp_integer. +/** + * This specialisation is enabled when \p T is an instance of piranha::mp_integer. + */ +template +struct divexact_impl::value>::type> +{ + /// Call operator. + /** + * The call operator will first determine quotient and remainder via piranha::mp_integer::divrem(). + * If the remainder is not null, an error will be thrown. + * + * @param[out] out return value. + * @param[in] a first argument. + * @param[in] b second argument. + * + * @return a reference to \p out. + * + * @throws std::invalid_argument if the division of \p a by \p b is not exact. + * @throws unspecified any exception thrown by piranha::mp_integer::divrem(). + */ + T &operator()(T &out, const T &a, const T &b) const + { + T r; + T::divrem(out,r,a,b); + if (!is_zero(r)) { + piranha_throw(std::invalid_argument,"integer division is not exact"); + } + return out; + } +}; + } namespace detail diff --git a/src/mp_rational.hpp b/src/mp_rational.hpp index 1c117ed58..5891be009 100644 --- a/src/mp_rational.hpp +++ b/src/mp_rational.hpp @@ -2086,6 +2086,32 @@ struct binomial_impl> } }; +/// Implementation of piranha::math::divexact() for piranha::mp_rational. +/** + * This specialisation is enabled when \p T is an instance of piranha::mp_rational. + */ +template +struct divexact_impl::value>::type> +{ + /// Call operator. + /** + * The call is equivalent to piranha::math::div3(). The exact result of the division + * of \p q1 by \p q2 will be stored in \p out. + * + * @param[out] out the output value. + * @param[in] q1 first argument. + * @param[in] q2 second argument. + * + * @return the output of piranha::math::div3(out,q1,q2). + * + * @throws unspecified any exception thrown by piranha::math::div3(). + */ + auto operator()(T &out, const T &q1, const T &q2) const -> decltype(div3(out,q1,q2)) + { + return div3(out,q1,q2); + } +}; + } namespace detail diff --git a/tests/math.cpp b/tests/math.cpp index 2f3085026..0b827e2f9 100644 --- a/tests/math.cpp +++ b/tests/math.cpp @@ -953,3 +953,11 @@ BOOST_AUTO_TEST_CASE(math_gcd_test) mock_type m0; BOOST_CHECK_NO_THROW(gcd3(m0,mock_type{},mock_type{})); } + +BOOST_AUTO_TEST_CASE(math_divexact_test) +{ + BOOST_CHECK(!has_divexact::value); + BOOST_CHECK(!has_divexact::value); + BOOST_CHECK(!has_divexact::value); + BOOST_CHECK(!has_divexact::value); +} diff --git a/tests/mp_integer_03.cpp b/tests/mp_integer_03.cpp index 65e83637a..09c542ac0 100644 --- a/tests/mp_integer_03.cpp +++ b/tests/mp_integer_03.cpp @@ -1342,3 +1342,25 @@ BOOST_AUTO_TEST_CASE(mp_integer_sin_cos_test) { boost::mpl::for_each(sin_cos_tester()); } + +struct math_divexact_tester +{ + template + void operator()(const T &) + { + typedef mp_integer int_type; + BOOST_CHECK(has_divexact::value); + int_type out; + math::divexact(out,int_type(4),int_type(-2)); + BOOST_CHECK_EQUAL(out,-2); + math::divexact(out,int_type(0),int_type(-2)); + BOOST_CHECK_EQUAL(out,0); + BOOST_CHECK_THROW(math::divexact(out,int_type(0),int_type(0)),zero_division_error); + BOOST_CHECK_THROW(math::divexact(out,int_type(3),int_type(2)),std::invalid_argument); + } +}; + +BOOST_AUTO_TEST_CASE(mp_integer_math_divexact_test) +{ + boost::mpl::for_each(math_divexact_tester()); +} diff --git a/tests/mp_rational.cpp b/tests/mp_rational.cpp index 985e1b6b9..bcf85b8dc 100644 --- a/tests/mp_rational.cpp +++ b/tests/mp_rational.cpp @@ -2295,3 +2295,26 @@ BOOST_AUTO_TEST_CASE(mp_rational_is_unitary_test) { boost::mpl::for_each(is_unitary_tester()); } + +struct divexact_tester +{ + template + void operator()(const T &) + { + using q_type = mp_rational; + BOOST_CHECK(has_divexact::value); + q_type out; + math::divexact(out,q_type{3},q_type{-2}); + BOOST_CHECK_EQUAL(out,q_type{3}/q_type{-2}); + math::divexact(out,q_type{8},q_type{-2}); + BOOST_CHECK_EQUAL(out,q_type{-4}); + BOOST_CHECK_THROW(math::divexact(out,q_type{8},q_type{0}),zero_division_error); + math::divexact(out,q_type{0},q_type{-2}); + BOOST_CHECK_EQUAL(out,0); + } +}; + +BOOST_AUTO_TEST_CASE(mp_rational_divexact_test) +{ + boost::mpl::for_each(divexact_tester()); +}