private float CompareLists(IList left, IList right, float threshold, bool output) { // Given two lists, find the elements in the list that correspond. // Two elements correspond if their 'difference metric' is less than // or equal to threshold. For the hunks of correspondent items, // recursively descend into items not truly equal. For hunks of // irreconsiliable material, raise the threshold to the next useful // level and rescan the items. if (left == null) throw new ArgumentNullException("left"); if (right == null) throw new ArgumentNullException("right"); if (left.Count == 0 && right.Count == 0) return 0; NodeComparerWrapper comparer = new NodeComparerWrapper(threshold, this); Diff diff = new Diff(left, right, comparer, new HashCodeProvider(this)); int nitems = 0, ndiffs = 0; foreach (Diff.Hunk hunk in diff) { if (hunk.Same || (hunk.Left.Count == 1 && hunk.Right.Count == 1)) { // This comprises a block of one-to-one correspondent items who // differ by no more than the threshold value. nitems += hunk.Left.Count; bool inSameRegion = false; for (int i = 0; i < hunk.Left.Count; i++) { object oleft = hunk.Left[i]; object oright = hunk.Right[i]; NodeInterface ileft = GetInterface(oleft); NodeInterface iright = GetInterface(oright); IList cleft = null, cright = null; cleft = ileft.GetChildren(oleft); cright = iright.GetChildren(oright); float comp = 0; if (ileft == iright) comp = ileft.Compare(oleft, oright, this); // If the nodes are equal, emit one node. if (ileft == iright && comp == 0) { if (output) { if (!inSameRegion) { WritePushSame(); inSameRegion = true; } WriteNodeSame(ileft, oleft, oright); } // Recurse into the lists of each node. } else if (ileft == iright && cleft != null && cright != null && cleft.Count > 0 && cright.Count > 0 && comp <= 1.0) { if (output && inSameRegion) { WritePopSame(); inSameRegion = false; } if (output) WritePushNode(ileft, oleft, oright); float d = CompareLists(cleft, cright, 0, output); d *= hunk.Left.Count; if (d < 1) d = 1; ndiffs += (int)d; if (output) WritePopNode(); // The nodes are not equal, so emit removed and added nodes. } else { if (output && inSameRegion) { WritePopSame(); inSameRegion = false; } if (output) WriteNodeChange(ileft, oleft, iright, oright); ndiffs += hunk.Left.Count; } } if (output && inSameRegion) WritePopSame(); } else { // We have some arbitrary sets on the left and right side. // Try to match nodes on the left with nodes on the right. ArrayList leftitems = new ArrayList(hunk.Left); ArrayList rightitems = new ArrayList(hunk.Right); while (leftitems.Count > 0 && rightitems.Count > 0) { // Find the first node on the left side that matches // a node on the right side. int li = -1, ri = -1; float bestD = 1; for (int lii = 0; lii < leftitems.Count; lii++) { object oleft = leftitems[lii]; NodeInterface ileft = GetInterface(oleft); for (int rii = 0; rii < rightitems.Count; rii++) { object oright = rightitems[rii]; NodeInterface iright = GetInterface(oright); float d = 1; if (ileft == iright) d = ileft.Compare(oleft, oright, this); if (d < bestD) { bestD = d; li = lii; ri = rii; } } } if (li == -1) break; // Try to extend the correspondence further down the right list. int count = 1; for (int ri2 = ri+1; ri2 < rightitems.Count && (li+(ri2-ri)) < leftitems.Count; ri2++) { object oleft2 = leftitems[li+(ri2-ri)]; object oright2 = rightitems[ri2]; NodeInterface ileft2 = GetInterface(oleft2); NodeInterface iright2 = GetInterface(oright2); float d2 = 1; if (ileft2 == iright2) d2 = ileft2.Compare(oleft2, oright2, this); if (d2 <= bestD) count++; else break; } // We have a correspondence starting at li and ri // over count items. // First, recurse on the first portion of the list that had no matches. CompareLists(leftitems.GetRange(0, li), rightitems.GetRange(0, ri), comparer.minimumDifference, output); // Then recurse on the correspondence range. CompareLists(leftitems.GetRange(li, count), rightitems.GetRange(ri, count), comparer.minimumDifference, output); // Lastly remove the items we just considered and do it again for the rest of the hunk. leftitems.RemoveRange(0, li+count); rightitems.RemoveRange(0, ri+count); } int ct = leftitems.Count + rightitems.Count; nitems += ct; ndiffs += ct; if (output) { bool noRecurse = comparer.minimumDifference >= 1; if (rightitems.Count == 0 || (leftitems.Count > 0 && noRecurse)) WriteNodesRemoved(leftitems); if (leftitems.Count == 0 || (rightitems.Count > 0 && noRecurse)) WriteNodesAdded(rightitems); if (rightitems.Count != 0 && leftitems.Count != 0 && !noRecurse) CompareLists(leftitems, rightitems, comparer.minimumDifference, output); } } } return (float)ndiffs / (float)nitems; }
private float CompareLists(IList left, IList right, float threshold, bool output) { // Given two lists, find the elements in the list that correspond. // Two elements correspond if their 'difference metric' is less than // or equal to threshold. For the hunks of correspondent items, // recursively descend into items not truly equal. For hunks of // irreconsiliable material, raise the threshold to the next useful // level and rescan the items. if (left == null) { throw new ArgumentNullException("left"); } if (right == null) { throw new ArgumentNullException("right"); } if (left.Count == 0 && right.Count == 0) { return(0); } NodeComparerWrapper comparer = new NodeComparerWrapper(threshold, this); Diff diff = new Diff(left, right, comparer, new HashCodeProvider(this)); int nitems = 0, ndiffs = 0; foreach (Diff.Hunk hunk in diff) { if (hunk.Same || (hunk.Left.Count == 1 && hunk.Right.Count == 1)) { // This comprises a block of correspondent items who // differ by no more than the threshold value. nitems += hunk.Left.Count; bool inSameRegion = false; for (int i = 0; i < hunk.Left.Count; i++) { object oleft = hunk.Left[i]; object oright = hunk.Right[i]; NodeInterface ileft = GetInterface(oleft); NodeInterface iright = GetInterface(oright); IList cleft = null, cright = null; cleft = ileft.GetChildren(oleft); cright = iright.GetChildren(oright); float comp = 0; if (ileft == iright) { comp = ileft.Compare(oleft, oright, this); } // If the nodes are equal, emit one node. if (ileft == iright && comp == 0) { if (output) { if (!inSameRegion) { WritePushSame(); inSameRegion = true; } WriteNodeSame(ileft, oleft, oright); } // Recurse into the lists of each node. } else if (ileft == iright && cleft != null && cright != null && cleft.Count > 0 && cright.Count > 0 && comp <= 1.0) { if (output && inSameRegion) { WritePopSame(); inSameRegion = false; } if (output) { WritePushNode(ileft, oleft, oright); } float d = CompareLists(cleft, cright, 0, output); d *= hunk.Left.Count; if (d < 1) { d = 1; } ndiffs += (int)d; if (output) { WritePopNode(); } // The nodes are not equal, so emit removed and added nodes. } else { if (output && inSameRegion) { WritePopSame(); inSameRegion = false; } if (output) { WriteNodeChange(ileft, oleft, iright, oright); } ndiffs += hunk.Left.Count; } } if (output && inSameRegion) { WritePopSame(); } } else { int ct = hunk.Left.Count + hunk.Right.Count; nitems += ct; ndiffs += ct; if (output) { bool noRecurse = comparer.minimumDifference >= 1; if (hunk.Right.Count == 0 || (hunk.Left.Count > 0 && noRecurse)) { WriteNodesRemoved(hunk.Left); } if (hunk.Left.Count == 0 || (hunk.Right.Count > 0 && noRecurse)) { WriteNodesAdded(hunk.Right); } if (hunk.Right.Count != 0 && hunk.Left.Count != 0 && !noRecurse) { CompareLists(hunk.Left, hunk.Right, comparer.minimumDifference, output); } } } } return((float)ndiffs / (float)nitems); }
private float CompareLists(IList left, IList right, float threshold, bool output) { // Given two lists, find the elements in the list that correspond. // Two elements correspond if their 'difference metric' is less than // or equal to threshold. For the hunks of correspondent items, // recursively descend into items not truly equal. For hunks of // irreconsiliable material, raise the threshold to the next useful // level and rescan the items. if (left.Count == 0 && right.Count == 0) return 0; NodeComparerWrapper comparer = new NodeComparerWrapper(threshold, this); Diff diff = new Diff(left, right, comparer, new HashCodeProvider(this)); int nitems = 0, ndiffs = 0; foreach (Diff.Hunk hunk in diff) { if (hunk.Same || (hunk.Left.Count == 1 && hunk.Right.Count == 1)) { // This comprises a block of correspondent items who // differ by no more than the threshold value. nitems += hunk.Left.Count; bool inSameRegion = false; for (int i = 0; i < hunk.Left.Count; i++) { object oleft = hunk.Left[i]; object oright = hunk.Right[i]; NodeInterface ileft = GetInterface(oleft); NodeInterface iright = GetInterface(oright); IList cleft = null, cright = null; cleft = ileft.GetChildren(oleft); cright = iright.GetChildren(oright); float comp = 0; if (ileft == iright) comp = ileft.Compare(oleft, oright, this); // If the nodes are equal, emit one node. if (ileft == iright && comp == 0) { if (output) { if (!inSameRegion) { WritePushSame(); inSameRegion = true; } WriteNodeSame(ileft, oleft, oright); } // Recurse into the lists of each node. } else if (ileft == iright && cleft != null && cright != null && cleft.Count > 0 && cright.Count > 0 && comp <= 1.0) { if (output && inSameRegion) { WritePopSame(); inSameRegion = false; } if (output) WritePushNode(ileft, oleft, oright); float d = CompareLists(cleft, cright, 0, output); d *= hunk.Left.Count; if (d < 1) d = 1; ndiffs += (int)d; if (output) WritePopNode(); // The nodes are not equal, so emit removed and added nodes. } else { if (output && inSameRegion) { WritePopSame(); inSameRegion = false; } if (output) WriteNodeChange(ileft, oleft, iright, oright); ndiffs += hunk.Left.Count; } } if (output && inSameRegion) WritePopSame(); } else { int ct = hunk.Left.Count + hunk.Right.Count; nitems += ct; ndiffs += ct; if (output) { bool noRecurse = comparer.minimumDifference >= 1; if (hunk.Right.Count == 0 || (hunk.Left.Count > 0 && noRecurse)) WriteNodesRemoved(hunk.Left); if (hunk.Left.Count == 0 || (hunk.Right.Count > 0 && noRecurse)) WriteNodesAdded(hunk.Right); if (hunk.Right.Count != 0 && hunk.Left.Count != 0 && !noRecurse) CompareLists(hunk.Left, hunk.Right, comparer.minimumDifference, output); } } } return (float)ndiffs / (float)nitems; }