525 lines
16 KiB
C#
525 lines
16 KiB
C#
using System;
|
|
using System.Diagnostics;
|
|
using Passer.LinearAlgebra;
|
|
#if UNITY_5_3_OR_NEWER
|
|
using Vector3Float = UnityEngine.Vector3;
|
|
#endif
|
|
|
|
public readonly struct Slice {
|
|
public int start { get; }
|
|
public int stop { get; }
|
|
public Slice(int start, int stop) {
|
|
this.start = start;
|
|
this.stop = stop;
|
|
}
|
|
}
|
|
|
|
public class Matrix2 {
|
|
public float[,] data { get; }
|
|
|
|
public uint nRows => (uint)data.GetLength(0);
|
|
public uint nCols => (uint)data.GetLength(1);
|
|
|
|
public Matrix2(uint nRows, uint nCols) {
|
|
this.data = new float[nRows, nCols];
|
|
}
|
|
public Matrix2(float[,] data) {
|
|
this.data = data;
|
|
}
|
|
|
|
public static Matrix2 Zero(uint nRows, uint nCols) {
|
|
return new Matrix2(nRows, nCols);
|
|
}
|
|
|
|
public static Matrix2 Identity(uint size) {
|
|
return Diagonal(1, size);
|
|
// float[,] resultData = new float[size, size];
|
|
// for (int i = 0; i < size; i++)
|
|
// resultData[i, i] = 1.0f;
|
|
// return new Matrix2(resultData);
|
|
}
|
|
|
|
public static Matrix2 Diagonal(Matrix1 v) {
|
|
float[,] resultData = new float[v.magnitude, v.magnitude];
|
|
for (int ix = 0; ix < v.magnitude; ix++)
|
|
resultData[ix, ix] = v.data[ix];
|
|
return new Matrix2(resultData);
|
|
}
|
|
public static Matrix2 Diagonal(float f, uint size) {
|
|
float[,] resultData = new float[size, size];
|
|
for (int ix = 0; ix < size; ix++)
|
|
resultData[ix, ix] = f;
|
|
return new Matrix2(resultData);
|
|
}
|
|
|
|
public static Matrix2 SkewMatrix(Vector3Float v) {
|
|
float[,] result = new float[3, 3] {
|
|
{0, -v.z, v.y},
|
|
{v.z, 0, -v.x},
|
|
{-v.y, v.x, 0}
|
|
};
|
|
return new Matrix2(result);
|
|
}
|
|
|
|
public Matrix2 Transpose() {
|
|
float[,] resultData = new float[this.nCols, this.nRows];
|
|
for (uint rowIx = 0; rowIx < this.nRows; rowIx++) {
|
|
for (uint colIx = 0; colIx < this.nCols; colIx++)
|
|
resultData[colIx, rowIx] = this.data[rowIx, colIx];
|
|
}
|
|
return new Matrix2(resultData);
|
|
// double checked code
|
|
}
|
|
|
|
public static Matrix2 operator -(Matrix2 m) {
|
|
float[,] result = new float[m.nRows, m.nCols];
|
|
|
|
for (int i = 0; i < m.nRows; i++) {
|
|
for (int j = 0; j < m.nCols; j++)
|
|
result[i, j] = -m.data[i, j];
|
|
}
|
|
return new Matrix2(result);
|
|
}
|
|
|
|
public static Matrix2 operator -(Matrix2 A, Matrix2 B) {
|
|
if (A.nRows != B.nRows || A.nCols != B.nCols)
|
|
throw new System.ArgumentException("Size of A must match size of B.");
|
|
|
|
float[,] result = new float[A.nRows, B.nCols];
|
|
|
|
for (int i = 0; i < A.nRows; i++) {
|
|
for (int j = 0; j < A.nCols; j++)
|
|
result[i, j] = A.data[i, j] - B.data[i, j];
|
|
}
|
|
return new Matrix2(result);
|
|
}
|
|
|
|
public static Matrix2 operator +(Matrix2 A, Matrix2 B) {
|
|
if (A.nRows != B.nRows || A.nCols != B.nCols)
|
|
throw new System.ArgumentException("Size of A must match size of B.");
|
|
|
|
float[,] result = new float[A.nRows, B.nCols];
|
|
|
|
for (int i = 0; i < A.nRows; i++) {
|
|
for (int j = 0; j < A.nCols; j++)
|
|
result[i, j] = A.data[i, j] + B.data[i, j];
|
|
}
|
|
return new Matrix2(result);
|
|
}
|
|
|
|
public static Matrix2 operator *(Matrix2 A, Matrix2 B) {
|
|
if (A.nCols != B.nRows)
|
|
throw new System.ArgumentException("Number of columns in A must match number of rows in B.");
|
|
|
|
float[,] result = new float[A.nRows, B.nCols];
|
|
|
|
for (int i = 0; i < A.nRows; i++) {
|
|
for (int j = 0; j < B.nCols; j++) {
|
|
float sum = 0.0f;
|
|
for (int k = 0; k < A.nCols; k++)
|
|
sum += A.data[i, k] * B.data[k, j];
|
|
|
|
result[i, j] = sum;
|
|
}
|
|
}
|
|
|
|
return new Matrix2(result);
|
|
// double checked code
|
|
}
|
|
|
|
public static Matrix1 operator *(Matrix2 A, Matrix1 v) {
|
|
float[] result = new float[A.nRows];
|
|
|
|
for (int i = 0; i < A.nRows; i++) {
|
|
for (int j = 0; j < A.nCols; j++) {
|
|
result[i] += A.data[i, j] * v.data[j];
|
|
}
|
|
}
|
|
|
|
return new Matrix1(result);
|
|
}
|
|
|
|
public static Vector3Float operator *(Matrix2 A, Vector3Float v) {
|
|
return new Vector3Float(
|
|
A.data[0, 0] * v.x + A.data[0, 1] * v.y + A.data[0, 2] * v.z,
|
|
A.data[1, 0] * v.x + A.data[1, 1] * v.y + A.data[1, 2] * v.z,
|
|
A.data[2, 0] * v.x + A.data[2, 1] * v.y + A.data[2, 2] * v.z
|
|
);
|
|
}
|
|
|
|
|
|
public static Matrix2 operator *(Matrix2 A, float s) {
|
|
float[,] result = new float[A.nRows, A.nCols];
|
|
|
|
for (int i = 0; i < A.nRows; i++) {
|
|
for (int j = 0; j < A.nCols; j++)
|
|
result[i, j] += A.data[i, j] * s;
|
|
}
|
|
|
|
return new Matrix2(result);
|
|
}
|
|
public static Matrix2 operator *(float scalar, Matrix2 A) {
|
|
return A * scalar;
|
|
}
|
|
|
|
public Matrix2 Slice(Slice slice) {
|
|
return Slice(slice.start, slice.stop);
|
|
}
|
|
public Matrix2 Slice(int from, int to) {
|
|
if (from < 0 || to >= this.nRows)
|
|
throw new System.ArgumentException("Slice index out of range.");
|
|
|
|
float[,] result = new float[to - from, this.nCols];
|
|
int resultRowIx = 0;
|
|
for (int rowIx = from; rowIx < to; rowIx++) {
|
|
for (int colIx = 0; colIx < this.nCols; colIx++) {
|
|
result[resultRowIx, colIx] = this.data[rowIx, colIx];
|
|
}
|
|
resultRowIx++;
|
|
}
|
|
|
|
return new Matrix2(result);
|
|
}
|
|
public void UpdateSlice(Slice slice, Matrix2 m) {
|
|
int mRowIx = 0;
|
|
for (int rowIx = slice.start; rowIx < slice.stop; rowIx++) {
|
|
for (int colIx = 0; colIx < this.nCols; colIx++)
|
|
this.data[rowIx, colIx] = m.data[mRowIx, colIx];
|
|
}
|
|
}
|
|
public void UpdateSlice(Slice rowRange, Slice colRange, Matrix2 m) {
|
|
for (int i = rowRange.start; i < rowRange.stop; i++) {
|
|
for (int j = colRange.start; j < colRange.stop; j++)
|
|
this.data[i, j] = m.data[i - rowRange.start, j - colRange.stop];
|
|
}
|
|
}
|
|
|
|
public Matrix2 Inverse() {
|
|
Matrix2 A = this;
|
|
// unchecked
|
|
uint n = A.nRows;
|
|
|
|
// Create an identity matrix of the same size as the original matrix
|
|
float[,] augmentedMatrix = new float[n, 2 * n];
|
|
for (int i = 0; i < n; i++) {
|
|
for (int j = 0; j < n; j++) {
|
|
augmentedMatrix[i, j] = A.data[i, j];
|
|
augmentedMatrix[i, j + n] = (i == j) ? 1 : 0; // Identity matrix
|
|
}
|
|
}
|
|
|
|
// Perform Gaussian elimination
|
|
for (int i = 0; i < n; i++) {
|
|
// Find the pivot row
|
|
float pivot = augmentedMatrix[i, i];
|
|
if (Math.Abs(pivot) < 1e-10) // Check for singular matrix
|
|
throw new InvalidOperationException("Matrix is singular and cannot be inverted.");
|
|
|
|
// Normalize the pivot row
|
|
for (int j = 0; j < 2 * n; j++)
|
|
augmentedMatrix[i, j] /= pivot;
|
|
|
|
// Eliminate the column below the pivot
|
|
for (int j = i + 1; j < n; j++) {
|
|
float factor = augmentedMatrix[j, i];
|
|
for (int k = 0; k < 2 * n; k++)
|
|
augmentedMatrix[j, k] -= factor * augmentedMatrix[i, k];
|
|
}
|
|
}
|
|
|
|
// Back substitution
|
|
for (uint i = n - 1; i >= 0; i--) {
|
|
// Eliminate the column above the pivot
|
|
for (uint j = i - 1; j >= 0; j--) {
|
|
float factor = augmentedMatrix[j, i];
|
|
for (int k = 0; k < 2 * n; k++)
|
|
augmentedMatrix[j, k] -= factor * augmentedMatrix[i, k];
|
|
}
|
|
}
|
|
|
|
// Extract the inverse matrix from the augmented matrix
|
|
float[,] inverse = new float[n, n];
|
|
for (int i = 0; i < n; i++) {
|
|
for (int j = 0; j < n; j++)
|
|
inverse[i, j] = augmentedMatrix[i, j + n];
|
|
}
|
|
|
|
return new Matrix2(inverse);
|
|
}
|
|
}
|
|
|
|
public class Matrix1 {
|
|
public float[] data { get; }
|
|
|
|
public uint magnitude => (uint)data.GetLength(0);
|
|
|
|
public Matrix1(uint magnitude) {
|
|
this.data = new float[magnitude];
|
|
}
|
|
|
|
public Matrix1(float[] data) {
|
|
this.data = data;
|
|
}
|
|
|
|
public static Matrix1 Zero(uint magnitude) {
|
|
return new Matrix1(magnitude);
|
|
}
|
|
|
|
public static Matrix1 FromVector2(Vector2Float v) {
|
|
float[] result = new float[2];
|
|
result[0] = v.x;
|
|
result[1] = v.y;
|
|
return new Matrix1(result);
|
|
}
|
|
|
|
public static Matrix1 FromVector3(Vector3Float v) {
|
|
float[] result = new float[3];
|
|
result[0] = v.x;
|
|
result[1] = v.y;
|
|
result[2] = v.z;
|
|
return new Matrix1(result);
|
|
}
|
|
|
|
public Vector2Float vector2 {
|
|
get {
|
|
if (this.magnitude != 2)
|
|
throw new System.ArgumentException("Matrix1 must be of size 2");
|
|
return new Vector2Float(this.data[0], this.data[1]);
|
|
}
|
|
}
|
|
public Vector3Float vector3 {
|
|
get {
|
|
if (this.magnitude != 3)
|
|
throw new System.ArgumentException("Matrix1 must be of size 3");
|
|
return new Vector3Float(this.data[0], this.data[1], this.data[2]);
|
|
}
|
|
}
|
|
|
|
public Matrix2 Transpose() {
|
|
float[,] r = new float[1, this.magnitude];
|
|
for (uint colIx = 0; colIx < this.magnitude; colIx++)
|
|
r[1, colIx] = this.data[colIx];
|
|
|
|
return new Matrix2(r);
|
|
}
|
|
|
|
public static float Dot(Matrix1 a, Matrix1 b) {
|
|
if (a.magnitude != b.magnitude)
|
|
throw new System.ArgumentException("Vectors must be of the same length.");
|
|
|
|
float result = 0.0f;
|
|
for (int i = 0; i < a.magnitude; i++) {
|
|
result += a.data[i] * b.data[i];
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public static Matrix1 operator *(Matrix1 A, float f) {
|
|
float[] result = new float[A.magnitude];
|
|
|
|
for (int i = 0; i < A.magnitude; i++)
|
|
result[i] += A.data[i] * f;
|
|
|
|
return new Matrix1(result);
|
|
}
|
|
public static Matrix1 operator *(float f, Matrix1 A) {
|
|
return A * f;
|
|
}
|
|
|
|
public Matrix1 Slice(Slice range) {
|
|
return Slice(range.start, range.stop);
|
|
}
|
|
public Matrix1 Slice(int from, int to) {
|
|
if (from < 0 || to >= this.magnitude)
|
|
throw new System.ArgumentException("Slice index out of range.");
|
|
|
|
float[] result = new float[to - from];
|
|
int resultIx = 0;
|
|
for (int ix = from; ix < to; ix++)
|
|
result[resultIx++] = this.data[ix];
|
|
|
|
return new Matrix1(result);
|
|
}
|
|
public void UpdateSlice(Slice slice, Matrix1 v) {
|
|
int vIx = 0;
|
|
for (int ix = slice.start; ix < slice.stop; ix++, vIx++)
|
|
this.data[ix] = v.data[vIx];
|
|
}
|
|
}
|
|
|
|
// public class Matrix {
|
|
// private readonly uint rows = 0;
|
|
// private readonly uint cols = 0;
|
|
// private float[] data;
|
|
|
|
// public Matrix(uint rows, uint cols) {
|
|
// this.rows = rows;
|
|
// this.cols = cols;
|
|
// }
|
|
|
|
// public static float[,] Diagonal(float[] v) {
|
|
// float[,] r = new float[v.Length, v.Length];
|
|
// for (int i = 0; i < v.Length; i++) {
|
|
// r[i, i] = v[i];
|
|
// }
|
|
// return r;
|
|
// }
|
|
|
|
// public static float[,] Transpose(float[,] m) {
|
|
// int rows = m.GetLength(0);
|
|
// int cols = m.GetLength(1);
|
|
// float[,] r = new float[cols, rows];
|
|
// for (uint rowIx = 0; rowIx < rows; rowIx++) {
|
|
// for (uint colIx = 0; colIx < cols; colIx++)
|
|
// r[colIx, rowIx] = m[rowIx, colIx];
|
|
// }
|
|
// return r;
|
|
// // double checked code
|
|
// }
|
|
|
|
// public static void NegateColumn(float[,] m, uint colIx) {
|
|
// for (uint rowIx = 0; rowIx < m.GetLength(0); rowIx++) {
|
|
// m[rowIx, colIx] = -m[rowIx, colIx];
|
|
// }
|
|
// }
|
|
|
|
// public static float Determinant(float[,] matrix) {
|
|
// int n = matrix.GetLength(0);
|
|
// if (n != matrix.GetLength(1))
|
|
// throw new System.ArgumentException("Matrix must be square.");
|
|
|
|
// if (n == 1)
|
|
// return matrix[0, 0]; // Base case for 1x1 matrix
|
|
|
|
// if (n == 2) // Base case for 2x2 matrix
|
|
// return matrix[0, 0] * matrix[1, 1] - matrix[0, 1] * matrix[1, 0];
|
|
|
|
// float det = 0;
|
|
// for (int col = 0; col < n; col++)
|
|
// det += (col % 2 == 0 ? 1 : -1) * matrix[0, col] * Determinant(Minor(matrix, 0, col));
|
|
|
|
// return det;
|
|
// }
|
|
|
|
// // Helper function to compute the minor of a matrix
|
|
// private static float[,] Minor(float[,] matrix, int rowToRemove, int colToRemove) {
|
|
// int n = matrix.GetLength(0);
|
|
// float[,] minor = new float[n - 1, n - 1];
|
|
|
|
// int r = 0, c = 0;
|
|
// for (int i = 0; i < n; i++) {
|
|
// if (i == rowToRemove) continue;
|
|
|
|
// c = 0;
|
|
// for (int j = 0; j < n; j++) {
|
|
// if (j == colToRemove) continue;
|
|
|
|
// minor[r, c] = matrix[i, j];
|
|
// c++;
|
|
// }
|
|
// r++;
|
|
// }
|
|
|
|
// return minor;
|
|
// }
|
|
|
|
// public static float[,] MultiplyMatrices(float[,] A, float[,] B) {
|
|
// int rowsA = A.GetLength(0);
|
|
// int colsA = A.GetLength(1);
|
|
// int rowsB = B.GetLength(0);
|
|
// int colsB = B.GetLength(1);
|
|
|
|
// if (colsA != rowsB)
|
|
// throw new System.ArgumentException("Number of columns in A must match number of rows in B.");
|
|
|
|
// float[,] result = new float[rowsA, colsB];
|
|
|
|
// for (int i = 0; i < rowsA; i++) {
|
|
// for (int j = 0; j < colsB; j++) {
|
|
// float sum = 0.0f;
|
|
// for (int k = 0; k < colsA; k++)
|
|
// sum += A[i, k] * B[k, j];
|
|
|
|
// result[i, j] = sum;
|
|
// }
|
|
// }
|
|
|
|
// return result;
|
|
// // double checked code
|
|
// }
|
|
|
|
// public static float[] MultiplyMatrixVector(float[,] A, float[] v) {
|
|
// int rows = A.GetLength(0);
|
|
// int cols = A.GetLength(1);
|
|
// float[] result = new float[rows];
|
|
|
|
// for (int i = 0; i < rows; i++) {
|
|
// for (int j = 0; j < cols; j++) {
|
|
// result[i] += A[i, j] * v[j];
|
|
// }
|
|
// }
|
|
|
|
// return result;
|
|
// }
|
|
|
|
// // Vector-matrix multiplication
|
|
// public static Vector3Float MultiplyMatrixVector3(float[,] A, Vector3Float v) {
|
|
// return new Vector3Float(
|
|
// A[0, 0] * v.x + A[0, 1] * v.y + A[0, 2] * v.z,
|
|
// A[1, 0] * v.x + A[1, 1] * v.y + A[1, 2] * v.z,
|
|
// A[2, 0] * v.x + A[2, 1] * v.y + A[2, 2] * v.z
|
|
// );
|
|
// }
|
|
|
|
// public static float[,] MultiplyMatrixScalar(float[,] A, float s) {
|
|
// int rows = A.GetLength(0);
|
|
// int cols = A.GetLength(1);
|
|
// float[,] result = new float[rows, cols];
|
|
|
|
// for (int i = 0; i < rows; i++) {
|
|
// for (int j = 0; j < cols; j++) {
|
|
// result[i, j] += A[i, j] * s;
|
|
// }
|
|
// }
|
|
|
|
// return result;
|
|
// }
|
|
|
|
// public static float[] GetColumn(float[,] M, int col) {
|
|
// int rows = M.GetLength(0);
|
|
// float[] column = new float[rows];
|
|
// for (int i = 0; i < rows; i++) {
|
|
// column[i] = M[i, col];
|
|
// }
|
|
// return column;
|
|
// }
|
|
|
|
// public static Vector3Float GetRow3(float[,] M, int rowIx) {
|
|
// int cols = M.GetLength(1);
|
|
// Vector3Float row = new(
|
|
// M[rowIx, 0],
|
|
// M[rowIx, 1],
|
|
// M[rowIx, 2]
|
|
// );
|
|
// return row;
|
|
// }
|
|
|
|
// public static void SetRow3(float[,] M, int rowIx, Vector3Float v) {
|
|
// M[rowIx, 0] = v.x;
|
|
// M[rowIx, 1] = v.y;
|
|
// M[rowIx, 2] = v.z;
|
|
// }
|
|
|
|
// public static float Dot(float[] a, float[] b) {
|
|
// float sum = 0;
|
|
// for (int i = 0; i < a.Length; i++) sum += a[i] * b[i];
|
|
// return sum;
|
|
// }
|
|
|
|
// public static float[,] IdentityMatrix(int size) {
|
|
// float[,] I = new float[size, size];
|
|
// for (int i = 0; i < size; i++) I[i, i] = 1.0f;
|
|
// return I;
|
|
// }
|
|
|
|
// } |