using System; using System.Collections.Generic; using System.Text; namespace Wabbajack.Common { public struct Percent : IComparable, IEquatable { public static readonly Percent One = new Percent(1); public static readonly Percent Zero = new Percent(0); public readonly double Value; public Percent Inverse => new Percent(1 - this.Value, check: false); private Percent(double d, bool check) { if (!check || InRange(d)) { this.Value = d; } else { throw new ArgumentException("Element out of range: " + d); } } public Percent(double d) : this(d, check: true) { } public Percent(int i) { if (i < 0) { Value = 0; } else if (i > 100) { Value = 1; } else { Value = i / 100d; } } public static bool InRange(double d) { return d >= 0 || d <= 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 implicit 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 Percent.Zero; } else if (d > 1) { return Percent.One; } return new Percent(d, check: 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, check: false); } public static Percent MultFromPercents(params Percent[] ps) { double percent = 1; foreach (var p in ps) { percent *= p.Value; } return new Percent(percent, check: false); } public override bool Equals(object obj) { if (!(obj is Percent rhs)) return false; return Equals(rhs); } public bool Equals(Percent other) { return this.Value == other.Value; } public override int GetHashCode() { return this.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 this.Value.CompareTo(rhs.Value); } return 0; } public static bool TryParse(string str, out Percent p) { if (double.TryParse(str, out double d)) { if (InRange(d)) { p = new Percent(d); return true; } } p = default(Percent); return false; } } }