using System; namespace Wabbajack.RateLimiter; public readonly struct Percent : IComparable, IEquatable { public static readonly Percent One = new(1d); public static readonly Percent Zero = new(0d); public readonly double Value; public Percent Inverse => new(1d - Value, false); private Percent(double d, bool check) { if (!check || InRange(d)) Value = d; else throw new ArgumentException("Element out of range: " + d); } public Percent(long max, long current) : this((double) current / max) { } public Percent(double d) : this(d, true) { } public static bool InRange(double d) { return d is >= 0 or <= 1; } public static Percent operator +(Percent c1, Percent c2) { return new Percent(c1.Value + c2.Value); } public static Percent operator *(Percent c1, Percent c2) { return new Percent(c1.Value * c2.Value); } public static Percent operator -(Percent c1, Percent c2) { return new Percent(c1.Value - c2.Value); } public static Percent operator /(Percent c1, Percent c2) { return new Percent(c1.Value / c2.Value); } public static bool operator ==(Percent c1, Percent c2) { return c1.Value == c2.Value; } public static bool operator !=(Percent c1, Percent c2) { return c1.Value != c2.Value; } public static bool operator >(Percent c1, Percent c2) { return c1.Value > c2.Value; } public static bool operator <(Percent c1, Percent c2) { return c1.Value < c2.Value; } public static bool operator >=(Percent c1, Percent c2) { return c1.Value >= c2.Value; } public static bool operator <=(Percent c1, Percent c2) { return c1.Value <= c2.Value; } public static explicit operator double(Percent c1) { return c1.Value; } public static Percent FactoryPutInRange(double d) { if (double.IsNaN(d) || double.IsInfinity(d)) throw new ArgumentException(); if (d < 0) return Zero; if (d > 1) return One; return new Percent(d, false); } public static Percent FactoryPutInRange(int cur, int max) { return FactoryPutInRange(1.0d * cur / max); } public static Percent FactoryPutInRange(long cur, long max) { return FactoryPutInRange(1.0d * cur / max); } public static Percent AverageFromPercents(params Percent[] ps) { double percent = 0; foreach (var p in ps) percent += p.Value; return new Percent(percent / ps.Length, false); } public static Percent MultFromPercents(params Percent[] ps) { double percent = 1; foreach (var p in ps) percent *= p.Value; return new Percent(percent, false); } public override bool Equals(object? obj) { if (!(obj is Percent rhs)) return false; return Equals(rhs); } public bool Equals(Percent other) { return Value == other.Value; } public override int GetHashCode() { return Value.GetHashCode(); } public override string ToString() { return ToString(0); } public string ToString(string format) { return $"{(Value * 100).ToString(format)}%"; } public string ToString(byte numDigits) { switch (numDigits) { case 0: return ToString("n0"); case 1: return ToString("n1"); case 2: return ToString("n2"); case 3: return ToString("n3"); case 4: return ToString("n4"); case 5: return ToString("n5"); case 6: return ToString("n6"); default: throw new NotImplementedException(); } } public int CompareTo(object? obj) { if (obj is Percent rhs) return Value.CompareTo(rhs.Value); return 0; } public static bool TryParse(string str, out Percent p) { if (double.TryParse(str, out var d)) if (InRange(d)) { p = new Percent(d); return true; } p = default; return false; } }