Click here to Skip to main content
15,891,828 members
Articles / Programming Languages / C#

The List Trifecta, Part 1

Rate me:
Please Sign up or sign in to vote.
4.97/5 (21 votes)
20 May 2016LGPL321 min read 37.1K   161   40  
The A-list is an all-purpose list, a data structure that can support most standard list operation in O(log n) time and does lots of other stuff, too
namespace Loyc.Math
{
	using System;
	using System.Diagnostics;

	public struct Vector<T> : IPoint<T>, INewPoint<Vector<T>, T> where T:IConvertible, IEquatable<T>
	{
		static ISignedMath<T> m = Maths<T>.SignedMath;

		public Vector(T x, T y) { _x = x; _y = y; }

		T _x, _y;
		public T X { get { return _x; } set { _x = value; } }
		public T Y { get { return _y; } set { _y = value; } }
	
		public override bool Equals(object other) { return other is Vector<T> && ((Vector<T>)other) == this; }
		public override int GetHashCode() { return _x.GetHashCode() ^ _y.GetHashCode(); }
		public override string ToString() { return string.Format("({0},{1})", _x, _y); }

		public Vector<T> New(T x, T y) { return new Vector<T>(x, y); }
		IPoint<T> INewPoint<IPoint<T>,T>.New(T x, T y) { return new Vector<T>(x, y); }

		public static explicit operator Point<T>(Vector<T> p) { return new Point<T>(p.X, p.Y); }

		public static explicit operator Vector<int>(Vector<T> p) { return new Vector<int>(p._x.ToInt32(null), p._y.ToInt32(null)); }
		public static explicit operator Vector<long>(Vector<T> p) { return new Vector<long>(p._x.ToInt64(null), p._y.ToInt64(null)); }
		public static explicit operator Vector<float>(Vector<T> p) { return new Vector<float>(p._x.ToSingle(null), p._y.ToSingle(null)); }
		public static explicit operator Vector<double>(Vector<T> p) { return new Vector<double>(p._x.ToDouble(null), p._y.ToDouble(null)); }

		public static Vector<T> operator+(Vector<T> a, Vector<T> b) { return new Vector<T>(m.Add(a.X,b.X), m.Add(a.Y,b.Y)); }
		public static Vector<T> operator-(Vector<T> a, Vector<T> b) { return new Vector<T>(m.Subtract(a.X,b.X), m.Subtract(a.Y,b.Y)); }
		public static Vector<T> operator*(Vector<T> p, T factor) { return new Vector<T>(m.Multiply(p.X,factor), m.Multiply(p.Y,factor)); }
		public static Vector<T> operator/(Vector<T> p, T factor) { return new Vector<T>(m.Divide(p.X,factor), m.Divide(p.Y,factor)); }
		public static Vector<T> operator<<(Vector<T> p, int amt) { return new Vector<T>(m.ShiftLeft(p.X, amt), m.ShiftLeft(p.Y, amt)); }
		public static Vector<T> operator>>(Vector<T> p, int amt) { return new Vector<T>(m.ShiftRight(p.X, amt), m.ShiftRight(p.Y, amt)); }

		/// <summary>Dot product. a*b equals lhs.Length*rhs.Length*Cos(theta) if theta 
		/// is the angle between two vectors.</summary>
		public static T operator*(Vector<T> a, Vector<T> b) { return m.Add(m.Multiply(a.X,b.X), m.Multiply(a.Y,b.Y)); }
		/// <summary>Computes the "cross product" of a pair of vectors.</summary>
		/// <remarks>
		/// This is not a general cross product, as cross product is only a 3D concept,
		/// but this operator acts as though the two points were in the Z=0 plane and
		/// returns the Z coordinate of the cross product: b.X * a.Y - b.Y * a.X.
		/// This value is zero if the vectors are parallel; it is a.Length * b.Length 
		/// or -a.Length * b.Length if the vectors are perpendicular. One use of 
		/// cross product is to determine whether the angle between two lines is greater 
		/// or less than 180 degrees, corresponding to return values less or greater than 
		/// zero.
		/// </remarks>
		public T Cross(Vector<T> b) { return m.Subtract(m.Multiply(X, b.Y), m.Multiply(Y, b.X)); }
		/// <summary>Rotates a vector 90 degrees.</summary>
		/// <remarks>
		/// Rotatation is clockwise if increasing Y goes downward, counter-
		/// clockwise if increasing Y goes upward. If the vector represents the 
		/// direction of a line, the result also represents the coefficients 
		/// (a,b) of the implicit line equation aX + bY + c = 0.
		/// </remarks>
		public Vector<T> Rot90() { return new Vector<T>(m.Negate(Y), X); }
		/// <summary>Reverses a vector.</summary>
		public static Vector<T> operator-(Vector<T> a) { return new Vector<T>(m.Negate(a.X), m.Negate(a.Y)); }

		public static bool operator== (Vector<T> a, Vector<T> b) { return a.X.Equals(b.X) && a.Y.Equals(b.Y); }
		public static bool operator!= (Vector<T> a, Vector<T> b) { return !a.X.Equals(b.X) || !a.Y.Equals(b.Y); }
		public bool Equals(Vector<T> other) { return this == other; }
		
		/// <summary>Gets the square of the length of the vector.</summary>
		public T Quadrance { get { return m.Add(m.Square(X), m.Square(Y)); } }
		/// <summary>Gets the length of the vector.</summary>
		public T Length { get { return m.Sqrt(Quadrance); } }
		
		/// <summary>Gets the angle from 0 to 2*PI of the vector, where (1,0) has 
		/// angle 0 and (0,1) has angle PI/2.</summary>
		public double Angle
		{ 
			get {
				double angle = Math.Atan2(Y.ToDouble(null), X.ToDouble(null));
				if (angle < 0)
					return angle + 2*Math.PI; 
				return angle;
			}
		}

		public bool Normalize()
		{
			Debug.Assert(!m.IsInteger);
			T len = Length;
			if (len.Equals(m.Zero))
				return false;
			if (len.Equals(m.One)) {
				_x = m.Divide(_x, len);
				_y = m.Divide(_y, len);
			}
			return true;
		}
		
		public Vector<T> Normalized()
		{
			Vector<T> v = this;
			v.Normalize();
			return v;
		}

		Vector<T> MulDiv(T mul, T div)
		{
			return new Vector<T>(m.MulDiv(_x, mul, div), m.MulDiv(_y, mul, div));
		}
	}

	public struct VectorMath<T> : IAdditionGroup<Vector<T>> where T:IConvertible, IEquatable<T>
	{
		static IMath<T> m = Maths<T>.Math;

		public Vector<T> Add(Vector<T> a, Vector<T> b) { return new Vector<T>(m.Add(a.X, b.X), m.Add(a.Y, b.Y)); }
		public Vector<T> Subtract(Vector<T> a, Vector<T> b) { return new Vector<T>(m.Subtract(a.X, b.X), m.Subtract(a.Y, b.Y)); }
		public Vector<T> Zero { get { return new Vector<T>(); } }
	}
}

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 GNU Lesser General Public License (LGPLv3)


Written By
Software Developer None
Canada Canada
Since I started programming when I was 11, I wrote the SNES emulator "SNEqr", the FastNav mapping component, the Enhanced C# programming language (in progress), the parser generator LLLPG, and LES, a syntax to help you start building programming languages, DSLs or build systems.

My overall focus is on the Language of your choice (Loyc) initiative, which is about investigating ways to improve interoperability between programming languages and putting more power in the hands of developers. I'm also seeking employment.

Comments and Discussions