//! [![github]](https://github.com/dtolnay/zmij)&ensp;[![crates-io]](https://crates.io/crates/zmij)&ensp;[![docs-rs]](https://docs.rs/zmij)
//!
//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs
//!
//! <br>
//!
//! A double-to-string conversion algorithm based on [Schubfach] and [yy].
//!
//! This Rust implementation is a line-by-line port of Victor Zverovich's
//! implementation in C++, <https://github.com/vitaut/zmij>.
//!
//! [Schubfach]: https://fmt.dev/papers/Schubfach4.pdf
//! [yy]: https://github.com/ibireme/c_numconv_benchmark/blob/master/vendor/yy_double/yy_double.c
//!
//! <br>
//!
//! # Example
//!
//! ```
//! fn main() {
//!     let mut buffer = zmij::Buffer::new();
//!     let printed = buffer.format(1.234);
//!     assert_eq!(printed, "1.234");
//! }
//! ```
//!
//! <br>
//!
//! ## Performance
//!
//! The [dtoa-benchmark] compares this library and other Rust floating point
//! formatting implementations across a range of precisions. The vertical axis
//! in this chart shows nanoseconds taken by a single execution of
//! `zmij::Buffer::new().format_finite(value)` so a lower result indicates a
//! faster library.
//!
//! [dtoa-benchmark]: https://github.com/dtolnay/dtoa-benchmark
//!
//! ![performance](https://raw.githubusercontent.com/dtolnay/zmij/master/dtoa-benchmark.png)

#![no_std]
#![doc(html_root_url = "https://docs.rs/zmij/1.0.6")]
#![deny(unsafe_op_in_unsafe_fn)]
#![allow(non_camel_case_types)]
#![allow(
    clippy::blocks_in_conditions,
    clippy::cast_possible_truncation,
    clippy::cast_possible_wrap,
    clippy::cast_sign_loss,
    clippy::doc_markdown,
    clippy::incompatible_msrv,
    clippy::items_after_statements,
    clippy::must_use_candidate,
    clippy::needless_doctest_main,
    clippy::redundant_else,
    clippy::similar_names,
    clippy::too_many_lines,
    clippy::unreadable_literal,
    clippy::wildcard_imports
)]

#[cfg(test)]
mod tests;
mod traits;

#[cfg(not(zmij_no_select_unpredictable))]
use core::hint;
use core::mem::{self, MaybeUninit};
#[cfg(test)]
use core::ops::Index;
use core::ptr;
use core::slice;
use core::str;
#[cfg(feature = "no-panic")]
use no_panic::no_panic;

const BUFFER_SIZE: usize = 24;
const NAN: &str = "NaN";
const INFINITY: &str = "inf";
const NEG_INFINITY: &str = "-inf";

// A decimal floating-point number sig * pow(10, exp).
// If exp is non_finite_exp then the number is a NaN or an infinity.
struct dec_fp {
    sig: i64, // significand
    exp: i32, // exponent
}

struct uint128 {
    hi: u64,
    lo: u64,
}

// Computes 128-bit result of multiplication of two 64-bit unsigned integers.
const fn umul128(x: u64, y: u64) -> u128 {
    x as u128 * y as u128
}

const fn umul128_upper64(x: u64, y: u64) -> u64 {
    (umul128(x, y) >> 64) as u64
}

#[cfg_attr(feature = "no-panic", no_panic)]
fn umul192_upper128(x_hi: u64, x_lo: u64, y: u64) -> uint128 {
    let p = umul128(x_hi, y);
    let lo = (p as u64).wrapping_add((umul128(x_lo, y) >> 64) as u64);
    uint128 {
        hi: (p >> 64) as u64 + u64::from(lo < p as u64),
        lo,
    }
}

// Computes upper 64 bits of multiplication of x and y, discards the least
// significant bit and rounds to odd, where x = uint128_t(x_hi << 64) | x_lo.
#[cfg_attr(feature = "no-panic", no_panic)]
fn umul_upper_inexact_to_odd<UInt>(x_hi: u64, x_lo: u64, y: UInt) -> UInt
where
    UInt: traits::UInt,
{
    let num_bits = mem::size_of::<UInt>() * 8;
    if num_bits == 64 {
        let uint128 { hi, lo } = umul192_upper128(x_hi, x_lo, y.into());
        UInt::truncate(hi | u64::from((lo >> 1) != 0))
    } else {
        let result = (umul128(x_hi, y.into()) >> 32) as u64;
        UInt::enlarge((result >> 32) as u32 | u32::from((result as u32 >> 1) != 0))
    }
}

trait FloatTraits: traits::Float {
    const NUM_BITS: i32;
    const NUM_SIG_BITS: i32 = Self::MANTISSA_DIGITS as i32 - 1;
    const NUM_EXP_BITS: i32 = Self::NUM_BITS - Self::NUM_SIG_BITS - 1;
    const EXP_MASK: i32 = (1 << Self::NUM_EXP_BITS) - 1;
    const EXP_BIAS: i32 = (1 << (Self::NUM_EXP_BITS - 1)) - 1;

