Fixed incorrect loop termination in lcm_range.

lcm_range used to incorrectly terminate on value of 1. Instead, it should
terminate on 0 since any further elements of the range no longer affect
the result of the function.

Also, added tests for the gcd_range and lcm_range functions verifying
that they terminate their loops at the correct points.
This commit is contained in:
Andrey Semashev
2019-03-26 01:55:18 +03:00
parent 5195d682ec
commit 33c165552c
2 changed files with 38 additions and 27 deletions

View File

@ -83,9 +83,9 @@ namespace boost {
} }
#endif #endif
template <class T, bool a = template <class T, bool a =
#ifndef BOOST_NO_CXX11_HDR_TYPE_TRAITS #ifndef BOOST_NO_CXX11_HDR_TYPE_TRAITS
std::is_unsigned<T>::value || std::is_unsigned<T>::value ||
#endif #endif
(std::numeric_limits<T>::is_specialized && !std::numeric_limits<T>::is_signed)> (std::numeric_limits<T>::is_specialized && !std::numeric_limits<T>::is_signed)>
struct gcd_traits_abs_defaults struct gcd_traits_abs_defaults
@ -210,28 +210,28 @@ namespace boost {
// this works for signed types too, as by the time these functions // this works for signed types too, as by the time these functions
// are called, all values are > 0. // are called, all values are > 0.
// //
template <> struct gcd_traits<long> : public gcd_traits_defaults<long> template <> struct gcd_traits<long> : public gcd_traits_defaults<long>
{ BOOST_FORCEINLINE static unsigned make_odd(long& val)BOOST_NOEXCEPT{ unsigned result = gcd_traits<unsigned long>::find_lsb(val); val >>= result; return result; } }; { BOOST_FORCEINLINE static unsigned make_odd(long& val)BOOST_NOEXCEPT{ unsigned result = gcd_traits<unsigned long>::find_lsb(val); val >>= result; return result; } };
template <> struct gcd_traits<unsigned int> : public gcd_traits_defaults<unsigned int> template <> struct gcd_traits<unsigned int> : public gcd_traits_defaults<unsigned int>
{ BOOST_FORCEINLINE static unsigned make_odd(unsigned int& val)BOOST_NOEXCEPT{ unsigned result = gcd_traits<unsigned long>::find_lsb(val); val >>= result; return result; } }; { BOOST_FORCEINLINE static unsigned make_odd(unsigned int& val)BOOST_NOEXCEPT{ unsigned result = gcd_traits<unsigned long>::find_lsb(val); val >>= result; return result; } };
template <> struct gcd_traits<int> : public gcd_traits_defaults<int> template <> struct gcd_traits<int> : public gcd_traits_defaults<int>
{ BOOST_FORCEINLINE static unsigned make_odd(int& val)BOOST_NOEXCEPT{ unsigned result = gcd_traits<unsigned long>::find_lsb(val); val >>= result; return result; } }; { BOOST_FORCEINLINE static unsigned make_odd(int& val)BOOST_NOEXCEPT{ unsigned result = gcd_traits<unsigned long>::find_lsb(val); val >>= result; return result; } };
template <> struct gcd_traits<unsigned short> : public gcd_traits_defaults<unsigned short> template <> struct gcd_traits<unsigned short> : public gcd_traits_defaults<unsigned short>
{ BOOST_FORCEINLINE static unsigned make_odd(unsigned short& val)BOOST_NOEXCEPT{ unsigned result = gcd_traits<unsigned long>::find_lsb(val); val >>= result; return result; } }; { BOOST_FORCEINLINE static unsigned make_odd(unsigned short& val)BOOST_NOEXCEPT{ unsigned result = gcd_traits<unsigned long>::find_lsb(val); val >>= result; return result; } };
template <> struct gcd_traits<short> : public gcd_traits_defaults<short> template <> struct gcd_traits<short> : public gcd_traits_defaults<short>
{ BOOST_FORCEINLINE static unsigned make_odd(short& val)BOOST_NOEXCEPT{ unsigned result = gcd_traits<unsigned long>::find_lsb(val); val >>= result; return result; } }; { BOOST_FORCEINLINE static unsigned make_odd(short& val)BOOST_NOEXCEPT{ unsigned result = gcd_traits<unsigned long>::find_lsb(val); val >>= result; return result; } };
template <> struct gcd_traits<unsigned char> : public gcd_traits_defaults<unsigned char> template <> struct gcd_traits<unsigned char> : public gcd_traits_defaults<unsigned char>
{ BOOST_FORCEINLINE static unsigned make_odd(unsigned char& val)BOOST_NOEXCEPT{ unsigned result = gcd_traits<unsigned long>::find_lsb(val); val >>= result; return result; } }; { BOOST_FORCEINLINE static unsigned make_odd(unsigned char& val)BOOST_NOEXCEPT{ unsigned result = gcd_traits<unsigned long>::find_lsb(val); val >>= result; return result; } };
template <> struct gcd_traits<signed char> : public gcd_traits_defaults<signed char> template <> struct gcd_traits<signed char> : public gcd_traits_defaults<signed char>
{ BOOST_FORCEINLINE static unsigned make_odd(signed char& val)BOOST_NOEXCEPT{ unsigned result = gcd_traits<unsigned long>::find_lsb(val); val >>= result; return result; } }; { BOOST_FORCEINLINE static unsigned make_odd(signed char& val)BOOST_NOEXCEPT{ unsigned result = gcd_traits<unsigned long>::find_lsb(val); val >>= result; return result; } };
template <> struct gcd_traits<char> : public gcd_traits_defaults<char> template <> struct gcd_traits<char> : public gcd_traits_defaults<char>
{ BOOST_FORCEINLINE static unsigned make_odd(char& val)BOOST_NOEXCEPT{ unsigned result = gcd_traits<unsigned long>::find_lsb(val); val >>= result; return result; } }; { BOOST_FORCEINLINE static unsigned make_odd(char& val)BOOST_NOEXCEPT{ unsigned result = gcd_traits<unsigned long>::find_lsb(val); val >>= result; return result; } };
#ifndef BOOST_NO_INTRINSIC_WCHAR_T #ifndef BOOST_NO_INTRINSIC_WCHAR_T
template <> struct gcd_traits<wchar_t> : public gcd_traits_defaults<wchar_t> template <> struct gcd_traits<wchar_t> : public gcd_traits_defaults<wchar_t>
{ BOOST_FORCEINLINE static unsigned make_odd(wchar_t& val)BOOST_NOEXCEPT{ unsigned result = gcd_traits<unsigned long>::find_lsb(val); val >>= result; return result; } }; { BOOST_FORCEINLINE static unsigned make_odd(wchar_t& val)BOOST_NOEXCEPT{ unsigned result = gcd_traits<unsigned long>::find_lsb(val); val >>= result; return result; } };
#endif #endif
#ifdef _M_X64 #ifdef _M_X64
template <> struct gcd_traits<__int64> : public gcd_traits_defaults<__int64> template <> struct gcd_traits<__int64> : public gcd_traits_defaults<__int64>
{ BOOST_FORCEINLINE static unsigned make_odd(__int64& val)BOOST_NOEXCEPT{ unsigned result = gcd_traits<unsigned __int64>::find_lsb(val); val >>= result; return result; } }; { BOOST_FORCEINLINE static unsigned make_odd(__int64& val)BOOST_NOEXCEPT{ unsigned result = gcd_traits<unsigned __int64>::find_lsb(val); val >>= result; return result; } };
#endif #endif
@ -360,7 +360,7 @@ namespace boost {
} }
/** Stein gcd (aka 'binary gcd') /** Stein gcd (aka 'binary gcd')
* *
* From Mathematics to Generic Programming, Alexander Stepanov, Daniel Rose * From Mathematics to Generic Programming, Alexander Stepanov, Daniel Rose
*/ */
template <typename SteinDomain> template <typename SteinDomain>
@ -388,11 +388,11 @@ namespace boost {
return m; return m;
} }
/** Euclidean algorithm /** Euclidean algorithm
* *
* From Mathematics to Generic Programming, Alexander Stepanov, Daniel Rose * From Mathematics to Generic Programming, Alexander Stepanov, Daniel Rose
* *
*/ */
template <typename EuclideanDomain> template <typename EuclideanDomain>
inline BOOST_CXX14_CONSTEXPR EuclideanDomain Euclid_gcd(EuclideanDomain a, EuclideanDomain b) BOOST_GCD_NOEXCEPT(EuclideanDomain) inline BOOST_CXX14_CONSTEXPR EuclideanDomain Euclid_gcd(EuclideanDomain a, EuclideanDomain b) BOOST_GCD_NOEXCEPT(EuclideanDomain)
@ -494,10 +494,10 @@ inline typename boost::enable_if_c<std::numeric_limits<Integer>::is_specialized,
* *
* Knuth counts down from n to zero but we naturally go from first to last. * Knuth counts down from n to zero but we naturally go from first to last.
* We also return the termination position because it might be useful to know. * We also return the termination position because it might be useful to know.
* *
* Partly by quirk, partly by design, this algorithm is defined for n = 1, * Partly by quirk, partly by design, this algorithm is defined for n = 1,
* because the gcd of {x} is x. It is not defined for n = 0. * because the gcd of {x} is x. It is not defined for n = 0.
* *
* @tparam I Input iterator. * @tparam I Input iterator.
* @return The gcd of the range and the iterator position at termination. * @return The gcd of the range and the iterator position at termination.
*/ */
@ -507,12 +507,13 @@ gcd_range(I first, I last) BOOST_GCD_NOEXCEPT(I)
{ {
BOOST_ASSERT(first != last); BOOST_ASSERT(first != last);
typedef typename std::iterator_traits<I>::value_type T; typedef typename std::iterator_traits<I>::value_type T;
T d = *first++; T d = *first;
++first;
while (d != T(1) && first != last) while (d != T(1) && first != last)
{ {
d = gcd(d, *first); d = gcd(d, *first);
first++; ++first;
} }
return std::make_pair(d, first); return std::make_pair(d, first);
} }
@ -522,12 +523,13 @@ lcm_range(I first, I last) BOOST_GCD_NOEXCEPT(I)
{ {
BOOST_ASSERT(first != last); BOOST_ASSERT(first != last);
typedef typename std::iterator_traits<I>::value_type T; typedef typename std::iterator_traits<I>::value_type T;
T d = *first++; T d = *first;
while (d != T(1) && first != last) ++first;
while (d != T(0) && first != last)
{ {
d = lcm(d, *first); d = lcm(d, *first);
first++; ++first;
} }
return std::make_pair(d, first); return std::make_pair(d, first);
} }
@ -544,7 +546,7 @@ public:
typedef IntegerType second_argument_type; typedef IntegerType second_argument_type;
typedef IntegerType result_type; typedef IntegerType result_type;
#endif #endif
IntegerType operator()(IntegerType const &a, IntegerType const &b)const IntegerType operator()(IntegerType const &a, IntegerType const &b) const
{ {
return boost::integer::gcd(a, b); return boost::integer::gcd(a, b);
} }

