#ifndef _KNOTKIT_ALGEBRA_POLYNOMIAL_H #define _KNOTKIT_ALGEBRA_POLYNOMIAL_H /* univariate polynomial in a single variable `x' with coefficients in T. */ template class polynomial { private: map coeffs; polynomial (const map &coeffs_) : coeffs(coeffs_) { } public: polynomial () { } polynomial (int x) { T c (x); if (c != 0) coeffs.push (0, c); } polynomial (T c) { if (c != 0) coeffs.push (0, c); } polynomial (int x, unsigned e) { T c (x); if (c != 0) coeffs.push (e, c); } polynomial (T c, unsigned e) { if (c != 0) coeffs.push (e, c); } polynomial (const polynomial &p) : coeffs(p.coeffs) { } polynomial (copy, const polynomial &p) : coeffs(COPY, p.coeffs) { } polynomial (reader &r) : coeffs(r) { } ~polynomial () { } polynomial &operator = (const polynomial &p) { coeffs = p.coeffs; return *this; } polynomial &operator = (int x) { coeffs = map (); T c (x); if (c != 0) coeffs.push (0, c); return *this; } polynomial &operator = (T c) { coeffs = map (); if (c != 0) coeffs.push (0, c); return *this; } unsigned degree () const; bool is_unit () const; polynomial recip () const { assert (coeffs.card () == 1); pair p = coeffs.head (); assert (p.first == 0); return polynomial (p.second.recip ()); } bool operator == (const polynomial &p) const { #ifndef NDEBUG check (); p.check (); #endif return coeffs == p.coeffs; } bool operator != (const polynomial &p) const { return !operator == (p); } bool operator == (int x) const { T c (x); if (c == 0) return coeffs.is_empty (); else if (coeffs.card () != 1) return 0; else { pair p = coeffs.head (); return p.first == 0 && p.second == c; } } bool operator != (int x) const { return !operator == (x); } polynomial &operator += (polynomial p); polynomial &operator -= (polynomial p); polynomial &operator *= (polynomial p) { return operator = (*this * p); } polynomial &operator *= (T s); // ??? rename polynomial &add_term (T c, unsigned e) { T &c2 = coeffs[e]; c2 += c; if (c2 == 0) coeffs -= e; return *this; } polynomial operator - () const { return polynomial () - *this; } polynomial operator + (polynomial p) const { polynomial r (COPY, *this); r += p; return r; } polynomial operator - (const polynomial &p) const { polynomial r (COPY, *this); r -= p; return r; } polynomial operator * (const polynomial &p) const; bool divides (const polynomial &num) const; // *this | num bool operator | (const polynomial &num) const { return divides (num); } tuple divide_with_remainder (const polynomial &denom) const; polynomial mod (const polynomial &denom) const; polynomial divide_exact (const polynomial &denom) const; polynomial div (const polynomial &denom) const { return divide_exact (denom); } polynomial gcd (const polynomial &b) const; polynomial lcm (const polynomial &b) const; tuple extended_gcd (const polynomial &b) const; #ifndef NDEBUG void check () const; #endif void write_self (writer &w) const { write (w, coeffs); } static void show_ring () { T::show_ring (); printf ("[x]"); } void display_self () const { show_self (); newline (); } void show_self () const; }; template unsigned polynomial::degree () const { return coeffs.tail ().first; } template bool polynomial::is_unit () const { // ??? is_singleton if (coeffs.card () != 1) return 0; pair p = coeffs.head (); return p.first == 0 && p.second.is_unit (); } template polynomial & polynomial::operator += (polynomial p) { for (typename map::const_iter i = p.coeffs; i; i ++) { unsigned e = i.key (); T &c = coeffs[e]; c += i.val (); if (c == 0) coeffs -= e; } return *this; } template polynomial & polynomial::operator -= (polynomial p) { for (typename map::const_iter i = p.coeffs; i; i ++) { unsigned e = i.key (); T &c = coeffs[e]; c -= i.val (); if (c == 0) coeffs -= e; } return *this; } template polynomial & polynomial::operator *= (T s) { if (s == 0) coeffs.clear (); else { for (typename map::iter i = coeffs; i; i ++) i.val () *= s; } } template polynomial polynomial::operator * (const polynomial &p) const { polynomial r; for (typename map::const_iter i = coeffs; i; i ++) for (typename map::const_iter j = p.coeffs; j; j ++) { unsigned e = i.key () + j.key (); T &c = r.coeffs[e]; c += i.val () * j.val (); if (c == 0) r.coeffs -= e; } return r; } template tuple, polynomial > polynomial::divide_with_remainder (const polynomial &d) const { // num = *this assert (d != 0); polynomial r (COPY, *this); polynomial q; pair d_leading_term = d.coeffs.tail (); for (;;) { if (r == 0) break; pair r_leading_term = r.coeffs.tail (); if (r_leading_term.first < d_leading_term.first) break; polynomial m (r_leading_term.second / d_leading_term.second, r_leading_term.first - d_leading_term.first); r -= m * d; q += m; } assert (r == 0 || r.degree () < d.degree ()); // assert (*this == q*d + r); return make_tuple (q, r); } template polynomial polynomial::mod (const polynomial &denom) const { polynomial q, r; tie (q, r) = divide_with_remainder (denom); return r; } template bool polynomial::divides (const polynomial &num) const { // denom = *this polynomial q, r; tie (q, r) = num.divide_with_remainder (*this); return r == 0; } template polynomial polynomial::divide_exact (const polynomial &denom) const { polynomial q, r; tie (q, r) = divide_with_remainder (denom); assert (r == 0); return q; } template polynomial polynomial::gcd (const polynomial &b) const { polynomial a = *this; while (b != 0) { polynomial r; tie (ignore, r) = a.divide_with_remainder (b); a = b; b = r; } return a; } template polynomial polynomial::lcm (const polynomial &b) const { // a = *this return divide_exact (gcd (b)) * b; } template tuple, polynomial, polynomial > polynomial::extended_gcd (const polynomial &b) const { // a = *this if (b == 0) return make_tuple (*this, polynomial (1), polynomial (0)); polynomial q, r; tie (q, r) = divide_with_remainder (b); if (r == 0) return make_tuple (b, polynomial (0), polynomial (1)); else { polynomial d, s, t; tie (d, s, t) = b.extended_gcd (r); polynomial s2 = t, t2 = s - t*q; return make_tuple (d, s2, t2); } } #ifndef NDEBUG template void polynomial::check () const { for (typename map::const_iter i = coeffs; i; i ++) assert (i.val () != 0); } #endif template void polynomial::show_self () const { unsigned first = 1; for (typename map::const_iter i = coeffs; i; i ++) { unsigned e = i.key (); T c = i.val (); assert (c != 0); if (first) first = 0; else printf (" + "); if (e == 0 && c == 1) printf ("1"); else if (e == 0) show (c); else if (c == 1) { if (e == 1) printf ("x"); else printf ("x^%d", e); } else { show (c); if (e == 1) printf ("*x"); else printf ("*x^%d", e); } } if (first) printf ("0"); } template<> class polynomial { private: set coeffs; explicit polynomial (const set &coeffs_) : coeffs(coeffs_) { } public: polynomial () { } polynomial (int x) { Z2 c (x); if (c != 0) coeffs.push (0); } polynomial (Z2 c) { if (c != 0) coeffs.push (0); } polynomial (Z2 c, unsigned e) { if (c != 0) coeffs.push (e); } polynomial (const polynomial &p) : coeffs(p.coeffs) { } polynomial (copy, const polynomial &p) : coeffs(COPY, p.coeffs) { } ~polynomial () { } polynomial &operator = (const polynomial &p) { coeffs = p.coeffs; return *this; } polynomial &operator = (int x) { coeffs = set (); Z2 c (x); if (c != 0) coeffs.push (0); return *this; } polynomial &operator = (Z2 c) { coeffs = set (); if (c == 1) coeffs.push (0); return *this; } bool operator == (const polynomial &p) const { return coeffs == p.coeffs; } bool operator != (const polynomial &p) const { return !operator == (p); } bool operator == (int x) const { Z2 c (x); if (c == 0) return coeffs.is_empty (); else return coeffs.card () == 1 && coeffs.head () == 0; } bool operator != (int x) const { return !operator == (x); } unsigned degree () const { return coeffs.tail (); } bool is_unit () const { return coeffs.card () == 1 && coeffs.head () == 0; } polynomial recip () const { assert (is_unit ()); return 1; } polynomial &operator += (polynomial p) { coeffs ^= p.coeffs; return *this; } polynomial &operator -= (polynomial p) { coeffs ^= p.coeffs; return *this; } polynomial &operator *= (polynomial p) { return operator = (*this * p); } polynomial &operator *= (Z2 s) { if (s == 0) coeffs.clear (); return *this; } polynomial &add_term (Z2 c, unsigned e) { if (c == 1) coeffs.toggle (e); return *this; } polynomial operator - () const { return polynomial (COPY, *this); }; polynomial operator + (polynomial p) const { polynomial r (COPY, *this); r += p; return r; } polynomial operator - (polynomial p) const { polynomial r (COPY, *this); r -= p; return r; } polynomial operator * (polynomial p) const; pair divide_with_remainder (polynomial d) const; polynomial mod (polynomial d) const; bool divides (polynomial d) const; polynomial divide_exact (polynomial d) const; polynomial gcd (polynomial b) const; static void show_ring () { printf ("Z2[x]"); } void display_self () const { show_self (); newline (); } void show_self () const; }; #endif // _KNOTKIT_ALGEBRA_POLYNOMIAL_H