    type SigType: traits::UInt;
    const IMPLICIT_BIT: Self::SigType;

    fn to_bits(self) -> Self::SigType;

    fn get_sig(bits: Self::SigType) -> Self::SigType {
        bits & (Self::IMPLICIT_BIT - Self::SigType::from(1))
    }

    fn get_exp(bits: Self::SigType) -> i32 {
        (bits >> Self::NUM_SIG_BITS).into() as i32 & Self::EXP_MASK
    }
}

impl FloatTraits for f32 {
    const NUM_BITS: i32 = 32;
    const IMPLICIT_BIT: u32 = 1 << Self::NUM_SIG_BITS;

    type SigType = u32;

    fn to_bits(self) -> Self::SigType {
        self.to_bits()
    }
}

impl FloatTraits for f64 {
    const NUM_BITS: i32 = 64;
    const IMPLICIT_BIT: u64 = 1 << Self::NUM_SIG_BITS;

    type SigType = u64;

    fn to_bits(self) -> Self::SigType {
        self.to_bits()
    }
}

struct Pow10SignificandsTable([(u64, u64); 617]);

impl Pow10SignificandsTable {
    unsafe fn get_unchecked(&self, dec_exp: i32) -> &(u64, u64) {
        const DEC_EXP_MIN: i32 = -292;
        unsafe { self.0.get_unchecked((dec_exp - DEC_EXP_MIN) as usize) }
    }
}

#[cfg(test)]
impl Index<i32> for Pow10SignificandsTable {
    type Output = (u64, u64);
    fn index(&self, dec_exp: i32) -> &Self::Output {
        const DEC_EXP_MIN: i32 = -292;
        &self.0[(dec_exp - DEC_EXP_MIN) as usize]
    }
}

// 128-bit significands of powers of 10 rounded down.
// Generated using 192-bit arithmetic method by Dougall Johnson.
static POW10_SIGNIFICANDS: Pow10SignificandsTable = {
    let mut data = [(0, 0); 617];

    struct uint192 {
        w0: u64, // least significant
        w1: u64,
        w2: u64, // most significant
    }

    // first element, rounded up to cancel out rounding down in the
    // multiplication, and minimise significant bits
    let mut current = uint192 {
        w0: 0xe000000000000000,
        w1: 0x25e8e89c13bb0f7a,
        w2: 0xff77b1fcbebcdc4f,
    };
    let ten = 0xa000000000000000;
    let mut i = 0;
    while i < data.len() {
        data[i] = (current.w2, current.w1);

        let h0: u64 = umul128_upper64(current.w0, ten);
        let h1: u64 = umul128_upper64(current.w1, ten);

        let c0: u64 = h0.wrapping_add(current.w1.wrapping_mul(ten));
        let c1: u64 = ((c0 < h0) as u64 + h1).wrapping_add(current.w2.wrapping_mul(ten));
        let c2: u64 = (c1 < h1) as u64 + umul128_upper64(current.w2, ten); // dodgy carry

        // normalise
        if (c2 >> 63) != 0 {
            current = uint192 {
                w0: c0,
                w1: c1,
                w2: c2,
            };
        } else {
            current = uint192 {
                w0: c0 << 1,
                w1: c1 << 1 | c0 >> 63,
                w2: c2 << 1 | c1 >> 63,
            };
        }

        i += 1;
    }

    Pow10SignificandsTable(data)
};

#[cfg_attr(feature = "no-panic", no_panic)]
fn count_trailing_nonzeros(x: u64) -> usize {
    // We count the number of bytes until there are only zeros left.
    // The code is equivalent to
    //    8 - x.leading_zeros() / 8
    // but if the BSR instruction is emitted (as gcc on x64 does with default
    // settings), subtracting the constant before dividing allows the compiler
    // to combine it with the subtraction which it inserts due to BSR counting
    // in the opposite direction.
    //
    // Additionally, the BSR instruction requires a zero check. Since the high
    // bit is unused we can avoid the zero check by shifting the datum left by
    // one and inserting a sentinel bit at the end. This can be faster than the
    // automatically inserted range check.
    (70 - ((x.to_le() << 1) | 1).leading_zeros()) as usize / 8
}

// Align data since unaligned access may be slower when crossing a
// hardware-specific boundary.
#[repr(C, align(2))]
struct Digits2([u8; 200]);

static DIGITS2: Digits2 = Digits2(
    *b"0001020304050607080910111213141516171819\
       2021222324252627282930313233343536373839\
       4041424344454647484950515253545556575859\
       6061626364656667686970717273747576777879\
       8081828384858687888990919293949596979899",
);

