commit 7fa2f28e575f926358b34a89857c1bfb80090fd5
Author: David Sorber <david.sorber@gmail.com>
Date:   Sat Feb 19 22:09:35 2022 -0500

    Adding int128.h header with support for GCC specific int128_t and
    uint128_t types.

diff --git a/software/fixed/fixed.h b/software/fixed/fixed.h
index 7327078..ecb6938 100644
--- a/software/fixed/fixed.h
+++ b/software/fixed/fixed.h
@@ -1,6 +1,6 @@
 
-#ifndef FIXED_H_
-#define FIXED_H_
+#ifndef __FIXED_H_
+#define __FIXED_H_
 
 #include <cctype>
 #include <cmath>
@@ -13,6 +13,14 @@
 #include <sstream>
 #include <type_traits>
 
+#if __GNUC__ >= 3
+# define __unlikely(cond)	__builtin_expect ((cond), 0)
+# define __likely(cond)	__builtin_expect ((cond), 1)
+#else
+# define __unlikely(cond)	(cond)
+# define __likely(cond)	(cond)
+#endif
+
 template <typename IntegerType, typename FractionalType>
 class fixed
 {
@@ -90,7 +98,7 @@ public:
     }
     
     // Parse value from string input
-    void parse(const char* input)
+    uint32_t parse(const char* input)
     {
         char* endPtr = nullptr;
         m_integer = __checkIntOverflow(strtoull(input, &endPtr, 10));
@@ -114,9 +122,10 @@ public:
             //~ std::cout << "invt len: " << fracLen << std::endl;
             
             m_fractional = __checkFracOverflow(fracTemp * SCALE_VALUES[fracLen]);
+            endPtr = fracEndPtr;
         }
         // TODO: could calculate and return overall length
-        //~ uint32_t length = endPtr - input;
+        return (endPtr - input);
     }
     
     constexpr inline fixed operator-() const noexcept = delete;
