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

stillwater-sc / universal / 25645477877

11 May 2026 01:19AM UTC coverage: 83.948% (+0.01%) from 83.937%
25645477877

Pull #826

github

web-flow
Merge b631ace92 into 84ffa7c79
Pull Request #826: feat(einteger): partial constexpr support (API surface)

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

43 existing lines in 2 files now uncovered.

45879 of 54652 relevant lines covered (83.95%)

6473951.67 hits per line

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

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

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

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

23
namespace sw { namespace universal {
24

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

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

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

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

56
        // initializers for native types
57
        einteger(short initial_value)              { *this = initial_value; }
58
        einteger(int initial_value)                { *this = initial_value; }
29✔
59
        einteger(long initial_value)               { *this = initial_value; }
56✔
60
        einteger(long long initial_value)          { *this = initial_value; }
28✔
61
        einteger(unsigned int initial_value)       { *this = initial_value; }
761✔
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,424✔
69
        einteger& operator=(long rhs)               noexcept { return convert_signed(rhs); }
10,942,008✔
70
        einteger& operator=(long long rhs)          noexcept { return convert_signed(rhs); }
28✔
71
        einteger& operator=(unsigned int rhs)       noexcept { return convert_unsigned(rhs); }
2,093,905✔
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) {
115✔
92
                if (shift == 0) return *this;
115✔
93
                if (shift < 0) return operator>>=(-shift);
115✔
94

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

197
        // binary arithmetic operators
198
        einteger& operator+=(const einteger& rhs) {
3,146,015✔
199
                if (sign() != rhs.sign()) {
3,146,015✔
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,013✔
214
                auto rhsSize = rhs._block.size();
3,146,013✔
215
                if (lhsSize < rhsSize) _block.resize(rhsSize, 0);
3,146,013✔
216

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

301
                clear();
325,403✔
302
                std::uint64_t segment(0);
325,403✔
303
                for (unsigned i = 0; i < ll; ++i) {
650,903✔
304
                        for (unsigned j = 0; j < rl; ++j) {
651,000✔
305
                                segment += static_cast<std::uint64_t>(base.block(i)) * static_cast<std::uint64_t>(rhs.block(j));
325,500✔
306
                                segment += block(i + j);
325,500✔
307
                                setblock(i + j, static_cast<bt>(segment));
325,500✔
308
                                segment >>= bitsInBlock;
325,500✔
309
                        }
310
                }
311
                if (segment != 0) setblock(ll + rl - 1, static_cast<bt>(segment));
325,403✔
312
                setsign(ls ^ rs);
325,403✔
313
                return *this;
325,403✔
314
        }
325,403✔
315
        einteger& operator*=(long long rhs) {
26✔
316
                return *this *= einteger(rhs);
26✔
317
        }
318
        einteger& operator/=(const einteger& rhs) {
2✔
319
                einteger q, r;
2✔
320
                q.reduce(*this, rhs, r);
2✔
321
                *this = q;
2✔
322
                return *this;
2✔
323
        }
2✔
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,519✔
339
                if (b.iszero()) {
3,146,519✔
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,447✔
348
                r.clear();
3,143,447✔
349
                if (a.iszero()) return;
3,143,447✔
350

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

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

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

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

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

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

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

425
                                while (qhat >= BASE || qhat * v_nminus2 > BASE * rhat + normalized_a.block(j + n - 2)) {
520,090✔
426
                                        --qhat;
385✔
427
                                        rhat += divisor;
385✔
428
                                        if (rhat < BASE) continue;
385✔
429
                                }
430
                                std::uint64_t borrow{ 0 };
519,705✔
431
                                std::uint64_t diff{ 0 };
519,705✔
432
                                for (unsigned i = 0; i < n; ++i) {
1,559,151✔
433
                                        std::uint64_t p = qhat * normalized_b.block(i);
1,039,446✔
434
                                        diff = normalized_a.block(i + j) - static_cast<BlockType>(p) - borrow;
1,039,446✔
435
                                        normalized_a.setblock(i + j, static_cast<BlockType>(diff));
1,039,446✔
436
                                        borrow = (p >> bitsInBlock) - (diff >> bitsInBlock);
1,039,446✔
437
                                }
438
                                std::int64_t signedBorrow = static_cast<int64_t>(normalized_a.block(j + n) - borrow);
519,705✔
439
                                normalized_a.setblock(j + n, static_cast<BlockType>(signedBorrow));
519,705✔
440

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

443
                                setblock(static_cast<unsigned>(j), static_cast<BlockType>(qhat));
519,705✔
444
                                if (signedBorrow < 0) { // subtracted too much, add back
519,705✔
445
                                        std::cout << "subtracted too much, add back\n";
×
446
                                        setblock(static_cast<size_t>(j), static_cast<BlockType>(_block[static_cast<size_t>(j)] - 1));
×
447
                                        std::uint64_t carry{ 0 };
×
448
                                        for (unsigned i = 0; i < n; ++i) {
×
449
                                                carry += static_cast<std::uint64_t>(normalized_a.block(i + j)) + static_cast<std::uint64_t>(normalized_b.block(i));
×
450
                                                normalized_a.setblock(i + j, static_cast<BlockType>(carry));
×
451
                                                carry >>= 32;
×
452
                                        }
453
                                        BlockType rectified = static_cast<BlockType>(normalized_a.block(j + n) + carry);
×
454
                                        normalized_a.setblock(j + n, rectified);
×
455
                                }
456
                                //std::cout << "   updated a : " << normalized_a.showLimbs() << " : " << normalized_a.showLimbValues() << '\n';
457
                        }
458

459
                        // remainder needs to be normalized
460
                        for (unsigned i = 0; i < n - 1; ++i) {
1,039,411✔
461
                                std::uint64_t remainder = static_cast<std::uint64_t>(normalized_a.block(i) >> shift);
519,714✔
462
                                remainder |= (static_cast<std::uint64_t>(normalized_a.block(i + 1)) << (32 - shift));
519,714✔
463
                                r.setblock(i, static_cast<BlockType>(remainder));
519,714✔
464
                        }
465
                        r.setblock(n - 1, static_cast<BlockType>(normalized_a.block(n - 1) >> shift));
519,697✔
466
                }
519,697✔
467
                remove_leading_zeros();
2,612,953✔
468
                _sign = a.sign() ^ b.sign();
2,612,953✔
469
        }
470

471
        // modifiers (vector::clear is constexpr in C++20; on the empty
472
        // constant-evaluated state both clear() and setzero() are trivially
473
        // constexpr-clean.  setsign writes only the bool member.)
474
        constexpr void clear() noexcept { _sign = false; _block.clear(); }
44,154,148✔
475
        constexpr void setzero() noexcept { clear(); }
530,044✔
476
        constexpr void setsign(bool sign = true) noexcept { _sign = sign; }
6,811,287✔
477
        // use un-interpreted raw bits to set the bits of the einteger
478
        void setbits(unsigned long long value) {
23,448,855✔
479
                clear();
23,448,855✔
480
                if constexpr (bitsInBlock == 8) {
481
                        std::uint8_t byte0 = static_cast<std::uint8_t>(value & 0x0000'0000'0000'00FF);
6,033,287✔
482
                        std::uint8_t byte1 = static_cast<std::uint8_t>((value & 0x0000'0000'0000'FF00) >> 8);
6,033,287✔
483
                        std::uint8_t byte2 = static_cast<std::uint8_t>((value & 0x0000'0000'00FF'0000) >> 16);
6,033,287✔
484
                        std::uint8_t byte3 = static_cast<std::uint8_t>((value & 0x0000'0000'FF00'0000) >> 24);
6,033,287✔
485
                        std::uint8_t byte4 = static_cast<std::uint8_t>((value & 0x0000'00FF'0000'0000) >> 32);
6,033,287✔
486
                        std::uint8_t byte5 = static_cast<std::uint8_t>((value & 0x0000'FF00'0000'0000) >> 40);
6,033,287✔
487
                        std::uint8_t byte6 = static_cast<std::uint8_t>((value & 0x00FF'0000'0000'0000) >> 48);
6,033,287✔
488
                        std::uint8_t byte7 = static_cast<std::uint8_t>((value & 0xFF00'0000'0000'0000) >> 56);
6,033,287✔
489
                        if (byte7 > 0) {
6,033,287✔
490
                                _block.push_back(byte0);
5✔
491
                                _block.push_back(byte1);
5✔
492
                                _block.push_back(byte2);
5✔
493
                                _block.push_back(byte3);
5✔
494
                                _block.push_back(byte4);
5✔
495
                                _block.push_back(byte5);
5✔
496
                                _block.push_back(byte6);
5✔
497
                                _block.push_back(byte7);
5✔
498
                        }
499
                        else if (byte6 > 0) {
6,033,282✔
500
                                _block.push_back(byte0);
6✔
501
                                _block.push_back(byte1);
6✔
502
                                _block.push_back(byte2);
6✔
503
                                _block.push_back(byte3);
6✔
504
                                _block.push_back(byte4);
6✔
505
                                _block.push_back(byte5);
6✔
506
                                _block.push_back(byte6);
6✔
507
                        }
508
                        else if (byte5 > 0) {
6,033,276✔
509
                                _block.push_back(byte0);
7✔
510
                                _block.push_back(byte1);
7✔
511
                                _block.push_back(byte2);
7✔
512
                                _block.push_back(byte3);
7✔
513
                                _block.push_back(byte4);
7✔
514
                                _block.push_back(byte5);
7✔
515
                        }
516
                        else if (byte4 > 0) {
6,033,269✔
517
                                _block.push_back(byte0);
6✔
518
                                _block.push_back(byte1);
6✔
519
                                _block.push_back(byte2);
6✔
520
                                _block.push_back(byte3);
6✔
521
                                _block.push_back(byte4);
6✔
522
                        }
523
                        else if (byte3 > 0) {
6,033,263✔
524
                                _block.push_back(byte0);
7✔
525
                                _block.push_back(byte1);
7✔
526
                                _block.push_back(byte2);
7✔
527
                                _block.push_back(byte3);
7✔
528
                        }
529
                        else if (byte2 > 0) {
6,033,256✔
530
                                _block.push_back(byte0);
523,820✔
531
                                _block.push_back(byte1);
523,820✔
532
                                _block.push_back(byte2);
523,820✔
533
                        }
534
                        else if (byte1 > 0) {
5,509,436✔
535
                                _block.push_back(byte0);
4,058,693✔
536
                                _block.push_back(byte1);
4,058,693✔
537
                        }
538
                        else if (byte0 > 0) {
1,450,743✔
539
                                _block.push_back(byte0);
1,447,120✔
540
                        }
541
                        else {
542
                                _block.clear();
3,623✔
543
                        }
544
                }
545
                else if constexpr (bitsInBlock == 16) {
546
                        std::uint16_t word0 = static_cast<std::uint16_t>( value & 0x0000'0000'0000'FFFF);
7,724,835✔
547
                        std::uint16_t word1 = static_cast<std::uint16_t>((value & 0x0000'0000'FFFF'0000) >> 16);
7,724,835✔
548
                        std::uint16_t word2 = static_cast<std::uint16_t>((value & 0x0000'FFFF'0000'0000) >> 32);
7,724,835✔
549
                        std::uint16_t word3 = static_cast<std::uint16_t>((value & 0xFFFF'0000'0000'0000) >> 48);
7,724,835✔
550
                        if (word3 > 0) {
7,724,835✔
551
                                _block.push_back(word0);
×
552
                                _block.push_back(word1);
×
553
                                _block.push_back(word2);
×
554
                                _block.push_back(word3);
×
555
                        }
556
                        else if (word2 > 0) {
7,724,835✔
557
                                _block.push_back(word0);
×
558
                                _block.push_back(word1);
×
559
                                _block.push_back(word2);
×
560
                        }
561
                        else if (word1 > 0) {
7,724,835✔
562
                                _block.push_back(word0);
588,839✔
563
                                _block.push_back(word1);
588,839✔
564
                        }
565
                        else if (word0 > 0) {
7,135,996✔
566
                                _block.push_back(word0);
7,132,149✔
567
                        }
568
                        else {
569
                                _block.clear();
3,847✔
570
                        }
571
                }
572
                else if constexpr (bitsInBlock == 32) {
573
                        std::uint32_t low  = static_cast<std::uint32_t>(value & 0x0000'0000'FFFF'FFFF);
9,690,733✔
574
                        std::uint32_t high = static_cast<std::uint32_t>((value & 0xFFFF'FFFF'0000'0000) >> bitsInBlock);
9,690,733✔
575
                        if (high > 0) {
9,690,733✔
576
                                _block.push_back(low);
587,344✔
577
                                _block.push_back(high);
587,344✔
578
                        }
579
                        else if (low > 0) {
9,103,389✔
580
                                _block.push_back(low);
9,098,775✔
581
                        }
582
                        else {
583
                                _block.clear();
4,614✔
584
                        }
585
                }
586
        }
23,448,855✔
587
        void setblock(unsigned i, BlockType value) noexcept {
6,238,103✔
588
                if (i >= _block.size()) _block.resize(i+1ull);
6,238,103✔
589
                _block[i] = value;
6,238,103✔
590
        }
6,238,103✔
591
        void setbyte(unsigned i, std::uint8_t byte) noexcept {
×
592
                std::cerr << "setbyte(" << i << ", " << int(byte) << ") TBD\n";
×
593
        }
×
594
        einteger& assign(const std::string& txt) {
2✔
595
                if (!parse(txt, *this)) {
2✔
596
                        std::cerr << "Unable to parse: " << txt << std::endl;
×
597
                }
598
                return *this;
2✔
599
        }
600

601
        // selectors (read-only access to _sign and _block; constexpr-callable
602
        // on the empty default-constructed state and on any persisted
603
        // constexpr einteger -- which by definition has empty _block)
604
        constexpr bool iszero() const noexcept { return (_block.size() == 0 || ((_block.size() == 1) && _block[0] == bt(0))); }
6,948,632✔
605
        constexpr bool isone()  const noexcept { return true; }
606
        constexpr bool isodd()  const noexcept { return (_block.size() > 0) ? (_block[0] & 0x1) : false; }
607
        constexpr bool iseven() const noexcept { return !isodd(); }
608
        constexpr bool ispos()  const noexcept { return !_sign; }
609
        constexpr bool isneg()  const noexcept { return _sign; }
147✔
610

611
        constexpr bool test(unsigned index) const noexcept {
241,142,552✔
612
                if (index < nbits()) {
241,142,552✔
613
                        unsigned blockIndex = index / bitsInBlock;
241,142,552✔
614
                        unsigned bitIndexInBlock = index % bitsInBlock;
241,142,552✔
615
                        BlockType data = _block[blockIndex];
241,142,552✔
616
                        BlockType mask = (0x1u << bitIndexInBlock);
241,142,552✔
617
                        if (data & mask) return true;
241,142,552✔
618
                }
619
                return false;
186,817,975✔
620
        }
621
        constexpr bool sign()   const noexcept { return _sign; }
27,451,382✔
622
        constexpr int scale()   const noexcept { return findMsb(); } // TODO: when value = 0, scale returns -1 which is incorrect
623

624
        constexpr BlockType block(unsigned b) const noexcept {
12,954,814✔
625
                if (b < _block.size()) return _block[b];
12,954,814✔
626
                return static_cast<BlockType>(0u);
325,519✔
627
        }
628
        constexpr unsigned limbs() const noexcept { return static_cast<unsigned>(_block.size()); }
64,962,422✔
629

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

632
        // findMsb takes an einteger reference and returns the position of the most significant bit, -1 if v == 0
633
        constexpr int findMsb() const noexcept {
634
                int nrBlocks = static_cast<int>(_block.size());
635
                if (nrBlocks == 0) return -1; // no significant bit found, all bits are zero
636
                int msb = nrBlocks * static_cast<int>(bitsInBlock);
637
                for (int b = nrBlocks - 1; b >= 0; --b) {
638
                        std::uint32_t segment = _block[static_cast<size_t>(b)];
639
                        std::uint32_t mask = 0x8000'0000ul;
640
                        for (int i = bitsInBlock - 1; i >= 0; --i) {
641
                                --msb;
642
                                if (segment & mask) return msb;
643
                                mask >>= 1;
644
                        }
645
                }
646
                return -1; // no significant bit found, all bits are zero
647
        }
648

649
        // convert to string containing digits number of digits
650
        std::string str(size_t nrDigits = 0) const {
651
                return std::string("tbd");
652
        }
653

654
        // show the binary encodings of the limbs
655
        std::string showLimbs() const {
656
                if (_block.empty()) return "no limbs";
657
                std::stringstream s;
658
                size_t i = _block.size() - 1;
659
                while (i > 0) {
660
                        s << to_binary(_block[i], sizeof(BlockType) * 8, true) << ' ';
661
                        --i;
662
                }
663
                s << to_binary(_block[0], sizeof(BlockType) * 8, true);
664
                return s.str();
665
        }
666
        // show the values of the limbs as a radix-BlockType number
667
        std::string showLimbValues() const {
668
                if (_block.empty()) return "no limbs";
669
                std::stringstream s;
670
                size_t i = _block.size() - 1;
671
                while (i > 0) {
672
                        s << std::setw(5) << unsigned(_block[i]) << ", ";
673
                        --i;
674
                }
675
                s << std::setw(5) << unsigned(_block[0]);
676
                return s.str();
677
        }
678

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

683
        // HELPER methods
684
        // compare_magnitude returns 1 if a > b, 0 if they are equal, and -1 if a < b
685
        int compare_magnitude(const einteger& a, const einteger& b) {
4,320,770✔
686
                unsigned aLimbs = a.limbs();
4,320,770✔
687
                unsigned bLimbs = b.limbs();
4,320,770✔
688
                if (aLimbs != bLimbs) {
4,320,770✔
689
                        return (aLimbs > bLimbs ? 1 : -1);  // return 1 if a > b, otherwise -1
396,283✔
690
                }
691
                for (int i = static_cast<int>(aLimbs) - 1; i >= 0; --i) {
4,125,697✔
692
                        BlockType _a = a._block[static_cast<size_t>(i)];
4,121,095✔
693
                        BlockType _b = b._block[static_cast<size_t>(i)];
4,121,095✔
694
                        if ( _a != _b) {
4,121,095✔
695
                                return (_a > _b ? 1 : -1);
3,919,885✔
696
                        }
697
                }
698
                return 0;
4,602✔
699
        }
700
        void remove_leading_zeros() {
6,937,472✔
701
                unsigned leadingZeroBlocks{ 0 };
6,937,472✔
702
                typename std::vector<BlockType>::reverse_iterator rit = _block.rbegin();
6,937,472✔
703
                while (rit != _block.rend()) {
7,336,596✔
704
                        if (*rit == 0) {
6,286,338✔
705
                                ++leadingZeroBlocks;
399,124✔
706
                        }
707
                        else {
708
                                break;
5,887,214✔
709
                        }
710
                        ++rit;
399,124✔
711
                }
712
                _block.resize(_block.size() - leadingZeroBlocks);
6,937,472✔
713
        }
6,937,472✔
714
        
715
        template<typename SignedInt>
716
        einteger& convert_signed(SignedInt v) {
13,036,460✔
717
                clear();
13,036,460✔
718
                if (v != 0) {
13,036,460✔
719
                        if (v < 0) {
10,927,789✔
720
                                setbits(static_cast<unsigned long long>(-v));
2,160,386✔
721
                                setsign(true);
2,160,386✔
722
                        }
723
                        else {
724
                                setbits(static_cast<unsigned long long>(v)); // TODO: what about -2^63
8,767,403✔
725
                        }
726
                }
727
                return *this;
13,036,460✔
728
        }
729

730
        template<typename UnsignedInt>
731
        einteger& convert_unsigned(UnsignedInt v) {
2,093,905✔
732
                if (0 == v) {
2,093,905✔
733
                        setzero();
530,044✔
734
                }
735
                else {
736
                        setbits(static_cast<unsigned long long>(v));
1,563,861✔
737
                }
738
                return *this;
2,093,905✔
739
        }
740

741
        template<typename Real>
742
        einteger& convert_ieee754(Real& rhs) {
118✔
743
                clear();
118✔
744
                bool s{ false };
118✔
745
                std::uint64_t rawExponent{ 0 };
118✔
746
                std::uint64_t rawFraction{ 0 };
118✔
747
                uint64_t bits{ 0 };
118✔
748
                extractFields(rhs, s, rawExponent, rawFraction, bits);
118✔
749
                if (rawExponent == ieee754_parameter<Real>::eallset) { // nan and inf
118✔
750
                        // we can't represent NaNs or Infinities
751
                        return *this;
3✔
752
                }
753
                int exponent = static_cast<int>(rawExponent) - ieee754_parameter<Real>::bias;
115✔
754
                if (exponent < 0) {
115✔
UNCOV
755
                        return *this; // we are zero
×
756
                }
757
                // normal and subnormal handling
758
                constexpr size_t fbits = ieee754_parameter<Real>::fbits;
115✔
759
                std::uint64_t hiddenBit = (0x1ull << fbits);
115✔
760
                rawFraction |= hiddenBit;
115✔
761
                setbits(rawFraction);
115✔
762
                setsign(s);
115✔
763
                // scale the fraction bits
764
                *this <<= static_cast<int>(exponent - fbits);
115✔
765
                return *this;
115✔
766
        }
767

768
        template<typename Integer>
769
        Integer convert_to_native_integer() const noexcept {
10,957,141✔
770
                using Unsigned = std::make_unsigned_t<Integer>;
771
                Unsigned v{ 0 };
10,957,141✔
772
                Unsigned m{ 1 };
10,957,141✔
773
                const bool neg = sign();
10,957,141✔
774
                constexpr Integer kMax = std::numeric_limits<Integer>::max();
10,957,141✔
775
                constexpr Integer kMin = std::numeric_limits<Integer>::min();
10,957,141✔
776
                constexpr unsigned kDigits = std::numeric_limits<Unsigned>::digits;
10,957,141✔
777
                for (unsigned i = 0; i < nbits(); ++i) {
252,091,045✔
778
                        if (i >= kDigits) {
241,133,904✔
UNCOV
779
                                if (test(i)) return neg ? kMin : kMax;
×
UNCOV
780
                                continue;
×
781
                        }
782
                        if (test(i)) {
241,133,904✔
783
                                if (v > (static_cast<Unsigned>(kMax) - m)) {
54,323,308✔
UNCOV
784
                                        return neg ? kMin : kMax;
×
785
                                }
786
                                v += m;
54,323,308✔
787
                        }
788
                        m <<= 1;
241,133,904✔
789
                }
790
                return (neg ? -static_cast<Integer>(v) : static_cast<Integer>(v));
10,957,141✔
791
        }
792
        template<typename Real>
793
        Real convert_to_native_ieee() const noexcept {
117✔
794
                Real v{ 0 };
117✔
795
                Real m{ 1.0 };
117✔
796
                for (unsigned i = 0; i < nbits(); ++i) {
8,765✔
797
                        if (test(i)) {
8,648✔
798
                                v += m;
1,269✔
799
                        }
800
                        m *= Real(2.0);
8,648✔
801
                }
802
                return (sign() ? -v : v);
117✔
803
        }
804

805
private:
806

807
        template<typename BBlockType>
808
        friend constexpr bool operator==(const einteger<BBlockType>&, const einteger<BBlockType>&) noexcept;
809
};
810

811
////////////////////////    einteger functions   /////////////////////////////////
812

813
template<typename BlockType>
814
inline einteger<BlockType> abs(const einteger<BlockType>& a) {
815
        return (a.isneg()  ? -a : a);
816
}
817

818
////////////////////////    INTEGER operators   /////////////////////////////////
819

820
/// stream operators
821

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

993
template<typename BlockType>
994
std::string convert_to_string(std::ios_base::fmtflags flags, const einteger<BlockType>& n) {
152✔
995
        using AdaptiveInteger = einteger<BlockType>;
996

997
        if (n.limbs() == 0) return std::string("0");
162✔
998

999
        // set the base of the target number system to convert to
1000
        int base = 10;
147✔
1001
        if ((flags & std::ios_base::oct) == std::ios_base::oct) base = 8;
147✔
1002
        if ((flags & std::ios_base::hex) == std::ios_base::hex) base = 16;
147✔
1003

1004
        unsigned nbits = n.limbs() * sizeof(BlockType) * 8;
147✔
1005

1006
        std::string result;
147✔
1007
        if (base == 8 || base == 16) {
147✔
UNCOV
1008
                if (n.sign()) return std::string("negative value: ignored");
×
1009

UNCOV
1010
                size_t shift = (base == 8 ? 3ull : 4ull);
×
1011
                BlockType mask = static_cast<BlockType>((1u << shift) - 1);
×
UNCOV
1012
                AdaptiveInteger t(n);
×
1013
                result.assign(nbits / shift + ((nbits % shift) ? 1 : 0), '0');
×
1014
                size_t pos = result.size() - 1ull;
×
1015
                for (size_t i = 0; i < nbits / shift; ++i) {
×
1016
                        char c = '0' + static_cast<char>(t.block(0) & mask);
×
1017
                        if (c > '9')
×
1018
                                c += 'A' - '9' - 1;
×
1019
                        result[pos--] = c;
×
1020
                        t >>= static_cast<int>(shift);
×
1021
                }
1022
                if (nbits % shift) {
×
1023
                        mask = static_cast<BlockType>((1u << (nbits % shift)) - 1);
×
UNCOV
1024
                        char c = '0' + static_cast<char>(t.block(0) & mask);
×
1025
                        if (c > '9')
×
1026
                                c += 'A' - '9';
×
1027
                        result[pos] = c;
×
1028
                }
1029
                //
1030
                // Get rid of leading zeros:
1031
                //
UNCOV
1032
                std::string::size_type fnz = result.find_first_not_of('0');
×
UNCOV
1033
                if (!result.empty() && (fnz == std::string::npos)) fnz = result.size() - 1;
×
UNCOV
1034
                result.erase(0, fnz);
×
1035
                if (flags & std::ios_base::showbase) {
×
1036
                        const char* pp = base == 8 ? "0" : "0x";
×
1037
                        result.insert(static_cast<std::string::size_type>(0), pp);
×
1038
                }
1039
        }
×
1040
        else {
1041
                unsigned block10;
1042
                unsigned digits_in_block10;
1043
                if constexpr (AdaptiveInteger::bitsInBlock == 8) {
1044
                        block10 = 100u;
67✔
1045
                        digits_in_block10 = 2;
67✔
1046
                }
1047
                else if constexpr (AdaptiveInteger::bitsInBlock == 16) {
1048
                        block10 = 10'000ul;
41✔
1049
                        digits_in_block10 = 4;
41✔
1050
                }
1051
                else if constexpr (AdaptiveInteger::bitsInBlock == 32) {
1052
                        block10 = 1'000'000'000ul;
39✔
1053
                        digits_in_block10 = 9;
39✔
1054
                }
1055
                else if constexpr (AdaptiveInteger::bitsInBlock == 64) {
1056
                        // not allowed as the whole multi-digit arithmetic
1057
                        // requires that there is a 'larger' type that
1058
                        // can receive carries and borrows.
1059
                        // If your platform does have a native 128bit
1060
                        // integer, this could be enabled
1061
                        //block10 = 1'000'000'000'000'000'000ull;
1062
                        //digits_in_block10 = 18;
1063
                }
1064
                result.assign(nbits / 3 + 1ull, '0');
147✔
1065
                size_t pos = result.size() - 1ull;
147✔
1066
                AdaptiveInteger t(n);
147✔
1067
                while (!t.iszero()) {
1,669✔
1068
                        AdaptiveInteger q,r;
761✔
1069
                        q.reduce(t, block10, r);
761✔
1070
                        BlockType v = r.block(0);
761✔
1071
//                        std::cout << "v  " << uint32_t(v) << '\n';
1072
                        for (unsigned i = 0; i < digits_in_block10; ++i) {
3,408✔
1073
                                char c = '0' + static_cast<char>(v % 10);
2,663✔
1074
                                v /= 10;
2,663✔
1075
                                result[pos] = c;
2,663✔
1076
//                                std::cout << result << " pos: " << pos << '\n';
1077
                                if (pos-- == 0)        break;
2,663✔
1078
                        }
1079
                        t = q;
761✔
1080
                }
1081

1082
                std::string::size_type firstDigit = result.find_first_not_of('0');
147✔
1083
                result.erase(0, firstDigit);
147✔
1084
                if (result.empty())
147✔
1085
                        result = "0";
1✔
1086
                if (n.isneg())
147✔
1087
                        result.insert(0ull, 1ull, '-');
52✔
1088
                else if (flags & std::ios_base::showpos)
95✔
UNCOV
1089
                        result.insert(0ull, 1ull, '+');
×
1090
        }
147✔
1091
        return result;
147✔
1092
}
147✔
1093

1094
// generate an einteger format ASCII format
1095
template<typename BlockType>
1096
inline std::ostream& operator<<(std::ostream& ostr, const einteger<BlockType>& i) {
152✔
1097
        std::string s = convert_to_string(ostr.flags(), i);
152✔
1098
        std::streamsize width = ostr.width();
152✔
1099
        if (width > static_cast<std::streamsize>(s.size())) {
152✔
UNCOV
1100
                char fill = ostr.fill();
×
UNCOV
1101
                if ((ostr.flags() & std::ios_base::left) == std::ios_base::left)
×
UNCOV
1102
                        s.append(static_cast<std::string::size_type>(width - s.size()), fill);
×
1103
                else
1104
                        s.insert(static_cast<std::string::size_type>(0), static_cast<std::string::size_type>(width - s.size()), fill);
×
1105
        }
1106
        return ostr << s;
304✔
1107
}
152✔
1108

1109
// read an ASCII einteger format
1110

1111
template<typename BlockType>
1112
inline std::istream& operator>>(std::istream& istr, einteger<BlockType>& p) {
1113
        std::string txt;
1114
        istr >> txt;
1115
        if (!parse(txt, p)) {
1116
                std::cerr << "unable to parse -" << txt << "- into a posit value\n";
1117
        }
1118
        return istr;
1119
}
1120

1121
////////////////// string operators
1122

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

1127
        std::stringstream s;
118✔
1128
        s << "0b";
118✔
1129
        for (int b = static_cast<int>(a.limbs()) - 1; b >= 0; --b) {
737✔
1130
                BlockType segment = a.block(static_cast<size_t>(b));
619✔
1131
                BlockType mask = (0x1u << (a.bitsInBlock - 1));
619✔
1132
                for (int i = a.bitsInBlock - 1; i >= 0; --i) {
9,427✔
1133
                        s << ((segment & mask) ? '1' : '0');
8,808✔
1134
                        if (i > 0 && (i % 4) == 0 && nibbleMarker) s << '\'';
8,808✔
1135
                        if (b > 0 && i == 0 && nibbleMarker) s << '\'';
8,808✔
1136
                        mask >>= 1;
8,808✔
1137
                }
1138
        }
1139

1140
        return s.str();
118✔
1141
}
118✔
1142

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

1147
        std::vector<char> nibbleLookup = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
6✔
1148
        std::stringstream s;
3✔
1149
        s << "0x";
3✔
1150
        unsigned bitIndex = a.limbs() * a.bitsInBlock - 1u;
3✔
1151
        for (int b = static_cast<int>(a.limbs()) - 1; b >= 0; --b) {
11✔
1152
                BlockType limb = a.block(static_cast<size_t>(b));
8✔
1153
                BlockType mask = (0x1u << (a.bitsInBlock - 1));
8✔
1154
                unsigned nibble{ 0 };
8✔
1155
                unsigned rightShift = a.bitsInBlock - 4u;
8✔
1156
                for (int i = a.bitsInBlock - 1; i >= 0; --i) {
136✔
1157

1158
                        nibble |= (limb & mask);
128✔
1159
                        if ((i % 4) == 0) {
128✔
1160
                                nibble >>= rightShift;
32✔
1161
                                s << nibbleLookup[nibble];
32✔
1162
                                nibble = 0;
32✔
1163
                                rightShift -= 4u;
32✔
1164
                        }
1165
                        if (bitIndex > 0 && ((bitIndex % 16) == 0) && wordMarker) s << '\'';
128✔
1166
                        mask >>= 1;
128✔
1167
                        --bitIndex;
128✔
1168
                }
1169
        }
1170

1171
        return s.str();
3✔
1172

1173
}
3✔
1174

1175
//////////////////////////////////////////////////////////////////////////////////////////////////////
1176
// einteger - einteger binary logic operators
1177

1178
// equal: precondition is that the storage is properly nulled in all arithmetic paths
1179
// (constexpr-callable: pure reads of the digit vector, no allocation)
1180

1181
template<typename BlockType>
1182
constexpr bool operator==(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) noexcept {
11,466,317✔
1183
        if (lhs.limbs() != rhs.limbs()) {
11,466,317✔
1184
                return false;
3,648✔
1185
        }
1186
        for (unsigned i = 0; i < lhs.limbs(); ++i) {
24,361,893✔
1187
                if (lhs._block[i] != rhs._block[i]) return false;
13,418,914✔
1188
        }
1189
        return true;
10,942,979✔
1190
}
1191

1192
template<typename BlockType>
1193
constexpr bool operator!=(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) noexcept {
10,941,952✔
1194
        return !operator==(lhs, rhs);
10,941,952✔
1195
}
1196

1197
template<typename BlockType>
1198
constexpr bool operator< (const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) noexcept {
1,047,122✔
1199
        unsigned ll = lhs.limbs();
1,047,122✔
1200
        unsigned rl = rhs.limbs();
1,047,122✔
1201
        if (ll < rl) return true;
1,047,122✔
1202
        if (ll > rl) return false;
1,044,062✔
1203
        if (ll == 0) return false; // both empty: equal in magnitude
1,040,414✔
1204
        for (unsigned b = ll - 1; b > 0; --b) {
1,044,550✔
1205
                BlockType l = lhs.block(b);
1,040,456✔
1206
                BlockType r = rhs.block(b);
1,040,456✔
1207
                if (l < r) return true;
1,040,456✔
1208
                else if (l == r) continue;
522,296✔
1209
                else return false;
518,160✔
1210
        }
1211
        BlockType l = lhs.block(0);
4,094✔
1212
        BlockType r = rhs.block(0);
4,094✔
1213
        if (l < r) return true;
4,094✔
1214
        return false; // lhs and rhs are the same
2,557✔
1215
}
1216

1217
template<typename BlockType>
1218
constexpr bool operator> (const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) noexcept {
1219
        return operator< (rhs, lhs);
1220
}
1221

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

1227
template<typename BlockType>
1228
constexpr bool operator>=(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) noexcept {
1229
        return !operator< (lhs, rhs);
1230
}
1231

1232
//////////////////////////////////////////////////////////////////////////////////////////////////////
1233
// einteger - literal binary logic operators
1234
// equal: precondition is that the byte-storage is properly nulled in all arithmetic paths
1235

1236
template<typename BlockType>
1237
inline bool operator==(const einteger<BlockType>& lhs, long long rhs) {
1238
        return operator==(lhs, einteger(rhs));
1239
}
1240

1241
template<typename BlockType>
1242
inline bool operator!=(const einteger<BlockType>& lhs, long long rhs) {
1243
        return !operator==(lhs, rhs);
1244
}
1245

1246
template<typename BlockType>
1247
inline bool operator< (const einteger<BlockType>& lhs, long long rhs) {
1248
        return operator<(lhs, einteger<BlockType>(rhs));
1249
}
1250

1251
template<typename BlockType>
1252
inline bool operator> (const einteger<BlockType>& lhs, long long rhs) {
1253
        return operator< (einteger<BlockType>(rhs), lhs);
1254
}
1255

1256
template<typename BlockType>
1257
inline bool operator<=(const einteger<BlockType>& lhs, long long rhs) {
1258
        return operator< (lhs, rhs) || operator==(lhs, rhs);
1259
}
1260

1261
template<typename BlockType>
1262
inline bool operator>=(const einteger<BlockType>& lhs, long long rhs) {
1263
        return !operator< (lhs, rhs);
1264
}
1265

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

1270

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

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

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

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

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

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

1301
//////////////////////////////////////////////////////////////////////////////////////////////////////
1302

1303
//////////////////////////////////////////////////////////////////////////////////////////////////////
1304
// einteger - einteger binary arithmetic operators
1305

1306
template<typename BlockType>
1307
inline einteger<BlockType> operator+(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) {
3,145,986✔
1308
        einteger sum = lhs;
3,145,986✔
1309
        sum += rhs;
3,145,986✔
1310
        return sum;
3,145,986✔
UNCOV
1311
}
×
1312

1313
template<typename BlockType>
1314
inline einteger<BlockType> operator-(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) {
4,325,378✔
1315
        einteger diff = lhs;
4,325,378✔
1316
        diff -= rhs;
4,325,378✔
1317
        return diff;
4,325,378✔
UNCOV
1318
}
×
1319

1320
template<typename BlockType>
1321
inline einteger<BlockType> operator*(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) {
327,965✔
1322
        einteger product = lhs;
327,965✔
1323
        product *= rhs;
327,965✔
1324
        return product;
327,965✔
UNCOV
1325
}
×
1326

1327
template<typename BlockType>
1328
inline einteger<BlockType> operator/(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) {
2✔
1329
        einteger ratio = lhs;
2✔
1330
        ratio /= rhs;
2✔
1331
        return ratio;
2✔
UNCOV
1332
}
×
1333

1334
template<typename BlockType>
1335
inline einteger<BlockType> operator%(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) {
2✔
1336
        einteger remainder = lhs;
2✔
1337
        remainder %= rhs;
2✔
1338
        return remainder;
2✔
UNCOV
1339
}
×
1340

1341
//////////////////////////////////////////////////////////////////////////////////////////////////////
1342
// einteger - literal binary arithmetic operators
1343

1344
template<typename BlockType>
1345
inline einteger<BlockType> operator+(const einteger<BlockType>& lhs, long long rhs) {
1346
        return operator+(lhs, einteger<BlockType>(rhs));
1347
}
1348

1349
template<typename BlockType>
1350
inline einteger<BlockType> operator-(const einteger<BlockType>& lhs, long long rhs) {
1351
        return operator-(lhs, einteger<BlockType>(rhs));
1352
}
1353

1354
template<typename BlockType>
1355
inline einteger<BlockType> operator*(const einteger<BlockType>& lhs, long long rhs) {
2✔
1356
        return operator*(lhs, einteger<BlockType>(rhs));
2✔
1357
}
1358

1359
template<typename BlockType>
1360
inline einteger<BlockType> operator/(const einteger<BlockType>& lhs, long long rhs) {
1361
        return operator/(lhs, einteger<BlockType>(rhs));
1362
}
1363

1364
template<typename BlockType>
1365
inline einteger<BlockType> operator%(const einteger<BlockType>& lhs, long long rhs) {
1366
        return operator/(lhs, einteger<BlockType>(rhs));
1367
}
1368

1369
template<typename BlockType>
1370
inline einteger<BlockType> operator/(const einteger<BlockType>& lhs, unsigned long long rhs) {
1371
        return operator/(lhs, einteger<BlockType>(rhs));
1372
}
1373

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

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

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

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

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

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

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