package sprig import ( "fmt" "math" "math/rand" "reflect" "strconv" "strings" ) // toFloat64 converts 64-bit floats func toFloat64(v any) float64 { if str, ok := v.(string); ok { iv, err := strconv.ParseFloat(str, 64) if err != nil { return 0 } return iv } val := reflect.Indirect(reflect.ValueOf(v)) switch val.Kind() { case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: return float64(val.Int()) case reflect.Uint8, reflect.Uint16, reflect.Uint32: return float64(val.Uint()) case reflect.Uint, reflect.Uint64: return float64(val.Uint()) case reflect.Float32, reflect.Float64: return val.Float() case reflect.Bool: if val.Bool() { return 1 } return 0 default: return 0 } } func toInt(v any) int { // It's not optimal. But I don't want duplicate toInt64 code. return int(toInt64(v)) } // toInt64 converts integer types to 64-bit integers func toInt64(v any) int64 { if str, ok := v.(string); ok { iv, err := strconv.ParseInt(str, 10, 64) if err != nil { return 0 } return iv } val := reflect.Indirect(reflect.ValueOf(v)) switch val.Kind() { case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: return val.Int() case reflect.Uint8, reflect.Uint16, reflect.Uint32: return int64(val.Uint()) case reflect.Uint, reflect.Uint64: tv := val.Uint() if tv <= math.MaxInt64 { return int64(tv) } // TODO: What is the sensible thing to do here? return math.MaxInt64 case reflect.Float32, reflect.Float64: return int64(val.Float()) case reflect.Bool: if val.Bool() { return 1 } return 0 default: return 0 } } func add1(i any) int64 { return toInt64(i) + 1 } func add(i ...any) int64 { var a int64 for _, b := range i { a += toInt64(b) } return a } func sub(a, b any) int64 { return toInt64(a) - toInt64(b) } func div(a, b any) int64 { return toInt64(a) / toInt64(b) } func mod(a, b any) int64 { return toInt64(a) % toInt64(b) } func mul(a any, v ...any) int64 { val := toInt64(a) for _, b := range v { val = val * toInt64(b) } return val } func randInt(min, max int) int { return rand.Intn(max-min) + min } func maxAsInt64(a any, i ...any) int64 { aa := toInt64(a) for _, b := range i { bb := toInt64(b) if bb > aa { aa = bb } } return aa } func maxAsFloat64(a any, i ...any) float64 { m := toFloat64(a) for _, b := range i { m = math.Max(m, toFloat64(b)) } return m } func minAsInt64(a any, i ...any) int64 { aa := toInt64(a) for _, b := range i { bb := toInt64(b) if bb < aa { aa = bb } } return aa } func minAsFloat64(a any, i ...any) float64 { m := toFloat64(a) for _, b := range i { m = math.Min(m, toFloat64(b)) } return m } func until(count int) []int { step := 1 if count < 0 { step = -1 } return untilStep(0, count, step) } func untilStep(start, stop, step int) []int { var v []int if step == 0 { return v } iterations := math.Abs(float64(stop)-float64(start)) / float64(step) if iterations > loopExecutionLimit { panic(fmt.Sprintf("too many iterations in untilStep; max allowed is %d, got %f", loopExecutionLimit, iterations)) } if stop < start { if step >= 0 { return v } for i := start; i > stop; i += step { v = append(v, i) } return v } if step <= 0 { return v } for i := start; i < stop; i += step { v = append(v, i) } return v } func floor(a any) float64 { return math.Floor(toFloat64(a)) } func ceil(a any) float64 { return math.Ceil(toFloat64(a)) } func round(a any, p int, rOpt ...float64) float64 { roundOn := .5 if len(rOpt) > 0 { roundOn = rOpt[0] } val := toFloat64(a) places := toFloat64(p) var round float64 pow := math.Pow(10, places) digit := pow * val _, div := math.Modf(digit) if div >= roundOn { round = math.Ceil(digit) } else { round = math.Floor(digit) } return round / pow } // converts unix octal to decimal func toDecimal(v any) int64 { result, err := strconv.ParseInt(fmt.Sprint(v), 8, 64) if err != nil { return 0 } return result } func atoi(a string) int { i, _ := strconv.Atoi(a) return i } func seq(params ...int) string { increment := 1 switch len(params) { case 0: return "" case 1: start := 1 end := params[0] if end < start { increment = -1 } return intArrayToString(untilStep(start, end+increment, increment), " ") case 3: start := params[0] end := params[2] step := params[1] if end < start { increment = -1 if step > 0 { return "" } } return intArrayToString(untilStep(start, end+increment, step), " ") case 2: start := params[0] end := params[1] step := 1 if end < start { step = -1 } return intArrayToString(untilStep(start, end+step, step), " ") default: return "" } } func intArrayToString(slice []int, delimiter string) string { return strings.Trim(strings.Join(strings.Fields(fmt.Sprint(slice)), delimiter), "[]") }