commit 852d046899b77e6bc14345d6b7278946e706ad73
Author: David Sorber <david.sorber@gmail.com>
Date:   Mon Feb 21 17:27:54 2022 -0500

    Modify template to include parameters for strtoXXX functions. Also begin
    integrating support for 128 bit integer types.

diff --git a/software/fixed/fixed.h b/software/fixed/fixed.h
index f8d5656..ad2d4ff 100644
--- a/software/fixed/fixed.h
+++ b/software/fixed/fixed.h
@@ -13,6 +13,10 @@
 #include <stdexcept>
 #include <type_traits>
 
+#ifdef USE_INT128
+#include "int128.h"
+#endif
+
 #if __GNUC__ >= 3
 #define __unlikely(cond) __builtin_expect((cond), 0)
 #define __likely(cond)   __builtin_expect((cond), 1)
@@ -21,7 +25,22 @@
 #define __likely(cond)   (cond)
 #endif
 
-template<typename IntegerType, typename FractionalType>
+template<typename T>
+constexpr T calculateMax(size_t decimal_digits)
+{
+    T val = 0;
+    for (uint32_t idx = 0; idx < decimal_digits; ++idx)
+    {
+        val *= 10;
+        val += 9;
+    }
+    return val;
+}
+
+
+template<typename IntegerType, typename FractionalType, 
+         typename StrToIntegerTypeFunc,
+         typename StrToFracTypeFunc>
 class fixed 
 {
     static_assert(std::is_integral<IntegerType>::value,
@@ -44,13 +63,13 @@ public:
     static constexpr size_t fractional_decimal_digits =
         std::floor(std::log10(std::numeric_limits<FractionalType>::max()));
 
-    static constexpr size_t MAX_INTEGER_VALUE =
-        (static_cast<uint64_t>(std::pow(10, integer_decimal_digits)) - 1);
-    static constexpr size_t MIN_INTEGER_VALUE = 0;
+    static constexpr IntegerType MAX_INTEGER_VALUE = 
+        (calculateMax<IntegerType>(integer_decimal_digits));
+    static constexpr IntegerType MIN_INTEGER_VALUE = 0;
 
-    static constexpr size_t MAX_FRACTIONAL_VALUE =
-        (static_cast<uint64_t>(std::pow(10, fractional_decimal_digits)) - 1);
-    static constexpr size_t MIN_FRACTIONAL_VALUE = 0;
+    static constexpr FractionalType MAX_FRACTIONAL_VALUE = 
+        (calculateMax<FractionalType>(fractional_decimal_digits));
+    static constexpr FractionalType MIN_FRACTIONAL_VALUE = 0;
 
     static const uint64_t SCALE_VALUES[20];
 
@@ -109,7 +128,8 @@ public:
     uint32_t parse(const char* input)
     {
         char* endPtr = nullptr;
-        m_integer = __checkIntOverflow(strtoll(input, &endPtr, 10));
+        //~ m_integer = __checkIntOverflow(strtoll(input, &endPtr, 10));
+        m_integer = __checkIntOverflow(strto_inttype(input, &endPtr, 10));
         m_fractional = 0;
 
         if (std::isdigit(*endPtr))
@@ -122,7 +142,8 @@ public:
         {
             char* fracEndPtr = nullptr;
             FractionalType fracTemp =
-                __checkFracOverflow(strtoull(endPtr + 1, &fracEndPtr, 10));
+                //~ __checkFracOverflow(strtoull(endPtr + 1, &fracEndPtr, 10));
+                __checkFracOverflow(strto_fractype(endPtr + 1, &fracEndPtr, 10));
             uint32_t fracLen = (fracEndPtr - endPtr) - 1;
 
             fracLen = fractional_decimal_digits - fracLen;
@@ -144,37 +165,40 @@ public:
     inline fixed& operator/=(const fixed& y) noexcept = delete;
 
     // Comparison operators
-    template<typename I, typename F>
+    template<typename I, typename F, typename STI, typename STF>
     friend constexpr inline bool operator==(
-        const fixed<I, F>& x, const fixed<I, F>& y) noexcept;
+        const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept;
 
-    template<typename I, typename F>
+    template<typename I, typename F, typename STI, typename STF>
     friend constexpr inline bool operator!=(
-        const fixed<I, F>& x, const fixed<I, F>& y) noexcept;
+        const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept;
 
-    template<typename I, typename F>
+    template<typename I, typename F, typename STI, typename STF>
     friend constexpr inline bool operator<(
-        const fixed<I, F>& x, const fixed<I, F>& y) noexcept;
+        const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept;
 
-    template<typename I, typename F>
+    template<typename I, typename F, typename STI, typename STF>
     friend constexpr inline bool operator>(
-        const fixed<I, F>& x, const fixed<I, F>& y) noexcept;
+        const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept;
 
-    template<typename I, typename F>
+    template<typename I, typename F, typename STI, typename STF>
     friend constexpr inline bool operator<=(
-        const fixed<I, F>& x, const fixed<I, F>& y) noexcept;
+        const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept;
 
-    template<typename I, typename F>
+    template<typename I, typename F, typename STI, typename STF>
     friend constexpr inline bool operator>=(
-        const fixed<I, F>& x, const fixed<I, F>& y) noexcept;
+        const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept;
 
     // Output stream operator
-    template<typename I, typename F>
+    template<typename I, typename F, typename STI, typename STF>
     friend inline std::ostream& operator<<(
-        std::ostream& os, const fixed<I, F>& rhs);
+        std::ostream& os, const fixed<I, F, STI, STF>& rhs);
 
 private:
 
+    static StrToIntegerTypeFunc strto_inttype;
+    static StrToFracTypeFunc strto_fractype;
+
     static inline IntegerType __checkIntOverflow(IntegerType integerVal)
     {
         if (__unlikely(integerVal > MAX_INTEGER_VALUE))
@@ -203,30 +227,73 @@ private:
     FractionalType m_fractional;
 };
 
+// Functor for calling std::strtoll()
+struct strtoll_ftor {
+    inline int64_t operator()(const char* str, char** str_end, int base) 
+    { 
+        return std::strtoll(str, str_end, base); 
+    }
+};
+
+// Functor for calling std::strtoull()
+struct strtoull_ftor {
+    inline uint64_t operator()(const char* str, char** str_end, int base) 
+    { 
+        return std::strtoull(str, str_end, base); 
+    }
+};
+
+#ifdef USE_INT128
+
+// Functor for calling strtoll_128()
+struct strtoll_128_ftor {
+    inline int64_t operator()(const char* str, char** str_end, int base) 
+    { 
+        //~ return strtoll_128(str, str_end, base); 
+        return strtoll_128_b10opt(str, str_end, base); 
+    }
+};
+
+// Functor for calling strtoull_128()
+struct strtoull_128_ftor {
+    inline uint64_t operator()(const char* str, char** str_end, int base) 
+    { 
+        //~ return strtoull_128(str, str_end, base); 
+        return strtoull_128_b10opt(str, str_end, base); 
+    }
+};
+
+#endif
+
+
 // Predefined types
-using fixed_8_8  = fixed<int8_t, uint8_t>;
-using fixed_8_16 = fixed<int8_t, uint16_t>;
-using fixed_8_32 = fixed<int8_t, uint32_t>;
-using fixed_8_64 = fixed<int8_t, uint64_t>;
-
-using fixed_16_8  = fixed<int16_t, uint8_t>;
-using fixed_16_16 = fixed<int16_t, uint16_t>;
-using fixed_16_32 = fixed<int16_t, uint32_t>;
-using fixed_16_64 = fixed<int16_t, uint64_t>;
-
-using fixed_32_8  = fixed<int32_t, uint8_t>;
-using fixed_32_16 = fixed<int32_t, uint16_t>;
-using fixed_32_32 = fixed<int32_t, uint32_t>;
-using fixed_32_64 = fixed<int32_t, uint64_t>;
-
-using fixed_64_8  = fixed<int64_t, uint8_t>;
-using fixed_64_16 = fixed<int64_t, uint16_t>;
-using fixed_64_32 = fixed<int64_t, uint32_t>;
-using fixed_64_64 = fixed<int64_t, uint64_t>;
+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>;
+
+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>;
+
+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>;
+
+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>;
+
+#ifdef USE_INT128
+using fixed_128_128 = fixed<int128_t, uint128_t, strtoll_128_ftor, strtoull_128_ftor>;
+#endif
 
 // Precomputed scale value constants
-template<typename I, typename F>
-const uint64_t fixed<I, F>::SCALE_VALUES[20] = 
+template<typename I, typename F, typename STI, typename STF>
+const uint64_t fixed<I, F, STI, STF>::SCALE_VALUES[20] = 
 {
     /*  0 */ 1ULL,
     /*  1 */ 10ULL,
@@ -250,23 +317,23 @@ const uint64_t fixed<I, F>::SCALE_VALUES[20] =
     /* 19 */ 10000000000000000000ULL
 };
 
-template<typename I, typename F>
+template<typename I, typename F, typename STI, typename STF>
 constexpr inline bool operator==(
-    const fixed<I, F>& x, const fixed<I, F>& y) noexcept
+    const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept
 {
     return (x.m_integer == y.m_integer && x.m_fractional == y.m_fractional);
 }
 
-template<typename I, typename F>
+template<typename I, typename F, typename STI, typename STF>
 constexpr inline bool operator!=(
-    const fixed<I, F>& x, const fixed<I, F>& y) noexcept
+    const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept
 {
     return (! (x == y));
 }
 
-template<typename I, typename F>
+template<typename I, typename F, typename STI, typename STF>
 constexpr inline bool operator<(
-    const fixed<I, F>& x, const fixed<I, F>& y) noexcept
+    const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept
 {
     if (x.m_integer == y.m_integer)
     {
@@ -280,9 +347,9 @@ constexpr inline bool operator<(
     return false;
 }
 
-template<typename I, typename F>
+template<typename I, typename F, typename STI, typename STF>
 constexpr inline bool operator>(
-    const fixed<I, F>& x, const fixed<I, F>& y) noexcept
+    const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept
 {
     if (x.m_integer == y.m_integer)
     {
@@ -296,9 +363,9 @@ constexpr inline bool operator>(
     return false;
 }
 
-template<typename I, typename F>
+template<typename I, typename F, typename STI, typename STF>
 constexpr inline bool operator<=(
-    const fixed<I, F>& x, const fixed<I, F>& y) noexcept
+    const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept
 {
     if (x.m_integer == y.m_integer)
     {
@@ -312,9 +379,9 @@ constexpr inline bool operator<=(
     return false;
 }
 
-template<typename I, typename F>
+template<typename I, typename F, typename STI, typename STF>
 constexpr inline bool operator>=(
-    const fixed<I, F>& x, const fixed<I, F>& y) noexcept
+    const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept
 {
     if (x.m_integer == y.m_integer)
     {
@@ -329,31 +396,31 @@ constexpr inline bool operator>=(
 }
 
 // Addition -- not yet supported
-template<typename I, typename F>
-constexpr inline fixed<I, F> operator+(
-    const fixed<I, F>& x, const fixed<I, F>& y) noexcept = delete;
+template<typename I, typename F, typename STI, typename STF>
+constexpr inline fixed<I, F, STI, STF> operator+(
+    const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept = delete;
 
 // Subtraction -- not yet supported
-template<typename I, typename F>
-constexpr inline fixed<I, F> operator-(
-    const fixed<I, F>& x, const fixed<I, F>& y) noexcept = delete;
+template<typename I, typename F, typename STI, typename STF>
+constexpr inline fixed<I, F, STI, STF> operator-(
+    const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept = delete;
 
 // Multiplication
-template<typename I, typename F>
-constexpr inline fixed<I, F> operator*(
-    const fixed<I, F>& x, const fixed<I, F>& y) noexcept = delete;
+template<typename I, typename F, typename STI, typename STF>
+constexpr inline fixed<I, F, STI, STF> operator*(
+    const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept = delete;
 
 // Division -- not yet supported
-template<typename I, typename F>
-constexpr inline fixed<I, F> operator/(
-    const fixed<I, F>& x, const fixed<I, F>& y) noexcept = delete;
+template<typename I, typename F, typename STI, typename STF>
+constexpr inline fixed<I, F, STI, STF> operator/(
+    const fixed<I, F, STI, STF>& x, const fixed<I, F, STI, STF>& y) noexcept = delete;
 
 // Stream output
-template<typename I, typename F>
-inline std::ostream& operator<<(std::ostream& os, const fixed<I, F>& rhs)
+template<typename I, typename F, typename STI, typename STF>
+inline std::ostream& operator<<(std::ostream& os, const fixed<I, F, STI, STF>& rhs)
 {
     return os << std::fixed << rhs.m_integer << "."
-              << std::setw(fixed<I, F>::fractional_decimal_digits)
+              << std::setw(fixed<I, F, STI, STF>::fractional_decimal_digits)
               << std::setfill('0') << rhs.m_fractional;
 }
 
diff --git a/software/fixed/fixed_util.cc b/software/fixed/fixed_util.cc
index 10108cf..17a764e 100644
--- a/software/fixed/fixed_util.cc
+++ b/software/fixed/fixed_util.cc
@@ -13,6 +13,7 @@ int main(int argc, char** argv)
 {
     std::cout << "Fixed Type Test Util\n" << std::endl;
 
+    //-------------------------------------------------------------------------
     fixed_64_64 foobar;
     std::cout << "Type size: " << sizeof(fixed_64_64) << std::endl;
     
@@ -24,7 +25,25 @@ int main(int argc, char** argv)
     std::cout << "Max fractional value: " << fixed_64_64::MAX_FRACTIONAL_VALUE << std::endl;
     
     std::cout << std::endl;
-    std::cout << "Value: " << foobar << std::endl;
+    std::cout << "Value: " << foobar << "\n\n" << std::endl;
+    //-------------------------------------------------------------------------
+#ifdef USE_INT128
+
+    fixed_128_128 barfoo;
+    std::cout << "Type size: " << sizeof(barfoo) << std::endl;
+    
+    std::cout << "Integer decimal digits: " << fixed_128_128::integer_decimal_digits << std::endl;
+    std::cout << "Fractional decimal digits: " << fixed_128_128::fractional_decimal_digits << std::endl;
+    
+    std::cout << "Max integer value: " << fixed_128_128::MAX_INTEGER_VALUE << std::endl;
+    
+    std::cout << "Max fractional value: " << fixed_128_128::MAX_FRACTIONAL_VALUE << std::endl;
+    
+    std::cout << std::endl;
+    std::cout << "Value: " << barfoo << "\n\n" << std::endl;
+
+#endif    
+    //-------------------------------------------------------------------------
     
     fixed_64_64 quxbar(3, 1415926353);
     std::cout << "Value: " << quxbar << std::endl;
diff --git a/software/fixed/parse_test.cc b/software/fixed/parse_test.cc
index dd03734..caa421f 100644
--- a/software/fixed/parse_test.cc
+++ b/software/fixed/parse_test.cc
@@ -167,6 +167,23 @@ int main(int argc, char** argv)
                   << " ms" << std::endl;
     }
 #endif
+    // 5) 
+    else if (parseType == 5)
+    {
+        std::cout << "Parse type 5: fixed_128_128" << std::endl;
+        auto start = std::chrono::system_clock::now();
+        for (auto& strValue : values)
+        {
+            fvalue.parse(strValue.c_str());
+            //~ std::cout << "In: " << strValue << " -- Out: " << fvalue << std::endl;
+        }
+        auto end = std::chrono::system_clock::now();
+        auto diff = end - start;
+        std::cout << "Total parse time: "
+                  << std::chrono::duration <double, std::milli>(diff).count() 
+                  << " ms" << std::endl;
+        
+    }
     else
     {
         std::cerr << "ERROR: invalid parse type: " << parseType << std::endl;