// Converts value in the range [0, 100) to a string. GCC generates a bit better
// code when value is pointer-size (https://www.godbolt.org/z/5fEPMT1cc).
#[cfg_attr(feature = "no-panic", no_panic)]
unsafe fn digits2(value: usize) -> &'static u16 {
    debug_assert!(value < 100);

    #[allow(clippy::cast_ptr_alignment)]
    unsafe {
        &*DIGITS2.0.as_ptr().cast::<u16>().add(value)
    }
}

#[cfg_attr(feature = "no-panic", no_panic)]
fn to_bcd8(abcdefgh: u64) -> u64 {
    // An optimization from Xiang JunBo.
    // Three steps BCD. Base 10000 -> base 100 -> base 10.
    // div and mod are evaluated simultaneously as, e.g.
    //   (abcdefgh / 10000) << 32 + (abcdefgh % 10000)
    //      == abcdefgh + (2^32 - 10000) * (abcdefgh / 10000)))
    // where the division on the RHS is implemented by the usual multiply + shift
    // trick and the fractional bits are masked away.
    let abcd_efgh = abcdefgh + (0x100000000 - 10000) * ((abcdefgh * 0x68db8bb) >> 40);
    let ab_cd_ef_gh = abcd_efgh + (0x10000 - 100) * (((abcd_efgh * 0x147b) >> 19) & 0x7f0000007f);
    let a_b_c_d_e_f_g_h =
        ab_cd_ef_gh + (0x100 - 10) * (((ab_cd_ef_gh * 0x67) >> 10) & 0xf000f000f000f);
    a_b_c_d_e_f_g_h.to_be()
}

unsafe fn write_if_nonzero(buffer: *mut u8, digit: u32) -> *mut u8 {
    unsafe {
        *buffer = b'0' + digit as u8;
        buffer.add(usize::from(digit != 0))
    }
}

unsafe fn write8(buffer: *mut u8, value: u64) {
    unsafe {
        buffer.cast::<u64>().write_unaligned(value);
    }
}

const ZEROS: u64 = 0x0101010101010101 * b'0' as u64;

