../

Numbers

int, float and friends in Python 3.14: literals, division and modulo rules, unbounded integers, bits, float traps, math and number theory, modular arithmetic, parsing and formatting, costs, and the integer idioms DS&A solutions are built from. The JS side is in Coming from JavaScript.

Types & literals

TypeLiteralsNotes
int42, -7, 1_000_000, 0xFF, 0o17, 0b1011unbounded: never overflows
float3.14, 1e9, 2.5e-3, float("inf"), math.infIEEE 754 double, like a JS number
boolTrue, Falsea subclass of int: True + True == 2, sum(flags) counts Trues
complex3 + 4jabs(3 + 4j) == 5.0; rare in DS&A
fractions.FractionFraction(1, 3)exact rationals: Fraction(1, 3) + Fraction(1, 6) == Fraction(1, 2)
decimal.DecimalDecimal("0.1")exact decimal digits for money; build it from a str, not a float
type(10**9), type(1e9)  # (<class 'int'>, <class 'float'>)
1e9 == 10**9  # True, but 1e9 is a float: use 10**9
int(" 42 "), int("1_000"), int("-7")  # (42, 1000, -7)
int("١٢٣")  # 123: any Unicode decimal digits parse
int(3.99), int(-3.99)  # (3, -3): truncates toward 0
True + True, isinstance(True, int)  # (2, True)
{1: "a", 1.0: "b", True: "c"}  # {1: 'c'}: one key

1 == 1.0 == True and they hash alike, so they collide as dict keys and set members. Write 10**9, not 1e9, wherever an int is expected (range(1e9) is a TypeError).

Arithmetic & division

