// Copyright (c) 2006-2013 INRIA Nancy-Grand Est (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; either version 3 of the License,
// or (at your option) any later version.

// 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$
// $Id$
// SPDX-License-Identifier: LGPL-3.0+
//
// Author: Luis Peñaranda <luis.penaranda@gmx.com>

#ifndef CGAL_RS_ALGEBRAIC_1_H
#define CGAL_RS_ALGEBRAIC_1_H

#include <boost/operators.hpp>
#include <CGAL/Real_embeddable_traits.h>
#include <CGAL/Gmpq.h>
#include <iostream>

namespace CGAL{
namespace RS_AK1{

// This class represents the simplest algebraic number one can think about.
// One algebraic number is represented by the polynomial of which it is
// root and the two endpoints of an interval that contains it, and no other
// root. Polynomial type and bound type are the first two template
// parameters.
//
// The third template parameter is a refiner, a function object that
// receives the polynomial and the bounds defining an algebraic number and
// an integer p, and modifies the two bounds until the difference between
// the two bounds is less than x*2^(-p), where x is the value of the
// represented algebraic number. The signature of a refiner must be:
//      void
//      Refiner_()(const Polynomial_&,Bound_&,Bound_&,int p);
//
// The fourth template argument is a comparator, a function object that
// receives the polynomials and bounds defining two algebraic numbres and
// just compares them, returning a CGAL::Comparison_result. The signature
// of a comparator must be:
//      CGAL::Comparison_result
//      Comparator_()(const Polynomial_&,Bound_&,Bound_&,
//                    const Polynomial_&,Bound_&,Bound_&);
// The comparator can modify the bounds, with the condition that the
// algebraic numbers remain consistent (one and only one root on each
// interval).

template <class Polynomial_,
          class Bound_,
          class Refiner_,
          class Comparator_,
          class Ptraits_>
class Algebraic_1:
boost::totally_ordered<Algebraic_1<Polynomial_,
                                   Bound_,
                                   Refiner_,
                                   Comparator_,
                                   Ptraits_>,
                       double>{
        protected:
        typedef Polynomial_                             Polynomial;
        typedef Bound_                                  Bound;
        typedef Refiner_                                Refiner;
        typedef Comparator_                             Comparator;
        typedef Ptraits_                                Ptraits;
        typedef typename Ptraits::Coefficient_type      Coefficient;
        typedef typename Ptraits::Scale                 Scale;
        typedef Algebraic_1<Polynomial,
                            Bound,
                            Refiner,
                            Comparator,
                            Ptraits>
                                                        Algebraic;

        Polynomial pol;
        mutable Bound left,right;

        public:
        Algebraic_1(){};
        Algebraic_1(const Polynomial &p,
                    const Bound &l,
                    const Bound &r):pol(p),left(l),right(r){
                CGAL_assertion(l<=r);
        }
        Algebraic_1(const Algebraic &a):
        pol(a.pol),left(a.left),right(a.right){}
        // XXX: This assumes that Gmpq is constructible from bound type and
        // that polynomial coefficient type is constructible from mpz_t.
        Algebraic_1(const Bound &b):left(b),right(b){
                typedef typename Ptraits::Shift         shift;
                Gmpq q(b);
                pol=Coefficient(mpq_denref(q.mpq()))*
                        shift()(Polynomial(1),1,0)-
                        Coefficient(mpq_numref(q.mpq()));
                CGAL_assertion(left==right&&left==b);
        }
        // XXX: This implementation assumes that the bound type is Gmpfr
        // and that T can be exactly converted to Gmpq. This constructor
        // can handle types such as int, unsigned and long. 
        template <class T>
        Algebraic_1(const T &t){
                typedef typename Ptraits::Shift         shift;
                CGAL::Gmpq q(t);
                pol=Coefficient(mpq_denref(q.mpq()))*
                    shift()(Polynomial(1),1,0)-
                        Coefficient(mpq_numref(q.mpq()));
                left=Bound(t,std::round_toward_neg_infinity);
                right=Bound(t,std::round_toward_infinity);
                CGAL_assertion(left<=t&&right>=t);
        }
        // XXX: This constructor assumes the bound is a Gmpfr.
        Algebraic_1(const CGAL::Gmpq &q){
                typedef typename Ptraits::Shift         shift;
                pol=Coefficient(mpq_denref(q.mpq()))*
                    shift()(Polynomial(1),1,0)-
                        Coefficient(mpq_numref(q.mpq()));
                left=Bound();
                right=Bound();
                mpfr_t b;
                mpfr_init(b);
                mpfr_set_q(b,q.mpq(),GMP_RNDD);
                mpfr_swap(b,left.fr());
                mpfr_set_q(b,q.mpq(),GMP_RNDU);
                mpfr_swap(b,right.fr());
                mpfr_clear(b);
                CGAL_assertion(left<=q&&right>=q);
        }
        ~Algebraic_1(){}

        Algebraic_1& operator=(const Algebraic &a){
                pol=a.get_pol();
                left=a.get_left();
                right=a.get_right();
                return *this;
        }

        Polynomial get_pol()const{return pol;}
        Bound& get_left()const{return left;}
        Bound& get_right()const{return right;}

        Algebraic operator-()const{
                return Algebraic(Scale()(get_pol(),Coefficient(-1)),
                                 -right,
                                 -left);
        }

#define CGAL_RS_COMPARE_ALGEBRAIC(_a) \
        (Comparator()(get_pol(),get_left(),get_right(), \
                      (_a).get_pol(),(_a).get_left(),(_a).get_right()))

        Comparison_result compare(Algebraic a)const{
                return CGAL_RS_COMPARE_ALGEBRAIC(a);
        };

#define CGAL_RS_COMPARE_ALGEBRAIC_TYPE(_t) \
        bool operator<(_t t)const \
        {Algebraic a(t);return CGAL_RS_COMPARE_ALGEBRAIC(a)==CGAL::SMALLER;} \
        bool operator>(_t t)const \
        {Algebraic a(t);return CGAL_RS_COMPARE_ALGEBRAIC(a)==CGAL::LARGER;} \
        bool operator==(_t t)const \
        {Algebraic a(t);return CGAL_RS_COMPARE_ALGEBRAIC(a)==CGAL::EQUAL;}

        bool operator==(Algebraic a)const
                {return CGAL_RS_COMPARE_ALGEBRAIC(a)==CGAL::EQUAL;}
        bool operator!=(Algebraic a)const
                {return CGAL_RS_COMPARE_ALGEBRAIC(a)!=CGAL::EQUAL;}
        bool operator<(Algebraic a)const
                {return CGAL_RS_COMPARE_ALGEBRAIC(a)==CGAL::SMALLER;}
        bool operator<=(Algebraic a)const
                {return CGAL_RS_COMPARE_ALGEBRAIC(a)!=CGAL::LARGER;}
        bool operator>(Algebraic a)const
                {return CGAL_RS_COMPARE_ALGEBRAIC(a)==CGAL::LARGER;}
        bool operator>=(Algebraic a)const
                {return CGAL_RS_COMPARE_ALGEBRAIC(a)!=CGAL::SMALLER;}

        CGAL_RS_COMPARE_ALGEBRAIC_TYPE(double)

#undef CGAL_RS_COMPARE_ALGEBRAIC_TYPE
#undef CGAL_RS_COMPARE_ALGEBRAIC

#ifdef IEEE_DBL_MANT_DIG
#define CGAL_RS_DBL_PREC        IEEE_DBL_MANT_DIG
#else
#define CGAL_RS_DBL_PREC        53
#endif
        // This function is const because left and right are mutable.
        double to_double()const{
                typedef Real_embeddable_traits<Bound>                   RT;
                typedef typename RT::To_double                          TD;
                Refiner()(pol,left,right,CGAL_RS_DBL_PREC);
                CGAL_assertion(TD()(left)==TD()(right));
                return TD()(left);
        }
        std::pair<double,double> to_interval()const{
                typedef Real_embeddable_traits<Bound>                   RT;
                typedef typename RT::To_interval                        TI;
                return std::make_pair(TI()(get_left()).first,
                                      TI()(get_right()).second);
        }
#undef CGAL_RS_DBL_PREC

        void set_left(const Bound &l)const{
                left=l;
        }
        void set_right(const Bound &r)const{
                right=r;
        }
        void set_pol(const Polynomial &p){
                pol=p;
        }

}; // class Algebraic_1

} // namespace RS_AK1

// We define Algebraic_1 as real-embeddable
template <class Polynomial_,
          class Bound_,
          class Refiner_,
          class Comparator_,
          class Ptraits_>
class Real_embeddable_traits<RS_AK1::Algebraic_1<Polynomial_,
                                                 Bound_,
                                                 Refiner_,
                                                 Comparator_,
                                                 Ptraits_> >:
public INTERN_RET::Real_embeddable_traits_base<
                RS_AK1::Algebraic_1<Polynomial_,
                                    Bound_,
                                    Refiner_,
                                    Comparator_,
                                    Ptraits_>,
                CGAL::Tag_true>{
        typedef Polynomial_                             P;
        typedef Bound_                                  B;
        typedef Refiner_                                R;
        typedef Comparator_                             C;
        typedef Ptraits_                                T;

        public:

        typedef RS_AK1::Algebraic_1<P,B,R,C,T> Type;
        typedef CGAL::Tag_true                          Is_real_embeddable;
        typedef bool                                    Boolean;
        typedef CGAL::Sign                              Sign;
        typedef CGAL::Comparison_result                 Comparison_result;

        typedef INTERN_RET::Real_embeddable_traits_base<Type,CGAL::Tag_true>
                                                        Base;
        typedef typename Base::Compare                  Compare;

        class Sgn:public CGAL::cpp98::unary_function<Type,CGAL::Sign>{
                public:
                CGAL::Sign operator()(const Type &a)const{
                        return Compare()(a,Type(0));
                }
        };

        class To_double:public CGAL::cpp98::unary_function<Type,double>{
                public:
                double operator()(const Type &a)const{return a.to_double();}
        };

        class To_interval:
        public CGAL::cpp98::unary_function<Type,std::pair<double,double> >{
                public:
                std::pair<double,double> operator()(const Type &a)const{
                        return a.to_interval();
                }
        };

        class Is_zero:public CGAL::cpp98::unary_function<Type,Boolean>{
                public:
                bool operator()(const Type &a)const{
                        return Sgn()(a)==CGAL::ZERO;
                }
        };

        class Is_finite:public CGAL::cpp98::unary_function<Type,Boolean>{
                public:
                bool operator()(const Type&)const{return true;}
        };

        class Abs:public CGAL::cpp98::unary_function<Type,Type>{
                public:
                Type operator()(const Type &a)const{
                        return Sgn()(a)==CGAL::NEGATIVE?-a:a;
                }
        };
};

template <class P,class B,class R,class C,class T>
inline
RS_AK1::Algebraic_1<P,B,R,C,T> min
BOOST_PREVENT_MACRO_SUBSTITUTION(RS_AK1::Algebraic_1<P,B,R,C,T> a,
                                 RS_AK1::Algebraic_1<P,B,R,C,T> b){
        return(a<b?a:b);
}

template <class P,class B,class R,class C,class T>
inline
RS_AK1::Algebraic_1<P,B,R,C,T> max
BOOST_PREVENT_MACRO_SUBSTITUTION(RS_AK1::Algebraic_1<P,B,R,C,T> a,
                                 RS_AK1::Algebraic_1<P,B,R,C,T> b){
        return(a>b?a:b);
}

template <class P,class B,class R,class C,class T>
inline
std::ostream& operator<<(std::ostream &o,
                         const RS_AK1::Algebraic_1<P,B,R,C,T> &a){
        return(o<<'['<<a.get_pol()<<','<<
               a.get_left()<<','<<
               a.get_right()<<']');
}

// XXX: This function works, but it will be nice to rewrite it cleanly.
template <class P,class B,class R,class C,class T>
inline
std::istream& operator>>(std::istream &i,
                         RS_AK1::Algebraic_1<P,B,R,C,T> &a){
        std::istream::int_type c;
        P pol;
        B lb,rb;
        c=i.get();
        if(c!='['){
                CGAL_error_msg("error reading istream, \'[\' expected");
                return i;
        }
        i>>pol;
        c=i.get();
        if(c!=','){
                CGAL_error_msg("error reading istream, \',\' expected");
                return i;
        }
        i>>lb;
        c=i.get();
        if(c!=','){
                CGAL_error_msg("error reading istream, \',\' expected");
                return i;
        }
        i>>rb;
        c=i.get();
        if(c!=']'){
                CGAL_error_msg("error reading istream, \']\' expected");
                return i;
        }
        a=RS_AK1::Algebraic_1<P,B,R,C,T>(pol,lb,rb);
        return i;
}

} // namespace CGAL

#endif // CGAL_RS_ALGEBRAIC_1_H
