def match_columns(columns, schema_index, renamed=None, renamed_to=True):
    """Compute matching of given list of 'id-aligned' columns to columns in the
    given snapshot schema. Columns are matched by name. If either list contains
    duplicate column names an error is raised.

    Returns a list of tuples (column, match) where column is an element from
    the list of columns and match is either an element from the schema or None
    for unmatched columns. The order of elements in the result corresponds to
    the order of elements in the column list.

    Parameters
    ----------
    columns: list((string, histore.archive.schema.ArchiveColumn or None)
        List of column names.
    schema_index: dict(string: histore.archive.schema.ArchiveColumn)
        Dictionary mapping of column names (in a snapshot schema) to their
        respective archive column.
    renamed: dict, default=None
        Optional mapping of columns that have been renamed. Maps the new column
        name to the original name.
    renamed_to: bool, default=True
        Flag that determines the semantics of the mapping in the renamed
        dictionary. By default a mapping from the original column name (i.e.,
        the dictionary key) to the new column name (the dictionary value) is
        assumed. If the flag is False a mapping from the new column name to the
        original column name is assumed.

    Returns
    -------
    list(string, histore.document.schema.Column)

    Raises
    ------
    ValueError
    """
    if renamed is None:
        renamed_from = dict()
    elif renamed_to:
        renamed_from = dict()
        for key, val in renamed.items():
            if val in renamed_from:
                raise ValueError('multiple columns renamed to same name')
            renamed_from[val] = key
    else:
        renamed_from = renamed
    matches = list()
    matched_schema_columns = set()
    names = set()
    for df_col, arch_col in columns:
        if arch_col is not None:
            matches.append((df_col, arch_col))
            continue
        name = str(df_col)
        if name in names:
            raise ValueError('duplicate column name %s' % str(name))
        names.add(name)
        match = schema_index.get(renamed_from.get(name, name))
        if match is not None:
            if match.identifier in matched_schema_columns:
                raise ValueError('multiple matches for %d' % match.colid)
            matched_schema_columns.add(match.identifier)
        matches.append((name, match))
    return matches
