// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package strconv

// Binary to decimal conversion using the Dragonbox algorithm by Junekey Jeon.
//
// Fixed precision format is not supported by the Dragonbox algorithm
// so we continue to use Ryū-printf for this purpose.
// See https://github.com/jk-jeon/dragonbox/issues/38 for more details.
//
// For binary to decimal rounding, uses round to nearest, tie to even.
// For decimal to binary rounding, assumes round to nearest, tie to even.
//
// The original paper by Junekey Jeon can be found at:
// https://github.com/jk-jeon/dragonbox/blob/d5dc40ae6a3f1a4559cda816738df2d6255b4e24/other_files/Dragonbox.pdf
//
// The reference implementation in C++ by Junekey Jeon can be found at:
// https://github.com/jk-jeon/dragonbox/blob/6c7c925b571d54486b9ffae8d9d18a822801cbda/subproject/simple/include/simple_dragonbox.h

// dragonboxFtoa computes the decimal significand and exponent
// from the binary significand and exponent using the Dragonbox algorithm
// and formats the decimal floating point number in d.
func dboxFtoa( *decimalSlice,  uint64,  int,  bool,  int) {
	if  == 32 {
		dboxFtoa32(, uint32(), , )
		return
	}
	dboxFtoa64(, , , )
}

func dboxFtoa64( *decimalSlice,  uint64,  int,  bool) {
	if  == 1<<float64MantBits && ! {
		// Algorithm 5.6 (page 24).
		 := -mulLog10_2MinusLog10_4Over3()
		,  := dboxPow64(, )
		,  := dboxRange64(, )
		if  != 2 &&  != 3 {
			++
		}
		 :=  / 10
		if  <= *10 {
			,  := trimZeros()
			dboxDigits(, , -+1+)
			return
		}
		 := dboxRoundUp64(, )
		if  == -77 && %2 != 0 {
			--
		} else if  <  {
			++
		}
		dboxDigits(, , -)
		return
	}

	// κ = 2 for float64 (section 5.1.3)
	const (
		     = 2
		  = 100       // 10**κ
		 =  * 10 // 10**(κ+1)
	)

	// Algorithm 5.2 (page 15).
	 := -mulLog10_2()
	,  := dboxPow64(+, )
	,  := dboxMulPow64(uint64(*2+1)<<, )
	,  := /, uint32(%)
	 := dboxDelta64(, )

	if  <  {
		if  != 0 || ! || %2 == 0 {
			,  := trimZeros()
			dboxDigits(, , -+1+)
			return
		}
		--
		 =  * 10
	} else if  ==  {
		,  := dboxParity64(uint64(*2-1), , )
		if  || ( && %2 == 0) {
			,  := trimZeros()
			dboxDigits(, , -+1+)
			return
		}
	}

	// Algorithm 5.4 (page 18).
	 :=  + /2 - /2
	,  := /, %
	 := 10* + uint64()
	if  == 0 {
		,  := dboxParity64(*2, , )
		if  != ((-/2)%2 != 0) ||  && %2 != 0 {
			--
		}
	}
	dboxDigits(, , -)
}

// Almost identical to dragonboxFtoa64.
// This is kept as a separate copy to minimize runtime overhead.
func dboxFtoa32( *decimalSlice,  uint32,  int,  bool) {
	if  == 1<<float32MantBits && ! {
		// Algorithm 5.6 (page 24).
		 := -mulLog10_2MinusLog10_4Over3()
		,  := dboxPow32(, )
		,  := dboxRange32(, )
		if  != 2 &&  != 3 {
			++
		}
		 :=  / 10
		if  <= *10 {
			,  := trimZeros(uint64())
			dboxDigits(, , -+1+)
			return
		}
		 := dboxRoundUp32(, )
		if  == -77 && %2 != 0 {
			--
		} else if  <  {
			++
		}
		dboxDigits(, uint64(), -)
		return
	}

	// κ = 1 for float32 (section 5.1.3)
	const (
		     = 1
		  = 10
		 =  * 10
	)

	// Algorithm 5.2 (page 15).
	 := -mulLog10_2()
	,  := dboxPow32(+, )
	,  := dboxMulPow32(uint32(*2+1)<<, )
	,  := /, uint32(%)
	 := dboxDelta32(, )

	if  <  {
		if  != 0 || ! || %2 == 0 {
			,  := trimZeros(uint64())
			dboxDigits(, , -+1+)
			return
		}
		--
		 =  * 10
	} else if  ==  {
		,  := dboxParity32(uint32(*2-1), , )
		if  || ( && %2 == 0) {
			,  := trimZeros(uint64())
			dboxDigits(, , -+1+)
			return
		}
	}

	// Algorithm 5.4 (page 18).
	 :=  + /2 - /2
	,  := /, %
	 := 10* + uint32()
	if  == 0 {
		,  := dboxParity32(*2, , )
		if  != ((-/2)%2 != 0) ||  && %2 != 0 {
			--
		}
	}
	dboxDigits(, uint64(), -)
}

// dboxDigits emits decimal digits of mant in d for float64
// and adjusts the decimal point based on exp.
func dboxDigits( *decimalSlice,  uint64,  int) {
	 := formatBase10(.d, )
	.d = .d[:]
	.nd = len(.d)
	.dp = .nd + 
}

// uadd128 returns the full 128 bits of u + n.
func uadd128( uint128,  uint64) uint128 {
	 := uint64(.Lo + )
	// Check if lo is wrapped around.
	if  < .Lo {
		.Hi++
	}
	.Lo = 
	return 
}

// umul64 returns the full 64 bits of x * y.
func umul64(,  uint32) uint64 {
	return uint64() * uint64()
}

