// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package decimal

import (
	
	
	
	
	
	
	
	

	
	
	
)

// DecimalTypes is a generic constraint representing the implemented decimal types
// in this package, and a single point of update for future additions. Everything
// else is constrained by this.
type DecimalTypes interface {
	Decimal32 | Decimal64 | Decimal128 | Decimal256
}

// Num is an interface that is useful for building generic types for all decimal
// type implementations. It presents all the methods and interfaces necessary to
// operate on the decimal objects without having to care about the bit width.
type Num[ DecimalTypes] interface {
	Negate() 
	Add() 
	Sub() 
	Mul() 
	Div() (res, rem )
	Pow() 

	FitsInPrecision(int32) bool
	Abs() 
	Sign() int
	Rescale(int32, int32) (, error)
	Cmp() int

	IncreaseScaleBy(int32) 
	ReduceScaleBy(int32, bool) 

	ToFloat32(int32) float32
	ToFloat64(int32) float64
	ToBigFloat(int32) *big.Float

	ToString(int32) string
}

type (
	Decimal32  int32
	Decimal64  int64
	Decimal128 = decimal128.Num
	Decimal256 = decimal256.Num
)

func [ DecimalTypes]() int {
	// max precision is computed by Floor(log10(2^(nbytes * 8 - 1) - 1))
	var  
	return int(math.Floor(math.Log10(math.Pow(2, float64(unsafe.Sizeof())*8-1) - 1)))
}

func ( Decimal32) () Decimal32 { return - }
func ( Decimal64) () Decimal64 { return - }

func ( Decimal32) ( Decimal32) Decimal32 { return  +  }
func ( Decimal64) ( Decimal64) Decimal64 { return  +  }

func ( Decimal32) ( Decimal32) Decimal32 { return  -  }
func ( Decimal64) ( Decimal64) Decimal64 { return  -  }

func ( Decimal32) ( Decimal32) Decimal32 { return  *  }
func ( Decimal64) ( Decimal64) Decimal64 { return  *  }

func ( Decimal32) ( Decimal32) (,  Decimal32) {
	return  / ,  % 
}

func ( Decimal64) ( Decimal64) (,  Decimal64) {
	return  / ,  % 
}

// about 4-5x faster than using math.Pow which requires converting to float64
// and back to integers
func intPow[ int32 | int64](,  )  {
	 := (1)
	for {
		if &1 == 1 {
			 *= 
		}
		 >>= 1
		if  == 0 {
			break
		}
		 *= 
	}

	return 
}

func ( Decimal32) ( Decimal32) Decimal32 {
	return Decimal32(intPow(int32(), int32()))
}

func ( Decimal64) ( Decimal64) Decimal64 {
	return Decimal64(intPow(int64(), int64()))
}

func ( Decimal32) () int {
	if  == 0 {
		return 0
	}
	return int(1 | ( >> 31))
}

func ( Decimal64) () int {
	if  == 0 {
		return 0
	}
	return int(1 | ( >> 63))
}

func ( Decimal32) () Decimal32 {
	if  < 0 {
		return -
	}
	return 
}

func ( Decimal64) () Decimal64 {
	if  < 0 {
		return -
	}
	return 
}

func ( Decimal32) ( int32) bool {
	debug.Assert( > 0, "precision must be > 0")
	debug.Assert( <= 9, "precision must be <= 9")
	return .Abs() < Decimal32(math.Pow10(int()))
}

func ( Decimal64) ( int32) bool {
	debug.Assert( > 0, "precision must be > 0")
	debug.Assert( <= 18, "precision must be <= 18")
	return .Abs() < Decimal64(math.Pow10(int()))
}

func ( Decimal32) ( int32) string {
	return .ToBigFloat().Text('f', int())
}

func ( Decimal64) ( int32) string {
	return .ToBigFloat().Text('f', int())
}

var pt5 = big.NewFloat(0.5)

func decimalFromString[ interface {
	Decimal32 | Decimal64
	(int32) bool
}]( string, ,  int32) ( ,  error) {
	var  = uint(unsafe.Sizeof((0))) * 8

	var  *big.Float
	, _,  = big.ParseFloat(, 10, , big.ToNearestEven)

	if  < 0 {
		var  big.Int
		,  := .Int(&)
		if .BitLen() > int() {
			return , fmt.Errorf("bitlen too large for decimal%d", )
		}

		 = (.Int64() / int64(math.Pow10(int(-))))
	} else {
		var  = uint(math.Round(float64(++1)/math.Log10(2))) + 1

		 := (&big.Float{}).SetInt(big.NewInt(int64(math.Pow10(int()))))
		.SetPrec().Mul(, )
		if .Signbit() {
			.Sub(, pt5)
		} else {
			.Add(, pt5)
		}

		var  big.Int
		,  := .Int(&)
		if .BitLen() > int() {
			return , fmt.Errorf("bitlen too large for decimal%d", )
		}
		 = (.Int64())
	}

	if !.() {
		 = fmt.Errorf("val %v doesn't fit in precision %d", , )
	}
	return
}

