package gotypes

import 

// Unify matches a pattern type (which may mention the template's type
// parameters) against a concrete demand type, recording the type-parameter
// bindings it discovers. It reports whether the match succeeds. Only the type
// parameters in params are treated as variables; every other type node must be
// structurally identical.
func (,  types.Type,  map[*types.TypeParam]bool,  map[*types.TypeParam]types.Type) bool {
	 = types.Unalias()
	 = types.Unalias()

	if ,  := .(*types.TypeParam);  && [] {
		if ,  := [];  {
			return types.Identical(, )
		}
		[] = 
		return true
	}

	// If the pattern mentions no template parameter, identity is enough.
	if !MentionsParams(, ) {
		return types.Identical(, )
	}

	switch p := .(type) {
	case *types.Pointer:
		,  := .(*types.Pointer)
		return  && (.Elem(), .Elem(), , )
	case *types.Slice:
		,  := .(*types.Slice)
		return  && (.Elem(), .Elem(), , )
	case *types.Array:
		,  := .(*types.Array)
		return  && .Len() == .Len() && (.Elem(), .Elem(), , )
	case *types.Chan:
		,  := .(*types.Chan)
		return  && .Dir() == .Dir() && (.Elem(), .Elem(), , )
	case *types.Map:
		,  := .(*types.Map)
		return  && (.Key(), .Key(), , ) && (.Elem(), .Elem(), , )
	case *types.Named:
		,  := .(*types.Named)
		if ! || !types.Identical(.Origin(), .Origin()) {
			return false
		}
		,  := .TypeArgs(), .TypeArgs()
		if .Len() != .Len() { // TypeList.Len is nil-safe
			return false
		}
		for  := range .Len() {
			if !(.At(), .At(), , ) {
				return false
			}
		}
		return true
	case *types.Signature:
		,  := .(*types.Signature)
		return  && .Variadic() == .Variadic() &&
			unifyTuple(.Params(), .Params(), , ) &&
			unifyTuple(.Results(), .Results(), , )
	case *types.Struct:
		,  := .(*types.Struct)
		if ! || .NumFields() != .NumFields() {
			return false
		}
		for  := range .NumFields() {
			,  := .Field(), .Field()
			if .Id() != .Id() || .Embedded() != .Embedded() || .Tag() != .Tag() {
				return false
			}
			if !(.Type(), .Type(), , ) {
				return false
			}
		}
		return true
	case *types.Interface:
		// Value-type interfaces are basic (method sets only); compare the
		// complete, name-sorted method sets and unify each signature.
		,  := .(*types.Interface)
		if ! || .NumMethods() != .NumMethods() {
			return false
		}
		for  := range .NumMethods() {
			,  := .Method(), .Method()
			if .Id() != .Id() || !(.Type(), .Type(), , ) {
				return false
			}
		}
		return true
	default:
		// Any remaining form (e.g. a union, which only appears in a constraint,
		// never a value type) is matched by identity, which fails safely.
		return types.Identical(, )
	}
}

// unifyTuple unifies two tuples element-wise; they must have equal arity.
func (,  *types.Tuple,  map[*types.TypeParam]bool,  map[*types.TypeParam]types.Type) bool {
	if .Len() != .Len() {
		return false
	}
	for  := range .Len() {
		if !Unify(.At().Type(), .At().Type(), , ) {
			return false
		}
	}
	return true
}

// MentionsParams reports whether t mentions any of the given type parameters.
// It shares typeContains's exhaustive type walk (which panics on an unhandled
// go/types kind rather than silently reporting false), testing membership in
// params at each type-parameter leaf.
func ( types.Type,  map[*types.TypeParam]bool) bool {
	return typeContains(, &Set[types.Type]{}, func( types.Type) bool {
		,  := .(*types.TypeParam)
		return  && []
	})
}

// MentionsParam reports whether t mentions the single type parameter tp. It is
// the singular form of MentionsParams over the same typeContains walk, for
// callers testing one parameter at a time without building a one-element set.
func ( types.Type,  *types.TypeParam) bool {
	return typeContains(, &Set[types.Type]{}, func( types.Type) bool {
		,  := .(*types.TypeParam)
		return  &&  == 
	})
}

// IsBareTypeParam reports whether t is exactly one of the given type parameters.
func ( types.Type,  map[*types.TypeParam]bool) bool {
	,  := types.Unalias().(*types.TypeParam)
	return  && []
}

// IsPointerToBareTypeParam reports whether t is *T for a type parameter T in
// params. Such a result matches every pointer demand, and through the
// value/pointer bridge every value demand too, so, like a bare T, it cannot be
// a demand-driven producer.
func ( types.Type,  map[*types.TypeParam]bool) bool {
	,  := types.Unalias().(*types.Pointer)
	return  && IsBareTypeParam(.Elem(), )
}

// TypeDepth returns a structural nesting depth used to bound non-terminating
// generic instantiation.
func ( types.Type) int {
	return typeDepthRec(, &Set[types.Type]{})
}

func ( types.Type,  *Set[types.Type]) int {
	 = types.Unalias()
	if !.Add() {
		return 0
	}
	switch u := .(type) {
	case *types.Pointer:
		return 1 + (.Elem(), )
	case *types.Slice:
		return 1 + (.Elem(), )
	case *types.Array:
		return 1 + (.Elem(), )
	case *types.Chan:
		return 1 + (.Elem(), )
	case *types.Map:
		return 1 + max((.Key(), ), (.Elem(), ))
	case *types.Named:
		 := 0
		if  := .TypeArgs();  != nil {
			for  := range .Types() {
				 = max(, (, ))
			}
		}
		return 1 + 
	case *types.Signature:
		// A template can grow through a function type (Box[func(T)] → Box[T]), so
		// the bound must see through signatures, not treat them as flat leaves.
		 := 0
		for  := range .Params().Variables() {
			 = max(, (.Type(), ))
		}
		for  := range .Results().Variables() {
			 = max(, (.Type(), ))
		}
		return 1 + 
	case *types.Struct:
		 := 0
		for  := range .Fields() {
			 = max(, (.Type(), ))
		}
		return 1 + 
	case *types.Interface:
		 := 0
		for  := range .ExplicitMethods() {
			 = max(, (.Type(), ))
		}
		for  := range .EmbeddedTypes() {
			 = max(, (, ))
		}
		return 1 + 
	}
	return 1
}