// Writes a significand consisting of up to 17 decimal digits (16-17 for
// normals) and removes trailing zeros.
#[cfg_attr(feature = "no-panic", no_panic)]
unsafe fn write_significand17(mut buffer: *mut u8, value: u64) -> *mut u8 {
    #[cfg(not(all(target_arch = "aarch64", target_feature = "neon", not(miri))))]
    {
        // Each digits is denoted by a letter so value is abbccddeeffgghhii where
        // digit a can be zero.
        let abbccddee = (value / 100_000_000) as u32;
        let ffgghhii = (value % 100_000_000) as u32;
        unsafe {
            buffer = write_if_nonzero(buffer, abbccddee / 100_000_000);
        }
        let bcd = to_bcd8(u64::from(abbccddee % 100_000_000));
        unsafe {
            write8(buffer, bcd | ZEROS);
        }
        if ffgghhii == 0 {
            return unsafe { buffer.add(count_trailing_nonzeros(bcd)) };
        }
        let bcd = to_bcd8(u64::from(ffgghhii));
        unsafe {
            write8(buffer.add(8), bcd | ZEROS);
            buffer.add(8).add(count_trailing_nonzeros(bcd))
        }
    }

    #[cfg(all(target_arch = "aarch64", target_feature = "neon", not(miri)))]
    {
        use core::arch::aarch64::*;
        use core::arch::asm;

        // An optimized version for NEON by Dougall Johnson.
        struct ToStringConstants {
            mul_const: u64,
            hundred_million: u64,
            multipliers32: int32x4_t,
            multipliers16: int16x8_t,
        }

        static CONSTANTS: ToStringConstants = ToStringConstants {
            mul_const: 0xabcc77118461cefd,
            hundred_million: 100000000,
            multipliers32: unsafe {
                mem::transmute::<[i32; 4], int32x4_t>([
                    0x68db8bb,
                    -10000 + 0x10000,
                    0x147b000,
                    -100 + 0x10000,
                ])
            },
            multipliers16: unsafe {
                mem::transmute::<[i16; 8], int16x8_t>([0xce0, -10 + 0x100, 0, 0, 0, 0, 0, 0])
            },
        };

        let mut c = ptr::addr_of!(CONSTANTS);

        // Compiler barrier, or clang doesn't load from memory and generates 15
        // more instructions
        let c = unsafe {
            asm!("/*{0}*/", inout(reg) c);
            &*c
        };

        let mut hundred_million = c.hundred_million;

        // Compiler barrier, or clang narrows the load to 32-bit and unpairs it.
        unsafe {
            asm!("/*{0}*/", inout(reg) hundred_million);
        }

        // Equivalent to abbccddee = value / 100000000, ffgghhii = value % 100000000.
        let mut abbccddee = ((u128::from(value) * u128::from(c.mul_const)) >> 90) as u64;
        let ffgghhii = value - abbccddee * hundred_million;

        // We could probably make this bit faster, but we're preferring to
        // reuse the constants for now.
        let a = ((u128::from(abbccddee) * u128::from(c.mul_const)) >> 90) as u64;
        abbccddee -= a * hundred_million;

        unsafe {
            buffer = write_if_nonzero(buffer, a as u32);

            let hundredmillions: uint64x1_t =
                mem::transmute::<u64, uint64x1_t>(abbccddee | (ffgghhii << 32));

            let high_10000: int32x2_t = mem::transmute::<uint32x2_t, int32x2_t>(vshr_n_u32(
                mem::transmute::<int32x2_t, uint32x2_t>(vqdmulh_n_s32(
                    mem::transmute::<uint64x1_t, int32x2_t>(hundredmillions),
                    mem::transmute::<int32x4_t, [i32; 4]>(c.multipliers32)[0],
                )),
                9,
            ));
            let tenthousands: int32x2_t = vmla_n_s32(
                mem::transmute::<uint64x1_t, int32x2_t>(hundredmillions),
                high_10000,
                mem::transmute::<int32x4_t, [i32; 4]>(c.multipliers32)[1],
            );

            let mut extended: int32x4_t = mem::transmute::<uint32x4_t, int32x4_t>(vshll_n_u16(
                mem::transmute::<int32x2_t, uint16x4_t>(tenthousands),
                0,
            ));

            // Compiler barrier, or clang breaks the subsequent MLA into UADDW +
            // MUL.
            asm!("/*{:v}*/", inout(vreg) extended);

            let high_100: int32x4_t = vqdmulhq_n_s32(
                extended,
                mem::transmute::<int32x4_t, [i32; 4]>(c.multipliers32)[2],
            );
            let hundreds: int32x4_t = vmlaq_n_s32(
                extended,
                high_100,
                mem::transmute::<int32x4_t, [i32; 4]>(c.multipliers32)[3],
            );
            let high_10: int16x8_t = vqdmulhq_n_s16(
                mem::transmute::<int32x4_t, int16x8_t>(hundreds),
                mem::transmute::<int16x8_t, [i16; 8]>(c.multipliers16)[0],
            );
            let digits: int16x8_t =
                mem::transmute::<uint8x16_t, int16x8_t>(vrev64q_u8(mem::transmute::<
                    int16x8_t,
                    uint8x16_t,
                >(vmlaq_n_s16(
                    mem::transmute::<int32x4_t, int16x8_t>(hundreds),
                    high_10,
                    mem::transmute::<int16x8_t, [i16; 8]>(c.multipliers16)[1],
                ))));
            let ascii: int16x8_t = mem::transmute::<uint16x8_t, int16x8_t>(vaddq_u16(
                mem::transmute::<int16x8_t, uint16x8_t>(digits),
                mem::transmute::<int8x16_t, uint16x8_t>(vdupq_n_s8(b'0' as i8)),
            ));

            buffer.cast::<int16x8_t>().write_unaligned(ascii);

            let is_zero: uint16x8_t =
                mem::transmute::<uint8x16_t, uint16x8_t>(vceqzq_u8(mem::transmute::<
                    int16x8_t,
                    uint8x16_t,
                >(digits)));
            let zeros: u64 = !vget_lane_u64(vreinterpret_u64_u8(vshrn_n_u16(is_zero, 4)), 0);

            buffer.add(16 - (zeros.leading_zeros() as usize >> 2))
        }
    }
}

// Writes a significand consisting of up to 9 decimal digits (8-9 for normals)
// and removes trailing zeros.
#[cfg_attr(feature = "no-panic", no_panic)]
unsafe fn write_significand9(mut buffer: *mut u8, value: u32) -> *mut u8 {
    unsafe {
        buffer = write_if_nonzero(buffer, value / 100_000_000);
    }
    let bcd = to_bcd8(u64::from(value % 100_000_000));
    unsafe {
        write8(buffer, bcd | ZEROS);
        buffer.add(count_trailing_nonzeros(bcd))
    }
}

// Computes the decimal exponent as floor(log10(2**bin_exp)) if regular or
// floor(log10(3/4 * 2**bin_exp)) otherwise, without branching.
const fn compute_dec_exp(bin_exp: i32, regular: bool) -> i32 {
    debug_assert!(bin_exp >= -1334 && bin_exp <= 2620);
    // log10_3_over_4_sig = -log10(3/4) * 2**log10_2_exp rounded to a power of 2
    const LOG10_3_OVER_4_SIG: i32 = 131_072;
    // log10_2_sig = round(log10(2) * 2**log10_2_exp)
    const LOG10_2_SIG: i32 = 315_653;
    const LOG10_2_EXP: i32 = 20;
    (bin_exp * LOG10_2_SIG - !regular as i32 * LOG10_3_OVER_4_SIG) >> LOG10_2_EXP
}

