// This file is part of the KDE project
// SPDX-FileCopyrightText: 2007 Sascha Pfau <MrPeacock@gmail.com>
// SPDX-FileCopyrightText: 2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
// SPDX-FileCopyrightText: 2006 Ariya Hidayat <ariya@kde.org>
// SPDX-License-Identifier: LGPL-2.0-only

#include "TestStatisticalFunctions.h"

#include <math.h>

#include <QTest>

#include <engine/CalculationSettings.h>
#include <engine/CellBaseStorage.h>
#include <engine/Formula.h>
#include <engine/Localization.h>
#include <engine/MapBase.h>
#include <engine/SheetBase.h>

#include "TestKspreadCommon.h"

using namespace Calligra::Sheets;

// NOTE: we do not compare the numbers _exactly_ because it is difficult
// to get one "true correct" expected values for the functions due to:
//  - different algorithms among spreadsheet programs
//  - precision limitation of floating-point number representation
//  - accuracy problem due to propagated error in the implementation
#define CHECK_EVAL(x, y) QCOMPARE(TestDouble(x, y, 6), y)
#define CHECK_EVAL_SHORT(x, y) QCOMPARE(TestDouble(x, y, 10), y)
#define CHECK_ARRAY(x, y) QCOMPARE(TestArray(x, y, 10), true)
#define CHECK_ARRAY_NOSIZE(x, y) QCOMPARE(TestArray(x, y, 10, false), true)
#define ROUND(x) (roundf(1e15 * x) / 1e15)

bool TestStatisticalFunctions::TestArray(const QString &formula, const QString &_Array, int accuracy, bool checkSize = true)
{
    // define epsilon
    double epsilon = DBL_EPSILON * pow(10.0, (double)(accuracy));

    Value Array = evaluate(_Array);
    //   qDebug()<<"Array = "<<Array;

    Value result = evaluate(formula);

    // test match size
    if (checkSize)
        if (Array.rows() != result.rows() || Array.columns() != result.columns()) {
            qDebug() << "Array size do not match";
            return false;
        }

    // if checkSize is disabled the count of Array array could be lower than result array
    for (int e = 0; e < (int)Array.count(); e++) {
        qDebug() << "check element (" << e << ") " << (double)Array.element(e).asFloat() << " " << (double)result.element(e).asFloat();
        bool res = (long double)fabsl(Array.element(e).asFloat() - result.element(e).asFloat()) < epsilon;
        if (!res) {
            qDebug() << "check failed -->"
                     << "Element(" << e << ") " << (double)Array.element(e).asFloat() << " to" << (double)result.element(e).asFloat()
                     << "  diff =" << (double)(Array.element(e).asFloat() - result.element(e).asFloat());
            return false;
        }
    }
    // test passed
    return true;
}

Value TestStatisticalFunctions::TestDouble(const QString &formula, const Value &v2, int accuracy)
{
    double epsilon = DBL_EPSILON * pow(10.0, (double)(accuracy));

    Formula f(m_map->sheet(0)); // bind to test case data set
    QString expr = formula;
    if (expr[0] != '=')
        expr.prepend('=');
    f.setExpression(expr);
    Value result = f.eval();

    bool res = fabs(v2.asFloat() - result.asFloat()) < epsilon;

    if (!res)
        qDebug() << "check failed -->"
                 << "Epsilon =" << epsilon << "" << (double)v2.asFloat() << " to" << (double)result.asFloat()
                 << "  diff =" << (double)(v2.asFloat() - result.asFloat());
    //   else
    //     qDebug()<<"check -->" <<"  diff =" << v2.asFloat()-result.asFloat();
    if (res)
        return v2;
    else
        return result;
}

// round to get at most 15-digits number
static Value RoundNumber(const Value &v)
{
    if (v.isNumber()) {
        double d = numToDouble(v.asFloat());
        if (fabs(d) < DBL_EPSILON)
            d = 0.0;
        return Value(ROUND(d));
    } else
        return v;
}

Value TestStatisticalFunctions::evaluate(const QString &formula)
{
    Formula f(m_map->sheet(0));
    QString expr = formula;
    if (expr[0] != '=')
        expr.prepend('=');
    f.setExpression(expr);
    Value result = f.eval();

#if 0
    // this magically generates the CHECKs
    printf("  CHECK_EVAL( \"%s\",  %15g) );\n", qPrintable(formula), result.asFloat());
#endif

    return RoundNumber(result);
}

void TestStatisticalFunctions::initTestCase()
{
    KLocalizedString::setApplicationDomain("calligrasheets");
    FunctionModuleRegistry::instance()->loadFunctionModules();
    m_map = new MapBase;
    m_map->addNewSheet();
    SheetBase *sheet = m_map->sheet(0);
    CellBaseStorage *storage = sheet->cellStorage();
    m_map->calculationSettings()->locale()->setLanguage(QLocale::C);

    //
    // Test case data set
    //

    // A19:A29
    storage->setValue(1, 19, Value(1));
    storage->setValue(1, 20, Value(2));
    storage->setValue(1, 21, Value(4));
    storage->setValue(1, 22, Value(8));
    storage->setValue(1, 23, Value(16));
    storage->setValue(1, 24, Value(32));
    storage->setValue(1, 25, Value(64));
    storage->setValue(1, 26, Value(128));
    storage->setValue(1, 27, Value(256));
    storage->setValue(1, 28, Value(512));
    storage->setValue(1, 29, Value(1024));
    storage->setValue(1, 30, Value(2048));
    storage->setValue(1, 31, Value(4096));

    // B3:B17
    storage->setValue(2, 3, Value("7"));
    storage->setValue(2, 4, Value(2));
    storage->setValue(2, 5, Value(3));
    storage->setValue(2, 6, Value(true));
    storage->setValue(2, 7, Value("Hello"));
    // B8 leave empty
    storage->setValue(2, 9, Value::errorDIV0());
    storage->setValue(2, 10, Value(0));
    storage->setValue(2, 11, Value(3));
    storage->setValue(2, 12, Value(4));
    storage->setValue(2, 13, Value("2005-0131T01:00:00"));
    storage->setValue(2, 14, Value(1));
    storage->setValue(2, 15, Value(2));
    storage->setValue(2, 16, Value(3));
    storage->setValue(2, 17, Value(4));
    CHECK_EVAL("AVEDEV(1;2;3;4)", Value(1));

    // C4:C6
    storage->setValue(3, 4, Value(4));
    storage->setValue(3, 5, Value(5));
    storage->setValue(3, 6, Value(7));
    // C11:C17
    storage->setValue(3, 11, Value(5));
    storage->setValue(3, 12, Value(6));
    storage->setValue(3, 13, Value(8));
    storage->setValue(3, 14, Value(4));
    storage->setValue(3, 15, Value(3));
    storage->setValue(3, 16, Value(2));
    storage->setValue(3, 17, Value(1));
    // C19:C31
    storage->setValue(3, 19, Value(0));
    storage->setValue(3, 20, Value(5));
    storage->setValue(3, 21, Value(2));
    storage->setValue(3, 22, Value(5));
    storage->setValue(3, 23, Value(3));
    storage->setValue(3, 24, Value(4));
    storage->setValue(3, 25, Value(4));
    storage->setValue(3, 26, Value(0));
    storage->setValue(3, 27, Value(8));
    storage->setValue(3, 28, Value(1));
    storage->setValue(3, 29, Value(9));
    storage->setValue(3, 30, Value(6));
    storage->setValue(3, 31, Value(2));
    // C51:C57
    storage->setValue(3, 51, Value(7));
    storage->setValue(3, 52, Value(9));
    storage->setValue(3, 53, Value(11));
    storage->setValue(3, 54, Value(12));
    storage->setValue(3, 55, Value(15));
    storage->setValue(3, 56, Value(17));
    storage->setValue(3, 57, Value(19));

    // D51:D57
    storage->setValue(4, 51, Value(100));
    storage->setValue(4, 52, Value(105));
    storage->setValue(4, 53, Value(104));
    storage->setValue(4, 54, Value(108));
    storage->setValue(4, 55, Value(111));
    storage->setValue(4, 56, Value(120));
    storage->setValue(4, 57, Value(133));

    // F19:F26
    storage->setValue(6, 19, Value(20));
    storage->setValue(6, 20, Value(5));
    storage->setValue(6, 21, Value(-20));
    storage->setValue(6, 22, Value(-60));
    storage->setValue(6, 23, Value(75));
    storage->setValue(6, 24, Value(-29));
    storage->setValue(6, 25, Value(20));
    storage->setValue(6, 26, Value(30));
    // F51:F60
    storage->setValue(6, 51, Value(3));
    storage->setValue(6, 52, Value(4));
    storage->setValue(6, 53, Value(5));
    storage->setValue(6, 54, Value(2));
    storage->setValue(6, 55, Value(3));
    storage->setValue(6, 56, Value(4));
    storage->setValue(6, 57, Value(5));
    storage->setValue(6, 58, Value(6));
    storage->setValue(6, 59, Value(4));
    storage->setValue(6, 60, Value(7));

    // G51:G60
    storage->setValue(7, 51, Value(23));
    storage->setValue(7, 52, Value(24));
    storage->setValue(7, 53, Value(25));
    storage->setValue(7, 54, Value(22));
    storage->setValue(7, 55, Value(23));
    storage->setValue(7, 56, Value(24));
    storage->setValue(7, 57, Value(25));
    storage->setValue(7, 58, Value(26));
    storage->setValue(7, 59, Value(24));
    storage->setValue(7, 60, Value(27));

    // H19:H31
    storage->setValue(8, 19, Value("2005-03-12"));
    storage->setValue(8, 20, Value("2002-02-03"));
    storage->setValue(8, 21, Value("2005-03-08"));
    storage->setValue(8, 22, Value("1991-03-27"));
    storage->setValue(8, 23, Value("1967-07-05"));
    storage->setValue(8, 24, Value("1912-12-23"));
    storage->setValue(8, 25, Value("1992-02-06"));
    storage->setValue(8, 26, Value("1934-07-04"));
    storage->setValue(8, 27, Value("1909-01-08"));
    storage->setValue(8, 28, Value("1989-11-28"));
    storage->setValue(8, 29, Value("2000-02-22"));
    storage->setValue(8, 30, Value("2004-03-29"));
    storage->setValue(8, 31, Value("1946-07-13"));

    // I19:I31
    storage->setValue(9, 19, Value(13));
    storage->setValue(9, 20, Value(12));
    storage->setValue(9, 21, Value(11));
    storage->setValue(9, 22, Value(10));
    storage->setValue(9, 23, Value(9));
    storage->setValue(9, 24, Value(8));
    storage->setValue(9, 25, Value(7));
    storage->setValue(9, 26, Value(6));
    storage->setValue(9, 27, Value(5));
    storage->setValue(9, 28, Value(4));
    storage->setValue(9, 29, Value(3));
    storage->setValue(9, 30, Value(2));
    storage->setValue(9, 31, Value(1));
}