@@ -154,7 +163,7 @@ private:
 
     static inline IntegerType __checkIntOverflow(IntegerType integerVal)
     {
-        if (integerVal > MAX_INTEGER_VALUE)
+        if (__unlikely(integerVal > MAX_INTEGER_VALUE))
         {
             std::ostringstream msg;
             msg << "Integer value: " << integerVal << " exceeds maximum "
@@ -166,7 +175,7 @@ private:
     
     static inline FractionalType __checkFracOverflow(FractionalType fractionalVal)
     {
-        if (fractionalVal > MAX_FRACTIONAL_VALUE)
+        if (__unlikely(fractionalVal > MAX_FRACTIONAL_VALUE))
         {
             std::ostringstream msg;
             msg << "Fractional value: " << fractionalVal << " exceeds maximum "
diff --git a/software/fixed/fixed_test.cc b/software/fixed/fixed_test.cc
index 5e2952a..aa56bc4 100644
--- a/software/fixed/fixed_test.cc
+++ b/software/fixed/fixed_test.cc
@@ -1,10 +1,12 @@
 #include <cmath>
 #include <cstdint>
+#include <limits>
 #include <iomanip>
 #include <iostream>
 #include <stdexcept>
 
 #include "fixed.h"
+#include "int128.h"
 
 // g++ fixed_test.cc -o fixed_test
 int main(int argc, char** argv)
@@ -75,6 +77,24 @@ int main(int argc, char** argv)
     //~ double foo = std::pow(10, 18);
     //~ std::cout << "Foo: " << std::fixed << std::setprecision(0) << foo << std::endl;
     //~ std::cout << "Foo: " << std::fixed << std::setprecision(0) << (static_cast<uint64_t>(foo) - 1) << std::endl;
-        
+    
+    uint128_t big = 1098298217598174ULL;
+    std::cout << "BIG INT: " << big << std::endl;
+    big *= 1098982694765;
+    std::cout << "BIGGER INT: " << big << std::endl;
+    
+    std::cout << "INT128T MAX: " << std::numeric_limits<int128_t>::max() << std::endl;
+    std::cout << "UINT128T MAX: " << std::numeric_limits<uint128_t>::max() << std::endl;
+    
+    strVal = "-1207010734831637608463359110";
+    int128_t s128Val = strtoll_128(strVal, nullptr, 0);
+    std::cout << "String value: " << strVal << std::endl;
+    std::cout << "Parsed value: " << s128Val << std::endl;
+    
+    strVal = "991207010734831637608463359110888";
+    uint128_t u128Val = strtoull_128(strVal, nullptr, 0);
+    std::cout << "String value: " << strVal << std::endl;
+    std::cout << "Parsed value: " << u128Val << std::endl;
+    
     return 0;
 }
diff --git a/software/fixed/int128.h b/software/fixed/int128.h
new file mode 100644
index 0000000..c3cc606
--- /dev/null
+++ b/software/fixed/int128.h
@@ -0,0 +1,432 @@
+#ifndef __INT128_H_
+#define __INT128_H_
+
+#include <cctype>
+#include <limits>
+#include <sstream>
+#include <stdexcept>
+
+// NOTE: These 128 bit types are GCC specific; see:
+// https://gcc.gnu.org/onlinedocs/gcc/_005f_005fint128.html
+typedef __int128 int128_t;
+typedef unsigned __int128 uint128_t;
+
+
+std::ostream& operator<<(std::ostream& dest, uint128_t value)
+{
+    std::ostream::sentry check(dest);
+    if (check) 
+    {
+        char buffer[128];
+        char* pos = std::end(buffer);
+        do
+        {
+            --pos;
+            *pos = "0123456789"[value % 10];
+            value /= 10;
+        } while (value != 0);
+        int len = std::end(buffer) - pos;
+        if (dest.rdbuf()->sputn(pos, len) != len) 
+        {
+            dest.setstate(std::ios_base::badbit);
+        }
+    }
+    return dest;
+}
+
+std::ostream& operator<<(std::ostream& dest, int128_t value)
+{
+    std::ostream::sentry check(dest);
+    if (check) 
+    {
+        uint128_t tmp = value < 0 ? -value : value;
+        char buffer[128];
+        char* pos = std::end(buffer);
+        do
+        {
+            --pos;
+            *pos = "0123456789"[tmp % 10];
+            tmp /= 10;
+        } while (tmp != 0);
+        if (value < 0) 
+        {
+            --pos;
+            *pos = '-';
+        }
+        int len = std::end(buffer) - pos;
+        if (dest.rdbuf()->sputn(pos, len) != len) 
+        {
+            dest.setstate(std::ios_base::badbit);
+        }
+    }
+    return dest;
+}
+
+constexpr uint128_t max_tab[]
+{
+    std::numeric_limits<uint128_t>::max() /  2,
+    std::numeric_limits<uint128_t>::max() /  3,
+    std::numeric_limits<uint128_t>::max() /  4,
+    std::numeric_limits<uint128_t>::max() /  5,
+    std::numeric_limits<uint128_t>::max() /  6,
+    std::numeric_limits<uint128_t>::max() /  7,
+    std::numeric_limits<uint128_t>::max() /  8,
+    std::numeric_limits<uint128_t>::max() /  9,
+    std::numeric_limits<uint128_t>::max() / 11,
+    std::numeric_limits<uint128_t>::max() / 12,
+    std::numeric_limits<uint128_t>::max() / 13,
+    std::numeric_limits<uint128_t>::max() / 14,
+    std::numeric_limits<uint128_t>::max() / 15,
+    std::numeric_limits<uint128_t>::max() / 16,
+    std::numeric_limits<uint128_t>::max() / 17,
+    std::numeric_limits<uint128_t>::max() / 18,
+    std::numeric_limits<uint128_t>::max() / 19,
+    std::numeric_limits<uint128_t>::max() / 20,
+    std::numeric_limits<uint128_t>::max() / 21,
+    std::numeric_limits<uint128_t>::max() / 22,
+    std::numeric_limits<uint128_t>::max() / 23,
+    std::numeric_limits<uint128_t>::max() / 24,
+    std::numeric_limits<uint128_t>::max() / 25,
+    std::numeric_limits<uint128_t>::max() / 26,
+    std::numeric_limits<uint128_t>::max() / 27,
+    std::numeric_limits<uint128_t>::max() / 28,
+    std::numeric_limits<uint128_t>::max() / 29,
+    std::numeric_limits<uint128_t>::max() / 31,
+    std::numeric_limits<uint128_t>::max() / 32,
+    std::numeric_limits<uint128_t>::max() / 33,
+    std::numeric_limits<uint128_t>::max() / 34,
+    std::numeric_limits<uint128_t>::max() / 35,
+    std::numeric_limits<uint128_t>::max() / 36
+};
+
+constexpr uint32_t rem_tab[]
+{
+    std::numeric_limits<uint128_t>::max() %  2,
+    std::numeric_limits<uint128_t>::max() %  3,
+    std::numeric_limits<uint128_t>::max() %  4,
+    std::numeric_limits<uint128_t>::max() %  5,
+    std::numeric_limits<uint128_t>::max() %  6,
+    std::numeric_limits<uint128_t>::max() %  7,
+    std::numeric_limits<uint128_t>::max() %  8,
+    std::numeric_limits<uint128_t>::max() %  9,
+    std::numeric_limits<uint128_t>::max() % 11,
+    std::numeric_limits<uint128_t>::max() % 12,
+    std::numeric_limits<uint128_t>::max() % 13,
+    std::numeric_limits<uint128_t>::max() % 14,
+    std::numeric_limits<uint128_t>::max() % 15,
+    std::numeric_limits<uint128_t>::max() % 16,
+    std::numeric_limits<uint128_t>::max() % 17,
+    std::numeric_limits<uint128_t>::max() % 18,
+    std::numeric_limits<uint128_t>::max() % 19,
+    std::numeric_limits<uint128_t>::max() % 20,
+    std::numeric_limits<uint128_t>::max() % 21,
+    std::numeric_limits<uint128_t>::max() % 22,
+    std::numeric_limits<uint128_t>::max() % 23,
+    std::numeric_limits<uint128_t>::max() % 24,
+    std::numeric_limits<uint128_t>::max() % 25,
+    std::numeric_limits<uint128_t>::max() % 26,
+    std::numeric_limits<uint128_t>::max() % 27,
+    std::numeric_limits<uint128_t>::max() % 28,
+    std::numeric_limits<uint128_t>::max() % 29,
+    std::numeric_limits<uint128_t>::max() % 31,
+    std::numeric_limits<uint128_t>::max() % 32,
+    std::numeric_limits<uint128_t>::max() % 33,
+    std::numeric_limits<uint128_t>::max() % 34,
+    std::numeric_limits<uint128_t>::max() % 35,
+    std::numeric_limits<uint128_t>::max() % 36
+};
+
+int128_t strtoll_128(const char* nptr, char** endptr, int base)
+{
+    bool negative = false;
+    uint128_t cutoff = 0;
+    uint32_t cutlim = 0;
+    int128_t value = 0;
+    const char* ptr;
+    unsigned char chr;
+    const char *save, *end;
+    bool overflow = false;
+
+    // Validate the base parameter
+    if (base < 0 || base == 1 || base > 36)
+    {
+        std::ostringstream msg;
+        msg << "Base value \"" << base << "\" is invalid; base must be "
+            << "between 2-36 (inclusive).";
+        throw std::out_of_range(msg.str());
+    }
+    
+    save = ptr = nptr;
+    
+    // Skip any leading whitespace
+    while (std::isspace(*ptr))
+    {
+        ++ptr;
+    }
+    
+    // Check for end of string
+    if (__builtin_expect((*ptr == '\0'), 0))
+    {
+        goto noconv;
+    }
+    
+    // Parse -/+
+    if (*ptr == '-')
+    {
+        negative = true;
+        ++ptr;
+    }
+    else if (*ptr == '+')
+    {
+        ++ptr;
+    }
+    
+    // Auto detect/validate base
+    if (*ptr == '0')
+    {
+        if ((base == 0 || base == 16) && std::toupper(ptr[1])== 'X')
+        {
+            ptr += 2;
+            base = 16;
+        }
+        else if (base == 0)
+        {
+            base = 8;
+        }
+    }
+    else if (base == 0)
+    {
+        base = 10;
+    }
+    
+    // Save start of digits
+    save = ptr;
+    end = nullptr;
+    
+    cutoff = max_tab[base - 2];;
+    cutlim = rem_tab[base - 2];
+    
+    // Iterate over the characters
+    chr = *ptr;
+    for (; chr != '\0'; chr = *++ptr)
+    {
+        if (ptr == end)
+        {
+            break;
+        }
+        
+        if (chr >= '0' && chr <= '9')
+        {
+            chr -= '0';
+        }
+        else if (std::isalpha(chr))
+        {
+            chr = std::toupper(chr) - 'A' + 10;
+        }
+        else
+        {
+            break;
+        }
+        
+        if (static_cast<int>(chr) >= base)
+        {
+            break;
+        }
+
+        if (value > cutoff || (value == cutoff && chr > cutlim))
+        {
+            overflow = true;
+            break;
+        }
+        else
+        {   
+            value *= static_cast<uint128_t>(base);
+            value += (int)chr;
+        }
+    }
+    
+    // Check if no conversion was done
+    if (ptr == save)
+    {
+        goto noconv;
+    }
+    
+    // Store end if requested
+    if (endptr != nullptr)
+    {
+        *endptr = const_cast<char*>(ptr);
+    }
+        
+    // Check for overflow
+    if (__builtin_expect((overflow), 0))
+    {
+        std::ostringstream msg;
+        msg << "Overflow detected at input offset " << (ptr - nptr);
+        throw std::overflow_error(msg.str());
+    }
+    
+    // Regular return path
+    return negative ? -value : value;
+    
+noconv:
+
+    if (endptr != nullptr)
+    {
+        if (save - nptr >= 2 && std::toupper(save[-1]) == 'X' && save[-2] == '0')
+        {
+            *endptr = const_cast<char*>(&save[-1]);
+        }
+        else
+        {
+            *endptr = const_cast<char*>(nptr);
+        }
+    }
+
+    return 0;
+}
+
+
+uint128_t strtoull_128(const char* nptr, char** endptr, int base)
+{
+    uint128_t cutoff = 0;
+    uint32_t cutlim = 0;
+    uint128_t value = 0;
+    const char* ptr;
+    unsigned char chr;
+    const char *save, *end;
+    bool overflow = false;
+
+    // Validate the base parameter
+    if (base < 0 || base == 1 || base > 36)
+    {
+        std::ostringstream msg;
+        msg << "Base value \"" << base << "\" is invalid; base must be "
+            << "between 2-36 (inclusive).";
+        throw std::out_of_range(msg.str());
+    }
+    
+    save = ptr = nptr;
+    
+    // Skip any leading whitespace
+    while (std::isspace(*ptr))
+    {
+        ++ptr;
+    }
+    
+    // Check for end of string
+    if (__builtin_expect((*ptr == '\0'), 0))
+    {
+        goto noconv;
+    }
+    
+    // Parse -/+
+    if (*ptr == '-' || *ptr == '+')
+    {
+        ++ptr;
+    }
+    
+    // Auto detect/validate base
+    if (*ptr == '0')
+    {
+        if ((base == 0 || base == 16) && std::toupper(ptr[1])== 'X')
+        {
+            ptr += 2;
+            base = 16;
+        }
+        else if (base == 0)
+        {
+            base = 8;
+        }
+    }
+    else if (base == 0)
+    {
+        base = 10;
+    }
+    
+    // Save start of digits
+    save = ptr;
+    end = nullptr;
+    
+    cutoff = max_tab[base - 2];;
+    cutlim = rem_tab[base - 2];
+    
+    // Iterate over the characters
+    chr = *ptr;
+    for (; chr != '\0'; chr = *++ptr)
+    {
+        if (ptr == end)
+        {
+            break;
+        }
+        
+        if (chr >= '0' && chr <= '9')
+        {
+            chr -= '0';
+        }
+        else if (std::isalpha(chr))
+        {
+            chr = std::toupper(chr) - 'A' + 10;
+        }
+        else
+        {
+            break;
+        }
+        
+        if (static_cast<int>(chr) >= base)
+        {
+            break;
+        }
+
+        if (value > cutoff || (value == cutoff && chr > cutlim))
+        {
+            overflow = true;
+            break;
+        }
+        else
+        {   
+            value *= static_cast<uint128_t>(base);
+            value += (int)chr;
+        }
+    }
+    
+    // Check if no conversion was done
+    if (ptr == save)
+    {
+        goto noconv;
+    }
+    
+    // Store end if requested
+    if (endptr != nullptr)
+    {
+        *endptr = const_cast<char*>(ptr);
+    }
+        
+    // Check for overflow
+    if (__builtin_expect((overflow), 0))
+    {
+        std::ostringstream msg;
+        msg << "Overflow detected at input offset " << (ptr - nptr);
+        throw std::overflow_error(msg.str());
+    }
+    
+    // Regular return path
+    return value;
+    
+noconv:
+
+    if (endptr != nullptr)
+    {
+        if (save - nptr >= 2 && std::toupper(save[-1]) == 'X' && save[-2] == '0')
+        {
+            *endptr = const_cast<char*>(&save[-1]);
+        }
+        else
+        {
+            *endptr = const_cast<char*>(nptr);
+        }
+    }
+
+    return 0;
+}
+
+#endif
diff --git a/software/fixed/parse_test.cc b/software/fixed/parse_test.cc
index fc37b7b..dd03734 100644
--- a/software/fixed/parse_test.cc
+++ b/software/fixed/parse_test.cc
@@ -23,7 +23,7 @@ void genRandomNumStrings(
     //~ std::mt19937 rng(dev());
     std::mt19937 rng;
     rng.seed(seed);
-    std::uniform_int_distribution<std::mt19937::result_type> numDigits(1, 20);
+    std::uniform_int_distribution<std::mt19937::result_type> numDigits(1, 18);
     std::uniform_int_distribution<std::mt19937::result_type> digits(0, 9);
     
     for (uint32_t idx = 0; idx < number; ++idx)
