// Copyright 2017 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.

//go:generate stringer -type RoundingMode

package number

import (
	
	
)

// RoundingMode determines how a number is rounded to the desired precision.
type RoundingMode byte

const (
	ToNearestEven RoundingMode = iota // towards the nearest integer, or towards an even number if equidistant.
	ToNearestZero                     // towards the nearest integer, or towards zero if equidistant.
	ToNearestAway                     // towards the nearest integer, or away from zero if equidistant.
	ToPositiveInf                     // towards infinity
	ToNegativeInf                     // towards negative infinity
	ToZero                            // towards zero
	AwayFromZero                      // away from zero
	numModes
)

const maxIntDigits = 20

// A Decimal represents a floating point number in decimal format.
// Digits represents a number [0, 1.0), and the absolute value represented by
// Decimal is Digits * 10^Exp. Leading and trailing zeros may be omitted and Exp
// may point outside a valid position in Digits.
//
// Examples:
//
//	Number     Decimal
//	12345      Digits: [1, 2, 3, 4, 5], Exp: 5
//	12.345     Digits: [1, 2, 3, 4, 5], Exp: 2
//	12000      Digits: [1, 2],          Exp: 5
//	12000.00   Digits: [1, 2],          Exp: 5
//	0.00123    Digits: [1, 2, 3],       Exp: -2
//	0          Digits: [],              Exp: 0
type Decimal struct {
	digits

	buf [maxIntDigits]byte
}

type digits struct {
	Digits []byte // mantissa digits, big-endian
	Exp    int32  // exponent
	Neg    bool
	Inf    bool // Takes precedence over Digits and Exp.
	NaN    bool // Takes precedence over Inf.
}

// Digits represents a floating point number represented in digits of the
// base in which a number is to be displayed. It is similar to Decimal, but
// keeps track of trailing fraction zeros and the comma placement for
// engineering notation. Digits must have at least one digit.
//
// Examples:
//
//	  Number     Decimal
//	decimal
//	  12345      Digits: [1, 2, 3, 4, 5], Exp: 5  End: 5
//	  12.345     Digits: [1, 2, 3, 4, 5], Exp: 2  End: 5
//	  12000      Digits: [1, 2],          Exp: 5  End: 5
//	  12000.00   Digits: [1, 2],          Exp: 5  End: 7
//	  0.00123    Digits: [1, 2, 3],       Exp: -2 End: 3
//	  0          Digits: [],              Exp: 0  End: 1
//	scientific (actual exp is Exp - Comma)
//	  0e0        Digits: [0],             Exp: 1, End: 1, Comma: 1
//	  .0e0       Digits: [0],             Exp: 0, End: 1, Comma: 0
//	  0.0e0      Digits: [0],             Exp: 1, End: 2, Comma: 1
//	  1.23e4     Digits: [1, 2, 3],       Exp: 5, End: 3, Comma: 1
//	  .123e5     Digits: [1, 2, 3],       Exp: 5, End: 3, Comma: 0
//	engineering
//	  12.3e3     Digits: [1, 2, 3],       Exp: 5, End: 3, Comma: 2
type Digits struct {
	digits
	// End indicates the end position of the number.
	End int32 // For decimals Exp <= End. For scientific len(Digits) <= End.
	// Comma is used for the comma position for scientific (always 0 or 1) and
	// engineering notation (always 0, 1, 2, or 3).
	Comma uint8
	// IsScientific indicates whether this number is to be rendered as a
	// scientific number.
	IsScientific bool
}

func ( *Digits) () int {
	if .Exp >= .End {
		return 0
	}
	return int(.End - .Exp)
}

// normalize returns a new Decimal with leading and trailing zeros removed.
func ( *Decimal) () ( Decimal) {
	 = *
	 := .Digits
	// Strip leading zeros. Resulting number of digits is significant digits.
	for len() > 0 && [0] == 0 {
		 = [1:]
		.Exp--
	}
	// Strip trailing zeros
	for len() > 0 && [len()-1] == 0 {
		 = [:len()-1]
	}
	if len() == 0 {
		.Exp = 0
	}
	.Digits = 
	return 
}

func ( *Decimal) () {
	 := .Digits
	if  == nil {
		 = .buf[:0]
	}
	* = Decimal{}
	.Digits = [:0]
}

func ( *Decimal) () string {
	if .NaN {
		return "NaN"
	}
	var  []byte
	if .Neg {
		 = append(, '-')
	}
	if .Inf {
		 = append(, "Inf"...)
		return string()
	}
	switch {
	case len(.Digits) == 0:
		 = append(, '0')
	case .Exp <= 0:
		// 0.00ddd
		 = append(, "0."...)
		 = appendZeros(, -int(.Exp))
		 = appendDigits(, .Digits)

	case /* 0 < */ int(.Exp) < len(.Digits):
		// dd.ddd
		 = appendDigits(, .Digits[:.Exp])
		 = append(, '.')
		 = appendDigits(, .Digits[.Exp:])

	default: // len(x.Digits) <= x.Exp
		// ddd00
		 = appendDigits(, .Digits)
		 = appendZeros(, int(.Exp)-len(.Digits))
	}
	return string()
}

func appendDigits( []byte,  []byte) []byte {
	for ,  := range  {
		 = append(, +'0')
	}
	return 
}

// appendZeros appends n 0 digits to buf and returns buf.
func appendZeros( []byte,  int) []byte {
	for ;  > 0; -- {
		 = append(, '0')
	}
	return 
}

func ( *digits) ( RoundingMode,  int) {
	if  >= len(.Digits) {
		return
	}
	// Make rounding decision: The result mantissa is truncated ("rounded down")
	// by default. Decide if we need to increment, or "round up", the (unsigned)
	// mantissa.
	 := false
	switch  {
	case ToNegativeInf:
		 = .Neg
	case ToPositiveInf:
		 = !.Neg
	case ToZero:
		// nothing to do
	case AwayFromZero:
		 = true
	case ToNearestEven:
		 = .Digits[] > 5 || .Digits[] == 5 &&
			(len(.Digits) > +1 ||  == 0 || .Digits[-1]&1 != 0)
	case ToNearestAway:
		 = .Digits[] >= 5
	case ToNearestZero:
		 = .Digits[] > 5 || .Digits[] == 5 && len(.Digits) > +1
	default:
		panic("unreachable")
	}
	if  {
		.roundUp()
	} else {
		.roundDown()
	}
}

// roundFloat rounds a floating point number.
func ( RoundingMode) ( float64) float64 {
	// Make rounding decision: The result mantissa is truncated ("rounded down")
	// by default. Decide if we need to increment, or "round up", the (unsigned)
	// mantissa.
	 := 
	if  < 0 {
		 = -
	}
	,  := math.Modf()
	if  == 0.0 {
		return 
	}
	 := false
	switch  {
	case ToNegativeInf:
		 =  < 0
	case ToPositiveInf:
		 =  >= 0
	case ToZero:
		// nothing to do
	case AwayFromZero:
		 = true
	case ToNearestEven:
		// TODO: check overflow
		 =  > 0.5 ||  == 0.5 && int64()&1 != 0
	case ToNearestAway:
		 =  >= 0.5
	case ToNearestZero:
		 =  > 0.5
	default:
		panic("unreachable")
	}
	if  {
		 += 1
	}
	if  !=  {
		 = -
	}
	return 
}

func ( *digits) ( int) {
	if  < 0 ||  >= len(.Digits) {
		return // nothing to do
	}
	// find first digit < 9
	for  > 0 && .Digits[-1] >= 9 {
		--
	}

	if  == 0 {
		// all digits are 9s => round up to 1 and update exponent
		.Digits[0] = 1 // ok since len(x.Digits) > n
		.Digits = .Digits[:1]
		.Exp++
		return
	}
	.Digits[-1]++
	.Digits = .Digits[:]
	// x already trimmed
}

func ( *digits) ( int) {
	if  < 0 ||  >= len(.Digits) {
		return // nothing to do
	}
	.Digits = .Digits[:]
	trim()
}

// trim cuts off any trailing zeros from x's mantissa;
// they are meaningless for the value of x.
func trim( *digits) {
	 := len(.Digits)
	for  > 0 && .Digits[-1] == 0 {
		--
	}
	.Digits = .Digits[:]
	if  == 0 {
		.Exp = 0
	}
}

// A Converter converts a number into decimals according to the given rounding
// criteria.
type Converter interface {
	Convert(d *Decimal, r RoundingContext)
}

const (
	signed   = true
	unsigned = false
)

