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