func ( string, ,  int32) ( Decimal32,  error) {
	return decimalFromString[Decimal32](, , )
}

func ( string, ,  int32) ( Decimal64,  error) {
	return decimalFromString[Decimal64](, , )
}

func ( string, ,  int32) ( Decimal128,  error) {
	return decimal128.FromString(, , )
}

func ( string, ,  int32) ( Decimal256,  error) {
	return decimal256.FromString(, , )
}

func scalePositiveFloat64( float64, ,  int32) (float64, error) {
	 *= math.Pow10(int())
	 = math.RoundToEven()

	 := math.Pow10(int())
	if  >=  {
		return 0, fmt.Errorf("cannot convert %f to decimal(precision=%d, scale=%d)", , , )
	}
	return , nil
}

func fromPositiveFloat[ Decimal32 | Decimal64,  float32 | float64]( , ,  int32) (, error) {
	if  > int32(MaxPrecision[]()) {
		return (0), fmt.Errorf("invalid precision %d for converting float to Decimal", )
	}

	,  := scalePositiveFloat64(float64(), , )
	if  != nil {
		return (0), 
	}

	return (()), nil
}

func [ float32 | float64]( , ,  int32) (Decimal32, error) {
	if  < 0 {
		,  := fromPositiveFloat[Decimal32](-, , )
		if  != nil {
			return , 
		}

		return -, nil
	}

	return fromPositiveFloat[Decimal32](, , )
}

func [ float32 | float64]( , ,  int32) (Decimal64, error) {
	if  < 0 {
		,  := fromPositiveFloat[Decimal64](-, , )
		if  != nil {
			return , 
		}

		return -, nil
	}

	return fromPositiveFloat[Decimal64](, , )
}

func ( float64, ,  int32) (Decimal128, error) {
	return decimal128.FromFloat64(, , )
}

func ( float64, ,  int32) (Decimal256, error) {
	return decimal256.FromFloat64(, , )
}

func ( Decimal32) ( int32) float32 {
	return float32(.ToFloat64())
}

func ( Decimal64) ( int32) float32 {
	return float32(.ToFloat64())
}

func ( Decimal32) ( int32) float64 {
	return float64() * math.Pow10(-int())
}

func ( Decimal64) ( int32) float64 {
	return float64() * math.Pow10(-int())
}

func ( Decimal32) ( int32) *big.Float {
	 := (&big.Float{}).SetInt64(int64())
	if  < 0 {
		.SetPrec(32).Mul(, (&big.Float{}).SetInt64(intPow(10, -int64())))
	} else {
		.SetPrec(32).Quo(, (&big.Float{}).SetInt64(intPow(10, int64())))
	}
	return 
}

func ( Decimal64) ( int32) *big.Float {
	 := (&big.Float{}).SetInt64(int64())
	if  < 0 {
		.SetPrec(64).Mul(, (&big.Float{}).SetInt64(intPow(10, -int64())))
	} else {
		.SetPrec(64).Quo(, (&big.Float{}).SetInt64(intPow(10, int64())))
	}
	return 
}

func cmpDec[ Decimal32 | Decimal64](,  ) int {
	switch {
	case  > :
		return 1
	case  < :
		return -1
	}
	return 0
}

func ( Decimal32) ( Decimal32) int {
	return cmpDec(, )
}

func ( Decimal64) ( Decimal64) int {
	return cmpDec(, )
}

func ( Decimal32) ( int32) Decimal32 {
	debug.Assert( >= 0, "invalid increase scale for decimal32")
	debug.Assert( <= 9, "invalid increase scale for decimal32")

	return  * Decimal32(intPow(10, ))
}

func ( Decimal64) ( int32) Decimal64 {
	debug.Assert( >= 0, "invalid increase scale for decimal64")
	debug.Assert( <= 18, "invalid increase scale for decimal64")

	return  * Decimal64(intPow(10, int64()))
}

func reduceScale[ interface {
	Decimal32 | Decimal64
	() 
}]( ,  int32,  bool)  {
	if  == 0 {
		return 
	}

	 := (intPow(10, ))
	if ! {
		return  / 
	}

	,  := /, %
	 :=  / 2
	if .() >=  {
		if  > 0 {
			++
		} else {
			--
		}
	}

	return 
}

