// Copyright 2020 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

// This file implements the Eisel-Lemire ParseFloat algorithm, published in
// 2020 and discussed extensively at
// https://nigeltao.github.io/blog/2020/eisel-lemire.html
//
// The original C++ implementation is at
// https://github.com/lemire/fast_double_parser/blob/644bef4306059d3be01a04e77d3cc84b379c596f/include/fast_double_parser.h#L840
//
// This Go re-implementation closely follows the C re-implementation at
// https://github.com/google/wuffs/blob/ba3818cb6b473a2ed0b38ecfc07dbbd3a97e8ae7/internal/cgen/base/floatconv-submodule-code.c#L990
//
// Additional testing (on over several million test strings) is done by
// https://github.com/nigeltao/parse-number-fxx-test-data/blob/5280dcfccf6d0b02a65ae282dad0b6d9de50e039/script/test-go-strconv.go

import (
	
)

func eiselLemire64( uint64,  int,  bool) ( float64,  bool) {
	// The terse comments in this function body refer to sections of the
	// https://nigeltao.github.io/blog/2020/eisel-lemire.html blog post.

	// Exp10 Range.
	if  == 0 {
		if  {
			 = float64frombits(0x8000000000000000) // Negative zero.
		}
		return , true
	}
	, ,  := pow10()
	if ! {
		return 0, false
	}

	// Normalization.
	 := bits.LeadingZeros64()
	 <<= uint()
	 := uint64(+63-float64Bias) - uint64()

	// Multiplication.
	,  := bits.Mul64(, .Hi)

	// Wider Approximation.
	if &0x1FF == 0x1FF && + <  {
		,  := bits.Mul64(, .Lo)
		,  := , +
		if  <  {
			++
		}
		if &0x1FF == 0x1FF && +1 == 0 && + <  {
			return 0, false
		}
		,  = , 
	}

	// Shifting to 54 Bits.
	 :=  >> 63
	 :=  >> ( + 9)
	 -= 1 ^ 

	// Half-way Ambiguity.
	if  == 0 && &0x1FF == 0 && &3 == 1 {
		return 0, false
	}

	// From 54 to 53 Bits.
	 +=  & 1
	 >>= 1
	if >>53 > 0 {
		 >>= 1
		 += 1
	}
	// retExp2 is a uint64. Zero or underflow means that we're in subnormal
	// float64 space. 0x7FF or above means that we're in Inf/NaN float64 space.
	//
	// The if block is equivalent to (but has fewer branches than):
	//   if retExp2 <= 0 || retExp2 >= 0x7FF { etc }
	if -1 >= 0x7FF-1 {
		return 0, false
	}
	 := <<float64MantBits | &(1<<float64MantBits-1)
	if  {
		 |= 0x8000000000000000
	}
	return float64frombits(), true
}

func eiselLemire32( uint64,  int,  bool) ( float32,  bool) {
	// The terse comments in this function body refer to sections of the
	// https://nigeltao.github.io/blog/2020/eisel-lemire.html blog post.
	//
	// That blog post discusses the float64 flavor (11 exponent bits with a
	// -1023 bias, 52 mantissa bits) of the algorithm, but the same approach
	// applies to the float32 flavor (8 exponent bits with a -127 bias, 23
	// mantissa bits). The computation here happens with 64-bit values (e.g.
	// man, xHi, retMantissa) before finally converting to a 32-bit float.

	// Exp10 Range.
	if  == 0 {
		if  {
			 = float32frombits(0x80000000) // Negative zero.
		}
		return , true
	}
	, ,  := pow10()
	if ! {
		return 0, false
	}

	// Normalization.
	 := bits.LeadingZeros64()
	 <<= uint()
	 := uint64(+63-float32Bias) - uint64()

	// Multiplication.
	,  := bits.Mul64(, .Hi)

	// Wider Approximation.
	if &0x3FFFFFFFFF == 0x3FFFFFFFFF && + <  {
		,  := bits.Mul64(, .Lo)
		,  := , +
		if  <  {
			++
		}
		if &0x3FFFFFFFFF == 0x3FFFFFFFFF && +1 == 0 && + <  {
			return 0, false
		}
		,  = , 
	}

	// Shifting to 54 Bits (and for float32, it's shifting to 25 bits).
	 :=  >> 63
	 :=  >> ( + 38)
	 -= 1 ^ 

	// Half-way Ambiguity.
	if  == 0 && &0x3FFFFFFFFF == 0 && &3 == 1 {
		return 0, false
	}

	// From 54 to 53 Bits (and for float32, it's from 25 to 24 bits).
	 +=  & 1
	 >>= 1
	if >>24 > 0 {
		 >>= 1
		 += 1
	}
	// retExp2 is a uint64. Zero or underflow means that we're in subnormal
	// float32 space. 0xFF or above means that we're in Inf/NaN float32 space.
	//
	// The if block is equivalent to (but has fewer branches than):
	//   if retExp2 <= 0 || retExp2 >= 0xFF { etc }
	if -1 >= 0xFF-1 {
		return 0, false
	}
	 := <<float32MantBits | &(1<<float32MantBits-1)
	if  {
		 |= 0x80000000
	}
	return float32frombits(uint32()), true
}