polynomial_gcd.hpp 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. // (C) Copyright Jeremy William Murphy 2016.
  2. // Use, modification and distribution are subject to the
  3. // Boost Software License, Version 1.0. (See accompanying file
  4. // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  5. #ifndef BOOST_MATH_TOOLS_POLYNOMIAL_GCD_HPP
  6. #define BOOST_MATH_TOOLS_POLYNOMIAL_GCD_HPP
  7. #ifdef _MSC_VER
  8. #pragma once
  9. #endif
  10. #include <boost/math/tools/polynomial.hpp>
  11. #include <boost/integer/common_factor_rt.hpp>
  12. #include <boost/type_traits/is_pod.hpp>
  13. namespace boost{
  14. namespace integer {
  15. namespace gcd_detail {
  16. template <class T>
  17. struct gcd_traits;
  18. template <class T>
  19. struct gcd_traits<boost::math::tools::polynomial<T> >
  20. {
  21. inline static const boost::math::tools::polynomial<T>& abs(const boost::math::tools::polynomial<T>& val) { return val; }
  22. static const method_type method = method_euclid;
  23. };
  24. }
  25. }
  26. namespace math{ namespace tools{
  27. /* From Knuth, 4.6.1:
  28. *
  29. * We may write any nonzero polynomial u(x) from R[x] where R is a UFD as
  30. *
  31. * u(x) = cont(u) . pp(u(x))
  32. *
  33. * where cont(u), the content of u, is an element of S, and pp(u(x)), the primitive
  34. * part of u(x), is a primitive polynomial over S.
  35. * When u(x) = 0, it is convenient to define cont(u) = pp(u(x)) = O.
  36. */
  37. template <class T>
  38. T content(polynomial<T> const &x)
  39. {
  40. return x ? boost::integer::gcd_range(x.data().begin(), x.data().end()).first : T(0);
  41. }
  42. // Knuth, 4.6.1
  43. template <class T>
  44. polynomial<T> primitive_part(polynomial<T> const &x, T const &cont)
  45. {
  46. return x ? x / cont : polynomial<T>();
  47. }
  48. template <class T>
  49. polynomial<T> primitive_part(polynomial<T> const &x)
  50. {
  51. return primitive_part(x, content(x));
  52. }
  53. // Trivial but useful convenience function referred to simply as l() in Knuth.
  54. template <class T>
  55. T leading_coefficient(polynomial<T> const &x)
  56. {
  57. return x ? x.data().back() : T(0);
  58. }
  59. namespace detail
  60. {
  61. /* Reduce u and v to their primitive parts and return the gcd of their
  62. * contents. Used in a couple of gcd algorithms.
  63. */
  64. template <class T>
  65. T reduce_to_primitive(polynomial<T> &u, polynomial<T> &v)
  66. {
  67. using boost::integer::gcd;
  68. T const u_cont = content(u), v_cont = content(v);
  69. u /= u_cont;
  70. v /= v_cont;
  71. return gcd(u_cont, v_cont);
  72. }
  73. }
  74. /**
  75. * Knuth, The Art of Computer Programming: Volume 2, Third edition, 1998
  76. * Algorithm 4.6.1C: Greatest common divisor over a unique factorization domain.
  77. *
  78. * The subresultant algorithm by George E. Collins [JACM 14 (1967), 128-142],
  79. * later improved by W. S. Brown and J. F. Traub [JACM 18 (1971), 505-514].
  80. *
  81. * Although step C3 keeps the coefficients to a "reasonable" size, they are
  82. * still potentially several binary orders of magnitude larger than the inputs.
  83. * Thus, this algorithm should only be used where T is a multi-precision type.
  84. *
  85. * @tparam T Polynomial coefficient type.
  86. * @param u First polynomial.
  87. * @param v Second polynomial.
  88. * @return Greatest common divisor of polynomials u and v.
  89. */
  90. template <class T>
  91. typename enable_if_c< std::numeric_limits<T>::is_integer, polynomial<T> >::type
  92. subresultant_gcd(polynomial<T> u, polynomial<T> v)
  93. {
  94. using std::swap;
  95. BOOST_ASSERT(u || v);
  96. if (!u)
  97. return v;
  98. if (!v)
  99. return u;
  100. typedef typename polynomial<T>::size_type N;
  101. if (u.degree() < v.degree())
  102. swap(u, v);
  103. T const d = detail::reduce_to_primitive(u, v);
  104. T g = 1, h = 1;
  105. polynomial<T> r;
  106. while (true)
  107. {
  108. BOOST_ASSERT(u.degree() >= v.degree());
  109. // Pseudo-division.
  110. r = u % v;
  111. if (!r)
  112. return d * primitive_part(v); // Attach the content.
  113. if (r.degree() == 0)
  114. return d * polynomial<T>(T(1)); // The content is the result.
  115. N const delta = u.degree() - v.degree();
  116. // Adjust remainder.
  117. u = v;
  118. v = r / (g * detail::integer_power(h, delta));
  119. g = leading_coefficient(u);
  120. T const tmp = detail::integer_power(g, delta);
  121. if (delta <= N(1))
  122. h = tmp * detail::integer_power(h, N(1) - delta);
  123. else
  124. h = tmp / detail::integer_power(h, delta - N(1));
  125. }
  126. }
  127. /**
  128. * @brief GCD for polynomials with unbounded multi-precision integral coefficients.
  129. *
  130. * The multi-precision constraint is enforced via numeric_limits.
  131. *
  132. * Note that intermediate terms in the evaluation can grow arbitrarily large, hence the need for
  133. * unbounded integers, otherwise numeric loverflow would break the algorithm.
  134. *
  135. * @tparam T A multi-precision integral type.
  136. */
  137. template <typename T>
  138. typename enable_if_c<std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_bounded, polynomial<T> >::type
  139. gcd(polynomial<T> const &u, polynomial<T> const &v)
  140. {
  141. return subresultant_gcd(u, v);
  142. }
  143. // GCD over bounded integers is not currently allowed:
  144. template <typename T>
  145. typename enable_if_c<std::numeric_limits<T>::is_integer && std::numeric_limits<T>::is_bounded, polynomial<T> >::type
  146. gcd(polynomial<T> const &u, polynomial<T> const &v)
  147. {
  148. BOOST_STATIC_ASSERT_MSG(sizeof(v) == 0, "GCD on polynomials of bounded integers is disallowed due to the excessive growth in the size of intermediate terms.");
  149. return subresultant_gcd(u, v);
  150. }
  151. // GCD over polynomials of floats can go via the Euclid algorithm:
  152. template <typename T>
  153. typename enable_if_c<!std::numeric_limits<T>::is_integer && (std::numeric_limits<T>::min_exponent != std::numeric_limits<T>::max_exponent) && !std::numeric_limits<T>::is_exact, polynomial<T> >::type
  154. gcd(polynomial<T> const &u, polynomial<T> const &v)
  155. {
  156. return boost::integer::gcd_detail::Euclid_gcd(u, v);
  157. }
  158. }
  159. //
  160. // Using declaration so we overload the default implementation in this namespace:
  161. //
  162. using boost::math::tools::gcd;
  163. }
  164. namespace integer
  165. {
  166. //
  167. // Using declaration so we overload the default implementation in this namespace:
  168. //
  169. using boost::math::tools::gcd;
  170. }
  171. } // namespace boost::math::tools
  172. #endif