void TestStatisticalFunctions::testAVEDEV()
{
    // ODF-tests
    CHECK_EVAL("AVEDEV(1;2;3;4)", Value(1)); //
}

void TestStatisticalFunctions::testAVERAGE()
{
    // ODF-tests
    CHECK_EVAL("AVERAGE(2; 4)", Value(3)); //
}

void TestStatisticalFunctions::testAVERAGEA()
{
    // ODF-tests
    CHECK_EVAL("AVERAGEA(2; 4)", Value(3)); //
    CHECK_EVAL("AVERAGEA(TRUE(); FALSE(); 5)", Value(2)); //
}

void TestStatisticalFunctions::testBETADIST()
{
    // ODF-tests

    // Cumulative tests
    CHECK_EVAL("BETADIST( 0  ; 3; 4)", Value(0)); //
    CHECK_EVAL("BETADIST( 0.5; 3; 4)", Value(0.656250)); //
    CHECK_EVAL("BETADIST( 0.9; 4; 3)", Value(0.984150)); //
    CHECK_EVAL("BETADIST( 2  ; 3; 4)", Value(1)); // constraints x > b should be 1 if cumulative
    CHECK_EVAL("BETADIST(-1  ; 3; 4)", Value(0)); // constraints x < a

    CHECK_EVAL_SHORT("BETADIST(1.5;3;4;1;2)", evaluate("BETADIST(0.5;3;4)")); // diff = -2.27021e-09
    CHECK_EVAL_SHORT("BETADIST(2;3;4;1;3)", evaluate("BETADIST(0.5;3;4)")); // diff = -2.27021e-09

    // last parameter FALSE (non - Cumulative)
    CHECK_EVAL("BETADIST( 0  ;3;4;0;1;FALSE())", Value(0)); //
    CHECK_EVAL("BETADIST( 0.5;3;4;0;1;FALSE())", Value(0.0005208333)); // 0.000521
    CHECK_EVAL("BETADIST( 0.9;4;3;0;1;FALSE())", Value(0.0001215000)); // 0.000122
    CHECK_EVAL("BETADIST( 2  ;3;4;0;1;FALSE())", Value(0)); // constraints x > b should be 0 if non-cumulative
    CHECK_EVAL("BETADIST(-1  ;3;4;0;1;FALSE())", Value(0)); // constraints x < a

    CHECK_EVAL("BETADIST(1.5;3;4;1;2;FALSE())", evaluate("BETADIST(0.5;3;4;0;1;FALSE())")); //
    CHECK_EVAL("BETADIST(2  ;3;4;1;3;FALSE())", evaluate("BETADIST(0.5;3;4;0;1;FALSE())")); //
}

void TestStatisticalFunctions::testBETAINV()
{
    // ODF-tests
    CHECK_EVAL("BETADIST(BETAINV(0;3;4);3;4)", Value(0)); //
    CHECK_EVAL("BETADIST(BETAINV(0.1;3;4);3;4)", Value(0.1)); //
    CHECK_EVAL("BETADIST(BETAINV(0.3;3;4);3;4)", Value(0.3)); //
    CHECK_EVAL("BETADIST(BETAINV(0.5;4;3);4;3)", Value(0.5)); //
    CHECK_EVAL("BETADIST(BETAINV(0.7;4;3);4;3)", Value(0.7)); //
    CHECK_EVAL("BETADIST(BETAINV(1;3;4);3;4)", Value(1)); //
    CHECK_EVAL("BETADIST(BETAINV(0;3;4;1;3);3;4;1;3)", Value(0)); //
    CHECK_EVAL("BETADIST(BETAINV(0.1;3;4;1;3);3;4;1;3)", Value(0.1)); //
    CHECK_EVAL("BETADIST(BETAINV(0.3;3;4;1;3);3;4;1;3)", Value(0.3)); //
    CHECK_EVAL("BETADIST(BETAINV(0.5;4;3;1;3);4;3;1;3)", Value(0.5)); //
    CHECK_EVAL("BETADIST(BETAINV(0.7;4;3;1;3);4;3;1;3)", Value(0.7)); //
    CHECK_EVAL("BETADIST(BETAINV(1;3;4;1;3);3;4;1;3)", Value(1)); //
}

void TestStatisticalFunctions::testBINOMDIST()
{
    // bettersolution.com
    CHECK_EVAL("BINOMDIST(10;10;  1  ;0)", Value(1)); // Prob.=100% - all trials successful
    CHECK_EVAL("BINOMDIST(9 ; 1; 10  ;0)", Value(0)); // Prob. of -exactly- 9 trials successful is 0 then
    CHECK_EVAL("BINOMDIST(10;10;  0.1;1)", Value(1)); // Sum of probabilities of 0..10 hits is 1.
    //     CHECK_EVAL("BINOMDIST(4 ;10;  0.4;1)", Value( 0.6331032576 ) ); // Some random values.
    // my tests
    CHECK_EVAL_SHORT("BINOMDIST(4 ;10;  0.4;1)", Value(0.6331032576)); // Some random values.
    CHECK_EVAL_SHORT("BINOMDIST(5 ;10;  0.4;1)", Value(0.8337613824)); // Some random values.
    CHECK_EVAL_SHORT("BINOMDIST(6 ;10;  0.4;1)", Value(0.9452381184)); // Some random values.
    CHECK_EVAL_SHORT("BINOMDIST(4 ;10;  0.2;1)", Value(0.9672065024)); // Some random values.
    CHECK_EVAL_SHORT("BINOMDIST(5 ;10;  0.2;1)", Value(0.9936306176)); // Some random values.
    CHECK_EVAL_SHORT("BINOMDIST(6 ;10;  0.2;1)", Value(0.9991356416)); // Some random values.
}

void TestStatisticalFunctions::testCHIDIST()
{
    // bettersolution.com
    CHECK_EVAL("CHIDIST( 18.307;10)", Value(0.0500005892)); //
    CHECK_EVAL("CHIDIST(      2;2)", Value(0.3678794412)); //
    CHECK_EVAL("CHIDIST(     -1;2)", Value(1)); // constraint x<0 TODO EXCEL return #NUM!
    //     CHECK_EVAL("CHIDIST(     4;\"texr\")", Value::VALUE()    ); // TODO
}

