github.com/opentofu/opentofu@v1.7.1/internal/lang/funcs/number.go (about)

     1  // Copyright (c) The OpenTofu Authors
     2  // SPDX-License-Identifier: MPL-2.0
     3  // Copyright (c) 2023 HashiCorp, Inc.
     4  // SPDX-License-Identifier: MPL-2.0
     5  
     6  package funcs
     7  
     8  import (
     9  	"math"
    10  	"math/big"
    11  
    12  	"github.com/zclconf/go-cty/cty"
    13  	"github.com/zclconf/go-cty/cty/function"
    14  	"github.com/zclconf/go-cty/cty/gocty"
    15  )
    16  
    17  // LogFunc contructs a function that returns the logarithm of a given number in a given base.
    18  var LogFunc = function.New(&function.Spec{
    19  	Params: []function.Parameter{
    20  		{
    21  			Name: "num",
    22  			Type: cty.Number,
    23  		},
    24  		{
    25  			Name: "base",
    26  			Type: cty.Number,
    27  		},
    28  	},
    29  	Type:         function.StaticReturnType(cty.Number),
    30  	RefineResult: refineNotNull,
    31  	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
    32  		var num float64
    33  		if err := gocty.FromCtyValue(args[0], &num); err != nil {
    34  			return cty.UnknownVal(cty.String), err
    35  		}
    36  
    37  		var base float64
    38  		if err := gocty.FromCtyValue(args[1], &base); err != nil {
    39  			return cty.UnknownVal(cty.String), err
    40  		}
    41  
    42  		return cty.NumberFloatVal(math.Log(num) / math.Log(base)), nil
    43  	},
    44  })
    45  
    46  // PowFunc contructs a function that returns the logarithm of a given number in a given base.
    47  var PowFunc = function.New(&function.Spec{
    48  	Params: []function.Parameter{
    49  		{
    50  			Name: "num",
    51  			Type: cty.Number,
    52  		},
    53  		{
    54  			Name: "power",
    55  			Type: cty.Number,
    56  		},
    57  	},
    58  	Type:         function.StaticReturnType(cty.Number),
    59  	RefineResult: refineNotNull,
    60  	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
    61  		var num float64
    62  		if err := gocty.FromCtyValue(args[0], &num); err != nil {
    63  			return cty.UnknownVal(cty.String), err
    64  		}
    65  
    66  		var power float64
    67  		if err := gocty.FromCtyValue(args[1], &power); err != nil {
    68  			return cty.UnknownVal(cty.String), err
    69  		}
    70  
    71  		return cty.NumberFloatVal(math.Pow(num, power)), nil
    72  	},
    73  })
    74  
    75  // SignumFunc contructs a function that returns the closest whole number greater
    76  // than or equal to the given value.
    77  var SignumFunc = function.New(&function.Spec{
    78  	Params: []function.Parameter{
    79  		{
    80  			Name: "num",
    81  			Type: cty.Number,
    82  		},
    83  	},
    84  	Type:         function.StaticReturnType(cty.Number),
    85  	RefineResult: refineNotNull,
    86  	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
    87  		var num int
    88  		if err := gocty.FromCtyValue(args[0], &num); err != nil {
    89  			return cty.UnknownVal(cty.String), err
    90  		}
    91  		switch {
    92  		case num < 0:
    93  			return cty.NumberIntVal(-1), nil
    94  		case num > 0:
    95  			return cty.NumberIntVal(+1), nil
    96  		default:
    97  			return cty.NumberIntVal(0), nil
    98  		}
    99  	},
   100  })
   101  
   102  // ParseIntFunc contructs a function that parses a string argument and returns an integer of the specified base.
   103  var ParseIntFunc = function.New(&function.Spec{
   104  	Params: []function.Parameter{
   105  		{
   106  			Name:        "number",
   107  			Type:        cty.DynamicPseudoType,
   108  			AllowMarked: true,
   109  		},
   110  		{
   111  			Name:        "base",
   112  			Type:        cty.Number,
   113  			AllowMarked: true,
   114  		},
   115  	},
   116  
   117  	Type: func(args []cty.Value) (cty.Type, error) {
   118  		if !args[0].Type().Equals(cty.String) {
   119  			return cty.Number, function.NewArgErrorf(0, "first argument must be a string, not %s", args[0].Type().FriendlyName())
   120  		}
   121  		return cty.Number, nil
   122  	},
   123  	RefineResult: refineNotNull,
   124  
   125  	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
   126  		var numstr string
   127  		var base int
   128  		var err error
   129  
   130  		numArg, numMarks := args[0].Unmark()
   131  		if err = gocty.FromCtyValue(numArg, &numstr); err != nil {
   132  			return cty.UnknownVal(cty.String), function.NewArgError(0, err)
   133  		}
   134  
   135  		baseArg, baseMarks := args[1].Unmark()
   136  		if err = gocty.FromCtyValue(baseArg, &base); err != nil {
   137  			return cty.UnknownVal(cty.Number), function.NewArgError(1, err)
   138  		}
   139  
   140  		if base < 2 || base > 62 {
   141  			return cty.UnknownVal(cty.Number), function.NewArgErrorf(
   142  				1,
   143  				"base must be a whole number between 2 and 62 inclusive",
   144  			)
   145  		}
   146  
   147  		num, ok := (&big.Int{}).SetString(numstr, base)
   148  		if !ok {
   149  			return cty.UnknownVal(cty.Number), function.NewArgErrorf(
   150  				0,
   151  				"cannot parse %s as a base %s integer",
   152  				redactIfSensitive(numstr, numMarks),
   153  				redactIfSensitive(base, baseMarks),
   154  			)
   155  		}
   156  
   157  		parsedNum := cty.NumberVal((&big.Float{}).SetInt(num)).WithMarks(numMarks, baseMarks)
   158  
   159  		return parsedNum, nil
   160  	},
   161  })
   162  
   163  // Log returns returns the logarithm of a given number in a given base.
   164  func Log(num, base cty.Value) (cty.Value, error) {
   165  	return LogFunc.Call([]cty.Value{num, base})
   166  }
   167  
   168  // Pow returns the logarithm of a given number in a given base.
   169  func Pow(num, power cty.Value) (cty.Value, error) {
   170  	return PowFunc.Call([]cty.Value{num, power})
   171  }
   172  
   173  // Signum determines the sign of a number, returning a number between -1 and
   174  // 1 to represent the sign.
   175  func Signum(num cty.Value) (cty.Value, error) {
   176  	return SignumFunc.Call([]cty.Value{num})
   177  }
   178  
   179  // ParseInt parses a string argument and returns an integer of the specified base.
   180  func ParseInt(num cty.Value, base cty.Value) (cty.Value, error) {
   181  	return ParseIntFunc.Call([]cty.Value{num, base})
   182  }