From 36d4411d70302bd162e1d9d5059064c7e4c4e8d3 Mon Sep 17 00:00:00 2001 From: That-One-Nerd Date: Thu, 14 Nov 2024 08:56:10 -0500 Subject: [PATCH] Prerequisites for matrices and 2x2 matrix done. --- Nerd_STF/Abstract/ICombinationIndexer.cs | 9 - Nerd_STF/Helpers/ToStringHelper.cs | 64 +++- Nerd_STF/ICombinationIndexer.cs | 7 + Nerd_STF/{Abstract => }/IFromTuple.cs | 2 +- Nerd_STF/{Abstract => }/ISplittable.cs | 2 +- Nerd_STF/ListTuple.cs | 134 +++++++ Nerd_STF/Mathematics/Algebra/IMatrix.cs | 36 ++ .../Mathematics/Algebra/IMatrixOperations.cs | 17 + Nerd_STF/Mathematics/Algebra/ISquareMatrix.cs | 8 + Nerd_STF/Mathematics/Algebra/IStaticMatrix.cs | 11 + .../Algebra/ISubmatrixOperations.cs | 9 + .../Algebra}/IVectorOperations.cs | 2 +- Nerd_STF/Mathematics/Algebra/Matrix2x2.cs | 351 ++++++++++++++++++ Nerd_STF/Mathematics/Algebra/RowColumn.cs | 8 + Nerd_STF/Mathematics/Angle.cs | 142 +++---- Nerd_STF/Mathematics/Equations/IEquation.cs | 1 + Nerd_STF/Mathematics/Float2.cs | 16 +- Nerd_STF/Mathematics/Float3.cs | 18 +- Nerd_STF/Mathematics/Float4.cs | 18 +- .../IInterpolable.cs | 2 +- .../{Abstract => Mathematics}/INumberGroup.cs | 7 +- .../{Abstract => Mathematics}/IPresets1d.cs | 2 +- .../{Abstract => Mathematics}/IPresets2d.cs | 2 +- .../{Abstract => Mathematics}/IPresets3d.cs | 2 +- .../{Abstract => Mathematics}/IPresets4d.cs | 2 +- Nerd_STF/Mathematics/IRefRoundable.cs | 12 + .../{Abstract => Mathematics}/IRoundable.cs | 2 +- .../ISimpleMathOperations.cs | 5 +- Nerd_STF/Mathematics/Int2.cs | 16 +- Nerd_STF/Mathematics/Int3.cs | 18 +- Nerd_STF/Mathematics/Int4.cs | 20 +- Nerd_STF/Mathematics/Numbers/Fraction.cs | 6 +- Nerd_STF/Nerd_STF.csproj | 31 +- 33 files changed, 850 insertions(+), 132 deletions(-) delete mode 100644 Nerd_STF/Abstract/ICombinationIndexer.cs create mode 100644 Nerd_STF/ICombinationIndexer.cs rename Nerd_STF/{Abstract => }/IFromTuple.cs (92%) rename Nerd_STF/{Abstract => }/ISplittable.cs (92%) create mode 100644 Nerd_STF/ListTuple.cs create mode 100644 Nerd_STF/Mathematics/Algebra/IMatrix.cs create mode 100644 Nerd_STF/Mathematics/Algebra/IMatrixOperations.cs create mode 100644 Nerd_STF/Mathematics/Algebra/ISquareMatrix.cs create mode 100644 Nerd_STF/Mathematics/Algebra/IStaticMatrix.cs create mode 100644 Nerd_STF/Mathematics/Algebra/ISubmatrixOperations.cs rename Nerd_STF/{Abstract => Mathematics/Algebra}/IVectorOperations.cs (93%) create mode 100644 Nerd_STF/Mathematics/Algebra/Matrix2x2.cs create mode 100644 Nerd_STF/Mathematics/Algebra/RowColumn.cs rename Nerd_STF/{Abstract => Mathematics}/IInterpolable.cs (87%) rename Nerd_STF/{Abstract => Mathematics}/INumberGroup.cs (81%) rename Nerd_STF/{Abstract => Mathematics}/IPresets1d.cs (86%) rename Nerd_STF/{Abstract => Mathematics}/IPresets2d.cs (91%) rename Nerd_STF/{Abstract => Mathematics}/IPresets3d.cs (88%) rename Nerd_STF/{Abstract => Mathematics}/IPresets4d.cs (88%) create mode 100644 Nerd_STF/Mathematics/IRefRoundable.cs rename Nerd_STF/{Abstract => Mathematics}/IRoundable.cs (92%) rename Nerd_STF/{Abstract => Mathematics}/ISimpleMathOperations.cs (82%) diff --git a/Nerd_STF/Abstract/ICombinationIndexer.cs b/Nerd_STF/Abstract/ICombinationIndexer.cs deleted file mode 100644 index 39e5cfe..0000000 --- a/Nerd_STF/Abstract/ICombinationIndexer.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Collections.Generic; - -namespace Nerd_STF.Abstract -{ - public interface ICombinationIndexer - { - IEnumerable this[string key] { get; set; } - } -} diff --git a/Nerd_STF/Helpers/ToStringHelper.cs b/Nerd_STF/Helpers/ToStringHelper.cs index b25c6f3..fd47c02 100644 --- a/Nerd_STF/Helpers/ToStringHelper.cs +++ b/Nerd_STF/Helpers/ToStringHelper.cs @@ -1,4 +1,7 @@ -using System.Text; +using Nerd_STF.Mathematics; +using Nerd_STF.Mathematics.Algebra; +using System.Collections.Generic; +using System.Text; namespace Nerd_STF.Helpers { @@ -48,5 +51,64 @@ namespace Nerd_STF.Helpers } return builder.Remove(builder.Length - 1, 1).ToString(); } + +#if CS8_OR_GREATER + public static string MatrixToString(T matrix, string? format) +#else + public static string MatrixToString(T matrix, string format) +#endif + where T : IMatrix + { + // First convert all items to their string counterparts, + // then measure the lengths and do spacing accordingly. + Int2 size = matrix.Size; + string[,] items = new string[size.x, size.y]; + for (int x = 0; x < size.x; x++) for (int y = 0; y < size.y; y++) + items[x, y] = matrix[y, x].ToString(format); + + // Then write each line separately. + StringBuilder[] lines = new StringBuilder[size.y + 2]; + for (int i = 0; i < lines.Length; i++) + { + StringBuilder builder = new StringBuilder(); + if (i == 0) builder.Append('┌'); + else if (i == lines.Length - 1) builder.Append('└'); + else builder.Append('│'); + + lines[i] = builder; + } + int totalLen = 0; + for (int x = 0; x < size.x; x++) + { + int maxLen = 0; + for (int y = 0; y < size.y; y++) + { + string item = items[x, y]; + if (item.Length > maxLen) maxLen = item.Length; + } + totalLen += maxLen + 1; + for (int y = 0; y < size.y; y++) + { + StringBuilder builder = lines[y + 1]; + string item = items[x, y]; + int spacing = maxLen - item.Length; + builder.Append(new string(' ', spacing + 1)); + builder.Append(item); + } + } + + // Finish up and merge. + StringBuilder total = new StringBuilder(); + for (int i = 0; i < lines.Length; i++) + { + StringBuilder builder = lines[i]; + if (i == 0) builder.Append(new string(' ', totalLen)).Append(" ┐"); + else if (i == lines.Length - 1) builder.Append(new string(' ', totalLen)).Append(" ┘"); + else builder.Append(" │"); + total.Append(builder); + if (i != lines.Length - 1) total.AppendLine(); + } + return total.ToString(); + } } } diff --git a/Nerd_STF/ICombinationIndexer.cs b/Nerd_STF/ICombinationIndexer.cs new file mode 100644 index 0000000..1c3d635 --- /dev/null +++ b/Nerd_STF/ICombinationIndexer.cs @@ -0,0 +1,7 @@ +namespace Nerd_STF +{ + public interface ICombinationIndexer + { + ListTuple this[string key] { get; set; } + } +} diff --git a/Nerd_STF/Abstract/IFromTuple.cs b/Nerd_STF/IFromTuple.cs similarity index 92% rename from Nerd_STF/Abstract/IFromTuple.cs rename to Nerd_STF/IFromTuple.cs index 68803a1..2566ccd 100644 --- a/Nerd_STF/Abstract/IFromTuple.cs +++ b/Nerd_STF/IFromTuple.cs @@ -1,7 +1,7 @@ #if CS11_OR_GREATER using System.Runtime.CompilerServices; -namespace Nerd_STF.Abstract +namespace Nerd_STF { public interface IFromTuple where TSelf : IFromTuple diff --git a/Nerd_STF/Abstract/ISplittable.cs b/Nerd_STF/ISplittable.cs similarity index 92% rename from Nerd_STF/Abstract/ISplittable.cs rename to Nerd_STF/ISplittable.cs index 4ea4aaf..3c4457b 100644 --- a/Nerd_STF/Abstract/ISplittable.cs +++ b/Nerd_STF/ISplittable.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; -namespace Nerd_STF.Abstract +namespace Nerd_STF { public interface ISplittable where TSelf : ISplittable diff --git a/Nerd_STF/ListTuple.cs b/Nerd_STF/ListTuple.cs new file mode 100644 index 0000000..22d8c73 --- /dev/null +++ b/Nerd_STF/ListTuple.cs @@ -0,0 +1,134 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; + +namespace Nerd_STF +{ + public readonly struct ListTuple : IEnumerable, + IEquatable> +#if NET471_OR_GREATER || NETCOREAPP2_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER + ,ITuple +#endif + { + public int Length => items.Length; + + private readonly T[] items; + + public ListTuple(IEnumerable items) + { + this.items = items.ToArray(); + } + public ListTuple(params T[] items) + { + this.items = items; + } + + public T this[int index] + { + get => items[index]; + set => items[index] = value; + } +#if NET471_OR_GREATER || NETCOREAPP2_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER +#if CS8_OR_GREATER + object? ITuple.this[int index] => this[index]; +#else + object ITuple.this[int index] => this[index]; +#endif +#endif + + public Enumerator GetEnumerator() => new Enumerator(this); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public bool Equals(ListTuple other) + { + if (Length != other.Length) return false; + for (int i = 0; i < Length; i++) + { + T itemA = items[i], itemB = other.items[i]; + if (itemA == null || itemB == null) + { + if (itemA == null && itemB == null) continue; + else return false; + } + if (!itemA.Equals(itemB)) return false; + } + return true; + } +#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 ListTuple otherTuple) return Equals(otherTuple); + else return false; + } + public override int GetHashCode() => items.GetHashCode(); + public override string ToString() + { + StringBuilder builder = new StringBuilder("("); + for (int i = 0; i < items.Length; i++) + { + builder.Append(items[i]); + if (i != items.Length - 1) builder.Append(", "); + } + builder.Append(')'); + return builder.ToString(); + } + + public static bool operator ==(ListTuple a, ListTuple b) => a.Equals(b); + public static bool operator !=(ListTuple a, ListTuple b) => !a.Equals(b); + + public static implicit operator ValueTuple(ListTuple tuple) => new ValueTuple(tuple[0]); + public static implicit operator ValueTuple(ListTuple tuple) => (tuple[0], tuple[1]); + public static implicit operator ValueTuple(ListTuple tuple) => (tuple[0], tuple[1], tuple[2]); + public static implicit operator ValueTuple(ListTuple tuple) => (tuple[0], tuple[1], tuple[2], tuple[3]); + public static implicit operator ValueTuple(ListTuple tuple) => (tuple[0], tuple[1], tuple[2], tuple[3], tuple[4]); + public static implicit operator ValueTuple(ListTuple tuple) => (tuple[0], tuple[1], tuple[2], tuple[3], tuple[4], tuple[5]); + public static implicit operator ValueTuple(ListTuple tuple) => (tuple[0], tuple[1], tuple[2], tuple[3], tuple[4], tuple[5], tuple[6]); + public static implicit operator T[](ListTuple tuple) => tuple.items; + + public static implicit operator ListTuple(ValueTuple tuple) => new ListTuple(tuple.Item1); + public static implicit operator ListTuple((T, T) tuple) => new ListTuple(tuple.Item1, tuple.Item2); + public static implicit operator ListTuple((T, T, T) tuple) => new ListTuple(tuple.Item1, tuple.Item2, tuple.Item3); + public static implicit operator ListTuple((T, T, T, T) tuple) => new ListTuple(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4); + public static implicit operator ListTuple((T, T, T, T, T) tuple) => new ListTuple(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4, tuple.Item5); + public static implicit operator ListTuple((T, T, T, T, T, T) tuple) => new ListTuple(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4, tuple.Item5, tuple.Item6); + public static implicit operator ListTuple((T, T, T, T, T, T, T) tuple) => new ListTuple(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4, tuple.Item5, tuple.Item6, tuple.Item7); + public static implicit operator ListTuple(T[] array) => new ListTuple(array); + + public struct Enumerator : IEnumerator + { + private int index; + private readonly ListTuple tuple; + + public T Current => tuple.items[index]; +#if CS8_OR_GREATER + object? IEnumerator.Current => Current; +#else + object IEnumerator.Current => Current; +#endif + public bool MoveNext() + { + index++; + return index < tuple.items.Length; + } + public void Reset() + { + index = -1; + } + public void Dispose() { } + + internal Enumerator(ListTuple tuple) + { + index = -1; + this.tuple = tuple; + } + } + } +} diff --git a/Nerd_STF/Mathematics/Algebra/IMatrix.cs b/Nerd_STF/Mathematics/Algebra/IMatrix.cs new file mode 100644 index 0000000..ba1d2c1 --- /dev/null +++ b/Nerd_STF/Mathematics/Algebra/IMatrix.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; + +namespace Nerd_STF.Mathematics.Algebra +{ + public interface IMatrix : IEnumerable, + IEquatable, + IFormattable +#if CS11_OR_GREATER + ,IMatrixOperations +#endif + where TSelf : IMatrix + { + Int2 Size { get; } + + double this[int r, int c] { get; set; } + double this[Int2 index] { get; set; } + ListTuple this[int index, RowColumn direction] { get; set; } + + ListTuple GetRow(int row); + ListTuple GetColumn(int column); + void SetRow(int row, IEnumerable vals); + void SetColumn(int column, IEnumerable vals); + + double Determinant(); + + TSelf Adjoint(); + TSelf Cofactor(); + TSelf Transpose(); +#if CS9_OR_GREATER + TSelf? Inverse(); +#else + TSelf Inverse(); +#endif + } +} diff --git a/Nerd_STF/Mathematics/Algebra/IMatrixOperations.cs b/Nerd_STF/Mathematics/Algebra/IMatrixOperations.cs new file mode 100644 index 0000000..a818875 --- /dev/null +++ b/Nerd_STF/Mathematics/Algebra/IMatrixOperations.cs @@ -0,0 +1,17 @@ +#if CS11_OR_GREATER +using System.Collections.Generic; + +namespace Nerd_STF.Mathematics.Algebra +{ + public interface IMatrixOperations : ISimpleMathOperations + where TSelf : IMatrix, IMatrixOperations + { + static abstract TSelf Average(IEnumerable vals); + static abstract TSelf Lerp(TSelf a, TSelf b, double t, bool clamp = true); + + static abstract TSelf operator *(TSelf a, double b); + static abstract TSelf operator /(TSelf a, double b); + static abstract TSelf? operator ~(TSelf m); + } +} +#endif diff --git a/Nerd_STF/Mathematics/Algebra/ISquareMatrix.cs b/Nerd_STF/Mathematics/Algebra/ISquareMatrix.cs new file mode 100644 index 0000000..0793e6b --- /dev/null +++ b/Nerd_STF/Mathematics/Algebra/ISquareMatrix.cs @@ -0,0 +1,8 @@ +namespace Nerd_STF.Mathematics.Algebra +{ + public interface ISquareMatrix : IMatrix + where TSelf : ISquareMatrix + { + double Trace(); + } +} diff --git a/Nerd_STF/Mathematics/Algebra/IStaticMatrix.cs b/Nerd_STF/Mathematics/Algebra/IStaticMatrix.cs new file mode 100644 index 0000000..3843932 --- /dev/null +++ b/Nerd_STF/Mathematics/Algebra/IStaticMatrix.cs @@ -0,0 +1,11 @@ +#if CS11_OR_GREATER +namespace Nerd_STF.Mathematics.Algebra +{ + public interface IStaticMatrix : IPresets1d, IMatrix + where TSelf : IStaticMatrix + { + static abstract TSelf Identity { get; } + static abstract TSelf SignField { get; } + } +} +#endif diff --git a/Nerd_STF/Mathematics/Algebra/ISubmatrixOperations.cs b/Nerd_STF/Mathematics/Algebra/ISubmatrixOperations.cs new file mode 100644 index 0000000..ac832b1 --- /dev/null +++ b/Nerd_STF/Mathematics/Algebra/ISubmatrixOperations.cs @@ -0,0 +1,9 @@ +namespace Nerd_STF.Mathematics.Algebra +{ + public interface ISubmatrixOperations + where TSelf : IMatrix, ISubmatrixOperations + where TSmaller : IMatrix + { + TSmaller Submatrix(int r, int c); + } +} diff --git a/Nerd_STF/Abstract/IVectorOperations.cs b/Nerd_STF/Mathematics/Algebra/IVectorOperations.cs similarity index 93% rename from Nerd_STF/Abstract/IVectorOperations.cs rename to Nerd_STF/Mathematics/Algebra/IVectorOperations.cs index 2a64f76..33c1803 100644 --- a/Nerd_STF/Abstract/IVectorOperations.cs +++ b/Nerd_STF/Mathematics/Algebra/IVectorOperations.cs @@ -1,7 +1,7 @@ #if CS11_OR_GREATER using System.Collections.Generic; -namespace Nerd_STF.Abstract +namespace Nerd_STF.Mathematics.Algebra { public interface IVectorOperations : ISimpleMathOperations where TSelf : IVectorOperations diff --git a/Nerd_STF/Mathematics/Algebra/Matrix2x2.cs b/Nerd_STF/Mathematics/Algebra/Matrix2x2.cs new file mode 100644 index 0000000..9816692 --- /dev/null +++ b/Nerd_STF/Mathematics/Algebra/Matrix2x2.cs @@ -0,0 +1,351 @@ +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, + ISquareMatrix +#if CS11_OR_GREATER + ,ISplittable, + IStaticMatrix +#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 r1c1, r1c2, + r2c1, r2c2; + + public Matrix2x2() + { + r1c1 = 0; + r1c2 = 0; + r2c1 = 0; + r2c2 = 0; + } + public Matrix2x2(Matrix2x2 copy) + { + r1c1 = copy.r1c1; + r1c2 = copy.r1c2; + r2c1 = copy.r2c1; + r2c2 = copy.r2c2; + } + public Matrix2x2(double r1c1, double r1c2, double r2c1, double r2c2) + { + this.r1c1 = r1c1; + this.r1c2 = r1c2; + this.r2c1 = r2c1; + this.r2c2 = r2c2; + } + /// if the array is of the form [c, r], if the array is of the form [r, c]. + public Matrix2x2(double[,] vals, bool byRows = true) + { + if (byRows) // Collection of rows ([c, r]) + { + r1c1 = vals[0, 0]; r1c2 = vals[0, 1]; + r2c1 = vals[1, 0]; r2c2 = vals[1, 1]; + } + else // Collection of columns ([r, c]) + { + r1c1 = vals[0, 0]; r1c2 = vals[1, 0]; + r2c1 = vals[0, 1]; r2c2 = vals[1, 1]; + } + } + /// if the enumerable is a collection of rows (form [c, r]), if the enumerable is a collection of columns (form [r, c]). + public Matrix2x2(IEnumerable> vals, bool byRows = true) + { + int x = 0; + foreach (IEnumerable part in vals) + { + int y = 0; + foreach (double v in part) + { + this[byRows ? (x, y) : (y, x)] = v; + y++; + if (y >= 2) break; + } + x++; + if (x >= 2) break; + } + } + /// if the enumerable is a collection of rows (form [c, r]), if the enumerable is a collection of columns (form [r, c]). + public Matrix2x2(IEnumerable> vals, bool byRows = true) + { + int x = 0; + foreach (IEnumerable part in vals) + { + int y = 0; + foreach (double v in part) + { + this[byRows ? (x, y) : (y, x)] = v; + y++; + if (y >= 2) break; + } + x++; + if (x >= 2) break; + } + } + + public double this[int r, int c] + { + get => this[(r, c)]; + set => this[(r, c)] = value; + } + public double this[Int2 index] + { + get + { + switch (index.x) // (r, c) + { + case 0: + switch (index.y) + { + case 0: return r1c1; + case 1: return r1c2; + default: throw new ArgumentOutOfRangeException(nameof(index)); + } + case 1: + switch (index.y) + { + case 0: return r2c1; + case 1: return r2c2; + default: throw new ArgumentOutOfRangeException(nameof(index)); + } + default: throw new ArgumentOutOfRangeException(nameof(index)); + } + } + set + { + switch (index.x) // (r, c) + { + case 0: + switch (index.y) + { + case 0: r1c1 = value; return; + case 1: r1c2 = value; return; + default: throw new ArgumentOutOfRangeException(nameof(index)); + } + case 1: + switch (index.y) + { + case 0: r2c1 = value; return; + case 1: r2c2 = value; return; + default: throw new ArgumentOutOfRangeException(nameof(index)); + } + default: throw new ArgumentOutOfRangeException(nameof(index)); + } + } + } + public ListTuple 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 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.r1c1, b.r1c1, t, clamp), MathE.Lerp(a.r1c2, b.r1c2, t, clamp), + MathE.Lerp(a.r2c1, b.r2c1, t, clamp), MathE.Lerp(a.r2c2, b.r2c2, t, clamp)); + public static Matrix2x2 Product(IEnumerable 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 vals) + { + Matrix2x2 result = Zero; + foreach (Matrix2x2 m in vals) result += m; + return result; + } + + public static (double[] r1c1, double[] r1c2, double[] r2c1, double[] r2c2) SplitArray(IEnumerable vals) + { + int count = vals.Count(); + double[] r1c1 = new double[count], r1c2 = new double[count], + r2c1 = new double[count], r2c2 = new double[count]; + int index = 0; + foreach (Matrix2x2 m in vals) + { + r1c1[index] = m.r1c1; + r1c2[index] = m.r1c2; + r2c1[index] = m.r2c1; + r2c2[index] = m.r2c2; + } + return (r1c1, r1c2, r2c1, r2c2); + } + + public ListTuple GetRow(int row) + { + double[] vals; + switch (row) + { + case 0: vals = new double[] { r1c1, r1c2 }; break; + case 1: vals = new double[] { r2c1, r2c2 }; break; + default: throw new ArgumentOutOfRangeException(nameof(row)); + } + return new ListTuple(vals); + } + public ListTuple GetColumn(int column) + { + double[] vals; + switch (column) + { + case 0: vals = new double[] { r1c1, r2c1 }; break; + case 1: vals = new double[] { r1c2, r2c2 }; break; + default: throw new ArgumentOutOfRangeException(nameof(column)); + } + return new ListTuple(vals); + } + public void SetRow(int row, IEnumerable vals) + { + int col = 0; + foreach (double v in vals) + { + this[(row, col)] = v; + col++; + if (col >= 2) return; + } + } + public void SetColumn(int column, IEnumerable vals) + { + int row = 0; + foreach (double v in vals) + { + this[(row, column)] = v; + row++; + if (row >= 2) return; + } + } + public void SetRow(int row, ListTuple vals) => SetRow(row, (IEnumerable)vals); + public void SetColumn(int row, ListTuple vals) => SetColumn(row, (IEnumerable)vals); + + public double Determinant() => r1c1 * r2c2 - r1c2 * r2c1; + + public Matrix2x2 Adjoint() => + new Matrix2x2( r2c2, -r1c2, + -r2c1, r1c1); + public Matrix2x2 Cofactor() => + new Matrix2x2( r2c2, -r2c1, + -r1c2, r1c1); + public Matrix2x2 Inverse() + { + double invDet = 1 / Determinant(); + return new Matrix2x2( r2c2 * invDet, -r1c2 * invDet, + -r2c1 * invDet, r1c1 * invDet); + } + public Matrix2x2 Transpose() => + new Matrix2x2(r1c1, r2c1, + r1c2, r2c2); + public double Trace() => r1c1 + r2c2; + + public IEnumerator GetEnumerator() + { + yield return r1c1; + yield return r1c2; + yield return r2c1; + yield return r2c2; + } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + +#if CS8_OR_GREATER + public bool Equals(Matrix2x2? other) => +#else + public bool Equals(Matrix2x2 other) => +#endif + !(other is null) && + r1c1 == other.r1c1 && r1c2 == other.r1c2 && + r2c1 == other.r2c1 && r2c2 == other.r2c2; +#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)r1c1.GetHashCode() & 0xFF000000 | + (uint)r1c2.GetHashCode() & 0x00FF0000 | + (uint)r2c1.GetHashCode() & 0x0000FF00 | + (uint)r2c2.GetHashCode() & 0x000000FF); + public override string ToString() => ToStringHelper.MatrixToString(this, null); +#if CS8_OR_GREATER + public string ToString(string? format) => ToStringHelper.MatrixToString(this, format); +#else + public string ToString(string format) => ToStringHelper.MatrixToString(this, format); +#endif +#if CS8_OR_GREATER + public string ToString(string? format, IFormatProvider? provider) => ToStringHelper.MatrixToString(this, format); +#else + public string ToString(string format, IFormatProvider provider) => ToStringHelper.MatrixToString(this, format); +#endif + + public static Matrix2x2 operator +(Matrix2x2 a) => + new Matrix2x2(a.r1c1, a.r1c2, + a.r2c1, a.r2c2); + public static Matrix2x2 operator +(Matrix2x2 a, Matrix2x2 b) => + new Matrix2x2(a.r1c1 + b.r1c1, a.r1c2 + b.r1c2, + a.r2c1 + b.r2c1, a.r2c2 + b.r2c2); + public static Matrix2x2 operator -(Matrix2x2 a) => + new Matrix2x2(-a.r1c1, -a.r1c2, + -a.r2c1, -a.r2c2); + public static Matrix2x2 operator -(Matrix2x2 a, Matrix2x2 b) => + new Matrix2x2(a.r1c1 - b.r1c1, a.r1c2 - b.r1c2, + a.r2c1 - b.r2c1, a.r2c2 - b.r2c2); + public static Matrix2x2 operator *(Matrix2x2 a, Matrix2x2 b) => + new Matrix2x2(a.r1c1 * b.r1c1 + a.r1c2 * b.r2c1, a.r1c1 * b.r1c2 + a.r1c2 * b.r2c2, + a.r2c1 * b.r1c1 + a.r2c2 * b.r2c1, a.r2c1 * b.r1c2 + a.r2c2 * b.r2c2); + public static Matrix2x2 operator *(Matrix2x2 a, double b) => + new Matrix2x2(a.r1c1 * b, a.r1c2 * b, + a.r2c1 * b, a.r2c2 * b); + public static Float2 operator *(Matrix2x2 a, Float2 b) => + new Float2(a.r1c1 * b.x + a.r1c2 * b.y, + a.r2c1 * b.x + a.r2c2 * b.y); + public static Matrix2x2 operator /(Matrix2x2 a, double b) => + new Matrix2x2(a.r1c1 / b, a.r1c2 / b, + a.r2c1 / b, a.r2c2 / b); + public static Matrix2x2 operator ~(Matrix2x2 a) => a.Inverse(); + } +} diff --git a/Nerd_STF/Mathematics/Algebra/RowColumn.cs b/Nerd_STF/Mathematics/Algebra/RowColumn.cs new file mode 100644 index 0000000..526981d --- /dev/null +++ b/Nerd_STF/Mathematics/Algebra/RowColumn.cs @@ -0,0 +1,8 @@ +namespace Nerd_STF.Mathematics.Algebra +{ + public enum RowColumn + { + Row, + Column + } +} diff --git a/Nerd_STF/Mathematics/Angle.cs b/Nerd_STF/Mathematics/Angle.cs index a790620..1ebd1d7 100644 --- a/Nerd_STF/Mathematics/Angle.cs +++ b/Nerd_STF/Mathematics/Angle.cs @@ -1,15 +1,15 @@ -using Nerd_STF.Abstract; -using System; +using System; using System.Collections.Generic; using System.Linq; namespace Nerd_STF.Mathematics { - public struct Angle : IComparable, - IEquatable + // Maybe move to .Numbers and add inheritance to INumber? Does this make sense? + public readonly struct Angle : IComparable, + IEquatable #if CS11_OR_GREATER - ,IPresets2d, - IFromTuple + ,IFromTuple, + IPresets2d #endif { public static Angle Down => new Angle(0.75); @@ -23,45 +23,29 @@ namespace Nerd_STF.Mathematics public static Angle Zero => new Angle(0); #if CS11_OR_GREATER - static Angle IPresets1d.One => new Angle(1, Unit.Degrees); + static Angle IPresets1d.One => new Angle(1, Units.Degrees); #endif - public double Degrees - { - get => revTheta * 360; - set => revTheta = value / 360; - } - public double Gradians - { - get => revTheta * 400; - set => revTheta = value / 400; - } - public double Radians - { - get => revTheta * Constants.Tau; - set => revTheta = value / Constants.Tau; - } - public double Revolutions - { - get => revTheta; - set => revTheta = value; - } + public double Degrees => revTheta * 360; + public double Gradians => revTheta * 400; + public double Radians => revTheta * Constants.Tau; + public double Revolutions => revTheta; public Angle Complimentary => new Angle(0.25 - MathE.ModAbs(revTheta, 1)); public Angle Supplimentary => new Angle(0.5 - MathE.ModAbs(revTheta, 1)); public Angle Normalized => new Angle(MathE.ModAbs(revTheta, 1)); public Angle Reflected => new Angle(MathE.ModAbs(-revTheta, 1)); - private double revTheta; + private readonly double revTheta; - public Angle(double theta, Unit unit) + public Angle(double theta, Units unit) { switch (unit) { - case Unit.Revolutions: revTheta = theta; break; - case Unit.Degrees: revTheta = theta / 360; break; - case Unit.Radians: revTheta = theta / Constants.Tau; break; - case Unit.Gradians: revTheta = theta / 400; break; + case Units.Revolutions: revTheta = theta; break; + case Units.Degrees: revTheta = theta / 360; break; + case Units.Radians: revTheta = theta / Constants.Tau; break; + case Units.Gradians: revTheta = theta / 400; break; default: throw new ArgumentException($"Unknown angle unit \"{unit}.\"", nameof(unit)); } } @@ -70,30 +54,64 @@ namespace Nerd_STF.Mathematics this.revTheta = revTheta; } - public double this[Unit unit] + public double this[Units unit] { get { switch (unit) { - case Unit.Revolutions: return revTheta; - case Unit.Degrees: return revTheta * 360; - case Unit.Radians: return revTheta * Constants.Tau; - case Unit.Gradians: return revTheta * 400; + case Units.Revolutions: return revTheta; + case Units.Degrees: return revTheta * 360; + case Units.Radians: return revTheta * Constants.Tau; + case Units.Gradians: return revTheta * 400; default: throw new ArgumentException($"Unknown angle unit \"{unit}.\"", nameof(unit)); } } - set + } + + public static double Convert(double value, Units from, Units to) + { + switch (from) { - switch (unit) - { - case Unit.Revolutions: revTheta = value; break; - case Unit.Degrees: revTheta = value / 360; break; - case Unit.Radians: revTheta = value / Constants.Tau; break; - case Unit.Gradians: revTheta = value / 400; break; - default: throw new ArgumentException($"Unknown angle unit \"{unit}.\"", nameof(unit)); - } + case Units.Revolutions: + switch (to) + { + case Units.Revolutions: return value; + case Units.Degrees: return value * 360; + case Units.Radians: return value * 6.28318530718; + case Units.Gradians: return value * 400; + default: goto _fail; + } + case Units.Degrees: + switch (to) + { + case Units.Revolutions: return value * 0.00277777777778; + case Units.Degrees: return value; + case Units.Radians: return value * 0.0174532925199; + case Units.Gradians: return value * 1.11111111111; + default: goto _fail; + } + case Units.Radians: + switch (to) + { + case Units.Revolutions: return value * 0.159154943092; + case Units.Degrees: return value * 57.2957795131; + case Units.Radians: return value; + case Units.Gradians: return value * 63.6619772368; + default: goto _fail; + } + case Units.Gradians: + switch (to) + { + case Units.Revolutions: return value * 0.0025; + case Units.Degrees: return value * 0.9; + case Units.Radians: return value * 0.0157079632679; + case Units.Gradians: return value; + default: goto _fail; + } + default: goto _fail; } + _fail: throw new ArgumentException($"Invalid conversion: {from} -> {to}."); } public static Angle Average(IEnumerable angles) @@ -109,8 +127,6 @@ namespace Nerd_STF.Mathematics } public static Angle Clamp(Angle value, Angle min, Angle max) => new Angle(MathE.Clamp(value.revTheta, min.revTheta, max.revTheta)); - public static void Clamp(ref Angle value, Angle min, Angle max) => - MathE.Clamp(ref value.revTheta, min.revTheta, max.revTheta); public static Angle Max(IEnumerable values) => Max(false, values); public static Angle Max(bool normalize, IEnumerable values) { @@ -172,7 +188,7 @@ namespace Nerd_STF.Mathematics return sum; } - public static double[] SplitArray(Unit unit, IEnumerable values) + public static double[] SplitArray(Units unit, IEnumerable values) { int count = values.Count(); double[] angles = new double[count]; @@ -185,6 +201,8 @@ namespace Nerd_STF.Mathematics return angles; } + public Angle Coterminal(int turns) => new Angle(revTheta + turns); + public int CompareTo(Angle other) => revTheta.CompareTo(other.revTheta); public bool Equals(Angle other) => revTheta == other.revTheta; #if CS8_OR_GREATER @@ -198,26 +216,26 @@ namespace Nerd_STF.Mathematics else return false; } public override int GetHashCode() => revTheta.GetHashCode(); - public override string ToString() => ToString(Unit.Degrees, null); - public string ToString(Unit unit) => ToString(unit, null); + public override string ToString() => ToString(Units.Degrees, null); + public string ToString(Units unit) => ToString(unit, null); #if CS8_OR_GREATER public string ToString(string? format) => #else public string ToString(string format) => #endif - ToString(Unit.Degrees, format); + ToString(Units.Degrees, format); #if CS8_OR_GREATER - public string ToString(Unit unit, string? format) + public string ToString(Units unit, string? format) #else - public string ToString(Unit unit, string format) + public string ToString(Units unit, string format) #endif { switch (unit) { - case Unit.Revolutions: return $"{revTheta.ToString(format)} rev"; - case Unit.Degrees: return $"{(revTheta * 360).ToString(format)} deg"; - case Unit.Radians: return $"{(revTheta * Constants.Tau).ToString(format)} rad"; - case Unit.Gradians: return $"{(revTheta * 400).ToString(format)} grad"; + case Units.Revolutions: return $"{revTheta.ToString(format)} rev"; + case Units.Degrees: return $"{(revTheta * 360).ToString(format)} deg"; + case Units.Radians: return $"{(revTheta * Constants.Tau).ToString(format)} rad"; + case Units.Gradians: return $"{(revTheta * 400).ToString(format)} grad"; default: throw new ArgumentException($"Unknown angle unit \"{unit}.\"", nameof(unit)); } } @@ -234,12 +252,12 @@ namespace Nerd_STF.Mathematics public static bool operator >=(Angle a, Angle b) => a.CompareTo(b) >= 0; public static bool operator <=(Angle a, Angle b) => a.CompareTo(b) <= 0; - public static implicit operator Angle((double, Unit) tuple) => new Angle(tuple.Item1, tuple.Item2); + public static implicit operator Angle((double, Units) tuple) => new Angle(tuple.Item1, tuple.Item2); #if CS11_OR_GREATER - static implicit IFromTuple.operator ValueTuple(Angle angle) => (angle.revTheta, Unit.Revolutions); + static implicit IFromTuple.operator ValueTuple(Angle angle) => (angle.revTheta, Units.Revolutions); #endif - public enum Unit + public enum Units { Revolutions, Degrees, diff --git a/Nerd_STF/Mathematics/Equations/IEquation.cs b/Nerd_STF/Mathematics/Equations/IEquation.cs index 7e57145..749e9ce 100644 --- a/Nerd_STF/Mathematics/Equations/IEquation.cs +++ b/Nerd_STF/Mathematics/Equations/IEquation.cs @@ -1,4 +1,5 @@ using System; +using System.Numerics; namespace Nerd_STF.Mathematics.Equations { diff --git a/Nerd_STF/Mathematics/Float2.cs b/Nerd_STF/Mathematics/Float2.cs index c4a478f..d381fc0 100644 --- a/Nerd_STF/Mathematics/Float2.cs +++ b/Nerd_STF/Mathematics/Float2.cs @@ -1,5 +1,4 @@ -using Nerd_STF.Abstract; -using Nerd_STF.Exceptions; +using Nerd_STF.Exceptions; using System; using System.Collections; using System.Collections.Generic; @@ -13,6 +12,7 @@ namespace Nerd_STF.Mathematics ,IFromTuple, IPresets2d, IRoundable, + IRefRoundable, ISplittable #endif { @@ -70,20 +70,22 @@ namespace Nerd_STF.Mathematics } } } - public IEnumerable this[string key] + public ListTuple this[string key] { get { + double[] items = new double[key.Length]; for (int i = 0; i < key.Length; i++) { char c = key[i]; switch (c) { - case 'x': yield return x; break; - case 'y': yield return y; break; + case 'x': items[i] = x; break; + case 'y': items[i] = y; break; default: throw new ArgumentException("Invalid key.", nameof(key)); } } + return new ListTuple(items); } set { @@ -274,12 +276,16 @@ namespace Nerd_STF.Mathematics public static implicit operator Float2(PointF point) => new Float2(point.X, point.Y); public static implicit operator Float2(Size point) => new Float2(point.Width, point.Height); public static implicit operator Float2(SizeF size) => new Float2(size.Width, size.Height); + public static implicit operator Float2(ListTuple tuple) => new Float2(tuple[0], tuple[1]); + public static implicit operator Float2(ListTuple tuple) => new Float2(tuple[0], tuple[1]); public static implicit operator Float2((double, double) tuple) => new Float2(tuple.Item1, tuple.Item2); public static explicit operator Point(Float2 group) => new Point((int)group.x, (int)group.y); public static implicit operator PointF(Float2 group) => new PointF((float)group.x, (float)group.y); public static explicit operator Size(Float2 group) => new Size((int)group.x, (int)group.y); public static implicit operator SizeF(Float2 group) => new SizeF((float)group.x, (float)group.y); + public static implicit operator ListTuple(Float2 group) => new ListTuple(group.x, group.y); + public static explicit operator ListTuple(Float2 group) => new ListTuple((int)group.x, (int)group.y); public static implicit operator ValueTuple(Float2 group) => (group.x, group.y); } } diff --git a/Nerd_STF/Mathematics/Float3.cs b/Nerd_STF/Mathematics/Float3.cs index a40bc83..45037f5 100644 --- a/Nerd_STF/Mathematics/Float3.cs +++ b/Nerd_STF/Mathematics/Float3.cs @@ -1,5 +1,4 @@ -using Nerd_STF.Abstract; -using Nerd_STF.Exceptions; +using Nerd_STF.Exceptions; using System; using System.Collections; using System.Collections.Generic; @@ -12,6 +11,7 @@ namespace Nerd_STF.Mathematics ,IFromTuple, IPresets2d, IRoundable, + IRefRoundable, ISplittable #endif { @@ -75,21 +75,23 @@ namespace Nerd_STF.Mathematics } } } - public IEnumerable this[string key] + public ListTuple this[string key] { get { + double[] items = new double[key.Length]; for (int i = 0; i < key.Length; i++) { char c = key[i]; switch (c) { - case 'x': yield return x; break; - case 'y': yield return y; break; - case 'z': yield return z; break; + case 'x': items[i] = x; break; + case 'y': items[i] = y; break; + case 'z': items[i] = z; break; default: throw new ArgumentException("Invalid key.", nameof(key)); } } + return new ListTuple(items); } set { @@ -296,8 +298,12 @@ namespace Nerd_STF.Mathematics public static implicit operator Float3(Int2 ints) => new Float3(ints.x, ints.y, 0); public static implicit operator Float3(Int3 ints) => new Float3(ints.x, ints.y, ints.z); public static explicit operator Float3(Int4 ints) => new Float3(ints.x, ints.y, ints.z); + public static implicit operator Float3(ListTuple tuple) => new Float3(tuple[0], tuple[1], tuple[2]); + public static implicit operator Float3(ListTuple tuple) => new Float3(tuple[0], tuple[1], tuple[2]); public static implicit operator Float3((double, double, double) tuple) => new Float3(tuple.Item1, tuple.Item2, tuple.Item3); + public static implicit operator ListTuple(Float3 group) => new ListTuple(group.x, group.y, group.z); + public static explicit operator ListTuple(Float3 group) => new ListTuple((int)group.x, (int)group.y, (int)group.z); public static implicit operator ValueTuple(Float3 group) => (group.x, group.y, group.z); } } diff --git a/Nerd_STF/Mathematics/Float4.cs b/Nerd_STF/Mathematics/Float4.cs index 5dbb6af..4ef34f5 100644 --- a/Nerd_STF/Mathematics/Float4.cs +++ b/Nerd_STF/Mathematics/Float4.cs @@ -2,7 +2,6 @@ using System; using System.Collections; using System.Collections.Generic; using System.Linq; -using Nerd_STF.Abstract; using Nerd_STF.Exceptions; namespace Nerd_STF.Mathematics @@ -12,6 +11,7 @@ namespace Nerd_STF.Mathematics ,IFromTuple, IPresets4d, IRoundable, + IRefRoundable, ISplittable #endif { @@ -81,22 +81,24 @@ namespace Nerd_STF.Mathematics } } } - public IEnumerable this[string key] + public ListTuple this[string key] { get { + double[] items = new double[key.Length]; for (int i = 0; i < key.Length; i++) { char c = key[i]; switch (c) { - case 'w': yield return w; break; - case 'x': yield return x; break; - case 'y': yield return y; break; - case 'z': yield return z; break; + case 'w': items[i] = w; break; + case 'x': items[i] = x; break; + case 'y': items[i] = y; break; + case 'z': items[i] = z; break; default: throw new ArgumentException("Invalid key.", nameof(key)); } } + return new ListTuple(items); } set { @@ -316,8 +318,12 @@ namespace Nerd_STF.Mathematics public static implicit operator Float4(Int4 ints) => new Float4(ints.w, ints.x, ints.y, ints.z); public static implicit operator Float4(Float2 floats) => new Float4(0, floats.x, floats.y, 0); public static implicit operator Float4(Float3 floats) => new Float4(0, floats.x, floats.y, floats.z); + public static implicit operator Float4(ListTuple tuple) => new Float4(tuple[0], tuple[1], tuple[2], tuple[3]); + public static implicit operator Float4(ListTuple tuple) => new Float4(tuple[0], tuple[1], tuple[2], tuple[3]); public static implicit operator Float4((double, double, double, double) tuple) => new Float4(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4); + public static implicit operator ListTuple(Float4 group) => new ListTuple(group.w, group.x, group.y, group.z); + public static explicit operator ListTuple(Float4 group) => new ListTuple((int)group.w, (int)group.x, (int)group.y, (int)group.z); public static implicit operator ValueTuple(Float4 group) => (group.w, group.x, group.y, group.z); } } diff --git a/Nerd_STF/Abstract/IInterpolable.cs b/Nerd_STF/Mathematics/IInterpolable.cs similarity index 87% rename from Nerd_STF/Abstract/IInterpolable.cs rename to Nerd_STF/Mathematics/IInterpolable.cs index 7f22116..9c074c3 100644 --- a/Nerd_STF/Abstract/IInterpolable.cs +++ b/Nerd_STF/Mathematics/IInterpolable.cs @@ -1,5 +1,5 @@ #if CS11_OR_GREATER -namespace Nerd_STF.Abstract +namespace Nerd_STF.Mathematics { public interface IInterpolable where TSelf : IInterpolable diff --git a/Nerd_STF/Abstract/INumberGroup.cs b/Nerd_STF/Mathematics/INumberGroup.cs similarity index 81% rename from Nerd_STF/Abstract/INumberGroup.cs rename to Nerd_STF/Mathematics/INumberGroup.cs index 6137148..42ee6e1 100644 --- a/Nerd_STF/Abstract/INumberGroup.cs +++ b/Nerd_STF/Mathematics/INumberGroup.cs @@ -1,14 +1,15 @@ -using System; +using Nerd_STF.Mathematics.Algebra; +using System; using System.Collections.Generic; using System.Numerics; -namespace Nerd_STF.Abstract +namespace Nerd_STF.Mathematics { public interface INumberGroup : ICombinationIndexer, IEnumerable, IEquatable #if CS11_OR_GREATER - ,IInterpolable, + , IInterpolable, ISimpleMathOperations, IVectorOperations #endif diff --git a/Nerd_STF/Abstract/IPresets1d.cs b/Nerd_STF/Mathematics/IPresets1d.cs similarity index 86% rename from Nerd_STF/Abstract/IPresets1d.cs rename to Nerd_STF/Mathematics/IPresets1d.cs index 1fbe0c7..f7e469d 100644 --- a/Nerd_STF/Abstract/IPresets1d.cs +++ b/Nerd_STF/Mathematics/IPresets1d.cs @@ -1,5 +1,5 @@ #if CS11_OR_GREATER -namespace Nerd_STF.Abstract +namespace Nerd_STF.Mathematics { public interface IPresets1d where TSelf : IPresets1d { diff --git a/Nerd_STF/Abstract/IPresets2d.cs b/Nerd_STF/Mathematics/IPresets2d.cs similarity index 91% rename from Nerd_STF/Abstract/IPresets2d.cs rename to Nerd_STF/Mathematics/IPresets2d.cs index bca7c92..1303492 100644 --- a/Nerd_STF/Abstract/IPresets2d.cs +++ b/Nerd_STF/Mathematics/IPresets2d.cs @@ -1,5 +1,5 @@ #if CS11_OR_GREATER -namespace Nerd_STF.Abstract +namespace Nerd_STF.Mathematics { public interface IPresets2d : IPresets1d where TSelf : IPresets2d diff --git a/Nerd_STF/Abstract/IPresets3d.cs b/Nerd_STF/Mathematics/IPresets3d.cs similarity index 88% rename from Nerd_STF/Abstract/IPresets3d.cs rename to Nerd_STF/Mathematics/IPresets3d.cs index e3f4f76..c49a85b 100644 --- a/Nerd_STF/Abstract/IPresets3d.cs +++ b/Nerd_STF/Mathematics/IPresets3d.cs @@ -1,5 +1,5 @@ #if CS11_OR_GREATER -namespace Nerd_STF.Abstract +namespace Nerd_STF.Mathematics { public interface IPresets3d : IPresets2d where TSelf : IPresets3d diff --git a/Nerd_STF/Abstract/IPresets4d.cs b/Nerd_STF/Mathematics/IPresets4d.cs similarity index 88% rename from Nerd_STF/Abstract/IPresets4d.cs rename to Nerd_STF/Mathematics/IPresets4d.cs index 097072b..af82370 100644 --- a/Nerd_STF/Abstract/IPresets4d.cs +++ b/Nerd_STF/Mathematics/IPresets4d.cs @@ -1,5 +1,5 @@ #if CS11_OR_GREATER -namespace Nerd_STF.Abstract +namespace Nerd_STF.Mathematics { public interface IPresets4d : IPresets3d where TSelf : IPresets4d diff --git a/Nerd_STF/Mathematics/IRefRoundable.cs b/Nerd_STF/Mathematics/IRefRoundable.cs new file mode 100644 index 0000000..fc664bb --- /dev/null +++ b/Nerd_STF/Mathematics/IRefRoundable.cs @@ -0,0 +1,12 @@ +#if CS11_OR_GREATER +namespace Nerd_STF.Mathematics +{ + public interface IRefRoundable + where TSelf : IRefRoundable + { + static abstract void Ceiling(ref TSelf val); + static abstract void Floor(ref TSelf val); + static abstract void Round(ref TSelf val); + } +} +#endif \ No newline at end of file diff --git a/Nerd_STF/Abstract/IRoundable.cs b/Nerd_STF/Mathematics/IRoundable.cs similarity index 92% rename from Nerd_STF/Abstract/IRoundable.cs rename to Nerd_STF/Mathematics/IRoundable.cs index 11d31cc..d868e92 100644 --- a/Nerd_STF/Abstract/IRoundable.cs +++ b/Nerd_STF/Mathematics/IRoundable.cs @@ -1,5 +1,5 @@ #if CS11_OR_GREATER -namespace Nerd_STF.Abstract +namespace Nerd_STF.Mathematics { public interface IRoundable : IRoundable where TSelf : IRoundable { } diff --git a/Nerd_STF/Abstract/ISimpleMathOperations.cs b/Nerd_STF/Mathematics/ISimpleMathOperations.cs similarity index 82% rename from Nerd_STF/Abstract/ISimpleMathOperations.cs rename to Nerd_STF/Mathematics/ISimpleMathOperations.cs index 3c07e18..4734333 100644 --- a/Nerd_STF/Abstract/ISimpleMathOperations.cs +++ b/Nerd_STF/Mathematics/ISimpleMathOperations.cs @@ -2,12 +2,11 @@ using System.Collections.Generic; using System.Numerics; -namespace Nerd_STF.Abstract +namespace Nerd_STF.Mathematics { public interface ISimpleMathOperations : IAdditionOperators, ISubtractionOperators, - IMultiplyOperators, - IDivisionOperators + IMultiplyOperators where TSelf : ISimpleMathOperations { static abstract TSelf Product(IEnumerable vals); diff --git a/Nerd_STF/Mathematics/Int2.cs b/Nerd_STF/Mathematics/Int2.cs index 6430b96..e01f297 100644 --- a/Nerd_STF/Mathematics/Int2.cs +++ b/Nerd_STF/Mathematics/Int2.cs @@ -1,5 +1,5 @@ -using Nerd_STF.Abstract; -using Nerd_STF.Exceptions; +using Nerd_STF.Exceptions; +using Nerd_STF.Mathematics.Algebra; using System; using System.Collections; using System.Collections.Generic; @@ -69,20 +69,22 @@ namespace Nerd_STF.Mathematics } } } - public IEnumerable this[string key] + public ListTuple this[string key] { get { + int[] items = new int[key.Length]; for (int i = 0; i < key.Length; i++) { char c = key[i]; switch (c) { - case 'x': yield return x; break; - case 'y': yield return y; break; + case 'x': items[i] = x; break; + case 'y': items[i] = y; break; default: throw new ArgumentException("Invalid key.", nameof(key)); } } + return new ListTuple(items); } set { @@ -248,12 +250,16 @@ namespace Nerd_STF.Mathematics public static explicit operator Int2(PointF point) => new Int2((int)point.X, (int)point.Y); public static implicit operator Int2(Size size) => new Int2(size.Width, size.Height); public static explicit operator Int2(SizeF size) => new Int2((int)size.Width, (int)size.Height); + public static explicit operator Int2(ListTuple tuple) => new Int2((int)tuple[0], (int)tuple[1]); + public static implicit operator Int2(ListTuple tuple) => new Int2(tuple[0], tuple[1]); public static implicit operator Int2((int, int) tuple) => new Int2(tuple.Item1, tuple.Item2); public static implicit operator Point(Int2 group) => new Point(group.x, group.y); public static explicit operator PointF(Int2 group) => new PointF(group.x, group.y); public static implicit operator Size(Int2 group) => new Size(group.x, group.y); public static explicit operator SizeF(Int2 group) => new SizeF(group.x, group.y); + public static implicit operator ListTuple(Int2 group) => new ListTuple(group.x, group.y); + public static implicit operator ListTuple(Int2 group) => new ListTuple(group.x, group.y); public static implicit operator ValueTuple(Int2 group) => (group.x, group.y); } } diff --git a/Nerd_STF/Mathematics/Int3.cs b/Nerd_STF/Mathematics/Int3.cs index 4bb1519..517e599 100644 --- a/Nerd_STF/Mathematics/Int3.cs +++ b/Nerd_STF/Mathematics/Int3.cs @@ -1,5 +1,5 @@ -using Nerd_STF.Abstract; -using Nerd_STF.Exceptions; +using Nerd_STF.Exceptions; +using Nerd_STF.Mathematics.Algebra; using System; using System.Collections; using System.Collections.Generic; @@ -74,21 +74,23 @@ namespace Nerd_STF.Mathematics } } } - public IEnumerable this[string key] + public ListTuple this[string key] { get { + int[] items = new int[key.Length]; for (int i = 0; i < key.Length; i++) { char c = key[i]; switch (c) { - case 'x': yield return x; break; - case 'y': yield return y; break; - case 'z': yield return z; break; + case 'x': items[i] = x; break; + case 'y': items[i] = y; break; + case 'z': items[i] = z; break; default: throw new ArgumentException("Invalid key.", nameof(key)); } } + return new ListTuple(items); } set { @@ -264,8 +266,12 @@ namespace Nerd_STF.Mathematics public static explicit operator Int3(Float2 floats) => new Int3((int)floats.x, (int)floats.y, 0); public static explicit operator Int3(Float3 floats) => new Int3((int)floats.x, (int)floats.y, (int)floats.z); public static explicit operator Int3(Float4 floats) => new Int3((int)floats.x, (int)floats.y, (int)floats.z); + public static explicit operator Int3(ListTuple tuple) => new Int3((int)tuple[0], (int)tuple[1], (int)tuple[2]); + public static implicit operator Int3(ListTuple tuple) => new Int3(tuple[0], tuple[1], tuple[2]); public static implicit operator Int3((int, int, int) tuple) => new Int3(tuple.Item1, tuple.Item2, tuple.Item3); + public static implicit operator ListTuple(Int3 group) => new ListTuple(group.x, group.y, group.z); + public static implicit operator ListTuple(Int3 group) => new ListTuple(group.x, group.y, group.z); public static implicit operator ValueTuple(Int3 group) => (group.x, group.y, group.z); } } diff --git a/Nerd_STF/Mathematics/Int4.cs b/Nerd_STF/Mathematics/Int4.cs index 5d752cb..9ea1c87 100644 --- a/Nerd_STF/Mathematics/Int4.cs +++ b/Nerd_STF/Mathematics/Int4.cs @@ -1,5 +1,5 @@ -using Nerd_STF.Abstract; -using Nerd_STF.Exceptions; +using Nerd_STF.Exceptions; +using Nerd_STF.Mathematics.Algebra; using System; using System.Collections; using System.Collections.Generic; @@ -80,22 +80,24 @@ namespace Nerd_STF.Mathematics } } } - public IEnumerable this[string key] + public ListTuple this[string key] { get { + int[] items = new int[key.Length]; for (int i = 0; i < key.Length; i++) { char c = key[i]; switch (c) { - case 'w': yield return w; break; - case 'x': yield return x; break; - case 'y': yield return y; break; - case 'z': yield return z; break; + case 'w': items[i] = w; break; + case 'x': items[i] = x; break; + case 'y': items[i] = y; break; + case 'z': items[i] = z; break; default: throw new ArgumentException("Invalid key.", nameof(key)); } } + return new ListTuple(items); } set { @@ -276,8 +278,12 @@ namespace Nerd_STF.Mathematics public static explicit operator Int4(Float2 floats) => new Int4(0, (int)floats.x, (int)floats.y, 0); public static explicit operator Int4(Float3 floats) => new Int4(0, (int)floats.x, (int)floats.y, (int)floats.z); public static explicit operator Int4(Float4 floats) => new Int4((int)floats.w, (int)floats.x, (int)floats.y, (int)floats.z); + public static explicit operator Int4(ListTuple tuple) => new Int4((int)tuple[0], (int)tuple[1], (int)tuple[2], (int)tuple[3]); + public static implicit operator Int4(ListTuple tuple) => new Int4(tuple[0], tuple[1], tuple[2], tuple[3]); public static implicit operator Int4((int, int, int, int) tuple) => new Int4(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4); + public static implicit operator ListTuple(Int4 group) => new ListTuple(group.w, group.x, group.y, group.z); + public static implicit operator ListTuple(Int4 group) => new ListTuple(group.w, group.x, group.y, group.z); public static implicit operator ValueTuple(Int4 group) => (group.w, group.x, group.y, group.z); } } diff --git a/Nerd_STF/Mathematics/Numbers/Fraction.cs b/Nerd_STF/Mathematics/Numbers/Fraction.cs index 383b19d..3dacdf6 100644 --- a/Nerd_STF/Mathematics/Numbers/Fraction.cs +++ b/Nerd_STF/Mathematics/Numbers/Fraction.cs @@ -1,5 +1,4 @@ -using Nerd_STF.Abstract; -using Nerd_STF.Helpers; +using Nerd_STF.Helpers; using System; using System.Collections.Generic; using System.Globalization; @@ -474,7 +473,8 @@ namespace Nerd_STF.Mathematics.Numbers else return false; } public override int GetHashCode() => - (int)(((uint)num.GetHashCode() & 0xFFFF0000u) | ((uint)den.GetHashCode() & 0x0000FFFFu)); + (int)((uint)num.GetHashCode() & 0xFFFF0000 | + (uint)den.GetHashCode() & 0x0000FFFF); #if CS8_OR_GREATER public override string ToString() => ToString(null, null); public string ToString(string? format) => ToString(format, null); diff --git a/Nerd_STF/Nerd_STF.csproj b/Nerd_STF/Nerd_STF.csproj index 416fe12..279ea3e 100644 --- a/Nerd_STF/Nerd_STF.csproj +++ b/Nerd_STF/Nerd_STF.csproj @@ -3,11 +3,12 @@ - netstandard1.1;netstandard1.3;netstandard2.1;netcoreapp3.0;net5.0;net7.0 + netstandard1.1;netstandard1.3;netstandard2.1;net471;netcoreapp3.0;net5.0;net7.0 true True portable True + false @@ -141,6 +142,10 @@ Anyway, that's most of the big changes! I don't know if I'll do the full changel + + + + @@ -156,23 +161,35 @@ Anyway, that's most of the big changes! I don't know if I'll do the full changel - - $(DefineConstants);CS8_OR_GREATER;SPAN_SUPPORT - enable + + $(DefineConstants);CS7_OR_GREATER - $(DefineConstants);CS8_OR_GREATER;SPAN_SUPPORT + $(DefineConstants);CS7_OR_GREATER;CS8_OR_GREATER enable - $(DefineConstants);CS8_OR_GREATER;CS9_OR_GREATER;SPAN_SUPPORT + $(DefineConstants);CS7_OR_GREATER;CS8_OR_GREATER;CS9_OR_GREATER enable - $(DefineConstants);CS10_OR_GREATER;CS11_OR_GREATER;CS8_OR_GREATER;CS9_OR_GREATER;SPAN_SUPPORT + $(DefineConstants);CS7_OR_GREATER;CS8_OR_GREATER;CS9_OR_GREATER;CS10_OR_GREATER;CS11_OR_GREATER + enable + + + + $(DefineConstants);CS7_OR_GREATER + + + + $(DefineConstants);CS7_OR_GREATER + + + + $(DefineConstants);CS7_OR_GREATER;CS8_OR_GREATER enable