diff --git a/.github/workflows/wide_integer.yml b/.github/workflows/wide_integer.yml index 125d22ba..0399d20b 100644 --- a/.github/workflows/wide_integer.yml +++ b/.github/workflows/wide_integer.yml @@ -614,7 +614,7 @@ jobs: - name: build-example-stm32f429 run: | mkdir -p bin - emu_env/gcc-arm-none-eabi-10.3-2021.10/bin/arm-none-eabi-g++ -std=${{ matrix.standard }} -Wall -Wextra -pedantic -O0 -g -gdwarf-2 -ffunction-sections -fdata-sections -x c++ -fno-rtti -fno-use-cxa-atexit -fno-exceptions -fno-nonansi-builtins -fno-threadsafe-statics -fno-enforce-eh-specs -ftemplate-depth=32 -mcpu=cortex-m4 -mtune=cortex-m4 -mthumb -mfloat-abi=soft -mno-unaligned-access -mno-long-calls -I. -DWIDE_INTEGER_DISABLE_IOSTREAM -DWIDE_INTEGER_DISABLE_TRIVIAL_COPY_AND_STD_LAYOUT_CHECKS -DWIDE_INTEGER_NAMESPACE=ckormanyos -DWIDE_INTEGER_STANDALONE_${{ steps.upcase_my_example.outputs.uppercase }} examples/${{ matrix.example }}.cpp ./target/micros/stm32f429/make/single/crt.cpp -nostartfiles -Wl,--gc-sections -Wl,-Map,./bin/${{ matrix.example }}.map -T ./target/micros/stm32f429/make/stm32f429.ld --specs=nano.specs --specs=nosys.specs -o ./bin/${{ matrix.example }}.elf + emu_env/gcc-arm-none-eabi-10.3-2021.10/bin/arm-none-eabi-g++ -std=${{ matrix.standard }} -Wall -Wextra -pedantic -O0 -g -gdwarf-2 -ffunction-sections -fdata-sections -x c++ -fno-rtti -fno-use-cxa-atexit -fno-exceptions -fno-nonansi-builtins -fno-threadsafe-statics -fno-enforce-eh-specs -ftemplate-depth=32 -mcpu=cortex-m4 -mtune=cortex-m4 -mthumb -mfloat-abi=soft -mno-unaligned-access -mno-long-calls -I. -DWIDE_INTEGER_DISABLE_IOSTREAM -DWIDE_INTEGER_DISABLE_TO_STRING -DWIDE_INTEGER_DISABLE_TRIVIAL_COPY_AND_STD_LAYOUT_CHECKS -DWIDE_INTEGER_NAMESPACE=ckormanyos -DWIDE_INTEGER_STANDALONE_${{ steps.upcase_my_example.outputs.uppercase }} examples/${{ matrix.example }}.cpp ./target/micros/stm32f429/make/single/crt.cpp -nostartfiles -Wl,--gc-sections -Wl,-Map,./bin/${{ matrix.example }}.map -T ./target/micros/stm32f429/make/stm32f429.ld --specs=nano.specs --specs=nosys.specs -o ./bin/${{ matrix.example }}.elf emu_env/gcc-arm-none-eabi-10.3-2021.10/bin/arm-none-eabi-objcopy ./bin/${{ matrix.example }}.elf -O ihex ./bin/${{ matrix.example }}.hex ls -la ./bin/${{ matrix.example }}.elf ./bin/${{ matrix.example }}.hex ./bin/${{ matrix.example }}.map working-directory: ./ @@ -658,8 +658,8 @@ jobs: mkdir bin echo ${{ steps.upcase_my_example.outputs.uppercase }} echo 'compile examples/${{ matrix.example }}.cpp with:' - echo 'avr-g++ -x c++ -std=${{ matrix.standard }} -Os -Werror -Wall -Wextra -pedantic -Wmain -Wundef -Wconversion -Wsign-conversion -Wunused-parameter -Wuninitialized -Wmissing-declarations -Wshadow -Wunreachable-code -Wswitch-default -Wswitch-enum -Wcast-align -Wmissing-include-dirs -Winit-self -Wfloat-equal -Wdouble-promotion -mmcu=atmega328p -mrelax -finline-functions -finline-limit=32 -fsigned-char -g -gdwarf-2 -fno-exceptions -ffunction-sections -fdata-sections -fno-rtti -fno-use-cxa-atexit -fno-exceptions -fno-threadsafe-statics -fno-enforce-eh-specs -ftemplate-depth=32 -Wzero-as-null-pointer-constant -I. -I../real-time-cpp-root/ref_app/src -I../real-time-cpp-root/ref_app/src/util/STL -DWIDE_INTEGER_DISABLE_IOSTREAM -DWIDE_INTEGER_DISABLE_TRIVIAL_COPY_AND_STD_LAYOUT_CHECKS -DWIDE_INTEGER_DISABLE_IMPLEMENT_UTIL_DYNAMIC_ARRAY -DWIDE_INTEGER_NAMESPACE=ckormanyos -DWIDE_INTEGER_STANDALONE_${{ steps.upcase_my_example.outputs.uppercase }} examples/${{ matrix.example }}.cpp -Wl,--gc-sections -Wl,-Map,./bin/${{ matrix.example }}.map -o bin/${{ matrix.example }}.elf' - avr-g++ -x c++ -std=${{ matrix.standard }} -Os -Werror -Wall -Wextra -pedantic -Wmain -Wundef -Wconversion -Wsign-conversion -Wunused-parameter -Wuninitialized -Wmissing-declarations -Wshadow -Wunreachable-code -Wswitch-default -Wswitch-enum -Wcast-align -Wmissing-include-dirs -Winit-self -Wfloat-equal -Wdouble-promotion -mmcu=atmega328p -mrelax -finline-functions -finline-limit=32 -fsigned-char -g -gdwarf-2 -fno-exceptions -ffunction-sections -fdata-sections -fno-rtti -fno-use-cxa-atexit -fno-exceptions -fno-threadsafe-statics -fno-enforce-eh-specs -ftemplate-depth=32 -Wzero-as-null-pointer-constant -I. -I../real-time-cpp-root/ref_app/src -I../real-time-cpp-root/ref_app/src/util/STL -DWIDE_INTEGER_DISABLE_IOSTREAM -DWIDE_INTEGER_DISABLE_TRIVIAL_COPY_AND_STD_LAYOUT_CHECKS -DWIDE_INTEGER_DISABLE_IMPLEMENT_UTIL_DYNAMIC_ARRAY -DWIDE_INTEGER_NAMESPACE=ckormanyos -DWIDE_INTEGER_STANDALONE_${{ steps.upcase_my_example.outputs.uppercase }} examples/${{ matrix.example }}.cpp -Wl,--gc-sections -Wl,-Map,./bin/${{ matrix.example }}.map -o bin/${{ matrix.example }}.elf + echo 'avr-g++ -x c++ -std=${{ matrix.standard }} -Os -Werror -Wall -Wextra -pedantic -Wmain -Wundef -Wconversion -Wsign-conversion -Wunused-parameter -Wuninitialized -Wmissing-declarations -Wshadow -Wunreachable-code -Wswitch-default -Wswitch-enum -Wcast-align -Wmissing-include-dirs -Winit-self -Wfloat-equal -Wdouble-promotion -mmcu=atmega328p -mrelax -finline-functions -finline-limit=32 -fsigned-char -g -gdwarf-2 -fno-exceptions -ffunction-sections -fdata-sections -fno-rtti -fno-use-cxa-atexit -fno-exceptions -fno-threadsafe-statics -fno-enforce-eh-specs -ftemplate-depth=32 -Wzero-as-null-pointer-constant -I. -I../real-time-cpp-root/ref_app/src -I../real-time-cpp-root/ref_app/src/util/STL -DWIDE_INTEGER_DISABLE_IOSTREAM -DWIDE_INTEGER_DISABLE_TO_STRING -DWIDE_INTEGER_DISABLE_TRIVIAL_COPY_AND_STD_LAYOUT_CHECKS -DWIDE_INTEGER_DISABLE_IMPLEMENT_UTIL_DYNAMIC_ARRAY -DWIDE_INTEGER_NAMESPACE=ckormanyos -DWIDE_INTEGER_STANDALONE_${{ steps.upcase_my_example.outputs.uppercase }} examples/${{ matrix.example }}.cpp -Wl,--gc-sections -Wl,-Map,./bin/${{ matrix.example }}.map -o bin/${{ matrix.example }}.elf' + avr-g++ -x c++ -std=${{ matrix.standard }} -Os -Werror -Wall -Wextra -pedantic -Wmain -Wundef -Wconversion -Wsign-conversion -Wunused-parameter -Wuninitialized -Wmissing-declarations -Wshadow -Wunreachable-code -Wswitch-default -Wswitch-enum -Wcast-align -Wmissing-include-dirs -Winit-self -Wfloat-equal -Wdouble-promotion -mmcu=atmega328p -mrelax -finline-functions -finline-limit=32 -fsigned-char -g -gdwarf-2 -fno-exceptions -ffunction-sections -fdata-sections -fno-rtti -fno-use-cxa-atexit -fno-exceptions -fno-threadsafe-statics -fno-enforce-eh-specs -ftemplate-depth=32 -Wzero-as-null-pointer-constant -I. -I../real-time-cpp-root/ref_app/src -I../real-time-cpp-root/ref_app/src/util/STL -DWIDE_INTEGER_DISABLE_IOSTREAM -DWIDE_INTEGER_DISABLE_TO_STRING -DWIDE_INTEGER_DISABLE_TRIVIAL_COPY_AND_STD_LAYOUT_CHECKS -DWIDE_INTEGER_DISABLE_IMPLEMENT_UTIL_DYNAMIC_ARRAY -DWIDE_INTEGER_NAMESPACE=ckormanyos -DWIDE_INTEGER_STANDALONE_${{ steps.upcase_my_example.outputs.uppercase }} examples/${{ matrix.example }}.cpp -Wl,--gc-sections -Wl,-Map,./bin/${{ matrix.example }}.map -o bin/${{ matrix.example }}.elf echo echo 'run objcopy with:' echo 'avr-objcopy bin/${{ matrix.example }}.elf -O ihex bin/${{ matrix.example }}.hex' diff --git a/README.md b/README.md index cdd68709..a37abc0c 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,6 @@ Wide-integer ================== ->ANNOUNCEMENT: Support for C++11 will be deprecated in this library starting in summer 2022. ->New features will require _at_ _least_ C++14, as will existing features starting with the deprecation. -

