commit 495f615f9ea32fcddeacf36d7f4bdeadb9c0ae57
Author: David Sorber <david.sorber@gmail.com>
Date:   Sat Jun 11 08:43:24 2022 -0400

    Adding combined updates to the fixed point type.

diff --git a/software/fixed/fixed.h b/software/fixed/fixed.h
index 9e275e8..fec118a 100644
--- a/software/fixed/fixed.h
+++ b/software/fixed/fixed.h
@@ -10,13 +10,19 @@
 #include <cstdio>
 #include <cstdint>
 #include <cstdlib>
+#include <cstring>
 #include <iomanip>
 #include <iostream>
 #include <limits>
+#include <map>
 #include <sstream>
 #include <stdexcept>
 #include <type_traits>
-#include <map>
+
+#ifndef CYBERLYNX
+#include <boost/serialization/access.hpp>
+#include <boost/serialization/base_object.hpp>
+#endif
 
 
 #if __GNUC__ >= 3
@@ -36,6 +42,15 @@
 #define SUPPORT_PARSE_EXPONENT      1   // Default: enable
 #endif
 
+#ifndef SUPPORT_128_INTS
+#define SUPPORT_128_INTS            1   // Default: enable
+#endif
+
+#if SUPPORT_128_INTS
+#include "Utilities/int128.h"
+#endif
+
+
 template<typename T>
 constexpr T calculateMax(size_t decimal_digits)
 {
@@ -78,7 +93,7 @@ public:
 
     static constexpr IntegerType MAX_INTEGER_VALUE = 
         (calculateMax<IntegerType>(integer_decimal_digits));
-    static constexpr IntegerType MIN_INTEGER_VALUE = 0;
+    static constexpr IntegerType MIN_INTEGER_VALUE = -MAX_INTEGER_VALUE;
 
     static constexpr FractionalType MAX_FRACTIONAL_VALUE = 
         (calculateMax<FractionalType>(fractional_decimal_digits));
@@ -86,8 +101,13 @@ public:
 
     static constexpr IntegerType NEGATIVE_ZERO = MAX_INTEGER_VALUE + 2;
 
+#if SUPPORT_128_INTS
+    static const uint128_t SCALE_VALUES[39];
+    static const std::map<uint128_t, uint32_t> DIGIT_LOOKUP_TABLE;
+#else
     static const uint64_t SCALE_VALUES[20];
     static const std::map<uint64_t, uint32_t> DIGIT_LOOKUP_TABLE;
+#endif
 
     // Constructors
     explicit fixed()
@@ -131,6 +151,16 @@ public:
         // Fractional value is prescaled to fractional type precision
         m_fractional = __checkFracOverflow(fractionalVal);
     }
+    
+    // Reassign value from existing fixed value of the same type
+    void assign(const fixed<IntegerType, 
+                            FractionalType, 
+                            StrToIntegerTypeFunc, 
+                            StrToFracTypeFunc>& other)
+    {
+        m_integer = other.m_integer;
+        m_fractional = other.m_fractional;
+    }
 
     // Assign a new value with an *unscaled* fractional part (NOTE: use the 
     // "leadingZeros" parameter to represent the number of leading zeros in the
@@ -155,27 +185,28 @@ public:
         m_integer = __checkIntOverflow(strto_inttype(input, &endPtr, 10));
         m_fractional = 0;
         bool negativeZeroFlag = false;
-
+        
         if (std::isdigit(*endPtr))
         {
             throw std::out_of_range("Integer value is out of range.");
         }
         
         // Check for negative zero corner case with leading zero digit
-        if (__unlikely(m_integer == 0 && endPtr - input >= 2 && *(endPtr - 2) == '-'))
+        if (__unlikely(m_integer == 0 && ((endPtr - input) >= 2) && 
+            *(endPtr - 2) == '-'))
         {
             negativeZeroFlag = true;
         }
 
         // If the ending char is a period we can now parse the fractional part
         uint32_t fracLen = 0;        
