| 1 | // Copyright (C) 2022 The Qt Company Ltd. |
| 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
| 3 | |
| 4 | #ifndef QCALENDARMATH_P_H |
| 5 | #define QCALENDARMATH_P_H |
| 6 | |
| 7 | // |
| 8 | // W A R N I N G |
| 9 | // ------------- |
| 10 | // |
| 11 | // This file is not part of the Qt API. It exists for the convenience |
| 12 | // of q*calendar.cpp. This header file may change from version to version |
| 13 | // without notice, or even be removed. |
| 14 | // |
| 15 | // We mean it. |
| 16 | // |
| 17 | |
| 18 | #include <QtCore/private/qglobal_p.h> |
| 19 | #include <QtCore/QtAlgorithms> |
| 20 | |
| 21 | QT_BEGIN_NAMESPACE |
| 22 | |
| 23 | namespace QRoundingDown { |
| 24 | // Note: qgregoriancalendar.cpp contains some static asserts to verify this all works. |
| 25 | namespace QRoundingDownPrivate { |
| 26 | #ifdef Q_CC_MSVC |
| 27 | // MSVC 2019 doesn't believe in the constexpr-ness of the #else clause's version :-( |
| 28 | #define QCALMATH_ISPOW2(b) ((b > 0) && !(b & (b - 1))) // See #else's comment. |
| 29 | #else |
| 30 | // Subtracting one toggles the least significant set bit and any unset bits less |
| 31 | // significant than it, leaving other bits unchanged. Thus the & of this with |
| 32 | // the original number preserves all more significant bits, clearing the least |
| 33 | // significant. If there are no such bits, either our number was 0 or it only |
| 34 | // had one bit set, hence is a power of two. |
| 35 | template <typename Int> |
| 36 | inline constexpr bool isPowerOfTwo(Int b) { return b > 0 && (b & (b - 1)) == 0; } |
| 37 | #define QCALMATH_ISPOW2(b) QRoundingDownPrivate::isPowerOfTwo(b) |
| 38 | #endif |
| 39 | } |
| 40 | /* |
| 41 | Division, rounding down (rather than towards zero). |
| 42 | |
| 43 | From C++11 onwards, integer division is defined to round towards zero, so we |
| 44 | can rely on that when implementing this. This is only used with denominator b |
| 45 | > 0, so we only have to treat negative numerator, a, specially. |
| 46 | |
| 47 | If a is a multiple of b, adding 1 before and subtracting it after dividing by |
| 48 | b gets us to where we should be (albeit by an eccentric path), since the |
| 49 | adding caused rounding up, undone by the subtracting. Otherwise, adding 1 |
| 50 | doesn't change the result of dividing by b; and we want one less than that |
| 51 | result. This is equivalent to subtracting b - 1 and simply dividing, except |
| 52 | when that subtraction would underflow. |
| 53 | |
| 54 | For the remainder, with negative a, aside from having to add one and subtract |
| 55 | it later to deal with the exact multiples, we can simply use the truncating |
| 56 | remainder and then add b. When b is a power of two we can, of course, get the |
| 57 | remainder correctly by the same masking that works for positive a. |
| 58 | */ |
| 59 | |
| 60 | // Fall-back, to ensure intelligible error messages on mis-use: |
| 61 | template <unsigned b, typename Int, std::enable_if_t<(int(b) < 2), bool> = true> |
| 62 | constexpr auto qDivMod(Int) |
| 63 | { |
| 64 | static_assert(b, "Division by 0 is undefined" ); |
| 65 | // Use complement of earlier cases || new check, to ensure only one error: |
| 66 | static_assert(!b || int(b) > 0, "Denominator is too big" ); |
| 67 | static_assert(int(b) < 1 || b > 1, "Division by 1 is fautous" ); |
| 68 | struct R { Int quotient; Int remainder; }; |
| 69 | return R { 0, 0 }; |
| 70 | } |
| 71 | |
| 72 | template <unsigned b, typename Int, |
| 73 | std::enable_if_t<(b > 1) && !QCALMATH_ISPOW2(b) && (int(b) > 0), |
| 74 | bool> = true> |
| 75 | constexpr auto qDivMod(Int a) |
| 76 | { |
| 77 | struct R { Int quotient; Int remainder; }; |
| 78 | if constexpr (std::is_signed_v<Int>) { |
| 79 | if (a < 0) { |
| 80 | ++a; // can't overflow, it's negative |
| 81 | return R { Int(a / int(b) - 1), Int(a % int(b) - 1 + int(b)) }; |
| 82 | } |
| 83 | } |
| 84 | return R { Int(a / int(b)), Int(a % int(b)) }; |
| 85 | } |
| 86 | |
| 87 | template <unsigned b, typename Int, |
| 88 | std::enable_if_t<(b > 1) && QCALMATH_ISPOW2(b) && (int(b) > 0), |
| 89 | bool> = true> |
| 90 | constexpr auto qDivMod(Int a) |
| 91 | { |
| 92 | constexpr unsigned w = QtPrivate::qConstexprCountTrailingZeroBits(v: b); |
| 93 | struct R { Int quotient; Int remainder; }; |
| 94 | if constexpr (std::is_signed_v<Int>) { |
| 95 | if (a < 0) |
| 96 | return R { Int((a + 1) / int(b) - 1), Int(a & int(b - 1)) }; |
| 97 | } |
| 98 | return R { Int(a >> w), Int(a & int(b - 1)) }; |
| 99 | } |
| 100 | |
| 101 | #undef QCALMATH_ISPOW2 |
| 102 | // </kludge> |
| 103 | |
| 104 | template <unsigned b, typename Int> constexpr Int qDiv(Int a) { return qDivMod<b>(a).quotient; } |
| 105 | template <unsigned b, typename Int> constexpr Int qMod(Int a) { return qDivMod<b>(a).remainder; } |
| 106 | |
| 107 | } // QRoundingDown |
| 108 | |
| 109 | namespace QRomanCalendrical { |
| 110 | // Julian Day number of Gregorian 1 BCE, February 29th: |
| 111 | inline constexpr qint64 LeapDayGregorian1Bce = 1721119; |
| 112 | // Aside from (maybe) some turns of centuries, one year in four is leap: |
| 113 | inline constexpr unsigned FourYears = 4 * 365 + 1; |
| 114 | inline constexpr unsigned FiveMonths = 31 + 30 + 31 + 30 + 31; // Mar-Jul or Aug-Dec. |
| 115 | |
| 116 | constexpr auto yearMonthToYearDays(int year, int month) |
| 117 | { |
| 118 | // Pre-digests year and month to (possibly denormal) year count and day-within-year. |
| 119 | struct R { qint64 year; qint64 days; }; |
| 120 | if (year < 0) // Represent -N BCE as 1-N so year numbering is contiguous. |
| 121 | ++year; |
| 122 | month -= 3; // Adjust month numbering so March = 0, ... |
| 123 | if (month < 0) { // and Jan = 10, Feb = 11, in the previous year. |
| 124 | --year; |
| 125 | month += 12; |
| 126 | } |
| 127 | return R { .year: year, .days: QRoundingDown::qDiv<5>(a: FiveMonths * month + 2) }; |
| 128 | } |
| 129 | |
| 130 | constexpr auto dayInYearToYmd(int dayInYear) |
| 131 | { |
| 132 | // The year is an adjustment to the year for which dayInYear may be denormal. |
| 133 | struct R { int year; int month; int day; }; |
| 134 | // Shared code for Julian and Milankovic (at least). |
| 135 | using namespace QRoundingDown; |
| 136 | const auto month5Day = qDivMod<FiveMonths>(a: 5 * dayInYear + 2); |
| 137 | // Its remainder changes by 5 per day, except at roughly monthly quotient steps. |
| 138 | const auto yearMonth = qDivMod<12>(a: month5Day.quotient + 2); |
| 139 | return R { .year: yearMonth.quotient, .month: yearMonth.remainder + 1, .day: qDiv<5>(a: month5Day.remainder) + 1 }; |
| 140 | } |
| 141 | } |
| 142 | |
| 143 | QT_END_NAMESPACE |
| 144 | |
| 145 | #endif // QCALENDARMATH_P_H |
| 146 | |