File indexing completed on 2024-04-28 03:48:07

0001 /*
0002     File                 : ColumnTest.cpp
0003     Project              : LabPlot
0004     Description          : Tests for Column
0005     --------------------------------------------------------------------
0006     SPDX-FileCopyrightText: 2022 Martin Marmsoler <martin.marmsoler@gmail.com>
0007     SPDX-FileCopyrightText: 2022-2023 Stefan Gerlach <stefan.gerlach@uni.kn>
0008     SPDX-FileCopyrightText: 2022-2023 Alexander Semke <alexander.semke@web.de>
0009 
0010     SPDX-License-Identifier: GPL-2.0-or-later
0011 */
0012 
0013 #include "ColumnTest.h"
0014 #include "backend/core/Project.h"
0015 #include "backend/core/column/Column.h"
0016 #include "backend/core/column/ColumnPrivate.h"
0017 #include "backend/lib/XmlStreamReader.h"
0018 #include "backend/lib/trace.h"
0019 #include "backend/spreadsheet/Spreadsheet.h"
0020 
0021 #include <QUndoStack>
0022 
0023 #define SETUP_C1_C2_COLUMNS(c1Vector, c2Vector)                                                                                                                \
0024     auto c1 = Column(QStringLiteral("DataColumn"), Column::ColumnMode::Double);                                                                                \
0025     c1.replaceValues(-1, c1Vector);                                                                                                                            \
0026     auto c2 = Column(QStringLiteral("FormulaColumn"), Column::ColumnMode::Double);                                                                             \
0027     c2.replaceValues(-1, c2Vector);
0028 
0029 #define COLUMN2_SET_FORMULA_AND_EVALUATE(formula, result)                                                                                                      \
0030     c2.setFormula(QStringLiteral(formula), {QStringLiteral("x")}, QVector<Column*>({&c1}), true);                                                              \
0031     c2.updateFormula();                                                                                                                                        \
0032     QCOMPARE(c2.rowCount(), 8);                                                                                                                                \
0033     for (int i = 0; i < c2.rowCount(); i++)                                                                                                                    \
0034         VALUES_EQUAL(c2.valueAt(i), result);
0035 
0036 void ColumnTest::initTestCase() {
0037     // needed in order to have the signals triggered by SignallingUndoCommand, see LabPlot.cpp
0038     // TODO: redesign/remove this
0039     qRegisterMetaType<const AbstractAspect*>("const AbstractAspect*");
0040     qRegisterMetaType<const AbstractColumn*>("const AbstractColumn*");
0041 }
0042 
0043 void ColumnTest::doubleMinimum() {
0044     Column c(QStringLiteral("Double column"), Column::ColumnMode::Double);
0045     c.setValues({-1.0, 2.0, 5.0});
0046     QCOMPARE(c.properties(), Column::Properties::MonotonicIncreasing);
0047     QCOMPARE(c.minimum(0, 2), -1.0);
0048     QCOMPARE(c.minimum(1, 2), 2.0);
0049 
0050     c.setValues({-1.0, -3.0, -4.0});
0051     QCOMPARE(c.properties(), Column::Properties::MonotonicDecreasing);
0052     QCOMPARE(c.minimum(0, 2), -4.0);
0053     QCOMPARE(c.minimum(1, 2), -4.0);
0054 
0055     c.setValues({-1.0, 2.0, -4.0});
0056     QCOMPARE(c.properties(), Column::Properties::NonMonotonic);
0057     QCOMPARE(c.minimum(0, 2), -4.0);
0058     QCOMPARE(c.minimum(0, 1), -1.0);
0059 }
0060 
0061 void ColumnTest::doubleMaximum() {
0062     Column c(QStringLiteral("Double column"), Column::ColumnMode::Double);
0063     c.setValues({-1.0, 2.0, 5.0});
0064     QCOMPARE(c.maximum(0, 2), 5.0);
0065     QCOMPARE(c.maximum(1, 1), 2.0);
0066 
0067     c.setValues({-1.0, -3.0, -4.0});
0068     QCOMPARE(c.maximum(0, 2), -1.0);
0069     QCOMPARE(c.maximum(1, 2), -3.0);
0070 
0071     c.setValues({-1.0, 2.0, -4.0});
0072     QCOMPARE(c.maximum(0, 2), 2.0);
0073     QCOMPARE(c.maximum(0, 1), 2.0);
0074 }
0075 
0076 void ColumnTest::integerMinimum() {
0077     Column c(QStringLiteral("Integer column"), Column::ColumnMode::Integer);
0078     c.setIntegers({-1, 2, 5});
0079     QCOMPARE(c.properties(), Column::Properties::MonotonicIncreasing);
0080     QCOMPARE(c.minimum(0, 2), -1);
0081     QCOMPARE(c.minimum(1, 2), 2);
0082 
0083     c.setIntegers({-1, -3, -4});
0084     QCOMPARE(c.properties(), Column::Properties::MonotonicDecreasing);
0085     QCOMPARE(c.minimum(0, 2), -4);
0086     QCOMPARE(c.minimum(1, 2), -4);
0087 
0088     c.setIntegers({-1, 2, -4});
0089     QCOMPARE(c.properties(), Column::Properties::NonMonotonic);
0090     QCOMPARE(c.minimum(0, 2), -4);
0091     QCOMPARE(c.minimum(0, 1), -1);
0092 }
0093 
0094 void ColumnTest::integerMaximum() {
0095     Column c(QStringLiteral("Integer column"), Column::ColumnMode::Integer);
0096     c.setIntegers({-1, 2, 5});
0097     QCOMPARE(c.maximum(0, 2), 5);
0098     QCOMPARE(c.maximum(1, 1), 2);
0099 
0100     c.setIntegers({-1, -3, -4});
0101     QCOMPARE(c.maximum(0, 2), -1);
0102     QCOMPARE(c.maximum(1, 2), -3);
0103 
0104     c.setIntegers({-1, 2, -4});
0105     QCOMPARE(c.maximum(0, 2), 2);
0106     QCOMPARE(c.maximum(0, 1), 2);
0107 }
0108 
0109 void ColumnTest::bigIntMinimum() {
0110     Column c(QStringLiteral("BigInt column"), Column::ColumnMode::BigInt);
0111     c.setBigInts({-1, 2, 5});
0112     QCOMPARE(c.properties(), Column::Properties::MonotonicIncreasing);
0113     QCOMPARE(c.minimum(0, 2), -1);
0114     QCOMPARE(c.minimum(1, 2), 2);
0115 
0116     c.setBigInts({-1, -3, -4});
0117     QCOMPARE(c.properties(), Column::Properties::MonotonicDecreasing);
0118     QCOMPARE(c.minimum(0, 2), -4);
0119     QCOMPARE(c.minimum(1, 2), -4);
0120 
0121     c.setBigInts({-1, 2, -4});
0122     QCOMPARE(c.properties(), Column::Properties::NonMonotonic);
0123     QCOMPARE(c.minimum(0, 2), -4);
0124     QCOMPARE(c.minimum(0, 1), -1);
0125 }
0126 
0127 void ColumnTest::bigIntMaximum() {
0128     Column c(QStringLiteral("BigInt column"), Column::ColumnMode::BigInt);
0129     c.setBigInts({-1, 2, 5});
0130     QCOMPARE(c.maximum(0, 2), 5);
0131     QCOMPARE(c.maximum(0, 1), 2);
0132 
0133     c.setBigInts({-1, -3, -4});
0134     QCOMPARE(c.maximum(0, 2), -1);
0135     QCOMPARE(c.maximum(1, 2), -3);
0136 
0137     c.setBigInts({-1, 2, -4});
0138     QCOMPARE(c.maximum(0, 2), 2);
0139     QCOMPARE(c.maximum(0, 1), 2);
0140 }
0141 
0142 /////////////////////////////////////////////////////
0143 
0144 void ColumnTest::statisticsDouble() {
0145     Column c(QStringLiteral("Double column"), Column::ColumnMode::Double);
0146     c.setValues({1.0, 1.0, 2.0, 5.0});
0147 
0148     auto& stats = c.statistics();
0149 
0150     QCOMPARE(stats.size, 4);
0151     QCOMPARE(stats.minimum, 1.);
0152     QCOMPARE(stats.maximum, 5.);
0153     QCOMPARE(stats.arithmeticMean, 2.25);
0154     QCOMPARE(stats.geometricMean, pow(10., 0.25));
0155     QCOMPARE(stats.harmonicMean, 40. / 27.);
0156     QCOMPARE(stats.contraharmonicMean, 31. / 9.);
0157 
0158     QCOMPARE(stats.mode, 1.);
0159     QCOMPARE(stats.firstQuartile, 1.);
0160     QCOMPARE(stats.median, 1.5);
0161     QCOMPARE(stats.thirdQuartile, 2.75);
0162     QCOMPARE(stats.iqr, 1.75);
0163     QCOMPARE(stats.percentile_1, 1.);
0164     QCOMPARE(stats.percentile_5, 1.);
0165     QCOMPARE(stats.percentile_10, 1.);
0166     QCOMPARE(stats.percentile_90, 4.1);
0167     QCOMPARE(stats.percentile_95, 4.55);
0168     QCOMPARE(stats.percentile_99, 4.91);
0169     QCOMPARE(stats.trimean, 1.6875);
0170     QCOMPARE(stats.variance, 3.58333333333);
0171     QCOMPARE(stats.standardDeviation, 1.8929694486);
0172     QCOMPARE(stats.meanDeviation, 1.375);
0173     QCOMPARE(stats.meanDeviationAroundMedian, 1.25);
0174     QCOMPARE(stats.medianDeviation, 0.5);
0175     QCOMPARE(stats.skewness, 0.95754916255356);
0176     QCOMPARE(stats.kurtosis, 2.1487290427258);
0177     QCOMPARE(stats.entropy, 1.5);
0178 }
0179 void ColumnTest::statisticsDoubleNegative() {
0180     Column c(QStringLiteral("Double column"), Column::ColumnMode::Double);
0181     c.setValues({-1.0, 0.0, 2.0, 5.0});
0182 
0183     auto& stats = c.statistics();
0184 
0185     QCOMPARE(stats.size, 4);
0186     QCOMPARE(stats.minimum, -1.);
0187     QCOMPARE(stats.maximum, 5.);
0188     QCOMPARE(stats.arithmeticMean, 1.5);
0189     QCOMPARE(stats.geometricMean, 1.474323891188); // special case
0190     QCOMPARE(stats.harmonicMean, 0.);
0191     QCOMPARE(stats.contraharmonicMean, 5.);
0192 
0193     QCOMPARE(stats.mode, NAN);
0194     QCOMPARE(stats.firstQuartile, -.25);
0195     QCOMPARE(stats.median, 1.);
0196     QCOMPARE(stats.thirdQuartile, 2.75);
0197     QCOMPARE(stats.iqr, 3.);
0198     QCOMPARE(stats.percentile_1, -.97);
0199     QCOMPARE(stats.percentile_5, -.85);
0200     QCOMPARE(stats.percentile_10, -.7);
0201     QCOMPARE(stats.percentile_90, 4.1);
0202     QCOMPARE(stats.percentile_95, 4.55);
0203     QCOMPARE(stats.percentile_99, 4.91);
0204     QCOMPARE(stats.trimean, 1.125);
0205     QCOMPARE(stats.variance, 7.);
0206     QCOMPARE(stats.standardDeviation, 2.64575131106459);
0207     QCOMPARE(stats.meanDeviation, 2.);
0208     QCOMPARE(stats.meanDeviationAroundMedian, 2.);
0209     QCOMPARE(stats.medianDeviation, 1.5);
0210     QCOMPARE(stats.skewness, 0.49878374911084);
0211     QCOMPARE(stats.kurtosis, 1.7619047619048);
0212     QCOMPARE(stats.entropy, 2.);
0213 }
0214 void ColumnTest::statisticsDoubleBigNegative() {
0215     Column c(QStringLiteral("Double column"), Column::ColumnMode::Double);
0216     c.setValues({-100.0, 0.0, 2.0, 5.0});
0217 
0218     auto& stats = c.statistics();
0219 
0220     QCOMPARE(stats.size, 4);
0221     QCOMPARE(stats.minimum, -100.);
0222     QCOMPARE(stats.maximum, 5.);
0223     QCOMPARE(stats.arithmeticMean, -23.25);
0224     QCOMPARE(stats.geometricMean, NAN); // special case
0225     QCOMPARE(stats.harmonicMean, 0.);
0226     QCOMPARE(stats.contraharmonicMean, -3343. / 31.);
0227 
0228     QCOMPARE(stats.mode, NAN);
0229     QCOMPARE(stats.firstQuartile, -25.);
0230     QCOMPARE(stats.median, 1.);
0231     QCOMPARE(stats.thirdQuartile, 2.75);
0232     QCOMPARE(stats.iqr, 27.75);
0233     QCOMPARE(stats.percentile_1, -97.);
0234     QCOMPARE(stats.percentile_5, -85.);
0235     QCOMPARE(stats.percentile_10, -70.);
0236     QCOMPARE(stats.percentile_90, 4.1);
0237     QCOMPARE(stats.percentile_95, 4.55);
0238     QCOMPARE(stats.percentile_99, 4.91);
0239     QCOMPARE(stats.trimean, -5.0625);
0240     QCOMPARE(stats.variance, 2622.25);
0241     QCOMPARE(stats.standardDeviation, 51.2079095453036);
0242     QCOMPARE(stats.meanDeviation, 38.375);
0243     QCOMPARE(stats.meanDeviationAroundMedian, 26.75);
0244     QCOMPARE(stats.medianDeviation, 2.5);
0245     QCOMPARE(stats.skewness, -1.1491083404244);
0246     QCOMPARE(stats.kurtosis, 2.3290867987696);
0247     QCOMPARE(stats.entropy, 2.);
0248 }
0249 void ColumnTest::statisticsDoubleZero() {
0250     Column c(QStringLiteral("Double column"), Column::ColumnMode::Double);
0251     c.setValues({1.0, 0.0, 2.0, 5.0});
0252 
0253     auto& stats = c.statistics();
0254 
0255     QCOMPARE(stats.size, 4);
0256     QCOMPARE(stats.minimum, 0.);
0257     QCOMPARE(stats.maximum, 5.);
0258     QCOMPARE(stats.arithmeticMean, 2.);
0259     QCOMPARE(stats.geometricMean, 1.77827941003892); // special case
0260     QCOMPARE(stats.harmonicMean, 0.);
0261     QCOMPARE(stats.contraharmonicMean, 3.75);
0262 
0263     QCOMPARE(stats.mode, NAN);
0264     QCOMPARE(stats.firstQuartile, 0.75);
0265     QCOMPARE(stats.median, 1.5);
0266     QCOMPARE(stats.thirdQuartile, 2.75);
0267     QCOMPARE(stats.iqr, 2.);
0268     QCOMPARE(stats.percentile_1, 0.03);
0269     QCOMPARE(stats.percentile_5, 0.15);
0270     QCOMPARE(stats.percentile_10, 0.3);
0271     QCOMPARE(stats.percentile_90, 4.1);
0272     QCOMPARE(stats.percentile_95, 4.55);
0273     QCOMPARE(stats.percentile_99, 4.91);
0274     QCOMPARE(stats.trimean, 1.625);
0275     QCOMPARE(stats.variance, 4.6666666666667);
0276     QCOMPARE(stats.standardDeviation, 2.16024689946929);
0277     QCOMPARE(stats.meanDeviation, 1.5);
0278     QCOMPARE(stats.meanDeviationAroundMedian, 1.5);
0279     QCOMPARE(stats.medianDeviation, 1.);
0280     QCOMPARE(stats.skewness, 0.68724319348909);
0281     QCOMPARE(stats.kurtosis, 2.);
0282     QCOMPARE(stats.entropy, 2.);
0283 }
0284 
0285 void ColumnTest::statisticsInt() {
0286     Column c(QStringLiteral("Integer column"), Column::ColumnMode::Integer);
0287     c.setIntegers({1, 1, 2, 5});
0288 
0289     auto& stats = c.statistics();
0290 
0291     QCOMPARE(stats.size, 4);
0292     QCOMPARE(stats.minimum, 1.);
0293     QCOMPARE(stats.maximum, 5.);
0294     QCOMPARE(stats.arithmeticMean, 2.25);
0295     QCOMPARE(stats.geometricMean, pow(10., 0.25));
0296     QCOMPARE(stats.harmonicMean, 40. / 27.);
0297     QCOMPARE(stats.contraharmonicMean, 31. / 9.);
0298 
0299     QCOMPARE(stats.mode, 1.);
0300     QCOMPARE(stats.firstQuartile, 1.);
0301     QCOMPARE(stats.median, 1.5);
0302     QCOMPARE(stats.thirdQuartile, 2.75);
0303     QCOMPARE(stats.iqr, 1.75);
0304     QCOMPARE(stats.percentile_1, 1.);
0305     QCOMPARE(stats.percentile_5, 1.);
0306     QCOMPARE(stats.percentile_10, 1.);
0307     QCOMPARE(stats.percentile_90, 4.1);
0308     QCOMPARE(stats.percentile_95, 4.55);
0309     QCOMPARE(stats.percentile_99, 4.91);
0310     QCOMPARE(stats.trimean, 1.6875);
0311     QCOMPARE(stats.variance, 3.58333333333);
0312     QCOMPARE(stats.standardDeviation, 1.8929694486);
0313     QCOMPARE(stats.meanDeviation, 1.375);
0314     QCOMPARE(stats.meanDeviationAroundMedian, 1.25);
0315     QCOMPARE(stats.medianDeviation, 0.5);
0316     QCOMPARE(stats.skewness, 0.95754916255356);
0317     QCOMPARE(stats.kurtosis, 2.1487290427258);
0318     QCOMPARE(stats.entropy, 1.5);
0319 }
0320 void ColumnTest::statisticsIntNegative() {
0321     Column c(QStringLiteral("Integer column"), Column::ColumnMode::Integer);
0322     c.setIntegers({-1, 0, 2, 5});
0323 
0324     auto& stats = c.statistics();
0325 
0326     QCOMPARE(stats.size, 4);
0327     QCOMPARE(stats.minimum, -1.);
0328     QCOMPARE(stats.maximum, 5.);
0329     QCOMPARE(stats.arithmeticMean, 1.5);
0330     QCOMPARE(stats.geometricMean, 1.474323891188); // special case
0331     QCOMPARE(stats.harmonicMean, 0.);
0332     QCOMPARE(stats.contraharmonicMean, 5.);
0333 
0334     QCOMPARE(stats.mode, NAN);
0335     QCOMPARE(stats.firstQuartile, -.25);
0336     QCOMPARE(stats.median, 1.);
0337     QCOMPARE(stats.thirdQuartile, 2.75);
0338     QCOMPARE(stats.iqr, 3.);
0339     QCOMPARE(stats.percentile_1, -.97);
0340     QCOMPARE(stats.percentile_5, -.85);
0341     QCOMPARE(stats.percentile_10, -.7);
0342     QCOMPARE(stats.percentile_90, 4.1);
0343     QCOMPARE(stats.percentile_95, 4.55);
0344     QCOMPARE(stats.percentile_99, 4.91);
0345     QCOMPARE(stats.trimean, 1.125);
0346     QCOMPARE(stats.variance, 7.);
0347     QCOMPARE(stats.standardDeviation, 2.64575131106459);
0348     QCOMPARE(stats.meanDeviation, 2.);
0349     QCOMPARE(stats.meanDeviationAroundMedian, 2.);
0350     QCOMPARE(stats.medianDeviation, 1.5);
0351     QCOMPARE(stats.skewness, 0.49878374911084);
0352     QCOMPARE(stats.kurtosis, 1.7619047619048);
0353     QCOMPARE(stats.entropy, 2.);
0354 }
0355 void ColumnTest::statisticsIntBigNegative() {
0356     Column c(QStringLiteral("Integer column"), Column::ColumnMode::Integer);
0357     c.setIntegers({-100, 0, 2, 5});
0358 
0359     auto& stats = c.statistics();
0360 
0361     QCOMPARE(stats.size, 4);
0362     QCOMPARE(stats.minimum, -100.);
0363     QCOMPARE(stats.maximum, 5.);
0364     QCOMPARE(stats.arithmeticMean, -23.25);
0365     QCOMPARE(stats.geometricMean, NAN); // special case
0366     QCOMPARE(stats.harmonicMean, 0.);
0367     QCOMPARE(stats.contraharmonicMean, -3343. / 31.);
0368 
0369     QCOMPARE(stats.mode, NAN);
0370     QCOMPARE(stats.firstQuartile, -25.);
0371     QCOMPARE(stats.median, 1.);
0372     QCOMPARE(stats.thirdQuartile, 2.75);
0373     QCOMPARE(stats.iqr, 27.75);
0374     QCOMPARE(stats.percentile_1, -97.);
0375     QCOMPARE(stats.percentile_5, -85.);
0376     QCOMPARE(stats.percentile_10, -70.);
0377     QCOMPARE(stats.percentile_90, 4.1);
0378     QCOMPARE(stats.percentile_95, 4.55);
0379     QCOMPARE(stats.percentile_99, 4.91);
0380     QCOMPARE(stats.trimean, -5.0625);
0381     QCOMPARE(stats.variance, 2622.25);
0382     QCOMPARE(stats.standardDeviation, 51.2079095453036);
0383     QCOMPARE(stats.meanDeviation, 38.375);
0384     QCOMPARE(stats.meanDeviationAroundMedian, 26.75);
0385     QCOMPARE(stats.medianDeviation, 2.5);
0386     QCOMPARE(stats.skewness, -1.1491083404244);
0387     QCOMPARE(stats.kurtosis, 2.3290867987696);
0388     QCOMPARE(stats.entropy, 2.);
0389 }
0390 void ColumnTest::statisticsIntZero() {
0391     Column c(QStringLiteral("Integer column"), Column::ColumnMode::Integer);
0392     c.setIntegers({1, 0, 2, 5});
0393 
0394     auto& stats = c.statistics();
0395 
0396     QCOMPARE(stats.size, 4);
0397     QCOMPARE(stats.minimum, 0.);
0398     QCOMPARE(stats.maximum, 5.);
0399     QCOMPARE(stats.arithmeticMean, 2.);
0400     QCOMPARE(stats.geometricMean, 1.77827941003892); // special case
0401     QCOMPARE(stats.harmonicMean, 0.);
0402     QCOMPARE(stats.contraharmonicMean, 3.75);
0403 
0404     QCOMPARE(stats.mode, NAN);
0405     QCOMPARE(stats.firstQuartile, 0.75);
0406     QCOMPARE(stats.median, 1.5);
0407     QCOMPARE(stats.thirdQuartile, 2.75);
0408     QCOMPARE(stats.iqr, 2.);
0409     QCOMPARE(stats.percentile_1, 0.03);
0410     QCOMPARE(stats.percentile_5, 0.15);
0411     QCOMPARE(stats.percentile_10, 0.3);
0412     QCOMPARE(stats.percentile_90, 4.1);
0413     QCOMPARE(stats.percentile_95, 4.55);
0414     QCOMPARE(stats.percentile_99, 4.91);
0415     QCOMPARE(stats.trimean, 1.625);
0416     QCOMPARE(stats.variance, 4.6666666666667);
0417     QCOMPARE(stats.standardDeviation, 2.16024689946929);
0418     QCOMPARE(stats.meanDeviation, 1.5);
0419     QCOMPARE(stats.meanDeviationAroundMedian, 1.5);
0420     QCOMPARE(stats.medianDeviation, 1.);
0421     QCOMPARE(stats.skewness, 0.68724319348909);
0422     QCOMPARE(stats.kurtosis, 2.);
0423     QCOMPARE(stats.entropy, 2.);
0424 }
0425 void ColumnTest::statisticsIntOverflow() {
0426     Column c(QStringLiteral("Integer column"), Column::ColumnMode::Integer);
0427     c.setIntegers({1000000000, 1100000000, 1200000000, 1300000000});
0428 
0429     auto& stats = c.statistics();
0430 
0431     QCOMPARE(stats.size, 4);
0432     QCOMPARE(stats.minimum, 1000000000);
0433     QCOMPARE(stats.maximum, 1300000000);
0434     QCOMPARE(stats.arithmeticMean, 1150000000);
0435     QCOMPARE(stats.geometricMean, 1144535640.12);
0436     QCOMPARE(stats.harmonicMean, 1139064055.75838);
0437     QCOMPARE(stats.contraharmonicMean, 1160869565.21739);
0438 
0439     QCOMPARE(stats.mode, NAN);
0440     QCOMPARE(stats.firstQuartile, 1075000000);
0441     QCOMPARE(stats.median, 1150000000);
0442     QCOMPARE(stats.thirdQuartile, 1225000000);
0443     QCOMPARE(stats.iqr, 150000000);
0444     QCOMPARE(stats.percentile_1, 1003000000);
0445     QCOMPARE(stats.percentile_5, 1015000000);
0446     QCOMPARE(stats.percentile_10, 1030000000);
0447     QCOMPARE(stats.percentile_90, 1270000000);
0448     QCOMPARE(stats.percentile_95, 1285000000);
0449     QCOMPARE(stats.percentile_99, 1297000000);
0450     QCOMPARE(stats.trimean, 1150000000);
0451     QCOMPARE(stats.variance, 1.66666666666667e+16);
0452     QCOMPARE(stats.standardDeviation, 129099444.873581);
0453     QCOMPARE(stats.meanDeviation, 100000000);
0454     QCOMPARE(stats.meanDeviationAroundMedian, 100000000);
0455     QCOMPARE(stats.medianDeviation, 100000000);
0456     QCOMPARE(stats.skewness, 0.);
0457     QCOMPARE(stats.kurtosis, 1.64);
0458     QCOMPARE(stats.entropy, 2.);
0459 }
0460 void ColumnTest::statisticsBigInt() {
0461     Column c(QStringLiteral("BigInt column"), Column::ColumnMode::BigInt);
0462     c.setBigInts({-10000000000, 0, 1000000000, 10000000000});
0463 
0464     auto& stats = c.statistics();
0465 
0466     QCOMPARE(stats.size, 4);
0467     QCOMPARE(stats.minimum, -10000000000);
0468     QCOMPARE(stats.maximum, 10000000000);
0469     QCOMPARE(stats.arithmeticMean, 250000000);
0470     QCOMPARE(stats.geometricMean, NAN);
0471     QCOMPARE(stats.harmonicMean, 0.);
0472     QCOMPARE(stats.contraharmonicMean, 201000000000);
0473 
0474     QCOMPARE(stats.mode, NAN);
0475 // Windows CI fails here
0476 #ifndef HAVE_WINDOWS
0477     QCOMPARE(stats.firstQuartile, -2500000000);
0478     QCOMPARE(stats.median, 500000000);
0479     QCOMPARE(stats.thirdQuartile, 3250000000);
0480     QCOMPARE(stats.iqr, 5750000000);
0481     QCOMPARE(stats.percentile_1, -9700000000);
0482     QCOMPARE(stats.percentile_5, -8500000000);
0483     QCOMPARE(stats.percentile_10, -7000000000);
0484     FuzzyCompare(stats.percentile_90, 7300000000.);
0485     FuzzyCompare(stats.percentile_95, 8650000000.);
0486     FuzzyCompare(stats.percentile_99, 9730000000.);
0487     QCOMPARE(stats.trimean, 437500000);
0488     QCOMPARE(stats.variance, 6.69166666666667e+19);
0489     QCOMPARE(stats.standardDeviation, 8180260794.53868);
0490     QCOMPARE(stats.meanDeviation, 5250000000);
0491     QCOMPARE(stats.meanDeviationAroundMedian, 5250000000);
0492     QCOMPARE(stats.medianDeviation, 5000000000);
0493     QCOMPARE(stats.skewness, -0.10520849985915);
0494     QCOMPARE(stats.kurtosis, 1.9925605877089);
0495     QCOMPARE(stats.entropy, 2.);
0496 #endif
0497 }
0498 
0499 void ColumnTest::statisticsText() {
0500     Column c(QStringLiteral("Text column"), Column::ColumnMode::Text);
0501     c.setTextAt(0, QStringLiteral("yes"));
0502     c.setTextAt(1, QStringLiteral("no"));
0503     c.setTextAt(2, QStringLiteral("no"));
0504     c.setTextAt(3, QStringLiteral("yes"));
0505     c.setTextAt(4, QStringLiteral("yes"));
0506 
0507     const auto& stats = c.statistics();
0508 
0509     QCOMPARE(stats.size, 5);
0510     QCOMPARE(stats.unique, 2);
0511 }
0512 
0513 void ColumnTest::statisticsMaskValues() {
0514     Project project;
0515     auto* c = new Column(QStringLiteral("Integer column"), Column::ColumnMode::Integer);
0516     c->setIntegers({1, 2, 3});
0517     project.addChild(c);
0518 
0519     // check the statistics
0520     auto& stats1 = c->statistics();
0521     QCOMPARE(stats1.size, 3);
0522     QCOMPARE(stats1.minimum, 1.);
0523     QCOMPARE(stats1.maximum, 3.);
0524 
0525     // mask the last value and check the statistics
0526     c->setMasked(2);
0527     auto& stats2 = c->statistics();
0528     QCOMPARE(stats2.size, 2);
0529     QCOMPARE(stats2.minimum, 1.);
0530     QCOMPARE(stats2.maximum, 2.);
0531 
0532     // undo the masking change and check the statistics
0533     project.undoStack()->undo();
0534     auto& stats3 = c->statistics();
0535     QCOMPARE(stats3.size, 3);
0536     QCOMPARE(stats3.minimum, 1.);
0537     QCOMPARE(stats3.maximum, 3.);
0538 
0539     // redo the masking change and check the statistics
0540     project.undoStack()->redo();
0541     auto& stats4 = c->statistics();
0542     QCOMPARE(stats4.size, 2);
0543     QCOMPARE(stats4.minimum, 1.);
0544     QCOMPARE(stats4.maximum, 2.);
0545 }
0546 
0547 void ColumnTest::statisticsClearSpreadsheetMasks() {
0548     Project project;
0549 
0550     auto* spreadsheet = new Spreadsheet(QStringLiteral("spreadsheet"));
0551     project.addChild(spreadsheet);
0552     spreadsheet->setColumnCount(1);
0553     spreadsheet->setRowCount(3);
0554     auto* c = spreadsheet->column(0);
0555     c->setValues({1., 2., 3.});
0556 
0557     // check the statistics
0558     auto& stats1 = c->statistics();
0559     QCOMPARE(stats1.size, 3);
0560     QCOMPARE(stats1.minimum, 1.);
0561     QCOMPARE(stats1.maximum, 3.);
0562 
0563     // mask the last value and check the statistics
0564     c->setMasked(2);
0565     auto& stats2 = c->statistics();
0566     QCOMPARE(stats2.size, 2);
0567     QCOMPARE(stats2.minimum, 1.);
0568     QCOMPARE(stats2.maximum, 2.);
0569 
0570     // clear the masked values in the spreadsheet
0571     spreadsheet->clearMasks();
0572     auto& stats3 = c->statistics();
0573     QCOMPARE(stats3.size, 3);
0574     QCOMPARE(stats3.minimum, 1.);
0575     QCOMPARE(stats3.maximum, 3.);
0576 
0577     // undo the "clear masked valus"-change and check the statistics
0578     project.undoStack()->undo();
0579     auto& stats4 = c->statistics();
0580     QCOMPARE(stats4.size, 2);
0581     QCOMPARE(stats4.minimum, 1.);
0582     QCOMPARE(stats4.maximum, 2.);
0583 
0584     // redo the "clear masked values"-change and check the statistics
0585     project.undoStack()->redo();
0586     auto& stats5 = c->statistics();
0587     QCOMPARE(stats5.size, 3);
0588     QCOMPARE(stats5.minimum, 1.);
0589     QCOMPARE(stats5.maximum, 3.);
0590 }
0591 
0592 void ColumnTest::testFormulaAutoUpdateEnabled() {
0593     Column sourceColumn(QStringLiteral("source"), Column::ColumnMode::Integer);
0594     sourceColumn.setIntegers({1, 2, 3});
0595 
0596     Column targetColumn(QStringLiteral("target"), Column::ColumnMode::Integer);
0597     targetColumn.setIntegers({3, 2, 1});
0598 
0599     // evaluatue 2*x and check the generated values in the target column
0600     targetColumn.setColumnMode(AbstractColumn::ColumnMode::Double); // should happen automatically in Column::setFormula()
0601     targetColumn.setFormula(QStringLiteral("2*x"),
0602                             QStringList{QStringLiteral("x")},
0603                             QVector<Column*>({&sourceColumn}),
0604                             true /* autoUpdate */,
0605                             false /* autoResize */);
0606     targetColumn.updateFormula();
0607     QCOMPARE(targetColumn.rowCount(), 3);
0608     QCOMPARE(targetColumn.valueAt(0), 2);
0609     QCOMPARE(targetColumn.valueAt(1), 4);
0610     QCOMPARE(targetColumn.valueAt(2), 6);
0611 
0612     // modify value in the source column and check the target column again which should be updated
0613     sourceColumn.setIntegers({3, 2, 1});
0614     QCOMPARE(targetColumn.rowCount(), 3);
0615     QCOMPARE(targetColumn.valueAt(0), 6);
0616     QCOMPARE(targetColumn.valueAt(1), 4);
0617     QCOMPARE(targetColumn.valueAt(2), 2);
0618 }
0619 
0620 void ColumnTest::testFormulaAutoUpdateDisabled() {
0621     Column sourceColumn(QStringLiteral("source"), Column::ColumnMode::Integer);
0622     sourceColumn.setIntegers({1, 2, 3});
0623 
0624     Column targetColumn(QStringLiteral("target"), Column::ColumnMode::Integer);
0625     targetColumn.setIntegers({3, 2, 1});
0626 
0627     // evaluatue 2*x and check the generated values in the target column
0628     targetColumn.setColumnMode(AbstractColumn::ColumnMode::Double);
0629     targetColumn.setFormula(QStringLiteral("2*x"),
0630                             QStringList{QStringLiteral("x")},
0631                             QVector<Column*>({&sourceColumn}),
0632                             false /* autoUpdate */,
0633                             false /* autoResize */);
0634     targetColumn.updateFormula();
0635     QCOMPARE(targetColumn.rowCount(), 3);
0636     QCOMPARE(targetColumn.valueAt(0), 2);
0637     QCOMPARE(targetColumn.valueAt(1), 4);
0638     QCOMPARE(targetColumn.valueAt(2), 6);
0639 
0640     // modify value in the source column and check the target column again which shouldn't be updated
0641     sourceColumn.setIntegers({3, 2, 1});
0642     QCOMPARE(targetColumn.rowCount(), 3);
0643     QCOMPARE(targetColumn.valueAt(0), 2);
0644     QCOMPARE(targetColumn.valueAt(1), 4);
0645     QCOMPARE(targetColumn.valueAt(2), 6);
0646 }
0647 
0648 void ColumnTest::testFormulaAutoResizeEnabled() {
0649     Column sourceColumn1(QStringLiteral("source1"), Column::ColumnMode::Integer);
0650     sourceColumn1.setIntegers({1, 2, 3});
0651 
0652     Column sourceColumn2(QStringLiteral("source2"), Column::ColumnMode::Integer);
0653     sourceColumn2.setIntegers({1, 2, 3});
0654 
0655     // spreadsheet needs to be created since the resize of the column is happening via the resize of the spreadsheet
0656     Spreadsheet targetSpreadsheet(QStringLiteral("target"));
0657     targetSpreadsheet.setColumnCount(1);
0658     targetSpreadsheet.setRowCount(1);
0659     Column* targetColumn = targetSpreadsheet.column(0);
0660 
0661     // evaluatue x+y
0662     targetColumn->setColumnMode(AbstractColumn::ColumnMode::Double);
0663     targetColumn->setFormula(QStringLiteral("x+y"),
0664                              QStringList{QStringLiteral("x"), QStringLiteral("y")},
0665                              QVector<Column*>({&sourceColumn1, &sourceColumn2}),
0666                              false /* autoUpdate */,
0667                              true /* autoResize */);
0668     targetColumn->updateFormula();
0669 
0670     // check the generated values in the target column which should have been resized
0671     QCOMPARE(targetColumn->rowCount(), 3);
0672     QCOMPARE(targetColumn->valueAt(0), 2);
0673     QCOMPARE(targetColumn->valueAt(1), 4);
0674     QCOMPARE(targetColumn->valueAt(2), 6);
0675 }
0676 
0677 void ColumnTest::testFormulaAutoResizeDisabled() {
0678     Column sourceColumn1(QStringLiteral("source1"), Column::ColumnMode::Integer);
0679     sourceColumn1.setIntegers({1, 2, 3});
0680 
0681     Column sourceColumn2(QStringLiteral("source2"), Column::ColumnMode::Integer);
0682     sourceColumn2.setIntegers({1, 2, 3});
0683 
0684     Column targetColumn(QStringLiteral("target"), Column::ColumnMode::Integer);
0685     targetColumn.setIntegers({1});
0686 
0687     // evaluatue x+y
0688     targetColumn.setColumnMode(AbstractColumn::ColumnMode::Double);
0689     targetColumn.setFormula(QStringLiteral("x+y"),
0690                             QStringList{QStringLiteral("x"), QStringLiteral("y")},
0691                             QVector<Column*>({&sourceColumn1, &sourceColumn2}),
0692                             false /* autoUpdate */,
0693                             false /* autoResize */);
0694     targetColumn.updateFormula();
0695 
0696     // check the generated values in the target column which should not have been resized
0697     QCOMPARE(targetColumn.rowCount(), 1);
0698     QCOMPARE(targetColumn.valueAt(0), 2);
0699 }
0700 
0701 void ColumnTest::testFormulaAutoUpdateEnabledResize() {
0702     Column sourceColumn(QStringLiteral("source"), Column::ColumnMode::Integer);
0703     sourceColumn.setIntegers({1, 2, 3, 4});
0704 
0705     Column targetColumn(QStringLiteral("target"), Column::ColumnMode::Integer);
0706     targetColumn.setIntegers({3, 2, 1});
0707 
0708     // evaluatue 2*x and check the generated values in the target column
0709     targetColumn.setColumnMode(AbstractColumn::ColumnMode::Double); // should happen automatically in Column::setFormula()
0710     targetColumn.setFormula(QStringLiteral("2*x"),
0711                             QStringList{QStringLiteral("x")},
0712                             QVector<Column*>({&sourceColumn}),
0713                             true /* autoUpdate */,
0714                             false /* autoResize */);
0715     targetColumn.updateFormula();
0716     QCOMPARE(targetColumn.rowCount(), 3);
0717     QCOMPARE(targetColumn.valueAt(0), 2);
0718     QCOMPARE(targetColumn.valueAt(1), 4);
0719     QCOMPARE(targetColumn.valueAt(2), 6);
0720 
0721     targetColumn.insertRows(3, 1); // Rowcount of the target changes
0722 
0723     // Values updated
0724     QCOMPARE(targetColumn.rowCount(), 4);
0725     QCOMPARE(targetColumn.valueAt(0), 2);
0726     QCOMPARE(targetColumn.valueAt(1), 4);
0727     QCOMPARE(targetColumn.valueAt(2), 6);
0728     QCOMPARE(targetColumn.valueAt(3), 8);
0729 }
0730 
0731 void ColumnTest::testDictionaryIndex() {
0732     Column c(QStringLiteral("Text column"), Column::ColumnMode::Text);
0733     c.setTextAt(0, QStringLiteral("yes"));
0734     c.setTextAt(1, QStringLiteral("no"));
0735     c.setTextAt(2, QStringLiteral("no"));
0736     c.setTextAt(3, QStringLiteral("yes"));
0737     c.setTextAt(4, QStringLiteral("yes"));
0738 
0739     // check the position of the distinct values in the dictionary
0740     QCOMPARE(c.dictionaryIndex(0), 0);
0741     QCOMPARE(c.dictionaryIndex(1), 1);
0742     QCOMPARE(c.dictionaryIndex(2), 1);
0743     QCOMPARE(c.dictionaryIndex(3), 0);
0744     QCOMPARE(c.dictionaryIndex(4), 0);
0745 
0746     // modify a value which will invalidate the dictionary and verify it again
0747     c.setTextAt(1, QStringLiteral("yes"));
0748 
0749     QCOMPARE(c.dictionaryIndex(0), 0);
0750     QCOMPARE(c.dictionaryIndex(1), 0);
0751     QCOMPARE(c.dictionaryIndex(2), 1);
0752     QCOMPARE(c.dictionaryIndex(3), 0);
0753     QCOMPARE(c.dictionaryIndex(4), 0);
0754 }
0755 
0756 void ColumnTest::testTextFrequencies() {
0757     Column c(QStringLiteral("Text column"), Column::ColumnMode::Text);
0758     c.setTextAt(0, QStringLiteral("yes"));
0759     c.setTextAt(1, QStringLiteral("no"));
0760     c.setTextAt(2, QStringLiteral("no"));
0761     c.setTextAt(3, QStringLiteral("yes"));
0762     c.setTextAt(4, QStringLiteral("yes"));
0763 
0764     const auto& frequencies = c.frequencies();
0765 
0766     QCOMPARE(frequencies[QStringLiteral("yes")], 3);
0767     QCOMPARE(frequencies[QStringLiteral("no")], 2);
0768 }
0769 
0770 //////////////////////////////////////////////////
0771 
0772 void ColumnTest::saveLoadDateTime() {
0773     Column c(QStringLiteral("Datetime column"), Column::ColumnMode::DateTime);
0774     c.setDateTimes({
0775         QDateTime::fromString(
0776             QStringLiteral("2017-03-26T02:14:34.000Z"),
0777             Qt::DateFormat::ISODateWithMs), // without the timezone declaration it would be invalid (in some regions), because of the daylight time
0778         QDateTime::fromString(QStringLiteral("2018-03-26T02:14:34.000Z"), Qt::DateFormat::ISODateWithMs),
0779         QDateTime::fromString(QStringLiteral("2019-03-26T02:14:34.000Z"), Qt::DateFormat::ISODateWithMs),
0780         QDateTime::fromString(QStringLiteral("2019-26-03 02:14:34:000"), QStringLiteral("yyyy-dd-MM hh:mm:ss:zzz")),
0781     });
0782 
0783     QByteArray array;
0784     QXmlStreamWriter writer(&array);
0785     c.save(&writer);
0786 
0787     QDEBUG(array);
0788 
0789     Column c2(QStringLiteral("Datetime 2 column"), Column::ColumnMode::DateTime);
0790     XmlStreamReader reader(array);
0791     bool found = false;
0792     while (!reader.atEnd()) {
0793         reader.readNext();
0794         if (reader.isStartElement() && reader.name() == QLatin1String("column")) {
0795             found = true;
0796             break;
0797         }
0798     }
0799     QCOMPARE(found, true);
0800     QCOMPARE(c2.load(&reader, false), true);
0801 
0802     QCOMPARE(c2.rowCount(), 4);
0803     QCOMPARE(c2.dateTimeAt(0).isValid(), true);
0804     QCOMPARE(c2.dateTimeAt(0), QDateTime::fromString(QStringLiteral("2017-03-26T02:14:34.000Z"), Qt::DateFormat::ISODateWithMs));
0805     QCOMPARE(c2.dateTimeAt(1).isValid(), true);
0806     QCOMPARE(c2.dateTimeAt(1), QDateTime::fromString(QStringLiteral("2018-03-26T02:14:34.000Z"), Qt::DateFormat::ISODateWithMs));
0807     QCOMPARE(c2.dateTimeAt(2).isValid(), true);
0808     QCOMPARE(c2.dateTimeAt(2), QDateTime::fromString(QStringLiteral("2019-03-26T02:14:34.000Z"), Qt::DateFormat::ISODateWithMs));
0809     QCOMPARE(c2.dateTimeAt(3).isValid(), true);
0810     QCOMPARE(c2.dateTimeAt(3), QDateTime::fromString(QStringLiteral("2019-03-26T02:14:34.000Z"), Qt::DateFormat::ISODateWithMs));
0811 }
0812 
0813 void ColumnTest::loadDoubleFromProject() {
0814     Project project;
0815     project.load(QFINDTESTDATA(QLatin1String("data/Load.lml")));
0816 
0817     auto* doublespreadSheet = project.child<AbstractAspect>(0);
0818     QVERIFY(doublespreadSheet != nullptr);
0819     QCOMPARE(doublespreadSheet->name(), QLatin1String("Double"));
0820     QCOMPARE(doublespreadSheet->type(), AspectType::Spreadsheet);
0821 
0822     auto childs = doublespreadSheet->children(AspectType::Column);
0823     QVERIFY(childs.count() >= 1);
0824     auto* doubleColumn = static_cast<Column*>(childs.at(0));
0825     QCOMPARE(doubleColumn->columnMode(), AbstractColumn::ColumnMode::Double);
0826     QCOMPARE(doubleColumn->rowCount(), 100);
0827     const double doubleValues[] = {0.625,
0828                                    1,
0829                                    1,
0830                                    4,
0831                                    1.125,
0832                                    1.66666666666667,
0833                                    11,
0834                                    6,
0835                                    2.16666666666667,
0836                                    14,
0837                                    1.5,
0838                                    2.28571428571429,
0839                                    2.125,
0840                                    2,
0841                                    1.9,
0842                                    2,
0843                                    3.5,
0844                                    22,
0845                                    2.3,
0846                                    2.66666666666667,
0847                                    5,
0848                                    26,
0849                                    3,
0850                                    4,
0851                                    4.14285714285714,
0852                                    3.33333333333333,
0853                                    10.3333333333333,
0854                                    32,
0855                                    8.25,
0856                                    3.4,
0857                                    8.75,
0858                                    5.14285714285714,
0859                                    9.25,
0860                                    6.33333333333333,
0861                                    4.33333333333333,
0862                                    4.44444444444444,
0863                                    4.55555555555556,
0864                                    4.2,
0865                                    6.14285714285714,
0866                                    5.5,
0867                                    7.5,
0868                                    46,
0869                                    4.7,
0870                                    12,
0871                                    7,
0872                                    7.14285714285714,
0873                                    6.375,
0874                                    5.2,
0875                                    26.5,
0876                                    18,
0877                                    27.5,
0878                                    14,
0879                                    11.4,
0880                                    8.28571428571429,
0881                                    29.5,
0882                                    10,
0883                                    15.25,
0884                                    12.4,
0885                                    12.6,
0886                                    64,
0887                                    65,
0888                                    33,
0889                                    8.375,
0890                                    13.6,
0891                                    17.25,
0892                                    14,
0893                                    23.6666666666667,
0894                                    8,
0895                                    8.11111111111111,
0896                                    10.5714285714286,
0897                                    12.5,
0898                                    8.44444444444444,
0899                                    19.25,
0900                                    8.66666666666667,
0901                                    11.2857142857143,
0902                                    8,
0903                                    20.25,
0904                                    27.3333333333333,
0905                                    20.75,
0906                                    16.8,
0907                                    42.5,
0908                                    9.55555555555556,
0909                                    9.66666666666667,
0910                                    17.6,
0911                                    44.5,
0912                                    90,
0913                                    18.2,
0914                                    46,
0915                                    31,
0916                                    47,
0917                                    23.75,
0918                                    96,
0919                                    12.125,
0920                                    19.6,
0921                                    16.5,
0922                                    11.1111111111111,
0923                                    33.6666666666667,
0924                                    12.75,
0925                                    25.75,
0926                                    13};
0927     for (int i = 0; i < 100; i++)
0928         QCOMPARE(doubleColumn->valueAt(i), doubleValues[i]);
0929 }
0930 
0931 void ColumnTest::loadIntegerFromProject() {
0932     Project project;
0933     project.load(QFINDTESTDATA(QLatin1String("data/Load.lml")));
0934 
0935     auto* integerSpreadsheet = project.child<AbstractAspect>(1);
0936     QVERIFY(integerSpreadsheet != nullptr);
0937     QCOMPARE(integerSpreadsheet->name(), QLatin1String("Integer"));
0938     QCOMPARE(integerSpreadsheet->type(), AspectType::Spreadsheet);
0939 
0940     auto childs = integerSpreadsheet->children(AspectType::Column);
0941     QVERIFY(childs.count() >= 1);
0942     auto* integerColumn = static_cast<Column*>(childs.at(0));
0943     QCOMPARE(integerColumn->columnMode(), AbstractColumn::ColumnMode::Integer);
0944     QCOMPARE(integerColumn->rowCount(), 133);
0945     const int integerValues[133] = {291, 75,  21,  627, 163, 677, 712, 66,  733, 653, 502, 515, 379, 70,  762, 261, 304, 541, 298, 462, 623, 382, 94,
0946                                     232, 679, 132, 124, 212, 122, 118, 486, 126, 107, 677, 386, 118, 731, 484, 638, 127, 779, 109, 708, 298, 680, 249,
0947                                     591, 155, 351, 178, 70,  768, 2,   504, 179, 747, 789, 213, 144, 143, 61,  761, 113, 766, 18,  617, 406, 489, 299,
0948                                     658, 326, 181, 773, 228, 653, 242, 382, 11,  267, 29,  283, 30,  251, 453, 699, 286, 739, 406, 729, 159, 506, 20,
0949                                     766, 443, 646, 161, 545, 400, 160, 693, 722, 463, 121, 350, 194, 558, 503, 72,  516, 509, 118, 340, 342, 495, 50,
0950                                     549, 643, 241, 248, 483, 408, 768, 634, 589, 159, 518, 475, 403, 165, 122, 268, 537, 33};
0951     for (int i = 0; i < 133; i++)
0952         QCOMPARE(integerColumn->integerAt(i), integerValues[i]);
0953 }
0954 
0955 void ColumnTest::loadBigIntegerFromProject() {
0956     Project project;
0957     project.load(QFINDTESTDATA(QLatin1String("data/Load.lml")));
0958 
0959     auto* bigIntegerSpreadsheet = project.child<AbstractAspect>(2);
0960     QVERIFY(bigIntegerSpreadsheet != nullptr);
0961     QCOMPARE(bigIntegerSpreadsheet->name(), QLatin1String("BigInteger"));
0962     QCOMPARE(bigIntegerSpreadsheet->type(), AspectType::Spreadsheet);
0963 
0964     auto childs = bigIntegerSpreadsheet->children(AspectType::Column);
0965     QVERIFY(childs.count() >= 1);
0966     auto* bigIntegerColumn = static_cast<Column*>(childs.at(0));
0967     QCOMPARE(bigIntegerColumn->columnMode(), AbstractColumn::ColumnMode::BigInt);
0968     QCOMPARE(bigIntegerColumn->rowCount(), 98);
0969     const qint64 bigIntegerValues[] = {
0970         423448954198, 5641410204,  30408827812, 28654888657, 49080407041, 49609860873, 3687941775,  19532027992, 35894087224, 5820636581,  28739047077,
0971         13946526866,  36153607843, 3240340694,  2375891120,  3014999117,  17758738424, 31303772749, 36400461519, 29813286102, 14068980943, 24595715523,
0972         390096547,    27927541822, 35442936843, 33577242277, 34966078315, 45550480998, 11834545810, 25714661808, 6979002160,  35138449350, 3597002515,
0973         707044300,    27971594979, 25699976843, 35231282278, 11659858605, 45858935838, 25070072891, 15136182059, 6266852861,  42582813575, 23784333993,
0974         14361566136,  27840747719, 41099229867, 40403331476, 21708972571, 10995493445, 36292237893, 4264327752,  45637575339, 13530360473, 40816873119,
0975         15346300490,  30807486688, 23771858665, 36762855436, 351630653,   22270715573, 31792268673, 25001237450, 16558491573, 21771715873, 20963298299,
0976         25197909817,  41130528918, 13134975803, 43222173019, 17071520882, 8356069280,  27671796182, 29309739294, 9377292482,  30451803959, 47318250898,
0977         21100469009,  28764337224, 36898356693, 36091695104, 12019973504, 15605135996, 13711330940, 13010481591, 45193969649, 25444985954, 34831527437,
0978         8208098526,   29897950771, 5631513384,  47590874807, 4659417951,  28338882094, 14853737313, 22965578753, 6544735402,  32209366817};
0979     for (int i = 0; i < 98; i++)
0980         QCOMPARE(bigIntegerColumn->bigIntAt(i), bigIntegerValues[i]);
0981 }
0982 
0983 void ColumnTest::loadTextFromProject() {
0984     Project project;
0985     project.load(QFINDTESTDATA(QLatin1String("data/Load.lml")));
0986 
0987     auto* textSpreadsheet = project.child<AbstractAspect>(3);
0988     QVERIFY(textSpreadsheet != nullptr);
0989     QCOMPARE(textSpreadsheet->name(), QLatin1String("Text"));
0990     QCOMPARE(textSpreadsheet->type(), AspectType::Spreadsheet);
0991 
0992     auto childs = textSpreadsheet->children(AspectType::Column);
0993     QVERIFY(childs.count() >= 1);
0994     auto* textColumn = static_cast<Column*>(childs.at(0));
0995     QCOMPARE(textColumn->columnMode(), AbstractColumn::ColumnMode::Text);
0996     QCOMPARE(textColumn->rowCount(), 10);
0997     QStringList texts = {QStringLiteral("first value"),
0998                          QStringLiteral("second value"),
0999                          QStringLiteral("third value"),
1000                          QStringLiteral("fourth value"),
1001                          QStringLiteral("fifth value"),
1002                          QStringLiteral("sixt value"),
1003                          QStringLiteral("sevent value"),
1004                          QStringLiteral("eigth value"),
1005                          QStringLiteral("ninth value"),
1006                          QStringLiteral("tenth value")};
1007     for (int i = 0; i < 10; i++) {
1008         QCOMPARE(textColumn->textAt(i), texts.at(i));
1009     }
1010 }
1011 
1012 void ColumnTest::loadDateTimeFromProject() {
1013     Project project;
1014     project.load(QFINDTESTDATA(QLatin1String("data/Load.lml")));
1015 
1016     auto* dateTimeSpreadsheet = project.child<AbstractAspect>(4);
1017     QVERIFY(dateTimeSpreadsheet != nullptr);
1018     QCOMPARE(dateTimeSpreadsheet->name(), QLatin1String("Datetime"));
1019     QCOMPARE(dateTimeSpreadsheet->type(), AspectType::Spreadsheet);
1020 
1021     auto childs = dateTimeSpreadsheet->children(AspectType::Column);
1022     QVERIFY(childs.count() == 3);
1023     auto* dateTimeColumn = static_cast<Column*>(childs.at(0));
1024     QCOMPARE(dateTimeColumn->rowCount(), 8);
1025     // TODO:
1026     // auto* dayColumn = static_cast<Column*>(childs.at(1));
1027     // auto* monthColumn = static_cast<Column*>(childs.at(2));
1028 
1029     // TODO: must be implemented
1030     //  for (int i=0; i < 8; i++) {
1031     //      QCOMPARE(dateTimeColumn->dateTimeAt(i), QDateTime::fromString("2022-01-12T12:30:24.920"));
1032     //  }
1033 }
1034 
1035 void ColumnTest::testIndexForValue() {
1036     {
1037         const double value = 5;
1038         QVector<QPointF> points{};
1039         Column::Properties properties = Column::Properties::MonotonicIncreasing;
1040         QCOMPARE(Column::indexForValue(value, points, properties), -1);
1041     }
1042 
1043     {
1044         const double value = 5;
1045         QVector<QPointF> points{QPointF(10, 1), QPointF(20, 1), QPointF(30, 1), QPointF(40, 1), QPointF(50, 1)};
1046         Column::Properties properties = Column::Properties::MonotonicIncreasing;
1047         QCOMPARE(Column::indexForValue(value, points, properties), 0);
1048     }
1049 
1050     {
1051         const double value = 60;
1052         QVector<QPointF> points{QPointF(10, 1), QPointF(20, 1), QPointF(30, 1), QPointF(40, 1), QPointF(50, 1)};
1053         Column::Properties properties = Column::Properties::MonotonicIncreasing;
1054         QCOMPARE(Column::indexForValue(value, points, properties), 4);
1055     }
1056 
1057     {
1058         const double value = 16;
1059         QVector<QPointF> points{QPointF(10, 1), QPointF(20, 1), QPointF(30, 1), QPointF(40, 1), QPointF(50, 1)};
1060         Column::Properties properties = Column::Properties::MonotonicIncreasing;
1061         QCOMPARE(Column::indexForValue(value, points, properties), 1);
1062     }
1063 
1064     {
1065         const double value = 20;
1066         QVector<QPointF> points{QPointF(10, 1), QPointF(20, 1), QPointF(30, 1), QPointF(40, 1), QPointF(50, 1)};
1067         Column::Properties properties = Column::Properties::MonotonicIncreasing;
1068         QCOMPARE(Column::indexForValue(value, points, properties), 1);
1069     }
1070 }
1071 
1072 void ColumnTest::testIndexForValueDoubleVector() {
1073     {
1074         const double value = 5;
1075         QVector<double> points{};
1076         Column::Properties properties = Column::Properties::MonotonicIncreasing;
1077         QCOMPARE(Column::indexForValue(value, points, properties), -1);
1078     }
1079 
1080     {
1081         const double value = 5;
1082         QVector<double> points{10, 20, 30, 40, 50};
1083         Column::Properties properties = Column::Properties::MonotonicIncreasing;
1084         QCOMPARE(Column::indexForValue(value, points, properties), 0);
1085     }
1086 
1087     {
1088         const double value = 60;
1089         QVector<double> points{10, 20, 30, 40, 50};
1090         Column::Properties properties = Column::Properties::MonotonicIncreasing;
1091         QCOMPARE(Column::indexForValue(value, points, properties), 4);
1092     }
1093 
1094     {
1095         const double value = 16;
1096         QVector<double> points{10, 20, 30, 40, 50};
1097         Column::Properties properties = Column::Properties::MonotonicIncreasing;
1098         QCOMPARE(Column::indexForValue(value, points, properties), 1);
1099     }
1100 
1101     {
1102         const double value = 20;
1103         QVector<double> points{10, 20, 30, 40, 50};
1104         Column::Properties properties = Column::Properties::MonotonicIncreasing;
1105         QCOMPARE(Column::indexForValue(value, points, properties), 1);
1106     }
1107 }
1108 
1109 void ColumnTest::testInsertRow() {
1110     Project project;
1111     auto* c = new Column(QStringLiteral("Test"), Column::ColumnMode::Double);
1112     project.addChild(c);
1113     c->resizeTo(100);
1114     QCOMPARE(c->rowCount(), 100);
1115 
1116     int rowsAboutToBeInsertedCounter = 0;
1117     connect(c, &Column::rowsAboutToBeInserted, [&rowsAboutToBeInsertedCounter, c](const AbstractColumn* source, int before, int count) {
1118         QCOMPARE(source, c);
1119         switch (rowsAboutToBeInsertedCounter) {
1120         case 0:
1121             QCOMPARE(before, 100);
1122             QCOMPARE(count, 2);
1123             break;
1124         case 1:
1125             QCOMPARE(before, 102);
1126             QCOMPARE(count, 3);
1127             break;
1128         case 3: // redo()
1129             QCOMPARE(before, 102);
1130             QCOMPARE(count, 3);
1131             break;
1132         }
1133         rowsAboutToBeInsertedCounter++;
1134     });
1135 
1136     int rowsAboutToBeRemovedCounter = 0;
1137     connect(c, &Column::rowsAboutToBeRemoved, [&rowsAboutToBeRemovedCounter, c](const AbstractColumn* source, int first, int count) {
1138         QCOMPARE(source, c);
1139         switch (rowsAboutToBeRemovedCounter) {
1140         case 0:
1141             QCOMPARE(first, 102);
1142             QCOMPARE(count, 3);
1143             break;
1144         }
1145         rowsAboutToBeRemovedCounter++;
1146     });
1147 
1148     int rowsInsertedCounter = 0;
1149     connect(c, &Column::rowsInserted, [&rowsInsertedCounter, c](const AbstractColumn* source, int before, int count) {
1150         QCOMPARE(source, c);
1151 
1152         switch (rowsInsertedCounter) {
1153         case 0:
1154             QCOMPARE(before, 100);
1155             QCOMPARE(count, 2);
1156             break;
1157         case 1:
1158             QCOMPARE(before, 102);
1159             QCOMPARE(count, 3);
1160             break;
1161         case 3: // redo()
1162             QCOMPARE(before, 102);
1163             QCOMPARE(count, 3);
1164             break;
1165         }
1166 
1167         rowsInsertedCounter++;
1168     });
1169 
1170     int rowsRemovedCounter = 0;
1171     connect(c, &Column::rowsRemoved, [&rowsRemovedCounter, c](const AbstractColumn* source, int first, int count) {
1172         QCOMPARE(source, c);
1173 
1174         switch (rowsRemovedCounter) {
1175         case 0:
1176             QCOMPARE(first, 102);
1177             QCOMPARE(count, 3);
1178             break;
1179         }
1180 
1181         rowsRemovedCounter++;
1182     });
1183 
1184     c->insertRows(c->rowCount(), 2);
1185     QCOMPARE(c->rowCount(), 102);
1186     c->insertRows(c->rowCount(), 3);
1187     QCOMPARE(c->rowCount(), 105);
1188 
1189     c->undoStack()->undo();
1190     QCOMPARE(c->rowCount(), 102);
1191     c->undoStack()->redo();
1192     QCOMPARE(c->rowCount(), 105);
1193 
1194     QCOMPARE(rowsAboutToBeInsertedCounter, 3);
1195     QCOMPARE(rowsAboutToBeRemovedCounter, 1);
1196     QCOMPARE(rowsInsertedCounter, 3);
1197     QCOMPARE(rowsRemovedCounter, 1);
1198 }
1199 
1200 void ColumnTest::testRemoveRow() {
1201     Project project;
1202     auto* c = new Column(QStringLiteral("Test"), Column::ColumnMode::Double);
1203     project.addChild(c);
1204     c->resizeTo(100);
1205     QCOMPARE(c->rowCount(), 100);
1206 
1207     int rowsAboutToBeInsertedCounter = 0;
1208     connect(c, &Column::rowsAboutToBeInserted, [&rowsAboutToBeInsertedCounter, c](const AbstractColumn* source, int before, int count) {
1209         QCOMPARE(source, c);
1210         QCOMPARE(before, 96);
1211         QCOMPARE(count, 3);
1212         rowsAboutToBeInsertedCounter++;
1213     });
1214 
1215     int rowsAboutToBeRemovedCounter = 0;
1216     connect(c, &Column::rowsAboutToBeRemoved, [&rowsAboutToBeRemovedCounter, c](const AbstractColumn* source, int first, int count) {
1217         QCOMPARE(source, c);
1218         switch (rowsAboutToBeRemovedCounter) {
1219         case 0:
1220             QCOMPARE(first, 99);
1221             QCOMPARE(count, 1);
1222             break;
1223         case 1:
1224             QCOMPARE(first, 96);
1225             QCOMPARE(count, 3);
1226             break;
1227         case 2: // redo()
1228             QCOMPARE(first, 96);
1229             QCOMPARE(count, 3);
1230             break;
1231         }
1232         rowsAboutToBeRemovedCounter++;
1233     });
1234 
1235     int rowsInsertedCounter = 0;
1236     connect(c, &Column::rowsInserted, [&rowsInsertedCounter, c](const AbstractColumn* source, int before, int count) {
1237         QCOMPARE(source, c);
1238 
1239         QCOMPARE(before, 96);
1240         QCOMPARE(count, 3);
1241 
1242         rowsInsertedCounter++;
1243     });
1244 
1245     int rowsRemovedCounter = 0;
1246     connect(c, &Column::rowsRemoved, [&rowsRemovedCounter, c](const AbstractColumn* source, int first, int count) {
1247         QCOMPARE(source, c);
1248 
1249         switch (rowsRemovedCounter) {
1250         case 0:
1251             QCOMPARE(first, 99);
1252             QCOMPARE(count, 1);
1253             break;
1254         case 1:
1255             QCOMPARE(first, 96);
1256             QCOMPARE(count, 3);
1257             break;
1258         case 2: // redo()
1259             QCOMPARE(first, 96);
1260             QCOMPARE(count, 3);
1261             break;
1262         }
1263 
1264         rowsRemovedCounter++;
1265     });
1266 
1267     c->removeRows(c->rowCount() - 1, 1);
1268     QCOMPARE(c->rowCount(), 99);
1269     c->removeRows(c->rowCount() - 3, 3);
1270     QCOMPARE(c->rowCount(), 96);
1271 
1272     c->undoStack()->undo();
1273     QCOMPARE(c->rowCount(), 99);
1274     c->undoStack()->redo();
1275     QCOMPARE(c->rowCount(), 96);
1276 
1277     QCOMPARE(rowsAboutToBeInsertedCounter, 1);
1278     QCOMPARE(rowsAboutToBeRemovedCounter, 3);
1279     QCOMPARE(rowsInsertedCounter, 1);
1280     QCOMPARE(rowsRemovedCounter, 3);
1281 }
1282 
1283 void ColumnTest::testFormula() {
1284     auto c1 = Column(QStringLiteral("DataColumn"), Column::ColumnMode::Double);
1285     c1.replaceValues(-1, {1., 2., 3.});
1286 
1287     auto c2 = Column(QStringLiteral("FormulaColumn"), Column::ColumnMode::Double);
1288     c2.replaceValues(-1, {11., 12., 13., 14., 15., 16., 17.});
1289 
1290     c2.setFormula(QStringLiteral("mean(x)"), {QStringLiteral("x")}, QVector<Column*>({&c1}), true);
1291     c2.updateFormula();
1292     QCOMPARE(c2.rowCount(), 7);
1293     for (int i = 0; i < c2.rowCount(); i++) {
1294         VALUES_EQUAL(c2.valueAt(i), 2.);
1295     }
1296 }
1297 
1298 void ColumnTest::testFormulaCell() {
1299     auto c1 = Column(QStringLiteral("DataColumn"), Column::ColumnMode::Double);
1300     c1.replaceValues(-1, {1., 5., -1.});
1301 
1302     auto c3 = Column(QStringLiteral("DataColumn"), Column::ColumnMode::Double);
1303     c3.replaceValues(-1, {3., 2., 1.});
1304 
1305     auto c2 = Column(QStringLiteral("FormulaColumn"), Column::ColumnMode::Double);
1306     c2.replaceValues(-1, {11., 12., 13., 14., 15., 16., 17.});
1307 
1308     c2.setFormula(QStringLiteral("cell(y; x)"), {QStringLiteral("x"), QStringLiteral("y")}, QVector<Column*>({&c1, &c3}), true);
1309     c2.updateFormula();
1310     QCOMPARE(c2.rowCount(), 7);
1311     VALUES_EQUAL(c2.valueAt(0), -1.);
1312     VALUES_EQUAL(c2.valueAt(1), 5.);
1313     VALUES_EQUAL(c2.valueAt(2), 1.);
1314     VALUES_EQUAL(c2.valueAt(3), NAN);
1315     VALUES_EQUAL(c2.valueAt(4), NAN);
1316     VALUES_EQUAL(c2.valueAt(5), NAN);
1317     VALUES_EQUAL(c2.valueAt(6), NAN);
1318 }
1319 
1320 /*!
1321  * index in cell higher than rownumber
1322  */
1323 void ColumnTest::testFormulaCellInvalid() {
1324     auto c1 = Column(QStringLiteral("DataColumn"), Column::ColumnMode::Double);
1325     c1.replaceValues(-1, {1., 2., 3.});
1326 
1327     auto c2 = Column(QStringLiteral("FormulaColumn"), Column::ColumnMode::Double);
1328     c2.replaceValues(-1, {11., 12., 13., 14., 15., 16., 17.});
1329 
1330     c2.setFormula(QStringLiteral("cell(10,x)"), {QStringLiteral("x")}, QVector<Column*>({&c1}), true);
1331     c2.updateFormula();
1332     QCOMPARE(c2.rowCount(), 7);
1333     // All invalid
1334     for (int i = 0; i < c2.rowCount(); i++)
1335         VALUES_EQUAL(c2.valueAt(i), NAN);
1336 }
1337 
1338 void ColumnTest::testFormulaCellConstExpression() {
1339     auto c1 = Column(QStringLiteral("DataColumn"), Column::ColumnMode::Double);
1340     c1.replaceValues(-1, {1., -1., 5.});
1341 
1342     auto c2 = Column(QStringLiteral("FormulaColumn"), Column::ColumnMode::Double);
1343     c2.replaceValues(-1, {11., 12., 13., 14., 15., 16., 17.});
1344 
1345     c2.setFormula(QStringLiteral("cell(2; x)"), {QStringLiteral("x")}, QVector<Column*>({&c1}), true);
1346     c2.updateFormula();
1347     QCOMPARE(c2.rowCount(), 7);
1348     // All invalid
1349     for (int i = 0; i < c2.rowCount(); i++)
1350         VALUES_EQUAL(c2.valueAt(i), -1.);
1351 }
1352 
1353 void ColumnTest::testFormulaCellMulti() {
1354     auto c1 = Column(QStringLiteral("DataColumn"), Column::ColumnMode::Double);
1355     c1.replaceValues(-1, {1., -1., 5.});
1356 
1357     auto c3 = Column(QStringLiteral("DataColumn"), Column::ColumnMode::Double);
1358     c3.replaceValues(-1, {-5., 100., 3});
1359 
1360     auto c2 = Column(QStringLiteral("FormulaColumn"), Column::ColumnMode::Double);
1361     c2.replaceValues(-1, {11., 12., 13., 14., 15., 16., 17.});
1362 
1363     c2.setFormula(QStringLiteral("cell(2; x) + cell(1; y)"), {QStringLiteral("x"), QStringLiteral("y")}, QVector<Column*>({&c1, &c3}), true);
1364     c2.updateFormula();
1365     QCOMPARE(c2.rowCount(), 7);
1366     for (int i = 0; i < c2.rowCount(); i++)
1367         VALUES_EQUAL(c2.valueAt(i), -6.);
1368 }
1369 
1370 void ColumnTest::testFormulasmmin() {
1371     auto c1 = Column(QStringLiteral("DataColumn"), Column::ColumnMode::Double);
1372     c1.replaceValues(-1, {1., -1., 5., 5., 3., 8., 10., -5});
1373 
1374     auto c2 = Column(QStringLiteral("FormulaColumn"), Column::ColumnMode::Double);
1375     c2.replaceValues(-1, {11., 12., 13., 14., 15., 16., 17., 18.});
1376 
1377     c2.setFormula(QStringLiteral("smmin(3; x)"), {QStringLiteral("x")}, {&c1}, true);
1378     c2.updateFormula();
1379     QCOMPARE(c2.rowCount(), 8);
1380     VALUES_EQUAL(c2.valueAt(0), 1.);
1381     VALUES_EQUAL(c2.valueAt(1), -1.);
1382     VALUES_EQUAL(c2.valueAt(2), -1.);
1383     VALUES_EQUAL(c2.valueAt(3), -1.);
1384     VALUES_EQUAL(c2.valueAt(4), 3.);
1385     VALUES_EQUAL(c2.valueAt(5), 3.);
1386     VALUES_EQUAL(c2.valueAt(6), 3.);
1387     VALUES_EQUAL(c2.valueAt(7), -5.);
1388 }
1389 
1390 void ColumnTest::testFormulasmmax() {
1391     auto c1 = Column(QStringLiteral("DataColumn"), Column::ColumnMode::Double);
1392     c1.replaceValues(-1, {1., -1., 5., 5., 3., 8., 10., -5});
1393 
1394     auto c2 = Column(QStringLiteral("FormulaColumn"), Column::ColumnMode::Double);
1395     c2.replaceValues(-1, {11., 12., 13., 14., 15., 16., 17., 18.});
1396 
1397     c2.setFormula(QStringLiteral("smmax(3; x)"), {QStringLiteral("x")}, QVector<Column*>({&c1}), true);
1398     c2.updateFormula();
1399     QCOMPARE(c2.rowCount(), 8);
1400     VALUES_EQUAL(c2.valueAt(0), 1.);
1401     VALUES_EQUAL(c2.valueAt(1), 1.);
1402     VALUES_EQUAL(c2.valueAt(2), 5.);
1403     VALUES_EQUAL(c2.valueAt(3), 5.);
1404     VALUES_EQUAL(c2.valueAt(4), 5.);
1405     VALUES_EQUAL(c2.valueAt(5), 8.);
1406     VALUES_EQUAL(c2.valueAt(6), 10.);
1407     VALUES_EQUAL(c2.valueAt(7), 10.);
1408 }
1409 
1410 void ColumnTest::testFormulasma() {
1411     auto c1 = Column(QStringLiteral("DataColumn"), Column::ColumnMode::Double);
1412     c1.replaceValues(-1, {1., -1., 5., 5., 3., 8., 10., -5});
1413 
1414     auto c2 = Column(QStringLiteral("FormulaColumn"), Column::ColumnMode::Double);
1415     c2.replaceValues(-1, {11., 12., 13., 14., 15., 16., 17., 18.});
1416 
1417     c2.setFormula(QStringLiteral("sma(3; x)"), {QStringLiteral("x")}, QVector<Column*>({&c1}), true);
1418     c2.updateFormula();
1419     QCOMPARE(c2.rowCount(), 8);
1420     VALUES_EQUAL(c2.valueAt(0), 1. / 3.);
1421     VALUES_EQUAL(c2.valueAt(1), 0.);
1422     VALUES_EQUAL(c2.valueAt(2), 5. / 3.);
1423     VALUES_EQUAL(c2.valueAt(3), 3.);
1424     VALUES_EQUAL(c2.valueAt(4), 13. / 3.);
1425     VALUES_EQUAL(c2.valueAt(5), 16. / 3.);
1426     VALUES_EQUAL(c2.valueAt(6), 7.);
1427     VALUES_EQUAL(c2.valueAt(7), 13. / 3.);
1428 }
1429 
1430 void ColumnTest::testFormulasMinColumnInvalid() {
1431     const QVector<double> c1Vector = {1., -1., 5., 5., 3., 8., 10., -5}, c2Vector = {11., 12., 13., 14., 15., 16., 17., 18.};
1432     SETUP_C1_C2_COLUMNS(c1Vector, c2Vector)
1433     COLUMN2_SET_FORMULA_AND_EVALUATE("min()", NAN) // All invalid
1434 }
1435 
1436 void ColumnTest::testFormulasSize() {
1437     const QVector<double> c1Vector = {1., -1., 8., 10., -5}, c2Vector = {11., 12., 13., 14., 15., 16., 17., 18.};
1438     SETUP_C1_C2_COLUMNS(c1Vector, c2Vector)
1439     COLUMN2_SET_FORMULA_AND_EVALUATE("size(x)", 5.)
1440 }
1441 void ColumnTest::testFormulasMin() {
1442     const QVector<double> c1Vector = {1., -1., 8., 10., -5}, c2Vector = {11., 12., 13., 14., 15., 16., 17., 18.};
1443     SETUP_C1_C2_COLUMNS(c1Vector, c2Vector)
1444     COLUMN2_SET_FORMULA_AND_EVALUATE("min(x)", -5.)
1445 }
1446 void ColumnTest::testFormulasMax() {
1447     const QVector<double> c1Vector = {1., -1., 8., 10., -5}, c2Vector = {11., 12., 13., 14., 15., 16., 17., 18.};
1448     SETUP_C1_C2_COLUMNS(c1Vector, c2Vector)
1449     COLUMN2_SET_FORMULA_AND_EVALUATE("max(x)", 10.)
1450 }
1451 void ColumnTest::testFormulasMean() {
1452     const QVector<double> c1Vector = {1., -1., 8., 10., -5}, c2Vector = {11., 12., 13., 14., 15., 16., 17., 18.};
1453     SETUP_C1_C2_COLUMNS(c1Vector, c2Vector)
1454     COLUMN2_SET_FORMULA_AND_EVALUATE("mean(x)", 13. / 5.)
1455 }
1456 void ColumnTest::testFormulasMedian() {
1457     const QVector<double> c1Vector = {1., -1., 8., 10., -5}, c2Vector = {11., 12., 13., 14., 15., 16., 17., 18.};
1458     SETUP_C1_C2_COLUMNS(c1Vector, c2Vector)
1459     COLUMN2_SET_FORMULA_AND_EVALUATE("median(x)", 1.)
1460 }
1461 void ColumnTest::testFormulasStdev() {
1462     const QVector<double> c1Vector = {1., -1., 8., 10., -5}, c2Vector = {11., 12., 13., 14., 15., 16., 17., 18.};
1463     SETUP_C1_C2_COLUMNS(c1Vector, c2Vector)
1464     COLUMN2_SET_FORMULA_AND_EVALUATE("stdev(x)", 6.2689712074) // calculated with octave "std"
1465 }
1466 void ColumnTest::testFormulasVar() {
1467     const QVector<double> c1Vector = {1., -1., 8., 10., -5}, c2Vector = {11., 12., 13., 14., 15., 16., 17., 18.};
1468     SETUP_C1_C2_COLUMNS(c1Vector, c2Vector)
1469     COLUMN2_SET_FORMULA_AND_EVALUATE("var(x)", 39.3) // calculated with octave "var"
1470 }
1471 void ColumnTest::testFormulasGm() {
1472     const QVector<double> c1Vector = {1., 100., 8., 10., 3}, c2Vector = {11., 12., 13., 14., 15., 16., 17., 18.};
1473     SETUP_C1_C2_COLUMNS(c1Vector, c2Vector)
1474     COLUMN2_SET_FORMULA_AND_EVALUATE("gm(x)", 7.51696) // Calculated with R exp(mean(log(x)))
1475 }
1476 void ColumnTest::testFormulasHm() {
1477     const QVector<double> c1Vector = {1., -3., 8., 10., -5}, c2Vector = {11., 12., 13., 14., 15., 16., 17., 18.};
1478     SETUP_C1_C2_COLUMNS(c1Vector, c2Vector)
1479     COLUMN2_SET_FORMULA_AND_EVALUATE("hm(x)", 7.228916) // calculated with R harmonic.mean(x)
1480 }
1481 void ColumnTest::testFormulasChm() {
1482     const QVector<double> c1Vector = {1.0, 0.0, 2.0, 5.0}, c2Vector = {11., 12., 13., 14., 15., 16., 17., 18.};
1483     SETUP_C1_C2_COLUMNS(c1Vector, c2Vector)
1484     COLUMN2_SET_FORMULA_AND_EVALUATE("chm(x)", 3.75) // Result used from: statisticsDoubleZero()
1485 }
1486 void ColumnTest::testFormulasStatisticsMode() {
1487     const QVector<double> c1Vector = {1., -1., 8., 10., -5}, c2Vector = {11., 12., 13., 14., 15., 16., 17., 18.};
1488 
1489     // Calculated with R:
1490     // Mode <- function(x) {
1491     // ux <- unique(x)
1492     //   ux[which.max(tabulate(match(x, ux)))]
1493     // }
1494     // Mode(x)
1495     SETUP_C1_C2_COLUMNS(c1Vector, c2Vector)
1496     COLUMN2_SET_FORMULA_AND_EVALUATE("mode(x)", NAN)
1497 }
1498 void ColumnTest::testFormulasQuartile1() {
1499     const QVector<double> c1Vector = {1., -1., 8., 10., -5}, c2Vector = {11., 12., 13., 14., 15., 16., 17., 18.};
1500     SETUP_C1_C2_COLUMNS(c1Vector, c2Vector)
1501     COLUMN2_SET_FORMULA_AND_EVALUATE("quartile1(x)", -1.) // Calculated with R: summary(x)
1502 }
1503 void ColumnTest::testFormulasQuartile3() {
1504     const QVector<double> c1Vector = {1., -1., 8., 10., -5}, c2Vector = {11., 12., 13., 14., 15., 16., 17., 18.};
1505 
1506     SETUP_C1_C2_COLUMNS(c1Vector, c2Vector)
1507     COLUMN2_SET_FORMULA_AND_EVALUATE("quartile3(x)", 8.) // Calculated with R: summary(x)
1508 }
1509 void ColumnTest::testFormulasIqr() {
1510     const QVector<double> c1Vector = {1., -1., 8., 10., -5}, c2Vector = {11., 12., 13., 14., 15., 16., 17., 18.};
1511 
1512     SETUP_C1_C2_COLUMNS(c1Vector, c2Vector)
1513     COLUMN2_SET_FORMULA_AND_EVALUATE("iqr(x)", 9.); // Calculated with R: IQR(x)
1514 }
1515 void ColumnTest::testFormulasPercentile1() {
1516     const QVector<double> c1Vector = {1., -1., 8., 10., -5}, c2Vector = {11., 12., 13., 14., 15., 16., 17., 18.};
1517 
1518     SETUP_C1_C2_COLUMNS(c1Vector, c2Vector)
1519     COLUMN2_SET_FORMULA_AND_EVALUATE("percentile1(x)", -4.84); // Calculated with R: quantile(x, 1/100)
1520 }
1521 void ColumnTest::testFormulasPercentile5() {
1522     const QVector<double> c1Vector = {1., -1., 8., 10., -5}, c2Vector = {11., 12., 13., 14., 15., 16., 17., 18.};
1523 
1524     SETUP_C1_C2_COLUMNS(c1Vector, c2Vector)
1525     COLUMN2_SET_FORMULA_AND_EVALUATE("percentile5(x)", -4.2); // Calculated with R: quantile(x, 5/100)
1526 }
1527 void ColumnTest::testFormulasPercentile10() {
1528     const QVector<double> c1Vector = {1., -1., 8., 10., -5}, c2Vector = {11., 12., 13., 14., 15., 16., 17., 18.};
1529 
1530     SETUP_C1_C2_COLUMNS(c1Vector, c2Vector)
1531     COLUMN2_SET_FORMULA_AND_EVALUATE("percentile10(x)", -3.4); // Calculated with R: quantile(x, 10/100)
1532 }
1533 void ColumnTest::testFormulasPercentile90() {
1534     const QVector<double> c1Vector = {1., -1., 8., 10., -5}, c2Vector = {11., 12., 13., 14., 15., 16., 17., 18.};
1535 
1536     SETUP_C1_C2_COLUMNS(c1Vector, c2Vector)
1537     COLUMN2_SET_FORMULA_AND_EVALUATE("percentile90(x)", 9.2); // Calculated with R: quantile(x, 90/100)
1538 }
1539 void ColumnTest::testFormulasPercentile95() {
1540     const QVector<double> c1Vector = {1., -1., 8., 10., -5}, c2Vector = {11., 12., 13., 14., 15., 16., 17., 18.};
1541 
1542     SETUP_C1_C2_COLUMNS(c1Vector, c2Vector)
1543     COLUMN2_SET_FORMULA_AND_EVALUATE("percentile95(x)", 9.6); // Calculated with R: quantile(x, 95/100)
1544 }
1545 void ColumnTest::testFormulasPercentile99() {
1546     const QVector<double> c1Vector = {1., -1., 8., 10., -5}, c2Vector = {11., 12., 13., 14., 15., 16., 17., 18.};
1547 
1548     SETUP_C1_C2_COLUMNS(c1Vector, c2Vector)
1549     COLUMN2_SET_FORMULA_AND_EVALUATE("percentile99(x)", 9.92); // Calculated with R: quantile(x, 99/100)
1550 }
1551 void ColumnTest::testFormulasTrimean() {
1552     const QVector<double> c1Vector = {1.0, 0.0, 2.0, 5.0}, c2Vector = {11., 12., 13., 14., 15., 16., 17., 18.};
1553 
1554     SETUP_C1_C2_COLUMNS(c1Vector, c2Vector)
1555     COLUMN2_SET_FORMULA_AND_EVALUATE("trimean(x)", 1.625); // Value used from statisticsDoubleZero()
1556 }
1557 void ColumnTest::testFormulasMeandev() {
1558     const QVector<double> c1Vector = {1.0, 0.0, 2.0, 5.0}, c2Vector = {11., 12., 13., 14., 15., 16., 17., 18.};
1559 
1560     SETUP_C1_C2_COLUMNS(c1Vector, c2Vector)
1561     COLUMN2_SET_FORMULA_AND_EVALUATE("meandev(x)", 1.5); // Value used from statisticsDoubleZero()
1562 }
1563 void ColumnTest::testFormulasMeandevmedian() {
1564     const QVector<double> c1Vector = {1.0, 0.0, 2.0, 5.0}, c2Vector = {11., 12., 13., 14., 15., 16., 17., 18.};
1565 
1566     SETUP_C1_C2_COLUMNS(c1Vector, c2Vector)
1567     COLUMN2_SET_FORMULA_AND_EVALUATE("meandevmedian(x)", 1.5); // Value used from statisticsDoubleZero()
1568 }
1569 void ColumnTest::testFormulasMediandev() {
1570     const QVector<double> c1Vector = {1.0, 0.0, 2.0, 5.0}, c2Vector = {11., 12., 13., 14., 15., 16., 17., 18.};
1571 
1572     SETUP_C1_C2_COLUMNS(c1Vector, c2Vector)
1573     COLUMN2_SET_FORMULA_AND_EVALUATE("mediandev(x)", 1.); // Value used from statisticsDoubleZero()
1574 }
1575 void ColumnTest::testFormulasSkew() {
1576     const QVector<double> c1Vector = {1., -1., 8., 10., -5}, c2Vector = {11., 12., 13., 14., 15., 16., 17., 18.};
1577 
1578     SETUP_C1_C2_COLUMNS(c1Vector, c2Vector)
1579     COLUMN2_SET_FORMULA_AND_EVALUATE("skew(x)", 0.082773441985478); // Calculated with R: skewness(x)
1580 }
1581 void ColumnTest::testFormulasKurt() {
1582     const QVector<double> c1Vector = {1., -1., 8., 10., -5}, c2Vector = {11., 12., 13., 14., 15., 16., 17., 18.};
1583 
1584     SETUP_C1_C2_COLUMNS(c1Vector, c2Vector)
1585     COLUMN2_SET_FORMULA_AND_EVALUATE("kurt(x)", 1.4891031991143); // Calculated with R: kurtosis(x)
1586 }
1587 void ColumnTest::testFormulasEntropy() {
1588     const QVector<double> c1Vector = {1.0, 0.0, 2.0, 5.0}, c2Vector = {11., 12., 13., 14., 15., 16., 17., 18.};
1589 
1590     SETUP_C1_C2_COLUMNS(c1Vector, c2Vector)
1591     COLUMN2_SET_FORMULA_AND_EVALUATE("entropy(x)", 2.); // Value from statisticsDoubleZero()
1592 }
1593 
1594 void ColumnTest::testFormulasQuantile() {
1595     QLocale::setDefault(QLocale::C); // . as decimal separator
1596     const QVector<double> c1Vector = {1., -1., 8., 10., -5}, c2Vector = {11., 12., 13., 14., 15., 16., 17., 18.};
1597 
1598     SETUP_C1_C2_COLUMNS(c1Vector, c2Vector)
1599     COLUMN2_SET_FORMULA_AND_EVALUATE("quantile(0.1;x)", -3.4); // Calculated with R: quantile(x, 0.1)
1600 }
1601 
1602 void ColumnTest::testFormulasPercentile() {
1603     const QVector<double> c1Vector = {1., -1., 8., 10., -5}, c2Vector = {11., 12., 13., 14., 15., 16., 17., 18.};
1604 
1605     SETUP_C1_C2_COLUMNS(c1Vector, c2Vector)
1606     COLUMN2_SET_FORMULA_AND_EVALUATE("percentile(30;x)", -0.6); // Calculated with R: quantile(x, 30/100)
1607 }
1608 
1609 void ColumnTest::clearContentNoFormula() {
1610     Project project;
1611     auto* c = new Column(QStringLiteral("Test"), Column::ColumnMode::Double);
1612     project.addChild(c);
1613     c->resizeTo(100);
1614     QCOMPARE(c->rowCount(), 100);
1615 
1616     for (int i = 0; i < c->rowCount(); i++)
1617         c->setValueAt(i, i);
1618 
1619     QCOMPARE(c->rowCount(), 100);
1620     for (int i = 0; i < c->rowCount(); i++)
1621         QCOMPARE(c->valueAt(i), i);
1622 
1623     c->clear();
1624 
1625     QCOMPARE(c->rowCount(), 100);
1626     for (int i = 0; i < c->rowCount(); i++)
1627         QCOMPARE(c->valueAt(i), NAN);
1628 }
1629 
1630 void ColumnTest::clearContentFormula() {
1631     Project project;
1632     auto* c = new Column(QStringLiteral("Test"), Column::ColumnMode::Double);
1633     project.addChild(c);
1634     c->resizeTo(100);
1635     QCOMPARE(c->rowCount(), 100);
1636 
1637     for (int i = 0; i < c->rowCount(); i++)
1638         c->setValueAt(i, i);
1639 
1640     QCOMPARE(c->rowCount(), 100);
1641     for (int i = 0; i < c->rowCount(); i++)
1642         QCOMPARE(c->valueAt(i), i);
1643 
1644     c->setFormula(QStringLiteral("x"), {QStringLiteral("zet")}, QVector<Column*>({c}), true);
1645     c->clear();
1646 
1647     QCOMPARE(c->rowCount(), 100);
1648     for (int i = 0; i < c->rowCount(); i++)
1649         QCOMPARE(c->valueAt(i), NAN);
1650 
1651     QCOMPARE(c->formula().isEmpty(), true);
1652 
1653     c->undoStack()->undo();
1654 
1655     QCOMPARE(c->rowCount(), 100);
1656     for (int i = 0; i < c->rowCount(); i++)
1657         QCOMPARE(c->valueAt(i), i);
1658 
1659     QCOMPARE(c->formula(), QStringLiteral("x"));
1660     QCOMPARE(c->formulaData().count(), 1);
1661     QCOMPARE(c->formulaData().at(0).column(), c);
1662     QCOMPARE(c->formulaData().at(0).variableName(), QStringLiteral("zet"));
1663 }
1664 
1665 void ColumnTest::testRowCountMonotonIncrease() {
1666     Column c(QStringLiteral("Test"), Column::ColumnMode::Double);
1667     c.replaceValues(-1, {-3., 1., 2., 5., 11.});
1668 
1669     QCOMPARE(c.rowCount(1., 5.), 3);
1670 }
1671 
1672 void ColumnTest::testRowCountMonotonDecrease() {
1673     Column c(QStringLiteral("Test"), Column::ColumnMode::Double);
1674     c.replaceValues(-1, {11., 9., 3., -4., -154.});
1675 
1676     QCOMPARE(c.rowCount(9., -4.), 3);
1677 }
1678 
1679 void ColumnTest::testRowCountNonMonoton() {
1680     Column c(QStringLiteral("Test"), Column::ColumnMode::Double);
1681     c.replaceValues(-1, {-3., 1., -5., 5., 11., 9., 100., 2., 4.});
1682 
1683     QCOMPARE(c.rowCount(1., 5.), 4);
1684 }
1685 
1686 void ColumnTest::testRowCountDateTime() {
1687     Column c(QStringLiteral("Test"), Column::ColumnMode::DateTime);
1688     c.replaceDateTimes(-1,
1689                        {QDateTime::fromString(QStringLiteral("2018-03-26T02:14:34.000Z"), Qt::DateFormat::ISODateWithMs),
1690                         QDateTime::fromString(QStringLiteral("2018-03-27T02:14:34.000Z"), Qt::DateFormat::ISODateWithMs),
1691                         QDateTime::fromString(QStringLiteral("2018-03-28T02:14:34.000Z"), Qt::DateFormat::ISODateWithMs),
1692                         QDateTime::fromString(QStringLiteral("2018-03-29T02:14:34.000Z"), Qt::DateFormat::ISODateWithMs),
1693                         QDateTime::fromString(QStringLiteral("2018-03-30T02:14:34.000Z"), Qt::DateFormat::ISODateWithMs),
1694                         QDateTime::fromString(QStringLiteral("2018-03-31T02:14:34.000Z"), Qt::DateFormat::ISODateWithMs)});
1695 
1696     QCOMPARE(c.rowCount(QDateTime::fromString(QStringLiteral("2018-03-27T02:14:34.000Z"), Qt::DateFormat::ISODateWithMs).toMSecsSinceEpoch(),
1697                         QDateTime::fromString(QStringLiteral("2018-03-30T02:14:34.000Z"), Qt::DateFormat::ISODateWithMs).toMSecsSinceEpoch()),
1698              4);
1699 }
1700 
1701 void ColumnTest::testRowCountDateTimeMonotonDecrease() {
1702     Column c(QStringLiteral("Test"), Column::ColumnMode::DateTime);
1703     c.replaceDateTimes(-1,
1704                        {
1705                            QDateTime::fromString(QStringLiteral("2018-03-31T02:14:34.000Z"), Qt::DateFormat::ISODateWithMs),
1706                            QDateTime::fromString(QStringLiteral("2018-03-30T02:14:34.000Z"), Qt::DateFormat::ISODateWithMs),
1707                            QDateTime::fromString(QStringLiteral("2018-03-29T02:14:34.000Z"), Qt::DateFormat::ISODateWithMs),
1708                            QDateTime::fromString(QStringLiteral("2018-03-28T02:14:34.000Z"), Qt::DateFormat::ISODateWithMs),
1709                            QDateTime::fromString(QStringLiteral("2018-03-27T02:14:34.000Z"), Qt::DateFormat::ISODateWithMs),
1710                            QDateTime::fromString(QStringLiteral("2018-03-26T02:14:34.000Z"), Qt::DateFormat::ISODateWithMs),
1711                        });
1712 
1713     QCOMPARE(c.rowCount(QDateTime::fromString(QStringLiteral("2018-03-27T02:14:34.000Z"), Qt::DateFormat::ISODateWithMs).toMSecsSinceEpoch(),
1714                         QDateTime::fromString(QStringLiteral("2018-03-30T02:14:34.000Z"), Qt::DateFormat::ISODateWithMs).toMSecsSinceEpoch()),
1715              4);
1716 }
1717 
1718 void ColumnTest::testRowCountValueLabels() {
1719     Column c(QStringLiteral("Test"), Column::ColumnMode::Double);
1720     c.addValueLabel(-2., QStringLiteral("Status 1"));
1721     c.addValueLabel(-1., QStringLiteral("Status 2"));
1722     c.addValueLabel(4., QStringLiteral("Status 3"));
1723     c.addValueLabel(5., QStringLiteral("Status 2"));
1724     c.addValueLabel(6., QStringLiteral("Status 3"));
1725 
1726     QCOMPARE(c.valueLabelsCount(-1., 5.), 3);
1727 }
1728 
1729 void ColumnTest::testRowCountValueLabelsDateTime() {
1730     Column c(QStringLiteral("Test"), Column::ColumnMode::DateTime);
1731     c.addValueLabel(QDateTime::fromString(QStringLiteral("2018-03-26T02:14:34.000Z"), Qt::DateFormat::ISODateWithMs), QStringLiteral("Status 1"));
1732     c.addValueLabel(QDateTime::fromString(QStringLiteral("2018-03-27T02:14:34.000Z"), Qt::DateFormat::ISODateWithMs), QStringLiteral("Status 2"));
1733     c.addValueLabel(QDateTime::fromString(QStringLiteral("2018-03-28T02:14:34.000Z"), Qt::DateFormat::ISODateWithMs), QStringLiteral("Status 3"));
1734     c.addValueLabel(QDateTime::fromString(QStringLiteral("2018-03-30T02:14:34.000Z"), Qt::DateFormat::ISODateWithMs), QStringLiteral("Status 4"));
1735     c.addValueLabel(QDateTime::fromString(QStringLiteral("2018-03-31T02:14:34.000Z"), Qt::DateFormat::ISODateWithMs), QStringLiteral("Status 5"));
1736 
1737     QCOMPARE(c.valueLabelsCount(QDateTime::fromString(QStringLiteral("2018-03-27T02:14:34.000Z"), Qt::DateFormat::ISODateWithMs).toMSecsSinceEpoch(),
1738                                 QDateTime::fromString(QStringLiteral("2018-03-30T02:14:34.000Z"), Qt::DateFormat::ISODateWithMs).toMSecsSinceEpoch()),
1739              3);
1740 }
1741 
1742 QTEST_MAIN(ColumnTest)