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

stillwater-sc / universal / 25994639561

17 May 2026 03:15PM UTC coverage: 84.051% (+0.02%) from 84.033%
25994639561

push

github

web-flow
feat: operator>> hygiene + ereal nan/inf for decimal/elastic family (Phase E of #835) (#858)

* feat: operator>> hygiene + ereal nan/inf token routing for decimal/elastic family (Phase E of #835)

Phase E wraps up the decimal/elastic family with minimal API parity work.
Six types covered (dfloat, einteger, edecimal, erational, efloat, ereal);
full-implementation work for each is captured in dedicated tracking
issues #852-#857 since the gaps vary significantly per type.

What ships in this PR (per type):

dfloat:
  - operator>> extraction guard + setstate(failbit). Existing assign()
    already implements full nan/inf-aware decimal parsing; no parse()
    body changes here.

einteger:
  - operator>> extraction guard + setstate(failbit).
  - Fixed pre-existing copy-paste typo "into a posit value" ->
    "into an einteger value".

edecimal:
  - operator>> extraction guard + setstate(failbit).

erational:
  - operator>> extraction guard + setstate(failbit).

efloat:
  - operator>> extraction guard + setstate(failbit). parse() body
    remains a stub; full implementation tracked in #856.

ereal:
  - operator>> extraction guard + setstate(failbit).
  - Added nan / inf / infinity token detection (case-insensitive,
    optional sign) before the digit-accumulation loop, routing to
    setnan / setinf(sign). The digit loop would otherwise reject any
    alphabetic character outright.

Tests (6 new files, ReportTestSuite pattern):
  - static/float/dfloat/conversion/string_parse.cpp
  - elastic/einteger/conversion/string_parse.cpp
  - elastic/decimal/conversion/string_parse.cpp
  - elastic/rational/decimal/conversion/string_parse.cpp
  - elastic/efloat/conversion/string_parse.cpp
  - elastic/ereal/conversion/string_parse.cpp

Each covers: canonical parse where supported, nan/inf token routing
where supported, operator>> failbit on a bad token. Tests intentionally
stay narrow -- broader coverage will land with the per-type full
implementations in #852-#857.

Verif... (continued)

75 of 80 new or added lines in 6 files covered. (93.75%)

32 existing lines in 3 files now uncovered.

46609 of 55453 relevant lines covered (84.05%)

6432960.87 hits per line

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

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

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

56
        // initializers for native types
57
        einteger(short initial_value)              { *this = initial_value; }
58
        einteger(int initial_value)                { *this = initial_value; }
46✔
59
        einteger(long initial_value)               { *this = initial_value; }
56✔
60
        einteger(long long initial_value)          { *this = initial_value; }
35✔
61
        einteger(unsigned int initial_value)       { *this = initial_value; }
589✔
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,353✔
69
        einteger& operator=(long rhs)               noexcept { return convert_signed(rhs); }
10,942,008✔
70
        einteger& operator=(long long rhs)          noexcept { return convert_signed(rhs); }
35✔
71
        einteger& operator=(unsigned int rhs)       noexcept { return convert_unsigned(rhs); }
2,093,733✔
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,022✔
199
                if (sign() != rhs.sign()) {
3,146,022✔
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,020✔
214
                auto rhsSize = rhs._block.size();
3,146,020✔
215
                if (lhsSize < rhsSize) _block.resize(rhsSize, 0);
3,146,020✔
216

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

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

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

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

379
                        // single limb divisor
380
                        if (n == 1) {
523,175✔
381
                                _block.resize(m);
3,478✔
382
                                std::uint64_t remainder{ 0 };
3,478✔
383
                                auto divisor = b.block(0);
3,478✔
384
                                for (unsigned j = m; j > 0; --j) {
12,131✔
385
                                        std::uint64_t dividend = remainder * BASE + static_cast<std::uint64_t>(a.block(j - 1));
8,653✔
386
                                        std::uint64_t limbQuotient = dividend / divisor;
8,653✔
387
                                        _block[j - 1] = static_cast<BlockType>(limbQuotient);
8,653✔
388
                                        remainder = dividend - limbQuotient * divisor;
8,653✔
389
                                }
390
                                remove_leading_zeros();
3,478✔
391
                                r.setblock(0, static_cast<BlockType>(remainder));
3,478✔
392
                                return;
3,478✔
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,909✔
468
                _sign = a.sign() ^ b.sign();
2,612,909✔
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,153,591✔
475
        constexpr void setzero() noexcept { clear(); }
530,044✔
476
        constexpr void setsign(bool sign = true) noexcept { _sign = sign; }
6,811,304✔
477
        // use un-interpreted raw bits to set the bits of the einteger
478
        void setbits(unsigned long long value) {
23,448,653✔
479
                clear();
23,448,653✔
480
                if constexpr (bitsInBlock == 8) {
481
                        std::uint8_t byte0 = static_cast<std::uint8_t>(value & 0x0000'0000'0000'00FF);
6,033,282✔
482
                        std::uint8_t byte1 = static_cast<std::uint8_t>((value & 0x0000'0000'0000'FF00) >> 8);
6,033,282✔
483
                        std::uint8_t byte2 = static_cast<std::uint8_t>((value & 0x0000'0000'00FF'0000) >> 16);
6,033,282✔
484
                        std::uint8_t byte3 = static_cast<std::uint8_t>((value & 0x0000'0000'FF00'0000) >> 24);
6,033,282✔
485
                        std::uint8_t byte4 = static_cast<std::uint8_t>((value & 0x0000'00FF'0000'0000) >> 32);
6,033,282✔
486
                        std::uint8_t byte5 = static_cast<std::uint8_t>((value & 0x0000'FF00'0000'0000) >> 40);
6,033,282✔
487
                        std::uint8_t byte6 = static_cast<std::uint8_t>((value & 0x00FF'0000'0000'0000) >> 48);
6,033,282✔
488
                        std::uint8_t byte7 = static_cast<std::uint8_t>((value & 0xFF00'0000'0000'0000) >> 56);
6,033,282✔
489
                        if (byte7 > 0) {
6,033,282✔
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,277✔
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,271✔
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,264✔
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,258✔
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,251✔
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,431✔
535
                                _block.push_back(byte0);
4,058,693✔
536
                                _block.push_back(byte1);
4,058,693✔
537
                        }
538
                        else if (byte0 > 0) {
1,450,738✔
539
                                _block.push_back(byte0);
1,447,115✔
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,620✔
547
                        std::uint16_t word1 = static_cast<std::uint16_t>((value & 0x0000'0000'FFFF'0000) >> 16);
7,724,620✔
548
                        std::uint16_t word2 = static_cast<std::uint16_t>((value & 0x0000'FFFF'0000'0000) >> 32);
7,724,620✔
549
                        std::uint16_t word3 = static_cast<std::uint16_t>((value & 0xFFFF'0000'0000'0000) >> 48);
7,724,620✔
550
                        if (word3 > 0) {
7,724,620✔
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,620✔
557
                                _block.push_back(word0);
×
558
                                _block.push_back(word1);
×
559
                                _block.push_back(word2);
×
560
                        }
561
                        else if (word1 > 0) {
7,724,620✔
562
                                _block.push_back(word0);
588,839✔
563
                                _block.push_back(word1);
588,839✔
564
                        }
565
                        else if (word0 > 0) {
7,135,781✔
566
                                _block.push_back(word0);
7,131,934✔
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,751✔
574
                        std::uint32_t high = static_cast<std::uint32_t>((value & 0xFFFF'FFFF'0000'0000) >> bitsInBlock);
9,690,751✔
575
                        if (high > 0) {
9,690,751✔
576
                                _block.push_back(low);
587,344✔
577
                                _block.push_back(high);
587,344✔
578
                        }
579
                        else if (low > 0) {
9,103,407✔
580
                                _block.push_back(low);
9,098,793✔
581
                        }
582
                        else {
583
                                _block.clear();
4,614✔
584
                        }
585
                }
586
        }
23,448,653✔
587
        void setblock(unsigned i, BlockType value) noexcept {
6,237,950✔
588
                if (i >= _block.size()) _block.resize(i+1ull);
6,237,950✔
589
                _block[i] = value;
6,237,950✔
590
        }
6,237,950✔
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,201✔
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; }
52,470,678✔
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,953,863✔
625
                if (b < _block.size()) return _block[b];
12,953,863✔
626
                return static_cast<BlockType>(0u);
325,524✔
627
        }
628
        constexpr unsigned limbs() const noexcept { return static_cast<unsigned>(_block.size()); }
64,961,457✔
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
                        // derive mask from the actual block width so this works for
639
                        // uint8_t / uint16_t / uint32_t instantiations (was hardcoded
640
                        // to 0x8000'0000ul, which only worked for uint32_t)
641
                        std::uint64_t segment = static_cast<std::uint64_t>(_block[static_cast<size_t>(b)]);
642
                        std::uint64_t mask = (std::uint64_t(1) << (bitsInBlock - 1));
643
                        for (int i = bitsInBlock - 1; i >= 0; --i) {
644
                                --msb;
645
                                if (segment & mask) return msb;
646
                                mask >>= 1;
647
                        }
648
                }
649
                return -1; // no significant bit found, all bits are zero
650
        }
651

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

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

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

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

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

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

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

808
private:
809

810
        template<typename BBlockType>
811
        friend constexpr bool operator==(const einteger<BBlockType>&, const einteger<BBlockType>&) noexcept;
812
};
813

814
////////////////////////    einteger functions   /////////////////////////////////
815

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

821
////////////////////////    INTEGER operators   /////////////////////////////////
822

823
/// stream operators
824

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

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

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

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

1007
        unsigned nbits = n.limbs() * sizeof(BlockType) * 8;
147✔
1008

1009
        std::string result;
147✔
1010
        if (base == 8 || base == 16) {
147✔
1011
                if (n.sign()) return std::string("negative value: ignored");
×
1012

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

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

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

1112
// read an ASCII einteger format
1113

1114
template<typename BlockType>
1115
inline std::istream& operator>>(std::istream& istr, einteger<BlockType>& p) {
1✔
1116
        std::string txt;
1✔
1117
        if (!(istr >> txt)) {
1✔
1118
                // extraction failed (already-bad stream or EOF); failbit set by >>.
NEW
1119
                return istr;
×
1120
        }
1121
        if (!parse(txt, p)) {
1✔
1122
                std::cerr << "unable to parse -" << txt << "- into an einteger value\n";
1✔
1123
                istr.setstate(std::ios::failbit);
1✔
1124
        }
1125
        return istr;
1✔
1126
}
1✔
1127

1128
////////////////// string operators
1129

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

1134
        std::stringstream s;
118✔
1135
        s << "0b";
118✔
1136
        for (int b = static_cast<int>(a.limbs()) - 1; b >= 0; --b) {
737✔
1137
                BlockType segment = a.block(static_cast<size_t>(b));
619✔
1138
                BlockType mask = (0x1u << (a.bitsInBlock - 1));
619✔
1139
                for (int i = a.bitsInBlock - 1; i >= 0; --i) {
9,427✔
1140
                        s << ((segment & mask) ? '1' : '0');
8,808✔
1141
                        if (i > 0 && (i % 4) == 0 && nibbleMarker) s << '\'';
8,808✔
1142
                        if (b > 0 && i == 0 && nibbleMarker) s << '\'';
8,808✔
1143
                        mask >>= 1;
8,808✔
1144
                }
1145
        }
1146

1147
        return s.str();
118✔
1148
}
118✔
1149

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

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

1165
                        nibble |= (limb & mask);
128✔
1166
                        if ((i % 4) == 0) {
128✔
1167
                                nibble >>= rightShift;
32✔
1168
                                s << nibbleLookup[nibble];
32✔
1169
                                nibble = 0;
32✔
1170
                                rightShift -= 4u;
32✔
1171
                        }
1172
                        if (bitIndex > 0 && ((bitIndex % 16) == 0) && wordMarker) s << '\'';
128✔
1173
                        mask >>= 1;
128✔
1174
                        --bitIndex;
128✔
1175
                }
1176
        }
1177

1178
        return s.str();
3✔
1179

1180
}
3✔
1181

1182
//////////////////////////////////////////////////////////////////////////////////////////////////////
1183
// einteger - einteger binary logic operators
1184

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

1189
template<typename BlockType>
1190
constexpr bool operator==(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) noexcept {
11,466,160✔
1191
        if (lhs.limbs() != rhs.limbs()) {
11,466,160✔
1192
                return false;
3,485✔
1193
        }
1194
        if (lhs.sign() != rhs.sign()) {
11,462,675✔
1195
                // keep +0 == -0, but require sign match for non-zero values
1196
                return lhs.iszero() && rhs.iszero();
9✔
1197
        }
1198
        for (unsigned i = 0; i < lhs.limbs(); ++i) {
24,361,892✔
1199
                if (lhs._block[i] != rhs._block[i]) return false;
13,418,916✔
1200
        }
1201
        return true;
10,942,976✔
1202
}
1203

1204
template<typename BlockType>
1205
constexpr bool operator!=(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) noexcept {
10,941,956✔
1206
        return !operator==(lhs, rhs);
10,941,956✔
1207
}
1208

1209
template<typename BlockType>
1210
constexpr bool operator< (const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) noexcept {
1,047,000✔
1211
        const bool ls = lhs.sign();
1,047,000✔
1212
        const bool rs = rhs.sign();
1,047,000✔
1213
        if (ls != rs) {
1,047,000✔
1214
                // +0 and -0 are equal, not ordered
1215
                if (lhs.iszero() && rhs.iszero()) return false;
41✔
1216
                return ls; // negative < positive
41✔
1217
        }
1218
        unsigned ll = lhs.limbs();
1,046,959✔
1219
        unsigned rl = rhs.limbs();
1,046,959✔
1220
        // for negatives, larger magnitude means smaller value -- swap the sense
1221
        if (ll != rl) return ls ? (ll > rl) : (ll < rl);
1,046,959✔
1222
        if (ll == 0) return false; // both empty: equal in magnitude
1,040,416✔
1223
        for (unsigned b = ll; b-- > 0;) {
1,045,579✔
1224
                BlockType l = lhs.block(b);
1,044,552✔
1225
                BlockType r = rhs.block(b);
1,044,552✔
1226
                if (l == r) continue;
1,044,552✔
1227
                return ls ? (l > r) : (l < r);
1,039,389✔
1228
        }
1229
        return false; // lhs and rhs are the same
1,027✔
1230
}
1231

1232
template<typename BlockType>
1233
constexpr bool operator> (const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) noexcept {
2✔
1234
        return operator< (rhs, lhs);
2✔
1235
}
1236

1237
template<typename BlockType>
1238
constexpr bool operator<=(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) noexcept {
1✔
1239
        return operator< (lhs, rhs) || operator==(lhs, rhs);
1✔
1240
}
1241

1242
template<typename BlockType>
1243
constexpr bool operator>=(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) noexcept {
1✔
1244
        return !operator< (lhs, rhs);
1✔
1245
}
1246

1247
//////////////////////////////////////////////////////////////////////////////////////////////////////
1248
// einteger - literal binary logic operators
1249
// equal: precondition is that the byte-storage is properly nulled in all arithmetic paths
1250

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

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

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

1266
template<typename BlockType>
1267
inline bool operator> (const einteger<BlockType>& lhs, long long rhs) {
1268
        return operator< (einteger<BlockType>(rhs), lhs);
1269
}
1270

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

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

1281
//////////////////////////////////////////////////////////////////////////////////////////////////////
1282
// literal - einteger binary logic operators
1283
// precondition is that the byte-storage is properly nulled in all arithmetic paths
1284

1285

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

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

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

1301
template<typename BlockType>
1302
inline bool operator> (long long lhs, const einteger<BlockType>& rhs) {
1303
        return operator< (rhs, lhs);
1304
}
1305

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

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

1316
//////////////////////////////////////////////////////////////////////////////////////////////////////
1317

1318
//////////////////////////////////////////////////////////////////////////////////////////////////////
1319
// einteger - einteger binary arithmetic operators
1320

1321
template<typename BlockType>
1322
inline einteger<BlockType> operator+(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) {
3,145,986✔
1323
        einteger sum = lhs;
3,145,986✔
1324
        sum += rhs;
3,145,986✔
1325
        return sum;
3,145,986✔
1326
}
×
1327

1328
template<typename BlockType>
1329
inline einteger<BlockType> operator-(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) {
4,325,378✔
1330
        einteger diff = lhs;
4,325,378✔
1331
        diff -= rhs;
4,325,378✔
1332
        return diff;
4,325,378✔
1333
}
×
1334

1335
template<typename BlockType>
1336
inline einteger<BlockType> operator*(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) {
327,972✔
1337
        einteger product = lhs;
327,972✔
1338
        product *= rhs;
327,972✔
1339
        return product;
327,972✔
1340
}
×
1341

1342
template<typename BlockType>
1343
inline einteger<BlockType> operator/(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) {
2✔
1344
        einteger ratio = lhs;
2✔
1345
        ratio /= rhs;
2✔
1346
        return ratio;
2✔
1347
}
×
1348

1349
template<typename BlockType>
1350
inline einteger<BlockType> operator%(const einteger<BlockType>& lhs, const einteger<BlockType>& rhs) {
2✔
1351
        einteger remainder = lhs;
2✔
1352
        remainder %= rhs;
2✔
1353
        return remainder;
2✔
1354
}
×
1355

1356
//////////////////////////////////////////////////////////////////////////////////////////////////////
1357
// einteger - literal binary arithmetic operators
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, long long rhs) {
2✔
1371
        return operator*(lhs, einteger<BlockType>(rhs));
2✔
1372
}
1373

1374
template<typename BlockType>
1375
inline einteger<BlockType> operator/(const einteger<BlockType>& lhs, long long rhs) {
1376
        return operator/(lhs, einteger<BlockType>(rhs));
1377
}
1378

1379
template<typename BlockType>
1380
inline einteger<BlockType> operator%(const einteger<BlockType>& lhs, long long rhs) {
1381
        return operator/(lhs, einteger<BlockType>(rhs));
1382
}
1383

1384
template<typename BlockType>
1385
inline einteger<BlockType> operator/(const einteger<BlockType>& lhs, unsigned long long rhs) {
1386
        return operator/(lhs, einteger<BlockType>(rhs));
1387
}
1388

1389
//////////////////////////////////////////////////////////////////////////////////////////////////////
1390
// literal - einteger binary arithmetic operators
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
template<typename BlockType>
1403
inline einteger<BlockType> operator*(long long lhs, const einteger<BlockType>& rhs) {
1404
        return operator*(einteger<BlockType>(lhs), rhs);
1405
}
1406

1407
template<typename BlockType>
1408
inline einteger<BlockType> operator/(long long lhs, const einteger<BlockType>& rhs) {
1409
        return operator/(einteger<BlockType>(lhs), rhs);
1410
}
1411

1412
template<typename BlockType>
1413
inline einteger<BlockType> operator%(long long lhs, const einteger<BlockType>& rhs) {
1414
        return operator/(einteger<BlockType>(lhs), rhs);
1415
}
1416

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