| """ Grid splitting model with connection database back references. | 
 |  | 
 | The Grid object provides methods to manipulate a 2D grid of Tile objects, that | 
 | contain zero or more Site objects. Site objects are considered immutable. | 
 |  | 
 | To construct the Grid object, the initial grid and an empty_tile_type_pkey must | 
 | be provided.  The initial grid should be provided as a map of 2 element int | 
 | tuples to Tile objects.  Tile objects should already contain their initial | 
 | sites prior to construction of the Grid object. | 
 |  | 
 | """ | 
 | from enum import Enum | 
 | from collections import namedtuple | 
 |  | 
 |  | 
 | class Direction(Enum): | 
 |     """ Grid directions. """ | 
 |     NORTH = 1 | 
 |     SOUTH = 2 | 
 |     EAST = 3 | 
 |     WEST = 4 | 
 |  | 
 |  | 
 | NORTH = Direction.NORTH | 
 | SOUTH = Direction.SOUTH | 
 | EAST = Direction.EAST | 
 | WEST = Direction.WEST | 
 |  | 
 | OPPOSITE_DIRECTIONS = { | 
 |     NORTH: SOUTH, | 
 |     SOUTH: NORTH, | 
 |     EAST: WEST, | 
 |     WEST: EAST, | 
 | } | 
 |  | 
 | # Zipper direction when splitting in a direction | 
 | SPLIT_NEXT_DIRECTIONS = { | 
 |     NORTH: EAST, | 
 |     SOUTH: EAST, | 
 |     EAST: SOUTH, | 
 |     WEST: SOUTH, | 
 | } | 
 |  | 
 |  | 
 | def opposite_direction(direction): | 
 |     """ Return opposite direction of given direction. | 
 |  | 
 |     >>> opposite_direction(NORTH) | 
 |     <Direction.SOUTH: 2> | 
 |     >>> opposite_direction(SOUTH) | 
 |     <Direction.NORTH: 1> | 
 |     >>> opposite_direction(EAST) | 
 |     <Direction.WEST: 4> | 
 |     >>> opposite_direction(WEST) | 
 |     <Direction.EAST: 3> | 
 |  | 
 |     """ | 
 |     return OPPOSITE_DIRECTIONS[direction] | 
 |  | 
 |  | 
 | # Right handed coordinate system, N/S in y, E/W in x, E is x-positive, | 
 | # S is y-positive. | 
 | DIRECTION_OFFSET = { | 
 |     NORTH: [0, -1], | 
 |     SOUTH: [0, 1], | 
 |     EAST: [1, 0], | 
 |     WEST: [-1, 0], | 
 | } | 
 |  | 
 |  | 
 | def coordinate_in_direction(coord, direction): | 
 |     """ Given a coordinate, returns a new coordinate 1 step in direction. | 
 |  | 
 |     Coordinate system is right handed, N/S in y, E/W in x.  E is x-positive. | 
 |     S is y-positive. | 
 |  | 
 |     Parameters | 
 |     ---------- | 
 |     coord : Tuple of 2 ints | 
 |         Starting coordinate | 
 |     direction : Direction | 
 |         Direction to add unit vector. | 
 |  | 
 |     Returns | 
 |     ------- | 
 |     Tuple of 2 ints | 
 |         Coordinate 1 unit step in specified direction.  Will return none if | 
 |         x or y coordinate is negative. | 
 |  | 
 |     Examples | 
 |     -------- | 
 |  | 
 |     >>> coordinate_in_direction((0, 0), SOUTH) | 
 |     (0, 1) | 
 |     >>> coordinate_in_direction((0, 0), EAST) | 
 |     (1, 0) | 
 |     >>> coordinate_in_direction((1, 1), SOUTH) | 
 |     (1, 2) | 
 |     >>> coordinate_in_direction((1, 1), NORTH) | 
 |     (1, 0) | 
 |     >>> coordinate_in_direction((1, 1), EAST) | 
 |     (2, 1) | 
 |     >>> coordinate_in_direction((1, 1), WEST) | 
 |     (0, 1) | 
 |  | 
 |     # Returns None for negative coordinates. | 
 |     >>> coordinate_in_direction((0, 0), NORTH) | 
 |     >>> coordinate_in_direction((1, 0), NORTH) | 
 |     >>> coordinate_in_direction((0, 0), WEST) | 
 |     >>> coordinate_in_direction((0, 1), WEST) | 
 |  | 
 |     """ | 
 |     x, y = coord | 
 |     dx, dy = DIRECTION_OFFSET[direction] | 
 |     x += dx | 
 |     y += dy | 
 |  | 
 |     if x < 0 or y < 0: | 
 |         return None | 
 |     else: | 
 |         return x, y | 
 |  | 
 |  | 
 | class Site(namedtuple('Site', ('name', 'phy_tile_pkey', 'tile_type_pkey', | 
 |                                'site_type_pkey', 'site_pkey', 'x', 'y'))): | 
 |     """ Object to hold back reference information for a site. """ | 
 |     pass | 
 |  | 
 |  | 
 | class Tile(object): | 
 |     """ Tile instance within the grid. | 
 |  | 
 |     Attributes | 
 |     ---------- | 
 |     root_phy_tile_pkeys : list of ints | 
 |         The list of root_phy_tile_pkey's. | 
 |  | 
 |         By default a tile typically has one root phy_tile_pkey, which is the | 
 |         phy_tile this initial represents. | 
 |  | 
 |         If two Tile objects are merged, the root_phy_tile_pkeys are also merged. | 
 |         If a Tile object is split, only one of the split tiles will take all | 
 |         of the root_phy_tile_pkeys, and the other tiles will have no | 
 |         root_phy_tile_pkeys. | 
 |  | 
 |         Invariant: Each phy_tile_pkey will appear in 1 and only 1 Tile | 
 |         object's root_phy_tile_pkeys list. | 
 |  | 
 |         Because of the invariant, the root_phy_tile_pkeys list can be used as | 
 |         a default assignment of children of the relevant phy_tile_pkey items | 
 |         (e.g. wires, pips, sites, etc). | 
 |  | 
 |     phy_tile_pkeys : list of ints | 
 |         The list of phy_tile_pkey's.  This is the list of all phy_tile_pkey's | 
 |         that are involved in this tile via either a tile merge or split. | 
 |  | 
 |         By default a tile typically has one phy_tile_pkey, which is the | 
 |         phy_tile this initial represents. | 
 |  | 
 |         If two Tile objects are split, all output tiles will get a copy of the | 
 |         original phy_tile_pkeys list.  This attribute can be used to determine | 
 |         what phy_tile_pkeys were used to make this tile. | 
 |  | 
 |     sites : list of Site objects | 
 |         This is the list of Site's contained within this tile.  This should | 
 |         be initial set to the Site objects contained within the original | 
 |         phy_tile. | 
 |  | 
 |         Invariant: Each Site object will be contained within exactly one Tile | 
 |         object. | 
 |  | 
 |     split_sites : boolean | 
 |         True if this tile was split. | 
 |  | 
 |         Invariant: Each split tile will contain exactly one Site object. | 
 |         Invariant: Two tiles that were split cannot be merged, otherwise the | 
 |         resulting Tile will have two Sites, potentially from different | 
 |         phy_tile_pkey, which cannot be presented using FASm prefixes. | 
 |  | 
 |     neighboors : Map of Direction to Tile object | 
 |         Linked list pointers to neighboors tiles. | 
 |  | 
 |         Invariant: Underlying linked link should be rectangular after an | 
 |         operation on the grid.  An single operation on the Tile will typically | 
 |         invalidate the overall grid, it is up to the Grid object to enforce | 
 |         the rectangular constraint. | 
 |  | 
 |         Invariant: Underlying linked link must not be circular. | 
 |  | 
 |     """ | 
 |  | 
 |     def __init__( | 
 |             self, root_phy_tile_pkeys, phy_tile_pkeys, tile_type_pkey, sites | 
 |     ): | 
 |         self.root_phy_tile_pkeys = root_phy_tile_pkeys | 
 |         self.phy_tile_pkeys = phy_tile_pkeys | 
 |         self.tile_type_pkey = tile_type_pkey | 
 |         self.sites = sites | 
 |         self.split_sites = False | 
 |         self.neighboors = {} | 
 |  | 
 |     def link_neighboor_in_direction(self, other_tile, direction_to_other_tile): | 
 |         """ Connect this tile to another tile in a specific direction. | 
 |  | 
 |         It is legal to call this method on an existing connection, but it is | 
 |         not legal to call this method to replace an existing connection. | 
 |  | 
 |         Parameters | 
 |         ---------- | 
 |         other_tile : Tile object | 
 |             Other Tile object to connect in specified direction. | 
 |         direction_to_other_tile : Direction | 
 |             Direction to connect other tile. | 
 |         """ | 
 |         if direction_to_other_tile in self.neighboors: | 
 |             assert id( | 
 |                 self.neighboors[direction_to_other_tile] | 
 |             ) == id(other_tile), (self.neighboors, direction_to_other_tile) | 
 |         self.neighboors[direction_to_other_tile] = other_tile | 
 |  | 
 |         direction_to_this_tile = opposite_direction(direction_to_other_tile) | 
 |         if direction_to_this_tile in other_tile.neighboors: | 
 |             assert id(other_tile.neighboors[direction_to_this_tile] | 
 |                       ) == id(self) | 
 |  | 
 |         other_tile.neighboors[direction_to_this_tile] = self | 
 |  | 
 |     def insert_in_direction(self, other_tile, direction_to_other_tile): | 
 |         """ Insert a tile in a specified direction. | 
 |  | 
 |         Parameters | 
 |         ---------- | 
 |         other_tile : Tile object | 
 |             Other Tile object to insert in specified direction. | 
 |         direction_to_other_tile : Direction | 
 |             Direction to insert other tile. | 
 |  | 
 |         """ | 
 |         old_neighboor = self.neighboors.get(direction_to_other_tile, None) | 
 |  | 
 |         direction_to_this_tile = opposite_direction(direction_to_other_tile) | 
 |  | 
 |         self.neighboors[direction_to_other_tile] = other_tile | 
 |         other_tile.neighboors[direction_to_this_tile] = self | 
 |  | 
 |         if old_neighboor is not None: | 
 |             other_tile.neighboors[direction_to_other_tile] = old_neighboor | 
 |             old_neighboor.neighboors[direction_to_this_tile] = other_tile | 
 |  | 
 |     def walk_in_direction(self, direction): | 
 |         """ Walk in specified direction from this Tile node. | 
 |  | 
 |         Parameters | 
 |         ---------- | 
 |  | 
 |         direction : Direction | 
 |             Direction to walk in. | 
 |  | 
 |         Yields | 
 |         ------ | 
 |  | 
 |         tile : Tile | 
 |             Tile in specified direction.  First Tile object will always be the | 
 |             tile whose walk_in_direction was invoked.  When the end of the grid | 
 |             is encounted, no more tiles will be yielded. | 
 |         """ | 
 |  | 
 |         node = self | 
 |  | 
 |         while True: | 
 |             yield node | 
 |             if direction in node.neighboors: | 
 |                 node = node.neighboors[direction] | 
 |             else: | 
 |                 break | 
 |  | 
 |  | 
 | def check_grid_loc(grid_loc_map): | 
 |     """ Verifies input grid makes sense. | 
 |  | 
 |     Internal grid consistency is defined as: | 
 |      - Has an origin location @ (0, 0) | 
 |      - Is rectangular | 
 |      - Has no gaps. | 
 |  | 
 |     Parameters | 
 |     ---------- | 
 |     grid_loc_map : Dict of 2 int tuple to Tile objects | 
 |         Grid being checked. | 
 |  | 
 |     Raises | 
 |     ------ | 
 |     AssertionError | 
 |         If provided grid does not conform to assumptions about grid. | 
 |  | 
 |     """ | 
 |     xs, ys = zip(*grid_loc_map.keys()) | 
 |  | 
 |     max_x = max(xs) | 
 |     max_y = max(ys) | 
 |  | 
 |     for x in range(max_x + 1): | 
 |         for y in range(max_y + 1): | 
 |             assert (x, y) in grid_loc_map, (x, y) | 
 |  | 
 |  | 
 | def build_mesh(current, visited, loc, grid_loc_map): | 
 |     """ Stitch grid_loc_map into a double-linked list 2D mesh. | 
 |  | 
 |     Modifies Tile object neighboors attributes to form a doubly linked list | 
 |     2D mesh. | 
 |  | 
 |     It is strongly recommended that grid_loc_map be passed to check_grid_loc | 
 |     prior to calling build_mesh to verify grid invariants. | 
 |  | 
 |     Parameters | 
 |     ---------- | 
 |     current : Tile object | 
 |     visited : set of python object id's | 
 |         Should be empty on root invocation. | 
 |     loc : Location of current Tile object argument | 
 |     grid_loc_map : Dict of 2 int tuple to Tile objects | 
 |         Grid being converted to linked list form. | 
 |  | 
 |     """ | 
 |  | 
 |     for direction in (SOUTH, EAST): | 
 |         new_loc = coordinate_in_direction(loc, direction) | 
 |         if new_loc in grid_loc_map: | 
 |             current.link_neighboor_in_direction( | 
 |                 grid_loc_map[new_loc], direction | 
 |             ) | 
 |             if id(grid_loc_map[new_loc]) not in visited: | 
 |                 visited.add(id(grid_loc_map[new_loc])) | 
 |                 build_mesh( | 
 |                     grid_loc_map[new_loc], visited, new_loc, grid_loc_map | 
 |                 ) | 
 |  | 
 |  | 
 | class Grid(object): | 
 |     """ Object for manipulating a 2D grid of Tile objects. | 
 |  | 
 |     Parameters | 
 |     ---------- | 
 |  | 
 |     grid_loc_map : Dict of 2 int tuple to Tile objects | 
 |         Initial grid of Tile objects. | 
 |     empty_tile_type_pkey : int | 
 |         tile_type_pkey to use when creating new empty tiles during tile splits. | 
 |  | 
 |     """ | 
 |  | 
 |     def __init__(self, grid_loc_map, empty_tile_type_pkey): | 
 |         # Make sure initial grid is sane | 
 |         check_grid_loc(grid_loc_map) | 
 |  | 
 |         # Keep root object of grid. | 
 |         self.origin = grid_loc_map[(0, 0)] | 
 |  | 
 |         # Convert grid to doubly-linked list. | 
 |         build_mesh(self.origin, set(), (0, 0), grid_loc_map) | 
 |  | 
 |         # Keep list of all Tile objects for convience. | 
 |         self.items = grid_loc_map.values() | 
 |  | 
 |         self.empty_tile_type_pkey = empty_tile_type_pkey | 
 |  | 
 |     def column(self, x): | 
 |         """ Return Tile object at top of column. | 
 |  | 
 |         Parameters | 
 |         ---------- | 
 |         x : int | 
 |             0 based column to retrive. | 
 |  | 
 |         Returns | 
 |         ------- | 
 |         top_of_column : Tile | 
 |             Tile object at top of column | 
 |  | 
 |         """ | 
 |         top_of_column = self.origin | 
 |  | 
 |         for _ in range(x): | 
 |             top_of_column = top_of_column.neighboors[EAST] | 
 |  | 
 |         return top_of_column | 
 |  | 
 |     def row(self, y): | 
 |         """ Return Tile object at right of row. | 
 |  | 
 |         Parameters | 
 |         ---------- | 
 |         y : int | 
 |             0 based row to retrive. | 
 |  | 
 |         Returns | 
 |         ------- | 
 |         right_of_row : Tile | 
 |             Tile object at right of row | 
 |  | 
 |         """ | 
 |         right_of_row = self.origin | 
 |  | 
 |         for _ in range(y): | 
 |             right_of_row = right_of_row.neighboors[SOUTH] | 
 |  | 
 |     def split_tile(self, tile, tile_type_pkeys, split_direction, split_map): | 
 |         """ Split tile in specified direction. | 
 |  | 
 |         This method requires that the tiles required to perform the split (e.g. | 
 |         len(tile_type_pkeys)-1 tiles in split_direction from tile) have | 
 |         tile_type_pkey == empty_tile_type_pkey, e.g. they are empty tiles. | 
 |  | 
 |         If empty tiles must be inserted into the grid to accomidate the split, | 
 |         this must be done prior to calling this method. | 
 |  | 
 |         Parameters | 
 |         ---------- | 
 |         tile : Tile object | 
 |             Tile being split | 
 |         tile_type_pkeys : List of int | 
 |             List of new tile_type_pkeys to be used after the tile split. | 
 |             The tile being split will become tile_type_pkeys[0], the next tile | 
 |             in split_direction will become tile_type_pkeys[1], etc. | 
 |         split_direction : Direction | 
 |             Which direction from tile should the split occur. | 
 |         split_map : Dict of (int, int) to int | 
 |             Mapping of site location (x, y) to tile_type_pkey indicies. | 
 |             This enables control over which sites go to which tiles based on | 
 |             their coordinate. | 
 |  | 
 |             min(split_map.values()) >= 0 | 
 |             max(split_map.values()) < len(tile_type_pkeys) | 
 |  | 
 |         """ | 
 |         sites = tile.sites | 
 |         tile.tile_type_pkey = self.empty_tile_type_pkey | 
 |         phy_tile_pkeys = set(tile.phy_tile_pkeys) | 
 |         new_tiles = [] | 
 |  | 
 |         for idx, tile in enumerate(tile.walk_in_direction(split_direction)): | 
 |             assert tile.tile_type_pkey == self.empty_tile_type_pkey, ( | 
 |                 tile.tile_type_pkey | 
 |             ) | 
 |             tile.phy_tile_pkeys = [] | 
 |  | 
 |             new_tiles.append(tile) | 
 |  | 
 |             if idx + 1 >= len(tile_type_pkeys): | 
 |                 break | 
 |  | 
 |         for tile, new_tile_type_pkey in zip(new_tiles, tile_type_pkeys): | 
 |             assert tile.tile_type_pkey == self.empty_tile_type_pkey | 
 |  | 
 |             tile.tile_type_pkey = new_tile_type_pkey | 
 |             tile.phy_tile_pkeys = list( | 
 |                 set(tile.phy_tile_pkeys) | phy_tile_pkeys | 
 |             ) | 
 |             tile.sites = [] | 
 |             tile.split_sites = True | 
 |  | 
 |         for site in sites: | 
 |             site_idx = split_map[site.x, site.y] | 
 |             assert site_idx < len(tile_type_pkeys), ( | 
 |                 site, site_idx, tile_type_pkeys | 
 |             ) | 
 |             new_tiles[site_idx].sites.append(site) | 
 |  | 
 |     def insert_empty(self, top, insert_in_direction): | 
 |         """ Insert empty row/colum. | 
 |  | 
 |         Insert a row/column of empty tiles from the tiles in the row/column specified | 
 |         by top_of_row/column tile.  The new empty tiles will have tile_type_pkey | 
 |         set to empty_tile_type_pkey, and have phy_tile_pkeys of the tile they | 
 |         were inserted from. | 
 |  | 
 |         Parameters | 
 |         ---------- | 
 |         top_of_row/column : Tile object | 
 |             Tile at top of row/column adjcent to where new row/column should be | 
 |             inserted. | 
 |         insert_in_direction : Direction | 
 |             Direction to insert empty tiles, from perspective of the row/column | 
 |             specified by top_of_row/column. | 
 |  | 
 |         """ | 
 |         # Verify that insert direction is not the same as zipper direction. | 
 |         next_dir = SPLIT_NEXT_DIRECTIONS[insert_in_direction] | 
 |  | 
 |         # Verify that top is in fact the top of the zipper | 
 |         assert OPPOSITE_DIRECTIONS[next_dir] not in top.neighboors | 
 |  | 
 |         empty_tiles = [] | 
 |         for tile in top.walk_in_direction(next_dir): | 
 |             empty_tile = Tile( | 
 |                 root_phy_tile_pkeys=[], | 
 |                 phy_tile_pkeys=list(tile.phy_tile_pkeys), | 
 |                 tile_type_pkey=self.empty_tile_type_pkey, | 
 |                 sites=[] | 
 |             ) | 
 |             empty_tiles.append(empty_tile) | 
 |  | 
 |             tile.insert_in_direction(empty_tile, insert_in_direction) | 
 |  | 
 |         for a, b in zip(empty_tiles, empty_tiles[1:]): | 
 |             a.link_neighboor_in_direction(b, next_dir) | 
 |  | 
 |         self.check_grid() | 
 |  | 
 |     def split_in_dir( | 
 |             self, | 
 |             top, | 
 |             tile_type_pkey, | 
 |             tile_type_pkeys, | 
 |             split_direction, | 
 |             split_map, | 
 |     ): | 
 |         """ Split row/column of tiles. | 
 |  | 
 |         Splits specified tile types into new row/column by first inserting any | 
 |         required empty row/column in the split direction, and then performing | 
 |         the split. | 
 |  | 
 |         Parameters | 
 |         ---------- | 
 |         top_of_row/column : Tile object | 
 |             Tile at top of row/column where split should be performed. | 
 |         tile_type_pkey : Tile type to split. | 
 |         tile_type_pkeys : Refer to split_tile documentation. | 
 |         split_direction : Direction | 
 |             Direction to insert perform split.  New row/column will be inserted | 
 |             in that direction to accomidate the tile split. | 
 |         split_map : Dict of (int, int) to int | 
 |             Mapping of site location (x, y) to tile_type_pkey indicies. | 
 |             This enables control over which sites go to which tiles based on | 
 |             their coordinate. | 
 |  | 
 |         """ | 
 |         next_dir = SPLIT_NEXT_DIRECTIONS[split_direction] | 
 |  | 
 |         # Find how many empty tiles are required to support the split | 
 |         num_to_insert = 0 | 
 |         for tile in top.walk_in_direction(next_dir): | 
 |             if tile.tile_type_pkey != tile_type_pkey: | 
 |                 continue | 
 |  | 
 |             for idx, tile_in_split in enumerate( | 
 |                     tile.walk_in_direction(split_direction)): | 
 |                 if idx == 0: | 
 |                     continue | 
 |                 else: | 
 |                     if tile_in_split.tile_type_pkey != self.empty_tile_type_pkey: | 
 |                         num_to_insert = max(num_to_insert, idx) | 
 |  | 
 |                     if idx + 1 >= len(tile_type_pkeys): | 
 |                         break | 
 |  | 
 |         for _ in range(num_to_insert): | 
 |             self.insert_empty(top, split_direction) | 
 |  | 
 |         for tile in top.walk_in_direction(next_dir): | 
 |             if tile.tile_type_pkey != tile_type_pkey: | 
 |                 continue | 
 |  | 
 |             self.split_tile(tile, tile_type_pkeys, split_direction, split_map) | 
 |  | 
 |     def split_tile_type( | 
 |             self, tile_type_pkey, tile_type_pkeys, split_direction, split_map | 
 |     ): | 
 |         """ Split a specified tile type within grid. | 
 |  | 
 |         Splits specified tile types by finding each column that contains the | 
 |         relevant tile type, and spliting each column. | 
 |  | 
 |         Parameters | 
 |         ---------- | 
 |         tile_type_pkey : Tile type to split. | 
 |         tile_type_pkeys : Refer to split_tile documentation. | 
 |         split_direction : Direction | 
 |             Direction to insert perform split. | 
 |         split_map : Dict of (int, int) to int | 
 |             Mapping of site location (x, y) to tile_type_pkey indicies. | 
 |             This enables control over which sites go to which tiles based on | 
 |             their coordinate. | 
 |  | 
 |         """ | 
 |         tiles_seen = set() | 
 |         tiles = [] | 
 |         tops_to_split = [] | 
 |  | 
 |         next_dir = SPLIT_NEXT_DIRECTIONS[split_direction] | 
 |  | 
 |         for tile in self.items: | 
 |             if id(tile) in tiles_seen: | 
 |                 continue | 
 |  | 
 |             if tile.tile_type_pkey == tile_type_pkey: | 
 |                 # Found a row/column that needs to be split, walk to the bottom of | 
 |                 # the row/column, then back to the top. | 
 |                 for tile in tile.walk_in_direction(next_dir): | 
 |                     pass | 
 |  | 
 |                 for tile in tile.walk_in_direction( | 
 |                         OPPOSITE_DIRECTIONS[next_dir]): | 
 |                     if id(tile) not in tiles_seen: | 
 |                         tiles_seen.add(id(tile)) | 
 |                         if tile.tile_type_pkey == tile_type_pkey: | 
 |                             tiles.append(tile) | 
 |  | 
 |                 tops_to_split.append(tile) | 
 |  | 
 |         for top in tops_to_split: | 
 |             self.split_in_dir( | 
 |                 top, tile_type_pkey, tile_type_pkeys, split_direction, | 
 |                 split_map | 
 |             ) | 
 |  | 
 |     def merge_in_dir(self, tile, merge_direction): | 
 |         """ Merge tile in specified direction. | 
 |  | 
 |         Merging a tile causes the connects of that tile to be merged into | 
 |         the tile in the merge direction.  The tile that was merged will become | 
 |         empty. | 
 |  | 
 |         The original tile root_phy_tile_pkeys and phy_tile_pkeys will appear | 
 |         first. | 
 |  | 
 |         Parameters | 
 |         ---------- | 
 |         tile : Tile | 
 |             Tile to merge | 
 |         merge_direction : Direction | 
 |             Direction to merge tiles. | 
 |         """ | 
 |         assert merge_direction in tile.neighboors, (tile, merge_direction) | 
 |  | 
 |         merge_into = tile.neighboors[merge_direction] | 
 |  | 
 |         merge_into.root_phy_tile_pkeys.extend(tile.root_phy_tile_pkeys) | 
 |         merge_into.phy_tile_pkeys.extend(tile.phy_tile_pkeys) | 
 |         merge_into.sites.extend(tile.sites) | 
 |  | 
 |         tile.sites = list() | 
 |         tile.tile_type_pkey = self.empty_tile_type_pkey | 
 |         tile.root_phy_tile_pkeys = list() | 
 |         tile.phy_tile_pkeys = list() | 
 |  | 
 |     def merge_tile_type(self, tile_type_pkey, merge_direction): | 
 |         """ Merge tile types in specified direction. | 
 |  | 
 |         Parameters | 
 |         ---------- | 
 |         tile_type_pkey : Tile type to split. | 
 |         merge_direction : Direction | 
 |             Direction to merge tiles. | 
 |  | 
 |         """ | 
 |         for tile in self.items: | 
 |             if tile.tile_type_pkey == tile_type_pkey: | 
 |                 self.merge_in_dir(tile, merge_direction) | 
 |  | 
 |     def output_grid(self): | 
 |         """ Convert grid back to coordinate lookup form. | 
 |  | 
 |         Returns | 
 |         ------- | 
 |         grid_loc_map : Dict of 2 int tuple to Tile objects | 
 |             Output grid of Tile objects. | 
 |  | 
 |         """ | 
 |         grid_loc_map = {} | 
 |         for x, tile in enumerate(self.origin.walk_in_direction(EAST)): | 
 |             for y, tile in enumerate(tile.walk_in_direction(SOUTH)): | 
 |                 grid_loc_map[(x, y)] = tile | 
 |  | 
 |         check_grid_loc(grid_loc_map) | 
 |  | 
 |         return grid_loc_map | 
 |  | 
 |     def check_grid(self): | 
 |         """ Verifies that grid linked list model still represents valid grid. | 
 |         """ | 
 |         self.output_grid() |