// Copyright 2020-2024 Junekey Jeon
//
// The contents of this file may be used under the terms of
// the Apache License v2.0 with LLVM Exceptions.
//
//    (See accompanying file LICENSE-Apache or copy at
//     https://llvm.org/foundation/relicensing/LICENSE.txt)
//
// Alternatively, the contents of this file may be used under the terms of
// the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE-Boost or copy at
//     https://www.boost.org/LICENSE_1_0.txt)
//
// Unless required by applicable law or agreed to in writing, this software
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.


//#include "dragonbox/dragonbox_to_chars.h"
#include "dragonbox_to_chars.h"

#ifndef JKJ_STATIC_DATA_SECTION
    #define JKJ_STATIC_DATA_SECTION
#endif

// C++17 if constexpr
#if defined(__cpp_if_constexpr) && __cpp_if_constexpr >= 201606L
    #define JKJ_HAS_IF_CONSTEXPR 1
#elif __cplusplus >= 201703L
    #define JKJ_HAS_IF_CONSTEXPR 1
#elif defined(_MSC_VER) && _MSC_VER >= 1911 && _MSVC_LANG >= 201703L
    #define JKJ_HAS_IF_CONSTEXPR 1
#else
    #define JKJ_HAS_IF_CONSTEXPR 0
#endif

#if JKJ_HAS_IF_CONSTEXPR
    #define JKJ_IF_CONSTEXPR if constexpr
#else
    #define JKJ_IF_CONSTEXPR if
#endif

#if defined(__GNUC__) || defined(__clang__)
    #define JKJ_FORCEINLINE inline __attribute__((always_inline))
#elif defined(_MSC_VER)
    #define JKJ_FORCEINLINE __forceinline
#else
    #define JKJ_FORCEINLINE inline
#endif

namespace jkj {
    namespace dragonbox {
        namespace detail {
            // These "//"'s are to prevent clang-format to ruin this nice alignment.
            // Thanks to reddit user u/mcmcc:
            // https://www.reddit.com/r/cpp/comments/so3wx9/dragonbox_110_is_released_a_fast_floattostring/hw8z26r/?context=3
            static constexpr char radix_100_table[200] JKJ_STATIC_DATA_SECTION = {
                '0', '0', '0', '1', '0', '2', '0', '3', '0', '4', //
                '0', '5', '0', '6', '0', '7', '0', '8', '0', '9', //
                '1', '0', '1', '1', '1', '2', '1', '3', '1', '4', //
                '1', '5', '1', '6', '1', '7', '1', '8', '1', '9', //
                '2', '0', '2', '1', '2', '2', '2', '3', '2', '4', //
                '2', '5', '2', '6', '2', '7', '2', '8', '2', '9', //
                '3', '0', '3', '1', '3', '2', '3', '3', '3', '4', //
                '3', '5', '3', '6', '3', '7', '3', '8', '3', '9', //
                '4', '0', '4', '1', '4', '2', '4', '3', '4', '4', //
                '4', '5', '4', '6', '4', '7', '4', '8', '4', '9', //
                '5', '0', '5', '1', '5', '2', '5', '3', '5', '4', //
                '5', '5', '5', '6', '5', '7', '5', '8', '5', '9', //
                '6', '0', '6', '1', '6', '2', '6', '3', '6', '4', //
                '6', '5', '6', '6', '6', '7', '6', '8', '6', '9', //
                '7', '0', '7', '1', '7', '2', '7', '3', '7', '4', //
                '7', '5', '7', '6', '7', '7', '7', '8', '7', '9', //
                '8', '0', '8', '1', '8', '2', '8', '3', '8', '4', //
                '8', '5', '8', '6', '8', '7', '8', '8', '8', '9', //
                '9', '0', '9', '1', '9', '2', '9', '3', '9', '4', //
                '9', '5', '9', '6', '9', '7', '9', '8', '9', '9'  //
            };
            static constexpr char radix_100_head_table[200] JKJ_STATIC_DATA_SECTION = {
                '0', '.', '1', '.', '2', '.', '3', '.', '4', '.', //
                '5', '.', '6', '.', '7', '.', '8', '.', '9', '.', //
                '1', '.', '1', '.', '1', '.', '1', '.', '1', '.', //
                '1', '.', '1', '.', '1', '.', '1', '.', '1', '.', //
                '2', '.', '2', '.', '2', '.', '2', '.', '2', '.', //
                '2', '.', '2', '.', '2', '.', '2', '.', '2', '.', //
                '3', '.', '3', '.', '3', '.', '3', '.', '3', '.', //
                '3', '.', '3', '.', '3', '.', '3', '.', '3', '.', //
                '4', '.', '4', '.', '4', '.', '4', '.', '4', '.', //
                '4', '.', '4', '.', '4', '.', '4', '.', '4', '.', //
                '5', '.', '5', '.', '5', '.', '5', '.', '5', '.', //
                '5', '.', '5', '.', '5', '.', '5', '.', '5', '.', //
                '6', '.', '6', '.', '6', '.', '6', '.', '6', '.', //
                '6', '.', '6', '.', '6', '.', '6', '.', '6', '.', //
                '7', '.', '7', '.', '7', '.', '7', '.', '7', '.', //
                '7', '.', '7', '.', '7', '.', '7', '.', '7', '.', //
                '8', '.', '8', '.', '8', '.', '8', '.', '8', '.', //
                '8', '.', '8', '.', '8', '.', '8', '.', '8', '.', //
                '9', '.', '9', '.', '9', '.', '9', '.', '9', '.', //
                '9', '.', '9', '.', '9', '.', '9', '.', '9', '.'  //
            };

