def num_container_updates(container_replicas, container_quorum,
                          object_replicas, object_quorum):
    """
    We need to send container updates via enough object servers such
    that, if the object PUT succeeds, then the container update is
    durable (either it's synchronously updated or written to async
    pendings).

    Define:
      Qc = the quorum size for the container ring
      Qo = the quorum size for the object ring
      Rc = the replica count for the container ring
      Ro = the replica count (or EC N+K) for the object ring

    A durable container update is one that's made it to at least Qc
    nodes. To always be durable, we have to send enough container
    updates so that, if only Qo object PUTs succeed, and all the
    failed object PUTs had container updates, at least Qc updates
    remain. Since (Ro - Qo) object PUTs may fail, we must have at
    least Qc + Ro - Qo container updates to ensure that Qc of them
    remain.

    Also, each container replica is named in at least one object PUT
    request so that, when all requests succeed, no work is generated
    for the container replicator. Thus, at least Rc updates are
    necessary.

    :param container_replicas: replica count for the container ring (Rc)
    :param container_quorum: quorum size for the container ring (Qc)
    :param object_replicas: replica count for the object ring (Ro)
    :param object_quorum: quorum size for the object ring (Qo)

    """
    return max(
        # Qc + Ro - Qo
        container_quorum + object_replicas - object_quorum,
        # Rc
        container_replicas)