diff --git a/Changelog.md b/Changelog.md index 71d4b4b..4394c6d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,242 +1,118 @@ -# Nerd_STF v2.4.0 (Equations and Numbers) +# Nerd_STF v2.4.1 -I've done a pretty good amount of stuff in this update, and I'm pretty proud of it. Good improvement. +Hey everyone! This is one of the larger small updates, and I'm pretty proud of what I got done in a week. -First of all, I've gone and added all of the applicable `Mathf` functions as an extension to the `Equation` delegate. That way if you want to say, calculate the square root of an entire equation, rather than going: -```csharp -Equation result = x => Mathf.Sqrt(equ(x)); -// Where `equ` is an `Equation` that represents the function we want to take the square root of. -``` -you can shorten it down and remove some of the weirdness. -```csharp -Equation result = equ.Sqrt(); -// Where `equ` is an `Equation` that represents the function we want to take the square root of. -``` +Along with adding setters to parts like `Float3.XY` and fixing a few bugs, almost all improvements in this update are related to matricies. First of all, I've added a bunch of new items to the `IMatrix` interface. Now, any deriving matrix has more requirements that fit the regular `Matrix` type. I don't know why one would use the `IMatrix` interface rather than a specific matrix type, but now the options are more sophisticated. -It works for any common function you'd want to apply to an equation. +I've added some new stuff to all the matrix types, including row operations. You can now scale a row, add a row to another, and swap two rows. If I become aware of any more commonly-used row operations, I'll add then in a `2.4.2` update. But I think I've got all the good ones. There is also a mutable version of each operation which, rather than returning a new matrix with changes made, instead applies the changes to itself. ---- +Did you know I made two seperate blunders in the `Cofactor()` method? For the `Matrix2x2` version of the `Cofactor()` method, I had the diagonal elements swapped. Whoops. For the `Matrix` version of the `Cofactor()` method, matricies with even column count would break because of the alternating sign pattern I was using. Now, as far as I know, that bug is fixed. -Speaking of math functions (I guess that's the whole update, given the name), I'm now utilizing an implementation of CORDIC I made to calculate all trigonometric functions, hyperbolic trig functions, exponents, and logs. It's quite a neat process, and I could also have it completely wrong, but whatever I have, CORDIC or something else, works wonders, and is considerably faster than some other methods I tried. Of course, it's still nowhere near as fast as the built-in math functions, but they will always be on a whole other level. Maybe in the optimization update I'll bother to improve them, but they work quite well as-is for most use cases. +The last thing I did was add the ability to turn a matrix into its equivalent row-echelon form. This is applicable only to the `Matrix` type (the dynamic one), and works with some levels of success. It's a little weird and tends to give results with lots of negative zeroes, but overall it's fine, I think. As far as I know there aren't any obvious bugs. We'll see though. -I've also implemented taylor series into the `Calculus` class. It's not particularly useful is most cases, so I wouldn't bother. The only time it would be a good idea to use it is if you've got some incredibly slow to calculate equation and want to optimize it. The function will take a long time at first, as it will have to generate second-derivatives and beyond, but afterwards the output equation will just be a simple-to-calculate polynomial (though the approximation gets worse the further you are from the reference point). If you've got an equation that's already fast to calculate, using the taylor series approximation will only be a negative. So take it with a grain of salt. - -That's mostly it. I've fixed some issues/bugs here and there, renamed some small stuff (check the full changelog for more information), removed all the stuff marked obsolete and to be removed in this update, added some more math stuff like prime calculators, and other tiny changes. The next update will be focused on reworking the badly made geometry stuff I did a while back ([Version 2.1](https://github.com/That-One-Nerd/Nerd_STF/releases/tag/v2.1.0)). I know I say this all the time, but version 2.5 should be substantially bigger than this one. I'm going to be reworking the `Polygon` object entirely and will be improving quite a lot of other things in the whole `Nerd_STF.Mathematics.Geometry` namespace. Stay tuned! +Anyway, that's everything in this update. Again, pretty small, but meaningful nonetheless. Unless I haven't screwed anything up, the next update I work on will be `2.5`, so I'll see you then! Here's the full changelog: ``` * Nerd_STF - * Exceptions - + BadMethodException - * Extensions - * ConversionExtension - - ToDictionary(IEnumerable>) - * EquationExtension - + ValidNumberTypes - + Absolute(Equation) - + AbsoluteMod(Equation, float) - + ArcCos(Equation) - + ArcCot(Equation) - + ArcCsc(Equation) - + ArcSec(Equation) - + ArcSin(Equation) - + ArcTan(Equation) - + ArcCosh(Equation) - + ArcCoth(Equation) - + ArcCsch(Equation) - + ArcSech(Equation) - + ArcSinh(Equation) - + ArcTanh(Equation) - + Average(Equation, float, float, float) - + Average(Equation, Equation, Equation, float) - + Binomial(Equation, int, float) - + Binomial(Equation, Equation, Equation) - + Cbrt(Equation) - + Ceiling(Equation) - + Clamp(Equation, float, float) - + Clamp(Equation, Equation, Equation) - + Combinations(Equation, int) - + Combinations(Equation, Equation) - + Cos(Equation) - + Cosh(Equation) - + Cot(Equation) - + Coth(Equation) - + Csc(Equation) - + Csch(Equation) - + Divide(Equation, float[]) - + Divide(Equation, Equation[]) - + Factorial(Equation) - + Floor(Equation) - + GetDerivative(Equation, float) - + GetDerivativeAtPoint(Equation, float, float) - + GetIntegral(Equation, float, float, float) - + GetDynamicIntegral(Equation, Equation, Equation, float) - + GetTaylorSeries(Equation, float, int, float) - + GetValues(Equation, float, float, float) - + GradientDescent(Equation, float, float, int, float) - + InverseSqrt(Equation) - + Log(Equation, float) - + Max(Equation, float, float, float) - + Min(Equation, float, float, float) - + Permutations(Equation, int) - + Permutations(Equation, Equation) - + Power(Equation, float) - + Power(Equation, Equation) - + Product(Equation, float[]) - + Product(Equation, Equation[]) - + Root(Equation, float) - + Root(Equation, Equation) - + Round(Equation) - + Sec(Equation) - + Sech(Equation) - + Sin(Equation) - + Sinh(Equation) - + SolveBisection(Equation, float, float, float, float, int) - + SolveEquation(Equation, float, float, float, int) - + SolveNewton(Equation, float, float, float, int) - + Sqrt(Equation) - + Subtract(Equation, float[]) - + Subtract(Equation, Equation[]) - + Sum(Equation, float[]) - + Sum(Equation, Equation[]) - + Tan(Equation) - + Tanh(Equation) - + ZScore(Equation, float[]) - + ZScore(Equation, Equation[]) - + ZScore(Equation, float, float) - + ZScore(Equation, Equation, Equation) - + InvokeMethod(Equation, MethodInfo, object?[]?) - + InvokeMathMethod(Equation, string, object?[]?) - + StringExtension - + Helpers - + CordicHelper - + MathfHelper - + RationalHelper - + UnsafeHelper * Mathematics * Abstract - = Renamed `IPresets1D` to `IPresets1d` - = Renamed `IPresets2D` to `IPresets2d` - = Renamed `IPresets3D` to `IPresets3d` - = Renamed `IPresets4D` to `IPresets4d` - = Renamed `IShape2D` to `IShape2d` - = Renamed `IShape3D` to `IShape3d` + * IMatrix + + AddRow(int, int, float) + + AddRowMutable(int, int, float) + + Cofactor() + + GetColumn(int) + + GetRow(int) + + ScaleRow(int, float) + + ScaleRowMutable(int, float) + + SetColumn(int, float[]) + + SetRow(int, float[]) + + Size + + SwapRows(int, int) + + SwapRowsMutable(int, int) + + this[int, int] + + this[Index, Index] + * Algebra + * Matrix + + AddRow(int, int, float) + + AddRowMutable(int, int, float) + + ScaleRow(int, float) + + ScaleRowMutable(int, float) + + SwapRows(int, int) + + SwapRowsMutable(int, int) + = Fixed a blunder in `SignGrid(Int2)` with signs being incorrectly placed on matrixes with even column count. + * Matrix2x2 + + AddRow(int, int, float) + + AddRowMutable(int, int, float) + + GetColumn(int) + + GetRow(int) + + ScaleRow(int, float) + + ScaleRowMutable(int, float) + + SetColumn(int, float[]) + + SetRow(int, float[]) + + Size + + SwapRows(int, int) + + SwapRowsMutable(int, int) + = Fixed a blunder in `Cofactor()` with the position of elements. + * Matrix3x3 + + AddRow(int, int, float) + + AddRowMutable(int, int, float) + + GetColumn(int) + + GetRow(int) + + ScaleRow(int, float) + + ScaleRowMutable(int, float) + + SetColumn(int, float[]) + + SetRow(int, float[]) + + Size + + SwapRows(int, int) + + SwapRowsMutable(int, int) + * Matrix4x4 + + AddRow(int, int, float) + + AddRowMutable(int, int, float) + + GetColumn(int) + + GetRow(int) + + ScaleRow(int, float) + + ScaleRowMutable(int, float) + + SetColumn(int, float[]) + + SetRow(int, float[]) + + Size + + SwapRows(int, int) + + SwapRowsMutable(int, int) * NumberSystems * Complex - - operator >(Complex, Complex) - - operator <(Complex, Complex) - - operator >=(Complex, Complex) - - operator <=(Complex, Complex) + + operator Complex(SystemComplex) + + operator SystemComplex(Complex) * Quaternion - - Far - - Near - - operator >(Complex, Complex) - - operator <(Complex, Complex) - - operator >=(Complex, Complex) - - operator <=(Complex, Complex) - * Samples - + Fills - * Equations - + FlatLine - + XLine - = Simplified `CosWave` - = Simplified `SinWave` - = Replaced a `readonly` term with a generating field in `CosWave` - = Replaced a `readonly` term with a generating field in `SinWave` - = Replaced a `readonly` term with a generating field in `SawWave` - = Replaced a `readonly` term with a generating field in `SquareWave` - = Replaced a `readonly` term with a generating field in `SgnFill` - = Moved `SgnFill` to `Fills` and renamed it to `SignFill` - * Calculus - + GetTaylorSeries(Equation, float, int, float) - = Fixed a blunder in `GetDerivativeAtPoint(Equation, float, float)` - = Renamed the `stepCount` parameter in `GradientDescent(Equation, float, float, float, float)` to "iterations" and changed its type from `float` to `int` - * Float2 - - Removed the `Obsolete` attribute from `CompareTo(Float2)` - - operator >(Float2, Float2) - - operator <(Float2, Float2) - - operator >=(Float2, Float2) - - operator <=(Float2, Float2) + + operator Quaternion(SystemQuaternion) + + operator SystemQuaternion(Quaternion) * Float3 - - Removed the `Obsolete` attribute from `CompareTo(Float3)` - - operator >(Float3, Float3) - - operator <(Float3, Float3) - - operator >=(Float3, Float3) - - operator <=(Float3, Float3) + = Added a setter to `XY` + = Added a setter to `XZ` + = Added a setter to `YZ` * Float4 - - Far - - Near - - Removed the `Obsolete` attribute from `CompareTo(Float4)` - - operator >(Float4, Float4) - - operator <(Float4, Float4) - - operator >=(Float4, Float4) - - operator <=(Float4, Float4) - * Int2 - - Removed the `Obsolete` attribute from `CompareTo(Int2)` - - operator >(Int2, Int2) - - operator <(Int2, Int2) - - operator >=(Int2, Int2) - - operator <=(Int2, Int2) + = Added a setter to `XW` + = Added a setter to `XY` + = Added a setter to `XZ` + = Added a setter to `YW` + = Added a setter to `YZ` + = Added a setter to `ZW` + = Added a setter to `XYW` + = Added a setter to `XYZ` + = Added a setter to `XZW` + = Added a setter to `YZW` * Int3 - - Removed the `Obsolete` attribute from `CompareTo(Int3)` - - operator >(Int3, Int3) - - operator <(Int3, Int3) - - operator >=(Int3, Int3) - - operator <=(Int3, Int3) + = Added a setter to `XY` + = Added a setter to `XZ` + = Added a setter to `YZ` * Int4 - - Far - - Deep - - Removed the `Obsolete` attribute from `CompareTo(Int4)` - - operator >(Int4, Int4) - - operator <(Int4, Int4) - - operator >=(Int4, Int4) - - operator <=(Int4, Int4) - * Mathf - + ArcCosh(float) - + ArcCoth(float) - + ArcCsch(float) - + ArcSech(float) - + ArcSinh(float) - + ArcTanh(float) - + ArcTanh2(float, float) - + Cbrt(float) - + Cosh(float) - + Coth(float) - + Csch(float) - + IsPrime(int, PrimeCheckMethod) - + Lerp(float, float, Equation, bool) - + Lerp(Equation, Equation, float, bool) - + Lerp(Equation, Equation, Equation, bool) - + Log(float, float) - + PrimeFactors(int) - + PowerMod(long, long, long) - + Sech(float) - + Sinh(float) - + SharedItems(T[][]) - + SolveBisection(Equation, float, float, float, float, int) - + SolveEquation(Equation, float, float, float, int) - + SolveNewton(Equation, float, float, float, int) - + Tanh(float) - = Improved the `Sqrt(float)` method by using a solution finder - = The `ArcSin(float)` method now uses a solution finder rather than the base math library - = The `Power(float, float)` method now utilizes a custom CORDIC implementation rather than the base math library - + PrimeCheckMethod - + Equation2d - + Rational - + SimplificationMethod - * Miscellaneous - * AssemblyConfig - - using System.Reflection - * GlobalUsings - + global using Nerd_STF.Helpers - + global using System.Reflection - - Foreach(object) - - Foreach(T) - = Moved `IEncapsulator` to `Nerd_STF.Mathematics.Abstract` and renamed it to `IEncapsulate` - = Renamed `Fill2D` to `Fill2d` - = Renamed `IGroup2D` to `IGroup2d` - = Renamed `Modifier2D` to `IModifier2d` - = Renamed `Modifier2D` to `IModifier2d` - = Renamed `Modifier2D` to `IModifier2d` -= Made `Nerd_STF` allow unsafe code blocks + = Added a setter to `XW` + = Added a setter to `XY` + = Added a setter to `XZ` + = Added a setter to `YW` + = Added a setter to `YZ` + = Added a setter to `ZW` + = Added a setter to `XYW` + = Added a setter to `XYZ` + = Added a setter to `XZW` + = Added a setter to `YZW` ``` diff --git a/Nerd_STF/Mathematics/Abstract/IMatrix.cs b/Nerd_STF/Mathematics/Abstract/IMatrix.cs index 84429c2..f664310 100644 --- a/Nerd_STF/Mathematics/Abstract/IMatrix.cs +++ b/Nerd_STF/Mathematics/Abstract/IMatrix.cs @@ -5,10 +5,29 @@ public interface IMatrix : IAbsolute, ICeiling, IClamp, IDivide, ISubtract, ISum where T : IMatrix { + public Int2 Size { get; } + + public float this[int r, int c] { get; set; } + public float this[Index r, Index c] { get; set; } + public T Adjugate(); + public T Cofactor(); public float Determinant(); public T? Inverse(); public T Transpose(); + + public float[] GetColumn(int column); + public float[] GetRow(int row); + + public void SetColumn(int column, float[] value); + public void SetRow(int row, float[] values); + + public T AddRow(int rowToChange, int referenceRow, float factor = 1); + public void AddRowMutable(int rowToChange, int referenceRow, float factor = 1); + public T ScaleRow(int rowIndex, float value); + public void ScaleRowMutable(int rowIndex, float value); + public T SwapRows(int rowA, int rowB); + public void SwapRowsMutable(int rowA, int rowB); } public interface IMatrix : IMatrix where This : IMatrix diff --git a/Nerd_STF/Mathematics/Abstract/IPresets0d.cs b/Nerd_STF/Mathematics/Abstract/IPresets0d.cs new file mode 100644 index 0000000..1da7e1b --- /dev/null +++ b/Nerd_STF/Mathematics/Abstract/IPresets0d.cs @@ -0,0 +1,6 @@ +namespace Nerd_STF.Mathematics.Abstract; + +public interface IPresets0d where T : IPresets0d +{ + public static abstract T Unit { get; } +} diff --git a/Nerd_STF/Mathematics/Abstract/IStaticMatrix.cs b/Nerd_STF/Mathematics/Abstract/IStaticMatrix.cs index e8287b9..ea3dfd3 100644 --- a/Nerd_STF/Mathematics/Abstract/IStaticMatrix.cs +++ b/Nerd_STF/Mathematics/Abstract/IStaticMatrix.cs @@ -3,5 +3,5 @@ public interface IStaticMatrix : IAverage, IMatrix, IMedian, IMatrixPresets where T : IStaticMatrix { - + } diff --git a/Nerd_STF/Mathematics/Algebra/Matrix.cs b/Nerd_STF/Mathematics/Algebra/Matrix.cs index 3d13abc..f97ad07 100644 --- a/Nerd_STF/Mathematics/Algebra/Matrix.cs +++ b/Nerd_STF/Mathematics/Algebra/Matrix.cs @@ -1,6 +1,4 @@ -using System.Buffers; - -namespace Nerd_STF.Mathematics.Algebra; +namespace Nerd_STF.Mathematics.Algebra; public class Matrix : IMatrix { @@ -20,7 +18,12 @@ public class Matrix : IMatrix return m; } public static Matrix One(Int2 size) => new(size, 1); - public static Matrix SignGrid(Int2 size) => new(size, Fills.SignFill); + public static Matrix SignGrid(Int2 size) => new(size, delegate (int x) + { + float sgnValue = Fills.SignFill(x); + if (size.y % 2 == 0 && x / size.y % 2 == 1) sgnValue *= -1; + return sgnValue; + }); public static Matrix Zero(Int2 size) => new(size); public bool HasMinors => Size.x > 1 && Size.y > 1; @@ -271,6 +274,70 @@ public class Matrix : IMatrix }); } + public Matrix AddRow(int rowToChange, int referenceRow, float factor = 1) + { + Matrix @this = this; + return new(Size, delegate (int r, int c) + { + if (r == rowToChange) return @this[r, c] += factor * @this[referenceRow, c]; + else return @this[r, c]; + }); + } + public void AddRowMutable(int rowToChange, int referenceRow, float factor) + { + for (int c = 0; c < Size.y; c++) this[rowToChange, c] += this[referenceRow, c] * factor; + } + public Matrix ScaleRow(int rowIndex, float factor) + { + Matrix @this = this; + return new(Size, delegate (int r, int c) + { + if (r == rowIndex) return @this[r, c] * factor; + else return @this[r, c]; + }); + } + public void ScaleRowMutable(int rowIndex, float factor) + { + for (int c = 0; c < Size.y; c++) this[rowIndex, c] *= factor; + } + public Matrix SwapRows(int rowA, int rowB) + { + Matrix @this = this; + return new(Size, delegate (int r, int c) + { + if (r == rowA) return @this[rowB, c]; + else if (r == rowB) return @this[rowA, c]; + else return @this[r, c]; + }); + } + public void SwapRowsMutable(int rowA, int rowB) + { + float[] dataA = GetRow(rowA), dataB = GetRow(rowB); + SetRow(rowA, dataB); + SetRow(rowB, dataA); + } + + public Matrix SolveRowEchelon() + { + Matrix result = (Matrix)MemberwiseClone(); + + // Scale the first row so the first element of that row is 1. + result.ScaleRowMutable(0, 1 / result[0, 0]); + + // For each row afterwards, subtract the required amount from all rows before it and normalize. + for (int r1 = 1; r1 < result.Size.x; r1++) + { + int min = Mathf.Min(r1, result.Size.y); + for (int r2 = 0; r2 < min; r2++) + { + result.AddRowMutable(r1, r2, -result[r1, r2]); + } + result.ScaleRowMutable(r1, 1 / result[r1, r1]); + } + + return result; + } + public override bool Equals([NotNullWhen(true)] object? obj) { if (obj == null) return base.Equals(obj); diff --git a/Nerd_STF/Mathematics/Algebra/Matrix2x2.cs b/Nerd_STF/Mathematics/Algebra/Matrix2x2.cs index 620b6a2..165ecf3 100644 --- a/Nerd_STF/Mathematics/Algebra/Matrix2x2.cs +++ b/Nerd_STF/Mathematics/Algebra/Matrix2x2.cs @@ -52,6 +52,8 @@ public record class Matrix2x2 : IStaticMatrix } } + public Int2 Size => (2, 2); + public float r1c1, r2c1, r1c2, r2c2; public Matrix2x2(float all) : this(all, all, all, all) { } @@ -223,8 +225,8 @@ public record class Matrix2x2 : IStaticMatrix { Matrix2x2 swapped = new(new[,] { - { r2c2, r1c2 }, - { r2c1, r1c1 } + { r2c2, r2c1 }, + { r1c2, r1c1 } }); return swapped ^ SignGrid; } @@ -241,6 +243,69 @@ public record class Matrix2x2 : IStaticMatrix { r1c2, r2c2 } }); + public float[] GetColumn(int column) => new[] { this[0, column], this[1, column] }; + public float[] GetRow(int row) => new[] { this[row, 0], this[row, 1] }; + + public void SetColumn(int column, float[] vals) + { + if (vals.Length < 2) + throw new InvalidSizeException("Array must contain enough values to fill the column."); + this[0, column] = vals[0]; + this[1, column] = vals[1]; + } + public void SetRow(int row, float[] vals) + { + if (vals.Length < 2) + throw new InvalidSizeException("Array must contain enough values to fill the row."); + this[row, 0] = vals[0]; + this[row, 1] = vals[1]; + } + + public Matrix2x2 AddRow(int rowToChange, int referenceRow, float factor = 1) + { + Matrix2x2 @this = this; + return new(delegate (int r, int c) + { + if (r == rowToChange) return @this[r, c] += factor * @this[referenceRow, c]; + else return @this[r, c]; + }); + } + public void AddRowMutable(int rowToChange, int referenceRow, float factor) + { + this[rowToChange, 0] += this[referenceRow, 0] * factor; + this[rowToChange, 1] += this[referenceRow, 1] * factor; + } + public Matrix2x2 ScaleRow(int rowIndex, float factor) + { + Matrix2x2 @this = this; + return new(delegate (int r, int c) + { + if (r == rowIndex) return @this[r, c] * factor; + else return @this[r, c]; + }); + } + public void ScaleRowMutable(int rowIndex, float factor) + { + this[rowIndex, 0] *= factor; + this[rowIndex, 1] *= factor; + } + public Matrix2x2 SwapRows(int rowA, int rowB) + { + Matrix2x2 @this = this; + return new(delegate (int r, int c) + { + if (r == rowA) return @this[rowB, c]; + else if (r == rowB) return @this[rowA, c]; + else return @this[r, c]; + }); + } + public void SwapRowsMutable(int rowA, int rowB) + { + float[] dataA = GetRow(rowA), dataB = GetRow(rowB); + SetRow(rowA, dataB); + SetRow(rowB, dataA); + } + public virtual bool Equals(Matrix2x2? other) { if (other is null) return false; diff --git a/Nerd_STF/Mathematics/Algebra/Matrix3x3.cs b/Nerd_STF/Mathematics/Algebra/Matrix3x3.cs index d028038..79c4ebb 100644 --- a/Nerd_STF/Mathematics/Algebra/Matrix3x3.cs +++ b/Nerd_STF/Mathematics/Algebra/Matrix3x3.cs @@ -78,6 +78,8 @@ public record class Matrix3x3 : IStaticMatrix } } + public Int2 Size => (3, 3); + public float r1c1, r2c1, r3c1, r1c2, r2c2, r3c2, r1c3, r2c3, r3c3; public Matrix3x3(float all) : this(all, all, all, all, all, all, all, all, all) { } @@ -336,6 +338,75 @@ public record class Matrix3x3 : IStaticMatrix { r1c3, r2c3, r3c3 } }); + public float[] GetColumn(int column) => new[] { this[0, column], this[1, column], this[2, column] }; + public float[] GetRow(int row) => new[] { this[row, 0], this[row, 1], this[row, 2] }; + + public void SetColumn(int column, float[] vals) + { + if (vals.Length < 3) + throw new InvalidSizeException("Array must contain enough values to fill the column."); + + this[0, column] = vals[0]; + this[1, column] = vals[1]; + this[2, column] = vals[2]; + } + public void SetRow(int row, float[] vals) + { + if (vals.Length < 3) + throw new InvalidSizeException("Array must contain enough values to fill the row."); + + this[row, 0] = vals[0]; + this[row, 1] = vals[1]; + this[row, 2] = vals[2]; + } + + public Matrix3x3 AddRow(int rowToChange, int referenceRow, float factor = 1) + { + Matrix3x3 @this = this; + return new(delegate (int r, int c) + { + if (r == rowToChange) return @this[r, c] += factor * @this[referenceRow, c]; + else return @this[r, c]; + }); + } + public void AddRowMutable(int rowToChange, int referenceRow, float factor) + { + this[rowToChange, 0] += this[referenceRow, 0] * factor; + this[rowToChange, 1] += this[referenceRow, 1] * factor; + this[rowToChange, 2] += this[referenceRow, 2] * factor; + } + public Matrix3x3 ScaleRow(int rowIndex, float factor) + { + Matrix3x3 @this = this; + return new(delegate (int r, int c) + { + if (r == rowIndex) return @this[r, c] * factor; + else return @this[r, c]; + }); + } + public void ScaleRowMutable(int rowIndex, float factor) + { + this[rowIndex, 0] *= factor; + this[rowIndex, 1] *= factor; + this[rowIndex, 2] *= factor; + } + public Matrix3x3 SwapRows(int rowA, int rowB) + { + Matrix3x3 @this = this; + return new(delegate (int r, int c) + { + if (r == rowA) return @this[rowB, c]; + else if (r == rowB) return @this[rowA, c]; + else return @this[r, c]; + }); + } + public void SwapRowsMutable(int rowA, int rowB) + { + float[] dataA = GetRow(rowA), dataB = GetRow(rowB); + SetRow(rowA, dataB); + SetRow(rowB, dataA); + } + public virtual bool Equals(Matrix3x3? other) { if (other is null) return false; diff --git a/Nerd_STF/Mathematics/Algebra/Matrix4x4.cs b/Nerd_STF/Mathematics/Algebra/Matrix4x4.cs index 1297034..dc35b12 100644 --- a/Nerd_STF/Mathematics/Algebra/Matrix4x4.cs +++ b/Nerd_STF/Mathematics/Algebra/Matrix4x4.cs @@ -1,4 +1,6 @@ -namespace Nerd_STF.Mathematics.Algebra; +using System.Data.Common; + +namespace Nerd_STF.Mathematics.Algebra; public record class Matrix4x4 : IStaticMatrix { @@ -108,6 +110,8 @@ public record class Matrix4x4 : IStaticMatrix } } + public Int2 Size => (4, 4); + public float r1c1, r2c1, r3c1, r4c1, r1c2, r2c2, r3c2, r4c2, r1c3, r2c3, r3c3, r4c3, r1c4, r2c4, r3c4, r4c4; public Matrix4x4(float all) : this(all, all, all, all, all, @@ -451,6 +455,81 @@ public record class Matrix4x4 : IStaticMatrix { r1c4, r2c4, r3c4, r4c4 } }); + public float[] GetColumn(int column) => + new[] { this[0, column], this[1, column], this[2, column], this[3, column] }; + public float[] GetRow(int row) => + new[] { this[row, 0], this[row, 1], this[row, 2], this[row, 3] }; + + public void SetColumn(int column, float[] vals) + { + if (vals.Length < 4) + throw new InvalidSizeException("Array must contain enough values to fill the column."); + + this[0, column] = vals[0]; + this[1, column] = vals[1]; + this[2, column] = vals[2]; + this[3, column] = vals[3]; + } + public void SetRow(int row, float[] vals) + { + if (vals.Length < 4) + throw new InvalidSizeException("Array must contain enough values to fill the row."); + + this[row, 0] = vals[0]; + this[row, 1] = vals[1]; + this[row, 2] = vals[2]; + this[row, 3] = vals[3]; + } + + public Matrix4x4 AddRow(int rowToChange, int referenceRow, float factor = 1) + { + Matrix4x4 @this = this; + return new(delegate (int r, int c) + { + if (r == rowToChange) return @this[r, c] += factor * @this[referenceRow, c]; + else return @this[r, c]; + }); + } + public void AddRowMutable(int rowToChange, int referenceRow, float factor) + { + this[rowToChange, 0] += this[referenceRow, 0] * factor; + this[rowToChange, 1] += this[referenceRow, 1] * factor; + this[rowToChange, 2] += this[referenceRow, 2] * factor; + this[rowToChange, 3] += this[referenceRow, 3] * factor; + } + public Matrix4x4 ScaleRow(int rowIndex, float factor) + { + Matrix4x4 @this = this; + return new(delegate (int r, int c) + { + if (r == rowIndex) return @this[r, c] * factor; + else return @this[r, c]; + }); + } + public void ScaleRowMutable(int rowIndex, float factor) + { + this[rowIndex, 0] *= factor; + this[rowIndex, 1] *= factor; + this[rowIndex, 2] *= factor; + this[rowIndex, 3] *= factor; + } + public Matrix4x4 SwapRows(int rowA, int rowB) + { + Matrix4x4 @this = this; + return new(delegate (int r, int c) + { + if (r == rowA) return @this[rowB, c]; + else if (r == rowB) return @this[rowA, c]; + else return @this[r, c]; + }); + } + public void SwapRowsMutable(int rowA, int rowB) + { + float[] dataA = GetRow(rowA), dataB = GetRow(rowB); + SetRow(rowA, dataB); + SetRow(rowB, dataA); + } + public virtual bool Equals(Matrix4x4? other) { if (other is null) return false; diff --git a/Nerd_STF/Mathematics/Float3.cs b/Nerd_STF/Mathematics/Float3.cs index c31186c..96c5d40 100644 --- a/Nerd_STF/Mathematics/Float3.cs +++ b/Nerd_STF/Mathematics/Float3.cs @@ -23,9 +23,33 @@ public record struct Float3 : IAbsolute, IAverage, public float Magnitude => Mathf.Sqrt(x * x + y * y + z * z); public Float3 Normalized => this * Mathf.InverseSqrt(x * x + y * y + z * z); - public Float2 XY => new(x, y); - public Float2 XZ => new(x, z); - public Float2 YZ => new(y, z); + public Float2 XY + { + get => (x, y); + set + { + x = value.x; + y = value.y; + } + } + public Float2 XZ + { + get => (x, z); + set + { + x = value.x; + z = value.y; + } + } + public Float2 YZ + { + get => (y, z); + set + { + y = value.x; + z = value.y; + } + } public float x, y, z; diff --git a/Nerd_STF/Mathematics/Float4.cs b/Nerd_STF/Mathematics/Float4.cs index 2c0a0ae..7a67bc5 100644 --- a/Nerd_STF/Mathematics/Float4.cs +++ b/Nerd_STF/Mathematics/Float4.cs @@ -24,17 +24,101 @@ public record struct Float4 : IAbsolute, public float Magnitude => Mathf.Sqrt(x * x + y * y + z * z + w * w); public Float4 Normalized => this * Mathf.InverseSqrt(x * x + y * y + z * z + w * w); - public Float2 XY => new(x, y); - public Float2 XZ => new(x, z); - public Float2 XW => new(x, w); - public Float2 YW => new(y, w); - public Float2 YZ => new(y, z); - public Float2 ZW => new(z, w); + public Float2 XW + { + get => (x, w); + set + { + x = value.x; + w = value.y; + } + } + public Float2 XY + { + get => (x, y); + set + { + x = value.x; + y = value.y; + } + } + public Float2 XZ + { + get => (x, z); + set + { + x = value.x; + z = value.y; + } + } + public Float2 YW + { + get => (y, w); + set + { + y = value.x; + w = value.y; + } + } + public Float2 YZ + { + get => (y, z); + set + { + y = value.x; + z = value.y; + } + } + public Float2 ZW + { + get => (z, w); + set + { + z = value.x; + w = value.y; + } + } - public Float3 XYW => new(x, y, w); - public Float3 XYZ => new(x, y, z); - public Float3 YZW => new(y, z, w); - public Float3 XZW => new(x, z, w); + public Float3 XYW + { + get => (x, y, w); + set + { + x = value.x; + y = value.y; + w = value.z; + } + } + public Float3 XYZ + { + get => (x, y, z); + set + { + x = value.x; + y = value.y; + z = value.z; + } + } + public Float3 XZW + { + get => (x, z, w); + set + { + x = value.x; + z = value.y; + w = value.z; + } + } + public Float3 YZW + { + get => (y, z, w); + set + { + y = value.x; + z = value.y; + w = value.z; + } + } public float x, y, z, w; diff --git a/Nerd_STF/Mathematics/Int3.cs b/Nerd_STF/Mathematics/Int3.cs index 22385a7..72705c1 100644 --- a/Nerd_STF/Mathematics/Int3.cs +++ b/Nerd_STF/Mathematics/Int3.cs @@ -21,9 +21,33 @@ public record struct Int3 : IAbsolute, IAverage, IClamp, IClam public float Magnitude => Mathf.Sqrt(x * x + y * y + z * z); public Int3 Normalized => (Int3)((Float3)this * Mathf.InverseSqrt(x * x + y * y + z * z)); - public Int2 XY => new(x, y); - public Int2 XZ => new(x, z); - public Int2 YZ => new(y, z); + public Int2 XY + { + get => (x, y); + set + { + x = value.x; + y = value.y; + } + } + public Int2 XZ + { + get => (x, z); + set + { + x = value.x; + z = value.y; + } + } + public Int2 YZ + { + get => (y, z); + set + { + y = value.x; + z = value.y; + } + } public int x, y, z; diff --git a/Nerd_STF/Mathematics/Int4.cs b/Nerd_STF/Mathematics/Int4.cs index f125c86..d2fbaab 100644 --- a/Nerd_STF/Mathematics/Int4.cs +++ b/Nerd_STF/Mathematics/Int4.cs @@ -21,17 +21,101 @@ public record struct Int4 : IAbsolute, IAverage, IClamp, IClam public float Magnitude => Mathf.Sqrt(x * x + y * y + z * z + w * w); public Int4 Normalized => (Int4)((Float4)this * Mathf.InverseSqrt(x * x + y * y + z * z + w * w)); - public Int2 XY => new(x, y); - public Int2 XZ => new(x, z); - public Int2 XW => new(x, w); - public Int2 YW => new(y, w); - public Int2 YZ => new(y, z); - public Int2 ZW => new(z, w); + public Int2 XW + { + get => (x, w); + set + { + x = value.x; + w = value.y; + } + } + public Int2 XY + { + get => (x, y); + set + { + x = value.x; + y = value.y; + } + } + public Int2 XZ + { + get => (x, z); + set + { + x = value.x; + z = value.y; + } + } + public Int2 YW + { + get => (y, w); + set + { + y = value.x; + w = value.y; + } + } + public Int2 YZ + { + get => (y, z); + set + { + y = value.x; + z = value.y; + } + } + public Int2 ZW + { + get => (z, w); + set + { + z = value.x; + w = value.y; + } + } - public Int3 XYW => new(x, y, w); - public Int3 XYZ => new(x, y, z); - public Int3 YZW => new(y, z, w); - public Int3 XZW => new(x, z, w); + public Int3 XYW + { + get => (x, y, w); + set + { + x = value.x; + y = value.y; + w = value.z; + } + } + public Int3 XYZ + { + get => (x, y, z); + set + { + x = value.x; + y = value.y; + z = value.z; + } + } + public Int3 XZW + { + get => (x, z, w); + set + { + x = value.x; + z = value.y; + w = value.z; + } + } + public Int3 YZW + { + get => (y, z, w); + set + { + y = value.x; + z = value.y; + w = value.z; + } + } public int x, y, z, w; diff --git a/Nerd_STF/Mathematics/NumberSystems/Complex.cs b/Nerd_STF/Mathematics/NumberSystems/Complex.cs index 6727473..5f1da3e 100644 --- a/Nerd_STF/Mathematics/NumberSystems/Complex.cs +++ b/Nerd_STF/Mathematics/NumberSystems/Complex.cs @@ -1,4 +1,6 @@ -namespace Nerd_STF.Mathematics.NumberSystems; +using SystemComplex = System.Numerics.Complex; + +namespace Nerd_STF.Mathematics.NumberSystems; public record struct Complex(float u, float i) : IAbsolute, IAverage, ICeiling, IClampMagnitude, IComparable, IDivide, IDot, @@ -204,4 +206,7 @@ public record struct Complex(float u, float i) : IAbsolute, IAverage fill) => new(fill); public static implicit operator Complex(Fill fill) => new(fill); public static implicit operator Complex((float u, float i) val) => new(val.u, val.i); + + public static implicit operator Complex(SystemComplex val) => ((float)val.Real, (float)val.Imaginary); + public static implicit operator SystemComplex(Complex val) => new(val.u, val.i); } diff --git a/Nerd_STF/Mathematics/NumberSystems/Quaternion.cs b/Nerd_STF/Mathematics/NumberSystems/Quaternion.cs index 9dd6991..06f5eb7 100644 --- a/Nerd_STF/Mathematics/NumberSystems/Quaternion.cs +++ b/Nerd_STF/Mathematics/NumberSystems/Quaternion.cs @@ -1,4 +1,6 @@ -namespace Nerd_STF.Mathematics.NumberSystems; +using SystemQuaternion = System.Numerics.Quaternion; + +namespace Nerd_STF.Mathematics.NumberSystems; public record struct Quaternion(float u, float i, float j, float k) : IAbsolute, IAverage, ICeiling, IClamp, IClampMagnitude, IComparable, @@ -332,4 +334,7 @@ public record struct Quaternion(float u, float i, float j, float k) : IAbsolute< public static implicit operator Quaternion(Fill fill) => new(fill); public static implicit operator Quaternion((float u, float i, float j, float k) val) => new(val.u, val.i, val.j, val.k); + + public static implicit operator Quaternion(SystemQuaternion val) => (val.X, val.Y, val.Z, val.W); + public static implicit operator SystemQuaternion(Quaternion val) => new(val.u, val.i, val.j, val.k); } diff --git a/Nerd_STF/Nerd_STF.csproj b/Nerd_STF/Nerd_STF.csproj index ee59f67..5bb893b 100644 --- a/Nerd_STF/Nerd_STF.csproj +++ b/Nerd_STF/Nerd_STF.csproj @@ -12,35 +12,24 @@ Copyright (c) 2023 That_One_Nerd README.md https://github.com/That-One-Nerd/Nerd_STF - 2.4.0 + 2.4.1 c#;csharp;c sharp;math;mathematics;mathametics;maths;color;rgb;rgba;cmyk;cmyka;hsv;hsva;calculus;linear algebra;linalg;linearalgebra;matrix;matrix2x2;matrix 2x2;matrix3x3;matrix 3x3;matrix4x4;matrix 4x4;matrix multiplication;vector;vector2d;vector3d;vector2;vector3;float2;float3;float4;int2;int3;int4;angle;geometry;vert;line;polygon;triangle;quadrilateral;sphere;circle;number system;numbersystem;complex numbers;complex;2d numbers;2dnumbers;quaternions;4d numbers;4dnumbers - 2.4.0 + 2.4.1 Nerd_STF Nerd_STF MIT Logo Square.png - I've done a pretty good amount of stuff in this update, and I'm pretty proud of it. Good improvement. + Hey everyone! This is one of the larger small updates, and I'm pretty proud of what I got done in a week. -First of all, I've gone and added all of the applicable `Mathf` functions as an extension to the `Equation` delegate. That way if you want to say, calculate the square root of an entire equation, rather than going: -```csharp -Equation result = x => Mathf.Sqrt(equ(x)); -// Where `equ` is an `Equation` that represents the function we want to take the square root of. -``` -you can shorten it down and remove some of the weirdness. -```csharp -Equation result = equ.Sqrt(); -// Where `equ` is an `Equation` that represents the function we want to take the square root of. -``` +Along with adding setters to parts like `Float3.XY` and fixing a few bugs, almost all improvements in this update are related to matricies. First of all, I've added a bunch of new items to the `IMatrix` interface. Now, any deriving matrix has more requirements that fit the regular `Matrix` type. I don't know why one would use the `IMatrix` interface rather than a specific matrix type, but now the options are more sophisticated. -It works for any common function you'd want to apply to an equation. +I've added some new stuff to all the matrix types, including row operations. You can now scale a row, add a row to another, and swap two rows. If I become aware of any more commonly-used row operations, I'll add then in a `2.4.2` update. But I think I've got all the good ones. There is also a mutable version of each operation which, rather than returning a new matrix with changes made, instead applies the changes to itself. ---- +Did you know I made two seperate blunders in the `Cofactor()` method? For the `Matrix2x2` version of the `Cofactor()` method, I had the diagonal elements swapped. Whoops. For the `Matrix` version of the `Cofactor()` method, matricies with even column count would break because of the alternating sign pattern I was using. Now, as far as I know, that bug is fixed. -Speaking of math functions (I guess that's the whole update, given the name), I'm now utilizing an implementation of CORDIC I made to calculate all trigonometric functions, hyperbolic trig functions, exponents, and logs. It's quite a neat process, and I could also have it completely wrong, but whatever I have, CORDIC or something else, works wonders, and is considerably faster than some other methods I tried. Of course, it's still nowhere near as fast as the built-in math functions, but they will always be on a whole other level. Maybe in the optimization update I'll bother to improve them, but they work quite well as-is for most use cases. +The last thing I did was add the ability to turn a matrix into its equivalent row-echelon form. This is applicable only to the `Matrix` type (the dynamic one), and works with some levels of success. It's a little weird and tends to give results with lots of negative zeroes, but overall it's fine, I think. As far as I know there aren't any obvious bugs. We'll see though. -I've also implemented taylor series into the `Calculus` class. It's not particularly useful is most cases, so I wouldn't bother. The only time it would be a good idea to use it is if you've got some incredibly slow to calculate equation and want to optimize it. The function will take a long time at first, as it will have to generate second-derivatives and beyond, but afterwards the output equation will just be a simple-to-calculate polynomial (though the approximation gets worse the further you are from the reference point). If you've got an equation that's already fast to calculate, using the taylor series approximation will only be a negative. So take it with a grain of salt. - -That's mostly it. I've fixed some issues/bugs here and there, renamed some small stuff (check the full changelog for more information), removed all the stuff marked obsolete and to be removed in this update, added some more math stuff like prime calculators, and other tiny changes. The next update will be focused on reworking the badly made geometry stuff I did a while back ([Version 2.1](https://github.com/That-One-Nerd/Nerd_STF/releases/tag/v2.1.0)). I know I say this all the time, but version 2.5 should be substantially bigger than this one. I'm going to be reworking the `Polygon` object entirely and will be improving quite a lot of other things in the whole `Nerd_STF.Mathematics.Geometry` namespace. Stay tuned! +Anyway, that's everything in this update. Again, pretty small, but meaningful nonetheless. Unless I haven't screwed anything up, the next update I work on will be `2.5`, so I'll see you then! https://github.com/That-One-Nerd/Nerd_STF False False