/***************************************************************************
**
**  This file is part of QGpCoreTools.
**
**  This library is free software; 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 2.1 of the License, or (at your option) any later version.
**
**  This file is distributed in the hope that it will be useful, but WITHOUT
**  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
**  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
**  License for more details.
**
**  You should have received a copy of the GNU Lesser General Public
**  License along with this library; if not, write to the Free Software
**  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
**
**  See http://www.geopsy.org for more information.
**
**  Created: 2018-06-28
**  Copyright: 2018-2019
**    Marc Wathelet (ISTerre, Grenoble, France)
**
***************************************************************************/

#ifndef REALMP_H
#define REALMP_H

#ifdef MULTI_PRECISION

#include <QtCore>

#include <mpfr.h>

#include "Global.h"
#include "QGpCoreToolsDLLExport.h"

namespace QGpCoreTools {

  class QGPCORETOOLS_EXPORT RealMP
  {
  public:
    RealMP() {}
    inline RealMP(int nbits);
    inline RealMP(int nbits, double v);
    inline RealMP(double v);
    inline RealMP(const RealMP& o);
    inline ~RealMP();

    inline bool operator==(const RealMP& o) const;
    inline bool operator!=(const RealMP& o) const;
    inline bool operator<(const RealMP& o) const;
    inline bool operator>(const RealMP& o) const;

    inline RealMP& operator=(const RealMP& o);
    inline RealMP& operator+=(const RealMP& o);
    inline RealMP& operator-=(const RealMP& o);
    inline RealMP& operator*=(const RealMP& o);
    inline RealMP& operator/=(const RealMP& o);

    inline RealMP& operator=(double o);
    inline RealMP& operator+=(double o);
    inline RealMP& operator-=(double o);
    inline RealMP& operator*=(double o);
    inline RealMP& operator/=(double o);

    int precision() const {return mpfr_get_prec(_val);}
    inline void setPrecision(int nbits);

    void sqrt() {mpfr_sqrt(_val, _val, MPFR_RNDN);}
    void negate() {mpfr_neg(_val, _val, MPFR_RNDN);}

    double toDouble() const {return mpfr_get_d(_val, MPFR_RNDN);}
    QString toString(char format='g', int precision=6) const;
    QString toString(char * format) const;
    static void formatString(char * str, char format, int precision);

    static const RealMP null;
    static const int defaultPrecision;
  private:
    mpfr_t _val;
  };

  inline void RealMP::setPrecision(int nbits)
  {
    mpfr_init2(_val, nbits);
  }

  inline RealMP sqrt(const RealMP& c);

  inline RealMP::RealMP(int nbits)
  {
    ASSERT(nbits>0);
    mpfr_init2(_val, nbits);
  }

  inline RealMP::RealMP(int nbits, double v)
  {
    mpfr_init2(_val, nbits);
    mpfr_set_d(_val, v, MPFR_RNDN);
  }

  inline RealMP::RealMP(double v)
  {
    mpfr_init2(_val, defaultPrecision);
    mpfr_set_d(_val, v, MPFR_RNDN);
  }

  inline RealMP::RealMP(const RealMP& o)
  {
    mpfr_init2(_val, o.precision());
    mpfr_set(_val, o._val, MPFR_RNDN);
  }

  inline RealMP::~RealMP()
  {
    mpfr_clear(_val);
  }

  inline bool RealMP::operator==(const RealMP& o) const
  {
    return mpfr_equal_p(_val, o._val);
  }

  inline bool RealMP::operator!=(const RealMP& o) const
  {
    return !mpfr_equal_p(_val, o._val);
  }

  inline bool RealMP::operator<(const RealMP& o) const
  {
    return mpfr_less_p(_val, o._val);
  }

  inline bool RealMP::operator>(const RealMP& o) const
  {
    return mpfr_greater_p(_val, o._val);
  }

  inline RealMP& RealMP::operator=(const RealMP& c)
  {
    mpfr_set(_val, c._val, MPFR_RNDN);
    return *this;
  }

  inline RealMP& RealMP::operator+=(const RealMP& c)
  {
    mpfr_add(_val, _val, c._val, MPFR_RNDN);
    return *this;
  }

  inline RealMP& RealMP::operator-=(const RealMP& c)
  {
    mpfr_sub(_val, _val, c._val, MPFR_RNDN);
    return *this;
  }

  inline RealMP& RealMP::operator*=(const RealMP& c)
  {
    mpfr_mul(_val, _val, c._val, MPFR_RNDN);
    return *this;
  }

  inline RealMP& RealMP::operator/=(const RealMP& c)
  {
    mpfr_div(_val, _val, c._val, MPFR_RNDN);
    return *this;
  }

  inline RealMP& RealMP::operator=(double c)
  {
    mpfr_set_d(_val, c, MPFR_RNDN);
    return *this;
  }

  inline RealMP& RealMP::operator+=(double c)
  {
    mpfr_add_d(_val, _val, c, MPFR_RNDN);
    return *this;
  }

  inline RealMP& RealMP::operator-=(double c)
  {
    mpfr_sub_d(_val, _val, c, MPFR_RNDN);
    return *this;
  }

  inline RealMP& RealMP::operator*=(double c)
  {
    mpfr_mul_d(_val, _val, c, MPFR_RNDN);
    return *this;
  }

  inline RealMP& RealMP::operator/=(double c)
  {
    mpfr_div_d(_val, _val, c, MPFR_RNDN);
    return *this;
  }

  inline RealMP sqrt(const RealMP& c)
  {
    RealMP v(c);
    v.sqrt();
    return v;
  }

} // namespace QGpCoreTools

#endif // MULTI_PRECISION
#endif // REALMP_H