void TestStatisticalFunctions::testCONFIDENCE()
{
    // ODF-tests
    CHECK_EVAL("CONFIDENCE(0.5 ; 1;1)", Value(0.67448975)); //
    CHECK_EVAL("CONFIDENCE(0.25; 1;1)", Value(1.1503493804)); //
    CHECK_EVAL("CONFIDENCE(0.5 ; 4;1)", Value(2.6979590008)); // Multiplying stddev by X multiplies result by X.
    CHECK_EVAL("CONFIDENCE(0.5 ; 1;4)", Value(0.3372448751)); // Multiplying count by X*X divides result by X.

    // check constraints
    CHECK_EVAL("CONFIDENCE(-0.5; 1;4)", Value::errorNUM()); // 0 < alpha < 1
    CHECK_EVAL("CONFIDENCE( 1.5; 1;4)", Value::errorNUM()); // 0 < alpha < 1
    CHECK_EVAL("CONFIDENCE( 0.5;-1;4)", Value::errorNUM()); // stddev > 0
    CHECK_EVAL("CONFIDENCE( 0.5; 1;0)", Value::errorNUM()); // size >= 1
}

void TestStatisticalFunctions::testCORREL()
{
    //  Cell | Value      Cell | Value
    // ------+------     ------+------
    //   B14 |  1          C14 |  4
    //   B15 |  2          C15 |  3
    //   B16 |  3          C16 |  2
    //   B17 |  4          C17 |  1

    // ODF-tests
    CHECK_EVAL("CORREL(B14:B17;B14:B17)", Value(1)); // Perfect positive correlation given identical sequences
    CHECK_EVAL("CORREL(B14:B17;C14:C17)", Value(-1)); // Perfect negative correlation given reversed sequences
    CHECK_EVAL("CORREL(1;2)", Value::errorNUM()); // Each list must contain at least 2 values
    CHECK_EVAL("CORREL(B14:B16;B15:B16)", Value::errorNUM()); // The length of each list must be equal
}

void TestStatisticalFunctions::testCOVAR()
{
    //  Cell | Value      Cell | Value
    // ------+------     ------+------
    //   B14 |  1          C14 |  4
    //   B15 |  2          C15 |  3
    //   B16 |  3          C16 |  2
    //   B17 |  4          C17 |  1

    // ODF-tests
    CHECK_EVAL("COVAR(C11:C17;C11:C17)", Value(4.9795918367)); //
    CHECK_EVAL("COVAR(B14:B17;C14:C17)", Value(-1.25)); //
    CHECK_EVAL("COVAR(B14:B17;C13:C17)", Value::errorNUM()); // TODO should we check for "array sizes don't match" or "value counts" in array?.
}

void TestStatisticalFunctions::testDEVSQ()
{
    // ODF-tests
    CHECK_EVAL("DEVSQ(4)", Value(0)); // One value - no deviation.
    CHECK_EVAL("DEVSQ(5;5;5;5)", Value(0)); // Identical values - no deviation.
    CHECK_EVAL("DEVSQ(2;4)", Value(2)); // Each value deviates by 1.
    CHECK_EVAL("DEVSQ(-5;5;-1;1)", Value(52)); // Average=0 must work properly.
    CHECK_EVAL("DEVSQ(C11:C17)", Value(34.8571428571)); // Test values.
    CHECK_EVAL("DEVSQ(B14:B17)", Value(5.00)); // Test values.
    CHECK_EVAL("DEVSQ(B14)", Value(0)); // One value - no deviation.
}

// void TestStatisticalFunctions::testDEVSQA()
// {
//     // no test available
// }

void TestStatisticalFunctions::testEXPONDIST()
{
    // ODF-tests
    CHECK_EVAL("EXPONDIST( 1;1;TRUE())", Value(0.6321205588)); //
    CHECK_EVAL("EXPONDIST( 2;2;TRUE())", Value(0.9816843611)); //
    CHECK_EVAL("EXPONDIST( 0;1;TRUE())", Value(0)); //
    CHECK_EVAL("EXPONDIST(-1;1;TRUE())", Value(0)); // constraint x<0

    CHECK_EVAL("EXPONDIST( 1;1;FALSE())", Value(0.3678794412)); //
    CHECK_EVAL("EXPONDIST( 2;2;FALSE())", Value(0.0366312778)); //
    CHECK_EVAL("EXPONDIST( 0;1;FALSE())", Value(1)); //
    CHECK_EVAL("EXPONDIST(-1;1;FALSE())", Value(0)); // constraint x<0

    // test disabled, because 3rd param. is not opt.!
    // CHECK_EVAL("EXPONDIST(1;1)", evaluate("EXPONDIST(1;1;TRUE())") );
}

void TestStatisticalFunctions::testFDIST()
{
    // ODF-tests

    // cumulative
    CHECK_EVAL("FDIST( 1;4;5)", Value(0.5143428033)); //
    CHECK_EVAL("FDIST( 2;5;4)", Value(0.7392019723)); //
    CHECK_EVAL("FDIST( 0;4;5)", Value(0)); //
    CHECK_EVAL("FDIST(-1;4;5)", Value(0)); //

    CHECK_EVAL_SHORT("FDIST( 1;4;5;TRUE())", evaluate("FDIST(1;4;5)")); // diff = -1.39644e-09

    // non-cumulative
    CHECK_EVAL("FDIST( 1;4;5;FALSE())", Value(0.3976140792)); //
    CHECK_EVAL("FDIST( 2;5;4;FALSE())", Value(0.1540004108)); //
    CHECK_EVAL("FDIST( 0;4;5;FALSE())", Value(0)); //
    CHECK_EVAL("FDIST(-1;4;5;FALSE())", Value(0)); //
}

void TestStatisticalFunctions::testFINV()
{
    // ODF-tests
    CHECK_EVAL("FDIST(FINV(0.1;3;4);3;4)", Value(0.1)); //
    CHECK_EVAL("FDIST(FINV(0.3;3;4);3;4)", Value(0.3)); //
    CHECK_EVAL("FDIST(FINV(0.5;3;4);3;4)", Value(0.5)); //
    CHECK_EVAL("FDIST(FINV(0.7;3;4);3;4)", Value(0.7)); //
    CHECK_EVAL("FDIST(FINV(0.0;3;4);3;4)", Value(0.0)); //
}

void TestStatisticalFunctions::testFISHER()
{
    // ODF-tests
    CHECK_EVAL("FISHER(0)", Value(0)); // Fisher of 0.
    CHECK_EVAL("FISHER((EXP(1)-1)/(EXP(1)+1))", Value(0.5)); // Argument chosen so that ln=1
    CHECK_EVAL_SHORT("FISHER(0.5)", Value(0.54930614)); // TODO - be more precise - Some random value.
    CHECK_EVAL("FISHER(0.47)+FISHER(-0.47)", Value(0)); // Function is symmetrical.
}

void TestStatisticalFunctions::testFISHERINV()
{
    // ODF-tests
    CHECK_EVAL("FISHERINV(0)", Value(0)); // Fisherinv of 0.
    CHECK_EVAL("FISHERINV(LN(2))", Value(0.6)); // e^(2*ln(2))=4
    CHECK_EVAL("FISHERINV(FISHER(0.5))", Value(0.5)); // Some random value.
    CHECK_EVAL("FISHERINV(0.47)+FISHERINV(-0.47)", Value(0)); // Function is symmetrical.
}

void TestStatisticalFunctions::testFREQUENCY()
{
    Value result(Value::Array);
    result.setElement(0, 0, Value(3));
    result.setElement(0, 1, Value(2));
    result.setElement(0, 2, Value(4));
    result.setElement(0, 3, Value(1));
    CHECK_EVAL("FREQUENCY({1;2;3;4;5;6;7;8;9;10};{3|5|9})", result);
    // the second arg has to be a column vector
    CHECK_EVAL("ISERROR(FREQUENCY({1;2;3;4;5;6;7;8;9;10};{3;5;9}))", Value(true));
    // an empty second arg returns the overall number count
    CHECK_EVAL("FREQUENCY({1;2;3;4;5;6;7;8;9;10};)", Value(10));
}

void TestStatisticalFunctions::testFTEST()
{
    // TODO - be more precise
    // ODF-tests
    CHECK_EVAL_SHORT("FTEST(B14:B17; C14:C17)", Value(1.0)); // Same data (second reversed),
    CHECK_EVAL_SHORT("FTEST(B14:B15; C13:C14)", Value(0.311916521)); // Significantly different variances,
    // so less likely to come from same data set.
}

