func (a *Args) similar(r *Args, similar Similarity) bool {
	if a.Elided != r.Elided || len(a.Values) != len(r.Values) {
		return false
	}
	for i, l := range a.Values {
		if !l.similar(&r.Values[i], similar) {
			return false
		}
	}
	return true
}
similar returns true if the two Args are equal or almost but not quite
equal.
package stack
import (
	"errors"
	"fmt"
	"net/url"
	"os"
	"sort"
	"strings"
	"unicode"
	"unicode/utf8"
)
type Func struct {
	Complete string
	ImportPath string
	DirName string
	Name string
	IsExported bool
	IsPkgMain bool
	_ struct{}
}
func (f *Func) Init(raw string) error {
	endPkg := 0
	if lastSlash := strings.LastIndexByte(raw, '/'); lastSlash != -1 {
		r := strings.IndexByte(raw[lastSlash+1:], '.')
		if r == -1 {
			return errors.New("bad function reference: expected to have at least one dot")
		}
		endPkg = lastSlash + r + 1
	} else {
		endPkg = strings.IndexByte(raw, '.')
	}
	var err error
	if f.Complete, err = url.QueryUnescape(raw); err != nil {
		return fmt.Errorf("bad function reference: "+wrap, err)
	}
	endPkg += len(f.Complete) - len(raw)
	if endPkg != -1 {
		f.ImportPath = f.Complete[:endPkg]
	}
	f.Name = f.Complete[endPkg+1:]
	f.DirName = f.ImportPath
	if i := strings.LastIndexByte(f.DirName, '/'); i != -1 {
		f.DirName = f.DirName[i+1:]
	}
	if f.ImportPath == "main" {
		f.IsPkgMain = true
		if f.Name == "main" {
			f.IsExported = true
		}
	} else {
		parts := strings.Split(f.Name, ".")
		r, _ := utf8.DecodeRuneInString(parts[len(parts)-1])
		f.IsExported = unicode.ToUpper(r) == r
	}
	return nil
}
func (f *Func) String() string {
	return f.Complete
}
type Arg struct {
	IsAggregate bool
	Name string
	Value uint64
	IsPtr bool
	IsOffsetTooLarge bool
	Fields Args
	_ struct{}
}
const zeroToNine = "0123456789"
func (a *Arg) String() string {
	if a.Name != "" {
		return a.Name
	}
	if a.IsOffsetTooLarge {
		return "_"
	}
	if a.IsAggregate {
		return "{" + a.Fields.String() + "}"
	}
	if a.Value < uint64(len(zeroToNine)) {
		return zeroToNine[a.Value : a.Value+1]
	}
	return fmt.Sprintf("0x%x", a.Value)
}
const (
	pointerFloor = 512 * 1024
	pointerCeiling = uint64((^uint(0)) >> 1)
)
func (a *Arg) equal(r *Arg) bool {
	return a.similar(r, ExactFlags)
}
func (a *Arg) similar(r *Arg, similar Similarity) bool {
	if a.IsAggregate != r.IsAggregate {
		return false
	}
	if a.IsAggregate {
		return a.Fields.similar(&r.Fields, similar)
	}
	switch similar {
	case ExactFlags, ExactLines:
		if a.Name != r.Name {
			return false
		}
		if a.IsOffsetTooLarge != r.IsOffsetTooLarge {
			return false
		}
		if a.IsPtr != r.IsPtr {
			return false
		}
		return a.Value == r.Value
	case AnyValue:
		return true
	case AnyPointer:
		if a.IsOffsetTooLarge != r.IsOffsetTooLarge {
			return false
		}
		if a.IsPtr != r.IsPtr {
			return false
		}
		return a.IsPtr || a.Value == r.Value
	default:
		return false
	}
}
type Args struct {
	Values []Arg
	Processed []string
	Elided bool
	_ struct{}
}
func (a *Args) String() string {
	var v []string
	if len(a.Processed) != 0 {
		v = a.Processed
	} else {
		v = make([]string, 0, len(a.Values))
		for _, item := range a.Values {
			v = append(v, item.String())
		}
	}
	if a.Elided {
		v = append(v, "...")
	}
	return strings.Join(v, ", ")
}
func (a *Args) equal(r *Args) bool {
	if a.Elided != r.Elided || len(a.Values) != len(r.Values) {
		return false
	}
	for i, l := range a.Values {
		if !l.equal(&r.Values[i]) {
			return false
		}
	}
	return true
}