func reduceOnce(p Process) (changed bool, err error) {
	switch p := p.(type) {
	case *NilProcess:
		return false, nil
	case *Par:
		var sends map[string]*Process  
		var recvs map[string]*Process  
		for i, proc := range p.Procs {
			switch proc := proc.(type) {
			case *Par:
				return reduceOnce(proc)
			case *Recv:
				if recvs == nil {
					recvs = make(map[string]*Process)
				}
				if IsFreeName(proc.Chan) {
					ch := proc.Chan.Ident()
					if _, exists := recvs[ch]; !exists {
						recvs[ch] = &p.Procs[i]
					}
				}
			case *Send:
				if sends == nil {
					sends = make(map[string]*Process)
				}
				if IsFreeName(proc.Chan) {
					ch := proc.Chan.Ident()
					if _, exists := sends[ch]; !exists {
						sends[ch] = &p.Procs[i]
					}
				}
			}
		}
		for ch, s := range sends {
			if r, hasSharedChan := recvs[ch]; hasSharedChan {
				recv := (*r).(*Recv)
				send := (*s).(*Send)
				if err := Subst(recv.Cont, send.Vals, recv.Vars); err != nil {
					return false, err
				}
				*s, *r = NewNilProcess(), recv.Cont
				return true, nil
			}
		}
		return false, nil
	case *Recv:
		return false, nil
	case *Restrict:
		return reduceOnce(p.Proc)
	case *Repeat:
		return reduceOnce(p.Proc)
	case *Send:
		return false, nil
	default:
		return false, UnknownProcessError{Proc: p}
	}
}
reduceOnce performs a single step of reduction.

This reduction combines multiple congruence relations
and attempts to perform an interaction.
package asyncpi
import (
	"go.nickng.io/asyncpi/internal/errors"
	"go.nickng.io/asyncpi/internal/name"
)
func errSubst(err error) error {
	return errors.Wrap(err, "substitution failed")
}
func Subst(p Process, vs, xs []Name) error {
	if len(xs) != len(vs) {
		return errSubst(ErrInvalid)
	}
	procs := []Process{p}
	for len(procs) > 0 {
		p, procs = procs[0], procs[1:]
		switch p := p.(type) {
		case *NilProcess:
		case *Par:
			procs = append(procs, p.Procs...)
		case *Recv:
			for i, x := range xs {
				if IsSameName(p.Chan, x) {
					if ch, canSetName := p.Chan.(name.Setter); canSetName {
						ch.SetName(vs[i].Ident())
					}
				}
				for _, rv := range p.Vars {
					if IsSameName(rv, x) {
						if ch, canSetName := rv.(name.Setter); canSetName {
							ch.SetName(vs[i].Ident())
						}
					}
				}
			}
			procs = append(procs, p.Cont)
		case *Restrict:
			procs = append(procs, p.Proc)
		case *Repeat:
			procs = append(procs, p.Proc)
		case *Send:
			for i, x := range xs {
				if IsSameName(p.Chan, x) {
					if ch, canSetName := p.Chan.(name.Setter); canSetName {
						ch.SetName(vs[i].Ident())
					}
				}
			}
		default:
			return errSubst(UnknownProcessError{Proc: p})
		}
	}
	return nil
}
func Reduce1(p Process) (changed bool, err error) {
	changed, err = reduceOnce(p)
	return changed, errors.Wrap(err, "cannot reduce process")
}