Prerequisites for matrices and 2x2 matrix done.

This commit is contained in:
That-One-Nerd 2024-11-14 08:56:10 -05:00
parent fcee608322
commit 36d4411d70
33 changed files with 850 additions and 132 deletions

View File

@ -1,9 +0,0 @@
using System.Collections.Generic;
namespace Nerd_STF.Abstract
{
public interface ICombinationIndexer<TItem>
{
IEnumerable<TItem> this[string key] { get; set; }
}
}

View File

@ -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 namespace Nerd_STF.Helpers
{ {
@ -48,5 +51,64 @@ namespace Nerd_STF.Helpers
} }
return builder.Remove(builder.Length - 1, 1).ToString(); return builder.Remove(builder.Length - 1, 1).ToString();
} }
#if CS8_OR_GREATER
public static string MatrixToString<T>(T matrix, string? format)
#else
public static string MatrixToString<T>(T matrix, string format)
#endif
where T : IMatrix<T>
{
// 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();
}
} }
} }

View File

@ -0,0 +1,7 @@
namespace Nerd_STF
{
public interface ICombinationIndexer<TItem>
{
ListTuple<TItem> this[string key] { get; set; }
}
}

View File

@ -1,7 +1,7 @@
#if CS11_OR_GREATER #if CS11_OR_GREATER
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace Nerd_STF.Abstract namespace Nerd_STF
{ {
public interface IFromTuple<TSelf, TTuple> public interface IFromTuple<TSelf, TTuple>
where TSelf : IFromTuple<TSelf, TTuple> where TSelf : IFromTuple<TSelf, TTuple>

View File

@ -2,7 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace Nerd_STF.Abstract namespace Nerd_STF
{ {
public interface ISplittable<TSelf, TTuple> public interface ISplittable<TSelf, TTuple>
where TSelf : ISplittable<TSelf, TTuple> where TSelf : ISplittable<TSelf, TTuple>

134
Nerd_STF/ListTuple.cs Normal file
View File

@ -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<T> : IEnumerable<T>,
IEquatable<ListTuple<T>>
#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<T> 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<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public bool Equals(ListTuple<T> 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<T> 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<T> a, ListTuple<T> b) => a.Equals(b);
public static bool operator !=(ListTuple<T> a, ListTuple<T> b) => !a.Equals(b);
public static implicit operator ValueTuple<T>(ListTuple<T> tuple) => new ValueTuple<T>(tuple[0]);
public static implicit operator ValueTuple<T, T>(ListTuple<T> tuple) => (tuple[0], tuple[1]);
public static implicit operator ValueTuple<T, T, T>(ListTuple<T> tuple) => (tuple[0], tuple[1], tuple[2]);
public static implicit operator ValueTuple<T, T, T, T>(ListTuple<T> tuple) => (tuple[0], tuple[1], tuple[2], tuple[3]);
public static implicit operator ValueTuple<T, T, T, T, T>(ListTuple<T> tuple) => (tuple[0], tuple[1], tuple[2], tuple[3], tuple[4]);
public static implicit operator ValueTuple<T, T, T, T, T, T>(ListTuple<T> tuple) => (tuple[0], tuple[1], tuple[2], tuple[3], tuple[4], tuple[5]);
public static implicit operator ValueTuple<T, T, T, T, T, T, T>(ListTuple<T> tuple) => (tuple[0], tuple[1], tuple[2], tuple[3], tuple[4], tuple[5], tuple[6]);
public static implicit operator T[](ListTuple<T> tuple) => tuple.items;
public static implicit operator ListTuple<T>(ValueTuple<T> tuple) => new ListTuple<T>(tuple.Item1);
public static implicit operator ListTuple<T>((T, T) tuple) => new ListTuple<T>(tuple.Item1, tuple.Item2);
public static implicit operator ListTuple<T>((T, T, T) tuple) => new ListTuple<T>(tuple.Item1, tuple.Item2, tuple.Item3);
public static implicit operator ListTuple<T>((T, T, T, T) tuple) => new ListTuple<T>(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4);
public static implicit operator ListTuple<T>((T, T, T, T, T) tuple) => new ListTuple<T>(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4, tuple.Item5);
public static implicit operator ListTuple<T>((T, T, T, T, T, T) tuple) => new ListTuple<T>(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4, tuple.Item5, tuple.Item6);
public static implicit operator ListTuple<T>((T, T, T, T, T, T, T) tuple) => new ListTuple<T>(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4, tuple.Item5, tuple.Item6, tuple.Item7);
public static implicit operator ListTuple<T>(T[] array) => new ListTuple<T>(array);
public struct Enumerator : IEnumerator<T>
{
private int index;
private readonly ListTuple<T> 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<T> tuple)
{
index = -1;
this.tuple = tuple;
}
}
}
}

View File

@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
namespace Nerd_STF.Mathematics.Algebra
{
public interface IMatrix<TSelf> : IEnumerable<double>,
IEquatable<TSelf>,
IFormattable
#if CS11_OR_GREATER
,IMatrixOperations<TSelf>
#endif
where TSelf : IMatrix<TSelf>
{
Int2 Size { get; }
double this[int r, int c] { get; set; }
double this[Int2 index] { get; set; }
ListTuple<double> this[int index, RowColumn direction] { get; set; }
ListTuple<double> GetRow(int row);
ListTuple<double> GetColumn(int column);
void SetRow(int row, IEnumerable<double> vals);
void SetColumn(int column, IEnumerable<double> vals);
double Determinant();
TSelf Adjoint();
TSelf Cofactor();
TSelf Transpose();
#if CS9_OR_GREATER
TSelf? Inverse();
#else
TSelf Inverse();
#endif
}
}

View File

@ -0,0 +1,17 @@
#if CS11_OR_GREATER
using System.Collections.Generic;
namespace Nerd_STF.Mathematics.Algebra
{
public interface IMatrixOperations<TSelf> : ISimpleMathOperations<TSelf>
where TSelf : IMatrix<TSelf>, IMatrixOperations<TSelf>
{
static abstract TSelf Average(IEnumerable<TSelf> 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

View File

@ -0,0 +1,8 @@
namespace Nerd_STF.Mathematics.Algebra
{
public interface ISquareMatrix<TSelf> : IMatrix<TSelf>
where TSelf : ISquareMatrix<TSelf>
{
double Trace();
}
}

View File

@ -0,0 +1,11 @@
#if CS11_OR_GREATER
namespace Nerd_STF.Mathematics.Algebra
{
public interface IStaticMatrix<TSelf> : IPresets1d<TSelf>, IMatrix<TSelf>
where TSelf : IStaticMatrix<TSelf>
{
static abstract TSelf Identity { get; }
static abstract TSelf SignField { get; }
}
}
#endif

View File

@ -0,0 +1,9 @@
namespace Nerd_STF.Mathematics.Algebra
{
public interface ISubmatrixOperations<TSelf, TSmaller>
where TSelf : IMatrix<TSelf>, ISubmatrixOperations<TSelf, TSmaller>
where TSmaller : IMatrix<TSmaller>
{
TSmaller Submatrix(int r, int c);
}
}

View File

@ -1,7 +1,7 @@
#if CS11_OR_GREATER #if CS11_OR_GREATER
using System.Collections.Generic; using System.Collections.Generic;
namespace Nerd_STF.Abstract namespace Nerd_STF.Mathematics.Algebra
{ {
public interface IVectorOperations<TSelf> : ISimpleMathOperations<TSelf> public interface IVectorOperations<TSelf> : ISimpleMathOperations<TSelf>
where TSelf : IVectorOperations<TSelf> where TSelf : IVectorOperations<TSelf>

View File

@ -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<Matrix2x2>,
ISquareMatrix<Matrix2x2>
#if CS11_OR_GREATER
,ISplittable<Matrix2x2, (double[] r1c1, double[] r1c2, double[] r2c1, double[] r2c2)>,
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 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;
}
/// <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 = 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];
}
}
/// <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 = true)
{
int x = 0;
foreach (IEnumerable<double> 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;
}
}
/// <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 = true)
{
int x = 0;
foreach (IEnumerable<double> 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<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.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<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[] r1c1, double[] r1c2, double[] r2c1, double[] r2c2) SplitArray(IEnumerable<Matrix2x2> 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<double> 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<double>(vals);
}
public ListTuple<double> 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<double>(vals);
}
public void SetRow(int row, IEnumerable<double> vals)
{
int col = 0;
foreach (double v in vals)
{
this[(row, col)] = v;
col++;
if (col >= 2) return;
}
}
public void SetColumn(int column, IEnumerable<double> vals)
{
int row = 0;
foreach (double v in vals)
{
this[(row, column)] = v;
row++;
if (row >= 2) return;
}
}
public void SetRow(int row, ListTuple<double> vals) => SetRow(row, (IEnumerable<double>)vals);
public void SetColumn(int row, ListTuple<double> vals) => SetColumn(row, (IEnumerable<double>)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<double> 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();
}
}

View File

@ -0,0 +1,8 @@
namespace Nerd_STF.Mathematics.Algebra
{
public enum RowColumn
{
Row,
Column
}
}

View File

@ -1,15 +1,15 @@
using Nerd_STF.Abstract; using System;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
namespace Nerd_STF.Mathematics namespace Nerd_STF.Mathematics
{ {
public struct Angle : IComparable<Angle>, // Maybe move to .Numbers and add inheritance to INumber? Does this make sense?
IEquatable<Angle> public readonly struct Angle : IComparable<Angle>,
IEquatable<Angle>
#if CS11_OR_GREATER #if CS11_OR_GREATER
,IPresets2d<Angle>, ,IFromTuple<Angle, (double, Angle.Units)>,
IFromTuple<Angle, (double, Angle.Unit)> IPresets2d<Angle>
#endif #endif
{ {
public static Angle Down => new Angle(0.75); public static Angle Down => new Angle(0.75);
@ -23,45 +23,29 @@ namespace Nerd_STF.Mathematics
public static Angle Zero => new Angle(0); public static Angle Zero => new Angle(0);
#if CS11_OR_GREATER #if CS11_OR_GREATER
static Angle IPresets1d<Angle>.One => new Angle(1, Unit.Degrees); static Angle IPresets1d<Angle>.One => new Angle(1, Units.Degrees);
#endif #endif
public double Degrees public double Degrees => revTheta * 360;
{ public double Gradians => revTheta * 400;
get => revTheta * 360; public double Radians => revTheta * Constants.Tau;
set => revTheta = value / 360; public double Revolutions => revTheta;
}
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 Angle Complimentary => new Angle(0.25 - MathE.ModAbs(revTheta, 1)); public Angle Complimentary => new Angle(0.25 - MathE.ModAbs(revTheta, 1));
public Angle Supplimentary => new Angle(0.5 - 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 Normalized => new Angle(MathE.ModAbs(revTheta, 1));
public Angle Reflected => 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) switch (unit)
{ {
case Unit.Revolutions: revTheta = theta; break; case Units.Revolutions: revTheta = theta; break;
case Unit.Degrees: revTheta = theta / 360; break; case Units.Degrees: revTheta = theta / 360; break;
case Unit.Radians: revTheta = theta / Constants.Tau; break; case Units.Radians: revTheta = theta / Constants.Tau; break;
case Unit.Gradians: revTheta = theta / 400; break; case Units.Gradians: revTheta = theta / 400; break;
default: throw new ArgumentException($"Unknown angle unit \"{unit}.\"", nameof(unit)); default: throw new ArgumentException($"Unknown angle unit \"{unit}.\"", nameof(unit));
} }
} }
@ -70,30 +54,64 @@ namespace Nerd_STF.Mathematics
this.revTheta = revTheta; this.revTheta = revTheta;
} }
public double this[Unit unit] public double this[Units unit]
{ {
get get
{ {
switch (unit) switch (unit)
{ {
case Unit.Revolutions: return revTheta; case Units.Revolutions: return revTheta;
case Unit.Degrees: return revTheta * 360; case Units.Degrees: return revTheta * 360;
case Unit.Radians: return revTheta * Constants.Tau; case Units.Radians: return revTheta * Constants.Tau;
case Unit.Gradians: return revTheta * 400; case Units.Gradians: return revTheta * 400;
default: throw new ArgumentException($"Unknown angle unit \"{unit}.\"", nameof(unit)); 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 Units.Revolutions:
{ switch (to)
case Unit.Revolutions: revTheta = value; break; {
case Unit.Degrees: revTheta = value / 360; break; case Units.Revolutions: return value;
case Unit.Radians: revTheta = value / Constants.Tau; break; case Units.Degrees: return value * 360;
case Unit.Gradians: revTheta = value / 400; break; case Units.Radians: return value * 6.28318530718;
default: throw new ArgumentException($"Unknown angle unit \"{unit}.\"", nameof(unit)); 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<Angle> angles) public static Angle Average(IEnumerable<Angle> angles)
@ -109,8 +127,6 @@ namespace Nerd_STF.Mathematics
} }
public static Angle Clamp(Angle value, Angle min, Angle max) => public static Angle Clamp(Angle value, Angle min, Angle max) =>
new Angle(MathE.Clamp(value.revTheta, min.revTheta, max.revTheta)); 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<Angle> values) => Max(false, values); public static Angle Max(IEnumerable<Angle> values) => Max(false, values);
public static Angle Max(bool normalize, IEnumerable<Angle> values) public static Angle Max(bool normalize, IEnumerable<Angle> values)
{ {
@ -172,7 +188,7 @@ namespace Nerd_STF.Mathematics
return sum; return sum;
} }
public static double[] SplitArray(Unit unit, IEnumerable<Angle> values) public static double[] SplitArray(Units unit, IEnumerable<Angle> values)
{ {
int count = values.Count(); int count = values.Count();
double[] angles = new double[count]; double[] angles = new double[count];
@ -185,6 +201,8 @@ namespace Nerd_STF.Mathematics
return angles; return angles;
} }
public Angle Coterminal(int turns) => new Angle(revTheta + turns);
public int CompareTo(Angle other) => revTheta.CompareTo(other.revTheta); public int CompareTo(Angle other) => revTheta.CompareTo(other.revTheta);
public bool Equals(Angle other) => revTheta == other.revTheta; public bool Equals(Angle other) => revTheta == other.revTheta;
#if CS8_OR_GREATER #if CS8_OR_GREATER
@ -198,26 +216,26 @@ namespace Nerd_STF.Mathematics
else return false; else return false;
} }
public override int GetHashCode() => revTheta.GetHashCode(); public override int GetHashCode() => revTheta.GetHashCode();
public override string ToString() => ToString(Unit.Degrees, null); public override string ToString() => ToString(Units.Degrees, null);
public string ToString(Unit unit) => ToString(unit, null); public string ToString(Units unit) => ToString(unit, null);
#if CS8_OR_GREATER #if CS8_OR_GREATER
public string ToString(string? format) => public string ToString(string? format) =>
#else #else
public string ToString(string format) => public string ToString(string format) =>
#endif #endif
ToString(Unit.Degrees, format); ToString(Units.Degrees, format);
#if CS8_OR_GREATER #if CS8_OR_GREATER
public string ToString(Unit unit, string? format) public string ToString(Units unit, string? format)
#else #else
public string ToString(Unit unit, string format) public string ToString(Units unit, string format)
#endif #endif
{ {
switch (unit) switch (unit)
{ {
case Unit.Revolutions: return $"{revTheta.ToString(format)} rev"; case Units.Revolutions: return $"{revTheta.ToString(format)} rev";
case Unit.Degrees: return $"{(revTheta * 360).ToString(format)} deg"; case Units.Degrees: return $"{(revTheta * 360).ToString(format)} deg";
case Unit.Radians: return $"{(revTheta * Constants.Tau).ToString(format)} rad"; case Units.Radians: return $"{(revTheta * Constants.Tau).ToString(format)} rad";
case Unit.Gradians: return $"{(revTheta * 400).ToString(format)} grad"; case Units.Gradians: return $"{(revTheta * 400).ToString(format)} grad";
default: throw new ArgumentException($"Unknown angle unit \"{unit}.\"", nameof(unit)); 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 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 #if CS11_OR_GREATER
static implicit IFromTuple<Angle, (double, Unit)>.operator ValueTuple<double, Unit>(Angle angle) => (angle.revTheta, Unit.Revolutions); static implicit IFromTuple<Angle, (double, Units)>.operator ValueTuple<double, Units>(Angle angle) => (angle.revTheta, Units.Revolutions);
#endif #endif
public enum Unit public enum Units
{ {
Revolutions, Revolutions,
Degrees, Degrees,

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Numerics;
namespace Nerd_STF.Mathematics.Equations namespace Nerd_STF.Mathematics.Equations
{ {

View File

@ -1,5 +1,4 @@
using Nerd_STF.Abstract; using Nerd_STF.Exceptions;
using Nerd_STF.Exceptions;
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
@ -13,6 +12,7 @@ namespace Nerd_STF.Mathematics
,IFromTuple<Float2, (double, double)>, ,IFromTuple<Float2, (double, double)>,
IPresets2d<Float2>, IPresets2d<Float2>,
IRoundable<Float2, Int2>, IRoundable<Float2, Int2>,
IRefRoundable<Float2>,
ISplittable<Float2, (double[] Xs, double[] Ys)> ISplittable<Float2, (double[] Xs, double[] Ys)>
#endif #endif
{ {
@ -70,20 +70,22 @@ namespace Nerd_STF.Mathematics
} }
} }
} }
public IEnumerable<double> this[string key] public ListTuple<double> this[string key]
{ {
get get
{ {
double[] items = new double[key.Length];
for (int i = 0; i < key.Length; i++) for (int i = 0; i < key.Length; i++)
{ {
char c = key[i]; char c = key[i];
switch (c) switch (c)
{ {
case 'x': yield return x; break; case 'x': items[i] = x; break;
case 'y': yield return y; break; case 'y': items[i] = y; break;
default: throw new ArgumentException("Invalid key.", nameof(key)); default: throw new ArgumentException("Invalid key.", nameof(key));
} }
} }
return new ListTuple<double>(items);
} }
set 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(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(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(SizeF size) => new Float2(size.Width, size.Height);
public static implicit operator Float2(ListTuple<double> tuple) => new Float2(tuple[0], tuple[1]);
public static implicit operator Float2(ListTuple<int> tuple) => new Float2(tuple[0], tuple[1]);
public static implicit operator Float2((double, double) tuple) => new Float2(tuple.Item1, tuple.Item2); 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 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 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 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 SizeF(Float2 group) => new SizeF((float)group.x, (float)group.y);
public static implicit operator ListTuple<double>(Float2 group) => new ListTuple<double>(group.x, group.y);
public static explicit operator ListTuple<int>(Float2 group) => new ListTuple<int>((int)group.x, (int)group.y);
public static implicit operator ValueTuple<double, double>(Float2 group) => (group.x, group.y); public static implicit operator ValueTuple<double, double>(Float2 group) => (group.x, group.y);
} }
} }

View File

@ -1,5 +1,4 @@
using Nerd_STF.Abstract; using Nerd_STF.Exceptions;
using Nerd_STF.Exceptions;
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
@ -12,6 +11,7 @@ namespace Nerd_STF.Mathematics
,IFromTuple<Float3, (double, double, double)>, ,IFromTuple<Float3, (double, double, double)>,
IPresets2d<Float3>, IPresets2d<Float3>,
IRoundable<Float3, Int3>, IRoundable<Float3, Int3>,
IRefRoundable<Float3>,
ISplittable<Float3, (double[] Xs, double[] Ys, double[] Zs)> ISplittable<Float3, (double[] Xs, double[] Ys, double[] Zs)>
#endif #endif
{ {
@ -75,21 +75,23 @@ namespace Nerd_STF.Mathematics
} }
} }
} }
public IEnumerable<double> this[string key] public ListTuple<double> this[string key]
{ {
get get
{ {
double[] items = new double[key.Length];
for (int i = 0; i < key.Length; i++) for (int i = 0; i < key.Length; i++)
{ {
char c = key[i]; char c = key[i];
switch (c) switch (c)
{ {
case 'x': yield return x; break; case 'x': items[i] = x; break;
case 'y': yield return y; break; case 'y': items[i] = y; break;
case 'z': yield return z; break; case 'z': items[i] = z; break;
default: throw new ArgumentException("Invalid key.", nameof(key)); default: throw new ArgumentException("Invalid key.", nameof(key));
} }
} }
return new ListTuple<double>(items);
} }
set 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(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 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 explicit operator Float3(Int4 ints) => new Float3(ints.x, ints.y, ints.z);
public static implicit operator Float3(ListTuple<double> tuple) => new Float3(tuple[0], tuple[1], tuple[2]);
public static implicit operator Float3(ListTuple<int> 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 Float3((double, double, double) tuple) => new Float3(tuple.Item1, tuple.Item2, tuple.Item3);
public static implicit operator ListTuple<double>(Float3 group) => new ListTuple<double>(group.x, group.y, group.z);
public static explicit operator ListTuple<int>(Float3 group) => new ListTuple<int>((int)group.x, (int)group.y, (int)group.z);
public static implicit operator ValueTuple<double, double, double>(Float3 group) => (group.x, group.y, group.z); public static implicit operator ValueTuple<double, double, double>(Float3 group) => (group.x, group.y, group.z);
} }
} }

View File

@ -2,7 +2,6 @@ using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Nerd_STF.Abstract;
using Nerd_STF.Exceptions; using Nerd_STF.Exceptions;
namespace Nerd_STF.Mathematics namespace Nerd_STF.Mathematics
@ -12,6 +11,7 @@ namespace Nerd_STF.Mathematics
,IFromTuple<Float4, (double, double, double, double)>, ,IFromTuple<Float4, (double, double, double, double)>,
IPresets4d<Float4>, IPresets4d<Float4>,
IRoundable<Float4, Int4>, IRoundable<Float4, Int4>,
IRefRoundable<Float4>,
ISplittable<Float4, (double[] Ws, double[] Xs, double[] Ys, double[] Zs)> ISplittable<Float4, (double[] Ws, double[] Xs, double[] Ys, double[] Zs)>
#endif #endif
{ {
@ -81,22 +81,24 @@ namespace Nerd_STF.Mathematics
} }
} }
} }
public IEnumerable<double> this[string key] public ListTuple<double> this[string key]
{ {
get get
{ {
double[] items = new double[key.Length];
for (int i = 0; i < key.Length; i++) for (int i = 0; i < key.Length; i++)
{ {
char c = key[i]; char c = key[i];
switch (c) switch (c)
{ {
case 'w': yield return w; break; case 'w': items[i] = w; break;
case 'x': yield return x; break; case 'x': items[i] = x; break;
case 'y': yield return y; break; case 'y': items[i] = y; break;
case 'z': yield return z; break; case 'z': items[i] = z; break;
default: throw new ArgumentException("Invalid key.", nameof(key)); default: throw new ArgumentException("Invalid key.", nameof(key));
} }
} }
return new ListTuple<double>(items);
} }
set 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(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(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(Float3 floats) => new Float4(0, floats.x, floats.y, floats.z);
public static implicit operator Float4(ListTuple<double> tuple) => new Float4(tuple[0], tuple[1], tuple[2], tuple[3]);
public static implicit operator Float4(ListTuple<int> 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 Float4((double, double, double, double) tuple) => new Float4(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4);
public static implicit operator ListTuple<double>(Float4 group) => new ListTuple<double>(group.w, group.x, group.y, group.z);
public static explicit operator ListTuple<int>(Float4 group) => new ListTuple<int>((int)group.w, (int)group.x, (int)group.y, (int)group.z);
public static implicit operator ValueTuple<double, double, double, double>(Float4 group) => (group.w, group.x, group.y, group.z); public static implicit operator ValueTuple<double, double, double, double>(Float4 group) => (group.w, group.x, group.y, group.z);
} }
} }

View File

@ -1,5 +1,5 @@
#if CS11_OR_GREATER #if CS11_OR_GREATER
namespace Nerd_STF.Abstract namespace Nerd_STF.Mathematics
{ {
public interface IInterpolable<TSelf> public interface IInterpolable<TSelf>
where TSelf : IInterpolable<TSelf> where TSelf : IInterpolable<TSelf>

View File

@ -1,14 +1,15 @@
using System; using Nerd_STF.Mathematics.Algebra;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Numerics; using System.Numerics;
namespace Nerd_STF.Abstract namespace Nerd_STF.Mathematics
{ {
public interface INumberGroup<TSelf, TItem> : ICombinationIndexer<TItem>, public interface INumberGroup<TSelf, TItem> : ICombinationIndexer<TItem>,
IEnumerable<TItem>, IEnumerable<TItem>,
IEquatable<TSelf> IEquatable<TSelf>
#if CS11_OR_GREATER #if CS11_OR_GREATER
,IInterpolable<TSelf>, , IInterpolable<TSelf>,
ISimpleMathOperations<TSelf>, ISimpleMathOperations<TSelf>,
IVectorOperations<TSelf> IVectorOperations<TSelf>
#endif #endif

View File

@ -1,5 +1,5 @@
#if CS11_OR_GREATER #if CS11_OR_GREATER
namespace Nerd_STF.Abstract namespace Nerd_STF.Mathematics
{ {
public interface IPresets1d<TSelf> where TSelf : IPresets1d<TSelf> public interface IPresets1d<TSelf> where TSelf : IPresets1d<TSelf>
{ {

View File

@ -1,5 +1,5 @@
#if CS11_OR_GREATER #if CS11_OR_GREATER
namespace Nerd_STF.Abstract namespace Nerd_STF.Mathematics
{ {
public interface IPresets2d<TSelf> : IPresets1d<TSelf> public interface IPresets2d<TSelf> : IPresets1d<TSelf>
where TSelf : IPresets2d<TSelf> where TSelf : IPresets2d<TSelf>

View File

@ -1,5 +1,5 @@
#if CS11_OR_GREATER #if CS11_OR_GREATER
namespace Nerd_STF.Abstract namespace Nerd_STF.Mathematics
{ {
public interface IPresets3d<TSelf> : IPresets2d<TSelf> public interface IPresets3d<TSelf> : IPresets2d<TSelf>
where TSelf : IPresets3d<TSelf> where TSelf : IPresets3d<TSelf>

View File

@ -1,5 +1,5 @@
#if CS11_OR_GREATER #if CS11_OR_GREATER
namespace Nerd_STF.Abstract namespace Nerd_STF.Mathematics
{ {
public interface IPresets4d<TSelf> : IPresets3d<TSelf> public interface IPresets4d<TSelf> : IPresets3d<TSelf>
where TSelf : IPresets4d<TSelf> where TSelf : IPresets4d<TSelf>

View File

@ -0,0 +1,12 @@
#if CS11_OR_GREATER
namespace Nerd_STF.Mathematics
{
public interface IRefRoundable<TSelf>
where TSelf : IRefRoundable<TSelf>
{
static abstract void Ceiling(ref TSelf val);
static abstract void Floor(ref TSelf val);
static abstract void Round(ref TSelf val);
}
}
#endif

View File

@ -1,5 +1,5 @@
#if CS11_OR_GREATER #if CS11_OR_GREATER
namespace Nerd_STF.Abstract namespace Nerd_STF.Mathematics
{ {
public interface IRoundable<TSelf> : IRoundable<TSelf, TSelf> public interface IRoundable<TSelf> : IRoundable<TSelf, TSelf>
where TSelf : IRoundable<TSelf> { } where TSelf : IRoundable<TSelf> { }

View File

@ -2,12 +2,11 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Numerics; using System.Numerics;
namespace Nerd_STF.Abstract namespace Nerd_STF.Mathematics
{ {
public interface ISimpleMathOperations<TSelf> : IAdditionOperators<TSelf, TSelf, TSelf>, public interface ISimpleMathOperations<TSelf> : IAdditionOperators<TSelf, TSelf, TSelf>,
ISubtractionOperators<TSelf, TSelf, TSelf>, ISubtractionOperators<TSelf, TSelf, TSelf>,
IMultiplyOperators<TSelf, TSelf, TSelf>, IMultiplyOperators<TSelf, TSelf, TSelf>
IDivisionOperators<TSelf, TSelf, TSelf>
where TSelf : ISimpleMathOperations<TSelf> where TSelf : ISimpleMathOperations<TSelf>
{ {
static abstract TSelf Product(IEnumerable<TSelf> vals); static abstract TSelf Product(IEnumerable<TSelf> vals);

View File

@ -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;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
@ -69,20 +69,22 @@ namespace Nerd_STF.Mathematics
} }
} }
} }
public IEnumerable<int> this[string key] public ListTuple<int> this[string key]
{ {
get get
{ {
int[] items = new int[key.Length];
for (int i = 0; i < key.Length; i++) for (int i = 0; i < key.Length; i++)
{ {
char c = key[i]; char c = key[i];
switch (c) switch (c)
{ {
case 'x': yield return x; break; case 'x': items[i] = x; break;
case 'y': yield return y; break; case 'y': items[i] = y; break;
default: throw new ArgumentException("Invalid key.", nameof(key)); default: throw new ArgumentException("Invalid key.", nameof(key));
} }
} }
return new ListTuple<int>(items);
} }
set 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 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 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(SizeF size) => new Int2((int)size.Width, (int)size.Height);
public static explicit operator Int2(ListTuple<double> tuple) => new Int2((int)tuple[0], (int)tuple[1]);
public static implicit operator Int2(ListTuple<int> 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 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 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 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 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 explicit operator SizeF(Int2 group) => new SizeF(group.x, group.y);
public static implicit operator ListTuple<double>(Int2 group) => new ListTuple<double>(group.x, group.y);
public static implicit operator ListTuple<int>(Int2 group) => new ListTuple<int>(group.x, group.y);
public static implicit operator ValueTuple<int, int>(Int2 group) => (group.x, group.y); public static implicit operator ValueTuple<int, int>(Int2 group) => (group.x, group.y);
} }
} }

View File

@ -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;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
@ -74,21 +74,23 @@ namespace Nerd_STF.Mathematics
} }
} }
} }
public IEnumerable<int> this[string key] public ListTuple<int> this[string key]
{ {
get get
{ {
int[] items = new int[key.Length];
for (int i = 0; i < key.Length; i++) for (int i = 0; i < key.Length; i++)
{ {
char c = key[i]; char c = key[i];
switch (c) switch (c)
{ {
case 'x': yield return x; break; case 'x': items[i] = x; break;
case 'y': yield return y; break; case 'y': items[i] = y; break;
case 'z': yield return z; break; case 'z': items[i] = z; break;
default: throw new ArgumentException("Invalid key.", nameof(key)); default: throw new ArgumentException("Invalid key.", nameof(key));
} }
} }
return new ListTuple<int>(items);
} }
set 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(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(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(Float4 floats) => new Int3((int)floats.x, (int)floats.y, (int)floats.z);
public static explicit operator Int3(ListTuple<double> tuple) => new Int3((int)tuple[0], (int)tuple[1], (int)tuple[2]);
public static implicit operator Int3(ListTuple<int> 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 Int3((int, int, int) tuple) => new Int3(tuple.Item1, tuple.Item2, tuple.Item3);
public static implicit operator ListTuple<double>(Int3 group) => new ListTuple<double>(group.x, group.y, group.z);
public static implicit operator ListTuple<int>(Int3 group) => new ListTuple<int>(group.x, group.y, group.z);
public static implicit operator ValueTuple<int, int, int>(Int3 group) => (group.x, group.y, group.z); public static implicit operator ValueTuple<int, int, int>(Int3 group) => (group.x, group.y, group.z);
} }
} }

View File

@ -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;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
@ -80,22 +80,24 @@ namespace Nerd_STF.Mathematics
} }
} }
} }
public IEnumerable<int> this[string key] public ListTuple<int> this[string key]
{ {
get get
{ {
int[] items = new int[key.Length];
for (int i = 0; i < key.Length; i++) for (int i = 0; i < key.Length; i++)
{ {
char c = key[i]; char c = key[i];
switch (c) switch (c)
{ {
case 'w': yield return w; break; case 'w': items[i] = w; break;
case 'x': yield return x; break; case 'x': items[i] = x; break;
case 'y': yield return y; break; case 'y': items[i] = y; break;
case 'z': yield return z; break; case 'z': items[i] = z; break;
default: throw new ArgumentException("Invalid key.", nameof(key)); default: throw new ArgumentException("Invalid key.", nameof(key));
} }
} }
return new ListTuple<int>(items);
} }
set 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(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(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(Float4 floats) => new Int4((int)floats.w, (int)floats.x, (int)floats.y, (int)floats.z);
public static explicit operator Int4(ListTuple<double> tuple) => new Int4((int)tuple[0], (int)tuple[1], (int)tuple[2], (int)tuple[3]);
public static implicit operator Int4(ListTuple<int> 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 Int4((int, int, int, int) tuple) => new Int4(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4);
public static implicit operator ListTuple<double>(Int4 group) => new ListTuple<double>(group.w, group.x, group.y, group.z);
public static implicit operator ListTuple<int>(Int4 group) => new ListTuple<int>(group.w, group.x, group.y, group.z);
public static implicit operator ValueTuple<int, int, int, int>(Int4 group) => (group.w, group.x, group.y, group.z); public static implicit operator ValueTuple<int, int, int, int>(Int4 group) => (group.w, group.x, group.y, group.z);
} }
} }

View File

@ -1,5 +1,4 @@
using Nerd_STF.Abstract; using Nerd_STF.Helpers;
using Nerd_STF.Helpers;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
@ -474,7 +473,8 @@ namespace Nerd_STF.Mathematics.Numbers
else return false; else return false;
} }
public override int GetHashCode() => 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 #if CS8_OR_GREATER
public override string ToString() => ToString(null, null); public override string ToString() => ToString(null, null);
public string ToString(string? format) => ToString(format, null); public string ToString(string? format) => ToString(format, null);

View File

@ -3,11 +3,12 @@
<!-- General stuff --> <!-- General stuff -->
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard1.1;netstandard1.3;netstandard2.1;netcoreapp3.0;net5.0;net7.0</TargetFrameworks> <TargetFrameworks>netstandard1.1;netstandard1.3;netstandard2.1;net471;netcoreapp3.0;net5.0;net7.0</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<ProduceReferenceAssembly>True</ProduceReferenceAssembly> <ProduceReferenceAssembly>True</ProduceReferenceAssembly>
<DebugType>portable</DebugType> <DebugType>portable</DebugType>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild> <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<CheckNotRecommendedTargetFramework>false</CheckNotRecommendedTargetFramework>
</PropertyGroup> </PropertyGroup>
<!-- NuGet package customization. --> <!-- NuGet package customization. -->
@ -141,6 +142,10 @@ Anyway, that's most of the big changes! I don't know if I'll do the full changel
<!-- Mostly used to reference system packages that are not included in this version of .NET Standard. --> <!-- Mostly used to reference system packages that are not included in this version of .NET Standard. -->
<!-- TODO: Maybe this isn't good practice, and we should define environment variables for specific features (tuples, drawing, etc) instead? --> <!-- TODO: Maybe this isn't good practice, and we should define environment variables for specific features (tuples, drawing, etc) instead? -->
<ItemGroup Condition="'$(TargetFramework)'=='net471'">
<PackageReference Include="System.Memory" Version="4.6.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='netstandard1.1'"> <ItemGroup Condition="'$(TargetFramework)'=='netstandard1.1'">
<PackageReference Include="System.Drawing.Primitives" Version="4.3.0" /> <PackageReference Include="System.Drawing.Primitives" Version="4.3.0" />
<PackageReference Include="System.Memory" Version="4.5.5" /> <PackageReference Include="System.Memory" Version="4.5.5" />
@ -156,23 +161,35 @@ Anyway, that's most of the big changes! I don't know if I'll do the full changel
<!-- PropertyGroup customization based on framework. --> <!-- PropertyGroup customization based on framework. -->
<!-- Used to define environment variables based on features the framework supports. --> <!-- Used to define environment variables based on features the framework supports. -->
<PropertyGroup Condition="'$(TargetFramework)'=='netstandard2.1'"> <PropertyGroup Condition="'$(TargetFramework)'=='net471'">
<DefineConstants>$(DefineConstants);CS8_OR_GREATER;SPAN_SUPPORT</DefineConstants> <DefineConstants>$(DefineConstants);CS7_OR_GREATER</DefineConstants>
<Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)'=='netcoreapp3.0'"> <PropertyGroup Condition="'$(TargetFramework)'=='netcoreapp3.0'">
<DefineConstants>$(DefineConstants);CS8_OR_GREATER;SPAN_SUPPORT</DefineConstants> <DefineConstants>$(DefineConstants);CS7_OR_GREATER;CS8_OR_GREATER</DefineConstants>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)'=='net5.0'"> <PropertyGroup Condition="'$(TargetFramework)'=='net5.0'">
<DefineConstants>$(DefineConstants);CS8_OR_GREATER;CS9_OR_GREATER;SPAN_SUPPORT</DefineConstants> <DefineConstants>$(DefineConstants);CS7_OR_GREATER;CS8_OR_GREATER;CS9_OR_GREATER</DefineConstants>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)'=='net7.0'"> <PropertyGroup Condition="'$(TargetFramework)'=='net7.0'">
<DefineConstants>$(DefineConstants);CS10_OR_GREATER;CS11_OR_GREATER;CS8_OR_GREATER;CS9_OR_GREATER;SPAN_SUPPORT</DefineConstants> <DefineConstants>$(DefineConstants);CS7_OR_GREATER;CS8_OR_GREATER;CS9_OR_GREATER;CS10_OR_GREATER;CS11_OR_GREATER</DefineConstants>
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)'=='netstandard1.1'">
<DefineConstants>$(DefineConstants);CS7_OR_GREATER</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)'=='netstandard1.3'">
<DefineConstants>$(DefineConstants);CS7_OR_GREATER</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)'=='netstandard2.1'">
<DefineConstants>$(DefineConstants);CS7_OR_GREATER;CS8_OR_GREATER</DefineConstants>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>