Convert and decode model entries.

    Model entries are converted to new entries using the translate functions
    and IDs are converted using the given coversion function. If ID conversion
    would create a clash of IDs, the ``create_unique_id`` function is called
    with a container of current IDs and the base ID to generate a unique ID
    from. The translation functions take an existing entry and the new ID.

    All references within the model are updated to use new IDs: compartment
    boundaries, limits, exchange, model, biomass reaction, etc.

    Args:
        model: :class:`NativeModel`.

def convert_model_entries(
        model, convert_id=create_convert_sbml_id_function(),
        create_unique_id=None,
        translate_compartment=translate_sbml_compartment,
        translate_reaction=translate_sbml_reaction,
        translate_compound=translate_sbml_compound):
    """Convert and decode model entries.

    Model entries are converted to new entries using the translate functions
    and IDs are converted using the given coversion function. If ID conversion
    would create a clash of IDs, the ``create_unique_id`` function is called
    with a container of current IDs and the base ID to generate a unique ID
    from. The translation functions take an existing entry and the new ID.

    All references within the model are updated to use new IDs: compartment
    boundaries, limits, exchange, model, biomass reaction, etc.

    Args:
        model: :class:`NativeModel`.
    """
    def find_new_ids(entries):
        """Create new IDs for entries."""
        id_map = {}
        new_ids = set()
        for entry in entries:
            new_id = convert_id(entry)
            if new_id in new_ids:
                if create_unique_id is not None:
                    new_id = create_unique_id(new_ids, new_id)
                else:
                    raise ValueError(
                        'Entity ID {!r} is not unique after conversion'.format(
                            entry.id))

            id_map[entry.id] = new_id
            new_ids.add(new_id)

        return id_map

    # Find new IDs for all entries
    compartment_map = find_new_ids(model.compartments)
    compound_map = find_new_ids(model.compounds)
    reaction_map = find_new_ids(model.reactions)

    # Create new compartment entries
    new_compartments = []
    for compartment in model.compartments:
        new_id = compartment_map[compartment.id]
        new_compartments.append(translate_compartment(compartment, new_id))

    # Create new compound entries
    new_compounds = []
    for compound in model.compounds:
        new_id = compound_map[compound.id]
        new_compounds.append(
            translate_compound(compound, new_id, compartment_map))

    # Create new reaction entries
    new_reactions = []
    for reaction in model.reactions:
        new_id = reaction_map[reaction.id]
        new_entry = translate_reaction(
            reaction, new_id, compartment_map, compound_map)
        new_reactions.append(new_entry)

    # Update entries
    model.compartments.clear()
    model.compartments.update(new_compartments)

    model.compounds.clear()
    model.compounds.update(new_compounds)

    model.reactions.clear()
    model.reactions.update(new_reactions)

    # Convert compartment boundaries
    new_boundaries = []
    for boundary in model.compartment_boundaries:
        c1, c2 = (compartment_map.get(c, c) for c in boundary)
        new_boundaries.append(tuple(sorted(c1, c2)))

    model.compartment_boundaries.clear()
    model.compartment_boundaries.update(new_boundaries)

    # Convert limits
    new_limits = []
    for reaction, lower, upper in itervalues(model.limits):
        new_reaction_id = reaction_map.get(reaction, reaction)
        new_limits.append((new_reaction_id, lower, upper))

    model.limits.clear()
    model.limits.update((limit[0], limit) for limit in new_limits)

    # Convert exchange
    new_exchanges = []
    for compound, reaction, lower, upper in itervalues(model.exchange):
        new_compound_id = compound.translated(
            lambda name: compound_map.get(name, name))
        new_reaction_id = reaction_map.get(reaction, reaction)
        new_exchanges.append((new_compound_id, new_reaction_id, lower, upper))

    model.exchange.clear()
    model.exchange.update((ex[0], ex) for ex in new_exchanges)

    # Convert model
    new_model = []
    for reaction in model.model:
        new_id = reaction_map.get(reaction, reaction)
        new_model.append(new_id)

    model.model.clear()
    model.model.update((new_id, None) for new_id in new_model)

    # Convert other properties
    if model.biomass_reaction is not None:
        old_id = model.biomass_reaction
        model.biomass_reaction = reaction_map.get(old_id, old_id)

    if model.extracellular_compartment is not None:
        old_id = model.extracellular_compartment
        model.extracellular_compartment = compartment_map.get(old_id, old_id)

    if model.default_compartment is not None:
        old_id = model.default_compartment
        model.default_compartment = compartment_map.get(old_id, old_id)