polynomial_gcd.hpp 7.3 KB

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