From 5f810b275473215c15919a67b23dbb3fed39eacc Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Thu, 29 Nov 2001 21:42:58 +0000 Subject: [PATCH] Daryle Walker's GCD library submission [SVN r11814] --- include/boost/math/common_factor.hpp | 464 +++++++++++++++++++++++++++ test/common_factor_test.cpp | 115 +++++++ 2 files changed, 579 insertions(+) create mode 100644 include/boost/math/common_factor.hpp create mode 100644 test/common_factor_test.cpp diff --git a/include/boost/math/common_factor.hpp b/include/boost/math/common_factor.hpp new file mode 100644 index 0000000..4be4315 --- /dev/null +++ b/include/boost/math/common_factor.hpp @@ -0,0 +1,464 @@ +// Boost common_factor.hpp header file -------------------------------------// + +// (C) Copyright Daryle Walker, Stephen Cleary, Paul Moore 2001. Permission +// to copy, use, modify, sell and distribute this software is granted provided +// this copyright notice appears in all copies. This software is provided "as +// is" without express or implied warranty, and with no claim as to its +// suitability for any purpose. + +// See http://www.boost.org for updates, documentation, and revision history. + +#ifndef BOOST_MATH_COMMON_FACTOR_HPP +#define BOOST_MATH_COMMON_FACTOR_HPP + +#include // self include + +#include // for BOOST_STATIC_CONSTANT, etc. +#include // for std::numeric_limits + + +namespace boost +{ +namespace math +{ + + +// Forward declarations for function templates -----------------------------// + +template < typename IntegerType > + IntegerType gcd( IntegerType const &a, IntegerType const &b ); + +template < typename IntegerType > + IntegerType lcm( IntegerType const &a, IntegerType const &b ); + + +// Greatest common divisor evaluator class declaration ---------------------// + +template < typename IntegerType > +class gcd_evaluator +{ +public: + // Types + typedef IntegerType result_type, first_argument_type, second_argument_type; + + // Function object interface + result_type operator ()( first_argument_type const &a, + second_argument_type const &b ) const; + +}; // boost::math::gcd_evaluator + + +// Least common multiple evaluator class declaration -----------------------// + +template < typename IntegerType > +class lcm_evaluator +{ +public: + // Types + typedef IntegerType result_type, first_argument_type, second_argument_type; + + // Function object interface + result_type operator ()( first_argument_type const &a, + second_argument_type const &b ) const; + +}; // boost::math::lcm_evaluator + + +// Implementation details --------------------------------------------------// + +namespace detail +{ +#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION + // Build GCD with Euclid's recursive algorithm + template < unsigned long Value1, unsigned long Value2 > + struct static_gcd_helper_t + { + private: + BOOST_STATIC_CONSTANT( unsigned long, new_value1 = Value2 ); + BOOST_STATIC_CONSTANT( unsigned long, new_value2 = Value1 % Value2 ); + + #ifndef __BORLANDC__ + #define BOOST_DETAIL_GCD_HELPER_VAL(Value) Value + #else + typedef static_gcd_helper_t self_type; + #define BOOST_DETAIL_GCD_HELPER_VAL(Value) (self_type:: Value ) + #endif + + typedef static_gcd_helper_t< BOOST_DETAIL_GCD_HELPER_VAL(new_value1), + BOOST_DETAIL_GCD_HELPER_VAL(new_value2) > next_step_type; + + #undef BOOST_DETAIL_GCD_HELPER_VAL + + public: + BOOST_STATIC_CONSTANT( unsigned long, value = next_step_type::value ); + }; + + // Non-recursive case + template < unsigned long Value1 > + struct static_gcd_helper_t< Value1, 0UL > + { + BOOST_STATIC_CONSTANT( unsigned long, value = Value1 ); + }; +#else + // Use inner class template workaround from Peter Dimov + template < unsigned long Value1 > + struct static_gcd_helper2_t + { + template < unsigned long Value2 > + struct helper + { + BOOST_STATIC_CONSTANT( unsigned long, value + = static_gcd_helper2_t::helper::value ); + }; + + template < > + struct helper< 0UL > + { + BOOST_STATIC_CONSTANT( unsigned long, value = Value1 ); + }; + }; + + // Special case + template < > + struct static_gcd_helper2_t< 0UL > + { + template < unsigned long Value2 > + struct helper + { + BOOST_STATIC_CONSTANT( unsigned long, value = Value2 ); + }; + }; + + // Build the GCD from the above template(s) + template < unsigned long Value1, unsigned long Value2 > + struct static_gcd_helper_t + { + BOOST_STATIC_CONSTANT( unsigned long, value + = static_gcd_helper2_t::helper::value ); + }; +#endif + +#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION + // Build the LCM from the GCD + template < unsigned long Value1, unsigned long Value2 > + struct static_lcm_helper_t + { + typedef static_gcd_helper_t gcd_type; + + BOOST_STATIC_CONSTANT( unsigned long, value = Value1 / gcd_type::value + * Value2 ); + }; + + // Special case for zero-GCD values + template < > + struct static_lcm_helper_t< 0UL, 0UL > + { + BOOST_STATIC_CONSTANT( unsigned long, value = 0UL ); + }; +#else + // Adapt GCD's inner class template workaround for LCM + template < unsigned long Value1 > + struct static_lcm_helper2_t + { + template < unsigned long Value2 > + struct helper + { + typedef static_gcd_helper_t gcd_type; + + BOOST_STATIC_CONSTANT( unsigned long, value = Value1 + / gcd_type::value * Value2 ); + }; + + template < > + struct helper< 0UL > + { + BOOST_STATIC_CONSTANT( unsigned long, value = 0UL ); + }; + }; + + // Special case + template < > + struct static_lcm_helper2_t< 0UL > + { + template < unsigned long Value2 > + struct helper + { + BOOST_STATIC_CONSTANT( unsigned long, value = 0UL ); + }; + }; + + // Build the LCM from the above template(s) + template < unsigned long Value1, unsigned long Value2 > + struct static_lcm_helper_t + { + BOOST_STATIC_CONSTANT( unsigned long, value + = static_lcm_helper2_t::helper::value ); + }; +#endif + + // Greatest common divisor for rings (including unsigned integers) + template < typename RingType > + RingType + gcd_euclidean + ( + RingType a, + RingType b + ) + { + // Avoid repeated construction + #ifndef __BORLANDC__ + RingType const zero = static_cast( 0 ); + #else + RingType zero = static_cast( 0 ); + #endif + + // Reduce by GCD-remainder property [GCD(a,b) == GCD(b,a MOD b)] + while ( true ) + { + if ( a == zero ) + return b; + b %= a; + + if ( b == zero ) + return a; + a %= b; + } + } + + // Greatest common divisor for (signed) integers + template < typename IntegerType > + inline + IntegerType + gcd_integer + ( + IntegerType const & a, + IntegerType const & b + ) + { + // Avoid repeated construction + IntegerType const zero = static_cast( 0 ); + IntegerType const result = gcd_euclidean( a, b ); + + return ( result < zero ) ? -result : result; + } + + // Least common multiple for rings (including unsigned integers) + template < typename RingType > + inline + RingType + lcm_euclidean + ( + RingType const & a, + RingType const & b + ) + { + RingType const zero = static_cast( 0 ); + RingType const temp = gcd_euclidean( a, b ); + + return ( temp != zero ) ? ( a / temp * b ) : zero; + } + + // Least common multiple for (signed) integers + template < typename IntegerType > + inline + IntegerType + lcm_integer + ( + IntegerType const & a, + IntegerType const & b + ) + { + // Avoid repeated construction + IntegerType const zero = static_cast( 0 ); + IntegerType const result = lcm_euclidean( a, b ); + + return ( result < zero ) ? -result : result; + } + + // Function objects to find the best way of computing GCD or LCM + template < typename T > + struct gcd_optimal_evaluator + { + template < bool IsSpecialized, bool IsSigned > + struct helper + { + T operator ()( T const &a, T const &b ) + { + return gcd_euclidean( a, b ); + } + }; + + template < > + struct helper< true, true > + { + T operator ()( T const &a, T const &b ) + { + return gcd_integer( a, b ); + } + }; + + T operator ()( T const &a, T const &b ) + { + typedef ::std::numeric_limits limits_type; + + typedef helper + helper_type; + + helper_type solver; + + return solver( a, b ); + } + }; + + template < typename T > + struct lcm_optimal_evaluator + { + template < bool IsSpecialized, bool IsSigned > + struct helper + { + T operator ()( T const &a, T const &b ) + { + return lcm_euclidean( a, b ); + } + }; + + template < > + struct helper< true, true > + { + T operator ()( T const &a, T const &b ) + { + return lcm_integer( a, b ); + } + }; + + T operator ()( T const &a, T const &b ) + { + typedef ::std::numeric_limits limits_type; + + typedef helper + helper_type; + + helper_type solver; + + return solver( a, b ); + } + }; + + // Functions to find the GCD or LCM in the best way + template < typename T > + inline + gcd_optimal + ( + T const & a, + T const & b + ) + { + gcd_optimal_evaluator solver; + + return solver( a, b ); + } + + template < typename T > + inline + lcm_optimal + ( + T const & a, + T const & b + ) + { + lcm_optimal_evaluator solver; + + return solver( a, b ); + } + +} // namespace detail + + +// Compile-time greatest common divisor evaluator class declaration --------// + +template < unsigned long Value1, unsigned long Value2 > +struct static_gcd +{ + BOOST_STATIC_CONSTANT( unsigned long, value + = (detail::static_gcd_helper_t::value) ); + +}; // boost::math::static_gcd + + +// Compile-time least common multiple evaluator class declaration ----------// + +template < unsigned long Value1, unsigned long Value2 > +struct static_lcm +{ + BOOST_STATIC_CONSTANT( unsigned long, value + = (detail::static_lcm_helper_t::value) ); + +}; // boost::math::static_lcm + + +// Greatest common divisor evaluator member function definition ------------// + +template < typename IntegerType > +inline +typename gcd_evaluator::result_type +gcd_evaluator::operator () +( + first_argument_type const & a, + second_argument_type const & b +) const +{ + return detail::gcd_optimal( a, b ); +} + + +// Least common multiple evaluator member function definition --------------// + +template < typename IntegerType > +inline +typename lcm_evaluator::result_type +lcm_evaluator::operator () +( + first_argument_type const & a, + second_argument_type const & b +) const +{ + return detail::lcm_optimal( a, b ); +} + + +// Greatest common divisor and least common multiple function definitions --// + +template < typename IntegerType > +inline +IntegerType +gcd +( + IntegerType const & a, + IntegerType const & b +) +{ + gcd_evaluator solver; + + return solver( a, b ); +} + +template < typename IntegerType > +inline +IntegerType +lcm +( + IntegerType const & a, + IntegerType const & b +) +{ + lcm_evaluator solver; + + return solver( a, b ); +} + + +} // namespace math +} // namespace boost + + +#endif // BOOST_MATH_COMMON_FACTOR_HPP diff --git a/test/common_factor_test.cpp b/test/common_factor_test.cpp new file mode 100644 index 0000000..d1c53a3 --- /dev/null +++ b/test/common_factor_test.cpp @@ -0,0 +1,115 @@ +// Boost GCD & LCM common_factor.hpp test program --------------------------// + +// (C) Copyright Daryle Walker 2001. Permission to copy, use, modify, sell +// and distribute this software is granted provided this copyright +// notice appears in all copies. This software is provided "as is" without +// express or implied warranty, and with no claim as to its suitability for +// any purpose. + +// See http://www.boost.org for most recent version including documentation. + +// Revision History +// 07 Nov 2001 Initial version (Daryle Walker) + +#define BOOST_INCLUDE_MAIN + +#include // for BOOST_MSVC +#include // for boost::exit_success +#include // for boost::math::gcd, etc. +#include // for main, BOOST_TEST + +#include // for std::cout (std::endl indirectly) + + +// Control to determine what kind of built-in integers are used +#ifndef CONTROL_INT_TYPE +#define CONTROL_INT_TYPE int +#endif + + +// Main testing function +int +test_main +( + int , // "argc" is unused + char * [] // "argv" is unused +) +{ + using std::cout; + using std::endl; + +#ifndef BOOST_MSVC + using boost::math::gcd; + using boost::math::static_gcd; + using boost::math::lcm; + using boost::math::static_lcm; +#else + using namespace boost::math; +#endif + + typedef CONTROL_INT_TYPE int_type; + + // GCD tests + cout << "Doing tests on gcd." << endl; + + BOOST_TEST( gcd( 1, -1) == 1 ); + BOOST_TEST( gcd( -1, 1) == 1 ); + BOOST_TEST( gcd( 1, 1) == 1 ); + BOOST_TEST( gcd( -1, -1) == 1 ); + BOOST_TEST( gcd( 0, 0) == 0 ); + BOOST_TEST( gcd( 7, 0) == 7 ); + BOOST_TEST( gcd( 0, 9) == 9 ); + BOOST_TEST( gcd( -7, 0) == 7 ); + BOOST_TEST( gcd( 0, -9) == 9 ); + BOOST_TEST( gcd( 42, 30) == 6 ); + BOOST_TEST( gcd( 6, -9) == 3 ); + BOOST_TEST( gcd(-10, -10) == 10 ); + BOOST_TEST( gcd(-25, -10) == 5 ); + BOOST_TEST( gcd( 3, 7) == 1 ); + BOOST_TEST( gcd( 8, 9) == 1 ); + BOOST_TEST( gcd( 7, 49) == 7 ); + + cout << "Doing tests on static_gcd." << endl; + + BOOST_TEST( (static_gcd< 1, 1>::value) == 1 ); + BOOST_TEST( (static_gcd< 0, 0>::value) == 0 ); + BOOST_TEST( (static_gcd< 7, 0>::value) == 7 ); + BOOST_TEST( (static_gcd< 0, 9>::value) == 9 ); + BOOST_TEST( (static_gcd<42, 30>::value) == 6 ); + BOOST_TEST( (static_gcd< 3, 7>::value) == 1 ); + BOOST_TEST( (static_gcd< 8, 9>::value) == 1 ); + BOOST_TEST( (static_gcd< 7, 49>::value) == 7 ); + + // LCM tests + cout << "Doing tests on lcm." << endl; + + BOOST_TEST( lcm( 1, -1) == 1 ); + BOOST_TEST( lcm( -1, 1) == 1 ); + BOOST_TEST( lcm( 1, 1) == 1 ); + BOOST_TEST( lcm( -1, -1) == 1 ); + BOOST_TEST( lcm( 0, 0) == 0 ); + BOOST_TEST( lcm( 6, 0) == 0 ); + BOOST_TEST( lcm( 0, 7) == 0 ); + BOOST_TEST( lcm( -5, 0) == 0 ); + BOOST_TEST( lcm( 0, -4) == 0 ); + BOOST_TEST( lcm( 18, 30) == 90 ); + BOOST_TEST( lcm( -6, 9) == 18 ); + BOOST_TEST( lcm(-10, -10) == 10 ); + BOOST_TEST( lcm( 25, -10) == 50 ); + BOOST_TEST( lcm( 3, 7) == 21 ); + BOOST_TEST( lcm( 8, 9) == 72 ); + BOOST_TEST( lcm( 7, 49) == 49 ); + + cout << "Doing tests on static_lcm." << endl; + + BOOST_TEST( (static_lcm< 1, 1>::value) == 1 ); + BOOST_TEST( (static_lcm< 0, 0>::value) == 0 ); + BOOST_TEST( (static_lcm< 6, 0>::value) == 0 ); + BOOST_TEST( (static_lcm< 0, 7>::value) == 0 ); + BOOST_TEST( (static_lcm<18, 30>::value) == 90 ); + BOOST_TEST( (static_lcm< 3, 7>::value) == 21 ); + BOOST_TEST( (static_lcm< 8, 9>::value) == 72 ); + BOOST_TEST( (static_lcm< 7, 49>::value) == 49 ); + + return boost::exit_success; +}