generated from ISP19/unittesting-start
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfraction.py
186 lines (153 loc) · 6.39 KB
/
fraction.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
import math
class Fraction:
"""A fraction with a numerator and denominator and arithmetic operations.
Fractions are always stored in proper form, without common factors in
numerator and denominator, and denominator >= 0.
Since Fractions are stored in proper form, each value has a
unique representation, e.g. 4/5, 24/30, and -20/-25 have the same
internal representation.
Note:
0/0 form is represented as NaN (short form of Not a Number)
1/0 denotes a indeterminate form of positive infinity (math.inf).
-1/0 denotes a indeterminate form of negative infinity (-math.inf).
"""
def __init__(self, numerator, denominator=1):
"""Initializes a new fraction with the given numerator
and denominator (default 1).
"""
if type(numerator) is not int or type(denominator) is not int:
raise ValueError("The numerator or denominator of a fraction must be an integer.")
self.is_infinity = False
gcd = math.gcd(numerator, denominator)
self.numerator = int(numerator / gcd)
self.denominator = int(denominator / gcd)
if self.denominator < 0 or self.numerator == 0:
if self.denominator < 0:
self.numerator *= -1
self.denominator *= -1
def __add__(self, other):
"""Returns the sum of two fractions as a new fraction.
Use the standard formula a/b + c/d = (ad+bc)/(b*d)
Args:
other (Fraction): Another fraction to add
Returns:
Fraction: The summation of both operated fractions
"""
if type(other) is not Fraction:
if math.isinf(other):
return other
else:
numerator_result = self.numerator + (other*self.denominator)
denominator_product = self.denominator
else:
numerator_result = (self.numerator*other.denominator) + (other.numerator*self.denominator)
denominator_product = self.denominator*other.denominator
return Fraction(numerator_result, denominator_product)
def __sub__(self, other):
"""Returns the difference of two fractions as a new fraction.
Args:
other (Fraction): Another fraction to subtract
Returns:
Fraction: The difference result of both operated fractions
"""
if type(other) is Fraction:
other.numerator *= -1
return self.__add__(other)
else:
return self.__add__(-other)
def __mul__(self, other):
"""Returns the product of two fractions according to multiplication rule of fractions
A fraction of which denominator of 0 will not be allowed unless it has a numerator of 1.
Args:
other (Fraction): Another fraction to multiply
Returns:
Fraction: The product of both operated fractions
"""
numerator_result = self.numerator*other.numerator
denominator_result = self.denominator*other.denominator
return Fraction(numerator_result, denominator_result)
def to_decimal(self):
"""Converts this fraction to its decimal equivalent
Returns:
float: The decimal representation
"""
return float(self.numerator/self.denominator)
@classmethod
def to_comparable(cls, obj):
"""Convert the given number into its comparable form
Args:
obj: The given number
Returns:
any: The given object in its comparable form
"""
if type(obj) != cls:
return obj
else:
return obj.to_decimal()
@classmethod
def from_str(cls, frac_str: str):
"""Converts a fraction representation into a Fraction object
Args:
frac_str (str): A fraction representation to convert
Returns:
Fraction: The parsed result from string representation
"""
if "/" not in frac_str:
raise ValueError("Invalid fraction representation")
numerator, denominator = frac_str.split("/")
return Fraction(int(numerator), int(denominator))
def __gt__(self, other):
"""Compares two fractions whether the first fraction is greater than the other or else.
Args:
other: Another fraction to compare with
Returns:
bool: Whether the first fraction is greater than the other or not
"""
return self.to_decimal() > Fraction.to_comparable(other)
def __lt__(self, other):
"""Compares two fractions whether the first fraction is less than the other or else.
Args:
other: Another fraction to compare with
Returns:
bool: Whether the first fraction is less than the other or not
"""
return self.to_decimal() < Fraction.to_comparable(other)
def __neg__(self):
"""Negates this fraction's sign from positive to negative
and negative to positive
Returns:
bool: A negation form of the Fraction (as a new one)
"""
numerator = -self.numerator
return Fraction(numerator, self.denominator)
def __eq__(self, frac):
"""Two fractions are equal if they have the same value.
Fractions are stored in proper form so the internal representation
is unique (3/6 is the same as 1/2).
"""
if type(frac) is int:
return self.numerator == frac
else:
return self.to_decimal() == Fraction.to_comparable(frac)
def __str__(self):
"""Represents fractions in terms of the numerator over denominator
The fraction of which denominator of 1 is represented in whole number instead.
Returns:
str: A string representation of a Fraction
"""
if self.denominator is 1:
return f"{self.numerator}"
else:
return f"{self.numerator}/{self.denominator}"
def __new__(cls, numerator, denominator=1):
if denominator is 0:
if numerator is 0:
return math.nan
elif numerator is 1 or numerator is -1:
return numerator*math.inf
else:
raise ValueError("A fraction cannot have a denominator of zero")
elif denominator is 1:
return numerator
else:
return object.__new__(cls)