Click here to Skip to main content
15,897,704 members
Articles / Programming Languages / C#

Investigating Myers' Diff Algorithm: Part 2 of 2

Rate me:
Please Sign up or sign in to vote.
4.93/5 (12 votes)
19 Sep 2009CPOL5 min read 32.7K   860   27  
The linear space refinement
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Drawing;

namespace DiffCommon
{

	//-----------------------------------------------------------------------------------------
	// Results

	public class Results
	{
		public long Space { get; protected set; }

		public IList<Snake> Snakes { get; protected set; }
		public IList<V> ForwardVs { get; protected set; }
		public IList<V> ReverseVs { get; protected set; }

		public Results( long space, IList<Snake> snakes, IList<V> forwardVs, IList<V> reverseVs )
		{
			Space = space;
			Snakes = snakes;
			ForwardVs = forwardVs;
			ReverseVs = reverseVs;
		}

		public Results( long space, IList<Snake> snakes, bool forward, IList<V> Vs )
		{
			Space = space;
			Snakes = snakes;

			if ( forward ) ForwardVs = Vs; else ReverseVs = Vs;
		}
	}

	//-----------------------------------------------------------------------------------------
	// SnakePair

	class SnakePair
	{
		public int D { get; protected set; }
		public Snake Forward { get; protected set; }
		public Snake Reverse { get; protected set; }

		public SnakePair( int d, Snake forward, Snake reverse )
		{
			D = d;
			Forward = forward;
			Reverse = reverse;
		}
	}

	//-----------------------------------------------------------------------------------------
	// Snake

	public class Snake
	{
		public int XStart;
		public int YStart;
		public int ADeleted;
		public int BInserted;
		public int DiagonalLength;
		public bool IsForward = true;
		public int DELTA = 0;

		public bool IsMiddle = false;
		public int D = -1;

		public Point StartPoint { get { return new Point( XStart, YStart ); } }

		public int XMid { get { return ( IsForward ? XStart + ADeleted : XStart - ADeleted ); } }
		public int YMid { get { return ( IsForward ? YStart + BInserted : YStart - BInserted ); } }
		public Point MidPoint { get { return new Point( XMid, YMid ); } }

		public int XEnd { get { return ( IsForward ? XStart + ADeleted + DiagonalLength : XStart - ADeleted - DiagonalLength ); } }
		public int YEnd { get { return ( IsForward ? YStart + BInserted + DiagonalLength : YStart - BInserted - DiagonalLength ); } }
		public Point EndPoint { get { return new Point( XEnd, YEnd ); } }

		public override string ToString()
		{
			return "Snake " + ( IsForward ? "F" : "R" ) + ": ( " + XStart + ", " + YStart + " ) + ( " +
				( ADeleted > 0 ? "D" + ADeleted : "" ) +
				"," +
				( BInserted > 0 ? " I" + BInserted : "" ) +
				" ) + " + DiagonalLength + " -> ( " + XEnd + ", " + YEnd + " )" +
				" k=" + ( XMid - YMid );
		}

		public Snake( bool isForward, int delta )
		{
			Debug.Assert( !( isForward && delta != 0 ) );

			IsForward = isForward;

			if ( !IsForward ) DELTA = delta;
		}

		void RemoveStubs( int a0, int N, int b0, int M )
		{
			int aMin, aMax, bMin, bMax;

			if ( IsForward )
			{
				if ( XStart == a0 && YStart == b0 - 1 && BInserted == 1 )
				{
					//Debug.WriteLine( "Stub before:" + this );

					YStart = b0;
					BInserted = 0;

					//Debug.WriteLine( "Stub after:" + this );
				}

				aMin = a0; aMax = a0 + N + M;
				bMin = b0; bMax = b0 + N + M;
			}
			else
			{
				if ( XStart == a0 + N && YStart == b0 + M + 1 && BInserted == 1 )
				{
					//Debug.WriteLine( "Stub before: " + this );

					YStart = b0 + M;
					BInserted = 0;

					//Debug.WriteLine( "Stub after:" + this );
				}

				aMin = a0 - 100; aMax = a0 + N;
				bMin = b0 - 100; bMax = b0 + M;
			}

			Debug.Assert( aMin <= XStart && XStart <= aMax );
			Debug.Assert( aMin <= XMid && XMid <= aMax );
			Debug.Assert( aMin <= XEnd && XEnd <= aMax );

			Debug.Assert( bMin <= YStart && YStart <= bMax );
			Debug.Assert( bMin <= YMid && YMid <= bMax );
			Debug.Assert( bMin <= YEnd && YEnd <= bMax );

			Debug.Assert( XMid - YMid == XEnd - YEnd ); // k
		}

