1# -*- coding: utf-8 -*- 2 3""" 4Integer Functions 5""" 6 7 8import sympy 9import string 10 11from mathics.version import __version__ # noqa used in loading to check consistency. 12 13from mathics.builtin.base import Builtin, SympyFunction 14from mathics.core.convert import from_sympy 15from mathics.core.expression import Integer, Integer0, String, Expression 16 17 18class Floor(SympyFunction): 19 """ 20 <dl> 21 <dt>'Floor[$x$]' 22 <dd>gives the smallest integer less than or equal to $x$. 23 24 <dt>'Floor[$x$, $a$]' 25 <dd>gives the smallest multiple of $a$ less than or equal to $x$. 26 </dl> 27 28 >> Floor[10.4] 29 = 10 30 >> Floor[10/3] 31 = 3 32 >> Floor[10] 33 = 10 34 >> Floor[21, 2] 35 = 20 36 >> Floor[2.6, 0.5] 37 = 2.5 38 >> Floor[-10.4] 39 = -11 40 41 For complex $x$, take the floor of real an imaginary parts. 42 >> Floor[1.5 + 2.7 I] 43 = 1 + 2 I 44 45 For negative $a$, the smallest multiple of $a$ greater than or equal to $x$ 46 is returned. 47 >> Floor[10.4, -1] 48 = 11 49 >> Floor[-10.4, -1] 50 = -10 51 """ 52 53 attributes = ("Listable", "NumericFunction", "Protected") 54 55 sympy_name = "floor" 56 rules = {"Floor[x_, a_]": "Floor[x / a] * a"} 57 58 def apply_real(self, x, evaluation): 59 "Floor[x_]" 60 x = x.to_sympy() 61 if x is not None: 62 return from_sympy(sympy.floor(x)) 63 64 65class Ceiling(SympyFunction): 66 """ 67 <dl> 68 <dt>'Ceiling[$x$]' 69 <dd>gives the first integer greater than $x$. 70 </dl> 71 72 >> Ceiling[1.2] 73 = 2 74 >> Ceiling[3/2] 75 = 2 76 77 For complex $x$, take the ceiling of real an imaginary parts. 78 >> Ceiling[1.3 + 0.7 I] 79 = 2 + I 80 """ 81 82 attributes = ("Listable", "NumericFunction", "Protected") 83 84 rules = {"Ceiling[x_, a_]": "Ceiling[x / a] * a"} 85 86 def apply(self, x, evaluation): 87 "Ceiling[x_]" 88 x = x.to_sympy() 89 if x is None: 90 return 91 return from_sympy(sympy.ceiling(x)) 92 93 94class IntegerLength(Builtin): 95 """ 96 <dl> 97 <dt>'IntegerLength[$x$]' 98 <dd>gives the number of digits in the base-10 representation of $x$. 99 <dt>'IntegerLength[$x$, $b$]' 100 <dd>gives the number of base-$b$ digits in $x$. 101 </dl> 102 103 >> IntegerLength[123456] 104 = 6 105 >> IntegerLength[10^10000] 106 = 10001 107 >> IntegerLength[-10^1000] 108 = 1001 109 'IntegerLength' with base 2: 110 >> IntegerLength[8, 2] 111 = 4 112 Check that 'IntegerLength' is correct for the first 100 powers of 10: 113 >> IntegerLength /@ (10 ^ Range[100]) == Range[2, 101] 114 = True 115 The base must be greater than 1: 116 >> IntegerLength[3, -2] 117 : Base -2 is not an integer greater than 1. 118 = IntegerLength[3, -2] 119 120 '0' is a special case: 121 >> IntegerLength[0] 122 = 0 123 124 #> IntegerLength /@ (10 ^ Range[100] - 1) == Range[1, 100] 125 = True 126 """ 127 128 attributes = ("Listable", "Protected") 129 130 rules = { 131 "IntegerLength[n_]": "IntegerLength[n, 10]", 132 } 133 134 messages = { 135 "base": "Base `1` is not an integer greater than 1.", 136 } 137 138 def apply(self, n, b, evaluation): 139 "IntegerLength[n_, b_]" 140 141 n, b = n.get_int_value(), b.get_int_value() 142 if n is None or b is None: 143 evaluation.message("IntegerLength", "int") 144 return 145 if b <= 1: 146 evaluation.message("IntegerLength", "base", b) 147 return 148 149 if n == 0: 150 # special case 151 return Integer0 152 153 n = abs(n) 154 155 # O(log(digits)) 156 157 # find bounds 158 j = 1 159 while b ** j <= n: 160 j *= 2 161 i = j // 2 162 163 # bisection 164 while i + 1 < j: 165 # assert b ** i <= n <= b ** j 166 k = (i + j) // 2 167 if b ** k <= n: 168 i = k 169 else: 170 j = k 171 return Integer(j) 172 173 174class BitLength(Builtin): 175 """ 176 <dl> 177 <dt>'BitLength[$x$]' 178 <dd>gives the number of bits needed to represent the integer $x$. $x$'s sign is ignored. 179 </dl> 180 181 >> BitLength[1023] 182 = 10 183 >> BitLength[100] 184 = 7 185 >> BitLength[-5] 186 = 3 187 >> BitLength[0] 188 = 0 189 """ 190 191 attributes = ("Listable", "Protected") 192 193 def apply(self, n, evaluation): 194 "BitLength[n_Integer]" 195 n = n.get_int_value() 196 if n < 0: 197 n = -1 - n 198 return Integer(n.bit_length()) 199 200 201def _reversed_digits( 202 number, base 203): # yield digits for number in base "base" in reverse order 204 number = abs(number) 205 if number == 0: 206 yield 0 207 else: 208 while number > 0: 209 rest, digit = divmod(number, base) 210 yield digit 211 number = rest 212 213 214def _pad(symbols, length, fill): # pads "symbols" to length "length" using "fill" 215 pad_length = length - len(symbols) 216 if pad_length <= 0: 217 return symbols[-pad_length:] 218 else: 219 return fill * pad_length + symbols 220 221 222class IntegerString(Builtin): 223 """ 224 <dl> 225 <dt>'IntegerString[$n$]' 226 <dd>returns the decimal representation of integer $x$ as string. $x$'s sign is ignored. 227 <dt>'IntegerString[$n$, $b$]' 228 <dd>returns the base $b$ representation of integer $x$ as string. $x$'s sign is ignored. 229 <dt>'IntegerString[$n$, $b$, $length$]' 230 <dd>returns a string of length $length$. If the number is too short, the string gets padded 231 with 0 on the left. If the number is too long, the $length$ least significant digits are 232 returned. 233 </dl> 234 235 For bases > 10, alphabetic characters a, b, ... are used to represent digits 11, 12, ... . Note 236 that base must be an integer in the range from 2 to 36. 237 238 >> IntegerString[12345] 239 = 12345 240 >> IntegerString[-500] 241 = 500 242 >> IntegerString[12345, 10, 8] 243 = 00012345 244 >> IntegerString[12345, 10, 3] 245 = 345 246 >> IntegerString[11, 2] 247 = 1011 248 >> IntegerString[123, 8] 249 = 173 250 >> IntegerString[32767, 16] 251 = 7fff 252 >> IntegerString[98765, 20] 253 = c6i5 254 """ 255 256 attributes = ("Listable", "Protected") 257 258 rules = { 259 "IntegerString[n_Integer]": "IntegerString[n, 10]", 260 } 261 262 messages = { 263 "basf": "Base `` must be an integer in the range from 2 to 36.", 264 } 265 266 list_of_symbols = string.digits + string.ascii_letters 267 268 _python_builtin = { 269 16: lambda number: hex(abs(number))[2:], 270 10: lambda number: str(abs(number)), 271 2: lambda number: bin(abs(number))[2:], 272 # oct() changed definition for Python 3 273 } 274 275 def _symbols(self, n, b, evaluation): 276 builtin = IntegerString._python_builtin.get(b) 277 if builtin: 278 return builtin(n) 279 else: 280 list_of_symbols = IntegerString.list_of_symbols 281 if b > len(list_of_symbols) or b < 2: 282 evaluation.message("IntegerString", "basf", b) 283 return False 284 else: 285 return "".join( 286 reversed([list_of_symbols[r] for r in _reversed_digits(n, b)]) 287 ) 288 289 def apply_n(self, n, b, evaluation): 290 "IntegerString[n_Integer, b_Integer]" 291 s = self._symbols(n.get_int_value(), b.get_int_value(), evaluation) 292 return String(s) if s else None 293 294 def apply_n_b_length(self, n, b, length, evaluation): 295 "IntegerString[n_Integer, b_Integer, length_Integer]" 296 s = self._symbols(n.get_int_value(), b.get_int_value(), evaluation) 297 return String(_pad(s, length.get_int_value(), "0")) if s else None 298 299 300class _IntBaseBuiltin(Builtin): 301 messages = { 302 "basf": "Base `` must be an integer greater than 1.", 303 } 304 305 def _valid_base(self, b, evaluation): 306 base = b.get_int_value() 307 if base < 2: 308 evaluation.message(self.get_name(), "basf", base) 309 return False 310 else: 311 return base 312 313 314class IntegerDigits(_IntBaseBuiltin): 315 """ 316 <dl> 317 <dt>'IntegerDigits[$n$]' 318 <dd>returns the decimal representation of integer $x$ as list of digits. $x$'s sign is ignored. 319 <dt>'IntegerDigits[$n$, $b$]' 320 <dd>returns the base $b$ representation of integer $x$ as list of digits. $x$'s sign is ignored. 321 <dt>'IntegerDigits[$n$, $b$, $length$]' 322 <dd>returns a list of length $length$. If the number is too short, the list gets padded 323 with 0 on the left. If the number is too long, the $length$ least significant digits are 324 returned. 325 </dl> 326 327 >> IntegerDigits[12345] 328 = {1, 2, 3, 4, 5} 329 >> IntegerDigits[-500] 330 = {5, 0, 0} 331 >> IntegerDigits[12345, 10, 8] 332 = {0, 0, 0, 1, 2, 3, 4, 5} 333 >> IntegerDigits[12345, 10, 3] 334 = {3, 4, 5} 335 >> IntegerDigits[11, 2] 336 = {1, 0, 1, 1} 337 >> IntegerDigits[123, 8] 338 = {1, 7, 3} 339 >> IntegerDigits[98765, 20] 340 = {12, 6, 18, 5} 341 """ 342 343 rules = { 344 "IntegerDigits[n_Integer]": "IntegerDigits[n, 10]", 345 } 346 347 _padding = [Integer0] 348 349 def apply_n_b(self, n, b, evaluation): 350 "IntegerDigits[n_Integer, b_Integer]" 351 base = self._valid_base(b, evaluation) 352 return ( 353 Expression( 354 "List", 355 *[ 356 Integer(d) 357 for d in reversed(list(_reversed_digits(n.get_int_value(), base))) 358 ] 359 ) 360 if base 361 else None 362 ) 363 364 def apply_n_b_length(self, n, b, length, evaluation): 365 "IntegerDigits[n_Integer, b_Integer, length_Integer]" 366 base = self._valid_base(b, evaluation) 367 return ( 368 Expression( 369 "List", 370 *_pad( 371 [ 372 Integer(d) 373 for d in reversed( 374 list(_reversed_digits(n.get_int_value(), base)) 375 ) 376 ], 377 length.get_int_value(), 378 self._padding, 379 ) 380 ) 381 if base 382 else None 383 ) 384 385 386class DigitCount(_IntBaseBuiltin): 387 """ 388 <dl> 389 <dt>'DigitCount[$n$, $b$, $d$]' 390 <dd>returns the number of times digit $d$ occurs in the base $b$ representation of $n$. 391 <dt>'DigitCount[$n$, $b$]' 392 <dd>returns a list indicating the number of times each digit occurs in the base $b$ representation of $n$. 393 <dt>'DigitCount[$n$, $b$]' 394 <dd>returns a list indicating the number of times each digit occurs in the decimal representation of $n$. 395 </dl> 396 397 >> DigitCount[1022] 398 = {1, 2, 0, 0, 0, 0, 0, 0, 0, 1} 399 >> DigitCount[Floor[Pi * 10^100]] 400 = {8, 12, 12, 10, 8, 9, 8, 12, 14, 8} 401 >> DigitCount[1022, 2] 402 = {9, 1} 403 >> DigitCount[1022, 2, 1] 404 = 9 405 """ 406 407 rules = { 408 "DigitCount[n_Integer]": "DigitCount[n, 10]", 409 } 410 411 def apply_n_b_d(self, n, b, d, evaluation): 412 "DigitCount[n_Integer, b_Integer, d_Integer]" 413 base = self._valid_base(b, evaluation) 414 if not base: 415 return 416 match = d.get_int_value() 417 return Integer( 418 sum( 419 1 420 for digit in _reversed_digits(n.get_int_value(), base) 421 if digit == match 422 ) 423 ) 424 425 def apply_n_b(self, n, b, evaluation): 426 "DigitCount[n_Integer, b_Integer]" 427 base = self._valid_base(b, evaluation) 428 if not base: 429 return 430 occurence_count = [0] * base 431 for digit in _reversed_digits(n.get_int_value(), base): 432 occurence_count[digit] += 1 433 # result list is rotated by one element to the left 434 return Expression("List", *(occurence_count[1:] + [occurence_count[0]])) 435 436 437class IntegerReverse(_IntBaseBuiltin): 438 """ 439 <dl> 440 <dt>'IntegerReverse[$n$]' 441 <dd>returns the integer that has the reverse decimal representation of $x$ without sign. 442 <dt>'IntegerReverse[$n$, $b$]' 443 <dd>returns the integer that has the reverse base $b$ represenation of $x$ without sign. 444 </dl> 445 446 >> IntegerReverse[1234] 447 = 4321 448 >> IntegerReverse[1022, 2] 449 = 511 450 >> IntegerReverse[-123] 451 = 321 452 """ 453 454 rules = { 455 "IntegerReverse[n_Integer]": "IntegerReverse[n, 10]", 456 } 457 458 def apply_n_b(self, n, b, evaluation): 459 "IntegerReverse[n_Integer, b_Integer]" 460 base = self._valid_base(b, evaluation) 461 if not base: 462 return 463 value = 0 464 for digit in _reversed_digits(n.get_int_value(), base): 465 value = value * base + digit 466 return Integer(value) 467 468 469class FromDigits(Builtin): 470 """ 471 <dl> 472 <dt>'FromDigits[$l$]' 473 <dd>returns the integer corresponding to the decimal representation given by $l$. $l$ can be a list of 474 digits or a string. 475 <dt>'FromDigits[$l$, $b$]' 476 <dd>returns the integer corresponding to the base $b$ representation given by $l$. $l$ can be a list of 477 digits or a string. 478 </dl> 479 480 >> FromDigits["123"] 481 = 123 482 >> FromDigits[{1, 2, 3}] 483 = 123 484 >> FromDigits[{1, 0, 1}, 1000] 485 = 1000001 486 487 FromDigits can handle symbolic input: 488 >> FromDigits[{a, b, c}, 5] 489 = c + 5 (5 a + b) 490 491 Note that FromDigits does not automatically detect if you are providing a non-decimal representation: 492 >> FromDigits["a0"] 493 = 100 494 >> FromDigits["a0", 16] 495 = 160 496 497 FromDigits on empty lists or strings returns 0: 498 >> FromDigits[{}] 499 = 0 500 >> FromDigits[""] 501 = 0 502 503 #> FromDigits[x] 504 : The input must be a string of digits or a list. 505 = FromDigits[x, 10] 506 """ 507 508 rules = {"FromDigits[l_]": "FromDigits[l, 10]"} 509 510 messages = {"nlst": "The input must be a string of digits or a list."} 511 512 @staticmethod 513 def _parse_string(s, b): 514 code_0 = ord("0") 515 code_a = ord("a") 516 assert code_a > code_0 517 518 value = Integer0 519 for char in s.lower(): 520 code = ord(char) 521 if code >= code_a: 522 digit = 10 + code - code_a 523 else: 524 digit = code - code_0 525 if 0 <= digit < 36: 526 value = Expression( 527 "Plus", Expression("Times", value, b), Integer(digit) 528 ) 529 else: 530 return None 531 532 return value 533 534 def apply(self, l, b, evaluation): 535 "FromDigits[l_, b_]" 536 if l.get_head_name() == "System`List": 537 value = Integer0 538 for leaf in l.leaves: 539 value = Expression("Plus", Expression("Times", value, b), leaf) 540 return value 541 elif isinstance(l, String): 542 value = FromDigits._parse_string(l.get_string_value(), b) 543 if value is None: 544 evaluation.message("FromDigits", "nlst") 545 else: 546 return value 547 else: 548 evaluation.message("FromDigits", "nlst") 549