In ElementSimilarityDiffElementAligner<T>.CalculateBestAlignment (src/DiffLib/Alignment/ElementSimilarityAligner.cs:171), the Modify candidate node is built like this:
AlignmentNode restAfterInsert = CalculateBestAlignment(nodes, collection1, lower1, upper1, collection2, lower2 + 1, upper2);
// ...
AlignmentNode restAfterChange = CalculateBestAlignment(nodes, collection1, lower1 + 1, upper1, collection2, lower2 + 1, upper2);
double similarity = _SimilarityFunc(collection1[lower1], collection2[lower2]);
AlignmentNode? resultChange = null;
if (similarity >= _ModificationThreshold)
resultChange = new AlignmentNode(DiffOperation.Modify, similarity + restAfterInsert.Similarity, restAfterChange.NodeCount + 1, restAfterChange);
A Modify consumes one element from each collection, so its continuation is restAfterChange (which is correctly used as Next and for NodeCount). But the cumulative Similarity is computed as similarity + restAfterInsert.Similarity — the similarity total of the insert branch, which advanced only collection2.
So the node's Similarity/AverageSimilarity (used to pick the winning operation) does not correspond to the path actually linked via Next. It should be:
resultChange = new AlignmentNode(DiffOperation.Modify, similarity + restAfterChange.Similarity, restAfterChange.NodeCount + 1, restAfterChange);
Impact: the aligner can pick a suboptimal operation (insert+delete vs. modify, or the wrong modify position) because the score it compares is inconsistent with the alignment it stores. It happens to not change the outcome on the current small test cases, which is why nothing failed — see the related testing-gap issue.
This looks like a copy/paste slip (restAfterInsert where restAfterChange was meant).
In
ElementSimilarityDiffElementAligner<T>.CalculateBestAlignment(src/DiffLib/Alignment/ElementSimilarityAligner.cs:171), theModifycandidate node is built like this:A
Modifyconsumes one element from each collection, so its continuation isrestAfterChange(which is correctly used asNextand forNodeCount). But the cumulativeSimilarityis computed assimilarity + restAfterInsert.Similarity— the similarity total of the insert branch, which advanced only collection2.So the node's
Similarity/AverageSimilarity(used to pick the winning operation) does not correspond to the path actually linked viaNext. It should be:Impact: the aligner can pick a suboptimal operation (insert+delete vs. modify, or the wrong modify position) because the score it compares is inconsistent with the alignment it stores. It happens to not change the outcome on the current small test cases, which is why nothing failed — see the related testing-gap issue.
This looks like a copy/paste slip (
restAfterInsertwhererestAfterChangewas meant).