You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
502 lines
11 KiB
502 lines
11 KiB
1 year ago
|
#ifndef _POLY_H_
|
||
|
#define _POLY_H_
|
||
|
|
||
|
#include <boost/array.hpp>
|
||
|
#include <algorithm>
|
||
|
#include <functional>
|
||
|
#include <iostream>
|
||
|
#include <libqi/rpl/bigint.h>
|
||
|
#include <libqi/rpl/rpl.h>
|
||
|
|
||
|
//! \file polynomial.h
|
||
|
|
||
|
namespace rpl {
|
||
|
|
||
|
//! univariate polynomial class
|
||
|
/*! that structure implements the univariate polynomial class
|
||
|
* all algorithms are under naive form (it is just a prototype)
|
||
|
* we use a array( i.e. compile-time bounded size array) for the storage
|
||
|
* we assume that maximal degree is 8!
|
||
|
*/
|
||
|
template <class T>
|
||
|
class polynomial {
|
||
|
public:
|
||
|
// members
|
||
|
typedef typename boost::array<T,9> storage_type;
|
||
|
|
||
|
storage_type coeff;
|
||
|
|
||
|
rpl_size_t deg;
|
||
|
|
||
|
// constructors
|
||
|
polynomial() { this->assign_zero(); }
|
||
|
|
||
|
polynomial(const T &x);
|
||
|
|
||
|
polynomial(const T *x, int d);
|
||
|
|
||
|
polynomial(const polynomial <T> &f);
|
||
|
|
||
|
// destructor
|
||
|
~polynomial() {}
|
||
|
|
||
|
// copy assignment
|
||
|
polynomial <T> & operator = (const polynomial <T> &a)
|
||
|
{
|
||
|
if (this != &a) // Beware of self-assignment
|
||
|
this->assign(a);
|
||
|
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
polynomial <T> & operator = (const bigint &a)
|
||
|
{
|
||
|
this->assign(a);
|
||
|
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
// assignments
|
||
|
void assign_zero();
|
||
|
|
||
|
void assign_one();
|
||
|
|
||
|
void assign_x();
|
||
|
|
||
|
void assign_x2();
|
||
|
|
||
|
void assign(const T &x);
|
||
|
|
||
|
void assign(const polynomial <T> &f);
|
||
|
|
||
|
// basic methods
|
||
|
void remove_leading_zeros();
|
||
|
|
||
|
// access methods
|
||
|
const rpl_size_t degree() const { return this->deg; }
|
||
|
|
||
|
rpl_size_t degree() { return this->deg; }
|
||
|
|
||
|
void set_degree(rpl_size_t n) { this->deg = n; }
|
||
|
|
||
|
T & operator[] (rpl_size_t i) { return this->coeff[i]; }
|
||
|
|
||
|
const T operator[] (rpl_size_t i) const { return this->coeff[i]; }
|
||
|
|
||
|
T lead_coeff() const;
|
||
|
|
||
|
T const_term() const { return this->coeff[0]; }
|
||
|
|
||
|
// binary operations as functions
|
||
|
void add(const polynomial <T> &g, const polynomial <T> &h);
|
||
|
|
||
|
void subtract(const polynomial <T> &g, const polynomial <T> &h);
|
||
|
|
||
|
void multiply(const polynomial <T> &g, const polynomial <T> &h);
|
||
|
|
||
|
void power(const polynomial <T> &g, rpl_size_t e);
|
||
|
|
||
|
void negate(const polynomial<T> &x);
|
||
|
|
||
|
// comparisons
|
||
|
bool is_zero () const;
|
||
|
|
||
|
bool is_one () const;
|
||
|
};
|
||
|
|
||
|
//////////////////////////////////////// deported constructors
|
||
|
|
||
|
template <typename T>
|
||
|
polynomial<T>::polynomial(const T & x) {
|
||
|
this->assign(x);
|
||
|
}
|
||
|
|
||
|
// constructor from a vector
|
||
|
template <typename T>
|
||
|
polynomial<T>::polynomial(const T *x, int d) {
|
||
|
std::copy(x, x+ d+ 1, this->coeff.begin());
|
||
|
this->deg = d;
|
||
|
remove_leading_zeros();
|
||
|
}
|
||
|
|
||
|
template <typename T>
|
||
|
polynomial<T>::polynomial(const polynomial<T> &f) {
|
||
|
this->assign(f);
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////// deported assignments
|
||
|
|
||
|
template <class T>
|
||
|
void polynomial<T>::assign_zero() {
|
||
|
this->deg = -1;
|
||
|
std::for_each(this->coeff.begin(),this->coeff.end(),std::mem_fun_ref(&T::assign_zero));
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
void polynomial<T>::assign_one() {
|
||
|
this->assign_zero();
|
||
|
this->coeff[0].assign_one();
|
||
|
this->deg = 0;
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
void polynomial<T>::assign_x() {
|
||
|
this->assign_zero();
|
||
|
this->coeff[1].assign_one();
|
||
|
this->deg = 1;
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
void polynomial<T>::assign_x2() {
|
||
|
this->assign_zero();
|
||
|
this->coeff[2].assign_one();
|
||
|
this->deg = 2;
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
void polynomial<T>::assign(const T & x) {
|
||
|
this->coeff[0].assign(x);
|
||
|
if (x == 0)
|
||
|
this->deg = -1;
|
||
|
else
|
||
|
this->deg = 0;
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
void polynomial<T>::assign(const polynomial <T> &f) {
|
||
|
this->coeff = f.coeff;
|
||
|
this->deg = f.deg;
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////// deported basic methods
|
||
|
|
||
|
// Checks if some coefficients have become zero after an operation, and adjusts degree
|
||
|
template <class T>
|
||
|
void polynomial <T>::remove_leading_zeros() {
|
||
|
// nothing to do for zero polynomials
|
||
|
if (this->deg < 0) {
|
||
|
// check that all coeffs are nul
|
||
|
assert (find_if(this->coeff.begin(), this->coeff.end(), std::mem_fun_ref(&T::is_zero)) != this->coeff.end());
|
||
|
return ;
|
||
|
}
|
||
|
typename storage_type::const_iterator i = this->coeff.begin() + this->deg;
|
||
|
while ( this->deg >= 0 && i->is_zero() ) {
|
||
|
this->deg--; i--;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////// deported access methods
|
||
|
|
||
|
// First non zero coeff of polynomial
|
||
|
template <class T>
|
||
|
T polynomial <T>::lead_coeff() const
|
||
|
{
|
||
|
T tmp(0);
|
||
|
|
||
|
for (rpl_size_t i = this->degree(); i >= 0; i--)
|
||
|
if (!this->coeff[i].is_zero())
|
||
|
{
|
||
|
tmp = this->coeff[i];
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return tmp;
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////// deported operations
|
||
|
|
||
|
//////////////////////// divide
|
||
|
|
||
|
// Divide the coefficients of a by b and store in c
|
||
|
template <class T>
|
||
|
void divide(polynomial <T> &c, const polynomial <T> &a, const T &b)
|
||
|
{
|
||
|
assert (b != 0);
|
||
|
c.deg = a.deg;
|
||
|
for (rpl_size_t i = 0; i <= c.deg; i++)
|
||
|
div_exact(c.coeff[i], a.coeff[i], b);
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
inline polynomial <T> operator/ (const polynomial <T> &a, const T &b) {
|
||
|
polynomial <T> res;
|
||
|
divide(res, a, b);
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
// Pseudo-division algorithm
|
||
|
// finds (q,r) such that d^{m-n+1} a = b * q + r, where d
|
||
|
// is the leading coefficient of b, m = deg(a), n = deg(b)
|
||
|
template <class T>
|
||
|
void div_rem(polynomial <T> &q, polynomial <T> &r,
|
||
|
const polynomial <T> &a, const polynomial <T> &b) {
|
||
|
if (a.degree() < b.degree())
|
||
|
{
|
||
|
q.assign_zero();
|
||
|
r = a;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
rpl_size_t m = a.deg;
|
||
|
rpl_size_t n = b.deg;
|
||
|
rpl_size_t e = m - n +1;
|
||
|
assert(!b.is_zero());
|
||
|
|
||
|
// b is a constant
|
||
|
if (b.deg == 0) {
|
||
|
assert(!b.coeff[0].is_zero());
|
||
|
bigint expo;
|
||
|
power(expo, b.lead_coeff(), m);
|
||
|
q.multiply(a, expo);
|
||
|
r.assign_zero();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
r.assign(a);
|
||
|
q.assign_zero();
|
||
|
T d = b.lead_coeff();
|
||
|
|
||
|
// non-optimized algorithm!
|
||
|
while (r.deg >= b.deg) {
|
||
|
// s <- l(r) * x^{deg(r)-deg(b)}
|
||
|
polynomial <T> s;
|
||
|
s.coeff[r.deg-b.deg].assign(r.lead_coeff());
|
||
|
s.deg = r.deg-b.deg;
|
||
|
// q <- d * q + s
|
||
|
q.multiply(q, d);
|
||
|
q.add(q,s);
|
||
|
// r <- d * r - s * b
|
||
|
r.multiply(r, d);
|
||
|
polynomial <T> tmp;
|
||
|
tmp.multiply(s, b);
|
||
|
r.subtract(r, tmp);
|
||
|
e--;
|
||
|
}
|
||
|
|
||
|
T ti_cul;
|
||
|
power(ti_cul, d, e);
|
||
|
q.multiply(q, ti_cul);
|
||
|
r.multiply(r, ti_cul);
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
inline void divide(polynomial <T> &q, const polynomial <T> &a, const polynomial <T> &b) {
|
||
|
polynomial <T> r;
|
||
|
div_rem(q, r, a, b);
|
||
|
}
|
||
|
|
||
|
//////////////////////// multiply
|
||
|
|
||
|
// Multiply the coefficients of a by b and store in c
|
||
|
template <class T>
|
||
|
void multiply(polynomial <T> &c, const polynomial <T> &a, const T &b)
|
||
|
{
|
||
|
if (b.is_zero())
|
||
|
c.assign_zero();
|
||
|
else
|
||
|
{
|
||
|
c.deg = a.deg;
|
||
|
for (rpl_size_t i = 0; i <= c.deg; i++)
|
||
|
multiply(c.coeff[i], a.coeff[i], b);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
polynomial <T> operator* (const polynomial <T> &f, const T &g) {
|
||
|
polynomial <T> res;
|
||
|
multiply(res, f, g);
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
polynomial <T> operator* (const T &f, const polynomial <T> &g) {
|
||
|
polynomial <T> res;
|
||
|
multiply(res, g, f);
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
// Multiply a by b
|
||
|
template <class T>
|
||
|
void polynomial<T>::multiply(const polynomial <T> &a, const polynomial <T> &b) {
|
||
|
if (a.deg < 0 || b.deg < 0) {
|
||
|
this->assign_zero();
|
||
|
return;
|
||
|
}
|
||
|
this->deg = a.deg + b.deg;
|
||
|
|
||
|
typename polynomial<T>::storage_type tmp;
|
||
|
// implements a naive convolution
|
||
|
for (int i = 0 ; i <= this->deg; ++i) {
|
||
|
tmp[i].assign_zero();
|
||
|
for(int j = 0; j<= i; ++j) {
|
||
|
tmp[i] += a[j] * b[i-j];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
this->coeff = tmp;
|
||
|
remove_leading_zeros();
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
inline void multiply(polynomial <T> &a, const polynomial <T> &b, const polynomial <T> &c) {
|
||
|
a.multiply(b,c);
|
||
|
}
|
||
|
|
||
|
//////////////////////// negate
|
||
|
|
||
|
// Negate a polynomial
|
||
|
template <class T>
|
||
|
void polynomial <T>::negate(const polynomial <T> &h) {
|
||
|
assign(h);
|
||
|
std::for_each(this->coeff.begin(), this->coeff.end(), std::mem_fun_ref(&T::negate));
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
inline void negate(polynomial <T> &a, const polynomial <T> &b) {
|
||
|
a.negate(b);
|
||
|
}
|
||
|
|
||
|
//////////////////////// add
|
||
|
|
||
|
// Add two polynomials
|
||
|
template <class T>
|
||
|
void polynomial <T>::add(const polynomial <T> &g, const polynomial <T> &h) {
|
||
|
if (g.deg < 0) {
|
||
|
this->assign(h);
|
||
|
}
|
||
|
else if (h.deg < 0) {
|
||
|
this->assign(g);
|
||
|
}
|
||
|
else {
|
||
|
std::transform(h.coeff.begin(), h.coeff.end(), g.coeff.begin(), this->coeff.begin(),
|
||
|
std::plus<T>());
|
||
|
this->deg = std::max(h.deg, g.deg);
|
||
|
remove_leading_zeros();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
inline void add(polynomial <T> &a, const polynomial <T> &b, const polynomial <T> &c) {
|
||
|
a.add(b,c);
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
polynomial <T> operator+ (const polynomial<T> & f, const polynomial<T> & g) {
|
||
|
polynomial<T> res;
|
||
|
res.add(f, g);
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
//////////////////////// subtract
|
||
|
|
||
|
// Subtract two polynomials
|
||
|
template <class T>
|
||
|
void polynomial <T>::subtract(const polynomial <T> &g, const polynomial <T> &h) {
|
||
|
std::transform(g.coeff.begin(), g.coeff.end(),
|
||
|
h.coeff.begin(), this->coeff.begin(), std::minus<T>());
|
||
|
this->deg = std::max(h.deg, g.deg);
|
||
|
remove_leading_zeros();
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
inline void subtract(polynomial <T> &a, const polynomial <T> &b, const polynomial <T> &c) {
|
||
|
a.subtract(b,c);
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
polynomial <T> operator- (const polynomial <T> &f, const polynomial <T> &g) {
|
||
|
polynomial<T> res;
|
||
|
res.subtract(f, g);
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
//////////////////////// power
|
||
|
|
||
|
template <class T>
|
||
|
void polynomial <T>::power(const polynomial <T> &g, rpl_size_t e) {
|
||
|
switch(e) {
|
||
|
case 0 : this->assign_one();
|
||
|
break;
|
||
|
case 1 : this->assign(g);
|
||
|
break;
|
||
|
case 2 : this->multiply(g, g);
|
||
|
break;
|
||
|
case 3 : this->multiply(g, g); this->multiply(*this, g);
|
||
|
break;
|
||
|
case 4 : { polynomial <T> tmp; tmp.multiply(g, g); this->multiply(tmp, tmp); }
|
||
|
break;
|
||
|
default : std::cerr << "problem with power : e must belong {0,...,4} "
|
||
|
<< std::endl;
|
||
|
assert(0);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
inline void power(polynomial <T> &a, const polynomial <T> &b, rpl_size_t e) {
|
||
|
a.power(b,e);
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////// deported comparisons
|
||
|
|
||
|
// Is a polynomial zero?
|
||
|
template <typename T>
|
||
|
bool polynomial <T>::is_zero() const {
|
||
|
return ((this->deg == -1) || (this->lead_coeff().is_zero()));
|
||
|
}
|
||
|
|
||
|
// Is a polynomial equal to 1?
|
||
|
template <typename T>
|
||
|
bool polynomial <T>::is_one() const {
|
||
|
return ((this->deg == 0) && (this->lead_coeff().is_one()));
|
||
|
}
|
||
|
|
||
|
// Are two polynomials equal?
|
||
|
template <class T>
|
||
|
bool operator == (const polynomial <T> &f, const polynomial <T> &g) {
|
||
|
return (f-g).is_zero();
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////// pretty printing
|
||
|
|
||
|
template <class T>
|
||
|
std::ostream & operator << (std::ostream &o, const polynomial <T> &f) {
|
||
|
for (int i=f.degree(); i > 0 ; i--) {
|
||
|
if (!f[i].is_zero() ) {
|
||
|
if (i!= f.degree() && f[i].sign() > 0) {
|
||
|
if (!f[i].is_one())
|
||
|
o << "+" << f[i] << "*x^"<< i;
|
||
|
else
|
||
|
o << "+" << "x^"<< i;
|
||
|
}
|
||
|
else {
|
||
|
if (!f[i].is_one())
|
||
|
o << f[i] << "*x^"<< i;
|
||
|
else
|
||
|
o << "x^"<< i;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if ((f.degree() == -1) || (!f[0].is_zero())) {
|
||
|
if (f[0].sign() > 0)
|
||
|
o << "+" ;
|
||
|
o << f[0] ;
|
||
|
}
|
||
|
|
||
|
return o;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////// declarations when T = bigint
|
||
|
////////////////////////////////////// see polynomial.cpp for implementation
|
||
|
|
||
|
bigint cont(const polynomial <bigint> &a);
|
||
|
|
||
|
polynomial <bigint> pp(const polynomial <bigint> &a);
|
||
|
|
||
|
polynomial <bigint> gcd(const polynomial <bigint> &aa,
|
||
|
const polynomial <bigint> &bb);
|
||
|
|
||
|
};
|
||
|
|
||
|
#endif
|