func valuesSDKEquivalentSets(a, b cty.Value) bool {
	if aLen, bLen := a.LengthInt(), b.LengthInt(); aLen != bLen {
		return false
	}
	as := a.AsValueSlice()
	bs := b.AsValueSlice()
	aeqs := make([]bool, len(as))
	beqs := make([]bool, len(bs))
	for ai, av := range as {
		for bi, bv := range bs {
			if ValuesSDKEquivalent(av, bv) {
				aeqs[ai] = true
				beqs[bi] = true
			}
		}
	}
	for _, eq := range aeqs {
		if !eq {
			return false
		}
	}
	for _, eq := range beqs {
		if !eq {
			return false
		}
	}
	return true
}
valuesSDKEquivalentSets returns true only if each of the elements in a can
be correlated with at least one equivalent element in b and vice-versa.
This is a fuzzy operation that prefers to signal non-equivalence if it cannot
be certain that all elements are accounted for.
package hcl2shim
import (
	"github.com/zclconf/go-cty/cty"
)
func ValuesSDKEquivalent(a, b cty.Value) bool {
	if a == cty.NilVal || b == cty.NilVal {
		return a == b  
	}
	if a.RawEquals(b) {
		return true
	}
	if !a.IsKnown() || !b.IsKnown() {
		return a.IsKnown() == b.IsKnown()
	}
	if aZero, bZero := valuesSDKEquivalentIsNullOrZero(a), valuesSDKEquivalentIsNullOrZero(b); aZero || bZero {
		return aZero == bZero
	}
	aTy := a.Type()
	bTy := b.Type()
	switch {
	case aTy.IsSetType() && bTy.IsSetType():
		return valuesSDKEquivalentSets(a, b)
	case aTy.IsListType() && bTy.IsListType():
		return valuesSDKEquivalentSequences(a, b)
	case aTy.IsTupleType() && bTy.IsTupleType():
		return valuesSDKEquivalentSequences(a, b)
	case aTy.IsMapType() && bTy.IsMapType():
		return valuesSDKEquivalentMappings(a, b)
	case aTy.IsObjectType() && bTy.IsObjectType():
		return valuesSDKEquivalentMappings(a, b)
	case aTy == cty.Number && bTy == cty.Number:
		return valuesSDKEquivalentNumbers(a, b)
	default:
		return false
	}
}
func valuesSDKEquivalentIsNullOrZero(v cty.Value) bool {
	if v == cty.NilVal {
		return true
	}
	ty := v.Type()
	switch {
	case !v.IsKnown():
		return false
	case v.IsNull():
		return true
	case ty.IsListType() || ty.IsSetType() || ty.IsMapType() || ty.IsObjectType() || ty.IsTupleType():
		return v.LengthInt() == 0
	case ty == cty.String:
		return v.RawEquals(cty.StringVal(""))
	case ty == cty.Number:
		return v.RawEquals(cty.Zero)
	case ty == cty.Bool:
		return v.RawEquals(cty.False)
	default:
		return false
	}
}