private static double Calculate(ITreeProgram <TOutput> prog1, ITreeProgram <TOutput> prog2) { // checks simple cases if (prog1 == null || prog2 == null) { return(0); } if (prog1.Equals(prog2)) { return(1); } // replace all common sub-programs by weighted variables var ignorePrograms = new HashSet <ITreeProgram <TOutput> >(); var minCount = Math.Min(prog1.Length, prog2.Length); for (var i = 0; i < minCount; i++) { var largestCommon = GetLargestCommon(ref prog1, ref prog2, ignorePrograms); if (largestCommon == null || largestCommon.Length < 2) { break; } var newSubProgram = new WeightedVariable(largestCommon.Expression, largestCommon.Length); prog1 = prog1.Replace(largestCommon, newSubProgram); prog2 = prog2.Replace(largestCommon, newSubProgram); ignorePrograms.Add(newSubProgram); } // gets sub-programs var subProgs1 = new List <ITreeProgram <TOutput> > { prog1 }; subProgs1.AddRange(prog1.GetSubPrograms()); var subProgs2 = new List <ITreeProgram <TOutput> > { prog2 }; subProgs2.AddRange(prog2.GetSubPrograms()); // tries to align both trees in all possible ways var minRelCost = double.MaxValue; for (var i = 0u; i < prog1.Length; i++) { for (var j = 0u; j < prog2.Length; j++) { // alignment is determined by the starting indexes of both trees var idx1 = i; var idx2 = j; // adds cost of having to add previous nodes (shifting/alignment cost) var cost = i + j; var totalCost = cost; // gets edit cost between sub-programs GetEditCost(subProgs1, subProgs2, ref idx1, ref idx2, ref cost, ref totalCost); // checks cost of having to add remaining nodes var remainder = prog1.Length - idx1 + prog2.Length - idx2 - 2; cost += remainder; totalCost += remainder; var relCost = (double)cost / totalCost; // updates min relative cost if (relCost < minRelCost) { minRelCost = relCost; } } } return(1d - minRelCost); }