private static void SelectCrossoverPoint(IRandom random, ISymbolicExpressionTree parent0, int maxBranchLength, int maxBranchDepth, out CutPoint crossoverPoint, PercentMatrix probabilities)
        {
            List <CutPoint> internalCrossoverPoints = new List <CutPoint>();
            List <CutPoint> leafCrossoverPoints     = new List <CutPoint>();

            parent0.Root.ForEachNodePostfix((n) => {
                if (n.SubtreeCount > 0 && n != parent0.Root)
                {
                    //avoid linq to reduce memory pressure
                    for (int i = 0; i < n.SubtreeCount; i++)
                    {
                        var child = n.GetSubtree(i);
                        if (child.GetLength() <= maxBranchLength &&
                            child.GetDepth() <= maxBranchDepth)
                        {
                            if (child.SubtreeCount > 0)
                            {
                                internalCrossoverPoints.Add(new CutPoint(n, child));
                            }
                            else
                            {
                                leafCrossoverPoints.Add(new CutPoint(n, child));
                            }
                        }
                    }

                    // add one additional extension point if the number of sub trees for the symbol is not full
                    if (n.SubtreeCount < n.Grammar.GetMaximumSubtreeCount(n.Symbol))
                    {
                        // empty extension point
                        internalCrossoverPoints.Add(new CutPoint(n, n.SubtreeCount));
                    }
                }
            }
                                            );

            List <CutPoint> allCrossoverPoints = new List <CutPoint>();

            allCrossoverPoints.AddRange(internalCrossoverPoints);
            allCrossoverPoints.AddRange(leafCrossoverPoints);
            var weights = allCrossoverPoints.Select(x => probabilities[probabilities.RowNames.ToList().IndexOf(x.Child.Symbol.Name), 0]);

            crossoverPoint = allCrossoverPoints.SampleProportional(random, 1, weights).First();
        }
        public ISymbolicExpressionTree Cross(IRandom random,
                                             ISymbolicExpressionTree parent0, ISymbolicExpressionTree parent1,
                                             int maxTreeLength, int maxTreeDepth, PercentMatrix probabilities)
        {
            // select a random crossover point in the first parent
            CutPoint crossoverPoint0;

            SelectCrossoverPoint(random, parent0, maxTreeLength, maxTreeDepth, out crossoverPoint0, probabilities);

            int childLength = crossoverPoint0.Child != null?crossoverPoint0.Child.GetLength() : 0;

            // calculate the max length and depth that the inserted branch can have
            int maxInsertedBranchLength = maxTreeLength - (parent0.Length - childLength);
            int maxInsertedBranchDepth  = maxTreeDepth - parent0.Root.GetBranchLevel(crossoverPoint0.Parent);

            List <ISymbolicExpressionTreeNode> allowedBranches = new List <ISymbolicExpressionTreeNode>();

            parent1.Root.ForEachNodePostfix((n) => {
                if (n.GetLength() <= maxInsertedBranchLength &&
                    n.GetDepth() <= maxInsertedBranchDepth && crossoverPoint0.IsMatchingPointType(n))
                {
                    allowedBranches.Add(n);
                }
            });
            // empty branch
            if (crossoverPoint0.IsMatchingPointType(null))
            {
                allowedBranches.Add(null);
            }

            if (allowedBranches.Count == 0)
            {
                return(parent0);
            }
            else
            {
                CutPointSymbolParameter.ActualValue = (ISymbol)crossoverPoint0.Parent.Symbol.Clone();
                var selectedBranch = SelectRandomBranch(random, allowedBranches);

                if (crossoverPoint0.Child != null)
                {
                    // manipulate the tree of parent0 in place
                    // replace the branch in tree0 with the selected branch from tree1
                    crossoverPoint0.Parent.RemoveSubtree(crossoverPoint0.ChildIndex);

                    RemovedBranchParameter.ActualValue = new SymbolicExpressionTree((ISymbolicExpressionTreeNode)crossoverPoint0.Child.Clone());
                    if (selectedBranch != null)
                    {
                        crossoverPoint0.Parent.InsertSubtree(crossoverPoint0.ChildIndex, selectedBranch);

                        AddedBranchParameter.ActualValue = new SymbolicExpressionTree((ISymbolicExpressionTreeNode)selectedBranch.Clone());
                    }
                }
                else
                {
                    // child is null (additional child should be added under the parent)
                    if (selectedBranch != null)
                    {
                        crossoverPoint0.Parent.AddSubtree(selectedBranch);

                        AddedBranchParameter.ActualValue = new SymbolicExpressionTree((ISymbolicExpressionTreeNode)selectedBranch.Clone());
                    }
                }
                return(parent0);
            }
        }