// Copyright (c) 2006-2009 Inria Lorraine (France). All rights reserved.
//
// This file is part of CGAL (www.cgal.org); you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; version 2.1 of the License.
// See the file LICENSE.LGPL distributed with CGAL.
//
// Licensees holding a valid commercial license may use this file in
// accordance with the commercial license agreement provided with the software.
//
// 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: svn+ssh://scm.gforge.inria.fr/svn/cgal/trunk/Algebraic_kernel_d/include/CGAL/RS/polynomial_1_member.h $
// $Id: polynomial_1_member.h 59002 2010-10-04 11:00:27Z lrineau $
//
// Author: Luis Peņaranda <luis.penaranda@loria.fr>

#ifndef CGAL_RS_POLYNOMIAL_1_MEMBER_H
#define CGAL_RS_POLYNOMIAL_1_MEMBER_H

namespace CGAL{

//////////////////////
// private functions

inline
void RS_polynomial_1::create_storage(int size){
        _coef=(mpz_t*)(*_allocf)(sizeof(mpz_t)*size);
        _capacity=size;
}

inline
void RS_polynomial_1::free_storage(){
        void *(*af)(size_t);
        void *(*rf)(void*,size_t,size_t);
        void (*ff)(void*,size_t);
        mp_get_memory_functions(&af,&rf,&ff);
        mp_set_memory_functions(_allocf,_reallocf,_freef);
        for(int i=0;i<_degree+1;++i)
                mpz_clear(coef(i));
        mp_set_memory_functions(af,rf,ff);
        (*_freef)(_coef,sizeof(mpz_t)*_capacity);
        _capacity=0;
}

inline
void RS_polynomial_1::fetch_gmp_functions(){
        mp_get_memory_functions(&_allocf,&_reallocf,&_freef);
}

//////////////////////
// private functions

// dangerous function! this will erase the coefficients if the new coefficients
// don't fit in the previously allocated space; otherwise, it will set to zero
// the new coefficients
inline
void RS_polynomial_1::set_degree(int d){
        if(d+1>_capacity){
                free_storage();
                _degree=d;
                ++d;
                create_storage(d);
                for(int i=0;i<=_degree;++i)
                        mpz_init(coef(i));
        }else{
                _degree=d;
                for(int i=_degree+1;i<=d;++i)
                        mpz_init(coef(i));
        }
        return;
}

inline
void RS_polynomial_1::force_degree(int n){
        _degree=n;
}

// to change the storage capacity of a polynomial object
inline
int RS_polynomial_1::resize(int newcap){
        if(newcap<=_capacity)
                return -1;
        int i;
        mpz_t *newcoef=(mpz_t*)(*_allocf)(newcap*sizeof(mpz_t));
        for(i=0;i<=_degree;++i){
                mpz_init_set(newcoef[i],_coef[i]);
                mpz_clear(_coef[i]);
        }
        for(i=_degree+1;i<newcap;++i)
                mpz_init(newcoef[i]);
        (*_freef)(_coef,sizeof(mpz_t)*_capacity);
        _coef=newcoef;
        _capacity=newcap;
        return newcap;
}

inline
void RS_polynomial_1::set_coef(int pow_x,mpz_srcptr z){
        mpz_set(_coef[pow_x],z);
}

inline
void RS_polynomial_1::set_coef(int pow_x,const CGAL::Gmpz &z){
        mpz_set(_coef[pow_x],z.mpz());
}

inline
void RS_polynomial_1::set_coef_ui(int pow_x,unsigned long z){
        mpz_set_ui(_coef[pow_x],z);
}

inline
void RS_polynomial_1::set_coef_si(int pow_x,long z){
        mpz_set_si(_coef[pow_x],z);
}

inline
int RS_polynomial_1::get_degree()const{
        while(!mpz_sgn(coef(_degree))&&_degree)
                --_degree;
        return _degree;
}

inline
int RS_polynomial_1::get_degree_static()const{
        return _degree;
}

inline
bool RS_polynomial_1::has_sfpart()const{
        return (_is_sf?true:(_sfpart.get()!=NULL?true:false));
}

inline
const RS_polynomial_1& RS_polynomial_1::sfpart()const{
        if(_is_sf)
                return *this;
        else
                return *_sfpart;
}

inline
void RS_polynomial_1::set_sfpart(RS_polynomial_1 *s)const{
        _is_sf=false;
        _sfpart=polyptr(s);
}

inline
void RS_polynomial_1::set_sfpart(const polyptr &s)const{
        _is_sf=false;
        _sfpart=s;
}

inline
void RS_polynomial_1::set_sf()const{
        _is_sf=true;
}

inline
bool RS_polynomial_1::has_sqfr()const{
        return (_sqfr.get()!=NULL?true:false);
}

inline
sqfrvec& RS_polynomial_1::sqfr()const{
        return *_sqfr;
}

inline
void RS_polynomial_1::set_sqfr(sqfrvec *s)const{
        _sqfr=sqfrptr(s);
}

inline
void RS_polynomial_1::set_sqfr(const sqfrptr &s)const{
        _sqfr=s;
}

inline
mpz_ptr RS_polynomial_1::leading_coefficient()const{
        return(_coef[get_degree()]);
}

// gets the power of the lowest coefficient non-zero monomial
inline
int RS_polynomial_1::first_non_zero()const{
        int i=0;
        while(i<=_degree)
                if(mpz_sgn(coef(i)))
                        return i;
                else
                        ++i;
        return -1;      // if all the coefficients are zero
}

inline
mpz_t* RS_polynomial_1::get_coefs()const{
        return _coef;
}

inline
mpz_ptr RS_polynomial_1::coef(int i)const{
        return _coef[i];
}

inline
RS_polynomial_1& RS_polynomial_1::derive()const{
        int d=get_degree();
        RS_polynomial_1 *derivative=new RS_polynomial_1(d-1);
        for(int x=0;x<d;++x)
                mpz_mul_si(derivative->coef(x),coef(x+1),x+1);
        return *derivative;
}

inline
RS_polynomial_1& RS_polynomial_1::minusx()const{
        int d=get_degree();
        RS_polynomial_1 *mx=new RS_polynomial_1(d);
        for(int x=0;x<=d;++x){
                if(x%2==1)
                        mpz_set(mx->coef(x),coef(x));
                else
                        mpz_neg(mx->coef(x),coef(x));
        }
        return *mx;
}

// This function copies into lb a number which is less than all roots. It just
// looks for the coefficient with the greatest absolute value. This is useful
// when evaluating curves in the infinite. We can refine this, but it makes no
// sense since it will be slower.
inline
void RS_polynomial_1::get_lower_bound(mpfr_ptr lb)const{
        int d=get_degree();
        mpz_t temp;
        mpz_init(temp);
        mpz_abs(temp,leading_coefficient());
        for(int i=0;i<d;++i)
                if(mpz_cmpabs(coef(i),temp)>0)
                        mpz_abs(temp,coef(i));
        mpz_add_ui(temp,temp,1);
        mpz_neg(temp,temp);
        mpfr_set_z(lb,temp,GMP_RNDD);
        mpz_clear(temp);
}

// the same that above, but the copied value is the upper bound of the roots
inline
void RS_polynomial_1::get_upper_bound(mpfr_ptr ub)const{
        int d=get_degree();
        mpz_t temp;
        mpz_init(temp);
        mpz_abs(temp,leading_coefficient());
        for(int i=0;i<d;++i)
                if(mpz_cmpabs(coef(i),temp)>0)
                        mpz_abs(temp,coef(i));
        mpz_add_ui(temp,temp,1);
        mpfr_set_z(ub,temp,GMP_RNDU);
        mpz_clear(temp);
}

// returns this polynomial times c*x^p
inline
RS_polynomial_1
RS_polynomial_1::times_monomial(mpz_srcptr c,int p)const{
        int dr=get_degree()+p;
        RS_polynomial_1 r(dr);
        for(int i=p;i<=dr;++i)
                mpz_mul(r.coef(i),coef(i-p),c);
        return r;
}

} // namespace CGAL

#endif  // CGAL_RS_POLYNOMIAL_1_MEMBER_H

// vim: tabstop=8: softtabstop=8: smarttab: shiftwidth=8: expandtab
