1 | """
|
2 | Math operations, e.g. for arbitrary precision integers
|
3 |
|
4 | They are currently int64_t, rather than C int, but we want to upgrade to
|
5 | heap-allocated integers.
|
6 |
|
7 | Regular int ops can use the normal operators + - * /, or maybe i_add() if we
|
8 | really want. Does that make code gen harder or worse?
|
9 |
|
10 | Float ops could be + - * / too, but it feels nicer to develop a formal
|
11 | interface?
|
12 | """
|
13 | from __future__ import print_function
|
14 |
|
15 | from typing import Tuple
|
16 |
|
17 |
|
18 | class 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 |
|
51 | ZERO = BigInt(0)
|
52 | ONE = BigInt(1)
|
53 | MINUS_ONE = BigInt(-1)
|
54 | MINUS_TWO = BigInt(-2) # for printf
|
55 |
|
56 |
|
57 | def ToStr(b):
|
58 | # type: (BigInt) -> str
|
59 | return str(b.i)
|
60 |
|
61 |
|
62 | def ToOctal(b):
|
63 | # type: (BigInt) -> str
|
64 | return '%o' % b.i
|
65 |
|
66 |
|
67 | def ToHexUpper(b):
|
68 | # type: (BigInt) -> str
|
69 | return '%X' % b.i
|
70 |
|
71 |
|
72 | def 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 |
|
108 | def FromStr(s, base=10):
|
109 | # type: (str, int) -> BigInt
|
110 | return BigInt(int(s, base))
|
111 |
|
112 |
|
113 | MAX_POS_INT = 2**63 - 1
|
114 | MAX_NEG_INT = 2**63
|
115 |
|
116 |
|
117 | def 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 | # Simulate C++ overflow
|
126 | if big_int.i > MAX_POS_INT:
|
127 | return (False, MINUS_ONE)
|
128 | if big_int.i < MAX_NEG_INT:
|
129 | return (False, MINUS_ONE)
|
130 |
|
131 | return (True, big_int)
|
132 | else:
|
133 | return (False, MINUS_ONE)
|
134 |
|
135 |
|
136 | def BigTruncate(b):
|
137 | # type: (BigInt) -> int
|
138 | """Only truncates in C++"""
|
139 | return b.i
|
140 |
|
141 |
|
142 | def IntWiden(i):
|
143 | # type: (int) -> BigInt
|
144 | """Only widens in C++"""
|
145 | return BigInt(i)
|
146 |
|
147 |
|
148 | def FromC(i):
|
149 | # type: (int) -> BigInt
|
150 | """A no-op in C, for RLIM_INFINITY"""
|
151 | return BigInt(i)
|
152 |
|
153 |
|
154 | def FromBool(b):
|
155 | # type: (bool) -> BigInt
|
156 | """Only widens in C++"""
|
157 | return BigInt(1) if b else BigInt(0)
|
158 |
|
159 |
|
160 | def ToFloat(b):
|
161 | # type: (BigInt) -> float
|
162 | """Used by float(42) in Oils"""
|
163 | return float(b.i)
|
164 |
|
165 |
|
166 | def 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 |
|
181 | def Negate(b):
|
182 | # type: (BigInt) -> BigInt
|
183 | return BigInt(-b.i)
|
184 |
|
185 |
|
186 | def Add(a, b):
|
187 | # type: (BigInt, BigInt) -> BigInt
|
188 | return BigInt(a.i + b.i)
|
189 |
|
190 |
|
191 | def Sub(a, b):
|
192 | # type: (BigInt, BigInt) -> BigInt
|
193 | return BigInt(a.i - b.i)
|
194 |
|
195 |
|
196 | def Mul(a, b):
|
197 | # type: (BigInt, BigInt) -> BigInt
|
198 | return BigInt(a.i * b.i)
|
199 |
|
200 |
|
201 | def 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 |
|
230 | def 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 |
|
251 | def Equal(a, b):
|
252 | # type: (BigInt, BigInt) -> bool
|
253 | return a.i == b.i
|
254 |
|
255 |
|
256 | def 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 |
|
264 | def 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 |
|
270 | def 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 |
|
276 | def BitAnd(a, b):
|
277 | # type: (BigInt, BigInt) -> BigInt
|
278 | return BigInt(a.i & b.i)
|
279 |
|
280 |
|
281 | def BitOr(a, b):
|
282 | # type: (BigInt, BigInt) -> BigInt
|
283 | return BigInt(a.i | b.i)
|
284 |
|
285 |
|
286 | def BitXor(a, b):
|
287 | # type: (BigInt, BigInt) -> BigInt
|
288 | return BigInt(a.i ^ b.i)
|
289 |
|
290 |
|
291 | def BitNot(a):
|
292 | # type: (BigInt) -> BigInt
|
293 | return BigInt(~a.i)
|