Skip to content

Instantly share code, notes, and snippets.

@AdamWhiteHat
Created January 25, 2023 17:57
Show Gist options
  • Save AdamWhiteHat/c40738ee62a63e5741458f0bc54f8009 to your computer and use it in GitHub Desktop.
Save AdamWhiteHat/c40738ee62a63e5741458f0bc54f8009 to your computer and use it in GitHub Desktop.
Generic Vector<T> Class
/// <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