func (n *Network) IsRecurrent(inNode, outNode *NNode, count *int, thresh int) bool {
	*count++
	if *count > thresh {
		return false  
	}
	if inNode == outNode {
		return true
	} else {
		for _, link := range inNode.Incoming {
			if !link.IsRecurrent {
				if n.IsRecurrent(link.InNode, outNode, count, thresh) {
					return true
				}
			}
		}
	}
	return false
}
IsRecurrent This checks a POTENTIAL link between a potential in_node
and potential out_node to see if it must be recurrent.
Use count and thresh to jump out in the case of an infinite loop.
package network
import (
	"bytes"
	"errors"
	"fmt"
	"github.com/yaricom/goNEAT/v2/neat/math"
	"gonum.org/v1/gonum/graph/path"
	"io"
)
type Network struct {
	Id int
	Name string
	Outputs []*NNode
	numLinks int
	allNodes []*NNode
	inputs []*NNode
	controlNodes []*NNode
	allNodesMIMO []*NNode
}
func NewNetwork(in, out, all []*NNode, netId int) *Network {
	n := Network{
		Id:           netId,
		inputs:       in,
		Outputs:      out,
		allNodes:     all,
		numLinks:     -1,
		allNodesMIMO: all,
	}
	return &n
}
func NewModularNetwork(in, out, all, control []*NNode, netId int) *Network {
	n := NewNetwork(in, out, all, netId)
	n.controlNodes = control
	n.allNodesMIMO = append(n.allNodesMIMO, control...)
	return n
}
func (n *Network) FastNetworkSolver() (Solver, error) {
	outputNeuronCount := len(n.Outputs)
	biasNeuronCount := 0
	inList := make([]*NNode, 0)
	biasList := make([]*NNode, 0)
	hiddenList := make([]*NNode, 0)
	for _, ne := range n.allNodes {
		switch ne.NeuronType {
		case BiasNeuron:
			biasNeuronCount += 1
			biasList = append(biasList, ne)
		case InputNeuron:
			inList = append(inList, ne)
		case HiddenNeuron:
			hiddenList = append(hiddenList, ne)
		}
	}
	inputNeuronCount := len(inList)
	totalNeuronCount := len(n.allNodes)
	activations := make([]math.NodeActivationType, totalNeuronCount)
	neuronLookup := make(map[int]int)  
	neuronIndex := processList(0, biasList, activations, neuronLookup)
	neuronIndex = processList(neuronIndex, inList, activations, neuronLookup)
	neuronIndex = processList(neuronIndex, n.Outputs, activations, neuronLookup)
	processList(neuronIndex, hiddenList, activations, neuronLookup)
	biases := make([]float64, totalNeuronCount)
	connections := make([]*FastNetworkLink, 0)
	if inConnects, err := n.processIncomingConnections(inList, biases, neuronLookup); err == nil {
		connections = append(connections, inConnects...)
	} else {
		return nil, err
	}
	if inConnects, err := n.processIncomingConnections(hiddenList, biases, neuronLookup); err == nil {
		connections = append(connections, inConnects...)
	} else {
		return nil, err
	}
	if inConnects, err := n.processIncomingConnections(n.Outputs, biases, neuronLookup); err == nil {
		connections = append(connections, inConnects...)
	} else {
		return nil, err
	}
	modules := make([]*FastControlNode, len(n.controlNodes))
	for i, cn := range n.controlNodes {
		inputs := make([]int, len(cn.Incoming))
		for j, in := range cn.Incoming {
			if inIndex, ok := neuronLookup[in.InNode.Id]; ok {
				inputs[j] = inIndex
			} else {
				return nil, fmt.Errorf("failed to lookup for input neuron with id: %d at control neuron: %d",
					in.InNode.Id, cn.Id)
			}
		}
		outputs := make([]int, len(cn.Outgoing))
		for j, out := range cn.Outgoing {
			if outIndex, ok := neuronLookup[out.OutNode.Id]; ok {
				outputs[j] = outIndex
			} else {
				return nil, fmt.Errorf("failed to lookup for output neuron with id: %d at control neuron: %d",
					out.InNode.Id, cn.Id)
			}
		}
		modules[i] = &FastControlNode{InputIndexes: inputs, OutputIndexes: outputs, ActivationType: cn.ActivationType}
	}
	return NewFastModularNetworkSolver(biasNeuronCount, inputNeuronCount, outputNeuronCount, totalNeuronCount,
		activations, connections, biases, modules), nil
}
func processList(startIndex int, nList []*NNode, activations []math.NodeActivationType, neuronLookup map[int]int) int {
	for _, ne := range nList {
		activations[startIndex] = ne.ActivationType
		neuronLookup[ne.Id] = startIndex
		startIndex += 1
	}
	return startIndex
}
func (n *Network) processIncomingConnections(nList []*NNode, biases []float64, neuronLookup map[int]int) ([]*FastNetworkLink, error) {
	connections := make([]*FastNetworkLink, 0)
	for _, ne := range nList {
		if targetIndex, ok := neuronLookup[ne.Id]; ok {
			for _, in := range ne.Incoming {
				if sourceIndex, ok := neuronLookup[in.InNode.Id]; ok {
					if in.InNode.NeuronType == BiasNeuron {
						biases[targetIndex] += in.ConnectionWeight
					} else {
						conn := FastNetworkLink{
							SourceIndex: sourceIndex,
							TargetIndex: targetIndex,
							Weight:      in.ConnectionWeight,
						}
						connections = append(connections, &conn)
					}
				} else {
					return nil, fmt.Errorf("failed to lookup for source neuron with id: %d", in.InNode.Id)
				}
			}
		} else {
			return nil, fmt.Errorf("failed to lookup for target neuron with id: %d", ne.Id)
		}
	}
	return connections, nil
}
func (n *Network) IsControlNode(nid int) bool {
	for _, cn := range n.controlNodes {
		if cn.Id == nid {
			return true
		}
	}
	return false
}
func (n *Network) Flush() (res bool, err error) {
	res = true
	for _, node := range n.allNodes {
		node.Flushback()
		err = node.FlushbackCheck()
		if err != nil {
			res = false
			break
		}
	}
	return res, err
}
func (n *Network) PrintActivation() string {
	out := bytes.NewBufferString(fmt.Sprintf("Network %s with id %d outputs: (", n.Name, n.Id))
	for i, node := range n.Outputs {
		_, _ = fmt.Fprintf(out, "[Output #%d: %s] ", i, node)
	}
	_, _ = fmt.Fprint(out, ")")
	return out.String()
}
func (n *Network) PrintInput() string {
	out := bytes.NewBufferString(fmt.Sprintf("Network %s with id %d inputs: (", n.Name, n.Id))
	for i, node := range n.inputs {
		_, _ = fmt.Fprintf(out, "[Input #%d: %s] ", i, node)
	}
	_, _ = fmt.Fprint(out, ")")
	return out.String()
}
func (n *Network) OutputIsOff() bool {
	for _, node := range n.Outputs {
		if node.ActivationsCount == 0 {
			return true
		}
	}
	return false
}
func (n *Network) ActivateSteps(maxSteps int) (bool, error) {
	addAmount := 0.0
	oneTime := false
	abortCount := 0
	for n.OutputIsOff() || !oneTime {
		if abortCount >= maxSteps {
			return false, NetErrExceededMaxActivationAttempts
		}
		for _, np := range n.allNodes {
			if np.IsNeuron() {
				np.ActivationSum = 0.0  
				for _, link := range np.Incoming {
					if !link.IsTimeDelayed {
						addAmount = link.ConnectionWeight * link.InNode.GetActiveOut()
						if link.InNode.isActive || link.InNode.IsSensor() {
							np.isActive = true
						}
					} else {
						addAmount = link.ConnectionWeight * link.InNode.GetActiveOutTd()
					}
					np.ActivationSum += addAmount
				}  
			}  
		}  
		for _, np := range n.allNodes {
			if np.IsNeuron() {
				if np.isActive {
					err := ActivateNode(np, math.NodeActivators)
					if err != nil {
						return false, err
					}
				}
			}
		}
		for _, cn := range n.controlNodes {
			cn.isActive = false
			err := ActivateModule(cn, math.NodeActivators)
			if err != nil {
				return false, err
			}
			cn.isActive = true
		}
		oneTime = true
		abortCount += 1
	}
	return true, nil
}
func (n *Network) Activate() (bool, error) {
	return n.ActivateSteps(20)
}
func (n *Network) ForwardSteps(steps int) (res bool, err error) {
	for i := 0; i < steps; i++ {
		res, err = n.Activate()
		if err != nil {
			break
		}
	}
	return res, err
}
func (n *Network) RecursiveSteps() (bool, error) {
	return false, errors.New("RecursiveSteps is not implemented")
}
func (n *Network) Relax(_ int, _ float64) (bool, error) {
	return false, errors.New("relax is not implemented")
}
func (n *Network) LoadSensors(sensors []float64) error {
	counter := 0
	if len(sensors) == len(n.inputs) {
		for _, node := range n.inputs {
			if node.IsSensor() {
				node.SensorLoad(sensors[counter])
				counter += 1
			}
		}
	} else {
		for _, node := range n.inputs {
			if node.NeuronType == InputNeuron {
				node.SensorLoad(sensors[counter])
				counter += 1
			} else {
				node.SensorLoad(1.0)  
			}
		}
	}
	return nil
}
func (n *Network) ReadOutputs() []float64 {
	outs := make([]float64, len(n.Outputs))
	for i, o := range n.Outputs {
		outs[i] = o.Activation
	}
	return outs
}
func (n *Network) NodeCount() int {
	if len(n.controlNodes) == 0 {
		return len(n.allNodes)
	} else {
		return len(n.allNodes) + len(n.controlNodes)
	}
}
func (n *Network) LinkCount() int {
	n.numLinks = 0
	for _, node := range n.allNodes {
		n.numLinks += len(node.Incoming)
	}
	if len(n.controlNodes) != 0 {
		for _, node := range n.controlNodes {
			n.numLinks += len(node.Incoming)
			n.numLinks += len(node.Outgoing)
		}
	}
	return n.numLinks
}
func (n *Network) Complexity() int {
	return n.NodeCount() + n.LinkCount()
}