-        if (*endPtr == '.'|| (endPtr[0] == '-' && endPtr[1] == '.'))
+        if (*endPtr == '.'|| ((endPtr[0] == '-' || endPtr[0] == '+') && endPtr[1] == '.'))
         {
             // Check for negative zero corner case without leading zero digit
-            if (__unlikely(endPtr[0] == '-'))
+            if (__unlikely(endPtr[0] == '-'|| endPtr[0] == '+'))
             {
+                negativeZeroFlag = (endPtr[0] == '-');
                 ++endPtr;
-                negativeZeroFlag = true;
             }
             
             char* fracEndPtr = nullptr;
@@ -257,10 +288,17 @@ public:
                     m_integer *= static_cast<IntegerType>(SCALE_VALUES[exponent]);
                 }
                 
-                uint64_t scaler = SCALE_VALUES[fractional_decimal_digits - 
-                                               fracExponent];
+                FractionalType scaler = SCALE_VALUES[fractional_decimal_digits - 
+                                                     fracExponent];
                 IntegerType temp = m_fractional / scaler;
-                m_integer += temp;
+                if (m_integer < 0)
+                {
+                    m_integer += -temp;
+                }
+                else
+                {
+                    m_integer += temp;
+                }
                 m_fractional -= (temp * scaler);
                 m_fractional *= SCALE_VALUES[fracExponent];
                 
@@ -301,18 +339,31 @@ public:
                 else
                 {
                     // CASE 2: fractional zero
-                    long intExponent = std::abs(exponent);
-                    if (static_cast<size_t>(intExponent) > integer_decimal_digits)
+                    size_t intExponent = -exponent;
+                    if (intExponent > integer_decimal_digits)
                     {
                         intExponent = integer_decimal_digits;
                     }
                     
                     temp = std::abs(m_integer) % SCALE_VALUES[intExponent];
                     temp *= SCALE_VALUES[fractional_decimal_digits - intExponent];
-                    m_integer /= SCALE_VALUES[intExponent];
+
+#if SUPPORT_128_INTS                    
+                    uint128_t unsignedAbsIntVal = static_cast<uint128_t>(std::abs(m_integer));
+#else                    
+                    uint64_t unsignedAbsIntVal = static_cast<uint64_t>(std::abs(m_integer));
+#endif
+                    if (unsignedAbsIntVal < SCALE_VALUES[intExponent])
+                    {
+                        m_integer = 0;
+                    }
+                    else
+                    {
+                        m_integer = unsignedAbsIntVal /  SCALE_VALUES[intExponent];
+                    }
                     m_fractional += temp;
                     
-                    if (std::abs(exponent) > intExponent)
+                    if (static_cast<size_t>(std::abs(exponent)) > intExponent)
                     {
                         long fracExponent = std::abs(exponent) - intExponent;
                         m_fractional /= SCALE_VALUES[fracExponent];
@@ -334,7 +385,7 @@ public:
         {
             m_integer = NEGATIVE_ZERO;
         }
-
+        
         // Calculate and return overall length
         return (endPtr - input);
     }
@@ -359,7 +410,11 @@ public:
     
     constexpr inline void negate()
     {
-        if (__unlikely(m_integer == 0))
+        if (__unlikely(m_integer == NEGATIVE_ZERO))
+        {
+            m_integer = 0;
+        }
+        else if (__unlikely(m_integer == 0))
         {
             m_integer = NEGATIVE_ZERO;
         }
@@ -432,6 +487,14 @@ private:
                 << "integer range of type (" << MAX_INTEGER_VALUE << ")!";
             throw std::out_of_range(msg.str());
         }
+        else if (__unlikely(integerVal < MIN_INTEGER_VALUE))
+        {
+            std::ostringstream msg;
+            msg << "Integer value: " << integerVal << " exceeds minimum "
+                << "integer range of type (" << MIN_INTEGER_VALUE << ")!";
+            throw std::out_of_range(msg.str());
+        }
+        
         return integerVal;
     }
 
@@ -461,6 +524,17 @@ private:
 
     IntegerType m_integer;
     FractionalType m_fractional;
+    
+#ifndef CYBERLYNX
+    friend class boost::serialization::access;
+
+    template <typename Archive>
+    void serialize(Archive &ar, const unsigned int version)
+    {
+        ar & m_integer;
+        ar & m_fractional;
+    }
+#endif    
 };
 
 // Functor for calling std::strtoll()
