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
21QT_BEGIN_NAMESPACE
22
23namespace QRoundingDown {
24// Note: qgregoriancalendar.cpp contains some static asserts to verify this all works.
25namespace 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.
35template <typename Int>
36inline 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:
61template <unsigned b, typename Int, std::enable_if_t<(int(b) < 2), bool> = true>
62constexpr 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
72template <unsigned b, typename Int,
73 std::enable_if_t<(b > 1) && !QCALMATH_ISPOW2(b) && (int(b) > 0),
74 bool> = true>
75constexpr 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
87template <unsigned b, typename Int,
88 std::enable_if_t<(b > 1) && QCALMATH_ISPOW2(b) && (int(b) > 0),
89 bool> = true>
90constexpr 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
104template <unsigned b, typename Int> constexpr Int qDiv(Int a) { return qDivMod<b>(a).quotient; }
105template <unsigned b, typename Int> constexpr Int qMod(Int a) { return qDivMod<b>(a).remainder; }
106
107} // QRoundingDown
108
109namespace QRomanCalendrical {
110// Julian Day number of Gregorian 1 BCE, February 29th:
111constexpr qint64 LeapDayGregorian1Bce = 1721119;
112// Aside from (maybe) some turns of centuries, one year in four is leap:
113constexpr unsigned FourYears = 4 * 365 + 1;
114constexpr unsigned FiveMonths = 31 + 30 + 31 + 30 + 31; // Mar-Jul or Aug-Dec.
115
116constexpr 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
130constexpr 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
143QT_END_NAMESPACE
144
145#endif // QCALENDARMATH_P_H
146

source code of qtbase/src/corelib/time/qcalendarmath_p.h