Not sure why I haven't been committing more. Pretty much finished fraction, made some other small changes.
This commit is contained in:
parent
0704b8eec7
commit
fcee608322
@ -1,10 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Nerd_STF.Abstract
|
|
||||||
{
|
|
||||||
public interface IModifiable<TSelf>
|
|
||||||
where TSelf : IModifiable<TSelf>
|
|
||||||
{
|
|
||||||
void Modify(Action<TSelf> action);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -6,8 +6,7 @@ namespace Nerd_STF.Abstract
|
|||||||
{
|
{
|
||||||
public interface INumberGroup<TSelf, TItem> : ICombinationIndexer<TItem>,
|
public interface INumberGroup<TSelf, TItem> : ICombinationIndexer<TItem>,
|
||||||
IEnumerable<TItem>,
|
IEnumerable<TItem>,
|
||||||
IEquatable<TSelf>,
|
IEquatable<TSelf>
|
||||||
IModifiable<TSelf>
|
|
||||||
#if CS11_OR_GREATER
|
#if CS11_OR_GREATER
|
||||||
,IInterpolable<TSelf>,
|
,IInterpolable<TSelf>,
|
||||||
ISimpleMathOperations<TSelf>,
|
ISimpleMathOperations<TSelf>,
|
||||||
|
|||||||
@ -10,10 +10,6 @@ namespace Nerd_STF.Abstract
|
|||||||
static abstract TOut Ceiling(TSelf val);
|
static abstract TOut Ceiling(TSelf val);
|
||||||
static abstract TOut Floor(TSelf val);
|
static abstract TOut Floor(TSelf val);
|
||||||
static abstract TOut Round(TSelf val);
|
static abstract TOut Round(TSelf val);
|
||||||
|
|
||||||
static abstract void Ceiling(ref TSelf val);
|
|
||||||
static abstract void Floor(ref TSelf val);
|
|
||||||
static abstract void Round(ref TSelf val);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -5,7 +5,9 @@
|
|||||||
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
[assembly: SuppressMessage("Style", "IDE0066:Use 'switch' expression", Justification = "Only available for C#8+.")]
|
[assembly: SuppressMessage("Style", "IDE0066:Use 'switch' expression", Justification = "Only available for C#8+.")]
|
||||||
[assembly: SuppressMessage("Style", "IDE0083:Use pattern matching", Justification = "Only available for C#9+")]
|
[assembly: SuppressMessage("Style", "IDE0083:Use pattern matching", Justification = "Only available for C#9+")]
|
||||||
[assembly: SuppressMessage("Style", "IDE0090:Use 'new(...)'", Justification = "Only available for C#9+")]
|
[assembly: SuppressMessage("Style", "IDE0090:Use 'new(...)'", Justification = "Only available for C#9+")]
|
||||||
[assembly: SuppressMessage("Style", "IDE0251:Make member 'readonly'", Justification = "Only available for C#8+. Also, what does applying 'readonly' to a method even do?")]
|
[assembly: SuppressMessage("Style", "IDE0251:Make member 'readonly'", Justification = "Only available for C#8+. Also, what does applying 'readonly' to a method even do?")]
|
||||||
|
[assembly: SuppressMessage("Style", "IDE0057:Use range operator", Justification = "Not supported in .NET Standard 1.1 and 1.3.")]
|
||||||
|
[assembly: SuppressMessage("Performance", "CA1846:Prefer 'AsSpan' over 'Substring'", Justification = "Not supported in .NET Standard 1.1 and 1.3.")]
|
||||||
|
|||||||
54
Nerd_STF/Helpers/ParseHelper.cs
Normal file
54
Nerd_STF/Helpers/ParseHelper.cs
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Nerd_STF.Helpers
|
||||||
|
{
|
||||||
|
internal static class ParseHelper
|
||||||
|
{
|
||||||
|
// TODO: Allow parsing more stuff (hexadecimal).
|
||||||
|
public static double ParseDouble(ReadOnlySpan<char> str)
|
||||||
|
{
|
||||||
|
// Turns out this is less accurate than copying and modifying
|
||||||
|
// the code from ParseDoubleWholeDecimals. I think because applying
|
||||||
|
// 0.1 to the whole number is worse than 0.1 to a each individual
|
||||||
|
// decimal point.
|
||||||
|
int raw = ParseDoubleWholeDecimals(str, out int places);
|
||||||
|
double value = raw;
|
||||||
|
for (int i = 0; i < places; i++) value *= 0.1;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
public static int ParseDoubleWholeDecimals(ReadOnlySpan<char> str, out int places)
|
||||||
|
{
|
||||||
|
str = str.Trim();
|
||||||
|
if (str.Length == 0) goto _fail;
|
||||||
|
places = 0;
|
||||||
|
|
||||||
|
bool negative = str.StartsWith("-".AsSpan());
|
||||||
|
|
||||||
|
int result = 0;
|
||||||
|
ReadOnlySpan<char>.Enumerator stepper = str.GetEnumerator();
|
||||||
|
if (negative) stepper.MoveNext();
|
||||||
|
bool decFound = false;
|
||||||
|
while (stepper.MoveNext())
|
||||||
|
{
|
||||||
|
char c = stepper.Current;
|
||||||
|
if (c == ',') continue;
|
||||||
|
else if (c == '.')
|
||||||
|
{
|
||||||
|
decFound = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c < '0' || c > '9') goto _fail;
|
||||||
|
int value = c - '0';
|
||||||
|
|
||||||
|
result = result * 10 + value;
|
||||||
|
if (decFound) places++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return negative ? -result : result;
|
||||||
|
|
||||||
|
_fail:
|
||||||
|
throw new FormatException("Cannot parse double from span.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,8 +6,7 @@ using System.Linq;
|
|||||||
namespace Nerd_STF.Mathematics
|
namespace Nerd_STF.Mathematics
|
||||||
{
|
{
|
||||||
public struct Angle : IComparable<Angle>,
|
public struct Angle : IComparable<Angle>,
|
||||||
IEquatable<Angle>,
|
IEquatable<Angle>
|
||||||
IModifiable<Angle>
|
|
||||||
#if CS11_OR_GREATER
|
#if CS11_OR_GREATER
|
||||||
,IPresets2d<Angle>,
|
,IPresets2d<Angle>,
|
||||||
IFromTuple<Angle, (double, Angle.Unit)>
|
IFromTuple<Angle, (double, Angle.Unit)>
|
||||||
@ -48,10 +47,10 @@ namespace Nerd_STF.Mathematics
|
|||||||
set => revTheta = value;
|
set => revTheta = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Angle Complimentary => new Angle(0.25 - MathE.AbsoluteMod(revTheta, 1));
|
public Angle Complimentary => new Angle(0.25 - MathE.ModAbs(revTheta, 1));
|
||||||
public Angle Supplimentary => new Angle(0.5 - MathE.AbsoluteMod(revTheta, 1));
|
public Angle Supplimentary => new Angle(0.5 - MathE.ModAbs(revTheta, 1));
|
||||||
public Angle Normalized => new Angle(MathE.AbsoluteMod(revTheta, 1));
|
public Angle Normalized => new Angle(MathE.ModAbs(revTheta, 1));
|
||||||
public Angle Reflected => new Angle(MathE.AbsoluteMod(-revTheta, 1));
|
public Angle Reflected => new Angle(MathE.ModAbs(-revTheta, 1));
|
||||||
|
|
||||||
private double revTheta;
|
private double revTheta;
|
||||||
|
|
||||||
@ -123,12 +122,12 @@ namespace Nerd_STF.Mathematics
|
|||||||
if (!any)
|
if (!any)
|
||||||
{
|
{
|
||||||
best = ang;
|
best = ang;
|
||||||
if (normalize) bestNormalized = MathE.AbsoluteMod(ang.revTheta, 1);
|
if (normalize) bestNormalized = MathE.ModAbs(ang.revTheta, 1);
|
||||||
any = true;
|
any = true;
|
||||||
}
|
}
|
||||||
else if (normalize)
|
else if (normalize)
|
||||||
{
|
{
|
||||||
double angNormalized = MathE.AbsoluteMod(ang.revTheta, 1);
|
double angNormalized = MathE.ModAbs(ang.revTheta, 1);
|
||||||
if (angNormalized > bestNormalized)
|
if (angNormalized > bestNormalized)
|
||||||
{
|
{
|
||||||
best = ang;
|
best = ang;
|
||||||
@ -150,12 +149,12 @@ namespace Nerd_STF.Mathematics
|
|||||||
if (!any)
|
if (!any)
|
||||||
{
|
{
|
||||||
best = ang;
|
best = ang;
|
||||||
if (normalize) bestNormalized = MathE.AbsoluteMod(ang.revTheta, 1);
|
if (normalize) bestNormalized = MathE.ModAbs(ang.revTheta, 1);
|
||||||
any = true;
|
any = true;
|
||||||
}
|
}
|
||||||
else if (normalize)
|
else if (normalize)
|
||||||
{
|
{
|
||||||
double angNormalized = MathE.AbsoluteMod(ang.revTheta, 1);
|
double angNormalized = MathE.ModAbs(ang.revTheta, 1);
|
||||||
if (angNormalized < bestNormalized)
|
if (angNormalized < bestNormalized)
|
||||||
{
|
{
|
||||||
best = ang;
|
best = ang;
|
||||||
@ -186,8 +185,6 @@ namespace Nerd_STF.Mathematics
|
|||||||
return angles;
|
return angles;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Modify(Action<Angle> action) => action(this);
|
|
||||||
|
|
||||||
public int CompareTo(Angle other) => revTheta.CompareTo(other.revTheta);
|
public int CompareTo(Angle other) => revTheta.CompareTo(other.revTheta);
|
||||||
public bool Equals(Angle other) => revTheta == other.revTheta;
|
public bool Equals(Angle other) => revTheta == other.revTheta;
|
||||||
#if CS8_OR_GREATER
|
#if CS8_OR_GREATER
|
||||||
|
|||||||
@ -236,8 +236,6 @@ namespace Nerd_STF.Mathematics
|
|||||||
y = this.y;
|
y = this.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Modify(Action<Float2> action) => action(this);
|
|
||||||
|
|
||||||
public bool Equals(Float2 other) => x == other.x && y == other.y;
|
public bool Equals(Float2 other) => x == other.x && y == other.y;
|
||||||
#if CS8_OR_GREATER
|
#if CS8_OR_GREATER
|
||||||
public override bool Equals(object? obj)
|
public override bool Equals(object? obj)
|
||||||
|
|||||||
@ -262,8 +262,6 @@ namespace Nerd_STF.Mathematics
|
|||||||
z = this.z;
|
z = this.z;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Modify(Action<Float3> action) => action(this);
|
|
||||||
|
|
||||||
public bool Equals(Float3 other) => x == other.x && y == other.y && z == other.z;
|
public bool Equals(Float3 other) => x == other.x && y == other.y && z == other.z;
|
||||||
#if CS8_OR_GREATER
|
#if CS8_OR_GREATER
|
||||||
public override bool Equals(object? obj)
|
public override bool Equals(object? obj)
|
||||||
|
|||||||
@ -282,8 +282,6 @@ namespace Nerd_STF.Mathematics
|
|||||||
z = this.z;
|
z = this.z;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Modify(Action<Float4> action) => action(this);
|
|
||||||
|
|
||||||
public bool Equals(Float4 other) => w == other.w && x == other.x && y == other.y && z == other.z;
|
public bool Equals(Float4 other) => w == other.w && x == other.x && y == other.y && z == other.z;
|
||||||
#if CS8_OR_GREATER
|
#if CS8_OR_GREATER
|
||||||
public override bool Equals(object? obj)
|
public override bool Equals(object? obj)
|
||||||
|
|||||||
@ -207,8 +207,6 @@ namespace Nerd_STF.Mathematics
|
|||||||
y = this.y;
|
y = this.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Modify(Action<Int2> action) => action(this);
|
|
||||||
|
|
||||||
public bool Equals(Int2 other) => x == other.x && y == other.y;
|
public bool Equals(Int2 other) => x == other.x && y == other.y;
|
||||||
#if CS8_OR_GREATER
|
#if CS8_OR_GREATER
|
||||||
public override bool Equals(object? obj)
|
public override bool Equals(object? obj)
|
||||||
|
|||||||
@ -227,8 +227,6 @@ namespace Nerd_STF.Mathematics
|
|||||||
z = this.z;
|
z = this.z;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Modify(Action<Int3> action) => action(this);
|
|
||||||
|
|
||||||
public bool Equals(Int3 other) => x == other.x && y == other.y && z == other.z;
|
public bool Equals(Int3 other) => x == other.x && y == other.y && z == other.z;
|
||||||
#if CS8_OR_GREATER
|
#if CS8_OR_GREATER
|
||||||
public override bool Equals(object? obj)
|
public override bool Equals(object? obj)
|
||||||
|
|||||||
@ -239,8 +239,6 @@ namespace Nerd_STF.Mathematics
|
|||||||
z = this.z;
|
z = this.z;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Modify(Action<Int4> action) => action(this);
|
|
||||||
|
|
||||||
public bool Equals(Int4 other) => w == other.w && x == other.x && y == other.y && z == other.z;
|
public bool Equals(Int4 other) => w == other.w && x == other.x && y == other.y && z == other.z;
|
||||||
#if CS8_OR_GREATER
|
#if CS8_OR_GREATER
|
||||||
public override bool Equals(object? obj)
|
public override bool Equals(object? obj)
|
||||||
|
|||||||
@ -10,36 +10,15 @@ namespace Nerd_STF.Mathematics
|
|||||||
{
|
{
|
||||||
public static class MathE
|
public static class MathE
|
||||||
{
|
{
|
||||||
public static int Absolute(int value) => value < 0 ? -value : value;
|
public static int Abs(int value) => value < 0 ? -value : value;
|
||||||
public static double Absolute(double value) => value < 0 ? -value : value;
|
public static double Abs(double value) => value < 0 ? -value : value;
|
||||||
#if CS11_OR_GREATER
|
#if CS11_OR_GREATER
|
||||||
public static T Absolute<T>(T num) where T : INumber<T>
|
public static T Abs<T>(T num) where T : INumber<T>
|
||||||
{
|
{
|
||||||
return num < T.Zero ? -num : num;
|
return num < T.Zero ? -num : num;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
public static int AbsoluteMod(int value, int mod)
|
|
||||||
{
|
|
||||||
while (value >= mod) value -= mod;
|
|
||||||
while (value < 0) value += mod;
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
public static double AbsoluteMod(double value, double mod)
|
|
||||||
{
|
|
||||||
while (value >= mod) value -= mod;
|
|
||||||
while (value < 0) value += mod;
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
#if CS11_OR_GREATER
|
|
||||||
public static T AbsoluteMod<T>(T value, T mod) where T : INumber<T>
|
|
||||||
{
|
|
||||||
while (value >= mod) value -= mod;
|
|
||||||
while (value < T.Zero) value += mod;
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
public static int Average(IEnumerable<int> values)
|
public static int Average(IEnumerable<int> values)
|
||||||
{
|
{
|
||||||
int sum = 0;
|
int sum = 0;
|
||||||
@ -78,7 +57,7 @@ namespace Nerd_STF.Mathematics
|
|||||||
public static double Average(IEquation equ, double lowerBound, double upperBound, double epsilon = 1e-3)
|
public static double Average(IEquation equ, double lowerBound, double upperBound, double epsilon = 1e-3)
|
||||||
{
|
{
|
||||||
double sum = 0;
|
double sum = 0;
|
||||||
double steps = Absolute(upperBound - lowerBound) / epsilon;
|
double steps = Abs(upperBound - lowerBound) / epsilon;
|
||||||
for (double x = lowerBound; x <= upperBound; x += epsilon) sum += equ[x];
|
for (double x = lowerBound; x <= upperBound; x += epsilon) sum += equ[x];
|
||||||
return sum / steps;
|
return sum / steps;
|
||||||
}
|
}
|
||||||
@ -235,6 +214,9 @@ namespace Nerd_STF.Mathematics
|
|||||||
public static IEquation DynamicIntegral(IEquation equ, IEquation lower, IEquation upper) =>
|
public static IEquation DynamicIntegral(IEquation equ, IEquation lower, IEquation upper) =>
|
||||||
new Equation((double x) => equ.Integrate(lower[x], upper[x]));
|
new Equation((double x) => equ.Integrate(lower[x], upper[x]));
|
||||||
|
|
||||||
|
public static double EulersMethod(IEquation equ, double refX, double deltaX) =>
|
||||||
|
equ.Derive()[refX] * deltaX + equ[refX];
|
||||||
|
|
||||||
// TODO: Gamma function at some point.
|
// TODO: Gamma function at some point.
|
||||||
public static BigInteger FactorialBig(int num)
|
public static BigInteger FactorialBig(int num)
|
||||||
{
|
{
|
||||||
@ -521,6 +503,27 @@ namespace Nerd_STF.Mathematics
|
|||||||
return best;
|
return best;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int ModAbs(int value, int mod)
|
||||||
|
{
|
||||||
|
while (value >= mod) value -= mod;
|
||||||
|
while (value < 0) value += mod;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
public static double ModAbs(double value, double mod)
|
||||||
|
{
|
||||||
|
while (value >= mod) value -= mod;
|
||||||
|
while (value < 0) value += mod;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
#if CS11_OR_GREATER
|
||||||
|
public static T ModAbs<T>(T value, T mod) where T : INumber<T>
|
||||||
|
{
|
||||||
|
while (value >= mod) value -= mod;
|
||||||
|
while (value < T.Zero) value += mod;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
public static BigInteger NprBig(int n, int r) => FactorialBig(n - r + 1, n);
|
public static BigInteger NprBig(int n, int r) => FactorialBig(n - r + 1, n);
|
||||||
public static int Npr(int n, int r) => (int)Factorial(n - r + 1, n);
|
public static int Npr(int n, int r) => (int)Factorial(n - r + 1, n);
|
||||||
|
|
||||||
@ -623,6 +626,13 @@ namespace Nerd_STF.Mathematics
|
|||||||
public static IEquation Round(IEquation equ) =>
|
public static IEquation Round(IEquation equ) =>
|
||||||
new Equation((double x) => Round(equ.Get(x)));
|
new Equation((double x) => Round(equ.Get(x)));
|
||||||
|
|
||||||
|
#if CS11_OR_GREATER
|
||||||
|
public static int Sign<T>(T num) where T : INumber<T> =>
|
||||||
|
num > T.Zero ? 1 : num < T.Zero ? -1 : 0;
|
||||||
|
#endif
|
||||||
|
public static int Sign(double num) => num > 0 ? 1 : num < 0 ? -1 : 0;
|
||||||
|
public static int Sign(int num) => num > 0 ? 1 : num < 0 ? -1 : 0;
|
||||||
|
|
||||||
public static double Sin(double rad, int terms = 8)
|
public static double Sin(double rad, int terms = 8)
|
||||||
{
|
{
|
||||||
bool flip = false;
|
bool flip = false;
|
||||||
@ -702,6 +712,12 @@ namespace Nerd_STF.Mathematics
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
public static Linear TangentLine(IEquation equ, double x)
|
||||||
|
{
|
||||||
|
double slope = equ.Derive()[x];
|
||||||
|
return new Linear(slope, equ[x] - slope * x);
|
||||||
|
}
|
||||||
|
|
||||||
public static Polynomial TaylorSeries(IEquation equ, double x, int terms)
|
public static Polynomial TaylorSeries(IEquation equ, double x, int terms)
|
||||||
{
|
{
|
||||||
IEquation current = equ;
|
IEquation current = equ;
|
||||||
|
|||||||
@ -1,41 +1,94 @@
|
|||||||
using System.Numerics;
|
using Nerd_STF.Abstract;
|
||||||
|
using Nerd_STF.Helpers;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
|
|
||||||
namespace Nerd_STF.Mathematics.Numbers
|
namespace Nerd_STF.Mathematics.Numbers
|
||||||
{
|
{
|
||||||
public struct Fraction
|
public readonly struct Fraction : IComparable<Fraction>,
|
||||||
|
IEquatable<Fraction>,
|
||||||
|
IFormattable
|
||||||
|
#if CS11_OR_GREATER
|
||||||
|
,INumber<Fraction>,
|
||||||
|
IInterpolable<Fraction>,
|
||||||
|
IPresets1d<Fraction>,
|
||||||
|
IRoundable<Fraction>,
|
||||||
|
ISimpleMathOperations<Fraction>,
|
||||||
|
ISplittable<Fraction, (int[] nums, int[] dens)>
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
|
public static Fraction NaN => new Fraction(0, 0);
|
||||||
|
public static Fraction NegativeInfinity => new Fraction(-1, 0);
|
||||||
public static Fraction One => new Fraction(1, 1);
|
public static Fraction One => new Fraction(1, 1);
|
||||||
|
public static Fraction PositiveInfinity => new Fraction(1, 0);
|
||||||
public static Fraction Zero => new Fraction(0, 1);
|
public static Fraction Zero => new Fraction(0, 1);
|
||||||
|
|
||||||
public int numerator;
|
public int Numerator => num;
|
||||||
public int denominator;
|
public int Denominator => den;
|
||||||
|
|
||||||
|
public int Whole => num / den;
|
||||||
|
public Fraction Partial => new Fraction(num % den, den);
|
||||||
|
|
||||||
|
public Fraction Simplified
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
int newNum = num, newDen = den;
|
||||||
|
List<int> numFactors = new List<int>(MathE.PrimeFactors(MathE.Abs(num))),
|
||||||
|
denFactors = new List<int>(MathE.PrimeFactors(den));
|
||||||
|
foreach (int fac in numFactors)
|
||||||
|
{
|
||||||
|
if (!denFactors.Contains(fac)) continue;
|
||||||
|
newNum /= fac;
|
||||||
|
newDen /= fac;
|
||||||
|
denFactors.Remove(fac);
|
||||||
|
}
|
||||||
|
return new Fraction(newNum, newDen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public Fraction Reciprocal => new Fraction(den, num);
|
||||||
|
|
||||||
|
private readonly int num, den;
|
||||||
|
|
||||||
public Fraction(int numerator, int denominator)
|
public Fraction(int numerator, int denominator)
|
||||||
{
|
{
|
||||||
this.numerator = numerator;
|
num = numerator;
|
||||||
this.denominator = denominator;
|
den = denominator;
|
||||||
|
|
||||||
|
if (den < 0)
|
||||||
|
{
|
||||||
|
num = -num;
|
||||||
|
den = -den;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Fraction Approximate(double number, int iterations = 32)
|
public static Fraction Approximate(double number, int iterations = 32)
|
||||||
{
|
{
|
||||||
// Forget what this algorithm is called. When I remember, I'll put its
|
int num, den;
|
||||||
// Wikipedia page here.
|
|
||||||
|
|
||||||
if (number == 0) return Zero;
|
if (number == 0) return Zero;
|
||||||
else if (number == 1) return One;
|
else if (number == 1) return One;
|
||||||
|
else if (number % 1 == 0) return new Fraction((int)number, 1);
|
||||||
else if (number < 0)
|
else if (number < 0)
|
||||||
{
|
{
|
||||||
Fraction result = Approximate(-number, iterations);
|
Approximate(-number, iterations, out num, out den, out _);
|
||||||
result.numerator = -result.numerator;
|
num = -num;
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
else if (number > 1)
|
else if (number > 1)
|
||||||
{
|
{
|
||||||
|
Approximate(number % 1, iterations, out num, out den, out _);
|
||||||
int whole = (int)number;
|
int whole = (int)number;
|
||||||
Fraction result = Approximate(number % 1, iterations);
|
num += whole * den;
|
||||||
result.numerator += whole * result.denominator;
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
else Approximate(number, iterations, out num, out den, out _);
|
||||||
|
return new Fraction(num, den);
|
||||||
|
}
|
||||||
|
private static void Approximate(double number, int iterations, out int num, out int den, out double error)
|
||||||
|
{
|
||||||
|
// Forget what this algorithm is called. When I remember, I'll put its
|
||||||
|
// Wikipedia page here.
|
||||||
|
|
||||||
int minNum = 0, maxNum = 1, newNum = minNum + maxNum,
|
int minNum = 0, maxNum = 1, newNum = minNum + maxNum,
|
||||||
minDen = 1, maxDen = 1, newDen = minDen + maxDen;
|
minDen = 1, maxDen = 1, newDen = minDen + maxDen;
|
||||||
@ -57,12 +110,456 @@ namespace Nerd_STF.Mathematics.Numbers
|
|||||||
newDen = minDen + maxDen;
|
newDen = minDen + maxDen;
|
||||||
newVal = (double)newNum / newDen;
|
newVal = (double)newNum / newDen;
|
||||||
}
|
}
|
||||||
return new Fraction(newNum, newDen);
|
num = newNum;
|
||||||
|
den = newDen;
|
||||||
|
error = MathE.Abs(newVal - number);
|
||||||
}
|
}
|
||||||
|
|
||||||
public double GetValue() => (double)numerator / denominator;
|
public static IEnumerable<Fraction> Egyptian(double number, int maxTerms) =>
|
||||||
|
Egyptian(Approximate(number, 256), maxTerms);
|
||||||
|
public static Fraction[] Egyptian(Fraction number, int maxTerms)
|
||||||
|
{
|
||||||
|
List<Fraction> parts = new List<Fraction>();
|
||||||
|
int terms = 0;
|
||||||
|
foreach (Fraction part in EgyptianE(number))
|
||||||
|
{
|
||||||
|
parts.Add(part);
|
||||||
|
terms++;
|
||||||
|
if (terms >= maxTerms) break;
|
||||||
|
}
|
||||||
|
return parts.ToArray();
|
||||||
|
}
|
||||||
|
public static IEnumerable<Fraction> EgyptianE(double number) =>
|
||||||
|
EgyptianE(Approximate(number, 256));
|
||||||
|
public static IEnumerable<Fraction> EgyptianE(Fraction number)
|
||||||
|
{
|
||||||
|
int wholes = number.Whole;
|
||||||
|
if (wholes > 0) yield return new Fraction(wholes, 1);
|
||||||
|
|
||||||
public override string ToString() => $"{numerator} / {denominator}";
|
number = number.Partial;
|
||||||
|
int newDen = 2, curNum = number.num, curDen = number.den;
|
||||||
|
while (curNum > 0 && newDen <= curDen)
|
||||||
|
{
|
||||||
|
if (curNum * newDen >= curDen)
|
||||||
|
{
|
||||||
|
yield return new Fraction(1, newDen);
|
||||||
|
curNum = curNum * newDen - curDen;
|
||||||
|
curDen *= newDen;
|
||||||
|
}
|
||||||
|
else newDen++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if CS8_OR_GREATER
|
||||||
|
public static Fraction Parse(string? str) =>
|
||||||
|
#else
|
||||||
|
public static Fraction Parse(string str) =>
|
||||||
|
#endif
|
||||||
|
str is null ? NaN : Parse(str.AsSpan());
|
||||||
|
public static Fraction Parse(ReadOnlySpan<char> str)
|
||||||
|
{
|
||||||
|
if (str.Length == 0) return NaN;
|
||||||
|
|
||||||
|
ReadOnlySpan<char> numSpan, denSpan;
|
||||||
|
str = str.Trim();
|
||||||
|
char first = str[0];
|
||||||
|
if (first == '\\')
|
||||||
|
{
|
||||||
|
// TeX format.
|
||||||
|
if (str.Length >= 5 && str.StartsWith("\\frac".AsSpan())) str = str.Slice(5);
|
||||||
|
else if (str.Length >= 6 && str.Slice(2, 4).Equals("frac".AsSpan(), StringComparison.Ordinal)) str = str.Slice(6); // Allows for \sfrac or things like that.
|
||||||
|
else goto _fail;
|
||||||
|
|
||||||
|
if (!str.StartsWith("{".AsSpan()) || !str.EndsWith("}".AsSpan())) goto _fail;
|
||||||
|
int separator = str.IndexOf(',');
|
||||||
|
if (separator == -1 || separator == 1 || separator == str.Length - 2) goto _fail;
|
||||||
|
|
||||||
|
numSpan = str.Slice(1, separator - 1);
|
||||||
|
denSpan = str.Slice(separator + 1, str.Length - separator - 2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Standard fraction format.
|
||||||
|
char[] allowedSeparators = new char[] { '/', ':' };
|
||||||
|
int separator = -1;
|
||||||
|
foreach (char c in allowedSeparators)
|
||||||
|
{
|
||||||
|
int newSep = str.IndexOf(c);
|
||||||
|
if (newSep == -1) continue;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (separator != -1) goto _fail; // More than one separator.
|
||||||
|
else separator = newSep;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (separator == 0 || separator == str.Length - 1) goto _fail;
|
||||||
|
|
||||||
|
numSpan = str.Slice(0, separator);
|
||||||
|
denSpan = str.Slice(separator + 1, str.Length - separator - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int top = ParseHelper.ParseDoubleWholeDecimals(numSpan, out int topPlaces),
|
||||||
|
bot = ParseHelper.ParseDoubleWholeDecimals(denSpan, out int botPlaces);
|
||||||
|
int topDen = 1, botDen = 1;
|
||||||
|
for (int i = 0; i < topPlaces; i++) topDen *= 10;
|
||||||
|
for (int i = 0; i < botPlaces; i++) botDen *= 10;
|
||||||
|
|
||||||
|
Fraction topF = new Fraction(top, topDen), botF = new Fraction(bot, botDen);
|
||||||
|
return topF / botF;
|
||||||
|
|
||||||
|
_fail:
|
||||||
|
throw new FormatException("Cannot parse fraction from span.");
|
||||||
|
}
|
||||||
|
#if CS8_OR_GREATER
|
||||||
|
public static bool TryParse(string? str, out Fraction frac) =>
|
||||||
|
#else
|
||||||
|
public static bool TryParse(string str, out Fraction frac) =>
|
||||||
|
#endif
|
||||||
|
TryParse(str.AsSpan(), out frac);
|
||||||
|
public static bool TryParse(ReadOnlySpan<char> str, out Fraction frac)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
frac = Parse(str);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
frac = NaN;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Fraction Abs(Fraction val) => new Fraction(MathE.Abs(val.num), val.den);
|
||||||
|
public static Fraction Ceiling(Fraction val)
|
||||||
|
{
|
||||||
|
int newNum = val.num;
|
||||||
|
if (val.num % val.den != 0)
|
||||||
|
{
|
||||||
|
if (val.num > 0) newNum += val.den - (val.num % val.den);
|
||||||
|
else newNum -= val.num % val.den;
|
||||||
|
}
|
||||||
|
return new Fraction(newNum, val.den);
|
||||||
|
}
|
||||||
|
public static Fraction Clamp(Fraction val, Fraction min, Fraction max)
|
||||||
|
{
|
||||||
|
int lcm = MathE.Lcm(val.den, min.den, max.den);
|
||||||
|
int valFac = val.den / lcm, minFac = min.den / lcm, maxFac = max.den / lcm;
|
||||||
|
int trueVal = val.num * valFac, trueMin = min.num * minFac, trueMax = max.num * maxFac;
|
||||||
|
if (trueVal > trueMax) return max;
|
||||||
|
else if (trueVal < trueMin) return min;
|
||||||
|
else return val;
|
||||||
|
}
|
||||||
|
public static Fraction Floor(Fraction val)
|
||||||
|
{
|
||||||
|
int newNum = val.num;
|
||||||
|
if (val.num % val.den != 0)
|
||||||
|
{
|
||||||
|
if (val.num > 0) newNum -= val.num % val.den;
|
||||||
|
else newNum -= val.den + (val.num % val.den);
|
||||||
|
}
|
||||||
|
return new Fraction(newNum, val.den);
|
||||||
|
}
|
||||||
|
public static Fraction Lerp(Fraction a, Fraction b, double t, bool clamp = true, bool fast = false)
|
||||||
|
{
|
||||||
|
if (fast)
|
||||||
|
{
|
||||||
|
int aNum = a.num * b.den, bNum = b.num * a.den, cDen = a.den * b.den;
|
||||||
|
int cNum = (int)(aNum + t * (bNum - aNum));
|
||||||
|
return new Fraction(cNum, cDen);
|
||||||
|
}
|
||||||
|
else return Approximate(MathE.Lerp(a, b, t, clamp), 128);
|
||||||
|
}
|
||||||
|
#if CS11_OR_GREATER
|
||||||
|
static Fraction IInterpolable<Fraction>.Lerp(Fraction a, Fraction b, double t, bool clamp) =>
|
||||||
|
Lerp(a, b, t, clamp, false);
|
||||||
|
#endif
|
||||||
|
public static Fraction Product(IEnumerable<Fraction> vals)
|
||||||
|
{
|
||||||
|
bool any = false;
|
||||||
|
int resultNum = 1, resultDen = 1;
|
||||||
|
foreach (Fraction frac in vals)
|
||||||
|
{
|
||||||
|
any = true;
|
||||||
|
resultNum *= frac.num;
|
||||||
|
resultDen *= frac.den;
|
||||||
|
}
|
||||||
|
return any ? new Fraction(resultNum, resultDen) : Zero;
|
||||||
|
}
|
||||||
|
public static Fraction Round(Fraction val)
|
||||||
|
{
|
||||||
|
int half = val.den / 2;
|
||||||
|
int newNum = val.num;
|
||||||
|
if (val.num > 0)
|
||||||
|
{
|
||||||
|
if (val.num % val.den > half) newNum += val.den - (val.num % val.den);
|
||||||
|
else newNum -= val.num % val.den;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (-val.num % val.den > half) newNum -= val.den + (val.num % val.den);
|
||||||
|
else newNum -= val.num % val.den;
|
||||||
|
}
|
||||||
|
return new Fraction(newNum, val.den);
|
||||||
|
}
|
||||||
|
public static Fraction Sum(IEnumerable<Fraction> vals)
|
||||||
|
{
|
||||||
|
bool any = false;
|
||||||
|
Fraction result = Zero;
|
||||||
|
foreach (Fraction frac in vals)
|
||||||
|
{
|
||||||
|
any = true;
|
||||||
|
result += frac;
|
||||||
|
}
|
||||||
|
return any ? result : NaN;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if CS8_OR_GREATER
|
||||||
|
private static bool TryConvertFrom(object? value, out Fraction result)
|
||||||
|
#else
|
||||||
|
private static bool TryConvertFrom(object value, out Fraction result)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
if (value is null)
|
||||||
|
{
|
||||||
|
result = NaN;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (value is Fraction valueFrac) result = valueFrac;
|
||||||
|
else if (value is double valueDouble) result = Approximate(valueDouble);
|
||||||
|
else if (value is float valueSingle) result = Approximate(valueSingle);
|
||||||
|
#if NET5_0_OR_GREATER
|
||||||
|
else if (value is Half valueHalf) result = Approximate((double)valueHalf);
|
||||||
|
#endif
|
||||||
|
#if NET7_0_OR_GREATER
|
||||||
|
else if (value is UInt128 valueUInt128) result = new Fraction((int)valueUInt128, 1);
|
||||||
|
else if (value is Int128 valueInt128) result = new Fraction((int)valueInt128, 1);
|
||||||
|
#endif
|
||||||
|
else if (value is ulong valueUInt64) result = new Fraction((int)valueUInt64, 1);
|
||||||
|
else if (value is long valueInt64) result = new Fraction((int)valueInt64, 1);
|
||||||
|
else if (value is uint valueUInt32) result = new Fraction((int)valueUInt32, 1);
|
||||||
|
else if (value is int valueInt32) result = new Fraction(valueInt32, 1);
|
||||||
|
else if (value is ushort valueUInt16) result = new Fraction(valueUInt16, 1);
|
||||||
|
else if (value is short valueInt16) result = new Fraction(valueInt16, 1);
|
||||||
|
else if (value is byte valueUInt8) result = new Fraction(valueUInt8, 1);
|
||||||
|
else if (value is sbyte valueInt8) result = new Fraction(valueInt8, 1);
|
||||||
|
else if (value is IntPtr valueInt) result = new Fraction((int)valueInt, 1);
|
||||||
|
else if (value is UIntPtr valueUInt) result = new Fraction((int)valueUInt, 1);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = NaN;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsCanonical(Fraction val)
|
||||||
|
{
|
||||||
|
IEnumerable<int> factorsNum = MathE.PrimeFactorsE(MathE.Abs(val.num)),
|
||||||
|
factorsDen = MathE.PrimeFactorsE(val.den),
|
||||||
|
shared = factorsNum.Where(x => factorsDen.Contains(x));
|
||||||
|
return shared.Any();
|
||||||
|
}
|
||||||
|
public static bool IsEvenInteger(Fraction val) =>
|
||||||
|
val.num % val.den == 0 && val.num / val.den % 2 == 0;
|
||||||
|
public static bool IsFinite(Fraction val) => val.den != 0 || val.num != 0;
|
||||||
|
public static bool IsInfinity(Fraction val) => val.den == 0 && val.num != 0;
|
||||||
|
public static bool IsInteger(Fraction val) => val.num % val.den == 0;
|
||||||
|
public static bool IsNaN(Fraction val) => val.num == 0 && val.den == 0;
|
||||||
|
public static bool IsNegative(Fraction val) => val.num < 0;
|
||||||
|
public static bool IsNegativeInfinity(Fraction val) => val.den == 0 && val.num < 0;
|
||||||
|
public static bool IsNormal(Fraction val) => val.den != 0 && val.num != 0;
|
||||||
|
public static bool IsOddInteger(Fraction val) =>
|
||||||
|
val.num % val.den == 0 && val.num / val.den % 2 == 1;
|
||||||
|
public static bool IsPositive(Fraction val) => val.num > 0;
|
||||||
|
public static bool IsPositiveInfinity(Fraction val) => val.den == 0 && val.num > 0;
|
||||||
|
public static bool IsRealNumber(Fraction val) => val.den != 0;
|
||||||
|
public static bool IsZero(Fraction val) => val.num == 0 && val.den != 0;
|
||||||
|
public static Fraction MaxMagnitude(Fraction a, Fraction b) => a > b ? a : b;
|
||||||
|
public static Fraction MaxMagnitudeNumber(Fraction a, Fraction b) => a > b ? a : b;
|
||||||
|
public static Fraction MinMagnitude(Fraction a, Fraction b) => a < b ? a : b;
|
||||||
|
public static Fraction MinMagnitudeNumber(Fraction a, Fraction b) => a < b ? a : b;
|
||||||
|
#if CS11_OR_GREATER
|
||||||
|
static bool INumberBase<Fraction>.IsComplexNumber(Fraction val) => false;
|
||||||
|
static bool INumberBase<Fraction>.IsImaginaryNumber(Fraction val) => false;
|
||||||
|
static bool INumberBase<Fraction>.IsSubnormal(Fraction val) => false; // What does this mean???
|
||||||
|
static Fraction INumberBase<Fraction>.Parse(string? str, NumberStyles style, IFormatProvider? provider) => Parse(str);
|
||||||
|
static Fraction INumberBase<Fraction>.Parse(ReadOnlySpan<char> str, NumberStyles style, IFormatProvider? provider) => Parse(str);
|
||||||
|
static bool INumberBase<Fraction>.TryParse(string? str, NumberStyles style, IFormatProvider? provider, out Fraction frac) => TryParse(str, out frac);
|
||||||
|
static bool INumberBase<Fraction>.TryParse(ReadOnlySpan<char> str, NumberStyles style, IFormatProvider? provider, out Fraction frac) => TryParse(str, out frac);
|
||||||
|
static Fraction IParsable<Fraction>.Parse(string? str, IFormatProvider? provider) => Parse(str);
|
||||||
|
static bool IParsable<Fraction>.TryParse(string? str, IFormatProvider? provider, out Fraction frac) => TryParse(str, out frac);
|
||||||
|
static Fraction ISpanParsable<Fraction>.Parse(ReadOnlySpan<char> str, IFormatProvider? provider) => Parse(str);
|
||||||
|
static bool ISpanParsable<Fraction>.TryParse(ReadOnlySpan<char> str, IFormatProvider? provider, out Fraction frac) => TryParse(str, out frac);
|
||||||
|
static Fraction IAdditiveIdentity<Fraction, Fraction>.AdditiveIdentity => Zero;
|
||||||
|
static Fraction IMultiplicativeIdentity<Fraction, Fraction>.MultiplicativeIdentity => One;
|
||||||
|
static int INumberBase<Fraction>.Radix => 2; // Not super sure what to put here.
|
||||||
|
|
||||||
|
private static bool TryConvertTo<T>(Fraction frac, out T value)
|
||||||
|
{
|
||||||
|
object? tempValue;
|
||||||
|
|
||||||
|
if (typeof(T) == typeof(Fraction)) tempValue = frac;
|
||||||
|
else if (typeof(T) == typeof(double)) tempValue = frac.GetValue();
|
||||||
|
else if (typeof(T) == typeof(float)) tempValue = (float)frac.GetValue();
|
||||||
|
#if NET5_0_OR_GREATER
|
||||||
|
else if (typeof(T) == typeof(Half)) tempValue = (Half)frac.GetValue();
|
||||||
|
#endif
|
||||||
|
#if NET7_0_OR_GREATER
|
||||||
|
else if (typeof(T) == typeof(UInt128)) tempValue = (UInt128)frac.GetValue();
|
||||||
|
else if (typeof(T) == typeof(Int128)) tempValue = (Int128)frac.GetValue();
|
||||||
|
#endif
|
||||||
|
else if (typeof(T) == typeof(ulong)) tempValue = (ulong)frac.GetValue();
|
||||||
|
else if (typeof(T) == typeof(long)) tempValue = (long)frac.GetValue();
|
||||||
|
else if (typeof(T) == typeof(uint)) tempValue = (uint)frac.GetValue();
|
||||||
|
else if (typeof(T) == typeof(int)) tempValue = (int)frac.GetValue();
|
||||||
|
else if (typeof(T) == typeof(ushort)) tempValue = (ushort)frac.GetValue();
|
||||||
|
else if (typeof(T) == typeof(short)) tempValue = (short)frac.GetValue();
|
||||||
|
else if (typeof(T) == typeof(byte)) tempValue = (byte)frac.GetValue();
|
||||||
|
else if (typeof(T) == typeof(sbyte)) tempValue = (sbyte)frac.GetValue();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
value = default!;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = (T)tempValue;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
static bool INumberBase<Fraction>.TryConvertFromChecked<TOther>(TOther value, out Fraction result) => TryConvertFrom(value, out result);
|
||||||
|
static bool INumberBase<Fraction>.TryConvertFromSaturating<TOther>(TOther value, out Fraction result) => TryConvertFrom(value, out result);
|
||||||
|
static bool INumberBase<Fraction>.TryConvertFromTruncating<TOther>(TOther value, out Fraction result) => TryConvertFrom(value, out result);
|
||||||
|
static bool INumberBase<Fraction>.TryConvertToChecked<TOther>(Fraction value, out TOther result) => TryConvertTo(value, out result);
|
||||||
|
static bool INumberBase<Fraction>.TryConvertToSaturating<TOther>(Fraction value, out TOther result) => TryConvertTo(value, out result);
|
||||||
|
static bool INumberBase<Fraction>.TryConvertToTruncating<TOther>(Fraction value, out TOther result) => TryConvertTo(value, out result);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public static (int[] nums, int[] dens) SplitArray(IEnumerable<Fraction> vals)
|
||||||
|
{
|
||||||
|
int count = vals.Count();
|
||||||
|
int[] nums = new int[count], dens = new int[count];
|
||||||
|
int index = 0;
|
||||||
|
foreach (Fraction val in vals)
|
||||||
|
{
|
||||||
|
nums[index] = val.num;
|
||||||
|
dens[index] = val.den;
|
||||||
|
}
|
||||||
|
return (nums, dens);
|
||||||
|
}
|
||||||
|
|
||||||
|
public double GetValue() => (double)num / den;
|
||||||
|
|
||||||
|
#if CS8_OR_GREATER
|
||||||
|
public int CompareTo(object? other)
|
||||||
|
#else
|
||||||
|
public int CompareTo(object other)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
if (other is null) return 1;
|
||||||
|
else if (other is Fraction otherFrac) return CompareTo(otherFrac);
|
||||||
|
else if (TryConvertFrom(other, out Fraction otherConvert)) return CompareTo(otherConvert);
|
||||||
|
else return -1;
|
||||||
|
}
|
||||||
|
public int CompareTo(Fraction other) => (num * other.den).CompareTo(other.num * den);
|
||||||
|
public bool Equals(Fraction other) => num * other.den == other.num * den;
|
||||||
|
#if CS8_OR_GREATER
|
||||||
|
public override bool Equals(object? other)
|
||||||
|
#else
|
||||||
|
public override bool Equals(object other)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
if (other is null) return false;
|
||||||
|
else if (other is Fraction otherFrac) return Equals(otherFrac);
|
||||||
|
else if (TryConvertFrom(other, out Fraction otherConvert)) return Equals(otherConvert);
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
public override int GetHashCode() =>
|
||||||
|
(int)(((uint)num.GetHashCode() & 0xFFFF0000u) | ((uint)den.GetHashCode() & 0x0000FFFFu));
|
||||||
|
#if CS8_OR_GREATER
|
||||||
|
public override string ToString() => ToString(null, null);
|
||||||
|
public string ToString(string? format) => ToString(format, null);
|
||||||
|
public string ToString(IFormatProvider? provider) => ToString(null, provider);
|
||||||
|
public string ToString(string? format, IFormatProvider? provider) => $"{num.ToString(format)}/{den.ToString(format)}";
|
||||||
|
#else
|
||||||
|
public override string ToString() => ToString(null, null);
|
||||||
|
public string ToString(string format) => ToString(format, null);
|
||||||
|
public string ToString(IFormatProvider provider) => ToString(null, provider);
|
||||||
|
public string ToString(string format, IFormatProvider provider) => $"{num.ToString(format)}/{den.ToString(format)}";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if CS11_OR_GREATER
|
||||||
|
public bool TryFormat(Span<char> dest, out int charsWritten, ReadOnlySpan<char> format, IFormatProvider? provider)
|
||||||
|
{
|
||||||
|
// Not really great, but I don't want to do this right now.
|
||||||
|
string result = ToString(format.ToString(), provider);
|
||||||
|
result.CopyTo(dest);
|
||||||
|
charsWritten = result.Length;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public static Fraction operator +(Fraction a) => a;
|
||||||
|
public static Fraction operator +(Fraction a, Fraction b)
|
||||||
|
{
|
||||||
|
int lcm = MathE.Lcm(a.den, b.den);
|
||||||
|
int facA = lcm / a.den, facB = lcm / b.den;
|
||||||
|
return new Fraction(a.num * facA + b.num * facB, lcm);
|
||||||
|
}
|
||||||
|
public static Fraction operator +(Fraction a, int b) => new Fraction(a.num + b * a.den, a.den);
|
||||||
|
public static Fraction operator +(Fraction a, double b) => a * Approximate(b);
|
||||||
|
public static Fraction operator ++(Fraction a) => new Fraction(a.num + a.den, a.den);
|
||||||
|
public static Fraction operator -(Fraction a) => new Fraction(-a.num, a.den);
|
||||||
|
public static Fraction operator -(Fraction a, Fraction b)
|
||||||
|
{
|
||||||
|
int lcm = MathE.Lcm(a.den, b.den);
|
||||||
|
int facA = lcm / a.den, facB = lcm / b.den;
|
||||||
|
return new Fraction(a.num * facA - b.num * facB, lcm);
|
||||||
|
}
|
||||||
|
public static Fraction operator -(Fraction a, int b) => new Fraction(a.num - b * a.den, a.den);
|
||||||
|
public static Fraction operator -(Fraction a, double b) => a * Approximate(b);
|
||||||
|
public static Fraction operator --(Fraction a) => new Fraction(a.num - a.den, a.den);
|
||||||
|
public static Fraction operator *(Fraction a, Fraction b) => new Fraction(a.num * b.num, a.den * b.den);
|
||||||
|
public static Fraction operator *(Fraction a, int b) => new Fraction(a.num * b, a.den);
|
||||||
|
public static Fraction operator *(Fraction a, double b) => a * Approximate(b);
|
||||||
|
public static Fraction operator /(Fraction a, Fraction b) => new Fraction(a.num * b.den, a.den * b.num);
|
||||||
|
public static Fraction operator /(Fraction a, int b) => new Fraction(a.num, a.den * b);
|
||||||
|
public static Fraction operator /(Fraction a, double b) => a / Approximate(b);
|
||||||
|
public static Fraction operator %(Fraction a, Fraction b)
|
||||||
|
{
|
||||||
|
// c = a / b
|
||||||
|
// f = b * mod(c, 1)
|
||||||
|
int cNum = a.num * b.den, cDen = a.den * b.num;
|
||||||
|
if (cDen < 0)
|
||||||
|
{
|
||||||
|
cNum = -cNum;
|
||||||
|
cDen = -cDen;
|
||||||
|
}
|
||||||
|
cNum = MathE.ModAbs(cNum, cDen); // Fractional portion.
|
||||||
|
return new Fraction(b.num * cNum, b.den * cDen);
|
||||||
|
}
|
||||||
|
public static Fraction operator %(Fraction a, int b)
|
||||||
|
{
|
||||||
|
// c = a / b
|
||||||
|
// f = b * mod(c, 1)
|
||||||
|
int cNum = a.num, cDen = a.den * b;
|
||||||
|
if (cDen < 0)
|
||||||
|
{
|
||||||
|
cNum = -cNum;
|
||||||
|
cDen = -cDen;
|
||||||
|
}
|
||||||
|
cNum = MathE.ModAbs(cNum, cDen); // Fractional portion.
|
||||||
|
return new Fraction(b * cNum, cDen);
|
||||||
|
}
|
||||||
|
public static Fraction operator %(Fraction a, double b) => a % Approximate(b);
|
||||||
|
public static Fraction operator ^(Fraction a, Fraction b) => new Fraction(a.num + b.num, a.den + b.den);
|
||||||
|
public static Fraction operator ~(Fraction a) => a.Reciprocal;
|
||||||
|
public static bool operator ==(Fraction a, Fraction b) => a.Equals(b);
|
||||||
|
public static bool operator !=(Fraction a, Fraction b) => !a.Equals(b);
|
||||||
|
public static bool operator >(Fraction a, Fraction b) => a.CompareTo(b) > 0;
|
||||||
|
public static bool operator <(Fraction a, Fraction b) => a.CompareTo(b) < 0;
|
||||||
|
public static bool operator >=(Fraction a, Fraction b) => a.CompareTo(b) >= 0;
|
||||||
|
public static bool operator <=(Fraction a, Fraction b) => a.CompareTo(b) <= 0;
|
||||||
|
// TODO: Comparisons with a double on the right (maybe).
|
||||||
|
|
||||||
public static implicit operator double(Fraction frac) => frac.GetValue();
|
public static implicit operator double(Fraction frac) => frac.GetValue();
|
||||||
public static explicit operator Fraction(double num) => Approximate(num);
|
public static explicit operator Fraction(double num) => Approximate(num);
|
||||||
|
|||||||
@ -1,11 +1,18 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<!-- General stuff -->
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netstandard1.1;netstandard1.3;netstandard2.1;netcoreapp3.0;net5.0;net7.0</TargetFrameworks>
|
<TargetFrameworks>netstandard1.1;netstandard1.3;netstandard2.1;netcoreapp3.0;net5.0;net7.0</TargetFrameworks>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<ProduceReferenceAssembly>True</ProduceReferenceAssembly>
|
<ProduceReferenceAssembly>True</ProduceReferenceAssembly>
|
||||||
<DebugType>embedded</DebugType>
|
<DebugType>portable</DebugType>
|
||||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<!-- NuGet package customization. -->
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
<Title>Nerd_STF</Title>
|
<Title>Nerd_STF</Title>
|
||||||
<Version>3.0.0-beta2</Version>
|
<Version>3.0.0-beta2</Version>
|
||||||
<Authors>That_One_Nerd</Authors>
|
<Authors>That_One_Nerd</Authors>
|
||||||
@ -14,7 +21,12 @@
|
|||||||
<PackageIcon>Logo Square.png</PackageIcon>
|
<PackageIcon>Logo Square.png</PackageIcon>
|
||||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
<RepositoryUrl>https://github.com/That-One-Nerd/Nerd_STF</RepositoryUrl>
|
<RepositoryUrl>https://github.com/That-One-Nerd/Nerd_STF</RepositoryUrl>
|
||||||
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
|
<IncludeSymbols>True</IncludeSymbols>
|
||||||
|
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||||
<PackageTags>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;equation;equations;polynomial;quadratic;linear equation</PackageTags>
|
<PackageTags>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;equation;equations;polynomial;quadratic;linear equation</PackageTags>
|
||||||
|
|
||||||
|
<!-- Sorry this is stupidly long, wish I could have linked a markdown file instead. -->
|
||||||
<PackageReleaseNotes># Nerd_STF v3.0-beta1
|
<PackageReleaseNotes># Nerd_STF v3.0-beta1
|
||||||
|
|
||||||
Hi! Pretty much nothing has remained the same from version 2. There are plenty of breaking changes, and the betas will have plenty of missing features from 2.4.1. The betas will continue until every feature from 2.4.1 has been added to 3.0 or scrapped.
|
Hi! Pretty much nothing has remained the same from version 2. There are plenty of breaking changes, and the betas will have plenty of missing features from 2.4.1. The betas will continue until every feature from 2.4.1 has been added to 3.0 or scrapped.
|
||||||
@ -123,78 +135,48 @@ I've tried to use this library when working with Windows Forms a few times. Prob
|
|||||||
---
|
---
|
||||||
|
|
||||||
Anyway, that's most of the big changes! I don't know if I'll do the full changelog like I have before. It takes a really long time to compile for large updates. We'll see. Thanks for checking out the update and I hope you use it well (or wait for the release version, that's fine too)!</PackageReleaseNotes>
|
Anyway, that's most of the big changes! I don't know if I'll do the full changelog like I have before. It takes a really long time to compile for large updates. We'll see. Thanks for checking out the update and I hope you use it well (or wait for the release version, that's fine too)!</PackageReleaseNotes>
|
||||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
|
||||||
<IncludeSymbols>True</IncludeSymbols>
|
|
||||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<!-- ItemGroup customization based on framework. -->
|
||||||
|
<!-- Mostly used to reference system packages that are not included in this version of .NET Standard. -->
|
||||||
|
<!-- TODO: Maybe this isn't good practice, and we should define environment variables for specific features (tuples, drawing, etc) instead? -->
|
||||||
|
|
||||||
|
<ItemGroup Condition="'$(TargetFramework)'=='netstandard1.1'">
|
||||||
|
<PackageReference Include="System.Drawing.Primitives" Version="4.3.0" />
|
||||||
|
<PackageReference Include="System.Memory" Version="4.5.5" />
|
||||||
|
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup Condition="'$(TargetFramework)'=='netstandard1.3'">
|
||||||
|
<PackageReference Include="System.Drawing.Primitives" Version="4.3.0" />
|
||||||
|
<PackageReference Include="System.Memory" Version="4.5.5" />
|
||||||
|
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- PropertyGroup customization based on framework. -->
|
||||||
|
<!-- Used to define environment variables based on features the framework supports. -->
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(TargetFramework)'=='netstandard2.1'">
|
<PropertyGroup Condition="'$(TargetFramework)'=='netstandard2.1'">
|
||||||
<DefineConstants>$(DefineConstants);CS8_OR_GREATER</DefineConstants>
|
<DefineConstants>$(DefineConstants);CS8_OR_GREATER;SPAN_SUPPORT</DefineConstants>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(TargetFramework)'=='netcoreapp3.0'">
|
<PropertyGroup Condition="'$(TargetFramework)'=='netcoreapp3.0'">
|
||||||
<DefineConstants>$(DefineConstants);CS8_OR_GREATER</DefineConstants>
|
<DefineConstants>$(DefineConstants);CS8_OR_GREATER;SPAN_SUPPORT</DefineConstants>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(TargetFramework)'=='net5.0'">
|
<PropertyGroup Condition="'$(TargetFramework)'=='net5.0'">
|
||||||
<DefineConstants>$(DefineConstants);CS8_OR_GREATER;CS9_OR_GREATER</DefineConstants>
|
<DefineConstants>$(DefineConstants);CS8_OR_GREATER;CS9_OR_GREATER;SPAN_SUPPORT</DefineConstants>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(TargetFramework)'=='net7.0'">
|
<PropertyGroup Condition="'$(TargetFramework)'=='net7.0'">
|
||||||
<DefineConstants>$(DefineConstants);CS10_OR_GREATER;CS11_OR_GREATER;CS8_OR_GREATER;CS9_OR_GREATER</DefineConstants>
|
<DefineConstants>$(DefineConstants);CS10_OR_GREATER;CS11_OR_GREATER;CS8_OR_GREATER;CS9_OR_GREATER;SPAN_SUPPORT</DefineConstants>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|netstandard1.1|AnyCPU'">
|
<!-- Pack extra stuff into the NuGet package. -->
|
||||||
<DebugType>portable</DebugType>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|netstandard1.3|AnyCPU'">
|
|
||||||
<DebugType>portable</DebugType>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|netstandard2.1|AnyCPU'">
|
|
||||||
<DebugType>portable</DebugType>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|netcoreapp3.0|AnyCPU'">
|
|
||||||
<DebugType>portable</DebugType>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net5.0|AnyCPU'">
|
|
||||||
<DebugType>portable</DebugType>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net7.0|AnyCPU'">
|
|
||||||
<DebugType>portable</DebugType>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|netstandard1.1|AnyCPU'">
|
|
||||||
<DebugType>portable</DebugType>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|netstandard1.3|AnyCPU'">
|
|
||||||
<DebugType>portable</DebugType>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|netstandard2.1|AnyCPU'">
|
|
||||||
<DebugType>portable</DebugType>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|netcoreapp3.0|AnyCPU'">
|
|
||||||
<DebugType>portable</DebugType>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net5.0|AnyCPU'">
|
|
||||||
<DebugType>portable</DebugType>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net7.0|AnyCPU'">
|
|
||||||
<DebugType>portable</DebugType>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="..\Extras\Logo Square.png">
|
<None Include="..\Extras\Logo Square.png">
|
||||||
@ -205,11 +187,14 @@ Anyway, that's most of the big changes! I don't know if I'll do the full changel
|
|||||||
<Pack>True</Pack>
|
<Pack>True</Pack>
|
||||||
<PackagePath>\</PackagePath>
|
<PackagePath>\</PackagePath>
|
||||||
</None>
|
</None>
|
||||||
</ItemGroup>
|
<None Include="..\Changelog.md">
|
||||||
|
<Pack>True</Pack>
|
||||||
<ItemGroup>
|
<PackagePath>\</PackagePath>
|
||||||
<PackageReference Include="System.Drawing.Primitives" Version="4.3.0" />
|
</None>
|
||||||
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
<None Include="..\LICENSE.md">
|
||||||
|
<Pack>True</Pack>
|
||||||
|
<PackagePath>\</PackagePath>
|
||||||
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user