File indexing completed on 2024-07-21 10:09:33

0001 // SPDX-FileCopyrightText: Lukas Sommer <sommerluk@gmail.com>
0002 // SPDX-License-Identifier: BSD-2-Clause OR MIT
0003 
0004 // First included header is the public header of the class we are testing;
0005 // this forces the header to be self-contained.
0006 #include "helpermath.h"
0007 
0008 #include <algorithm>
0009 #include <cmath>
0010 #include <limits>
0011 #include <optional>
0012 #include <qgenericmatrix.h>
0013 #include <qglobal.h>
0014 #include <qobject.h>
0015 #include <qtest.h>
0016 #include <qtestcase.h>
0017 #include <qtestdata.h>
0018 
0019 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0020 #include <qtmetamacros.h>
0021 #else
0022 #include <qobjectdefs.h>
0023 #include <qstring.h>
0024 #endif
0025 
0026 namespace PerceptualColor
0027 {
0028 
0029 class TestHelperMath : public QObject
0030 {
0031     Q_OBJECT
0032 
0033 public:
0034     explicit TestHelperMath(QObject *parent = nullptr)
0035         : QObject(parent)
0036     {
0037     }
0038 
0039 private Q_SLOTS:
0040 
0041     void initTestCase()
0042     {
0043         // Called before the first test function is executed
0044     }
0045 
0046     void cleanupTestCase()
0047     {
0048         // Called after the last test function was executed
0049     }
0050 
0051     void init()
0052     {
0053         // Called before each test function is executed
0054     }
0055     void cleanup()
0056     {
0057         // Called after every test function
0058     }
0059 
0060     void testInRangeInt()
0061     {
0062         QCOMPARE(PerceptualColor::isInRange<int>(3, 3, 2), false);
0063         QCOMPARE(PerceptualColor::isInRange<int>(3, 2, 2), false);
0064         QCOMPARE(PerceptualColor::isInRange<int>(3, 0, 2), false);
0065         QCOMPARE(PerceptualColor::isInRange<int>(3, 4, 2), false);
0066         QCOMPARE(PerceptualColor::isInRange<int>(3, 3, 3), true);
0067         QCOMPARE(PerceptualColor::isInRange<int>(3, 4, 3), false);
0068         QCOMPARE(PerceptualColor::isInRange<int>(3, 2, 3), false);
0069         QCOMPARE(PerceptualColor::isInRange<int>(0, 1, 2), true);
0070         QCOMPARE(PerceptualColor::isInRange<int>(0, 0, 2), true);
0071         QCOMPARE(PerceptualColor::isInRange<int>(0, 2, 2), true);
0072         QCOMPARE(PerceptualColor::isInRange<int>(0, 3, 2), false);
0073         QCOMPARE(PerceptualColor::isInRange<int>(0, -1, 2), false);
0074         QCOMPARE(PerceptualColor::isInRange<int>(1, 2, 3), true);
0075         QCOMPARE(PerceptualColor::isInRange<int>(1, 1, 3), true);
0076         QCOMPARE(PerceptualColor::isInRange<int>(1, 3, 3), true);
0077         QCOMPARE(PerceptualColor::isInRange<int>(1, 0, 3), false);
0078         QCOMPARE(PerceptualColor::isInRange<int>(1, 4, 3), false);
0079         QCOMPARE(PerceptualColor::isInRange<int>(-1, 0, 1), true);
0080         QCOMPARE(PerceptualColor::isInRange<int>(-1, -1, 1), true);
0081         QCOMPARE(PerceptualColor::isInRange<int>(-1, 1, 1), true);
0082         QCOMPARE(PerceptualColor::isInRange<int>(-1, 2, 1), false);
0083         QCOMPARE(PerceptualColor::isInRange<int>(-1, -2, 1), false);
0084         QCOMPARE(PerceptualColor::isInRange<int>(-2, -1, 0), true);
0085         QCOMPARE(PerceptualColor::isInRange<int>(-2, -2, 0), true);
0086         QCOMPARE(PerceptualColor::isInRange<int>(-2, 0, 0), true);
0087         QCOMPARE(PerceptualColor::isInRange<int>(-2, -3, 0), false);
0088         QCOMPARE(PerceptualColor::isInRange<int>(-2, 1, 0), false);
0089         QCOMPARE(PerceptualColor::isInRange<int>(-3, -2, -1), true);
0090         QCOMPARE(PerceptualColor::isInRange<int>(-3, -3, -1), true);
0091         QCOMPARE(PerceptualColor::isInRange<int>(-3, -1, -1), true);
0092         QCOMPARE(PerceptualColor::isInRange<int>(-3, -4, -1), false);
0093         QCOMPARE(PerceptualColor::isInRange<int>(-3, 0, -1), false);
0094         QCOMPARE(PerceptualColor::isInRange<double>(3, 3, 2), false);
0095         QCOMPARE(PerceptualColor::isInRange<double>(3, 2, 2), false);
0096         QCOMPARE(PerceptualColor::isInRange<double>(3, 0, 2), false);
0097         QCOMPARE(PerceptualColor::isInRange<double>(3, 4, 2), false);
0098         QCOMPARE(PerceptualColor::isInRange<double>(3, 3, 3), true);
0099         QCOMPARE(PerceptualColor::isInRange<double>(3, 4, 3), false);
0100         QCOMPARE(PerceptualColor::isInRange<double>(3, 2, 3), false);
0101         QCOMPARE(PerceptualColor::isInRange<double>(0, 1, 2), true);
0102         QCOMPARE(PerceptualColor::isInRange<double>(0, 0, 2), true);
0103         QCOMPARE(PerceptualColor::isInRange<double>(0, 2, 2), true);
0104         QCOMPARE(PerceptualColor::isInRange<double>(0, 3, 2), false);
0105         QCOMPARE(PerceptualColor::isInRange<double>(0, -1, 2), false);
0106         QCOMPARE(PerceptualColor::isInRange<double>(1, 2, 3), true);
0107         QCOMPARE(PerceptualColor::isInRange<double>(1, 1, 3), true);
0108         QCOMPARE(PerceptualColor::isInRange<double>(1, 3, 3), true);
0109         QCOMPARE(PerceptualColor::isInRange<double>(1, 0, 3), false);
0110         QCOMPARE(PerceptualColor::isInRange<double>(1, 4, 3), false);
0111         QCOMPARE(PerceptualColor::isInRange<double>(-1, 0, 1), true);
0112         QCOMPARE(PerceptualColor::isInRange<double>(-1, -1, 1), true);
0113         QCOMPARE(PerceptualColor::isInRange<double>(-1, 1, 1), true);
0114         QCOMPARE(PerceptualColor::isInRange<double>(-1, 2, 1), false);
0115         QCOMPARE(PerceptualColor::isInRange<double>(-1, -2, 1), false);
0116         QCOMPARE(PerceptualColor::isInRange<double>(-2, -1, 0), true);
0117         QCOMPARE(PerceptualColor::isInRange<double>(-2, -2, 0), true);
0118         QCOMPARE(PerceptualColor::isInRange<double>(-2, 0, 0), true);
0119         QCOMPARE(PerceptualColor::isInRange<double>(-2, -3, 0), false);
0120         QCOMPARE(PerceptualColor::isInRange<double>(-2, 1, 0), false);
0121         QCOMPARE(PerceptualColor::isInRange<double>(-3, -2, -1), true);
0122         QCOMPARE(PerceptualColor::isInRange<double>(-3, -3, -1), true);
0123         QCOMPARE(PerceptualColor::isInRange<double>(-3, -1, -1), true);
0124         QCOMPARE(PerceptualColor::isInRange<double>(-3, -4, -1), false);
0125         QCOMPARE(PerceptualColor::isInRange<double>(-3, 0, -1), false);
0126 
0127         QCOMPARE(PerceptualColor::isInRange<double>(-3.1, 0.2, -1.3), false);
0128 
0129         if constexpr (std::numeric_limits<double>::has_infinity) {
0130             constexpr auto inf = std::numeric_limits<double>::infinity();
0131             QCOMPARE(PerceptualColor::isInRange<double>(-inf, 0, inf), //
0132                      true);
0133             QCOMPARE(PerceptualColor::isInRange<double>(-5, 0, inf), //
0134                      true);
0135             QCOMPARE(PerceptualColor::isInRange<double>(-inf, 0, 5), //
0136                      true);
0137             QCOMPARE(PerceptualColor::isInRange<double>(inf, 0, -inf), //
0138                      false);
0139             QCOMPARE(PerceptualColor::isInRange<double>(5, 0, -inf), //
0140                      false);
0141             QCOMPARE(PerceptualColor::isInRange<double>(inf, 0, -5), //
0142                      false);
0143             QCOMPARE(PerceptualColor::isInRange<double>(-inf, -inf, inf), //
0144                      true);
0145             QCOMPARE(PerceptualColor::isInRange<double>(-inf, inf, inf), //
0146                      true);
0147             QCOMPARE(PerceptualColor::isInRange<double>(-5, inf, inf), //
0148                      true);
0149             QCOMPARE(PerceptualColor::isInRange<double>(-inf, -inf, 5), //
0150                      true);
0151         }
0152 
0153         if constexpr (std::numeric_limits<double>::has_quiet_NaN) {
0154             constexpr auto nan = std::numeric_limits<double>::quiet_NaN();
0155             QCOMPARE(PerceptualColor::isInRange<double>(3, 4, 5), //
0156                      true);
0157             QCOMPARE(PerceptualColor::isInRange<double>(nan, 4, 5), //
0158                      false);
0159             QCOMPARE(PerceptualColor::isInRange<double>(3, nan, 5), //
0160                      false);
0161             QCOMPARE(PerceptualColor::isInRange<double>(3, 4, nan), //
0162                      false);
0163 
0164             QCOMPARE(PerceptualColor::isInRange<double>(5, 4, 3), //
0165                      false);
0166             QCOMPARE(PerceptualColor::isInRange<double>(nan, 4, 3), //
0167                      false);
0168             QCOMPARE(PerceptualColor::isInRange<double>(5, nan, 3), //
0169                      false);
0170             QCOMPARE(PerceptualColor::isInRange<double>(5, 4, nan), //
0171                      false);
0172         }
0173     }
0174 
0175     void testRounding()
0176     {
0177         QCOMPARE(roundToDigits(12.3456, 6), 12.345600);
0178         QCOMPARE(roundToDigits(12.3456, 5), 12.34560);
0179         QCOMPARE(roundToDigits(12.3456, 4), 12.3456);
0180         QCOMPARE(roundToDigits(12.3456, 3), 12.346);
0181         QCOMPARE(roundToDigits(12.3456, 2), 12.35);
0182         QCOMPARE(roundToDigits(12.3456, 1), 12.3);
0183         QCOMPARE(roundToDigits(12.3456, 0), 12.);
0184         QCOMPARE(roundToDigits(12.3456, -1), 10.);
0185         QCOMPARE(roundToDigits(12.3456, -2), 0.);
0186         QCOMPARE(roundToDigits(92.3456, -2), 100.);
0187 
0188         QCOMPARE(roundToDigits(-12.3456, 6), -12.345600);
0189         QCOMPARE(roundToDigits(-12.3456, 5), -12.34560);
0190         QCOMPARE(roundToDigits(-12.3456, 4), -12.3456);
0191         QCOMPARE(roundToDigits(-12.3456, 3), -12.346);
0192         QCOMPARE(roundToDigits(-12.3456, 2), -12.35);
0193         QCOMPARE(roundToDigits(-12.3456, 1), -12.3);
0194         QCOMPARE(roundToDigits(-12.3456, 0), -12.);
0195         QCOMPARE(roundToDigits(-12.3456, -1), -10.);
0196         QCOMPARE(roundToDigits(-12.3456, -2), -0.);
0197         QCOMPARE(roundToDigits(-92.3456, -2), -100.);
0198 
0199         if constexpr (std::numeric_limits<double>::has_infinity) {
0200             QCOMPARE( //
0201                 roundToDigits(std::numeric_limits<double>::infinity(), 6), //
0202                 std::numeric_limits<double>::infinity());
0203             QCOMPARE( //
0204                 roundToDigits(-std::numeric_limits<double>::infinity(), 6), //
0205                 -std::numeric_limits<double>::infinity());
0206         }
0207 
0208         if constexpr (std::numeric_limits<double>::has_quiet_NaN) {
0209             constexpr auto quietNan = std::numeric_limits<double>::quiet_NaN();
0210             QVERIFY(std::isnan(roundToDigits(quietNan, 6)));
0211         }
0212 
0213         if constexpr (std::numeric_limits<double>::has_signaling_NaN) {
0214             constexpr auto signalingNan = //
0215                 std::numeric_limits<double>::signaling_NaN();
0216             QVERIFY(std::isnan(roundToDigits(signalingNan, 6)));
0217         }
0218     }
0219 
0220     void testIsOdd()
0221     {
0222         QCOMPARE(isOdd(-2), false);
0223         QCOMPARE(isOdd(-1), true);
0224         QCOMPARE(isOdd(-0), false);
0225         QCOMPARE(isOdd(0), false);
0226         QCOMPARE(isOdd(1), true);
0227         QCOMPARE(isOdd(2), false);
0228     }
0229 
0230     void testIsNearlyEqual()
0231     {
0232         QVERIFY(isNearlyEqual(2., 2.));
0233         QVERIFY(isNearlyEqual(1.5, 1.5));
0234         QVERIFY(isNearlyEqual(1., 1.));
0235         QVERIFY(isNearlyEqual(0.5, 0.5));
0236         QVERIFY(isNearlyEqual(0., 0.));
0237         QVERIFY(isNearlyEqual(-0., 0.));
0238         QVERIFY(isNearlyEqual(0., -0.));
0239         QVERIFY(isNearlyEqual(-0., -0.));
0240         QVERIFY(isNearlyEqual(-0.5, -0.5));
0241 
0242         QVERIFY(!isNearlyEqual(0., 1.));
0243 
0244         if constexpr (std::numeric_limits<double>::has_infinity) {
0245             QVERIFY(isNearlyEqual(std::numeric_limits<double>::infinity(), //
0246                                   std::numeric_limits<double>::infinity()));
0247             QVERIFY(isNearlyEqual(-std::numeric_limits<double>::infinity(), //
0248                                   -std::numeric_limits<double>::infinity()));
0249             QVERIFY(!isNearlyEqual(std::numeric_limits<double>::infinity(), //
0250                                    -std::numeric_limits<double>::infinity()));
0251             QVERIFY(!isNearlyEqual(std::numeric_limits<double>::infinity(), //
0252                                    5.));
0253             QVERIFY(!isNearlyEqual(-std::numeric_limits<double>::infinity(), //
0254                                    5.));
0255         }
0256 
0257         if constexpr (std::numeric_limits<double>::has_quiet_NaN) {
0258             // NaN should never compare equal to itself or any other value.
0259             QVERIFY(!isNearlyEqual(std::numeric_limits<double>::quiet_NaN(), //
0260                                    5.));
0261             QVERIFY(!isNearlyEqual(std::numeric_limits<double>::quiet_NaN(), //
0262                                    0.));
0263             QVERIFY(!isNearlyEqual(std::numeric_limits<double>::quiet_NaN(), //
0264                                    std::numeric_limits<double>::infinity()));
0265             QVERIFY(!isNearlyEqual(std::numeric_limits<double>::quiet_NaN(), //
0266                                    -std::numeric_limits<double>::infinity()));
0267             QVERIFY(!isNearlyEqual(std::numeric_limits<double>::quiet_NaN(), //
0268                                    std::numeric_limits<double>::quiet_NaN()));
0269 
0270             // NaN should never compare equal to itself or any other value.
0271             QVERIFY( //
0272                 !isNearlyEqual(std::numeric_limits<double>::signaling_NaN(), //
0273                                5.));
0274             QVERIFY( //
0275                 !isNearlyEqual(std::numeric_limits<double>::signaling_NaN(), //
0276                                0.));
0277             QVERIFY( //
0278                 !isNearlyEqual(std::numeric_limits<double>::signaling_NaN(), //
0279                                std::numeric_limits<double>::infinity()));
0280             QVERIFY( //
0281                 !isNearlyEqual(std::numeric_limits<double>::signaling_NaN(), //
0282                                -std::numeric_limits<double>::infinity()));
0283             QVERIFY( //
0284                 !isNearlyEqual(std::numeric_limits<double>::signaling_NaN(), //
0285                                std::numeric_limits<double>::signaling_NaN()));
0286         }
0287 
0288         if constexpr (std::numeric_limits<double>::has_signaling_NaN //
0289                       && std::numeric_limits<double>::has_quiet_NaN //
0290         ) {
0291             QVERIFY( //
0292                 !isNearlyEqual(std::numeric_limits<double>::signaling_NaN(), //
0293                                std::numeric_limits<double>::quiet_NaN()));
0294         }
0295     }
0296 
0297     void testIsNearlyEqualEpsilon()
0298     {
0299         QVERIFY(isNearlyEqual(2., 2., 0.1));
0300         QVERIFY(isNearlyEqual(1.5, 1.5, 0.1));
0301         QVERIFY(isNearlyEqual(1., 1., 0.1));
0302         QVERIFY(isNearlyEqual(0.5, 0.5, 0.1));
0303         QVERIFY(isNearlyEqual(0., 0., 0.1));
0304         QVERIFY(isNearlyEqual(-0., 0., 0.1));
0305         QVERIFY(isNearlyEqual(0., -0., 0.1));
0306         QVERIFY(isNearlyEqual(-0., -0., 0.1));
0307         QVERIFY(isNearlyEqual(-0.5, -0.5, 0.1));
0308 
0309         QVERIFY(isNearlyEqual(2., 2., 0.));
0310         QVERIFY(isNearlyEqual(2., 2., -0.));
0311         QVERIFY(isNearlyEqual(2., 2., -1.));
0312 
0313         QVERIFY(isNearlyEqual(5., 6., 2.));
0314 
0315         QVERIFY(!isNearlyEqual(0., 1., 0.));
0316         QVERIFY(!isNearlyEqual(0., 1., -0.));
0317         QVERIFY(!isNearlyEqual(0., 1., -1.));
0318 
0319         if constexpr (std::numeric_limits<double>::has_infinity) {
0320             QVERIFY(isNearlyEqual(std::numeric_limits<double>::infinity(), //
0321                                   std::numeric_limits<double>::infinity(),
0322                                   2.));
0323             QVERIFY(isNearlyEqual(-std::numeric_limits<double>::infinity(), //
0324                                   -std::numeric_limits<double>::infinity(),
0325                                   2.));
0326             QVERIFY(!isNearlyEqual(std::numeric_limits<double>::infinity(), //
0327                                    -std::numeric_limits<double>::infinity(),
0328                                    2.));
0329             QVERIFY(!isNearlyEqual(std::numeric_limits<double>::infinity(), //
0330                                    5.,
0331                                    2.));
0332             QVERIFY(!isNearlyEqual(-std::numeric_limits<double>::infinity(), //
0333                                    5.,
0334                                    2.));
0335 
0336             QVERIFY(isNearlyEqual(std::numeric_limits<double>::infinity(), //
0337                                   std::numeric_limits<double>::infinity(),
0338                                   std::numeric_limits<double>::infinity()));
0339             QVERIFY(isNearlyEqual(-std::numeric_limits<double>::infinity(), //
0340                                   -std::numeric_limits<double>::infinity(),
0341                                   std::numeric_limits<double>::infinity()));
0342 
0343             QVERIFY(isNearlyEqual(std::numeric_limits<double>::infinity(), //
0344                                   std::numeric_limits<double>::infinity(),
0345                                   -std::numeric_limits<double>::infinity()));
0346             QVERIFY(isNearlyEqual(-std::numeric_limits<double>::infinity(), //
0347                                   -std::numeric_limits<double>::infinity(),
0348                                   -std::numeric_limits<double>::infinity()));
0349             QVERIFY(!isNearlyEqual(std::numeric_limits<double>::infinity(), //
0350                                    -std::numeric_limits<double>::infinity(),
0351                                    -std::numeric_limits<double>::infinity()));
0352             QVERIFY(!isNearlyEqual(std::numeric_limits<double>::infinity(), //
0353                                    5.,
0354                                    -std::numeric_limits<double>::infinity()));
0355             QVERIFY(!isNearlyEqual(-std::numeric_limits<double>::infinity(), //
0356                                    5.,
0357                                    -std::numeric_limits<double>::infinity()));
0358 
0359             if constexpr (std::numeric_limits<double>::has_quiet_NaN) {
0360                 QVERIFY( //
0361                     !isNearlyEqual(std::numeric_limits<double>::infinity(), //
0362                                    std::numeric_limits<double>::infinity(),
0363                                    std::numeric_limits<double>::quiet_NaN()));
0364                 QVERIFY( //
0365                     !isNearlyEqual(-std::numeric_limits<double>::infinity(), //
0366                                    -std::numeric_limits<double>::infinity(),
0367                                    std::numeric_limits<double>::quiet_NaN()));
0368                 QVERIFY( //
0369                     !isNearlyEqual(std::numeric_limits<double>::infinity(), //
0370                                    -std::numeric_limits<double>::infinity(),
0371                                    std::numeric_limits<double>::quiet_NaN()));
0372                 QVERIFY( //
0373                     !isNearlyEqual(std::numeric_limits<double>::infinity(), //
0374                                    5.,
0375                                    std::numeric_limits<double>::quiet_NaN()));
0376                 QVERIFY( //
0377                     !isNearlyEqual(-std::numeric_limits<double>::infinity(), //
0378                                    5.,
0379                                    std::numeric_limits<double>::quiet_NaN()));
0380             }
0381         }
0382 
0383         if constexpr (std::numeric_limits<double>::has_quiet_NaN) {
0384             // NaN should never compare equal to itself or any other value.
0385             QVERIFY(!isNearlyEqual(std::numeric_limits<double>::quiet_NaN(), //
0386                                    5.,
0387                                    2.));
0388             QVERIFY(!isNearlyEqual(std::numeric_limits<double>::quiet_NaN(), //
0389                                    0.,
0390                                    2.));
0391             QVERIFY(!isNearlyEqual(std::numeric_limits<double>::quiet_NaN(), //
0392                                    std::numeric_limits<double>::infinity(),
0393                                    2.));
0394             QVERIFY(!isNearlyEqual(std::numeric_limits<double>::quiet_NaN(), //
0395                                    -std::numeric_limits<double>::infinity(),
0396                                    2.));
0397             QVERIFY(!isNearlyEqual(std::numeric_limits<double>::quiet_NaN(), //
0398                                    std::numeric_limits<double>::quiet_NaN(),
0399                                    2.));
0400             QVERIFY(!isNearlyEqual(std::numeric_limits<double>::quiet_NaN(), //
0401                                    5.,
0402                                    std::numeric_limits<double>::infinity()));
0403             QVERIFY(!isNearlyEqual(std::numeric_limits<double>::quiet_NaN(), //
0404                                    0.,
0405                                    std::numeric_limits<double>::infinity()));
0406             QVERIFY(!isNearlyEqual(std::numeric_limits<double>::quiet_NaN(), //
0407                                    std::numeric_limits<double>::infinity(),
0408                                    std::numeric_limits<double>::infinity()));
0409             QVERIFY(!isNearlyEqual(std::numeric_limits<double>::quiet_NaN(), //
0410                                    -std::numeric_limits<double>::infinity(),
0411                                    std::numeric_limits<double>::infinity()));
0412             QVERIFY(!isNearlyEqual(std::numeric_limits<double>::quiet_NaN(), //
0413                                    std::numeric_limits<double>::quiet_NaN(),
0414                                    std::numeric_limits<double>::infinity()));
0415 
0416             // NaN should never compare equal to itself or any other value.
0417             QVERIFY( //
0418                 !isNearlyEqual(std::numeric_limits<double>::signaling_NaN(), //
0419                                5.,
0420                                2.));
0421             QVERIFY( //
0422                 !isNearlyEqual(std::numeric_limits<double>::signaling_NaN(), //
0423                                0.,
0424                                2.));
0425             QVERIFY( //
0426                 !isNearlyEqual(std::numeric_limits<double>::signaling_NaN(), //
0427                                std::numeric_limits<double>::infinity(),
0428                                2.));
0429             QVERIFY( //
0430                 !isNearlyEqual(std::numeric_limits<double>::signaling_NaN(), //
0431                                -std::numeric_limits<double>::infinity(),
0432                                2.));
0433             QVERIFY( //
0434                 !isNearlyEqual(std::numeric_limits<double>::signaling_NaN(), //
0435                                std::numeric_limits<double>::signaling_NaN(),
0436                                2.));
0437 
0438             QVERIFY( //
0439                 !isNearlyEqual(std::numeric_limits<double>::signaling_NaN(), //
0440                                5.,
0441                                std::numeric_limits<double>::infinity()));
0442             QVERIFY( //
0443                 !isNearlyEqual(std::numeric_limits<double>::signaling_NaN(), //
0444                                0.,
0445                                std::numeric_limits<double>::infinity()));
0446             QVERIFY( //
0447                 !isNearlyEqual(std::numeric_limits<double>::signaling_NaN(), //
0448                                std::numeric_limits<double>::infinity(),
0449                                std::numeric_limits<double>::infinity()));
0450             QVERIFY( //
0451                 !isNearlyEqual(std::numeric_limits<double>::signaling_NaN(), //
0452                                -std::numeric_limits<double>::infinity(),
0453                                std::numeric_limits<double>::infinity()));
0454             QVERIFY( //
0455                 !isNearlyEqual(std::numeric_limits<double>::signaling_NaN(), //
0456                                std::numeric_limits<double>::signaling_NaN(),
0457                                std::numeric_limits<double>::infinity()));
0458         }
0459 
0460         if constexpr (std::numeric_limits<double>::has_signaling_NaN //
0461                       && std::numeric_limits<double>::has_quiet_NaN //
0462         ) {
0463             QVERIFY( //
0464                 !isNearlyEqual(std::numeric_limits<double>::signaling_NaN(), //
0465                                std::numeric_limits<double>::quiet_NaN(),
0466                                2.));
0467             QVERIFY( //
0468                 !isNearlyEqual(std::numeric_limits<double>::signaling_NaN(), //
0469                                std::numeric_limits<double>::quiet_NaN(),
0470                                std::numeric_limits<double>::infinity()));
0471         }
0472     }
0473 
0474     void testNormalizeAngle()
0475     {
0476         QCOMPARE(normalizedAngle360(0.), 0);
0477         QCOMPARE(normalizedAngle360(359.9), 359.9);
0478         QCOMPARE(normalizedAngle360(360.), 0);
0479         QCOMPARE(normalizedAngle360(720.), 0);
0480         QCOMPARE(normalizedAngle360(-1.), 359);
0481         QCOMPARE(normalizedAngle360(-1.3), 358.7);
0482     }
0483 
0484     void testCreateSquareMatrix3()
0485     {
0486         // clang-format off
0487         const auto temp = createSquareMatrix3(
0488             1, 2, 3,
0489             4, 5, 6,
0490             7, 8, 9);
0491         // clang-format on
0492         QCOMPARE(temp(0, 0), 1);
0493         QCOMPARE(temp(0, 1), 2);
0494         QCOMPARE(temp(0, 2), 3);
0495         QCOMPARE(temp(1, 0), 4);
0496         QCOMPARE(temp(1, 1), 5);
0497         QCOMPARE(temp(1, 2), 6);
0498         QCOMPARE(temp(2, 0), 7);
0499         QCOMPARE(temp(2, 1), 8);
0500         QCOMPARE(temp(2, 2), 9);
0501     }
0502 
0503     void testInverseMatrixFromNonInvertible()
0504     {
0505         // clang-format off
0506         const auto temp = createSquareMatrix3(
0507             1, 2, 3, //
0508             4, 5, 6, //
0509             7, 8, 9);
0510         // clang-format on
0511         const auto inverse = inverseMatrix(temp);
0512         QVERIFY(!inverse.has_value());
0513     }
0514 
0515     void testInverseMatrixFromInvertible()
0516     {
0517         // clang-format off
0518         const auto temp = createSquareMatrix3(
0519             1, 2, 1, //
0520             0, 1, 3, //
0521             -1, 0, 1);
0522         // clang-format on
0523         const auto actualInverse = inverseMatrix(temp);
0524         // clang-format off
0525         const auto expectedInverse = createSquareMatrix3(
0526             -0.25, 0.5, -1.25,
0527             0.75, -0.5, 0.75,
0528             -0.25, 0.5, -0.25);
0529         // clang-format on
0530         QCOMPARE(actualInverse, expectedInverse);
0531     }
0532 
0533     void testCreateTrio()
0534     {
0535         const Trio temp = createTrio(7, 6, 5);
0536         QCOMPARE(temp(0, 0), 7);
0537         QCOMPARE(temp(1, 0), 6);
0538         QCOMPARE(temp(2, 0), 5);
0539     }
0540 
0541     void testCreateMatrix()
0542     {
0543         const auto temp = createMatrix<2, 2, int>(7, 6, 5, 4);
0544         QCOMPARE(temp(0, 0), 7);
0545         QCOMPARE(temp(0, 1), 6);
0546         QCOMPARE(temp(1, 0), 5);
0547         QCOMPARE(temp(1, 1), 4);
0548     }
0549 
0550     void testDecimalPlaces_data()
0551     {
0552         QTest::addColumn<int>("maxRange");
0553         QTest::addColumn<int>("significantFigures");
0554         QTest::addColumn<int>("expected");
0555 
0556         QTest::newRow("0 << -1 << 0") << 0 << -1 << 0;
0557         QTest::newRow("0 << 0 << 0") << 0 << 0 << 0;
0558         QTest::newRow("0 << 1 << 0") << 0 << 1 << 0;
0559         QTest::newRow("0 << 2 << 1") << 0 << 2 << 1;
0560         QTest::newRow("0 << 3 << 2") << 0 << 3 << 2;
0561         QTest::newRow("0 << 4 << 3") << 0 << 4 << 3;
0562 
0563         QTest::newRow("1 << -1 << 0") << 1 << -1 << 0;
0564         QTest::newRow("1 << 0 << 0") << 1 << 0 << 0;
0565         QTest::newRow("1 << 1 << 0") << 1 << 1 << 0;
0566         QTest::newRow("1 << 2 << 1") << 1 << 2 << 1;
0567         QTest::newRow("1 << 3 << 2") << 1 << 3 << 2;
0568         QTest::newRow("1 << 4 << 3") << 1 << 4 << 3;
0569 
0570         QTest::newRow("2 << -1 << 0") << 2 << -1 << 0;
0571         QTest::newRow("2 << 0 << 0") << 2 << 0 << 0;
0572         QTest::newRow("2 << 1 << 0") << 2 << 1 << 0;
0573         QTest::newRow("2 << 2 << 1") << 2 << 2 << 1;
0574         QTest::newRow("2 << 3 << 2") << 2 << 3 << 2;
0575         QTest::newRow("2 << 4 << 3") << 2 << 4 << 3;
0576 
0577         QTest::newRow("100 << -1 << 0") << 100 << -1 << 0;
0578         QTest::newRow("100 << 0 << 0") << 100 << 0 << 0;
0579         QTest::newRow("100 << 1 << 0") << 100 << 1 << 0;
0580         QTest::newRow("100 << 2 << 0") << 100 << 2 << 0;
0581         QTest::newRow("100 << 3 << 0") << 100 << 3 << 0;
0582         QTest::newRow("100 << 4 << 1") << 100 << 4 << 1;
0583 
0584         QTest::newRow("255 << -1 << 0") << 255 << -1 << 0;
0585         QTest::newRow("255 << 0 << 0") << 255 << 0 << 0;
0586         QTest::newRow("255 << 1 << 0") << 255 << 1 << 0;
0587         QTest::newRow("255 << 2 << 0") << 255 << 2 << 0;
0588         QTest::newRow("255 << 3 << 0") << 255 << 3 << 0;
0589         QTest::newRow("255 << 4 << 1") << 255 << 4 << 1;
0590 
0591         QTest::newRow("-255 << -1 << 0") << -255 << -1 << 0;
0592         QTest::newRow("-255 << 0 << 0") << -255 << 0 << 0;
0593         QTest::newRow("-255 << 1 << 0") << -255 << 1 << 0;
0594         QTest::newRow("-255 << 2 << 0") << -255 << 2 << 0;
0595         QTest::newRow("-255 << 3 << 0") << -255 << 3 << 0;
0596         QTest::newRow("-255 << 4 << 1") << -255 << 4 << 1;
0597 
0598         QTest::newRow("360 << -1 << 0") << 360 << -1 << 0;
0599         QTest::newRow("360 << 0 << 0") << 360 << 0 << 0;
0600         QTest::newRow("360 << 1 << 0") << 360 << 1 << 0;
0601         QTest::newRow("360 << 2 << 0") << 360 << 2 << 0;
0602         QTest::newRow("360 << 3 << 0") << 360 << 3 << 0;
0603         QTest::newRow("360 << 4 << 1") << 360 << 4 << 1;
0604     }
0605 
0606     void testDecimalPlaces()
0607     {
0608         QFETCH(int, maxRange);
0609         QFETCH(int, significantFigures);
0610         QFETCH(int, expected);
0611 
0612         QCOMPARE(decimalPlaces(maxRange, significantFigures), expected);
0613     }
0614 
0615     void testNormalizePolar360()
0616     {
0617         double radius;
0618         double angleDegree;
0619 
0620         // Value of 0, 0°
0621         radius = 0;
0622         angleDegree = 0;
0623         normalizePolar360(radius, angleDegree);
0624         normalizePolar360(radius, angleDegree);
0625         QCOMPARE(radius, 0);
0626         QCOMPARE(angleDegree, 0);
0627 
0628         // Same for initialization with -0, 0
0629         radius = -0;
0630         angleDegree = 0;
0631         normalizePolar360(radius, angleDegree);
0632         QCOMPARE(radius, 0);
0633         QCOMPARE(angleDegree, 0);
0634 
0635         // Yet normalized values are taken as-is
0636         radius = 2;
0637         angleDegree = 3;
0638         normalizePolar360(radius, angleDegree);
0639         QCOMPARE(radius, 2);
0640         QCOMPARE(angleDegree, 3);
0641 
0642         // Negative radii are normalized (180° shift for angle)
0643         radius = -2;
0644         angleDegree = 183;
0645         normalizePolar360(radius, angleDegree);
0646         QCOMPARE(radius, 2);
0647         QCOMPARE(angleDegree, 3);
0648 
0649         // Out-of-range angle is normalized
0650         radius = 2;
0651         angleDegree = 363;
0652         normalizePolar360(radius, angleDegree);
0653         QCOMPARE(radius, 2);
0654         QCOMPARE(angleDegree, 3);
0655 
0656         radius = 2;
0657         angleDegree = -357;
0658         normalizePolar360(radius, angleDegree);
0659         QCOMPARE(radius, 2);
0660         QCOMPARE(angleDegree, 3);
0661 
0662         // Also when both radius and angle are
0663         // out-of-range, normalization works
0664         radius = -2;
0665         angleDegree = -357;
0666         normalizePolar360(radius, angleDegree);
0667         QCOMPARE(radius, 2);
0668         QCOMPARE(angleDegree, 183);
0669         radius = -2;
0670         angleDegree = -717;
0671         normalizePolar360(radius, angleDegree);
0672         QCOMPARE(radius, 2);
0673         QCOMPARE(angleDegree, 183);
0674         radius = -2;
0675         angleDegree = 363;
0676         normalizePolar360(radius, angleDegree);
0677         QCOMPARE(radius, 2);
0678         QCOMPARE(angleDegree, 183);
0679         radius = -2;
0680         angleDegree = 723;
0681         normalizePolar360(radius, angleDegree);
0682         QCOMPARE(radius, 2);
0683         QCOMPARE(angleDegree, 183);
0684 
0685         // When radius is 0, angle (while meaningless) is
0686         // preserved (but normalized)
0687         radius = 0;
0688         angleDegree = 150;
0689         normalizePolar360(radius, angleDegree);
0690         QCOMPARE(radius, 0);
0691         QCOMPARE(angleDegree, 150);
0692         radius = 0;
0693         angleDegree = 370;
0694         normalizePolar360(radius, angleDegree);
0695         QCOMPARE(radius, 0);
0696         QCOMPARE(angleDegree, 10);
0697 
0698         // When radius is -0, angle (while meaningless) is
0699         // preserved (but normalized)
0700         radius = -0;
0701         angleDegree = 150;
0702         normalizePolar360(radius, angleDegree);
0703         QCOMPARE(radius, 0);
0704         QCOMPARE(angleDegree, 150);
0705         radius = -0;
0706         angleDegree = 370;
0707         normalizePolar360(radius, angleDegree);
0708         QCOMPARE(radius, 0);
0709         QCOMPARE(angleDegree, 10);
0710 
0711         // Edge case: 360°
0712         radius = -0;
0713         angleDegree = 360;
0714         normalizePolar360(radius, angleDegree);
0715         QCOMPARE(radius, 0);
0716         QCOMPARE(angleDegree, 0);
0717         radius = 0;
0718         angleDegree = 360;
0719         normalizePolar360(radius, angleDegree);
0720         QCOMPARE(radius, 0);
0721         QCOMPARE(angleDegree, 0);
0722         radius = 5;
0723         angleDegree = 360;
0724         normalizePolar360(radius, angleDegree);
0725         QCOMPARE(radius, 5);
0726         QCOMPARE(angleDegree, 0);
0727     }
0728 };
0729 
0730 } // namespace PerceptualColor
0731 
0732 QTEST_MAIN(PerceptualColor::TestHelperMath)
0733 // The following “include” is necessary because we do not use a header file:
0734 #include "testhelpermath.moc"