def all_products():
    for pos in grid.keys():
        lines = (
            line(pos, (1, 0)),  # east
            line(pos, (0, 1)),  # south
            line(pos, (1, 1)),  # southeast
            line(pos, (1, -1)),  # northeast
        )
        for l in lines:
            yield product(l)
12540698747158523863050715693290963295227443043557\
66896648950445244523161731856403098711121722383113\
62229893423380308135336276614282806444486645238749\
30358907296290491560440772390713810515859307960866\
70172427121883998797908792274921901699720888093776\
65727333001053367881220235421809751254540594752243\
52584907711670556013604839586446706324415722155397\
53697817977846174064955149290862569321978468622482\
83972241375657056057490261407972968652414535100474\
82166370484403199890008895243450658541227588666881\
16427171479924442928230863465674813919123162824586\
17866458359124566529476545682848912883142607690042\
24219022671055626321111109370544217506941658960408\
07198403850962455444362981230987879927244284909188\
84580156166097919133875499200524063689912560717606\
05886116467109405077541002256983155200055935729725\
71636269561882670428252483600823257530420752963450'

section_size = 5

results = {}
for i in range(len(digits) + 1 - section_size):
    section = [int(c) for c in digits[i:i+section_size]]
    p = product(section)
    results[p] = section

m = max(results.keys())
#s = ' * '.join(str(d) for d in results[m])
#print('%s = %i' % (s, m))
print(m)
    >>> cancel_unorthodox(11, 12)
    (1, 2)
    """
    n_digits = digits_of(numerator)
    d_digits = digits_of(denominator)
    matching_digits = [d for d in n_digits if d in d_digits]
    for m in matching_digits:
        if m in n_digits and m in d_digits:
            n_digits.remove(m)
            d_digits.remove(m)
    return from_digits(n_digits), from_digits(d_digits)

two_digit_nums = [i for i in range(10, 100)
                  if 0 not in digits_of(i)]
unorthodox_numbers = []
for n in two_digit_nums:
    for d in two_digit_nums:
        f = Fraction(n, d)
        if f >= 1:
            continue
        n2, d2 = cancel_unorthodox(n, d)
        if d2 == 0:
            continue
        if (n, d) == (n2, d2):
            continue
        f2 = Fraction(n2, d2)
        if f == f2:
            unorthodox_numbers.append(f2)

print(product(unorthodox_numbers).denominator)
import itertools
from utility import digits_of, product

def iter_counting_digits():
    """
    >>> from utility import nth
    >>> nth(12 - 1, iter_counting_digits())
    1
    >>> ''.join(str(d) for d in itertools.islice(iter_counting_digits(), 33))
    '123456789101112131415161718192021'
    """
    for i in itertools.count(1):
        for d in digits_of(i):
            yield d

nums = {10**p - 1 for p in range(7)}
max_num = max(nums)
results = []
for i, d in enumerate(iter_counting_digits()):
    if i in nums:
        results.append(d)
        if len(results) >= len(nums):
            break
print(product(results))
# find the only pythagorean triple whose numbers sum to 1000, and print the
# product of the numbers

from utility import product

def pythagorean_triple_summing_to(n):
    for a in range(1, n // 2):
        for b in range(a, n // 2):
            c = n - a - b # a + b + c sum to n
            if c < b:
                continue # numbers out of order
            if c > a + b:
                continue # cannot make a triangle at all, c is too large
            if a**2 + b**2 == c**2:
                return (a, b, c)

print(product(pythagorean_triple_summing_to(1000)))