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

285 lines, 111 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 recognizing integers:
78#
79# - mops.FromStr() uses StringToInt64() under the hood, which uses strtoll().
80# But we DO NOT want to rely on strtoll() to define a language, .e. to reject
81# user-facing strings. We want to use something like match.LooksLikeInteger()
82# This is part of our spec-driven philosophy.
83
84# Regarding leading zeros, these are DIFFERENT:
85#
86# 1. trap ' 42 ' x - unsigned, including 09, but not -1
87# 2. echo $(( x )) - 0123 is octal, but no -0123 because that's separate I think
88# 3. int(), j8 - 077 is decimal
89
90# - a problem though is if we support 00, because sometimes that is OCTAL
91# - int("00") is zero
92# - match.LooksLikeInteger returns true
93#
94# Uses LooksLikeInteger and then FromStr()
95# - YSH int()
96# - printf builtin
97# - YSH expression conversion
98
99# Uses only FromStr()
100# - j8 - uses its own regex though
101# - ulimit
102# - trap - NON-NEGATIVE only
103# - arg parser
104
105MAX_POS_INT = 2**63 - 1
106MAX_NEG_INT = -(2**63)
107
108
109def FromStr2(s, base=10):
110 # type: (str, int) -> Tuple[bool, BigInt]
111 """
112 Simulate C++
113 """
114 try:
115 big_int = BigInt(int(s, base))
116 except ValueError:
117 return (False, MINUS_ONE)
118 else:
119 # Simulate C++ overflow
120 if big_int.i > MAX_POS_INT:
121 return (False, MINUS_ONE)
122 if big_int.i < MAX_NEG_INT:
123 return (False, MINUS_ONE)
124
125 return (True, big_int)
126
127
128def BigTruncate(b):
129 # type: (BigInt) -> int
130 """Only truncates in C++"""
131 return b.i
132
133
134def IntWiden(i):
135 # type: (int) -> BigInt
136 """Only widens in C++"""
137 return BigInt(i)
138
139
140def FromC(i):
141 # type: (int) -> BigInt
142 """A no-op in C, for RLIM_INFINITY"""
143 return BigInt(i)
144
145
146def FromBool(b):
147 # type: (bool) -> BigInt
148 """Only widens in C++"""
149 return BigInt(1) if b else BigInt(0)
150
151
152def ToFloat(b):
153 # type: (BigInt) -> float
154 """Used by float(42) in Oils"""
155 return float(b.i)
156
157
158def FromFloat(f):
159 # type: (float) -> Tuple[bool, BigInt]
160 """Used by int(3.14) in Oils"""
161 try:
162 big = int(f)
163 except ValueError: # NAN
164 return False, MINUS_ONE
165 except OverflowError: # INFINITY
166 return False, MINUS_ONE
167 return True, BigInt(big)
168
169
170# Can't use operator overloading
171
172
173def Negate(b):
174 # type: (BigInt) -> BigInt
175 return BigInt(-b.i)
176
177
178def Add(a, b):
179 # type: (BigInt, BigInt) -> BigInt
180 return BigInt(a.i + b.i)
181
182
183def Sub(a, b):
184 # type: (BigInt, BigInt) -> BigInt
185 return BigInt(a.i - b.i)
186
187
188def Mul(a, b):
189 # type: (BigInt, BigInt) -> BigInt
190 return BigInt(a.i * b.i)
191
192
193def Div(a, b):
194 # type: (BigInt, BigInt) -> BigInt
195 """Integer division.
196
197 Oils rounds toward zero.
198
199 Python rounds toward negative infinity, while C++ rounds toward zero. We
200 have to work around Python a bit.
201 """
202 assert b.i != 0, b.i # divisor can't be zero -- caller checks
203
204 # Only use Python // on non-negative numbers. Apply sign afterward.
205 sign = 1
206
207 if a.i < 0:
208 pa = -a.i
209 sign = -1
210 else:
211 pa = a.i
212
213 if b.i < 0:
214 pb = -b.i
215 sign = -sign
216 else:
217 pb = b.i
218
219 return BigInt(sign * (pa // pb))
220
221
222def Rem(a, b):
223 # type: (BigInt, BigInt) -> BigInt
224 """Integer remainder."""
225 assert b.i != 0, b.i # YSH divisor must be positive, but OSH can be negative
226
227 # Only use Python % on non-negative numbers. Apply sign afterward.
228 if a.i < 0:
229 pa = -a.i
230 sign = -1
231 else:
232 pa = a.i
233 sign = 1
234
235 if b.i < 0:
236 pb = -b.i
237 else:
238 pb = b.i
239
240 return BigInt(sign * (pa % pb))
241
242
243def Equal(a, b):
244 # type: (BigInt, BigInt) -> bool
245 return a.i == b.i
246
247
248def Greater(a, b):
249 # type: (BigInt, BigInt) -> bool
250 return a.i > b.i
251
252
253# GreaterEq, Less, LessEq can all be expressed as the 2 ops above
254
255
256def LShift(a, b):
257 # type: (BigInt, BigInt) -> BigInt
258 assert b.i >= 0, b.i # Must be checked by caller
259 return BigInt(a.i << b.i)
260
261
262def RShift(a, b):
263 # type: (BigInt, BigInt) -> BigInt
264 assert b.i >= 0, b.i # Must be checked by caller
265 return BigInt(a.i >> b.i)
266
267
268def BitAnd(a, b):
269 # type: (BigInt, BigInt) -> BigInt
270 return BigInt(a.i & b.i)
271
272
273def BitOr(a, b):
274 # type: (BigInt, BigInt) -> BigInt
275 return BigInt(a.i | b.i)
276
277
278def BitXor(a, b):
279 # type: (BigInt, BigInt) -> BigInt
280 return BigInt(a.i ^ b.i)
281
282
283def BitNot(a):
284 # type: (BigInt) -> BigInt
285 return BigInt(~a.i)