/// <summary> /// The function merges the two sequences and returns the merged /// html text string with deleted(exists in source sequence but /// not in destination sequence) and added(exists in destination /// but not in source) decorated extra html tags defined in class /// commentoff and class added. /// </summary> /// <param name="src"> /// The source sequence /// </param> /// <param name="des"> /// The DES sequence. /// </param> /// <returns> /// The merged html string /// </returns> private string DoMerge(Sequence src, Sequence des) { Sequence s; var result = new StringBuilder(); var tail = string.Empty; var y = des.StartIndex; // strip off the leading common sequence while (src.StartIndex < src.EndIndex && des.StartIndex < des.EndIndex && this.original[src.StartIndex].CompareTo(this.modified[des.StartIndex]) == 0) { src.StartIndex++; des.StartIndex++; } if (des.StartIndex > y) { s = new Sequence(y, des.StartIndex); result.Append(this.ConstructText(s, SequenceStatus.NoChange)); } y = des.EndIndex; // strip off the tailing common sequence while (src.StartIndex < src.EndIndex && des.StartIndex < des.EndIndex && this.original[src.EndIndex - 1].CompareTo(this.modified[des.EndIndex - 1]) == 0) { src.EndIndex--; des.EndIndex--; } if (des.EndIndex < y) { s = new Sequence(des.EndIndex, y); tail = this.ConstructText(s, SequenceStatus.NoChange); } // length of the sequences var n = src.EndIndex - src.StartIndex; var m = des.EndIndex - des.StartIndex; // Special cases if (n < 1 && m < 1) { // both source and destination are // empty return(result.Append(tail).ToString()); } if (n < 1) { // source is already empty, report // destination as added result.Append(this.ConstructText(des, SequenceStatus.Inserted)); result.Append(tail); return(result.ToString()); } if (m < 1) { // destination is empty, report source as // deleted result.Append(this.ConstructText(src, SequenceStatus.Deleted)); result.Append(tail); return(result.ToString()); } if (m == 1 && n == 1) { // each of source and destination has only // one word left. At this point, we are sure // that they are not equal. result.Append(this.ConstructText(src, SequenceStatus.Deleted)); result.Append(this.ConstructText(des, SequenceStatus.Inserted)); result.Append(tail); return(result.ToString()); } // find the middle snake MiddleSnake snake = this.FindMiddleSnake(src, des); if (snake.SES_Length > 1) { // prepare the parameters for recursion var leftSrc = new Sequence(src.StartIndex, snake.Source.StartIndex); var leftDes = new Sequence(des.StartIndex, snake.Destination.StartIndex); var rightSrc = new Sequence(snake.Source.EndIndex, src.EndIndex); var rightDes = new Sequence(snake.Destination.EndIndex, des.EndIndex); result.Append(this.DoMerge(leftSrc, leftDes)); if (snake.Source.StartIndex < snake.Source.EndIndex) { // the snake is not empty, report it as common // sequence result.Append(this.ConstructText(snake.Destination, SequenceStatus.NoChange)); } result.Append(this.DoMerge(rightSrc, rightDes)); result.Append(tail); return(result.ToString()); } // Separating this case out can at least save one // level of recursion. // Only one edit edge suggests the 4 possible cases. // if N > M, it will be either: // - or \ // \ (case 1) \ (case 2) // \ - // if N < M, it will be either: // | or \ // \ (case 3) \ (case 4) // \ | // N and M can't be equal! if (n > m) { if (src.StartIndex != snake.Source.StartIndex) { // case 1 var leftSrc = new Sequence(src.StartIndex, snake.Source.StartIndex); result.Append(this.ConstructText(leftSrc, SequenceStatus.Deleted)); result.Append(this.ConstructText(snake.Destination, SequenceStatus.NoChange)); } else { // case 2 var rightSrc = new Sequence(snake.Source.StartIndex, src.EndIndex); result.Append(this.ConstructText(rightSrc, SequenceStatus.Deleted)); result.Append(this.ConstructText(snake.Destination, SequenceStatus.NoChange)); } } else { if (des.StartIndex != snake.Destination.StartIndex) { // case 3 var updes = new Sequence(des.StartIndex, snake.Destination.StartIndex); result.Append(this.ConstructText(updes, SequenceStatus.Inserted)); result.Append(this.ConstructText(snake.Destination, SequenceStatus.NoChange)); } else { // case 4 var bottomDes = new Sequence(snake.Destination.EndIndex, des.EndIndex); result.Append(this.ConstructText(bottomDes, SequenceStatus.Inserted)); result.Append(this.ConstructText(snake.Destination, SequenceStatus.NoChange)); } } result.Append(tail); return(result.ToString()); }
/// <summary> /// In the edit graph for the sequences src and des, search for the /// optimal(shortest) path from (src.StartIndex, des.StartIndex) to /// (src.EndIndex, des.EndIndex). /// The searching starts from both ends of the graph and when the /// furthest forward reaching overlaps with the furthest backward /// reaching, the overlapped point is reported as the middle point /// of the shortest path. /// See the listed reference for the detailed description of the /// algorithm /// </summary> /// <param name="src"> /// Represents a (sub)sequence of _original /// </param> /// <param name="des"> /// The DES sequence. /// </param> /// <returns> /// The found middle snake /// </returns> private MiddleSnake FindMiddleSnake(Sequence src, Sequence des) { int d; var midSnake = new MiddleSnake(); // the range of diagonal values var minDiag = src.StartIndex - des.EndIndex; var maxDiag = src.EndIndex - des.StartIndex; // middle point of forward searching var fwdMid = src.StartIndex - des.StartIndex; // middle point of backward searching var bwdMid = src.EndIndex - des.EndIndex; // forward seaching range var fwdMin = fwdMid; var fwdMax = fwdMid; // backward seaching range var bwdMin = bwdMid; var bwdMax = bwdMid; var odd = ((fwdMin - bwdMid) & 1) == 1; this.fwdVector[fwdMid] = src.StartIndex; this.bwdVector[bwdMid] = src.EndIndex; #if (MyDEBUG) Debug.WriteLine("-- Entering Function findMiddleSnake(src, des) --"); #endif for (d = 1;; d++) { // extend or shrink the search range if (fwdMin > minDiag) { this.fwdVector[--fwdMin - 1] = -1; } else { ++fwdMin; } if (fwdMax < maxDiag) { this.fwdVector[++fwdMax + 1] = -1; } else { --fwdMax; } #if (MyDEBUG) Debug.WriteLine(d, " D path"); #endif // top-down search int k; int x; int y; for (k = fwdMax; k >= fwdMin; k -= 2) { if (this.fwdVector[k - 1] < this.fwdVector[k + 1]) { x = this.fwdVector[k + 1]; } else { x = this.fwdVector[k - 1] + 1; } y = x - k; midSnake.Source.StartIndex = x; midSnake.Destination.StartIndex = y; while (x < src.EndIndex && y < des.EndIndex && this.original[x].CompareTo(this.modified[y]) == 0) { x++; y++; } // update forward vector this.fwdVector[k] = x; #if (MyDEBUG) Debug.WriteLine(" Inside forward loop"); Debug.WriteLine(k, " Diagonal value"); Debug.WriteLine(x, " X value"); Debug.WriteLine(y, " Y value"); #endif if (odd && k >= bwdMin && k <= bwdMax && x >= this.bwdVector[k]) { // this is the snake we are looking for // and set the end indeses of the snake midSnake.Source.EndIndex = x; midSnake.Destination.EndIndex = y; midSnake.SES_Length = (2 * d) - 1; #if (MyDEBUG) Debug.WriteLine("!!!Report snake from forward search"); Debug.WriteLine(midSnake.Source.StartIndex, " middle snake source start index"); Debug.WriteLine(midSnake.Source.EndIndex, " middle snake source end index"); Debug.WriteLine(midSnake.Destination.StartIndex, " middle snake destination start index"); Debug.WriteLine(midSnake.Destination.EndIndex, " middle snake destination end index"); #endif return(midSnake); } } // extend the search range if (bwdMin > minDiag) { this.bwdVector[--bwdMin - 1] = int.MaxValue; } else { ++bwdMin; } if (bwdMax < maxDiag) { this.bwdVector[++bwdMax + 1] = int.MaxValue; } else { --bwdMax; } // bottom-up search for (k = bwdMax; k >= bwdMin; k -= 2) { if (this.bwdVector[k - 1] < this.bwdVector[k + 1]) { x = this.bwdVector[k - 1]; } else { x = this.bwdVector[k + 1] - 1; } y = x - k; midSnake.Source.EndIndex = x; midSnake.Destination.EndIndex = y; while (x > src.StartIndex && y > des.StartIndex && this.original[x - 1].CompareTo(this.modified[y - 1]) == 0) { x--; y--; } // update backward Vector this.bwdVector[k] = x; #if (MyDEBUG) Debug.WriteLine(" Inside backward loop"); Debug.WriteLine(k, " Diagonal value"); Debug.WriteLine(x, " X value"); Debug.WriteLine(y, " Y value"); #endif if (!odd && k >= fwdMin && k <= fwdMax && x <= this.fwdVector[k]) { // this is the snake we are looking for // and set the start indexes of the snake midSnake.Source.StartIndex = x; midSnake.Destination.StartIndex = y; midSnake.SES_Length = 2 * d; #if (MyDEBUG) Debug.WriteLine("!!!Report snake from backward search"); Debug.WriteLine(midSnake.Source.StartIndex, " middle snake source start index"); Debug.WriteLine(midSnake.Source.EndIndex, " middle snake source end index"); Debug.WriteLine(midSnake.Destination.StartIndex, " middle snake destination start index"); Debug.WriteLine(midSnake.Destination.EndIndex, " middle snake destination end index"); #endif return(midSnake); } } } }
/// <summary> /// In the edit graph for the sequences src and des, search for the /// optimal(shortest) path from (src.StartIndex, des.StartIndex) to /// (src.EndIndex, des.EndIndex). /// The searching starts from both ends of the graph and when the /// furthest forward reaching overlaps with the furthest backward /// reaching, the overlapped point is reported as the middle point /// of the shortest path. /// See the listed reference for the detailed description of the /// algorithm /// </summary> /// <param name="src"> /// Represents a (sub)sequence of _original /// </param> /// <param name="des"> /// The DES sequence. /// </param> /// <returns> /// The found middle snake /// </returns> private MiddleSnake FindMiddleSnake(Sequence src, Sequence des) { int d; var midSnake = new MiddleSnake(); // the range of diagonal values var minDiag = src.StartIndex - des.EndIndex; var maxDiag = src.EndIndex - des.StartIndex; // middle point of forward searching var fwdMid = src.StartIndex - des.StartIndex; // middle point of backward searching var bwdMid = src.EndIndex - des.EndIndex; // forward seaching range var fwdMin = fwdMid; var fwdMax = fwdMid; // backward seaching range var bwdMin = bwdMid; var bwdMax = bwdMid; var odd = ((fwdMin - bwdMid) & 1) == 1; this.fwdVector[fwdMid] = src.StartIndex; this.bwdVector[bwdMid] = src.EndIndex; #if (MyDEBUG) Debug.WriteLine("-- Entering Function findMiddleSnake(src, des) --"); #endif for (d = 1;; d++) { // extend or shrink the search range if (fwdMin > minDiag) { this.fwdVector[--fwdMin - 1] = -1; } else { ++fwdMin; } if (fwdMax < maxDiag) { this.fwdVector[++fwdMax + 1] = -1; } else { --fwdMax; } #if (MyDEBUG) Debug.WriteLine(d, " D path"); #endif // top-down search int k; int x; int y; for (k = fwdMax; k >= fwdMin; k -= 2) { if (this.fwdVector[k - 1] < this.fwdVector[k + 1]) { x = this.fwdVector[k + 1]; } else { x = this.fwdVector[k - 1] + 1; } y = x - k; midSnake.Source.StartIndex = x; midSnake.Destination.StartIndex = y; while (x < src.EndIndex && y < des.EndIndex && this.original[x].CompareTo(this.modified[y]) == 0) { x++; y++; } // update forward vector this.fwdVector[k] = x; #if (MyDEBUG) Debug.WriteLine(" Inside forward loop"); Debug.WriteLine(k, " Diagonal value"); Debug.WriteLine(x, " X value"); Debug.WriteLine(y, " Y value"); #endif if (odd && k >= bwdMin && k <= bwdMax && x >= this.bwdVector[k]) { // this is the snake we are looking for // and set the end indeses of the snake midSnake.Source.EndIndex = x; midSnake.Destination.EndIndex = y; midSnake.SES_Length = (2 * d) - 1; #if (MyDEBUG) Debug.WriteLine("!!!Report snake from forward search"); Debug.WriteLine(midSnake.Source.StartIndex, " middle snake source start index"); Debug.WriteLine(midSnake.Source.EndIndex, " middle snake source end index"); Debug.WriteLine(midSnake.Destination.StartIndex, " middle snake destination start index"); Debug.WriteLine(midSnake.Destination.EndIndex, " middle snake destination end index"); #endif return midSnake; } } // extend the search range if (bwdMin > minDiag) { this.bwdVector[--bwdMin - 1] = int.MaxValue; } else { ++bwdMin; } if (bwdMax < maxDiag) { this.bwdVector[++bwdMax + 1] = int.MaxValue; } else { --bwdMax; } // bottom-up search for (k = bwdMax; k >= bwdMin; k -= 2) { if (this.bwdVector[k - 1] < this.bwdVector[k + 1]) { x = this.bwdVector[k - 1]; } else { x = this.bwdVector[k + 1] - 1; } y = x - k; midSnake.Source.EndIndex = x; midSnake.Destination.EndIndex = y; while (x > src.StartIndex && y > des.StartIndex && this.original[x - 1].CompareTo(this.modified[y - 1]) == 0) { x--; y--; } // update backward Vector this.bwdVector[k] = x; #if (MyDEBUG) Debug.WriteLine(" Inside backward loop"); Debug.WriteLine(k, " Diagonal value"); Debug.WriteLine(x, " X value"); Debug.WriteLine(y, " Y value"); #endif if (!odd && k >= fwdMin && k <= fwdMax && x <= this.fwdVector[k]) { // this is the snake we are looking for // and set the start indexes of the snake midSnake.Source.StartIndex = x; midSnake.Destination.StartIndex = y; midSnake.SES_Length = 2 * d; #if (MyDEBUG) Debug.WriteLine("!!!Report snake from backward search"); Debug.WriteLine(midSnake.Source.StartIndex, " middle snake source start index"); Debug.WriteLine(midSnake.Source.EndIndex, " middle snake source end index"); Debug.WriteLine(midSnake.Destination.StartIndex, " middle snake destination start index"); Debug.WriteLine(midSnake.Destination.EndIndex, " middle snake destination end index"); #endif return midSnake; } } } }