func (val Value) GetAttr(name string) Value {
	if val.ty == DynamicPseudoType {
		return DynamicVal
	}
	if !val.ty.IsObjectType() {
		panic("value is not an object")
	}
	name = NormalizeString(name)
	if !val.ty.HasAttribute(name) {
		panic("value has no attribute of that name")
	}
	attrType := val.ty.AttributeType(name)
	if !val.IsKnown() {
		return UnknownVal(attrType)
	}
	return Value{
		ty: attrType,
		v:  val.v.(map[string]interface{})[name],
	}
}
GetAttr returns the value of the given attribute of the receiver, which
must be of an object type that has an attribute of the given name.
This method will panic if the receiver type is not compatible.

The method will also panic if the given attribute name is not defined
for the value's type. Use the attribute-related methods on Type to
check for the validity of an attribute before trying to use it.

This method may be called on a value whose type is DynamicPseudoType,
in which case the result will also be DynamicVal.
package cty
import (
	"fmt"
	"math/big"
	"reflect"
	"github.com/zclconf/go-cty/cty/set"
)
func (val Value) GoString() string {
	if val == NilVal {
		return "cty.NilVal"
	}
	if val.IsNull() {
		return fmt.Sprintf("cty.NullVal(%#v)", val.ty)
	}
	if val == DynamicVal {  
		return "cty.DynamicVal"
	}
	if !val.IsKnown() {
		return fmt.Sprintf("cty.UnknownVal(%#v)", val.ty)
	}
	switch val.ty {
	case Bool:
		if val.v.(bool) {
			return "cty.True"
		}
		return "cty.False"
	case Number:
		fv := val.v.(*big.Float)
		if fv.IsInt() {
			return fmt.Sprintf("cty.NumberIntVal(%#v)", fv)
		}
		if rfv, accuracy := fv.Float64(); accuracy == big.Exact {
			return fmt.Sprintf("cty.NumberFloatVal(%#v)", rfv)
		}
		return fmt.Sprintf("cty.MustParseNumberVal(%q)", fv.Text('f', -1))
	case String:
		return fmt.Sprintf("cty.StringVal(%#v)", val.v)
	}
	switch {
	case val.ty.IsSetType():
		vals := val.AsValueSlice()
		if len(vals) == 0 {
			return fmt.Sprintf("cty.SetValEmpty(%#v)", val.ty.ElementType())
		}
		return fmt.Sprintf("cty.SetVal(%#v)", vals)
	case val.ty.IsListType():
		vals := val.AsValueSlice()
		if len(vals) == 0 {
			return fmt.Sprintf("cty.ListValEmpty(%#v)", val.ty.ElementType())
		}
		return fmt.Sprintf("cty.ListVal(%#v)", vals)
	case val.ty.IsMapType():
		vals := val.AsValueMap()
		if len(vals) == 0 {
			return fmt.Sprintf("cty.MapValEmpty(%#v)", val.ty.ElementType())
		}
		return fmt.Sprintf("cty.MapVal(%#v)", vals)
	case val.ty.IsTupleType():
		if val.ty.Equals(EmptyTuple) {
			return "cty.EmptyTupleVal"
		}
		vals := val.AsValueSlice()
		return fmt.Sprintf("cty.TupleVal(%#v)", vals)
	case val.ty.IsObjectType():
		if val.ty.Equals(EmptyObject) {
			return "cty.EmptyObjectVal"
		}
		vals := val.AsValueMap()
		return fmt.Sprintf("cty.ObjectVal(%#v)", vals)
	case val.ty.IsCapsuleType():
		return fmt.Sprintf("cty.CapsuleVal(%#v, %#v)", val.ty, val.v)
	}
	return fmt.Sprintf("cty.Value{ty: %#v, v: %#v}", val.ty, val.v)
}
func (val Value) Equals(other Value) Value {
	switch {
	case !val.IsKnown() && !other.IsKnown():
		return UnknownVal(Bool)
	case val.IsKnown() && !other.IsKnown():
		switch {
		case val.IsNull(), other.ty.HasDynamicTypes():
			return UnknownVal(Bool)
		case !val.ty.Equals(other.ty):
			return False
		default:
			return UnknownVal(Bool)
		}
	case other.IsKnown() && !val.IsKnown():
		switch {
		case other.IsNull(), val.ty.HasDynamicTypes():
			return UnknownVal(Bool)
		case !other.ty.Equals(val.ty):
			return False
		default:
			return UnknownVal(Bool)
		}
	}
	switch {
	case val.IsNull() && other.IsNull():
		return BoolVal(true)
	case val.IsNull() || other.IsNull():
		return BoolVal(false)
	}
	if val.ty.HasDynamicTypes() || other.ty.HasDynamicTypes() {
		return UnknownVal(Bool)
	}
	if !val.ty.Equals(other.ty) {
		return BoolVal(false)
	}
	ty := val.ty
	result := false
	switch {
	case ty == Number:
		result = val.v.(*big.Float).Cmp(other.v.(*big.Float)) == 0
	case ty == Bool:
		result = val.v.(bool) == other.v.(bool)
	case ty == String:
		result = val.v.(string) == other.v.(string)
	case ty.IsObjectType():
		oty := ty.typeImpl.(typeObject)
		result = true
		for attr, aty := range oty.AttrTypes {
			lhs := Value{
				ty: aty,
				v:  val.v.(map[string]interface{})[attr],
			}
			rhs := Value{
				ty: aty,
				v:  other.v.(map[string]interface{})[attr],
			}
			eq := lhs.Equals(rhs)
			if !eq.IsKnown() {
				return UnknownVal(Bool)
			}
			if eq.False() {
				result = false
				break
			}
		}
	case ty.IsTupleType():
		tty := ty.typeImpl.(typeTuple)
		result = true
		for i, ety := range tty.ElemTypes {
			lhs := Value{
				ty: ety,
				v:  val.v.([]interface{})[i],
			}
			rhs := Value{
				ty: ety,
				v:  other.v.([]interface{})[i],
			}
			eq := lhs.Equals(rhs)
			if !eq.IsKnown() {
				return UnknownVal(Bool)
			}
			if eq.False() {
				result = false
				break
			}
		}
	case ty.IsListType():
		ety := ty.typeImpl.(typeList).ElementTypeT
		if len(val.v.([]interface{})) == len(other.v.([]interface{})) {
			result = true
			for i := range val.v.([]interface{}) {
				lhs := Value{
					ty: ety,
					v:  val.v.([]interface{})[i],
				}
				rhs := Value{
					ty: ety,
					v:  other.v.([]interface{})[i],
				}
				eq := lhs.Equals(rhs)
				if !eq.IsKnown() {
					return UnknownVal(Bool)
				}
				if eq.False() {
					result = false
					break
				}
			}
		}
	case ty.IsSetType():
		s1 := val.v.(set.Set)
		s2 := other.v.(set.Set)
		equal := true
		s1.EachValue(func(v interface{}) {
			if !s2.Has(v) {
				equal = false
			}
		})
		s2.EachValue(func(v interface{}) {
			if !s1.Has(v) {
				equal = false
			}
		})
		result = equal
	case ty.IsMapType():
		ety := ty.typeImpl.(typeMap).ElementTypeT
		if len(val.v.(map[string]interface{})) == len(other.v.(map[string]interface{})) {
			result = true
			for k := range val.v.(map[string]interface{}) {
				if _, ok := other.v.(map[string]interface{})[k]; !ok {
					result = false
					break
				}
				lhs := Value{
					ty: ety,
					v:  val.v.(map[string]interface{})[k],
				}
				rhs := Value{
					ty: ety,
					v:  other.v.(map[string]interface{})[k],
				}
				eq := lhs.Equals(rhs)
				if !eq.IsKnown() {
					return UnknownVal(Bool)
				}
				if eq.False() {
					result = false
					break
				}
			}
		}
	case ty.IsCapsuleType():
		return BoolVal(val.v == other.v)
	default:
		panic(fmt.Errorf("unsupported value type %#v in Equals", ty))
	}
	return BoolVal(result)
}
func (val Value) NotEqual(other Value) Value {
	return val.Equals(other).Not()
}
func (val Value) True() bool {
	if val.ty != Bool {
		panic("not bool")
	}
	return val.Equals(True).v.(bool)
}
func (val Value) False() bool {
	return !val.True()
}
func (val Value) RawEquals(other Value) bool {
	if !val.ty.Equals(other.ty) {
		return false
	}
	if (!val.IsKnown()) && (!other.IsKnown()) {
		return true
	}
	if (val.IsKnown() && !other.IsKnown()) || (other.IsKnown() && !val.IsKnown()) {
		return false
	}
	if val.IsNull() && other.IsNull() {
		return true
	}
	if (val.IsNull() && !other.IsNull()) || (other.IsNull() && !val.IsNull()) {
		return false
	}
	if val.ty == DynamicPseudoType && other.ty == DynamicPseudoType {
		return true
	}
	ty := val.ty
	switch {
	case ty == Number || ty == Bool || ty == String || ty == DynamicPseudoType:
		return val.Equals(other).True()
	case ty.IsObjectType():
		oty := ty.typeImpl.(typeObject)
		for attr, aty := range oty.AttrTypes {
			lhs := Value{
				ty: aty,
				v:  val.v.(map[string]interface{})[attr],
			}
			rhs := Value{
				ty: aty,
				v:  other.v.(map[string]interface{})[attr],
			}
			eq := lhs.RawEquals(rhs)
			if !eq {
				return false
			}
		}
		return true
	case ty.IsTupleType():
		tty := ty.typeImpl.(typeTuple)
		for i, ety := range tty.ElemTypes {
			lhs := Value{
				ty: ety,
				v:  val.v.([]interface{})[i],
			}
			rhs := Value{
				ty: ety,
				v:  other.v.([]interface{})[i],
			}
			eq := lhs.RawEquals(rhs)
			if !eq {
				return false
			}
		}
		return true
	case ty.IsListType():
		ety := ty.typeImpl.(typeList).ElementTypeT
		if len(val.v.([]interface{})) == len(other.v.([]interface{})) {
			for i := range val.v.([]interface{}) {
				lhs := Value{
					ty: ety,
					v:  val.v.([]interface{})[i],
				}
				rhs := Value{
					ty: ety,
					v:  other.v.([]interface{})[i],
				}
				eq := lhs.RawEquals(rhs)
				if !eq {
					return false
				}
			}
			return true
		}
		return false
	case ty.IsSetType():
		s1 := val.v.(set.Set)
		s2 := other.v.(set.Set)
		return reflect.DeepEqual(s1, s2)
	case ty.IsMapType():
		ety := ty.typeImpl.(typeMap).ElementTypeT
		if len(val.v.(map[string]interface{})) == len(other.v.(map[string]interface{})) {
			for k := range val.v.(map[string]interface{}) {
				if _, ok := other.v.(map[string]interface{})[k]; !ok {
					return false
				}
				lhs := Value{
					ty: ety,
					v:  val.v.(map[string]interface{})[k],
				}
				rhs := Value{
					ty: ety,
					v:  other.v.(map[string]interface{})[k],
				}
				eq := lhs.RawEquals(rhs)
				if !eq {
					return false
				}
			}
			return true
		}
		return false
	case ty.IsCapsuleType():
		return val.v == other.v
	default:
		panic(fmt.Errorf("unsupported value type %#v in RawEquals", ty))
	}
}
func (val Value) Add(other Value) Value {
	if shortCircuit := mustTypeCheck(Number, Number, val, other); shortCircuit != nil {
		shortCircuit = forceShortCircuitType(shortCircuit, Number)
		return *shortCircuit
	}
	ret := new(big.Float)
	ret.Add(val.v.(*big.Float), other.v.(*big.Float))
	return NumberVal(ret)
}
func (val Value) Subtract(other Value) Value {
	if shortCircuit := mustTypeCheck(Number, Number, val, other); shortCircuit != nil {
		shortCircuit = forceShortCircuitType(shortCircuit, Number)
		return *shortCircuit
	}
	return val.Add(other.Negate())
}
func (val Value) Negate() Value {
	if shortCircuit := mustTypeCheck(Number, Number, val); shortCircuit != nil {
		shortCircuit = forceShortCircuitType(shortCircuit, Number)
		return *shortCircuit
	}
	ret := new(big.Float).Neg(val.v.(*big.Float))
	return NumberVal(ret)
}
func (val Value) Multiply(other Value) Value {
	if shortCircuit := mustTypeCheck(Number, Number, val, other); shortCircuit != nil {
		shortCircuit = forceShortCircuitType(shortCircuit, Number)
		return *shortCircuit
	}
	ret := new(big.Float)
	ret.Mul(val.v.(*big.Float), other.v.(*big.Float))
	return NumberVal(ret)
}
func (val Value) Divide(other Value) Value {
	if shortCircuit := mustTypeCheck(Number, Number, val, other); shortCircuit != nil {
		shortCircuit = forceShortCircuitType(shortCircuit, Number)
		return *shortCircuit
	}
	ret := new(big.Float)
	ret.Quo(val.v.(*big.Float), other.v.(*big.Float))
	return NumberVal(ret)
}
func (val Value) Modulo(other Value) Value {
	if shortCircuit := mustTypeCheck(Number, Number, val, other); shortCircuit != nil {
		shortCircuit = forceShortCircuitType(shortCircuit, Number)
		return *shortCircuit
	}
	if val == PositiveInfinity || val == NegativeInfinity || other == PositiveInfinity || other == NegativeInfinity {
		return val.Multiply(other)
	}
	if other.RawEquals(Zero) {
		return val
	}
	rat := val.Divide(other)
	ratFloorInt := &big.Int{}
	rat.v.(*big.Float).Int(ratFloorInt)
	work := (&big.Float{}).SetInt(ratFloorInt)
	work.Mul(other.v.(*big.Float), work)
	work.Sub(val.v.(*big.Float), work)
	return NumberVal(work)
}
func (val Value) Absolute() Value {
	if shortCircuit := mustTypeCheck(Number, Number, val); shortCircuit != nil {
		shortCircuit = forceShortCircuitType(shortCircuit, Number)
		return *shortCircuit
	}
	ret := (&big.Float{}).Abs(val.v.(*big.Float))
	return NumberVal(ret)
}