Click here to Skip to main content
15,892,480 members
Articles / Programming Languages / C#

Back to Basics - Generic Data Structures and Algorithms In .NET 2.0

Rate me:
Please Sign up or sign in to vote.
4.96/5 (93 votes)
23 Apr 2007Ms-PL15 min read 279.3K   2.6K   300  
Implementations of generic data structures and algorithms in .NET 2.0.
using System;
using System.Collections.Generic;
using System.Text;
using DataStructuresDotNet.Misc;

namespace DataStructuresDotNet.DataStructures
{
	/// <summary>
	/// A Matrix datastructure corresponding to the mathematical matrix used in linear algebra.	
	/// </summary>
	public class Matrix : IVisitableCollection<double>
	{
		#region Globals

		private int noOfRows;		
		private int noOfColumns;
		private double[,] data;

		#endregion

		#region Construction

		/// <summary>
		/// Initializes a new instance of the <see cref="Matrix"/> class.
		/// </summary>
		/// <param name="rows_">The number of rows in the matrix.</param>
		/// <param name="columns_">The number of columns in the matrix.</param>
		public Matrix(int rows_, int columns_)
		{
			if ((rows_ <= 0) || (columns_ <= 0))
			{
				throw new ArgumentException(Resources.RowsOrColumnsInvalid);
			}

			this.noOfRows = rows_;
			this.noOfColumns = columns_;

			data = new double[noOfRows, noOfColumns];
		}

		#endregion

		#region Public Members

		/// <summary>
		/// Gets the number of rows in this matrix.
		/// </summary>
		/// <value>The rows.</value>
		public int Rows
		{
			get
			{
				return noOfRows;
			}
		}

		/// <summary>
		/// Gets the number of columns in this matrix.
		/// </summary>
		/// <value>The columns.</value>
		public int Columns
		{
			get
			{
				return noOfColumns;
			}
		}

		/// <summary>
		/// Times the matrices according to the linear algebra operator *.
		/// </summary>
		/// <param name="matrix">The result of the times operation.</param>
		/// <returns></returns>
		public Matrix Times(Matrix matrix)
		{
			// Check the dimensions to make sure the operation is a valid one.
			if (noOfColumns != matrix.noOfRows)
			{
				throw new ArgumentException(Resources.IncompatibleMatricesTimes);
			}

			Matrix ret = new Matrix(noOfRows, matrix.noOfColumns);

			for (int i = 0; i < noOfRows; i++)
			{
				for (int j = 0; j < matrix.noOfColumns; j++)
				{
					double sum = 0;

					for (int k = 0; k < noOfColumns; k++)
					{
						sum += (data[i, k] * matrix.data[k, j]);
					}

					ret.data[i, j] = sum;
				}
			}

			return ret;
		}

		/// <summary>
		/// Times the matrices according to the linear algebra operator *.
		/// </summary>
		/// <param name="number"></param>
		/// <returns></returns>
		public Matrix Times(double number)
		{
			Matrix ret = new Matrix(noOfRows, noOfColumns);

			for (int i = 0; i < noOfRows; i++)
			{
				for (int j = 0; j < noOfColumns; j++)
				{
					ret[i, j] = this[i, j] * number;
				}
			}

			return ret;
		}

		/// <summary>
		/// Adds to matrices according to the linear algebra operator +.
		/// </summary>
		/// <param name="matrix">The result of the addition.</param>
		/// <returns></returns>
		public Matrix Plus(Matrix matrix)
		{
			if ((this.noOfRows != matrix.noOfRows) || (this.noOfColumns != matrix.noOfColumns))
			{
				throw new ArgumentException(Resources.IncompatibleMatricesSameSize);
			}

			Matrix ret = new Matrix(noOfRows, noOfColumns);

			for (int i = 0; i < noOfRows; i++)
			{
				for (int j = 0; j < noOfColumns; j++)
				{
					ret[i, j] = this[i, j] + matrix[i, j];
				}
			}

			return ret;
		}

		/// <summary>
		/// Inverts this instance.
		/// </summary>
		/// <returns></returns>
		public Matrix Invert()
		{
			return this * -1;
		}