// umul96Upper64 returns the upper 64 bits (out of 96 bits) of x * y.
func umul96Upper64( uint32,  uint64) uint64 {
	 := uint32( >> 32)
	 := uint32()

	 := umul64(, )
	 := umul64(, )

	return  + ( >> 32)
}

// umul96Lower64 returns the lower 64 bits (out of 96 bits) of x * y.
func umul96Lower64( uint32,  uint64) uint64 {
	return uint64(uint64() * )
}

// umul128Upper64 returns the upper 64 bits (out of 128 bits) of x * y.
func umul128Upper64(,  uint64) uint64 {
	 := uint32( >> 32)
	 := uint32()
	 := uint32( >> 32)
	 := uint32()

	 := umul64(, )
	 := umul64(, )
	 := umul64(, )
	 := umul64(, )

	 := ( >> 32) + uint64(uint32()) + uint64(uint32())

	return  + ( >> 32) + ( >> 32) + ( >> 32)
}

// umul192Upper128 returns the upper 128 bits (out of 192 bits) of x * y.
func umul192Upper128( uint64,  uint128) uint128 {
	 := umul128(, .Hi)
	 := umul128Upper64(, .Lo)
	return uadd128(, )
}

// umul192Lower128 returns the lower 128 bits (out of 192 bits) of x * y.
func umul192Lower128( uint64,  uint128) uint128 {
	 :=  * .Hi
	 := umul128(, .Lo)
	return uint128{uint64( + .Hi), .Lo}
}

// dboxMulPow64 computes x^(i), y^(i), z^(i)
// from the precomputed value of φ̃k for float64
// and also checks if x^(f), y^(f), z^(f) == 0 (section 5.2.1).
func dboxMulPow64( uint64,  uint128) ( uint64,  bool) {
	 := umul192Upper128(, )
	 = .Hi
	 = .Lo == 0
	return
}

// dboxMulPow32 computes x^(i), y^(i), z^(i)
// from the precomputed value of φ̃k for float32
// and also checks if x^(f), y^(f), z^(f) == 0 (section 5.2.1).
func dboxMulPow32( uint32,  uint64) ( uint32,  bool) {
	 := umul96Upper64(, )
	 = uint32( >> 32)
	 = uint32() == 0
	return
}

// dboxParity64 computes only the parity of x^(i), y^(i), z^(i)
// from the precomputed value of φ̃k for float64
// and also checks if x^(f), y^(f), z^(f) = 0 (section 5.2.1).
func dboxParity64( uint64,  uint128,  int) ( bool,  bool) {
	 := umul192Lower128(, )
	 = ((.Hi >> (64 - )) & 1) != 0
	 = ((uint64(.Hi << )) | (.Lo >> (64 - ))) == 0
	return
}

// dboxParity32 computes only the parity of x^(i), y^(i), z^(i)
// from the precomputed value of φ̃k for float32
// and also checks if x^(f), y^(f), z^(f) = 0 (section 5.2.1).
func dboxParity32( uint32,  uint64,  int) ( bool,  bool) {
	 := umul96Lower64(, )
	 = (( >> (64 - )) & 1) != 0
	 = uint32(>>(32-)) == 0
	return
}

// dboxDelta64 returns δ^(i) from the precomputed value of φ̃k for float64.
func dboxDelta64( uint128,  int) uint32 {
	return uint32(.Hi >> (64 - 1 - ))
}

// dboxDelta32 returns δ^(i) from the precomputed value of φ̃k for float32.
func dboxDelta32( uint64,  int) uint32 {
	return uint32( >> (64 - 1 - ))
}

// mulLog10_2MinusLog10_4Over3 computes
// ⌊e*log10(2)-log10(4/3)⌋ = ⌊log10(2^e)-log10(4/3)⌋ (section 6.3).
func mulLog10_2MinusLog10_4Over3( int) int {
	// e should be in the range [-2985, 2936].
	return (*631305 - 261663) >> 21
}

const (
	floatMantBits64 = 52 // p = 52 for float64.
	floatMantBits32 = 23 // p = 23 for float32.
)

// dboxRange64 returns the left and right float64 endpoints.
func dboxRange64( uint128,  int) (,  uint64) {
	 = (.Hi - (.Hi >> (float64MantBits + 2))) >> (64 - float64MantBits - 1 - )
	 = (.Hi + (.Hi >> (float64MantBits + 1))) >> (64 - float64MantBits - 1 - )
	return , 
}

// dboxRange32 returns the left and right float32 endpoints.
func dboxRange32( uint64,  int) (,  uint32) {
	 = uint32(( - ( >> (floatMantBits32 + 2))) >> (64 - floatMantBits32 - 1 - ))
	 = uint32(( + ( >> (floatMantBits32 + 1))) >> (64 - floatMantBits32 - 1 - ))
	return , 
}

// dboxRoundUp64 computes the round up of y (i.e., y^(ru)).
func dboxRoundUp64( uint128,  int) uint64 {
	return (.Hi>>(128/2-floatMantBits64-2-) + 1) / 2
}

// dboxRoundUp32 computes the round up of y (i.e., y^(ru)).
func dboxRoundUp32( uint64,  int) uint32 {
	return uint32(>>(64-floatMantBits32-2-)+1) / 2
}

// dboxPow64 gets the precomputed value of φ̃̃k for float64.
func dboxPow64(,  int) ( uint128,  int) {
	, ,  := pow10()
	if  < 0 ||  > 55 {
		.Lo++
	}
	 =  +  - 1
	return , 
}

// dboxPow32 gets the precomputed value of φ̃̃k for float32.
func dboxPow32(,  int) ( uint64,  int) {
	, ,  := pow10()
	if  < 0 ||  > 27 {
		.Hi++
	}
	 =  +  - 1
	return .Hi, 
}