View File

@ -542,6 +542,15 @@ void variadics()
BOOST_TEST_EQ(boost::integer::gcd_range(i, i + 4).second, i + 4); BOOST_TEST_EQ(boost::integer::gcd_range(i, i + 4).second, i + 4);
BOOST_TEST_EQ(boost::integer::lcm_range(i, i + 4).first, 11704); BOOST_TEST_EQ(boost::integer::lcm_range(i, i + 4).first, 11704);
BOOST_TEST_EQ(boost::integer::lcm_range(i, i + 4).second, i + 4); BOOST_TEST_EQ(boost::integer::lcm_range(i, i + 4).second, i + 4);
unsigned i_gcd_unity[] = { 44, 56, 1, 88 };
BOOST_TEST_EQ(boost::integer::gcd_range(i_gcd_unity, i_gcd_unity + 4).first, 1);
BOOST_TEST_EQ(boost::integer::gcd_range(i_gcd_unity, i_gcd_unity + 4).second, i_gcd_unity + 3);
unsigned i_lcm_unity[] = { 44, 56, 0, 88 };
BOOST_TEST_EQ(boost::integer::lcm_range(i_lcm_unity, i_lcm_unity + 4).first, 0);
BOOST_TEST_EQ(boost::integer::lcm_range(i_lcm_unity, i_lcm_unity + 4).second, i_lcm_unity + 3);
#ifndef BOOST_NO_CXX11_VARIADIC_TEMPLATES #ifndef BOOST_NO_CXX11_VARIADIC_TEMPLATES
BOOST_TEST_EQ(boost::integer::gcd(i[0], i[1], i[2], i[3]), 4); BOOST_TEST_EQ(boost::integer::gcd(i[0], i[1], i[2], i[3]), 4);
BOOST_TEST_EQ(boost::integer::lcm(i[0], i[1], i[2], i[3]), 11704); BOOST_TEST_EQ(boost::integer::lcm(i[0], i[1], i[2], i[3]), 11704);