// Copyright 2018 Ulf Adams
// Modifications copyright 2019 Caleb Spare
//
// The contents of this file may be used under the terms of the Apache License,
// Version 2.0.
//
//    (See accompanying file LICENSE or copy at
//     http://www.apache.org/licenses/LICENSE-2.0)
//
// 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.
//
// The code in this file is part of a Go translation of the C code written by
// Ulf Adams which may be found at https://github.com/ulfjack/ryu. That source
// code is licensed under Apache 2.0 and this code is derivative work thereof.

// Package ryu implements the Ryu algorithm for quickly converting floating
// point numbers into strings.
package ryu

import (
	"math"
	"reflect"
	"unsafe"
)

//go:generate go run maketables.go

const (
	mantBits32 = 23
	expBits32  = 8
	bias32     = 127

	mantBits64 = 52
	expBits64  = 11
	bias64     = 1023
)

// FormatFloat32 converts a 32-bit floating point number f to a string.
// It behaves like strconv.FormatFloat(float64(f), 'e', -1, 32).
func FormatFloat32(f float32) string {
	b := make([]byte, 0, 15)
	b = AppendFloat32(b, f)

	// Convert the output to a string without copying.
	var s string
	sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
	sh.Data = uintptr(unsafe.Pointer(&b[0]))
	sh.Len = len(b)
	return s
}

// AppendFloat32 appends the string form of the 32-bit floating point number f,
// as generated by FormatFloat32, to b and returns the extended buffer.
func AppendFloat32(b []byte, f float32) []byte {
	// Step 1: Decode the floating-point number.
	// Unify normalized and subnormal cases.
	u := math.Float32bits(f)
	neg := u>>(mantBits32+expBits32) != 0
	mant := u & (uint32(1)<<mantBits32 - 1)
	exp := (u >> mantBits32) & (uint32(1)<<expBits32 - 1)

	// Exit early for easy cases.
	if exp == uint32(1)<<expBits32-1 || (exp == 0 && mant == 0) {
		return appendSpecial(b, neg, exp == 0, mant == 0)
	}

	d, ok := float32ToDecimalExactInt(mant, exp)
	if !ok {
		d = float32ToDecimal(mant, exp)
	}
	return d.append(b, neg)
}

// FormatFloat64 converts a 64-bit floating point number f to a string.
// It behaves like strconv.FormatFloat(f, 'e', -1, 64).
func FormatFloat64(f float64) string {
	b := make([]byte, 0, 24)
	b = AppendFloat64(b, f)

	// Convert the output to a string without copying.
	var s string
	sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
	sh.Data = uintptr(unsafe.Pointer(&b[0]))
	sh.Len = len(b)
	return s
}

// AppendFloat64 appends the string form of the 64-bit floating point number f,
// as generated by FormatFloat64, to b and returns the extended buffer.
func AppendFloat64(b []byte, f float64) []byte {
	// Step 1: Decode the floating-point number.
	// Unify normalized and subnormal cases.
	u := math.Float64bits(f)
	neg := u>>(mantBits64+expBits64) != 0
	mant := u & (uint64(1)<<mantBits64 - 1)
	exp := (u >> mantBits64) & (uint64(1)<<expBits64 - 1)

	// Exit early for easy cases.
	if exp == uint64(1)<<expBits64-1 || (exp == 0 && mant == 0) {
		return appendSpecial(b, neg, exp == 0, mant == 0)
	}

	d, ok := float64ToDecimalExactInt(mant, exp)
	if !ok {
		d = float64ToDecimal(mant, exp)
	}
	return d.append(b, neg)
}

func appendSpecial(b []byte, neg, expZero, mantZero bool) []byte {
	if !mantZero {
		return append(b, "NaN"...)
	}
	if !expZero {
		if neg {
			return append(b, "-Inf"...)
		} else {
			return append(b, "+Inf"...)
		}
	}
	if neg {
		b = append(b, '-')
	}
	return append(b, "0e+00"...)
}

func assert(t bool, msg string) {
	if !t {
		panic(msg)
	}
}

// log10Pow2 returns floor(log_10(2^e)).
func log10Pow2(e int32) uint32 {
	// The first value this approximation fails for is 2^1651
	// which is just greater than 10^297.
	assert(e >= 0, "e >= 0")
	assert(e <= 1650, "e <= 1650")
	return (uint32(e) * 78913) >> 18
}

// log10Pow5 returns floor(log_10(5^e)).
func log10Pow5(e int32) uint32 {
	// The first value this approximation fails for is 5^2621
	// which is just greater than 10^1832.
	assert(e >= 0, "e >= 0")
	assert(e <= 2620, "e <= 2620")
	return (uint32(e) * 732923) >> 20
}

// pow5Bits returns ceil(log_2(5^e)), or else 1 if e==0.
func pow5Bits(e int32) int32 {
	// This approximation works up to the point that the multiplication
	// overflows at e = 3529. If the multiplication were done in 64 bits,
	// it would fail at 5^4004 which is just greater than 2^9297.
	assert(e >= 0, "e >= 0")
	assert(e <= 3528, "e <= 3528")
	return int32((uint32(e)*1217359)>>19 + 1)
}

// These boolToXxx all inline as a movzx.

func boolToInt(b bool) int {
	if b {
		return 1
	}
	return 0
}

func boolToUint32(b bool) uint32 {
	if b {
		return 1
	}
	return 0
}

func boolToUint64(b bool) uint64 {
	if b {
		return 1
	}
	return 0
}
