def _selection_share_common(sel: Antlr4Selection, start_index):
    assert start_index == 0 or start_index == -1
    assert isinstance(sel, Antlr4Selection), sel.__class__
    # find options which starting with the same element
    with_item = {i: set([
        i,
    ])
                 for i in range(len(sel))}
    for i0, p_opt in enumerate(sel):
        for i1, opt in enumerate(islice(sel, i0 + 1, None)):
            if p_opt and opt and p_opt[start_index] == opt[start_index]:
                prefix_cls = with_item[i0]
                prefix_cls.update(with_item[i0 + i1 + 1])
                with_item[i0 + i1 + 1] = prefix_cls
    resolved = set()
    new_options = []
    changed = False
    for i, _prefix_eq_cls in sorted(with_item.items()):
        if i in resolved:
            continue
        if len(_prefix_eq_cls) == 1:
            # keep option as it is
            new_opt = sel[i]
        else:
            changed = True
            # extraction of shared prefix
            eq_cls = list(sorted(_prefix_eq_cls))
            extracted_item = sel[i][start_index]
            if start_index == 0:
                new_opt = Antlr4Sequence([
                    extracted_item,
                    Antlr4Selection(
                        [Antlr4Sequence(sel[i0][1:]) for i0 in eq_cls])
                ])
            else:
                assert start_index == -1
                new_opt = Antlr4Sequence([
                    Antlr4Selection(
                        [Antlr4Sequence(sel[i0][:-1]) for i0 in eq_cls]),
                    extracted_item
                ])
            resolved.update(eq_cls)
        new_options.append(new_opt)
    if len(new_options) == 1:
        return new_options[0], changed
    else:
        sel.clear()
        sel.extend(new_options)
        return sel, changed
def _selection_reduce_optional(o: Antlr4Selection):
    """
    a? | b? -> (a | b)?
    """
    assert isinstance(o, Antlr4Selection)
    non_optional_items = []
    for c in o:
        if isinstance(c, Antlr4Sequence):
            if len(c) != 1:
                return o, False
            c = c[0]

        if not isinstance(c, Antlr4Option):
            return o, False
        non_optional_items.append(c.body)

    o.clear()
    o.extend(non_optional_items)
    return Antlr4Sequence([
        Antlr4Option(o),
    ]), True