the beginning of 2.4

This commit is contained in:
That_One_Nerd 2023-04-02 12:27:42 -04:00
parent 6d44cbe438
commit 85dad1f7f5
52 changed files with 897 additions and 1299 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,18 @@
namespace Nerd_STF.Exceptions;
[Serializable]
public class BadMethodException : Nerd_STFException
{
public MethodInfo? MethodInfo;
public BadMethodException() : base("The method or delegate provided is invalid for this operation.") { }
public BadMethodException(string message) : base(message) { }
public BadMethodException(Exception inner) : base("The method or delegate provided is invalid for this operation.", inner) { }
public BadMethodException(MethodInfo method) : this() => MethodInfo = method;
public BadMethodException(MethodInfo method, Exception inner) : this(inner) => MethodInfo = method;
public BadMethodException(string message, Exception inner) : base(message, inner) { }
public BadMethodException(string message, MethodInfo method) : this(message) => MethodInfo = method;
public BadMethodException(string message, MethodInfo method, Exception inner) : this(message, inner) => MethodInfo = method;
protected BadMethodException(SerializationInfo info, StreamingContext context) : base(info, context) { }
}

View File

@ -2,18 +2,7 @@
public static class ConversionExtension
{
[Obsolete("This extension turns out to already exist as a constructor in the " +
"System.Collections.Generic.Dictionary<TKey, TValue> type. This will be removed in v2.4.0")]
public static Dictionary<TKey, TValue> ToDictionary<TKey, TValue>
(this IEnumerable<KeyValuePair<TKey, TValue>> pairs)
where TKey : notnull
{
Dictionary<TKey, TValue> res = new();
foreach (KeyValuePair<TKey, TValue> pair in pairs) res.Add(pair.Key, pair.Value);
return res;
}
public static Fill<T> ToFill<T>(this T[] arr) => i => arr[i];
public static Fill<T> ToFill<T>(this T[,] arr, Int2? size) => arr.Flatten(size).ToFill();
public static Fill2D<T> ToFill2D<T>(this T[,] arr) => (x, y) => arr[x, y];
public static Fill2d<T> ToFill2D<T>(this T[,] arr) => (x, y) => arr[x, y];
}

View File

