from itertools import count, chain
from collections import deque
 
def primes(_cache=[2, 3]):
    yield from _cache
    for n in count(_cache[-1]+2, 2):
        if isprime(n):
            _cache.append(n)
            yield n
 
def isprime(n):
    for p in primes():
        if n%p == 0:
            return False
        if p*p > n:
            return True
 
def factors(n):
    for p in primes():
    # prime factoring is such a non-issue for small numbers that, for
    # this example, we might even just say
    # for p in count(2):
        if p*p > n:
            if n > 1:
                yield(n, 1, 1)
            break
 
        if n%p == 0:
            cnt = 0
            while True:
                n, cnt = n//p, cnt+1
                if n%p != 0: break
            yield p, cnt, n
# ^^ not the most sophisticated prime number routines, because no need
 
# Returns (list1, list2) representing the division between
# two polinomials. A list p of integers means the product
#   (x^p[0] - 1) * (x^p[1] - 1) * ...
def cyclotomic(n):
    def poly_div(num, den):
        return (num[0] + den[1], num[1] + den[0])
 
    def elevate(poly, n): # replace poly p(x) with p(x**n)
        powerup = lambda p, n: [a*n for a in p]
        return poly if n == 1 else (powerup(poly[0], n), powerup(poly[1], n))
 
 
    if n == 0:
        return ([], [])
    if n == 1:
        return ([1], [])
 
    p, m, r = next(factors(n))
    poly = cyclotomic(r)
    return elevate(poly_div(elevate(poly, p), poly), p**(m-1))
 
def to_text(poly):
    def getx(c, e):
        if e == 0:
            return '1'
        elif e == 1:
            return 'x'
        return 'x' + (''.join(''[i] for i in map(int, str(e))))
 
    parts = []
    for (c,e) in (poly):
        if c < 0:
            coef = ' - ' if c == -1 else f' - {-c} '
        else:
            coef = (parts and ' + ' or '') if c == 1 else f' + {c}'
        parts.append(coef + getx(c,e))
    return ''.join(parts)
 
def terms(poly):
    # convert above representation of division to (coef, power) pairs
 
    def merge(a, b):
        # a, b should be deques. They may change during the course.
        while a or b:
            l = a[0] if a else (0, -1) # sentinel value
            r = b[0] if b else (0, -1)
            if l[1] > r[1]:
                a.popleft()
            elif l[1] < r[1]:
                b.popleft()
                l = r
            else:
                a.popleft()
                b.popleft()
                l = (l[0] + r[0], l[1])
            yield l
 
    def mul(poly, p): # p means polynomial x^p - 1
        poly = list(poly)
        return merge(deque((c, e+p) for c,e in poly),
                     deque((-c, e) for c,e in poly))
 
    def div(poly, p): # p means polynomial x^p - 1
        q = deque()
        for c,e in merge(deque(poly), q):
            if c:
                q.append((c, e - p))
                yield (c, e - p)
            if e == p: break
 
    p = [(1, 0)]  # 1*x^0, i.e. 1
 
    for x in poly[0]: # numerator
        p = mul(p, x)
    for x in sorted(poly[1], reverse=True): # denominator
        p = div(p, x)
    return p
 
for n in chain(range(11), [2]):
    print(f'{n}: {to_text(terms(cyclotomic(n)))}')
 
want = 1
for n in count():
    c = [c for c,_ in terms(cyclotomic(n))]
    while want in c or -want in c:
        print(f'C[{want}]: {n}')
        want += 1