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

stillwater-sc / universal / 26004245518

17 May 2026 10:14PM UTC coverage: 84.155% (+0.07%) from 84.081%
26004245518

push

github

web-flow
feat(einteger): complete binary/octal parse + setbyte + reduce sign fix (#863)

* feat(einteger): complete binary/octal parse + setbyte impl + reduce sign fix

Closes #853 by completing the parse() coverage for einteger:

- Implement setbyte() (was a printing TBD stub). All three einteger block
  widths now correctly route a byte write into the right limb and offset.
  Hex parsing previously called this stub and silently produced 0;
  hex now actually works.
- Implement the octal parse branch (was a TODO that printed each char
  to std::cout and returned false). Walks left-to-right after the
  optional sign and the leading '0', accumulating value*8 + digit.
- Rewrite the binary parse branch. The previous implementation
  (i) commented out the negative-sign assignment, (ii) only flushed
  the running byte once at bitIndex==7 (so binary literals beyond
  ~16 bits dropped digits), and (iii) walked the '0b' prefix through
  the bit accumulator. New implementation walks the digits
  left-to-right with value = value*2 + bit, ignoring apostrophe
  digit-group separators, with proper sign tracking.
- Fix reduce()'s early-exit magnitude check. It used signed
  operator<, so a negative numerator was always "less than" a positive
  denominator and short-circuited to q=0, r=a even when |a| >= |b|.
  Replace with explicit magnitude comparisons (limb count then
  top-down limb compare). This shows up as the print of
  negative numbers > one limb being truncated to the low limb;
  convert_to_string is the canonical caller.

Tests (elastic/einteger/conversion/string_parse.cpp) cover:
- decimal positive / negative / very-large
- hex with and without 0x prefix, with and without apostrophe
  separators, signs, and a multi-limb -0xFFFF'FFFF'FFFF'FFFF case
- binary positive / negative, with separators, up to 64 bits
- octal small + large (up to 0o37777777777)
- malformed-input rejection (empty, alpha-only, bad-digit-in-prefix)
- block-width parity (uint8_t, uint16_t, uint... (continued)

51 of 51 new or added lines in 1 file covered. (100.0%)

10 existing lines in 3 files now uncovered.

46743 of 55544 relevant lines covered (84.15%)

6425213.55 hits per line

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

90.6
/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,130,064✔
51
        constexpr einteger(einteger&&) = default;
52

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

56
        // initializers for native types
57
        einteger(short initial_value)              { *this = initial_value; }
58
        einteger(int initial_value)                { *this = initial_value; }
115✔
59
        einteger(long initial_value)               { *this = initial_value; }
56✔
60
        einteger(long long initial_value)          { *this = initial_value; }
660✔
61
        einteger(unsigned int initial_value)       { *this = initial_value; }
853✔
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,526✔
69
        einteger& operator=(long rhs)               noexcept { return convert_signed(rhs); }
10,942,008✔
70
        einteger& operator=(long long rhs)          noexcept { return convert_signed(rhs); }
666✔
71
        einteger& operator=(unsigned int rhs)       noexcept { return convert_unsigned(rhs); }
2,094,063✔
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,610✔
174
                einteger negated(*this);
4,610✔
175
                negated.setsign(!_sign);
4,610✔
176
                return negated;
4,610✔
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,308✔
199
                if (sign() != rhs.sign()) {
3,146,308✔
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,306✔
214
                auto rhsSize = rhs._block.size();
3,146,306✔
215
                if (lhsSize < rhsSize) _block.resize(rhsSize, 0);
3,146,306✔
216

217
                std::uint64_t carry{ 0 };
3,146,306✔
218
                typename std::vector<BlockType>::iterator li = _block.begin();
3,146,306✔
219
                typename std::vector<BlockType>::const_iterator ri = rhs._block.begin();
3,146,306✔
220
                while (li != _block.end()) {
7,341,379✔
221
                        if (ri != rhs._block.end()) {
4,195,073✔
222
                                carry += static_cast<std::uint64_t>(*li) + static_cast<std::uint64_t>(*ri);
4,187,789✔
223
                                ++ri;
4,187,789✔
224
                        }
225
                        else {
226
                                carry += static_cast<std::uint64_t>(*li);
7,284✔
227
                        }
228
                        *li = static_cast<BlockType>(carry);
4,195,073✔
229
                        carry >>= bitsInBlock;
4,195,073✔
230
                        ++li; 
4,195,073✔
231
                }
232
                if (carry == 0x1ull) {
3,146,306✔
233
                        _block.push_back(static_cast<BlockType>(carry));
1,571,335✔
234
                }
235
                return *this;
3,146,306✔
236
        }
237
        einteger& operator+=(long long rhs) {
222✔
238
                return *this += einteger(rhs);
222✔
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,474✔
291
                if (iszero() || rhs.iszero()) {
328,474✔
292
                        clear();
2,621✔
293
                        return *this;
2,621✔
294
                }
295
                einteger base(*this);
325,853✔
296
                bool ls = sign();
325,853✔
297
                unsigned ll = limbs();
325,853✔
298
                bool rs = rhs.sign();
325,853✔
299
                unsigned rl = rhs.limbs();
325,853✔
300

301
                clear();
325,853✔
302
                std::uint64_t segment(0);
325,853✔
303
                for (unsigned i = 0; i < ll; ++i) {
652,054✔
304
                        for (unsigned j = 0; j < rl; ++j) {
652,402✔
305
                                segment += static_cast<std::uint64_t>(base.block(i)) * static_cast<std::uint64_t>(rhs.block(j));
326,201✔
306
                                segment += block(i + j);
326,201✔
307
                                setblock(i + j, static_cast<bt>(segment));
326,201✔
308
                                segment >>= bitsInBlock;
326,201✔
309
                        }
310
                }
311
                if (segment != 0) setblock(ll + rl - 1, static_cast<bt>(segment));
325,853✔
312
                setsign(ls ^ rs);
325,853✔
313
                return *this;
325,853✔
314
        }
325,853✔
315
        einteger& operator*=(long long rhs) {
438✔
316
                return *this *= einteger(rhs);
438✔
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,614✔
339
                if (b.iszero()) {
3,146,614✔
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,542✔
348
                r.clear();
3,143,542✔
349
                if (a.iszero()) return;
3,143,542✔
350

351
                size_t aBlocks = a.limbs();
3,140,473✔
352
                size_t bBlocks = b.limbs();
3,140,473✔
353
                if (aBlocks == 1 && aBlocks == bBlocks) { // completely reduce this to native div and rem
3,140,473✔
354
                        std::uint64_t a0 = a._block[0];
2,093,297✔
355
                        std::uint64_t b0 = b._block[0];
2,093,297✔
356
                        *this = static_cast<BlockType>(a0 / b0);
2,093,297✔
357
                        r = static_cast<BlockType>(a0 % b0);
2,093,297✔
358
                }
2,093,297✔
359
                        else {
360
                                // filter out the easy stuff. The early-exits below need to
361
                                // compare *magnitudes* (truncated-division semantics apply
362
                                // |a|/|b| with the final sign = a.sign ^ b.sign). The
363
                                // generic operator< is signed, so a negative a is always
364
                                // "less than" a positive b and we'd discard quotient bits
365
                                // for large negative numerators. Compare by limb count and
366
                                // then top-down by limb value, ignoring sign.
367
                                auto magnitude_less = [](const einteger& x, const einteger& y) {
1,047,176✔
368
                                        if (x.limbs() != y.limbs()) return x.limbs() < y.limbs();
1,047,176✔
369
                                        for (size_t i = x.limbs(); i > 0; --i) {
1,045,577✔
370
                                                BlockType xb = x.block(i - 1);
1,044,550✔
371
                                                BlockType yb = y.block(i - 1);
1,044,550✔
372
                                                if (xb != yb) return xb < yb;
1,044,550✔
373
                                        }
374
                                        return false;
1,027✔
375
                                };
376
                                auto magnitude_equal = [](const einteger& x, const einteger& y) {
524,419✔
377
                                        if (x.limbs() != y.limbs()) return false;
524,419✔
378
                                        for (size_t i = x.limbs(); i > 0; --i)
524,322✔
379
                                                if (x.block(i - 1) != y.block(i - 1)) return false;
523,295✔
380
                                        return true;
1,027✔
381
                                };
382
                                if (magnitude_less(a, b))  { r = a; clear(); return; }
1,051,895✔
383
                                if (magnitude_equal(a, b)) {
524,419✔
384
                                        *this = 1;
1,027✔
385
                                        setsign(a.sign() ^ b.sign());
1,027✔
386
                                        r.clear();
1,027✔
387
                                        return;
1,027✔
388
                                }
389

390
                        // determine first non-zero limbs
391
                        unsigned m{ 0 }, n{ 0 };
523,392✔
392
                        for (size_t i = aBlocks; i > 0; --i) {
523,399✔
393
                                if (a._block[i - 1] != 0) {
523,399✔
394
                                        m = static_cast<unsigned>(i);
523,392✔
395
                                        break;
523,392✔
396
                                }
397
                        }
398
                        for (size_t i = bBlocks; i > 0; --i) {
523,392✔
399
                                if (b._block[i - 1] != 0) {
523,392✔
400
                                        n = static_cast<unsigned>(i);
523,392✔
401
                                        break;
523,392✔
402
                                }
403
                        }
404

405
                        // single limb divisor
406
                        if (n == 1) {
523,392✔
407
                                _block.resize(m);
3,692✔
408
                                std::uint64_t remainder{ 0 };
3,692✔
409
                                auto divisor = b.block(0);
3,692✔
410
                                for (unsigned j = m; j > 0; --j) {
13,243✔
411
                                        std::uint64_t dividend = remainder * BASE + static_cast<std::uint64_t>(a.block(j - 1));
9,551✔
412
                                        std::uint64_t limbQuotient = dividend / divisor;
9,551✔
413
                                        _block[j - 1] = static_cast<BlockType>(limbQuotient);
9,551✔
414
                                        remainder = dividend - limbQuotient * divisor;
9,551✔
415
                                }
416
                                remove_leading_zeros();
3,692✔
417
                                r.setblock(0, static_cast<BlockType>(remainder));
3,692✔
418
                                return;
3,692✔
419
                        }
420

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

425
                        int shift = nlz(b.block(n - 1));
519,700✔
426
                        einteger normalized_a;
519,700✔
427
                        normalized_a.setblock(m, static_cast<BlockType>((a.block(m - 1) >> (bitsInBlock - shift))));
519,700✔
428
                        for (unsigned i = m - 1; i > 0; --i) {
1,039,453✔
429
                                normalized_a.setblock(i, static_cast<BlockType>((a.block(i) << shift) | (a.block(i - 1) >> (bitsInBlock - shift))));
519,753✔
430
                        }
431
                        normalized_a.setblock(0, static_cast<BlockType>(a.block(0) << shift));
519,700✔
432
                        // normalize b
433
                        einteger normalized_b;
519,700✔
434
                        unsigned n_minus_1 = n - 1;
519,700✔
435
                        for (unsigned i = n_minus_1; i > 0; --i) {
1,039,421✔
436
                                normalized_b.setblock(i, static_cast<BlockType>((b.block(i) << shift) | (b.block(i - 1) >> (bitsInBlock - shift))));
519,721✔
437
                        }
438
                        normalized_b.setblock(0, static_cast<BlockType>(b.block(0) << shift));
519,700✔
439

440
                        //std::cout << "normalized a : " << normalized_a.showLimbs() << " : " << normalized_a.showLimbValues() << '\n';
441
                        //std::cout << "normalized b :             " << normalized_b.showLimbs() << " : " << normalized_b.showLimbValues() << '\n';
442

443
                        // divide by limb
444
                        std::uint64_t divisor = normalized_b._block[n - 1];
519,700✔
445
                        std::uint64_t v_nminus2 = normalized_b._block[n - 2]; // n > 1 at this point
519,700✔
446
                        for (int j = static_cast<int>(m - n); j >= 0; --j) {
1,039,432✔
447
                                std::uint64_t dividend = normalized_a.block(j + n) * BASE + normalized_a.block(j + n - 1);
519,732✔
448
                                std::uint64_t qhat = dividend / divisor;
519,732✔
449
                                std::uint64_t rhat = dividend - qhat * divisor;
519,732✔
450

451
                                while (qhat >= BASE || qhat * v_nminus2 > BASE * rhat + normalized_a.block(j + n - 2)) {
520,126✔
452
                                        --qhat;
394✔
453
                                        rhat += divisor;
394✔
454
                                        if (rhat < BASE) continue;
394✔
455
                                }
456
                                // Knuth Algorithm D, step D4 (multi-precision subtraction
457
                                // with borrow propagation). `diff` MUST be signed so that
458
                                // the arithmetic right shift `diff >> bitsInBlock` yields
459
                                // the -1/0 underflow indicator. With uint64_t the shift
460
                                // instead returns all-ones, and `p_hi - (diff >> shift)`
461
                                // wraps to a huge value that corrupts the next iteration
462
                                // and ultimately produces a quotient that is too large
463
                                // by exactly one limb (issue #842). The low-bits mask
464
                                // must also track bitsInBlock so the algorithm works for
465
                                // uint8_t and uint16_t blocks in addition to uint32_t.
466
                                constexpr std::uint64_t LOW_MASK = BASE - 1u;
519,732✔
467
                                std::int64_t borrow{ 0 };
519,732✔
468
                                std::int64_t diff{ 0 };
519,732✔
469
                                for (unsigned i = 0; i < n; ++i) {
1,559,285✔
470
                                        std::uint64_t p     = qhat * normalized_b.block(i);
1,039,553✔
471
                                        std::uint64_t p_lo  = p & LOW_MASK;
1,039,553✔
472
                                        std::uint64_t p_hi  = p >> bitsInBlock;
1,039,553✔
473
                                        diff = static_cast<std::int64_t>(normalized_a.block(i + j))
1,039,553✔
474
                                             - static_cast<std::int64_t>(p_lo)
1,039,553✔
475
                                             - borrow;
476
                                        normalized_a.setblock(i + j, static_cast<BlockType>(diff));
1,039,553✔
477
                                        // borrow_out = p_hi + (1 if underflow else 0). The
478
                                        // signed shift `diff >> bitsInBlock` yields -1 on
479
                                        // underflow and 0 otherwise, so subtracting it adds
480
                                        // 1 in the underflow case.
481
                                        borrow = static_cast<std::int64_t>(p_hi) - (diff >> bitsInBlock);
1,039,553✔
482
                                }
483
                                std::int64_t signedBorrow = static_cast<std::int64_t>(normalized_a.block(j + n)) - borrow;
519,732✔
484
                                normalized_a.setblock(j + n, static_cast<BlockType>(signedBorrow));
519,732✔
485

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

488
                                setblock(static_cast<unsigned>(j), static_cast<BlockType>(qhat));
519,732✔
489
                                if (signedBorrow < 0) { // subtracted too much, add back
519,732✔
490
                                        setblock(static_cast<size_t>(j), static_cast<BlockType>(_block[static_cast<size_t>(j)] - 1));
×
491
                                        std::uint64_t carry{ 0 };
×
492
                                        for (unsigned i = 0; i < n; ++i) {
×
493
                                                carry += static_cast<std::uint64_t>(normalized_a.block(i + j)) + static_cast<std::uint64_t>(normalized_b.block(i));
×
494
                                                normalized_a.setblock(i + j, static_cast<BlockType>(carry));
×
495
                                                carry >>= bitsInBlock;
×
496
                                        }
497
                                        BlockType rectified = static_cast<BlockType>(normalized_a.block(j + n) + carry);
×
498
                                        normalized_a.setblock(j + n, rectified);
×
499
                                }
500
                                //std::cout << "   updated a : " << normalized_a.showLimbs() << " : " << normalized_a.showLimbValues() << '\n';
501
                        }
502

503
                        // remainder needs to be normalized
504
                        for (unsigned i = 0; i < n - 1; ++i) {
1,039,421✔
505
                                std::uint64_t remainder = static_cast<std::uint64_t>(normalized_a.block(i) >> shift);
519,721✔
506
                                remainder |= (static_cast<std::uint64_t>(normalized_a.block(i + 1)) << (bitsInBlock - shift));
519,721✔
507
                                r.setblock(i, static_cast<BlockType>(remainder));
519,721✔
508
                        }
509
                        r.setblock(n - 1, static_cast<BlockType>(normalized_a.block(n - 1) >> shift));
519,700✔
510
                }
519,700✔
511
                remove_leading_zeros();
2,612,997✔
512
                _sign = a.sign() ^ b.sign();
2,612,997✔
513
        }
514

515
        // modifiers (vector::clear is constexpr in C++20; on the empty
516
        // constant-evaluated state both clear() and setzero() are trivially
517
        // constexpr-clean.  setsign writes only the bool member.)
518
        constexpr void clear() noexcept { _sign = false; _block.clear(); }
44,156,489✔
519
        constexpr void setzero() noexcept { clear(); }
530,073✔
520
        constexpr void setsign(bool sign = true) noexcept { _sign = sign; }
6,812,797✔
521
        // use un-interpreted raw bits to set the bits of the einteger
522
        void setbits(unsigned long long value) {
23,449,701✔
523
                clear();
23,449,701✔
524
                if constexpr (bitsInBlock == 8) {
525
                        std::uint8_t byte0 = static_cast<std::uint8_t>(value & 0x0000'0000'0000'00FF);
6,033,397✔
526
                        std::uint8_t byte1 = static_cast<std::uint8_t>((value & 0x0000'0000'0000'FF00) >> 8);
6,033,397✔
527
                        std::uint8_t byte2 = static_cast<std::uint8_t>((value & 0x0000'0000'00FF'0000) >> 16);
6,033,397✔
528
                        std::uint8_t byte3 = static_cast<std::uint8_t>((value & 0x0000'0000'FF00'0000) >> 24);
6,033,397✔
529
                        std::uint8_t byte4 = static_cast<std::uint8_t>((value & 0x0000'00FF'0000'0000) >> 32);
6,033,397✔
530
                        std::uint8_t byte5 = static_cast<std::uint8_t>((value & 0x0000'FF00'0000'0000) >> 40);
6,033,397✔
531
                        std::uint8_t byte6 = static_cast<std::uint8_t>((value & 0x00FF'0000'0000'0000) >> 48);
6,033,397✔
532
                        std::uint8_t byte7 = static_cast<std::uint8_t>((value & 0xFF00'0000'0000'0000) >> 56);
6,033,397✔
533
                        if (byte7 > 0) {
6,033,397✔
534
                                _block.push_back(byte0);
5✔
535
                                _block.push_back(byte1);
5✔
536
                                _block.push_back(byte2);
5✔
537
                                _block.push_back(byte3);
5✔
538
                                _block.push_back(byte4);
5✔
539
                                _block.push_back(byte5);
5✔
540
                                _block.push_back(byte6);
5✔
541
                                _block.push_back(byte7);
5✔
542
                        }
543
                        else if (byte6 > 0) {
6,033,392✔
544
                                _block.push_back(byte0);
6✔
545
                                _block.push_back(byte1);
6✔
546
                                _block.push_back(byte2);
6✔
547
                                _block.push_back(byte3);
6✔
548
                                _block.push_back(byte4);
6✔
549
                                _block.push_back(byte5);
6✔
550
                                _block.push_back(byte6);
6✔
551
                        }
552
                        else if (byte5 > 0) {
6,033,386✔
553
                                _block.push_back(byte0);
7✔
554
                                _block.push_back(byte1);
7✔
555
                                _block.push_back(byte2);
7✔
556
                                _block.push_back(byte3);
7✔
557
                                _block.push_back(byte4);
7✔
558
                                _block.push_back(byte5);
7✔
559
                        }
560
                        else if (byte4 > 0) {
6,033,379✔
561
                                _block.push_back(byte0);
6✔
562
                                _block.push_back(byte1);
6✔
563
                                _block.push_back(byte2);
6✔
564
                                _block.push_back(byte3);
6✔
565
                                _block.push_back(byte4);
6✔
566
                        }
567
                        else if (byte3 > 0) {
6,033,373✔
568
                                _block.push_back(byte0);
7✔
569
                                _block.push_back(byte1);
7✔
570
                                _block.push_back(byte2);
7✔
571
                                _block.push_back(byte3);
7✔
572
                        }
573
                        else if (byte2 > 0) {
6,033,366✔
574
                                _block.push_back(byte0);
523,820✔
575
                                _block.push_back(byte1);
523,820✔
576
                                _block.push_back(byte2);
523,820✔
577
                        }
578
                        else if (byte1 > 0) {
5,509,546✔
579
                                _block.push_back(byte0);
4,058,693✔
580
                                _block.push_back(byte1);
4,058,693✔
581
                        }
582
                        else if (byte0 > 0) {
1,450,853✔
583
                                _block.push_back(byte0);
1,447,230✔
584
                        }
585
                        else {
586
                                _block.clear();
3,623✔
587
                        }
588
                }
589
                else if constexpr (bitsInBlock == 16) {
590
                        std::uint16_t word0 = static_cast<std::uint16_t>( value & 0x0000'0000'0000'FFFF);
7,724,954✔
591
                        std::uint16_t word1 = static_cast<std::uint16_t>((value & 0x0000'0000'FFFF'0000) >> 16);
7,724,954✔
592
                        std::uint16_t word2 = static_cast<std::uint16_t>((value & 0x0000'FFFF'0000'0000) >> 32);
7,724,954✔
593
                        std::uint16_t word3 = static_cast<std::uint16_t>((value & 0xFFFF'0000'0000'0000) >> 48);
7,724,954✔
594
                        if (word3 > 0) {
7,724,954✔
595
                                _block.push_back(word0);
×
596
                                _block.push_back(word1);
×
597
                                _block.push_back(word2);
×
598
                                _block.push_back(word3);
×
599
                        }
600
                        else if (word2 > 0) {
7,724,954✔
601
                                _block.push_back(word0);
×
602
                                _block.push_back(word1);
×
603
                                _block.push_back(word2);
×
604
                        }
605
                        else if (word1 > 0) {
7,724,954✔
606
                                _block.push_back(word0);
588,839✔
607
                                _block.push_back(word1);
588,839✔
608
                        }
609
                        else if (word0 > 0) {
7,136,115✔
610
                                _block.push_back(word0);
7,132,268✔
611
                        }
612
                        else {
613
                                _block.clear();
3,847✔
614
                        }
615
                }
616
                else if constexpr (bitsInBlock == 32) {
617
                        std::uint32_t low  = static_cast<std::uint32_t>(value & 0x0000'0000'FFFF'FFFF);
9,691,350✔
618
                        std::uint32_t high = static_cast<std::uint32_t>((value & 0xFFFF'FFFF'0000'0000) >> bitsInBlock);
9,691,350✔
619
                        if (high > 0) {
9,691,350✔
620
                                _block.push_back(low);
587,344✔
621
                                _block.push_back(high);
587,344✔
622
                        }
623
                        else if (low > 0) {
9,104,006✔
624
                                _block.push_back(low);
9,099,392✔
625
                        }
626
                        else {
627
                                _block.clear();
4,614✔
628
                        }
629
                }
630
        }
23,449,701✔
631
        void setblock(unsigned i, BlockType value) noexcept {
6,239,140✔
632
                if (i >= _block.size()) _block.resize(i+1ull);
6,239,140✔
633
                _block[i] = value;
6,239,140✔
634
        }
6,239,140✔
635
        // Set the i-th byte (counting from the least-significant) of the value
636
        // while preserving the other bytes within the same limb. Grows the
637
        // limb vector if needed.
638
        void setbyte(unsigned i, std::uint8_t byte) noexcept {
39✔
639
                if constexpr (bitsInBlock == 8) {
640
                        setblock(i, static_cast<BlockType>(byte));
4✔
641
                }
642
                else {
643
                        constexpr unsigned bytesInBlock = sizeof(BlockType);
35✔
644
                        unsigned blockIndex      = i / bytesInBlock;
35✔
645
                        unsigned positionInBlock = i % bytesInBlock;
35✔
646
                        unsigned shiftAmount     = positionInBlock * 8u;
35✔
647
                        BlockType byteMask       = static_cast<BlockType>(static_cast<BlockType>(0xFFu) << shiftAmount);
35✔
648
                        BlockType existing       = (blockIndex < _block.size()) ? _block[blockIndex] : BlockType{};
35✔
649
                        BlockType updated        = static_cast<BlockType>((existing & ~byteMask)
35✔
650
                                                 | (static_cast<BlockType>(byte) << shiftAmount));
35✔
651
                        setblock(blockIndex, updated);
35✔
652
                }
653
        }
39✔
654
        einteger& assign(const std::string& txt) {
2✔
655
                if (!parse(txt, *this)) {
2✔
656
                        std::cerr << "Unable to parse: " << txt << std::endl;
×
657
                }
658
                return *this;
2✔
659
        }
660

661
        // selectors (read-only access to _sign and _block; constexpr-callable
662
        // on the empty default-constructed state and on any persisted
663
        // constexpr einteger -- which by definition has empty _block)
664
        constexpr bool iszero() const noexcept { return (_block.size() == 0 || ((_block.size() == 1) && _block[0] == bt(0))); }
6,949,917✔
665
        constexpr bool isone()  const noexcept { return true; }
666
        constexpr bool isodd()  const noexcept { return (_block.size() > 0) ? (_block[0] & 0x1) : false; }
667
        constexpr bool iseven() const noexcept { return !isodd(); }
668
        constexpr bool ispos()  const noexcept { return !_sign; }
669
        constexpr bool isneg()  const noexcept { return _sign; }
185✔
670

671
        constexpr bool test(unsigned index) const noexcept {
241,142,552✔
672
                if (index < nbits()) {
241,142,552✔
673
                        unsigned blockIndex = index / bitsInBlock;
241,142,552✔
674
                        unsigned bitIndexInBlock = index % bitsInBlock;
241,142,552✔
675
                        BlockType data = _block[blockIndex];
241,142,552✔
676
                        BlockType mask = (0x1u << bitIndexInBlock);
241,142,552✔
677
                        if (data & mask) return true;
241,142,552✔
678
                }
679
                return false;
186,817,975✔
680
        }
681
        constexpr bool sign()   const noexcept { return _sign; }
49,338,936✔
682
        constexpr int scale()   const noexcept { return findMsb(); } // TODO: when value = 0, scale returns -1 which is incorrect
683

684
        constexpr BlockType block(unsigned b) const noexcept {
14,004,340✔
685
                if (b < _block.size()) return _block[b];
14,004,340✔
686
                return static_cast<BlockType>(0u);
326,220✔
687
        }
688
        constexpr unsigned limbs() const noexcept { return static_cast<unsigned>(_block.size()); }
65,886,141✔
689

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

692
        // findMsb takes an einteger reference and returns the position of the most significant bit, -1 if v == 0
693
        constexpr int findMsb() const noexcept {
694
                int nrBlocks = static_cast<int>(_block.size());
695
                if (nrBlocks == 0) return -1; // no significant bit found, all bits are zero
696
                int msb = nrBlocks * static_cast<int>(bitsInBlock);
697
                for (int b = nrBlocks - 1; b >= 0; --b) {
698
                        // derive mask from the actual block width so this works for
699
                        // uint8_t / uint16_t / uint32_t instantiations (was hardcoded
700
                        // to 0x8000'0000ul, which only worked for uint32_t)
701
                        std::uint64_t segment = static_cast<std::uint64_t>(_block[static_cast<size_t>(b)]);
702
                        std::uint64_t mask = (std::uint64_t(1) << (bitsInBlock - 1));
703
                        for (int i = bitsInBlock - 1; i >= 0; --i) {
704
                                --msb;
705
                                if (segment & mask) return msb;
706
                                mask >>= 1;
707
                        }
708
                }
709
                return -1; // no significant bit found, all bits are zero
710
        }
711

712
        // convert to string containing digits number of digits
713
        std::string str(size_t nrDigits = 0) const {
714
                return std::string("tbd");
715
        }
716

717
        // show the binary encodings of the limbs
718
        std::string showLimbs() const {
719
                if (_block.empty()) return "no limbs";
720
                std::stringstream s;
721
                size_t i = _block.size() - 1;
722
                while (i > 0) {
723
                        s << to_binary(_block[i], sizeof(BlockType) * 8, true) << ' ';
724
                        --i;
725
                }
726
                s << to_binary(_block[0], sizeof(BlockType) * 8, true);
727
                return s.str();
728
        }
729
        // show the values of the limbs as a radix-BlockType number
730
        std::string showLimbValues() const {
731
                if (_block.empty()) return "no limbs";
732
                std::stringstream s;
733
                size_t i = _block.size() - 1;
734
                while (i > 0) {
735
                        s << std::setw(5) << unsigned(_block[i]) << ", ";
736
                        --i;
737
                }
738
                s << std::setw(5) << unsigned(_block[0]);
739
                return s.str();
740
        }
741

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

746
        // HELPER methods
747
        // compare_magnitude returns 1 if a > b, 0 if they are equal, and -1 if a < b
748
        int compare_magnitude(const einteger& a, const einteger& b) {
4,320,770✔
749
                unsigned aLimbs = a.limbs();
4,320,770✔
750
                unsigned bLimbs = b.limbs();
4,320,770✔
751
                if (aLimbs != bLimbs) {
4,320,770✔
752
                        return (aLimbs > bLimbs ? 1 : -1);  // return 1 if a > b, otherwise -1
396,283✔
753
                }
754
                for (int i = static_cast<int>(aLimbs) - 1; i >= 0; --i) {
4,125,697✔
755
                        BlockType _a = a._block[static_cast<size_t>(i)];
4,121,095✔
756
                        BlockType _b = b._block[static_cast<size_t>(i)];
4,121,095✔
757
                        if ( _a != _b) {
4,121,095✔
758
                                return (_a > _b ? 1 : -1);
3,919,885✔
759
                        }
760
                }
761
                return 0;
4,602✔
762
        }
763
        void remove_leading_zeros() {
6,937,570✔
764
                unsigned leadingZeroBlocks{ 0 };
6,937,570✔
765
                typename std::vector<BlockType>::reverse_iterator rit = _block.rbegin();
6,937,570✔
766
                while (rit != _block.rend()) {
7,336,738✔
767
                        if (*rit == 0) {
6,286,443✔
768
                                ++leadingZeroBlocks;
399,168✔
769
                        }
770
                        else {
771
                                break;
5,887,275✔
772
                        }
773
                        ++rit;
399,168✔
774
                }
775
                _block.resize(_block.size() - leadingZeroBlocks);
6,937,570✔
776
        }
6,937,570✔
777
        
778
        template<typename SignedInt>
779
        einteger& convert_signed(SignedInt v) {
13,037,200✔
780
                clear();
13,037,200✔
781
                if (v != 0) {
13,037,200✔
782
                        if (v < 0) {
10,928,506✔
783
                                setbits(static_cast<unsigned long long>(-v));
2,160,388✔
784
                                setsign(true);
2,160,388✔
785
                        }
786
                        else {
787
                                setbits(static_cast<unsigned long long>(v)); // TODO: what about -2^63
8,768,118✔
788
                        }
789
                }
790
                return *this;
13,037,200✔
791
        }
792

793
        template<typename UnsignedInt>
794
        einteger& convert_unsigned(UnsignedInt v) {
2,094,063✔
795
                if (0 == v) {
2,094,063✔
796
                        setzero();
530,073✔
797
                }
798
                else {
799
                        setbits(static_cast<unsigned long long>(v));
1,563,990✔
800
                }
801
                return *this;
2,094,063✔
802
        }
803

804
        template<typename Real>
805
        einteger& convert_ieee754(Real& rhs) {
118✔
806
                clear();
118✔
807
                bool s{ false };
118✔
808
                std::uint64_t rawExponent{ 0 };
118✔
809
                std::uint64_t rawFraction{ 0 };
118✔
810
                uint64_t bits{ 0 };
118✔
811
                extractFields(rhs, s, rawExponent, rawFraction, bits);
118✔
812
                if (rawExponent == ieee754_parameter<Real>::eallset) { // nan and inf
118✔
813
                        // we can't represent NaNs or Infinities
814
                        return *this;
3✔
815
                }
816
                int exponent = static_cast<int>(rawExponent) - ieee754_parameter<Real>::bias;
115✔
817
                if (exponent < 0) {
115✔
818
                        return *this; // we are zero
×
819
                }
820
                // normal and subnormal handling
821
                constexpr size_t fbits = ieee754_parameter<Real>::fbits;
115✔
822
                std::uint64_t hiddenBit = (0x1ull << fbits);
115✔
823
                rawFraction |= hiddenBit;
115✔
824
                setbits(rawFraction);
115✔
825
                setsign(s);
115✔
826
                // scale the fraction bits
827
                *this <<= static_cast<int>(exponent - fbits);
115✔
828
                return *this;
115✔
829
        }
830

831
        template<typename Integer>
832
        Integer convert_to_native_integer() const noexcept {
10,957,141✔
833
                using Unsigned = std::make_unsigned_t<Integer>;
834
                Unsigned v{ 0 };
10,957,141✔
835
                Unsigned m{ 1 };
10,957,141✔
836
                const bool neg = sign();
10,957,141✔
837
                constexpr Integer kMax = std::numeric_limits<Integer>::max();
10,957,141✔
838
                constexpr Integer kMin = std::numeric_limits<Integer>::min();
10,957,141✔
839
                constexpr unsigned kDigits = std::numeric_limits<Unsigned>::digits;
10,957,141✔
840
                for (unsigned i = 0; i < nbits(); ++i) {
252,091,045✔
841
                        if (i >= kDigits) {
241,133,904✔
842
                                if (test(i)) return neg ? kMin : kMax;
×
843
                                continue;
×
844
                        }
845
                        if (test(i)) {
241,133,904✔
846
                                if (v > (static_cast<Unsigned>(kMax) - m)) {
54,323,308✔
847
                                        return neg ? kMin : kMax;
×
848
                                }
849
                                v += m;
54,323,308✔
850
                        }
851
                        m <<= 1;
241,133,904✔
852
                }
853
                return (neg ? -static_cast<Integer>(v) : static_cast<Integer>(v));
10,957,141✔
854
        }
855
        template<typename Real>
856
        Real convert_to_native_ieee() const noexcept {
117✔
857
                Real v{ 0 };
117✔
858
                Real m{ 1.0 };
117✔
859
                for (unsigned i = 0; i < nbits(); ++i) {
8,765✔
860
                        if (test(i)) {
8,648✔
861
                                v += m;
1,269✔
862
                        }
863
                        m *= Real(2.0);
8,648✔
864
                }
865
                return (sign() ? -v : v);
117✔
866
        }
867

868
private:
869

870
        template<typename BBlockType>
871
        friend constexpr bool operator==(const einteger<BBlockType>&, const einteger<BBlockType>&) noexcept;
872
};
873

874
////////////////////////    einteger functions   /////////////////////////////////
875

876
template<typename BlockType>
877
inline einteger<BlockType> abs(const einteger<BlockType>& a) {
878
        return (a.isneg()  ? -a : a);
879
}
880

881
////////////////////////    INTEGER operators   /////////////////////////////////
882

883
/// stream operators
884

885
// read a einteger ASCII format and make a binary einteger out of it
886
template<typename BlockType>
887
bool parse(const std::string& number, einteger<BlockType>& value) {
55✔
888
        using Integer = einteger<BlockType>;
889
        bool bSuccess = false;
55✔
890
        value.clear();
55✔
891
        std::regex binary_regex("^[-+]*0b[01']+");
55✔
892
        // check if the txt is an integer form: [0123456789]+
893
        std::regex decimal_regex("^[-+]*[0-9]+");
55✔
894
        std::regex octal_regex("^[-+]*0[1-7][0-7]*$");
55✔
895
        std::regex hex_regex("^[-+]*0[xX][0-9a-fA-F']+");
55✔
896
        // setup associative array to map chars to nibbles
897
        std::map<char, int> charLookup{
110✔
898
                { '0', 0 },
899
                { '1', 1 },
900
                { '2', 2 },
901
                { '3', 3 },
902
                { '4', 4 },
903
                { '5', 5 },
904
                { '6', 6 },
905
                { '7', 7 },
906
                { '8', 8 },
907
                { '9', 9 },
908
                { 'a', 10 },
909
                { 'b', 11 },
910
                { 'c', 12 },
911
                { 'd', 13 },
912
                { 'e', 14 },
913
                { 'f', 15 },
914
                { 'A', 10 },
915
                { 'B', 11 },
916
                { 'C', 12 },
917
                { 'D', 13 },
918
                { 'E', 14 },
919
                { 'F', 15 },
920
        };
921
        if (std::regex_match(number, octal_regex)) {
55✔
922
                // Format: [+-]*0[1-7][0-7]*  (C-style octal, no separators).
923
                // Walk left-to-right: skip sign(s), skip the leading '0', then
924
                // accumulate value = value * 8 + digit.
925
                bool sign = false;
9✔
926
                std::string::size_type pos = 0;
9✔
927
                while (pos < number.size() && (number[pos] == '+' || number[pos] == '-')) {
10✔
928
                        if (number[pos] == '-') sign = !sign;
1✔
929
                        ++pos;
1✔
930
                }
931
                // regex guarantees a leading '0' followed by an octal digit
932
                ++pos;
9✔
933
                for (; pos < number.size(); ++pos) {
45✔
934
                        value *= 8LL;
36✔
935
                        value += static_cast<long long>(number[pos] - '0');
36✔
936
                }
937
                value.setsign(sign && !value.iszero());
9✔
938
                bSuccess = true;
9✔
939
        }
940
        else if (std::regex_match(number, hex_regex)) {
46✔
941
                //std::cout << "found a hexadecimal representation\n";
942
                // each char is a nibble
943
                int byte = 0;
12✔
944
                int byteIndex = 0;
12✔
945
                bool odd = false;
12✔
946
                for (std::string::const_reverse_iterator r = number.rbegin();
12✔
947
                        r != number.rend();
96✔
948
                        ++r) {
84✔
949
                        if (*r == '\'') {
96✔
950
                                // ignore
951
                        }
952
                        else if (*r == 'x' || *r == 'X') {
89✔
953
                                if (odd) {
12✔
954
                                        // complete the most significant byte
955
                                        value.setbyte(static_cast<size_t>(byteIndex), static_cast<uint8_t>(byte));
1✔
956
                                }
957
                                // check that we have [-+]0[xX] format
958
                                ++r;
12✔
959
                                if (r != number.rend()) {
12✔
960
                                        if (*r == '0') {
12✔
961
                                                // check if we have a sign
962
                                                ++r;
12✔
963
                                                if (r == number.rend()) {
12✔
964
                                                        // no sign, thus by definition positive
965
                                                        bSuccess = true;
9✔
966
                                                }
967
                                                else if (*r == '+') {
3✔
968
                                                        // optional positive sign, no further action necessary
969
                                                        bSuccess = true;
1✔
970
                                                }
971
                                                else if (*r == '-') {
2✔
972
                                                        // negative sign, invert
973
                                                        value = -value;
2✔
974
                                                        bSuccess = true;
2✔
975
                                                }
976
                                                else {
977
                                                        // the regex will have filtered this out
978
                                                        bSuccess = false;
×
979
                                                }
980
                                        }
981
                                        else {
982
                                                // we didn't find the obligatory '0', the regex should have filtered this out
983
                                                bSuccess = false;
×
984
                                        }
985
                                }
986
                                else {
987
                                        // we are missing the obligatory '0', the regex should have filtered this out
988
                                        bSuccess = false;
×
989
                                }
990
                                // we have reached the end of our parse
991
                                break;
12✔
992
                        }
993
                        else {
994
                                if (odd) {
77✔
995
                                        byte += charLookup.at(*r) << 4;
38✔
996
                                        value.setbyte(static_cast<size_t>(byteIndex), static_cast<uint8_t>(byte));
38✔
997
                                        ++byteIndex;
38✔
998
                                }
999
                                else {
1000
                                        byte = charLookup.at(*r);
39✔
1001
                                }
1002
                                odd = !odd;
77✔
1003
                        }
1004
                }
1005
        }
1006
        else if (std::regex_match(number, decimal_regex)) {
34✔
1007
                //std::cout << "found a decimal integer representation\n";
1008
                Integer scale = 1;
9✔
1009
                bool sign{ false };
9✔
1010
                for (std::string::const_reverse_iterator r = number.rbegin();
9✔
1011
                        r != number.rend();
109✔
1012
                        ++r) {
100✔
1013
                        if (*r == '-') {
101✔
1014
                                sign = true;;
2✔
1015
                        }
1016
                        else if (*r == '+') {
99✔
1017
                                break;
1✔
1018
                        }
1019
                        else {
1020
                                Integer digit = charLookup.at(*r);
98✔
1021
                                value += scale * digit;
98✔
1022
                                scale *= 10;
98✔
1023
                        }
98✔
1024
                }
1025
                value.setsign(sign);
9✔
1026
                bSuccess = true;
9✔
1027
        }
9✔
1028
        else if (std::regex_match(number, binary_regex)) {
25✔
1029
                // '0b' prefix is at positions [0..2) after an optional leading sign.
1030
                // We walk the digits left-to-right, accumulating value = value*2 + bit.
1031
                // Apostrophes are digit-group separators and are ignored.
1032
                bool sign = false;
11✔
1033
                std::string::size_type pos = 0;
11✔
1034
                while (pos < number.size() && (number[pos] == '+' || number[pos] == '-')) {
12✔
1035
                        if (number[pos] == '-') sign = !sign;
1✔
1036
                        ++pos;
1✔
1037
                }
1038
                // regex guarantees '0b' present after the sign run
1039
                pos += 2;
11✔
1040
                for (; pos < number.size(); ++pos) {
245✔
1041
                        char c = number[pos];
234✔
1042
                        if (c == '\'') continue;
234✔
1043
                        value *= 2LL;
211✔
1044
                        if (c == '1') value += 1LL;
211✔
1045
                }
1046
                value.setsign(sign && !value.iszero());
11✔
1047
                bSuccess = true;
11✔
1048
        }
1049
        return bSuccess;
55✔
1050
}
55✔
1051

1052
template<typename BlockType>
1053
std::string convert_to_string(std::ios_base::fmtflags flags, const einteger<BlockType>& n) {
192✔
1054
        using AdaptiveInteger = einteger<BlockType>;
1055

1056
        if (n.limbs() == 0) return std::string("0");
206✔
1057

1058
        // set the base of the target number system to convert to
1059
        int base = 10;
185✔
1060
        if ((flags & std::ios_base::oct) == std::ios_base::oct) base = 8;
185✔
1061
        if ((flags & std::ios_base::hex) == std::ios_base::hex) base = 16;
185✔
1062

1063
        unsigned nbits = n.limbs() * sizeof(BlockType) * 8;
185✔
1064

1065
        std::string result;
185✔
1066
        if (base == 8 || base == 16) {
185✔
1067
                if (n.sign()) return std::string("negative value: ignored");
×
1068

1069
                size_t shift = (base == 8 ? 3ull : 4ull);
×
1070
                BlockType mask = static_cast<BlockType>((1u << shift) - 1);
×
1071
                AdaptiveInteger t(n);
×
1072
                result.assign(nbits / shift + ((nbits % shift) ? 1 : 0), '0');
×
1073
                size_t pos = result.size() - 1ull;
×
1074
                for (size_t i = 0; i < nbits / shift; ++i) {
×
1075
                        char c = '0' + static_cast<char>(t.block(0) & mask);
×
1076
                        if (c > '9')
×
1077
                                c += 'A' - '9' - 1;
×
1078
                        result[pos--] = c;
×
1079
                        t >>= static_cast<int>(shift);
×
1080
                }
1081
                if (nbits % shift) {
×
1082
                        mask = static_cast<BlockType>((1u << (nbits % shift)) - 1);
×
1083
                        char c = '0' + static_cast<char>(t.block(0) & mask);
×
1084
                        if (c > '9')
×
1085
                                c += 'A' - '9';
×
1086
                        result[pos] = c;
×
1087
                }
1088
                //
1089
                // Get rid of leading zeros:
1090
                //
1091
                std::string::size_type fnz = result.find_first_not_of('0');
×
1092
                if (!result.empty() && (fnz == std::string::npos)) fnz = result.size() - 1;
×
1093
                result.erase(0, fnz);
×
1094
                if (flags & std::ios_base::showbase) {
×
1095
                        const char* pp = base == 8 ? "0" : "0x";
×
1096
                        result.insert(static_cast<std::string::size_type>(0), pp);
×
1097
                }
1098
        }
×
1099
        else {
1100
                unsigned block10;
1101
                unsigned digits_in_block10;
1102
                if constexpr (AdaptiveInteger::bitsInBlock == 8) {
1103
                        block10 = 100u;
71✔
1104
                        digits_in_block10 = 2;
71✔
1105
                }
1106
                else if constexpr (AdaptiveInteger::bitsInBlock == 16) {
1107
                        block10 = 10'000ul;
45✔
1108
                        digits_in_block10 = 4;
45✔
1109
                }
1110
                else if constexpr (AdaptiveInteger::bitsInBlock == 32) {
1111
                        block10 = 1'000'000'000ul;
69✔
1112
                        digits_in_block10 = 9;
69✔
1113
                }
1114
                else if constexpr (AdaptiveInteger::bitsInBlock == 64) {
1115
                        // not allowed as the whole multi-digit arithmetic
1116
                        // requires that there is a 'larger' type that
1117
                        // can receive carries and borrows.
1118
                        // If your platform does have a native 128bit
1119
                        // integer, this could be enabled
1120
                        //block10 = 1'000'000'000'000'000'000ull;
1121
                        //digits_in_block10 = 18;
1122
                }
1123
                result.assign(nbits / 3 + 1ull, '0');
185✔
1124
                size_t pos = result.size() - 1ull;
185✔
1125
                AdaptiveInteger t(n);
185✔
1126
                while (!t.iszero()) {
1,891✔
1127
                        AdaptiveInteger q,r;
853✔
1128
                        q.reduce(t, block10, r);
853✔
1129
                        BlockType v = r.block(0);
853✔
1130
//                        std::cout << "v  " << uint32_t(v) << '\n';
1131
                        for (unsigned i = 0; i < digits_in_block10; ++i) {
4,004✔
1132
                                char c = '0' + static_cast<char>(v % 10);
3,176✔
1133
                                v /= 10;
3,176✔
1134
                                result[pos] = c;
3,176✔
1135
//                                std::cout << result << " pos: " << pos << '\n';
1136
                                if (pos-- == 0)        break;
3,176✔
1137
                        }
1138
                        t = q;
853✔
1139
                }
1140

1141
                std::string::size_type firstDigit = result.find_first_not_of('0');
185✔
1142
                result.erase(0, firstDigit);
185✔
1143
                if (result.empty())
185✔
1144
                        result = "0";
2✔
1145
                if (n.isneg())
185✔
1146
                        result.insert(0ull, 1ull, '-');
58✔
1147
                else if (flags & std::ios_base::showpos)
127✔
1148
                        result.insert(0ull, 1ull, '+');
×
1149
        }
185✔
1150
        return result;
185✔
1151
}
185✔
1152

1153
// generate an einteger format ASCII format
1154
template<typename BlockType>
1155
inline std::ostream& operator<<(std::ostream& ostr, const einteger<BlockType>& i) {
192✔
1156
        std::string s = convert_to_string(ostr.flags(), i);
192✔
1157
        std::streamsize width = ostr.width();
192✔
1158
        if (width > static_cast<std::streamsize>(s.size())) {
192✔
1159
                char fill = ostr.fill();
×
1160
                if ((ostr.flags() & std::ios_base::left) == std::ios_base::left)
×
1161
                        s.append(static_cast<std::string::size_type>(width - s.size()), fill);
×
1162
                else
1163
                        s.insert(static_cast<std::string::size_type>(0), static_cast<std::string::size_type>(width - s.size()), fill);
×
1164
        }
1165
        return ostr << s;
384✔
1166
}
192✔
1167

1168
// read an ASCII einteger format
1169

1170
template<typename BlockType>
1171
inline std::istream& operator>>(std::istream& istr, einteger<BlockType>& p) {
4✔
1172
        std::string txt;
4✔
1173
        if (!(istr >> txt)) {
4✔
1174
                // extraction failed (already-bad stream or EOF); failbit set by >>.
1175
                return istr;
1✔
1176
        }
1177
        if (!parse(txt, p)) {
3✔
1178
                std::cerr << "unable to parse -" << txt << "- into an einteger value\n";
1✔
1179
                istr.setstate(std::ios::failbit);
1✔
1180
        }
1181
        return istr;
3✔
1182
}
4✔
1183

1184
////////////////// string operators
1185

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

1190
        std::stringstream s;
118✔
1191
        s << "0b";
118✔
1192
        for (int b = static_cast<int>(a.limbs()) - 1; b >= 0; --b) {
738✔
1193
                BlockType segment = a.block(static_cast<size_t>(b));
620✔
1194
                BlockType mask = (0x1u << (a.bitsInBlock - 1));
620✔
1195
                for (int i = a.bitsInBlock - 1; i >= 0; --i) {
9,444✔
1196
                        s << ((segment & mask) ? '1' : '0');
8,824✔
1197
                        if (i > 0 && (i % 4) == 0 && nibbleMarker) s << '\'';
8,824✔
1198
                        if (b > 0 && i == 0 && nibbleMarker) s << '\'';
8,824✔
1199
                        mask >>= 1;
8,824✔
1200
                }
1201
        }
1202

1203
        return s.str();
118✔
1204
}
118✔
1205

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

1210
        std::vector<char> nibbleLookup = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
6✔
1211
        std::stringstream s;
3✔
1212
        s << "0x";
3✔
1213
        unsigned bitIndex = a.limbs() * a.bitsInBlock - 1u;
3✔
1214
        for (int b = static_cast<int>(a.limbs()) - 1; b >= 0; --b) {
12✔
1215
                BlockType limb = a.block(static_cast<size_t>(b));
9✔
1216
                BlockType mask = (0x1u << (a.bitsInBlock - 1));
9✔
1217
                unsigned nibble{ 0 };
9✔
1218
                unsigned rightShift = a.bitsInBlock - 4u;
9✔
1219
                for (int i = a.bitsInBlock - 1; i >= 0; --i) {
153✔
1220

1221
                        nibble |= (limb & mask);
144✔
1222
                        if ((i % 4) == 0) {
144✔
1223
                                nibble >>= rightShift;
36✔
1224
                                s << nibbleLookup[nibble];
36✔
1225
                                nibble = 0;
36✔
1226
                                rightShift -= 4u;
36✔
1227
                        }
1228
                        if (bitIndex > 0 && ((bitIndex % 16) == 0) && wordMarker) s << '\'';
144✔
1229
                        mask >>= 1;
144✔
1230
                        --bitIndex;
144✔
1231
                }
1232
        }
1233

1234
        return s.str();
3✔
1235

1236
}
3✔
1237

1238
//////////////////////////////////////////////////////////////////////////////////////////////////////
1239
// einteger - einteger binary logic operators
1240

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

1245
template<typename BlockType>
1246
constexpr bool operator==(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) noexcept {
10,941,957✔
1247
        if (lhs.limbs() != rhs.limbs()) {
10,941,957✔
UNCOV
1248
                return false;
×
1249
        }
1250
        if (lhs.sign() != rhs.sign()) {
10,941,957✔
1251
                // keep +0 == -0, but require sign match for non-zero values
1252
                return lhs.iszero() && rhs.iszero();
9✔
1253
        }
1254
        for (unsigned i = 0; i < lhs.limbs(); ++i) {
23,709,559✔
1255
                if (lhs._block[i] != rhs._block[i]) return false;
12,767,611✔
1256
        }
1257
        return true;
10,941,948✔
1258
}
1259

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

1265
template<typename BlockType>
1266
constexpr bool operator< (const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) noexcept {
6✔
1267
        const bool ls = lhs.sign();
6✔
1268
        const bool rs = rhs.sign();
6✔
1269
        if (ls != rs) {
6✔
1270
                // +0 and -0 are equal, not ordered
1271
                if (lhs.iszero() && rhs.iszero()) return false;
4✔
1272
                return ls; // negative < positive
4✔
1273
        }
1274
        unsigned ll = lhs.limbs();
2✔
1275
        unsigned rl = rhs.limbs();
2✔
1276
        // for negatives, larger magnitude means smaller value -- swap the sense
1277
        if (ll != rl) return ls ? (ll > rl) : (ll < rl);
2✔
1278
        if (ll == 0) return false; // both empty: equal in magnitude
2✔
1279
        for (unsigned b = ll; b-- > 0;) {
2✔
1280
                BlockType l = lhs.block(b);
2✔
1281
                BlockType r = rhs.block(b);
2✔
1282
                if (l == r) continue;
2✔
1283
                return ls ? (l > r) : (l < r);
2✔
1284
        }
UNCOV
1285
        return false; // lhs and rhs are the same
×
1286
}
1287

1288
template<typename BlockType>
1289
constexpr bool operator> (const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) noexcept {
2✔
1290
        return operator< (rhs, lhs);
2✔
1291
}
1292

1293
template<typename BlockType>
1294
constexpr bool operator<=(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) noexcept {
1✔
1295
        return operator< (lhs, rhs) || operator==(lhs, rhs);
1✔
1296
}
1297

1298
template<typename BlockType>
1299
constexpr bool operator>=(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) noexcept {
1✔
1300
        return !operator< (lhs, rhs);
1✔
1301
}
1302

1303
//////////////////////////////////////////////////////////////////////////////////////////////////////
1304
// einteger - literal binary logic operators
1305
// equal: precondition is that the byte-storage is properly nulled in all arithmetic paths
1306

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

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

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

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

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

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

1337
//////////////////////////////////////////////////////////////////////////////////////////////////////
1338
// literal - einteger binary logic operators
1339
// precondition is that the byte-storage is properly nulled in all arithmetic paths
1340

1341

1342
template<typename BlockType>
1343
inline bool operator==(long long lhs, const einteger<BlockType>& rhs) {
1344
        return operator==(einteger<BlockType>(lhs), rhs);
1345
}
1346

1347
template<typename BlockType>
1348
inline bool operator!=(long long lhs, const einteger<BlockType>& rhs) {
1349
        return !operator==(lhs, rhs);
1350
}
1351

1352
template<typename BlockType>
1353
inline bool operator< (long long lhs, const einteger<BlockType>& rhs) {
1354
        return operator<(einteger<BlockType>(lhs), rhs);
1355
}
1356

1357
template<typename BlockType>
1358
inline bool operator> (long long lhs, const einteger<BlockType>& rhs) {
1359
        return operator< (rhs, lhs);
1360
}
1361

1362
template<typename BlockType>
1363
inline bool operator<=(long long lhs, const einteger<BlockType>& rhs) {
1364
        return operator< (lhs, rhs) || operator==(lhs, rhs);
1365
}
1366

1367
template<typename BlockType>
1368
inline bool operator>=(long long lhs, const einteger<BlockType>& rhs) {
1369
        return !operator< (lhs, rhs);
1370
}
1371

1372
//////////////////////////////////////////////////////////////////////////////////////////////////////
1373

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

1377
template<typename BlockType>
1378
inline einteger<BlockType> operator+(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) {
3,145,986✔
1379
        einteger sum = lhs;
3,145,986✔
1380
        sum += rhs;
3,145,986✔
1381
        return sum;
3,145,986✔
1382
}
×
1383

1384
template<typename BlockType>
1385
inline einteger<BlockType> operator-(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) {
4,325,378✔
1386
        einteger diff = lhs;
4,325,378✔
1387
        diff -= rhs;
4,325,378✔
1388
        return diff;
4,325,378✔
1389
}
×
1390

1391
template<typename BlockType>
1392
inline einteger<BlockType> operator*(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) {
328,036✔
1393
        einteger product = lhs;
328,036✔
1394
        product *= rhs;
328,036✔
1395
        return product;
328,036✔
1396
}
×
1397

1398
template<typename BlockType>
1399
inline einteger<BlockType> operator/(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) {
2✔
1400
        einteger ratio = lhs;
2✔
1401
        ratio /= rhs;
2✔
1402
        return ratio;
2✔
1403
}
×
1404

1405
template<typename BlockType>
1406
inline einteger<BlockType> operator%(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) {
2✔
1407
        einteger remainder = lhs;
2✔
1408
        remainder %= rhs;
2✔
1409
        return remainder;
2✔
1410
}
×
1411

1412
//////////////////////////////////////////////////////////////////////////////////////////////////////
1413
// einteger - literal binary arithmetic operators
1414

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

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

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

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

1435
template<typename BlockType>
1436
inline einteger<BlockType> operator%(const einteger<BlockType>& lhs, long long rhs) {
1437
        return operator/(lhs, einteger<BlockType>(rhs));
1438
}
1439

1440
template<typename BlockType>
1441
inline einteger<BlockType> operator/(const einteger<BlockType>& lhs, unsigned long long rhs) {
1442
        return operator/(lhs, einteger<BlockType>(rhs));
1443
}
1444

1445
//////////////////////////////////////////////////////////////////////////////////////////////////////
1446
// literal - einteger binary arithmetic operators
1447

1448
template<typename BlockType>
1449
inline einteger<BlockType> operator+(long long lhs, const einteger<BlockType>& rhs) {
1450
        return operator+(einteger<BlockType>(lhs), rhs);
1451
}
1452

1453
template<typename BlockType>
1454
inline einteger<BlockType> operator-(long long lhs, const einteger<BlockType>& rhs) {
1455
        return operator-(einteger<BlockType>(lhs), rhs);
1456
}
1457

1458
template<typename BlockType>
1459
inline einteger<BlockType> operator*(long long lhs, const einteger<BlockType>& rhs) {
1460
        return operator*(einteger<BlockType>(lhs), rhs);
1461
}
1462

1463
template<typename BlockType>
1464
inline einteger<BlockType> operator/(long long lhs, const einteger<BlockType>& rhs) {
1465
        return operator/(einteger<BlockType>(lhs), rhs);
1466
}
1467

1468
template<typename BlockType>
1469
inline einteger<BlockType> operator%(long long lhs, const einteger<BlockType>& rhs) {
1470
        return operator/(einteger<BlockType>(lhs), rhs);
1471
}
1472

1473
}} // 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