OILS / mycpp / mops.py View on Github | oilshell.org

293 lines, 113 significant
1"""
2Math operations, e.g. for arbitrary precision integers
3
4They are currently int64_t, rather than C int, but we want to upgrade to
5heap-allocated integers.
6
7Regular int ops can use the normal operators + - * /, or maybe i_add() if we
8really want. Does that make code gen harder or worse?
9
10Float ops could be + - * / too, but it feels nicer to develop a formal
11interface?
12"""
13from __future__ import print_function
14
15from typing import Tuple
16
17
18class BigInt(object):
19
20 def __init__(self, i):
21 # type: (int) -> None
22 self.i = i
23
24 def __eq__(self, other):
25 # type: (object) -> bool
26
27 # Disabled check
28 # Prevent possible mistakes. Could do this with other operators
29 # raise AssertionError('Use mops.Equal()')
30
31 if not isinstance(other, BigInt):
32 raise AssertionError()
33
34 # Used for hashing
35 return self.i == other.i
36
37 def __gt__(self, other):
38 # type: (object) -> bool
39 raise AssertionError('Use functions in mops.py')
40
41 def __ge__(self, other):
42 # type: (object) -> bool
43 raise AssertionError('Use functions in mops.py')
44
45 def __hash__(self):
46 # type: () -> int
47 """For dict lookups."""
48 return hash(self.i)
49
50
51ZERO = BigInt(0)
52ONE = BigInt(1)
53MINUS_ONE = BigInt(-1)
54MINUS_TWO = BigInt(-2) # for printf
55
56
57def ToStr(b):
58 # type: (BigInt) -> str
59 return str(b.i)
60
61
62def ToOctal(b):
63 # type: (BigInt) -> str
64 return '%o' % b.i
65
66
67def ToHexUpper(b):
68 # type: (BigInt) -> str
69 return '%X' % b.i
70
71
72def ToHexLower(b):
73 # type: (BigInt) -> str
74 return '%x' % b.i
75
76
77# Notes on FromStr() and recognizing integers
78#
79# 3 similar but DIFFERENT cases:
80#
81# 1. trap ' 42 ' x - unsigned, including 09, but not -1
82# 2. echo $(( x )) - 0123 is octal, but no -0123 because that's separate I think
83# 3. int(), j8 - 077 is decimal
84#
85# - mops.FromStr should not use exceptions? That is consistent with mops.FromFloat
86# - under the hood it uses StringToInt64, which uses strtoll
87# - problem: we DO NOT want to rely on strtoll() to define a language, to
88# reject user-facing strings - we want to use something like
89# match.LooksLikeInteger() usually. This is part of our spec-driven
90# philosophy.
91#
92# - a problem though is if we support 00, because sometimes that is OCTAL
93# - int("00") is zero
94# - match.LooksLikeInteger returns it
95
96# uses LooksLikeInteger and then FromStr()
97# - YSH int()
98# - printf builtin
99# - YSH expression conversion
100
101# Uses only FromStr()
102# - j8 - uses its own regex though
103# - ulimit
104# - trap - NON-NEGATIVE only
105# - arg parser
106
107
108def FromStr(s, base=10):
109 # type: (str, int) -> BigInt
110 return BigInt(int(s, base))
111
112
113MAX_POS_INT = 2**63 - 1
114MAX_NEG_INT = -(2**63)
115
116
117def FromStr2(s, base=10):
118 # type: (str, int) -> Tuple[bool, BigInt]
119 """
120 Simulate C++
121 """
122 try:
123 big_int = BigInt(int(s, base))
124 except ValueError:
125 return (False, MINUS_ONE)
126 else:
127 # Simulate C++ overflow
128 if big_int.i > MAX_POS_INT:
129 return (False, MINUS_ONE)
130 if big_int.i < MAX_NEG_INT:
131 return (False, MINUS_ONE)
132
133 return (True, big_int)
134
135
136def BigTruncate(b):
137 # type: (BigInt) -> int
138 """Only truncates in C++"""
139 return b.i
140
141
142def IntWiden(i):
143 # type: (int) -> BigInt
144 """Only widens in C++"""
145 return BigInt(i)
146
147
148def FromC(i):
149 # type: (int) -> BigInt
150 """A no-op in C, for RLIM_INFINITY"""
151 return BigInt(i)
152
153
154def FromBool(b):
155 # type: (bool) -> BigInt
156 """Only widens in C++"""
157 return BigInt(1) if b else BigInt(0)
158
159
160def ToFloat(b):
161 # type: (BigInt) -> float
162 """Used by float(42) in Oils"""
163 return float(b.i)
164
165
166def FromFloat(f):
167 # type: (float) -> Tuple[bool, BigInt]
168 """Used by int(3.14) in Oils"""
169 try:
170 big = int(f)
171 except ValueError: # NAN
172 return False, MINUS_ONE
173 except OverflowError: # INFINITY
174 return False, MINUS_ONE
175 return True, BigInt(big)
176
177
178# Can't use operator overloading
179
180
181def Negate(b):
182 # type: (BigInt) -> BigInt
183 return BigInt(-b.i)
184
185
186def Add(a, b):
187 # type: (BigInt, BigInt) -> BigInt
188 return BigInt(a.i + b.i)
189
190
191def Sub(a, b):
192 # type: (BigInt, BigInt) -> BigInt
193 return BigInt(a.i - b.i)
194
195
196def Mul(a, b):
197 # type: (BigInt, BigInt) -> BigInt
198 return BigInt(a.i * b.i)
199
200
201def Div(a, b):
202 # type: (BigInt, BigInt) -> BigInt
203 """Integer division.
204
205 Oils rounds toward zero.
206
207 Python rounds toward negative infinity, while C++ rounds toward zero. We
208 have to work around Python a bit.
209 """
210 assert b.i != 0, b.i # divisor can't be zero -- caller checks
211
212 # Only use Python // on non-negative numbers. Apply sign afterward.
213 sign = 1
214
215 if a.i < 0:
216 pa = -a.i
217 sign = -1
218 else:
219 pa = a.i
220
221 if b.i < 0:
222 pb = -b.i
223 sign = -sign
224 else:
225 pb = b.i
226
227 return BigInt(sign * (pa // pb))
228
229
230def Rem(a, b):
231 # type: (BigInt, BigInt) -> BigInt
232 """Integer remainder."""
233 assert b.i != 0, b.i # YSH divisor must be positive, but OSH can be negative
234
235 # Only use Python % on non-negative numbers. Apply sign afterward.
236 if a.i < 0:
237 pa = -a.i
238 sign = -1
239 else:
240 pa = a.i
241 sign = 1
242
243 if b.i < 0:
244 pb = -b.i
245 else:
246 pb = b.i
247
248 return BigInt(sign * (pa % pb))
249
250
251def Equal(a, b):
252 # type: (BigInt, BigInt) -> bool
253 return a.i == b.i
254
255
256def Greater(a, b):
257 # type: (BigInt, BigInt) -> bool
258 return a.i > b.i
259
260
261# GreaterEq, Less, LessEq can all be expressed as the 2 ops above
262
263
264def LShift(a, b):
265 # type: (BigInt, BigInt) -> BigInt
266 assert b.i >= 0, b.i # Must be checked by caller
267 return BigInt(a.i << b.i)
268
269
270def RShift(a, b):
271 # type: (BigInt, BigInt) -> BigInt
272 assert b.i >= 0, b.i # Must be checked by caller
273 return BigInt(a.i >> b.i)
274
275
276def BitAnd(a, b):
277 # type: (BigInt, BigInt) -> BigInt
278 return BigInt(a.i & b.i)
279
280
281def BitOr(a, b):
282 # type: (BigInt, BigInt) -> BigInt
283 return BigInt(a.i | b.i)
284
285
286def BitXor(a, b):
287 # type: (BigInt, BigInt) -> BigInt
288 return BigInt(a.i ^ b.i)
289
290
291def BitNot(a):
292 # type: (BigInt) -> BigInt
293 return BigInt(~a.i)