void TestStatisticalFunctions::testGAMMADIST()
{
    // bettersolution.com non-cumulative
    CHECK_EVAL("GAMMADIST(10 ;9;2;FALSE())", Value(0.0326390197)); //

    // bettersolution.com cumulative
    CHECK_EVAL("GAMMADIST(10 ;9;2;TRUE())", Value(0.0680936347)); //
    CHECK_EVAL("GAMMADIST(10 ;10;5;TRUE())", Value(0.0000464981)); // Bettersolution = 0 .rounded?
    CHECK_EVAL("GAMMADIST(7 ;5;1;TRUE())", Value(0.8270083921)); // TODO NOK / Bettersolution = 1

    // bettersolution.com constraints
    CHECK_EVAL("GAMMADIST(10 ;9;0;TRUE())", Value::errorNUM()); // beta = 0 not allowed
    CHECK_EVAL("GAMMADIST(10 ;-2;2;TRUE())", Value::errorNUM()); // was wird getestet? alpha
    CHECK_EVAL("GAMMADIST(-1 ;9;2;TRUE())", Value::errorNUM()); // NOK
    CHECK_EVAL("GAMMADIST(7 ;\"text\";1;TRUE())", Value::errorVALUE()); // text not allowed
    CHECK_EVAL("GAMMADIST(7 ;5;\"text\";TRUE())", Value::errorVALUE()); // text not allowed

    // ODF-tests non-cumulative
    CHECK_EVAL("GAMMADIST(0  ;3;4;FALSE())", Value(0));
    CHECK_EVAL("GAMMADIST(0.5;3;4;FALSE())", Value(0.0017236268)); //
    CHECK_EVAL("GAMMADIST(9  ;4;3;FALSE())", Value(0.0746806026)); // ODF-Specs -> 0.0666979468 should be 0,0746
    CHECK_EVAL("GAMMADIST(0  ;3;4;FALSE())", Value(0));
    CHECK_EVAL("GAMMADIST(9  ;4;3;FALSE())", Value(0.0746806026)); // TODO check ODF-Specs -> 0.390661

    // ODF-tests cumulative
    CHECK_EVAL("GAMMADIST(0.5;3;4;TRUE())", Value(0.0002964775)); //
    CHECK_EVAL("GAMMADIST(9  ;4;3;TRUE())", Value(0.3527681112));
    CHECK_EVAL("GAMMADIST(-1 ;4;3;TRUE())", Value(0)); // neg. x return always 0
    CHECK_EVAL("GAMMADIST(-1 ;3;4;FALSE())", Value(0)); // neg. x return always 0

    // various tests cumulative
    CHECK_EVAL("GAMMADIST(9 ;9;2;TRUE())", Value(0.0402573125)); //
    CHECK_EVAL("GAMMADIST(9 ;8;2;TRUE())", Value(0.0865864716)); //
}

void TestStatisticalFunctions::testGAMMAINV()
{
    // ODF-tests
    CHECK_EVAL("GAMMADIST(GAMMAINV(0.1;3;4);3;4;1)", Value(0.1)); //
    CHECK_EVAL("GAMMADIST(GAMMAINV(0.3;3;4);3;4;1)", Value(0.3)); //
    CHECK_EVAL("GAMMADIST(GAMMAINV(0.5;3;4);3;4;1)", Value(0.5)); //
    CHECK_EVAL("GAMMADIST(GAMMAINV(0.7;3;4);3;4;1)", Value(0.7)); //
    CHECK_EVAL("GAMMADIST(GAMMAINV(0  ;3;4);3;4;1)", Value(0)); //
}

void TestStatisticalFunctions::testGAUSS()
{
    // ODF-tests
    CHECK_EVAL("GAUSS(0)", Value(0)); // Mean of one value.
    CHECK_EVAL("GAUSS(1)", Value(0.341344746)); // Multiple equivalent values.
    // my test
    CHECK_EVAL("GAUSS(-0.25)", Value(-0.0987063257)); // check neg. values. test for fixes gauss_func
}

void TestStatisticalFunctions::testGROWTH()
{
    // constraints
    CHECK_EVAL("GROWTH({}; C19:C23; 1)", Value::errorNA()); // empty knownY matrix
    CHECK_EVAL("GROWTH({5.0;\"a\"}; C19:C23; 1)", Value::errorNA()); // knownY matrix constrains chars

    // ODF-tests
    CHECK_ARRAY("GROWTH( A19:A23; C19:C23; 1 )", "{2.5198420998}"); // with offset
    CHECK_ARRAY("GROWTH( A19:A23; C19:C23; 1; FALSE() )", "{1.4859942891}"); // without offset

    // http://www.techonthenet.com/excel/formulas/growth.php
    CHECK_ARRAY("GROWTH({4;5;6};{10;20;30};{15;30;45})", "{4.4569483434;6.0409611796;8.1879369384}"); //
    CHECK_ARRAY("GROWTH({4;5;6};{10;20;30})", "{4.0273074534;4.9324241487;6.0409611796}"); //
    CHECK_ARRAY_NOSIZE("GROWTH({4;5;6})", "{4.0273074534}"); //
}

void TestStatisticalFunctions::testGEOMEAN()
{
    // ODF-tests
    CHECK_EVAL("GEOMEAN(7)", Value(7)); // Mean of one value.
    CHECK_EVAL("GEOMEAN(5;5;5;5)", Value(5)); // Multiple equivalent values.
    CHECK_EVAL("GEOMEAN(2;8;2;8)", Value(4)); // Some values.
    CHECK_EVAL("GEOMEAN(8;0;8;8;8;8)", Value::errorNUM()); // Error if there is a 0 in the range.
    CHECK_EVAL("GEOMEAN(C11)", Value(5)); // One value, range.
    CHECK_EVAL("GEOMEAN(C11:C17)", Value(3.4451109418)); // Some values, range.
    CHECK_EVAL("GEOMEAN(B14:B17)", Value(2.2133638394)); // Some values, range.
}

void TestStatisticalFunctions::testHARMEAN()
{
    // ODF-tests
    CHECK_EVAL("HARMEAN(7)", Value(7)); // Mean of one value.
    CHECK_EVAL("HARMEAN(4;4;4;4)", Value(4)); // Multiple equivalent values.
    CHECK_EVAL("HARMEAN(2;4;4)", Value(3)); // Some values.
    CHECK_EVAL("HARMEAN(8;0;8;8;8;8)", Value::errorNUM()); // Error if there is a 0 in the range.
    CHECK_EVAL("HARMEAN(C11)", Value(5)); // One value, range.
    CHECK_EVAL("HARMEAN(C11:C17)", Value(2.7184466019)); // Some values, range.
    CHECK_EVAL("HARMEAN(B14:B17)", Value(1.92)); // Some values, range.
}

void TestStatisticalFunctions::testHYPGEOMDIST()
{
    // ODF-tests
    CHECK_EVAL("HYPGEOMDIST( 2  ;3;3;6;FALSE())", Value(0.45)); // If an urn contains 3 red balls and 3 green balls, the probability
    // that 2 red balls will be selected after 3 selections without replacement.
    // (0.45=27/60).
    CHECK_EVAL("HYPGEOMDIST( 2  ;3;3;6)", Value(0.45)); // The default for cumulative is FALSE().
    CHECK_EVAL("HYPGEOMDIST( 0  ;3;3;6)", Value(0.05)); // There is a small (5%) chance of selecting only green balls.
    CHECK_EVAL("HYPGEOMDIST( 2  ;3;3;6;TRUE())", Value(0.95)); // The probability of selecting at most two red balls (i.e 0, 1 or 2).
    CHECK_EVAL("HYPGEOMDIST( 4  ;3;3;6)", Value::errorNUM()); // X must be <= M
    CHECK_EVAL("HYPGEOMDIST( 2.8;3;3;6)", Value(0.45)); // Non-integers are truncated.
    CHECK_EVAL("HYPGEOMDIST(-2  ;3;3;6)", Value::errorNUM()); // Values must be >= 0.
    CHECK_EVAL("HYPGEOMDIST( 0  ;0;0;0)", Value(1)); //
}

void TestStatisticalFunctions::testINTERCEPT()
{
    // bettersolution.com
    CHECK_EVAL_SHORT("INTERCEPT({2;3;9;1;8};{6;5;11;7;5})", Value(0.048387097)); // TODO - be more precise
    //     CHECK_EVAL_SHORT("INTERCEPT({2;4;6};{6;3;8})",          Value( 2.21053     ) ); // TODO - be more precise
    CHECK_EVAL("INTERCEPT({2;3;9};{6;5;11;7;5})", Value::errorNUM()); //
    CHECK_EVAL("INTERCEPT(\"text\";{6;5;11;7;5})", Value::errorNUM()); // text is not allowed
}

void TestStatisticalFunctions::testKURT()
{
    // TODO check function

    // ODF-tests
    CHECK_EVAL("KURT(C20:C25)", Value(-0.446162998)); //
    CHECK_EVAL("KURT(C20:C23;4;4)", Value(-0.446162998)); //
}

void TestStatisticalFunctions::testLARGE()
{
    //  Cell | Value | N'th
    // ------+-------+------
    //   B14 |   1   |   3
    //   B15 |   2   |   2
    //   B16 |   3   |   1

    // ODF-tests
    CHECK_EVAL("LARGE(B14:B16;1)", Value(3)); //
    CHECK_EVAL("LARGE(B14:B16;3)", Value(1)); //
    CHECK_EVAL("LARGE(B14:B16;4)", Value::errorNUM()); // N is greater than the length of the list
}

