-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfraction_nof.py
More file actions
162 lines (121 loc) · 5.31 KB
/
fraction_nof.py
File metadata and controls
162 lines (121 loc) · 5.31 KB
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
"""
Fractional calculations. Even though fractions.Fraction exists.
This is like fraction.py, except that functools is not used, since Skulpt
doesn't support it, so the code in fraction.py cannot be used in pythonds
(at https://runestone.academy/runestone/books/published/pythonds/Introduction/ObjectOrientedProgramminginPythonDefiningClasses.html).
"""
__all__ = ['Fraction']
def _gcd(a, b):
while b != 0:
a, b = b, a % b
return a
class Fraction:
"""
A fraction type, even though fractions.Fraction exists and is good.
NOTE: This class does NOT make use of NotImplemented for arithmetic
operations. It assumes the second operand of an arithmetic operation is
always of a compatible type. This behavior is wrong. See nimpl.md for why
it is wrong, even here, and why I've nonetheless done it in this code. Note
that it's not *justified* here. This code should not be used in production.
"""
__slots__ = ('_numerator', '_denominator')
def __init__(self, numerator, denominator=1):
"""Create a fraction with the specified numerator and denominator."""
if denominator == 0:
raise ZeroDivisionError('denominator cannot be zero')
gcd = _gcd(numerator, denominator)
self._numerator = numerator // gcd
self._denominator = denominator // gcd
if self._denominator < 0:
self._numerator = -self._numerator
self._denominator = -self._denominator
def __eq__(self, other):
"""Check if two fractions are equal."""
if not isinstance(other, Fraction):
return NotImplemented
return (self._numerator == other.numerator
and self._denominator == other.denominator)
def __lt__(self, other):
"""Check if this fraction is smaller than another."""
if not isinstance(other, Fraction):
return NotImplemented
lhs = self._numerator * other.denominator
rhs = other.numerator * self._denominator
return lhs < rhs
def __gt__(self, other):
"""Check if this fraction is larger than another."""
return other.__lt__(self)
def __le__(self, other):
"""Check if this fraction is no larger than another."""
lt_result = self.__lt__(other)
if lt_result is NotImplemented or lt_result:
return lt_result
return self.__eq__(other)
def __ge__(self, other):
"""Check if this fraction is no smaller than another."""
gt_result = self.__gt__(other)
if gt_result is NotImplemented or gt_result:
return gt_result
return self.__eq__(other)
def __hash__(self):
"""Compute a prehash to store this fraction in a dict or set."""
return hash((self._numerator, self._denominator))
def __repr__(self):
"""Representation of a fraction that can be passed to eval()."""
return f'Fraction({self._numerator!r}, {self._denominator!r})'
def __str__(self):
"""Representation of a fraction suitable for user interfaces."""
return f'{self._numerator}/{self._denominator}'
def __neg__(self):
"""Compute the additive inverse (unary minus)."""
return Fraction(-self._numerator, self._denominator)
def __pos__(self):
"""Return the same value (unary plus)."""
return self
def __abs__(self):
"""Return the absolute value."""
return Fraction(abs(self._numerator), self._denominator)
def __add__(self, other):
"""Compute the sum of this and another fraction."""
return Fraction(self._numerator * other.denominator
+ other.numerator * self._denominator,
self._denominator * other.denominator)
def __sub__(self, other):
"""Compute the difference of this and another fraction."""
return self + -other
def __mul__(self, other):
"""Compute the the product of this and another fraction."""
return Fraction(self._numerator * other.numerator,
self._denominator * other.denominator)
def __truediv__(self, other):
"""Compute the quotient of this and another fraction."""
if other.numerator == 0:
raise ZeroDivisionError('cannot divide by the zero fraction')
return self * other.reciprocal
def __pow__(self, exponent):
"""Raise this fraction to a particular integer exponent."""
if exponent >= 0:
return self._do_pow(exponent)
if self._numerator == 0:
raise ZeroDivisionError(
'cannot raise the zero fraction to a negative power')
return self._do_pow(-exponent).reciprocal
@property
def numerator(self):
"""The fraction's numerator (dividend)."""
return self._numerator
@property
def denominator(self):
"""The fraction's denominator (divisor)."""
return self._denominator
@property
def reciprocal(self):
"""The fraction's multiplicative inverse (reciprocal)."""
return Fraction(self._denominator, self._numerator)
def _do_pow(self, exponent):
if exponent == 0:
return Fraction.ONE
ret = self._do_pow(exponent // 2)
return ret * ret if exponent % 2 == 0 else ret * ret * self
Fraction.ZERO = Fraction(0)
Fraction.ONE = Fraction(1)