• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

stillwater-sc / universal / 26002766134

17 May 2026 09:00PM UTC coverage: 84.106% (+0.01%) from 84.094%
26002766134

Pull #861

github

web-flow
Merge b4625f823 into 088ccd080
Pull Request #861: fix(einteger): correct Knuth Algorithm D step D4 borrow propagation

11 of 12 new or added lines in 1 file covered. (91.67%)

3 existing lines in 1 file now uncovered.

46699 of 55524 relevant lines covered (84.11%)

6349479.21 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.1
/include/sw/universal/number/einteger/einteger_impl.hpp
1
#pragma once
2
// einteger_impl.hpp: implementation of an adaptive precision binary integer
3
//
4
// Copyright (C) 2017-2023 Stillwater Supercomputing, Inc.
5
//
6
// This file is part of the universal numbers project, which is released under an MIT Open Source license.
7
#include <string>
8
#include <sstream>
9
#include <iostream>
10
#include <iomanip>
11
#include <regex>
12
#include <map>
13
#include <vector>
14
#include <limits>
15
#include <type_traits>
16

17
#include <universal/number/einteger/exceptions.hpp>
18
#include <universal/number/einteger/einteger_fwd.hpp>
19

20
// supporting types and functions
21
#include <universal/native/ieee754.hpp>
22

