def _flattenKerning(pairs, groups):
    """
    >>> kerning = {
    ...     ("@LEFT_A", "@RIGHT_A") : -25,  # group, group
    ...     ("@LEFT_A", "Agrave") : -75,    # group, glyph (excpetion to @LEFT_A, @RIGHT_A)
    ...     ("@LEFT_A", "Aacute") : -74,    # group, glyph (excpetion to @LEFT_A, @RIGHT_A)
    ...     ("Agrave", "Agrave") : -100,    # glyph, glyph (exception to @LEFT_A, Agrave)
    ...     ("@LEFT_D", "@RIGHT_D") : 25,   # group, group
    ...     ("Dcroat", "@RIGHT_D") : 75,    # glyph, group (exception to @LEFT_D, @RIGHT_D)
    ...     ("Eth", "@RIGHT_D") : 74,       # glyph, group (exception to @LEFT_D, @RIGHT_D)
    ...     ("Dcroat", "Dcroat") : 100,     # glyph, glyph (excpetion to Dcroat, @RIGHT_D)
    ...     ("@LEFT_D", "X") : -25,         # group, glyph
    ...     ("X", "@RIGHT_D") : -25,        # glyph, group
    ... }
    >>> groups = {
    ...     "@LEFT_A" : ["A", "Aacute", "Agrave"],
    ...     "@RIGHT_A" : ["A", "Aacute", "Agrave"],
    ...     "@LEFT_D" : ["D", "Dcroat", "Eth"],
    ...     "@RIGHT_D" : ["D", "Dcroat", "Eth"],
    ... }
    >>> flattened = _flattenKerning(kerning, groups)
    >>> expected = {
    ...     # from ("@LEFT_A", "@RIGHT_A") : -25
    ...     ("A", "A") : -25,
    ...     ("Aacute", "A") : -25,
    ...     ("Agrave", "A") : -25,
    ...     # from ("@LEFT_A", "Agrave") : -75,
    ...     ("A", "Agrave") : -75,
    ...     ("Aacute", "Agrave") : -75,
    ...     # from ("@LEFT_A", "Aacute") : -74
    ...     ("A", "Aacute") : -74,
    ...     ("Aacute", "Aacute") : -74,
    ...     ("Agrave", "Aacute") : -74,
    ...     # from (Agrave, Agrave) : -100
    ...     ("Agrave", "Agrave") : -100,
    ...     # from ("@LEFT_D", "@RIGHT_D") : 25,
    ...     ("D", "D") : 25,
    ...     ("D", "Dcroat") : 25,
    ...     ("D", "Eth") : 25,
    ...     # from ("Dcroat", "@RIGHT_D") : 75,
    ...     ("Dcroat", "D") : 75,
    ...     ("Dcroat", "Eth") : 75,
    ...     # from ("Eth", "@RIGHT_D") : 74,
    ...     ("Eth", "D") : 74,
    ...     ("Eth", "Dcroat") : 74,
    ...     ("Eth", "Eth") : 74,
    ...     # from ("Dcroat", "Dcroat") : 100,
    ...     ("Dcroat", "Dcroat") : 100,
    ...     # from ("@LEFT_D", "X") : -25,
    ...     ("D", "X") : -25,
    ...     ("Dcroat", "X") : -25,
    ...     ("Eth", "X") : -25,
    ...     # from ("X", "@RIGHT_D") : -25,
    ...     ("X", "D") : -25,
    ...     ("X", "Dcroat") : -25,
    ...     ("X", "Eth") : -25,
    ... }
    >>> flattened == expected
    True
    """
    # Organize the pairs into sets based on the
    # presence or absence of groups.
    glyphGlyph = {}
    groupGlyph = {}
    glyphGroup = {}
    groupGroup = {}
    for (left, right), value in pairs.items():
        if left in groups and right in groups:
            d = groupGroup
        elif left in groups:
            d = groupGlyph
        elif right in groups:
            d = glyphGroup
        else:
            d = glyphGlyph
        d[left, right] = value
    # Start with the glyph, glyph pairs since
    # they can't be flattened any further.
    flattened = dict(glyphGlyph)
    # Flatten group, glyph
    for (left, right), value in groupGlyph.items():
        # The left is a group, so work through
        # all group members plus the right.
        for l in groups[left]:
            # If the pair is already flattened, skip it.
            if (l, right) in flattened:
                continue
            flattened[l, right] = value
    # Flatten glyph, group
    for (left, right), value in glyphGroup.items():
        # The right is a group, so work through
        # all group members plus the left.
        for r in groups[right]:
            # If the pair is already flattened, skip it.
            if (left, r) in flattened:
                continue
            flattened[left, r] = value
    # Flatten group, group
    for (left, right), value in groupGroup.items():
        # Work through the left group.
        for l in groups[left]:
            # Work through the right group.
            for r in groups[right]:
                # If the pair is already flattened, skip it.
                if (l, r) in flattened:
                    continue
                flattened[l, r] = value
    # Done.
    return flattened