// Computes a shift so that, after scaling by a power of 10, the intermediate
// result always has a fixed 128-bit fractional part (for double).
//
// Different binary exponents can map to the same decimal exponent, but place
// the decimal point at different bit positions. The shift compensates for this.
//
// For example, both 3 * 2**59 and 3 * 2**60 have dec_exp = 2, but dividing by
// 10^dec_exp puts the decimal point in different bit positions:
//   3 * 2**59 / 100 = 1.72...e+16  (needs shift = 1 + 1)
//   3 * 2**60 / 100 = 3.45...e+16  (needs shift = 2 + 1)
const fn compute_exp_shift(bin_exp: i32, dec_exp: i32) -> i32 {
    debug_assert!(dec_exp >= -350 && dec_exp <= 350);
    // log2_pow10_sig = round(log2(10) * 2**log2_pow10_exp) + 1
    const LOG2_POW10_SIG: i32 = 217_707;
    const LOG2_POW10_EXP: i32 = 16;
    // pow10_bin_exp = floor(log2(10**-dec_exp))
    let pow10_bin_exp = (-dec_exp * LOG2_POW10_SIG) >> LOG2_POW10_EXP;
    // pow10 = ((pow10_hi << 64) | pow10_lo) * 2**(pow10_bin_exp - 127)

    // Shift to ensure the intermediate result of multiplying by a power of 10
    // has a fixed 128-bit fractional part. For example, 3 * 2**59 and 3 * 2**60
    // both have dec_exp = 2 and dividing them by 10**dec_exp would have the
    // decimal point in different (bit) positions without the shift:
    //   3 * 2**59 / 100 = 1.72...e+16 (exp_shift = 1 + 1)
    //   3 * 2**60 / 100 = 3.45...e+16 (exp_shift = 2 + 1)
    bin_exp + pow10_bin_exp + 1
}

fn normalize<UInt>(mut dec: dec_fp, subnormal: bool) -> dec_fp
where
    UInt: traits::UInt,
{
    if !subnormal {
        return dec;
    }
    let num_bits = mem::size_of::<UInt>() * 8;
    while dec.sig
        < if num_bits == 64 {
            10_000_000_000_000_000
        } else {
            100_000_000
        }
    {
        dec.sig *= 10;
        dec.exp -= 1;
    }
    dec
}

// Converts a binary FP number bin_sig * 2**bin_exp to the shortest decimal
// representation.
#[cfg_attr(feature = "no-panic", no_panic)]
fn to_decimal<UInt>(
    bin_sig: UInt,
    bin_exp: i32,
    mut dec_exp: i32,
    regular: bool,
    subnormal: bool,
) -> dec_fp
where
    UInt: traits::UInt,
{
    let num_bits = mem::size_of::<UInt>() as i32 * 8;
    if regular && !subnormal {
        let exp_shift = compute_exp_shift(bin_exp, dec_exp);
        let (pow10_hi, pow10_lo) = *unsafe { POW10_SIGNIFICANDS.get_unchecked(-dec_exp) };

        let integral; // integral part of bin_sig * pow10
        let fractional; // fractional part of bin_sig * pow10
        if num_bits == 64 {
            let result = umul192_upper128(pow10_hi, pow10_lo, (bin_sig << exp_shift).into());
            integral = UInt::truncate(result.hi);
            fractional = result.lo;
        } else {
            let result = umul128(pow10_hi, (bin_sig << exp_shift).into());
            integral = UInt::truncate((result >> 64) as u64);
            fractional = result as u64;
        }
        #[cfg(all(any(target_arch = "aarch64", target_arch = "x86_64"), not(miri)))]
        let digit = {
            use core::arch::asm;
            // An optimization of integral % 10 by Dougall Johnson. Relies on
            // range calculation: (max_bin_sig << max_exp_shift) * max_u128.
            let div10 = ((u128::from(integral.into()) * ((1 << 64) / 10 + 1)) >> 64) as u64;
            let mut digit = integral.into() - div10 * 10;
            unsafe {
                asm!("/*{0}*/", inout(reg) digit); // or it narrows to 32-bit and doesn't use madd/msub
            }
            digit
        };
        #[cfg(not(all(any(target_arch = "aarch64", target_arch = "x86_64"), not(miri))))]
        let digit = integral.into() % 10;

        // Switch to a fixed-point representation with the least significant
        // integral digit in the upper bits and fractional digits in the lower
        // bits.
        let num_integral_bits = if num_bits == 64 { 4 } else { 32 };
        let num_fractional_bits = 64 - num_integral_bits;
        let ten = 10u64 << num_fractional_bits;
        // Fixed-point remainder of the scaled significand modulo 10.
        let scaled_sig_mod10 = (digit << num_fractional_bits) | (fractional >> num_integral_bits);

        // scaled_half_ulp = 0.5 * pow10 in the fixed-point format.
        // dec_exp is chosen so that 10**dec_exp <= 2**bin_exp < 10**(dec_exp + 1).
        // Since 1ulp == 2**bin_exp it will be in the range [1, 10) after scaling
        // by 10**dec_exp. Add 1 to combine the shift with division by two.
        let scaled_half_ulp = pow10_hi >> (num_integral_bits - exp_shift + 1);
        let upper = scaled_sig_mod10 + scaled_half_ulp;
        const HALF_ULP: u64 = 1 << 63;

        // An optimization from yy by Yaoyuan Guo:
        if {
            // Exact half-ulp tie when rounding to nearest integer.
            fractional != HALF_ULP &&
            // Exact half-ulp tie when rounding to nearest 10.
            scaled_sig_mod10 != scaled_half_ulp &&
            // Near-boundary case for rounding to nearest 10.
            ten.wrapping_sub(upper) > 1
        } {
            let round_up = upper >= ten;
            let shorter = (integral.into() - digit + u64::from(round_up) * 10) as i64;
            let longer = (integral.into() + u64::from(fractional >= HALF_ULP)) as i64;
            let use_shorter = scaled_sig_mod10 <= scaled_half_ulp || round_up;
            return dec_fp {
                #[cfg(zmij_no_select_unpredictable)]
                sig: if use_shorter { shorter } else { longer },
                #[cfg(not(zmij_no_select_unpredictable))]
                sig: hint::select_unpredictable(use_shorter, shorter, longer),
                exp: dec_exp,
            };
        }
    }

    dec_exp = compute_dec_exp(bin_exp, regular);
    let exp_shift = compute_exp_shift(bin_exp, dec_exp);
    let (mut pow10_hi, mut pow10_lo) = *unsafe { POW10_SIGNIFICANDS.get_unchecked(-dec_exp) };

    // Fallback to Schubfach to guarantee correctness in boundary cases. This
    // requires switching to strict overestimates of powers of 10.
    if num_bits == 64 {
        pow10_lo += 1;
    } else {
        pow10_hi += 1;
    }

    // Shift the significand so that boundaries are integer.
    const BOUND_SHIFT: u32 = 2;
    let bin_sig_shifted = bin_sig << BOUND_SHIFT;

    // Compute the estimates of lower and upper bounds of the rounding interval
    // by multiplying them by the power of 10 and applying modified rounding.
    let lsb = bin_sig & UInt::from(1);
    let lower = (bin_sig_shifted - (UInt::from(regular) + UInt::from(1))) << exp_shift;
    let lower = umul_upper_inexact_to_odd(pow10_hi, pow10_lo, lower) + lsb;
    let upper = (bin_sig_shifted + UInt::from(2)) << exp_shift;
    let upper = umul_upper_inexact_to_odd(pow10_hi, pow10_lo, upper) - lsb;

    // The idea of using a single shorter candidate is by Cassio Neri.
    // It is less or equal to the upper bound by construction.
    let shorter = UInt::from(10) * ((upper >> BOUND_SHIFT) / UInt::from(10));
    if (shorter << BOUND_SHIFT) >= lower {
        return normalize::<UInt>(
            dec_fp {
                sig: shorter.into() as i64,
                exp: dec_exp,
            },
            subnormal,
        );
    }

    let scaled_sig = umul_upper_inexact_to_odd(pow10_hi, pow10_lo, bin_sig_shifted << exp_shift);
    let dec_sig_below = scaled_sig >> BOUND_SHIFT;
    let dec_sig_above = dec_sig_below + UInt::from(1);

    // Pick the closest of dec_sig_below and dec_sig_above and check if it's in
    // the rounding interval.
    let cmp = scaled_sig
        .wrapping_sub((dec_sig_below + dec_sig_above) << 1)
        .to_signed();
    let below_closer = cmp < UInt::from(0).to_signed()
        || (cmp == UInt::from(0).to_signed() && (dec_sig_below & UInt::from(1)) == UInt::from(0));
    let below_in = (dec_sig_below << BOUND_SHIFT) >= lower;
    let dec_sig = if below_closer & below_in {
        dec_sig_below
    } else {
        dec_sig_above
    };
    normalize::<UInt>(
        dec_fp {
            sig: dec_sig.into() as i64,
            exp: dec_exp,
        },
        subnormal,
    )
}

