'Returns a defaultdict of (tier: replicanths) for all tiers in the ring
based on unique-as-possible (full dispersion) with respect to their
weights and device counts.
N.B.  _build_max_replicas_by_tier calculates the upper bound on the
replicanths each tier may hold irrespective of the weights of the
tier; this method will calculate the minimum replicanth <=
max_replicas[tier] that will still solve dispersion.  However, it is
not guaranteed to return a fully dispersed solution if failure domains
are over-weighted for their device count.'

def _build_wanted_replicas_by_tier(self):

    weighted_replicas = self._build_weighted_replicas_by_tier()
    dispersed_replicas = {t: {'min': math.floor(r), 'max': math.ceil(r)} for (t, r) in self._build_max_replicas_by_tier(bound=float).items()}
    num_devices = defaultdict(int)
    for d in self._iter_devs():
        if (d['weight'] <= 0):
            continue
        for t in (d.get('tiers') or tiers_for_dev(d)):
            num_devices[t] += 1
        num_devices[()] += 1
    tier2children = self._build_tier2children()
    wanted_replicas = defaultdict(float)
    def place_replicas(tier, replicanths):
        if (replicanths > num_devices[tier]):
            raise exceptions.RingValidationError(('More    replicanths    (%s)    than    devices    (%s)    in    tier    (%s)' % (replicanths, num_devices[tier], tier)))
        wanted_replicas[tier] = replicanths
        sub_tiers = sorted(tier2children[tier])
        if (not sub_tiers):
            return
        to_place = defaultdict(float)
        remaining = replicanths
        tiers_to_spread = sub_tiers
        device_limited = False
        while True:
            rel_weight = (remaining / sum((weighted_replicas[t] for t in tiers_to_spread)))
            for t in tiers_to_spread:
                replicas = (to_place[t] + (weighted_replicas[t] * rel_weight))
                if (replicas < dispersed_replicas[t]['min']):
                    replicas = dispersed_replicas[t]['min']
                elif ((replicas > dispersed_replicas[t]['max']) and (not device_limited)):
                    replicas = dispersed_replicas[t]['max']
                if (replicas > num_devices[t]):
                    replicas = num_devices[t]
                to_place[t] = replicas
            remaining = (replicanths - sum(to_place.values()))
            if (remaining < (-1e-10)):
                tiers_to_spread = [t for t in sub_tiers if (to_place[t] > dispersed_replicas[t]['min'])]
            elif (remaining > 1e-10):
                tiers_to_spread = [t for t in sub_tiers if (num_devices[t] > to_place[t] < dispersed_replicas[t]['max'])]
                if (not tiers_to_spread):
                    device_limited = True
                    tiers_to_spread = [t for t in sub_tiers if (to_place[t] < num_devices[t])]
            else:
                break
        for t in sub_tiers:
            self.logger.debug('Planning    %s    on    %s', to_place[t], t)
            place_replicas(t, to_place[t])
    place_replicas((), self.replicas)
    tiers = ['cluster', 'regions', 'zones', 'servers', 'devices']
    for (i, tier_name) in enumerate(tiers):
        replicas_at_tier = sum((wanted_replicas[t] for t in wanted_replicas if (len(t) == i)))
        if (abs((self.replicas - replicas_at_tier)) > 1e-10):
            raise exceptions.RingValidationError(('%s    !=    %s    at    tier    %s' % (replicas_at_tier, self.replicas, tier_name)))
    return wanted_replicas
