func (f ForkId) Equal(o ForkId) bool {
	if len(f) != len(o) {
		return false
	}
	for i, fi := range f {
		if !fi.Equal(o[i]) {
			return false
		}
	}
	return true
}
Returns true if the two fork Ids are equal.
package core
import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/url"
	"reflect"
	"sort"
	"strconv"
	"strings"
	"github.com/martian-lang/martian/martian/syntax"
	"github.com/martian-lang/martian/martian/util"
)
type (
	ForkIdPart interface {
		fmt.Stringer
		syntax.CollectionIndex
		forkString() string
	}
	ForkIdRange interface {
		Allow(syntax.CollectionIndex) error
		Length() int
	}
	mapKeyFork string
	arrayIndexFork int
	undeterminedFork struct{}
	emptyFork struct{}
	mapSourceRange struct {
		Source syntax.MapCallSource
	}
	arrayLengthRange int
	mapKeyRange []string
	ForkSourcePart struct {
		Split *syntax.SplitExp
		Id    ForkIdPart
		Range ForkIdRange
	}
	ForkId []*ForkSourcePart
	ForkIdSet struct {
		List  []ForkId
		Table map[*syntax.CallStm][]*ForkSourcePart
	}
)
func convertForkPart(f syntax.CollectionIndex) ForkIdPart {
	switch f := f.(type) {
	case mapKeyFork:
		return f
	case arrayIndexFork:
		return f
	case emptyFork:
		return f
	case undeterminedFork:
		return f
	}
	if f.IndexSource() != nil {
		return undeterminedFork{}
	}
	switch f.Mode() {
	case syntax.ModeArrayCall:
		return arrayIndexFork(f.ArrayIndex())
	case syntax.ModeMapCall:
		return mapKeyFork(f.MapKey())
	}
	panic("invalid index")
}
func (fork ForkId) SourceIndexMap() map[*syntax.CallStm]syntax.CollectionIndex {
	if len(fork) == 0 {
		return nil
	}
	result := make(map[*syntax.CallStm]syntax.CollectionIndex, len(fork))
	for _, part := range fork {
		result[part.Split.Call] = part.Id
	}
	return result
}
func countForkParts(src syntax.MapCallSource,
	index map[*syntax.CallStm]syntax.CollectionIndex,
	lookup *syntax.TypeLookup) int {
	if src.KnownLength() {
		switch src.CallMode() {
		case syntax.ModeArrayCall:
			return src.ArrayLength()
		case syntax.ModeMapCall:
			return len(src.Keys())
		}
	}
	switch src := src.(type) {
	case *syntax.SplitExp:
		if src.Source.KnownLength() {
			switch src.Source.CallMode() {
			case syntax.ModeArrayCall:
				c := 0
				if old, ok := index[src.Call]; ok {
					defer func() { index[src.Call] = old }()
				} else if index != nil {
					defer delete(index, src.Call)
				}
				for i := 0; i < src.Source.ArrayLength(); i++ {
					if index == nil {
						index = make(map[*syntax.CallStm]syntax.CollectionIndex)
					}
					index[src.Call] = arrayIndexFork(i)
					sub, err := src.BindingPath("", index, lookup)
					if err != nil || sub == src {
						c++
					} else if subs, ok := sub.(syntax.MapCallSource); ok {
						c += countForkParts(subs, index, lookup)
					} else {
						c++
					}
				}
				return c
			case syntax.ModeMapCall:
				c := 0
				if old, ok := index[src.Call]; ok {
					defer func() { index[src.Call] = old }()
				} else if index != nil {
					defer delete(index, src.Call)
				}
				for i := range src.Source.Keys() {
					if index == nil {
						index = make(map[*syntax.CallStm]syntax.CollectionIndex)
					}
					index[src.Call] = mapKeyFork(i)
					sub, err := src.BindingPath("", index, lookup)
					if err != nil || sub == src {
						c++
					} else if subs, ok := sub.(syntax.MapCallSource); ok {
						c += countForkParts(subs, index, lookup)
					} else {
						c++
					}
				}
				return c
			}
		}
	case *syntax.MergeExp:
		return countForkParts(src.MergeOver, index, lookup)
	}
	return 1
}
func makeForkIdParts(split *syntax.SplitExp,
	lookup *syntax.TypeLookup) ([]*ForkSourcePart, int) {
	switch split.Source.CallMode() {
	case syntax.ModeArrayCall:
		if alen := split.Source.ArrayLength(); alen >= 0 {
			re := make([]ForkSourcePart, alen)
			result := make([]*ForkSourcePart, alen)
			for i := range result {
				r := arrayIndexFork(i)
				re[i].Split = split
				re[i].Id = r
				result[i] = &re[i]
			}
			return result, 0
		}
	case syntax.ModeMapCall:
		if keys := split.Source.Keys(); keys != nil {
			re := make([]ForkSourcePart, 0, len(keys))
			result := make([]*ForkSourcePart, 0, len(keys))
			for k := range keys {
				r := mapKeyFork(k)
				re = append(re, ForkSourcePart{
					Split: split,
					Id:    r,
				})
				result = append(result, &re[len(re)-1])
			}
			sort.Slice(result, func(i, j int) bool {
				return result[i].Id.(mapKeyFork) < result[j].Id.(mapKeyFork)
			})
			return result, 0
		}
	case syntax.ModeNullMapCall:
		return []*ForkSourcePart{
			{
				Split: split,
				Id:    dummyForkId,
			},
		}, 0
	}
	return []*ForkSourcePart{
		{
			Split: split,
			Id:    dummyForkId,
		},
	}, countForkParts(split.Source, nil, lookup)
}
var dummyForkId undeterminedFork
func (set *ForkIdSet) MakeForkIds(srcs syntax.ForkRootList,
	lookup *syntax.TypeLookup) {
	if len(srcs) == 0 {
		return
	}
	set.Table = make(map[*syntax.CallStm][]*ForkSourcePart, len(srcs))
	if len(srcs) == 1 {
		these, extra := makeForkIdParts(srcs[0].Split(), lookup)
		set.Table[srcs[0].Call()] = these
		set.List = make([]ForkId, len(these), len(these)+extra)
		for i, part := range these {
			set.List[i] = ForkId{part}
		}
		return
	}
	count := 1
	extra := 0
	for _, srcNode := range srcs {
		these, e := makeForkIdParts(srcNode.Split(), lookup)
		set.Table[srcNode.Call()] = these
		count *= len(these)
		if e == 0 {
			extra *= len(these)
		} else {
			extra += e
		}
	}
	idBlock := make([]*ForkSourcePart, count*len(srcs))
	set.List = make([]ForkId, count, count+extra)
	for i := range set.List {
		set.List[i] = ForkId(idBlock[0:0:len(srcs)])
		idBlock = idBlock[len(srcs):]
	}
	stride := 1
	for _, src := range srcs {
		i := 0
		these := set.Table[src.Call()]
		if these != nil {
			for i < len(set.List) {
				for _, p := range these {
					for j := 0; j < stride; j++ {
						set.List[i] = append(set.List[i], p)
						i++
					}
				}
			}
			stride *= len(these)
		}
	}
	if extra >= 0 {
		set.expandStaticForks(lookup)
	}
}
func (set *ForkIdSet) expandStaticForks(lookup *syntax.TypeLookup) {
	index := make(map[*syntax.CallStm]syntax.CollectionIndex, len(set.List[0]))
	for i := 0; i < len(set.List); i++ {
		fork := set.List[i]
		newForks := fork.expandStaticForks(index, set.List[len(set.List):], lookup)
		for ; len(newForks) > 0; newForks = fork.expandStaticForks(index,
			set.List[len(set.List):], lookup) {
			set.List = append(set.List, newForks...)
		}
	}
}
func (fork ForkId) expandStaticForks(index map[*syntax.CallStm]syntax.CollectionIndex,
	slice []ForkId,
	lookup *syntax.TypeLookup) []ForkId {
	for i := 0; i < len(fork); i++ {
		part := fork[i]
		if part.Id.IndexSource() != nil {
			if ids := fork.expandStaticForkPart(i, part,
				part.Split, index, slice, lookup); len(ids) > 0 {
				return ids
			} else if part.Id == arrayIndexFork(-1) {
				return nil
			}
		}
	}
	return nil
}
func (fork ForkId) getForkSrc(split *syntax.SplitExp,
	index map[*syntax.CallStm]syntax.CollectionIndex,
	lookup *syntax.TypeLookup) syntax.MapCallSource {
	if split.Source.KnownLength() || len(fork) == 0 {
		return split.Source
	}
	for _, part := range fork {
		if part.Id.IndexSource() == nil {
			if id, ok := index[part.Split.Call]; ok {
				defer func() { index[part.Split.Call] = id }()
			} else {
				defer delete(index, part.Split.Call)
			}
			index[part.Split.Call] = part.Id
		}
	}
	exp, err := split.BindingPath("", index, lookup)
	if err != nil {
		panic("resolving source for " + split.GoString() +
			" of fork " + fork.GoString() + ": " + err.Error())
	}
	switch exp := exp.(type) {
	case *syntax.SplitExp:
		if exp.Call == split.Call {
			if ss, ok := exp.Value.(syntax.MapCallSource); ok {
				return ss
			} else {
				return exp.Source
			}
		} else {
			return exp
		}
	case syntax.MapCallSource:
		return exp
	}
	return split.Source
}
func (fork ForkId) expandStaticForkPart(i int, part *ForkSourcePart,
	split *syntax.SplitExp,
	index map[*syntax.CallStm]syntax.CollectionIndex,
	result []ForkId,
	lookup *syntax.TypeLookup) []ForkId {
	forkSrc := fork.getForkSrc(split, index, lookup)
	if forkSrc == nil || !forkSrc.KnownLength() {
		return nil
	}
	var r ForkIdRange = mapSourceRange{Source: forkSrc}
	if part.Range != nil {
		r = part.Range
	}
	if r.Length() <= 0 {
		p := *part
		fork[i] = &p
		p.Range = r
		p.Id = emptyFork{}
		return nil
	}
	if s, ok := forkSrc.(*syntax.SplitExp); ok {
		split = s
	}
	switch forkSrc.CallMode() {
	case syntax.ModeArrayCall:
		if forkSrc.ArrayLength() == 1 {
			p := *part
			p.Range = r
			p.Id = arrayIndexFork(0)
			p.Split = split
			fork[i] = &p
			return nil
		}
		parts := make([]ForkSourcePart, r.Length())
		ids := make([]*ForkSourcePart, len(fork)*(len(parts)-1))
		for j := range parts {
			parts[j] = *part
			parts[j].Range = r
			parts[j].Split = split
			parts[j].Id = arrayIndexFork(j)
			if j == 0 {
				fork[i] = &parts[0]
			} else {
				id := ids[:len(fork)]
				ids = ids[len(fork):]
				for k := range id {
					if k == i {
						id[k] = &parts[j]
					} else {
						id[k] = fork[k]
					}
				}
				result = append(result, id)
			}
		}
		return result
	case syntax.ModeMapCall:
		keyMap := forkSrc.Keys()
		if len(keyMap) == 1 {
			p := *part
			p.Range = r
			for k := range keyMap {
				p.Id = mapKeyFork(k)
			}
			p.Split = split
			fork[i] = &p
			return nil
		}
		keys := make([]string, 0, len(keyMap))
		for k := range keyMap {
			keys = append(keys, k)
		}
		sort.Strings(keys)
		parts := make([]ForkSourcePart, len(keys))
		ids := make(ForkId, len(fork)*(len(parts)-1))
		for j := range parts {
			parts[j] = *part
			parts[j].Range = r
			parts[j].Split = split
			parts[j].Id = mapKeyFork(keys[j])
			if j == 0 {
				fork[i] = &parts[0]
			} else {
				id := ids[:len(fork)]
				ids = ids[len(fork):]
				for k := range id {
					if k == i {
						id[k] = &parts[j]
					} else {
						id[k] = fork[k]
					}
				}
				result = append(result, id)
			}
		}
		return result
	}
	panic("invalid fork mode " + forkSrc.CallMode().String())
}
func (f ForkId) Match(ref map[*syntax.CallStm]syntax.CollectionIndex,
	upstream []*syntax.CallStm) (ForkId, error) {
	if len(upstream) == 0 {
		return nil, nil
	}
	result := make(ForkId, len(upstream))
	for i, src := range upstream {
		if r, err := f.matchPart(src); err != nil {
			if j, ok := ref[src]; ok && j != nil {
				if j.IndexSource() == nil {
					result[i] = &ForkSourcePart{
						Id: convertForkPart(j),
						Split: &syntax.SplitExp{
							Value:  &syntax.MergeExp{MergeOver: src},
							Call:   src,
							Source: src,
						},
					}
					if src.CallMode() != result[i].Id.Mode() {
						panic(result[i].GoString() + " from " + j.Mode().String())
					}
				} else {
					return result, &elementError{
						element: "unknown index for " + j.IndexSource().GoString(),
						inner:   err,
					}
				}
			} else {
				found := false
				for s, j := range ref {
					if j != nil && s == src {
						if j.IndexSource() == nil {
							found = true
							result[i] = &ForkSourcePart{
								Id: convertForkPart(j),
								Split: &syntax.SplitExp{
									Value:  &syntax.MergeExp{MergeOver: src},
									Call:   src,
									Source: src,
								},
							}
							if src.CallMode() != result[i].Id.Mode() {
								panic(result[i].GoString() + " from " + j.Mode().String())
							}
						} else {
							return result, &elementError{
								element: "unknown index for " + j.IndexSource().GoString(),
								inner:   err,
							}
						}
					}
				}
				if !found {
					return result, err
				}
			}
		} else {
			result[i] = r
		}
	}
	return result, nil
}
func (f ForkId) Matches(other ForkId) bool {
	for _, p1 := range f {
		if len(other) == 0 {
			return true
		}
		for j, p2 := range other {
			if p1.Split.Call == p2.Split.Call {
				if p1.Id.IndexSource() == nil &&
					p2.Id.IndexSource() == nil &&
					!indexEqual(p1.Id, p2.Id) {
					return false
				}
				other = other[j+1:]
				break
			}
		}
	}
	return true
}
func indexEqual(i, j syntax.CollectionIndex) bool {
	if i == j {
		return true
	}
	if m := i.Mode(); m == syntax.ModeNullMapCall {
		return true
	} else if n := j.Mode(); n == syntax.ModeNullMapCall {
		return true
	} else if s := i.IndexSource(); s != j.IndexSource() || s != nil {
		return false
	} else if m != n {
		return false
	} else if m == syntax.ModeArrayCall {
		return i.ArrayIndex() == j.ArrayIndex()
	} else if m == syntax.ModeMapCall {
		return i.MapKey() == j.MapKey()
	} else {
		return true
	}
}
func (f ForkId) UnmatchedParts(upstream syntax.ForkRootList) syntax.ForkRootList {
	if len(upstream) == 0 {
		return nil
	}
	if len(f) == 0 {
		return upstream
	}
	match := func(i int, upstream syntax.ForkRootList) bool {
		if len(upstream) > i {
			_, err := f.matchPart(upstream[i].Call())
			return err == nil
		}
		return false
	}
	for match(0, upstream) {
		upstream = upstream[1:]
	}
	for len(upstream) > 1 && match(len(upstream)-1, upstream) {
		upstream = upstream[:len(upstream)-1]
	}
	if len(upstream) <= 2 {
		return upstream
	}
	result := upstream[:1]
	if cap(result) != cap(upstream) {
		panic(cap(result))
	}
	for i := 1; i < len(upstream)-1; i++ {
		if _, err := f.matchPart(upstream[i].Call()); err != nil {
			result = append(result, upstream[i])
		} else if cap(result) == cap(upstream) {
			result = make(syntax.ForkRootList, i-1, len(upstream)-1)
			copy(result, upstream[:i])
		}
	}
	return append(result, upstream[len(upstream)-1])
}
type forkMatchNotFoundError struct {
	src  *syntax.CallStm
	fork string
}
func (err forkMatchNotFoundError) Error() string {
	return "no match found for " + err.src.GoString() + " in " + err.fork
}
func (f ForkId) matchPart(src *syntax.CallStm) (*ForkSourcePart, error) {
	for _, part := range f {
		if part.Split.Call == src {
			return part, nil
		}
	}
	return nil, forkMatchNotFoundError{
		src:  src,
		fork: f.GoString(),
	}
}