mirror of https://github.com/CGAL/cgal
1140 lines
36 KiB
C++
1140 lines
36 KiB
C++
// TODO: Add licence
|
|
//
|
|
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
|
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
|
//
|
|
// $URL$
|
|
// $Id$
|
|
//
|
|
//
|
|
// Author(s) : Arno Eigenwillig <arno@mpi-inf.mpg.de>
|
|
// Tobias Reithmann <treith@mpi-inf.mpg.de>
|
|
// Michael Hemmer <hemmer@informatik.uni-mainz.de>
|
|
//
|
|
// ============================================================================
|
|
|
|
/*! \file NiX/polynomial_gcd.h
|
|
* \brief Greatest common divisors and related operations on polynomials.
|
|
*/
|
|
|
|
#include <CGAL/basic.h>
|
|
#include <CGAL/Polynomial.h>
|
|
#include <CGAL/Algebraic_structure_traits.h>
|
|
#include <CGAL/Fraction_traits.h>
|
|
|
|
#ifndef CGAL_POLYNOMIAL_GCD_H
|
|
#define CGAL_POLYNOMIAL_GCD_H
|
|
|
|
#ifndef CGAL_POLYNOMIAL_GCD_AVOID_CANONICALIZE
|
|
#define CGAL_POLYNOMIAL_GCD_AVOID_CANONICALIZE 1
|
|
#endif
|
|
|
|
|
|
|
|
CGAL_BEGIN_NAMESPACE
|
|
|
|
// TODO: This is a dummy-version of may_have_multiple_root. The original
|
|
// EXACUS function is defined in polynomial_utils.h, but does currently
|
|
// not work because of the missing modular traits (and other stuff).
|
|
template <class NT> inline
|
|
bool may_have_multiple_root(const Polynomial<NT>& P){
|
|
return true;
|
|
}
|
|
|
|
|
|
// 1) gcd (basic form without cofactors)
|
|
// uses three level of dispatch on tag types:
|
|
// a) if the algebra type of the innermost coefficient is a field,
|
|
// ask for decomposability. for UFDs compute the gcd directly
|
|
// b) if NT supports integralization, the gcd is computed on
|
|
// integralized polynomials
|
|
// c) over a field (unless integralized), use the Euclidean algorithm;
|
|
// over a UFD, use the subresultant algorithm
|
|
|
|
namespace INTERN_POLYNOMIAL_GCD {
|
|
|
|
template <class NT> inline
|
|
Polynomial<NT> gcd_(
|
|
const Polynomial<NT>& p1, const Polynomial<NT>& p2
|
|
) {
|
|
typedef typename Polynomial_traits_d<NT>::Innermost_coefficient Innermost_coefficient;
|
|
typedef typename Algebraic_structure_traits<Innermost_coefficient>::Algebraic_category Algebraic_category;
|
|
|
|
// dispatch 1a) innermost coefficients algebra type is field or UFD
|
|
return gcd_innermost_coefficient_dispatch(p1, p2, Algebraic_category());
|
|
}
|
|
|
|
template <class NT> inline
|
|
Polynomial<NT> gcd_innermost_coefficient_dispatch(
|
|
const Polynomial<NT>& p1, const Polynomial<NT>& p2, Field_tag
|
|
) {
|
|
typedef typename Fraction_traits< Polynomial<NT> >
|
|
::Is_fraction Is_fraction;
|
|
|
|
// dispatch 1b) NT is decomposable or not
|
|
return gcd_(p1, p2, Is_fraction());
|
|
}
|
|
|
|
template <class NT> inline
|
|
Polynomial<NT> gcd_innermost_coefficient_dispatch(
|
|
const Polynomial<NT>& p1, const Polynomial<NT>& p2, Unique_factorization_domain_tag tag
|
|
) {
|
|
return gcd_(p1, p2, tag);
|
|
}
|
|
|
|
template <class NT> inline
|
|
Polynomial<NT> gcd_(
|
|
const Polynomial<NT>& p1, const Polynomial<NT>& p2, ::CGAL::Tag_false
|
|
) {
|
|
typedef typename Algebraic_structure_traits<NT>::Algebraic_category Algebraic_category;
|
|
|
|
// dispatch 1c) choose the right algorithm for a field or a UFD
|
|
return gcd_(p1, p2, Algebraic_category());
|
|
}
|
|
|
|
template <class NT> inline
|
|
Polynomial<NT> gcd_(
|
|
const Polynomial<NT>& p1, const Polynomial<NT>& p2, ::CGAL::Tag_true tag
|
|
) {
|
|
return gcd_utcf_(p1, p2, tag);
|
|
}
|
|
|
|
#ifndef NiX_POLY_USE_PRIMITIVE_GCD
|
|
template <class NT>
|
|
Polynomial<NT> gcd_(
|
|
Polynomial<NT> p1, Polynomial<NT> p2, Unique_factorization_domain_tag
|
|
) {
|
|
// implemented using the subresultant algorithm for gcd computation
|
|
// see [Cohen, 1993], algorithm 3.3.1
|
|
|
|
// handle trivial cases
|
|
if (p1.is_zero())
|
|
if (p2.is_zero()) return Polynomial<NT>(NT(1));
|
|
else return p2 / p2.unit_part();
|
|
if (p2.is_zero())
|
|
return p1 / p1.unit_part();
|
|
if (p2.degree() > p1.degree()) {
|
|
Polynomial<NT> p3 = p1; p1 = p2; p2 = p3;
|
|
}
|
|
|
|
// compute gcd of content
|
|
NT p1c = p1.content(), p2c = p2.content();
|
|
NT gcdcont = gcd(p1c,p2c);
|
|
|
|
// compute gcd of primitive parts
|
|
p1 /= p1c; p2 /= p2c;
|
|
|
|
NT dummy;
|
|
Polynomial<NT> q, r;
|
|
|
|
NT g = NT(1), h = NT(1);
|
|
for (;;) {
|
|
Polynomial<NT>::pseudo_division(p1, p2, q, r, dummy);
|
|
if (r.is_zero()) { break; }
|
|
if (r.degree() == 0) { return Polynomial<NT>(gcdcont); }
|
|
int delta = p1.degree() - p2.degree();
|
|
p1 = p2;
|
|
p2 = r / (g * ipower(h, delta));
|
|
g = p1.lcoeff();
|
|
// h = h^(1-delta) * g^delta
|
|
INTERN_POLYNOMIAL::hgdelta_update(h, g, delta);
|
|
}
|
|
|
|
|
|
p2 /= p2.content() * p2.unit_part();
|
|
|
|
// combine both parts to proper gcd
|
|
p2 *= gcdcont;
|
|
p2.simplify_coefficients();
|
|
return p2;
|
|
}
|
|
|
|
#else
|
|
|
|
template <class NT>
|
|
Polynomial<NT> gcd_(
|
|
Polynomial<NT> p1, Polynomial<NT> p2, UFDomain_tag
|
|
) {
|
|
// implemented by computing the primitive remainder sequence
|
|
|
|
// handle trivial cases
|
|
if (p1.is_zero())
|
|
if (p2.is_zero()) return Polynomial<NT>(NT(1));
|
|
else return p2 / p2.unit_part();
|
|
if (p2.is_zero())
|
|
return p1 / p1.unit_part();
|
|
if (p2.degree() > p1.degree()) {
|
|
Polynomial<NT> p3 = p1; p1 = p2; p2 = p3;
|
|
}
|
|
|
|
NT p1c = p1.content(), p2c = p2.content();
|
|
p1 /= p1c; p2 /= p2c;
|
|
NT F = gcd(p1c,p2c);
|
|
Polynomial<NT> q,r;
|
|
NT D;
|
|
while ( ! p2.is_zero() ) {
|
|
Polynomial<NT>::pseudo_division(p1,p2,q,r,D);
|
|
r /= r.content();
|
|
p1=p2; p2=r;
|
|
}
|
|
p1 /= p1.unit_part();
|
|
return F * p1;
|
|
}
|
|
|
|
#endif // NiX_POLY_USE_PRIMITIVE_GCD
|
|
|
|
template <class NT>
|
|
Polynomial<NT> gcd_(
|
|
Polynomial<NT> p1, Polynomial<NT> p2, Field_tag
|
|
) {
|
|
// handle trivial cases
|
|
if (p1.is_zero())
|
|
if (p2.is_zero()) return Polynomial<NT>(NT(1));
|
|
else return p2 / p2.unit_part();
|
|
if (p2.is_zero())
|
|
return p1 / p1.unit_part();
|
|
if (p2.degree() > p1.degree()) {
|
|
Polynomial<NT> p3 = p1; p1 = p2; p2 = p3;
|
|
}
|
|
|
|
Polynomial<NT> q, r;
|
|
while (!p2.is_zero()) {
|
|
Polynomial<NT>::euclidean_division(p1, p2, q, r);
|
|
p1 = p2; p2 = r;
|
|
}
|
|
p1 /= p1.lcoeff();
|
|
p1.simplify_coefficients();
|
|
return p1;
|
|
}
|
|
|
|
} // namespace INTERN_POLYNOMIAL_GCD
|
|
|
|
// name gcd() forwarded to the Intern::gcd_() dispatch function
|
|
/*! \ingroup NiX_Polynomial
|
|
* \relates NiX::Polynomial
|
|
* \brief return the greatest common divisor of \c p1 and \c p2
|
|
*
|
|
* \pre Requires \c NT to be a \c Field or a \c UFDomain.
|
|
*
|
|
* Internally, computation is performed ``denominator-free'' if
|
|
* supported by the coefficient type via \c NiX::Fraction_traits.
|
|
* The gcd is computed from a polynomial remainder sequence (euclidean
|
|
* or subresultant.) By defining \c NiX_POLY_USE_PRIMITIVE_GCD,
|
|
* the subresultant PRS can be replaced globally by the primitive PRS.
|
|
*
|
|
* Mathematically, a gcd in NT[x] is defined only up to multiplication
|
|
* with a unit (invertible element) of the coefficient ring NT. This
|
|
* function returns a unit-normal gcd <I>d</I>, i.e.
|
|
* <I>d</I><TT>.unit_part() == NT(1)</TT>.
|
|
* If \c NT is a \c Field , all non-zero scalars are units and
|
|
* unit-normality means that \e d is normalized to have
|
|
* <I>d</I><TT>.lcoeff() == NT(1)</TT>.
|
|
* If \c NT is a \c UFDomain, then non-invertible scalar factors
|
|
* <B>do</B> matter, and unit-normality typically means something
|
|
* like <I>d</I><TT>.lcoeff()</TT> being positive.
|
|
*/
|
|
namespace INTERN_POLYNOMIAL_GCD {
|
|
template <class NT> inline
|
|
Polynomial<NT> gcd(const Polynomial<NT>& p1, const Polynomial<NT>& p2)
|
|
{ return INTERN_POLYNOMIAL_GCD::gcd_(p1,p2); }
|
|
} // namespace INTERN_POLYNOMIAL_GCD
|
|
|
|
// 2) gcd_utcf computation
|
|
// (gcd up to scalar factors, for non-UFD non-field coefficients)
|
|
// a) first try to decompose the coefficients
|
|
// b) second dispatch depends on the algebra type of NT
|
|
|
|
namespace INTERN_POLYNOMIAL_GCD {
|
|
|
|
template <class NT> inline
|
|
NT gcd_utcf_(const NT& a, const NT& b)
|
|
{ return NT(1); }
|
|
|
|
template <class NT> inline
|
|
Polynomial<NT> gcd_utcf_(
|
|
const Polynomial<NT>& p1, const Polynomial<NT>& p2
|
|
) {
|
|
typedef typename Fraction_traits< Polynomial<NT> >
|
|
::Is_fraction Is_fraction;
|
|
|
|
// dispatch 2a) NT is decomposable or not
|
|
return gcd_utcf_(p1, p2, Is_fraction());
|
|
}
|
|
|
|
template <class NT> inline
|
|
Polynomial<NT> gcd_utcf_(
|
|
const Polynomial<NT>& p1, const Polynomial<NT>& p2, ::CGAL::Tag_false
|
|
) {
|
|
typedef typename Algebraic_structure_traits<NT>::Algebraic_category Algebraic_category;
|
|
|
|
// dispatch 2b) choose the right algorithm for field and ring
|
|
return gcd_utcf_(p1, p2, Algebraic_category());
|
|
}
|
|
|
|
template <class NT>
|
|
Polynomial<NT> gcd_utcf_(
|
|
Polynomial<NT> p1, Polynomial<NT> p2, ::CGAL::Tag_true
|
|
) {
|
|
typedef Polynomial<NT> POLY;
|
|
typedef typename Fraction_traits<POLY>::Numerator_type INTPOLY;
|
|
typedef typename Fraction_traits<POLY>::Denominator_type DENOM;
|
|
typedef typename INTPOLY::NT INTNT;
|
|
|
|
DENOM dummy;
|
|
p1.simplify_coefficients();
|
|
p2.simplify_coefficients();
|
|
INTPOLY p1i = integralize_polynomial(p1, dummy);
|
|
INTPOLY p2i = integralize_polynomial(p2, dummy);
|
|
|
|
typedef typename Algebraic_structure_traits<INTNT>::Algebraic_category Algebraic_category;
|
|
INTPOLY d0 = gcd_utcf_(p1i, p2i, Algebraic_category());
|
|
POLY d = fractionalize_polynomial<POLY>(d0, DENOM(1));
|
|
d /= d.unit_part();
|
|
d.simplify_coefficients();
|
|
return d;
|
|
}
|
|
|
|
template <class NT> inline
|
|
Polynomial<NT> gcd_utcf_(
|
|
const Polynomial<NT>& p1, const Polynomial<NT>& p2, Field_tag tag
|
|
) {
|
|
return gcd_(p1, p2, tag);
|
|
}
|
|
|
|
template <class NT>
|
|
NT content_utcf_(const Polynomial<NT>& p)
|
|
{
|
|
typename Algebraic_structure_traits<NT>::Integral_division idiv;
|
|
typename Algebraic_structure_traits<NT>::Unit_part upart;
|
|
typedef typename Polynomial<NT>::const_iterator const_iterator;
|
|
|
|
const_iterator it = p.begin(), ite = p.end();
|
|
while (*it == NT(0)) it++;
|
|
NT cont = idiv(*it, upart(*it));
|
|
for( ; it != ite; it++) {
|
|
if (cont == NT(1)) break;
|
|
if (*it != NT(0)) cont = gcd_utcf_(cont, *it);
|
|
}
|
|
return cont;
|
|
}
|
|
|
|
template <class NT>
|
|
Polynomial<NT> gcd_utcf_(
|
|
Polynomial<NT> p1, Polynomial<NT> p2, Integral_domain_tag
|
|
) {
|
|
|
|
// handle trivial cases
|
|
if (p1.is_zero())
|
|
if (p2.is_zero()){
|
|
return Polynomial<NT>(NT(1));
|
|
}else{
|
|
#if NiX_POLYNOMIAL_GCD_AVOID_CANONICALIZE
|
|
return p2;
|
|
#else
|
|
return canonicalize_polynomial(p2);
|
|
#endif
|
|
}
|
|
if (p2.is_zero()){
|
|
#if NiX_POLYNOMIAL_GCD_AVOID_CANONICALIZE
|
|
return p1;
|
|
#else
|
|
return canonicalize_polynomial(p1);
|
|
#endif
|
|
}
|
|
if (p2.degree() > p1.degree()) {
|
|
Polynomial<NT> p3 = p1; p1 = p2; p2 = p3;
|
|
}
|
|
|
|
// remove redundant scalar factors
|
|
p1=canonicalize_polynomial(p1);
|
|
p2=canonicalize_polynomial(p2);
|
|
|
|
// compute content of p1 and p2
|
|
NT p1c = content_utcf_(p1);
|
|
NT p2c = content_utcf_(p2);
|
|
|
|
// compute gcd of content
|
|
NT gcdcont = gcd_utcf_(p1c, p2c);
|
|
|
|
// compute gcd of primitive parts
|
|
p1 = div_utcf(p1, p1c, true);
|
|
p2 = div_utcf(p2, p2c, true);
|
|
|
|
|
|
Polynomial<NT> q, r;
|
|
|
|
// TODO measure preformance of both methodes with respect to
|
|
// univariat polynomials on Integeres
|
|
// univariat polynomials on Sqrt_extension<Integer,Integer>
|
|
// multivariat polynomials
|
|
// May write specializations for different cases
|
|
#if 0
|
|
// implemented using the subresultant algorithm for gcd computation
|
|
// with respect to constant scalar factors
|
|
// see [Cohen, 1993], algorithm 3.3.1
|
|
NT g = NT(1), h = NT(1), dummy;
|
|
for (;;) {
|
|
Polynomial<NT>::pseudo_division(p1, p2, q, r, dummy);
|
|
if (r.is_zero()) { break; }
|
|
if (r.degree() == 0) { return Polynomial<NT>(gcdcont); }
|
|
int delta = p1.degree() - p2.degree();
|
|
p1 = p2;
|
|
p2 = r / (g * ipower(h, delta));
|
|
g = p1.lcoeff();
|
|
// h = h^(1-delta) * g^delta
|
|
Intern::hgdelta_update(h, g, delta);
|
|
}
|
|
#else
|
|
// implentaion using just the 'naive' methode
|
|
// but performed much better as the one by Cohen
|
|
// (for univariat polynomials with Sqrt_extension coeffs )
|
|
NT dummy;
|
|
for (;;) {
|
|
Polynomial<NT>::pseudo_division(p1, p2, q, r, dummy);
|
|
if (r.is_zero()) { break; }
|
|
if (r.degree() == 0) { return Polynomial<NT>(gcdcont); }
|
|
p1 = p2;
|
|
p2 = r ;
|
|
p2=canonicalize_polynomial(p2);
|
|
}
|
|
#endif
|
|
|
|
p2 = div_utcf(p2, content_utcf_(p2), true);
|
|
|
|
// combine both parts to proper gcd
|
|
p2 *= gcdcont;
|
|
|
|
#if NiX_POLYNOMIAL_GCD_AVOID_CANONICALIZE
|
|
return p2;
|
|
#else
|
|
// make poly unique
|
|
return canonicalize_polynomial(p2);
|
|
#endif
|
|
|
|
}
|
|
|
|
} // namespace INTERN_POLYNOMIAL_GCD
|
|
|
|
// name gcd_utcf() forwarded to the Intern::gcd_utcf_() dispatch function
|
|
/*! \relates NiX::Polynomial
|
|
* \brief return a constant multiple of gcd(p1,p2)
|
|
*
|
|
* Over a non-UFD or non-field NT, the polynomial ring NT[x]
|
|
* may not possess greatest common divisors. However, concept
|
|
* \c IntegralDomain requires NT to be an integral domain, such that we
|
|
* can consider its quotient field Q(NT) over which gcds
|
|
* of polynomials exist. \c gcd_utcf(p1,p2) is a constant
|
|
* denominator-free multiple of gcd(p1,p2) in Q(NT)[x].
|
|
* It may not be a divisor of \c p1 and \c p2 in NT[x].
|
|
*
|
|
* The result is unit-normal.
|
|
*/
|
|
template <class NT> inline
|
|
Polynomial<NT> gcd_utcf(const Polynomial<NT>& p1, const Polynomial<NT>& p2)
|
|
{ return INTERN_POLYNOMIAL_GCD::gcd_utcf_(p1,p2); }
|
|
|
|
|
|
// 3) extended gcd computation (with cofactors)
|
|
// with dispatch similar to gcd
|
|
|
|
namespace INTERN_POLYNOMIAL_GCD {
|
|
|
|
template <class NT> inline
|
|
Polynomial<NT> gcdex_(
|
|
Polynomial<NT> x, Polynomial<NT> y,
|
|
Polynomial<NT>& xf, Polynomial<NT>& yf,
|
|
::CGAL::Tag_false
|
|
) {
|
|
typedef typename Algebraic_structure_traits<NT>::Algebraic_category Algebraic_category;
|
|
return gcdex_(x, y, xf, yf, Algebraic_category());
|
|
};
|
|
|
|
template <class NT>
|
|
Polynomial<NT> gcdex_(
|
|
Polynomial<NT> x, Polynomial<NT> y,
|
|
Polynomial<NT>& xf, Polynomial<NT>& yf,
|
|
Field_tag
|
|
) {
|
|
/* The extended Euclidean algorithm for univariate polynomials.
|
|
* See [Cohen, 1993], algorithm 3.2.2
|
|
*/
|
|
typedef Polynomial<NT> POLY;
|
|
typename Algebraic_structure_traits<NT>::Integral_division idiv;
|
|
|
|
// handle trivial cases
|
|
if (x.is_zero()) {
|
|
if (y.is_zero()) CGAL_error("gcdex(0,0) is undefined");
|
|
xf = NT(0); yf = idiv(NT(1), y.unit_part());
|
|
return yf * y;
|
|
}
|
|
if (y.is_zero()) {
|
|
yf = NT(0); xf = idiv(NT(1), x.unit_part());
|
|
return xf * x;
|
|
}
|
|
bool swapped = x.degree() < y.degree();
|
|
if (swapped) { POLY t = x; x = y; y = t; }
|
|
|
|
// main loop
|
|
POLY u = x, v = y, q, r, m11(1), m21(0), m21old;
|
|
for (;;) {
|
|
/* invariant: (i) There exist m12 and m22 such that
|
|
* u = m11*x + m12*y
|
|
* v = m21*x + m22*y
|
|
* (ii) and we have
|
|
* gcd(u,v) == gcd(x,y)
|
|
*/
|
|
|
|
// compute next element of remainder sequence
|
|
POLY::euclidean_division(u, v, q, r); // u == qv + r
|
|
if (r.is_zero()) break;
|
|
|
|
// update u and v while preserving invariant
|
|
u = v; v = r;
|
|
/* Since r = u - qv, this preserves invariant (part ii)
|
|
* and corresponds to the matrix assignment
|
|
* (u) = (0 1) (u)
|
|
* (v) (1 -q) (v)
|
|
*/
|
|
m21old = m21; m21 = m11 - q*m21; m11 = m21old;
|
|
/* This simulates the matching matrix assignment
|
|
* (m11 m12) = (0 1) (m11 m12)
|
|
* (m21 m22) (1 -q) (m21 m22)
|
|
* which preserves the invariant (part i)
|
|
*/
|
|
if (r.degree() == 0) break;
|
|
}
|
|
/* postcondition: invariant holds and v divides u */
|
|
|
|
// make gcd unit-normal
|
|
m21 /= v.unit_part(); v /= v.unit_part();
|
|
|
|
// obtain m22 such that v == m21*x + m22*y
|
|
POLY m22;
|
|
POLY::euclidean_division(v - m21*x, y, m22, r);
|
|
CGAL_assertion(r.is_zero());
|
|
|
|
// check computation
|
|
CGAL_assertion(v == m21*x + m22*y);
|
|
|
|
// return results
|
|
if (swapped) {
|
|
xf = m22; yf = m21;
|
|
} else {
|
|
xf = m21; yf = m22;
|
|
}
|
|
return v;
|
|
}
|
|
|
|
template <class NT> inline
|
|
Polynomial<NT> gcdex_(
|
|
Polynomial<NT> x, Polynomial<NT> y,
|
|
Polynomial<NT>& xf, Polynomial<NT>& yf,
|
|
::CGAL::Tag_true
|
|
) {
|
|
typedef Polynomial<NT> POLY;
|
|
typedef typename Fraction_traits<POLY>::Numerator_type INTPOLY;
|
|
typedef typename Fraction_traits<POLY>::Denominator_type DENOM;
|
|
typedef typename INTPOLY::NT INTNT;
|
|
|
|
// rewrite x as xi/xd and y as yi/yd with integral polynomials xi, yi
|
|
DENOM xd, yd;
|
|
x.simplify_coefficients();
|
|
y.simplify_coefficients();
|
|
INTPOLY xi = integralize_polynomial(x, xd);
|
|
INTPOLY yi = integralize_polynomial(y, yd);
|
|
|
|
// compute the integral gcd with cofactors:
|
|
// vi = gcd(xi, yi); vfi*vi == xfi*xi + yfi*yi
|
|
INTPOLY xfi, yfi; INTNT vfi;
|
|
INTPOLY vi = pseudo_gcdex(xi, yi, xfi, yfi, vfi);
|
|
|
|
// proceed to vfi*v == xfi*x + yfi*y with v = gcd(x,y) (unit-normal)
|
|
POLY v = fractionalize_polynomial<POLY>(vi, vi.lcoeff());
|
|
v.simplify_coefficients();
|
|
CGAL_assertion(v.unit_part() == NT(1));
|
|
vfi *= vi.lcoeff(); xfi *= xd; yfi *= yd;
|
|
|
|
// compute xf, yf such that gcd(x,y) == v == xf*x + yf*y
|
|
xf = fractionalize_polynomial<POLY>(xfi, vfi);
|
|
yf = fractionalize_polynomial<POLY>(yfi, vfi);
|
|
xf.simplify_coefficients();
|
|
yf.simplify_coefficients();
|
|
return v;
|
|
};
|
|
|
|
} // namespace INTERN_POLYNOMIAL_GCD
|
|
|
|
/*! \ingroup NiX_Polynomial
|
|
* \relates NiX::Polynomial
|
|
* \brief compute gcd with cofactors
|
|
*
|
|
* This function computes the gcd of polynomials \c p1 and \c p2
|
|
* along with two other polynomials \c f1 and \c f2 such that
|
|
* gcd(\e p1, \e p2) = <I>f1*p1 + f2*p2</I>. This is called
|
|
* <I>extended</I> gcd computation, and <I>f1, f2</I> are called
|
|
* <I>Bézout factors</I> or <I>cofactors</I>.
|
|
*
|
|
* Internally, computation is performed ``denominator-free'' if
|
|
* supported by the coefficient type via \c NiX::Fraction_traits
|
|
* (using \c pseudo_gcdex() ), otherwise the euclidean remainder
|
|
* sequence is used.
|
|
*
|
|
* \pre \c NT must be a \c Field.
|
|
*
|
|
* The result <I>d</I> is unit-normal,
|
|
* i.e. <I>d</I><TT>.lcoeff() == NT(1)</TT>.
|
|
*
|
|
*/
|
|
template <class NT> inline
|
|
Polynomial<NT> gcdex(
|
|
Polynomial<NT> p1, Polynomial<NT> p2,
|
|
Polynomial<NT>& f1, Polynomial<NT>& f2
|
|
) {
|
|
typedef typename Fraction_traits< Polynomial<NT> >
|
|
::Is_fraction Is_fraction;
|
|
return INTERN_POLYNOMIAL_GCD::gcdex_(p1, p2, f1, f2, Is_fraction());
|
|
};
|
|
|
|
|
|
/*! \ingroup NiX_Polynomial
|
|
* \relates NiX::Polynomial
|
|
* \brief compute gcd with ``almost'' cofactors
|
|
*
|
|
* This is a variant of \c exgcd() for use over non-field \c NT.
|
|
* It computes the gcd of polynomials \c p1 and \c p2
|
|
* along with two other polynomials \c f1 and \c f2 and a scalar \c v
|
|
* such that \e v * gcd(\e p1, \e p2) = <I>f1*p1 + f2*p2</I>,
|
|
* using the subresultant remainder sequence. That \c NT is not a field
|
|
* implies that one cannot achieve \e v = 1 for all inputs.
|
|
*
|
|
* \pre \c NT must be a \c UFDomain of scalars (not polynomials).
|
|
*
|
|
* The result is unit-normal.
|
|
*
|
|
*/
|
|
template <class NT>
|
|
Polynomial<NT> pseudo_gcdex(
|
|
#ifdef DOXYGEN_RUNNING
|
|
Polynomial<NT> p1, Polynomial<NT> p2,
|
|
Polynomial<NT>& f2, Polynomial<NT>& f2, NT& v
|
|
#else
|
|
Polynomial<NT> x, Polynomial<NT> y,
|
|
Polynomial<NT>& xf, Polynomial<NT>& yf, NT& vf
|
|
#endif // DOXYGEN_RUNNING
|
|
) {
|
|
/* implemented using the extended subresultant algorithm
|
|
* for gcd computation with Bezout factors
|
|
*
|
|
* To understand this, you need to understand the computation of
|
|
* cofactors as in the basic extended Euclidean algorithm (see
|
|
* the code above of gcdex_(..., Field_tag)), and the subresultant
|
|
* gcd algorithm, see gcd_(..., UFDomain_tag).
|
|
*
|
|
* The crucial point of the combination of both is the observation
|
|
* that the subresultant factor (called rho here) divided out of the
|
|
* new remainder in each step can also be divided out of the
|
|
* cofactors.
|
|
*/
|
|
|
|
typedef Polynomial<NT> POLY;
|
|
typename Algebraic_structure_traits<NT>::Integral_division idiv;
|
|
typename Algebraic_structure_traits<NT>::Gcd gcd;
|
|
|
|
// handle trivial cases
|
|
if (x.is_zero()) {
|
|
if (y.is_zero()) CGAL_error("gcdex(0,0) is undefined");
|
|
xf = NT(0); yf = NT(1); vf = y.unit_part();
|
|
return y / vf;
|
|
}
|
|
if (y.is_zero()) {
|
|
xf = NT(1); yf = NT(0); vf = x.unit_part();
|
|
return x / vf;
|
|
}
|
|
bool swapped = x.degree() < y.degree();
|
|
if (swapped) { POLY t = x; x = y; y = t; }
|
|
|
|
// compute gcd of content
|
|
NT xcont = x.content(); NT ycont = y.content();
|
|
NT gcdcont = gcd(xcont, ycont);
|
|
|
|
// compute gcd of primitive parts
|
|
POLY xprim = x / xcont; POLY yprim = y / ycont;
|
|
POLY u = xprim, v = yprim, q, r;
|
|
POLY m11(1), m21(0), m21old;
|
|
NT g(1), h(1), d, rho;
|
|
for (;;) {
|
|
int delta = u.degree() - v.degree();
|
|
POLY::pseudo_division(u, v, q, r, d);
|
|
CGAL_assertion(d == ipower(v.lcoeff(), delta+1));
|
|
if (r.is_zero()) break;
|
|
rho = g * ipower(h, delta);
|
|
u = v; v = r / rho;
|
|
m21old = m21; m21 = (d*m11 - q*m21) / rho; m11 = m21old;
|
|
/* The transition from (u, v) to (v, r/rho) corresponds
|
|
* to multiplication with the matrix
|
|
* __1__ (0 rho)
|
|
* rho (d -q)
|
|
* The comments and correctness arguments from
|
|
* gcdex(..., Field_tag) apply analogously.
|
|
*/
|
|
g = u.lcoeff();
|
|
INTERN_POLYNOMIAL::hgdelta_update(h, g, delta);
|
|
if (r.degree() == 0) break;
|
|
}
|
|
|
|
// obtain v == m21*xprim + m22*yprim
|
|
// the correct m21 was already computed above
|
|
POLY m22;
|
|
POLY::euclidean_division(v - m21*xprim, yprim, m22, r);
|
|
CGAL_assertion(r.is_zero());
|
|
|
|
// now obtain gcd(x,y) == gcdcont * v/v.content() == (m21*x + m22*y)/denom
|
|
NT vcont = v.content(), vup = v.unit_part();
|
|
v /= vup * vcont; v *= gcdcont;
|
|
m21 *= ycont; m22 *= xcont;
|
|
vf = idiv(xcont, gcdcont) * ycont * (vup * vcont);
|
|
CGAL_assertion(vf * v == m21*x + m22*y);
|
|
|
|
// return results
|
|
if (swapped) {
|
|
xf = m22; yf = m21;
|
|
} else {
|
|
xf = m21; yf = m22;
|
|
}
|
|
return v;
|
|
}
|
|
|
|
|
|
// 5) square-free factorization
|
|
|
|
namespace INTERN_POLYNOMIAL_GCD {
|
|
|
|
// constant factor
|
|
//
|
|
// used by square-free factorization
|
|
// returns the constant term of a polynomial depending on its coefficient's algebra type
|
|
|
|
template <class NT>
|
|
inline
|
|
NT constant_factor( Polynomial<NT> p){
|
|
typedef typename Algebraic_structure_traits<NT>::Algebraic_category Algebraic_category;
|
|
return constant_factor(p,Algebraic_category());
|
|
}
|
|
template <class NT>
|
|
inline
|
|
NT constant_factor( Polynomial<NT> p, Field_tag){
|
|
return p.lcoeff();
|
|
}
|
|
template <class NT>
|
|
inline
|
|
NT constant_factor( Polynomial<NT> p, Unique_factorization_domain_tag){
|
|
return p.content()*p.unit_part();
|
|
}
|
|
template <class NT>
|
|
inline
|
|
NT constant_factor( Polynomial<NT> p, Integral_domain_tag){
|
|
return p.unit_part();
|
|
}
|
|
|
|
// square-free factorization
|
|
//
|
|
// the implementation uses two dispatches:
|
|
// a) first look at the coefficient's algebra type
|
|
// b) if the algebra type is of the concept field, try to decompose
|
|
// the same holds for square-free factorization up to constant factors (utcf)
|
|
//
|
|
// sqff -------> algebra type ? ----field-----> decomposable ?
|
|
// | A | |
|
|
// UFD | no yes
|
|
// | | | |
|
|
// V | |
|
|
// Yun's algo <---------------------- |
|
|
// A |
|
|
// | | |
|
|
// UFD | V
|
|
// | | decompose and use
|
|
// sqff_utcf --> algebra_type ? ----field-- sqff_utcf with numerator
|
|
// |
|
|
// integral domain
|
|
// |
|
|
// V
|
|
// Yun's algo (utcf)
|
|
|
|
template <class NT, class OutputIterator1, class OutputIterator2> inline
|
|
int square_free_factorization_(Polynomial<NT> a, OutputIterator1 factors,
|
|
OutputIterator2 multiplicities, Field_tag
|
|
) {
|
|
typedef typename Fraction_traits< Polynomial<NT> >::Is_fraction
|
|
Is_fraction;
|
|
|
|
return square_free_factorization_(a, factors, multiplicities, Is_fraction());
|
|
}
|
|
|
|
template <class NT, class OutputIterator1, class OutputIterator2> inline
|
|
int square_free_factorization_(Polynomial<NT> a, OutputIterator1 factors,
|
|
OutputIterator2 multiplicities, Unique_factorization_domain_tag
|
|
) {
|
|
return square_free_factorization_(a, factors, multiplicities);
|
|
}
|
|
|
|
template <class NT, class OutputIterator1, class OutputIterator2> inline
|
|
int square_free_factorization_(Polynomial<NT> a, OutputIterator1 factors,
|
|
OutputIterator2 multiplicities, ::CGAL::Tag_false
|
|
) {
|
|
return square_free_factorization_(a, factors, multiplicities);
|
|
}
|
|
|
|
template <class NT, class OutputIterator1, class OutputIterator2> inline
|
|
int square_free_factorization_(Polynomial<NT> a, OutputIterator1 factors,
|
|
OutputIterator2 multiplicities, ::CGAL::Tag_true
|
|
) {
|
|
typedef Polynomial<NT> POLY;
|
|
typedef typename Fraction_traits<POLY>::Numerator_type INTPOLY;
|
|
typedef typename Fraction_traits<POLY>::Denominator_type DENOM;
|
|
typedef typename INTPOLY::NT INTNT;
|
|
typedef std::vector<INTPOLY> PVEC;
|
|
typedef typename PVEC::iterator Iterator;
|
|
typedef typename Algebraic_structure_traits<INTNT>::Algebraic_category Algebraic_category;
|
|
|
|
DENOM dummy;
|
|
PVEC fac;
|
|
std::back_insert_iterator<PVEC> fac_bi(fac);
|
|
|
|
a.simplify_coefficients();
|
|
INTPOLY p = integralize_polynomial(a, dummy);
|
|
int d = square_free_factorization_utcf_(p, fac_bi, multiplicities, Algebraic_category());
|
|
for (Iterator it = fac.begin(); it != fac.end(); ++it) {
|
|
POLY q = fractionalize_polynomial<POLY>(*it, DENOM(1));
|
|
q /= q.lcoeff();
|
|
q.simplify_coefficients();
|
|
*factors++ = q;
|
|
}
|
|
return d;
|
|
}
|
|
|
|
template <class NT, class OutputIterator1, class OutputIterator2>
|
|
int square_free_factorization_(Polynomial<NT> a, OutputIterator1 factors,
|
|
OutputIterator2 multiplicities
|
|
) {
|
|
// Yun's Square-Free Factorization
|
|
// see [Geddes et al, 1992], Algorithm 8.2
|
|
|
|
/*
|
|
@inproceedings{y-osfda-76,
|
|
author = {David Y.Y. Yun},
|
|
title = {On square-free decomposition algorithms},
|
|
booktitle = {SYMSAC '76: Proceedings of the third ACM symposium on Symbolic
|
|
and algebraic computation},
|
|
year = {1976},
|
|
pages = {26--35},
|
|
location = {Yorktown Heights, New York, United States},
|
|
doi = {http://doi.acm.org/10.1145/800205.806320},
|
|
publisher = {ACM Press},
|
|
address = {New York, NY, USA},
|
|
}
|
|
*/
|
|
|
|
typedef Polynomial<NT> POLY;
|
|
if (a.degree() == 0) return 0;
|
|
POLY b = diff(a);
|
|
POLY c = gcd(a, b);
|
|
|
|
if (c == NT(1)) {
|
|
*factors = a;
|
|
*multiplicities = 1;
|
|
return 1;
|
|
}
|
|
|
|
int i = 1, n = 0;
|
|
POLY w = a/c, y = b/c, z = y - diff(w), g;
|
|
while (!z.is_zero()) {
|
|
g = gcd(w, z);
|
|
if (g.degree() > 0) {
|
|
*factors++ = g;
|
|
*multiplicities++ = i;
|
|
n++;
|
|
}
|
|
i++;
|
|
w /= g;
|
|
y = z/g;
|
|
z = y - diff(w);
|
|
}
|
|
*factors = w;
|
|
*multiplicities++ = i;
|
|
n++;
|
|
|
|
return n;
|
|
}
|
|
|
|
} // namespace INTERN_POLYNOMIAL_GCD
|
|
|
|
/*! \ingroup NiX_Polynomial
|
|
* \relates NiX::Polynomial
|
|
* \brief factor the univariate polynomial \c p by multiplicities.
|
|
*
|
|
* That means: factor it into square-free and pairwise coprime non-constant
|
|
* factors <I>g<SUB>i</SUB></I> with multiplicities <I>m<SUB>i</SUB></I>
|
|
* such that <I>p = alpha * g<SUB>1</SUB><SUP>m<SUB>1</SUB></SUP> *
|
|
* ... * g<SUB>n</SUB><SUP>m<SUB>n</SUB></SUP> </I>.
|
|
* This is known as square-free factorization in the literature.
|
|
* The number \e n is returned. The factors \e gi and multiplicities
|
|
* \e mi are written through the respective output iterators.
|
|
*
|
|
* \pre The coefficient domain \c NT must be a \c field or \c UFDomain
|
|
* of characteristic 0.
|
|
* \c OutputIterator1 must allow the value type \c Polynomial<NT>.
|
|
* \c OutputIterator2 must allow the value type \c int.
|
|
*
|
|
* Use this function if you are sure, that the polynomial has multiple
|
|
* factors, otherwise use NiX::filtered_square_free_factorization.
|
|
*/
|
|
template <class NT, class OutputIterator1, class OutputIterator2> inline
|
|
int square_free_factorization(
|
|
Polynomial<NT> p,
|
|
OutputIterator1 factors,
|
|
OutputIterator2 multiplicities,
|
|
NT& alpha)
|
|
{
|
|
typedef Algebraic_structure_traits<NT> AST;
|
|
typedef typename AST::Algebraic_category Algebraic_category;
|
|
|
|
alpha = INTERN_POLYNOMIAL_GCD::constant_factor(p);
|
|
return INTERN_POLYNOMIAL_GCD::square_free_factorization_
|
|
(p/alpha, factors, multiplicities, Algebraic_category());
|
|
}
|
|
|
|
template <class NT, class OutputIterator1, class OutputIterator2> inline
|
|
int square_free_factorization(
|
|
Polynomial<NT> p,
|
|
OutputIterator1 factors,
|
|
OutputIterator2 multiplicities)
|
|
{
|
|
typedef typename Algebraic_structure_traits<NT>::Algebric_structure_tag Algebraic_category;
|
|
|
|
NT alpha = INTERN_POLYNOMIAL_GCD::constant_factor(p);
|
|
return INTERN_POLYNOMIAL_GCD::square_free_factorization_(p/alpha, factors, multiplicities,
|
|
Algebraic_category());
|
|
}
|
|
|
|
|
|
namespace INTERN_POLYNOMIAL_GCD {
|
|
|
|
template <class NT, class OutputIterator1, class OutputIterator2> inline
|
|
int square_free_factorization_utcf_(
|
|
Polynomial<NT> a,
|
|
OutputIterator1 factors,
|
|
OutputIterator2 multiplicities,
|
|
Field_tag)
|
|
{
|
|
typedef typename Fraction_traits< Polynomial<NT> >::Is_fraction
|
|
Is_fraction;
|
|
|
|
return square_free_factorization_(a, factors, multiplicities, Is_fraction());
|
|
}
|
|
|
|
template <class NT, class OutputIterator1, class OutputIterator2> inline
|
|
int square_free_factorization_utcf_(
|
|
Polynomial<NT> a,
|
|
OutputIterator1 factors,
|
|
OutputIterator2 multiplicities,
|
|
Unique_factorization_domain_tag)
|
|
{
|
|
return square_free_factorization_(a, factors, multiplicities);
|
|
}
|
|
|
|
template <class NT, class OutputIterator1, class OutputIterator2>
|
|
int square_free_factorization_utcf_(
|
|
Polynomial<NT> a,
|
|
OutputIterator1 factors,
|
|
OutputIterator2 multiplicities,
|
|
Integral_domain_tag)
|
|
{
|
|
// Yun's Square-Free Factorization
|
|
// see [Geddes et al, 1992], Algorithm 8.2
|
|
|
|
typedef Polynomial<NT> POLY;
|
|
typedef typename Polynomial_traits_d<POLY>::Innermost_coefficient IC;
|
|
typename Polynomial_traits_d<POLY>::Innermost_leading_coefficient ilcoeff;
|
|
//typename Polynomial_traits_d<POLY>::Innermost_coefficient_to_polynomial ictp;
|
|
typename Polynomial_traits_d<POLY>::Innermost_coefficient_begin begin;
|
|
typename Polynomial_traits_d<POLY>::Innermost_coefficient_end end;
|
|
typename Algebraic_extension_traits<IC>::Denominator_for_algebraic_integers dfai;
|
|
typename Algebraic_extension_traits<IC>::Normalization_factor nfac;
|
|
typename Scalar_factor_traits<POLY>::Scalar_factor sfac;
|
|
typename Scalar_factor_traits<POLY>::Scalar_div sdiv;
|
|
typedef typename Scalar_factor_traits<POLY>::Scalar Scalar;
|
|
|
|
if (a.degree() == 0) return 0;
|
|
|
|
a = canonicalize_polynomial(a);
|
|
POLY b = diff(a);
|
|
POLY c = gcd_utcf(a, b);
|
|
|
|
if (c == NT(1)) {
|
|
*factors = a;
|
|
*multiplicities = 1;
|
|
return 1;
|
|
}
|
|
|
|
int i = 1, n = 0;
|
|
|
|
// extending both polynomials a and b by the denominator for algebraic
|
|
// integers, which comes out from c=gcd(a,b), such that a and b are
|
|
// divisible by c
|
|
IC lcoeff = ilcoeff(c);
|
|
IC denom = dfai(begin(c), end(c));
|
|
lcoeff *= denom * nfac(denom);
|
|
POLY w = (a * POLY(lcoeff)) / c;
|
|
POLY y = (b * POLY(lcoeff)) / c;
|
|
|
|
// extracting a common scalar factor out of w=a/c and y=b/c simultaneously,
|
|
// such that the parts in z=y-w' are canceled out as they should
|
|
Scalar sfactor = sfac(y,sfac(w));
|
|
sdiv(w, sfactor);
|
|
sdiv(y, sfactor);
|
|
|
|
POLY z = y - diff(w);
|
|
POLY g;
|
|
|
|
while (!z.is_zero()) {
|
|
g = gcd_utcf(w, z);
|
|
if (g.degree() > 0) {
|
|
*factors++ = g;
|
|
*multiplicities++ = i;
|
|
n++;
|
|
}
|
|
i++;
|
|
lcoeff = ilcoeff(g); // same as above
|
|
denom =dfai(begin(c), end(c));
|
|
lcoeff *= denom * nfac(denom);
|
|
w = (w * POLY(lcoeff)) / g;
|
|
y = (z * POLY(lcoeff)) / g;
|
|
Scalar sfactor = sfac(y,sfac(w));
|
|
sdiv(w, sfactor);
|
|
sdiv(y, sfactor);
|
|
|
|
z = y - diff(w);
|
|
}
|
|
*factors = w;
|
|
*multiplicities++ = i;
|
|
n++;
|
|
|
|
return n;
|
|
}
|
|
|
|
} // namespace INTERN_POLYNOMIAL_GCD
|
|
|
|
/*! \ingroup NiX_Polynomial
|
|
* \relates NiX::Polynomial
|
|
* \brief factor the univariate polynomial \c p by multiplicities with respect
|
|
* to constant factors
|
|
*
|
|
* Same functionality as square_free_factorization, but
|
|
* a) no prefactor \c alpha is returned due to the respect to constant factors,
|
|
* b) the coefficient domain \c NT may also be \c IntegralDomain now.
|
|
*
|
|
*/
|
|
template <class NT, class OutputIterator1, class OutputIterator2> inline
|
|
int square_free_factorization_utcf(
|
|
const Polynomial<NT>& p,
|
|
OutputIterator1 factors,
|
|
OutputIterator2 multiplicities)
|
|
{
|
|
typedef typename Algebraic_structure_traits<NT>::Algebraic_category Algebraic_category;
|
|
|
|
NT alpha = INTERN_POLYNOMIAL_GCD::constant_factor(p);
|
|
return INTERN_POLYNOMIAL_GCD::square_free_factorization_utcf_
|
|
(p/alpha, factors, multiplicities, Algebraic_category());
|
|
}
|
|
|
|
// for Arno's convenience
|
|
template <class NT, class OutputIterator1, class OutputIterator2> inline
|
|
int pseudo_square_free_factorization(
|
|
const Polynomial<NT>& p,
|
|
OutputIterator1 factors,
|
|
OutputIterator2 multiplicities)
|
|
{
|
|
return square_free_factorization_utcf(p, factors, multiplicities);
|
|
}
|
|
|
|
// ### filtered versions ####
|
|
|
|
/*! \brief As NiX::square_free_factorization, but filtered by
|
|
* NiX::may_have_multiple_root
|
|
*
|
|
* Use this function if the polynomial might be square free.
|
|
*/
|
|
template <class NT, class OutputIterator1, class OutputIterator2>
|
|
inline
|
|
int filtered_square_free_factorization(
|
|
Polynomial<NT> p,
|
|
OutputIterator1 factors,
|
|
OutputIterator2 multiplicities)
|
|
{
|
|
if(may_have_multiple_root(p)){
|
|
return square_free_factorization(p, factors, multiplicities);
|
|
}else{
|
|
#if NiX_POLYNOMIAL_GCD_AVOID_CANONICALIZE
|
|
*factors++ = p;
|
|
#else
|
|
*factors++ = canonicalize_polynomial(p);
|
|
#endif
|
|
*multiplicities++ = 1;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/*! \brief As NiX::square_free_factorization, but filtered by
|
|
* NiX::may_have_multiple_root
|
|
*
|
|
* Use this function if the polynomial might be square free.
|
|
*/
|
|
template <class NT, class OutputIterator1, class OutputIterator2>
|
|
inline
|
|
int filtered_square_free_factorization(Polynomial<NT> p,
|
|
OutputIterator1 factors,
|
|
OutputIterator2 multiplicities,
|
|
NT& alpha)
|
|
{
|
|
if(may_have_multiple_root(p)){
|
|
return square_free_factorization(p, factors, multiplicities, alpha);
|
|
}else{
|
|
alpha = INTERN_POLYNOMIAL_GCD::constant_factor(p);
|
|
p/=alpha;
|
|
*factors++ = p;
|
|
*multiplicities++ = 1;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/*! \brief As NiX::square_free_factorization_utcf, but filtered by
|
|
* NiX::may_have_multiple_root
|
|
*
|
|
* Use this function if the polynomial might be square free.
|
|
*/
|
|
template <class NT, class OutputIterator1, class OutputIterator2>
|
|
inline
|
|
int filtered_square_free_factorization_utcf( const Polynomial<NT>& p,
|
|
OutputIterator1 factors,
|
|
OutputIterator2 multiplicities)
|
|
{
|
|
if(may_have_multiple_root(p)){
|
|
return square_free_factorization_utcf(p,factors,multiplicities);
|
|
}else{
|
|
#if NiX_POLYNOMIAL_GCD_AVOID_CANONICALIZE
|
|
*factors++ = p;
|
|
#else
|
|
*factors++ = canonicalize_polynomial(p);
|
|
#endif
|
|
*multiplicities++ = 1;
|
|
return 1;
|
|
|
|
}
|
|
}
|
|
|
|
CGAL_END_NAMESPACE
|
|
|
|
#endif // CGAL_POLYNOMIAL_GCD_H
|
|
|
|
// EOF
|