// This file is part of the KDE project
// SPDX-FileCopyrightText: 2007 Tomas Mecir <mecirt@gmail.com>
// SPDX-License-Identifier: LGPL-2.0-only

#include "Number.h"

#ifdef CALLIGRA_SHEETS_HIGH_PRECISION_SUPPORT

#include <math.h>

using namespace Calligra::Sheets;

class Q_DECL_HIDDEN Number::Private : public QSharedData
{
public:
    Private()
    {
        type = Number::Float;
        f = 0;
    }

    Private(const Private &o)
        : QSharedData(o)
        , type(o.type)
    {
        switch (type) {
        case Number::Float:
            f = o.f;
            break;
        }
    }

    Number::Type type;

    union {
        long double f;
    };

    // static empty data to be shared
    static Private *null()
    {
        if (!s_null)
            s_null = new Private;
        return s_null;
    }

private:
    void operator=(const Number::Private &o);
    static Private *s_null;
};

Number::Private *Number::Private::s_null = 0;

// constructors

Number::Number()
    : d(Private::null())
{
    d->type = Number::Float;
    d->f = 0.0;
}

Number::Number(int num)
    : d(Private::null())
{
    d->type = Number::Float;
    d->f = (long double)num;
}

Number::Number(long double num)
    : d(Private::null())
{
    d->type = Number::Float;
    d->f = num;
}

Number::Number(const Number &n)
    : d(n.d)
{
}

// this destructor must exist here for the header file to compile properly,
// otherwise QSharedDataPointer destructor screams at us
Number::~Number()
{
}

// set/get
Number &Number::operator=(const Number &n)
{
    d = n.d;
    return *this;
}

long double Number::asFloat() const
{
    return d->f;
}

// basic operations
Number Number::operator+(const Number &n) const
{
    return Number(d->f + n.d->f);
}

Number Number::operator-(const Number &n) const
{
    return Number(d->f - n.d->f);
}

Number Number::operator*(const Number &n) const
{
    return Number(d->f * n.d->f);
}

Number Number::operator/(const Number &n) const
{
    return Number(d->f / n.d->f);
}

void Number::operator+=(const Number &n)
{
    d->f += n.d->f;
}

void Number::operator-=(const Number &n)
{
    d->f -= n.d->f;
}

void Number::operator*=(const Number &n)
{
    d->f *= n.d->f;
}

void Number::operator/=(const Number &n)
{
    d->f /= n.d->f;
}

Number Number::operator-() const
{
    return -(d->f);
}

// comparison
bool Number::operator<=(const Number &n) const
{
    return (d->f <= n.d->f);
}

bool Number::operator<(const Number &n) const
{
    return (d->f < n.d->f);
}

bool Number::operator==(const Number &n) const
{
    return (d->f == n.d->f);
}

Number Number::mod(const Number &n) const
{
    return Number(::fmod(d->f, n.d->f));
}

Number Number::abs() const
{
    return Number(::fabs(d->f));
}

Number Number::neg() const
{
    return Number(-1 * d->f);
}

Number Number::pow(const Number &exp) const
{
    return Number(::pow(d->f, exp.d->f));
}

Number Number::log(Number base) const
{
    long double logbase = ::log10(base.d->f);
    return Number(::log10(d->f) / logbase);
}

Number Number::ln() const
{
    return Number(::log(d->f));
}

Number Number::exp() const
{
    return Number(::exp(d->f));
}

// goniometric functions
Number Number::sin() const
{
    return Number(::sin(d->f));
}

Number Number::cos() const
{
    return Number(::cos(d->f));
}

Number Number::tg() const
{
    return Number(::tan(d->f));
}

Number Number::cotg() const
{
    return Number(1 / ::tan(d->f));
}

Number Number::asin() const
{
    return Number(::asin(d->f));
}

Number Number::acos() const
{
    return Number(::acos(d->f));
}