@@ -479,28 +553,111 @@ struct strtoull_ftor {
     }
 };
 
+#if SUPPORT_128_INTS
+// Functor for strtoll_128()
+struct strto128_ftor {
+    inline int128_t operator()(const char* str, char** str_end, int base) 
+    { 
+        return strtoll_128_b10opt(str, str_end, base); 
+    }
+};
+
+// Functor for calling strtoull_128
+struct strtou128_ftor {
+    inline uint128_t operator()(const char* str, char** str_end, int base) 
+    { 
+        return strtoull_128_b10opt(str, str_end, base); 
+    }
+};
+#endif
+
 // Predefined types
 using fixed_8_8  = fixed<int8_t, uint8_t, strtoll_ftor, strtoull_ftor>;
 using fixed_8_16 = fixed<int8_t, uint16_t, strtoll_ftor, strtoull_ftor>;
 using fixed_8_32 = fixed<int8_t, uint32_t, strtoll_ftor, strtoull_ftor>;
 using fixed_8_64 = fixed<int8_t, uint64_t, strtoll_ftor, strtoull_ftor>;
+#if SUPPORT_128_INTS
+using fixed_8_128 = fixed<int8_t, uint128_t, strtoll_ftor, strtou128_ftor>;
+#endif
 
 using fixed_16_8  = fixed<int16_t, uint8_t, strtoll_ftor, strtoull_ftor>;
 using fixed_16_16 = fixed<int16_t, uint16_t, strtoll_ftor, strtoull_ftor>;
 using fixed_16_32 = fixed<int16_t, uint32_t, strtoll_ftor, strtoull_ftor>;
 using fixed_16_64 = fixed<int16_t, uint64_t, strtoll_ftor, strtoull_ftor>;
+#if SUPPORT_128_INTS
+using fixed_16_128 = fixed<int16_t, uint128_t, strtoll_ftor, strtou128_ftor>;
+#endif
 
 using fixed_32_8  = fixed<int32_t, uint8_t, strtoll_ftor, strtoull_ftor>;
 using fixed_32_16 = fixed<int32_t, uint16_t, strtoll_ftor, strtoull_ftor>;
 using fixed_32_32 = fixed<int32_t, uint32_t, strtoll_ftor, strtoull_ftor>;
 using fixed_32_64 = fixed<int32_t, uint64_t, strtoll_ftor, strtoull_ftor>;
+#if SUPPORT_128_INTS
+using fixed_32_128 = fixed<int32_t, uint128_t, strtoll_ftor, strtou128_ftor>;
+#endif
 
 using fixed_64_8  = fixed<int64_t, uint8_t, strtoll_ftor, strtoull_ftor>;
 using fixed_64_16 = fixed<int64_t, uint16_t, strtoll_ftor, strtoull_ftor>;
 using fixed_64_32 = fixed<int64_t, uint32_t, strtoll_ftor, strtoull_ftor>;
 using fixed_64_64 = fixed<int64_t, uint64_t, strtoll_ftor, strtoull_ftor>;
+#if SUPPORT_128_INTS
+using fixed_64_128 = fixed<int64_t, uint128_t, strtoll_ftor, strtou128_ftor>;
+#endif
+
+#if SUPPORT_128_INTS
+using fixed_128_8  = fixed<int128_t, uint8_t, strto128_ftor, strtoull_ftor>;
+using fixed_128_16 = fixed<int128_t, uint16_t, strto128_ftor, strtoull_ftor>;
+using fixed_128_32 = fixed<int128_t, uint32_t, strto128_ftor, strtoull_ftor>;
+using fixed_128_64 = fixed<int128_t, uint64_t, strto128_ftor, strtoull_ftor>;
+using fixed_128_128 = fixed<int128_t, uint128_t, strto128_ftor, strtou128_ftor>;
+#endif
 
 // Precomputed scale value constants
