func (mr *MResolver) GroupsForEntity(entity string) []string {
	mr.uMutex.RLock()
	vset, ok := mr.atom.dm[entity]
	mr.uMutex.RUnlock()
	if !ok {
		return []string{}
	}
	return mr.atom.gs.Filter(vset)
}
GroupsForEntity returns a string slice of groups that include a
given entity.
package mresolver
import (
	"github.com/the-maldridge/bsfilter"
)
func (mr *MResolver) SyncDirectGroups(entity string, groups []string) {
	list := make(map[string]struct{}, len(groups))
	for i := range groups {
		list[groups[i]] = struct{}{}
	}
	mr.uMutex.Lock()
	mr.atom.dm[entity] = list
	mr.uMutex.Unlock()
	mr.l.Trace("Synced direct groups", "entity", entity, "groups", groups)
}
func (mr *MResolver) RemoveEntity(entity string) {
	mr.uMutex.Lock()
	delete(mr.atom.dm, entity)
	mr.uMutex.Unlock()
}
func (mr *MResolver) SyncGroup(group string, include, exclude []string) {
	g := resolvableGroup{
		self:    group,
		include: include,
		exclude: exclude,
	}
	mr.gMutex.Lock()
	mr.atom.gc[group] = &g
	mr.gMutex.Unlock()
	mr.l.Trace("Syncing group", "group", group)
	mr.Resolve(group)
	addAffector := func(affectee, affector string) {
		if mr.atom.ga[affectee] == nil {
			mr.atom.ga[affectee] = make(map[string]struct{})
		}
		mr.atom.ga[affectee][affector] = struct{}{}
	}
	mr.gMutex.Lock()
	for _, g := range include {
		addAffector(g, group)
	}
	for _, g := range exclude {
		addAffector(g, group)
	}
	mr.gMutex.Unlock()
	mr.resolveChanges(group)
}
func (mr *MResolver) resolveChanges(group string) {
	mr.Resolve(group)
	mr.gMutex.RLock()
	cascade := mr.atom.ga[group]
	mr.gMutex.RUnlock()
	for g := range cascade {
		mr.resolveChanges(g)
	}
}
func (mr *MResolver) RemoveGroup(group string) {
	mr.gMutex.Lock()
	delete(mr.atom.gc, group)
	delete(mr.atom.gr, group)
	delete(mr.atom.gt, group)
	delete(mr.atom.ga, group)
	mr.atom.gs.Del(group)
	for _, ga := range mr.atom.ga {
		delete(ga, group)
	}
	mr.gMutex.Unlock()
}
func (mr *MResolver) Resolve(group string) error {
	mr.gMutex.RLock()
	g, ok := mr.atom.gc[group]
	mr.gMutex.RUnlock()
	if !ok {
		mr.l.Error("Insufficient knowledge to resolve group", "group", group)
		return ErrInsufficientKnowledge
	}
	mr.l.Trace("Resolving Group", "group", group)
	res := []bsfilter.Symbol{}
	res = append(res, bsfilter.Symbol{T: bsfilter.SymbolLParen})
	res = append(res, bsfilter.Symbol{T: bsfilter.SymbolIdent, Ident: g.self})
	for _, gn := range g.include {
		res = append(res, bsfilter.Symbol{T: bsfilter.SymbolBinaryOr})
		res = append(res, bsfilter.Symbol{T: bsfilter.SymbolLParen})
		mr.gMutex.RLock()
		r, ok := mr.atom.gt[gn]
		mr.gMutex.RUnlock()
		if !ok {
			mr.l.Trace("Resolution not cached; recursing", "group", gn)
			if err := mr.Resolve(gn); err != nil {
				return err
			}
			mr.gMutex.RLock()
			r = mr.atom.gt[gn]
			mr.gMutex.RUnlock()
		}
		res = append(res, r...)
		res = append(res, bsfilter.Symbol{T: bsfilter.SymbolRParen})
	}
	res = append(res, bsfilter.Symbol{T: bsfilter.SymbolRParen})
	for _, gn := range g.exclude {
		res = append(res, bsfilter.Symbol{T: bsfilter.SymbolBinaryAnd})
		res = append(res, bsfilter.Symbol{T: bsfilter.SymbolUnaryNot})
		res = append(res, bsfilter.Symbol{T: bsfilter.SymbolLParen})
		mr.gMutex.RLock()
		r, ok := mr.atom.gt[gn]
		mr.gMutex.RUnlock()
		if !ok {
			mr.l.Trace("Resolution not cached; recursing", "group", gn)
			if err := mr.Resolve(gn); err != nil {
				return err
			}
			mr.gMutex.RLock()
			r = mr.atom.gt[gn]
			mr.gMutex.RUnlock()
		}
		res = append(res, r...)
		res = append(res, bsfilter.Symbol{T: bsfilter.SymbolRParen})
	}
	expr := bsfilter.NewFromTokens(res)
	mr.gMutex.Lock()
	mr.atom.gt[group] = res
	mr.atom.gr[group] = expr
	mr.atom.gs.Add(group, expr)
	mr.gMutex.Unlock()
	mr.l.Debug("Group Resolved", "group", group, "expression", expr)
	return nil
}
func (mr *MResolver) MembersOfGroup(group string) []string {
	mr.gMutex.RLock()
	exp, ok := mr.atom.gr[group]
	mr.gMutex.RUnlock()
	if !ok {
		return []string{}
	}
	return exp.FilterValues(mr.atom.dm)
}