		/// <summary>
		/// Subtracts the matrices according to the linear algebra operator -.
		/// </summary>
		/// <param name="matrix">The result of the subtraction.</param>
		/// <returns></returns>
		public Matrix Minus(Matrix matrix)
		{
			if (matrix == null)
			{
				throw new ArgumentNullException(Resources.MatrixIsNull);
			}

			return this + matrix.Invert();
		}

		/// <summary>
		/// Transposes the matrix.
		/// </summary>
		/// <returns></returns>
		/// <value>The transposed matrix.</value>
		public Matrix Transpose()
		{
			Matrix ret = new Matrix(noOfColumns, noOfRows);

			for (int i = 0; i < noOfRows; i++)
			{
				for (int j = 0; j < noOfColumns; j++)
				{
					ret[j, i] = this[i, j];
				}
			}

			return ret;
		}
			

		#endregion

		#region Operator Overloads

		/// <summary>
		/// Gets or sets the value at the specified index.
		/// </summary>
		/// <value></value>
		public double this[int i, int j]
		{
			get
			{
				return data[i, j];
			}
			set
			{
				data[i, j] = value;
			}
		}

		/// <summary>
		/// Overload of the operator + as in linear algebra.
		/// </summary>
		/// <param name="m1">The left hand matrix.</param>
		/// <param name="m2">The right hand matrix.</param>
		/// <returns></returns>
		public static Matrix operator +(Matrix m1, Matrix m2)
		{
			return m1.Plus(m2);
		}

		/*
		/// <summary>
		/// Operator overload for the IsEqual method.
		/// </summary>
		/// <param name="m1">The left hand matrix.</param>
		/// <param name="m2">The right hand matrix</param>
		/// <returns></returns>
		public static Matrix operator ==(Matrix m1, Matrix m2)
		{
			return m1.IsEqual(m2);

		}

		/// <summary>
		/// Operator overload for the IsEqual method.
		/// </summary>
		/// <param name="m1">The left hand matrix.</param>
		/// <param name="m2">The right hand matrix</param>
		/// <returns></returns>
		public static Matrix operator !=(Matrix m1, Matrix m2)
		{
			return !m1.IsEqual(m2);

		}
		*/

		/// <summary>
		/// Overload of the operator - as in linear algebra.
		/// </summary>
		/// <param name="m1">The left hand matrix.</param>
		/// <param name="m2">The right hand matrix.</param>
		/// <returns></returns>
		public static Matrix operator -(Matrix m1, Matrix m2)
		{
			return m1.Minus(m2);
		}

		/// <summary>
		/// Overload of the operator * as in linear algebra.
		/// </summary>
		/// <param name="m1">The left hand matrix.</param>
		/// <param name="m2">The right hand matrix.</param>
		/// <returns></returns>
		public static Matrix operator *(Matrix m1, Matrix m2)
		{
			return m1.Times(m2);
		}

		/// <summary>
		/// Overload of the operator * as in linear algebra.
		/// </summary>
		/// <param name="number">The number.</param>
		/// <param name="m2">The right hand matrix.</param>
		/// <returns></returns>
		public static Matrix operator *(double number, Matrix m2)
		{
			return m2.Times(number);
		}

		/// <summary>
		/// Overload of the operator * as in linear algebra.
		/// </summary>
		/// <param name="m1">The number to be multiplied with.</param>
		/// <param name="number">The number.</param>
		/// <returns></returns>
		public static Matrix operator *(Matrix m1, double number)
		{
			return m1.Times(number);
		}

		/// <summary>
		/// Determines whether the specified matrix is equal to the current one (length, width, values).
		/// </summary>
		/// <param name="m">The matrix to be compared to.</param>
		/// <returns>
		/// 	<c>true</c> if the specified matrix is equal to the current one; otherwise, <c>false</c>.
		/// </returns>
		public bool IsEqual(Matrix m)
		{			
			if (m == null)
			{
				return false;
			}

			if (m.Rows != Rows)
			{
				return false;
			}

			if (m.Columns != Columns)
			{
				return false;
			}

			for (int i = 0; i < Rows; i++)
			{
				for (int j = 0; j < Columns; j++)
				{
					if (this[i, j] != m[i, j])
					{
						return false;
					}
				}
			}

			return true;
		}