            static void print_1_digit(int n, char* buffer) noexcept {
                JKJ_IF_CONSTEXPR(('0' & 0xf) == 0) { *buffer = char('0' | n); }
                else {
                    *buffer = char('0' + n);
                }
            }

            static void print_2_digits(int n, char* buffer) noexcept {
                stdr::memcpy(buffer, radix_100_table + n * 2, 2);
            }

            // These digit generation routines are inspired by James Anhalt's itoa algorithm:
            // https://github.com/jeaiii/itoa
            // The main idea is for given n, find y such that floor(10^k * y / 2^32) = n holds,
            // where k is an appropriate integer depending on the length of n.
            // For example, if n = 1234567, we set k = 6. In this case, we have
            // floor(y / 2^32) = 1,
            // floor(10^2 * ((10^0 * y) mod 2^32) / 2^32) = 23,
            // floor(10^2 * ((10^2 * y) mod 2^32) / 2^32) = 45, and
            // floor(10^2 * ((10^4 * y) mod 2^32) / 2^32) = 67.
            // See https://jk-jeon.github.io/posts/2022/02/jeaiii-algorithm/ for more explanation.

            JKJ_FORCEINLINE static void print_9_digits(stdr::uint_least32_t s32, int& exponent,
                                                       char*& buffer) noexcept {
                // -- IEEE-754 binary32
                // Since we do not cut trailing zeros in advance, s32 must be of 6~9 digits
                // unless the original input was subnormal.
                // In particular, when it is of 9 digits it shouldn't have any trailing zeros.
                // -- IEEE-754 binary64
                // In this case, s32 must be of 7~9 digits unless the input is subnormal,
                // and it shouldn't have any trailing zeros if it is of 9 digits.
                if (s32 >= UINT32_C(100000000)) {
                    // 9 digits.
                    // 1441151882 = ceil(2^57 / 1'0000'0000) + 1
                    auto prod = s32 * UINT64_C(1441151882);
                    prod >>= 25;
                    stdr::memcpy(buffer, radix_100_head_table + int(prod >> 32) * 2, 2);

                    prod = (prod & UINT32_C(0xffffffff)) * 100;
                    print_2_digits(int(prod >> 32), buffer + 2);
                    prod = (prod & UINT32_C(0xffffffff)) * 100;
                    print_2_digits(int(prod >> 32), buffer + 4);
                    prod = (prod & UINT32_C(0xffffffff)) * 100;
                    print_2_digits(int(prod >> 32), buffer + 6);
                    prod = (prod & UINT32_C(0xffffffff)) * 100;
                    print_2_digits(int(prod >> 32), buffer + 8);

                    exponent += 8;
                    buffer += 10;
                }
                else if (s32 >= UINT32_C(1000000)) {
                    // 7 or 8 digits.
                    // 281474978 = ceil(2^48 / 100'0000) + 1
                    auto prod = s32 * UINT64_C(281474978);
                    prod >>= 16;
                    auto const head_digits = int(prod >> 32);
                    // If s32 is of 8 digits, increase the exponent by 7.
                    // Otherwise, increase it by 6.
                    exponent += (6 + int(head_digits >= 10));

                    // Write the first digit and the decimal point.
                    stdr::memcpy(buffer, radix_100_head_table + head_digits * 2, 2);
                    // This third character may be overwritten later but we don't care.
                    buffer[2] = radix_100_table[head_digits * 2 + 1];

                    // Remaining 6 digits are all zero?
                    if ((prod & UINT32_C(0xffffffff)) <=
                        stdr::uint_least32_t((stdr::uint_least64_t(1) << 32) / UINT32_C(1000000))) {
                        // The number of characters actually need to be written is:
                        //   1, if only the first digit is nonzero, which means that either s32 is of 7
                        //   digits or it is of 8 digits but the second digit is zero, or
                        //   3, otherwise.
                        // Note that buffer[2] is never '0' if s32 is of 7 digits, because the input is
                        // never zero.
                        buffer += (1 + (int(head_digits >= 10) & int(buffer[2] > '0')) * 2);
                    }
                    else {
                        // At least one of the remaining 6 digits are nonzero.
                        // After this adjustment, now the first destination becomes buffer + 2.
                        buffer += int(head_digits >= 10);

                        // Obtain the next two digits.
                        prod = (prod & UINT32_C(0xffffffff)) * 100;
                        print_2_digits(int(prod >> 32), buffer + 2);

                        // Remaining 4 digits are all zero?
                        if ((prod & UINT32_C(0xffffffff)) <=
                            stdr::uint_least32_t((stdr::uint_least64_t(1) << 32) / 10000)) {
                            buffer += (3 + int(buffer[3] > '0'));
                        }
                        else {
                            // At least one of the remaining 4 digits are nonzero.

                            // Obtain the next two digits.
                            prod = (prod & UINT32_C(0xffffffff)) * 100;
                            print_2_digits(int(prod >> 32), buffer + 4);

                            // Remaining 2 digits are all zero?
                            if ((prod & UINT32_C(0xffffffff)) <=
                                stdr::uint_least32_t((stdr::uint_least64_t(1) << 32) / 100)) {
                                buffer += (5 + int(buffer[5] > '0'));
                            }
                            else {
                                // Obtain the last two digits.
                                prod = (prod & UINT32_C(0xffffffff)) * 100;
                                print_2_digits(int(prod >> 32), buffer + 6);

                                buffer += (7 + int(buffer[7] > '0'));
                            }
                        }
                    }
                }
                else if (s32 >= 10000) {
                    // 5 or 6 digits.
                    // 429497 = ceil(2^32 / 1'0000)
                    auto prod = s32 * UINT64_C(429497);
                    auto const head_digits = int(prod >> 32);

                    // If s32 is of 6 digits, increase the exponent by 5.
                    // Otherwise, increase it by 4.
                    exponent += (4 + int(head_digits >= 10));

                    // Write the first digit and the decimal point.
                    stdr::memcpy(buffer, radix_100_head_table + head_digits * 2, 2);
                    // This third character may be overwritten later but we don't care.
                    buffer[2] = radix_100_table[head_digits * 2 + 1];

                    // Remaining 4 digits are all zero?
                    if ((prod & UINT32_C(0xffffffff)) <=
                        stdr::uint_least32_t((stdr::uint_least64_t(1) << 32) / 10000)) {
                        // The number of characters actually written is 1 or 3, similarly to the case of
                        // 7 or 8 digits.
                        buffer += (1 + (int(head_digits >= 10) & int(buffer[2] > '0')) * 2);
                    }
                    else {
                        // At least one of the remaining 4 digits are nonzero.
                        // After this adjustment, now the first destination becomes buffer + 2.
                        buffer += int(head_digits >= 10);

                        // Obtain the next two digits.
                        prod = (prod & UINT32_C(0xffffffff)) * 100;
                        print_2_digits(int(prod >> 32), buffer + 2);

                        // Remaining 2 digits are all zero?
                        if ((prod & UINT32_C(0xffffffff)) <=
                            stdr::uint_least32_t((stdr::uint_least64_t(1) << 32) / 100)) {
                            buffer += (3 + int(buffer[3] > '0'));
                        }
                        else {
                            // Obtain the last two digits.
                            prod = (prod & UINT32_C(0xffffffff)) * 100;
                            print_2_digits(int(prod >> 32), buffer + 4);

                            buffer += (5 + int(buffer[5] > '0'));
                        }
                    }
                }
                else if (s32 >= 100) {
                    // 3 or 4 digits.
                    // 42949673 = ceil(2^32 / 100)
                    auto prod = s32 * UINT64_C(42949673);
                    auto const head_digits = int(prod >> 32);

                    // If s32 is of 4 digits, increase the exponent by 3.
                    // Otherwise, increase it by 2.
                    exponent += (2 + int(head_digits >= 10));

                    // Write the first digit and the decimal point.
                    stdr::memcpy(buffer, radix_100_head_table + head_digits * 2, 2);
                    // This third character may be overwritten later but we don't care.
                    buffer[2] = radix_100_table[head_digits * 2 + 1];

                    // Remaining 2 digits are all zero?
                    if ((prod & UINT32_C(0xffffffff)) <=
                        stdr::uint_least32_t((stdr::uint_least64_t(1) << 32) / 100)) {
                        // The number of characters actually written is 1 or 3, similarly to the case of
                        // 7 or 8 digits.
                        buffer += (1 + (int(head_digits >= 10) & int(buffer[2] > '0')) * 2);
                    }
                    else {
                        // At least one of the remaining 2 digits are nonzero.
                        // After this adjustment, now the first destination becomes buffer + 2.
                        buffer += int(head_digits >= 10);

                        // Obtain the last two digits.
                        prod = (prod & UINT32_C(0xffffffff)) * 100;
                        print_2_digits(int(prod >> 32), buffer + 2);

                        buffer += (3 + int(buffer[3] > '0'));
                    }
                }
                else {
                    // 1 or 2 digits.
                    // If s32 is of 2 digits, increase the exponent by 1.
                    exponent += int(s32 >= 10);

                    // Write the first digit and the decimal point.
                    stdr::memcpy(buffer, radix_100_head_table + s32 * 2, 2);
                    // This third character may be overwritten later but we don't care.
                    buffer[2] = radix_100_table[s32 * 2 + 1];

                    // The number of characters actually written is 1 or 3, similarly to the case of
                    // 7 or 8 digits.
                    buffer += (1 + (int(s32 >= 10) & int(buffer[2] > '0')) * 2);
                }
            }

