package pgtype

import (
	
	
	
	
	
	
	
	

	
)

// PostgreSQL internal numeric storage uses 16-bit "digits" with base of 10,000
const nbase = 10000

const (
	pgNumericNaN     = 0x00000000c0000000
	pgNumericNaNSign = 0xc000

	pgNumericPosInf     = 0x00000000d0000000
	pgNumericPosInfSign = 0xd000

	pgNumericNegInf     = 0x00000000f0000000
	pgNumericNegInfSign = 0xf000
)

var big0 *big.Int = big.NewInt(0)
var big1 *big.Int = big.NewInt(1)
var big10 *big.Int = big.NewInt(10)
var big100 *big.Int = big.NewInt(100)
var big1000 *big.Int = big.NewInt(1000)

var bigNBase *big.Int = big.NewInt(nbase)
var bigNBaseX2 *big.Int = big.NewInt(nbase * nbase)
var bigNBaseX3 *big.Int = big.NewInt(nbase * nbase * nbase)
var bigNBaseX4 *big.Int = big.NewInt(nbase * nbase * nbase * nbase)

type NumericScanner interface {
	ScanNumeric(v Numeric) error
}

type NumericValuer interface {
	NumericValue() (Numeric, error)
}

type Numeric struct {
	Int              *big.Int
	Exp              int32
	NaN              bool
	InfinityModifier InfinityModifier
	Valid            bool
}

func ( *Numeric) ( Numeric) error {
	* = 
	return nil
}

func ( Numeric) () (Numeric, error) {
	return , nil
}

func ( Numeric) () (Float8, error) {
	if !.Valid {
		return Float8{}, nil
	} else if .NaN {
		return Float8{Float64: math.NaN(), Valid: true}, nil
	} else if .InfinityModifier == Infinity {
		return Float8{Float64: math.Inf(1), Valid: true}, nil
	} else if .InfinityModifier == NegativeInfinity {
		return Float8{Float64: math.Inf(-1), Valid: true}, nil
	}

	 := make([]byte, 0, 32)

	if .Int == nil {
		 = append(, '0')
	} else {
		 = append(, .Int.String()...)
	}
	 = append(, 'e')
	 = append(, strconv.FormatInt(int64(.Exp), 10)...)

	,  := strconv.ParseFloat(string(), 64)
	if  != nil {
		return Float8{}, 
	}

	return Float8{Float64: , Valid: true}, nil
}

func ( *Numeric) ( Int8) error {
	if !.Valid {
		* = Numeric{}
		return nil
	}

	* = Numeric{Int: big.NewInt(.Int64), Valid: true}
	return nil
}

func ( Numeric) () (Int8, error) {
	if !.Valid {
		return Int8{}, nil
	}

	,  := .toBigInt()
	if  != nil {
		return Int8{}, 
	}

	if !.IsInt64() {
		return Int8{}, fmt.Errorf("cannot convert %v to int64", )
	}

	return Int8{Int64: .Int64(), Valid: true}, nil
}

func ( *Numeric) () (*big.Int, error) {
	if .Exp == 0 {
		return .Int, nil
	}

	 := &big.Int{}
	.Set(.Int)
	if .Exp > 0 {
		 := &big.Int{}
		.Exp(big10, big.NewInt(int64(.Exp)), nil)
		.Mul(, )
		return , nil
	}

	 := &big.Int{}
	.Exp(big10, big.NewInt(int64(-.Exp)), nil)
	 := &big.Int{}
	.DivMod(, , )
	if .Cmp(big0) != 0 {
		return nil, fmt.Errorf("cannot convert %v to integer", )
	}
	return , nil
}

func parseNumericString( string) ( *big.Int,  int32,  error) {
	 := strings.IndexByte(, '.')

	if  == -1 {
		for len() > 1 && [len()-1] == '0' && [len()-2] != '-' {
			 = [:len()-1]
			++
		}
	} else {
		 = int32(-(len() -  - 1))
		 = [:] + [+1:]
	}

	 := &big.Int{}
	if ,  := .SetString(, 10); ! {
		return nil, 0, fmt.Errorf("%s is not a number", )
	}

	return , , nil
}

