func interpolationFuncJoin() ast.Function {
	return ast.Function{
		ArgTypes:     []ast.Type{ast.TypeString},
		Variadic:     true,
		VariadicType: ast.TypeList,
		ReturnType:   ast.TypeString,
		Callback: func(args []interface{}) (interface{}, error) {
			var list []string
			if len(args) < 2 {
				return nil, fmt.Errorf("not enough arguments to join()")
			}
			for _, arg := range args[1:] {
				for _, part := range arg.([]ast.Variable) {
					if part.Type != ast.TypeString {
						return nil, fmt.Errorf(
							"only works on flat lists, this list contains elements of %s",
							part.Type.Printable())
					}
					list = append(list, part.Value.(string))
				}
			}
			return strings.Join(list, args[0].(string)), nil
		},
	}
}
interpolationFuncJoin implements the "join" function that allows
multi-variable values to be joined by some character.
package config
import (
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math"
	"net"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"time"
	"github.com/apparentlymart/go-cidr/cidr"
	"github.com/hashicorp/go-uuid"
	"github.com/hashicorp/hil"
	"github.com/hashicorp/hil/ast"
	"github.com/mitchellh/go-homedir"
)
func stringSliceToVariableValue(values []string) []ast.Variable {
	output := make([]ast.Variable, len(values))
	for index, value := range values {
		output[index] = ast.Variable{
			Type:  ast.TypeString,
			Value: value,
		}
	}
	return output
}
func listVariableValueToStringSlice(values []ast.Variable) ([]string, error) {
	output := make([]string, len(values))
	for index, value := range values {
		if value.Type != ast.TypeString {
			return []string{}, fmt.Errorf("list has non-string element (%T)", value.Type.String())
		}
		output[index] = value.Value.(string)
	}
	return output, nil
}
func Funcs() map[string]ast.Function {
	return map[string]ast.Function{
		"base64decode": interpolationFuncBase64Decode(),
		"base64encode": interpolationFuncBase64Encode(),
		"base64sha256": interpolationFuncBase64Sha256(),
		"ceil":         interpolationFuncCeil(),
		"cidrhost":     interpolationFuncCidrHost(),
		"cidrnetmask":  interpolationFuncCidrNetmask(),
		"cidrsubnet":   interpolationFuncCidrSubnet(),
		"coalesce":     interpolationFuncCoalesce(),
		"compact":      interpolationFuncCompact(),
		"concat":       interpolationFuncConcat(),
		"distinct":     interpolationFuncDistinct(),
		"element":      interpolationFuncElement(),
		"file":         interpolationFuncFile(),
		"floor":        interpolationFuncFloor(),
		"format":       interpolationFuncFormat(),
		"formatlist":   interpolationFuncFormatList(),
		"index":        interpolationFuncIndex(),
		"join":         interpolationFuncJoin(),
		"jsonencode":   interpolationFuncJSONEncode(),
		"length":       interpolationFuncLength(),
		"list":         interpolationFuncList(),
		"lower":        interpolationFuncLower(),
		"map":          interpolationFuncMap(),
		"max":          interpolationFuncMax(),
		"md5":          interpolationFuncMd5(),
		"merge":        interpolationFuncMerge(),
		"min":          interpolationFuncMin(),
		"pathexpand":   interpolationFuncPathExpand(),
		"uuid":         interpolationFuncUUID(),
		"replace":      interpolationFuncReplace(),
		"sha1":         interpolationFuncSha1(),
		"sha256":       interpolationFuncSha256(),
		"signum":       interpolationFuncSignum(),
		"slice":        interpolationFuncSlice(),
		"sort":         interpolationFuncSort(),
		"split":        interpolationFuncSplit(),
		"timestamp":    interpolationFuncTimestamp(),
		"title":        interpolationFuncTitle(),
		"trimspace":    interpolationFuncTrimSpace(),
		"upper":        interpolationFuncUpper(),
		"zipmap":       interpolationFuncZipMap(),
	}
}
func interpolationFuncList() ast.Function {
	return ast.Function{
		ArgTypes:     []ast.Type{},
		ReturnType:   ast.TypeList,
		Variadic:     true,
		VariadicType: ast.TypeAny,
		Callback: func(args []interface{}) (interface{}, error) {
			var outputList []ast.Variable
			for i, val := range args {
				switch v := val.(type) {
				case string:
					outputList = append(outputList, ast.Variable{Type: ast.TypeString, Value: v})
				case []ast.Variable:
					outputList = append(outputList, ast.Variable{Type: ast.TypeList, Value: v})
				case map[string]ast.Variable:
					outputList = append(outputList, ast.Variable{Type: ast.TypeMap, Value: v})
				default:
					return nil, fmt.Errorf("unexpected type %T for argument %d in list", v, i)
				}
			}
			if len(outputList) > 0 {
				firstType := outputList[0].Type
				for i, v := range outputList[1:] {
					if v.Type != firstType {
						return nil, fmt.Errorf("unexpected type %s for argument %d in list", v.Type, i+1)
					}
				}
			}
			return outputList, nil
		},
	}
}
func interpolationFuncMap() ast.Function {
	return ast.Function{
		ArgTypes:     []ast.Type{},
		ReturnType:   ast.TypeMap,
		Variadic:     true,
		VariadicType: ast.TypeAny,
		Callback: func(args []interface{}) (interface{}, error) {
			outputMap := make(map[string]ast.Variable)
			if len(args)%2 != 0 {
				return nil, fmt.Errorf("requires an even number of arguments, got %d", len(args))
			}
			var firstType *ast.Type
			for i := 0; i < len(args); i += 2 {
				key, ok := args[i].(string)
				if !ok {
					return nil, fmt.Errorf("argument %d represents a key, so it must be a string", i+1)
				}
				val := args[i+1]
				variable, err := hil.InterfaceToVariable(val)
				if err != nil {
					return nil, err
				}
				if firstType == nil {
					firstType = &variable.Type
				} else if variable.Type != *firstType {
					return nil, fmt.Errorf("all map values must have the same type, got %s then %s", firstType.Printable(), variable.Type.Printable())
				}
				if _, ok := outputMap[key]; ok {
					return nil, fmt.Errorf("argument %d is a duplicate key: %q", i+1, key)
				}
				outputMap[key] = variable
			}
			return outputMap, nil
		},
	}
}
func interpolationFuncCompact() ast.Function {
	return ast.Function{
		ArgTypes:   []ast.Type{ast.TypeList},
		ReturnType: ast.TypeList,
		Variadic:   false,
		Callback: func(args []interface{}) (interface{}, error) {
			inputList := args[0].([]ast.Variable)
			var outputList []string
			for _, val := range inputList {
				strVal, ok := val.Value.(string)
				if !ok {
					return nil, fmt.Errorf(
						"compact() may only be used with flat lists, this list contains elements of %s",
						val.Type.Printable())
				}
				if strVal == "" {
					continue
				}
				outputList = append(outputList, strVal)
			}
			return stringSliceToVariableValue(outputList), nil
		},
	}
}
func interpolationFuncCidrHost() ast.Function {
	return ast.Function{
		ArgTypes: []ast.Type{
			ast.TypeString,  
			ast.TypeInt,     
		},
		ReturnType: ast.TypeString,
		Variadic:   false,
		Callback: func(args []interface{}) (interface{}, error) {
			hostNum := args[1].(int)
			_, network, err := net.ParseCIDR(args[0].(string))
			if err != nil {
				return nil, fmt.Errorf("invalid CIDR expression: %s", err)
			}
			ip, err := cidr.Host(network, hostNum)
			if err != nil {
				return nil, err
			}
			return ip.String(), nil
		},
	}
}
func interpolationFuncCidrNetmask() ast.Function {
	return ast.Function{
		ArgTypes: []ast.Type{
			ast.TypeString,  
		},
		ReturnType: ast.TypeString,
		Variadic:   false,
		Callback: func(args []interface{}) (interface{}, error) {
			_, network, err := net.ParseCIDR(args[0].(string))
			if err != nil {
				return nil, fmt.Errorf("invalid CIDR expression: %s", err)
			}
			return net.IP(network.Mask).String(), nil
		},
	}
}
func interpolationFuncCidrSubnet() ast.Function {
	return ast.Function{
		ArgTypes: []ast.Type{
			ast.TypeString,  
			ast.TypeInt,     
			ast.TypeInt,     
		},
		ReturnType: ast.TypeString,
		Variadic:   false,
		Callback: func(args []interface{}) (interface{}, error) {
			extraBits := args[1].(int)
			subnetNum := args[2].(int)
			_, network, err := net.ParseCIDR(args[0].(string))
			if err != nil {
				return nil, fmt.Errorf("invalid CIDR expression: %s", err)
			}
			if extraBits > 32 {
				return nil, fmt.Errorf("may not extend prefix by more than 32 bits")
			}
			newNetwork, err := cidr.Subnet(network, extraBits, subnetNum)
			if err != nil {
				return nil, err
			}
			return newNetwork.String(), nil
		},
	}
}
func interpolationFuncCoalesce() ast.Function {
	return ast.Function{
		ArgTypes:     []ast.Type{ast.TypeString},
		ReturnType:   ast.TypeString,
		Variadic:     true,
		VariadicType: ast.TypeString,
		Callback: func(args []interface{}) (interface{}, error) {
			if len(args) < 2 {
				return nil, fmt.Errorf("must provide at least two arguments")
			}
			for _, arg := range args {
				argument := arg.(string)
				if argument != "" {
					return argument, nil
				}
			}
			return "", nil
		},
	}
}
func interpolationFuncConcat() ast.Function {
	return ast.Function{
		ArgTypes:     []ast.Type{ast.TypeList},
		ReturnType:   ast.TypeList,
		Variadic:     true,
		VariadicType: ast.TypeList,
		Callback: func(args []interface{}) (interface{}, error) {
			var outputList []ast.Variable
			for _, arg := range args {
				for _, v := range arg.([]ast.Variable) {
					switch v.Type {
					case ast.TypeString:
						outputList = append(outputList, v)
					case ast.TypeList:
						outputList = append(outputList, v)
					case ast.TypeMap:
						outputList = append(outputList, v)
					default:
						return nil, fmt.Errorf("concat() does not support lists of %s", v.Type.Printable())
					}
				}
			}
			if len(outputList) > 0 {
				firstType := outputList[0].Type
				for _, v := range outputList[1:] {
					if v.Type != firstType {
						return nil, fmt.Errorf("unexpected %s in list of %s", v.Type.Printable(), firstType.Printable())
					}
				}
			}
			return outputList, nil
		},
	}
}
func interpolationFuncFile() ast.Function {
	return ast.Function{
		ArgTypes:   []ast.Type{ast.TypeString},
		ReturnType: ast.TypeString,
		Callback: func(args []interface{}) (interface{}, error) {
			path, err := homedir.Expand(args[0].(string))
			if err != nil {
				return "", err
			}
			data, err := ioutil.ReadFile(path)
			if err != nil {
				return "", err
			}
			return string(data), nil
		},
	}
}
func interpolationFuncFormat() ast.Function {
	return ast.Function{
		ArgTypes:     []ast.Type{ast.TypeString},
		Variadic:     true,
		VariadicType: ast.TypeAny,
		ReturnType:   ast.TypeString,
		Callback: func(args []interface{}) (interface{}, error) {
			format := args[0].(string)
			return fmt.Sprintf(format, args[1:]...), nil
		},
	}
}
func interpolationFuncMax() ast.Function {
	return ast.Function{
		ArgTypes:     []ast.Type{ast.TypeFloat},
		ReturnType:   ast.TypeFloat,
		Variadic:     true,
		VariadicType: ast.TypeFloat,
		Callback: func(args []interface{}) (interface{}, error) {
			max := args[0].(float64)
			for i := 1; i < len(args); i++ {
				max = math.Max(max, args[i].(float64))
			}
			return max, nil
		},
	}
}
func interpolationFuncMin() ast.Function {
	return ast.Function{
		ArgTypes:     []ast.Type{ast.TypeFloat},
		ReturnType:   ast.TypeFloat,
		Variadic:     true,
		VariadicType: ast.TypeFloat,
		Callback: func(args []interface{}) (interface{}, error) {
			min := args[0].(float64)
			for i := 1; i < len(args); i++ {
				min = math.Min(min, args[i].(float64))
			}
			return min, nil
		},
	}
}
func interpolationFuncPathExpand() ast.Function {
	return ast.Function{
		ArgTypes:   []ast.Type{ast.TypeString},
		ReturnType: ast.TypeString,
		Callback: func(args []interface{}) (interface{}, error) {
			return homedir.Expand(args[0].(string))
		},
	}
}
func interpolationFuncCeil() ast.Function {
	return ast.Function{
		ArgTypes:   []ast.Type{ast.TypeFloat},
		ReturnType: ast.TypeInt,
		Callback: func(args []interface{}) (interface{}, error) {
			return int(math.Ceil(args[0].(float64))), nil
		},
	}
}
func interpolationFuncFloor() ast.Function {
	return ast.Function{
		ArgTypes:   []ast.Type{ast.TypeFloat},
		ReturnType: ast.TypeInt,
		Callback: func(args []interface{}) (interface{}, error) {
			return int(math.Floor(args[0].(float64))), nil
		},
	}
}
func interpolationFuncZipMap() ast.Function {
	return ast.Function{
		ArgTypes: []ast.Type{
			ast.TypeList,  
			ast.TypeList,  
		},
		ReturnType: ast.TypeMap,
		Callback: func(args []interface{}) (interface{}, error) {
			keys := args[0].([]ast.Variable)
			values := args[1].([]ast.Variable)
			if len(keys) != len(values) {
				return nil, fmt.Errorf("count of keys (%d) does not match count of values (%d)",
					len(keys), len(values))
			}
			for i, val := range keys {
				if val.Type != ast.TypeString {
					return nil, fmt.Errorf("keys must be strings. value at position %d is %s",
						i, val.Type.Printable())
				}
			}
			result := map[string]ast.Variable{}
			for i := 0; i < len(keys); i++ {
				result[keys[i].Value.(string)] = values[i]
			}
			return result, nil
		},
	}
}
func interpolationFuncFormatList() ast.Function {
	return ast.Function{
		ArgTypes:     []ast.Type{ast.TypeAny},
		Variadic:     true,
		VariadicType: ast.TypeAny,
		ReturnType:   ast.TypeList,
		Callback: func(args []interface{}) (interface{}, error) {
			varargs := make([]interface{}, len(args)-1)
			copy(varargs, args[1:])
			if len(varargs) == 0 {
				return nil, fmt.Errorf("no arguments to formatlist")
			}
			var n int
			listSeen := false
			for i := 1; i < len(args); i++ {
				s, ok := args[i].([]ast.Variable)
				if !ok {
					continue
				}
				listSeen = true
				parts, err := listVariableValueToStringSlice(s)
				if err != nil {
					return nil, err
				}
				varargs[i-1] = parts
				if n == 0 {
					n = len(parts)
					continue
				}
				if n != len(parts) {
					return nil, fmt.Errorf("format: mismatched list lengths: %d != %d", n, len(parts))
				}
			}
			if !listSeen {
				return nil, fmt.Errorf(
					"formatlist requires at least one list argument")
			}
			format := args[0].(string)
			list := make([]string, n)
			fmtargs := make([]interface{}, len(varargs))
			for i := 0; i < n; i++ {
				for j, arg := range varargs {
					switch arg := arg.(type) {
					default:
						fmtargs[j] = arg
					case []string:
						fmtargs[j] = arg[i]
					}
				}
				list[i] = fmt.Sprintf(format, fmtargs...)
			}
			return stringSliceToVariableValue(list), nil
		},
	}
}
func interpolationFuncIndex() ast.Function {
	return ast.Function{
		ArgTypes:   []ast.Type{ast.TypeList, ast.TypeString},
		ReturnType: ast.TypeInt,
		Callback: func(args []interface{}) (interface{}, error) {
			haystack := args[0].([]ast.Variable)
			needle := args[1].(string)
			for index, element := range haystack {
				if needle == element.Value {
					return index, nil
				}
			}
			return nil, fmt.Errorf("Could not find '%s' in '%s'", needle, haystack)
		},
	}
}
func interpolationFuncDistinct() ast.Function {
	return ast.Function{
		ArgTypes:     []ast.Type{ast.TypeList},
		ReturnType:   ast.TypeList,
		Variadic:     true,
		VariadicType: ast.TypeList,
		Callback: func(args []interface{}) (interface{}, error) {
			var list []string
			if len(args) != 1 {
				return nil, fmt.Errorf("accepts only one argument.")
			}
			if argument, ok := args[0].([]ast.Variable); ok {
				for _, element := range argument {
					if element.Type != ast.TypeString {
						return nil, fmt.Errorf(
							"only works for flat lists, this list contains elements of %s",
							element.Type.Printable())
					}
					list = appendIfMissing(list, element.Value.(string))
				}
			}
			return stringSliceToVariableValue(list), nil
		},
	}
}
func appendIfMissing(slice []string, element string) []string {
	for _, ele := range slice {
		if ele == element {
			return slice
		}
	}
	return append(slice, element)
}