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  }