+#if SUPPORT_128_INTS
+template<typename I, typename F, typename STI, typename STF>
+const uint128_t fixed<I, F, STI, STF>::SCALE_VALUES[39] = 
+{
+    /*  0 */ 1_u128,
+    /*  1 */ 10_u128,
+    /*  2 */ 100_u128,
+    /*  3 */ 1000_u128,
+    /*  4 */ 10000_u128,
+    /*  5 */ 100000_u128,
+    /*  6 */ 1000000_u128,
+    /*  7 */ 10000000_u128,
+    /*  8 */ 100000000_u128,
+    /*  9 */ 1000000000_u128,
+    /* 10 */ 10000000000_u128,
+    /* 11 */ 100000000000_u128,
+    /* 12 */ 1000000000000_u128,
+    /* 13 */ 10000000000000_u128,
+    /* 14 */ 100000000000000_u128,
+    /* 15 */ 1000000000000000_u128,
+    /* 16 */ 10000000000000000_u128,
+    /* 17 */ 100000000000000000_u128,
+    /* 18 */ 1000000000000000000_u128,
+    /* 19 */ 10000000000000000000_u128,
+    /* 20 */ 100000000000000000000_u128,
+    /* 21 */ 1000000000000000000000_u128,
+    /* 22 */ 10000000000000000000000_u128,
+    /* 23 */ 100000000000000000000000_u128,
+    /* 24 */ 1000000000000000000000000_u128,
+    /* 25 */ 10000000000000000000000000_u128,
+    /* 26 */ 100000000000000000000000000_u128,
+    /* 27 */ 1000000000000000000000000000_u128,
+    /* 28 */ 10000000000000000000000000000_u128,
+    /* 29 */ 100000000000000000000000000000_u128,
+    /* 30 */ 1000000000000000000000000000000_u128,
+    /* 31 */ 10000000000000000000000000000000_u128,
+    /* 32 */ 100000000000000000000000000000000_u128,
+    /* 33 */ 1000000000000000000000000000000000_u128,
+    /* 34 */ 10000000000000000000000000000000000_u128,
+    /* 35 */ 100000000000000000000000000000000000_u128,
+    /* 36 */ 1000000000000000000000000000000000000_u128,
+    /* 37 */ 10000000000000000000000000000000000000_u128,
+    /* 38 */ 100000000000000000000000000000000000000_u128,
+};
+#else
 template<typename I, typename F, typename STI, typename STF>
 const uint64_t fixed<I, F, STI, STF>::SCALE_VALUES[20] = 
 {
@@ -525,7 +682,52 @@ const uint64_t fixed<I, F, STI, STF>::SCALE_VALUES[20] =
     /* 18 */ 1000000000000000000ULL,
     /* 19 */ 10000000000000000000ULL
 };
+#endif
 