func nbaseDigitsToInt64( []byte) ( int64, ,  int) {
	 := len() / 2
	if  > 4 {
		 = 4
	}

	 := 0

	for  := 0;  < ; ++ {
		if  > 0 {
			 *= nbase
		}
		 += int64(binary.BigEndian.Uint16([:]))
		 += 2
	}

	return , , 
}

// Scan implements the database/sql Scanner interface.
func ( *Numeric) ( any) error {
	if  == nil {
		* = Numeric{}
		return nil
	}

	switch src := .(type) {
	case string:
		return scanPlanTextAnyToNumericScanner{}.Scan([]byte(), )
	}

	return fmt.Errorf("cannot scan %T", )
}

// Value implements the database/sql/driver Valuer interface.
func ( Numeric) () (driver.Value, error) {
	if !.Valid {
		return nil, nil
	}

	,  := NumericCodec{}.PlanEncode(nil, 0, TextFormatCode, ).Encode(, nil)
	if  != nil {
		return nil, 
	}
	return string(), 
}

func ( Numeric) () ([]byte, error) {
	if !.Valid {
		return []byte("null"), nil
	}

	if .NaN {
		return []byte(`"NaN"`), nil
	}

	return .numberTextBytes(), nil
}

func ( *Numeric) ( []byte) error {
	if bytes.Equal(, []byte(`null`)) {
		* = Numeric{}
		return nil
	}
	if bytes.Equal(, []byte(`"NaN"`)) {
		* = Numeric{NaN: true, Valid: true}
		return nil
	}
	return scanPlanTextAnyToNumericScanner{}.Scan(, )
}

// numberString returns a string of the number. undefined if NaN, infinite, or NULL
func ( Numeric) () []byte {
	 := .Int.String()

	 := &bytes.Buffer{}

	if len() > 0 && [:1] == "-" {
		 = [1:]
		.WriteByte('-')
	}

	 := int(.Exp)
	if  > 0 {
		.WriteString()
		for  := 0;  < ; ++ {
			.WriteByte('0')
		}
	} else if  < 0 {
		if len() <= - {
			.WriteString("0.")
			 := - - len()
			for  := 0;  < ; ++ {
				.WriteByte('0')
			}
			.WriteString()
		} else if len() > - {
			 := len() + 
			.WriteString([:])
			.WriteByte('.')
			.WriteString([:])
		}
	} else {
		.WriteString()
	}

	return .Bytes()
}

type NumericCodec struct{}

func (NumericCodec) ( int16) bool {
	return  == TextFormatCode ||  == BinaryFormatCode
}

func (NumericCodec) () int16 {
	return BinaryFormatCode
}

func (NumericCodec) ( *Map,  uint32,  int16,  any) EncodePlan {
	switch  {
	case BinaryFormatCode:
		switch .(type) {
		case NumericValuer:
			return encodePlanNumericCodecBinaryNumericValuer{}
		case Float64Valuer:
			return encodePlanNumericCodecBinaryFloat64Valuer{}
		case Int64Valuer:
			return encodePlanNumericCodecBinaryInt64Valuer{}
		}
	case TextFormatCode:
		switch .(type) {
		case NumericValuer:
			return encodePlanNumericCodecTextNumericValuer{}
		case Float64Valuer:
			return encodePlanNumericCodecTextFloat64Valuer{}
		case Int64Valuer:
			return encodePlanNumericCodecTextInt64Valuer{}
		}
	}

	return nil
}

type encodePlanNumericCodecBinaryNumericValuer struct{}

func (encodePlanNumericCodecBinaryNumericValuer) ( any,  []byte) ( []byte,  error) {
	,  := .(NumericValuer).NumericValue()
	if  != nil {
		return nil, 
	}

	return encodeNumericBinary(, )
}

type encodePlanNumericCodecBinaryFloat64Valuer struct{}