Build Status @@ -35,15 +32,16 @@ unsigned and signed integral types. This C++ template header-only library implements drop-in big integer types such as `uint128_t`, `uint256_t`, `uint384_t`, `uint512_t`, `uint1024_t`, `uint1536_t`, etc. These can be used essentially like regular built-in integers. -Corresponding _signed_ integer types such as `int128_t`, `int256_t`, etc. can also be used. +Corresponding _signed_ integer types such as `int128_t`, `int256_t`, and the like +can also be used. The big integer class is called `math::wide_integer::uintwide_t` (i.e., `uintwide_t` residing in the `namespace` `math::wide_integer`), as shown in greater detail below. Wide-integer supports both unsigned as well as -signed integral types having width of $1{\ldots}63{\times}2^{N}$ -while being $16$, $24$, $32$ or larger. +signed integral types having width of $1{\ldots}63{\times}2^N$ +while being 16, 24, 32 or larger. In addition, small integer types such as software-synthesized versions of `uint24_t`, `uint48_t`, `uint64_t`, `uint96_t`, `uint128_t`, etc. (or signed counterparts of these) can also be created with wide-integer. @@ -321,11 +319,10 @@ readme page. Wide-Integer has been tested with numerous compilers, for target systems ranging from 8 to 64 bits. The library is specifically designed for efficiency with small to medium bit counts. -Supported bit counts include integers -$1{\ldots}63{\times}2^{N}$ -while being $16$, $24$, $32$ or larger such as -$256$, $384$, $512$, $768$, $1024$, -or other less common bit counts such as $11,264$, etc. +Supported bit counts include integers $1{\ldots}63{\times}2^N$ +while being 16, 24, 32 or larger such as +256, 384, 512, 768, 1024, +or other less common bit counts such as 11,264, etc. Small, medium and large bit counts are supported. Common applications might use the range of `uint128_t`, `uint256_t` or `uint512_t`. @@ -347,6 +344,7 @@ enabled or disabled at compile time with the compiler switches: ```cpp #define WIDE_INTEGER_DISABLE_IOSTREAM +#define WIDE_INTEGER_DISABLE_TO_STRING #define WIDE_INTEGER_DISABLE_FLOAT_INTEROP #define WIDE_INTEGER_DISABLE_IMPLEMENT_UTIL_DYNAMIC_ARRAY #define WIDE_INTEGER_HAS_LIMB_TYPE_UINT64 @@ -365,6 +363,17 @@ I/O streaming can optionally be disabled with the compiler switch: The default setting is `WIDE_INTEGER_DISABLE_IOSTREAM` not set and I/O streaming operations are enabled. +Conversion to `std::string` is supported with the namespace-specific function +`to_string`. This analagous to the standard library's `std::to_string` function, +but implemented specifically for instances of `uintwide_t`. +Wide-integer's local, namespace-specific `to_string` +function (and the inclusion of the necessary `` header) +are both deactivated with: + +```cpp +#define WIDE_INTEGER_DISABLE_TO_STRING +``` + Interoperability with built-in floating-point types such as construct-from, cast-to, binary arithmetic with built-in floating-point types can be @@ -535,8 +544,8 @@ auto main() -> int ``` Wide-integer also supports a small selection of number-theoretical -functions such as least and most significant bit, square root, -$k^{th}$ root, +functions such as least and most significant bit, +square root, $k^{th}$ root, power, power-modulus, greatest common denominator and random number generation. These functions are be found via ADL. @@ -605,7 +614,7 @@ auto main() -> int The next example computes the real-valued cube root of $10^{3,333}$. The real-valued cube root of this very large unsigned integer is $10^{1,111}$. We will use the (somewhat uncommon) integral data type `uint11264_t`. -Since `uint11264_t` has approximately $3,390$ decimal digits of precision, +Since `uint11264_t` has approximately 3,390 decimal digits of precision, it is large enough to hold the value of $10^{3,333}$ prior to (and following) the cube root operation. @@ -739,7 +748,7 @@ negative arguments in number theoretical functions. - `cbrt` of `x` nexative integer returns `-cbrt(-x)`. - $k^{th}$ root of `x` negative returns zero unless the cube root is being computed, in which case `-cbrt(-x)` is returned. - GCD of `a`, `b` signed converts both arguments to positive and negates the result for `a`, `b` having opposite signs. - - Miller-Rabin primality testing treats negative inetegers as positive when testing for prime, thus extending the set of primes $p{\in}{\mathbb{Z}}$. + - Miller-Rabin primality testing treats negative inetegers as positive when testing for prime, thus extending the set of primes to negative integers. - MSB/LSB (most/least significant bit) do not differentiate between positive or negative argument such that MSB of a negative integer will be the highest bit of the corresponding unsigned type. - Printing both positive-valued and negative-valued signed integers in hexadecimal format is supported. When printing negative-valued, signed `uintwide_t` in hexadecimal format, the sign bit and all other bits are treated as if the integer were unsigned. The negative sign is not explicitly shown when using hexadecimal format, even if the underlying integer is signed and negative-valued. A potential positive sign, however, will be shown for positive-valued signed integers in hexadecimal form in the presence of `std::showpos`. diff --git a/boost/multiprecision/uintwide_t_backend.hpp b/boost/multiprecision/uintwide_t_backend.hpp index 7554cef1..f837d70e 100644 --- a/boost/multiprecision/uintwide_t_backend.hpp +++ b/boost/multiprecision/uintwide_t_backend.hpp @@ -235,16 +235,14 @@ static_cast::size_type>(representation_type::wr_string_max_buffer_size_dec) ); - const std::uint_fast8_t base_rep = (((format_flags & std::ios::hex) != 0) ? 16U : 10U); - const bool show_base = ( (format_flags & std::ios::showbase) != 0); - const bool show_pos = ( (format_flags & std::ios::showpos) != 0); - const bool is_uppercase = ( (format_flags & std::ios::uppercase) != 0); + const auto base_rep = static_cast(((format_flags & std::ios::hex) != 0) ? 16U : 10U); + const auto show_base = ((format_flags & std::ios::showbase) != 0); + const auto show_pos = ((format_flags & std::ios::showpos) != 0); + const auto is_uppercase = ((format_flags & std::ios::uppercase) != 0); - const bool wr_string_is_ok = m_value.wr_string(pstr.data(), base_rep, show_base, show_pos, is_uppercase); + const auto wr_string_is_ok = m_value.wr_string(pstr.data(), base_rep, show_base, show_pos, is_uppercase); - std::string str_result = (wr_string_is_ok ? std::string(pstr.data()) : std::string()); - - return str_result; + return (wr_string_is_ok ? std::string(pstr.data()) : std::string()); } WIDE_INTEGER_CONSTEXPR auto negate() -> void diff --git a/math/wide_integer/uintwide_t.h b/math/wide_integer/uintwide_t.h index edee9f26..3ded8a46 100644 --- a/math/wide_integer/uintwide_t.h +++ b/math/wide_integer/uintwide_t.h @@ -10,6 +10,9 @@ #include #include + #if defined(__cpp_lib_to_chars) + #include + #endif #include #if !defined(WIDE_INTEGER_DISABLE_FLOAT_INTEROP) #include @@ -32,6 +35,9 @@ #include #include #endif + #if !defined(WIDE_INTEGER_DISABLE_TO_STRING) + #include + #endif #include #if (defined(__clang__) && (__clang_major__ <= 9)) @@ -814,6 +820,25 @@ DistributionType& distribution, GeneratorType& generator) -> bool; + #if defined(__cpp_lib_to_chars) + template + auto to_chars(char* first, + char* last, + const uintwide_t& x, + int base = static_cast(INT8_C(10))) -> std::to_chars_result; + #endif + + #if !defined(WIDE_INTEGER_DISABLE_TO_STRING) + template + auto to_string(const uintwide_t& x) -> std::string; + #endif + #if(__cplusplus >= 201703L) } // namespace math::wide_integer #else @@ -1292,16 +1317,15 @@ // The type of the internal data representation. using representation_type = - typename std::conditional + std::conditional_t ::value, detail::fixed_static_array , detail::fixed_dynamic_array::value, - std::allocator, - AllocatorType>::type>::template rebind_alloc> - >::type; + typename std::allocator_traits::value, + std::allocator, + AllocatorType>>::template rebind_alloc>>; // The iterator types of the internal data representation. using iterator = typename representation_type::iterator; @@ -2098,16 +2122,15 @@ const auto mask = static_cast(static_cast(0x7U)); using string_storage_oct_type = - typename std::conditional + std::conditional_t (UINT32_C(2048)), detail::fixed_static_array , detail::fixed_dynamic_array::value, - std::allocator, - AllocatorType>::type>::template rebind_alloc> - >::type; + typename std::allocator_traits::value, + std::allocator, + AllocatorType>>::template rebind_alloc>>; string_storage_oct_type str_temp; // LCOV_EXCL_LINE @@ -2185,16 +2208,15 @@ } using string_storage_dec_type = - typename std::conditional + std::conditional_t (UINT32_C(2048)), detail::fixed_static_array , detail::fixed_dynamic_array::value, - std::allocator, - AllocatorType>::type>::template rebind_alloc> - >::type; + typename std::allocator_traits::value, + std::allocator, + AllocatorType>>::template rebind_alloc>>; string_storage_dec_type str_temp; @@ -2252,16 +2274,15 @@ uintwide_t t(*this); using string_storage_hex_type = - typename std::conditional + std::conditional_t (UINT32_C(2048)), detail::fixed_static_array , detail::fixed_dynamic_array::value, - std::allocator, - AllocatorType>::type>::template rebind_alloc> - >::type; + typename std::allocator_traits::value, + std::allocator, + AllocatorType>>::template rebind_alloc>>; string_storage_hex_type str_temp; @@ -2673,22 +2694,22 @@ // Good examples for this (both threaded as well as non-threaded) // can be found in the wide_decimal project. using result_array_type = - typename std::conditional::value, - detail::fixed_static_array , - detail::fixed_dynamic_array::value, - std::allocator, - AllocatorType>::type>::template rebind_alloc>>::type; + std::conditional_t::value, + detail::fixed_static_array , + detail::fixed_dynamic_array::value, + std::allocator, + AllocatorType>>::template rebind_alloc>>; using storage_array_type = - typename std::conditional::value, - detail::fixed_static_array , - detail::fixed_dynamic_array::value, - std::allocator, - AllocatorType>::type>::template rebind_alloc>>::type; + std::conditional_t::value, + detail::fixed_static_array , + detail::fixed_dynamic_array::value, + std::allocator, + AllocatorType>>::template rebind_alloc>>; result_array_type result; storage_array_type t; @@ -3263,7 +3284,12 @@ for(auto j = static_cast(0U); j < static_cast(count - i); ++j) { - const auto i_plus_j = static_cast(i + j); + const auto i_plus_j = + static_cast + ( + static_cast(i) + + static_cast(j) + ); carry = static_cast(carry + static_cast(static_cast(*(a + static_cast(i))) * *(b + static_cast(j)))); carry = static_cast(carry + *(r + i_plus_j)); @@ -3704,13 +3730,13 @@ // Step D1(c): normalize v -> v * d = vv. using uu_array_type = - typename std::conditional::value, - detail::fixed_static_array , - detail::fixed_dynamic_array::value, - std::allocator, - AllocatorType>::type>::template rebind_alloc>>::type; + std::conditional_t::value, + detail::fixed_static_array , + detail::fixed_dynamic_array::value, + std::allocator, + AllocatorType>>::template rebind_alloc>>; uu_array_type uu; representation_type vv; @@ -4229,7 +4255,7 @@ typename AllocatorType, const bool IsSigned> WIDE_INTEGER_NUM_LIMITS_CLASS_TYPE numeric_limits_uintwide_t_base - : public std::numeric_limits::type> + : public std::numeric_limits> { private: using local_wide_integer_type = uintwide_t; @@ -4469,16 +4495,15 @@ if(base_rep == static_cast(UINT8_C(8))) { using string_storage_oct_type = - typename std::conditional + std::conditional_t (UINT32_C(2048)), detail::fixed_static_array , detail::fixed_dynamic_array::value, - std::allocator, - AllocatorType>::type>::template rebind_alloc> - >::type; + typename std::allocator_traits::value, + std::allocator, + AllocatorType>>::template rebind_alloc>>; // TBD: There is redundant storage of this kind both here // in this subroutine as well as in the wr_string method. @@ -4493,16 +4518,15 @@ else if(base_rep == static_cast(UINT8_C(10))) { using string_storage_dec_type = - typename std::conditional + std::conditional_t (UINT32_C(2048)), detail::fixed_static_array , detail::fixed_dynamic_array::value, - std::allocator, - AllocatorType>::type>::template rebind_alloc> - >::type; + typename std::allocator_traits::value, + std::allocator, + AllocatorType>>::template rebind_alloc>>; // TBD: There is redundant storage of this kind both here // in this subroutine as well as in the wr_string method. @@ -4517,16 +4541,15 @@ else if(base_rep == static_cast(UINT8_C(16))) { using string_storage_hex_type = - typename std::conditional + std::conditional_t (UINT32_C(2048)), detail::fixed_static_array , detail::fixed_dynamic_array::value, - std::allocator, - AllocatorType>::type>::template rebind_alloc> - >::type; + typename std::allocator_traits::value, + std::allocator, + AllocatorType>>::template rebind_alloc>>; // TBD: There is redundant storage of this kind both here // in this subroutine as well as in the wr_string method. @@ -5902,6 +5925,249 @@ return is_probably_prime; } + #if defined(__cpp_lib_to_chars) + template + auto to_chars(char* first, + char* last, + const uintwide_t& x, + int base) -> std::to_chars_result + { + using local_wide_integer_type = uintwide_t; + using local_limb_type = typename local_wide_integer_type::limb_type; + + constexpr auto local_my_width2 = local_wide_integer_type::my_width2; + + constexpr auto char_fill = '.'; + + const auto base_rep = static_cast(base); + const auto show_base = false; + const auto show_pos = false; + const auto is_uppercase = false; + + std::to_chars_result result { last, std::errc::value_too_large }; + + if(base_rep == static_cast(UINT8_C(8))) + { + using string_storage_oct_type = + std::conditional_t + (UINT32_C(2048)), + detail::fixed_static_array , + detail::fixed_dynamic_array::value, + std::allocator, + AllocatorType>>::template rebind_alloc>>; + + string_storage_oct_type str_temp { }; + + str_temp.fill(char_fill); + + const auto wr_string_is_ok = x.wr_string(str_temp.data(), base_rep, show_base, show_pos, is_uppercase); + + auto rit_trim = std::find_if(str_temp.crbegin(), + str_temp.crend(), + [](const char& c) + { + return (c != char_fill); + }); + + const auto wr_string_and_trim_is_ok = + ( + (rit_trim != str_temp.crend()) + && wr_string_is_ok + ); + + if(wr_string_and_trim_is_ok) + { + const auto chars_retrieved = + static_cast + ( + str_temp.size() - static_cast(std::distance(str_temp.crbegin(), rit_trim)) + ); + + const auto chars_to_get = static_cast(std::distance(first, last)); + + result.ptr = std::copy(str_temp.data(), + str_temp.data() + (std::min)(chars_retrieved, chars_to_get), + first); + + result.ec = std::errc(); + } + } + else if(base_rep == static_cast(UINT8_C(16))) + { + using string_storage_hex_type = + std::conditional_t + (UINT32_C(2048)), + detail::fixed_static_array , + detail::fixed_dynamic_array::value, + std::allocator, + AllocatorType>>::template rebind_alloc>>; + + string_storage_hex_type str_temp { }; + + str_temp.fill(char_fill); + + const auto wr_string_is_ok = x.wr_string(str_temp.data(), base_rep, show_base, show_pos, is_uppercase); + + auto rit_trim = std::find_if(str_temp.crbegin(), + str_temp.crend(), + [](const char& c) + { + return (c != char_fill); + }); + + const auto wr_string_and_trim_is_ok = + ( + (rit_trim != str_temp.crend()) + && wr_string_is_ok + ); + + if(wr_string_and_trim_is_ok) + { + const auto chars_retrieved = + static_cast + ( + str_temp.size() - static_cast(std::distance(str_temp.crbegin(), rit_trim)) + ); + + const auto chars_to_get = static_cast(std::distance(first, last)); + + result.ptr = std::copy(str_temp.data(), + str_temp.data() + (std::min)(chars_retrieved, chars_to_get), + first); + + result.ec = std::errc(); + } + } + else + { + using string_storage_dec_type = + std::conditional_t + (UINT32_C(2048)), + detail::fixed_static_array , + detail::fixed_dynamic_array::value, + std::allocator, + AllocatorType>>::template rebind_alloc>>; + + string_storage_dec_type str_temp { }; + + str_temp.fill(char_fill); + + const auto wr_string_is_ok = x.wr_string(str_temp.data(), base_rep, show_base, show_pos, is_uppercase); + + auto rit_trim = std::find_if(str_temp.crbegin(), + str_temp.crend(), + [](const char& c) + { + return (c != char_fill); + }); + + const auto wr_string_and_trim_is_ok = + ( + (rit_trim != str_temp.crend()) + && wr_string_is_ok + ); + + if(wr_string_and_trim_is_ok) + { + const auto chars_retrieved = + static_cast + ( + str_temp.size() - static_cast(std::distance(str_temp.crbegin(), rit_trim)) + ); + + const auto chars_to_get = static_cast(std::distance(first, last)); + + result.ptr = std::copy(str_temp.data(), + str_temp.data() + (std::min)(chars_retrieved, chars_to_get), + first); + + result.ec = std::errc(); + } + } + + return result; + } + #endif + + #if !defined(WIDE_INTEGER_DISABLE_TO_STRING) + template + auto to_string(const uintwide_t& x) -> std::string + { + using local_wide_integer_type = uintwide_t; + using local_limb_type = typename local_wide_integer_type::limb_type; + + constexpr auto local_my_width2 = local_wide_integer_type::my_width2; + + using string_storage_dec_type = + std::conditional_t + (UINT32_C(2048)), + detail::fixed_static_array , + detail::fixed_dynamic_array::value, + std::allocator, + AllocatorType>>::template rebind_alloc>>; + + string_storage_dec_type str_temp; // LCOV_EXCL_LINE + + constexpr auto char_fill = '.'; + + str_temp.fill(char_fill); + + const auto base_rep = static_cast(UINT8_C(10)); + const auto show_base = false; + const auto show_pos = false; + const auto is_uppercase = false; + + const auto wr_string_is_ok = x.wr_string(str_temp.data(), base_rep, show_base, show_pos, is_uppercase); + + auto rit_trim = std::find_if(str_temp.crbegin(), + str_temp.crend(), + [](const char& c) + { + return (c != char_fill); + }); + + const auto wr_string_and_trim_is_ok = + ( + (rit_trim != str_temp.crend()) + && wr_string_is_ok + ); + + std::string str_result { }; + + if(wr_string_and_trim_is_ok) + { + const auto str_result_size = + static_cast + ( + str_temp.size() + - static_cast(std::distance(str_temp.crbegin(), rit_trim)) + ); + + str_result = std::string(str_temp.cbegin(), str_temp.cbegin() + str_result_size); + } + + return str_result; + } + #endif + #if(__cplusplus >= 201703L) } // namespace math::wide_integer #else diff --git a/test/test_uintwide_t_edge_cases.cpp b/test/test_uintwide_t_edge_cases.cpp index 758c9922..9c39bec4 100644 --- a/test/test_uintwide_t_edge_cases.cpp +++ b/test/test_uintwide_t_edge_cases.cpp @@ -627,8 +627,8 @@ auto test_various_roots_and_pow_etc() -> bool } { - const auto u = zero_as_small_unsigned_type(); - const auto u_root = sqrt(u); + const auto& u = zero_as_small_unsigned_type(); + const auto u_root = sqrt(u); const auto result_sqrt_zero_is_ok = (u_root == 0U); @@ -636,8 +636,8 @@ auto test_various_roots_and_pow_etc() -> bool } { - const auto u = zero_as_small_unsigned_type(); - const auto u_root = cbrt(u); + const auto& u = zero_as_small_unsigned_type(); + const auto u_root = cbrt(u); const auto result_cbrt_zero_is_ok = (u_root == zero_as_small_unsigned_type()); @@ -645,8 +645,8 @@ auto test_various_roots_and_pow_etc() -> bool } { - const auto u = zero_as_small_unsigned_type(); - const auto u_root = rootk(u, 7U); + const auto& u = zero_as_small_unsigned_type(); + const auto u_root = rootk(u, 7U); const auto result_rootk_zero_is_ok = (u_root == zero_as_small_unsigned_type()); @@ -974,14 +974,14 @@ auto test_various_isolated_edge_cases() -> bool // NOLINT(readability-function-c auto a(ten_pow_forty); const auto b(local_uintwide_t_small_unsigned_type("10000000000000000000000000000000000000000")); - const auto c(a %= b); + const auto& c(a %= b); #if (defined(__clang__) && (defined(__clang_major__) && (__clang_major__ > 6))) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wself-assign-overloaded" #endif - const auto d(a %= a); // NOLINT(clang-diagnostic-self-assign-overloaded) + const auto& d(a %= a); // NOLINT(clang-diagnostic-self-assign-overloaded) #if (defined(__clang__) && (defined(__clang_major__) && (__clang_major__ > 6))) #pragma GCC diagnostic pop @@ -1070,6 +1070,191 @@ auto test_various_isolated_edge_cases() -> bool // NOLINT(readability-function-c return result_is_ok; } +auto test_to_chars_and_to_string() -> bool // NOLINT(readability-function-cognitive-complexity) +{ + auto result_is_ok = true; + + #if defined(__cpp_lib_to_chars) + for(auto i = static_cast(UINT32_C(0)); + i < static_cast(UINT32_C(256)); + ++i) + { + // Verify write to_chars() and read back from string of unsigned uintwide_t. + // Use all three bases octal, decimal, and hexadecimal. + + using to_chars_storage_array_oct_type = + std::array(local_uintwide_t_small_unsigned_type::wr_string_max_buffer_size_oct)>; + + using to_chars_storage_array_dec_type = + std::array(local_uintwide_t_small_unsigned_type::wr_string_max_buffer_size_dec)>; + + using to_chars_storage_array_hex_type = + std::array(local_uintwide_t_small_unsigned_type::wr_string_max_buffer_size_hex)>; + + constexpr auto char_fill = '\0'; + + to_chars_storage_array_oct_type arr_oct { }; arr_oct.fill(char_fill); + to_chars_storage_array_dec_type arr_dec { }; arr_dec.fill(char_fill); + to_chars_storage_array_hex_type arr_hex { }; arr_hex.fill(char_fill); + + auto u_gen = generate_wide_integer_value(); + + using std::to_chars; + + const auto result_oct_as_chars = to_chars(arr_oct.data(), arr_oct.data() + arr_oct.size(), u_gen, 8); + const auto result_dec_as_chars = to_chars(arr_dec.data(), arr_dec.data() + arr_dec.size(), u_gen, 10); + const auto result_hex_as_chars = to_chars(arr_hex.data(), arr_hex.data() + arr_hex.size(), u_gen, 16); + + const auto result_oct_as_str = "0" + std::string(arr_oct.data()); + const auto result_dec_as_str = std::string(arr_dec.data()); + const auto result_hex_as_str = "0x" + std::string(arr_hex.data()); + + const local_uintwide_t_small_unsigned_type u_from_string_oct(result_oct_as_str.c_str()); + const local_uintwide_t_small_unsigned_type u_from_string_dec(result_dec_as_str.c_str()); + const local_uintwide_t_small_unsigned_type u_from_string_hex(result_hex_as_str.c_str()); + + const auto result_u_to_from_string_oct_is_ok = ((u_gen == u_from_string_oct) && (result_oct_as_chars.ec == std::errc())); + const auto result_u_to_from_string_dec_is_ok = ((u_gen == u_from_string_dec) && (result_dec_as_chars.ec == std::errc())); + const auto result_u_to_from_string_hex_is_ok = ((u_gen == u_from_string_hex) && (result_hex_as_chars.ec == std::errc())); + + result_is_ok = (result_u_to_from_string_oct_is_ok && result_is_ok); + result_is_ok = (result_u_to_from_string_dec_is_ok && result_is_ok); + result_is_ok = (result_u_to_from_string_hex_is_ok && result_is_ok); + } + + for(auto i = static_cast(UINT32_C(0)); + i < static_cast(UINT32_C(256)); + ++i) + { + // Verify write to_chars() and read back from string of signed uintwide_t. + // Use only base decimal. + + using to_chars_storage_array_dec_type = + std::array(local_uintwide_t_small_signed_type::wr_string_max_buffer_size_dec)>; + + constexpr auto char_fill = '\0'; + + to_chars_storage_array_dec_type arr_dec { }; arr_dec.fill(char_fill); + + auto n_gen = generate_wide_integer_value(false); + + using std::to_chars; + + const auto result_dec_as_chars = to_chars(arr_dec.data(), arr_dec.data() + arr_dec.size(), n_gen, 10); + + static_cast(result_dec_as_chars); + + const auto result_dec_as_str = std::string(arr_dec.data()); + + const local_uintwide_t_small_signed_type n_from_string_dec(result_dec_as_str.c_str()); + + const auto result_n_to_from_string_dec_is_ok = (n_gen == n_from_string_dec); + + result_is_ok = (result_n_to_from_string_dec_is_ok && result_is_ok); + } + #endif // __cpp_lib_to_chars + + #if !defined(WIDE_INTEGER_DISABLE_TO_STRING) + for(auto i = static_cast(UINT32_C(0)); + i < static_cast(UINT32_C(256)); + ++i) + { + // Verify write to_string() and read back from string of unsigned uintwide_t. + + auto u_gen = generate_wide_integer_value(); + + using std::to_string; + + const auto str_u = to_string(u_gen); + + const local_uintwide_t_small_unsigned_type u_from_string(str_u.c_str()); + + const auto result_u_to_from_string_is_ok = (u_gen == u_from_string); + + result_is_ok = (result_u_to_from_string_is_ok && result_is_ok); + } + + for(auto i = static_cast(UINT32_C(0)); + i < static_cast(UINT32_C(256)); + ++i) + { + // Verify write to_string() and read back from string of signed uintwide_t. + + auto n_gen = generate_wide_integer_value(false); + + using std::to_string; + + const auto str_n = to_string(n_gen); + + const local_uintwide_t_small_signed_type n_from_string(str_n.c_str()); + + const auto result_n_to_from_string_is_ok = (n_gen == n_from_string); + + result_is_ok = (result_n_to_from_string_is_ok && result_is_ok); + } + + { + // Ensure that uintwide_t's function to_string (in namespace + // math::wide_integer) does *not* conflict with the standard library's + // std::to_string function name. Also ensure that ADL works properly + // for uintwide_t's namespace-specific to_string function. + + using std::to_string; + + const auto u_gen = generate_wide_integer_value(); + const auto n_gen = generate_wide_integer_value(false); + + const auto str_u = to_string(u_gen); + const auto str_n = to_string(n_gen); + + const auto u64 = static_cast(UINT64_C(0xFFFFFFFF55555555)); + const auto ni = static_cast(INT8_C(42)); + + const auto str_u64 = to_string(u64); + const auto str_ni = to_string(ni); + + const auto str2_u64 = std::to_string(u64); + const auto str2_ni = std::to_string(ni); + + const auto result_to_strings_are_ok = ( (!str_u.empty()) + && (!str_n.empty()) + && (!str_u64.empty()) + && (!str_ni.empty()) + && (!str2_u64.empty()) + && (!str2_ni.empty())); + + result_is_ok = (result_to_strings_are_ok && result_is_ok); + } + + for(auto i = static_cast(UINT32_C(0)); + i < static_cast(UINT32_C(32)); + ++i) + { + // Verify write to_string() and read back from string of + // this test file's wide unsigned uintwide_t type. + // Thereby we ensure that the to_string() function + // will use dynamic allocation instead of stack allocation + // in this particular test. + + using local_derived_uint_type = typename local_uint_backend_type::representation_type; + + using std::to_string; + + const auto u_gen = generate_wide_integer_value(); + + const auto str_u = to_string(u_gen); + + const local_derived_uint_type u_from_string(str_u.c_str()); + + const auto result_u_to_from_string_is_ok = (u_gen == u_from_string); + + result_is_ok = (result_u_to_from_string_is_ok && result_is_ok); + } + #endif // !WIDE_INTEGER_DISABLE_TO_STRING + + return result_is_ok; +} + } // namespace test_uintwide_t_edge #if defined(WIDE_INTEGER_NAMESPACE) @@ -1084,6 +1269,7 @@ auto math::wide_integer::test_uintwide_t_edge_cases() -> bool result_is_ok = (test_uintwide_t_edge::test_various_ostream_ops () && result_is_ok); result_is_ok = (test_uintwide_t_edge::test_various_roots_and_pow_etc () && result_is_ok); result_is_ok = (test_uintwide_t_edge::test_various_isolated_edge_cases() && result_is_ok); + result_is_ok = (test_uintwide_t_edge::test_to_chars_and_to_string () && result_is_ok); return result_is_ok; } @@ -1119,7 +1305,7 @@ auto test_uintwide_t_edge::one_as_small_unsigned_type() -> const test_uintwide_t static const auto local_one_as_small_signed_type = local_uintwide_t_small_unsigned_type ( - static_cast::type>(UINT8_C(1)) + static_cast>(UINT8_C(1)) ); return local_one_as_small_signed_type; @@ -1132,7 +1318,7 @@ auto test_uintwide_t_edge::m_one_as_small_signed_type() -> const test_uintwide_t static const auto local_m_one_as_small_signed_type = local_uintwide_t_small_signed_type ( - static_cast::type>(INT8_C(-1)) + static_cast>(INT8_C(-1)) ); return local_m_one_as_small_signed_type;