		/// <summary>
		/// Gets a value indicating whether this matrix instance is symmetric.
		/// </summary>
		/// <value>
		/// 	<c>true</c> if this matrix instance is symmetric; otherwise, <c>false</c>.
		/// </value>
		public bool IsSymmetric
		{
			get
			{
				if (Rows == Columns)
				{
					return this.IsEqual(this.Transpose());
				}
				else
				{
					return false;
				}
			}
		}

		#endregion

		#region IVisitableCollection<double> Members

		/// <summary>
		/// Gets a value indicating whether this instance is of a fixed size.
		/// </summary>
		/// <value>
		/// 	<c>true</c> if this instance is fixed size; otherwise, <c>false</c>.
		/// </value>
		public bool IsFixedSize
		{
			get {
				return true;
			}
		}

		/// <summary>
		/// Gets a value indicating whether this collection is empty.
		/// </summary>
		/// <value>
		/// 	<c>true</c> if this collection is empty; otherwise, <c>false</c>.
		/// </value>
		public bool IsEmpty
		{
			get {
				return false;
			}
		}

		/// <summary>
		/// Gets a value indicating whether this collection is full.
		/// </summary>
		/// <value><c>true</c> if this collection is full; otherwise, <c>false</c>.</value>
		public bool IsFull
		{
			get {
				return false;
			}
		}

		/// <summary>
		/// Gets the number of elements contained in the <see cref="T:System.Collections.ICollection"></see>.
		/// </summary>
		/// <value></value>
		/// <returns>The number of elements contained in the <see cref="T:System.Collections.ICollection"></see>.</returns>
		public int Count
		{
			get
			{
				return noOfRows * noOfColumns;
			}
		}

		/// <summary>
		/// Determines whether the <see cref="T:System.Collections.Generic.ICollection`1"></see> contains a specific value.
		/// </summary>
		/// <param name="item">The object to locate in the <see cref="T:System.Collections.Generic.ICollection`1"></see>.</param>
		/// <returns>
		/// true if item is found in the <see cref="T:System.Collections.Generic.ICollection`1"></see>; otherwise, false.
		/// </returns>
		public bool Contains(double item)
		{
			for (int i = 0; i < noOfRows; i++)
			{
				for (int j = 0; j < noOfColumns; j++)
				{
					if (this[i, j] == item)
					{
						return true;
					}
				}
			}

			return false;
		}

		/// <summary>
		/// Accepts the specified visitor.
		/// </summary>
		/// <param name="visitor">The visitor.</param>
		public void Accept(DataStructuresDotNet.Visitors.IVisitor<double> visitor)
		{
			if (visitor == null)
			{
				throw new ArgumentNullException("visitor");
			}

			for (int i = 0; i < noOfRows; i++)
			{
				for (int j = 0; j < noOfColumns; j++)
				{
					visitor.Visit(this[i, j]);
				}
			}
		}

		/// <summary>
		/// Copies the elements of the <see cref="T:System.Collections.Generic.ICollection`1"></see> to an <see cref="T:System.Array"></see>, starting at a particular <see cref="T:System.Array"></see> index.
		/// </summary>
		/// <param name="array">The one-dimensional <see cref="T:System.Array"></see> that is the destination of the elements copied from <see cref="T:System.Collections.Generic.ICollection`1"></see>. The <see cref="T:System.Array"></see> must have zero-based indexing.</param>
		/// <param name="arrayIndex">The zero-based index in array at which copying begins.</param>
		/// <exception cref="T:System.ArgumentOutOfRangeException">arrayIndex is less than 0.</exception>
		/// <exception cref="T:System.ArgumentNullException">array is null.</exception>
		/// <exception cref="T:System.ArgumentException">array is multidimensional.-or-arrayIndex is equal to or greater than the length of array.-or-The number of elements in the source <see cref="T:System.Collections.Generic.ICollection`1"></see> is greater than the available space from arrayIndex to the end of the destination array.-or-Type T cannot be cast automatically to the type of the destination array.</exception>
		public void CopyTo(double[] array, int arrayIndex)
		{
			if (array == null)
			{
				throw new ArgumentNullException("array");
			}

			if ((array.Length - arrayIndex) < this.Count)
			{
				throw new ArgumentException(Resources.NotEnoughSpaceInTargetArray);
			}

			int counter = arrayIndex;

			for (int i = 0; i < noOfRows; i++)
			{
				for (int j = 0; j < noOfColumns; j++)
				{
					array.SetValue(this[i, j], counter);
					counter++;
				}
			}
		}