		public Snake( int a0, int N, int b0, int M, bool isForward, int xStart, int yStart, int aDeleted, int bInserted, int diagonal )
		{
			XStart = xStart;
			YStart = yStart;
			ADeleted = aDeleted;
			BInserted = bInserted;
			DiagonalLength = diagonal;
			IsForward = isForward;

			RemoveStubs( a0, N, b0, M );
		}

		public Snake( int a0, int N, int b0, int M, bool isForward, int xStart, int yStart, bool down, int diagonal )
		{
			XStart = xStart;
			YStart = yStart;
			ADeleted = down ? 0 : 1;
			BInserted = down ? 1 : 0;
			DiagonalLength = diagonal;
			IsForward = isForward;

			RemoveStubs( a0, N, b0, M );
		}

		//-----------------------------------------------------------------------------------------
		// Calculate char

		internal Snake( int a0, int N, int b0, int M, bool forward, int delta, V V, int k, int d, char[] pa, char[] pb )
			: this( forward, delta )
		{
			Calculate( V, k, d, pa, a0, N, pb, b0, M );
		}

		internal Snake Calculate( V V, int k, int d, char[] pa, int a0, int N, char[] pb, int b0, int M )
		{
			if ( IsForward ) return CalculateForward( V, k, d, pa, a0, N, pb, b0, M );

			return CalculateBackward( V, k, d, pa, a0, N, pb, b0, M );
		}

		Snake CalculateForward( V V, int k, int d, char[] pa, int a0, int N, char[] pb, int b0, int M )
		{
			bool down = ( k == -d || ( k != d && V[ k - 1 ] < V[ k + 1 ] ) );

			int xStart = down ? V[ k + 1 ] : V[ k - 1 ];
			int yStart = xStart - ( down ? k + 1 : k - 1 );

			int xEnd = down ? xStart : xStart + 1;
			int yEnd = xEnd - k;

			int snake = 0;
			while ( xEnd < N && yEnd < M && pa[ xEnd + a0 ] == pb[ yEnd + b0 ] ) { xEnd++; yEnd++; snake++; }

			XStart = xStart + a0;
			YStart = yStart + b0;
			ADeleted = down ? 0 : 1;
			BInserted = down ? 1 : 0;
			DiagonalLength = snake;

			RemoveStubs( a0, N, b0, M );

			return this;
		}

		Snake CalculateBackward( V V, int k, int d, char[] pa, int a0, int N, char[] pb, int b0, int M )
		{
			bool up = ( k == d + DELTA || ( k != -d + DELTA && V[ k - 1 ] < V[ k + 1 ] ) );

			int xStart = up ? V[ k - 1 ] : V[ k + 1 ];
			int yStart = xStart - ( up ? k - 1 : k + 1 );

			int xEnd = up ? xStart : xStart - 1;
			int yEnd = xEnd - k;

			int snake = 0;
			while ( xEnd > 0 && yEnd > 0 && pa[ xEnd - 1 ] == pb[ yEnd - 1 ] ) { xEnd--; yEnd--; snake++; }

			XStart = xStart;
			YStart = yStart;
			ADeleted = up ? 0 : 1;
			BInserted = up ? 1 : 0;
			DiagonalLength = snake;

			RemoveStubs( a0, N, b0, M );

			return this;
		}

		//-----------------------------------------------------------------------------------------

	}

	//-----------------------------------------------------------------------------------------
	// V

	public class V
	{
		public bool IsForward { get; private set; }
		public int N { get; private set; }
		public int M { get; private set; }

