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 | constexpr qint64 LeapDayGregorian1Bce = 1721119; |
112 | // Aside from (maybe) some turns of centuries, one year in four is leap: |
113 | constexpr unsigned FourYears = 4 * 365 + 1; |
114 | 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 | |