/src/s2geometry/src/s2/util/math/exactfloat/bignum.h
Line | Count | Source |
1 | | // Copyright 2025 Google LLC |
2 | | // Author: smcallis@google.com (Sean McAllister) |
3 | | // |
4 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
5 | | // you may not use this file except in compliance with the License. |
6 | | // You may obtain a copy of the License at |
7 | | // |
8 | | // http://www.apache.org/licenses/LICENSE-2.0 |
9 | | // |
10 | | // Unless required by applicable law or agreed to in writing, software |
11 | | // distributed under the License is distributed on an "AS IS" BASIS, |
12 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | | // See the License for the specific language governing permissions and |
14 | | // limitations under the License. |
15 | | |
16 | | #ifndef S2_UTIL_MATH_EXACTFLOAT_BIGNUM_H_ |
17 | | #define S2_UTIL_MATH_EXACTFLOAT_BIGNUM_H_ |
18 | | |
19 | | #include <cmath> |
20 | | #include <cstdint> |
21 | | #include <limits> |
22 | | #include <optional> |
23 | | #include <type_traits> |
24 | | |
25 | | #include "absl/algorithm/container.h" |
26 | | #include "absl/base/attributes.h" |
27 | | #include "absl/container/inlined_vector.h" |
28 | | #include "absl/log/absl_check.h" |
29 | | #include "absl/numeric/bits.h" |
30 | | #include "absl/numeric/int128.h" |
31 | | #include "absl/strings/ascii.h" |
32 | | #include "absl/strings/str_format.h" |
33 | | |
34 | | namespace exactfloat_internal { |
35 | | |
36 | | // A digit of a bignum. A contraction of "big digit" (rhymes with the latter). |
37 | | using Bigit = uint64_t; |
38 | | |
39 | | // A class to support arithmetic on large, arbitrary precision integers. |
40 | | // |
41 | | // Large integers are represented as an array of uint64_t values. |
42 | | class Bignum { |
43 | | public: |
44 | | // The most common use of ExactFloat involves evaluating a 3x3 determinant to |
45 | | // determine whether 3 points are oriented clockwise or counter-clockwise. |
46 | | // |
47 | | // The typical number of mantissa bits in the result is probably about 170, so |
48 | | // we allocate 4 bigits (256 bits) inline. |
49 | | using BigitVector = absl::InlinedVector<Bigit, 4>; |
50 | | |
51 | | static constexpr int kBigitBits = std::numeric_limits<Bigit>::digits; |
52 | | |
53 | 0 | Bignum() = default; |
54 | | |
55 | | // Constructs a bignum from an integral value (signed or unsigned). |
56 | | template <typename T, |
57 | | typename = std::enable_if_t<std::numeric_limits<T>::is_integer>> |
58 | | explicit Bignum(T value); |
59 | | |
60 | | //-------------------------------------- |
61 | | // String formatting and parsing. |
62 | | //-------------------------------------- |
63 | | |
64 | | // Constructs a bignum from an ASCII string containing decimal digits. |
65 | | // |
66 | | // The input string must only have an optional leading +/- and decimal digits. |
67 | | // Any other characters will yield std::nullopt. |
68 | | static std::optional<Bignum> FromString(absl::string_view s); |
69 | | |
70 | | // Formats the bignum as a decimal integer into an abseil sink. |
71 | | template <typename Sink> |
72 | | friend void AbslStringify(Sink& sink, const Bignum& b); |
73 | | |
74 | 0 | friend std::ostream& operator<<(std::ostream& os, const Bignum& b) { |
75 | 0 | return os << absl::StrFormat("%v", b); |
76 | 0 | } |
77 | | |
78 | | friend std::ostream& operator<<(std::ostream& os, |
79 | 0 | const std::optional<Bignum>& b) { |
80 | 0 | if (!b) { |
81 | 0 | return os << "[nullopt]"; |
82 | 0 | } |
83 | 0 | return os << *b; |
84 | 0 | } |
85 | | |
86 | | //-------------------------------------- |
87 | | // Casting and coercion. |
88 | | //-------------------------------------- |
89 | | |
90 | | // Returns true if bignum can be stored in T without truncation or overflow. |
91 | | template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>> |
92 | | bool FitsIn() const; |
93 | | |
94 | | // Cast to an integral type T unconditionally. Use FitsIn<T>() or |
95 | | // ConvertTo<T> to perform conversion with bounds checking. |
96 | | template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>> |
97 | | T Cast() const; |
98 | | |
99 | | // Casts the value to the given type if it fits, otherwise std::nullopt. |
100 | | template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>> |
101 | | std::optional<T> ConvertTo() const { |
102 | | if (!FitsIn<T>()) { |
103 | | return std::nullopt; |
104 | | } |
105 | | return Cast<T>(); |
106 | | } |
107 | | |
108 | | //-------------------------------------- |
109 | | // General accessors. |
110 | | //-------------------------------------- |
111 | | |
112 | | // Returns the number of bits required for the magnitude of the value. |
113 | | // |
114 | | // Named to match std::bit_width. |
115 | | friend int bit_width(const Bignum& a); |
116 | | |
117 | | // Returns the number of consecutive 0 bits in the value, starting from the |
118 | | // least significant bit. |
119 | | // |
120 | | // Named to match std::countr_zero. |
121 | | friend int countr_zero(const Bignum& a); |
122 | | |
123 | | // Returns true if the n-th bit of the number's magnitude is 1. |
124 | | bool is_bit_set(int nbit) const; |
125 | | |
126 | | // Clears this bignum and sets it to zero. |
127 | 0 | Bignum& set_zero() { |
128 | 0 | negative_ = false; |
129 | 0 | bigits_.clear(); |
130 | 0 | return *this; |
131 | 0 | } |
132 | | |
133 | | // Sets the negative flag on the value. If the value is zero, has no effect. |
134 | 0 | Bignum& set_negative(bool negative = true) { |
135 | 0 | negative_ = !bigits_.empty() && negative; |
136 | 0 | return *this; |
137 | 0 | } |
138 | | |
139 | | // Returns true if the number is zero. |
140 | 0 | bool is_zero() const { return bigits_.empty(); } |
141 | | |
142 | | // Returns true if the number is less than zero. |
143 | 0 | bool is_negative() const { return negative_; } |
144 | | |
145 | | // Returns true if the number is odd (least significant bit is 1). |
146 | 0 | bool is_odd() const { return is_bit_set(0); } |
147 | | |
148 | | // Returns true if the number is even (least significant bit is 0). |
149 | 0 | bool is_even() const { return !is_odd(); } |
150 | | |
151 | | //-------------------------------------- |
152 | | // Comparisons. |
153 | | //-------------------------------------- |
154 | | |
155 | 0 | bool operator==(const Bignum& b) const { |
156 | 0 | return negative_ == b.negative_ && bigits_ == b.bigits_; |
157 | 0 | } |
158 | | |
159 | 0 | bool operator!=(const Bignum& b) const { return !(*this == b); } |
160 | 0 | bool operator<(const Bignum& b) const { return Compare(b) < 0; } |
161 | 0 | bool operator<=(const Bignum& b) const { return Compare(b) <= 0; } |
162 | 0 | bool operator>(const Bignum& b) const { return Compare(b) > 0; } |
163 | 0 | bool operator>=(const Bignum& b) const { return Compare(b) >= 0; } |
164 | | |
165 | | //-------------------------------------- |
166 | | // Arithmetic operators. |
167 | | //-------------------------------------- |
168 | | |
169 | 0 | Bignum operator+() const { return *this; } |
170 | | Bignum operator-() const; |
171 | | |
172 | | // Raise this value to the given power, which must be non-negative. |
173 | | Bignum Pow(int32_t pow) const; |
174 | | |
175 | | Bignum& operator+=(const Bignum& b); |
176 | | Bignum& operator-=(const Bignum& b); |
177 | | Bignum& operator*=(const Bignum& b); |
178 | | Bignum& operator<<=(int nbit); |
179 | | Bignum& operator>>=(int nbit); |
180 | | |
181 | 0 | friend Bignum operator+(Bignum a, const Bignum& b) { return a += b; } |
182 | 0 | friend Bignum operator-(Bignum a, const Bignum& b) { return a -= b; } |
183 | 0 | friend Bignum operator*(Bignum a, const Bignum& b) { return a *= b; } |
184 | 0 | friend Bignum operator<<(Bignum a, int nbit) { return a <<= nbit; } |
185 | 0 | friend Bignum operator>>(Bignum a, int nbit) { return a >>= nbit; } |
186 | | |
187 | | // Negates this bignum in place. |
188 | 0 | void negate() { |
189 | 0 | negative_ = !negative_; |
190 | 0 | if (bigits_.empty()) { |
191 | 0 | negative_ = false; |
192 | 0 | } |
193 | 0 | } |
194 | | |
195 | | // Compares to another bignum, returning -1, 0, +1. |
196 | | int Compare(const Bignum& b) const; |
197 | | |
198 | | private: |
199 | | // Constructs a Bignum from bigits and an optional sign bit. |
200 | | explicit Bignum(BigitVector bigits, bool negative = false) |
201 | 0 | : bigits_(std::move(bigits)), negative_(negative) { |
202 | 0 | Normalize(); |
203 | 0 | } |
204 | | |
205 | | // Drop leading zero bigits, and ensure sign is positive if result is zero. |
206 | 0 | void Normalize() { |
207 | 0 | while (!bigits_.empty() && bigits_.back() == 0) { |
208 | 0 | bigits_.pop_back(); |
209 | 0 | } |
210 | |
|
211 | 0 | if (bigits_.empty()) { |
212 | 0 | negative_ = false; |
213 | 0 | } |
214 | 0 | } |
215 | | |
216 | | // We store bignums in sign-magnitude form. bigits_ contains the individual |
217 | | // 64-bit digits of the bignum, stored in little-endian order. If bigits_ is |
218 | | // non-empty, then the last element must be non-zero and when it is empty |
219 | | // (representing a zero value), negative_ must be false. |
220 | | BigitVector bigits_; |
221 | | bool negative_ = false; |
222 | | }; |
223 | | |
224 | | //////////////////////////////////////////////////////////////////////////////// |
225 | | // Implementation Details |
226 | | //////////////////////////////////////////////////////////////////////////////// |
227 | | |
228 | | // Constructs a bignum from an integral value (signed or unsigned). |
229 | | template <typename T, typename> |
230 | 0 | Bignum::Bignum(T value) { |
231 | 0 | using UT = std::make_unsigned_t<T>; |
232 | |
|
233 | 0 | if (value == 0) { |
234 | 0 | return; |
235 | 0 | } |
236 | | |
237 | 0 | negative_ = false; |
238 | 0 | if constexpr (std::is_signed_v<T>) { |
239 | | // Put into constexpr if to avoid warnings when T is unsigned. |
240 | 0 | negative_ = (value < 0); |
241 | 0 | } |
242 | | |
243 | | // Get magnitude of value, handle minimum value of T cleanly. |
244 | 0 | UT mag = static_cast<UT>(value); |
245 | 0 | if constexpr (std::is_signed_v<T>) { |
246 | 0 | if (value < 0) { |
247 | 0 | mag = UT(0) - mag; |
248 | 0 | } |
249 | 0 | } |
250 | | |
251 | | // Pack the magnitude into bigits. |
252 | 0 | if constexpr (std::numeric_limits<UT>::digits <= kBigitBits) { |
253 | 0 | bigits_.push_back(static_cast<Bigit>(mag)); |
254 | | } else { |
255 | | while (mag) { |
256 | | bigits_.push_back(static_cast<Bigit>(mag)); |
257 | | mag >>= kBigitBits; |
258 | | } |
259 | | } |
260 | 0 | } Unexecuted instantiation: exactfloat_internal::Bignum::Bignum<unsigned long, void>(unsigned long) Unexecuted instantiation: exactfloat_internal::Bignum::Bignum<unsigned int, void>(unsigned int) Unexecuted instantiation: exactfloat_internal::Bignum::Bignum<int, void>(int) |
261 | | |
262 | | // Formats the bignum as a decimal integer into an abseil sink. |
263 | | template <typename Sink> |
264 | 0 | void AbslStringify(Sink& sink, const Bignum& b) { |
265 | 0 | if (b.is_zero()) { |
266 | 0 | sink.Append("0"); |
267 | 0 | return; |
268 | 0 | } |
269 | | |
270 | | // Sign |
271 | 0 | if (b.is_negative()) { |
272 | 0 | sink.Append("-"); |
273 | 0 | } |
274 | | |
275 | | // Work on a copy of the magnitude. |
276 | 0 | Bignum copy = b; |
277 | 0 | copy.negative_ = false; |
278 | | |
279 | | // 10**19 is the largest power of 10 that fits in 64-bits. So we can |
280 | | // repeatedly divide and modulo the bignum to get uint64_t values we can |
281 | | // format as 19 decimal digits. |
282 | 0 | static_assert(sizeof(Bigit) * 8 == 64); |
283 | 0 | static constexpr uint64_t kChunkDivisor = 10'000'000'000'000'000'000u; |
284 | 0 | Bignum::BigitVector chunks; |
285 | |
|
286 | 0 | while (!copy.is_zero()) { |
287 | 0 | absl::uint128 rem = 0; |
288 | 0 | for (int i = static_cast<int>(copy.bigits_.size()) - 1; i >= 0; --i) { |
289 | 0 | absl::uint128 acc = (rem << 64) + copy.bigits_[i]; |
290 | 0 | Bigit quot = static_cast<Bigit>(acc / kChunkDivisor); |
291 | 0 | rem = acc - absl::uint128(quot) * kChunkDivisor; |
292 | 0 | copy.bigits_[i] = quot; |
293 | 0 | } |
294 | |
|
295 | 0 | copy.Normalize(); |
296 | 0 | chunks.push_back(static_cast<Bigit>(rem)); |
297 | 0 | } |
298 | 0 | ABSL_DCHECK(!chunks.empty()); |
299 | | |
300 | | // Emit most significant chunk without zero padding. |
301 | 0 | absl::Format(&sink, "%d", chunks.back()); |
302 | | |
303 | | // Emit remaining chunks as fixed-width 19-digit zero-padded blocks. |
304 | 0 | for (int i = static_cast<int>(chunks.size()) - 2; i >= 0; --i) { |
305 | 0 | absl::Format(&sink, "%019d", chunks[i]); |
306 | 0 | } |
307 | 0 | } |
308 | | |
309 | | template <typename T, typename> |
310 | | inline bool Bignum::FitsIn() const { |
311 | | using UT = std::make_unsigned_t<T>; |
312 | | |
313 | | if (is_zero()) { |
314 | | return true; |
315 | | } |
316 | | |
317 | | // Maximum number of bits that could fit in the output type. |
318 | | constexpr int kTBitWidth = std::numeric_limits<UT>::digits; |
319 | | constexpr int kMaxBigits = (kTBitWidth + (kBigitBits - 1)) / kBigitBits; |
320 | | |
321 | | // Fast reject if the bignum couldn't conceivably fit. |
322 | | if (bigits_.size() > kMaxBigits) { |
323 | | return false; |
324 | | } |
325 | | |
326 | | // Unsigned type T can hold the value iff the value is non-negative and |
327 | | // the bitwidth is <= the maximum bit width of the type. |
328 | | if constexpr (!std::is_signed_v<T>) { |
329 | | if (is_negative()) { |
330 | | return false; |
331 | | } |
332 | | return bit_width(*this) <= kTBitWidth; |
333 | | } |
334 | | |
335 | | // T is signed and our bignum isn't zero. |
336 | | ABSL_DCHECK(std::is_signed_v<T> && !is_zero()); |
337 | | |
338 | | if (is_negative()) { |
339 | | // Magnitude must fit in negative value. If the value is negative and |
340 | | // the same bit width as the output type, the only valid value is |
341 | | // -2^(k-1). |
342 | | if (bit_width(*this) == kTBitWidth) { |
343 | | return countr_zero(*this) == kTBitWidth - 1; |
344 | | } |
345 | | return bit_width(*this) < kTBitWidth; |
346 | | } else /* positive */ { |
347 | | return bit_width(*this) <= (kTBitWidth - 1); |
348 | | } |
349 | | } |
350 | | |
351 | | template <typename T, typename> |
352 | 0 | T Bignum::Cast() const { |
353 | 0 | using UT = std::make_unsigned_t<T>; |
354 | |
|
355 | 0 | constexpr int kTBitWidth = std::numeric_limits<UT>::digits; |
356 | |
|
357 | 0 | if (bigits_.empty()) { |
358 | 0 | return 0; |
359 | 0 | } |
360 | | |
361 | | // T fits in a Bigit, so just cast to truncate. |
362 | 0 | UT residue = 0; |
363 | 0 | if (kTBitWidth <= kBigitBits) { |
364 | 0 | residue = static_cast<UT>(bigits_[0]); |
365 | 0 | } else { |
366 | 0 | ABSL_DCHECK_EQ(kTBitWidth % kBigitBits, 0); |
367 | 0 | for (int i = 0; i < kTBitWidth / kBigitBits; ++i) { |
368 | 0 | UT chunk = static_cast<UT>(bigits_[i]); |
369 | 0 | residue |= chunk << (i * kBigitBits); |
370 | 0 | } |
371 | 0 | } |
372 | | |
373 | | // Compute two's complement of the residue if value is negative. |
374 | 0 | if (is_negative()) { |
375 | 0 | residue = UT(0) - residue; |
376 | 0 | } |
377 | |
|
378 | 0 | return static_cast<T>(residue); |
379 | 0 | } Unexecuted instantiation: unsigned long exactfloat_internal::Bignum::Cast<unsigned long, void>() const Unexecuted instantiation: long exactfloat_internal::Bignum::Cast<long, void>() const |
380 | | |
381 | | } // namespace exactfloat_internal |
382 | | |
383 | | #endif // S2_UTIL_MATH_EXACTFLOAT_BIGNUM_H_ |