+#if SUPPORT_128_INTS
+template<typename I, typename F, typename STI, typename STF>
+const std::map<uint128_t, uint32_t> fixed<I, F, STI, STF>::DIGIT_LOOKUP_TABLE{
+    {1_u128, 0},
+    {10_u128, 1},
+    {100_u128, 2},
+    {1000_u128, 3},
+    {10000_u128, 4},
+    {100000_u128, 5},
+    {1000000_u128, 6},
+    {10000000_u128, 7},
+    {100000000_u128, 8},
+    {1000000000_u128, 9},
+    {10000000000_u128, 10},
+    {100000000000_u128, 11},
+    {1000000000000_u128, 12},
+    {10000000000000_u128, 13},
+    {100000000000000_u128, 14},
+    {1000000000000000_u128, 15},
+    {10000000000000000_u128, 16},
+    {100000000000000000_u128, 17},
+    {1000000000000000000_u128, 18},
+    {10000000000000000000_u128, 19},
+    {100000000000000000000_u128, 20},
+    {1000000000000000000000_u128, 21},
+    {10000000000000000000000_u128, 22},
+    {100000000000000000000000_u128, 23},
+    {1000000000000000000000000_u128, 24},
+    {10000000000000000000000000_u128, 25},
+    {100000000000000000000000000_u128, 26},
+    {1000000000000000000000000000_u128, 27},
+    {10000000000000000000000000000_u128, 28},
+    {100000000000000000000000000000_u128, 29},
+    {1000000000000000000000000000000_u128, 30},
+    {10000000000000000000000000000000_u128, 31},
+    {100000000000000000000000000000000_u128, 32},
+    {1000000000000000000000000000000000_u128, 33},
+    {10000000000000000000000000000000000_u128, 34},
+    {100000000000000000000000000000000000_u128, 35},
+    {1000000000000000000000000000000000000_u128, 36},
+    {10000000000000000000000000000000000000_u128, 37},
+    {std::numeric_limits<uint128_t>::max(), 38}  // Special case for uint128_t max value
+};
+#else
 template<typename I, typename F, typename STI, typename STF>
 const std::map<uint64_t, uint32_t> fixed<I, F, STI, STF>::DIGIT_LOOKUP_TABLE{
     {1ULL, 0},
@@ -549,6 +751,7 @@ const std::map<uint64_t, uint32_t> fixed<I, F, STI, STF>::DIGIT_LOOKUP_TABLE{
     {1000000000000000000ULL, 18},
     {std::numeric_limits<uint64_t>::max(), 19}  // Special case for uint64_t max value
 };
+#endif
 
 template<typename I, typename F, typename STI, typename STF>
 constexpr inline bool operator==(
@@ -592,7 +795,15 @@ constexpr inline bool operator<(
         
         if (integerX == integerY)
         {
-            return (x.m_fractional < y.m_fractional);
+            // Consider sign of integer portion
+            if (integerX < 0)
+            {
+                return (x.m_fractional > y.m_fractional);
+            }
+            else
+            {
+                return (x.m_fractional < y.m_fractional);
+            }
         }
         else
         {
@@ -631,7 +842,15 @@ constexpr inline bool operator>(
         
         if (integerX == integerY)
         {
-            return (x.m_fractional > y.m_fractional);
+            // Consider sign of integer portion
+            if (integerX < 0)
+            {
+                return (x.m_fractional < y.m_fractional);
+            }
+            else
+            {
+                return (x.m_fractional > y.m_fractional);
+            }
         }
         else
         {
@@ -724,8 +943,15 @@ inline std::ostream& operator<<(std::ostream& os, const fixed<I, F, STI, STF>& r
 #else
     // Print fractional part without trailing zeros
     char buffer[fixed<I, F, STI, STF>::fractional_decimal_digits + 1]{};
+#if SUPPORT_128_INTS    
+    // TODO: figure out better way to do this
+    std::ostringstream msg;
+    msg << rhs.m_fractional;
+    std::memcpy(buffer, msg.str().c_str(), msg.str().size());
+#else
     std::snprintf(buffer, fixed<I, F, STI, STF>::fractional_decimal_digits + 1,
                   "%" PRIu64, rhs.m_fractional);
+#endif
     
     // NOTE: if fractional part is zero always print at least one zero
     int idx = fixed<I, F, STI, STF>::fractional_decimal_digits;
@@ -743,7 +969,11 @@ inline std::ostream& operator<<(std::ostream& os, const fixed<I, F, STI, STF>& r
         buffer[idx + 1] = '\0';
     }
     
+#if SUPPORT_128_INTS
+    uint32_t leadingZeros = (rhs.m_fractional == 0_u128) ? 1 : 
+#else
     uint32_t leadingZeros = (rhs.m_fractional == 0) ? 1 : 
+#endif
         fixed<I, F, STI, STF>::fractional_decimal_digits - 
         fixed<I, F, STI, STF>::DIGIT_LOOKUP_TABLE.upper_bound(rhs.m_fractional)->second;
         
diff --git a/software/fixed/int128.cc b/software/fixed/int128.cc
new file mode 100644
index 0000000..1a94263
--- /dev/null
+++ b/software/fixed/int128.cc
@@ -0,0 +1,576 @@
+#include "int128.h"
+
+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;
+}
+
+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 > static_cast<int128_t>(cutoff) || 
+            (value == static_cast<int128_t>(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;
+}
+
+int128_t strtoll_128_b10opt(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 0
+    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());
+    }
+#endif
+    
+    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;
+    }
+    
+    // Save start of digits
+    save = ptr;
+    end = nullptr;
+    
+    cutoff = max_tab[8];    // base 10
+    cutlim = rem_tab[8];
+    
+    // Iterate over the characters
+    chr = *ptr;
+    for (; chr != '\0'; chr = *++ptr)
+    {
+        if (ptr == end)
+        {
+            break;
+        }
+        
+        if (chr >= '0' && chr <= '9')
+        {
+            chr -= '0';
+        }
+        else
+        {
+            break;
+        }
+
+        if (value > static_cast<int128_t>(cutoff) || 
+            (value == static_cast<int128_t>(cutoff) && chr > cutlim))
+        {
+            overflow = true;
+            break;
+        }
+        else
+        {   
+            value *= static_cast<uint128_t>(10);
+            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)
+    {
+        *endptr = const_cast<char*>(nptr);
+    }
+
+    return 0;
+}
+
+uint128_t strtoull_128_b10opt(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 0
+    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());
+    }
+#endif
+    
+    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;
+    }
+    
+    // Save start of digits
+    save = ptr;
+    end = nullptr;
+    
+    cutoff = max_tab[8];    // base 10
+    cutlim = rem_tab[8];
+    
+    // Iterate over the characters
+    chr = *ptr;
+    for (; chr != '\0'; chr = *++ptr)
+    {
+        if (ptr == end)
+        {
+            break;
+        }
+        
+        if (chr >= '0' && chr <= '9')
+        {
+            chr -= '0';
+        }
+        else
+        {
+            break;
+        }
+        
+        if (value > cutoff || (value == cutoff && chr > cutlim))
+        {
+            overflow = true;
+            break;
+        }
+        else
+        {   
+            value *= static_cast<uint128_t>(10);
+            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)
+    {
+        *endptr = const_cast<char*>(nptr);
+    }
+
+    return 0;
+}
+
diff --git a/software/fixed/int128.h b/software/fixed/int128.h
index e31bc36..1c16bd0 100644
--- a/software/fixed/int128.h
+++ b/software/fixed/int128.h
@@ -12,55 +12,9 @@ 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, uint128_t value);
 
-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;
-}
+std::ostream& operator<<(std::ostream& dest, int128_t value);
 
 constexpr uint128_t max_tab[]
 {
@@ -97,7 +51,9 @@ constexpr uint128_t max_tab[]
     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
+    std::numeric_limits<uint128_t>::max() / 36,
+    std::numeric_limits<uint128_t>::max() / 37,
+    std::numeric_limits<uint128_t>::max() / 38
 };
 
 constexpr uint32_t rem_tab[]
@@ -135,530 +91,174 @@ constexpr uint32_t rem_tab[]
     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
+    std::numeric_limits<uint128_t>::max() % 36,
+    std::numeric_limits<uint128_t>::max() % 37,
+    std::numeric_limits<uint128_t>::max() % 38
 };
 