		public int _Max;
		public int _Delta;
		public int[] _Array;

		public int this[ int k ]
		{
			get
			{
				return _Array[ k - _Delta + _Max ];
			}

			set
			{
				_Array[ k - _Delta + _Max ] = value;
			}
		}

		public int Y( int k ) { return this[ k ] - k; }

		public long Memory { get { return sizeof( bool ) + IntPtr.Size + sizeof( int ) * ( 4 + _Array.Length ); } }

		V() { }

		public V( int n, int m, bool forward, bool linear )
		{
			Debug.Assert( n >= 0 && m >= 0, "V.ctor N:" + n + " M:" + m );

			IsForward = forward;
			N = n;
			M = m;

			_Max = ( linear ? ( n + m ) / 2 + 1 : n + m );
			if ( _Max <= 0 ) _Max = 1;

			_Array = new int[ 2 * _Max + 1 ];

			InitStub( n, m, _Max );
		}

		public void InitStub( int n, int m, int max )
		{
			if ( IsForward ) this[ 1 ] = 0; // stub for forward
			else
			{
				_Delta = n - m;
				this[ n - m - 1 ] = n; // stub for backward
			}

			CheckBounds( max );
		}

		[Conditional( "DEBUG" )]
		void CheckBounds( int max )
		{
			var m = String.Format(
					"max: {0:N0}, delta: {1:N0} => [ {2:N0} .. {3:N0} ] {4}",
					max, _Delta, _Delta - max, _Delta + max, ToString() );

			if ( _Max - max < 0 || _Max + max >= _Array.Length )
				Debug.Assert( false, "CheckBounds failed: " + m );
			else
			{
				//Debug.WriteLine( "CheckBounds good: " + m );
			}
		}

		//public unsafe V CreateCopy( int d, bool isForward, int delta )
		//{
		//    Debug.Assert( !( isForward && delta != 0 ) );

		//    if ( d == 0 ) d++;

		//    var o = new V();

		//    o.IsForward = isForward;
		//    o._Max = d;
		//    if ( !isForward ) o._Delta = delta;
		//    o._Array = new int[ 2 * d + 1 ];


		//    fixed ( int* po = o._Array )
		//        if ( d <= _Max )
		//        {
		//            Marshal.Copy( _Array, ( _Max - _Delta ) - ( o._Max - o._Delta ), new IntPtr( po ), o._Array.Length );
		//        }
		//        else
		//        {
		//            Debug.Assert( false );
		//            Debug.Assert( _Delta == 0 );
		//            Marshal.Copy( _Array, 0, new IntPtr( po + d - _Max ), _Array.Length );
		//        }

		//    return o;
		//}

		public V CreateCopy( int d, bool isForward, int delta )
		{
			Debug.Assert( !( isForward && delta != 0 ) );

			if ( d == 0 ) d++;

			var o = new V();

			o.IsForward = isForward;
			o._Max = d;
			if ( !isForward ) o._Delta = delta;
			o._Array = new int[ 2 * d + 1 ];

			if ( d <= _Max )
			{
				Array.Copy( _Array, ( _Max - _Delta ) - ( o._Max - o._Delta ), o._Array, 0, o._Array.Length );
			}
			else
			{
				throw new NotImplementedException( "V.CreateCopy" );
				//Debug.Assert( false );
				//Debug.Assert( _Delta == 0 );
				//Marshal.Copy( _Array, 0, new IntPtr( po + d - _Max ), _Array.Length );
			}

			return o;
		}

		public override string ToString()
		{
			return "V " + _Array.Length + " [ " + ( _Delta - _Max ) + " .. " + _Delta + " .. " + ( _Delta + _Max ) + " ]";
		}
	}

	//-----------------------------------------------------------------------------------------
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United Kingdom United Kingdom
I discovered C# and .NET 1.0 Beta 1 in late 2000 and loved them immediately.
I have been writing software professionally in C# ever since

In real life, I have spent 3 years travelling abroad,
I have held a UK Private Pilots Licence for 20 years,
and I am a PADI Divemaster.

I now live near idyllic Bournemouth in England.

I can work 'virtually' anywhere!

Comments and Discussions