/***************************************************************************
**
**  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-25
**  Copyright: 2018-2019
**    Marc Wathelet (ISTerre, Grenoble, France)
**
***************************************************************************/

#ifndef COMPLEXMP_H
#define COMPLEXMP_H

#ifdef MULTI_PRECISION

#include "RealMP.h"
#include "Complex.h"
#include "QGpCoreToolsDLLExport.h"

namespace QGpCoreTools {

  class QGPCORETOOLS_EXPORT ComplexMP
  {
  public:
    ComplexMP() {}
    ComplexMP(const ComplexMP& c) : _re(c._re), _im(c._im) {}
    ComplexMP(const RealMP& re, const RealMP& im) : _re(re), _im(im) {}
    ComplexMP(double re, double im=0.0)
      : _re(RealMP::defaultPrecision, re), _im(RealMP::defaultPrecision, im) {}
    ComplexMP(int nbits, double re, double im=0.0)
      : _re(nbits, re), _im(nbits, im) {}
    ~ComplexMP() {}

    bool operator==(const ComplexMP& o) const {return _re==o._re && _im==o._im;}
    bool operator!=(const ComplexMP& o) const {return _re!=o._re && _im!=o._im;}

    ComplexMP& operator=(const ComplexMP& o) {_re=o._re; _im=o._im; return *this;}
    ComplexMP& operator+=(const ComplexMP& o) {_re+=o._re; _im+=o._im; return *this;}
    ComplexMP& operator-=(const ComplexMP& o) {_re-=o._re; _im-=o._im; return *this;}
    ComplexMP& operator*=(const ComplexMP& o);
    inline ComplexMP& operator/=(const ComplexMP& o);

    inline ComplexMP& operator=(const Complex& o) {_re=o.re(); _im=o.im(); return *this;}
    ComplexMP& operator+=(const Complex& o) {_re+=o.re(); _im+=o.im(); return *this;}
    ComplexMP& operator-=(const Complex& o) {_re-=o.re(); _im-=o.im(); return *this;}
    ComplexMP& operator*=(const Complex& o);
    inline ComplexMP& operator/=(const Complex& o);

    ComplexMP& operator=(const RealMP& r) {_re=r; _im=0.0; return *this;}
    ComplexMP& operator+=(const RealMP& r) {_re+=r; return *this;}
    ComplexMP& operator-=(const RealMP& r) {_re-=r; return *this;}
    ComplexMP& operator*=(const RealMP& r) {_re*=r; _im*=r; return *this;}
    inline ComplexMP& operator/=(const RealMP& r);

    ComplexMP& operator=(const double& r) {_re=r; _im=0.0; return *this;}
    ComplexMP& operator+=(const double& r) {_re+=r; return *this;}
    ComplexMP& operator-=(const double& r) {_re-=r; return *this;}
    ComplexMP& operator*=(const double& r) {_re*=r; _im*=r; return *this;}
    ComplexMP& operator/=(const double& r) {return operator/=(RealMP(r));}

    RealMP re() const {return _re;}
    void setRe(const RealMP& re) {_re=re;}
    RealMP im() const {return _im;}
    void setIm(const RealMP& im) {_im=im;}
    RealMP abs() const;
    RealMP abs2() const;
    void conjugate() {_im.negate();}

    int precision() const {return _re.precision();}
    inline void setPrecision(int nbits);

    QString toString(char format='g', int precision=6) const;

    static const ComplexMP null;
  private:
    RealMP _re, _im;
  };

  inline ComplexMP operator+(const double& d, const ComplexMP& c);
  inline ComplexMP operator-(const double& d, const ComplexMP& c);
  inline ComplexMP operator*(const double& d, const ComplexMP& c);
  inline ComplexMP operator/(const double& d, const ComplexMP& c);

  inline ComplexMP sqrt(const ComplexMP& c);
  inline ComplexMP cos(const ComplexMP& c);
  inline ComplexMP sin(const ComplexMP& c);
  inline ComplexMP tan(const ComplexMP& c);
  inline ComplexMP asin(const ComplexMP& c);
  inline ComplexMP log(const ComplexMP& c);
  inline ComplexMP exp(const ComplexMP& c);
  inline ComplexMP conjugate(const ComplexMP& c);
  inline ComplexMP inverse(const ComplexMP& c);
  inline RealMP abs(const ComplexMP& c);
  inline RealMP abs2(const ComplexMP& c);

  inline void ComplexMP::setPrecision(int nbits)
  {
    _re.setPrecision(nbits);
    _im.setPrecision(nbits);
  }


  inline ComplexMP& ComplexMP::operator/=(const ComplexMP& o)
  {
    operator*=(QGpCoreTools::conjugate(o));
    operator/=(o.abs2());
    return *this;
  }

  inline ComplexMP& ComplexMP::operator/=(const Complex& o)
  {
    operator*=(QGpCoreTools::conjugate(o));
    operator/=(o.abs2());
    return *this;
  }

  inline ComplexMP& ComplexMP::operator/=(const RealMP& o)
  {
    RealMP inv(1.0);
    inv/=o;
    _re*=inv;
    _im*=inv;
    return *this;
  }

  inline ComplexMP conjugate(const ComplexMP& o)
  {
    ComplexMP c(o);
    c.conjugate();
    return c;
  }

  inline RealMP ComplexMP::abs() const
  {
    return sqrt(abs2());
  }

} // namespace QGpCoreTools

#endif // MULTI_PRECISION
#endif // COMPLEXMP_H

