%--------------------------------------------------------------------------- %KILLSTART DISS REP %LDEL TRACE.*?\)\; %LDEL \/\/-+ %LDEL \/\/ SPECIALIZE.* %LDEL \/\*CGAL_KERNEL_INLINE\*\/ %LDEL \/\*CGAL_KERNEL_MEDIUM_INLINE\*\/ %LDEL \#ifndef CGAL_SIMPLE_NEF_INTERFACE.* %LDEL \#endif //CGAL_SIMPLE_NEF_INTERFACE.* %LDEL \/\/ SPECIALIZE_IMPLEMENTATION.* %LDEL \/\/ KILL.* \documentclass[a4paper]{article} \usepackage{MyLweb,version} \excludeversion{ignoreindiss} \excludeversion{ignore} \includeversion{onlyindiss} \input{defs} \begin{document} \title{Polynomials} \author{Michael Seel} \maketitle %KILLEND REP \section{The Manual Page}\label{Polynomial} \input manpages/Polynomial.man \section{Introduction} %KILLEND DISS We present the implementation of a simple polynomial type |Polynomial| in one variable. The interface is specified in the manual page on page \pageref{Polynomial}. Let |NT| be either a field number type or an Euclidean ring number type\footnote{An Euclidean ring type is a ring that additionally offers division with remainder and as such is a unique factorization domain. }. We use |NT[x]| to represent the polynomial ring in one variable. For a polynomial $p = \sum_{i=0}^{d} a_i x^i \in |NT[x]|$ we store its coefficients along its rising exponents $|coeff[i]| = a_i$ in a vector of size $d+1$. We keep the invariant that $a_d \neq 0$ and do not allow modifying access to the coefficients via the interface. Flexibility in the creation of polynomials is achieved via iterator ranges which can specify a sequence of coefficients of a polynomial. We offer basic arithmetic operations like |+,-,*|, as well as destructive self modifying operations |+=,-=,*=|. When working destructively we need a cloning scheme to cope with the alias effects of one common representation referenced by several handles. For number types that are fields or Euclidean rings we also offer polynomial division. For the field types this can be done directly by so called Euclidean division. For the number types that are Euclidean rings we provide it via so called pseudo division. Based on that operation we also provide a gcd-operation on the ring |NT[x]|. \displayeps{Polynomial}{Some details of the handle scheme. |Polynomial_rep| stores the coefficients in an STL vector |coeff|. It is plugged into |Handle_for|. Thereby it is extended by a wrapper that carries a reference counter. |Polynomial| is derived from |Handle_for< Polynomial_rep >|. |Handle_for| provides the copy construction and assignment mechanisms.}{10cm} \subsubsection*{Implementation} Polynomials are implemented by using a smart-pointer scheme. First we implement the common representation class storing an |NT| vector. The whole smart-pointer scheme is shown in Figure \figref{Polynomial}. For the representation class we keep the invariant that the coefficient vector |coeff| is always reduced such that the highest-order entry is nonzero except when the degree is zero. In this case we allow a zero entry. By doing so the degree of the polynomial is always |coeff.size()-1|. To keep our invariant we \emph{reduce} the coefficient representation after each construction and arithmetic modification, which basically means we shrink the coefficient vector until the last entry is nonzero or until the polynomial is constant. \begin{onlyindiss} In this description we only show the most interesting facets of the implementation. The whole implementation project is presented in our implementation report. \end{onlyindiss} \begin{ignoreindiss} <>= template class Polynomial_rep { <> <> }; @ The coefficients are stored in an STL vector. Its resizing operations are usefull when dealing with polynomials of different degree. <>= typedef pNT NT; #ifndef CGAL_SIMPLE_NEF_INTERFACE typedef std::vector Vector; #else typedef CGAL::vector_MSC Vector; #endif typedef typename Vector::size_type size_type; typedef typename Vector::iterator iterator; typedef typename Vector::const_iterator const_iterator; Vector coeff; @ The interface allows direct initialization up to degree 3, and via iterator ranges for higher degree. <>= Polynomial_rep() : coeff() {} Polynomial_rep(const NT& n) : coeff(1) { coeff[0]=n; } Polynomial_rep(const NT& n, const NT& m) : coeff(2) { coeff[0]=n; coeff[1]=m; } Polynomial_rep(const NT& a, const NT& b, const NT& c) : coeff(3) { coeff[0]=a; coeff[1]=b; coeff[2]=c; } Polynomial_rep(size_type s) : coeff(s,NT(0)) {} #ifndef CGAL_SIMPLE_NEF_INTERFACE template Polynomial_rep(Forward_iterator first, Forward_iterator last SNIHACK) : coeff(first,last) {} #else template Polynomial_rep(Forward_iterator first, Forward_iterator last SNIHACK) : coeff() { while (first!=last) coeff.push_back(*first++); } #endif @ \end{ignoreindiss}% The |pop_back()| operation of the STL vector nicely supports the |reduce()| method. <>= void reduce() { while ( coeff.size()>1 && coeff.back()==NT(0) ) coeff.pop_back(); } @ \begin{ignoreindiss}% <>= friend class Polynomial; friend class Polynomial; friend class Polynomial; friend std::istream& operator >> <> (std::istream&, Polynomial&); @ Now for the general template class derived from the generic handle type wrapping the smart pointer architecture. <>= /*{\Msubst typename iterator_traits::value_type#NT <># }*/ /*{\Manpage{Polynomial}{NT}{Polynomials in one variable}{p}}*/ template class Polynomial : public Handle_for< Polynomial_rep > { /*{\Mdefinition An instance |\Mvar| of the data type |\Mname| represents a polynomial $p = a_0 + a_1 x + \ldots a_d x^d $ from the ring |NT[x]|. The data type offers standard ring operations and a sign operation which determines the sign for the limit process $x \rightarrow \infty$. |NT[x]| becomes a unique factorization domain, if the number type |NT| is either a field type (1) or a unique factorization domain (2). In both cases there's a polynomial division operation defined.}*/ <> <> <> <> <> <> <> }; @ The iterator is obtained from the vector data type. <>= /*{\Mtypes 5}*/ public: typedef pNT NT; /*{\Mtypemember the component type representing the coefficients.}*/ typedef Handle_for< Polynomial_rep > Base; typedef Polynomial_rep Rep; typedef typename Rep::Vector Vector; typedef typename Rep::size_type size_type; typedef typename Rep::iterator iterator; typedef typename Rep::const_iterator const_iterator; /*{\Mtypemember a random access iterator for read-only access to the coefficient vector.}*/ @ We provide a bunch of operations to implement the arithmetic operations. The |reduce()| ensures our invariant that the leading coefficient is nonzero. The static member |R_| is used for the evaluation of a set of polynomials at a constant value. <>= protected: void reduce() { ptr()->reduce(); } Vector& coeffs() { return ptr()->coeff; } const Vector& coeffs() const { return ptr()->coeff; } Polynomial(size_type s) : Base( Polynomial_rep(s) ) {} // creates a polynomial of degree s-1 static NT R_; // for visualization only @ Each constructor creates a representation object on the heap and initializes it according to the specification. Assignment is handled by the handle base class which just creates an additional link to the representation object. |copy_on_write()| allows us to single out a cloned representation object when we want to manipulate the value of a polynomial without alias effects. <>= /*{\Mcreation 3}*/ public: Polynomial() /*{\Mcreate introduces a variable |\Mvar| of type |\Mname| of undefined value. }*/ : Base( Polynomial_rep() ) {} Polynomial(const NT& a0) /*{\Mcreate introduces a variable |\Mvar| of type |\Mname| representing the constant polynomial $a_0$.}*/ : Base(Polynomial_rep(a0)) { reduce(); } Polynomial(const NT& a0, const NT& a1) /*{\Mcreate introduces a variable |\Mvar| of type |\Mname| representing the polynomial $a_0 + a_1 x$. }*/ : Base(Polynomial_rep(a0,a1)) { reduce(); } Polynomial(const NT& a0, const NT& a1,const NT& a2) /*{\Mcreate introduces a variable |\Mvar| of type |\Mname| representing the polynomial $a_0 + a_1 x + a_2 x^2$. }*/ : Base(Polynomial_rep(a0,a1,a2)) { reduce(); } #ifndef CGAL_SIMPLE_NEF_INTERFACE template Polynomial(Forward_iterator first, Forward_iterator last) /*{\Mcreate introduces a variable |\Mvar| of type |\Mname| representing the polynomial whose coefficients are determined by the iterator range, i.e. let $(a_0 = |*first|, a_1 = |*++first|, \ldots a_d = |*it|)$, where |++it == last| then |\Mvar| stores the polynomial $a_0 + a_1 x + \ldots a_d x^d$.}*/ : Base(Polynomial_rep(first,last)) { reduce(); } #else #define RPOL(I)\ Polynomial(I first, I last) : \ Base(Polynomial_rep(first,last SNIINST)) { reduce(); } RPOL(const NT*) // KILL int START RPOL(const int*) // KILL int END // KILL double START RPOL(const double*) // KILL double END #undef RPOL #endif // CGAL_SIMPLE_NEF_INTERFACE @ There are also specialized construtors for the builtin types |int| and |double| that we don't show. <>= // KILL double START Polynomial(double n) : Base(Polynomial_rep(NT(n))) { reduce(); } Polynomial(double n1, double n2) : Base(Polynomial_rep(NT(n1),NT(n2))) { reduce(); } // KILL double END // KILL int START Polynomial(int n) : Base(Polynomial_rep(NT(n))) { reduce(); } Polynomial(int n1, int n2) : Base(Polynomial_rep(NT(n1),NT(n2))) { reduce(); } // KILL int END Polynomial(const Polynomial& p) : Base(p) {} protected: // accessing coefficients internally: NT& coeff(unsigned int i) { CGAL_assertion(!is_shared() && i<(ptr()->coeff.size())); return ptr()->coeff[i]; } public: @ \end{ignoreindiss}% @ In this chunk we present the method interface of |Polynomial<>|. The array operators offer read-only access. For manipulation we offer a protected method |coeff(int)|, that is only for internal use. Evaluation, sign, and content are simple to realize. <>= /*{\Moperations 3 3 }*/ const_iterator begin() const { return ptr()->coeff.begin(); } /*{\Mop a random access iterator pointing to $a_0$.}*/ const_iterator end() const { return ptr()->coeff.end(); } /*{\Mop a random access iterator pointing beyond $a_d$.}*/ int degree() const { return ptr()->coeff.size()-1; } /*{\Mop the degree of the polynomial.}*/ const NT& operator[](unsigned int i) const { CGAL_assertion( i<(ptr()->coeff.size()) ); return ptr()->coeff[i]; } /*{\Marrop the coefficient $a_i$ of the polynomial.}*/ const NT& operator[](unsigned int i) { CGAL_assertion( i<(ptr()->coeff.size()) ); return ptr()->coeff[i]; } NT eval_at(const NT& r) const /*{\Mop evaluates the polynomial at |r|.}*/ { CGAL_assertion( degree()>=0 ); NT res = ptr()->coeff[0], x = r; for(int i=1; i<=degree(); ++i) { res += ptr()->coeff[i]*x; x*=r; } return res; } CGAL::Sign sign() const /*{\Mop returns the sign of the limit process for $x \rightarrow \infty$ (the sign of the leading coefficient).}*/ { const NT& leading_coeff = ptr()->coeff.back(); if (leading_coeff < NT(0)) return (CGAL::NEGATIVE); if (leading_coeff > NT(0)) return (CGAL::POSITIVE); return CGAL::ZERO; } bool is_zero() const /*{\Mop returns true iff |\Mvar| is the zero polynomial.}*/ { return degree()==0 && ptr()->coeff[0]==NT(0); } Polynomial abs() const /*{\Mop returns |-\Mvar| if |\Mvar.sign()==NEGATIVE| and |\Mvar| otherwise.}*/ { if ( sign()==CGAL::NEGATIVE ) return -*this; return *this; } #ifndef CGAL_SIMPLE_NEF_INTERFACE NT content() const /*{\Mop returns the content of |\Mvar| (the gcd of its coefficients). \precond Requires |NT| to provide a |gdc| operation.}*/ { CGAL_assertion( degree()>=0 ); return gcd_of_range(ptr()->coeff.begin(),ptr()->coeff.end()); } @ \begin{ignoreindiss} <>= #else // CGAL_SIMPLE_NEF_INTERFACE NT content() const { CGAL_assertion( degree()>=0 ); const_iterator its=ptr()->coeff.begin(),ite=ptr()->coeff.end(); NT res = *its++; for(; its!=ite; ++its) res = (*its==0 ? res : ring_or_field::gcd(res, *its)); if (res==0) res = 1; return res; } #endif static void set_R(const NT& R) { R_ = R; } <>= /*{\Mtext Additionally |\Mname| offers standard arithmetic ring opertions like |+,-,*,+=,-=,*=|. By means of the sign operation we can also offer comparison predicates as $<,>,\leq,\geq$. Where $p_1 < p_2$ holds iff $|sign|(p_1 - p_2) < 0$. This data type is fully compliant to the requirements of CGAL number types. \setopdims{3cm}{2cm}}*/ friend /*CGAL_KERNEL_MEDIUM_INLINE*/ Polynomial operator - <> (const Polynomial&); friend /*CGAL_KERNEL_MEDIUM_INLINE*/ Polynomial operator + <> (const Polynomial&, const Polynomial&); friend /*CGAL_KERNEL_MEDIUM_INLINE*/ Polynomial operator - <> (const Polynomial&, const Polynomial&); friend /*CGAL_KERNEL_MEDIUM_INLINE*/ Polynomial operator * <> (const Polynomial&, const Polynomial&); friend /*CGAL_KERNEL_MEDIUM_INLINE*/ Polynomial operator / <> (const Polynomial& p1, const Polynomial& p2); /*{\Mbinopfunc implements polynomial division of |p1| and |p2|. if |p1 = p2 * p3| then |p2| is returned. The result is undefined if |p3| does not exist in |NT[x]|. The correct division algorithm is chosen according to a traits class |ring_or_field| provided by the user. If |ring_or_field::kind == ring_with_gcd| then the division is done by \emph{pseudo division} based on a |gcd| operation of |NT|. If |ring_or_field::kind == field_with_div| then the division is done by \emph{euclidean division} based on the division operation of the field |NT|. \textbf{Note} that |NT=int| quickly leads to overflow errors when using this operation.}*/ /*{\Mtext \headerline{Static member functions}}*/ static Polynomial gcd (const Polynomial& p1, const Polynomial& p2); /*{\Mstatic returns the greatest common divisor of |p1| and |p2|. \textbf{Note} that |NT=int| quickly leads to overflow errors when using this operation. \precond Requires |NT| to be a unique factorization domain, i.e. to provide a |gdc| operation.}*/ static void pseudo_div (const Polynomial& f, const Polynomial& g, Polynomial& q, Polynomial& r, NT& D); /*{\Mstatic implements division with remainder on polynomials of the ring |NT[x]|: $D*f = g*q + r$. \precond |NT| is a unique factorization domain, i.e., there exists a |gcd| operation and an integral division operation on |NT|.}*/ static void euclidean_div (const Polynomial& f, const Polynomial& g, Polynomial& q, Polynomial& r); /*{\Mstatic implements division with remainder on polynomials of the ring |NT[x]|: $f = g*q + r$. \precond |NT| is a field, i.e., there exists a division operation on |NT|. }*/ friend /*CGAL_KERNEL_INLINE*/ double to_double <> (const Polynomial& p); @ We allow self manipulating operations of a polynomial. Note that we have to ensure that we avoid alias effects by a clone call when several front end handles point to one backend rep. <>= Polynomial& operator += (const Polynomial& p1) { copy_on_write(); int d = std::min(degree(),p1.degree()), i; for(i=0; i<=d; ++i) coeff(i) += p1[i]; while (i<=p1.degree()) ptr()->coeff.push_back(p1[i++]); reduce(); return (*this); } Polynomial& operator -= (const Polynomial& p1) { copy_on_write(); int d = std::min(degree(),p1.degree()), i; for(i=0; i<=d; ++i) coeff(i) -= p1[i]; while (i<=p1.degree()) ptr()->coeff.push_back(-p1[i++]); reduce(); return (*this); } Polynomial& operator *= (const Polynomial& p1) { (*this)=(*this)*p1; return (*this); } Polynomial& operator /= (const Polynomial& p1) { (*this)=(*this)/p1; return (*this); } @ We need the same operations with arguments of type |const NT&| to avoid ambiguity errors with the compiler. We do not show their similar definition. <>= //------------------------------------------------------------------ // SPECIALIZE_MEMBERS(int double) START Polynomial& operator += (const NT& num) { copy_on_write(); coeff(0) += (NT)num; return *this; } Polynomial& operator -= (const NT& num) { copy_on_write(); coeff(0) -= (NT)num; return *this; } Polynomial& operator *= (const NT& num) { copy_on_write(); for(int i=0; i<=degree(); ++i) coeff(i) *= (NT)num; reduce(); return *this; } Polynomial& operator /= (const NT& num) { copy_on_write(); CGAL_assertion(num!=0); for(int i=0; i<=degree(); ++i) coeff(i) /= (NT)num; reduce(); return *this; } // SPECIALIZE_MEMBERS(int double) END //------------------------------------------------------------------ @ \begin{ignore} <>= /*{\Mimplementation This data type is implemented as an item type via a smart pointer scheme. The coefficients are stored in a vector of |NT| entries. The simple arithmetic operations $+,-$ take time $O(d*T(NT))$, multiplication is quadratic in the maximal degree of the arguments times $T(NT)$, where $T(NT)$ is the time for a corresponding operation on two instances of the ring type.}*/ @ \end{ignore}% \end{ignoreindiss}% \subsubsection*{Arithmetic Ring Operations} Next we come to the implementation of basic arithmetic operations. The negation is trivial. We just iterate over the coefficient array and invert each sign. \begin{ignoreindiss} <>= template /*CGAL_KERNEL_MEDIUM_INLINE*/ Polynomial operator - (const Polynomial& p) { CGAL_assertion(p.degree()>=0); Polynomial res(p.coeffs().begin(),p.coeffs().end()); typename Polynomial::iterator it, ite=res.coeffs().end(); for(it=res.coeffs().begin(); it!=ite; ++it) *it = -*it; return res; } @ \end{ignoreindiss} Addition |p1+p2| is also easy. Just add all coefficients of the two monomials with the same degree. Note however that the polynomials themselves might have different degree, such that we have to copy all coefficients in the range $|min|(d_{p1},d_{p2})+1$ up to $|max|(d_{p1},d_{p2})$ into the result. Afterwards we have to reduce the coefficient vector. The subtraction routine is symmetric. We only have to deal with the different sign of |p2|. <>= template /*CGAL_KERNEL_MEDIUM_INLINE*/ Polynomial operator + (const Polynomial& p1, const Polynomial& p2) { typedef typename Polynomial::size_type size_type; CGAL_assertion(p1.degree()>=0 && p2.degree()>=0); bool p1d_smaller_p2d = p1.degree() < p2.degree(); int min,max,i; if (p1d_smaller_p2d) { min = p1.degree(); max = p2.degree(); } else { max = p1.degree(); min = p2.degree(); } Polynomial p( size_type(max + 1)); for (i = 0; i <= min; ++i ) p.coeff(i) = p1[i]+p2[i]; if (p1d_smaller_p2d) for (; i <= max; ++i ) p.coeff(i)=p2[i]; else /* p1d >= p2d */ for (; i <= max; ++i ) p.coeff(i)=p1[i]; p.reduce(); return p; } @ \begin{ignoreindiss} Subtraction is symmetric to the addition. <>= template /*CGAL_KERNEL_MEDIUM_INLINE*/ Polynomial operator - (const Polynomial& p1, const Polynomial& p2) { typedef typename Polynomial::size_type size_type; CGAL_assertion(p1.degree()>=0 && p2.degree()>=0); bool p1d_smaller_p2d = p1.degree() < p2.degree(); int min,max,i; if (p1d_smaller_p2d) { min = p1.degree(); max = p2.degree(); } else { max = p1.degree(); min = p2.degree(); } Polynomial p( size_type(max+1) ); for (i = 0; i <= min; ++i ) p.coeff(i)=p1[i]-p2[i]; if (p1d_smaller_p2d) for (; i <= max; ++i ) p.coeff(i)= -p2[i]; else /* p1d >= p2d */ for (; i <= max; ++i ) p.coeff(i)= p1[i]; p.reduce(); return p; } @ \end{ignoreindiss} Multiplication is also straightforward. The degree formula tells us that |p1*p2| has degree |p1.degree()+p2.degree()|. We just allocate a polynomial |p| of corresponding size initialized to zero and add the products of all monomials |p1[i]*p2[j]| for $0 \leq i \leq d_{p1}$, $0 \leq j \leq d_{p2}$ slotwise to $a_{i+j}$. <>= template /*CGAL_KERNEL_MEDIUM_INLINE*/ Polynomial operator * (const Polynomial& p1, const Polynomial& p2) { typedef typename Polynomial::size_type size_type; CGAL_assertion(p1.degree()>=0 && p2.degree()>=0); Polynomial p( size_type(p1.degree()+p2.degree()+1) ); // initialized with zeros for (int i=0; i <= p1.degree(); ++i) for (int j=0; j <= p2.degree(); ++j) p.coeff(i+j) += (p1[i]*p2[j]); p.reduce(); return p; } @ \renewcommand{\R}{\mathcal{R}} \newcommand{\K}{\mathcal{K}} \newcommand{\Kx}{\K^*} \newcommand{\Quot}{\mathit{Quot}} \newcommand{\divk}{\,\vert_\K\,} \newcommand{\divr}{\,\vert_\R\,} \newcommand{\assocr}{\sim_\R}\newcommand{\assock}{\sim_\K} \newcommand{\gdwdef}{\ :\Leftrightarrow\ } \newcommand{\poly}[2]{\sum_{i=0}^{#2} #1_i x^i} \subsubsection*{Polynomial Division and Reduction} Next we implement polynomial division operations. See also the books of Cohen \cite{cohen93} or Knuth \cite{knuth-ii} for a thourough treatment. The result of our division operation $p_1 / p_2$ in |NT[x]| is defined as the polynomial $p_3$ such that $p_1 = p_2 p_3$. In case there is no such polynomial the result is undefined. The implementation of |operator/| depends on the number type plugged into the template. To provide the division we implement two division operations |pseudo_div| and |euclidean_div|. The first operation works with ring number types providing a |gcd| operation, so called \emph{unique factorization domains}. The second operation works with polynomials over \emph{field} number types. To separate our number types we introduce a traits class providing tags to choose one or the other code variant. In the header file of |Polynomial| there are three predefined class types |ring_or_field_dont_know|, |ring_with_gcd|, and |field_with_div|. As a prerequisite a user has just to specialize the class template |ring_or_field<>| to the number type that she wants to plug into |Polynomial<>|. For the LEDA integer type this can be done as follows @c template <> struct ring_or_field { typedef ring_with_gcd kind; static leda_integer gcd(const leda_integer& a, const leda_integer& b) { return ::gcd(a,b); } }; @ In case of a Euclidean ring the class |ring_or_field| has to provide the gcd operation of two |RT| operands as a static method \footnote{This makes life easier when working with compilers that lack Koenig-lookup.}. Based on this number type flag |Polynomial| provides the division operation with the help of |pseudo_div| based on the |gcd| operation of |leda_integer|. For users not providing the |ring_or_field<>| specialization an error message is raised. \begin{ignoreindiss} <>= template /*CGAL_KERNEL_MEDIUM_INLINE*/ Polynomial divop (const Polynomial& p1, const Polynomial& p2, ring_or_field_dont_know) { CGAL_assertion_msg(0,"\n\ The division operation on polynomials requires that you\n\ specify if your number type provides a binary gcd() operation\n\ or is a field type including an operator/().\n\ You do this by creating a specialized class:\n\ template <> class ring_or_field with a member type:\n\ typedef ring_with_gcd kind; OR\n\ typedef field_with_div kind;\n"); return Polynomial(); // never reached } @ \end{ignoreindiss} The division operator is implemented depending on the number type |NT|. Our number type traits |ring_or_field| provides a tag type to specialize it via three overloaded methods |divop()| that are implemented below. <>= template inline Polynomial operator / (const Polynomial& p1, const Polynomial& p2) { return divop(p1,p2,ring_or_field::kind()); } @ \subsubsection*{Field Number Types} @ We first implement standard polynomial division. Starting from polynomials $f$ and $g$ we determine two polynomials $q$ and $r$ such that $f = q*g + r$ where $d_r \leq d_g$. <>= template /*CGAL_KERNEL_MEDIUM_INLINE*/ void Polynomial::euclidean_div( const Polynomial& f, const Polynomial& g, Polynomial& q, Polynomial& r) { int fd = f.degree(), gd = g.degree(); if ( fd < gd ) { q = Polynomial(NT(0)); r = f; CGAL_postcondition(f==q*g+r); return; } // now we know fd >= gd int qd = fd-gd, delta = qd+1, rd = fd; q = Polynomial(size_t(delta)); r = f; r.copy_on_write(); while ( qd >= 0 ) { NT Q = r[rd] / g[gd]; q.coeff(qd) += Q; r.minus_offsetmult(g,Q,qd); if (r.is_zero()) break; rd = r.degree(); qd = rd - gd; } CGAL_postcondition( f==q*g+r ); } @ We need an operation which allows us to subtract a polynomial |s| which is the product of a polynomial $p = \sum_{i=0}^{d} a_i x^i$ and a monomial $m = b x^{k}$. The result is $mp = \sum_{i=0}^{d+k} b \tilde{a}_i x^i$ where $\tilde{a}_i = 0$ for $0 \leq i >= void minus_offsetmult(const Polynomial& p, const NT& b, int k) { CGAL_assertion(!is_shared()); Polynomial s(size_type(p.degree()+k+1)); // zero entries for (int i=k; i <= s.degree(); ++i) s.coeff(i) = b*p[i-k]; operator-=(s); } @ Now we can just specialize |divop| in its third argument: <>= template /*CGAL_KERNEL_MEDIUM_INLINE*/ Polynomial divop (const Polynomial& p1, const Polynomial& p2, field_with_div) { CGAL_assertion(!p2.is_zero()); if (p1.is_zero()) return 0; Polynomial q,r; Polynomial::euclidean_div(p1,p2,q,r); CGAL_postcondition( (p2*q+r==p1) ); return q; } @ \subsubsection*{Unique factorization domains} Polynomial division avoiding using the notion of an inverse that is present in fields, is not so trivial. We first introduce the algebraic notions necessary for some theoretic results. We start with the introduction of divisibility and greatest common divisors. Let $f = \sum_{i=0}^{d_f} a_i x^i$, $g = \sum_{i=0}^{d_g} b_i x^i$ be polynomials of \emph{degree} $d_f$ and $d_g$. We assume the \emph{leading coefficient} $a_{d_f}$ and $b_{d_g}$ to be nonzero and thereby degree defining. \begin{deff} A \emph{commutative ring} $\R$ with unit 1 and containing no zero divisors is called an \emph{integrity domain}. An integrity domain where every nonzero element is either a \emph{unit} or has a unique representation\footnote{Uniqueness up to permutations and multiplication with units.} as a product of primes is called a \emph{unique factorization domain}. \end{deff} The integers $\Z$ are our default example of a unique factorization domain. One example for a ring that is no unique factorization domain is $\Z/4\Z$ which contains the zero divisor $2$. \begin{deff} Let $\R$ be an integrity domain. $\K = \Quot(\R)$, $\Kx = \K - \{0\}$. Let $a,b \in \Kx$. \begin{eqnarray*} & (1) & a \divr b \mbox{\ \ "$a$ divides $b$ in $\R$"\ \ } \gdwdef \exists c \in \R : b = ac \\ & (2) & a \assocr b \gdwdef a \divr b \AND b \divr a \end{eqnarray*} Let $d \in \Kx$. $d$ is called $\gcd_\K(a,b)$ $\gdwdef$ \begin{eqnarray*} & (1) & d \divr a \AND d \divr b \\ & (2) & \forall c \in \Kx : c \divr a \AND c \divr b \Longrightarrow c \divr d \end{eqnarray*} $d$ is determined uniquely except for multiplication with units. \end{deff} The following lemma assures that we can divide $f$ by $g$ in the integral domain when we accept an expansion of $f$ by a power of the leading coefficient of $g$. \begin{lemma}\label{polynomialdivision} Let $\R$ be a ring and $f ,g \in \R[x], g \neq 0$. Let $b$ be the leading coefficient of $g$. Then there are polynomials $q,r \in \R[x]$, such that \begin{displaymath} b^s f = q g + r \end{displaymath} where either $r=0$ or $r \neq 0$ and $d_r < d_g$ and the integer $s=0$ if $f=0$ and $s= \max \{ 0, d_f - d_g + 1 \}$ if $f \neq 0$. If $b$ is no zero divisor in $\R$ then $q$ and $r$ are uniquely defined. \end{lemma} \begin{proof} \textbf{Existence} --- In case $f=0$ and $f\neq 0, d_f < d_g$ the pair $q=0,r=f$ is a trivial solution. Let $f \neq 0$, $\Delta(f,g) := d_f - d_g \geq 0$. We show the existence of $q,r$ by induction on $\Delta(f,g)$. Let $q,r$ exist for polynomials $f,g, f \neq 0, \Delta(f,g) < \Delta_0, \Delta_0 \geq 0$. Now let $f \in \R[x], f \neq 0, \Delta(f,g) = \Delta_0$ and $a$ be the leading coefficient of $f$. We look at $f' := b f - a x ^{\Delta_0} g$. Either $f' = 0$ or $f' \neq 0$ but $d_{f'} < d_f, \Delta(f',g) < \Delta_0$. By the induction hypothesis there are $r',q' \in \R[x]$ such that $b^{s'}f = q' g + r'$ where either $r'=0$ or $r \neq 0$ and $d_{r'} < d_g$ and the non-negative integer $s' \leq d_f - d_g$. Thus $b^{s'+1} f' = (b^{s'} a x^{\Delta_0} + q') g + r'$. Because of $s' + 1 \leq d_f - d_g +1$ the existence of $q$ and $r$ is clear. \textbf{Uniqueness} --- Let $b$ be no zero divisor in $\R$ and assume that there is another representation $b^s f = \tilde{q} g + \tilde{r}$ where either $\tilde{r}=0$ or $\tilde{r} \neq 0$ and $d_{q} > d_{\tilde{q}}$. By subtracting one from the other we obtain $(q-\tilde{q})g = \tilde{r}-r$. As the $b$ is no zero divisor in $\R$ the degree formula tells us that $\deg (q-\tilde{q})g = \deg (q-\tilde{q}) + d_g \geq d_g$. Thus $\tilde{r}-r \neq 0$ and $\deg (\tilde{r}-r) \geq d_g$. On the other hand $\tilde{r}-r$ is the difference of two polynomials of degree smaller than $d_g$ and therefore $\deg (\tilde{r}-r) < d_g$. From the contradiction we obtain $q-\tilde{q}=\tilde{r}-r=0$. \end{proof} See any algebra book like \cite{rescheve-algebra} for more details. Knuth \cite{knuth-ii} calls the algorithm based on the above lemma \emph{pseudo-division}. According to this lemma one can determine $q$ and $r$ within the ring without resorting to the quotient field. We follow the construction in the proof in reverse order to reduce $f$ down to $r$. We use |minus_offsetmult| for the reduction from $f$ to $f'$. <>= template /*CGAL_KERNEL_MEDIUM_INLINE*/ void Polynomial::pseudo_div( const Polynomial& f, const Polynomial& g, Polynomial& q, Polynomial& r, NT& D) { TRACEN("pseudo_div "<(0); r = f; D = 1; CGAL_postcondition(Polynomial(D)*f==q*g+r); return; } // now we know fd >= gd and f>=g int qd=fd-gd, delta=qd+1, rd=fd; q = Polynomial( size_t(delta) ); NT G = g[gd]; // highest order coeff of g D = G; while (--delta) D*=G; // D = G^delta Polynomial res = Polynomial(D)*f; TRACEN(" pseudo_div start "<= 0) { NT F = res[rd]; // highest order coeff of res NT t = F/G; // ensured to be integer by multiplication of D q.coeff(qd) = t; // store q coeff res.minus_offsetmult(g,t,qd); if (res.is_zero()) break; rd = res.degree(); qd = rd - gd; } r = res; CGAL_postcondition(Polynomial(D)*f==q*g+r); TRACEN(" returning "<>= template /*CGAL_KERNEL_MEDIUM_INLINE*/ Polynomial divop (const Polynomial& p1, const Polynomial& p2, ring_with_gcd) { CGAL_assertion(!p2.is_zero()); if (p1.is_zero()) return 0; Polynomial q,r; NT D; Polynomial::pseudo_div(p1,p2,q,r,D); return q/=D; } @ For the reduction of polynomials we finally implement the greatest common divisor method as introduced by Euclid. For two elements $a,b \in \R$ Euclids algorithm uses the reduction $\gcd(a,b) \assocr \gcd(b, a \mod b)$. \newcommand{\cont}{\mathrm{cont}\,} \newcommand{\pp}{\mathrm{pp}} \newcommand{\fo}{f^\circ} \newcommand{\go}{g^\circ} \renewcommand{\do}{d^\circ} \begin{deff} For a polynomial $f = \poly{a}{d}$ we define its \emph{content} as $\cont (f) = \gcd(a_0,\ldots,a_d)$ and its \emph{primitive part} as $\pp(f) = f/\cont(f)$. \end{deff} Again the content of a polynomial is only unique up to multiplication by units of $\R$. Note that $\cont(f)$ is a divisor of all coefficients of $f$ in $\R$ and therefore the division is reducing the representation of $f$ such that $\cont(\pp(f)) = 1$. The following lemma tells us something about the composition of the $\gcd$ of two polynomials from the contents and the primitive parts. A polynomial whose content is $1$ is called \emph{primitive}. \begin{lemma}[Gauss] The product of two \emph{primitive} polynomials $f$ and $g$ over a unique factorization domain is again \emph{primitive}. Moreover let $f$ and $g$ be two general polynomials over a unique factorization domain $\R$. Then $\cont(\gcd (f,g) ) \assocr \gcd(\cont(f),\cont(g))$ and $\pp(\gcd(f,g)) \assocr \gcd(\pp(f),\pp(g))$. \end{lemma} \begin{proof} Let $f = \poly{a}{d_f}, g = \poly{b}{d_g}$ be primitive polynomials. We show for any prime $p$ of the domain that it does not divide all the coefficients of $f \cdot g$. For both polynomials we chose the smallest indices $j$ and $k$ for which $p$ does \emph{not} divide $a_j$ and $b_k$ from $(a_i)_i$ and $(b_i)_i$. We then examine the coefficient of $x^{j+k}$ of $f \cdot g$: \[a_jb_k + a_{j+1}b_{k-1} + \cdots + a_{j+k}b_{0} + a_{j-1}b_{k+1} + \cdots + a_{0}b_{k+j}.\] As $p$ does not divide the first term and but all of the following ones, therefore $p$ does not divide the sum. From the above we can deduce for general polynomials $f$ and $g$ that $\pp(fg) \assocr \pp(f)\pp(g)$. The product $fg$ can be decomposed as $fg = \cont(f)\pp(f)\cont(g)\pp(g) = \cont(f)\cont(g)\pp(g)\pp(f)$ and $fg = \cont(fg)\pp(fg)$. Thereby we can deduce that $\cont(fg) \assocr \cont(f)\cont(g)$. Now assume that $h \assocr \gcd(f,g)$ and thus $f = hF$ and $g = hG$ for some polynomials $F$ and $G$ from $\R[x]$. By the previous result we get $\cont(f) \assocr \cont(h)\cont(F)$ and $\cont(g) \assocr \cont(h)\cont(G)$ and thereby $\cont(\gcd(f,g))\assocr\cont(h)\assocr\gcd_\R(\cont(f),\cont(g))$. The latter equality follows from the fact that $\gcd_\R(\cont(F),\cont(G)) \assocr 1$ due to the properties of $h$ in the decomposition of $f$ and $g$. A similar argument shows that $\pp(\gcd(f,g)) \assocr \gcd(\pp(f),\pp(g))$. \end{proof} This result simplifies the problem and allows us to keep the size of the coefficients of the polynomials as small as possible. An elaborate treatment of the topic can be found in \cite{cohen93,knuth-ii}. By the above lemma we obtain the following strategy. First calculate $F = \gcd_\R(\cont(f),\cont(g))$ by the gcd routine on the ring number type |NT|. Reduce both polynomials by their content to their primitive parts $\fo = \pp(f)$ and $\go = \pp(g)$. Then reduce $\gcd(\fo,\go) \assocr \gcd(\go,\fo \mod \go)$. However our pseudo-division |pseudo_div| only allows reductions of the form $(D\fo,\go)$ to $(\go,D\fo \mod \go)$ where $D = b^s$ as described above. This is ok though as $\gcd(D\fo,\go) \assocr \gcd_\R(\cont(D\fo),\cont(\go)) \gcd(\pp(D\fo),\pp(\go)) \assocr \gcd_\R(D,1) \gcd(\fo,\go) \assocr \gcd(\fo,\go)$. The final result of the Euclidean reduction delivers $\do = \gcd(\fo,\go)$. We obtain the desired result $\gcd(f,g) = F\cdot\do$. <>= template /*CGAL_KERNEL_MEDIUM_INLINE*/ Polynomial Polynomial::gcd( const Polynomial& p1, const Polynomial& p2) { TRACEN("gcd("<(NT(1)); else return p2.abs(); if ( p2.is_zero() ) return p1.abs(); Polynomial f1 = p1.abs(); Polynomial f2 = p2.abs(); NT f1c = f1.content(), f2c = f2.content(); f1 /= f1c; f2 /= f2c; NT F = ring_or_field::gcd(f1c,f2c); Polynomial q,r; NT M=1,D; bool first = true; while ( ! f2.is_zero() ) { Polynomial::pseudo_div(f1,f2,q,r,D); if (!first) M*=D; TRACEV(f1);TRACEV(f2);TRACEV(q);TRACEV(r);TRACEV(M); r /= r.content(); f1=f2; f2=r; first=false; } TRACEV(f1.content()); return Polynomial(F)*f1.abs(); } @ \begin{ignoreindiss} The rest of this section just implements all kinds of comparison predicates based on our sign evaluation. <>= template inline Polynomial gcd(const Polynomial& p1, const Polynomial& p2) { return Polynomial::gcd(p1,p2); } template /*CGAL_KERNEL_INLINE*/ bool operator == (const Polynomial& p1, const Polynomial& p2) { return ( (p1-p2).sign() == CGAL::ZERO ); } template /*CGAL_KERNEL_INLINE*/ bool operator != (const Polynomial& p1, const Polynomial& p2) { return ( (p1-p2).sign() != CGAL::ZERO ); } template /*CGAL_KERNEL_INLINE*/ bool operator < (const Polynomial& p1, const Polynomial& p2) { return ( (p1-p2).sign() == CGAL::NEGATIVE ); } template /*CGAL_KERNEL_INLINE*/ bool operator <= (const Polynomial& p1, const Polynomial& p2) { return ( (p1-p2).sign() != CGAL::POSITIVE ); } template /*CGAL_KERNEL_INLINE*/ bool operator > (const Polynomial& p1, const Polynomial& p2) { return ( (p1-p2).sign() == CGAL::POSITIVE ); } template /*CGAL_KERNEL_INLINE*/ bool operator >= (const Polynomial& p1, const Polynomial& p2) { return ( (p1-p2).sign() != CGAL::NEGATIVE ); } #ifndef _MSC_VER template /*CGAL_KERNEL_INLINE*/ CGAL::Sign sign(const Polynomial& p) { return p.sign(); } #endif // collides with global CGAL sign //------------------------------------------------------------------ // SPECIALIZE_FUNCTION(NT,int double) START // lefthand side template Polynomial operator + (const NT& num, const Polynomial& p2) { return (Polynomial(num) + p2); } template Polynomial operator - (const NT& num, const Polynomial& p2) { return (Polynomial(num) - p2); } template Polynomial operator * (const NT& num, const Polynomial& p2) { return (Polynomial(num) * p2); } template Polynomial operator / (const NT& num, const Polynomial& p2) { return (Polynomial(num)/p2); } // righthand side template Polynomial operator + (const Polynomial& p1, const NT& num) { return (p1 + Polynomial(num)); } template Polynomial operator - (const Polynomial& p1, const NT& num) { return (p1 - Polynomial(num)); } template Polynomial operator * (const Polynomial& p1, const NT& num) { return (p1 * Polynomial(num)); } template Polynomial operator / (const Polynomial& p1, const NT& num) { return (p1 / Polynomial(num)); } // lefthand side template bool operator == (const NT& num, const Polynomial& p) { return ( (Polynomial(num)-p).sign() == CGAL::ZERO );} template bool operator != (const NT& num, const Polynomial& p) { return ( (Polynomial(num)-p).sign() != CGAL::ZERO );} template bool operator < (const NT& num, const Polynomial& p) { return ( (Polynomial(num)-p).sign() == CGAL::NEGATIVE );} template bool operator <= (const NT& num, const Polynomial& p) { return ( (Polynomial(num)-p).sign() != CGAL::POSITIVE );} template bool operator > (const NT& num, const Polynomial& p) { return ( (Polynomial(num)-p).sign() == CGAL::POSITIVE );} template bool operator >= (const NT& num, const Polynomial& p) { return ( (Polynomial(num)-p).sign() != CGAL::NEGATIVE );} // righthand side template bool operator == (const Polynomial& p, const NT& num) { return ( (p-Polynomial(num)).sign() == CGAL::ZERO );} template bool operator != (const Polynomial& p, const NT& num) { return ( (p-Polynomial(num)).sign() != CGAL::ZERO );} template bool operator < (const Polynomial& p, const NT& num) { return ( (p-Polynomial(num)).sign() == CGAL::NEGATIVE );} template bool operator <= (const Polynomial& p, const NT& num) { return ( (p-Polynomial(num)).sign() != CGAL::POSITIVE );} template bool operator > (const Polynomial& p, const NT& num) { return ( (p-Polynomial(num)).sign() == CGAL::POSITIVE );} template bool operator >= (const Polynomial& p, const NT& num) { return ( (p-Polynomial(num)).sign() != CGAL::NEGATIVE );} // SPECIALIZE_FUNCTION(NT,int double) END //------------------------------------------------------------------ @ Finally we offer standard I/O operations. <>= template void print_monomial(std::ostream& os, const NT& n, int i) { if (i==0) os << n; if (i==1) os << n << "R"; if (i>1) os << n << "R^" << i; } #define POLYNOMIAL_EXPLICIT_OUTPUT // I/O template std::ostream& operator << (std::ostream& os, const Polynomial& p) { int i; switch( os.iword(CGAL::IO::mode) ) { case CGAL::IO::ASCII : os << p.degree() << ' '; for(i=0; i<=p.degree(); ++i) os << p[i] << ' '; return os; case CGAL::IO::BINARY : CGAL::write(os, p.degree()); for(i=0; i<=p.degree(); ++i) CGAL::write(os, p[i]); return os; default: #ifndef POLYNOMIAL_EXPLICIT_OUTPUT os << "Polynomial(" << p.degree() << ", "; for(i=0; i<=p.degree(); ++i) { os << p[i]; if (i < p.degree()) os << ", "; } os << ")"; #else print_monomial(os,p[p.degree()],p.degree()); for(i=p.degree()-1; i>=0; --i) { if (p[i]!=NT(0)) { os << " + "; print_monomial(os,p[i],i); } } #endif return os; } } template std::istream& operator >> (std::istream& is, Polynomial& p) { int i,d; NT c; switch( is.iword(CGAL::IO::mode) ) { case CGAL::IO::ASCII : is >> d; if (d < 0) p = Polynomial(); else { typename Polynomial::Vector coeffs(d+1); for(i=0; i<=d; ++i) is >> coeffs[i]; p = Polynomial(coeffs.begin(),coeffs.end()); } break; case CGAL::IO::BINARY : CGAL::read(is, d); if (d < 0) p = Polynomial(); else { typename Polynomial::Vector coeffs(d+1); for(i=0; i<=d; ++i) { CGAL::read(is,c); coeffs[i]=c; } p = Polynomial(coeffs.begin(),coeffs.end()); } break; default: CGAL_assertion_msg(0,"\nStream must be in ascii or binary mode\n"); break; } return is; } @ We finally give the file wrapper for our definition and implementation part. <>= <> #ifndef CGAL_POLYNOMIAL_H #define CGAL_POLYNOMIAL_H #include #include #include #include #include #include #undef _DEBUG #define _DEBUG 3 #include #if defined(_MSC_VER) || defined(__BORLANDC__) #include #define CGAL_SIMPLE_NEF_INTERFACE #define SNIHACK ,char,char #define SNIINST ,'c','c' #else #include #define SNIHACK #define SNIINST #endif #define GCD_FOR_BUILTIN(RT) \ static RT gcd(const RT& a, const RT& b) \ { if (a == 0) \ if (b == 0) return 1; \ else return CGAL_NTS abs(b); \ if (b == 0) return CGAL_NTS abs(a); \ int u = CGAL_NTS abs(a); \ int v = CGAL_NTS abs(b); \ if (u < v) v = v%u; \ while (v != 0) \ { int tmp = u % v; \ u = v; \ v = tmp; \ } \ return u; \ } class ring_or_field_dont_know {}; class ring_with_gcd {}; class field_with_div {}; template struct ring_or_field { typedef ring_or_field_dont_know kind; }; template <> struct ring_or_field { typedef ring_with_gcd kind; typedef int NT; GCD_FOR_BUILTIN(int) }; template <> struct ring_or_field { typedef ring_with_gcd kind; typedef long NT; GCD_FOR_BUILTIN(long) }; template <> struct ring_or_field { typedef field_with_div kind; typedef double NT; static double gcd(const double&, const double&) { return 1.0; } }; CGAL_BEGIN_NAMESPACE template class Polynomial_rep; // SPECIALIZE_CLASS(NT,int double) START template class Polynomial; // SPECIALIZE_CLASS(NT,int double) END <> <> <> // SPECIALIZE_CLASS(NT,int double) START <> // SPECIALIZE_CLASS(NT,int double) END template NT Polynomial::R_; int Polynomial::R_; double Polynomial::R_; <> <> // SPECIALIZE_IMPLEMENTATION(NT,int double) START <> // SPECIALIZE_IMPLEMENTATION(NT,int double) END CGAL_END_NAMESPACE #endif // CGAL_POLYNOMIAL_H @ \end{ignoreindiss}% Finally we provide a gcd calculation routine for a sequence of numbers. This routine requires the existence of a |gcd| operation as provided by our number type traits |ring_or_field|. <>= /*{\Mtext \headerline{Range template}}*/ #ifndef CGAL_SIMPLE_NEF_INTERFACE template typename std::iterator_traits::value_type gcd_of_range(Forward_iterator its, Forward_iterator ite) /*{\Mfunc calculates the greates common divisor of the set of numbers $\{ |*its|, |*++its|, \ldots, |*it| \}$ of type |NT|, where |++it == ite| and |NT| is the value type of |Forward_iterator|. \precond there exists a pairwise gcd operation |NT gcd(NT,NT)| and |its!=ite|.}*/ { CGAL_assertion(its!=ite); typedef typename std::iterator_traits::value_type NT; NT res = *its++; for(; its!=ite; ++its) res = (*its==0 ? res : ring_or_field::gcd(res, *its)); if (res==0) res = 1; return res; } #endif //CGAL_SIMPLE_NEF_INTERFACE @ \begin{onlyindiss} In this presentation we omit the details of input and output operations and all additional technical requirement necessary to make |Polynomial<>| a CGAL number type. The type is specialized for the built-in number types |int| and |double|. This is necessary as the method interface for the general template class has to have methods for |NT| as well as for |int| and |double| to resolve construction ambiguities when using numeric constants of the built-in types. On the other hand these initialization methods collide when the general template would be instantiated for these types. Therefore the specialized class types have an interface avoiding the collisions. \end{onlyindiss} \begin{ignoreindiss} Now for some CGAL specific number type reqirements. <>= template /*CGAL_KERNEL_INLINE*/ double to_double (const Polynomial& p) { return (CGAL::to_double(p.eval_at(Polynomial::R_))); } template /*CGAL_KERNEL_INLINE*/ bool is_valid (const Polynomial& p) { return (CGAL::is_valid(p[0])); } template /*CGAL_KERNEL_INLINE*/ bool is_finite (const Polynomial& p) { return CGAL::is_finite(p[0]); } template /*CGAL_KERNEL_INLINE*/ CGAL::io_Operator io_tag(const Polynomial&) { return CGAL::io_Operator(); } @ Now the prototypes which have to be declared due to a friend statement in the rep class. <>= template /*CGAL_KERNEL_MEDIUM_INLINE*/ Polynomial operator - (const Polynomial&); template /*CGAL_KERNEL_MEDIUM_INLINE*/ Polynomial operator + (const Polynomial&, const Polynomial&); template /*CGAL_KERNEL_MEDIUM_INLINE*/ Polynomial operator - (const Polynomial&, const Polynomial&); template /*CGAL_KERNEL_MEDIUM_INLINE*/ Polynomial operator * (const Polynomial&, const Polynomial&); template inline Polynomial operator / (const Polynomial&, const Polynomial&); #ifndef _MSC_VER template /*CGAL_KERNEL_INLINE*/ CGAL::Sign sign(const Polynomial& p); #endif // collides with global CGAL sign template /*CGAL_KERNEL_INLINE*/ double to_double(const Polynomial& p) ; template /*CGAL_KERNEL_INLINE*/ bool is_valid(const Polynomial& p) ; template /*CGAL_KERNEL_INLINE*/ bool is_finite(const Polynomial& p) ; template std::ostream& operator << (std::ostream& os, const Polynomial& p); template std::istream& operator >> (std::istream& is, Polynomial& p); #ifndef _MSC_VER // lefthand side template inline Polynomial operator + (const NT& num, const Polynomial& p2); template inline Polynomial operator - (const NT& num, const Polynomial& p2); template inline Polynomial operator * (const NT& num, const Polynomial& p2); template inline Polynomial operator / (const NT& num, const Polynomial& p2); // righthand side template inline Polynomial operator + (const Polynomial& p1, const NT& num); template inline Polynomial operator - (const Polynomial& p1, const NT& num); template inline Polynomial operator * (const Polynomial& p1, const NT& num); template inline Polynomial operator / (const Polynomial& p1, const NT& num); // lefthand side template inline bool operator == (const NT& num, const Polynomial& p); template inline bool operator != (const NT& num, const Polynomial& p); template inline bool operator < (const NT& num, const Polynomial& p); template inline bool operator <= (const NT& num, const Polynomial& p); template inline bool operator > (const NT& num, const Polynomial& p); template inline bool operator >= (const NT& num, const Polynomial& p); // righthand side template inline bool operator == (const Polynomial& p, const NT& num); template inline bool operator != (const Polynomial& p, const NT& num); template inline bool operator < (const Polynomial& p, const NT& num); template inline bool operator <= (const Polynomial& p, const NT& num); template inline bool operator > (const Polynomial& p, const NT& num); template inline bool operator >= (const Polynomial& p, const NT& num); #endif // _MSC_VER @ \end{ignoreindiss} \begin{ignoreindiss} \section{A Test of Polynomial} We test in different settings concerning instantiation number types and operator argument types. We basically use |leda_integer|, |int|, and |double|. <>= #include #ifndef _MSC_VER #include #else #include #endif #include #ifdef CGAL_USE_LEDA #include typedef leda_integer Integer; template <> struct ring_or_field { typedef ring_with_gcd kind; typedef leda_integer RT; static RT gcd(const RT& r1, const RT& r2) { return ::gcd(r1,r2); } }; #else #ifdef CGAL_USE_GMP #include typedef CGAL::Gmpz Integer; template <> struct ring_or_field { typedef ring_with_gcd kind; typedef CGAL::Gmpz RT; static RT gcd(const RT& r1, const RT& r2) { return CGAL::gcd(r1,r2); } }; #else typedef int Integer; #endif #endif using namespace CGAL; #ifdef _MSC_VER #define MSCCAST(n) static_cast(n) #define Polynomial Polynomial_MSC typedef std::iterator_traits::iterator_category iiii; #else #define MSCCAST(n) n #endif #define PRT(t1,t2) std::cout<<"testing instances "<<#t1<<" "<<#t2< RP; <> } { PRT(int,Integer); typedef int NT; typedef Polynomial RP; <> } { PRT(double,Integer); typedef double NT; typedef Polynomial RP; <> } { PRT(int,int); typedef int NT; typedef Polynomial RP; <> } { PRT(double,int); typedef double NT; typedef Polynomial RP; <> } CGAL_TEST_END; } <>= RP::NT seq[4] = { 0, 1, 2, 0 }; RP p1, p2(NT(1)), p3(NT(1),NT(1)), p4(5,2), p5(-2,5), p6(4,1), p7(3,0), p8(seq,seq+4); RP p10(-1,0,1), p11(-1,1), p12(1,1); NT r1(2), r2(-2); CGAL_TEST(p1.degree()==-1); CGAL_TEST(p2.degree()==0); CGAL_TEST(p4.degree()==1); CGAL_TEST(p7.degree()==0); CGAL_TEST(p8.degree()==2); CGAL_TEST((-(-p4)) == p4); CGAL_TEST((-(-p7)) == p7); CGAL_TEST((p4+p5) == RP(3,7)); CGAL_TEST((p4-p5) == RP(7,-3)); RP::NT prod[3] = { -10, 21, 10 }; CGAL_TEST((p4*p5) == RP(prod,prod+3)); CGAL_TEST((p2*p3) == p3); MSCCAST(r1)+p3; p3+MSCCAST(r1); CGAL_TEST((MSCCAST(r1)+p3) == RP(3,1)); CGAL_TEST((MSCCAST(r1)-p3) == RP(1,-1)); CGAL_TEST((MSCCAST(r1)*p3) == RP(2,2)); CGAL_TEST((p3+MSCCAST(r1)) == RP(3,1)); CGAL_TEST((p3-MSCCAST(r1)) == RP(-1,1)); CGAL_TEST((p3*MSCCAST(r1)) == RP(2,2)); CGAL_TEST(p2 != p3); CGAL_TEST(p2 < p3); CGAL_TEST(p2 <= p3); CGAL_TEST(p5 > p4); CGAL_TEST(p5 >= p4); CGAL_TEST(MSCCAST(r1) != p2); CGAL_TEST(MSCCAST(r2) < p2); CGAL_TEST(MSCCAST(r2) <= p2); CGAL_TEST(MSCCAST(r1) > p2); CGAL_TEST(MSCCAST(r1) >= p2); CGAL_TEST(p2 != MSCCAST(r1)); CGAL_TEST(p2 > MSCCAST(r2)); CGAL_TEST(p2 >= MSCCAST(r2)); CGAL_TEST(p2 < MSCCAST(r1)); CGAL_TEST(p2 <= MSCCAST(r1)); CGAL_TEST(CGAL_NTS sign(p5)==+1); CGAL_TEST(CGAL_NTS sign(-p5)==-1); CGAL_TEST(CGAL_NTS sign(p2)==+1); CGAL_TEST(CGAL_NTS sign(-p2)==-1); p3 += p2; p3 -= p2; p3 *= p5; p3 += MSCCAST(r1); p3 -= MSCCAST(r1); p3 *= MSCCAST(r2); RP::NT D; RP q1(17),q2(5),q3,q4; RP::pseudo_div(q1,q2,q3,q4,D); CGAL_TEST(MSCCAST(D)*q1==q2*q3+q4); RP::pseudo_div(-q1,q2,q3,q4,D); CGAL_TEST(MSCCAST(D)*-q1==q2*q3+q4); RP::pseudo_div(q1,-q2,q3,q4,D); CGAL_TEST(MSCCAST(D)*q1==-q2*q3+q4); RP::pseudo_div(-q1,-q2,q3,q4,D); CGAL_TEST(MSCCAST(D)*-q1==-q2*q3+q4); RP qq1(5),qq2(17),qq3,qq4; RP::pseudo_div(qq1,qq2,qq3,qq4,D); CGAL_TEST(MSCCAST(D)*qq1==qq2*qq3+qq4); RP::pseudo_div(-qq1,qq2,qq3,qq4,D); CGAL_TEST(MSCCAST(D)*-qq1==qq2*qq3+qq4); RP::pseudo_div(qq1,-qq2,qq3,qq4,D); CGAL_TEST(MSCCAST(D)*qq1==-qq2*qq3+qq4); RP::pseudo_div(-qq1,-qq2,qq3,qq4,D); CGAL_TEST(MSCCAST(D)*-qq1==-qq2*qq3+qq4); CGAL_TEST(p10/p11 == p12); q3 = RP::gcd(q1,q2); CGAL_TEST(q3 == MSCCAST(1)); CGAL_IO_TEST(p4,p1); CGAL::to_double(p6); CGAL::is_finite(p6); CGAL::is_valid(p6); @ \section{A Demo of Polynomial} <>= #include #include #include #include #include #include #define POLYNOMIAL_EXPLICIT_OUTPUT #ifndef CARTESIAN #include #include typedef leda_integer NT; #else #include #include typedef leda_rational NT; template <> struct ring_or_field { typedef field_with_div kind; typedef leda_rational FT; static FT gcd(const FT&, const FT&) { return FT(1); } }; #endif typedef CGAL::Polynomial Poly; typedef leda_string lstring; enum { NEW,PLUS,MINUS,MULT,DIV,MOD,GCD,END,HIST,ERROR }; static leda_d_array M(Poly(1)); void strip(lstring& s) { lstring res; for (int i=0; i V; if (contains(s,"=",l,b)) { if (contains(b,"+",p1,p2)) res=PLUS; if (contains(b,"-",p1,p2)) res=MINUS; if (contains(b,"*",p1,p2)) res=MULT; if (contains(b,"/",p1,p2)) res=DIV; if (contains(b,"%",p1,p2)) res=MOD; if (contains(b,"gcd(",dummy,b) && contains(b,")",b,dummy) && contains(b,",",p1,p2)) res=GCD; if (contains(b,"(",dummy,b) && contains(b,")",b,dummy)) { while (contains(b,",",n,b)) { string_istream IS(n.cstring()); IS >> N; V.push_back(N); } string_istream IS(b.cstring()); IS >> N; V.push_back(N); P1 = Poly(V.begin(),V.end()); return NEW; } } P1=M[p1]; P2=M[p2]; return res; } int main(int argc, char* argv[]) { //SETDTHREAD(3); CGAL::set_pretty_mode ( cout ); CGAL::set_pretty_mode ( cerr ); cout << "insert simple assigments of the following form\n"; cout << "v1 = (a0,a1,a2) -> creates a0 + a1 x + a2 x^2\n"; cout << "v1 = v2 [+-*/] v3 -> triggers arithmetic operation\n"; cout << "v1 = gcd(v2,v3) -> triggers gcd operation\n"; cout << "end -> quits program"<< endl; cout << "history -> prints all current vars"<< endl; lstring logname = lstring(argv[0])+".log"; { file_istream logfile(logname.cstring()); lstring line,s; Poly p; if (logfile) { cout << "initializing history\n"; while ( (line.read_line(logfile), line!="") ) { string_istream line_is(line); line_is >> s >> p; cout << s << " = " << p << endl; M[s]=p; } } logfile.close(); } lstring command,label; Poly p1,p2,res,dummy; NT D; bool loop=true; while (loop) { cout @<< "@>>"; command.read_line(cin); strip(command); // strip whitespace switch (parse(command,p1,p2,label)) { case NEW: res = p1; break; case PLUS: res = p1+p2; break; case MINUS: res = p1-p2; break; case MULT: res = p1*p2; break; case DIV: res = p1/p2; break; case MOD: Poly::pseudo_div(p1,p2,dummy,res,D); break; case GCD: res = Poly::gcd(p1,p2); break; case END: loop=false; continue; case HIST: { lstring s; forall_defined(s,M) cout << s << " = " << M[s] << endl; continue; } default: cout << "wrong syntax\n"; continue; } cout << " " << label << " = " << res << endl << endl; M[label] = res; } file_ostream logfile(logname); lstring s; forall_defined(s,M) logfile << s << " " << M[s] << endl; return 0; } @ \begin{ignore} <>= // ============================================================================ // // Copyright (c) 1997-2000 The CGAL Consortium // // This software and related documentation is part of an INTERNAL release // of the Computational Geometry Algorithms Library (CGAL). It is not // intended for general use. // // ---------------------------------------------------------------------------- // // release : $CGAL_Revision$ // release_date : $CGAL_Date$ // // file : include/CGAL/Nef_2/Nef_2/Polynomial.h // package : Nef_2 // chapter : Nef Polyhedra // // revision : $Id$ // revision_date : $Date$ // // author(s) : Michael Seel // maintainer : Michael Seel // coordinator : Michael Seel // // implementation: Polynomials in one variable // ============================================================================ @ \end{ignore} \section{Perl expansion of specialization} The following perl script expands the Polynomial implementation above with respect to |int| and |double| specialization. <>= #! /opt/gnu/bin/perl $specialize = 0; $spec_arg = ""; while (<>) { # specializing class templates if ( /SPECIALIZE_CLASS\(.*\).*END/ ) { print specialize_members($spec_class,$nt_from); my @arglist = split(/ /,$class_to); foreach my $arg (@arglist) { my $copied_class = $spec_class; $copied_class =~ s/template\s*\(\s*)class\s*(\w+)\s*/template \<\>$1class $2\<$arg\> /sg; $copied_class =~ s/template\s*\(\s*)class\s*(\w+)\s*/template \<\>$1class $2\<$arg\> /sg; $copied_class =~ s/template\s*\(\s*)class\s*(\w+)\s*/template \<\>$1class $2\<$arg\> /sg; $copied_class =~ s/template\s*\/template \<\>/sg; $copied_class =~ s/template\s*\/template \<\>/sg; $copied_class =~ s/([\W]|^)$nt_from([\W]|$)/$1$arg$2/smg; $copied_class =~ s/([\W]|^)p$nt_from([\W]|$)/$1$arg$2/smg; $copied_class =~ s/typename//sg; $copied_class =~ s/typedef $arg $arg/typedef $arg $nt_from/sg; $copied_class =~ s/\/\*\{\\M/\/\*\{\\X/sg; print specialize_members($copied_class,$arg); } $nt_from = ""; $class_to = ""; $spec_class = ""; } if ( $spec_class ) { $spec_class .= $_; $_=""; } if ( /SPECIALIZE_CLASS\((\w+),([\w\ ]+)\).*START/ ) { $nt_from = $1; $class_to = $2; $spec_class = "// CLASS TEMPLATE $nt_from: \n" } # specializing function templates if ( /SPECIALIZE\_FUNCTION\(.*\).*END/ ) { my @arglist = split(/ /,$args_to); foreach my $arg (@arglist) { my $copied_spec2 = $spec_func; my $header2 = "// SPECIALIZING inline to $nt_to:\n"; $copied_spec2 =~ s/^.*?\n/$header2/; $copied_spec2 =~ s/$arg_from/$arg/sg; $copied_spec2 =~ s/template\s*\/inline/sg; $copied_spec2 =~ s/template\s*\/inline/sg; print $copied_spec2; my $copied_spec1 = $spec_func; my $header1 = "// SPECIALIZING pure $arg params:\n"; $copied_spec1 =~ s/^.*?\n/$header1/; $copied_spec1 =~ s/const\s*$arg_from\s*\&/const $arg\&/sg; print $copied_spec1; } print $spec_func; $spec_func = ""; $args_to = ""; $arg_from = ""; } if ( $spec_func ) { $spec_func .= $_; $_ = ""; } if ( /SPECIALIZE\_FUNCTION\((\w+),([\w\ ]+)\).*START/ ) { $arg_from = $1; $args_to = $2; $spec_func = "// SPECIALIZE_FUNCTION ORIGINAL\n"; } # specializing implementation if ( /SPECIALIZE\_IMPLEMENTATION\(.*\).*END/ ) { my @arglist = split(/ /,$args_to); foreach my $arg (@arglist) { my $copied_spec2 = $spec_impl; my $header2 = "// SPECIALIZING to $nt_to:\n"; $copied_spec2 =~ s/^.*?\n/$header2/; $copied_spec2 =~ s/$arg_from/$arg/sg; $copied_spec2 =~ s/template\s*\//sg; $copied_spec2 =~ s/template\s*\//sg; print $copied_spec2; } print $spec_impl; $spec_impl = ""; $args_to = ""; $arg_from = ""; } if ( $spec_impl ) { $spec_impl .= $_; $_ = ""; } if ( /SPECIALIZE\_IMPLEMENTATION\((\w+),([\w\ ]+)\).*START/ ) { $arg_from = $1; $args_to = $2; $spec_impl = "// SPECIALIZE_FUNCTION ORIGINAL\n"; } print; } sub specialize_members { local($spec,$nt) = @_; local($result,$args_to,$spec_mem,$kill); $kill=0; $spec =~ s/\n\n/\n>>><<>><<>= #include #include // SPECIALIZE_CLASS(T,int) START template class A; // SPECIALIZE_CLASS(T,int) END template A operator+ (const A&, const A&); //A operator+ (const int& num, const A& a2); // SPECIALIZE_CLASS(T,int) START template class A { T a; friend /*CGAL_KERNEL_MEDIUM_INLINE*/ A operator+ <> (const A&, const A&); public: A() : a() {} A(T i) : a(i) {} // KILL int START A(int i) : a(i) {} // KILL int END // SPECIALIZE_MEMBERS(int double) START A& operator +=(const T& i) { a += (T)i; return *this; } // SPECIALIZE_MEMBERS(int double) END static T R_; }; // SPECIALIZE_CLASS(T,int) END template T A::R_ = T(0); #ifdef INITFORGNU template <> int A::R_ = 0; #endif // symmetric op+ template A operator+ (const A& a1, const A& a2) { return A(a1.a+a2.a); } // SPECIALIZE_FUNCTION(T,int) START // asymmetric op+ template A operator+ (const T& num, const A& a2) { return (A(num) + a2); } // SPECIALIZE_FUNCTION(T,int) END typedef leda_integer NT; int main() { A ad1(1),ad2(2), ad3; ad3 = ad1 + ad2; ad3 = 3 + ad1; A::R_ = 3; A ai1(1), ai2(2), ai3; ai3 = ai1 + ai2; ai3 = 3 + ai1; A::R_ = 3; return 0; } @ \end{ignoreindiss} %KILLSTART DISS REP \bibliographystyle{alpha} \bibliography{geo_mod,diss} \end{document} %KILLEND DISS REP