func (fg *flowgraph) flatten() []*fgbase.Node {
	if flatDot {
		fgbase.DotOutput = true
	}
	nodes := make([]*fgbase.Node, 0)
	for _, v := range fg.hubs {
		if gv, ok := v.(GraphHub); ok && (flatDot || !fgbase.DotOutput) {
			nodes = gv.(*graphhub).flatten(nodes)
			if fgbase.DotOutput {
				nodes = append(nodes, v.Base().(*fgbase.Node))
				v.Base().(*fgbase.Node).SetDotAttr("style=\"dashed\"")
			} else {
				for i := 0; i < v.NumSource(); i++ {
					v.Source(i).Base().(*fgbase.Edge).Disconnect(v.Base().(*fgbase.Node))
				}
				for i := 0; i < v.NumResult(); i++ {
					v.Result(i).Base().(*fgbase.Edge).Disconnect(v.Base().(*fgbase.Node))
				}
			}
		} else {
			nodes = append(nodes, v.Base().(*fgbase.Node))
		}
	}
	if fgbase.TraceLevel >= fgbase.V {
		fmt.Printf("\n")
		for _, v := range nodes {
			fmt.Printf("// %s\n", v)
		}
		fmt.Printf("\n")
	}
	return nodes
}
flatten connects GraphHub external ports to internal dangling streams
package flowgraph
import (
	"github.com/vectaport/fgbase"
	"flag"
	"fmt"
	"log"
)
type Error string
func (e Error) Error() string {
	return string(e)
}
const EOF = Error("EOF")
var flatDot = false
func ParseFlags() {
	flag.BoolVar(&flatDot, "flatdot", false, "flatten dot output")
	fgbase.ConfigByFlag(map[string]interface{}{"trace": "V"})
	fgbase.TraceStyle = fgbase.New
}
type Flowgraph interface {
	Title() string
	Hub(n int) Hub
	Stream(n int) Stream
	NumHub() int
	NumStream() int
	NewHub(name string, code HubCode, init interface{}) Hub
	NewStream(name string) Stream
	NewGraphHub(name string, code HubCode) GraphHub
	FindHub(name string) Hub
	FindStream(name string) Stream
	Connect(
		upstream Hub, upstreamPort interface{},
		dnstream Hub, dnstreamPort interface{}) Stream
	ConnectInit(
		upstream Hub, upstreamPort interface{},
		dnstream Hub, dnstreamPort interface{},
		init interface{}) Stream
	Run()
}
type flowgraph struct {
	title        string
	hubs         []Hub
	streams      []Stream
	nameToHub    map[string]Hub
	nameToStream map[string]Stream
}
func New(title string) Flowgraph {
	nameToHub := make(map[string]Hub)
	nameToStream := make(map[string]Stream)
	fg := flowgraph{title, nil, nil, nameToHub, nameToStream}
	return &fg
}
func (fg *flowgraph) Title() string {
	return fg.title
}
func (fg *flowgraph) Hub(n int) Hub {
	return fg.hubs[n]
}
func (fg *flowgraph) Stream(n int) Stream {
	return fg.streams[n]
}
func (fg *flowgraph) NumHub() int {
	return len(fg.hubs)
}
func (fg *flowgraph) NumStream() int {
	return len(fg.streams)
}
type fgTransformer struct {
	fg *flowgraph
	t  Transformer
}
func (f *fgTransformer) String() string {
	return fgbase.String(f.t)
}
type fgRetriever struct {
	fg *flowgraph
	r  Retriever
}
func (f *fgRetriever) String() string {
	return fgbase.String(f.r)
}
type fgTransmitter struct {
	fg *flowgraph
	t  Transmitter
}
func (f *fgTransmitter) String() string {
	return fgbase.String(f.t)
}
func (fg *flowgraph) NewHub(name string, code HubCode, init interface{}) Hub {
	var n fgbase.Node
	switch code {
	case Retrieve:
		if _, ok := init.(Retriever); !ok {
			panic(fmt.Sprintf("Hub with Retrieve code not given Retriever for init %T(%+v)", init, init))
		}
		n = fgbase.MakeNode(name, nil, nil, retrieveRdy, retrieveFire)
		init = &fgRetriever{fg, init.(Retriever)}
	case Transmit:
		if _, ok := init.(Transmitter); !ok {
			panic(fmt.Sprintf("Hub with Transmit code not given Transmitter for init %T(%+v)", init, init))
		}
		n = fgbase.MakeNode(name, nil, nil, nil, transmitFire)
		init = &fgTransmitter{fg, init.(Transmitter)}
	case AllOf:
		if _, ok := init.(Transformer); !ok {
			panic(fmt.Sprintf("Hub with AllOf code not given Transformer for init %T(%+v)", init, init))
		}
		n = fgbase.MakeNode(name, nil, nil, nil, allOfFire)
		init = &fgTransformer{fg, init.(Transformer)}
	case OneOf:
		if _, ok := init.(Transformer); !ok {
			panic(fmt.Sprintf("Hub with OneOf code not given Transformer for init %T(%+v)", init, init))
		}
		n = fgbase.MakeNode(name, nil, nil, oneOfRdy, oneOfFire)
		init = &fgTransformer{fg, init.(Transformer)}
	case Wait:
		if _, ok := init.(Transmitter); ok {
			init = &fgTransmitter{fg, init.(Transmitter)}
		}
		n = fgbase.MakeNode(name, nil, nil, waitRdy, waitFire)
	case Select:
		n = fgbase.MakeNode(name, nil, nil, fgbase.SteervRdy, selectFire)
	case Steer:
		n = fgbase.MakeNode(name, nil, nil, fgbase.SteervRdy, fgbase.SteervFire)
	case Cross:
		n = fgbase.MakeNode(name, nil, nil, crossRdy, crossFire)
	case Array:
		n = fgbase.MakeNode(name, nil, []*fgbase.Edge{nil}, nil, fgbase.ArrayFire)
	case Constant:
		n = fgbase.MakeNode(name, nil, []*fgbase.Edge{nil}, nil, fgbase.ConstFire)
	case Pass:
		n = fgbase.MakeNode(name, []*fgbase.Edge{nil}, []*fgbase.Edge{nil}, nil, nil)
	case Split:
		n = fgbase.MakeNode(name, nil, []*fgbase.Edge{nil}, nil, splitFire)
	case Join:
		n = fgbase.MakeNode(name, nil, []*fgbase.Edge{nil}, nil, joinFire)
	case Sink:
		if init != nil {
			if _, ok := init.(Sinker); !ok {
				panic(fmt.Sprintf("Hub with Sink code not given Sinker for init %T(%+v)", init, init))
			}
		}
		n = fgbase.MakeNode(name, []*fgbase.Edge{nil}, nil, nil, fgbase.SinkFire)
	case Add:
		n = fgbase.MakeNode(name, []*fgbase.Edge{nil, nil}, []*fgbase.Edge{nil}, nil, fgbase.AddFire)
	case Subtract:
		n = fgbase.MakeNode(name, []*fgbase.Edge{nil, nil}, []*fgbase.Edge{nil}, nil, fgbase.SubFire)
	case Multiply:
		n = fgbase.MakeNode(name, []*fgbase.Edge{nil, nil}, []*fgbase.Edge{nil}, nil, fgbase.MulFire)
	case Divide:
		n = fgbase.MakeNode(name, []*fgbase.Edge{nil, nil}, []*fgbase.Edge{nil}, nil, fgbase.DivFire)
	case Modulo:
		n = fgbase.MakeNode(name, []*fgbase.Edge{nil, nil}, []*fgbase.Edge{nil}, nil, fgbase.ModFire)
	case And:
		n = fgbase.MakeNode(name, []*fgbase.Edge{nil, nil}, []*fgbase.Edge{nil}, nil, andFire)
	case Or:
		n = fgbase.MakeNode(name, []*fgbase.Edge{nil, nil}, []*fgbase.Edge{nil}, nil, orFire)
	case Not:
		n = fgbase.MakeNode(name, []*fgbase.Edge{nil}, []*fgbase.Edge{nil}, nil, notFire)
	case Shift:
		n = fgbase.MakeNode(name, []*fgbase.Edge{nil, nil}, []*fgbase.Edge{nil}, nil, shiftFire)
	default:
		log.Panicf("Unexpected Hub code NewHub:  %s\n", code)
	}
	if n.Aux == nil {
		n.Aux = init
	}
	h := &hub{&n, fg, code}
	n.Owner = h
	fg.hubs = append(fg.hubs, h)
	fg.nameToHub[name] = h
	return h
}
func (fg *flowgraph) NewStream(name string) Stream {
	e := fgbase.MakeEdge(name, nil)
	s := &stream{&e, fg}
	fg.streams = append(fg.streams, s)
	fg.nameToStream[name] = s
	return s
}
func (fg *flowgraph) NewGraphHub(name string, code HubCode) GraphHub {
	newfg := New(name + "_fg")
	var n fgbase.Node
	switch code {
	case While:
		n = fgbase.MakeNode(name, nil, nil, nil, whileFire)
	case During:
		n = fgbase.MakeNode(name, nil, nil, nil, duringFire)
	case Graph:
		n = fgbase.MakeNode(name, nil, nil, nil, graphFire)
	default:
		log.Panicf("Unexpected HubCode for NewGraphHub:  %v\n", code)
	}
	gh := &graphhub{&hub{&n, fg, code}, newfg, nil, nil}
	n.Owner = gh
	fg.hubs = append(fg.hubs, gh)
	fg.nameToHub[name] = gh
	return gh
}
func (fg *flowgraph) FindHub(name string) Hub {
	return fg.nameToHub[name]
}
func (fg *flowgraph) FindStream(name string) Stream {
	return fg.nameToStream[name]
}
func (fg *flowgraph) Connect(
	upstream Hub, upstreamPort interface{},
	dnstream Hub, dnstreamPort interface{}) Stream {
	return fg.connectInit(upstream, upstreamPort, dnstream, dnstreamPort, nil)
}
func (fg *flowgraph) ConnectInit(
	upstream Hub, upstreamPort interface{},
	dnstream Hub, dnstreamPort interface{},
	init interface{}) Stream {
	return fg.connectInit(upstream, upstreamPort, dnstream, dnstreamPort, init)
}
func (fg *flowgraph) connectInit(
	upstream Hub, upstreamPort interface{},
	dnstream Hub, dnstreamPort interface{},
	init interface{}) Stream {
	checkInternalHub(fg, upstream)
	checkInternalHub(fg, dnstream)
	var us Stream
	var usok bool
	switch v := upstreamPort.(type) {
	case string:
		us, usok = upstream.Result(v), !upstream.Empty()
		if !usok {
			upstream.Panicf("No result port \"%s\" found on Hub \"%s\"\n", v, upstream.Name())
		}
	case int:
		usok = v >= 0 && v < upstream.NumResult()
		if !usok {
			upstream.Panicf("No result port %d found on Hub \"%s\"\n", v, upstream.Name())
		}
		us = upstream.Result(v)
	default:
		upstream.Panicf("Need string or int to specify port on upstream Hub \"%s\"\n", upstream.Name())
	}
	var ds Stream
	var dsok bool
	switch v := dnstreamPort.(type) {
	case string:
		ds, dsok = dnstream.Source(v), !dnstream.Empty()
		if !dsok {
			dnstream.Panicf("No source port \"%s\" found on Hub \"%s\"\n", v, dnstream.Name())
		}
	case int:
		dsok = v >= 0 && v < dnstream.NumSource()
		if !dsok {
			upstream.Panicf("No source port %d found on Hub \"%s\"\n", v, dnstream.Name())
		}
		ds = dnstream.Source(v)
	default:
		dnstream.Panicf("Need string or int to specify port on downstream Hub \"%s\"\n", dnstream.Name())
	}
	if us.Empty() && ds.Empty() {
		s := fg.NewStream("")
		s.Init(init)
		fg.streams = append(fg.streams, s)
		upstream.SetResult(upstreamPort, s)
		dnstream.SetSource(dnstreamPort, s)
		return s
	}
	if us.Empty() {
		upstream.SetResult(upstreamPort, ds)
		return ds
	}
	if ds.Empty() {
		dnstream.SetSource(dnstreamPort, us)
		return us
	}
	return nil
}
func (fg *flowgraph) Run() {
	fg.run()
}
func checkInternalHub(fg Flowgraph, h Hub) {
	if h == nil {
		return
	}
	fgknown := fg
	fgtest := h.Flowgraph()
	if fgknown != fgtest {
		panic(fmt.Sprintf("Hub %q created by flowgraph %q (expected it to be created by flowgraph %q)",
			h.Name(), fgtest.Title(), fgknown.Title()))
	}
}
func checkExternalHub(fg Flowgraph, h Hub) {
	if h == nil {
		return
	}
	fgknown := fg
	fgtest := h.Flowgraph()
	if fgknown == fgtest {
		panic(fmt.Sprintf("External Hub %q created by same flowgraph %q",
			h.Name(), fgtest.Title()))
	}
}
func checkInternalStream(fg Flowgraph, s Stream) {
	if s == nil {
		return
	}
	fgknown := fg
	fgtest := s.Flowgraph()
	if fgknown != fgtest {
		panic(fmt.Sprintf("Stream %q created by flowgraph %q (expected it to be created by flowgraph %q)",
			s.Name(), fgtest.Title(), fgknown.Title()))
	}
}
func checkExternalStream(fg Flowgraph, s Stream) {
	if s == nil {
		return
	}
	fgknown := fg
	fgtest := s.Flowgraph()
	if fgknown == fgtest {
		panic(fmt.Sprintf("External Stream %q created by same flowgraph %q",
			s.Name(), fgtest.Title()))
	}
}