Example #1
0
        /// <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;
                    }
                }
            }
        }
Example #2
0
        /// <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();
        }
Example #3
0
        /// <summary>
        /// The public function merges the two copies of
        ///     files stored inside this class. The html tags
        ///     of the destination file is used in the merged
        ///     file.
        /// </summary>
        /// <returns>
        /// The merged file
        /// </returns>
        public string merge()
        {
            var src = new Sequence(0, this.original.Count);
            var des = new Sequence(0, this.modified.Count);

            return this.DoMerge(src, des);
        }
Example #4
0
        /// <summary>
        /// The function returns a html text string reconstructed
        ///     from the sub collection of words its starting and ending
        ///     indexes are marked by parameter seq and its collection is
        ///     denoted by parameter status. If the status is "deleted",
        ///     then the _original collection is used, otherwise, _modified
        ///     is used.
        /// </summary>
        /// <param name="seq">
        /// Sequence object that marks the start index and end
        ///     index of the sub sequence
        /// </param>
        /// <param name="status">
        /// Denoting the status of the sequence. When its value is
        ///     Deleted or Added, some extra decoration will be added
        ///     around the word.
        /// </param>
        /// <returns>
        /// The html text string constructed
        /// </returns>
        private string ConstructText(Sequence seq, SequenceStatus status)
        {
            var result = new StringBuilder();

            switch (status)
            {
                case SequenceStatus.Deleted:

                    // the sequence exists in _original and
                    // will be marked as deleted in the merged
                    // file.
                    for (var i = seq.StartIndex; i < seq.EndIndex; i++)
                    {
                        result.Append(this.original[i].reconstruct(CommentOff.BeginTag, CommentOff.EndTag));
                    }

                    break;
                case SequenceStatus.Inserted:

                    // the sequence exists in _modified and
                    // will be marked as added in the merged
                    // file.
                    for (var i = seq.StartIndex; i < seq.EndIndex; i++)
                    {
                        result.Append(this.modified[i].reconstruct(Added.BeginTag, Added.EndTag));
                    }

                    break;
                case SequenceStatus.NoChange:

                    // the sequence exists in both _original and
                    // _modified and will be left as what it is in
                    // the merged file. We chose to reconstruct from
                    // _modified collection
                    for (var i = seq.StartIndex; i < seq.EndIndex; i++)
                    {
                        result.Append(this.modified[i].reconstruct());
                    }

                    break;
                default:

                    // this will not happen (hope)
                    break;
            }

            return result.ToString();
        }
Example #5
0
 /// <summary>
 /// Initializes a new instance of the <see cref="MiddleSnake"/> class.
 /// </summary>
 public MiddleSnake()
 {
     this.Source = new Sequence();
     this.Destination = new Sequence();
 }