···6464 for x in tup)
656566666767+def gcd(a,b):
6868+ """Compute the greatest common divisor of a and b"""
6969+ while b > 0:
7070+ a, b = b, a % b
7171+ return a
7272+7373+7474+def lcm(a, b):
7575+ """Compute the lowest common multiple of a and b"""
7676+ return a * b / gcd(a, b)
7777+7878+7979+def min_max_xy(points):
8080+ if len(points) == 0:
8181+ return None, None, None, None
8282+ if type(points[0]) == tuple:
8383+ min_x = min(p[0] for p in points)
8484+ max_x = max(p[0] for p in points)
8585+ min_y = min(p[1] for p in points)
8686+ max_y = max(p[1] for p in points)
8787+ else:
8888+ min_x = min(p.x for p in points)
8989+ max_x = max(p.x for p in points)
9090+ min_y = min(p.y for p in points)
9191+ max_y = max(p.y for p in points)
9292+9393+ return min_x, max_x, min_y, max_y
9494+9595+9696+def print_grid(grid, f=None):
9797+ if f is None:
9898+ f = lambda x: x # NOQA
9999+100100+ if type(grid) is dict:
101101+ positions = grid.keys()
102102+ min_x, max_x, min_y, max_y = min_max_xy(positions)
103103+ if type(positions[0]) is tuple:
104104+ for y in range(min_y, max_y + 1):
105105+ print ''.join(f(grid.get((x, y), ' ')) for x in range(min_x, max_x + 1))
106106+ else:
107107+ # (x, y) => point
108108+ for y in range(min_y, max_y + 1):
109109+ print ''.join(f(grid.get(Point(x, y), ' ')) for x in range(min_x, max_x + 1))
110110+ else:
111111+ for y in range(len(grid)):
112112+ print ''.join(f(grid[y][x]) for x in range(len(grid[0])))
113113+114114+67115def memoize(f):
68116 """Simple dictionary-based memoization decorator"""
69117 cache = {}