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 }