363 lines
14 KiB
C#

using Nerd_STF.Helpers;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace Nerd_STF.Mathematics.Algebra
{
public class Matrix2x2 : IMatrix<Matrix2x2>,
ISquareMatrix<Matrix2x2>
#if CS11_OR_GREATER
,ISplittable<Matrix2x2, (double[] r0c0, double[] r0c1, double[] r1c0, double[] r1c1)>,
IStaticMatrix<Matrix2x2>
#endif
{
public static Matrix2x2 Identity =>
new Matrix2x2(1, 0,
0, 1);
public static Matrix2x2 SignField =>
new Matrix2x2(+1, -1,
-1, +1);
public static Matrix2x2 One => new Matrix2x2(1, 1, 1, 1);
public static Matrix2x2 Zero => new Matrix2x2(0, 0, 0, 0);
public Int2 Size => (2, 2);
public double r0c0, r0c1,
r1c0, r1c1;
public Matrix2x2()
{
r0c0 = 0; r0c1 = 0;
r1c0 = 0; r1c1 = 0;
}
public Matrix2x2(Matrix2x2 copy)
{
r0c0 = copy.r0c0; r0c1 = copy.r0c1;
r1c0 = copy.r1c0; r1c1 = copy.r1c1;
}
public Matrix2x2(double r0c0, double r0c1, double r1c0, double r1c1)
{
this.r0c0 = r0c0;
this.r0c1 = r0c1;
this.r1c0 = r1c0;
this.r1c1 = r1c1;
}
/// <param name="byRows"><see langword="true"/> if the array is of the form [c, r], <see langword="false"/> if the array is of the form [r, c].</param>
public Matrix2x2(double[,] vals, bool byRows = false)
{
if (byRows) // Collection of rows ([c, r])
{
r0c0 = vals[0, 0]; r0c1 = vals[1, 0];
r1c0 = vals[0, 1]; r1c1 = vals[1, 1];
}
else // Collection of columns ([r, c])
{
r0c0 = vals[0, 0]; r0c1 = vals[0, 1];
r1c0 = vals[1, 0]; r1c1 = vals[1, 1];
}
}
/// <param name="byRows"><see langword="true"/> if the enumerable is a collection of rows (form [c, r]), <see langword="false"/> if the enumerable is a collection of columns (form [r, c]).</param>
public Matrix2x2(IEnumerable<IEnumerable<double>> vals, bool byRows = false)
{
MatrixHelper.SetMatrixValues(this, vals, byRows);
}
/// <param name="byRows"><see langword="true"/> if the enumerable is a collection of rows (form [c, r]), <see langword="false"/> if the enumerable is a collection of columns (form [r, c]).</param>
public Matrix2x2(IEnumerable<ListTuple<double>> vals, bool byRows = false)
{
MatrixHelper.SetMatrixValues(this, vals, byRows);
}
/// <param name="byRows"><see langword="true"/> if the fill goes through columns for each row, <see langword="false"/> if the fill goes through rows for each column.</param>
public Matrix2x2(Fill<double> fill, bool byRows = false)
{
if (byRows)
{
r0c0 = fill(0); r0c1 = fill(2);
r1c0 = fill(1); r1c1 = fill(3);
}
else
{
r0c0 = fill(0); r0c1 = fill(1);
r1c0 = fill(2); r1c1 = fill(3);
}
}
/// <param name="byRows"><see langword="true"/> if the fill is a collection of rows (form [c, r]), <see langword="false"/> if the fill is a collection of columns (form [r, c]).</param>
public Matrix2x2(Fill2d<double> fill, bool byRows = false)
{
if (byRows)
{
r0c0 = fill(0, 0); r0c1 = fill(1, 0);
r1c0 = fill(0, 1); r1c1 = fill(1, 1);
}
else
{
r0c0 = fill(0, 0); r0c1 = fill(0, 1);
r1c0 = fill(1, 0); r1c1 = fill(1, 1);
}
}
public double this[int r, int c]
{
get
{
switch (r)
{
case 0:
switch (c)
{
case 0: return r0c0;
case 1: return r0c1;
default: throw new ArgumentOutOfRangeException(nameof(c));
}
case 1:
switch (c)
{
case 0: return r1c0;
case 1: return r1c1;
default: throw new ArgumentOutOfRangeException(nameof(c));
}
default: throw new ArgumentOutOfRangeException(nameof(r));
}
}
set
{
switch (r)
{
case 0:
switch (c)
{
case 0: r0c0 = value; return;
case 1: r0c1 = value; return;
default: throw new ArgumentOutOfRangeException(nameof(c));
}
case 1:
switch (c)
{
case 0: r1c0 = value; return;
case 1: r1c1 = value; return;
default: throw new ArgumentOutOfRangeException(nameof(c));
}
default: throw new ArgumentOutOfRangeException(nameof(r));
}
}
}
public double this[Int2 index]
{
get => this[index.x, index.y];
set => this[index.x, index.y] = value;
}
public ListTuple<double> this[int index, RowColumn direction]
{
get
{
switch (direction)
{
case RowColumn.Row: return GetRow(index);
case RowColumn.Column: return GetColumn(index);
default: throw new ArgumentException($"Invalid direction {direction}.");
}
}
set
{
switch (direction)
{
case RowColumn.Row: SetRow(index, value); break;
case RowColumn.Column: SetColumn(index, value); break;
default: throw new ArgumentException($"Invalid direction {direction}.");
}
}
}
public static Matrix2x2 Average(IEnumerable<Matrix2x2> vals)
{
Matrix2x2 sum = Zero;
int count = 0;
foreach (Matrix2x2 m in vals)
{
sum += m;
count++;
}
return sum / count;
}
public static Matrix2x2 Lerp(Matrix2x2 a, Matrix2x2 b, double t, bool clamp = true) =>
new Matrix2x2(MathE.Lerp(a.r0c0, b.r0c0, t, clamp), MathE.Lerp(a.r0c1, b.r0c1, t, clamp),
MathE.Lerp(a.r1c0, b.r1c0, t, clamp), MathE.Lerp(a.r1c1, b.r1c1, t, clamp));
public static Matrix2x2 Product(IEnumerable<Matrix2x2> vals)
{
bool any = false;
Matrix2x2 result = One;
foreach (Matrix2x2 m in vals)
{
any = true;
result *= m;
}
return any ? result : Zero;
}
public static Matrix2x2 Sum(IEnumerable<Matrix2x2> vals)
{
Matrix2x2 result = Zero;
foreach (Matrix2x2 m in vals) result += m;
return result;
}
public static (double[] r0c0, double[] r0c1, double[] r1c0, double[] r1c1) SplitArray(IEnumerable<Matrix2x2> vals)
{
int count = vals.Count();
double[] r0c0 = new double[count], r0c1 = new double[count],
r1c0 = new double[count], r1c1 = new double[count];
int index = 0;
foreach (Matrix2x2 m in vals)
{
r0c0[index] = m.r0c0; r0c1[index] = m.r0c1;
r1c0[index] = m.r1c0; r1c1[index] = m.r1c1;
}
return (r0c0, r0c1,
r1c0, r1c1);
}
public ListTuple<double> GetRow(int row)
{
double[] vals;
switch (row)
{
case 0: vals = new double[] { r0c0, r0c1 }; break;
case 1: vals = new double[] { r1c0, r1c1 }; break;
default: throw new ArgumentOutOfRangeException(nameof(row));
}
return new ListTuple<double>(vals);
}
public ListTuple<double> GetColumn(int column)
{
double[] vals;
switch (column)
{
case 0: vals = new double[] { r0c0, r1c0 }; break;
case 1: vals = new double[] { r0c1, r1c1 }; break;
default: throw new ArgumentOutOfRangeException(nameof(column));
}
return new ListTuple<double>(vals);
}
public void SetRow(int row, IEnumerable<double> vals) => MatrixHelper.SetRow(this, row, vals);
public void SetColumn(int column, IEnumerable<double> vals) => MatrixHelper.SetColumn(this, column, vals);
public void SetRow(int row, ListTuple<double> vals)
{
switch (row)
{
case 0: r0c0 = vals[0]; r0c1 = vals[1]; break;
case 1: r1c0 = vals[0]; r1c1 = vals[1]; break;
default: throw new ArgumentOutOfRangeException(nameof(row));
}
}
public void SetColumn(int column, ListTuple<double> vals)
{
switch (column)
{
case 0: r0c0 = vals[0]; r1c0 = vals[1]; break;
case 1: r0c1 = vals[0]; r1c1 = vals[1]; break;
default: throw new ArgumentOutOfRangeException(nameof(column));
}
}
public double Determinant() => r0c0 * r1c1 - r0c1 * r1c0;
public Matrix2x2 Adjoint() =>
new Matrix2x2( r1c1, -r0c1,
-r1c0, r0c0);
public Matrix2x2 Cofactor() =>
new Matrix2x2( r1c1, -r1c0,
-r0c1, r0c0);
public Matrix2x2 Inverse()
{
double invDet = 1 / Determinant();
return new Matrix2x2( r1c1 * invDet, -r0c1 * invDet,
-r1c0 * invDet, r0c0 * invDet);
}
public Matrix2x2 Transpose() =>
new Matrix2x2(r0c0, r1c0,
r0c1, r1c1);
public double Trace() => r0c0 + r1c1;
public IEnumerator<double> GetEnumerator()
{
yield return r0c0; yield return r0c1;
yield return r1c0; yield return r1c1;
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
#if CS8_OR_GREATER
public bool Equals(Matrix2x2? other) =>
#else
public bool Equals(Matrix2x2 other) =>
#endif
!(other is null) &&
r0c0 == other.r0c0 && r0c1 == other.r0c1 &&
r1c0 == other.r1c0 && r1c1 == other.r1c1;
#if CS8_OR_GREATER
public override bool Equals(object? other)
#else
public override bool Equals(object other)
#endif
{
if (other is null) return false;
else if (other is Matrix2x2 otherMat) return Equals(otherMat);
else return false;
}
public override int GetHashCode() =>
(int)((uint)r0c0.GetHashCode() & 0xFF000000 |
(uint)r0c1.GetHashCode() & 0x00FF0000 |
(uint)r1c0.GetHashCode() & 0x0000FF00 |
(uint)r1c1.GetHashCode() & 0x000000FF);
public override string ToString() => ToStringHelper.MatrixToString(this, null);
#if CS8_OR_GREATER
public string ToString(string? format) => ToStringHelper.MatrixToString(this, format);
public string ToString(string? format, IFormatProvider? provider) => ToStringHelper.MatrixToString(this, format);
#else
public string ToString(string format) => ToStringHelper.MatrixToString(this, format);
public string ToString(string format, IFormatProvider provider) => ToStringHelper.MatrixToString(this, format);
#endif
public static Matrix2x2 operator +(Matrix2x2 a) =>
new Matrix2x2(a.r0c0, a.r0c1,
a.r1c0, a.r1c1);
public static Matrix2x2 operator +(Matrix2x2 a, Matrix2x2 b) =>
new Matrix2x2(a.r0c0 + b.r0c0, a.r0c1 + b.r0c1,
a.r1c0 + b.r1c0, a.r1c1 + b.r1c1);
public static Matrix2x2 operator -(Matrix2x2 a) =>
new Matrix2x2(-a.r0c0, -a.r0c1,
-a.r1c0, -a.r1c1);
public static Matrix2x2 operator -(Matrix2x2 a, Matrix2x2 b) =>
new Matrix2x2(a.r0c0 - b.r0c0, a.r0c1 - b.r0c1,
a.r1c0 - b.r1c0, a.r1c1 - b.r1c1);
public static Matrix2x2 operator *(Matrix2x2 a, double b) =>
new Matrix2x2(a.r0c0 * b, a.r0c1 * b,
a.r1c0 * b, a.r1c1 * b);
public static Float2 operator *(Matrix2x2 a, Float2 b) =>
new Float2(a.r0c0 * b.x + a.r0c1 * b.y,
a.r1c0 * b.x + a.r1c1 * b.y);
public static Matrix2x2 operator *(Matrix2x2 a, Matrix2x2 b) =>
new Matrix2x2(a.r0c0 * b.r0c0 + a.r0c1 * b.r1c0, a.r0c0 * b.r0c1 + a.r0c1 * b.r1c1,
a.r1c0 * b.r0c0 + a.r1c1 * b.r1c0, a.r1c0 * b.r0c1 + a.r1c1 * b.r1c1);
public static Matrix2x2 operator /(Matrix2x2 a, double b) =>
new Matrix2x2(a.r0c0 / b, a.r0c1 / b,
a.r1c0 / b, a.r1c1 / b);
public static Matrix2x2 operator ^(Matrix2x2 a, Matrix2x2 b) =>
new Matrix2x2(a.r0c0 * b.r0c0, a.r0c1 * b.r0c1,
a.r1c0 * b.r1c0, a.r1c1 * b.r1c1);
public static Matrix2x2 operator ~(Matrix2x2 a) => a.Inverse();
public static bool operator ==(Matrix2x2 a, Matrix2x2 b) => a.Equals(b);
public static bool operator !=(Matrix2x2 a, Matrix2x2 b) => !a.Equals(b);
public static explicit operator Matrix2x2(Matrix mat) =>
new Matrix2x2(mat.TryGet(0, 0), mat.TryGet(0, 1),
mat.TryGet(1, 0), mat.TryGet(1, 1));
public static explicit operator Matrix2x2(Matrix3x3 mat) =>
new Matrix2x2(mat.r0c0, mat.r0c1,
mat.r1c0, mat.r1c1);
public static explicit operator Matrix2x2(Matrix4x4 mat) =>
new Matrix2x2(mat.r0c0, mat.r0c1,
mat.r1c0, mat.r1c1);
}
}