Coverage Report

Created: 2026-11-10 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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_