/// <summary>
        /// Add a ConnectionGene to the builder, but only if the connection is not already present (as determined by it's neuron ID endpoints).
        /// </summary>
        /// <param name="connectionGene">The connection to add.</param>
        /// <param name="parentGenome">The connection's parent genome. This is used to obtain NeuronGene(s) for the connection endpoints.</param>
        /// <param name="overwriteExisting">A flag that indicates if this connection should take precedence over an existing connection with
        /// the same endpoints.</param>
        public void TryAddGene(ConnectionGene connectionGene, NeatGenome parentGenome, bool overwriteExisting)
        {
            // Check if a matching gene has already been added.
            ConnectionEndpointsStruct connectionKey = new ConnectionEndpointsStruct(connectionGene.SourceNodeId, connectionGene.TargetNodeId);

            ConnectionGene existingConnectionGene;

            if (!_connectionGeneDictionary.TryGetValue(connectionKey, out existingConnectionGene))
            {   // Add new connection gene.
                ConnectionGene connectionGeneCopy = new ConnectionGene(connectionGene);
                _connectionGeneDictionary.Add(connectionKey, connectionGeneCopy);

                // Insert connection gene into a list. Use more efficient approach (append to end) if we know the gene belongs at the end.
                if (connectionGeneCopy.InnovationId > _highestConnectionGeneId)
                {
                    _connectionGeneList.Add(connectionGeneCopy);
                    _highestConnectionGeneId = connectionGeneCopy.InnovationId;
                }
                else
                {
                    _connectionGeneList.InsertIntoPosition(connectionGeneCopy);
                }

                // Add neuron genes (if not already added).
                // Source neuron.
                NeuronGene srcNeuronGene;
                if (!_neuronDictionary.TryGetValue(connectionGene.SourceNodeId, out srcNeuronGene))
                {
                    srcNeuronGene = parentGenome.NeuronGeneList.GetNeuronById(connectionGene.SourceNodeId);
                    srcNeuronGene = new NeuronGene(srcNeuronGene, false); // Make a copy.
                    _neuronDictionary.Add(srcNeuronGene.Id, srcNeuronGene);
                }

                // Target neuron.
                NeuronGene tgtNeuronGene;
                if (!_neuronDictionary.TryGetValue(connectionGene.TargetNodeId, out tgtNeuronGene))
                {
                    tgtNeuronGene = parentGenome.NeuronGeneList.GetNeuronById(connectionGene.TargetNodeId);
                    tgtNeuronGene = new NeuronGene(tgtNeuronGene, false); // Make a copy.
                    _neuronDictionary.Add(tgtNeuronGene.Id, tgtNeuronGene);
                }

                // Register connectivity with each neuron.
                srcNeuronGene.TargetNeurons.Add(tgtNeuronGene.Id);
                tgtNeuronGene.SourceNeurons.Add(srcNeuronGene.Id);
            }
            else if (overwriteExisting)
            {   // The genome we are building already has a connection with the same neuron endpoints as the one we are
                // trying to add. It didn't match up during correlation because it has a different innovation number, this
                // is possible because the innovation history buffers throw away old innovations in a FIFO manner in order
                // to prevent them from bloating.

                // Here the 'overwriteExisting' flag is set so the gene we are currently trying to add is probably from the
                // fitter parent, and therefore we want to use its connection weight in place of the existing gene's weight.
                existingConnectionGene.Weight = connectionGene.Weight;
            }
        }
        public void ConectionBufferTest()
        {
            NeatGenomeFactory genomeFactory = new NeatGenomeFactory(3, 1);

            for (uint i = 0; i < 200000; i++)
            {
                ConnectionEndpointsStruct endpointsStruct = new ConnectionEndpointsStruct(i, i+1);
                AddedNeuronGeneStruct addedNeuronGeneStruct = new AddedNeuronGeneStruct(genomeFactory.InnovationIdGenerator);

                genomeFactory.AddedConnectionBuffer.Enqueue(endpointsStruct, i);
                genomeFactory.AddedNeuronBuffer.Enqueue(i, addedNeuronGeneStruct);
            }

            //Assert.Fail();
        }
        /// <summary>
        /// Performs an integrity check on the network definition.
        /// Returns true if OK.
        /// </summary>
        public bool PerformIntegrityCheck()
        {
            // We will always have at least a bias and an output.
            int count = _nodeList.Count;
            if(count < 2) {
                Debug.WriteLine(string.Format("Node list has less than the minimum number of neuron genes [{0}]", count));
                return false;
            }

            // Check bias neuron.
            if(NodeType.Bias != _nodeList[0].NodeType) {
                Debug.WriteLine("Missing bias gene");
                return false;
            }

            if(0u != _nodeList[0].Id) {
                Debug.WriteLine(string.Format("Bias neuron ID != 0. [{0}]",  _nodeList[0].Id));
                return false;
            }

            // Check input nodes.
            uint prevId = 0u;
            int idx = 1;
            for(int i=0; i<_inputNodeCount; i++, idx++)
            {
                if(NodeType.Input != _nodeList[idx].NodeType) {
                    Debug.WriteLine(string.Format("Invalid node type. Expected Input, got [{0}]", _nodeList[idx].NodeType));
                    return false;
                }

                if(_nodeList[idx].Id <= prevId) {
                    Debug.WriteLine("Input node is out of order and/or a duplicate.");
                    return false;
                }

                prevId = _nodeList[idx].Id;
            }

            // Check output neurons.
            for(int i=0; i<_outputNodeCount; i++, idx++)
            {
                if(NodeType.Output != _nodeList[idx].NodeType) {
                    Debug.WriteLine(string.Format("Invalid node type. Expected Output, got [{0}]", _nodeList[idx].NodeType));
                    return false;
                }

                if(_nodeList[idx].Id <= prevId) {
                    Debug.WriteLine("Output node is out of order and/or a duplicate.");
                    return false;
                }

                prevId = _nodeList[idx].Id;
            }

            // Check hidden neurons.
            // All remaining neurons should be hidden neurons.
            for(; idx<count; idx++)
            {
                if(NodeType.Hidden != _nodeList[idx].NodeType) {
                    Debug.WriteLine(string.Format("Invalid node type. Expected Hidden, got [{0}]", _nodeList[idx].NodeType));
                    return false;
                }

                if(_nodeList[idx].Id <= prevId) {
                    Debug.WriteLine("Hidden node is out of order and/or a duplicate.");
                    return false;
                }

                prevId = _nodeList[idx].Id;
            }

            // Check connections.
            count = _connectionList.Count;
            if(0 == count) 
            {   // At least one connection is required, connectionless genomes are pointless.
                Debug.WriteLine("Zero connections.");
                return false;
            }

            // Check for multiple connections between nodes.
            // Build a dictionary of connection endpoints.
            Dictionary<ConnectionEndpointsStruct, object> endpointDict = new Dictionary<ConnectionEndpointsStruct,object>(count);
            foreach(NetworkConnection conn in _connectionList)
            {
                ConnectionEndpointsStruct key = new ConnectionEndpointsStruct(conn.SourceNodeId, conn.TargetNodeId);
                if(endpointDict.ContainsKey(key))
                {
                    Debug.WriteLine("Connection error. A connection between the specified endpoints already exists.");
                    return false;
                }
                endpointDict.Add(key, null);
            }

            // Integrity check OK.
            return true;
        }
        /// <summary>
        /// Add a ConnectionGene to the builder, but only if the connection is not already present (as determined by it's neuron ID endpoints).
        /// </summary>
        /// <param name="connectionGene">The connection to add.</param>
        /// <param name="parentGenome">The conenction's parent genome. This is used to obtain NeuronGene(s) for the connection endpoints.</param>
        /// <param name="overwriteExisting">A flag that indicates if this connection should take precedence oevr an existing connection with
        /// the same endpoints.</param>
        public void TryAddGene(ConnectionGene connectionGene, NeatGenome parentGenome, bool overwriteExisting)
        {
            // Check if a matching gene has already been added.
            ConnectionEndpointsStruct connectionKey = new ConnectionEndpointsStruct(connectionGene.SourceNodeId, connectionGene.TargetNodeId);
            
            ConnectionGene existingConnectionGene;
            if(!_connectionGeneDictionary.TryGetValue(connectionKey, out existingConnectionGene))
            {   // Add new connection gene.
                ConnectionGene connectionGeneCopy = new ConnectionGene(connectionGene);
                _connectionGeneDictionary.Add(connectionKey, connectionGeneCopy);

                // Insert connection gene into a list. Use more efficient approach (append to end) if we know the gene belongs at the end.
                if(connectionGeneCopy.InnovationId > _highestConnectionGeneId) {
                    _connectionGeneList.Add(connectionGeneCopy);
                    _highestConnectionGeneId = connectionGeneCopy.InnovationId;
                } else {
                    _connectionGeneList.InsertIntoPosition(connectionGeneCopy);
                }

                // Add neuron genes (if not already added).
                // Source neuron.
                NeuronGene srcNeuronGene;
                if(!_neuronDictionary.TryGetValue(connectionGene.SourceNodeId, out srcNeuronGene))
                {
                    srcNeuronGene = parentGenome.NeuronGeneList.GetNeuronById(connectionGene.SourceNodeId);
                    srcNeuronGene = new NeuronGene(srcNeuronGene, false); // Make a copy.
                    _neuronDictionary.Add(srcNeuronGene.Id, srcNeuronGene);
                }

                // Target neuron.
                NeuronGene tgtNeuronGene;
                if(!_neuronDictionary.TryGetValue(connectionGene.TargetNodeId, out tgtNeuronGene))
                {
                    tgtNeuronGene = parentGenome.NeuronGeneList.GetNeuronById(connectionGene.TargetNodeId);
                    tgtNeuronGene = new NeuronGene(tgtNeuronGene, false); // Make a copy.
                    _neuronDictionary.Add(tgtNeuronGene.Id, tgtNeuronGene);
                }

                // Register connectivity with each neuron.
                srcNeuronGene.TargetNeurons.Add(tgtNeuronGene.Id);
                tgtNeuronGene.SourceNeurons.Add(srcNeuronGene.Id);
            }
            else if(overwriteExisting)
            {   // The genome we are building already has a connection with the same neuron endpoints as the one we are
                // trying to add. It didn't match up during correlation because it has a different innovation number, this
                // is possible because the innovation history buffers throw away old innovations in a FIFO manner in order
                // to prevent them from bloating.

                // Here the 'overwriteExisting' flag is set so the gene we are currently trying to add is probably from the
                // fitter parent, and therefore we want to use its connection weight in place of the existing gene's weight.
                existingConnectionGene.Weight = connectionGene.Weight;
            }
        }