func (encodePlanNumericCodecBinaryFloat64Valuer) ( any,  []byte) ( []byte,  error) {
	,  := .(Float64Valuer).Float64Value()
	if  != nil {
		return nil, 
	}

	if !.Valid {
		return nil, nil
	}

	if math.IsNaN(.Float64) {
		return encodeNumericBinary(Numeric{NaN: true, Valid: true}, )
	} else if math.IsInf(.Float64, 1) {
		return encodeNumericBinary(Numeric{InfinityModifier: Infinity, Valid: true}, )
	} else if math.IsInf(.Float64, -1) {
		return encodeNumericBinary(Numeric{InfinityModifier: NegativeInfinity, Valid: true}, )
	}
	, ,  := parseNumericString(strconv.FormatFloat(.Float64, 'f', -1, 64))
	if  != nil {
		return nil, 
	}

	return encodeNumericBinary(Numeric{Int: , Exp: , Valid: true}, )
}

type encodePlanNumericCodecBinaryInt64Valuer struct{}

func (encodePlanNumericCodecBinaryInt64Valuer) ( any,  []byte) ( []byte,  error) {
	,  := .(Int64Valuer).Int64Value()
	if  != nil {
		return nil, 
	}

	if !.Valid {
		return nil, nil
	}

	return encodeNumericBinary(Numeric{Int: big.NewInt(.Int64), Valid: true}, )
}

func encodeNumericBinary( Numeric,  []byte) ( []byte,  error) {
	if !.Valid {
		return nil, nil
	}

	if .NaN {
		 = pgio.AppendUint64(, pgNumericNaN)
		return , nil
	} else if .InfinityModifier == Infinity {
		 = pgio.AppendUint64(, pgNumericPosInf)
		return , nil
	} else if .InfinityModifier == NegativeInfinity {
		 = pgio.AppendUint64(, pgNumericNegInf)
		return , nil
	}

	var  int16
	if .Int.Cmp(big0) < 0 {
		 = 16384
	}

	 := &big.Int{}
	 := &big.Int{}
	 := &big.Int{}
	 := &big.Int{}
	.Abs(.Int)

	// Normalize absInt and exp to where exp is always a multiple of 4. This makes
	// converting to 16-bit base 10,000 digits easier.
	var  int32
	switch .Exp % 4 {
	case 1, -3:
		 = .Exp - 1
		.Mul(, big10)
	case 2, -2:
		 = .Exp - 2
		.Mul(, big100)
	case 3, -1:
		 = .Exp - 3
		.Mul(, big1000)
	default:
		 = .Exp
	}

	if  < 0 {
		 := &big.Int{}
		.Exp(big10, big.NewInt(int64(-)), nil)
		.DivMod(, , )
		.Add(, )
	} else {
		 = 
	}

	var ,  []int16

	for .Cmp(big0) != 0 {
		.DivMod(, bigNBase, )
		 = append(, int16(.Int64()))
	}

	if .Cmp(big0) != 0 {
		for .Cmp(big1) != 0 {
			.DivMod(, bigNBase, )
			 = append(, int16(.Int64()))
		}
	}

	 = pgio.AppendInt16(, int16(len()+len()))

	var  int16
	if len() > 0 {
		 = int16(len() - 1)
		if  > 0 {
			 += int16( / 4)
		}
	} else {
		 = int16(/4) - 1 + int16(len())
	}
	 = pgio.AppendInt16(, )

	 = pgio.AppendInt16(, )

	var  int16
	if .Exp < 0 {
		 = int16(-.Exp)
	}
	 = pgio.AppendInt16(, )

	for  := len() - 1;  >= 0; -- {
		 = pgio.AppendInt16(, [])
	}

	for  := len() - 1;  >= 0; -- {
		 = pgio.AppendInt16(, [])
	}

	return , nil
}

type encodePlanNumericCodecTextNumericValuer struct{}

func (encodePlanNumericCodecTextNumericValuer) ( any,  []byte) ( []byte,  error) {
	,  := .(NumericValuer).NumericValue()
	if  != nil {
		return nil, 
	}

	return encodeNumericText(, )
}

type encodePlanNumericCodecTextFloat64Valuer struct{}