-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;
+int128_t strtoll_128(const char* nptr, char** endptr, int base);
+uint128_t strtoull_128(const char* nptr, char** endptr, int base);
+int128_t strtoll_128_b10opt(const char* nptr, char** endptr, int base=10);
+uint128_t strtoull_128_b10opt(const char* nptr, char** endptr, int base=10);
+
+// Source: https://github.com/jbapple/128-bit-literals/blob/master/suffix128.hpp
+namespace suffix128 {
+
+// Returns the value of the hexadecimal character
+constexpr unsigned __int128 CharValue(char c) {
+  return (c >= '0' && c <= '9')
+             ? (c - '0')
+             : ((c >= 'a' && c <= 'f') ? (10 + (c - 'a')) : (10 + (c - 'A')));
 }
 
+static constexpr unsigned __int128 MAX128 = ~static_cast<unsigned __int128>(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;
+// ValidateU128Helper<BASE, c1, c2, ... , cn>(v) returns true iff cn + ... + c2
+// * BASE^(n-2) + c1 * BASE^(n-1) + v * BASE^n is a valid 128-bit unsigned
+// number when interpreted in base BASE.
+template <int BASE>
+constexpr bool ValidateU128Helper(unsigned __int128) {
+  return true;
 }
 
+template <int BASE, char C, char... CS>
+constexpr bool ValidateU128Helper(unsigned __int128 accumulate) {
+  return (C == '\'') ? ValidateU128Helper<BASE, CS...>(accumulate)
+                     : ((accumulate <= MAX128 / BASE) &&
+                        (BASE * accumulate <= MAX128 - CharValue(C)) &&
+                        ValidateU128Helper<BASE, CS...>(accumulate * BASE +
+                                                        CharValue(C)));
+}
 
-int128_t strtoll_128_b10opt(const char* nptr, char** endptr, int base=10)
-{
-    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 0
-    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());
-    }
-#endif
-    
-    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;
-    }
-    
-    // Save start of digits
-    save = ptr;
-    end = nullptr;
-    
-    cutoff = max_tab[8];    // base 10
-    cutlim = rem_tab[8];
-    
-    // Iterate over the characters
-    chr = *ptr;
-    for (; chr != '\0'; chr = *++ptr)
-    {
-        if (ptr == end)
-        {
-            break;
-        }
-        
-        if (chr >= '0' && chr <= '9')
-        {
-            chr -= '0';
-        }
-        else
-        {
-            break;
-        }
-
-        if (value > cutoff || (value == cutoff && chr > cutlim))
-        {
-            overflow = true;
-            break;
-        }
-        else
-        {   
-            value *= static_cast<uint128_t>(10);
-            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)
-    {
-        *endptr = const_cast<char*>(nptr);
-    }
-
-    return 0;
+// ValidateU128<BASE, c1, c2, ... , cn>(v) returns true iff cn + ... + c2 *
+// BASE^(n-2) + c1 * BASE^(n-1) is a valid 128-bit unsigned number when
+// interpreted in base BASE.
+template <int BASE, char... CS>
+constexpr bool ValidateU128() {
+  return ValidateU128Helper<BASE, CS...>(0);
 }
 
+// MakeU128Helper<BASE, c1, c2, ... , cn>(v) returns cn + ... + c2 *
+// BASE^(n-2) + c1 * BASE^(n-1) + result * BASE^n.
+template <int BASE>
+constexpr unsigned __int128 MakeU128Helper(unsigned __int128 result) {
+  return result;
+}
 
-uint128_t strtoull_128_b10opt(const char* nptr, char** endptr, int base=10)
-{
-    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 0
-    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());
-    }
-#endif
-    
-    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;
-    }
-    
-    // Save start of digits
-    save = ptr;
-    end = nullptr;
-    
-    cutoff = max_tab[8];    // base 10
-    cutlim = rem_tab[8];
-    
-    // Iterate over the characters
-    chr = *ptr;
-    for (; chr != '\0'; chr = *++ptr)
-    {
-        if (ptr == end)
-        {
-            break;
-        }
-        
-        if (chr >= '0' && chr <= '9')
-        {
-            chr -= '0';
-        }
-        else
-        {
-            break;
-        }
-        
-        if (value > cutoff || (value == cutoff && chr > cutlim))
-        {
-            overflow = true;
-            break;
-        }
-        else
-        {   
-            value *= static_cast<uint128_t>(10);
-            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)
-    {
-        *endptr = const_cast<char*>(nptr);
-    }
-
-    return 0;
+template <int BASE, char C, char... CS>
+constexpr unsigned __int128 MakeU128Helper(unsigned __int128 result) {
+  return MakeU128Helper<BASE, CS...>(
+      (C == '\'') ? result : (result * BASE + CharValue(C)));
+}
+
+// MakeU128<BASE, c1, c2, ... , cn>(v) returns cn + ... + c2 * BASE^(n-2) + c1 *
+// BASE^(n-1).
+template <int BASE, char... CS>
+constexpr unsigned __int128 MakeU128() {
+  return MakeU128Helper<BASE, CS...>(0);
+}
+
+template <char... CS>
+struct StaticU128 {
+  static constexpr bool IS_VALID = ValidateU128<10, CS...>();
+  static constexpr unsigned __int128 PAYLOAD = MakeU128<10, CS...>();
+};
+
+template <char... CS>
+struct StaticU128<'0', 'x', CS...> {
+  static constexpr bool IS_VALID = ValidateU128<16, CS...>();
+  static constexpr unsigned __int128 PAYLOAD = MakeU128<16, CS...>();
+};
+
+template <char... CS>
+struct StaticU128<'0', 'X', CS...> {
+  static constexpr bool IS_VALID = ValidateU128<16, CS...>();
+  static constexpr unsigned __int128 PAYLOAD = MakeU128<16, CS...>();
+};
+
+template <char... CS>
+struct StaticU128<'0', 'b', CS...> {
+  static constexpr bool IS_VALID = ValidateU128<2, CS...>();
+  static constexpr unsigned __int128 PAYLOAD = MakeU128<2, CS...>();
+};
+
+template <char... CS>
+struct StaticU128<'0', 'B', CS...> {
+  static constexpr bool IS_VALID = ValidateU128<2, CS...>();
+  static constexpr unsigned __int128 PAYLOAD = MakeU128<2, CS...>();
+};
+
+template <char... CS>
+struct StaticU128<'0', CS...> {
+  static constexpr bool IS_VALID = ValidateU128<8, CS...>();
+  static constexpr unsigned __int128 PAYLOAD = MakeU128<8, CS...>();
+};
+
+static constexpr unsigned __int128 SIGNED_LIMIT =
+    (static_cast<unsigned __int128>(1) << 127) - 1;
+
+template <char... CS>
+struct Static128 {
+  static constexpr bool IS_VALID =
+      ValidateU128<10, CS...>() && (MakeU128<10, CS...>() <= SIGNED_LIMIT);
+  static constexpr __int128 PAYLOAD =
+      static_cast<__int128>(MakeU128<10, CS...>());
+};
+
+template <char... CS>
+struct Static128<'0', 'x', CS...> {
+  static constexpr bool IS_VALID =
+      ValidateU128<16, CS...>() && (MakeU128<16, CS...>() <= SIGNED_LIMIT);
+  static constexpr __int128 PAYLOAD =
+      static_cast<__int128>(MakeU128<16, CS...>());
+};
+
+template <char... CS>
+struct Static128<'0', 'X', CS...> {
+  static constexpr bool IS_VALID =
+      ValidateU128<16, CS...>() && (MakeU128<16, CS...>() <= SIGNED_LIMIT);
+  static constexpr __int128 PAYLOAD =
+      static_cast<__int128>(MakeU128<16, CS...>());
+};
+
+template <char... CS>
+struct Static128<'0', 'b', CS...> {
+  static constexpr bool IS_VALID =
+      ValidateU128<2, CS...>() && (MakeU128<2, CS...>() <= SIGNED_LIMIT);
+  static constexpr __int128 PAYLOAD =
+      static_cast<__int128>(MakeU128<2, CS...>());
+};
+
+template <char... CS>
+struct Static128<'0', 'B', CS...> {
+  static constexpr bool IS_VALID =
+      ValidateU128<2, CS...>() && (MakeU128<2, CS...>() <= SIGNED_LIMIT);
+  static constexpr __int128 PAYLOAD =
+      static_cast<__int128>(MakeU128<2, CS...>());
+};
+
+template <char... CS>
+struct Static128<'0', CS...> {
+  static constexpr bool IS_VALID =
+      ValidateU128<8, CS...>() && (MakeU128<8, CS...>() <= SIGNED_LIMIT);
+  static constexpr __int128 PAYLOAD =
+      static_cast<__int128>(MakeU128<8, CS...>());
+};
+
+}  // namespace suffix128
+
+template <char... CS>
+constexpr unsigned __int128 operator"" _u128() {
+  static_assert(suffix128::StaticU128<CS...>::IS_VALID,
+                "Invalid characters or number too large");
+  return suffix128::StaticU128<CS...>::PAYLOAD;
+}
+
+template <char... CS>
+constexpr __int128 operator"" _128() {
+  static_assert(suffix128::Static128<CS...>::IS_VALID,
+                "Invalid characters or number too large");
+  return suffix128::Static128<CS...>::PAYLOAD;
 }
 
 #endif
