def lookup(self, header_bag, direction, strict=False):
        """
        Given a single cell (usually a value), a bag containing the headers
        of a particular type for that cell, and the direction in which to
        search for the relevant header

        e.g. for value cell V, searching up:

         [ ]                         [ ]
                                     [O]
                                                [ ]
                   ---> [ ]
                                      V
                                     [ ]
                             [ ]

        the cell with the arrow will be returned.

        Strict restricts the selection to cells in the same row/column as
        the value, so O is selected instead."""
        def mult(cell):
            return cell.x * direction[0] + cell.y * direction[1]

        def same_row_col(a, b, direction):
            return  (a.x - b.x  == 0 and direction[0] == 0) or \
                    (a.y - b.y  == 0 and direction[1] == 0)

        best_cell = None
        second_best_cell = None
        for target_cell in header_bag.unordered_cells:
            if mult(self) <= mult(target_cell):
                if not best_cell or mult(target_cell) <= mult(best_cell):
                    if not strict or same_row_col(self, target_cell, direction):
                        second_best_cell = best_cell
                        best_cell = target_cell
        if second_best_cell and mult(best_cell) == mult(second_best_cell):
            raise LookupConfusionError("{!r} is as good as {!r} for {!r}".format(
                best_cell, second_best_cell, self))
        if best_cell is None:
            raise NoLookupError("No lookup for {!r}".format(self))
        return best_cell