func (encodePlanNumericCodecTextFloat64Valuer) ( any,  []byte) ( []byte,  error) {
	,  := .(Float64Valuer).Float64Value()
	if  != nil {
		return nil, 
	}

	if !.Valid {
		return nil, nil
	}

	if math.IsNaN(.Float64) {
		 = append(, "NaN"...)
	} else if math.IsInf(.Float64, 1) {
		 = append(, "Infinity"...)
	} else if math.IsInf(.Float64, -1) {
		 = append(, "-Infinity"...)
	} else {
		 = append(, strconv.FormatFloat(.Float64, 'f', -1, 64)...)
	}
	return , nil
}

type encodePlanNumericCodecTextInt64Valuer struct{}

func (encodePlanNumericCodecTextInt64Valuer) ( any,  []byte) ( []byte,  error) {
	,  := .(Int64Valuer).Int64Value()
	if  != nil {
		return nil, 
	}

	if !.Valid {
		return nil, nil
	}

	 = append(, strconv.FormatInt(.Int64, 10)...)
	return , nil
}

func encodeNumericText( Numeric,  []byte) ( []byte,  error) {
	if !.Valid {
		return nil, nil
	}

	if .NaN {
		 = append(, "NaN"...)
		return , nil
	} else if .InfinityModifier == Infinity {
		 = append(, "Infinity"...)
		return , nil
	} else if .InfinityModifier == NegativeInfinity {
		 = append(, "-Infinity"...)
		return , nil
	}

	 = append(, .numberTextBytes()...)

	return , nil
}

func (NumericCodec) ( *Map,  uint32,  int16,  any) ScanPlan {

	switch  {
	case BinaryFormatCode:
		switch .(type) {
		case NumericScanner:
			return scanPlanBinaryNumericToNumericScanner{}
		case Float64Scanner:
			return scanPlanBinaryNumericToFloat64Scanner{}
		case Int64Scanner:
			return scanPlanBinaryNumericToInt64Scanner{}
		case TextScanner:
			return scanPlanBinaryNumericToTextScanner{}
		}
	case TextFormatCode:
		switch .(type) {
		case NumericScanner:
			return scanPlanTextAnyToNumericScanner{}
		case Float64Scanner:
			return scanPlanTextAnyToFloat64Scanner{}
		case Int64Scanner:
			return scanPlanTextAnyToInt64Scanner{}
		}
	}

	return nil
}

type scanPlanBinaryNumericToNumericScanner struct{}

func (scanPlanBinaryNumericToNumericScanner) ( []byte,  any) error {
	 := ().(NumericScanner)

	if  == nil {
		return .ScanNumeric(Numeric{})
	}

	if len() < 8 {
		return fmt.Errorf("numeric incomplete %v", )
	}

	 := 0
	 := binary.BigEndian.Uint16([:])
	 += 2
	 := int16(binary.BigEndian.Uint16([:]))
	 += 2
	 := binary.BigEndian.Uint16([:])
	 += 2
	 := int16(binary.BigEndian.Uint16([:]))
	 += 2

	if  == pgNumericNaNSign {
		return .ScanNumeric(Numeric{NaN: true, Valid: true})
	} else if  == pgNumericPosInfSign {
		return .ScanNumeric(Numeric{InfinityModifier: Infinity, Valid: true})
	} else if  == pgNumericNegInfSign {
		return .ScanNumeric(Numeric{InfinityModifier: NegativeInfinity, Valid: true})
	}

	if  == 0 {
		return .ScanNumeric(Numeric{Int: big.NewInt(0), Valid: true})
	}

	if len([:]) < int()*2 {
		return fmt.Errorf("numeric incomplete %v", )
	}

	 := &big.Int{}

	for  := 0;  < int(+3)/4; ++ {
		, ,  := nbaseDigitsToInt64([:])
		 += 

		if  > 0 {
			var  *big.Int
			switch  {
			case 1:
				 = bigNBase
			case 2:
				 = bigNBaseX2
			case 3:
				 = bigNBaseX3
			case 4:
				 = bigNBaseX4
			default:
				return fmt.Errorf("invalid digitsRead: %d (this can't happen)", )
			}
			.Mul(, )
		}

		.Add(, big.NewInt())
	}

	 := (int32() - int32() + 1) * 4

	if  > 0 {
		 := int16(int32() - int32() - 1)
		 :=  * 4

		if  >  {
			 := int( - )
			for  := 0;  < ; ++ {
				.Mul(, big10)
				--
			}
		} else if  <  {
			 := int( - )
			for  := 0;  < ; ++ {
				.Div(, big10)
				++
			}
		}
	}

	 := &big.Int{}
	 := &big.Int{}
	if  >= 0 {
		for {
			.DivMod(, big10, )
			if .Cmp(big0) != 0 {
				break
			}
			.Set()
			++
		}
	}

	if  != 0 {
		.Neg()
	}

	return .ScanNumeric(Numeric{Int: , Exp: , Valid: true})
}

