Miller-Rabin primality test. Keep in mind that this is not a deterministic algorithm: if it return True,
    it means that n is probably a prime.

    Args:
        n (int): the integer to check

    Returns:
         True if n is probably a prime number, False if it is not

    Raises:
        TypeError: if n is not an integer

    Note:
        Adapted from https://rosettacode.org/wiki/Miller%E2%80%93Rabin_primality_test#Python

def is_prime(n):

    """ Miller-Rabin primality test. Keep in mind that this is not a deterministic algorithm: if it return True,
    it means that n is probably a prime.

    Args:
        n (int): the integer to check

    Returns:
         True if n is probably a prime number, False if it is not

    Raises:
        TypeError: if n is not an integer

    Note:
        Adapted from https://rosettacode.org/wiki/Miller%E2%80%93Rabin_primality_test#Python

    """

    if not isinstance(n, int):
        raise TypeError("Expecting an integer")

    if n < 2:
        return False
    if n in __known_primes:
        return True
    if any((n % p) == 0 for p in __known_primes):
        return False
    d, s = n - 1, 0
    while not d % 2:
        d, s = d >> 1, s + 1

    def try_composite(a):
        if pow(a, d, n) == 1:
            return False
        for i in range(s):
            if pow(a, 2 ** i * d, n) == n - 1:
                return False
        return True

    return not any(try_composite(a) for a in __known_primes[:16])