/// Writes the shortest correctly rounded decimal representation of `value` to
/// `buffer`. `buffer` should point to a buffer of size `buffer_size` or larger.
#[cfg_attr(feature = "no-panic", no_panic)]
unsafe fn write<Float>(value: Float, mut buffer: *mut u8) -> *mut u8
where
    Float: FloatTraits,
{
    let bits = value.to_bits();
    let raw_exp = Float::get_exp(bits); // binary exponent
    let mut bin_exp = raw_exp - Float::NUM_SIG_BITS - Float::EXP_BIAS;
    // Compute the decimal exponent early to overlap its latency with other work.
    let mut dec_exp = compute_dec_exp(bin_exp, true);

    unsafe {
        *buffer = b'-';
        buffer = buffer.add((bits >> (Float::NUM_BITS - 1)).into() as usize);
    }

    let mut bin_sig = Float::get_sig(bits); // binary significand
    let mut regular = bin_sig != Float::SigType::from(0);
    let mut subnormal = false;
    if raw_exp == 0 {
        if bin_sig == Float::SigType::from(0) {
            return unsafe {
                *buffer = b'0';
                *buffer.add(1) = b'.';
                *buffer.add(2) = b'0';
                buffer.add(3)
            };
        }
        // Handle subnormals.
        bin_exp = 1 - Float::NUM_SIG_BITS - Float::EXP_BIAS;
        dec_exp = compute_dec_exp(bin_exp, true);
        bin_sig |= Float::IMPLICIT_BIT;
        subnormal = true;
        // Setting regular is not redundant: it has a measurable perf impact.
        regular = true;
    }
    bin_sig ^= Float::IMPLICIT_BIT;

    // Here be 🐉s.
    let dec = to_decimal(bin_sig, bin_exp, dec_exp, regular, subnormal);
    dec_exp = dec.exp;
    let mut dec_sig = dec.sig;

    // Write significand.
    let end = if Float::NUM_BITS == 64 {
        dec_exp += Float::MAX_DIGITS10 as i32 + i32::from(dec_sig >= 10_000_000_000_000_000) - 2;
        unsafe { write_significand17(buffer.add(1), dec_sig as u64) }
    } else {
        if dec_sig < 10_000_000 {
            dec_sig *= 10;
            dec_exp -= 1;
        }
        dec_exp += Float::MAX_DIGITS10 as i32 + i32::from(dec_sig >= 100_000_000) - 2;
        unsafe { write_significand9(buffer.add(1), dec_sig as u32) }
    };

    let length = unsafe { end.offset_from(buffer.add(1)) } as usize;

    if Float::NUM_BITS == 32 && (-6..=12).contains(&dec_exp)
        || Float::NUM_BITS == 64 && (-5..=15).contains(&dec_exp)
    {
        if length as i32 - 1 <= dec_exp {
            // 1234e7 -> 12340000000.0
            return unsafe {
                ptr::copy(buffer.add(1), buffer, length);
                ptr::write_bytes(buffer.add(length), b'0', dec_exp as usize + 3 - length);
                *buffer.add(dec_exp as usize + 1) = b'.';
                buffer.add(dec_exp as usize + 3)
            };
        } else if 0 <= dec_exp {
            // 1234e-2 -> 12.34
            return unsafe {
                ptr::copy(buffer.add(1), buffer, dec_exp as usize + 1);
                *buffer.add(dec_exp as usize + 1) = b'.';
                buffer.add(length + 1)
            };
        } else {
            // 1234e-6 -> 0.001234
            return unsafe {
                ptr::copy(buffer.add(1), buffer.add((1 - dec_exp) as usize), length);
                ptr::write_bytes(buffer, b'0', (1 - dec_exp) as usize);
                *buffer.add(1) = b'.';
                buffer.add((1 - dec_exp) as usize + length)
            };
        }
    }

    unsafe {
        // 1234e30 -> 1.234e33
        *buffer = *buffer.add(1);
        *buffer.add(1) = b'.';
        buffer = buffer.add(length + usize::from(length > 1));
    }

    // Write exponent.
    let sign_ptr = buffer;
    let e_sign = if dec_exp >= 0 {
        (u16::from(b'+') << 8) | u16::from(b'e')
    } else {
        (u16::from(b'-') << 8) | u16::from(b'e')
    };
    buffer = unsafe { buffer.add(1) };
    let mask = i32::from(dec_exp >= 0) - 1;
    dec_exp = (dec_exp + mask) ^ mask; // absolute value
    buffer = unsafe { buffer.add(usize::from(dec_exp >= 10)) };
    if Float::MIN_10_EXP >= -99 && Float::MAX_10_EXP <= 99 {
        unsafe {
            buffer
                .cast::<u16>()
                .write_unaligned(*digits2(dec_exp as usize));
            sign_ptr.cast::<u16>().write_unaligned(e_sign.to_le());
            return buffer.add(2);
        }
    }
    // 19 is faster or equal to 12 even for 3 digits.
    const DIV_EXP: u32 = 19;
    const DIV_SIG: u32 = (1 << DIV_EXP) / 100 + 1;
    let digit = (dec_exp as u32 * DIV_SIG) >> DIV_EXP; // value / 100
    unsafe {
        *buffer = b'0' + digit as u8;
        buffer = buffer.add(usize::from(dec_exp >= 100));
        buffer
            .cast::<u16>()
            .write_unaligned(*digits2((dec_exp as u32 - digit * 100) as usize));
        sign_ptr.cast::<u16>().write_unaligned(e_sign.to_le());
        buffer.add(2)
    }
}

/// Safe API for formatting floating point numbers to text.
///
/// ## Example
///
/// ```
/// let mut buffer = zmij::Buffer::new();
/// let printed = buffer.format_finite(1.234);
/// assert_eq!(printed, "1.234");
/// ```
pub struct Buffer {
    bytes: [MaybeUninit<u8>; BUFFER_SIZE],
}