Number Number::atg() const
{
    return Number(::atan(d->f));
}

Number Number::atan2(const Number &y, const Number &x)
{
    return Number(::atan2(y.d->f, x.d->f));
}

// hyperbolic functions
Number Number::sinh() const
{
    return Number(::sinh(d->f));
}

Number Number::cosh() const
{
    return Number(::cosh(d->f));
}

Number Number::tgh() const
{
    return Number(::tanh(d->f));
}

Number Number::asinh() const
{
    return Number(::asinh(d->f));
}

Number Number::acosh() const
{
    return Number(::acosh(d->f));
}

Number Number::atgh() const
{
    return Number(::atanh(d->f));
}

// *** EXTERNAL FUNCTIONS ***

namespace Calligra
{
namespace Sheets
{

long double numToDouble(Number n)
{
    return n.asFloat();
}

// external operators, so that we can do things like 4+a without having to create temporary objects
// not provided for complex numbers, as we won't be using them often like that
Number operator+(long double n1, const Number &n2)
{
    return n2 + n1;
}
Number operator-(long double n1, const Number &n2)
{
    return (n2 - n1).neg();
}
Number operator*(long double n1, const Number &n2)
{
    return n2 * n1;
}
Number operator/(long double n1, const Number &n2)
{
    return Number(n1) / n2; /* TODO optimize perhaps */
}
bool operator<=(long double n1, const Number &n2)
{
    return (n2 >= n1);
}
bool operator<(long double n1, const Number &n2)
{
    return (n2 > n1);
}
bool operator==(long double n1, const Number &n2)
{
    return (n2 == n1);
}
bool operator!=(long double n1, const Number &n2)
{
    return (n2 != n1);
}
bool operator>=(long double n1, const Number &n2)
{
    return (n2 <= n1);
}
bool operator>(long double n1, const Number &n2)
{
    return (n2 < n1);
}

// external versions of the functions
Number fmod(const Number &n1, const Number &n2)
{
    return n1.mod(n2);
}
Number fabs(const Number &n)
{
    return n.abs();
}
Number abs(const Number &n)
{
    return n.abs();
}
Number neg(const Number &n)
{
    return n.neg();
}
Number pow(const Number &n, const Number &exp)
{
    return n.pow(exp);
}
Number sqrt(const Number &n)
{
    return n.pow(0.5);
}
Number log(const Number &n, Number base)
{
    return n.log(base);
}
Number ln(const Number &n)
{
    return n.ln();
}
Number log(const Number &n)
{
    return n.ln();
}
Number log10(const Number &n)
{
    return n.log(10);
}
Number exp(const Number &n)
{
    return n.exp();
}
Number sin(const Number &n)
{
    return n.sin();
}
Number cos(const Number &n)
{
    return n.cos();
}
Number tg(const Number &n)
{
    return n.tg();
}
Number cotg(const Number &n)
{
    return n.cotg();
}
Number asin(const Number &n)
{
    return n.asin();
}
Number acos(const Number &n)
{
    return n.acos();
}
Number atg(const Number &n)
{
    return n.atg();
}
Number atan2(const Number &y, const Number &x)
{
    return Number::atan2(y, x);
}
Number sinh(const Number &n)
{
    return n.sinh();
}
Number cosh(const Number &n)
{
    return n.cosh();
}
Number tgh(const Number &n)
{
    return n.tgh();
}
Number asinh(const Number &n)
{
    return n.asinh();
}
Number acosh(const Number &n)
{
    return n.acosh();
}
Number atgh(const Number &n)
{
    return n.atgh();
}

} // namespace Sheets
} // namespace Calligra

#endif // CALLIGRA_SHEETS_HIGH_PRECISION_SUPPORT

QDebug operator<<(QDebug dbg, const Number &n)
{
    dbg.noquote() << QString("Number(%1)").arg((double)numToDouble(n), 0, 'g', 10);
    return dbg;
}