		/// <summary>
		/// Returns an enumerator that iterates through the collection.
		/// </summary>
		/// <returns>
		/// A <see cref="T:System.Collections.Generic.IEnumerator`1"></see> that can be used to iterate through the collection.
		/// </returns>
		public IEnumerator<double> GetEnumerator()
		{
			for (int i = 0; i < noOfRows; i++)
			{
				for (int j = 0; j < noOfColumns; j++)
				{
					yield return data[i, j];
				}
			}
		}

		/// <summary>
		/// Clears all the objects in this instance.
		/// </summary>
		public void Clear()
		{
			for (int i = 0; i < noOfRows; i++)
			{
				for (int j = 0; j < noOfColumns; j++)
				{
					data[i, j] = 0;
				}
			}
		}

		/// <summary>
		/// Adds an item to the <see cref="T:System.Collections.Generic.ICollection`1"></see>.
		/// </summary>
		/// <param name="item">The object to add to the <see cref="T:System.Collections.Generic.ICollection`1"></see>.</param>
		/// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"></see> is read-only.</exception>
		void ICollection<double>.Add(double item)
		{
			throw new NotSupportedException();
		}

		/// <summary>
		/// Removes the first occurrence of a specific object from the <see cref="T:System.Collections.Generic.ICollection`1"></see>.
		/// </summary>
		/// <param name="item">The object to remove from the <see cref="T:System.Collections.Generic.ICollection`1"></see>.</param>
		/// <returns>
		/// true if item was successfully removed from the <see cref="T:System.Collections.Generic.ICollection`1"></see>; otherwise, false. This method also returns false if item is not found in the original <see cref="T:System.Collections.Generic.ICollection`1"></see>.
		/// </returns>
		/// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"></see> is read-only.</exception>
		bool ICollection<double>.Remove(double item)
		{
			throw new NotSupportedException();
		}

		/// <summary>
		/// Compares the current instance with another object of the same type.
		/// </summary>
		/// <param name="obj">An object to compare with this instance.</param>
		/// <returns>
		/// A 32-bit signed integer that indicates the relative order of the objects being compared. The return value has these meanings: Value Meaning Less than zero This instance is less than obj. Zero This instance is equal to obj. Greater than zero This instance is greater than obj.
		/// </returns>
		/// <exception cref="T:System.ArgumentException">obj is not the same type as this instance. </exception>
		public int CompareTo(object obj)
		{
			if (obj == null)
			{
				throw new ArgumentNullException("obj");
			}

			if (obj.GetType() == this.GetType())
			{
				Matrix m = obj as Matrix;
				return this.Count.CompareTo(m.Count);
			}
			else
			{
				return this.GetType().FullName.CompareTo(obj.GetType().FullName);
			}
		}

		#endregion

		#region ICollection<double> Members

		/// <summary>
		/// Gets a value indicating whether this instance is read only.
		/// </summary>
		/// <value>
		/// 	<c>true</c> if this instance is read only; otherwise, <c>false</c>.
		/// </value>
		public bool IsReadOnly
		{
			get {
				return false;
			}
		}

		#endregion

		#region IEnumerable Members

		/// <summary>
		/// Gets the enumerator.
		/// </summary>
		/// <returns></returns>
		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
		{
			return this.GetEnumerator();
		}

		#endregion
	}
}

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 Microsoft Public License (Ms-PL)


Written By
Web Developer
South Africa South Africa
The author is a software consultant in South Africa, specializing in bespoke software solutions.

Comments and Discussions