void TestStatisticalFunctions::testLEGACYCHIDIST()
{
    // ODF-tests LEGACY.CHIDIST
    CHECK_EVAL("LEGACYCHIDIST(-1;2)", Value(1)); // constraint x<0
    CHECK_EVAL("LEGACYCHIDIST( 0;2)", Value(1)); // constraint x=0
    CHECK_EVAL("LEGACYCHIDIST( 2;2)", Value(0.3678794412)); //
    CHECK_EVAL("LEGACYCHIDIST( 4;4)", Value(0.4060058497)); //
}

void TestStatisticalFunctions::testLEGACYCHIINV()
{
    // ODF-tests LEGACY.CHIINV
    CHECK_EVAL("LEGACYCHIDIST(LEGACYCHIINV(0.1;3);3)", Value(0.1)); //
    CHECK_EVAL("LEGACYCHIDIST(LEGACYCHIINV(0.3;3);3)", Value(0.3)); //
    CHECK_EVAL("LEGACYCHIDIST(LEGACYCHIINV(0.5;3);3)", Value(0.5)); //
    CHECK_EVAL("LEGACYCHIDIST(LEGACYCHIINV(0.7;3);3)", Value(0.7)); //
    CHECK_EVAL("LEGACYCHIDIST(LEGACYCHIINV(0.9;3);3)", Value(0.9)); //
    CHECK_EVAL("LEGACYCHIDIST(LEGACYCHIINV(0.1;20);20)", Value(0.1)); //
    CHECK_EVAL("LEGACYCHIDIST(LEGACYCHIINV(0.3;20);20)", Value(0.3)); //
    CHECK_EVAL("LEGACYCHIDIST(LEGACYCHIINV(0.5;20);20)", Value(0.5)); //
    CHECK_EVAL("LEGACYCHIDIST(LEGACYCHIINV(0.7;20);20)", Value(0.7)); //
    CHECK_EVAL("LEGACYCHIDIST(LEGACYCHIINV(0.9;20);20)", Value(0.9)); //
    CHECK_EVAL("LEGACYCHIDIST(LEGACYCHIINV(1.0;20);20)", Value(1.0)); //
}

void TestStatisticalFunctions::testLEGACYFDIST()
{
    // ODF-tests
    CHECK_EVAL("LEGACYFDIST( 1;4;5)", Value(0.4856571967)); //
    CHECK_EVAL("LEGACYFDIST( 2;5;4)", Value(0.2607980277)); //
    CHECK_EVAL("LEGACYFDIST( 0;4;5)", Value(1)); //
    CHECK_EVAL("LEGACYFDIST(-1;4;5)", Value::errorNUM()); //
}

void TestStatisticalFunctions::testLEGACYFINV()
{
    // ODF-tests
    CHECK_EVAL("LEGACYFDIST(LEGACYFINV(0.1;3;4);3;4)", Value(0.1)); //
    CHECK_EVAL("LEGACYFDIST(LEGACYFINV(0.3;3;4);3;4)", Value(0.3)); //
    CHECK_EVAL("LEGACYFDIST(LEGACYFINV(0.5;3;4);3;4)", Value(0.5)); //
    CHECK_EVAL("LEGACYFDIST(LEGACYFINV(0.7;3;4);3;4)", Value(0.7)); //
    CHECK_EVAL("LEGACYFDIST(LEGACYFINV(1.0;3;4);3;4)", Value(1.0)); //
}

void TestStatisticalFunctions::testLOGINV()
{
    // TODO check function

    // ODF-tests
    CHECK_EVAL("LOGNORMDIST(LOGINV(0.1;0;1);0;1;TRUE())", Value(0.1)); //
    CHECK_EVAL("LOGNORMDIST(LOGINV(0.3;0;1);0;1;TRUE())", Value(0.3)); //
    CHECK_EVAL("LOGNORMDIST(LOGINV(0.5;0;1);0;1;TRUE())", Value(0.5)); //
    CHECK_EVAL("LOGNORMDIST(LOGINV(0.7;0;1);0;1;TRUE())", Value(0.7)); //
    CHECK_EVAL("LOGNORMDIST(LOGINV(0.9;0;1);0;1;TRUE())", Value(0.9)); //
    CHECK_EVAL("LOGNORMDIST(LOGINV(0.1;1;4);1;4;TRUE())", Value(0.1)); //
    CHECK_EVAL("LOGNORMDIST(LOGINV(0.3;1;4);1;4;TRUE())", Value(0.3)); //
    CHECK_EVAL("LOGNORMDIST(LOGINV(0.5;1;4);1;4;TRUE())", Value(0.5)); //
    CHECK_EVAL("LOGNORMDIST(LOGINV(0.7;1;4);1;4;TRUE())", Value(0.7)); //
    CHECK_EVAL("LOGNORMDIST(LOGINV(0.9;1;4);1;4;TRUE())", Value(0.9)); //
    CHECK_EVAL("LOGINV(0.5)", Value(1)); //
}

void TestStatisticalFunctions::testLOGNORMDIST()
{
    // TODO - implement cumulative calculation
    //      - check definition cumulative/non-cumulative and constraints

    // ODF-tests

    // cumulative
    CHECK_EVAL("LOGNORMDIST(1)", Value(0.5)); //
    CHECK_EVAL("LOGNORMDIST(1;1;4)", Value(0.4012936743)); //
    CHECK_EVAL("LOGNORMDIST(1;0;1;TRUE())", Value(0.5)); //
    CHECK_EVAL("LOGNORMDIST(1;1;4;TRUE())", Value(0.4012936743)); //
    CHECK_EVAL("LOGNORMDIST(1;-1;4;TRUE())", Value(0.5987063257)); //
    //     CHECK_EVAL("LOGNORMDIST(2;-1;4;TRUE())",  Value( 0.663957 ) ); // ??????
    CHECK_EVAL("LOGNORMDIST(3;0;1;TRUE())", Value(0.8640313924)); //
    CHECK_EVAL("LOGNORMDIST(100;0;1;TRUE())", Value(0.9999979394)); //
    CHECK_EVAL("LOGNORMDIST(-1;0;1;TRUE())", Value(0)); // constraint x<0 returns 0

    // non-cumulative
    //     CHECK_EVAL("LOGNORMDIST( 1; 0; 1;FALSE())", Value( 0.398942 ) ); //
    //     CHECK_EVAL("LOGNORMDIST( 1; 1; 4;FALSE())", Value( 0.096667 ) ); //
    //     CHECK_EVAL("LOGNORMDIST( 1;-1; 4;FALSE())", Value( 0.096667 ) ); //
    //     CHECK_EVAL("LOGNORMDIST( 2;-1; 4;FALSE())", Value( 0.045595 ) ); //
    //     CHECK_EVAL("LOGNORMDIST(-1; 0; 1;FALSE())", Value::errorNUM() ); // constraint failure
    //     CHECK_EVAL("LOGNORMDIST( 1; 0;-1;FALSE())", Value::errorNUM() ); // constraint failure
}

void TestStatisticalFunctions::testMAX()
{
    //  Cell | Value      Cell | Value
    // ------+------     ------+------
    //   B3  |  "7"        C14 |  4
    //   B4  |  2          C15 |  3
    //   B5  |  3          C16 |  2
    //   B6  |  true       C17 |  1
    //   B7  |  "Hello"
    //   B8  |
    //   B9  | DIV/0

    // ODF-tests
    CHECK_EVAL("MAX(2;4;1;-8)", Value(4)); // Negative numbers are smaller than positive numbers.
    CHECK_EVAL("MAX(B4:B5)", Value(3)); // The maximum of (2,3) is 3.
    //     CHECK_EVAL("ISNA(MAXA(NA())", Value(true)); // nline errors are propagated.
    CHECK_EVAL("MAX(B3:B5)", Value(3)); // Strings are not converted to numbers and are ignored.
    CHECK_EVAL("MAX(-1;B7)", Value(-1)); // Strings are not converted to numbers and are ignored.
    CHECK_EVAL("MAX(B3:B9)", Value::errorVALUE()); // TODO check function - Errors inside ranges are NOT ignored.
}

void TestStatisticalFunctions::testMAXA()
{
    // ODF-tests
    CHECK_EVAL("MAXA(2;4;1;-8)", Value(4)); // Negative numbers are smaller than positive numbers.
    CHECK_EVAL("MAXA(B4:B5)", Value(3)); // The maximum of (2,3) is 3.
    //     CHECK_EVAL("ISNA(MAXA(NA())", Value(true)); // Inline errors are propagated.

    // TODO check function - inline Text must be converted, but not Text in Cells
    //     CHECK_EVAL("MAXA(B3:B5)",    Value(          3 ) ); // Cell text is converted to 0.

    CHECK_EVAL("MAXA(-1;B7)", Value(0)); // Cell text is converted to 0.
    CHECK_EVAL("MAXA(\"a\")", Value::errorVALUE()); // Text inline is NOT ignored.
    CHECK_EVAL("MAXA(B3:B9)", Value::errorVALUE()); // TODO check function - Errors inside ranges are NOT ignored.
    CHECK_EVAL("MAXA(B6:B7)", Value(1)); // Logicals are considered numbers.
}

void TestStatisticalFunctions::testMEDIAN()
{
    // ODF-tests
    CHECK_EVAL("=MEDIAN(10.5;7.2)", Value(8.85));
    CHECK_EVAL("=MEDIAN(7.2;200;5.4;45)", Value(26.1));
    CHECK_EVAL("=MEDIAN(7.2;200;5.4;8.1)", Value(7.65));
    CHECK_EVAL("=MEDIAN(1;3;13;14;15)", Value(13.0));
    CHECK_EVAL("=MEDIAN(1;3;13;14;15;35)", Value(13.5));
    // Bug 148574: MEDIAN function gives incorrect results
    CHECK_EVAL("=MEDIAN(1;2;3)", Value(2));
    CHECK_EVAL("=MEDIAN(1;2;3;4;5)", Value(3));
}

void TestStatisticalFunctions::testMIN()
{
    // ODF-tests
    CHECK_EVAL("MIN(2;4;1;-8)", Value(-8)); // Negative numbers are smaller than positive numbers.
    CHECK_EVAL("MIN(B4:B5)", Value(2)); // The minimum of (2,3) is 2.
    CHECK_EVAL("MIN(B3)", Value(0)); // If no numbers are provided in all ranges, MIN returns 0
    CHECK_EVAL("MIN(\"a\")", Value::errorNUM()); // Non-numbers inline are NOT ignored.
    CHECK_EVAL("MIN(B3:B5)", Value(2)); // Cell text is not converted to numbers and is ignored.
}

void TestStatisticalFunctions::testMINA()
{
    // ODF-tests
    CHECK_EVAL("MINA(2;4;1;-8)", Value(-8)); // Negative numbers are smaller than positive numbers.
    CHECK_EVAL("MINA(B4:B5)", Value(2)); // The minimum of (2,3) is 2.
    CHECK_EVAL("MINA(1;B7)", Value(0)); // Cell text is converted to 0.
    CHECK_EVAL("MINA(\"a\")", Value::errorNUM()); // Cell text inline is NOT ignored.

    // TODO check function - inline Text must be converted, but not Text in Cells
    //     CHECK_EVAL("MINA(B3:B5)",    Value(        0 ) ); // Cell text is converted to 0.

    CHECK_EVAL("MINA(B6:C6)", Value(1)); // The value "True" is considered equivalent to 1.
}

void TestStatisticalFunctions::testMODE()
{
    // ODF-tests
    CHECK_EVAL("MODE(F51:F60)", Value(4)); //
    CHECK_EVAL("MODE(G51;G52;G53;G54;G55;G56;G57;G58;G59;G60)", Value(24)); //
    CHECK_EVAL("MODE(1;2;3;4;5;6;7;8;9;10)", Value::errorNUM()); //
}

void TestStatisticalFunctions::testNEGBINOMDIST()
{
    // ODF-test
    //     CHECK_EVAL("NEGBINOMDIST(F20;I29;H6)", Value( 0.000130947 ) ); //

    // bettersolutions.com
    CHECK_EVAL("NEGBINOMDIST( 0;1; 0.25)", Value(0.25)); //
    CHECK_EVAL("NEGBINOMDIST( 0;1; 0.5)", Value(0.5)); //
    CHECK_EVAL("NEGBINOMDIST( 1;6; 0.5)", Value(0.046875)); //
    CHECK_EVAL("NEGBINOMDIST(10;5; 0.25)", Value(0.0550486604)); //
    CHECK_EVAL("NEGBINOMDIST(10;5;-4)", Value::errorNUM()); //
    //     CHECK_EVAL("NEGBINOMDIST(10;"text";0.25)", Value::NUM() ); //
}

void TestStatisticalFunctions::testNORMDIST()
{
    // ODF-tests
    CHECK_EVAL("NORMDIST(0;1;4;TRUE())", Value(0.4012936743)); //
    CHECK_EVAL("NORMDIST(0;0;1;FALSE())", Value(0.3989422804)); //
    CHECK_EVAL("NORMDIST(0;0;1;TRUE())", Value(0.5)); //
    CHECK_EVAL("NORMDIST(0;1;4;FALSE())", Value(0.0966670292)); //
    CHECK_EVAL("NORMDIST(0;-1;4;FALSE())", Value(0.0966670292)); //
    CHECK_EVAL("NORMDIST(0;-1;4;TRUE())", Value(0.5987063257)); //
    CHECK_EVAL("NORMDIST(1;-1;4;FALSE())", Value(0.0880163317)); //
    CHECK_EVAL("NORMDIST(1;-1;4;TRUE())", Value(0.6914624613)); //
    CHECK_EVAL("NORMDIST(1.281552;0;1;TRUE())", Value(0.9000000762)); //
    CHECK_EVAL("NORMDIST(0;-1.281552;1;TRUE())", Value(0.9000000762)); //
    CHECK_EVAL("NORMDIST(0;0;-1;FALSE())", Value::errorNUM()); //
}

void TestStatisticalFunctions::testNORMINV()
{
    // ODF-tests
    CHECK_EVAL("NORMDIST(NORMINV(0.1;0;1);0;1;TRUE())", Value(0.1)); //
    CHECK_EVAL("NORMDIST(NORMINV(0.3;0;1);0;1;TRUE())", Value(0.3)); //
    CHECK_EVAL("NORMDIST(NORMINV(0.5;0;1);0;1;TRUE())", Value(0.5)); //
    CHECK_EVAL("NORMDIST(NORMINV(0.7;0;1);0;1;TRUE())", Value(0.7)); //
    CHECK_EVAL("NORMDIST(NORMINV(0.9;0;1);0;1;TRUE())", Value(0.9)); //
    CHECK_EVAL("NORMDIST(NORMINV(0.1;1;4);1;4;TRUE())", Value(0.1)); //
    CHECK_EVAL("NORMDIST(NORMINV(0.3;1;4);1;4;TRUE())", Value(0.3)); //
    CHECK_EVAL("NORMDIST(NORMINV(0.5;1;4);1;4;TRUE())", Value(0.5)); //
    CHECK_EVAL("NORMDIST(NORMINV(0.7;1;4);1;4;TRUE())", Value(0.7)); //
    CHECK_EVAL("NORMDIST(NORMINV(0.9;1;4);1;4;TRUE())", Value(0.9)); //
}

void TestStatisticalFunctions::testPEARSON()
{
    //  Cell | Value       Cell | Value       Cell | Value       Cell | Value
    // ------+-------     ------+-------     ------+-------     ------+-------
    //  A19  |    1        C19  |  0           C51 |   7          D51 |  100
    //  A20  |    2        C20  |  5           C51 |   9          D52 |  105
    //  A21  |    4        C21  |  2           C53 |  11          D53 |  104
    //  A22  |    8        C22  |  5           C54 |  12          D54 |  108
    //  A23  |   16        C23  |  3           C55 |  15          D55 |  111
    //  A24  |   32        C24  |  4           C56 |  17          D56 |  120
    //  A25  |   64        C25  |  4           C57 |  19          D57 |  133
    //  A26  |  128        C26  |  0
    //  A27  |  256        C27  |  8
    //  A28  |  512        C28  |  1
    //  A29  | 1024        C29  |  9
    //  A30  | 2048        C30  |  6
    //  A31  | 4096        C31  |  2

    // ODF-tests
    CHECK_EVAL_SHORT("PEARSON(A19:A31;C19:C31)", Value(0.045989147)); //
    CHECK_EVAL_SHORT("PEARSON(C51:C57;D51:D57)", Value(0.930164207)); //
    CHECK_EVAL("PEARSON(C51:C57;D51:D56)", Value::errorNUM()); //
}

void TestStatisticalFunctions::testPERCENTILE()
{
    // ODF-tests
    CHECK_EVAL("PERCENTILE(A19:A31;0.38)", Value(24.96)); //
    CHECK_EVAL("PERCENTILE(A19:A31;0.95)", Value(2867.2)); //
    CHECK_EVAL("PERCENTILE(A19:A31;0.05)", Value(1.6)); //

    // my tests
    CHECK_EVAL("PERCENTILE(A10:A15;-0.1)", Value::errorVALUE()); //
    CHECK_EVAL("PERCENTILE(A19:A25;1.1)", Value::errorVALUE()); //
}

void TestStatisticalFunctions::testPERMUT()
{
    // ODF-tests
    CHECK_EVAL("PERMUT(2;2)", Value(2)); // =2!/(2-2)!
    CHECK_EVAL("PERMUT(4;2)", Value(12)); // =4!/(4-2)!
    CHECK_EVAL("PERMUT(4.3;2.1)", Value(12)); // =PERMUT(4;2)
    CHECK_EVAL("PERMUT(-4;2)", Value::errorNUM()); //
    CHECK_EVAL("PERMUT(4;-2)", Value::errorNUM()); //
}

void TestStatisticalFunctions::testPERMUTATIONA()
{
    // ODF-tests
    CHECK_EVAL("PERMUTATIONA(64;2)", Value(4096)); //
    CHECK_EVAL("PERMUTATIONA(6;3)", Value(216)); //

    // my tests
    CHECK_EVAL("PERMUTATIONA(0;0)", Value(1)); //
    CHECK_EVAL("PERMUTATIONA(-4;2)", Value::errorNUM()); //
    CHECK_EVAL("PERMUTATIONA(4;-2)", Value::errorNUM()); //
}

void TestStatisticalFunctions::testPHI()
{
    //  Cell | Value
    // ------+-------
    //   C23 |   3
    //       |

    // ODF-tests
    CHECK_EVAL_SHORT("PHI(C23/10)", Value(0.381387815)); // TODO - be more precise /
    CHECK_EVAL_SHORT("PHI(-C23/10)", Value(0.381387815)); // TODO - be more precise /
    CHECK_EVAL_SHORT("PHI(0)", Value(0.398942280)); // TODO - be more precise /
}

void TestStatisticalFunctions::testPOISSON()
{
    // ODF-tests
    CHECK_EVAL_SHORT("POISSON(0;1;FALSE())", Value(0.367880)); // TODO - be more precise /
    CHECK_EVAL_SHORT("POISSON(0;2;FALSE())", Value(0.135335)); // TODO - be more precise /
}

void TestStatisticalFunctions::testRANK()
{
    //  Cell | Value
    // ------+------
    //  A19  |   1
    //  A20  |   2
    //  A21  |   4
    //  A22  |   8
    //  A23  |  16
    //  A24  |  32
    //  A25  |  64

    // ODF-tests
    CHECK_EVAL("RANK(A20;A19:A25;1)", Value(2)); // ascending
    CHECK_EVAL("RANK(A25;A19:A25;0)", Value(1)); // descending
    CHECK_EVAL("RANK(A21;A19:A25  )", Value(5)); // omitted equals descending order
}

void TestStatisticalFunctions::testRSQ()
{
    // ODF-tests
    CHECK_EVAL("RSQ(H19:H31;I19:I31)", Value(0.075215010)); //
    CHECK_EVAL("RSQ(H19:H31;I19:I30)", Value::errorNA()); // array does not have the same size
}

void TestStatisticalFunctions::testQUARTILE()
{
    // flag:
    //  0 equals MIN()
    //  1 25th percentile
    //  2 50th percentile equals MEDIAN()
    //  3 75th percentile
    //  4 equals MAX()

    // ODF-tests
    CHECK_EVAL("QUARTILE(A19:A25;3)", Value(24)); //
    CHECK_EVAL("QUARTILE(F19:F26;1)", Value(-22.25)); //
    CHECK_EVAL("QUARTILE(A10:A15;2)", Value::errorVALUE()); //
    CHECK_EVAL("QUARTILE(A19:A25;5)", Value::errorVALUE()); // flag > 4
    CHECK_EVAL("QUARTILE(F19:F26;1.5)", Value(-22.25)); // 1.5 rounded down to 1
    CHECK_EVAL("QUARTILE({1;2;4;8;16;32;64};3)", Value(24)); //

    // my tests
    CHECK_EVAL("QUARTILE(A19:A25;0)", Value(1)); // MIN()
    CHECK_EVAL("QUARTILE(A19:A25;4)", Value(64)); // MAX()
}

void TestStatisticalFunctions::testSKEW()
{
    // ODF-tests
    CHECK_EVAL_SHORT("SKEW( 1; 2; 4 )", Value(0.935219)); // TODO - be more precise / Expectation value: 2.333333
    // Standard deviation: 1.257525
    // Third central moment: 0.740741
    CHECK_EVAL_SHORT("SKEW(A19:A23)", Value(1.325315)); // TODO - be more precise /
    CHECK_EVAL("SKEW( 1; 2 )", Value::errorNUM()); // At least three numbers.
}

void TestStatisticalFunctions::testSKEWP()
{
    // ODF-tests
    CHECK_EVAL_SHORT("SKEWP( 1; 2; 4 )", Value(0.381802)); // TODO - be more precise / Expectation value: 2.333333
    // Standard deviation: 1.247219
    // Third central moment: 0.740741
    CHECK_EVAL_SHORT("SKEWP(A19:A23)", Value(0.889048)); // TODO - be more precise /
    CHECK_EVAL("SKEW( 1; 2 )", Value::errorNUM()); // At least three numbers.
}

void TestStatisticalFunctions::testSLOPE()
{
    // ODF-tests
    CHECK_EVAL("SLOPE(B4:B5;C4:C5)", Value(1)); //
    CHECK_EVAL_SHORT("SLOPE(A19:A24;A26:A31)", Value(0.007813)); // TODO - be more precise /
}

void TestStatisticalFunctions::testSMALL()
{
    // ODF-tests
    CHECK_EVAL("SMALL(B14:B16;1)", Value(1)); //
    CHECK_EVAL("SMALL(B14:B16;3)", Value(3)); //
    CHECK_EVAL("SMALL(B14:B16;4)", Value::errorNUM()); // N is greater than the length of the list
}

void TestStatisticalFunctions::testSTANDARDIZE()
{
    // ODF-tests
    CHECK_EVAL("STANDARDIZE( 1; 2.5; 0.1 )", Value(-15)); //
    CHECK_EVAL("STANDARDIZE( -1; -2; 2 )", Value(0.5)); //
    CHECK_EVAL("STANDARDIZE( 1; 1; 0 )", Value::errorNUM()); // N is greater than the length of the list
}

void TestStatisticalFunctions::testSTDEV()
{
    // ODF-tests
    CHECK_EVAL("STDEV(2;4)/SQRT(2)", Value(1)); // The sample standard deviation of (2;4) is SQRT(2).
    CHECK_EVAL("STDEV(B4:B5)*SQRT(2)", Value(1)); // The sample standard deviation of (2;3) is 1/SQRT(2).
    CHECK_EVAL("STDEV(B3:B5)*SQRT(2)", Value(1)); // Strings are not converted to numbers and are ignored.
    CHECK_EVAL(
        "STDEV({10000000001;10000000002;"
        "10000000003;10000000004;10000000005;"
        "10000000006;10000000007;10000000008;"
        "10000000009;10000000010})",
        Value(3.0276503541)); // Ensure that implementations use a reasonably stable way of calculating STDEV.
    CHECK_EVAL("STDEV(1)", Value::errorNUM()); // At least two numbers must be included
}

void TestStatisticalFunctions::testSTDEVA()
{
    // ODF-tests
    CHECK_EVAL("STDEVA(2;4)/SQRT(2)", Value(1)); // The sample standard deviation of (2;4) is SQRT(2).
    CHECK_EVAL_SHORT("STDEVA(B5:C6)", Value(2.581989)); // TODO - be more precise / Logicals (referenced) are converted to numbers.
    CHECK_EVAL_SHORT("STDEVA( TRUE();FALSE() )", Value(0.707107)); // TODO - be more precise / Logicals (inlined) are converted to numbers.
    CHECK_EVAL("STDEVA(1)", Value::errorNUM()); // Logicals (inlined) are converted to numbers.
}

void TestStatisticalFunctions::testSTDEVP()
{
    // ODF-tests
    CHECK_EVAL("STDEVP(2;4)", Value(1)); // The standard deviation of the set for (2;4) is 1.
    CHECK_EVAL("STDEVP(B4:B5)*2", Value(1)); // The standard deviation of the set for (2;3) is 0.5.
    CHECK_EVAL("STDEVP(B3:B5)*2", Value(1)); // Strings are not converted to numbers and are ignored.
    CHECK_EVAL("STDEVP(1)", Value(0)); // STDEVP(1) is 0.
}

void TestStatisticalFunctions::testSTDEVPA()
{
    // ODF-tests
    CHECK_EVAL("STDEVPA(2;4)", Value(1)); // The sample standard deviation of (2;4) is 1.
    CHECK_EVAL_SHORT("STDEVPA(B5:C6)", Value(2.236068)); // TODO - be more precise / Logicals (referenced) are converted to numbers.
    CHECK_EVAL("STDEVPA(TRUE();FALSE())", Value(0.5)); // Logicals (inlined) are converted to numbers.
}

void TestStatisticalFunctions::testSTEYX()
{
    // ODF-tests
    CHECK_EVAL_SHORT("STEYX(C19:C23;A19:A23)", Value(2.370953)); // TODO - be more precise
    CHECK_EVAL("STEYX(A19:A23;A25:A29)", Value(0)); //
    CHECK_EVAL("STEYX(B4:B5;C4:C5)", Value::errorNUM()); // at least three number per sequence
}

