github.com/coveo/gotemplate@v2.7.7+incompatible/template/math_utilities.go (about) 1 package template 2 3 import ( 4 "fmt" 5 "math" 6 "strconv" 7 8 "github.com/coveo/gotemplate/collections" 9 ) 10 11 func toInt(value interface{}) int { 12 // We convert to the string representation to ensure that any type is converted to int 13 return must(strconv.Atoi(fmt.Sprintf("%v", value))).(int) 14 } 15 16 func toInt64(value interface{}) int64 { 17 // We convert to the string representation to ensure that any type is converted to int64 18 return must(strconv.ParseInt(fmt.Sprintf("%v", value), 10, 64)).(int64) 19 } 20 21 func toUnsignedInteger(value interface{}) uint64 { 22 // We convert to the string representation to ensure that any type is converted to uint64 23 return must(strconv.ParseUint(fmt.Sprintf("%v", value), 10, 64)).(uint64) 24 } 25 26 func toFloat(value interface{}) float64 { 27 // We convert to the string representation to ensure that any type is converted to float64 28 return must(strconv.ParseFloat(fmt.Sprintf("%v", value), 64)).(float64) 29 } 30 31 func toListOfFloats(values iList) (result iList, err error) { 32 if values == nil { 33 return collections.CreateList(), nil 34 } 35 values = convertArgs(nil, values.AsArray()...) 36 result = values.Clone() 37 defer func() { 38 if err = trapError(err, recover()); err != nil { 39 result = nil 40 } 41 }() 42 for i := range result.AsArray() { 43 result.Set(i, toFloat(result.Get(i))) 44 } 45 return 46 } 47 48 func asFloats(values iList) ([]float64, error) { 49 result, err := toListOfFloats(values) 50 if err != nil { 51 return nil, err 52 } 53 return mustAsFloats(result), nil 54 } 55 56 func mustAsFloats(values iList) (result []float64) { 57 result = make([]float64, values.Len()) 58 for i := range result { 59 result[i] = values.Get(i).(float64) 60 } 61 return 62 } 63 64 func process(arg, handler interface{}) (r interface{}, err error) { 65 defer func() { err = trapError(err, recover()) }() 66 arguments := convertArgs(arg) 67 if arguments.Len() == 0 { 68 return 69 } 70 argArray := arguments.AsArray() 71 switch len(argArray) { 72 case 0: 73 r = 0 74 case 1: 75 r = execute(argArray[0], handler) 76 default: 77 result := arguments.Clone() 78 for i := range result.AsArray() { 79 result.Set(i, execute(result.Get(i), handler)) 80 } 81 r = result 82 } 83 return 84 } 85 86 func processFloat(arg interface{}, handler func(float64) float64) (r interface{}, err error) { 87 return process(arg, handler) 88 } 89 90 func processFloat2(a, b interface{}, handler func(float64, float64) float64) (r interface{}, err error) { 91 return processFloat(a, func(a float64) float64 { 92 return handler(a, toFloat(b)) 93 }) 94 } 95 96 func execute(arg, handler interface{}) interface{} { 97 switch handler := handler.(type) { 98 case func(float64) float64: 99 return simplify(handler(toFloat(arg))) 100 case func(interface{}) interface{}: 101 return handler(arg) 102 case func(float64) (float64, float64): 103 r1, r2 := handler(toFloat(arg)) 104 return []interface{}{r1, r2} 105 default: 106 panic(fmt.Errorf("Unknown handler function %v", handler)) 107 } 108 } 109 110 func convertArgs(arg1 interface{}, args ...interface{}) (result collections.IGenericList) { 111 if arg1 == nil { 112 // There is no first argument, so we isolate it from the other args 113 if len(args) == 0 { 114 return collections.CreateList() 115 } 116 arg1, args = args[0], args[1:] 117 } 118 if len(args) == 0 { 119 // There is a single argument, we try to convert it into a list 120 return collections.AsList(arg1) 121 } 122 123 if list, err := collections.TryAsList(arg1); err == nil { 124 return list.Create(0, len(args)+1).Append(arg1).Append(args...) 125 } 126 return collections.NewList(arg1).Append(args...) 127 } 128 129 func simplify(value float64) interface{} { 130 return iif(math.Floor(value) == value, int64(value), value) 131 } 132 133 func compareNumerics(values []interface{}, useMinFunc bool) interface{} { 134 if len(values) == 0 { 135 return nil 136 } 137 numerics, err := asFloats(collections.AsList(values)) 138 if err != nil { 139 return compareStrings(values, useMinFunc) 140 } 141 result := numerics[0] 142 comp := iif(useMinFunc, math.Min, math.Max).(func(a, b float64) float64) 143 for _, value := range numerics[1:] { 144 result = comp(result, value) 145 } 146 return simplify(result) 147 } 148 149 func compareStrings(values []interface{}, useMinFunc bool) (result string) { 150 sa := collections.ToStrings(values) 151 result = sa[0] 152 for _, value := range sa[1:] { 153 if (useMinFunc && value < result) || (!useMinFunc && value > result) { 154 result = value 155 } 156 } 157 return result 158 } 159 160 func generateNumericArray(limit bool, params ...interface{}) (result collections.IGenericList, err error) { 161 defer func() { err = trapError(err, recover()) }() 162 163 var start, stop float64 164 var step float64 = 1 165 var precision int 166 switch len(params) { 167 case 1: 168 start = float64(iif(limit, 1, 0).(int)) 169 stop = toFloat(params[0]) 170 case 3: 171 step = math.Abs(toFloat(params[2])) 172 _, frac := collections.Split2(fmt.Sprintf("%g", step), ".") 173 precision = len(frac) 174 fallthrough 175 case 2: 176 start = toFloat(params[0]) 177 stop = toFloat(params[1]) 178 default: 179 return nil, fmt.Errorf("Invalid arguments, must be start [stop] [step]") 180 } 181 if step == 0 { 182 return nil, fmt.Errorf("Step cannot be zero") 183 } 184 array := make([]interface{}, 0, int64(math.Abs(stop-start))) 185 forward := stop > start 186 if !forward { 187 step = -step 188 } 189 for current := start; (forward && current <= stop || !forward && current >= stop) && (limit || current != stop); { 190 current = sprigRound(current, precision) 191 array = append(array, simplify(current)) 192 current += step 193 } 194 result = collections.AsList(array) 195 return 196 }