Created
January 25, 2023 17:57
-
-
Save AdamWhiteHat/c40738ee62a63e5741458f0bc54f8009 to your computer and use it in GitHub Desktop.
Generic Vector<T> Class
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <summary> | |
/// A generic Vector class. | |
/// Uses/Requires the GenericArithmeticFactory class: | |
/// https://gist.github.com/AdamWhiteHat/71d548ebfb2ee67fcbd78a39a9751423 | |
/// </summary> | |
public class Vector<T> | |
{ | |
/// <summary>The dimension of the vector, i.e. the number of elements or its length.</summary> | |
public int Dimensions { get { return Elements == null ? 0 : Elements.Length; } } | |
/// <summary>An array for accessing the elements of this vector.</summary> | |
public T[] Elements { get; set; } | |
/// <summary>An indexer for accessing the elements of this vector.</summary> | |
public T this[int index] { get { return Elements[index]; } } | |
/// <summary>Instantiates a new zero vector instance.</summary> | |
public Vector() { Elements = new T[0]; } | |
/// <summary>Instantiayes a new Vector instance given an array of elements.</summary> | |
public Vector(T[] elements) | |
{ | |
Elements = elements; | |
} | |
/// <summary>Instantiayes a new Vector instance given an array of elements.</summary> | |
public static Vector<T> Factory(params T[] elements) | |
{ | |
return new Vector<T>(elements); | |
} | |
/// <summary>Turns a coefficient array from a ExtendedArithmetic.Polynomial instance into a Vector instance.</summary> | |
public static Vector<T> FromPolynomial(Polynomial m) | |
{ | |
T[] elements = m.Terms.Select(trm => (double)trm.CoEfficient) | |
.Select(d => GenericArithmeticFactory<T>.ConvertImplementation<double, T>.Convert(d)) | |
.ToArray(); | |
return Vector<T>.Factory(elements); | |
} | |
/// <summary>Clones a vector by creating a new copy of each element into a new instance.</summary> | |
public Vector<T> Clone() | |
{ | |
return new Vector<T>(Elements.ToArray()); | |
} | |
/// <summary> | |
/// Returns the normalized or unit vector of a Vector. | |
/// That is, it returns a vector with the same direction as the given vector, but with a length of 1. | |
/// </summary> | |
public static Vector<T> Normalize(Vector<T> input) | |
{ | |
T norm = Norm(input); | |
return ScalarDivide(input, norm); | |
} | |
/// <summary> | |
/// Returns the L2-Norm of a Vector. | |
/// That is, it returns the square root of the sum of every element squared. | |
/// </summary> | |
public static T Norm(Vector<T> input) | |
{ | |
Func<T, T> squareRootOperation = GenericArithmeticFactory<T>.GetSquareRootOperation(); | |
T dotProduct = DotProduct(input, input); | |
return squareRootOperation.Invoke(dotProduct); | |
} | |
/// <summary> | |
/// Returns the dot product of two vectors. | |
/// </summary> | |
/// <param name="left">The first vector.</param> | |
/// <param name="right">The second vector.</param> | |
/// <returns>The dot product.</returns> | |
public static T DotProduct(Vector<T> left, Vector<T> right) | |
{ | |
Func<T, T, T> addOperation = GenericArithmeticFactory<T>.GetArithmeticOperation(ExpressionType.Add); | |
Vector<T> productVector = Multiply(left, right); | |
T result = default(T); | |
bool isFirstPass = true; | |
foreach (T t in productVector.Elements) | |
{ | |
if (isFirstPass) | |
{ | |
isFirstPass = false; | |
result = t; | |
} | |
else | |
{ | |
result = addOperation(result, t); | |
} | |
} | |
return result; | |
} | |
/// <summary> | |
/// Adds two vectors together. | |
/// </summary> | |
/// <param name="left">The first source vector.</param> | |
/// <param name="right">The second source vector.</param> | |
/// <returns>The summed vector.</returns> | |
public static Vector<T> Add(Vector<T> left, Vector<T> right) | |
{ | |
Func<T, T, T> addOperation = GenericArithmeticFactory<T>.GetArithmeticOperation(ExpressionType.Add); | |
return PairwiseForEach(left, right, addOperation); | |
} | |
/// <summary> | |
/// Subtracts the second vector from the first. | |
/// </summary> | |
/// <param name="left">The first source vector.</param> | |
/// <param name="right">The second source vector.</param> | |
/// <returns>The difference vector.</returns> | |
public static Vector<T> Subtract(Vector<T> left, Vector<T> right) | |
{ | |
Func<T, T, T> subtractOperation = GenericArithmeticFactory<T>.GetArithmeticOperation(ExpressionType.Subtract); | |
return PairwiseForEach(left, right, subtractOperation); | |
} | |
/// <summary> | |
/// Multiplies two vectors together. | |
/// </summary> | |
/// <param name="left">The first source vector.</param> | |
/// <param name="right">The second source vector.</param> | |
/// <returns>The product vector.</returns> | |
public static Vector<T> Multiply(Vector<T> left, Vector<T> right) | |
{ | |
Func<T, T, T> multiplyOperation = GenericArithmeticFactory<T>.GetArithmeticOperation(ExpressionType.Multiply); | |
return PairwiseForEach(left, right, multiplyOperation); | |
} | |
/// <summary> | |
/// Divides the first vector by the second. | |
/// </summary> | |
/// <param name="left">The first source vector.</param> | |
/// <param name="right">The second source vector.</param> | |
/// <returns>The vector resulting from the division.</returns> | |
public static Vector<T> Divide(Vector<T> left, Vector<T> right) | |
{ | |
Func<T, T, T> divideOperation = GenericArithmeticFactory<T>.GetArithmeticOperation(ExpressionType.Divide); | |
return PairwiseForEach(left, right, divideOperation); | |
} | |
/// <summary> | |
/// Adds a vector by a given scalar. | |
/// </summary> | |
/// <param name="vector">The source vector.</param> | |
/// <param name="scalar">The scalar value.</param> | |
/// <returns>The result of summation.</returns> | |
public static Vector<T> ScalarAdd(Vector<T> vector, T scalar) | |
{ | |
Func<T, T, T> addOperation = GenericArithmeticFactory<T>.GetArithmeticOperation(ExpressionType.Add); | |
Vector<T> scalarVector = new Vector<T>(Enumerable.Repeat(scalar, vector.Dimensions).ToArray()); | |
return PairwiseForEach(vector, scalarVector, addOperation); | |
} | |
/// <summary> | |
/// Multiplies a vector by a given scalar. | |
/// </summary> | |
/// <param name="vector">The source vector.</param> | |
/// <param name="scalar">The scalar value.</param> | |
/// <returns>The scaled vector.</returns> | |
public static Vector<T> ScalarMultiply(Vector<T> vector, T scalar) | |
{ | |
Func<T, T, T> multiplyOperation = GenericArithmeticFactory<T>.GetArithmeticOperation(ExpressionType.Multiply); | |
Vector<T> scalarVector = new Vector<T>(Enumerable.Repeat(scalar, vector.Dimensions).ToArray()); | |
return PairwiseForEach(vector, scalarVector, multiplyOperation); | |
} | |
/// <summary> | |
/// Divides a vector by a given scalar. | |
/// </summary> | |
/// <param name="vector">The source vector.</param> | |
/// <param name="scalar">The scalar value.</param> | |
/// <returns>The result of the division.</returns> | |
public static Vector<T> ScalarDivide(Vector<T> vector, T scalar) | |
{ | |
Func<T, T, T> divideOperation = GenericArithmeticFactory<T>.GetArithmeticOperation(ExpressionType.Divide); | |
Vector<T> scalarVector = new Vector<T>(Enumerable.Repeat(scalar, vector.Dimensions).ToArray()); | |
return PairwiseForEach(vector, scalarVector, divideOperation); | |
} | |
/// <summary> | |
/// Returns a vector whose elements are the square root of each of the source vector's elements. | |
/// </summary> | |
/// <param name="value">The source vector.</param> | |
/// <returns>The square root vector.</returns> | |
public static Vector<T> SquareRoot(Vector<T> vector) | |
{ | |
Func<T, T> sqrtOperation = GenericArithmeticFactory<T>.GetSquareRootOperation(); | |
return ForEach(vector, sqrtOperation); | |
} | |
/// <summary> | |
/// Returns the reflection of a vector off a surface that has the specified normal. | |
/// </summary> | |
/// <param name="vector">The source vector.</param> | |
/// <param name="normal">The normal of the surface being reflected off.</param> | |
/// <returns>The reflected vector.</returns> | |
public static Vector<T> Reflect(Vector<T> vector, Vector<T> normal) | |
{ | |
var dot = DotProduct(vector, normal); | |
var result = ScalarMultiply(normal, dot); | |
return ScalarMultiply(result, GenericArithmeticFactory<T>.ConvertImplementation<int, T>.Convert(2)); | |
} | |
/// <summary> | |
/// Turns a Vector into a polynomial, using the vector values in order as the coefficients. | |
/// Starts with the highest exponent of one less than the number of elements and works | |
/// down to zero, in order. | |
/// </summary> | |
/// <returns>The ExtendedArithmetic.Polynomial class.</returns> | |
public Polynomial ToPolynomial() | |
{ | |
List<int> intElements = | |
Elements | |
.Select(d => GenericArithmeticFactory<T>.ConvertImplementation<T, int>.Convert(d)) | |
.Where(i => i != 0) | |
.ToList(); | |
int degree = intElements.Count - 1; | |
List<Term> terms = new List<Term>(); | |
foreach (int i in intElements) | |
{ | |
Term newTerm = new Term(i, degree); | |
terms.Add(newTerm); | |
degree--; | |
} | |
return new Polynomial(terms.ToArray()); | |
} | |
/// <summary>Begins two vectors, takes an element from each vector and applies a function to each bijective pair, collecting the results in a new vector.</summary> | |
private static Vector<T> PairwiseForEach(Vector<T> left, Vector<T> right, Func<T, T, T> operation) | |
{ | |
if (left.Dimensions != right.Dimensions) | |
{ | |
throw new Exception("Both vector dimensions must be the same."); | |
} | |
int index = 0; | |
int max = left.Dimensions; | |
List<T> results = new List<T>(); | |
while (index < max) | |
{ | |
T result = operation.Invoke(left[index], right[index]); | |
results.Add(result); | |
index++; | |
} | |
return new Vector<T>(results.ToArray()); | |
} | |
/// <summary>Applies a function to each vector element, collecting the results in a new vector.</summary> | |
private static Vector<T> ForEach(Vector<T> input, Func<T, T> operation) | |
{ | |
if (input.Dimensions == 0) | |
{ | |
return input; | |
} | |
List<T> results = new List<T>(); | |
foreach (T element in input.Elements) | |
{ | |
results.Add(operation.Invoke(element)); | |
} | |
return new Vector<T>(results.ToArray()); | |
} | |
/// <summary> | |
/// Returns a String representing this Vector instance. | |
/// </summary> | |
/// <returns>The string representation.</returns> | |
public override string ToString() | |
{ | |
return $"[ {string.Join(", ", this.Elements.Select(e => e.ToString()))} ]"; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment