package dagro

import (
	
	
	
	
	
	
	
)

// Attrs is Dagro's equivalent of a JavaScript object used as a graph, node,
// or edge label by the verified Dagre profile.
type Attrs map[string]any

// Point is a point in the coordinate system produced by Layout.
type Point struct {
	X float64 `json:"x"`
	Y float64 `json:"y"`
}

func asAttrs( any) Attrs {
	if  == nil {
		return nil
	}
	,  := .(Attrs)
	if ! {
		panic(fmt.Sprintf("dagro: expected Attrs, got %T", ))
	}
	return 
}

func has( Attrs,  string) bool {
	,  := []
	return 
}

func number( any) float64 {
	switch n := .(type) {
	case float64:
		return 
	case float32:
		return float64()
	case int:
		return float64()
	case int8:
		return float64()
	case int16:
		return float64()
	case int32:
		return float64()
	case int64:
		return float64()
	case uint:
		return float64()
	case uint8:
		return float64()
	case uint16:
		return float64()
	case uint32:
		return float64()
	case uint64:
		return float64()
	case nil:
		return 0
	case bool:
		if  {
			return 1
		}
		return 0
	case string:
		 := strings.TrimSpace()
		if  == "" {
			return 0
		}
		if  == "Infinity" ||  == "+Infinity" {
			return math.Inf(1)
		}
		if  == "-Infinity" {
			return math.Inf(-1)
		}
		for ,  := range []struct {
			,  string
			         int
		}{
			{: "0b", : "0B", : 2},
			{: "0o", : "0O", : 8},
			{: "0x", : "0X", : 16},
		} {
			if strings.HasPrefix(, .) || strings.HasPrefix(, .) {
				return parseRadixNumber([2:], .)
			}
		}
		,  := strconv.ParseFloat(, 64)
		if  != nil && !errors.Is(, strconv.ErrRange) {
			return math.NaN()
		}
		// ParseFloat accepts spellings such as "Inf" that JavaScript Number
		// rejects. Exact "Infinity" spellings were handled above; an infinity
		// accompanied by ErrRange is instead a valid decimal overflow.
		if  == nil && math.IsInf(, 0) {
			return math.NaN()
		}
		return 
	default:
		panic(fmt.Sprintf("dagro: expected number, got %T", ))
	}
}

func parseRadixNumber( string,  int) float64 {
	if  == "" {
		return math.NaN()
	}
	for ,  := range  {
		 := -1
		switch {
		case '0' <=  &&  <= '9':
			 = int( - '0')
		case 'a' <=  &&  <= 'f':
			 = int(-'a') + 10
		case 'A' <=  &&  <= 'F':
			 = int(-'A') + 10
		}
		if  < 0 ||  >=  {
			return math.NaN()
		}
	}
	,  := new(big.Int).SetString(, )
	if ! {
		return math.NaN()
	}
	,  := new(big.Float).SetInt().Float64()
	return 
}

func num( Attrs,  string) float64 {
	,  := []
	if ! {
		return math.NaN()
	}
	return number()
}

func integer( Attrs,  string) int { return int(num(, )) }

func stringValue( Attrs,  string) string {
	,  := [].(string)
	return 
}

func boolValue( Attrs,  string) bool {
	,  := [].(bool)
	return 
}

func cloneAttrs( Attrs) Attrs {
	if  == nil {
		return nil
	}
	 := make(Attrs, len())
	for ,  := range  {
		[] = cloneValue()
	}
	return 
}

func cloneValue( any) any {
	switch x := .(type) {
	case Attrs:
		return cloneAttrs()
	case []string:
		return append([]string(nil), ...)
	case []Point:
		return append([]Point(nil), ...)
	case []Edge:
		return append([]Edge(nil), ...)
	case []Attrs:
		 := make([]Attrs, len())
		for  := range  {
			[] = cloneAttrs([])
		}
		return 
	case map[string]string:
		 := make(map[string]string, len())
		for ,  := range  {
			[] = 
		}
		return 
	case map[string]float64:
		 := make(map[string]float64, len())
		for ,  := range  {
			[] = 
		}
		return 
	case map[int]string:
		 := make(map[int]string, len())
		for ,  := range  {
			[] = 
		}
		return 
	default:
		return 
	}
}

func median( []float64) float64 {
	if len() == 0 {
		return math.NaN()
	}
	 := append([]float64(nil), ...)
	sort.Float64s()
	 := len() / 2
	if len()%2 == 1 {
		return []
	}
	return ([-1] + []) / 2
}