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

stillwater-sc / universal / 26381803614

25 May 2026 03:35AM UTC coverage: 84.028% (+0.02%) from 84.008%
26381803614

Pull #993

github

web-flow
Merge 7c2dc4724 into 9c3e26d09
Pull Request #993: fix(erational): correct ieee754 conversion (both directions) + add tests (#986)

73 of 76 new or added lines in 1 file covered. (96.05%)

64 existing lines in 3 files now uncovered.

47438 of 56455 relevant lines covered (84.03%)

8737173.17 hits per line

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

91.87
/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,452,983✔
51
        constexpr einteger(einteger&&) = default;
52

53
        constexpr einteger& operator=(const einteger&) = default;
523,683✔
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; }
34,480✔
59
        einteger(long initial_value)               { *this = initial_value; }
450,056✔
60
        einteger(long long initial_value)          { *this = initial_value; }
660✔
61
        einteger(unsigned int initial_value)       { *this = initial_value; }
919✔
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,128,903✔
69
        einteger& operator=(long rhs)               noexcept { return convert_signed(rhs); }
11,392,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,135✔
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) {
34,483✔
92
                if (shift == 0) return *this;
34,483✔
93
                if (shift < 0) return operator>>=(-shift);
34,102✔
94

95
                // by default extend the limbs by 1: TODO: can this be improved?
96
                _block.push_back(0);
34,084✔
97
                size_t MSU = _block.size() - 1;
34,084✔
98
                if (shift >= static_cast<int>(bitsInBlock)) {
34,084✔
99
                        int blockShift = shift / static_cast<int>(bitsInBlock);
29,541✔
100
                        if (blockShift > 0) _block.resize(_block.size() + blockShift, 0ul);
29,541✔
101
                        MSU = _block.size() - 1;
29,541✔
102
                        for (int i = static_cast<int>(MSU); i >= blockShift; --i) {
88,718✔
103
                                _block[static_cast<size_t>(i)] = _block[static_cast<size_t>(i) - static_cast<size_t>(blockShift)];
59,177✔
104
                        }
105
                        for (int i = blockShift - 1; i >= 0; --i) {
127,388✔
106
                                _block[static_cast<size_t>(i)] = BlockType(0);
97,847✔
107
                        }
108
                        // adjust the shift
109
                        shift -= static_cast<int>(blockShift * bitsInBlock);
29,541✔
110
                        // when the shift is an exact multiple of the block width there is no
111
                        // intra-block shift left to do; trim the speculative high limb from
112
                        // the push_back(0) above before returning, so the result stays
113
                        // canonical (the trailing path below cannot run here -- a shift of 0
114
                        // would form a (<< bitsInBlock) mask, which is undefined).
115
                        if (shift == 0) { remove_leading_zeros(); return *this; }
29,541✔
116
                }
117
                if (MSU > 0) {
32,841✔
118
                        // construct the mask for the upper bits in the block that needs to move to the higher word
119
                        BlockType mask = static_cast<BlockType>(0xFFFF'FFFFul << (bitsInBlock - shift));
32,841✔
120
                        for (size_t i = MSU; i > 0; --i) {
158,927✔
121
                                _block[static_cast<size_t>(i)] <<= shift;
126,086✔
122
                                // mix in the bits from the right
123
                                BlockType bits = BlockType(mask & _block[i - 1]);
126,086✔
124
                                _block[static_cast<size_t>(i)] |= (bits >> (bitsInBlock - shift));
126,086✔
125
                        }
126
                        _block[0] <<= shift;
32,841✔
127
                }
128
                else {
UNCOV
129
                        _block[0] <<= shift;
×
130
                }
131
                remove_leading_zeros();
32,841✔
132
                return *this;
32,841✔
133
        }
134
        einteger& operator>>=(int shift) {
32✔
135
                if (shift == 0) return *this;
32✔
136
                if (shift < 0) return operator>>=(-shift);
32✔
137
                // shift == nbits shifts ALL significant bits out, so the result is
138
                // 0.  The previous `>` test let this case fall through to the
139
                // block-shift path with MSU < blockShift, where the inner copy
140
                // loop is skipped and the original limbs survive unchanged.
141
                if (shift >= static_cast<int>(nbits())) {
32✔
142
                        setzero();
5✔
143
                        return *this;
5✔
144
                }
145
                size_t MSU = _block.size() - 1;
27✔
146
                size_t blockShift = 0;
27✔
147
                if (shift >= static_cast<int>(bitsInBlock)) {
27✔
148
                        blockShift = shift / bitsInBlock;
13✔
149
                        if (MSU >= blockShift) {
13✔
150
                                // Shift down by whole limbs: copy [blockShift..MSU] into
151
                                // [0..MSU-blockShift], then zero all the high limbs that
152
                                // no longer have a source.  The previous implementation
153
                                // did the zeroing inline (`_block[i+blockShift] = 0`),
154
                                // which only covered positions [blockShift, MSU] -- when
155
                                // blockShift > (MSU+1)/2 the gap (MSU-blockShift,
156
                                // blockShift) was left untouched and the original limb
157
                                // value leaked through to the result.  Issue #862.
158
                                for (size_t i = 0; i <= MSU - blockShift; ++i) {
73✔
159
                                        _block[i] = _block[i + blockShift];
60✔
160
                                }
161
                                for (size_t i = MSU - blockShift + 1; i <= MSU; ++i) {
42✔
162
                                        _block[i] = 0;
29✔
163
                                }
164
                        }
165
                        // adjust the shift
166
                        shift -= static_cast<int>(blockShift * bitsInBlock);
13✔
167
                        if (shift == 0) {
13✔
168
                                remove_leading_zeros();
6✔
169
                                return *this;
6✔
170
                        }
171
                }
172
                if (MSU > 0) {
21✔
173
                        BlockType mask = static_cast<BlockType>(0xFFFF'FFFFul);
15✔
174
                        mask >>= (bitsInBlock - shift); // this is a mask for the lower bits in the block that need to move to the lower word
15✔
175
                        for (size_t i = 0; i < MSU; ++i) {
64✔
176
                                _block[i] >>= shift;
49✔
177
                                // mix in the bits from the left
178
                                BlockType bits = BlockType(mask & _block[i + 1]);
49✔
179
                                _block[i] |= (bits << (bitsInBlock - shift));
49✔
180
                        }
181
                        _block[MSU] >>= shift;
15✔
182
                }
183
                else {
184
                        _block[0] >>= shift;
6✔
185
                }
186
                remove_leading_zeros();
21✔
187
                return *this;
21✔
188
        }
189
        
190
        // unary arithmetic operators
191
        einteger operator-() const {
4,610✔
192
                einteger negated(*this);
4,610✔
193
                negated.setsign(!_sign);
4,610✔
194
                return negated;
4,610✔
195
        }
196
        einteger operator++(int) {
197
                einteger tmp(*this);
198
                operator++();
199
                return tmp;
200
        }
201
        einteger& operator++() {
202
                *this += 1;
203
                return *this;
204
        }
205
        einteger operator--(int) {
206
                einteger tmp(*this);
207
                operator--();
208
                return tmp;
209
        }
210
        einteger& operator--() {
211
                *this -= 1;
212
                return *this;
213
        }
214

215
        // binary arithmetic operators
216
        einteger& operator+=(const einteger& rhs) {
3,146,308✔
217
                if (sign() != rhs.sign()) {
3,146,308✔
218
                        if (sign()) {
2✔
UNCOV
219
                                einteger negated(*this);
×
UNCOV
220
                                negated.setsign(false);
×
UNCOV
221
                                *this = rhs - negated;
×
UNCOV
222
                                return *this;
×
UNCOV
223
                        }
×
224
                        else {
225
                                einteger negated(rhs);
2✔
226
                                negated.setsign(false);
2✔
227
                                *this -= negated;
2✔
228
                                return *this;
2✔
229
                        }
2✔
230
                }
231
                auto lhsSize = _block.size();
3,146,306✔
232
                auto rhsSize = rhs._block.size();
3,146,306✔
233
                if (lhsSize < rhsSize) _block.resize(rhsSize, 0);
3,146,306✔
234

235
                std::uint64_t carry{ 0 };
3,146,306✔
236
                typename std::vector<BlockType>::iterator li = _block.begin();
3,146,306✔
237
                typename std::vector<BlockType>::const_iterator ri = rhs._block.begin();
3,146,306✔
238
                while (li != _block.end()) {
7,341,379✔
239
                        if (ri != rhs._block.end()) {
4,195,073✔
240
                                carry += static_cast<std::uint64_t>(*li) + static_cast<std::uint64_t>(*ri);
4,187,789✔
241
                                ++ri;
4,187,789✔
242
                        }
243
                        else {
244
                                carry += static_cast<std::uint64_t>(*li);
7,284✔
245
                        }
246
                        *li = static_cast<BlockType>(carry);
4,195,073✔
247
                        carry >>= bitsInBlock;
4,195,073✔
248
                        ++li; 
4,195,073✔
249
                }
250
                if (carry == 0x1ull) {
3,146,306✔
251
                        _block.push_back(static_cast<BlockType>(carry));
1,571,335✔
252
                }
253
                return *this;
3,146,306✔
254
        }
255
        einteger& operator+=(long long rhs) {
222✔
256
                return *this += einteger(rhs);
222✔
257
        }
258
        einteger& operator-=(const einteger& rhs) {
4,325,380✔
259
                if (rhs.sign()) {
4,325,380✔
260
                        einteger negated(rhs);
2✔
261
                        negated.setsign(false);
2✔
262
                        return *this += negated;
2✔
263
                }
2✔
264
                auto lhsSize = _block.size();
4,325,378✔
265
                if (lhsSize == 0) {
4,325,378✔
266
                        *this = -rhs;
4,608✔
267
                        return *this;
4,608✔
268
                }
269
                auto rhsSize = rhs._block.size();
4,320,770✔
270
                int magnitude = compare_magnitude(*this, rhs); // if -1 result is going to be negative
4,320,770✔
271

272
                auto overlap = (lhsSize < rhsSize ? lhsSize : rhsSize);
4,320,770✔
273
                auto extent = (lhsSize < rhsSize ? rhsSize : lhsSize);
4,320,770✔
274

275
                // prep storage
276
                if (lhsSize < rhsSize) _block.resize(rhsSize, 0);
4,320,770✔
277

278
                typename std::vector<BlockType>::const_iterator aIter, bIter;
4,320,770✔
279
                if (magnitude == 1) {
4,320,770✔
280
                        aIter = _block.begin();
2,160,386✔
281
                        bIter = rhs._block.begin();
2,160,386✔
282
                }
283
                else {
284
                        aIter = rhs._block.begin();
2,160,384✔
285
                        bIter = _block.begin();
2,160,384✔
286
                }
287
                std::uint64_t borrow{ 0 };
4,320,770✔
288
                unsigned i{ 0 };
4,320,770✔
289
                while (i < overlap) {
9,226,762✔
290
                        borrow = static_cast<std::uint64_t>(*aIter) - static_cast<std::uint64_t>(*bIter) - borrow;
4,905,992✔
291
                        _block[i] = static_cast<BlockType>(borrow);
4,905,992✔
292
                        borrow = (borrow >> bitsInBlock) & 0x1u;
4,905,992✔
293
                        ++i; ++aIter; ++bIter;
4,905,992✔
294
                }
295
                while ((i < extent)) {
4,717,821✔
296
                        borrow = static_cast<BlockType>(*aIter) - borrow;
397,051✔
297
                        _block[i] = static_cast<BlockType>(borrow);
397,051✔
298
                        borrow = (borrow >> bitsInBlock) & 0x1u;
397,051✔
299
                        ++i; ++aIter;
397,051✔
300
                }
301
                remove_leading_zeros();
4,320,770✔
302
                setsign(magnitude == -1);
4,320,770✔
303
                return *this;
4,320,770✔
304
        }
305
        einteger& operator-=(long long rhs) {
306
                return *this -= einteger(rhs);
307
        }
308
        einteger& operator*=(const einteger& rhs) {
489,929✔
309
                if (iszero() || rhs.iszero()) {
489,929✔
310
                        clear();
2,621✔
311
                        return *this;
2,621✔
312
                }
313
                einteger base(*this);
487,308✔
314
                bool ls = sign();
487,308✔
315
                unsigned ll = limbs();
487,308✔
316
                bool rs = rhs.sign();
487,308✔
317
                unsigned rl = rhs.limbs();
487,308✔
318

319
                clear();
487,308✔
320
                // Schoolbook multiply with a per-row carry. block(k) reads 0 past the
321
                // end and setblock(k, ...) grows storage, so reading/writing ahead of
322
                // the current size is safe. The carry is reset for each outer limb and
323
                // rippled into the higher limbs at the end of each row.
324
                // Fixes #991: the previous version kept a single carry accumulator
325
                // across all rows and flushed it only once at block(ll+rl-1), which
326
                // misplaced carries at the limb boundary -- e.g. (2^32)*(2^32) did not
327
                // equal 2^64, and ~99.99% of 53-bit x 53-bit products were wrong.
328
                for (unsigned i = 0; i < ll; ++i) {
1,197,746✔
329
                        std::uint64_t carry = 0;
710,438✔
330
                        for (unsigned j = 0; j < rl; ++j) {
2,187,069✔
331
                                std::uint64_t segment = static_cast<std::uint64_t>(base.block(i)) * static_cast<std::uint64_t>(rhs.block(j))
1,476,631✔
332
                                                      + static_cast<std::uint64_t>(block(i + j))
1,476,631✔
333
                                                      + carry;
334
                                setblock(i + j, static_cast<bt>(segment));
1,476,631✔
335
                                carry = segment >> bitsInBlock;
1,476,631✔
336
                        }
337
                        for (unsigned k = i + rl; carry != 0; ++k) {
1,248,059✔
338
                                std::uint64_t segment = static_cast<std::uint64_t>(block(k)) + carry;
537,621✔
339
                                setblock(k, static_cast<bt>(segment));
537,621✔
340
                                carry = segment >> bitsInBlock;
537,621✔
341
                        }
342
                }
343
                // trim high zero limbs so equality/comparison (which short-circuit on
344
                // limb count) see a canonical representation, consistent with the other
345
                // mutating operators.
346
                remove_leading_zeros();
487,308✔
347
                setsign(ls ^ rs);
487,308✔
348
                return *this;
487,308✔
349
        }
487,308✔
350
        einteger& operator*=(long long rhs) {
438✔
351
                return *this *= einteger(rhs);
438✔
352
        }
353
        einteger& operator/=(const einteger& rhs) {
5✔
354
                einteger q, r;
5✔
355
                q.reduce(*this, rhs, r);
5✔
356
                *this = q;
5✔
357
                return *this;
5✔
358
        }
5✔
359
        einteger& operator/=(long long rhs) {
360
                return *this /= einteger(rhs);
361
        }
362
        einteger& operator%=(const einteger& rhs) {
2✔
363
                einteger q, a(*this), r;
2✔
364
                q.reduce(a, rhs, r);
2✔
365
                *this = r;
2✔
366
                return *this;
2✔
367
        }
2✔
368
        einteger& operator%=(long long rhs) {
369
                return *this %= einteger(rhs);
370
        }
371
        
372
        // reduce returns the ratio and remainder of a and b in *this and r
373
        void reduce(const einteger& a, const einteger& b, einteger& r) {
3,146,680✔
374
                if (b.iszero()) {
3,146,680✔
375
#if EINTEGER_THROW_ARITHMETIC_EXCEPTION
376
                        throw einteger_divide_by_zero{};
9,216✔
377
#else
UNCOV
378
                        std::cerr << "einteger_divide_by_zero\n";
×
UNCOV
379
                        return;
×
380
#endif // EINTEGER_THROW_ARITHMETIC_EXCEPTION
381
                }
382
                clear();
3,143,608✔
383
                r.clear();
3,143,608✔
384
                if (a.iszero()) return;
3,143,608✔
385

386
                size_t aBlocks = a.limbs();
3,140,539✔
387
                size_t bBlocks = b.limbs();
3,140,539✔
388
                if (aBlocks == 1 && aBlocks == bBlocks) { // completely reduce this to native div and rem
3,140,539✔
389
                        std::uint64_t a0 = a._block[0];
2,093,306✔
390
                        std::uint64_t b0 = b._block[0];
2,093,306✔
391
                        *this = static_cast<BlockType>(a0 / b0);
2,093,306✔
392
                        r = static_cast<BlockType>(a0 % b0);
2,093,306✔
393
                }
2,093,306✔
394
                        else {
395
                                // filter out the easy stuff. The early-exits below need to
396
                                // compare *magnitudes* (truncated-division semantics apply
397
                                // |a|/|b| with the final sign = a.sign ^ b.sign). The
398
                                // generic operator< is signed, so a negative a is always
399
                                // "less than" a positive b and we'd discard quotient bits
400
                                // for large negative numerators. Compare by limb count and
401
                                // then top-down by limb value, ignoring sign.
402
                                auto magnitude_less = [](const einteger& x, const einteger& y) {
1,047,233✔
403
                                        if (x.limbs() != y.limbs()) return x.limbs() < y.limbs();
1,047,233✔
404
                                        for (size_t i = x.limbs(); i > 0; --i) {
1,045,577✔
405
                                                BlockType xb = x.block(i - 1);
1,044,550✔
406
                                                BlockType yb = y.block(i - 1);
1,044,550✔
407
                                                if (xb != yb) return xb < yb;
1,044,550✔
408
                                        }
409
                                        return false;
1,027✔
410
                                };
411
                                auto magnitude_equal = [](const einteger& x, const einteger& y) {
524,476✔
412
                                        if (x.limbs() != y.limbs()) return false;
524,476✔
413
                                        for (size_t i = x.limbs(); i > 0; --i)
524,322✔
414
                                                if (x.block(i - 1) != y.block(i - 1)) return false;
523,295✔
415
                                        return true;
1,027✔
416
                                };
417
                                if (magnitude_less(a, b))  { r = a; clear(); return; }
1,052,009✔
418
                                if (magnitude_equal(a, b)) {
524,476✔
419
                                        *this = 1;
1,027✔
420
                                        setsign(a.sign() ^ b.sign());
1,027✔
421
                                        r.clear();
1,027✔
422
                                        return;
1,027✔
423
                                }
424

425
                        // determine first non-zero limbs
426
                        unsigned m{ 0 }, n{ 0 };
523,449✔
427
                        for (size_t i = aBlocks; i > 0; --i) {
523,449✔
428
                                if (a._block[i - 1] != 0) {
523,449✔
429
                                        m = static_cast<unsigned>(i);
523,449✔
430
                                        break;
523,449✔
431
                                }
432
                        }
433
                        for (size_t i = bBlocks; i > 0; --i) {
523,449✔
434
                                if (b._block[i - 1] != 0) {
523,449✔
435
                                        n = static_cast<unsigned>(i);
523,449✔
436
                                        break;
523,449✔
437
                                }
438
                        }
439

440
                        // single limb divisor
441
                        if (n == 1) {
523,449✔
442
                                _block.resize(m);
3,749✔
443
                                std::uint64_t remainder{ 0 };
3,749✔
444
                                auto divisor = b.block(0);
3,749✔
445
                                for (unsigned j = m; j > 0; --j) {
13,611✔
446
                                        std::uint64_t dividend = remainder * BASE + static_cast<std::uint64_t>(a.block(j - 1));
9,862✔
447
                                        std::uint64_t limbQuotient = dividend / divisor;
9,862✔
448
                                        _block[j - 1] = static_cast<BlockType>(limbQuotient);
9,862✔
449
                                        remainder = dividend - limbQuotient * divisor;
9,862✔
450
                                }
451
                                remove_leading_zeros();
3,749✔
452
                                r.setblock(0, static_cast<BlockType>(remainder));
3,749✔
453
                                return;
3,749✔
454
                        }
455

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

460
                        int shift = nlz(b.block(n - 1));
519,700✔
461
                        einteger normalized_a;
519,700✔
462
                        normalized_a.setblock(m, static_cast<BlockType>((a.block(m - 1) >> (bitsInBlock - shift))));
519,700✔
463
                        for (unsigned i = m - 1; i > 0; --i) {
1,039,453✔
464
                                normalized_a.setblock(i, static_cast<BlockType>((a.block(i) << shift) | (a.block(i - 1) >> (bitsInBlock - shift))));
519,753✔
465
                        }
466
                        normalized_a.setblock(0, static_cast<BlockType>(a.block(0) << shift));
519,700✔
467
                        // normalize b
468
                        einteger normalized_b;
519,700✔
469
                        unsigned n_minus_1 = n - 1;
519,700✔
470
                        for (unsigned i = n_minus_1; i > 0; --i) {
1,039,421✔
471
                                normalized_b.setblock(i, static_cast<BlockType>((b.block(i) << shift) | (b.block(i - 1) >> (bitsInBlock - shift))));
519,721✔
472
                        }
473
                        normalized_b.setblock(0, static_cast<BlockType>(b.block(0) << shift));
519,700✔
474

475
                        //std::cout << "normalized a : " << normalized_a.showLimbs() << " : " << normalized_a.showLimbValues() << '\n';
476
                        //std::cout << "normalized b :             " << normalized_b.showLimbs() << " : " << normalized_b.showLimbValues() << '\n';
477

478
                        // divide by limb
479
                        std::uint64_t divisor = normalized_b._block[n - 1];
519,700✔
480
                        std::uint64_t v_nminus2 = normalized_b._block[n - 2]; // n > 1 at this point
519,700✔
481
                        for (int j = static_cast<int>(m - n); j >= 0; --j) {
1,039,432✔
482
                                std::uint64_t dividend = normalized_a.block(j + n) * BASE + normalized_a.block(j + n - 1);
519,732✔
483
                                std::uint64_t qhat = dividend / divisor;
519,732✔
484
                                std::uint64_t rhat = dividend - qhat * divisor;
519,732✔
485

486
                                while (qhat >= BASE || qhat * v_nminus2 > BASE * rhat + normalized_a.block(j + n - 2)) {
520,126✔
487
                                        --qhat;
394✔
488
                                        rhat += divisor;
394✔
489
                                        if (rhat < BASE) continue;
394✔
490
                                }
491
                                // Knuth Algorithm D, step D4 (multi-precision subtraction
492
                                // with borrow propagation). `diff` MUST be signed so that
493
                                // the arithmetic right shift `diff >> bitsInBlock` yields
494
                                // the -1/0 underflow indicator. With uint64_t the shift
495
                                // instead returns all-ones, and `p_hi - (diff >> shift)`
496
                                // wraps to a huge value that corrupts the next iteration
497
                                // and ultimately produces a quotient that is too large
498
                                // by exactly one limb (issue #842). The low-bits mask
499
                                // must also track bitsInBlock so the algorithm works for
500
                                // uint8_t and uint16_t blocks in addition to uint32_t.
501
                                constexpr std::uint64_t LOW_MASK = BASE - 1u;
519,732✔
502
                                std::int64_t borrow{ 0 };
519,732✔
503
                                std::int64_t diff{ 0 };
519,732✔
504
                                for (unsigned i = 0; i < n; ++i) {
1,559,285✔
505
                                        std::uint64_t p     = qhat * normalized_b.block(i);
1,039,553✔
506
                                        std::uint64_t p_lo  = p & LOW_MASK;
1,039,553✔
507
                                        std::uint64_t p_hi  = p >> bitsInBlock;
1,039,553✔
508
                                        diff = static_cast<std::int64_t>(normalized_a.block(i + j))
1,039,553✔
509
                                             - static_cast<std::int64_t>(p_lo)
1,039,553✔
510
                                             - borrow;
511
                                        normalized_a.setblock(i + j, static_cast<BlockType>(diff));
1,039,553✔
512
                                        // borrow_out = p_hi + (1 if underflow else 0). The
513
                                        // signed shift `diff >> bitsInBlock` yields -1 on
514
                                        // underflow and 0 otherwise, so subtracting it adds
515
                                        // 1 in the underflow case.
516
                                        borrow = static_cast<std::int64_t>(p_hi) - (diff >> bitsInBlock);
1,039,553✔
517
                                }
518
                                std::int64_t signedBorrow = static_cast<std::int64_t>(normalized_a.block(j + n)) - borrow;
519,732✔
519
                                normalized_a.setblock(j + n, static_cast<BlockType>(signedBorrow));
519,732✔
520

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

523
                                setblock(static_cast<unsigned>(j), static_cast<BlockType>(qhat));
519,732✔
524
                                if (signedBorrow < 0) { // subtracted too much, add back
519,732✔
UNCOV
525
                                        setblock(static_cast<size_t>(j), static_cast<BlockType>(_block[static_cast<size_t>(j)] - 1));
×
UNCOV
526
                                        std::uint64_t carry{ 0 };
×
UNCOV
527
                                        for (unsigned i = 0; i < n; ++i) {
×
UNCOV
528
                                                carry += static_cast<std::uint64_t>(normalized_a.block(i + j)) + static_cast<std::uint64_t>(normalized_b.block(i));
×
UNCOV
529
                                                normalized_a.setblock(i + j, static_cast<BlockType>(carry));
×
UNCOV
530
                                                carry >>= bitsInBlock;
×
531
                                        }
UNCOV
532
                                        BlockType rectified = static_cast<BlockType>(normalized_a.block(j + n) + carry);
×
UNCOV
533
                                        normalized_a.setblock(j + n, rectified);
×
534
                                }
535
                                //std::cout << "   updated a : " << normalized_a.showLimbs() << " : " << normalized_a.showLimbValues() << '\n';
536
                        }
537

538
                        // remainder needs to be normalized
539
                        for (unsigned i = 0; i < n - 1; ++i) {
1,039,421✔
540
                                std::uint64_t remainder = static_cast<std::uint64_t>(normalized_a.block(i) >> shift);
519,721✔
541
                                remainder |= (static_cast<std::uint64_t>(normalized_a.block(i + 1)) << (bitsInBlock - shift));
519,721✔
542
                                r.setblock(i, static_cast<BlockType>(remainder));
519,721✔
543
                        }
544
                        r.setblock(n - 1, static_cast<BlockType>(normalized_a.block(n - 1) >> shift));
519,700✔
545
                }
519,700✔
546
                remove_leading_zeros();
2,613,006✔
547
                _sign = a.sign() ^ b.sign();
2,613,006✔
548
        }
549

550
        // modifiers (vector::clear is constexpr in C++20; on the empty
551
        // constant-evaluated state both clear() and setzero() are trivially
552
        // constexpr-clean.  setsign writes only the bool member.)
553
        constexpr void clear() noexcept { _sign = false; _block.clear(); }
45,286,901✔
554
        constexpr void setzero() noexcept { clear(); }
530,081✔
555
        constexpr void setsign(bool sign = true) noexcept { _sign = sign; }
7,198,784✔
556
        // use un-interpreted raw bits to set the bits of the einteger
557
        void setbits(unsigned long long value) {
23,934,141✔
558
                clear();
23,934,141✔
559
                if constexpr (bitsInBlock == 8) {
560
                        std::uint8_t byte0 = static_cast<std::uint8_t>(value & 0x0000'0000'0000'00FF);
6,185,159✔
561
                        std::uint8_t byte1 = static_cast<std::uint8_t>((value & 0x0000'0000'0000'FF00) >> 8);
6,185,159✔
562
                        std::uint8_t byte2 = static_cast<std::uint8_t>((value & 0x0000'0000'00FF'0000) >> 16);
6,185,159✔
563
                        std::uint8_t byte3 = static_cast<std::uint8_t>((value & 0x0000'0000'FF00'0000) >> 24);
6,185,159✔
564
                        std::uint8_t byte4 = static_cast<std::uint8_t>((value & 0x0000'00FF'0000'0000) >> 32);
6,185,159✔
565
                        std::uint8_t byte5 = static_cast<std::uint8_t>((value & 0x0000'FF00'0000'0000) >> 40);
6,185,159✔
566
                        std::uint8_t byte6 = static_cast<std::uint8_t>((value & 0x00FF'0000'0000'0000) >> 48);
6,185,159✔
567
                        std::uint8_t byte7 = static_cast<std::uint8_t>((value & 0xFF00'0000'0000'0000) >> 56);
6,185,159✔
568
                        if (byte7 > 0) {
6,185,159✔
569
                                _block.push_back(byte0);
45,471✔
570
                                _block.push_back(byte1);
45,471✔
571
                                _block.push_back(byte2);
45,471✔
572
                                _block.push_back(byte3);
45,471✔
573
                                _block.push_back(byte4);
45,471✔
574
                                _block.push_back(byte5);
45,471✔
575
                                _block.push_back(byte6);
45,471✔
576
                                _block.push_back(byte7);
45,471✔
577
                        }
578
                        else if (byte6 > 0) {
6,139,688✔
579
                                _block.push_back(byte0);
4,508✔
580
                                _block.push_back(byte1);
4,508✔
581
                                _block.push_back(byte2);
4,508✔
582
                                _block.push_back(byte3);
4,508✔
583
                                _block.push_back(byte4);
4,508✔
584
                                _block.push_back(byte5);
4,508✔
585
                                _block.push_back(byte6);
4,508✔
586
                        }
587
                        else if (byte5 > 0) {
6,135,180✔
588
                                _block.push_back(byte0);
39✔
589
                                _block.push_back(byte1);
39✔
590
                                _block.push_back(byte2);
39✔
591
                                _block.push_back(byte3);
39✔
592
                                _block.push_back(byte4);
39✔
593
                                _block.push_back(byte5);
39✔
594
                        }
595
                        else if (byte4 > 0) {
6,135,141✔
596
                                _block.push_back(byte0);
6✔
597
                                _block.push_back(byte1);
6✔
598
                                _block.push_back(byte2);
6✔
599
                                _block.push_back(byte3);
6✔
600
                                _block.push_back(byte4);
6✔
601
                        }
602
                        else if (byte3 > 0) {
6,135,135✔
603
                                _block.push_back(byte0);
99,190✔
604
                                _block.push_back(byte1);
99,190✔
605
                                _block.push_back(byte2);
99,190✔
606
                                _block.push_back(byte3);
99,190✔
607
                        }
608
                        else if (byte2 > 0) {
6,035,945✔
609
                                _block.push_back(byte0);
524,636✔
610
                                _block.push_back(byte1);
524,636✔
611
                                _block.push_back(byte2);
524,636✔
612
                        }
613
                        else if (byte1 > 0) {
5,511,309✔
614
                                _block.push_back(byte0);
4,058,694✔
615
                                _block.push_back(byte1);
4,058,694✔
616
                        }
617
                        else if (byte0 > 0) {
1,452,615✔
618
                                _block.push_back(byte0);
1,448,992✔
619
                        }
620
                        else {
621
                                _block.clear();
3,623✔
622
                        }
623
                }
624
                else if constexpr (bitsInBlock == 16) {
625
                        std::uint16_t word0 = static_cast<std::uint16_t>( value & 0x0000'0000'0000'FFFF);
7,881,537✔
626
                        std::uint16_t word1 = static_cast<std::uint16_t>((value & 0x0000'0000'FFFF'0000) >> 16);
7,881,537✔
627
                        std::uint16_t word2 = static_cast<std::uint16_t>((value & 0x0000'FFFF'0000'0000) >> 32);
7,881,537✔
628
                        std::uint16_t word3 = static_cast<std::uint16_t>((value & 0xFFFF'0000'0000'0000) >> 48);
7,881,537✔
629
                        if (word3 > 0) {
7,881,537✔
630
                                _block.push_back(word0);
49,968✔
631
                                _block.push_back(word1);
49,968✔
632
                                _block.push_back(word2);
49,968✔
633
                                _block.push_back(word3);
49,968✔
634
                        }
635
                        else if (word2 > 0) {
7,831,569✔
636
                                _block.push_back(word0);
32✔
637
                                _block.push_back(word1);
32✔
638
                                _block.push_back(word2);
32✔
639
                        }
640
                        else if (word1 > 0) {
7,831,537✔
641
                                _block.push_back(word0);
688,838✔
642
                                _block.push_back(word1);
688,838✔
643
                        }
644
                        else if (word0 > 0) {
7,142,699✔
645
                                _block.push_back(word0);
7,138,852✔
646
                        }
647
                        else {
648
                                _block.clear();
3,847✔
649
                        }
650
                }
651
                else if constexpr (bitsInBlock == 32) {
652
                        std::uint32_t low  = static_cast<std::uint32_t>(value & 0x0000'0000'FFFF'FFFF);
9,867,445✔
653
                        std::uint32_t high = static_cast<std::uint32_t>((value & 0xFFFF'FFFF'0000'0000) >> bitsInBlock);
9,867,445✔
654
                        if (high > 0) {
9,867,445✔
655
                                _block.push_back(low);
637,344✔
656
                                _block.push_back(high);
637,344✔
657
                        }
658
                        else if (low > 0) {
9,230,101✔
659
                                _block.push_back(low);
9,225,487✔
660
                        }
661
                        else {
662
                                _block.clear();
4,614✔
663
                        }
664
                }
665
        }
23,934,141✔
666
        void setblock(unsigned i, BlockType value) noexcept {
7,735,203✔
667
                if (i >= _block.size()) _block.resize(i+1ull);
7,735,203✔
668
                _block[i] = value;
7,735,203✔
669
        }
7,735,203✔
670
        // Set the i-th byte (counting from the least-significant) of the value
671
        // while preserving the other bytes within the same limb. Grows the
672
        // limb vector if needed.
673
        void setbyte(unsigned i, std::uint8_t byte) noexcept {
39✔
674
                if constexpr (bitsInBlock == 8) {
675
                        setblock(i, static_cast<BlockType>(byte));
4✔
676
                }
677
                else {
678
                        constexpr unsigned bytesInBlock = sizeof(BlockType);
35✔
679
                        unsigned blockIndex      = i / bytesInBlock;
35✔
680
                        unsigned positionInBlock = i % bytesInBlock;
35✔
681
                        unsigned shiftAmount     = positionInBlock * 8u;
35✔
682
                        BlockType byteMask       = static_cast<BlockType>(static_cast<BlockType>(0xFFu) << shiftAmount);
35✔
683
                        BlockType existing       = (blockIndex < _block.size()) ? _block[blockIndex] : BlockType{};
35✔
684
                        BlockType updated        = static_cast<BlockType>((existing & ~byteMask)
35✔
685
                                                 | (static_cast<BlockType>(byte) << shiftAmount));
35✔
686
                        setblock(blockIndex, updated);
35✔
687
                }
688
        }
39✔
689
        einteger& assign(const std::string& txt) {
2✔
690
                if (!parse(txt, *this)) {
2✔
UNCOV
691
                        std::cerr << "Unable to parse: " << txt << std::endl;
×
692
                }
693
                return *this;
2✔
694
        }
695

696
        // selectors (read-only access to _sign and _block; constexpr-callable
697
        // on the empty default-constructed state and on any persisted
698
        // constexpr einteger -- which by definition has empty _block)
699
        constexpr bool iszero() const noexcept { return (_block.size() == 0 || ((_block.size() == 1) && _block[0] == bt(0))); }
7,273,039✔
700
        constexpr bool isone()  const noexcept { return true; }
701
        constexpr bool isodd()  const noexcept { return (_block.size() > 0) ? (_block[0] & 0x1) : false; }
702
        constexpr bool iseven() const noexcept { return !isodd(); }
703
        constexpr bool ispos()  const noexcept { return !_sign; }
704
        constexpr bool isneg()  const noexcept { return _sign; }
194✔
705

706
        constexpr bool test(unsigned index) const noexcept {
241,142,456✔
707
                if (index < nbits()) {
241,142,456✔
708
                        unsigned blockIndex = index / bitsInBlock;
241,142,456✔
709
                        unsigned bitIndexInBlock = index % bitsInBlock;
241,142,456✔
710
                        BlockType data = _block[blockIndex];
241,142,456✔
711
                        BlockType mask = (0x1u << bitIndexInBlock);
241,142,456✔
712
                        if (data & mask) return true;
241,142,456✔
713
                }
714
                return false;
186,817,879✔
715
        }
716
        constexpr bool sign()   const noexcept { return _sign; }
49,984,774✔
717
        constexpr int scale()   const noexcept { return findMsb(); } // TODO: when value = 0, scale returns -1 which is incorrect
718

719
        constexpr BlockType block(unsigned b) const noexcept {
17,993,735✔
720
                if (b < _block.size()) return _block[b];
17,993,735✔
721
                return static_cast<BlockType>(0u);
1,276,752✔
722
        }
723
        constexpr unsigned limbs() const noexcept { return static_cast<unsigned>(_block.size()); }
67,475,297✔
724

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

727
        // findMsb takes an einteger reference and returns the position of the most significant bit, -1 if v == 0
728
        constexpr int findMsb() const noexcept {
729
                int nrBlocks = static_cast<int>(_block.size());
730
                if (nrBlocks == 0) return -1; // no significant bit found, all bits are zero
731
                int msb = nrBlocks * static_cast<int>(bitsInBlock);
732
                for (int b = nrBlocks - 1; b >= 0; --b) {
733
                        // derive mask from the actual block width so this works for
734
                        // uint8_t / uint16_t / uint32_t instantiations (was hardcoded
735
                        // to 0x8000'0000ul, which only worked for uint32_t)
736
                        std::uint64_t segment = static_cast<std::uint64_t>(_block[static_cast<size_t>(b)]);
737
                        std::uint64_t mask = (std::uint64_t(1) << (bitsInBlock - 1));
738
                        for (int i = bitsInBlock - 1; i >= 0; --i) {
739
                                --msb;
740
                                if (segment & mask) return msb;
741
                                mask >>= 1;
742
                        }
743
                }
744
                return -1; // no significant bit found, all bits are zero
745
        }
746

747
        // convert to string containing digits number of digits
748
        std::string str(size_t nrDigits = 0) const {
749
                return std::string("tbd");
750
        }
751

752
        // show the binary encodings of the limbs
753
        std::string showLimbs() const {
754
                if (_block.empty()) return "no limbs";
755
                std::stringstream s;
756
                size_t i = _block.size() - 1;
757
                while (i > 0) {
758
                        s << to_binary(_block[i], sizeof(BlockType) * 8, true) << ' ';
759
                        --i;
760
                }
761
                s << to_binary(_block[0], sizeof(BlockType) * 8, true);
762
                return s.str();
763
        }
764
        // show the values of the limbs as a radix-BlockType number
765
        std::string showLimbValues() const {
766
                if (_block.empty()) return "no limbs";
767
                std::stringstream s;
768
                size_t i = _block.size() - 1;
769
                while (i > 0) {
770
                        s << std::setw(5) << unsigned(_block[i]) << ", ";
771
                        --i;
772
                }
773
                s << std::setw(5) << unsigned(_block[0]);
774
                return s.str();
775
        }
776

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

781
        // HELPER methods
782
        // compare_magnitude returns 1 if a > b, 0 if they are equal, and -1 if a < b
783
        int compare_magnitude(const einteger& a, const einteger& b) {
4,320,770✔
784
                unsigned aLimbs = a.limbs();
4,320,770✔
785
                unsigned bLimbs = b.limbs();
4,320,770✔
786
                if (aLimbs != bLimbs) {
4,320,770✔
787
                        return (aLimbs > bLimbs ? 1 : -1);  // return 1 if a > b, otherwise -1
396,283✔
788
                }
789
                for (int i = static_cast<int>(aLimbs) - 1; i >= 0; --i) {
4,125,697✔
790
                        BlockType _a = a._block[static_cast<size_t>(i)];
4,121,095✔
791
                        BlockType _b = b._block[static_cast<size_t>(i)];
4,121,095✔
792
                        if ( _a != _b) {
4,121,095✔
793
                                return (_a > _b ? 1 : -1);
3,919,885✔
794
                        }
795
                }
796
                return 0;
4,602✔
797
        }
798
        void remove_leading_zeros() {
7,458,944✔
799
                unsigned leadingZeroBlocks{ 0 };
7,458,944✔
800
                typename std::vector<BlockType>::reverse_iterator rit = _block.rbegin();
7,458,944✔
801
                while (rit != _block.rend()) {
7,892,175✔
802
                        if (*rit == 0) {
6,841,871✔
803
                                ++leadingZeroBlocks;
433,231✔
804
                        }
805
                        else {
806
                                break;
6,408,640✔
807
                        }
808
                        ++rit;
433,231✔
809
                }
810
                _block.resize(_block.size() - leadingZeroBlocks);
7,458,944✔
811
        }
7,458,944✔
812
        
813
        template<typename SignedInt>
814
        einteger& convert_signed(SignedInt v) {
13,521,577✔
815
                clear();
13,521,577✔
816
                if (v != 0) {
13,521,577✔
817
                        if (v < 0) {
11,412,877✔
818
                                setbits(static_cast<unsigned long long>(-v));
2,384,920✔
819
                                setsign(true);
2,384,920✔
820
                        }
821
                        else {
822
                                setbits(static_cast<unsigned long long>(v)); // TODO: what about -2^63
9,027,957✔
823
                        }
824
                }
825
                return *this;
13,521,577✔
826
        }
827

828
        template<typename UnsignedInt>
829
        einteger& convert_unsigned(UnsignedInt v) {
2,094,135✔
830
                if (0 == v) {
2,094,135✔
831
                        setzero();
530,076✔
832
                }
833
                else {
834
                        setbits(static_cast<unsigned long long>(v));
1,564,059✔
835
                }
836
                return *this;
2,094,135✔
837
        }
838

839
        template<typename Real>
840
        einteger& convert_ieee754(Real& rhs) {
118✔
841
                clear();
118✔
842
                bool s{ false };
118✔
843
                std::uint64_t rawExponent{ 0 };
118✔
844
                std::uint64_t rawFraction{ 0 };
118✔
845
                uint64_t bits{ 0 };
118✔
846
                extractFields(rhs, s, rawExponent, rawFraction, bits);
118✔
847
                if (rawExponent == ieee754_parameter<Real>::eallset) { // nan and inf
118✔
848
                        // we can't represent NaNs or Infinities
849
                        return *this;
3✔
850
                }
851
                int exponent = static_cast<int>(rawExponent) - ieee754_parameter<Real>::bias;
115✔
852
                if (exponent < 0) {
115✔
UNCOV
853
                        return *this; // we are zero
×
854
                }
855
                // normal and subnormal handling
856
                constexpr size_t fbits = ieee754_parameter<Real>::fbits;
115✔
857
                std::uint64_t hiddenBit = (0x1ull << fbits);
115✔
858
                rawFraction |= hiddenBit;
115✔
859
                setbits(rawFraction);
115✔
860
                setsign(s);
115✔
861
                // scale the fraction bits
862
                *this <<= static_cast<int>(exponent - fbits);
115✔
863
                return *this;
115✔
864
        }
865

866
        template<typename Integer>
867
        Integer convert_to_native_integer() const noexcept {
10,957,141✔
868
                using Unsigned = std::make_unsigned_t<Integer>;
869
                Unsigned v{ 0 };
10,957,141✔
870
                Unsigned m{ 1 };
10,957,141✔
871
                const bool neg = sign();
10,957,141✔
872
                constexpr Integer kMax = std::numeric_limits<Integer>::max();
10,957,141✔
873
                constexpr Integer kMin = std::numeric_limits<Integer>::min();
10,957,141✔
874
                constexpr unsigned kDigits = std::numeric_limits<Unsigned>::digits;
10,957,141✔
875
                for (unsigned i = 0; i < nbits(); ++i) {
252,091,045✔
876
                        if (i >= kDigits) {
241,133,904✔
UNCOV
877
                                if (test(i)) return neg ? kMin : kMax;
×
UNCOV
878
                                continue;
×
879
                        }
880
                        if (test(i)) {
241,133,904✔
881
                                if (v > (static_cast<Unsigned>(kMax) - m)) {
54,323,308✔
UNCOV
882
                                        return neg ? kMin : kMax;
×
883
                                }
884
                                v += m;
54,323,308✔
885
                        }
886
                        m <<= 1;
241,133,904✔
887
                }
888
                return (neg ? -static_cast<Integer>(v) : static_cast<Integer>(v));
10,957,141✔
889
        }
890
        template<typename Real>
891
        Real convert_to_native_ieee() const noexcept {
117✔
892
                Real v{ 0 };
117✔
893
                Real m{ 1.0 };
117✔
894
                for (unsigned i = 0; i < nbits(); ++i) {
8,669✔
895
                        if (test(i)) {
8,552✔
896
                                v += m;
1,269✔
897
                        }
898
                        m *= Real(2.0);
8,552✔
899
                }
900
                return (sign() ? -v : v);
117✔
901
        }
902

903
private:
904

905
        template<typename BBlockType>
906
        friend constexpr bool operator==(const einteger<BBlockType>&, const einteger<BBlockType>&) noexcept;
907
};
908

909
////////////////////////    einteger functions   /////////////////////////////////
910

911
template<typename BlockType>
912
inline einteger<BlockType> abs(const einteger<BlockType>& a) {
913
        return (a.isneg()  ? -a : a);
914
}
915

916
////////////////////////    INTEGER operators   /////////////////////////////////
917

918
/// stream operators
919

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

1087
template<typename BlockType>
1088
std::string convert_to_string(std::ios_base::fmtflags flags, const einteger<BlockType>& n) {
201✔
1089
        using AdaptiveInteger = einteger<BlockType>;
1090

1091
        if (n.limbs() == 0) return std::string("0");
215✔
1092

1093
        // set the base of the target number system to convert to
1094
        int base = 10;
194✔
1095
        if ((flags & std::ios_base::oct) == std::ios_base::oct) base = 8;
194✔
1096
        if ((flags & std::ios_base::hex) == std::ios_base::hex) base = 16;
194✔
1097

1098
        unsigned nbits = n.limbs() * sizeof(BlockType) * 8;
194✔
1099

1100
        std::string result;
194✔
1101
        if (base == 8 || base == 16) {
194✔
UNCOV
1102
                if (n.sign()) return std::string("negative value: ignored");
×
1103

1104
                size_t shift = (base == 8 ? 3ull : 4ull);
×
1105
                BlockType mask = static_cast<BlockType>((1u << shift) - 1);
×
1106
                AdaptiveInteger t(n);
×
1107
                result.assign(nbits / shift + ((nbits % shift) ? 1 : 0), '0');
×
1108
                size_t pos = result.size() - 1ull;
×
1109
                for (size_t i = 0; i < nbits / shift; ++i) {
×
UNCOV
1110
                        char c = '0' + static_cast<char>(t.block(0) & mask);
×
1111
                        if (c > '9')
×
UNCOV
1112
                                c += 'A' - '9' - 1;
×
UNCOV
1113
                        result[pos--] = c;
×
UNCOV
1114
                        t >>= static_cast<int>(shift);
×
1115
                }
UNCOV
1116
                if (nbits % shift) {
×
UNCOV
1117
                        mask = static_cast<BlockType>((1u << (nbits % shift)) - 1);
×
UNCOV
1118
                        char c = '0' + static_cast<char>(t.block(0) & mask);
×
UNCOV
1119
                        if (c > '9')
×
UNCOV
1120
                                c += 'A' - '9';
×
UNCOV
1121
                        result[pos] = c;
×
1122
                }
1123
                //
1124
                // Get rid of leading zeros:
1125
                //
UNCOV
1126
                std::string::size_type fnz = result.find_first_not_of('0');
×
UNCOV
1127
                if (!result.empty() && (fnz == std::string::npos)) fnz = result.size() - 1;
×
UNCOV
1128
                result.erase(0, fnz);
×
UNCOV
1129
                if (flags & std::ios_base::showbase) {
×
UNCOV
1130
                        const char* pp = base == 8 ? "0" : "0x";
×
UNCOV
1131
                        result.insert(static_cast<std::string::size_type>(0), pp);
×
1132
                }
UNCOV
1133
        }
×
1134
        else {
1135
                unsigned block10;
1136
                unsigned digits_in_block10;
1137
                if constexpr (AdaptiveInteger::bitsInBlock == 8) {
1138
                        block10 = 100u;
74✔
1139
                        digits_in_block10 = 2;
74✔
1140
                }
1141
                else if constexpr (AdaptiveInteger::bitsInBlock == 16) {
1142
                        block10 = 10'000ul;
48✔
1143
                        digits_in_block10 = 4;
48✔
1144
                }
1145
                else if constexpr (AdaptiveInteger::bitsInBlock == 32) {
1146
                        block10 = 1'000'000'000ul;
72✔
1147
                        digits_in_block10 = 9;
72✔
1148
                }
1149
                else if constexpr (AdaptiveInteger::bitsInBlock == 64) {
1150
                        // not allowed as the whole multi-digit arithmetic
1151
                        // requires that there is a 'larger' type that
1152
                        // can receive carries and borrows.
1153
                        // If your platform does have a native 128bit
1154
                        // integer, this could be enabled
1155
                        //block10 = 1'000'000'000'000'000'000ull;
1156
                        //digits_in_block10 = 18;
1157
                }
1158
                result.assign(nbits / 3 + 1ull, '0');
194✔
1159
                size_t pos = result.size() - 1ull;
194✔
1160
                AdaptiveInteger t(n);
194✔
1161
                while (!t.iszero()) {
2,032✔
1162
                        AdaptiveInteger q,r;
919✔
1163
                        q.reduce(t, block10, r);
919✔
1164
                        BlockType v = r.block(0);
919✔
1165
//                        std::cout << "v  " << uint32_t(v) << '\n';
1166
                        for (unsigned i = 0; i < digits_in_block10; ++i) {
4,306✔
1167
                                char c = '0' + static_cast<char>(v % 10);
3,413✔
1168
                                v /= 10;
3,413✔
1169
                                result[pos] = c;
3,413✔
1170
//                                std::cout << result << " pos: " << pos << '\n';
1171
                                if (pos-- == 0)        break;
3,413✔
1172
                        }
1173
                        t = q;
919✔
1174
                }
1175

1176
                std::string::size_type firstDigit = result.find_first_not_of('0');
194✔
1177
                result.erase(0, firstDigit);
194✔
1178
                if (result.empty())
194✔
1179
                        result = "0";
2✔
1180
                if (n.isneg())
194✔
1181
                        result.insert(0ull, 1ull, '-');
58✔
1182
                else if (flags & std::ios_base::showpos)
136✔
UNCOV
1183
                        result.insert(0ull, 1ull, '+');
×
1184
        }
194✔
1185
        return result;
194✔
1186
}
194✔
1187

1188
// generate an einteger format ASCII format
1189
template<typename BlockType>
1190
inline std::ostream& operator<<(std::ostream& ostr, const einteger<BlockType>& i) {
201✔
1191
        std::string s = convert_to_string(ostr.flags(), i);
201✔
1192
        std::streamsize width = ostr.width();
201✔
1193
        if (width > static_cast<std::streamsize>(s.size())) {
201✔
UNCOV
1194
                char fill = ostr.fill();
×
UNCOV
1195
                if ((ostr.flags() & std::ios_base::left) == std::ios_base::left)
×
UNCOV
1196
                        s.append(static_cast<std::string::size_type>(width - s.size()), fill);
×
1197
                else
UNCOV
1198
                        s.insert(static_cast<std::string::size_type>(0), static_cast<std::string::size_type>(width - s.size()), fill);
×
1199
        }
1200
        return ostr << s;
402✔
1201
}
201✔
1202

1203
// read an ASCII einteger format
1204

1205
template<typename BlockType>
1206
inline std::istream& operator>>(std::istream& istr, einteger<BlockType>& p) {
4✔
1207
        std::string txt;
4✔
1208
        if (!(istr >> txt)) {
4✔
1209
                // extraction failed (already-bad stream or EOF); failbit set by >>.
1210
                return istr;
1✔
1211
        }
1212
        if (!parse(txt, p)) {
3✔
1213
                std::cerr << "unable to parse -" << txt << "- into an einteger value\n";
1✔
1214
                istr.setstate(std::ios::failbit);
1✔
1215
        }
1216
        return istr;
3✔
1217
}
4✔
1218

1219
////////////////// string operators
1220

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

1225
        std::stringstream s;
118✔
1226
        s << "0b";
118✔
1227
        for (int b = static_cast<int>(a.limbs()) - 1; b >= 0; --b) {
731✔
1228
                BlockType segment = a.block(static_cast<size_t>(b));
613✔
1229
                BlockType mask = (0x1u << (a.bitsInBlock - 1));
613✔
1230
                for (int i = a.bitsInBlock - 1; i >= 0; --i) {
9,341✔
1231
                        s << ((segment & mask) ? '1' : '0');
8,728✔
1232
                        if (i > 0 && (i % 4) == 0 && nibbleMarker) s << '\'';
8,728✔
1233
                        if (b > 0 && i == 0 && nibbleMarker) s << '\'';
8,728✔
1234
                        mask >>= 1;
8,728✔
1235
                }
1236
        }
1237

1238
        return s.str();
118✔
1239
}
118✔
1240

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

1245
        std::vector<char> nibbleLookup = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
6✔
1246
        std::stringstream s;
3✔
1247
        s << "0x";
3✔
1248
        unsigned bitIndex = a.limbs() * a.bitsInBlock - 1u;
3✔
1249
        for (int b = static_cast<int>(a.limbs()) - 1; b >= 0; --b) {
12✔
1250
                BlockType limb = a.block(static_cast<size_t>(b));
9✔
1251
                BlockType mask = (0x1u << (a.bitsInBlock - 1));
9✔
1252
                unsigned nibble{ 0 };
9✔
1253
                unsigned rightShift = a.bitsInBlock - 4u;
9✔
1254
                for (int i = a.bitsInBlock - 1; i >= 0; --i) {
153✔
1255

1256
                        nibble |= (limb & mask);
144✔
1257
                        if ((i % 4) == 0) {
144✔
1258
                                nibble >>= rightShift;
36✔
1259
                                s << nibbleLookup[nibble];
36✔
1260
                                nibble = 0;
36✔
1261
                                rightShift -= 4u;
36✔
1262
                        }
1263
                        if (bitIndex > 0 && ((bitIndex % 16) == 0) && wordMarker) s << '\'';
144✔
1264
                        mask >>= 1;
144✔
1265
                        --bitIndex;
144✔
1266
                }
1267
        }
1268

1269
        return s.str();
3✔
1270

1271
}
3✔
1272

1273
//////////////////////////////////////////////////////////////////////////////////////////////////////
1274
// einteger - einteger binary logic operators
1275

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

1280
template<typename BlockType>
1281
constexpr bool operator==(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) noexcept {
11,103,412✔
1282
        if (lhs.limbs() != rhs.limbs()) {
11,103,412✔
UNCOV
1283
                return false;
×
1284
        }
1285
        if (lhs.sign() != rhs.sign()) {
11,103,412✔
1286
                // keep +0 == -0, but require sign match for non-zero values
1287
                return lhs.iszero() && rhs.iszero();
9✔
1288
        }
1289
        for (unsigned i = 0; i < lhs.limbs(); ++i) {
24,629,350✔
1290
                if (lhs._block[i] != rhs._block[i]) return false;
13,525,947✔
1291
        }
1292
        return true;
11,103,403✔
1293
}
1294

1295
template<typename BlockType>
1296
constexpr bool operator!=(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) noexcept {
11,103,410✔
1297
        return !operator==(lhs, rhs);
11,103,410✔
1298
}
1299

1300
template<typename BlockType>
1301
constexpr bool operator< (const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) noexcept {
6✔
1302
        const bool ls = lhs.sign();
6✔
1303
        const bool rs = rhs.sign();
6✔
1304
        if (ls != rs) {
6✔
1305
                // +0 and -0 are equal, not ordered
1306
                if (lhs.iszero() && rhs.iszero()) return false;
4✔
1307
                return ls; // negative < positive
4✔
1308
        }
1309
        unsigned ll = lhs.limbs();
2✔
1310
        unsigned rl = rhs.limbs();
2✔
1311
        // for negatives, larger magnitude means smaller value -- swap the sense
1312
        if (ll != rl) return ls ? (ll > rl) : (ll < rl);
2✔
1313
        if (ll == 0) return false; // both empty: equal in magnitude
2✔
1314
        for (unsigned b = ll; b-- > 0;) {
2✔
1315
                BlockType l = lhs.block(b);
2✔
1316
                BlockType r = rhs.block(b);
2✔
1317
                if (l == r) continue;
2✔
1318
                return ls ? (l > r) : (l < r);
2✔
1319
        }
UNCOV
1320
        return false; // lhs and rhs are the same
×
1321
}
1322

1323
template<typename BlockType>
1324
constexpr bool operator> (const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) noexcept {
2✔
1325
        return operator< (rhs, lhs);
2✔
1326
}
1327

1328
template<typename BlockType>
1329
constexpr bool operator<=(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) noexcept {
1✔
1330
        return operator< (lhs, rhs) || operator==(lhs, rhs);
1✔
1331
}
1332

1333
template<typename BlockType>
1334
constexpr bool operator>=(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) noexcept {
1✔
1335
        return !operator< (lhs, rhs);
1✔
1336
}
1337

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

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

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

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

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

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

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

1372
//////////////////////////////////////////////////////////////////////////////////////////////////////
1373
// literal - einteger binary logic operators
1374
// precondition is that the byte-storage is properly nulled in all arithmetic paths
1375

1376

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

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

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

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

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

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

1407
//////////////////////////////////////////////////////////////////////////////////////////////////////
1408

1409
//////////////////////////////////////////////////////////////////////////////////////////////////////
1410
// einteger - einteger binary arithmetic operators
1411

1412
template<typename BlockType>
1413
inline einteger<BlockType> operator+(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) {
3,145,986✔
1414
        einteger sum = lhs;
3,145,986✔
1415
        sum += rhs;
3,145,986✔
1416
        return sum;
3,145,986✔
UNCOV
1417
}
×
1418

1419
template<typename BlockType>
1420
inline einteger<BlockType> operator-(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) {
4,325,378✔
1421
        einteger diff = lhs;
4,325,378✔
1422
        diff -= rhs;
4,325,378✔
1423
        return diff;
4,325,378✔
UNCOV
1424
}
×
1425

1426
template<typename BlockType>
1427
inline einteger<BlockType> operator*(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) {
489,491✔
1428
        einteger product = lhs;
489,491✔
1429
        product *= rhs;
489,491✔
1430
        return product;
489,491✔
UNCOV
1431
}
×
1432

1433
template<typename BlockType>
1434
inline einteger<BlockType> operator/(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) {
2✔
1435
        einteger ratio = lhs;
2✔
1436
        ratio /= rhs;
2✔
1437
        return ratio;
2✔
UNCOV
1438
}
×
1439

1440
template<typename BlockType>
1441
inline einteger<BlockType> operator%(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) {
2✔
1442
        einteger remainder = lhs;
2✔
1443
        remainder %= rhs;
2✔
1444
        return remainder;
2✔
UNCOV
1445
}
×
1446

1447
//////////////////////////////////////////////////////////////////////////////////////////////////////
1448
// einteger - literal binary arithmetic operators
1449

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

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

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

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

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

1475
template<typename BlockType>
1476
inline einteger<BlockType> operator/(const einteger<BlockType>& lhs, unsigned long long rhs) {
1477
        return operator/(lhs, einteger<BlockType>(rhs));
1478
}
1479

1480
//////////////////////////////////////////////////////////////////////////////////////////////////////
1481
// literal - einteger binary arithmetic operators
1482

1483
template<typename BlockType>
1484
inline einteger<BlockType> operator+(long long lhs, const einteger<BlockType>& rhs) {
1485
        return operator+(einteger<BlockType>(lhs), rhs);
1486
}
1487

1488
template<typename BlockType>
1489
inline einteger<BlockType> operator-(long long lhs, const einteger<BlockType>& rhs) {
1490
        return operator-(einteger<BlockType>(lhs), rhs);
1491
}
1492

1493
template<typename BlockType>
1494
inline einteger<BlockType> operator*(long long lhs, const einteger<BlockType>& rhs) {
1495
        return operator*(einteger<BlockType>(lhs), rhs);
1496
}
1497

1498
template<typename BlockType>
1499
inline einteger<BlockType> operator/(long long lhs, const einteger<BlockType>& rhs) {
1500
        return operator/(einteger<BlockType>(lhs), rhs);
1501
}
1502

1503
template<typename BlockType>
1504
inline einteger<BlockType> operator%(long long lhs, const einteger<BlockType>& rhs) {
1505
        return operator/(einteger<BlockType>(lhs), rhs);
1506
}
1507

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