// Convert converts the given number to the decimal representation using the
// supplied RoundingContext.
func ( *Decimal) ( RoundingContext,  interface{}) {
	switch f := .(type) {
	case Converter:
		.clear()
		.Convert(, )
	case float32:
		.ConvertFloat(, float64(), 32)
	case float64:
		.ConvertFloat(, , 64)
	case int:
		.ConvertInt(, signed, uint64())
	case int8:
		.ConvertInt(, signed, uint64())
	case int16:
		.ConvertInt(, signed, uint64())
	case int32:
		.ConvertInt(, signed, uint64())
	case int64:
		.ConvertInt(, signed, uint64())
	case uint:
		.ConvertInt(, unsigned, uint64())
	case uint8:
		.ConvertInt(, unsigned, uint64())
	case uint16:
		.ConvertInt(, unsigned, uint64())
	case uint32:
		.ConvertInt(, unsigned, uint64())
	case uint64:
		.ConvertInt(, unsigned, )

	default:
		.NaN = true
		// TODO:
		// case string: if produced by strconv, allows for easy arbitrary pos.
		// case reflect.Value:
		// case big.Float
		// case big.Int
		// case big.Rat?
		// catch underlyings using reflect or will this already be done by the
		//    message package?
	}
}

// ConvertInt converts an integer to decimals.
func ( *Decimal) ( RoundingContext,  bool,  uint64) {
	if .Increment > 0 {
		// TODO: if uint64 is too large, fall back to float64
		if  {
			.ConvertFloat(, float64(int64()), 64)
		} else {
			.ConvertFloat(, float64(), 64)
		}
		return
	}
	.clear()
	if  && int64() < 0 {
		 = uint64(-int64())
		.Neg = true
	}
	.fillIntDigits()
	.Exp = int32(len(.Digits))
}

// ConvertFloat converts a floating point number to decimals.
func ( *Decimal) ( RoundingContext,  float64,  int) {
	.clear()
	if math.IsNaN() {
		.NaN = true
		return
	}
	// Simple case: decimal notation
	if .Increment > 0 {
		 := int(.IncrementScale)
		 := 1.0
		if  >= len(scales) {
			 = math.Pow(10, float64())
		} else {
			 = scales[]
		}
		// We multiply x instead of dividing inc as it gives less rounding
		// issues.
		 *= 
		 /= float64(.Increment)
		 = .Mode.roundFloat()
		 *= float64(.Increment)
		 /= 
	}

	 := 
	if  < 0 {
		.Neg = true
		 = -
	}
	if math.IsInf(, 1) {
		.Inf = true
		return
	}

	// By default we get the exact decimal representation.
	 := byte('g')
	 := -1
	// As the strconv API does not return the rounding accuracy, we can only
	// round using ToNearestEven.
	if .Mode == ToNearestEven {
		if  := .RoundSignificantDigits();  >= 0 {
			 = 
		} else if  = .RoundFractionDigits();  >= 0 {
			 = 
			 = 'f'
		}
	} else {
		// TODO: At this point strconv's rounding is imprecise to the point that
		// it is not usable for this purpose.
		// See https://github.com/golang/go/issues/21714
		// If rounding is requested, we ask for a large number of digits and
		// round from there to simulate rounding only once.
		// Ideally we would have strconv export an AppendDigits that would take
		// a rounding mode and/or return an accuracy. Something like this would
		// work:
		// AppendDigits(dst []byte, x float64, base, size, prec int) (digits []byte, exp, accuracy int)
		 := .RoundSignificantDigits() >= 0
		 := .RoundFractionDigits() >= 0
		if  ||  {
			// prec is the number of mantissa bits plus some extra for safety.
			// We need at least the number of mantissa bits as decimals to
			// accurately represent the floating point without rounding, as each
			// bit requires one more decimal to represent: 0.5, 0.25, 0.125, ...
			 = 60
		}
	}

	 := strconv.AppendFloat(.Digits[:0], , , , )
	 := 0
	 := 0
	 := 1
	for  < len() {
		if  := []; '0' <=  &&  <= '9' {
			[] =  - '0'
			++
			.Exp += int32()
		} else if  == '.' {
			 = 0
			.Exp = int32()
		} else {
			break
		}
		++
	}
	.Digits = [:]
	if  != len() {
		 += len("e")
		 := 
		 := 0
		for ++;  < len(); ++ {
			 *= 10
			 += int([] - '0')
		}
		if [] == '-' {
			 = -
		}
		.Exp = int32() + 1
	}
}

func ( *Decimal) ( uint64) {
	if cap(.Digits) < maxIntDigits {
		.Digits = .buf[:]
	} else {
		.Digits = .buf[:maxIntDigits]
	}
	 := 0
	for ;  > 0;  /= 10 {
		.Digits[] = byte( % 10)
		++
	}
	.Digits = .Digits[:]
	for  := 0;  < ; ++ {
		--
		.Digits[], .Digits[] = .Digits[], .Digits[]
	}
}

var scales [70]float64

func init() {
	 := 1.0
	for  := range scales {
		scales[] = 
		 *= 10
	}
}