
cdef extern from *:
    ctypedef long long int128_t "__int128_t"
    ctypedef unsigned long long uint128_t "__uint128_t"


def bigint(x):
    print(str(x).rstrip('L'))


def unsigned_conversion(x):
    """
    >>> bigint(unsigned_conversion(0))
    0
    >>> bigint(unsigned_conversion(2))
    2

    >>> unsigned_conversion(-2)  # doctest: +ELLIPSIS
    Traceback (most recent call last):
    OverflowError: can't convert negative value to ...uint128_t
    >>> unsigned_conversion(-2**120)  # doctest: +ELLIPSIS
    Traceback (most recent call last):
    OverflowError: can't convert negative value to ...uint128_t
    >>> unsigned_conversion(-2**127)  # doctest: +ELLIPSIS
    Traceback (most recent call last):
    OverflowError: can't convert negative value to ...uint128_t
    >>> unsigned_conversion(-2**128)  # doctest: +ELLIPSIS
    Traceback (most recent call last):
    OverflowError: can't convert negative value to ...uint128_t

    >>> bigint(unsigned_conversion(2**20))
    1048576
    >>> bigint(unsigned_conversion(2**30-1))
    1073741823
    >>> bigint(unsigned_conversion(2**30))
    1073741824
    >>> bigint(unsigned_conversion(2**30+1))
    1073741825

    >>> bigint(2**60)
    1152921504606846976
    >>> bigint(unsigned_conversion(2**60-1))
    1152921504606846975
    >>> bigint(unsigned_conversion(2**60))
    1152921504606846976
    >>> bigint(unsigned_conversion(2**60+1))
    1152921504606846977
    >>> bigint(2**64)
    18446744073709551616
    >>> bigint(unsigned_conversion(2**64))
    18446744073709551616

    >>> bigint(2**120)
    1329227995784915872903807060280344576
    >>> bigint(unsigned_conversion(2**120))
    1329227995784915872903807060280344576
    >>> bigint(2**128-1)
    340282366920938463463374607431768211455
    >>> bigint(unsigned_conversion(2**128-1))
    340282366920938463463374607431768211455
    >>> bigint(unsigned_conversion(2**128))  # doctest: +ELLIPSIS
    Traceback (most recent call last):
    OverflowError: ... too big to convert
    """
    cdef uint128_t n = x
    return n


def signed_conversion(x):
    """
    >>> bigint(signed_conversion(0))
    0
    >>> bigint(signed_conversion(2))
    2
    >>> bigint(signed_conversion(-2))
    -2

    >>> bigint(signed_conversion(2**20))
    1048576
    >>> bigint(signed_conversion(2**32))
    4294967296
    >>> bigint(2**64)
    18446744073709551616
    >>> bigint(signed_conversion(2**64))
    18446744073709551616
    >>> bigint(signed_conversion(-2**64))
    -18446744073709551616

    >>> bigint(2**118)
    332306998946228968225951765070086144
    >>> bigint(signed_conversion(2**118))
    332306998946228968225951765070086144
    >>> bigint(signed_conversion(-2**118))
    -332306998946228968225951765070086144

    >>> bigint(2**120)
    1329227995784915872903807060280344576
    >>> bigint(signed_conversion(2**120))
    1329227995784915872903807060280344576
    >>> bigint(signed_conversion(-2**120))
    -1329227995784915872903807060280344576

    >>> bigint(2**127-1)
    170141183460469231731687303715884105727
    >>> bigint(signed_conversion(2**127-2))
    170141183460469231731687303715884105726
    >>> bigint(signed_conversion(2**127-1))
    170141183460469231731687303715884105727
    >>> bigint(signed_conversion(2**127))  # doctest: +ELLIPSIS
    Traceback (most recent call last):
    OverflowError: ... too big to convert
    >>> bigint(signed_conversion(-2**127))
    -170141183460469231731687303715884105728
    >>> bigint(signed_conversion(-2**127-1))  # doctest: +ELLIPSIS
    Traceback (most recent call last):
    OverflowError: ... too big to convert
    """
    cdef int128_t n = x
    return n


def get_int_distribution(shuffle=True):
    """
    >>> L = get_int_distribution()
    >>> bigint(L[0])
    682
    >>> bigint(L[ len(L) // 2 ])
    5617771410183435
    >>> bigint(L[-1])
    52818775009509558395695966805
    >>> len(L)
    66510
    """
    # Large integers that cover 1-4 (30 bits) or 1-7 (15 bits) PyLong digits.
    # Uses only integer calculations to avoid rounding issues.
    pow2 = [2**exp for exp in range(98)]
    ints = [
        n // 3
        for i in range(11, len(pow2) - 1)
        # Take a low but growing number of integers from each power-of-2 range.
        for n in range(pow2[i], pow2[i+1], pow2[i - 8] - 1)
    ]
    return ints * 3  # longer list, but keeps median in the middle


def intsum(L):
    """
    >>> L = get_int_distribution()
    >>> bigint(intsum(L))
    61084913298497804284622382871263
    >>> bigint(sum(L))
    61084913298497804284622382871263

    >>> from random import shuffle
    >>> shuffle(L)
    >>> bigint(intsum(L))
    61084913298497804284622382871263
    """
    cdef uint128_t i, x = 0
    for i in L:
        x += i
    return x


def intxor(L):
    """
    >>> L = get_int_distribution()
    >>> bigint(intxor(L))
    31773794341658093722410838161
    >>> bigint(intxor(L * 2))
    0
    >>> import operator
    >>> from functools import reduce
    >>> bigint(reduce(operator.xor, L))
    31773794341658093722410838161
    >>> bigint(reduce(operator.xor, L * 2))
    0

    >>> from random import shuffle
    >>> shuffle(L)
    >>> bigint(intxor(L))
    31773794341658093722410838161
    >>> bigint(intxor(L * 2))
    0
    """
    cdef uint128_t i, x = 0
    for i in L:
        x ^= i
    return x