void TestStatisticalFunctions::testSUMPRODUCT()
{
    CHECK_EVAL("SUMPRODUCT(C19:C23;A19:A23)", Value(106));
    //    CHECK_EVAL("SUMPRODUCT(C19:C23^2;2*A19:A23)", Value(820));
}

void TestStatisticalFunctions::testTDIST()
{
    // mode
    // 1 = one tailed distribution
    // 2 = two tailed distribution

    // ODF-tests
    CHECK_EVAL("TDIST( 0.5; 1; 1 )", Value(0.3524163823)); // ODF-specs -> 0.352416
    CHECK_EVAL_SHORT("TDIST( -1.5; 2; 2 )", Value(0.272393)); //  TODO - be more precise / OOo-2.3.0 returns error!!!
    CHECK_EVAL("TDIST( 0.5; 5; 1 )", Value(0.3191494358)); // ODF-specs -> 0.319149
    CHECK_EVAL("TDIST( 1; 1; 3 )", Value::errorNUM()); // mode = { 1; 2 }
    CHECK_EVAL("TDIST( 1; 0; 1 )", Value::errorNUM()); // degreeOfFreedom >= 1
}

void TestStatisticalFunctions::testTINV()
{
    // TODO - be more precise

    // ODF-tests
    CHECK_EVAL("TINV( 1; 2 )", Value(0)); // p=1 -> t=0
    CHECK_EVAL_SHORT("TINV( 0.5; 2 )", Value(0.816497)); //
    CHECK_EVAL("TDIST( TINV(0.25;3); 3;2 )", Value(0.25)); //
    CHECK_EVAL("TDIST( TINV(0.5 ;3); 3;2 )", Value(0.5)); //
    CHECK_EVAL("TDIST( TINV(0.75;3); 3;2 )", Value(0.75)); //
    CHECK_EVAL("TDIST( 2; 3 )", Value::errorNUM()); // 0 <= probability <= 1
    CHECK_EVAL("TDIST( 1; 0 )", Value::errorNUM()); // degreeOfFreedom >= 1
}

void TestStatisticalFunctions::testTREND()
{
    //  Cell | Value      Cell | Value
    // ------+------     ------+------
    //  A19  |   1        C19  |  0
    //  A20  |   2        C20  |  5
    //  A21  |   4        C21  |  2
    //  A22  |   8        C22  |  5
    //  A23  |  16        C23  |  3

    CHECK_ARRAY("TREND(A19:A23; C19:C23; 1)     ", "{4.7555555555}"); // with    offset
    CHECK_ARRAY("TREND(A19:A23; C19:C23; 1; 0 ) ", "{1.6825396825}"); // without offset
}

void TestStatisticalFunctions::testTRIMMEAN()
{
    // ODF-tests
    CHECK_EVAL("TRIMMEAN(A19:A23; 0.8 )", Value(4)); // cutOff = 2
    CHECK_EVAL("TRIMMEAN(A19:A23; 0.6 )", Value(4.6666666666)); // cutOff = FLOOR(5 * 0.6/ 2) = FLOOR(1.5) = 1;
    // result = 14 / 3
    CHECK_EVAL("TRIMMEAN(A19:A23; 0.19 )", Value(6.2)); // cutOff = 0
    CHECK_EVAL("TRIMMEAN(A19:A23; 0.999999 )", Value(4)); // cutOff = 2
    CHECK_EVAL("TRIMMEAN(A19:A23; 1)", Value::errorNUM()); // 0 <= cutOffFraction < 1
}

void TestStatisticalFunctions::testTTEST()
{
    // ODF-tests
    CHECK_EVAL("TTEST(A19:A23;A24:A28; 1; 1 )", Value(0.0427206184)); //
    CHECK_EVAL("TTEST(A19:A23;A24:A28; 2; 1 )", Value(0.0854412368)); //
    CHECK_EVAL("TTEST(A19:A23;A24:A28; 1; 2 )", Value(0.0294544970)); //
    CHECK_EVAL("TTEST(A19:A23;A24:A28; 1; 3 )", Value(0.0462125526)); //
    CHECK_EVAL("TTEST(A19:A23;A24:A29; 1; 1 )", Value::errorNUM()); // same amount of numbers for paired samples
    CHECK_EVAL("TTEST(A19:A19;A24:A24; 1; 3 )", Value::errorNUM()); // two numbers at least for each sequence
}

void TestStatisticalFunctions::testVAR()
{
    // ODF-tests
    CHECK_EVAL("VAR(2;4)", Value(2)); // The sample variance of (2;4) is 2.
    CHECK_EVAL("VAR(B4:B5)*2", Value(1)); // The sample variance of (2;3) is 0.5.
    CHECK_EVAL("VAR(B3:B5)*2", Value(1)); // Strings are not converted to numbers and are ignored.
    CHECK_EVAL("VAR(1)", Value::errorNUM()); // At least two numbers must be included
}

void TestStatisticalFunctions::testVARA()
{
    // ODF-tests
    CHECK_EVAL("VARA(2;4)", Value(2)); // The sample variance of (2;4) is 2.
    CHECK_EVAL("VARA(B5:C6)", Value(6.6666666667)); // Logicals (referenced) are converted to numbers.
    CHECK_EVAL("VARA(TRUE();FALSE())", Value(0.5)); // Logicals (inlined) are converted to numbers.
    CHECK_EVAL("VARA(1)", Value::errorNUM()); // Two numbers at least.
}

void TestStatisticalFunctions::testVARIANCE()
{
    // same as VAR

    // ODF-tests
    CHECK_EVAL("VARIANCE(2;4)", Value(2)); // The sample variance of (2;4) is 2.
    CHECK_EVAL("VARIANCE(B4:B5)*2", Value(1)); // The sample variance of (2;3) is 0.5.
    CHECK_EVAL("VARIANCE(B3:B5)*2", Value(1)); // Strings are not converted to numbers and are ignored.
    CHECK_EVAL("VARIANCE(1)", Value::errorNUM()); // At least two numbers must be included
}

void TestStatisticalFunctions::testVARP()
{
    //  Cell | Value
    // ------+-------
    //   B3  |  "7"
    //   B4  |   2
    //   B5  |   3

    // ODF-tests
    CHECK_EVAL("VARP(2;4)", Value(1)); // The variance of the set for (2;4) is 1.
    CHECK_EVAL("VARP(B4:B5)*4", Value(1)); // The variance of the set for (2;3) is 0.25.
    CHECK_EVAL("VARP(B3:B5)*4", Value(1)); // Strings are not converted to numbers and are ignored.
}

void TestStatisticalFunctions::testVARPA()
{
    //  Cell | Value      Cell | Value
    // ------+------     ------+------
    //   B5  |   3         C5  |  5
    //   B6  | true        C6  |  7

    // ODF-tests
    CHECK_EVAL("VARPA(2;4)", Value(1)); // The sample variance of (2;4) is 1.
    CHECK_EVAL("VARPA(B5:C6)", Value(5)); // Logicals (referenced) are converted to numbers.
    CHECK_EVAL("VARPA(TRUE();FALSE())", Value(0.25)); // Logicals (inlined) are converted to numbers.
}

void TestStatisticalFunctions::testWEIBULL()
{
    // TODO - be more precise

    // ODF-tests
    CHECK_EVAL_SHORT("WEIBULL(  2; 3; 4; 0 )", Value(0.165468)); // pdf
    CHECK_EVAL_SHORT("WEIBULL(  2; 3; 4; 1 )", Value(0.117503)); // cdf
    CHECK_EVAL_SHORT("WEIBULL( -1; 3; 4; 0 )", Value::errorNUM()); // value >= 0
    CHECK_EVAL_SHORT("WEIBULL(  2; 0; 4; 0 )", Value::errorNUM()); // alpha > 0
    CHECK_EVAL_SHORT("WEIBULL(  2; 3; 0; 0 )", Value::errorNUM()); // beta > 0
}

void TestStatisticalFunctions::testZTEST()
{
    // ODF-tests
    CHECK_EVAL("ZTEST(B4:C5; 3.5      )", Value(0)); // mean = average, estimated standard deviation: fits well
    CHECK_EVAL("ZTEST(B4:C5; 3  ; 2   )", Value(0.3829249225)); // mean near average, standard deviation greater than estimate: probable
    CHECK_EVAL("ZTEST(B4:C5; 4  ; 0.5 )", Value(0.9544997361)); // mean near the average, but small deviation: not probable
    CHECK_EVAL("ZTEST(B4:C5; 5        )", Value(0.9798632484)); // mean at a border value, standard deviation ~ 1,3: nearly improbable
    CHECK_EVAL("ZTEST(B4:C5; 5  ; 0.1 )", Value(1)); // mean at a border value, small standard deviation: improbable
}

void TestStatisticalFunctions::cleanupTestCase()
{
    delete m_map;
}

QTEST_MAIN(TestStatisticalFunctions)