ExpressionResultRule
7 / 2, 6 / 33.5, 2.0/ always returns a float
7 // 2, -7 // 2, 7 // -23, -4, -4// floors toward minus infinity (C and JS truncate)
-7 % 3, 7 % -32, -2% takes the divisor's sign, so a == (a // b) * b + a % b
divmod(-7, 2)(-4, 1)(a // b, a % b) in one call
math.fmod(-7, 3)-1.0C-style remainder (sign of the dividend), float result
-(-7 // 2)4ceiling division, exact for any int size
2 ** 10, 2 ** -11024, 0.5a negative exponent gives a float
-2 ** 2-4** binds tighter than unary minus: write (-2) ** 2
2 ** 3 ** 2512** is right-associative: 2 ** 9
pow(2, 10, 1000)24modular power, fast for huge exponents
abs(-5), max(3, 7), min(...)5, 7max/min take an iterable or several arguments
round(2.5), round(3.5), round(-0.5)2, 4, 0round half to even (banker's rounding)
round(1250, -2), round(2.675, 2)1200, 2.67negative digits round to tens, hundreds; 2.675 is really 2.67499...
a, b = -7, 2
q, r = divmod(a, b)  # (-4, 1)
assert q * b + r == a  # always holds
 
# truncating division (C, Java, JS Math.trunc), exactly
def tdiv(a: int, b: int) -> int:
    q = abs(a) // abs(b)
    return q if (a < 0) == (b < 0) else -q
 
 
tdiv(-7, 2), int(-7 / 2)  # (-3, -3)
int(10**30 / 7) == 10**30 // 7  # False: / went via float
-8 -7 -6 -5 -4 -3 -2 -1 0 a = -7 -7 / 2 = -3.5 -7 // 2 = -4 int(-7 / 2) = -3 -4 * 2 == -8 -7 % 2 == -7 - (-8) == 1 -3 * 2 == -6 math.fmod(-7, 2) == -7 - (-6) == -1.0 -7 == (-7 // 2) * 2 + (-7 % 2) == -8 + 1 -7 == int(-7 / 2) * 2 + fmod(-7, 2) == -6 - 1 Python: floor C, Java, JS: truncate

1/6

-7 / 2 is -3.5, between -4 and -3. Integer division has to pick one of them.

Unbounded integers

An int grows as needed, so 2**1000 is exact and nothing ever wraps around. Problems written for 32-bit languages expect you to detect or simulate overflow yourself.

NeedPython
32-bit range check-2**31 <= x <= 2**31 - 1 (-2**31 is -(2**31), the right value)
wrap to signed 32-bit(x + 2**31) % 2**32 - 2**31
wrap to unsigned 32-bitx & 0xFFFFFFFF
wrap to signed 64-bit(x + 2**63) % 2**64 - 2**63
number of bitsx.bit_length(): (255).bit_length() == 8, (0).bit_length() == 0
the biggest intthere isn't one; sys.maxsize (2**63 - 1 on 64-bit) is the largest container size, not an int limit
import sys
 
INT_MAX = 2**31 - 1
x = INT_MAX + 1  # 2147483648: no overflow
(x + 2**31) % 2**32 - 2**31  # -2147483648: wrapped
sys.maxsize + 1  # 9223372036854775808: still fine
 
sys.get_int_max_str_digits()  # 4300
try:
    str(7**6000)  # computing 7**6000 is fine; printing isn't
except ValueError as e:
    print(type(e).__name__)  # ValueError
len(hex(7**6000)), (7**6000).bit_length()  # (4214, 16845)
sys.set_int_max_str_digits(0)  # 0 = no limit
len(str(7**6000))  # 5071

is compares identity, not value. CPython caches the ints from -5 to 256, so int("256") is int("256") is True and int("257") is int("257") is False. Compare numbers with ==.

Bits

OperatorMeaningPython detail
a & b, a | b, a ^ band, or, xorbind tighter than ==: n & 1 == 0 means (n & 1) == 0, unlike C and JS
~xnot-x - 1: ~5 == -6
x << kshift leftexact: 1 << 100 is a 101-bit int
x >> kshift rightfloors: -7 >> 1 == -4, -1 >> 10 == -1; there is no >>>
x.bit_count()number of 1 bits (3.10+)counts the bits of abs(x): (-5).bit_count() == 2
x.bit_length()bits needed without the signn.bit_length() - 1 is ⌊log₂ n⌋ for n > 0
bin(x), format(x, "b")'0b101', '101'bin(-5) is '-0b101': sign and magnitude, not two's complement
f"{x:08b}", f"{x:#x}"zero-padded binary, 0x hexf"{5:08b}" == "00000101"
int(s, 2)parse binaryint("1011", 2) == 11, int("0b1011", 0) == 11

Negative ints behave as if they had infinitely many leading 1 bits. Mask with & 0xFFFFFFFF to see a fixed-width two's complement pattern, and convert back to signed by hand:

MASK = 0xFFFFFFFF
x = -5
x & MASK  # 4294967291
format(x & MASK, "032b")[-8:]  # '11111011'
 
 
def to_signed32(u: int) -> int:
    u &= MASK
    return u - (1 << 32) if u & (1 << 31) else u
 
 
to_signed32(0xFFFFFFFF), to_signed32(2**31)
# (-1, -2147483648)
-7 >> 1, ~5, 6 & -6  # (-4, -6, 2): lowest set bit
5 ...0 0 0 0 0 0 0 0 0 0 1 0 1 -5 ...1 1 1 1 1 1 1 1 1 1 0 1 1 infinitely many 1s to the left -5 & 0xFFFFFFFF 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 bit 31 bit 0 = 4294967291: a 32-bit window, read as unsigned bin(-5) '-0b101' sign and magnitude, not the bit pattern ~x ~5 == -6: flips every bit, the infinite 0s too
A negative int has infinitely many leading 1s; a mask cuts a fixed-width window out of them

The tricks themselves (lowest set bit, submasks, xor cancellation, popcount DP) are in Bit manipulation. Two of them, bit by bit:

7 6 5 4 3 2 1 0 bit x = 12 0 0 0 0 1 1 0 0 x = 12 0 0 0 0 1 1 0 0 x - 1 = 11 0 0 0 0 1 0 1 1 x = 12 0 0 0 0 1 1 0 0 x - 1 = 11 0 0 0 0 1 0 1 1 x & (x - 1) = 8 0 0 0 0 1 0 0 0 & x = 12 0 0 0 0 1 1 0 0 -x = -12 1 1 1 1 0 1 0 0 x = 12 0 0 0 0 1 1 0 0 -x = -12 1 1 1 1 0 1 0 0 x & -x = 4 0 0 0 0 0 1 0 0 & n = 8 0 0 0 0 1 0 0 0 n - 1 = 7 0 0 0 0 0 1 1 1 n & (n - 1) = 0 0 0 0 0 0 0 0 0 & (low 8 bits) lowest set bit differs from the top row 1 in the result

1/6

x = 12 is 0b1100. Its lowest set bit is bit 2.

Floats

A float is an IEEE 754 double: 53 bits of mantissa, so every integer up to 2**53 is exact and most decimal fractions are not.

adjacent floats are evenly spaced within each power of two; the gap, math.ulp(x), doubles at every power of two near 1.0 gap math.ulp(1.0) == 2**-52 = 2.220446049250313e-16 near 1e9 gap math.ulp(1e9) == 2**-23 = 1.1920928955078125e-07 at 2**52 gap math.ulp(2**52) == 1.0: whole numbers only at 2**53 gap math.ulp(2**53) == 2.0: odd integers are skipped 2**53 +2 +4 +1 float(2**53 + 1) == 2**53: the odd integer rounds to its even neighbor
The gap between adjacent floats, math.ulp(x), doubles at each power of two
ExpressionResultWhy
0.1 + 0.20.30000000000000004binary fractions; compare with math.isclose
math.isclose(0.1 + 0.2, 0.3)Truedefaults rel_tol=1e-09, abs_tol=0.0
math.isclose(1e-10, 0)Falserelative tolerance is useless near 0: pass abs_tol=1e-9
float(2**53 + 1) == 2**53Truepast 2**53 floats skip integers
int(1e23)999999999999999916113921e23 was never exactly 10²³
float(10**400), 10**400 / 3OverflowErroran int too big for a float
math.inf > 10**1000Trueinf compares above every int
math.inf - math.inf, math.inf * 0nan
math.nan == math.nanFalsetest with math.isnan(x)
max(1, math.nan), max(math.nan, 1)1, nanNaN poisons comparisons, order-dependent
int(math.inf), int(math.nan)OverflowError, ValueError
math.floor(-2.5), math.ceil(-2.5), math.trunc(-2.5)-3, -2, -2all three return an int
(2.0).is_integer()Trueint.is_integer() exists too (3.12+)
sum([0.1] * 10)1.0compensated summation since 3.12 (3.11 gave 0.9999999999999999)
math.fsum(xs)accurate totaltracks exact partial sums; the docs allow a rare last-bit error on some builds
sys.float_info.epsilon2.220446049250313e-162**-52, the gap after 1.0
import math
from fractions import Fraction
 
(0.1).as_integer_ratio()
# (3602879701896397, 36028797018963968)
Fraction("0.1") + Fraction("0.2") == Fraction("0.3")  # True
math.isclose(1e-10, 0, abs_tol=1e-9)  # True
10**16 / 3 == (10**16 + 1) / 3  # True: different, same float

Binary search over floats: loop a fixed number of times (for _ in range(100):) instead of while hi - lo > eps, which spins forever when eps is smaller than the gap between adjacent floats at that magnitude (about 1.2e-7 near 10**9).

math & number theory

CallResultNotes
math.gcd(12, 18, 24)6any number of arguments (3.9+); gcd(0, 5) == 5; always non-negative
math.lcm(4, 6, 10)603.9+
math.isqrt(n)⌊√n⌋, exact3.8+; ValueError for n < 0; ceiling ⌈√n⌉ is 1 + isqrt(n - 1) for n > 0
math.comb(5, 2), math.perm(5, 2)10, 20exact ints; comb(3, 5) == 0
math.factorial(5)120
math.prod([1, 2, 3, 4])24prod([]) == 1
math.sumprod([1, 2], [3, 4])11dot product (3.12+)
pow(b, e, m)b**e % mO(log e) multiplications
pow(a, -1, m)modular inverse3.8+; ValueError when gcd(a, m) != 1
math.log2(x), math.log10(x)float logsinexact for big ints: use bit_length or len(str(n))
math.log(x, 2)floatworse than log2: math.log(2**29, 2) != 29
math.dist(p, q), math.hypot(dx, dy)Euclidean distance
import math
 
math.isqrt(10**30 + 1)  # 1000000000000000
n = 10**34 - 1
int(n**0.5), math.isqrt(n)
# (100000000000000000, 99999999999999999)
math.log2(2**50 - 1), (2**50 - 1).bit_length() - 1
# (50.0, 49): only bit_length is right
pow(38, -1, 97)  # 23, since 38 * 23 % 97 == 1
math.comb(52, 5)  # 2598960 poker hands

Modular arithmetic

"Return the answer modulo 10⁹ + 7" keeps answers small; in Python it also keeps intermediate numbers small, which keeps them fast. Reduce after every + and *.

RulePython
sum, product(a + b) % MOD, a * b % MOD
difference(a - b) % MOD: already in [0, MOD), no + MOD needed
powerpow(a, e, MOD), never a**e % MOD
division by bmultiply by pow(b, -1, MOD); for a prime MOD also pow(b, MOD - 2, MOD) (Fermat)
constantMOD = 10**9 + 7; 10e9 + 7 is the float 10000000007.0
MOD = 10**9 + 7
(3 - 5) % MOD  # 1000000005: non-negative already
pow(2, 10**18, MOD)  # 719476260, instantly
inv3 = pow(3, -1, MOD)
6 * inv3 % MOD  # 2: "6 / 3" mod p
pow(3, MOD - 2, MOD) == inv3  # True: Fermat

Counting DP with large answers (paths, ways to climb, subsets) reduces each table cell; precomputed factorials give nCr % p in O(1) per query (recipe below).

Parsing & formatting

CallResultNotes
int("ff", 16), int("z", 36)255, 35bases 2–36
int("0x1f", 0)31base 0 reads the prefix like a literal; rejects "010"
int("12px"), int(""), int("1.5")ValueErrorthe whole string must be an integer (JS parseInt stops at junk)
float(" 1.5 "), float("1e3"), float("-inf")1.5, 1000.0, -inf
bin(10), oct(8), hex(255)'0b1010', '0o10', '0xff'bin(10)[2:] or format(10, "b") without the prefix
f"{1234567:,}", f"{1234567:_}"'1,234,567', '1_234_567'grouping
f"{3.14159:.2f}", f"{0.5:.0%}", f"{1e-7:.2e}"'3.14', '50%', '1.00e-07'fixed, percent, scientific
f"{42:>5}", f"{-42:05d}"' 42', '-0042'width and zero padding
str(n), [int(d) for d in str(n)]digits as text or ints4300-digit limit applies
n.to_bytes(4, "big"), int.from_bytes(b, "big")bytes ↔ intsigned=True for two's complement

The full format-spec grammar is on the Strings sheet, next to f-strings.

n = 9045
[int(d) for d in str(n)]  # [9, 0, 4, 5]
sum(map(int, str(n)))  # 18
 
digits: list[int] = []
m = n
while m:
    m, d = divmod(m, 10)
    digits.append(d)  # least significant first
print(digits)  # [5, 4, 0, 9]
int("".join(map(str, [9, 0, 4, 5])))  # 9045

Costs

OperationCostNotes
+, -, *, //, %, comparisons on ordinary intsO(1) in practicean int is stored in 30-bit "digits"; a 64-bit value is 3 of them
+, - on d-digit intsO(d)
* on d-digit intsO(d²), then Karatsuba O(d^1.585)Karatsuba once both sides pass 70 internal digits (about 630 decimal digits)
pow(b, e, m)O(log e) multiplicationsof numbers below m
str(n), int(s) (base 10)superlinearhence the 4300-digit cap; bases 2, 4, 8, 16, 32 are linear
math.isqrt(n)exact, Newton's method on intsn ** 0.5 is a float: wrong past 2**53, OverflowError past ~10³⁰⁸
math.factorial, math.combfast C codestill big ints: comb(10**5, 5 * 10**4) has 30,101 digits
import sys
 
sys.int_info.bits_per_digit  # 30
sys.getsizeof(1), sys.getsizeof(2**30)  # (28, 32)
sys.getsizeof(10**100)  # 72: 12 digits of 30 bits

Every int is a heap object with a header, so a list of a million ints costs pointers plus objects; array.array("q") or NumPy stores raw 8-byte values (see Lists for container choices).

DS&A idioms

The integer moves problem solutions use, and the trap in each. The patterns are in the DS&A sheets (linked in the last column).

JobPythonWatch outMore
Sentinelsbest = math.inf, worst = -math.infa float: int(math.inf) raises OverflowError; for an int sentinel use sys.maxsizeLists
32-bit overflowif not -2**31 <= x <= 2**31 - 1: return 0ints never overflow, so check by hand where the problem says 32-bitPython gotchas
Ceiling division-(-a // b), or (a + b - 1) // b for a >= 0math.ceil(a / b) goes through a float
Midpointmid = (lo + hi) // 2; upper mid (lo + hi + 1) // 2no overflow to dodge; // floors, so negative ranges still workBinary search
Search on the answerlo, hi = 0, 10**9 then shrinkintegers: exact; floats: a fixed number of iterationsOn the answer
Parityn % 2, n & 1both give 1 for odd negatives (-3 % 2 == 1; JS gives -1)
Power of twon > 0 and n & (n - 1) == 0, or n.bit_count() == 1& binds tighter than == in Python, so no parentheses neededBit manipulation
Bitmask subsetsfor m in range(1 << n):, test m >> i & 12ⁿ masks: only for n up to about 20Input size
Grid index ↔ cellr, c = divmod(i, cols), i = r * cols + ccols, not rows, is the divisorGrids as graphs
Digitswhile n: n, d = divmod(n, 10) or map(int, str(n))take abs(n) first: -123 % 10 == 7Fast & slow pointers
Slopes, ratiosg = gcd(dx, dy), key (dx // g, dy // g) with one sign rulefloat slopes merge different lines and split equal ones
Answers mod pMOD = 10**9 + 7, reduce after every step10e9 + 7 is a float, and the wrong number
Fast powerpow(b, e, MOD)b ** e % MOD builds the whole huge power first
Divisor loopsfor d in range(1, math.isqrt(n) + 1):int(n ** 0.5) is off by one for big n
Perfect squaremath.isqrt(n) ** 2 == n
Missing numbern * (n + 1) // 2 - sum(xs) or xor everythingno overflow worry, unlike in Java/C++Index as hash
Float equalitymath.isclose(a, b, abs_tol=1e-9)default abs_tol=0.0: isclose(x, 0) is False for every x != 0
Floor logn.bit_length() - 1 (base 2), len(str(n)) - 1 (base 10)math.floor(math.log10(999999999999999)) is 15, not 14
Average(a + b) // 2 floors, (a + b) / 2 is a float
import math
from math import gcd
 
rows, cols = 3, 4
r, c = divmod(7, cols)  # (1, 3): cell of index 7
r * cols + c  # 7: and back
 
n = 36
divisors = sorted(
    d
    for i in range(1, math.isqrt(n) + 1)
    if n % i == 0
    for d in {i, n // i}
)
print(divisors)  # [1, 2, 3, 4, 6, 9, 12, 18, 36]
 
dx, dy = -4, 6
g = gcd(dx, dy)  # 2: gcd is never negative
key = (dx // g, dy // g)  # (-2, 3)
if key[0] < 0:  # one sign convention per slope
    key = (-key[0], -key[1])
print(key)  # (2, -3)
 
-(-7 // 2), 8 & 7 == 0, (-3) % 2  # (4, True, 1)

Coming from JavaScript

The TypeScript version of this sheet is Numbers.

JSPython
number (double), BigIntint (exact, unbounded) and float (double); no n suffix
Number.MAX_SAFE_INTEGER (2**53 - 1)no limit for int; floats lose integers past 2**53 the same way
Math.floor(a / b)a // b
Math.trunc(a / b)int(a / b) while it fits a float; exact: tdiv above
a % b (sign of a: -7 % 3 === -1)math.fmod(a, b); Python's % gives 2
Math.round(2.5) === 3, Math.round(-2.5) === -2round(2.5) == 2, round(-2.5) == -2: half to even
(0.125).toFixed(2) === "0.13"f"{0.125:.2f}" == "0.12"
parseInt("12px") === 12, Number("") === 0int("12px") and int("") raise ValueError
parseInt("ff", 16), (255).toString(2)int("ff", 16), format(255, "b")
Number.isInteger(x)isinstance(x, int), or x.is_integer() for a float
1 << 31 === -2147483648, x >>> 01 << 31 == 2147483648; x & 0xFFFFFFFF (no >>>)
Infinity, NaN, isNaN(x)math.inf, math.nan, math.isnan(x)
Number.EPSILONsys.float_info.epsilon
Math.max(...xs)max(xs); max([]) raises unless default=
x ** y, Math.pow(x, y)x ** y, pow(x, y), pow(x, y, m)
Math.sqrt(n)math.sqrt(n) (float), math.isqrt(n) (exact int)
Math.random()random.random() in [0, 1)
Math.floor(Math.random() * n)random.randrange(n); random.randint(a, b) includes b

Recipes

Sieve of Eratosthenes

When you need every prime up to n (counting primes, prime factors of many numbers).

import math
 
 
def primes_upto(n: int) -> list[int]:
    if n < 2:
        return []
    is_p = bytearray([1]) * (n + 1)  # 1 byte per number
    is_p[0] = is_p[1] = 0
    for p in range(2, math.isqrt(n) + 1):
        if is_p[p]:  # cross off p*p, p*p + p, ...
            hits = range(p * p, n + 1, p)
            is_p[p * p :: p] = bytes(len(hits))
    return [i for i, flag in enumerate(is_p) if flag]
 
 
print(primes_upto(30))
# [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
print(len(primes_upto(10**6)))  # 78498

O(n log log n) time, n bytes of memory. The slice assignment zeroes a whole stride in C instead of a Python loop.

Prime factorization

When you need the prime factors of one number (divisor counts, gcd tricks).

def factorize(n: int) -> dict[int, int]:
    factors: dict[int, int] = {}
    d = 2
    while d * d <= n:
        while n % d == 0:
            factors[d] = factors.get(d, 0) + 1
            n //= d
        d += 1 if d == 2 else 2  # 2, then odd numbers
    if n > 1:  # what is left is prime
        factors[n] = factors.get(n, 0) + 1
    return factors
 
 
f = factorize(360)
print(f)  # {2: 3, 3: 2, 5: 1}
count = 1
for e in f.values():
    count *= e + 1
print(count)  # 24 divisors

O(√n). Many queries up to the same limit: sieve the smallest prime factor of every number once instead.

nCr mod p

When a counting problem needs many binomial coefficients modulo a prime.

MOD = 10**9 + 7
N = 10**5
 
fact = [1] * (N + 1)
for i in range(1, N + 1):
    fact[i] = fact[i - 1] * i % MOD
inv_fact = [1] * (N + 1)
inv_fact[N] = pow(fact[N], -1, MOD)  # one inverse
for i in range(N, 0, -1):  # the rest by stepping down
    inv_fact[i - 1] = inv_fact[i] * i % MOD
 
 
def ncr(n: int, r: int) -> int:
    if not 0 <= r <= n:
        return 0
    top = fact[n] * inv_fact[r] % MOD
    return top * inv_fact[n - r] % MOD
 
 
print(ncr(5, 2), ncr(10**5, 5 * 10**4))  # 10 149033233

O(N) setup, O(1) per query. math.comb(n, r) % MOD gives the same answer but builds the exact, enormous binomial first.

Convert to any base

When a problem asks for base 7, a binary string, or a short id in base 36.

ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyz"
 
 
def to_base(n: int, base: int) -> str:
    if not 2 <= base <= 36:
        raise ValueError("base must be in 2..36")
    if n == 0:
        return "0"
    sign = "-" if n < 0 else ""
    n = abs(n)
    out: list[str] = []
    while n:
        n, r = divmod(n, base)
        out.append(ALPHABET[r])
    return sign + "".join(reversed(out))
 
 
print(to_base(100, 7), to_base(-10, 2))  # 202 -1010
print(int(to_base(123456789, 36), 36))  # 123456789

int(s, base) parses any of these back. Bases 2, 8 and 16 are built in: format(n, "b"), "o", "x".

Reverse Integer with a 32-bit clamp

When a problem written for fixed-width ints wants 0 on overflow.

INT_MIN, INT_MAX = -(2**31), 2**31 - 1
 
 
def reverse_int(x: int) -> int:
    n, r = abs(x), 0  # abs first: -123 % 10 == 7
    while n:
        n, d = divmod(n, 10)
        r = r * 10 + d
    r = -r if x < 0 else r
    return r if INT_MIN <= r <= INT_MAX else 0
 
 
print(reverse_int(-123), reverse_int(120))  # -321 21
print(reverse_int(1534236469))  # 0: 9646324351 overflows

The string version, int(str(abs(x))[::-1]) with the sign put back, gives the same results.

Exact slopes: points on a line

When grouping points by direction; float slopes merge or split lines at large coordinates.

from collections import Counter
from math import gcd
 
 
def direction(dx: int, dy: int) -> tuple[int, int]:
    g = gcd(dx, dy)  # > 0 unless both are 0
    dx, dy = dx // g, dy // g
    if dx < 0 or (dx == 0 and dy < 0):
        dx, dy = -dx, -dy  # one sign per direction
    return dx, dy
 
 
def max_points(pts: list[tuple[int, int]]) -> int:
    best = min(len(pts), 1)
    for i, (x1, y1) in enumerate(pts):
        dirs = Counter(
            direction(x2 - x1, y2 - y1)
            for x2, y2 in pts[i + 1 :]
        )
        best = max(best, 1 + max(dirs.values(), default=0))
    return best
 
 
pts = [(1, 1), (3, 2), (5, 3), (4, 1), (2, 3), (1, 4)]
print(max_points(pts))  # 4

O(n²) pairs. Assumes distinct points; with duplicates, count them separately and add them to every line through that point.

Fibonacci mod m by matrix power

When n is huge (10¹⁸) and the recurrence is linear: square the matrix instead of looping n times.

type Mat = tuple[int, int, int, int]  # [[a, b], [c, d]]
 
 
def mat_mul(x: Mat, y: Mat, m: int) -> Mat:
    a, b, c, d = x
    e, f, g, h = y
    return (
        (a * e + b * g) % m,
        (a * f + b * h) % m,
        (c * e + d * g) % m,
        (c * f + d * h) % m,
    )
 
 
def fib(n: int, m: int = 10**9 + 7) -> int:
    result: Mat = (1, 0, 0, 1)  # identity
    base: Mat = (1, 1, 1, 0)
    while n:  # square-and-multiply, like pow
        if n & 1:
            result = mat_mul(result, base, m)
        base = mat_mul(base, base, m)
        n >>= 1
    return result[1]  # base**n = [[F(n+1), F(n)], ...]
 
 
print([fib(i) for i in range(10)])
# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
print(fib(10**18))  # 209783453

O(log n) matrix products. Any recurrence f(n) = c1·f(n−1) + … + ck·f(n−k) works with a k × k matrix.

References