func (t *tupleLabels) MapKey() (string, error) {
	t.SortBtKey()
	b, err := json.Marshal(t)
	if err != nil {
		return "", err
	}
	return string(b), nil
}
MapKey gets a string key that can be used as a map key.
package data
import (
	"encoding/json"
	"fmt"
	"sort"
	"strconv"
	"time"
)
type TimeSeriesType int
const (
	TimeSeriesTypeNot TimeSeriesType = iota
	TimeSeriesTypeLong
	TimeSeriesTypeWide
)
type FillMode int
const (
	FillModePrevious FillMode = iota
	FillModeNull
	FillModeValue
)
type FillMissing struct {
	Mode  FillMode
	Value float64
}
func (t TimeSeriesType) String() string {
	switch t {
	case TimeSeriesTypeLong:
		return "long"
	case TimeSeriesTypeWide:
		return "wide"
	}
	return "not"
}
func (f *Frame) TimeSeriesSchema() (tsSchema TimeSeriesSchema) {
	tsSchema.Type = TimeSeriesTypeNot
	if f.Fields == nil || len(f.Fields) == 0 {
		return
	}
	nonValueIndices := make(map[int]struct{})
	timeIndices := f.TypeIndices(FieldTypeTime, FieldTypeNullableTime)
	if len(timeIndices) == 0 {
		return
	}
	tsSchema.TimeIndex = timeIndices[0]
	nonValueIndices[tsSchema.TimeIndex] = struct{}{}
	tsSchema.TimeIsNullable = f.Fields[tsSchema.TimeIndex].Nullable()
	tsSchema.FactorIndices = f.TypeIndices(FieldTypeString, FieldTypeNullableString, FieldTypeBool, FieldTypeNullableBool)
	for _, factorIdx := range tsSchema.FactorIndices {
		nonValueIndices[factorIdx] = struct{}{}
	}
	for i := range f.Fields {
		if _, ok := nonValueIndices[i]; ok {
			continue
		}
		tsSchema.ValueIndices = append(tsSchema.ValueIndices, i)
	}
	if len(tsSchema.ValueIndices) == 0 {
		return
	}
	if len(tsSchema.FactorIndices) == 0 {
		tsSchema.Type = TimeSeriesTypeWide
		return
	}
	tsSchema.Type = TimeSeriesTypeLong
	return tsSchema
}
func float64ToType(val float64, ftype FieldType) (interface{}, error) {
	switch ftype {
	case FieldTypeInt8:
		return int8(val), nil
	case FieldTypeNullableInt8:
		c := int8(val)
		return &c, nil
	case FieldTypeInt16:
		return int16(val), nil
	case FieldTypeNullableInt16:
		c := int16(val)
		return &c, nil
	case FieldTypeInt32:
		return int32(val), nil
	case FieldTypeNullableInt32:
		c := int32(val)
		return &c, nil
	case FieldTypeInt64:
		return int64(val), nil
	case FieldTypeNullableInt64:
		c := int64(val)
		return &c, nil
	case FieldTypeUint8:
		return uint8(val), nil
	case FieldTypeNullableUint8:
		c := uint8(val)
		return &c, nil
	case FieldTypeUint16:
		return uint16(val), nil
	case FieldTypeNullableUint16:
		c := uint16(val)
		return &c, nil
	case FieldTypeUint32:
		return uint32(val), nil
	case FieldTypeNullableUint32:
		c := uint32(val)
		return &c, nil
	case FieldTypeUint64:
		return uint64(val), nil
	case FieldTypeNullableUint64:
		c := uint64(val)
		return &c, nil
	case FieldTypeFloat32:
		return float32(val), nil
	case FieldTypeNullableFloat32:
		c := float32(val)
		return &c, nil
	case FieldTypeFloat64:
		return val, nil
	case FieldTypeNullableFloat64:
		return &val, nil
	}
	return val, fmt.Errorf("no numeric value")
}
func GetMissing(fillMissing *FillMissing, field *Field, previousRowIdx int) (interface{}, error) {
	if fillMissing == nil {
		return nil, fmt.Errorf("fill missing is disabled")
	}
	var fillVal interface{}
	switch fillMissing.Mode {
	case FillModeNull:
	case FillModeValue:
		convertedVal, err := float64ToType(fillMissing.Value, field.Type())
		if err != nil {
			return nil, err
		}
		fillVal = convertedVal
	case FillModePrevious:
		if previousRowIdx >= 0 {
			fillVal = field.At(previousRowIdx)
		}
	}
	return fillVal, nil
}
func LongToWide(longFrame *Frame, fillMissing *FillMissing) (*Frame, error) {
	tsSchema := longFrame.TimeSeriesSchema()
	if tsSchema.Type != TimeSeriesTypeLong {
		return nil, fmt.Errorf("can not convert to wide series, expected long format series input but got %s series", tsSchema.Type)
	}
	longLen, err := longFrame.RowLen()
	if err != nil {
		return nil, err
	}
	if longLen == 0 {
		return nil, fmt.Errorf("can not convert to wide series, input fields have no rows")
	}
	wideFrame := NewFrame(longFrame.Name, NewField(longFrame.Fields[tsSchema.TimeIndex].Name, nil, []time.Time{}))
	wideFrame.Meta = longFrame.Meta
	sortKeys := make([]string, len(tsSchema.FactorIndices))
	for i, v := range tsSchema.FactorIndices {  
		sortKeys[i] = longFrame.Fields[v].Name
	}
	lastTime, err := timeAt(0, longFrame, tsSchema)  
	if err != nil {
		return nil, err
	}
	wideFrame.Fields[0].Append(lastTime)
	valueFactorToWideFieldIdx := make(map[int]map[string]int)  
	for _, i := range tsSchema.ValueIndices {                  
		valueFactorToWideFieldIdx[i] = make(map[string]int)
	}
	proc := longRowProcessor{
		lastTime:                  lastTime,
		wideFrame:                 wideFrame,
		longFrame:                 longFrame,
		tsSchema:                  tsSchema,
		fillMissing:               fillMissing,
		seenFactors:               map[string]struct{}{},
		valueFactorToWideFieldIdx: valueFactorToWideFieldIdx,
	}
	for longRowIdx := 0; longRowIdx < longLen; longRowIdx++ {  
		if err := proc.process(longRowIdx); err != nil {
			return nil, err
		}
	}
	err = SortWideFrameFields(wideFrame, sortKeys...)
	if err != nil {
		return nil, err
	}
	if wideFrame.Meta == nil {
		wideFrame.Meta = &FrameMeta{}
	}
	wideFrame.Meta.Type = FrameTypeTimeSeriesWide
	return wideFrame, nil
}
type longRowProcessor struct {
	lastTime            time.Time
	wideFrameRowCounter int
	wideFrame           *Frame
	longFrame           *Frame
	tsSchema            TimeSeriesSchema
	fillMissing         *FillMissing
	seenFactors map[string]struct{}
	valueFactorToWideFieldIdx map[int]map[string]int
}
func (p *longRowProcessor) process(longRowIdx int) error {
	currentTime, err := timeAt(longRowIdx, p.longFrame, p.tsSchema)
	if err != nil {
		return err
	}
	if currentTime.After(p.lastTime) {  
		p.wideFrameRowCounter++
		p.lastTime = currentTime
		for wideFrameIdx, field := range p.wideFrame.Fields {
			field.Extend(1)
			if wideFrameIdx == 0 {
				p.wideFrame.Set(wideFrameIdx, p.wideFrameRowCounter, currentTime)
				continue
			}
			fillVal, err := GetMissing(p.fillMissing, field, p.wideFrameRowCounter-1)
			if err == nil {
				p.wideFrame.Set(wideFrameIdx, p.wideFrameRowCounter, fillVal)
			}
		}
	}
	if currentTime.Before(p.lastTime) {
		return fmt.Errorf("long series must be sorted ascending by time to be converted")
	}
	sliceKey := make(tupleLabels, len(p.tsSchema.FactorIndices))  
	namedKey := make(tupleLabels, len(p.tsSchema.FactorIndices))  
	for i, factorLongFieldIdx := range p.tsSchema.FactorIndices {
		val, _ := p.longFrame.ConcreteAt(factorLongFieldIdx, longRowIdx)
		var strVal string
		switch v := val.(type) {
		case string:
			strVal = v
		case bool:
			if v {
				strVal = "true"
			} else {
				strVal = "false"
			}
		default:
			return fmt.Errorf(
				"unexpected type, want a string or bool but got type %T for '%v'", val, val)
		}
		sliceKey[i] = tupleLabel{strconv.FormatInt(int64(factorLongFieldIdx), 10), strVal}
		namedKey[i] = tupleLabel{p.longFrame.Fields[factorLongFieldIdx].Name, strVal}
	}
	factorKey, err := sliceKey.MapKey()
	if err != nil {
		return err
	}
	if _, ok := p.seenFactors[factorKey]; !ok {
		currentFieldLen := len(p.wideFrame.Fields)  
		p.seenFactors[factorKey] = struct{}{}
		for offset, longFieldIdx := range p.tsSchema.ValueIndices {
			labels, err := tupleLablesToLabels(namedKey)
			if err != nil {
				return err
			}
			longField := p.longFrame.Fields[p.tsSchema.ValueIndices[offset]]
			newWideField := NewFieldFromFieldType(longField.Type(), p.wideFrameRowCounter+1)
			if p.fillMissing != nil && p.fillMissing.Mode != FillModeValue {
				newWideField = NewFieldFromFieldType(longField.Type().NullableType(), p.wideFrameRowCounter+1)
			}
			newWideField.Name, newWideField.Labels = longField.Name, labels
			p.wideFrame.Fields = append(p.wideFrame.Fields, newWideField)
			fillVal, err := GetMissing(p.fillMissing, newWideField, p.wideFrameRowCounter-1)
			if err == nil {
				for i := 0; i < p.wideFrameRowCounter; i++ {
					p.wideFrame.Set(currentFieldLen+offset, i, fillVal)
				}
			}
			p.valueFactorToWideFieldIdx[longFieldIdx][factorKey] = currentFieldLen + offset
		}
	}
	for _, longFieldIdx := range p.tsSchema.ValueIndices {
		wideFieldIdx := p.valueFactorToWideFieldIdx[longFieldIdx][factorKey]
		if p.wideFrame.Fields[wideFieldIdx].Nullable() && !p.longFrame.Fields[longFieldIdx].Nullable() {
			p.wideFrame.SetConcrete(wideFieldIdx, p.wideFrameRowCounter, p.longFrame.CopyAt(longFieldIdx, longRowIdx))
			continue
		}
		p.wideFrame.Set(wideFieldIdx, p.wideFrameRowCounter, p.longFrame.CopyAt(longFieldIdx, longRowIdx))
	}
	return nil
}
func timeAt(idx int, longFrame *Frame, tsSchema TimeSeriesSchema) (time.Time, error) {  
	val, ok := longFrame.ConcreteAt(tsSchema.TimeIndex, idx)
	if !ok {
		return time.Time{}, fmt.Errorf("can not convert to wide series, input has null time values")
	}
	return val.(time.Time), nil
}
func WideToLong(wideFrame *Frame) (*Frame, error) {
	tsSchema := wideFrame.TimeSeriesSchema()
	if tsSchema.Type != TimeSeriesTypeWide {
		return nil, fmt.Errorf("can not convert to long series, expected wide format series input but got %s series", tsSchema.Type)
	}
	wideLen, err := wideFrame.RowLen()
	if err != nil {
		return nil, err
	} else if wideLen == 0 {
		return nil, fmt.Errorf("can not convert to long series, input fields have no rows")
	}
	var uniqueValueNames []string                         
	uniqueValueNamesToType := make(map[string]FieldType)  
	uniqueLabelKeys := make(map[string]struct{})          
	labelKeyToWideIndices := make(map[string][]int)       
	for _, vIdx := range tsSchema.ValueIndices {
		wideField := wideFrame.Fields[vIdx]
		if pType, ok := uniqueValueNamesToType[wideField.Name]; ok {
			if wideField.Type() != pType {
				return nil, fmt.Errorf("two fields in input frame may not have the same name but different types, field name %s has type %s but also type %s and field idx %v", wideField.Name, pType, wideField.Type(), vIdx)
			}
		} else {
			uniqueValueNamesToType[wideField.Name] = wideField.Type()
			uniqueValueNames = append(uniqueValueNames, wideField.Name)
		}
		tKey, err := labelsTupleKey(wideField.Labels)  
		if err != nil {
			return nil, err
		}
		labelKeyToWideIndices[tKey] = append(labelKeyToWideIndices[tKey], vIdx)
		for k := range wideField.Labels {
			uniqueLabelKeys[k] = struct{}{}
		}
	}
	sort.Strings(uniqueValueNames)
	var sortedUniqueLabelKeys []string
	for k := range labelKeyToWideIndices {
		sortedUniqueLabelKeys = append(sortedUniqueLabelKeys, k)
	}
	sort.Strings(sortedUniqueLabelKeys)
	uniqueFactorNames := make([]string, 0, len(uniqueLabelKeys))
	for k := range uniqueLabelKeys {
		uniqueFactorNames = append(uniqueFactorNames, k)
	}
	sort.Strings(uniqueFactorNames)
	longFrame := NewFrame(wideFrame.Name,  
		NewField(wideFrame.Fields[tsSchema.TimeIndex].Name, nil, []time.Time{}))  
	longFrame.Meta = wideFrame.Meta
	i := 1
	valueNameToLongFieldIdx := map[string]int{}  
	for _, name := range uniqueValueNames {
		newWideField := NewFieldFromFieldType(uniqueValueNamesToType[name], 0)  
		newWideField.Name = name
		longFrame.Fields = append(longFrame.Fields, newWideField)
		valueNameToLongFieldIdx[name] = i
		i++
	}
	factorNameToLongFieldIdx := map[string]int{}  
	for _, name := range uniqueFactorNames {
		longFrame.Fields = append(longFrame.Fields, NewField(name, nil, []string{}))  
		factorNameToLongFieldIdx[name] = i
		i++
	}
	longFrameCounter := 0
	for wideRowIdx := 0; wideRowIdx < wideLen; wideRowIdx++ {  
		tm, ok := wideFrame.ConcreteAt(tsSchema.TimeIndex, wideRowIdx)
		if !ok {
			return nil, fmt.Errorf("time may not have nil values")
		}
		for _, labelKey := range sortedUniqueLabelKeys {
			longFrame.Extend(1)  
			longFrame.Set(0, longFrameCounter, tm)
			for i, wideFieldIdx := range labelKeyToWideIndices[labelKey] {
				wideField := wideFrame.Fields[wideFieldIdx]
				if i == 0 {
					for k, v := range wideField.Labels {
						longFrame.Set(factorNameToLongFieldIdx[k], longFrameCounter, v)
					}
				}
				longValueFieldIdx := valueNameToLongFieldIdx[wideField.Name]
				longFrame.Set(longValueFieldIdx, longFrameCounter, wideFrame.CopyAt(wideFieldIdx, wideRowIdx))
			}
			longFrameCounter++
		}
	}
	if longFrame.Meta == nil {
		longFrame.SetMeta(&FrameMeta{})
	}
	longFrame.Meta.Type = FrameTypeTimeSeriesLong
	return longFrame, nil
}
type TimeSeriesSchema struct {
	Type           TimeSeriesType  
	TimeIndex      int             
	TimeIsNullable bool            
	ValueIndices   []int           
	FactorIndices  []int           
}
type tupleLabels []tupleLabel
type tupleLabel [2]string
func tupleLablesToLabels(tuples tupleLabels) (Labels, error) {
	labels := make(map[string]string)
	for _, tuple := range tuples {
		if key, ok := labels[tuple[0]]; ok {
			return nil, fmt.Errorf("duplicate key '%v' in lables: %v", key, tuples)
		}
		labels[tuple[0]] = tuple[1]
	}
	return labels, nil
}