type scanPlanBinaryNumericToFloat64Scanner struct{}

func (scanPlanBinaryNumericToFloat64Scanner) ( []byte,  any) error {
	 := ().(Float64Scanner)

	if  == nil {
		return .ScanFloat64(Float8{})
	}

	var  Numeric

	 := scanPlanBinaryNumericToNumericScanner{}.Scan(, &)
	if  != nil {
		return 
	}

	,  := .Float64Value()
	if  != nil {
		return 
	}

	return .ScanFloat64()
}

type scanPlanBinaryNumericToInt64Scanner struct{}

func (scanPlanBinaryNumericToInt64Scanner) ( []byte,  any) error {
	 := ().(Int64Scanner)

	if  == nil {
		return .ScanInt64(Int8{})
	}

	var  Numeric

	 := scanPlanBinaryNumericToNumericScanner{}.Scan(, &)
	if  != nil {
		return 
	}

	,  := .toBigInt()
	if  != nil {
		return 
	}

	if !.IsInt64() {
		return fmt.Errorf("%v is out of range for int64", )
	}

	return .ScanInt64(Int8{Int64: .Int64(), Valid: true})
}

type scanPlanBinaryNumericToTextScanner struct{}

func (scanPlanBinaryNumericToTextScanner) ( []byte,  any) error {
	 := ().(TextScanner)

	if  == nil {
		return .ScanText(Text{})
	}

	var  Numeric

	 := scanPlanBinaryNumericToNumericScanner{}.Scan(, &)
	if  != nil {
		return 
	}

	,  := encodeNumericText(, nil)
	if  != nil {
		return 
	}

	return .ScanText(Text{String: string(), Valid: true})
}

type scanPlanTextAnyToNumericScanner struct{}

func (scanPlanTextAnyToNumericScanner) ( []byte,  any) error {
	 := ().(NumericScanner)

	if  == nil {
		return .ScanNumeric(Numeric{})
	}

	if string() == "NaN" {
		return .ScanNumeric(Numeric{NaN: true, Valid: true})
	} else if string() == "Infinity" {
		return .ScanNumeric(Numeric{InfinityModifier: Infinity, Valid: true})
	} else if string() == "-Infinity" {
		return .ScanNumeric(Numeric{InfinityModifier: NegativeInfinity, Valid: true})
	}

	, ,  := parseNumericString(string())
	if  != nil {
		return 
	}

	return .ScanNumeric(Numeric{Int: , Exp: , Valid: true})
}

func ( NumericCodec) ( *Map,  uint32,  int16,  []byte) (driver.Value, error) {
	if  == nil {
		return nil, nil
	}

	if  == TextFormatCode {
		return string(), nil
	}

	var  Numeric
	 := codecScan(, , , , , &)
	if  != nil {
		return nil, 
	}

	,  := .Encode(, TextFormatCode, , nil)
	if  != nil {
		return nil, 
	}
	return string(), nil
}

func ( NumericCodec) ( *Map,  uint32,  int16,  []byte) (any, error) {
	if  == nil {
		return nil, nil
	}

	var  Numeric
	 := codecScan(, , , , , &)
	if  != nil {
		return nil, 
	}
	return , nil
}