23
namespace sw { namespace universal {
24

25
// einteger is an adaptive precision integer type
26
template<typename BlockType = std::uint32_t>
27
class einteger {
28
public:
29
        using bt = BlockType;
30
        static constexpr unsigned bitsInBlock = sizeof(BlockType) * 8;
31
        static constexpr bt       ALL_ONES = bt(0xFFFF'FFFF'FFFF'FFFFull); // block type specific all 1's value
32
        static constexpr uint64_t BASE = uint64_t(ALL_ONES) + 1ull;
33
        static_assert(bitsInBlock <= 32, "BlockType must be one of [uint8_t, uint16_t, uint32_t]");
34

35
        // Partial-constexpr surface (issue #748): einteger carries a
36
        // std::vector<BlockType> _block member, so any non-empty digit
37
        // storage escapes constant evaluation under C++20's transient-
38
        // allocation rules.  The default ctor already initializes _block
39
        // to an empty vector (no push_back), so it is constexpr-friendly
40
        // without is_constant_evaluated() dispatch -- iszero() recognizes
41
        // the empty state via the size() == 0 branch.  Sign-only and
42
        // state-only selectors / modifiers are constexpr-clean; free
43
        // comparison operators (type vs type) are pure reads of the digit
44
        // vector and thus also constexpr.  All digit-mutating paths
45
        // (setbits, setblock, parse, native-type ctors / operator=,
46
        // arithmetic operators, conversion-out) remain non-constexpr until
47
        // C++23 P2738 relaxes the transient-allocation rule.
48
        constexpr einteger() noexcept : _sign(false), _block{} { }
49

50
        constexpr einteger(const einteger&) = default;
8,129,613✔
51
        constexpr einteger(einteger&&) = default;
52

53
        constexpr einteger& operator=(const einteger&) = default;
523,419✔
54
        constexpr einteger& operator=(einteger&&) = default;
7,803,914✔
55

56
        // initializers for native types
57
        einteger(short initial_value)              { *this = initial_value; }
58
        einteger(int initial_value)                { *this = initial_value; }
46✔
59
        einteger(long initial_value)               { *this = initial_value; }
56✔
60
        einteger(long long initial_value)          { *this = initial_value; }
176✔
61
        einteger(unsigned int initial_value)       { *this = initial_value; }
620✔
62
        einteger(unsigned long initial_value)      { *this = initial_value; }
63
        einteger(unsigned long long initial_value) { *this = initial_value; }
64
        einteger(float initial_value)              { *this = initial_value; }
1✔
65
        einteger(double initial_value)             { *this = initial_value; }
66

67
        // assignment operators for native types
68
        einteger& operator=(int rhs)                noexcept { return convert_signed(rhs); }
2,094,357✔
69
        einteger& operator=(long rhs)               noexcept { return convert_signed(rhs); }
10,942,008✔
70
        einteger& operator=(long long rhs)          noexcept { return convert_signed(rhs); }
182✔
71
        einteger& operator=(unsigned int rhs)       noexcept { return convert_unsigned(rhs); }
2,093,766✔
72
        einteger& operator=(unsigned long rhs)      noexcept { return convert_unsigned(rhs); }
73
        einteger& operator=(unsigned long long rhs) noexcept { return convert_unsigned(rhs); }
74
        einteger& operator=(float rhs)              noexcept { return convert_ieee754(rhs); }
118✔
75
        einteger& operator=(double rhs)             noexcept { return convert_ieee754(rhs); }
76

77
        // conversion operators
78
        explicit operator int() const noexcept         { return convert_to_native_integer<int>(); }
79
        explicit operator long() const noexcept        { return convert_to_native_integer<long>(); }
10,957,088✔
80
        explicit operator long long() const noexcept   { return convert_to_native_integer<long long>(); }
53✔
81
        explicit operator float() const noexcept       { return convert_to_native_ieee<float>(); }
117✔
82
        explicit operator double() const noexcept      { return convert_to_native_ieee<double>(); }
83

84
#if LONG_DOUBLE_SUPPORT
85
        einteger(long double initial_value) { *this = initial_value; }
86
        einteger& operator=(long double rhs)        noexcept { return convert_ieee754(rhs); }
87
        explicit operator long double() const noexcept { return convert_to_native_ieee<long double>(); }
88
#endif
89

90
        // logic shift operators
91
        einteger& operator<<=(int shift) {
118✔
92
                if (shift == 0) return *this;
118✔
93
                if (shift < 0) return operator>>=(-shift);
118✔
94

95
                // by default extend the limbs by 1: TODO: can this be improved?
96
                _block.push_back(0);
100✔
97
                size_t MSU = _block.size() - 1;
100✔
98
                if (shift >= static_cast<int>(bitsInBlock)) {
100✔
99
                        int blockShift = shift / static_cast<int>(bitsInBlock);
81✔
100
                        if (blockShift > 0) _block.resize(_block.size() + blockShift, 0ul);
81✔
101
                        MSU = _block.size() - 1;
81✔
102
                        for (int i = static_cast<int>(MSU); i >= blockShift; --i) {
338✔
103
                                _block[static_cast<size_t>(i)] = _block[static_cast<size_t>(i) - static_cast<size_t>(blockShift)];
257✔
104
                        }
105
                        for (int i = blockShift - 1; i >= 0; --i) {
425✔
106
                                _block[static_cast<size_t>(i)] = BlockType(0);
344✔
107
                        }
108
                        // adjust the shift
109
                        shift -= static_cast<int>(blockShift * bitsInBlock);
81✔
110
                        if (shift == 0) return *this;
81✔
111
                }
112
                if (MSU > 0) {
93✔
113
                        // construct the mask for the upper bits in the block that needs to move to the higher word
114
                        BlockType mask = static_cast<BlockType>(0xFFFF'FFFFul << (bitsInBlock - shift));
93✔
115
                        for (size_t i = MSU; i > 0; --i) {
581✔
116
                                _block[static_cast<size_t>(i)] <<= shift;
488✔
117
                                // mix in the bits from the right
118
                                BlockType bits = BlockType(mask & _block[i - 1]);
488✔
119
                                _block[static_cast<size_t>(i)] |= (bits >> (bitsInBlock - shift));
488✔
120
                        }
121
                        _block[0] <<= shift;
93✔
122
                }
123
                else {
124
                        _block[0] <<= shift;
×
125
                }
126
                remove_leading_zeros();
93✔
127
                return *this;
93✔
128
        }
129
        einteger& operator>>=(int shift) {
18✔
130
                if (shift == 0) return *this;
18✔
131
                if (shift < 0) return operator>>=(-shift);
18✔
132
                if (shift > static_cast<int>(nbits())) {
18✔
133
                        setzero();
×
134
                        return *this;
×
135
                }
136
                size_t MSU = _block.size() - 1;
18✔
137
                size_t blockShift = 0;
18✔
138
                if (shift >= static_cast<int>(bitsInBlock)) {
18✔
139
                        blockShift = shift / bitsInBlock;
6✔
140
                        if (MSU >= blockShift) {
6✔
141
                                // shift by blocks
142
                                for (size_t i = 0; i <= MSU - blockShift; ++i) {
14✔
143
                                        _block[i] = _block[i + blockShift];
8✔
144
                                        _block[i + blockShift] = 0; // null the upper block
8✔
145
                                }
146
                        }
147
                        // adjust the shift
148
                        shift -= static_cast<int>(blockShift * bitsInBlock);
6✔
149
                        if (shift == 0) {
6✔
150
                                remove_leading_zeros();
2✔
151
                                return *this;
2✔
152
                        }
153
                }
154
                if (MSU > 0) {
16✔
155
                        BlockType mask = static_cast<BlockType>(0xFFFF'FFFFul);
10✔
156
                        mask >>= (bitsInBlock - shift); // this is a mask for the lower bits in the block that need to move to the lower word
10✔
157
                        for (size_t i = 0; i < MSU; ++i) {
25✔
158
                                _block[i] >>= shift;
15✔
159
                                // mix in the bits from the left
160
                                BlockType bits = BlockType(mask & _block[i + 1]);
15✔
161
                                _block[i] |= (bits << (bitsInBlock - shift));
15✔
162
                        }
163
                        _block[MSU] >>= shift;
10✔
164
                }
165
                else {
166
                        _block[0] >>= shift;
6✔
167
                }
168
                remove_leading_zeros();
16✔
169
                return *this;
16✔
170
        }
171
        
172
        // unary arithmetic operators
173
        einteger operator-() const {
4,608✔
174
                einteger negated(*this);
4,608✔
175
                negated.setsign(!_sign);
4,608✔
176
                return negated;
4,608✔
177
        }
178
        einteger operator++(int) {
179
                einteger tmp(*this);
180
                operator++();
181
                return tmp;
182
        }
183
        einteger& operator++() {
184
                *this += 1;
185
                return *this;
186
        }
187
        einteger operator--(int) {
188
                einteger tmp(*this);
189
                operator--();
190
                return tmp;
191
        }
192
        einteger& operator--() {
193
                *this -= 1;
194
                return *this;
195
        }
196

197
        // binary arithmetic operators
198
        einteger& operator+=(const einteger& rhs) {
3,146,070✔
199
                if (sign() != rhs.sign()) {
3,146,070✔
200
                        if (sign()) {
2✔
201
                                einteger negated(*this);
×
202
                                negated.setsign(false);
×
203
                                *this = rhs - negated;
×
204
                                return *this;
×
205
                        }
×
206
                        else {
207
                                einteger negated(rhs);
2✔
208
                                negated.setsign(false);
2✔
209
                                *this -= negated;
2✔
210
                                return *this;
2✔
211
                        }
2✔
212
                }
213
                auto lhsSize = _block.size();
3,146,068✔
214
                auto rhsSize = rhs._block.size();
3,146,068✔
215
                if (lhsSize < rhsSize) _block.resize(rhsSize, 0);
3,146,068✔
216

217
                std::uint64_t carry{ 0 };
3,146,068✔
218
                typename std::vector<BlockType>::iterator li = _block.begin();
3,146,068✔
219
                typename std::vector<BlockType>::const_iterator ri = rhs._block.begin();
3,146,068✔
220
                while (li != _block.end()) {
7,340,810✔
221
                        if (ri != rhs._block.end()) {
4,194,742✔
222
                                carry += static_cast<std::uint64_t>(*li) + static_cast<std::uint64_t>(*ri);
4,187,502✔
223
                                ++ri;
4,187,502✔
224
                        }
225
                        else {
226
                                carry += static_cast<std::uint64_t>(*li);
7,240✔
227
                        }
228
                        *li = static_cast<BlockType>(carry);
4,194,742✔
229
                        carry >>= bitsInBlock;
4,194,742✔
230
                        ++li; 
4,194,742✔
231
                }
232
                if (carry == 0x1ull) {
3,146,068✔
233
                        _block.push_back(static_cast<BlockType>(carry));
1,571,335✔
234
                }
235
                return *this;
3,146,068✔
236
        }
237
        einteger& operator+=(long long rhs) {
48✔
238
                return *this += einteger(rhs);
48✔
239
        }
240
        einteger& operator-=(const einteger& rhs) {
4,325,380✔
241
                if (rhs.sign()) {
4,325,380✔
242
                        einteger negated(rhs);
2✔
243
                        negated.setsign(false);
2✔
244
                        return *this += negated;
2✔
245
                }
2✔
246
                auto lhsSize = _block.size();
4,325,378✔
247
                if (lhsSize == 0) {
4,325,378✔
248
                        *this = -rhs;
4,608✔
249
                        return *this;
4,608✔
250
                }
251
                auto rhsSize = rhs._block.size();
4,320,770✔
252
                int magnitude = compare_magnitude(*this, rhs); // if -1 result is going to be negative
4,320,770✔
253

254
                auto overlap = (lhsSize < rhsSize ? lhsSize : rhsSize);
4,320,770✔
255
                auto extent = (lhsSize < rhsSize ? rhsSize : lhsSize);
4,320,770✔
256

257
                // prep storage
258
                if (lhsSize < rhsSize) _block.resize(rhsSize, 0);
4,320,770✔
259

260
                typename std::vector<BlockType>::const_iterator aIter, bIter;
4,320,770✔
261
                if (magnitude == 1) {
4,320,770✔
262
                        aIter = _block.begin();
2,160,386✔
263
                        bIter = rhs._block.begin();
2,160,386✔
264
                }
265
                else {
266
                        aIter = rhs._block.begin();
2,160,384✔
267
                        bIter = _block.begin();
2,160,384✔
268
                }
269
                std::uint64_t borrow{ 0 };
4,320,770✔
270
                unsigned i{ 0 };
4,320,770✔
271
                while (i < overlap) {
9,226,762✔
272
                        borrow = static_cast<std::uint64_t>(*aIter) - static_cast<std::uint64_t>(*bIter) - borrow;
4,905,992✔
273
                        _block[i] = static_cast<BlockType>(borrow);
4,905,992✔
274
                        borrow = (borrow >> bitsInBlock) & 0x1u;
4,905,992✔
275
                        ++i; ++aIter; ++bIter;
4,905,992✔
276
                }
277
                while ((i < extent)) {
4,717,821✔
278
                        borrow = static_cast<BlockType>(*aIter) - borrow;
397,051✔
279
                        _block[i] = static_cast<BlockType>(borrow);
397,051✔
280
                        borrow = (borrow >> bitsInBlock) & 0x1u;
397,051✔
281
                        ++i; ++aIter;
397,051✔
282
                }
283
                remove_leading_zeros();
4,320,770✔
284
                setsign(magnitude == -1);
4,320,770✔
285
                return *this;
4,320,770✔
286
        }
287
        einteger& operator-=(long long rhs) {
288
                return *this -= einteger(rhs);
289
        }
290
        einteger& operator*=(const einteger& rhs) {
328,098✔
291
                if (iszero() || rhs.iszero()) {
328,098✔
292
                        clear();
2,595✔
293
                        return *this;
2,595✔
294
                }
295
                einteger base(*this);
325,503✔
296
                bool ls = sign();
325,503✔
297
                unsigned ll = limbs();
325,503✔
298
                bool rs = rhs.sign();
325,503✔
299
                unsigned rl = rhs.limbs();
325,503✔
300

301
                clear();
325,503✔
302
                std::uint64_t segment(0);
325,503✔
303
                for (unsigned i = 0; i < ll; ++i) {
651,198✔
304
                        for (unsigned j = 0; j < rl; ++j) {
651,390✔
305
                                segment += static_cast<std::uint64_t>(base.block(i)) * static_cast<std::uint64_t>(rhs.block(j));
325,695✔
306
                                segment += block(i + j);
325,695✔
307
                                setblock(i + j, static_cast<bt>(segment));
325,695✔
308
                                segment >>= bitsInBlock;
325,695✔
309
                        }
310
                }
311
                if (segment != 0) setblock(ll + rl - 1, static_cast<bt>(segment));
325,503✔
312
                setsign(ls ^ rs);
325,503✔
313
                return *this;
325,503✔
314
        }
325,503✔
315
        einteger& operator*=(long long rhs) {
126✔
316
                return *this *= einteger(rhs);
126✔
317
        }
318
        einteger& operator/=(const einteger& rhs) {
5✔
319
                einteger q, r;
5✔
320
                q.reduce(*this, rhs, r);
5✔
321
                *this = q;
5✔
322
                return *this;
5✔
323
        }
5✔
324
        einteger& operator/=(long long rhs) {
325
                return *this /= einteger(rhs);
326
        }
327
        einteger& operator%=(const einteger& rhs) {
2✔
328
                einteger q, a(*this), r;
2✔
329
                q.reduce(a, rhs, r);
2✔
330
                *this = r;
2✔
331
                return *this;
2✔
332
        }
2✔
333
        einteger& operator%=(long long rhs) {
334
                return *this %= einteger(rhs);
335
        }
336
        
337
        // reduce returns the ratio and remainder of a and b in *this and r
338
        void reduce(const einteger& a, const einteger& b, einteger& r) {
3,146,381✔
339
                if (b.iszero()) {
3,146,381✔
340
#if EINTEGER_THROW_ARITHMETIC_EXCEPTION
341
                        throw einteger_divide_by_zero{};
9,216✔
342
#else
343
                        std::cerr << "einteger_divide_by_zero\n";
×
344
                        return;
×
345
#endif // EINTEGER_THROW_ARITHMETIC_EXCEPTION
346
                }
347
                clear();
3,143,309✔
348
                r.clear();
3,143,309✔
349
                if (a.iszero()) return;
3,143,309✔
350

351
                size_t aBlocks = a.limbs();
3,140,240✔
352
                size_t bBlocks = b.limbs();
3,140,240✔
353
                if (aBlocks == 1 && aBlocks == bBlocks) { // completely reduce this to native div and rem
3,140,240✔
354
                        std::uint64_t a0 = a._block[0];
2,093,215✔
355
                        std::uint64_t b0 = b._block[0];
2,093,215✔
356
                        *this = static_cast<BlockType>(a0 / b0);
2,093,215✔
357
                        r = static_cast<BlockType>(a0 % b0);
2,093,215✔
358
                }
2,093,215✔
359
                        else {
360
                                // filter out the easy stuff
361
                                if (a < b) { r = a; clear(); return; }
1,051,558✔
362
                                if (a == b) { *this = 1; r.clear(); return; }
524,233✔
363

364
                        // determine first non-zero limbs
365
                        unsigned m{ 0 }, n{ 0 };
523,206✔
366
                        for (size_t i = aBlocks; i > 0; --i) {
523,211✔
367
                                if (a._block[i - 1] != 0) {
523,211✔
368
                                        m = static_cast<unsigned>(i);
523,206✔
369
                                        break;
523,206✔
370
                                }
371
                        }
372
                        for (size_t i = bBlocks; i > 0; --i) {
523,206✔
373
                                if (b._block[i - 1] != 0) {
523,206✔
374
                                        n = static_cast<unsigned>(i);
523,206✔
375
                                        break;
523,206✔
376
                                }
377
                        }
378

379
                        // single limb divisor
380
                        if (n == 1) {
523,206✔
381
                                _block.resize(m);
3,506✔
382
                                std::uint64_t remainder{ 0 };
3,506✔
383
                                auto divisor = b.block(0);
3,506✔
384
                                for (unsigned j = m; j > 0; --j) {
12,350✔
385
                                        std::uint64_t dividend = remainder * BASE + static_cast<std::uint64_t>(a.block(j - 1));
8,844✔
386
                                        std::uint64_t limbQuotient = dividend / divisor;
8,844✔
387
                                        _block[j - 1] = static_cast<BlockType>(limbQuotient);
8,844✔
388
                                        remainder = dividend - limbQuotient * divisor;
8,844✔
389
                                }
390
                                remove_leading_zeros();
3,506✔
391
                                r.setblock(0, static_cast<BlockType>(remainder));
3,506✔
392
                                return;
3,506✔
393
                        }
394

395
                        // Knuth's algorithm calculates a normalization factor d
396
                        // that perfectly aligns b so that b0 >= floor(BASE/2),
397
                        // a requirement for the relationship: (qHat - 2) <= q <= qHat
398

399
                        int shift = nlz(b.block(n - 1));
519,700✔
400
                        einteger normalized_a;
519,700✔
401
                        normalized_a.setblock(m, static_cast<BlockType>((a.block(m - 1) >> (bitsInBlock - shift))));
519,700✔
402
                        for (unsigned i = m - 1; i > 0; --i) {
1,039,453✔
403
                                normalized_a.setblock(i, static_cast<BlockType>((a.block(i) << shift) | (a.block(i - 1) >> (bitsInBlock - shift))));
519,753✔
404
                        }
405
                        normalized_a.setblock(0, static_cast<BlockType>(a.block(0) << shift));
519,700✔
406
                        // normalize b
407
                        einteger normalized_b;
519,700✔
408
                        unsigned n_minus_1 = n - 1;
519,700✔
409
                        for (unsigned i = n_minus_1; i > 0; --i) {
1,039,421✔
410
                                normalized_b.setblock(i, static_cast<BlockType>((b.block(i) << shift) | (b.block(i - 1) >> (bitsInBlock - shift))));
519,721✔
411
                        }
412
                        normalized_b.setblock(0, static_cast<BlockType>(b.block(0) << shift));
519,700✔
413

414
                        //std::cout << "normalized a : " << normalized_a.showLimbs() << " : " << normalized_a.showLimbValues() << '\n';
415
                        //std::cout << "normalized b :             " << normalized_b.showLimbs() << " : " << normalized_b.showLimbValues() << '\n';
416

417
                        // divide by limb
418
                        std::uint64_t divisor = normalized_b._block[n - 1];
519,700✔
419
                        std::uint64_t v_nminus2 = normalized_b._block[n - 2]; // n > 1 at this point
519,700✔
420
                        for (int j = static_cast<int>(m - n); j >= 0; --j) {
1,039,432✔
421
                                std::uint64_t dividend = normalized_a.block(j + n) * BASE + normalized_a.block(j + n - 1);
519,732✔
422
                                std::uint64_t qhat = dividend / divisor;
519,732✔
423
                                std::uint64_t rhat = dividend - qhat * divisor;
519,732✔
424

425
                                while (qhat >= BASE || qhat * v_nminus2 > BASE * rhat + normalized_a.block(j + n - 2)) {
520,126✔
426
                                        --qhat;
394✔
427
                                        rhat += divisor;
394✔
428
                                        if (rhat < BASE) continue;
394✔
429
                                }
430
                                // Knuth Algorithm D, step D4 (multi-precision subtraction
431
                                // with borrow propagation). `diff` MUST be signed so that
432
                                // the arithmetic right shift `diff >> bitsInBlock` yields
433
                                // the -1/0 underflow indicator. With uint64_t the shift
434
                                // instead returns all-ones, and `p_hi - (diff >> shift)`
435
                                // wraps to a huge value that corrupts the next iteration
436
                                // and ultimately produces a quotient that is too large
437
                                // by exactly one limb (issue #842). The low-bits mask
438
                                // must also track bitsInBlock so the algorithm works for
439
                                // uint8_t and uint16_t blocks in addition to uint32_t.
440
                                constexpr std::uint64_t LOW_MASK = BASE - 1u;
519,732✔
441
                                std::int64_t borrow{ 0 };
519,732✔
442
                                std::int64_t diff{ 0 };
519,732✔
443
                                for (unsigned i = 0; i < n; ++i) {
1,559,285✔
444
                                        std::uint64_t p     = qhat * normalized_b.block(i);
1,039,553✔
445
                                        std::uint64_t p_lo  = p & LOW_MASK;
1,039,553✔
446
                                        std::uint64_t p_hi  = p >> bitsInBlock;
1,039,553✔
447
                                        diff = static_cast<std::int64_t>(normalized_a.block(i + j))
1,039,553✔
448
                                             - static_cast<std::int64_t>(p_lo)
1,039,553✔
449
                                             - borrow;
450
                                        normalized_a.setblock(i + j, static_cast<BlockType>(diff));
1,039,553✔
451
                                        // borrow_out = p_hi + (1 if underflow else 0). The
452
                                        // signed shift `diff >> bitsInBlock` yields -1 on
453
                                        // underflow and 0 otherwise, so subtracting it adds
454
                                        // 1 in the underflow case.
455
                                        borrow = static_cast<std::int64_t>(p_hi) - (diff >> bitsInBlock);
1,039,553✔
456
                                }
457
                                std::int64_t signedBorrow = static_cast<std::int64_t>(normalized_a.block(j + n)) - borrow;
519,732✔
458
                                normalized_a.setblock(j + n, static_cast<BlockType>(signedBorrow));
519,732✔
459

460
                                //std::cout << "   updated a : " << normalized_a.showLimbs() << " : " << normalized_a.showLimbValues() << '\n';
461

462
                                setblock(static_cast<unsigned>(j), static_cast<BlockType>(qhat));
519,732✔
463
                                if (signedBorrow < 0) { // subtracted too much, add back
519,732✔
464
                                        setblock(static_cast<size_t>(j), static_cast<BlockType>(_block[static_cast<size_t>(j)] - 1));
×
465
                                        std::uint64_t carry{ 0 };
×
466
                                        for (unsigned i = 0; i < n; ++i) {
×
467
                                                carry += static_cast<std::uint64_t>(normalized_a.block(i + j)) + static_cast<std::uint64_t>(normalized_b.block(i));
×
468
                                                normalized_a.setblock(i + j, static_cast<BlockType>(carry));
×
NEW
469
                                                carry >>= bitsInBlock;
×
470
                                        }
471
                                        BlockType rectified = static_cast<BlockType>(normalized_a.block(j + n) + carry);
×
472
                                        normalized_a.setblock(j + n, rectified);
×
473
                                }
474
                                //std::cout << "   updated a : " << normalized_a.showLimbs() << " : " << normalized_a.showLimbValues() << '\n';
475
                        }
476

477
                        // remainder needs to be normalized
478
                        for (unsigned i = 0; i < n - 1; ++i) {
1,039,421✔
479
                                std::uint64_t remainder = static_cast<std::uint64_t>(normalized_a.block(i) >> shift);
519,721✔
480
                                remainder |= (static_cast<std::uint64_t>(normalized_a.block(i + 1)) << (bitsInBlock - shift));
519,721✔
481
                                r.setblock(i, static_cast<BlockType>(remainder));
519,721✔
482
                        }
483
                        r.setblock(n - 1, static_cast<BlockType>(normalized_a.block(n - 1) >> shift));
519,700✔
484
                }
519,700✔
485
                remove_leading_zeros();
2,612,915✔
486
                _sign = a.sign() ^ b.sign();
2,612,915✔
487
        }
488

489
        // modifiers (vector::clear is constexpr in C++20; on the empty
490
        // constant-evaluated state both clear() and setzero() are trivially
491
        // constexpr-clean.  setsign writes only the bool member.)
492
        constexpr void clear() noexcept { _sign = false; _block.clear(); }
44,154,082✔
493
        constexpr void setzero() noexcept { clear(); }
530,045✔
494
        constexpr void setsign(bool sign = true) noexcept { _sign = sign; }
6,811,394✔
495
        // use un-interpreted raw bits to set the bits of the einteger
496
        void setbits(unsigned long long value) {
23,448,831✔
497
                clear();
23,448,831✔
498
                if constexpr (bitsInBlock == 8) {
499
                        std::uint8_t byte0 = static_cast<std::uint8_t>(value & 0x0000'0000'0000'00FF);
6,033,349✔
500
                        std::uint8_t byte1 = static_cast<std::uint8_t>((value & 0x0000'0000'0000'FF00) >> 8);
6,033,349✔
501
                        std::uint8_t byte2 = static_cast<std::uint8_t>((value & 0x0000'0000'00FF'0000) >> 16);
6,033,349✔
502
                        std::uint8_t byte3 = static_cast<std::uint8_t>((value & 0x0000'0000'FF00'0000) >> 24);
6,033,349✔
503
                        std::uint8_t byte4 = static_cast<std::uint8_t>((value & 0x0000'00FF'0000'0000) >> 32);
6,033,349✔
504
                        std::uint8_t byte5 = static_cast<std::uint8_t>((value & 0x0000'FF00'0000'0000) >> 40);
6,033,349✔
505
                        std::uint8_t byte6 = static_cast<std::uint8_t>((value & 0x00FF'0000'0000'0000) >> 48);
6,033,349✔
506
                        std::uint8_t byte7 = static_cast<std::uint8_t>((value & 0xFF00'0000'0000'0000) >> 56);
6,033,349✔
507
                        if (byte7 > 0) {
6,033,349✔
508
                                _block.push_back(byte0);
5✔
509
                                _block.push_back(byte1);
5✔
510
                                _block.push_back(byte2);
5✔
511
                                _block.push_back(byte3);
5✔
512
                                _block.push_back(byte4);
5✔
513
                                _block.push_back(byte5);
5✔
514
                                _block.push_back(byte6);
5✔
515
                                _block.push_back(byte7);
5✔
516
                        }
517
                        else if (byte6 > 0) {
6,033,344✔
518
                                _block.push_back(byte0);
6✔
519
                                _block.push_back(byte1);
6✔
520
                                _block.push_back(byte2);
6✔
521
                                _block.push_back(byte3);
6✔
522
                                _block.push_back(byte4);
6✔
523
                                _block.push_back(byte5);
6✔
524
                                _block.push_back(byte6);
6✔
525
                        }
526
                        else if (byte5 > 0) {
6,033,338✔
527
                                _block.push_back(byte0);
7✔
528
                                _block.push_back(byte1);
7✔
529
                                _block.push_back(byte2);
7✔
530
                                _block.push_back(byte3);
7✔
531
                                _block.push_back(byte4);
7✔
532
                                _block.push_back(byte5);
7✔
533
                        }
534
                        else if (byte4 > 0) {
6,033,331✔
535
                                _block.push_back(byte0);
6✔
536
                                _block.push_back(byte1);
6✔
537
                                _block.push_back(byte2);
6✔
538
                                _block.push_back(byte3);
6✔
539
                                _block.push_back(byte4);
6✔
540
                        }
541
                        else if (byte3 > 0) {
6,033,325✔
542
                                _block.push_back(byte0);
7✔
543
                                _block.push_back(byte1);
7✔
544
                                _block.push_back(byte2);
7✔
545
                                _block.push_back(byte3);
7✔
546
                        }
547
                        else if (byte2 > 0) {
6,033,318✔
548
                                _block.push_back(byte0);
523,820✔
549
                                _block.push_back(byte1);
523,820✔
550
                                _block.push_back(byte2);
523,820✔
551
                        }
552
                        else if (byte1 > 0) {
5,509,498✔
553
                                _block.push_back(byte0);
4,058,693✔
554
                                _block.push_back(byte1);
4,058,693✔
555
                        }
556
                        else if (byte0 > 0) {
1,450,805✔
557
                                _block.push_back(byte0);
1,447,182✔
558
                        }
559
                        else {
560
                                _block.clear();
3,623✔
561
                        }
562
                }
563
                else if constexpr (bitsInBlock == 16) {
564
                        std::uint16_t word0 = static_cast<std::uint16_t>( value & 0x0000'0000'0000'FFFF);
7,724,678✔
565
                        std::uint16_t word1 = static_cast<std::uint16_t>((value & 0x0000'0000'FFFF'0000) >> 16);
7,724,678✔
566
                        std::uint16_t word2 = static_cast<std::uint16_t>((value & 0x0000'FFFF'0000'0000) >> 32);
7,724,678✔
567
                        std::uint16_t word3 = static_cast<std::uint16_t>((value & 0xFFFF'0000'0000'0000) >> 48);
7,724,678✔
568
                        if (word3 > 0) {
7,724,678✔
569
                                _block.push_back(word0);
×
570
                                _block.push_back(word1);
×
571
                                _block.push_back(word2);
×
572
                                _block.push_back(word3);
×
573
                        }
574
                        else if (word2 > 0) {
7,724,678✔
575
                                _block.push_back(word0);
×
576
                                _block.push_back(word1);
×
577
                                _block.push_back(word2);
×
578
                        }
579
                        else if (word1 > 0) {
7,724,678✔
580
                                _block.push_back(word0);
588,839✔
581
                                _block.push_back(word1);
588,839✔
582
                        }
583
                        else if (word0 > 0) {
7,135,839✔
584
                                _block.push_back(word0);
7,131,992✔
585
                        }
586
                        else {
587
                                _block.clear();
3,847✔
588
                        }
589
                }
590
                else if constexpr (bitsInBlock == 32) {
591
                        std::uint32_t low  = static_cast<std::uint32_t>(value & 0x0000'0000'FFFF'FFFF);
9,690,804✔
592
                        std::uint32_t high = static_cast<std::uint32_t>((value & 0xFFFF'FFFF'0000'0000) >> bitsInBlock);
9,690,804✔
593
                        if (high > 0) {
9,690,804✔
594
                                _block.push_back(low);
587,344✔
595
                                _block.push_back(high);
587,344✔
596
                        }
597
                        else if (low > 0) {
9,103,460✔
598
                                _block.push_back(low);
9,098,846✔
599
                        }
600
                        else {
601
                                _block.clear();
4,614✔
602
                        }
603
                }
604
        }
23,448,831✔
605
        void setblock(unsigned i, BlockType value) noexcept {
6,238,398✔
606
                if (i >= _block.size()) _block.resize(i+1ull);
6,238,398✔
607
                _block[i] = value;
6,238,398✔
608
        }
6,238,398✔
609
        void setbyte(unsigned i, std::uint8_t byte) noexcept {
×
610
                std::cerr << "setbyte(" << i << ", " << int(byte) << ") TBD\n";
×
611
        }
×
612
        einteger& assign(const std::string& txt) {
2✔
613
                if (!parse(txt, *this)) {
2✔
614
                        std::cerr << "Unable to parse: " << txt << std::endl;
×
615
                }
616
                return *this;
2✔
617
        }
618

619
        // selectors (read-only access to _sign and _block; constexpr-callable
620
        // on the empty default-constructed state and on any persisted
621
        // constexpr einteger -- which by definition has empty _block)
622
        constexpr bool iszero() const noexcept { return (_block.size() == 0 || ((_block.size() == 1) && _block[0] == bt(0))); }
6,948,486✔
623
        constexpr bool isone()  const noexcept { return true; }
624
        constexpr bool isodd()  const noexcept { return (_block.size() > 0) ? (_block[0] & 0x1) : false; }
625
        constexpr bool iseven() const noexcept { return !isodd(); }
626
        constexpr bool ispos()  const noexcept { return !_sign; }
627
        constexpr bool isneg()  const noexcept { return _sign; }
150✔
628

629
        constexpr bool test(unsigned index) const noexcept {
241,142,552✔
630
                if (index < nbits()) {
241,142,552✔
631
                        unsigned blockIndex = index / bitsInBlock;
241,142,552✔
632
                        unsigned bitIndexInBlock = index % bitsInBlock;
241,142,552✔
633
                        BlockType data = _block[blockIndex];
241,142,552✔
634
                        BlockType mask = (0x1u << bitIndexInBlock);
241,142,552✔
635
                        if (data & mask) return true;
241,142,552✔
636
                }
637
                return false;
186,817,975✔
638
        }
639
        constexpr bool sign()   const noexcept { return _sign; }
52,471,028✔
640
        constexpr int scale()   const noexcept { return findMsb(); } // TODO: when value = 0, scale returns -1 which is incorrect
641

642
        constexpr BlockType block(unsigned b) const noexcept {
12,955,104✔
643
                if (b < _block.size()) return _block[b];
12,955,104✔
644
                return static_cast<BlockType>(0u);
325,709✔
645
        }
646
        constexpr unsigned limbs() const noexcept { return static_cast<unsigned>(_block.size()); }
64,961,835✔
647

648
        constexpr unsigned nbits() const noexcept { return static_cast<unsigned>(_block.size() * sizeof(BlockType) * 8); }
493,242,380✔
649

650
        // findMsb takes an einteger reference and returns the position of the most significant bit, -1 if v == 0
651
        constexpr int findMsb() const noexcept {
652
                int nrBlocks = static_cast<int>(_block.size());
653
                if (nrBlocks == 0) return -1; // no significant bit found, all bits are zero
654
                int msb = nrBlocks * static_cast<int>(bitsInBlock);
655
                for (int b = nrBlocks - 1; b >= 0; --b) {
656
                        // derive mask from the actual block width so this works for
657
                        // uint8_t / uint16_t / uint32_t instantiations (was hardcoded
658
                        // to 0x8000'0000ul, which only worked for uint32_t)
659
                        std::uint64_t segment = static_cast<std::uint64_t>(_block[static_cast<size_t>(b)]);
660
                        std::uint64_t mask = (std::uint64_t(1) << (bitsInBlock - 1));
661
                        for (int i = bitsInBlock - 1; i >= 0; --i) {
662
                                --msb;
663
                                if (segment & mask) return msb;
664
                                mask >>= 1;
665
                        }
666
                }
667
                return -1; // no significant bit found, all bits are zero
668
        }
669

670
        // convert to string containing digits number of digits
671
        std::string str(size_t nrDigits = 0) const {
672
                return std::string("tbd");
673
        }
674

675
        // show the binary encodings of the limbs
676
        std::string showLimbs() const {
677
                if (_block.empty()) return "no limbs";
678
                std::stringstream s;
679
                size_t i = _block.size() - 1;
680
                while (i > 0) {
681
                        s << to_binary(_block[i], sizeof(BlockType) * 8, true) << ' ';
682
                        --i;
683
                }
684
                s << to_binary(_block[0], sizeof(BlockType) * 8, true);
685
                return s.str();
686
        }
687
        // show the values of the limbs as a radix-BlockType number
688
        std::string showLimbValues() const {
689
                if (_block.empty()) return "no limbs";
690
                std::stringstream s;
691
                size_t i = _block.size() - 1;
692
                while (i > 0) {
693
                        s << std::setw(5) << unsigned(_block[i]) << ", ";
694
                        --i;
695
                }
696
                s << std::setw(5) << unsigned(_block[0]);
697
                return s.str();
698
        }
699

700
protected:
701
        bool                   _sign;   // sign of the number: -1 if true, +1 if false, zero is positive
702
        std::vector<BlockType> _block;  // building blocks representing a 1's complement magnitude
703

704
        // HELPER methods
705
        // compare_magnitude returns 1 if a > b, 0 if they are equal, and -1 if a < b
706
        int compare_magnitude(const einteger& a, const einteger& b) {
4,320,770✔
707
                unsigned aLimbs = a.limbs();
4,320,770✔
708
                unsigned bLimbs = b.limbs();
4,320,770✔
709
                if (aLimbs != bLimbs) {
4,320,770✔
710
                        return (aLimbs > bLimbs ? 1 : -1);  // return 1 if a > b, otherwise -1
396,283✔
711
                }
712
                for (int i = static_cast<int>(aLimbs) - 1; i >= 0; --i) {
4,125,697✔
713
                        BlockType _a = a._block[static_cast<size_t>(i)];
4,121,095✔
714
                        BlockType _b = b._block[static_cast<size_t>(i)];
4,121,095✔
715
                        if ( _a != _b) {
4,121,095✔
716
                                return (_a > _b ? 1 : -1);
3,919,885✔
717
                        }
718
                }
719
                return 0;
4,602✔
720
        }
721
        void remove_leading_zeros() {
6,937,302✔
722
                unsigned leadingZeroBlocks{ 0 };
6,937,302✔
723
                typename std::vector<BlockType>::reverse_iterator rit = _block.rbegin();
6,937,302✔
724
                while (rit != _block.rend()) {
7,336,315✔
725
                        if (*rit == 0) {
6,286,089✔
726
                                ++leadingZeroBlocks;
399,013✔
727
                        }
728
                        else {
729
                                break;
5,887,076✔
730
                        }
731
                        ++rit;
399,013✔
732
                }
733
                _block.resize(_block.size() - leadingZeroBlocks);
6,937,302✔
734
        }
6,937,302✔
735
        
736
        template<typename SignedInt>
737
        einteger& convert_signed(SignedInt v) {
13,036,547✔
738
                clear();
13,036,547✔
739
                if (v != 0) {
13,036,547✔
740
                        if (v < 0) {
10,927,905✔
741
                                setbits(static_cast<unsigned long long>(-v));
2,160,389✔
742
                                setsign(true);
2,160,389✔
743
                        }
744
                        else {
745
                                setbits(static_cast<unsigned long long>(v)); // TODO: what about -2^63
8,767,516✔
746
                        }
747
                }
748
                return *this;
13,036,547✔
749
        }
750

751
        template<typename UnsignedInt>
752
        einteger& convert_unsigned(UnsignedInt v) {
2,093,766✔
753
                if (0 == v) {
2,093,766✔
754
                        setzero();
530,045✔
755
                }
756
                else {
757
                        setbits(static_cast<unsigned long long>(v));
1,563,721✔
758
                }
759
                return *this;
2,093,766✔
760
        }
761

762
        template<typename Real>
763
        einteger& convert_ieee754(Real& rhs) {
118✔
764
                clear();
118✔
765
                bool s{ false };
118✔
766
                std::uint64_t rawExponent{ 0 };
118✔
767
                std::uint64_t rawFraction{ 0 };
118✔
768
                uint64_t bits{ 0 };
118✔
769
                extractFields(rhs, s, rawExponent, rawFraction, bits);
118✔
770
                if (rawExponent == ieee754_parameter<Real>::eallset) { // nan and inf
118✔
771
                        // we can't represent NaNs or Infinities
772
                        return *this;
3✔
773
                }
774
                int exponent = static_cast<int>(rawExponent) - ieee754_parameter<Real>::bias;
115✔
775
                if (exponent < 0) {
115✔
776
                        return *this; // we are zero
×
777
                }
778
                // normal and subnormal handling
779
                constexpr size_t fbits = ieee754_parameter<Real>::fbits;
115✔
780
                std::uint64_t hiddenBit = (0x1ull << fbits);
115✔
781
                rawFraction |= hiddenBit;
115✔
782
                setbits(rawFraction);
115✔
783
                setsign(s);
115✔
784
                // scale the fraction bits
785
                *this <<= static_cast<int>(exponent - fbits);
115✔
786
                return *this;
115✔
787
        }
788

789
        template<typename Integer>
790
        Integer convert_to_native_integer() const noexcept {
10,957,141✔
791
                using Unsigned = std::make_unsigned_t<Integer>;
792
                Unsigned v{ 0 };
10,957,141✔
793
                Unsigned m{ 1 };
10,957,141✔
794
                const bool neg = sign();
10,957,141✔
795
                constexpr Integer kMax = std::numeric_limits<Integer>::max();
10,957,141✔
796
                constexpr Integer kMin = std::numeric_limits<Integer>::min();
10,957,141✔
797
                constexpr unsigned kDigits = std::numeric_limits<Unsigned>::digits;
10,957,141✔
798
                for (unsigned i = 0; i < nbits(); ++i) {
252,091,045✔
799
                        if (i >= kDigits) {
241,133,904✔
800
                                if (test(i)) return neg ? kMin : kMax;
×
801
                                continue;
×
802
                        }
803
                        if (test(i)) {
241,133,904✔
804
                                if (v > (static_cast<Unsigned>(kMax) - m)) {
54,323,308✔
805
                                        return neg ? kMin : kMax;
×
806
                                }
807
                                v += m;
54,323,308✔
808
                        }
809
                        m <<= 1;
241,133,904✔
810
                }
811
                return (neg ? -static_cast<Integer>(v) : static_cast<Integer>(v));
10,957,141✔
812
        }
813
        template<typename Real>
814
        Real convert_to_native_ieee() const noexcept {
117✔
815
                Real v{ 0 };
117✔
816
                Real m{ 1.0 };
117✔
817
                for (unsigned i = 0; i < nbits(); ++i) {
8,765✔
818
                        if (test(i)) {
8,648✔
819
                                v += m;
1,269✔
820
                        }
821
                        m *= Real(2.0);
8,648✔
822
                }
823
                return (sign() ? -v : v);
117✔
824
        }
825

826
private:
827

828
        template<typename BBlockType>
829
        friend constexpr bool operator==(const einteger<BBlockType>&, const einteger<BBlockType>&) noexcept;
830
};
831

832
////////////////////////    einteger functions   /////////////////////////////////
833

834
template<typename BlockType>
835
inline einteger<BlockType> abs(const einteger<BlockType>& a) {
836
        return (a.isneg()  ? -a : a);
837
}
838

839
////////////////////////    INTEGER operators   /////////////////////////////////
840

841
/// stream operators
842

843
// read a einteger ASCII format and make a binary einteger out of it
844
template<typename BlockType>
845
bool parse(const std::string& number, einteger<BlockType>& value) {
6✔
846
        using Integer = einteger<BlockType>;
847
        bool bSuccess = false;
6✔
848
        value.clear();
6✔
849
        std::regex binary_regex("^[-+]*0b[01']+");
6✔
850
        // check if the txt is an integer form: [0123456789]+
851
        std::regex decimal_regex("^[-+]*[0-9]+");
6✔
852
        std::regex octal_regex("^[-+]*0[1-7][0-7]*$");
6✔
853
        std::regex hex_regex("^[-+]*0[xX][0-9a-fA-F']+");
6✔
854
        // setup associative array to map chars to nibbles
855
        std::map<char, int> charLookup{
12✔
856
                { '0', 0 },
857
                { '1', 1 },
858
                { '2', 2 },
859
                { '3', 3 },
860
                { '4', 4 },
861
                { '5', 5 },
862
                { '6', 6 },
863
                { '7', 7 },
864
                { '8', 8 },
865
                { '9', 9 },
866
                { 'a', 10 },
867
                { 'b', 11 },
868
                { 'c', 12 },
869
                { 'd', 13 },
870
                { 'e', 14 },
871
                { 'f', 15 },
872
                { 'A', 10 },
873
                { 'B', 11 },
874
                { 'C', 12 },
875
                { 'D', 13 },
876
                { 'E', 14 },
877
                { 'F', 15 },
878
        };
879
        if (std::regex_match(number, octal_regex)) {
6✔
880
                std::cout << "found an octal representation\n";
×
881
                for (std::string::const_reverse_iterator r = number.rbegin();
×
882
                        r != number.rend();
×
883
                        ++r) {
×
884
                        std::cout << "char = " << *r << std::endl;
×
885
                }
886
                bSuccess = false; // TODO
×
887
        }
888
        else if (std::regex_match(number, hex_regex)) {
6✔
889
                //std::cout << "found a hexadecimal representation\n";
890
                // each char is a nibble
891
                int byte = 0;
×
892
                int byteIndex = 0;
×
893
                bool odd = false;
×
894
                for (std::string::const_reverse_iterator r = number.rbegin();
×
895
                        r != number.rend();
×
896
                        ++r) {
×
897
                        if (*r == '\'') {
×
898
                                // ignore
899
                        }
900
                        else if (*r == 'x' || *r == 'X') {
×
901
                                if (odd) {
×
902
                                        // complete the most significant byte
903
                                        value.setbyte(static_cast<size_t>(byteIndex), static_cast<uint8_t>(byte));
×
904
                                }
905
                                // check that we have [-+]0[xX] format
906
                                ++r;
×
907
                                if (r != number.rend()) {
×
908
                                        if (*r == '0') {
×
909
                                                // check if we have a sign
910
                                                ++r;
×
911
                                                if (r == number.rend()) {
×
912
                                                        // no sign, thus by definition positive
913
                                                        bSuccess = true;
×
914
                                                }
915
                                                else if (*r == '+') {
×
916
                                                        // optional positive sign, no further action necessary
917
                                                        bSuccess = true;
×
918
                                                }
919
                                                else if (*r == '-') {
×
920
                                                        // negative sign, invert
921
                                                        value = -value;
×
922
                                                        bSuccess = true;
×
923
                                                }
924
                                                else {
925
                                                        // the regex will have filtered this out
926
                                                        bSuccess = false;
×
927
                                                }
928
                                        }
929
                                        else {
930
                                                // we didn't find the obligatory '0', the regex should have filtered this out
931
                                                bSuccess = false;
×
932
                                        }
933
                                }
934
                                else {
935
                                        // we are missing the obligatory '0', the regex should have filtered this out
936
                                        bSuccess = false;
×
937
                                }
938
                                // we have reached the end of our parse
939
                                break;
×
940
                        }
941
                        else {
942
                                if (odd) {
×
943
                                        byte += charLookup.at(*r) << 4;
×
944
                                        value.setbyte(static_cast<size_t>(byteIndex), static_cast<uint8_t>(byte));
×
945
                                        ++byteIndex;
×
946
                                }
947
                                else {
948
                                        byte = charLookup.at(*r);
×
949
                                }
950
                                odd = !odd;
×
951
                        }
952
                }
953
        }
954
        else if (std::regex_match(number, decimal_regex)) {
6✔
955
                //std::cout << "found a decimal integer representation\n";
956
                Integer scale = 1;
4✔
957
                bool sign{ false };
4✔
958
                for (std::string::const_reverse_iterator r = number.rbegin();
4✔
959
                        r != number.rend();
37✔
960
                        ++r) {
33✔
961
                        if (*r == '-') {
33✔
962
                                sign = true;;
1✔
963
                        }
964
                        else if (*r == '+') {
32✔
965
                                break;
×
966
                        }
967
                        else {
968
                                Integer digit = charLookup.at(*r);
32✔
969
                                value += scale * digit;
32✔
970
                                scale *= 10;
32✔
971
                        }
32✔
972
                }
973
                value.setsign(sign);
4✔
974
                bSuccess = true;
4✔
975
        }
4✔
976
        else if (std::regex_match(number, binary_regex)) {
2✔
977
                //std::cout << "found a binary integer representation\n";
978
                Integer scale = 1;
1✔
979
                //bool sign{ false };
980
                unsigned byte{ 0 }; // using an unsigned to simplify accumulation, but accumulating 8-bit byte values
1✔
981
                unsigned bitIndex = 0;
1✔
982
                for (std::string::const_reverse_iterator r = number.rbegin();
1✔
983
                        r != number.rend();
24✔
984
                        ++r) {
23✔
985
                        if (*r == '-') {
23✔
986
                                //sign = true;;
987
                        }
988
                        else if (*r == '+') {
23✔
989
                                break;
×
990
                        }
991
                        else if (*r == '\'') {
23✔
992
                                // ignore separator
993
                        }
994
                        else {
995
                                if (*r == '1') {
19✔
996
                                        byte |= (1u << (bitIndex % 8));
9✔
997
                                }
998
                                if (bitIndex == 7) {
19✔
999
                                        value += scale * byte;
1✔
1000
                                        scale *= 256;
1✔
1001
                                        byte = 0;
1✔
1002
                                }
1003
                                ++bitIndex;
19✔
1004
                        }
1005
                }
1006
                if (bitIndex % 8) {
1✔
1007
                        value += scale * byte;
1✔
1008
                }
1009
                bSuccess = true;
1✔
1010
        }
1✔
1011
        return bSuccess;
6✔
1012
}
6✔
1013

1014
template<typename BlockType>
1015
std::string convert_to_string(std::ios_base::fmtflags flags, const einteger<BlockType>& n) {
155✔
1016
        using AdaptiveInteger = einteger<BlockType>;
1017

1018
        if (n.limbs() == 0) return std::string("0");
165✔
1019

1020
        // set the base of the target number system to convert to
1021
        int base = 10;
150✔
1022
        if ((flags & std::ios_base::oct) == std::ios_base::oct) base = 8;
150✔
1023
        if ((flags & std::ios_base::hex) == std::ios_base::hex) base = 16;
150✔
1024

1025
        unsigned nbits = n.limbs() * sizeof(BlockType) * 8;
150✔
1026

1027
        std::string result;
150✔
1028
        if (base == 8 || base == 16) {
150✔
1029
                if (n.sign()) return std::string("negative value: ignored");
×
1030

1031
                size_t shift = (base == 8 ? 3ull : 4ull);
×
1032
                BlockType mask = static_cast<BlockType>((1u << shift) - 1);
×
1033
                AdaptiveInteger t(n);
×
1034
                result.assign(nbits / shift + ((nbits % shift) ? 1 : 0), '0');
×
1035
                size_t pos = result.size() - 1ull;
×
1036
                for (size_t i = 0; i < nbits / shift; ++i) {
×
1037
                        char c = '0' + static_cast<char>(t.block(0) & mask);
×
1038
                        if (c > '9')
×
1039
                                c += 'A' - '9' - 1;
×
1040
                        result[pos--] = c;
×
1041
                        t >>= static_cast<int>(shift);
×
1042
                }
1043
                if (nbits % shift) {
×
1044
                        mask = static_cast<BlockType>((1u << (nbits % shift)) - 1);
×
1045
                        char c = '0' + static_cast<char>(t.block(0) & mask);
×
1046
                        if (c > '9')
×
1047
                                c += 'A' - '9';
×
1048
                        result[pos] = c;
×
1049
                }
1050
                //
1051
                // Get rid of leading zeros:
1052
                //
1053
                std::string::size_type fnz = result.find_first_not_of('0');
×
1054
                if (!result.empty() && (fnz == std::string::npos)) fnz = result.size() - 1;
×
1055
                result.erase(0, fnz);
×
1056
                if (flags & std::ios_base::showbase) {
×
1057
                        const char* pp = base == 8 ? "0" : "0x";
×
1058
                        result.insert(static_cast<std::string::size_type>(0), pp);
×
1059
                }
1060
        }
×
1061
        else {
1062
                unsigned block10;
1063
                unsigned digits_in_block10;
1064
                if constexpr (AdaptiveInteger::bitsInBlock == 8) {
1065
                        block10 = 100u;
68✔
1066
                        digits_in_block10 = 2;
68✔
1067
                }
1068
                else if constexpr (AdaptiveInteger::bitsInBlock == 16) {
1069
                        block10 = 10'000ul;
42✔
1070
                        digits_in_block10 = 4;
42✔
1071
                }
1072
                else if constexpr (AdaptiveInteger::bitsInBlock == 32) {
1073
                        block10 = 1'000'000'000ul;
40✔
1074
                        digits_in_block10 = 9;
40✔
1075
                }
1076
                else if constexpr (AdaptiveInteger::bitsInBlock == 64) {
1077
                        // not allowed as the whole multi-digit arithmetic
1078
                        // requires that there is a 'larger' type that
1079
                        // can receive carries and borrows.
1080
                        // If your platform does have a native 128bit
1081
                        // integer, this could be enabled
1082
                        //block10 = 1'000'000'000'000'000'000ull;
1083
                        //digits_in_block10 = 18;
1084
                }
1085
                result.assign(nbits / 3 + 1ull, '0');
150✔
1086
                size_t pos = result.size() - 1ull;
150✔
1087
                AdaptiveInteger t(n);
150✔
1088
                while (!t.iszero()) {
1,390✔
1089
                        AdaptiveInteger q,r;
620✔
1090
                        q.reduce(t, block10, r);
620✔
1091
                        BlockType v = r.block(0);
620✔
1092
//                        std::cout << "v  " << uint32_t(v) << '\n';
1093
                        for (unsigned i = 0; i < digits_in_block10; ++i) {
2,696✔
1094
                                char c = '0' + static_cast<char>(v % 10);
2,089✔
1095
                                v /= 10;
2,089✔
1096
                                result[pos] = c;
2,089✔
1097
//                                std::cout << result << " pos: " << pos << '\n';
1098
                                if (pos-- == 0)        break;
2,089✔
1099
                        }
1100
                        t = q;
620✔
1101
                }
1102

1103
                std::string::size_type firstDigit = result.find_first_not_of('0');
150✔
1104
                result.erase(0, firstDigit);
150✔
1105
                if (result.empty())
150✔
1106
                        result = "0";
29✔
1107
                if (n.isneg())
150✔
1108
                        result.insert(0ull, 1ull, '-');
52✔
1109
                else if (flags & std::ios_base::showpos)
98✔
1110
                        result.insert(0ull, 1ull, '+');
×
1111
        }
150✔
1112
        return result;
150✔
1113
}
150✔
1114

1115
// generate an einteger format ASCII format
1116
template<typename BlockType>
1117
inline std::ostream& operator<<(std::ostream& ostr, const einteger<BlockType>& i) {
155✔
1118
        std::string s = convert_to_string(ostr.flags(), i);
155✔
1119
        std::streamsize width = ostr.width();
155✔
1120
        if (width > static_cast<std::streamsize>(s.size())) {
155✔
1121
                char fill = ostr.fill();
×
1122
                if ((ostr.flags() & std::ios_base::left) == std::ios_base::left)
×
1123
                        s.append(static_cast<std::string::size_type>(width - s.size()), fill);
×
1124
                else
1125
                        s.insert(static_cast<std::string::size_type>(0), static_cast<std::string::size_type>(width - s.size()), fill);
×
1126
        }
1127
        return ostr << s;
310✔
1128
}
155✔
1129

1130
// read an ASCII einteger format
1131

1132
template<typename BlockType>
1133
inline std::istream& operator>>(std::istream& istr, einteger<BlockType>& p) {
1✔
1134
        std::string txt;
1✔
1135
        if (!(istr >> txt)) {
1✔
1136
                // extraction failed (already-bad stream or EOF); failbit set by >>.
1137
                return istr;
×
1138
        }
1139
        if (!parse(txt, p)) {
1✔
1140
                std::cerr << "unable to parse -" << txt << "- into an einteger value\n";
1✔
1141
                istr.setstate(std::ios::failbit);
1✔
1142
        }
1143
        return istr;
1✔
1144
}
1✔
1145

1146
////////////////// string operators
1147

1148
template<typename BlockType>
1149
inline std::string to_binary(const einteger<BlockType>& a, bool nibbleMarker = true) {
122✔
1150
        if (a.limbs() == 0) return std::string("0b0");
130✔
1151

1152
        std::stringstream s;
118✔
1153
        s << "0b";
118✔
1154
        for (int b = static_cast<int>(a.limbs()) - 1; b >= 0; --b) {
737✔
1155
                BlockType segment = a.block(static_cast<size_t>(b));
619✔
1156
                BlockType mask = (0x1u << (a.bitsInBlock - 1));
619✔
1157
                for (int i = a.bitsInBlock - 1; i >= 0; --i) {
9,427✔
1158
                        s << ((segment & mask) ? '1' : '0');
8,808✔
1159
                        if (i > 0 && (i % 4) == 0 && nibbleMarker) s << '\'';
8,808✔
1160
                        if (b > 0 && i == 0 && nibbleMarker) s << '\'';
8,808✔
1161
                        mask >>= 1;
8,808✔
1162
                }
1163
        }
1164

1165
        return s.str();
118✔
1166
}
118✔
1167

1168
template<typename BlockType>
1169
inline std::string to_hex(const einteger<BlockType>& a, bool wordMarker = true) {
4✔
1170
        if (a.limbs() == 0) return std::string("0x0");
6✔
1171

1172
        std::vector<char> nibbleLookup = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
6✔
1173
        std::stringstream s;
3✔
1174
        s << "0x";
3✔
1175
        unsigned bitIndex = a.limbs() * a.bitsInBlock - 1u;
3✔
1176
        for (int b = static_cast<int>(a.limbs()) - 1; b >= 0; --b) {
11✔
1177
                BlockType limb = a.block(static_cast<size_t>(b));
8✔
1178
                BlockType mask = (0x1u << (a.bitsInBlock - 1));
8✔
1179
                unsigned nibble{ 0 };
8✔
1180
                unsigned rightShift = a.bitsInBlock - 4u;
8✔
1181
                for (int i = a.bitsInBlock - 1; i >= 0; --i) {
136✔
1182

1183
                        nibble |= (limb & mask);
128✔
1184
                        if ((i % 4) == 0) {
128✔
1185
                                nibble >>= rightShift;
32✔
1186
                                s << nibbleLookup[nibble];
32✔
1187
                                nibble = 0;
32✔
1188
                                rightShift -= 4u;
32✔
1189
                        }
1190
                        if (bitIndex > 0 && ((bitIndex % 16) == 0) && wordMarker) s << '\'';
128✔
1191
                        mask >>= 1;
128✔
1192
                        --bitIndex;
128✔
1193
                }
1194
        }
1195

1196
        return s.str();
3✔
1197

1198
}
3✔
1199

1200
//////////////////////////////////////////////////////////////////////////////////////////////////////
1201
// einteger - einteger binary logic operators
1202

1203
// equal: sign-aware (treats +0 == -0 by canonicalizing zero); pre-existing
1204
// implementation ignored sign entirely, so +n == -n returned true.
1205
// Constexpr-callable: pure reads of the digit vector, no allocation.
1206

1207
template<typename BlockType>
1208
constexpr bool operator==(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) noexcept {
11,466,191✔
1209
        if (lhs.limbs() != rhs.limbs()) {
11,466,191✔
1210
                return false;
3,516✔
1211
        }
1212
        if (lhs.sign() != rhs.sign()) {
11,462,675✔
1213
                // keep +0 == -0, but require sign match for non-zero values
1214
                return lhs.iszero() && rhs.iszero();
9✔
1215
        }
1216
        for (unsigned i = 0; i < lhs.limbs(); ++i) {
24,361,892✔
1217
                if (lhs._block[i] != rhs._block[i]) return false;
13,418,916✔
1218
        }
1219
        return true;
10,942,976✔
1220
}
1221

1222
template<typename BlockType>
1223
constexpr bool operator!=(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) noexcept {
10,941,956✔
1224
        return !operator==(lhs, rhs);
10,941,956✔
1225
}
1226

1227
template<typename BlockType>
1228
constexpr bool operator< (const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) noexcept {
1,047,031✔
1229
        const bool ls = lhs.sign();
1,047,031✔
1230
        const bool rs = rhs.sign();
1,047,031✔
1231
        if (ls != rs) {
1,047,031✔
1232
                // +0 and -0 are equal, not ordered
1233
                if (lhs.iszero() && rhs.iszero()) return false;
41✔
1234
                return ls; // negative < positive
41✔
1235
        }
1236
        unsigned ll = lhs.limbs();
1,046,990✔
1237
        unsigned rl = rhs.limbs();
1,046,990✔
1238
        // for negatives, larger magnitude means smaller value -- swap the sense
1239
        if (ll != rl) return ls ? (ll > rl) : (ll < rl);
1,046,990✔
1240
        if (ll == 0) return false; // both empty: equal in magnitude
1,040,416✔
1241
        for (unsigned b = ll; b-- > 0;) {
1,045,579✔
1242
                BlockType l = lhs.block(b);
1,044,552✔
1243
                BlockType r = rhs.block(b);
1,044,552✔
1244
                if (l == r) continue;
1,044,552✔
1245
                return ls ? (l > r) : (l < r);
1,039,389✔
1246
        }
1247
        return false; // lhs and rhs are the same
1,027✔
1248
}
1249

1250
template<typename BlockType>
1251
constexpr bool operator> (const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) noexcept {
2✔
1252
        return operator< (rhs, lhs);
2✔
1253
}
1254

1255
template<typename BlockType>
1256
constexpr bool operator<=(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) noexcept {
1✔
1257
        return operator< (lhs, rhs) || operator==(lhs, rhs);
1✔
1258
}
1259

1260
template<typename BlockType>
1261
constexpr bool operator>=(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) noexcept {
1✔
1262
        return !operator< (lhs, rhs);
1✔
1263
}
1264

1265
//////////////////////////////////////////////////////////////////////////////////////////////////////
1266
// einteger - literal binary logic operators
1267
// equal: precondition is that the byte-storage is properly nulled in all arithmetic paths
1268

1269
template<typename BlockType>
1270
inline bool operator==(const einteger<BlockType>& lhs, long long rhs) {
1271
        return operator==(lhs, einteger(rhs));
1272
}
1273

1274
template<typename BlockType>
1275
inline bool operator!=(const einteger<BlockType>& lhs, long long rhs) {
1276
        return !operator==(lhs, rhs);
1277
}
1278

1279
template<typename BlockType>
1280
inline bool operator< (const einteger<BlockType>& lhs, long long rhs) {
1281
        return operator<(lhs, einteger<BlockType>(rhs));
1282
}
1283

1284
template<typename BlockType>
1285
inline bool operator> (const einteger<BlockType>& lhs, long long rhs) {
1286
        return operator< (einteger<BlockType>(rhs), lhs);
1287
}
1288

1289
template<typename BlockType>
1290
inline bool operator<=(const einteger<BlockType>& lhs, long long rhs) {
1291
        return operator< (lhs, rhs) || operator==(lhs, rhs);
1292
}
1293

1294
template<typename BlockType>
1295
inline bool operator>=(const einteger<BlockType>& lhs, long long rhs) {
1296
        return !operator< (lhs, rhs);
1297
}
1298

1299
//////////////////////////////////////////////////////////////////////////////////////////////////////
1300
// literal - einteger binary logic operators
1301
// precondition is that the byte-storage is properly nulled in all arithmetic paths
1302

1303

1304
template<typename BlockType>
1305
inline bool operator==(long long lhs, const einteger<BlockType>& rhs) {
1306
        return operator==(einteger<BlockType>(lhs), rhs);
1307
}
1308

1309
template<typename BlockType>
1310
inline bool operator!=(long long lhs, const einteger<BlockType>& rhs) {
1311
        return !operator==(lhs, rhs);
1312
}
1313

1314
template<typename BlockType>
1315
inline bool operator< (long long lhs, const einteger<BlockType>& rhs) {
1316
        return operator<(einteger<BlockType>(lhs), rhs);
1317
}
1318

1319
template<typename BlockType>
1320
inline bool operator> (long long lhs, const einteger<BlockType>& rhs) {
1321
        return operator< (rhs, lhs);
1322
}
1323

1324
template<typename BlockType>
1325
inline bool operator<=(long long lhs, const einteger<BlockType>& rhs) {
1326
        return operator< (lhs, rhs) || operator==(lhs, rhs);
1327
}
1328

1329
template<typename BlockType>
1330
inline bool operator>=(long long lhs, const einteger<BlockType>& rhs) {
1331
        return !operator< (lhs, rhs);
1332
}
1333

1334
//////////////////////////////////////////////////////////////////////////////////////////////////////
1335

1336
//////////////////////////////////////////////////////////////////////////////////////////////////////
1337
// einteger - einteger binary arithmetic operators
1338

1339
template<typename BlockType>
1340
inline einteger<BlockType> operator+(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) {
3,145,986✔
1341
        einteger sum = lhs;
3,145,986✔
1342
        sum += rhs;
3,145,986✔
1343
        return sum;
3,145,986✔
1344
}
×
1345

1346
template<typename BlockType>
1347
inline einteger<BlockType> operator-(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) {
4,325,378✔
1348
        einteger diff = lhs;
4,325,378✔
1349
        diff -= rhs;
4,325,378✔
1350
        return diff;
4,325,378✔
1351
}
×
1352

1353
template<typename BlockType>
1354
inline einteger<BlockType> operator*(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) {
327,972✔
1355
        einteger product = lhs;
327,972✔
1356
        product *= rhs;
327,972✔
1357
        return product;
327,972✔
1358
}
×
1359

1360
template<typename BlockType>
1361
inline einteger<BlockType> operator/(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) {
2✔
1362
        einteger ratio = lhs;
2✔
1363
        ratio /= rhs;
2✔
1364
        return ratio;
2✔
1365
}
×
1366

1367
template<typename BlockType>
1368
inline einteger<BlockType> operator%(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) {
2✔
1369
        einteger remainder = lhs;
2✔
1370
        remainder %= rhs;
2✔
1371
        return remainder;
2✔
1372
}
×
1373

1374
//////////////////////////////////////////////////////////////////////////////////////////////////////
1375
// einteger - literal binary arithmetic operators
1376

1377
template<typename BlockType>
1378
inline einteger<BlockType> operator+(const einteger<BlockType>& lhs, long long rhs) {
1379
        return operator+(lhs, einteger<BlockType>(rhs));
1380
}
1381

1382
template<typename BlockType>
1383
inline einteger<BlockType> operator-(const einteger<BlockType>& lhs, long long rhs) {
1384
        return operator-(lhs, einteger<BlockType>(rhs));
1385
}
1386

1387
template<typename BlockType>
1388
inline einteger<BlockType> operator*(const einteger<BlockType>& lhs, long long rhs) {
2✔
1389
        return operator*(lhs, einteger<BlockType>(rhs));
2✔
1390
}
1391

1392
template<typename BlockType>
1393
inline einteger<BlockType> operator/(const einteger<BlockType>& lhs, long long rhs) {
1394
        return operator/(lhs, einteger<BlockType>(rhs));
1395
}
1396

1397
template<typename BlockType>
1398
inline einteger<BlockType> operator%(const einteger<BlockType>& lhs, long long rhs) {
1399
        return operator/(lhs, einteger<BlockType>(rhs));
1400
}
1401

1402
template<typename BlockType>
1403
inline einteger<BlockType> operator/(const einteger<BlockType>& lhs, unsigned long long rhs) {
1404
        return operator/(lhs, einteger<BlockType>(rhs));
1405
}
1406

1407
//////////////////////////////////////////////////////////////////////////////////////////////////////
1408
// literal - einteger binary arithmetic operators
1409

1410
template<typename BlockType>
1411
inline einteger<BlockType> operator+(long long lhs, const einteger<BlockType>& rhs) {
1412
        return operator+(einteger<BlockType>(lhs), rhs);
1413
}
1414

1415
template<typename BlockType>
1416
inline einteger<BlockType> operator-(long long lhs, const einteger<BlockType>& rhs) {
1417
        return operator-(einteger<BlockType>(lhs), rhs);
1418
}
1419

1420
template<typename BlockType>
1421
inline einteger<BlockType> operator*(long long lhs, const einteger<BlockType>& rhs) {
1422
        return operator*(einteger<BlockType>(lhs), rhs);
1423
}
1424

1425
template<typename BlockType>
1426
inline einteger<BlockType> operator/(long long lhs, const einteger<BlockType>& rhs) {
1427
        return operator/(einteger<BlockType>(lhs), rhs);
1428
}
1429

1430
template<typename BlockType>
1431
inline einteger<BlockType> operator%(long long lhs, const einteger<BlockType>& rhs) {
1432
        return operator/(einteger<BlockType>(lhs), rhs);
1433
}
1434

1435
}} // namespace sw::universal
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc