def classify_changes(changes, allowed_changes):
    """
    Classify list of changes into good(allowed) and bad(not allowed) based on allowed changes.

    :param list[(tuple,object,object)] changes: result of get_doc_changes
    :param allowed_changes: mapping from key to change policy (subset, superset, any)
    :return: good_changes, bad_chages


    >>> classify_changes([], {})
    ([], [])
    >>> classify_changes([(('a',), 1, 2)], {})
    ([], [(('a',), 1, 2)])
    >>> classify_changes([(('a',), 1, 2)], {('a',): allow_any})
    ([(('a',), 1, 2)], [])

    >>> changes = [(('a2',), {'b1': 1}, MISSING)]  # {'a1': 1, 'a2': {'b1': 1}} → {'a1': 1}
    >>> good_change = (changes, [])
    >>> bad_change = ([], changes)
    >>> classify_changes(changes, {}) == bad_change
    True
    >>> classify_changes(changes, {tuple(): allow_any}) == good_change
    True
    >>> classify_changes(changes, {tuple(): allow_removal}) == bad_change
    True
    >>> classify_changes(changes, {tuple(): allow_addition}) == bad_change
    True
    >>> classify_changes(changes, {tuple(): allow_truncation}) == good_change
    True
    >>> classify_changes(changes, {tuple(): allow_extension}) == bad_change
    True
    >>> classify_changes(changes, {('a1', ): allow_any}) == bad_change
    True
    >>> classify_changes(changes, {('a1', ): allow_removal}) == bad_change
    True
    >>> classify_changes(changes, {('a1', ): allow_addition}) == bad_change
    True
    >>> classify_changes(changes, {('a1', ): allow_truncation}) == bad_change
    True
    >>> classify_changes(changes, {('a1', ): allow_extension}) == bad_change
    True
    >>> classify_changes(changes, {('a2', ): allow_any}) == good_change
    True
    >>> classify_changes(changes, {('a2', ): allow_removal}) == good_change
    True
    >>> classify_changes(changes, {('a2', ): allow_addition}) == bad_change
    True
    >>> classify_changes(changes, {('a2', ): allow_truncation}) == bad_change
    True
    >>> classify_changes(changes, {('a2', ): allow_extension}) == bad_change
    True

    """
    allowed_changes_index = dict(allowed_changes)
    good_changes = []
    bad_changes = []
    for offset, old_val, new_val in changes:
        allowance = allowed_changes_index.get(offset)
        allowance_offset = offset
        while allowance is None:
            if not allowance_offset:
                break
            allowance_offset = allowance_offset[:-1]
            allowance = allowed_changes_index.get(allowance_offset)
        if allowance is None:
            bad_changes.append((offset, old_val, new_val))
        elif hasattr(allowance, '__call__'):
            if allowance(allowance_offset, offset, old_val, new_val):
                good_changes.append((offset, old_val, new_val))
            else:
                bad_changes.append((offset, old_val, new_val))
        else:
            raise RuntimeError(
                'Unknown change type: expecting validation function at %r' %
                offset)
    return good_changes, bad_changes