impl Buffer {
    /// This is a cheap operation; you don't need to worry about reusing buffers
    /// for efficiency.
    #[inline]
    #[cfg_attr(feature = "no-panic", no_panic)]
    pub fn new() -> Self {
        let bytes = [MaybeUninit::<u8>::uninit(); BUFFER_SIZE];
        Buffer { bytes }
    }

    /// Print a floating point number into this buffer and return a reference to
    /// its string representation within the buffer.
    ///
    /// # Special cases
    ///
    /// This function formats NaN as the string "NaN", positive infinity as
    /// "inf", and negative infinity as "-inf" to match std::fmt.
    ///
    /// If your input is known to be finite, you may get better performance by
    /// calling the `format_finite` method instead of `format` to avoid the
    /// checks for special cases.
    #[cfg_attr(feature = "no-panic", no_panic)]
    pub fn format<F: Float>(&mut self, f: F) -> &str {
        if f.is_nonfinite() {
            f.format_nonfinite()
        } else {
            self.format_finite(f)
        }
    }

    /// Print a floating point number into this buffer and return a reference to
    /// its string representation within the buffer.
    ///
    /// # Special cases
    ///
    /// This function **does not** check for NaN or infinity. If the input
    /// number is not a finite float, the printed representation will be some
    /// correctly formatted but unspecified numerical value.
    ///
    /// Please check [`is_finite`] yourself before calling this function, or
    /// check [`is_nan`] and [`is_infinite`] and handle those cases yourself.
    ///
    /// [`is_finite`]: f64::is_finite
    /// [`is_nan`]: f64::is_nan
    /// [`is_infinite`]: f64::is_infinite
    #[cfg_attr(feature = "no-panic", no_panic)]
    pub fn format_finite<F: Float>(&mut self, f: F) -> &str {
        unsafe {
            let end = f.write_to_zmij_buffer(self.bytes.as_mut_ptr().cast::<u8>());
            let len = end.offset_from(self.bytes.as_ptr().cast::<u8>()) as usize;
            let slice = slice::from_raw_parts(self.bytes.as_ptr().cast::<u8>(), len);
            str::from_utf8_unchecked(slice)
        }
    }
}

/// A floating point number, f32 or f64, that can be written into a
/// [`zmij::Buffer`][Buffer].
///
/// This trait is sealed and cannot be implemented for types outside of the
/// `zmij` crate.
#[allow(unknown_lints)] // rustc older than 1.74
#[allow(private_bounds)]
pub trait Float: private::Sealed {}
impl Float for f32 {}
impl Float for f64 {}

mod private {
    pub trait Sealed: crate::traits::Float {
        fn is_nonfinite(self) -> bool;
        fn format_nonfinite(self) -> &'static str;
        unsafe fn write_to_zmij_buffer(self, buffer: *mut u8) -> *mut u8;
    }

    impl Sealed for f32 {
        #[inline]
        fn is_nonfinite(self) -> bool {
            const EXP_MASK: u32 = 0x7f800000;
            let bits = self.to_bits();
            bits & EXP_MASK == EXP_MASK
        }

        #[cold]
        #[cfg_attr(feature = "no-panic", inline)]
        fn format_nonfinite(self) -> &'static str {
            const MANTISSA_MASK: u32 = 0x007fffff;
            const SIGN_MASK: u32 = 0x80000000;
            let bits = self.to_bits();
            if bits & MANTISSA_MASK != 0 {
                crate::NAN
            } else if bits & SIGN_MASK != 0 {
                crate::NEG_INFINITY
            } else {
                crate::INFINITY
            }
        }

        #[cfg_attr(feature = "no-panic", inline)]
        unsafe fn write_to_zmij_buffer(self, buffer: *mut u8) -> *mut u8 {
            unsafe { crate::write(self, buffer) }
        }
    }

    impl Sealed for f64 {
        #[inline]
        fn is_nonfinite(self) -> bool {
            const EXP_MASK: u64 = 0x7ff0000000000000;
            let bits = self.to_bits();
            bits & EXP_MASK == EXP_MASK
        }

        #[cold]
        #[cfg_attr(feature = "no-panic", inline)]
        fn format_nonfinite(self) -> &'static str {
            const MANTISSA_MASK: u64 = 0x000fffffffffffff;
            const SIGN_MASK: u64 = 0x8000000000000000;
            let bits = self.to_bits();
            if bits & MANTISSA_MASK != 0 {
                crate::NAN
            } else if bits & SIGN_MASK != 0 {
                crate::NEG_INFINITY
            } else {
                crate::INFINITY
            }
        }

        #[cfg_attr(feature = "no-panic", inline)]
        unsafe fn write_to_zmij_buffer(self, buffer: *mut u8) -> *mut u8 {
            unsafe { crate::write(self, buffer) }
        }
    }
}

impl Default for Buffer {
    #[inline]
    #[cfg_attr(feature = "no-panic", no_panic)]
    fn default() -> Self {
        Buffer::new()
    }
}
