Source code for titan_pylib.math.fraction

  1from typing import Union
  2from math import gcd
  3
  4
[docs] 5class Fraction: 6 7 class _NAN: 8 9 def __init__(self): 10 pass 11 12 def calc(self, other): 13 return Fraction.NAN 14 15 __add__ = calc 16 __iadd__ = calc 17 __radd__ = calc 18 __sub__ = calc 19 __isub__ = calc 20 __rsub__ = calc 21 __mul__ = calc 22 __imul__ = calc 23 __rmul__ = calc 24 __pow__ = calc 25 __ipow__ = calc 26 __rpow__ = calc 27 __div__ = calc 28 __idiv__ = calc 29 __rdiv__ = calc 30 __floordiv__ = calc 31 __ifloordiv__ = calc 32 __rfloordiv__ = calc 33 __truediv__ = calc 34 __itruediv__ = calc 35 __rtruediv__ = calc 36 __pos__ = calc 37 __neg__ = calc 38 39 def __hash__(self): 40 return hash(str(self)) 41 42 def __str__(self): 43 return "NAN" 44 45 __repr__ = __str__ 46 47 NAN = _NAN() 48 49 class _INF: 50 51 def __init__(self): 52 self.sgn = 1 53 54 def __add__(self, other): 55 if isinstance(other, Fraction._INF) or other == float("inf"): 56 return Fraction.NAN 57 return self 58 59 def __sub__(self, other): 60 if isinstance(other, Fraction._INF) or other == float("inf"): 61 return Fraction.NAN 62 return self 63 64 def __mul__(self, other): 65 res = Fraction._INF() 66 if other < 0: 67 res.sgn = 1 if res.sgn == -1 else 1 68 return res 69 70 def __truediv__(self, other): 71 if isinstance(other, Fraction._INF) or other == float("inf"): 72 return Fraction.NAN 73 res = Fraction._INF() 74 if other < 0: 75 res *= -1 76 return res 77 78 def __rtruediv__(self, other): 79 if isinstance(other, Fraction._INF) or other == float("inf"): 80 return Fraction.NAN 81 res = Fraction(0, 1) 82 if other < 0: 83 res *= -1 84 return res 85 86 __iadd__ = __add__ 87 __radd__ = __add__ 88 __imul__ = __mul__ 89 __rmul__ = __mul__ 90 __div__ = __truediv__ 91 __rdiv__ = __rtruediv__ 92 __floordiv__ = __truediv__ 93 __rfloordiv__ = __rtruediv__ 94 95 def __pow__(self, other): 96 return Fraction._INF() * self.sgn 97 98 def __gt__(self, other): 99 if self.sgn == 1: 100 return not isinstance(other, Fraction._INF) or other == float("INF") 101 else: 102 return isinstance(other, Fraction._INF) or other == -float("INF") 103 104 def __le__(self, other): 105 if self.sgn == 1: 106 return isinstance(other, Fraction._INF) or other == float("INF") 107 else: 108 return not (isinstance(other, Fraction._INF) or other == -float("INF")) 109 110 def __lt__(self, other): 111 if self.sgn == 1: 112 return isinstance(other, Fraction._INF) or other == float("INF") 113 else: 114 return not (isinstance(other, Fraction._INF) or other == -float("INF")) 115 116 def __eq__(self, other): 117 if self.sgn == 1: 118 return isinstance(other, Fraction._INF) or other == float("INF") 119 else: 120 return not (isinstance(other, Fraction._INF) or other == -float("INF")) 121 122 def __float__(self, other): 123 return float("INF") 124 125 def __abs__(self): 126 return Fraction._INF() 127 128 def __hash__(self): 129 return hash(str(self)) 130 131 def __pos__(self): 132 return Fraction._INF() 133 134 def __neg__(self): 135 res = Fraction._INF() 136 res.sgn = -1 137 return res 138 139 def __str__(self): 140 return "INF" if self.sgn == 1 else "-INF" 141 142 __repr__ = __str__ 143 144 def __init__(self, p: Union[int, str], q: int = 1): 145 if isinstance(p, int) and isinstance(q, int): 146 if q < 0: 147 p, q = -p, -q 148 elif isinstance(p, str): 149 if "." not in p: 150 if p == "inf" or p == "INF": 151 p, q = 1, 0 152 else: 153 p = int(p) 154 else: 155 p, q = map(int, p.split(".")) 156 elif isinstance(p, Fraction._INF) and isinstance(q, Fraction._INF): 157 p, q = 1, 1 158 elif isinstance(p, Fraction._INF): 159 p, q = (-1 if p * q < 0 else 1), 0 160 elif isinstance(q, Fraction._INF): 161 p, q = 0, 1 162 elif isinstance(p, Fraction._NAN): 163 self.n = Fraction.NAN 164 self.d = Fraction.NAN 165 return 166 else: 167 raise TypeError(f"p={p}, q={q}") 168 g = gcd(p, q) if q != 0 else -1 169 self.n: int = p // g if g != -1 else ((1 if p > 0 else -1) * Fraction._INF()) 170 self.d: int = q // g if g != -1 else 1 171 172 @staticmethod 173 def _lcm(a: int, b: int) -> int: 174 return a // gcd(a, b) * b 175 176 def __add__(self, other): 177 if not isinstance(other, Fraction): 178 other = Fraction(other) 179 return Fraction(self.n * other.d + self.d * other.n, self.d * other.d) 180 181 def __sub__(self, other): 182 if not isinstance(other, Fraction): 183 other = Fraction(other) 184 l = Fraction._lcm(self.d, other.d) 185 return Fraction(self.n * l // self.d - other.n * l // other.d, l) 186 187 def __mul__(self, other): 188 if not isinstance(other, Fraction): 189 other = Fraction(other) 190 return Fraction(self.n * other.n, self.d * other.d) 191 192 def __truediv__(self, other): 193 if not isinstance(other, Fraction): 194 other = Fraction(other) 195 return Fraction(self.n * other.d, self.d * other.n) 196 197 __iadd__ = __add__ 198 __isub__ = __sub__ 199 __imul__ = __mul__ 200 __itruediv__ = __truediv__ 201 __radd__ = __add__ 202 __rmul__ = __mul__ 203 204 def __rsub__(self, other): 205 return -self + other 206 207 def __rtruediv__(self, other): 208 return other * Fraction(self.d, self.n) 209 210 def __eq__(self, other: "Fraction"): 211 return self.n == other.n and self.d == other.d 212 213 def __ne__(self, other: "Fraction"): 214 return self.n != other.n or self.d != other.d 215 216 def __lt__(self, other: "Fraction"): 217 return self.n * other.d < self.d * other.n 218 219 def __le__(self, other: "Fraction"): 220 return self.n * other.d <= self.d * other.n 221 222 def __gt__(self, other: "Fraction"): 223 return self.n * other.d > self.d * other.n 224 225 def __ge__(self, other: "Fraction"): 226 return self.n * other.d >= self.d * other.n 227 228 def __pos__(self): 229 return Fraction(self.n, self.d) 230 231 def __neg__(self): 232 return Fraction(-self.n, self.d) 233 234 def __abs__(self): 235 return Fraction((self.n if self.n >= 0 else -self.n), self.d) 236 237 def __int__(self): 238 return self.n // self.d 239 240 def __float__(self): 241 return self.n / self.d 242 243 def __bool__(self): 244 return self.n != 0 245 246 def __hash__(self): 247 return hash(str(self)) 248 249 def __str__(self): 250 return f"({self.n}/{self.d})" 251 252 def __repr__(self): 253 # return f'Fraction({self.n}, {self.d})' 254 return str(self)