public static void crossover(GeneticTree current, GeneticTree other)
        {
            List <string> currentIndices  = current._nodeIndices.Where(x => x.Length != 1).ToList();
            List <string> otherIndices    = other._nodeIndices.Where(x => x.Length != 1).ToList();
            int           currentIndexAux = Rng.Rnd.Next(currentIndices.Count);
            int           otherIndexAux   = Rng.Rnd.Next(otherIndices.Count);
            string        currentIndex    = currentIndices[currentIndexAux];
            string        otherIndex      = otherIndices[otherIndexAux];

            GeneticNode iterCurrent = current._root;
            GeneticNode iterOther   = other._root;

            for (int i = 1; i < currentIndex.Length; i++)
            {
                int index = GeneticTree._TREE_NODES_CHAR_TO_INT[currentIndex[i]];
                iterCurrent = iterCurrent._children._children[index];
            }

            for (int i = 1; i < otherIndex.Length; i++)
            {
                int index = GeneticTree._TREE_NODES_CHAR_TO_INT[otherIndex[i]];
                iterOther = iterOther._children._children[index];
            }

            current.attachNode(iterOther, currentIndex);
            other.attachNode(iterCurrent, otherIndex);

            current.recalculateIndices();
            other.recalculateIndices();
        }
        public static void mutateOpportunistic(GeneticTree current)
        {
            NodeStatistics       stats         = current.allNodesStats(current._root, 0, "", 0);
            List <NodeStatsPair> opportunities = new List <NodeStatsPair>();

            foreach (var item in current._nodeStatsDictionary)
            {
                //Debug.WriteLine("Node " + item.Key + " /// Operator variance: " + item.Value._opVariance + " Variable variance: " + item.Value._varVariance + " Avg depth: " + item.Value._avgDepth + " Avg branching: " + item.Value._avgBranching);
                opportunities.Add(new NodeStatsPair(item.Key, item.Value._avgBranching + item.Value._avgDepth + item.Value._opVariance + item.Value._varVariance));
            }

            float max = opportunities.Max(x => x._mutationValue);

            foreach (var item in opportunities)
            {
                item._mutationValue = max - item._mutationValue + 1; // make less branchy, depthy and variant more likely to be mutated
            }
            opportunities = opportunities.OrderByDescending(x => x._mutationValue).ToList();


            float[] wSum = new float[opportunities.Count];

            wSum[0] = opportunities[0]._mutationValue;
            for (int i = 1; i < wSum.Length; i++)
            {
                wSum[i] = opportunities[i]._mutationValue + wSum[i - 1];
            }

            float sumF = opportunities.Sum(x => x._mutationValue);

            double centil = Rng.Rnd.NextDouble() * sumF;

            int index = 0;

            for (int i = 0; i < opportunities.Count && centil < wSum[i]; i++)
            {
                index = i;
            }

            string currentIndex = opportunities[index]._node;

            int         newDepth = (int)current._maxDepth - currentIndex.Length + 1;
            GeneticTree added    = new GeneticTree((uint)newDepth, current._maxBranching);

            GeneticNode tmp = current._root;

            for (int i = 1; i < currentIndex.Length; i++) // traversing to find proper index
            {
                index = GeneticTree._TREE_NODES_CHAR_TO_INT[currentIndex[i]];
                tmp   = tmp._children._children[index];
            }

            current.attachNode(added._root, currentIndex);

            current.recalculateIndices();
        }
        public static void mutateSwap(GeneticTree current)
        {
            List <string> currentIndices  = current._nodeIndices.Where(x => x.Length != 1).ToList();
            int           currentIndexAux = Rng.Rnd.Next(currentIndices.Count);
            string        currentIndex    = currentIndices[currentIndexAux];

            List <string> otherIndices = current._nodeIndices.
                                         Where(x => x.Length != 1).ToList();

            List <string> shorter = otherIndices.Where(x => x.Length <= currentIndex.Length).Where(x => currentIndex.Substring(0, x.Length).CompareTo(x) != 0).ToList();
            List <string> longer  = otherIndices.Where(x => x.Length > currentIndex.Length).Where(x => x.Substring(0, currentIndex.Length).CompareTo(currentIndex) != 0).ToList();


            otherIndices = shorter.Union(longer).ToList();
            if (otherIndices == null || otherIndices.Count == 0)
            {
                return;
            }

            int    otherIndexAux = Rng.Rnd.Next(otherIndices.Count);
            string otherIndex    = otherIndices[otherIndexAux];


            GeneticNode tmp  = current._root;
            GeneticNode tmp2 = current._root;

            for (int i = 1; i < currentIndex.Length; i++)
            {
                int index = GeneticTree._TREE_NODES_CHAR_TO_INT[currentIndex[i]];
                tmp = tmp._children._children[index];
            }

            for (int i = 1; i < otherIndex.Length; i++)
            {
                int index = GeneticTree._TREE_NODES_CHAR_TO_INT[otherIndex[i]];
                tmp2 = tmp2._children._children[index];
            }

            current.attachNode(tmp2, currentIndex);
            current.attachNode(tmp, otherIndex);

            current.recalculateIndices();
        }
        public static void mutateReplace(GeneticTree current)
        {
            List <string> currentIndices  = current._nodeIndices.Where(x => x.Length != 1).ToList();
            int           currentIndexAux = Rng.Rnd.Next(currentIndices.Count);
            string        currentIndex    = currentIndices[currentIndexAux];

            int         newDepth = (int)current._maxDepth - currentIndex.Length + 1;
            GeneticTree added    = new GeneticTree((uint)newDepth, current._maxBranching);

            GeneticNode tmp = current._root;

            for (int i = 1; i < currentIndex.Length; i++) // traversing to find proper index
            {
                int index = GeneticTree._TREE_NODES_CHAR_TO_INT[currentIndex[i]];
                tmp = tmp._children._children[index];
            }

            current.attachNode(added._root, currentIndex);

            current.recalculateIndices();
        }