1 // 2 // Copyright (c) 2010-2024 Antmicro 3 // 4 // This file is licensed under the MIT License. 5 // Full license text is available in 'licenses/MIT.txt'. 6 // 7 using System; 8 using System.Runtime.CompilerServices; 9 10 namespace Antmicro.Renode.Utilities 11 { 12 public 13 #if NET 14 readonly 15 #endif 16 struct Fraction 17 { 18 public readonly ulong Numerator; 19 public readonly ulong Denominator; 20 public readonly bool Minus; 21 22 public ulong Integer => Numerator / Denominator; 23 public Fraction Fractional => new Fraction(Numerator % Denominator, Denominator, skipReduce: true); 24 FractionAntmicro.Renode.Utilities.Fraction25 public Fraction(ulong numerator, ulong denominator, bool minus = false) : this(numerator, denominator, minus, skipReduce: false) 26 { 27 } 28 29 [MethodImpl(MethodImplOptions.AggressiveInlining)] 30 public static Fraction operator +(Fraction a) => a; 31 32 [MethodImpl(MethodImplOptions.AggressiveInlining)] 33 public static Fraction operator -(Fraction a) => new Fraction(a.Numerator, a.Denominator, !a.Minus, skipReduce: true); 34 operator +Antmicro.Renode.Utilities.Fraction35 public static Fraction operator +(Fraction a, Fraction b) 36 { 37 var anum = a.Numerator; 38 var bnum = b.Numerator; 39 var newden = a.Denominator; 40 41 if(anum == 0) 42 { 43 return b; 44 } 45 if(bnum == 0) 46 { 47 return a; 48 } 49 50 if(a.Denominator != b.Denominator) 51 { 52 newden = Misc.LCM(a.Denominator, b.Denominator); 53 anum *= newden / a.Denominator; 54 bnum *= newden / b.Denominator; 55 } 56 57 if(a.Minus == b.Minus) 58 { 59 return new Fraction(anum + bnum, newden, a.Minus); 60 } 61 else 62 { 63 var minus = bnum > anum ? b.Minus : a.Minus; 64 return new Fraction(Math.Max(bnum, anum) - Math.Min(bnum, anum), newden, minus); 65 } 66 } 67 68 [MethodImpl(MethodImplOptions.AggressiveInlining)] operator -Antmicro.Renode.Utilities.Fraction69 public static Fraction operator -(Fraction a, Fraction b) 70 => a + (-b); 71 operator *Antmicro.Renode.Utilities.Fraction72 public static Fraction operator *(Fraction a, Fraction b) 73 { 74 var anum = a.Numerator; 75 var bnum = b.Numerator; 76 var aden = a.Denominator; 77 var bden = b.Denominator; 78 Reduce(ref anum, ref bden); 79 Reduce(ref aden, ref bnum); 80 return new Fraction(anum * bnum, aden * bden, a.Minus != b.Minus, skipReduce: true); 81 } 82 operator /Antmicro.Renode.Utilities.Fraction83 public static Fraction operator /(Fraction a, Fraction b) 84 { 85 var anum = a.Numerator; 86 var bnum = b.Numerator; 87 var aden = a.Denominator; 88 var bden = b.Denominator; 89 if(bnum == 0) 90 { 91 throw new DivideByZeroException(); 92 } 93 Reduce(ref anum, ref bnum); 94 Reduce(ref aden, ref bden); 95 return new Fraction(anum * bden, aden * bnum, a.Minus != b.Minus, skipReduce: true); 96 } 97 98 [MethodImpl(MethodImplOptions.AggressiveInlining)] operator +Antmicro.Renode.Utilities.Fraction99 public static Fraction operator +(Fraction a, ulong b) 100 { 101 if(b == 0) 102 { 103 return a; 104 } 105 return a + new Fraction(b, 1, skipReduce: false); 106 } 107 108 [MethodImpl(MethodImplOptions.AggressiveInlining)] operator +Antmicro.Renode.Utilities.Fraction109 public static Fraction operator +(ulong b, Fraction a) 110 => a + b; 111 112 [MethodImpl(MethodImplOptions.AggressiveInlining)] operator -Antmicro.Renode.Utilities.Fraction113 public static Fraction operator -(Fraction a, ulong b) 114 { 115 if(b == 0) 116 { 117 return a; 118 } 119 return a + new Fraction(b, 1, minus: true, skipReduce: true); 120 } 121 122 [MethodImpl(MethodImplOptions.AggressiveInlining)] operator -Antmicro.Renode.Utilities.Fraction123 public static Fraction operator -(ulong b, Fraction a) 124 { 125 if(b == 0) 126 { 127 return -a; 128 } 129 return new Fraction(b, 1, skipReduce: true) - a; 130 } 131 132 [MethodImpl(MethodImplOptions.AggressiveInlining)] operator *Antmicro.Renode.Utilities.Fraction133 public static Fraction operator *(Fraction a, ulong b) 134 { 135 if(b == 0) 136 { 137 return Zero; 138 } 139 if(b == 1) 140 { 141 return a; 142 } 143 return a * new Fraction(b, 1, skipReduce: true); 144 } 145 146 [MethodImpl(MethodImplOptions.AggressiveInlining)] operator *Antmicro.Renode.Utilities.Fraction147 public static Fraction operator *(ulong b, Fraction a) 148 => a * b; 149 150 [MethodImpl(MethodImplOptions.AggressiveInlining)] operator /Antmicro.Renode.Utilities.Fraction151 public static Fraction operator /(Fraction a, ulong b) 152 { 153 if(b == 1) 154 { 155 return a; 156 } 157 return a / new Fraction(b, 1, skipReduce: true); 158 } 159 160 [MethodImpl(MethodImplOptions.AggressiveInlining)] operator /Antmicro.Renode.Utilities.Fraction161 public static Fraction operator /(ulong b, Fraction a) 162 { 163 if(b == 0) 164 { 165 return Zero; 166 } 167 return new Fraction(b, 1, skipReduce: true) / a; 168 } 169 170 [MethodImpl(MethodImplOptions.AggressiveInlining)] operator ==Antmicro.Renode.Utilities.Fraction171 public static bool operator ==(Fraction a, Fraction b) 172 => (a.Numerator == b.Numerator && a.Denominator == b.Denominator && a.Minus == b.Minus) || (a.Numerator == 0 && b.Numerator == 0); 173 174 [MethodImpl(MethodImplOptions.AggressiveInlining)] operator !=Antmicro.Renode.Utilities.Fraction175 public static bool operator !=(Fraction a, Fraction b) 176 => !(a == b); 177 178 [MethodImpl(MethodImplOptions.AggressiveInlining)] operator <Antmicro.Renode.Utilities.Fraction179 public static bool operator <(Fraction a, Fraction b) 180 => (a - b).Minus; 181 182 [MethodImpl(MethodImplOptions.AggressiveInlining)] operator >Antmicro.Renode.Utilities.Fraction183 public static bool operator >(Fraction a, Fraction b) 184 => (b - a).Minus; 185 186 [MethodImpl(MethodImplOptions.AggressiveInlining)] operator <=Antmicro.Renode.Utilities.Fraction187 public static bool operator <=(Fraction a, Fraction b) 188 => !(a > b); 189 190 [MethodImpl(MethodImplOptions.AggressiveInlining)] operator >=Antmicro.Renode.Utilities.Fraction191 public static bool operator >=(Fraction a, Fraction b) 192 => !(a < b); 193 194 [MethodImpl(MethodImplOptions.AggressiveInlining)] operator ==Antmicro.Renode.Utilities.Fraction195 public static bool operator ==(Fraction a, ulong b) 196 => a.Integer == b; 197 198 [MethodImpl(MethodImplOptions.AggressiveInlining)] operator !=Antmicro.Renode.Utilities.Fraction199 public static bool operator !=(Fraction a, ulong b) 200 => !(a == b); 201 202 [MethodImpl(MethodImplOptions.AggressiveInlining)] operator <Antmicro.Renode.Utilities.Fraction203 public static bool operator <(Fraction a, ulong b) 204 => a.Integer < b; 205 206 [MethodImpl(MethodImplOptions.AggressiveInlining)] 207 public static bool operator >(Fraction a, ulong b) 208 => a.Integer > b || (a.Integer == b && a.Fractional.Numerator > 0); 209 210 [MethodImpl(MethodImplOptions.AggressiveInlining)] operator <=Antmicro.Renode.Utilities.Fraction211 public static bool operator <=(Fraction a, ulong b) 212 => !(a > b); 213 214 [MethodImpl(MethodImplOptions.AggressiveInlining)] operator >=Antmicro.Renode.Utilities.Fraction215 public static bool operator >=(Fraction a, ulong b) 216 => !(a < b); 217 CompareToAntmicro.Renode.Utilities.Fraction218 public int CompareTo(Fraction other) 219 { 220 if(this == other) 221 { 222 return 0; 223 } 224 if(this < other) 225 { 226 return -1; 227 } 228 return 1; 229 } 230 EqualsAntmicro.Renode.Utilities.Fraction231 public override bool Equals(object obj) 232 { 233 return (obj is Fraction f) && this == f; 234 } 235 GetHashCodeAntmicro.Renode.Utilities.Fraction236 public override int GetHashCode() 237 { 238 return (int)(Numerator ^ Denominator * (Minus ? 2L : 1UL)); 239 } 240 ToStringAntmicro.Renode.Utilities.Fraction241 public override string ToString() 242 { 243 char sign = Minus ? '-' : '+'; 244 if(Denominator == 1) 245 { 246 return $"{sign}{Numerator}"; 247 } 248 else 249 { 250 return $"{sign}{Numerator}/{Denominator}"; 251 } 252 } 253 254 public static explicit operator ulong(Fraction f) => f.Integer; operator FractionAntmicro.Renode.Utilities.Fraction255 public static explicit operator Fraction(ulong u) => new Fraction(u, 1, false, skipReduce: true); 256 public static readonly Fraction Zero = new Fraction(0, 1, skipReduce: true); 257 FractionAntmicro.Renode.Utilities.Fraction258 private Fraction(ulong numerator, ulong denominator, bool minus = false, bool skipReduce = false) 259 { 260 // Denominator cannot be zero. If the numerator is 0, represent as 0/1 (see Zero) 261 if(denominator == 0 || numerator == 0) 262 { 263 denominator = 1; 264 } 265 266 if(!skipReduce) 267 { 268 Reduce(ref numerator, ref denominator); 269 } 270 Numerator = numerator; 271 Denominator = denominator; 272 Minus = minus; 273 } 274 ReduceAntmicro.Renode.Utilities.Fraction275 private static void Reduce(ref ulong a, ref ulong b) 276 { 277 var gcd = Misc.GCD(a, b); 278 if(gcd > 1) 279 { 280 a /= gcd; 281 b /= gcd; 282 } 283 } 284 } 285 } 286