That-One-Nerd 516e70709d made the rational struct immutable
ill add a mutable equivalent in 2.4.1
2023-04-02 17:40:25 -04:00

178 lines
7.1 KiB
C#

namespace Nerd_STF.Mathematics;
public readonly 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>, IIndexGet<int>, IIndexRangeGet<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 readonly int numerator;
public readonly int denominator;
public Rational Reciprocal => new(denominator, numerator);
public Rational Simplified
{
get
{
int[] denFactors = Mathf.PrimeFactors(denominator);
int newNum = numerator,
newDen = denominator;
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)
{
this.numerator = numerator * Math.Sign(denominator);
this.denominator = Mathf.Absolute(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 => numerator,
1 => denominator,
_ => throw new IndexOutOfRangeException(nameof(index))
};
}
public int this[Index index]
{
get => this[index.IsFromEnd ? 2 - index.Value : index.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();
}
}
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.numerator), value.denominator);
public static Rational Average(params Rational[] vals) => Sum(vals) / (float)vals.Length;
public static int Ceiling(Rational r)
{
int mod = r.numerator % r.denominator;
if (mod == 0) return r.numerator / r.denominator;
return r.numerator + (r.denominator - 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.numerator / val.denominator;
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.numerator, r.denominator) / r.denominator;
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].numerator;
dens[i] = vals[i].denominator;
}
return (nums, dens);
}
public float GetValue() => numerator / (float)denominator;
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.numerator == otherSim.numerator &&
thisSim.denominator == otherSim.denominator;
}
public bool Equals(float other) => GetValue() == other;
public override int GetHashCode() => base.GetHashCode();
private bool PrintMembers(StringBuilder builder)
{
builder.Append(numerator);
builder.Append(" / ");
builder.Append(denominator);
return true;
}
public static Rational operator +(Rational a, Rational b)
{
int sharedDen = a.denominator * b.denominator,
newNumA = a.numerator * b.denominator,
newNumB = b.numerator * a.denominator;
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.numerator, r.denominator);
public static Rational operator -(Rational a, Rational b)
{
int sharedDen = a.denominator * b.denominator,
newNumA = a.numerator * b.denominator,
newNumB = b.numerator * a.denominator;
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.numerator * b.numerator, a.denominator * b.denominator).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) => 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);
}