-
Notifications
You must be signed in to change notification settings - Fork 12
/
infinity.py
207 lines (161 loc) · 5.97 KB
/
infinity.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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# Copyright 2009-2011 Ram Rachum.
# This program is distributed under the LGPL2.1 license.
'''
This module defines the `Infinity` class and related exceptions.
See their documentation for more info.
'''
from garlicsim.general_misc.exceptions import CuteException
from garlicsim.general_misc import math_tools
__all__ = ['infinity', 'InfinityError', 'InfinityRaceError']
def is_floatable(x):
try:
float(x)
return True
except Exception:
return False
def is_nonfractional(x):
try:
int(x)
return int(x) == x
except Exception:
return False
class InfinityRaceError(CuteException):
'''
An "infinity race" between two infinite sizes.
A calculation is being made between two quantities that involve infinity,
and the two infinities are "pitted" against each other in a way which makes
it impossible to determine what the result of the computation would be.
'''
class InfinityError(CuteException):
'''infinity-related exception.'''
class Infinity(object):
'''
A class for infinity numbers.
There are only two distinct instances of this class: infinity and
(-infinity).
'''
#todo: add __assign__ or whatever it's called
#todo: add more interoperability with float(inf). (Need to detect its
#existance)
#todo: calling it Infinity is a bit wrong./
def __init__(self, direction=1):
self.direction = direction
def __abs__(self):
return infinity
def __add__(self, other):
if isinstance(other, Infinity):
if self.direction == other.direction:
return self
else:
raise InfinityRaceError
elif is_floatable(other):
return self
def __sub__(self, other):
return self.__add__(-other)
def __cmp__(self, other):
if isinstance(other, Infinity):
d_cmp = cmp(self.direction, other.direction)
if d_cmp != 0:
return d_cmp
else:
raise InfinityRaceError
elif is_floatable(other):
return self.direction
else:
raise NotImplementedError
def __div__(self, other):
if isinstance(other, Infinity):
raise InfinityRaceError
elif is_floatable(other):
s = math_tools.sign(other)
if s==0:
raise InfinityRaceError
else:
return Infinity(direction=self.direction * s)
def __float__(self):
raise ValueError("Can't convert infinite number to float")
def __mul__(self, other):
if isinstance(other, Infinity):
return Infinity(self.direction * other.direction)
elif is_floatable(other):
s = math_tools.sign(other)
if s==0:
raise InfinityRaceError
else:
return Infinity(direction=self.direction * s)
def __neg__(self):
return Infinity(-self.direction)
def __nonzero__(self):
return True
def __pos__(self):
return self
def __pow__(self, other):
if isinstance(other, Infinity):
raise object # todo
elif is_floatable(other):
s = math_tools.sign(other)
if s==0:
raise InfinityRaceError
else:
if self.direction==1:
if s==1:
return self
if s==-1:
return 0
else: #self.direction == -1
if is_nonfractional(other):
if s==-1:
return 0
if s==1:
if s % 2 == 0:
return Infinity()
else:
return Infinity(-1)
else: # is_nonfractional(other) is False
raise ValueError("Negative number cannot be raised "
"to a fractional power")
def __rpow__(self, other):
if isinstance(other, Infinity):
return other.__pow__(self)
elif not is_floatable(other):
raise NotImplementedError
else: # is_floatable(number) is True
raise NotImplementedError # todo
def __radd__(self, other):
return self.__add__(other)
def __rsub__(self, other):
return ( - self.__sub__(other))
def __rmul__(self, other):
return self.__mul__(other)
__truediv__ = __floordiv__ = __div__
def __eq__(self, other):
if isinstance(other, Infinity):
return other.direction == self.direction
elif isinstance(other, float):
# We're checking to see if `other` is equal to `float('inf')` or
# `-float('inf')`. But we must `try` it carefully, because in Python
# 2.5 there is no `float('inf')`.
#
# Todo: It seems this takes precedence over `float.__eq__`,
# fortunately. How come this happens?
try:
float_inf = float('inf')
except ValueError:
return False
if other == float_inf:
return self.direction == 1
elif other == -float_inf:
return self.direction == -1
else:
return False
else: # `other` is not any kind of infinity
return False
def __neq__(self, other):
return not self.__eq__(other)
def __repr__(self):
if self.direction==1:
suffix=''
else: # self.direction == -1
suffix='-'
return suffix + 'infinity'
infinity = Infinity()