Need to find where in the tree the two plates intersect, e.g.

        We are given as input plates D, E, whose positions in the tree are:

        root -> A -> B -> C -> D
        root -> A -> B -> E

        The results should then be the cartesian product between C, D, E looped over A and B

        If there's a shared plate in the hierarchy, we need to join on this shared plate, e.g.:

        [self.plates[p].values for p in plate_ids][0] =
          [(('house', '1'), ('location', 'hallway'), ('wearable', 'A')),
           (('house', '1'), ('location', 'kitchen'), ('wearable', 'A'))]
        [self.plates[p].values for p in plate_ids][1] =
          [(('house', '1'), ('scripted', '15')),
           (('house', '1'), ('scripted', '13'))]

        Result should be one stream for each of:
          [(('house', '1'), ('location', 'hallway'), ('wearable', 'A'), ('scripted', '15)),
           (('house', '1'), ('location', 'hallway'), ('wearable', 'A'), ('scripted', '13)),
           (('house', '1'), ('location', 'kitchen'), ('wearable', 'A'), ('scripted', '15)),
           (('house', '1'), ('location', 'kitchen'), ('wearable', 'A'), ('scripted', '13))]

        :param plates: The input plates
        :return: The plate values
        :type plates: list[Plate] | list[Plate]

def get_overlapping_values(plates):
        """
        Need to find where in the tree the two plates intersect, e.g.

        We are given as input plates D, E, whose positions in the tree are:

        root -> A -> B -> C -> D
        root -> A -> B -> E

        The results should then be the cartesian product between C, D, E looped over A and B

        If there's a shared plate in the hierarchy, we need to join on this shared plate, e.g.:

        [self.plates[p].values for p in plate_ids][0] =
          [(('house', '1'), ('location', 'hallway'), ('wearable', 'A')),
           (('house', '1'), ('location', 'kitchen'), ('wearable', 'A'))]
        [self.plates[p].values for p in plate_ids][1] =
          [(('house', '1'), ('scripted', '15')),
           (('house', '1'), ('scripted', '13'))]

        Result should be one stream for each of:
          [(('house', '1'), ('location', 'hallway'), ('wearable', 'A'), ('scripted', '15)),
           (('house', '1'), ('location', 'hallway'), ('wearable', 'A'), ('scripted', '13)),
           (('house', '1'), ('location', 'kitchen'), ('wearable', 'A'), ('scripted', '15)),
           (('house', '1'), ('location', 'kitchen'), ('wearable', 'A'), ('scripted', '13))]

        :param plates: The input plates
        :return: The plate values
        :type plates: list[Plate] | list[Plate]
        """
        if not plates:
            return None

        if len(plates) == 1:
            return plates[0].values

        if len(plates) > 2:
            raise NotImplementedError

        # First check for the simple case where one of the plates has no parent
        # and does not share meta data with the other
        plates_sorted = sorted(plates, key=lambda item: len(item.ancestor_plates))
        if plates_sorted[0].is_root:
            if plates_sorted[0].meta_data_id not in plates_sorted[1].ancestor_meta_data_ids:
                return map(lambda x: tuple(itertools.chain(*x)), itertools.product(plates[0].values, plates[1].values))

        # Get all of the ancestors zipped together, padded with None
        ancestors = deque(itertools.izip_longest(*(p.ancestor_plates for p in plates)))

        last_values = []
        while len(ancestors) > 0:
            current = ancestors.popleft()
            if current[0] == current[1]:
                # Plates are identical, take all values valid for matching parents
                if last_values:
                    raise NotImplementedError
                else:
                    last_values.extend(current[0].values)

            elif current[0] is not None and current[1] is not None \
                    and current[0].meta_data_id == current[1].meta_data_id:
                # Not identical, but same meta data id. Take all overlapping values valid for matching parents
                if last_values:
                    raise NotImplementedError
                else:
                    raise NotImplementedError
            else:
                # Different plates, take cartesian product of values with matching parents.
                # Note that one of them may be none
                if last_values:
                    tmp = []
                    for v in last_values:
                        # Get the valid ones based on v
                        # valid = [filter(lambda x: all(xx in v for xx in x[:-1]), c.values)
                        #          for c in current if c is not None]
                        valid = [filter(lambda x: all(vv in x for vv in v), c.values)
                                 for c in current if c is not None]

                        # Strip out v from the valid ones
                        stripped = [map(lambda y: tuple(itertools.chain(*(yy for yy in y if yy not in v))), val)
                                    for val in valid]

                        # Get the cartesian product. Note that this still works if one of the current is None
                        prod = list(itertools.product(*stripped))

                        # Now update the last values be the product with v put back in
                        new_values = [v + p for p in prod]
                        if new_values:
                            tmp.append(new_values)

                    last_values = list(itertools.chain(*tmp))
                    if not last_values:
                        raise ValueError("Plate value computation failed - possibly there were no shared plate values")
                else:
                    raise NotImplementedError

        if not last_values:
            raise ValueError("Plate value computation failed - possibly there were no shared plate values")

        return last_values