def sieve_eratosthenes(range_to):
    """
    A Very efficient way to generate all prime numbers upto a number.
    Space complexity: O(n)
    Time complexity: O(n * log(logn))
    n = the number upto which prime numbers are to be generated.
    """
    range_to=int(range_to)
    # creating a boolean list first
    prime = [True for i in range(range_to + 1)]
    p = 2
    while (p * p <= range_to):

        # If prime[p] is not changed, then it is a prime
        if (prime[p] == True):

            # Update all multiples of p
            for i in range(p * 2, range_to + 1, p):
                prime[i] = False

        p += 1

    # return the list of primes
    return [p for p in range(2, range_to + 1) if prime[p]]