func ( Decimal32) ( int32,  bool) Decimal32 {
	debug.Assert( >= 0, "invalid reduce scale for decimal32")
	debug.Assert( <= 9, "invalid reduce scale for decimal32")

	return reduceScale(, , )
}

func ( Decimal64) ( int32,  bool) Decimal64 {
	debug.Assert( >= 0, "invalid reduce scale for decimal32")
	debug.Assert( <= 18, "invalid reduce scale for decimal32")

	return reduceScale(, , )
}

//lint:ignore U1000 function is being used, staticcheck seems to not follow generics
func ( Decimal32) ( int32,  Decimal32) ( Decimal32,  bool) {
	if  < 0 {
		debug.Assert( != 0, "multiplier must not be zero")
		,  := bits.Div32(0, uint32(), uint32())
		return Decimal32(),  != 0
	}

	,  := bits.Mul32(uint32(), uint32())
	if  != 0 {
		return Decimal32(), true
	}

	 = Decimal32()
	return ,  < 
}

//lint:ignore U1000 function is being used, staticcheck seems to not follow generics
func ( Decimal64) ( int32,  Decimal64) ( Decimal64,  bool) {
	if  < 0 {
		debug.Assert( != 0, "multiplier must not be zero")
		,  := bits.Div32(0, uint32(), uint32())
		return Decimal64(),  != 0
	}

	,  := bits.Mul32(uint32(), uint32())
	if  != 0 {
		return Decimal64(), true
	}

	 = Decimal64()
	return ,  < 
}

func rescale[ interface {
	Decimal32 | Decimal64
	(int32, ) (, bool)
	() int
}]( , ,  int32) ( ,  error) {
	if  ==  {
		return , nil
	}

	 :=  - 
	 := int32(math.Abs(float64()))

	 := .()
	if  < 0 {
		 = -
	}

	 := (intPow(10, ))
	var  bool
	,  = .(, )
	if  {
		 = errors.New("rescale data loss")
	}
	 *= ()
	return
}

func ( Decimal32) (,  int32) ( Decimal32,  error) {
	return rescale(, , )
}

func ( Decimal64) (,  int32) ( Decimal64,  error) {
	return rescale(, , )
}

var (
	_ Num[Decimal32]  = Decimal32(0)
	_ Num[Decimal64]  = Decimal64(0)
	_ Num[Decimal128] = Decimal128{}
	_ Num[Decimal256] = Decimal256{}
)

type decComponents struct {
	wholeDigits string
	fractDigits string
	exp         int32
	sign        byte
	hasExponent bool
}

func isSign( byte) bool {
	return  == '-' ||  == '+'
}

func isDot( byte) bool {
	return  == '.'
}

func startsExponent( byte) bool {
	return  == 'e' ||  == 'E'
}

func parseDigitsRun( string) (,  string) {
	 := strings.IndexFunc(, func( rune) bool {
		return  < '0' ||  > '9'
	})
	if  == -1 {
		return , ""
	}

	return [:], [:]
}

func parseDecimalComponents( string) ( decComponents,  bool) {
	if len() == 0 {
		return
	}

	if isSign([0]) {
		.sign = [0]
		 = [1:]
	}

	// first run of digits
	.wholeDigits,  = parseDigitsRun()
	if len() == 0 {
		return , len(.wholeDigits) > 0
	}

	if isDot([0]) {
		 = [1:]
		// second run of digits
		.fractDigits,  = parseDigitsRun()
	}

	if len(.wholeDigits) == 0 && len(.fractDigits) == 0 {
		// need at least some digits (whole or fractional)
		return
	}

	if len() == 0 {
		return , true
	}

	// optional exponent
	if startsExponent([0]) {
		 = [1:]
		if len() > 0 && [0] == '+' {
			 = [1:]
		}
		.hasExponent = true
		,  := strconv.Atoi()
		if  != nil {
			return , false
		}
		.exp = int32()
	}
	return , len() == 0
}

func ( string) (,  int32,  error) {
	if len() == 0 {
		return 0, 0, errors.New("empty string cannot be parsed as decimal")
	}

	// parse the string into components
	,  := parseDecimalComponents()
	if ! {
		return 0, 0, fmt.Errorf("the string '%s' is not a valid decimal number", )
	}

	// remove leading zeros
	 := strings.TrimLeft(.wholeDigits, "0")
	 := len(.fractDigits) + len()
	 = int32()

	if .hasExponent {
		 := .exp
		 = - + int32(len(.fractDigits))
	} else {
		 = int32(len(.fractDigits))
	}

	return
}