            template <>
            char* to_chars<ieee754_binary32, stdr::uint_least32_t>(stdr::uint_least32_t s32,
                                                                   int exponent,
                                                                   char* buffer) noexcept {
                // Print significand.
                print_9_digits(s32, exponent, buffer);

                // Print exponent and return
                if (exponent < 0) {
                    stdr::memcpy(buffer, "E-", 2);
                    buffer += 2;
                    exponent = -exponent;
                }
                else {
                    buffer[0] = 'E';
                    buffer += 1;
                }

                if (exponent >= 10) {
                    print_2_digits(exponent, buffer);
                    buffer += 2;
                }
                else {
                    print_1_digit(exponent, buffer);
                    buffer += 1;
                }

                return buffer;
            }

            template <>
            char*
            to_chars<ieee754_binary64, stdr::uint_least64_t>(stdr::uint_least64_t const significand,
                                                             int exponent, char* buffer) noexcept {
                // Print significand by decomposing it into a 9-digit block and a 8-digit block.
                stdr::uint_least32_t first_block, second_block;
                bool no_second_block;

                if (significand >= UINT64_C(100000000)) {
                    first_block = stdr::uint_least32_t(significand / UINT64_C(100000000));
                    second_block =
                        stdr::uint_least32_t(significand) - first_block * UINT32_C(100000000);
                    exponent += 8;
                    no_second_block = (second_block == 0);
                }
                else {
                    first_block = stdr::uint_least32_t(significand);
                    no_second_block = true;
                }

                if (no_second_block) {
                    print_9_digits(first_block, exponent, buffer);
                }
                else {
                    // We proceed similarly to print_9_digits(), but since we do not need to remove
                    // trailing zeros, the procedure is a bit simpler.
                    if (first_block >= UINT32_C(100000000)) {
                        // The input is of 17 digits, thus there should be no trailing zero at all.
                        // The first block is of 9 digits.
                        // 1441151882 = ceil(2^57 / 1'0000'0000) + 1
                        auto prod = first_block * UINT64_C(1441151882);
                        prod >>= 25;
                        stdr::memcpy(buffer, radix_100_head_table + int(prod >> 32) * 2, 2);
                        prod = (prod & UINT32_C(0xffffffff)) * 100;
                        print_2_digits(int(prod >> 32), buffer + 2);
                        prod = (prod & UINT32_C(0xffffffff)) * 100;
                        print_2_digits(int(prod >> 32), buffer + 4);
                        prod = (prod & UINT32_C(0xffffffff)) * 100;
                        print_2_digits(int(prod >> 32), buffer + 6);
                        prod = (prod & UINT32_C(0xffffffff)) * 100;
                        print_2_digits(int(prod >> 32), buffer + 8);

                        // The second block is of 8 digits.
                        // 281474978 = ceil(2^48 / 100'0000) + 1
                        prod = second_block * UINT64_C(281474978);
                        prod >>= 16;
                        prod += 1;
                        print_2_digits(int(prod >> 32), buffer + 10);
                        prod = (prod & UINT32_C(0xffffffff)) * 100;
                        print_2_digits(int(prod >> 32), buffer + 12);
                        prod = (prod & UINT32_C(0xffffffff)) * 100;
                        print_2_digits(int(prod >> 32), buffer + 14);
                        prod = (prod & UINT32_C(0xffffffff)) * 100;
                        print_2_digits(int(prod >> 32), buffer + 16);

                        exponent += 8;
                        buffer += 18;
                    }
                    else {
                        if (first_block >= UINT32_C(1000000)) {
                            // 7 or 8 digits.
                            // 281474978 = ceil(2^48 / 100'0000) + 1
                            auto prod = first_block * UINT64_C(281474978);
                            prod >>= 16;
                            auto const head_digits = int(prod >> 32);

                            stdr::memcpy(buffer, radix_100_head_table + head_digits * 2, 2);
                            buffer[2] = radix_100_table[head_digits * 2 + 1];

                            exponent += (6 + int(head_digits >= 10));
                            buffer += int(head_digits >= 10);

                            // Print remaining 6 digits.
                            prod = (prod & UINT32_C(0xffffffff)) * 100;
                            print_2_digits(int(prod >> 32), buffer + 2);
                            prod = (prod & UINT32_C(0xffffffff)) * 100;
                            print_2_digits(int(prod >> 32), buffer + 4);
                            prod = (prod & UINT32_C(0xffffffff)) * 100;
                            print_2_digits(int(prod >> 32), buffer + 6);

                            buffer += 8;
                        }
                        else if (first_block >= 10000) {
                            // 5 or 6 digits.
                            // 429497 = ceil(2^32 / 1'0000)
                            auto prod = first_block * UINT64_C(429497);
                            auto const head_digits = int(prod >> 32);

                            stdr::memcpy(buffer, radix_100_head_table + head_digits * 2, 2);
                            buffer[2] = radix_100_table[head_digits * 2 + 1];

                            exponent += (4 + int(head_digits >= 10));
                            buffer += int(head_digits >= 10);

                            // Print remaining 4 digits.
                            prod = (prod & UINT32_C(0xffffffff)) * 100;
                            print_2_digits(int(prod >> 32), buffer + 2);
                            prod = (prod & UINT32_C(0xffffffff)) * 100;
                            print_2_digits(int(prod >> 32), buffer + 4);

                            buffer += 6;
                        }
                        else if (first_block >= 100) {
                            // 3 or 4 digits.
                            // 42949673 = ceil(2^32 / 100)
                            auto prod = first_block * UINT64_C(42949673);
                            auto const head_digits = int(prod >> 32);

                            stdr::memcpy(buffer, radix_100_head_table + head_digits * 2, 2);
                            buffer[2] = radix_100_table[head_digits * 2 + 1];

                            exponent += (2 + int(head_digits >= 10));
                            buffer += int(head_digits >= 10);

                            // Print remaining 2 digits.
                            prod = (prod & UINT32_C(0xffffffff)) * 100;
                            print_2_digits(int(prod >> 32), buffer + 2);

                            buffer += 4;
                        }
                        else {
                            // 1 or 2 digits.
                            stdr::memcpy(buffer, radix_100_head_table + first_block * 2, 2);
                            buffer[2] = radix_100_table[first_block * 2 + 1];

                            exponent += int(first_block >= 10);
                            buffer += (2 + int(first_block >= 10));
                        }

                        // Next, print the second block.
                        // The second block is of 8 digits, but we may have trailing zeros.
                        // 281474978 = ceil(2^48 / 100'0000) + 1
                        auto prod = second_block * UINT64_C(281474978);
                        prod >>= 16;
                        prod += 1;
                        print_2_digits(int(prod >> 32), buffer);

                        // Remaining 6 digits are all zero?
                        if ((prod & UINT32_C(0xffffffff)) <=
                            stdr::uint_least32_t((stdr::uint_least64_t(1) << 32) / UINT64_C(1000000))) {
                            buffer += (1 + int(buffer[1] > '0'));
                        }
                        else {
                            // Obtain the next two digits.
                            prod = (prod & UINT32_C(0xffffffff)) * 100;
                            print_2_digits(int(prod >> 32), buffer + 2);

                            // Remaining 4 digits are all zero?
                            if ((prod & UINT32_C(0xffffffff)) <=
                                stdr::uint_least32_t((stdr::uint_least64_t(1) << 32) / 10000)) {
                                buffer += (3 + int(buffer[3] > '0'));
                            }
                            else {
                                // Obtain the next two digits.
                                prod = (prod & UINT32_C(0xffffffff)) * 100;
                                print_2_digits(int(prod >> 32), buffer + 4);

                                // Remaining 2 digits are all zero?
                                if ((prod & UINT32_C(0xffffffff)) <=
                                    stdr::uint_least32_t((stdr::uint_least64_t(1) << 32) / 100)) {
                                    buffer += (5 + int(buffer[5] > '0'));
                                }
                                else {
                                    // Obtain the last two digits.
                                    prod = (prod & UINT32_C(0xffffffff)) * 100;
                                    print_2_digits(int(prod >> 32), buffer + 6);
                                    buffer += (7 + int(buffer[7] > '0'));
                                }
                            }
                        }
                    }
                }

                // Print exponent and return
                if (exponent < 0) {
                    stdr::memcpy(buffer, "E-", 2);
                    buffer += 2;
                    exponent = -exponent;
                }
                else {
                    buffer[0] = 'E';
                    buffer += 1;
                }

                if (exponent >= 100) {
                    // d1 = exponent / 10; d2 = exponent % 10;
                    // 6554 = ceil(2^16 / 10)
                    auto d1 = (std::uint_least32_t(exponent) * UINT32_C(6554)) >> 16;
                    auto d2 = std::uint_least32_t(exponent) - UINT32_C(10) * d1;
                    print_2_digits(int(d1), buffer);
                    print_1_digit(int(d2), buffer + 2);
                    buffer += 3;
                }
                else if (exponent >= 10) {
                    print_2_digits(exponent, buffer);
                    buffer += 2;
                }
                else {
                    print_1_digit(exponent, buffer);
                    buffer += 1;
                }

                return buffer;
            }
        }
    }
}