@ -2,6 +2,264 @@
public static class EquationExtension
{
private static readonly List<Type> ValidNumberTypes = new()
{
typeof(byte),
typeof(sbyte),
typeof(short),
typeof(ushort),
typeof(int),
typeof(uint),
typeof(long),
typeof(ulong),
typeof(float),
typeof(double),
typeof(decimal)
};
public static Equation Absolute(this Equation equ) => x => Mathf.Absolute(equ(x));
public static Equation AbsoluteMod(this Equation equ, float mod) => x => Mathf.AbsoluteMod(equ(x), mod);
public static Equation ArcCos(this Equation equ) => x => Mathf.ArcCos(equ(x)).Radians;
public static Equation ArcCot(this Equation equ) => x => Mathf.ArcCot(equ(x)).Radians;
public static Equation ArcCsc(this Equation equ) => x => Mathf.ArcCsc(equ(x)).Radians;
public static Equation ArcSec(this Equation equ) => x => Mathf.ArcSec(equ(x)).Radians;
public static Equation ArcSin(this Equation equ) => x => Mathf.ArcSin(equ(x)).Radians;
public static Equation ArcTan(this Equation equ) => x => Mathf.ArcTan(equ(x)).Radians;
public static float Average(this Equation equ, float min, float max, float step = Calculus.DefaultStep) =>
Mathf.Average(equ, min, max, step);
public static Equation Average(this Equation equ, Equation min, Equation max, float step = Calculus.DefaultStep) =>
x => Mathf.Average(equ, min(x), max(x), step);
public static Equation Binomial(this Equation equ, int total, float successRate) =>
x => Mathf.Binomial((int)equ(x), total, successRate);
public static Equation Binomial(this Equation equ, Equation total, Equation successRate) =>
x => Mathf.Binomial((int)equ(x), (int)total(x), successRate(x));
public static Equation Cbrt(this Equation equ) => x => Mathf.Cbrt(equ(x));
public static Equation Ceiling(this Equation equ) => x => Mathf.Ceiling(equ(x));
public static Equation Clamp(this Equation equ, float min, float max) => x => Mathf.Clamp(equ(x), min, max);
public static Equation Clamp(this Equation equ, Equation min, Equation max) =>
x => Mathf.Clamp(equ(x), min(x), max(x));
public static Equation Combinations(this Equation equ, int size) =>
x => Mathf.Combinations(size, (int)equ(x));
public static Equation Combinations(this Equation equ, Equation size) =>
x => Mathf.Combinations((int)size(x), (int)equ(x));
public static Equation Cos(this Equation equ) => x => Mathf.Cos(equ(x));
public static Equation Cot(this Equation equ) => x => Mathf.Cot(equ(x));
public static Equation Csc(this Equation equ) => x => Mathf.Csc(equ(x));
public static Equation Divide(this Equation equ, params float[] dividends) =>
x => Mathf.Divide(equ(x), dividends);
public static Equation Divide(this Equation equ, params Equation[] dividends) => delegate (float x)
{
float[] dividendsAtValue = new float[dividends.Length];
for (int i = 0; i < dividends.Length; i++) dividendsAtValue[i] = dividends[i](x);
return Mathf.Divide(equ(x), dividendsAtValue);
};
public static Equation Factorial(this Equation equ) => x => Mathf.Factorial((int)equ(x));
public static Equation Floor(this Equation equ) => x => Mathf.Floor(equ(x));
public static Dictionary<float, float> GetValues(this Equation equ, float min, float max,
float step = Calculus.DefaultStep) => Mathf.GetValues(equ, min, max, step);
public static Equation InverseSqrt(this Equation equ) => x => Mathf.InverseSqrt(equ(x));
public static float Max(this Equation equ, float min, float max, float step = Calculus.DefaultStep) =>
Mathf.Max(equ, min, max, step);
public static float Min(this Equation equ, float min, float max, float step = Calculus.DefaultStep) =>
Mathf.Min(equ, min, max, step);
public static Equation Permutations(this Equation equ, int size) =>
x => Mathf.Permutations(size, (int)equ(x));
public static Equation Permutations(this Equation equ, Equation size) =>
x => Mathf.Permutations((int)size(x), (int)equ(x));
public static Equation Power(this Equation equ, float pow) => x => Mathf.Power(equ(x), pow);
public static Equation Power(this Equation equ, Equation pow) => x => Mathf.Power(equ(x), pow(x));
public static Equation Product(this Equation equ, params float[] vals) => delegate (float x)
{
float[] valsAtValue = new float[vals.Length + 1];
valsAtValue[0] = equ(x);
for (int i = 0; i < vals.Length; i++) valsAtValue[i + 1] = vals[i];
return Mathf.Product(valsAtValue);
};
public static Equation Product(this Equation equ, params Equation[] vals) => delegate (float x)
{
float[] valsAtValue = new float[vals.Length + 1];
valsAtValue[0] = equ(x);
for (int i = 0; i < vals.Length; i++) valsAtValue[i + 1] = vals[i](x);
return Mathf.Product(valsAtValue);
};
public static Equation Root(this Equation equ, float index) => x => Mathf.Root(equ(x), index);
public static Equation Root(this Equation equ, Equation index) => x => Mathf.Root(equ(x), index(x));
public static Equation Round(this Equation equ) => x => Mathf.Round(equ(x));
public static Equation Sec(this Equation equ) => x => Mathf.Sec(equ(x));
public static Equation Sin(this Equation equ) => x => Mathf.Sin(equ(x));
public static float SolveBisection(this Equation equ, float initialA, float initialB, float tolerance = 1e-5f,
int maxIterations = 1000) =>
Mathf.SolveBisection(equ, initialA, initialB, tolerance, maxIterations);
public static float SolveEquation(this Equation equ, float initial, float tolerance = 1e-5f,
float step = Calculus.DefaultStep, int maxIterations = 1000) =>
Mathf.SolveEquation(equ, initial, tolerance, step, maxIterations);
public static float SolveNewton(this Equation equ, float initial, float tolerance = 1e-5f,
float step = Calculus.DefaultStep, int maxIterations = 1000) =>
Mathf.SolveNewton(equ, initial, tolerance, step, maxIterations);
public static Equation Sqrt(this Equation equ) => x => Mathf.Sqrt(equ(x));
public static Equation Subtract(this Equation equ, params float[] vals) =>
x => Mathf.Subtract(equ(x), vals);
public static Equation Subtract(this Equation equ, params Equation[] vals) => delegate (float x)
{
float[] valsAtValue = new float[vals.Length];
for (int i = 0; i < vals.Length; i++) valsAtValue[i] = vals[i](x);
return Mathf.Subtract(equ(x), valsAtValue);
};
public static Equation Sum(this Equation equ, params float[] vals) => delegate (float x)
{
float[] valsAtValue = new float[vals.Length + 1];
valsAtValue[0] = equ(x);
for (int i = 0; i < vals.Length; i++) valsAtValue[i + 1] = vals[i];
return Mathf.Sum(valsAtValue);
};
public static Equation Sum(this Equation equ, params Equation[] vals) => delegate (float x)
{
float[] valsAtValue = new float[vals.Length + 1];
valsAtValue[0] = equ(x);
for (int i = 0; i < vals.Length; i++) valsAtValue[i + 1] = vals[i](x);
return Mathf.Sum(valsAtValue);
};
public static Equation Tan(this Equation equ) => x => Mathf.Tan(equ(x));
public static Equation ZScore(this Equation equ, params float[] vals) => x => Mathf.ZScore(equ(x), vals);
public static Equation ZScore(this Equation equ, params Equation[] vals) => delegate (float x)
{
float[] valsAtValue = new float[vals.Length];
for (int i = 0; i < vals.Length; i++) valsAtValue[i] = vals[i](x);
return Mathf.ZScore(equ(x), valsAtValue);
};
public static Equation ZScore(this Equation equ, float mean, float stdev) =>
x => Mathf.ZScore(equ(x), mean, stdev);
public static Equation ZScore(this Equation equ, Equation mean, Equation stdev) =>
x => Mathf.ZScore(equ(x), mean(x), stdev(x));
public static Equation InvokeMethod(this Equation equ, MethodInfo method, params object?[]? args)
{
// Determine if this method is a valid method. This exception will be thrown if this method
// shouldn't be invoked this way. Might be able to be handled a bit better, but it works.
Exception throwIfBad = new BadMethodException("This method cannot be invoked in the context of an " +
nameof(Equation), method);
// Basic method property check.
if (method.IsAbstract || method.IsConstructor || method.IsGenericMethod || !method.IsPublic)
throw throwIfBad;
// Check if a valid number of arguments is provided and the first one takes a number.
ParameterInfo[] paramTypes = method.GetParameters();
int requiredParams = 0;
while (requiredParams < paramTypes.Length && !paramTypes[requiredParams].IsOptional) requiredParams++;
args ??= Array.Empty<object>();
if (args.Length + 1 < requiredParams || args.Length > paramTypes.Length) throw throwIfBad;
if (paramTypes.Length < 1) throw throwIfBad;
if (!ValidNumberTypes.Contains(paramTypes[0].ParameterType)) throw throwIfBad;
// Check if the return type is also a number.
if (!ValidNumberTypes.Contains(method.ReturnType)) throw throwIfBad;
// This is a good method. Generate the arguments required using the equation and invoke it.
// The first item in this list will be the float value of the equation.
List<object?> invokeArgs = new() { 0 };
invokeArgs.AddRange(args);
return delegate (float x)
{
// Invoke the method (with some casting of course).
invokeArgs[0] = Convert.ChangeType(equ(x), method.ReturnType);
object? result = method.Invoke(null, invokeArgs.ToArray());
if (result is null) throw new UndefinedException($"Invoked method \"{method.Name}\" returned null " +
"for this input.");
return (float)Convert.ChangeType(result, typeof(float));
};
}
public static Equation InvokeMathMethod(this Equation equ, string name, params object?[]? args)
{
// Check a couple math classes to see if the method is found. If at least one is found,
// compare the parameters and return type to what is expected. If more than one perfect
// match exists, the first one will be selected.
args ??= Array.Empty<object>();
Type[] toCheck = { typeof(Mathf), typeof(Math) }; // This is the order methods should be searched in.
foreach (Type t in toCheck)
{
// Basic property and return checks.
List<MethodInfo> possibleMethods = (from m in t.GetMethods()
let basicCheck = !m.IsAbstract && !m.IsConstructor &&
!m.IsGenericMethod && m.IsPublic
let nameCheck = m.Name == name
let returnCheck = ValidNumberTypes.Contains(m.ReturnType)
where basicCheck && nameCheck && returnCheck
select m).ToList();
if (possibleMethods.Count < 1) continue;
foreach (MethodInfo m in possibleMethods)
{
// Check if a valid number of arguments is provided and the first one takes a number.
ParameterInfo[] paramTypes = m.GetParameters();
int requiredParams = 0;
while (requiredParams < paramTypes.Length && !paramTypes[requiredParams].IsOptional) requiredParams++;
args ??= Array.Empty<object>();
if (args.Length + 1 < requiredParams || args.Length > paramTypes.Length) continue;
if (paramTypes.Length < 1) continue;
if (!ValidNumberTypes.Contains(paramTypes[0].ParameterType)) continue;
// This is a good method. Generate the arguments required using the equation and invoke it.
// The first item in this list will be the float value of the equation.
List<object?> invokeArgs = new() { 0 };
invokeArgs.AddRange(args);
return delegate (float x)
{
// Invoke the method (with some casting of course).
invokeArgs[0] = Convert.ChangeType(equ(x), m.ReturnType);
object? result = m.Invoke(null, invokeArgs.ToArray());
if (result is null) throw new UndefinedException($"Invoked method \"{m.Name}\" returned " +
"null for this input.");
return (float)Convert.ChangeType(result, typeof(float));
};
}
}
throw new BadMethodException("No method that fits this criteria found in the math types.");
}
public static Equation Scale(this Equation equ, float value, ScaleType type = ScaleType.Both) => type switch
{
ScaleType.X => x => equ(x / value),

View File

@ -1,3 +1,3 @@
namespace Nerd_STF;
public delegate T Fill2D<T>(int indexX, int indexY);
public delegate T Fill2d<T>(int indexX, int indexY);

View File

@ -1,6 +0,0 @@
namespace Nerd_STF;
[Obsolete("This delegate is kind of useless and will be removed in Nerd_STF v2.4.0.")]
public delegate void Foreach(object item);
[Obsolete("This delegate is kind of useless and will be removed in Nerd_STF v2.4.0.")]
public delegate void Foreach<T>(T item);

View File

@ -29,7 +29,7 @@ public class Image : ICloneable, IEnumerable<IColor>, IEquatable<Image>
Size = new(width, height);
for (int y = 0; y < height; y++) for (int x = 0; x < width; x++) Pixels[x, y] = fill(y * width + x);
}
public Image(int width, int height, Fill2D<IColor> fill)
public Image(int width, int height, Fill2d<IColor> fill)
{
Pixels = new IColor[width, height];
Size = new(width, height);
@ -39,7 +39,7 @@ public class Image : ICloneable, IEnumerable<IColor>, IEquatable<Image>
public Image(Int2 size, IColor[] cols) : this(size.x, size.y, cols) { }
public Image(Int2 size, IColor[,] cols) : this(size.x, size.y, cols) { }
public Image(Int2 size, Fill<IColor> fill) : this(size.x, size.y, fill) { }
public Image(Int2 size, Fill2D<IColor> fill) : this(size.x, size.y, fill) { }
public Image(Int2 size, Fill2d<IColor> fill) : this(size.x, size.y, fill) { }
public IColor this[int indexX, int indexY]
{

View File

@ -0,0 +1,10 @@
namespace Nerd_STF.Helpers;
internal static class MathfHelper
{
public static bool IsPrimeClassic(int num)
{
for (int i = 2; i <= num / 2; i++) if (num % i == 0) return false;
return true;
}
}

View File

@ -0,0 +1,46 @@
namespace Nerd_STF.Helpers;
internal static class RationalHelper
{
public static Rational SimplifyAuto(float value)
{
string valueStr = value.ToString();
int pointIndex = valueStr.IndexOf(".");
if (pointIndex < 0) return new((int)value, 1);
int raise = valueStr.Substring(pointIndex + 1).Length;
int den = Mathf.Power(10, raise);
return new((int)(value * den), den);
}
public static Rational SimplifyFarey(float value, float tolerance, int maxIters)
{
float remainder = value % 1;
if (remainder == 0) return new((int)value, 1);
int additional = (int)(value - remainder);
Rational min = Rational.Zero, max = Rational.One;
Rational result;
float resultValue;
int iters = 0;
do
{
result = new(min.Numerator + max.Numerator, min.Denominator + max.Denominator, false);
resultValue = result.GetValue();
if (remainder == resultValue) break;
else if (remainder > resultValue) min = result;
else if (remainder < resultValue) max = result;
iters++;
if (maxIters != -1 && iters > maxIters) break;
}
while (Mathf.Absolute(resultValue - value) > tolerance);
result.Numerator += additional * result.Denominator;
return result;
}
}

View File

@ -1,6 +0,0 @@
namespace Nerd_STF;
public interface IEncapsulator<T, TE> : IContains<TE> where T : IEquatable<T> where TE : IEquatable<TE>
{
public T Encapsulate(TE val);
}

View File

@ -1,7 +1,7 @@
namespace Nerd_STF;
public interface IGroup2D<T> : IGroup<T>
public interface IGroup2d<T> : IGroup<T>
{
public T[,] ToArray2D();
public Fill2D<T> ToFill2D();
public Fill2d<T> ToFill2D();
}

View File

@ -0,0 +1,6 @@
namespace Nerd_STF.Mathematics.Abstract;
public interface IEncapsulate<T, TE> : IContains<TE> where T : IEquatable<T> where TE : IEquatable<TE>
{
public T Encapsulate(TE val);
}

View File

@ -1,7 +1,7 @@
namespace Nerd_STF.Mathematics.Abstract;
public interface IMatrix<T> : IAbsolute<T>, ICeiling<T>, IClamp<T>, IDivide<T>,
IEquatable<T>, IFloor<T>, IGroup2D<float>, ILerp<T, float>, IProduct<T>, IRound<T>,
IEquatable<T>, IFloor<T>, IGroup2d<float>, ILerp<T, float>, IProduct<T>, IRound<T>,
ISubtract<T>, ISum<T>
where T : IMatrix<T>
{

View File

@ -1,6 +1,6 @@
namespace Nerd_STF.Mathematics.Abstract;
public interface IPresets1D<T> where T : IPresets1D<T>
public interface IPresets1d<T> where T : IPresets1d<T>
{
public static abstract T One { get; }
public static abstract T Zero { get; }

View File

@ -1,6 +1,6 @@
namespace Nerd_STF.Mathematics.Abstract;
public interface IPresets2D<T> : IPresets1D<T> where T : IPresets2D<T>
public interface IPresets2d<T> : IPresets1d<T> where T : IPresets2d<T>
{
public static abstract T Down { get; }
public static abstract T Left { get; }

View File

@ -1,6 +1,6 @@
namespace Nerd_STF.Mathematics.Abstract;
public interface IPresets3D<T> : IPresets2D<T> where T : IPresets3D<T>
public interface IPresets3d<T> : IPresets2d<T> where T : IPresets3d<T>
{
public static abstract T Back { get; }
public static abstract T Forward { get; }

View File

@ -1,6 +1,6 @@
namespace Nerd_STF.Mathematics.Abstract;
public interface IPresets4D<T> : IPresets2D<T>, IPresets3D<T> where T : IPresets4D<T>
public interface IPresets4d<T> : IPresets2d<T>, IPresets3d<T> where T : IPresets4d<T>
{
public static abstract T HighW { get; }
public static abstract T LowW { get; }

View File

@ -2,7 +2,7 @@
namespace Nerd_STF.Mathematics.Abstract;
public interface IShape2D<TNumber> where TNumber : INumber<TNumber>
public interface IShape2d<TNumber> where TNumber : INumber<TNumber>
{
public TNumber Area { get; }
public TNumber Perimeter { get; }

View File

@ -2,7 +2,7 @@
namespace Nerd_STF.Mathematics.Abstract;
public interface IShape3D<TNumber> where TNumber : INumber<TNumber>
public interface IShape3d<TNumber> where TNumber : INumber<TNumber>
{
public TNumber SurfaceArea { get; }
public TNumber Volume { get; }

View File

@ -20,7 +20,7 @@ public class Matrix : IMatrix<Matrix, Matrix>
return m;
}
public static Matrix One(Int2 size) => new(size, 1);
public static Matrix SignGrid(Int2 size) => new(size, Equations.SgnFill);
public static Matrix SignGrid(Int2 size) => new(size, Fills.SignFill);
public static Matrix Zero(Int2 size) => new(size);
public bool HasMinors => Size.x > 1 && Size.y > 1;
@ -85,13 +85,13 @@ public class Matrix : IMatrix<Matrix, Matrix>
array = new float[size.x, size.y];
for (int r = 0; r < size.x; r++) for (int c = 0; c < size.y; c++) array[r, c] = vals[r, c];
}
public Matrix(Int2 size, Fill2D<float> vals)
public Matrix(Int2 size, Fill2d<float> vals)
{
Size = size;
array = new float[size.x, size.y];
for (int r = 0; r < size.x; r++) for (int c = 0; c < size.y; c++) array[r, c] = vals(r, c);
}
public Matrix(Int2 size, Fill2D<int> vals)
public Matrix(Int2 size, Fill2d<int> vals)
{
Size = size;
array = new float[size.x, size.y];
@ -188,7 +188,7 @@ public class Matrix : IMatrix<Matrix, Matrix>
return val;
}
public void Apply(Modifier2D modifier)
public void Apply(Modifier2d modifier)
{
for (int r = 0; r < Size.x; r++) for (int c = 0; c < Size.y; c++)
array[r, c] = modifier(new(r, c), array[r, c]);
@ -310,7 +310,7 @@ public class Matrix : IMatrix<Matrix, Matrix>
public float[] ToArray() => array.Flatten(new(Size.y, Size.x));
public float[,] ToArray2D() => array;
public Fill<float> ToFill() => ToFillExtension.ToFill(this);
public Fill2D<float> ToFill2D()
public Fill2d<float> ToFill2D()
{
Matrix @this = this;
return (x, y) => @this[x, y];

View File

@ -68,8 +68,8 @@ public record class Matrix2x2 : IStaticMatrix<Matrix2x2>
public Matrix2x2(Fill<int> fill) : this(fill(0), fill(1), fill(2), fill(3)) { }
public Matrix2x2(float[,] nums) : this(nums[0, 0], nums[0, 1], nums[1, 0], nums[1, 1]) { }
public Matrix2x2(int[,] nums) : this(nums[0, 0], nums[0, 1], nums[1, 0], nums[1, 1]) { }
public Matrix2x2(Fill2D<float> fill) : this(fill(0, 0), fill(0, 1), fill(1, 0), fill(1, 1)) { }
public Matrix2x2(Fill2D<int> fill) : this(fill(0, 0), fill(0, 1), fill(1, 0), fill(1, 1)) { }
public Matrix2x2(Fill2d<float> fill) : this(fill(0, 0), fill(0, 1), fill(1, 0), fill(1, 1)) { }
public Matrix2x2(Fill2d<int> fill) : this(fill(0, 0), fill(0, 1), fill(1, 0), fill(1, 1)) { }
public Matrix2x2(Float2 r1, Float2 r2) : this(r1.x, r1.y, r2.x, r2.y) { }
public Matrix2x2(Fill<Float2> fill) : this(fill(0), fill(1)) { }
public Matrix2x2(Fill<Int2> fill) : this((IEnumerable<int>)fill(0), fill(1)) { }
@ -265,7 +265,7 @@ public record class Matrix2x2 : IStaticMatrix<Matrix2x2>
{ r2c1, r2c2 }
};
public Fill<float> ToFill() => ToFillExtension.ToFill(this);
public Fill2D<float> ToFill2D()
public Fill2d<float> ToFill2D()
{
Matrix2x2 @this = this;
return (x, y) => @this[x, y];

View File

@ -106,9 +106,9 @@ public record class Matrix3x3 : IStaticMatrix<Matrix3x3>
nums[1, 0], nums[1, 1], nums[1, 2], nums[2, 0], nums[2, 1], nums[2, 2]) { }
public Matrix3x3(int[,] nums) : this(nums[0, 0], nums[0, 1], nums[0, 2],
nums[1, 0], nums[1, 1], nums[1, 2], nums[2, 0], nums[2, 1], nums[2, 2]) { }
public Matrix3x3(Fill2D<float> fill) : this(fill(0, 0), fill(0, 1), fill(0, 2),
public Matrix3x3(Fill2d<float> fill) : this(fill(0, 0), fill(0, 1), fill(0, 2),
fill(1, 0), fill(1, 1), fill(1, 2), fill(2, 0), fill(2, 1), fill(2, 2)) { }
public Matrix3x3(Fill2D<int> fill) : this(fill(0, 0), fill(0, 1), fill(0, 2),
public Matrix3x3(Fill2d<int> fill) : this(fill(0, 0), fill(0, 1), fill(0, 2),
fill(1, 0), fill(1, 1), fill(1, 2), fill(2, 0), fill(2, 1), fill(2, 2)) { }
public Matrix3x3(Float3 r1, Float3 r2, Float3 r3) : this(r1.x, r1.y, r1.z, r2.x, r2.y, r2.z, r3.x, r3.y, r3.z) { }
public Matrix3x3(Fill<Float3> fill) : this(fill(0), fill(1), fill(2)) { }
@ -371,7 +371,7 @@ public record class Matrix3x3 : IStaticMatrix<Matrix3x3>
{ r3c1, r3c2, r3c3 }
};
public Fill<float> ToFill() => ToFillExtension.ToFill(this);
public Fill2D<float> ToFill2D()
public Fill2d<float> ToFill2D()
{
Matrix3x3 @this = this;
return (x, y) => @this[x, y];

View File

@ -146,10 +146,10 @@ public record class Matrix4x4 : IStaticMatrix<Matrix4x4>
public Matrix4x4(int[,] nums) : this(nums[0, 0], nums[0, 1], nums[0, 2], nums[0, 3], nums[1, 0],
nums[1, 1], nums[1, 2], nums[1, 3], nums[2, 0], nums[2, 1], nums[2, 2], nums[2, 3], nums[3, 0],
nums[3, 1], nums[3, 2], nums[3, 3]) { }
public Matrix4x4(Fill2D<float> fill) : this(fill(0, 0), fill(0, 1), fill(0, 2), fill(0, 3), fill(1, 0),
public Matrix4x4(Fill2d<float> fill) : this(fill(0, 0), fill(0, 1), fill(0, 2), fill(0, 3), fill(1, 0),
fill(1, 1), fill(1, 2), fill(1, 3), fill(2, 0), fill(2, 1), fill(2, 2), fill(2, 3), fill(3, 0),
fill(3, 1), fill(3, 2), fill(3, 3)) { }
public Matrix4x4(Fill2D<int> fill) : this(fill(0, 0), fill(0, 1), fill(0, 2), fill(0, 3), fill(1, 0),
public Matrix4x4(Fill2d<int> fill) : this(fill(0, 0), fill(0, 1), fill(0, 2), fill(0, 3), fill(1, 0),
fill(1, 1), fill(1, 2), fill(1, 3), fill(2, 0), fill(2, 1), fill(2, 2), fill(2, 3), fill(3, 0),
fill(3, 1), fill(3, 2), fill(3, 3)) { }
public Matrix4x4(Float4 r1, Float4 r2, Float4 r3, Float4 r4) : this(r1.x, r1.y, r1.z,
@ -502,7 +502,7 @@ public record class Matrix4x4 : IStaticMatrix<Matrix4x4>
{ r4c1, r4c2, r4c3, r4c4 }
};
public Fill<float> ToFill() => ToFillExtension.ToFill(this);
public Fill2D<float> ToFill2D()
public Fill2d<float> ToFill2D()
{
Matrix4x4 @this = this;
return (x, y) => @this[x, y];

View File

@ -4,7 +4,7 @@ public record struct Vector2d : IAbsolute<Vector2d>, IAverage<Vector2d>,
IClampMagnitude<Vector2d, float>, IComparable<Vector2d>, ICross<Vector2d, Vector3d>,
IDot<Vector2d, float>, IEquatable<Vector2d>, IFromTuple<Vector2d, (Angle angle, float mag)>,
ILerp<Vector2d, float>, IMax<Vector2d>, IMagnitude<float>, IMedian<Vector2d>, IMin<Vector2d>,
IPresets2D<Vector2d>, ISplittable<Vector2d, (Angle[] rots, float[] mags)>, ISubtract<Vector2d>,
IPresets2d<Vector2d>, ISplittable<Vector2d, (Angle[] rots, float[] mags)>, ISubtract<Vector2d>,
ISum<Vector2d>
{
public static Vector2d Down => new(Angle.Down);

View File

@ -4,7 +4,7 @@ public record struct Vector3d : IAbsolute<Vector3d>, IAverage<Vector3d>, IClampM
IComparable<Vector3d>, ICross<Vector3d>, IDot<Vector3d, float>, IEquatable<Vector3d>,
IFromTuple<Vector3d, (Angle yaw, Angle pitch, float mag)>, IIndexAll<Angle>, IIndexRangeAll<Angle>,
ILerp<Vector3d, float>, IMagnitude<float>, IMax<Vector3d>, IMedian<Vector3d>, IMin<Vector3d>,
IPresets3D<Vector3d>, ISubtract<Vector3d>, ISum<Vector3d>
IPresets3d<Vector3d>, ISubtract<Vector3d>, ISum<Vector3d>
{
public static Vector3d Back => new(Angle.Zero, Angle.Up);
public static Vector3d Down => new(Angle.Down, Angle.Zero);

View File

@ -2,7 +2,7 @@
public struct Angle : IAbsolute<Angle>, IAverage<Angle>, IClamp<Angle>, ICloneable,
IComparable<Angle>, IEquatable<Angle>, ILerp<Angle, float>, IMax<Angle>, IMedian<Angle>,
IMin<Angle>, IPresets2D<Angle>
IMin<Angle>, IPresets2d<Angle>
{
public static Angle Down => new(270);
public static Angle Left => new(180);

View File

@ -1,4 +1,6 @@
namespace Nerd_STF.Mathematics;
using System.Linq.Expressions;
namespace Nerd_STF.Mathematics;
public static class Calculus
{
@ -7,7 +9,7 @@ public static class Calculus
public static Equation GetDerivative(Equation equ, float step = DefaultStep) =>
x => GetDerivativeAtPoint(equ, x, step);
public static float GetDerivativeAtPoint(Equation equ, float x, float step = DefaultStep) =>
(equ(x + DefaultStep) - equ(x)) / step;
(equ(x + step) - equ(x)) / step;
public static float GetIntegral(Equation equ, float lowerBound, float upperBound, float step = DefaultStep)
{

View File

@ -0,0 +1,3 @@
namespace Nerd_STF.Mathematics;
public delegate Complex Equation2d(Complex input);

View File

@ -5,7 +5,7 @@ public record struct Float2 : IAbsolute<Float2>, IAverage<Float2>, ICeiling<Floa
ICross<Float2, Float3>, IDivide<Float2>, IDot<Float2, float>, IEquatable<Float2>,
IFloor<Float2, Int2>, IFromTuple<Float2, (float x, float y)>, IGroup<float>,
ILerp<Float2, float>, IMathOperators<Float2>, IMax<Float2>, IMedian<Float2>, IMin<Float2>,
IIndexAll<float>, IIndexRangeAll<float>, IPresets2D<Float2>, IProduct<Float2>, IRound<Float2, Int2>,
IIndexAll<float>, IIndexRangeAll<float>, IPresets2d<Float2>, IProduct<Float2>, IRound<Float2, Int2>,
ISplittable<Float2, (float[] Xs, float[] Ys)>, ISubtract<Float2>, ISum<Float2>
{
public static Float2 Down => new(0, -1);
@ -162,8 +162,6 @@ public record struct Float2 : IAbsolute<Float2>, IAverage<Float2>, ICeiling<Floa
return (Xs, Ys);
}
[Obsolete("This method is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public int CompareTo(Float2 other) => Magnitude.CompareTo(other.Magnitude);
public bool Equals(Float2 other) => x == other.x && y == other.y;
public override int GetHashCode() => base.GetHashCode();
@ -204,18 +202,6 @@ public record struct Float2 : IAbsolute<Float2>, IAverage<Float2>, ICeiling<Floa
public static Float2 operator /(Float2 a, Float2 b) => new(a.x / b.x, a.y / b.y);
public static Float2 operator /(Float2 a, float b) => new(a.x / b, a.y / b);
public static Float2 operator /(Float2 a, Matrix b) => (Float2)((Matrix)a / b);
[Obsolete("This operator is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public static bool operator >(Float2 a, Float2 b) => a.CompareTo(b) > 0;
[Obsolete("This operator is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public static bool operator <(Float2 a, Float2 b) => a.CompareTo(b) < 0;
[Obsolete("This operator is a bit ambiguous (and misleading at times). " +
"You should instead compare " + nameof(Magnitude) + "s directly.")]
public static bool operator >=(Float2 a, Float2 b) => a == b || a > b;
[Obsolete("This operator is a bit ambiguous (and misleading at times). " +
"You should instead compare " + nameof(Magnitude) + "s directly.")]
public static bool operator <=(Float2 a, Float2 b) => a == b || a < b;
public static implicit operator Float2(Complex val) => new(val.u, val.i);
public static explicit operator Float2(Quaternion val) => new(val.u, val.i);

View File

@ -7,7 +7,7 @@ public record struct Float3 : IAbsolute<Float3>, IAverage<Float3>,
ICross<Float3>, IDivide<Float3>, IDot<Float3, float>, IEquatable<Float3>,
IFloor<Float3, Int3>, IFromTuple<Float3, (float x, float y, float z)>, IGroup<float>,
IIndexAll<float>, IIndexRangeAll<float>, ILerp<Float3, float>, IMathOperators<Float3>, IMax<Float3>,
IMedian<Float3>, IMin<Float3>, IPresets3D<Float3>, IProduct<Float3>, IRound<Float3, Int3>,
IMedian<Float3>, IMin<Float3>, IPresets3d<Float3>, IProduct<Float3>, IRound<Float3, Int3>,
ISplittable<Float3, (float[] Xs, float[] Ys, float[] Zs)>, ISubtract<Float3>, ISum<Float3>
{
public static Float3 Back => new(0, 0, -1);
@ -186,8 +186,6 @@ public record struct Float3 : IAbsolute<Float3>, IAverage<Float3>,
return (Xs, Ys, Zs);
}
[Obsolete("This method is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public int CompareTo(Float3 other) => Magnitude.CompareTo(other.Magnitude);
public bool Equals(Float3 other) => x == other.x && y == other.y && z == other.z;
public override int GetHashCode() => base.GetHashCode();
@ -238,18 +236,6 @@ public record struct Float3 : IAbsolute<Float3>, IAverage<Float3>,
public static Float3 operator /(Float3 a, Float3 b) => new(a.x / b.x, a.y / b.y, a.z / b.z);
public static Float3 operator /(Float3 a, float b) => new(a.x / b, a.y / b, a.z / b);
public static Float3 operator /(Float3 a, Matrix b) => (Float3)((Matrix)a / b);
[Obsolete("This operator is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public static bool operator >(Float3 a, Float3 b) => a.CompareTo(b) > 0;
[Obsolete("This operator is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public static bool operator <(Float3 a, Float3 b) => a.CompareTo(b) < 0;
[Obsolete("This operator is a bit ambiguous (and misleading at times). " +
"You should instead compare " + nameof(Magnitude) + "s directly.")]
public static bool operator >=(Float3 a, Float3 b) => a == b || a > b;
[Obsolete("This operator is a bit ambiguous (and misleading at times). " +
"You should instead compare " + nameof(Magnitude) + "s directly.")]
public static bool operator <=(Float3 a, Float3 b) => a == b || a < b;
public static implicit operator Float3(Complex val) => new(val.u, val.i, 0);
public static explicit operator Float3(Quaternion val) => new(val.u, val.i, val.j);

View File

@ -5,22 +5,16 @@ public record struct Float4 : IAbsolute<Float4>,
IComparable<Float4>, IDivide<Float4>, IDot<Float4, float>, IEquatable<Float4>,
IFloor<Float4, Int4>, IFromTuple<Float4, (float x, float y, float z, float w)>,
IGroup<float>, IIndexAll<float>, IIndexRangeAll<float>, ILerp<Float4, float>, IMathOperators<Float4>,
IMax<Float4>, IMedian<Float4>, IMin<Float4>, IPresets4D<Float4>, IProduct<Float4>, IRound<Float4, Int4>,
IMax<Float4>, IMedian<Float4>, IMin<Float4>, IPresets4d<Float4>, IProduct<Float4>, IRound<Float4, Int4>,
ISplittable<Float4, (float[] Xs, float[] Ys, float[] Zs, float[] Ws)>, ISubtract<Float4>,
ISum<Float4>
{
public static Float4 Back => new(0, 0, -1, 0);
public static Float4 Down => new(0, -1, 0, 0);
[Obsolete("Field has been replaced by " + nameof(HighW) + ", because it has a better name. " +
"This field will be removed in v2.4.0.", false)]
public static Float4 Far => new(0, 0, 0, 1);
public static Float4 Forward => new(0, 0, 1, 0);
public static Float4 HighW => new(0, 0, 0, 1);
public static Float4 Left => new(-1, 0, 0, 0);
public static Float4 LowW => new(0, 0, 0, -1);
[Obsolete("Field has been replaced by " + nameof(LowW) + ", because it has a better name. " +
"This field will be removed in v2.4.0.", false)]
public static Float4 Near => new(0, 0, 0, -1);
public static Float4 Right => new(1, 0, 0, 0);
public static Float4 Up => new(0, 1, 0, 0);
@ -207,8 +201,6 @@ public record struct Float4 : IAbsolute<Float4>,
return (Xs, Ys, Zs, Ws);
}
[Obsolete("This method is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public int CompareTo(Float4 other) => Magnitude.CompareTo(other.Magnitude);
public bool Equals(Float4 other) => x == other.x && y == other.y && z == other.z && w == other.w;
public override int GetHashCode() => base.GetHashCode();
@ -252,18 +244,6 @@ public record struct Float4 : IAbsolute<Float4>,
public static Float4 operator /(Float4 a, Float4 b) => new(a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w);
public static Float4 operator /(Float4 a, float b) => new(a.x / b, a.y / b, a.z / b, a.w / b);
public static Float4 operator /(Float4 a, Matrix b) => (Float4)((Matrix)a / b);
[Obsolete("This operator is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public static bool operator >(Float4 a, Float4 b) => a.CompareTo(b) > 0;
[Obsolete("This operator is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public static bool operator <(Float4 a, Float4 b) => a.CompareTo(b) < 0;
[Obsolete("This operator is a bit ambiguous (and misleading at times). " +
"You should instead compare " + nameof(Magnitude) + "s directly.")]
public static bool operator >=(Float4 a, Float4 b) => a == b || a > b;
[Obsolete("This operator is a bit ambiguous (and misleading at times). " +
"You should instead compare " + nameof(Magnitude) + "s directly.")]
public static bool operator <=(Float4 a, Float4 b) => a == b || a < b;
public static implicit operator Float4(Complex val) => new(val.u, val.i, 0, 0);
public static implicit operator Float4(Quaternion val) => new(val.u, val.i, val.j, val.k);

View File

@ -1,7 +1,7 @@
namespace Nerd_STF.Mathematics.Geometry;
public record class Box2D : IAbsolute<Box2D>, IAverage<Box2D>, ICeiling<Box2D>, IClamp<Box2D>, IContains<Vert>,
IEquatable<Box2D>, IFloor<Box2D>, ILerp<Box2D, float>, IMedian<Box2D>, IRound<Box2D>, IShape2D<float>,
IEquatable<Box2D>, IFloor<Box2D>, ILerp<Box2D, float>, IMedian<Box2D>, IRound<Box2D>, IShape2d<float>,
ISplittable<Box2D, (Vert[] centers, Float2[] sizes)>
{
public static Box2D Unit => new(Vert.Zero, Float2.One);

View File

@ -2,7 +2,7 @@
public record class Box3D : IAbsolute<Box3D>, IAverage<Box3D>, ICeiling<Box3D>, IClamp<Box3D>,
IContains<Vert>, IEquatable<Box3D>, IFloor<Box3D>, ILerp<Box3D, float>, IMedian<Box3D>,
IRound<Box3D>, IShape3D<float>, ISplittable<Box3D, (Vert[] centers, Float3[] sizes)>
IRound<Box3D>, IShape3d<float>, ISplittable<Box3D, (Vert[] centers, Float3[] sizes)>
{
public static Box3D Unit => new(Vert.Zero, Float3.One);

View File

@ -4,7 +4,7 @@ namespace Nerd_STF.Mathematics.Geometry;
public record class Line : IAbsolute<Line>, IAverage<Line>, ICeiling<Line>, IClamp<Line>, IClosestTo<Vert>,
IComparable<Line>, IContains<Vert>, IEquatable<Line>, IFloor<Line>, IFromTuple<Line, (Vert start, Vert end)>,
IGroup<Vert>, IIndexAll<Vert>, IIndexRangeAll<Vert>, ILerp<Line, float>, IMedian<Line>, IPresets3D<Line>,
IGroup<Vert>, IIndexAll<Vert>, IIndexRangeAll<Vert>, ILerp<Line, float>, IMedian<Line>, IPresets3d<Line>,
IRound<Line>, ISplittable<Line, (Vert[] starts, Vert[] ends)>, ISubdivide<Line[]>
{
public static Line Back => new(Vert.Zero, Vert.Back);

View File

@ -3,7 +3,7 @@
public record class Quadrilateral : IAbsolute<Quadrilateral>, IAverage<Quadrilateral>, ICeiling<Quadrilateral>,
IClamp<Quadrilateral>, IEquatable<Quadrilateral>, IFloor<Quadrilateral>,
IFromTuple<Quadrilateral, (Vert a, Vert b, Vert c, Vert d)>, IGroup<Vert>, IIndexAll<Vert>, IIndexRangeAll<Vert>,
ILerp<Quadrilateral, float>, IRound<Quadrilateral>, IShape2D<float>, ITriangulate
ILerp<Quadrilateral, float>, IRound<Quadrilateral>, IShape2d<float>, ITriangulate
{
public Vert A
{

View File

@ -4,7 +4,7 @@ namespace Nerd_STF.Mathematics.Geometry;
public record class Triangle : IAbsolute<Triangle>, IAverage<Triangle>, ICeiling<Triangle>, IClamp<Triangle>,
IEquatable<Triangle>, IFloor<Triangle>, IFromTuple<Triangle, (Vert a, Vert b, Vert c)>, IGroup<Vert>,
IIndexAll<Vert>, IIndexRangeAll<Vert>, ILerp<Triangle, float>, IRound<Triangle>, IShape2D<float>
IIndexAll<Vert>, IIndexRangeAll<Vert>, ILerp<Triangle, float>, IRound<Triangle>, IShape2d<float>
{
public Vert A
{

View File

@ -3,7 +3,7 @@
public record struct Int2 : IAbsolute<Int2>, IAverage<Int2>, IClamp<Int2>, IClampMagnitude<Int2, int>,
IComparable<Int2>, ICross<Int2, Int3>, IDivide<Int2>, IDot<Int2, int>, IEquatable<Int2>,
IFromTuple<Int2, (int x, int y)>, IGroup<int>, IIndexAll<int>, IIndexRangeAll<int>, ILerp<Int2, float>,
IMathOperators<Int2>, IMax<Int2>, IMedian<Int2>, IMin<Int2>, IPresets2D<Int2>, IProduct<Int2>,
IMathOperators<Int2>, IMax<Int2>, IMedian<Int2>, IMin<Int2>, IPresets2d<Int2>, IProduct<Int2>,
ISplittable<Int2, (int[] Xs, int[] Ys)>, ISubtract<Int2>, ISum<Int2>
{
public static Int2 Down => new(0, -1);
@ -154,8 +154,6 @@ public record struct Int2 : IAbsolute<Int2>, IAverage<Int2>, IClamp<Int2>, IClam
return (Xs, Ys);
}
[Obsolete("This method is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public int CompareTo(Int2 other) => Magnitude.CompareTo(other.Magnitude);
public bool Equals(Int2 other) => x == other.x && y == other.y;
public override int GetHashCode() => base.GetHashCode();
@ -198,18 +196,6 @@ public record struct Int2 : IAbsolute<Int2>, IAverage<Int2>, IClamp<Int2>, IClam
public static Int2 operator &(Int2 a, Int2 b) => new(a.x & b.x, a.y & b.y);
public static Int2 operator |(Int2 a, Int2 b) => new(a.x | b.x, a.y | b.y);
public static Int2 operator ^(Int2 a, Int2 b) => new(a.x ^ b.x, a.y ^ b.y);
[Obsolete("This operator is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public static bool operator >(Int2 a, Int2 b) => a.CompareTo(b) > 0;
[Obsolete("This operator is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public static bool operator <(Int2 a, Int2 b) => a.CompareTo(b) < 0;
[Obsolete("This operator is a bit ambiguous (and misleading at times). " +
"You should instead compare " + nameof(Magnitude) + "s directly.")]
public static bool operator >=(Int2 a, Int2 b) => a == b || a > b;
[Obsolete("This operator is a bit ambiguous (and misleading at times). " +
"You should instead compare " + nameof(Magnitude) + "s directly.")]
public static bool operator <=(Int2 a, Int2 b) => a == b || a < b;
public static explicit operator Int2(Complex val) => new((int)val.u, (int)val.i);
public static explicit operator Int2(Quaternion val) => new((int)val.u, (int)val.i);

View File

@ -5,7 +5,7 @@ namespace Nerd_STF.Mathematics;
public record struct Int3 : IAbsolute<Int3>, IAverage<Int3>, IClamp<Int3>, IClampMagnitude<Int3, int>,
IComparable<Int3>, ICross<Int3>, IDivide<Int3>, IDot<Int3, int>, IEquatable<Int3>,
IFromTuple<Int3, (int x, int y, int z)>, IGroup<int>, IIndexAll<int>, IIndexRangeAll<int>, ILerp<Int3, float>,
IMathOperators<Int3>, IMax<Int3>, IMedian<Int3>, IMin<Int3>, IPresets3D<Int3>, IProduct<Int3>,
IMathOperators<Int3>, IMax<Int3>, IMedian<Int3>, IMin<Int3>, IPresets3d<Int3>, IProduct<Int3>,
ISplittable<Int3, (int[] Xs, int[] Ys, int[] Zs)>, ISubtract<Int3>, ISum<Int3>
{
public static Int3 Back => new(0, 0, -1);
@ -177,8 +177,6 @@ public record struct Int3 : IAbsolute<Int3>, IAverage<Int3>, IClamp<Int3>, IClam
return (Xs, Ys, Zs);
}
[Obsolete("This method is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public int CompareTo(Int3 other) => Magnitude.CompareTo(other.Magnitude);
public bool Equals(Int3 other) => x == other.x && y == other.y && z == other.z;
public override int GetHashCode() => base.GetHashCode();
@ -224,18 +222,6 @@ public record struct Int3 : IAbsolute<Int3>, IAverage<Int3>, IClamp<Int3>, IClam
public static Int3 operator &(Int3 a, Int3 b) => new(a.x & b.x, a.y & b.y, a.z & b.z);
public static Int3 operator |(Int3 a, Int3 b) => new(a.x | b.x, a.y | b.y, a.z | b.z);
public static Int3 operator ^(Int3 a, Int3 b) => new(a.x ^ b.x, a.y ^ b.y, a.z ^ b.z);
[Obsolete("This operator is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public static bool operator >(Int3 a, Int3 b) => a.CompareTo(b) > 0;
[Obsolete("This operator is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public static bool operator <(Int3 a, Int3 b) => a.CompareTo(b) < 0;
[Obsolete("This operator is a bit ambiguous (and misleading at times). " +
"You should instead compare " + nameof(Magnitude) + "s directly.")]
public static bool operator >=(Int3 a, Int3 b) => a == b || a > b;
[Obsolete("This operator is a bit ambiguous (and misleading at times). " +
"You should instead compare " + nameof(Magnitude) + "s directly.")]
public static bool operator <=(Int3 a, Int3 b) => a == b || a < b;
public static explicit operator Int3(Complex val) => new((int)val.u, (int)val.i, 0);
public static explicit operator Int3(Quaternion val) => new((int)val.u, (int)val.i, (int)val.j);

View File

@ -3,17 +3,11 @@
public record struct Int4 : IAbsolute<Int4>, IAverage<Int4>, IClamp<Int4>, IClampMagnitude<Int4, int>,
IComparable<Int4>, IDivide<Int4>, IDot<Int4, int>, IEquatable<Int4>,
IFromTuple<Int4, (int x, int y, int z, int w)>, IGroup<int>, IIndexAll<int>, IIndexRangeAll<int>,
ILerp<Int4, float>, IMathOperators<Int4>, IMax<Int4>, IMedian<Int4>, IMin<Int4>, IPresets4D<Int4>,
ILerp<Int4, float>, IMathOperators<Int4>, IMax<Int4>, IMedian<Int4>, IMin<Int4>, IPresets4d<Int4>,
IProduct<Int4>, ISplittable<Int4, (int[] Xs, int[] Ys, int[] Zs, int[] Ws)>, ISubtract<Int4>, ISum<Int4>
{
public static Int4 Back => new(0, 0, -1, 0);
[Obsolete("Field has been replaced by " + nameof(HighW) + ", because it has a better name. " +
"This field will be removed in v2.4.0.", false)]
public static Int4 Deep => new(0, 0, 0, -1);
public static Int4 Down => new(0, -1, 0, 0);
[Obsolete("Field has been replaced by " + nameof(HighW) + ", because it has a better name. " +
"This field will be removed in v2.4.0.", false)]
public static Int4 Far => new(0, 0, 0, 1);
public static Int4 Forward => new(0, 0, 1, 0);
public static Int4 HighW => new(0, 0, 0, 1);
public static Int4 Left => new(-1, 0, 0, 0);
@ -196,8 +190,6 @@ public record struct Int4 : IAbsolute<Int4>, IAverage<Int4>, IClamp<Int4>, IClam
return (Xs, Ys, Zs, Ws);
}
[Obsolete("This method is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public int CompareTo(Int4 other) => Magnitude.CompareTo(other.Magnitude);
public bool Equals(Int4 other) => x == other.x && y == other.y && z == other.z && w == other.w;
public override int GetHashCode() => base.GetHashCode();
@ -244,18 +236,6 @@ public record struct Int4 : IAbsolute<Int4>, IAverage<Int4>, IClamp<Int4>, IClam
public static Int4 operator &(Int4 a, Int4 b) => new(a.x & b.x, a.y & b.y, a.z & b.z, a.w & b.w);
public static Int4 operator |(Int4 a, Int4 b) => new(a.x | b.x, a.y | b.y, a.z | b.z, a.w | b.w);
public static Int4 operator ^(Int4 a, Int4 b) => new(a.x ^ b.x, a.y ^ b.y, a.z ^ b.z, a.w ^ b.w);
[Obsolete("This operator is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public static bool operator >(Int4 a, Int4 b) => a.CompareTo(b) > 0;
[Obsolete("This operator is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public static bool operator <(Int4 a, Int4 b) => a.CompareTo(b) < 0;
[Obsolete("This operator is a bit ambiguous (and misleading at times). " +
"You should instead compare " + nameof(Magnitude) + "s directly.")]
public static bool operator >=(Int4 a, Int4 b) => a == b || a > b;
[Obsolete("This operator is a bit ambiguous (and misleading at times). " +
"You should instead compare " + nameof(Magnitude) + "s directly.")]
public static bool operator <=(Int4 a, Int4 b) => a == b || a < b;
public static explicit operator Int4(Complex val) => new((int)val.u, (int)val.i, 0, 0);
public static explicit operator Int4(Quaternion val) => new((int)val.u, (int)val.i, (int)val.j, (int)val.k);

View File

@ -44,6 +44,8 @@ public static class Mathf
public static float Binomial(int n, int total, float successRate) =>
Combinations(total, n) * Power(successRate, n) * Power(1 - successRate, total - n);
public static float Cbrt(float value) => SolveNewton(x => x * x * x - value, 1);
public static int Ceiling(float val)
{
float mod = val % 1;
@ -135,6 +137,13 @@ public static class Mathf
public static float InverseSqrt(float val) => 1 / Sqrt(val);
public static bool IsPrime(int num, PrimeCheckMethod method = PrimeCheckMethod.Classic) =>
method switch
{
PrimeCheckMethod.Classic => MathfHelper.IsPrimeClassic(num),
_ => throw new ArgumentException("Unknown prime check method.", nameof(method))
};
public static int LeastCommonMultiple(params int[] vals) => Product(vals) / GreatestCommonFactor(vals);
public static float Lerp(float a, float b, float t, bool clamp = true)
@ -144,6 +153,12 @@ public static class Mathf
return v;
}
public static int Lerp(int a, int b, float t, bool clamp = true) => (int)Lerp((float)a, b, t, clamp);
public static Equation Lerp(float a, float b, Equation t, bool clamp = true) =>
x => Lerp(a, b, t(x), clamp);
public static Equation Lerp(Equation a, Equation b, float t, bool clamp = true) =>
x => Lerp(a(x), b(x), t, clamp);
public static Equation Lerp(Equation a, Equation b, Equation t, bool clamp = true) =>
x => Lerp(a(x), b(x), t(x), clamp);
public static Equation MakeEquation(Dictionary<float, float> vals) => delegate (float x)
{
@ -280,6 +295,20 @@ public static class Mathf
// nPr (n = total, r = size)
public static int Permutations(int total, int size) => Factorial(total) / Factorial(total - size);
public static int[] PrimeFactors(int num)
{
List<int> factors = new();
for (int i = 2; i <= num; i++)
{
while (num % i == 0)
{
factors.Add(i);
num /= i;
}
}
return factors.ToArray();
}
public static float Product(params float[] vals)
{
if (vals.Length < 1) return 0;
@ -339,6 +368,16 @@ public static class Mathf
public static float Sec(Angle angle) => Sec(angle.Radians);
public static float Sec(float radians) => 1 / Cos(radians);
public static T[] SharedItems<T>(params T[][] arrays) where T : IEquatable<T>
{
if (arrays.Length < 1) return Array.Empty<T>();
IEnumerable<T> results = arrays[0];
foreach (T[] array in arrays) results = results.Where(x => array.Any(y => y.Equals(x)));
return UniqueItems(results.ToArray());
}
public static float Sin(Angle angle) => Sin(angle.Radians);
public static float Sin(float radians)
{
@ -361,7 +400,70 @@ public static class Mathf
+ (j * x * x * x * x * x * x * x * x * x);
}
public static float Sqrt(float value) => Root(value, 2);
public static float SolveBisection(Equation equ, float initialA, float initialB, float tolerance = 1e-5f,
int maxIterations = 1000)
{
if (equ(initialA) == 0) return initialA;
else if (equ(initialB) == 0) return initialB;
float guessA = initialA, guessB = initialB, guessMid;
if (Math.Sign(equ(guessA)) == Math.Sign(equ(guessB)))
{
// Guess doesn't contain a zero (or isn't continuous). Return NaN.
return float.NaN;
}
int iterations = 0;
do
{
guessMid = (guessA + guessB) / 2;
float valMid = equ(guessMid);
if (valMid == 0) return guessMid;
if (Math.Sign(equ(guessA)) != Math.Sign(valMid)) guessB = guessMid;
else guessA = guessMid;
iterations++;
if (iterations > maxIterations)
{
// Result isn't good enough. Return NaN.
return float.NaN;
}
}
while ((guessB - guessA) > tolerance);
return guessMid;
}
public static float SolveEquation(Equation equ, float initial, float tolerance = 1e-5f,
float step = Calculus.DefaultStep, int maxIterations = 1000) =>
SolveNewton(equ, initial, tolerance, step, maxIterations);
public static float SolveNewton(Equation equ, float initial, float tolerance = 1e-5f,
float step = Calculus.DefaultStep, int maxIterations = 1000)
{
if (equ(initial) == 0) return initial;
float lastResult = initial, result;
int iterations = 0;
do
{
result = lastResult - (equ(lastResult) / Calculus.GetDerivativeAtPoint(equ, lastResult, step));
lastResult = result;
iterations++;
if (iterations > maxIterations)
{
// Result isn't good enough. Return NaN.
return float.NaN;
}
}
while (Absolute(equ(result)) > tolerance);
return result;
}
public static float Sqrt(float value) => SolveNewton(x => x * x - value, 1);
public static float Subtract(float num, params float[] vals) => num - Sum(vals);
public static int Subtract(int num, params int[] vals) => num - Sum(vals);

View File

@ -3,7 +3,7 @@
public record struct Complex(float u, float i) : IAbsolute<Complex>, IAverage<Complex>, ICeiling<Complex>,
IClampMagnitude<Complex, float>, IComparable<Complex>, IDivide<Complex>, IDot<Complex, float>,
IEquatable<Complex>, IFloor<Complex>, IGroup<float>, IIndexAll<float>, IIndexRangeAll<float>,
ILerp<Complex, float>, IMax<Complex>, IMedian<Complex>, IMin<Complex>, IPresets2D<Complex>, IProduct<Complex>,
ILerp<Complex, float>, IMax<Complex>, IMedian<Complex>, IMin<Complex>, IPresets2d<Complex>, IProduct<Complex>,
IRound<Complex>, ISplittable<Complex, (float[] Us, float[] Is)>, ISum<Complex>
{
public static Complex Down => new(0, -1);
@ -190,18 +190,6 @@ public record struct Complex(float u, float i) : IAbsolute<Complex>, IAverage<Co
public static Complex operator /(Complex a, float b) => new(a.u / b, a.i / b);
public static Complex operator /(Complex a, Matrix b) => (Complex)((Matrix)a / b);
public static Complex operator ~(Complex v) => v.Conjugate;
[Obsolete("This operator is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public static bool operator >(Complex a, Complex b) => a.CompareTo(b) > 0;
[Obsolete("This operator is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public static bool operator <(Complex a, Complex b) => a.CompareTo(b) < 0;
[Obsolete("This operator is a bit ambiguous (and misleading at times). " +
"You should instead compare " + nameof(Magnitude) + "s directly.")]
public static bool operator >=(Complex a, Complex b) => a == b || a > b;
[Obsolete("This operator is a bit ambiguous (and misleading at times). " +
"You should instead compare " + nameof(Magnitude) + "s directly.")]
public static bool operator <=(Complex a, Complex b) => a == b || a < b;
public static explicit operator Complex(Quaternion val) => new(val.u, val.i);
public static implicit operator Complex(Float2 val) => new(val.x, val.y);

View File

@ -4,21 +4,15 @@ public record struct Quaternion(float u, float i, float j, float k) : IAbsolute<
ICeiling<Quaternion>, IClamp<Quaternion>, IClampMagnitude<Quaternion, float>, IComparable<Quaternion>,
IDivide<Quaternion>, IDot<Quaternion, float>, IEquatable<Quaternion>, IFloor<Quaternion>, IGroup<float>,
IIndexAll<float>, IIndexRangeAll<float>, ILerp<Quaternion, float>, IMax<Quaternion>, IMedian<Quaternion>,
IMin<Quaternion>, IPresets4D<Quaternion>, IProduct<Quaternion>, IRound<Quaternion>,
IMin<Quaternion>, IPresets4d<Quaternion>, IProduct<Quaternion>, IRound<Quaternion>,
ISplittable<Quaternion, (float[] Us, float[] Is, float[] Js, float[] Ks)>, ISum<Quaternion>
{
public static Quaternion Back => new(0, 0, -1, 0);
public static Quaternion Down => new(0, -1, 0, 0);
[Obsolete("Field has been replaced by " + nameof(HighW) + ", because it has a better name. " +
"This field will be removed in v2.4.0.", false)]
public static Quaternion Far => new(0, 0, 0, 1);
public static Quaternion Forward => new(0, 0, 1, 0);
public static Quaternion HighW => new(0, 0, 0, 1);
public static Quaternion Left => new(-1, 0, 0, 0);
public static Quaternion LowW => new(0, 0, 0, -1);
[Obsolete("Field has been replaced by " + nameof(LowW) + ", because it has a better name. " +
"This field will be removed in v2.4.0.", false)]
public static Quaternion Near => new(0, 0, 0, -1);
public static Quaternion Right => new(1, 0, 0, 0);
public static Quaternion Up => new(0, 1, 0, 0);
@ -323,18 +317,6 @@ public record struct Quaternion(float u, float i, float j, float k) : IAbsolute<
public static Quaternion operator /(Quaternion a, Matrix b) => (Quaternion)((Matrix)a / b);
public static Quaternion operator /(Quaternion a, Float3 b) => a / new Quaternion(b);
public static Quaternion operator ~(Quaternion v) => v.Conjugate;
[Obsolete("This operator is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public static bool operator >(Quaternion a, Quaternion b) => a.CompareTo(b) > 0;
[Obsolete("This operator is a bit ambiguous. You should instead compare " +
nameof(Magnitude) + "s directly.")]
public static bool operator <(Quaternion a, Quaternion b) => a.CompareTo(b) < 0;
[Obsolete("This operator is a bit ambiguous (and misleading at times). " +
"You should instead compare " + nameof(Magnitude) + "s directly.")]
public static bool operator >=(Quaternion a, Quaternion b) => a == b || a > b;
[Obsolete("This operator is a bit ambiguous (and misleading at times). " +
"You should instead compare " + nameof(Magnitude) + "s directly.")]
public static bool operator <=(Quaternion a, Quaternion b) => a == b || a < b;
public static implicit operator Quaternion(Complex val) => new(val.u, val.i, 0, 0);
public static implicit operator Quaternion(Int2 val) => new(val);

View File

@ -0,0 +1,6 @@
namespace Nerd_STF.Mathematics;
public enum PrimeCheckMethod
{
Classic
}

View File

@ -0,0 +1,218 @@
namespace Nerd_STF.Mathematics;
public record struct Rational : IAbsolute<Rational>, IAverage<Rational>, ICeiling<Rational, int>, IClamp<Rational>,
IComparable<Rational>, IComparable<float>, IDivide<Rational>, IEquatable<Rational>, IEquatable<float>,
IFloor<Rational, int>, IIndexAll<int>, IIndexRangeAll<int>, ILerp<Rational, float>, IMathOperators<Rational>,
IMax<Rational>, IMedian<Rational>, IMin<Rational>, IPresets1d<Rational>, IProduct<Rational>,
IRound<Rational, int>, ISplittable<Rational, (int[] nums, int[] dens)>, ISubtract<Rational>,
ISum<Rational>
{
public static Rational One => new(1, 1);
public static Rational Zero => new(0, 1);
public int Numerator
{
get => p_num;
set => p_num = value;
}
public int Denominator
{
get => p_den;
set
{
if (Math.Sign(value) == -1)
{
p_num *= -1;
p_den = -value;
}
else p_den = value;
}
}
private int p_num;
private int p_den;
public Rational Reciprocal => new(p_den, p_num);
public Rational Simplified
{
get
{
int[] denFactors = Mathf.PrimeFactors(p_den);
int newNum = p_num,
newDen = p_den;
foreach (int factor in denFactors)
{
if (newNum % factor != 0) continue;
newNum /= factor;
newDen /= factor;
}
return new(newNum, newDen, false);
}
}
public Rational() : this(0, 1) { }
public Rational(int numerator, int denominator, bool simplified = true)
{
Numerator = numerator;
Denominator = denominator;
if (simplified) this = Simplified;
}
public Rational(Fill<int> fill, bool simplified = true) : this(fill(0), fill(1), simplified) { }
public int this[int index]
{
get => index switch
{
0 => p_num,
1 => p_den,
_ => throw new IndexOutOfRangeException(nameof(index))
};
set
{
switch (index)
{
case 0:
p_num = value;
break;
case 1:
p_den = value;
break;
default: throw new IndexOutOfRangeException(nameof(index));
}
}
}
public int this[Index index]
{
get => this[index.IsFromEnd ? 2 - index.Value : index.Value];
set => this[index.IsFromEnd ? 2 - index.Value : index.Value] = value;
}
public int[] this[Range range]
{
get
{
int start = range.Start.IsFromEnd ? 2 - range.Start.Value : range.Start.Value;
int end = range.End.IsFromEnd ? 2 - range.End.Value : range.End.Value;
List<int> res = new();
for (int i = start; i < end; i++) res.Add(this[i]);
return res.ToArray();
}
set
{
int start = range.Start.IsFromEnd ? 2 - range.Start.Value : range.Start.Value;
int end = range.End.IsFromEnd ? 2 - range.End.Value : range.End.Value;
for (int i = start; i < end; i++) this[i] = value[i];
}
}
public static Rational FromFloat(float value, float tolerance = 1e-5f,
SimplificationMethod method = SimplificationMethod.FareySequence, int maxIterations = 100) =>
method switch
{
SimplificationMethod.AutoSimplify => RationalHelper.SimplifyAuto(value),
SimplificationMethod.FareySequence => RationalHelper.SimplifyFarey(value, tolerance, maxIterations),
_ => throw new ArgumentException("Unknown simplification method.", nameof(method))
};
public static Rational Absolute(Rational value) =>
new(Mathf.Absolute(value.p_num), value.p_den);
public static Rational Average(params Rational[] vals) => Sum(vals) / (float)vals.Length;
public static int Ceiling(Rational r)
{
int mod = r.p_num % r.p_den;
if (mod == 0) return r.p_num / r.p_den;
return r.p_num + (r.p_den - mod);
}
public static Rational Clamp(Rational val, Rational min, Rational max)
=> FromFloat(Mathf.Clamp(val.GetValue(), min.GetValue(), max.GetValue()));
public static Rational Divide(Rational val, params Rational[] vals) =>
val / Product(vals);
public static int Floor(Rational val) => val.p_num / val.p_den;
public static Rational Lerp(Rational a, Rational b, float t, bool clamp = true) =>
FromFloat(Mathf.Lerp(a.GetValue(), b.GetValue(), t, clamp));
public static Rational Product(params Rational[] vals)
{
Rational res = One;
foreach (Rational r in vals) res *= r;
return res;
}
public static int Round(Rational r) => (int)Mathf.Round(r.p_num, r.p_den) / r.p_den;
public static Rational Subtract(Rational val, params Rational[] vals) =>
val - Sum(vals);
public static Rational Sum(params Rational[] vals)
{
Rational sum = Zero;
foreach (Rational r in vals) sum += r;
return sum;
}
public static (int[] nums, int[] dens) SplitArray(params Rational[] vals)
{
int[] nums = new int[vals.Length], dens = new int[vals.Length];
for (int i = 0; i < vals.Length; i++)
{
nums[i] = vals[i].p_num;
dens[i] = vals[i].p_den;
}
return (nums, dens);
}
public float GetValue() => p_num / (float)p_den;
public int CompareTo(Rational other) => GetValue().CompareTo(other.GetValue());
public int CompareTo(float other) => GetValue().CompareTo(other);
public bool Equals(Rational other)
{
Rational thisSim = Simplified,
otherSim = other.Simplified;
return thisSim.p_num == otherSim.p_num &&
thisSim.p_den == otherSim.p_den;
}
public bool Equals(float other) => GetValue() == other;
public override int GetHashCode() => base.GetHashCode();
private bool PrintMembers(StringBuilder builder)
{
builder.Append(p_num);
builder.Append(" / ");
builder.Append(p_den);
return true;
}
public static Rational operator +(Rational a, Rational b)
{
int sharedDen = a.p_den * b.p_den,
newNumA = a.p_num * b.p_den,
newNumB = b.p_num * a.p_den;
return new Rational(newNumA + newNumB, sharedDen).Simplified;
}
public static Rational operator +(Rational a, float b) => a + FromFloat(b);
public static Rational operator +(float a, Rational b) => FromFloat(a) + b;
public static Rational operator -(Rational r) => new(-r.p_num, r.p_den);
public static Rational operator -(Rational a, Rational b)
{
int sharedDen = a.p_den * b.p_den,
newNumA = a.p_num * b.p_den,
newNumB = b.p_num * a.p_den;
return new Rational(newNumA - newNumB, sharedDen).Simplified;
}
public static Rational operator -(Rational a, float b) => a - FromFloat(b);
public static Rational operator -(float a, Rational b) => FromFloat(a) - b;
public static Rational operator *(Rational a, Rational b) =>
new Rational(a.p_num * b.p_num, a.p_den * b.p_den);
public static Rational operator *(Rational a, float b) => a * FromFloat(b);
public static Rational operator *(float a, Rational b) => FromFloat(a) * b;
public static Rational operator /(Rational a, Rational b) => a * b.Reciprocal;
public static Rational operator /(Rational a, float b) => a * FromFloat(b).Reciprocal;
public static Rational operator /(float a, Rational b) => FromFloat(a) * b.Reciprocal;
public static implicit operator float(Rational r) => r.GetValue();
public static implicit operator Rational(float f) => FromFloat(f);
}

View File

@ -2,10 +2,11 @@
public static class Equations
{
public static readonly Fill<int> SgnFill = i => i % 2 == 0 ? 1 : -1;
public static Equation CosWave => Mathf.Cos;
public static Equation SinWave => Mathf.Sin;
public static Equation SawWave => x => x % 1;
public static Equation SquareWave => x => x % 2 < 1 ? 1 : 0;
public static readonly Equation CosWave = x => Mathf.Cos(x);
public static readonly Equation SinWave = x => Mathf.Sin(x);
public static readonly Equation SawWave = x => x % 1;
public static readonly Equation SquareWave = x => x % 2 < 1 ? 1 : 0;
public static Equation FlatLine => x => 0;
public static Equation XLine => x => x;
}

View File

@ -0,0 +1,6 @@
namespace Nerd_STF.Mathematics.Samples;
public static class Fills
{
public static Fill<int> SignFill => i => i % 2 == 0 ? 1 : -1;
}

View File

@ -0,0 +1,7 @@
namespace Nerd_STF.Mathematics;
public enum SimplificationMethod
{
AutoSimplify,
FareySequence
}

View File

@ -1,4 +1,2 @@
using System.Reflection;
// Includes assembly configuration that isn't automatically handled by the compiler.
// Includes assembly configuration that isn't automatically handled by the compiler.
// So far, there is none. There may be some in the future. We will see.

View File

@ -3,6 +3,7 @@ global using Nerd_STF.Graphics;
global using Nerd_STF.Graphics.Abstract;
global using Nerd_STF.Exceptions;
global using Nerd_STF.Extensions;
global using Nerd_STF.Helpers;
global using Nerd_STF.Mathematics;
global using Nerd_STF.Mathematics.Abstract;
global using Nerd_STF.Mathematics.Algebra;
@ -16,6 +17,7 @@ global using System.Diagnostics.CodeAnalysis;
global using System.IO;
global using System.Linq;
global using System.Net.Http;
global using System.Reflection;
global using System.Runtime.Serialization;
global using System.Text;
global using System.Threading;

View File

@ -1,5 +1,5 @@
namespace Nerd_STF;
public delegate float Modifier2D(Int2 index, float value);
public delegate T Modifier2D<T>(Int2 index, T value);
public delegate VT Modifier2D<IT, VT>(IT x, IT y, VT value);
public delegate float Modifier2d(Int2 index, float value);
public delegate T Modifier2d<T>(Int2 index, T value);
public delegate VT Modifier2d<IT, VT>(IT x, IT y, VT value);

View File

@ -17,5 +17,5 @@ public static class Nerd_STF
{ "nuget", "https://www.nuget.org/packages/Nerd_STF/" }
};
public const string MainDeveloper = "That_One_Nerd";
public const string Version = "2.3.2";
public const string Version = "2.4.0";
}

View File

@ -51,7 +51,7 @@ Anyway, that's it for this update. The longest delay was just getting this proje
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
<WarningLevel>9999</WarningLevel>
<CheckForOverflowUnderflow>True</CheckForOverflowUnderflow>
<CheckForOverflowUnderflow>False</CheckForOverflowUnderflow>
</PropertyGroup>
<ItemGroup>