sp_phragmen/
lib.rs

1// This file is part of Substrate.
2
3// Copyright (C) 2019-2020 Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Rust implementation of the Phragmén election algorithm. This is used in several pallets to
19//! optimally distribute the weight of a set of voters among an elected set of candidates. In the
20//! context of staking this is mapped to validators and nominators.
21//!
22//! The algorithm has two phases:
23//!   - Sequential phragmen: performed in [`elect`] function which is first pass of the distribution
24//!     The results are not optimal but the execution time is less.
25//!   - Equalize post-processing: tries to further distribute the weight fairly among candidates.
26//!     Incurs more execution time.
27//!
28//! The main objective of the assignments done by phragmen is to maximize the minimum backed
29//! candidate in the elected set.
30//!
31//! Reference implementation: https://github.com/w3f/consensus
32//! Further details:
33//! https://research.web3.foundation/en/latest/polkadot/NPoS/4.%20Sequential%20Phragm%C3%A9n%E2%80%99s%20method/
34
35#![cfg_attr(not(feature = "std"), no_std)]
36
37use sp_std::{prelude::*, collections::btree_map::BTreeMap, fmt::Debug, cmp::Ordering, convert::TryFrom};
38use sp_arithmetic::{
39	PerThing, Rational128,
40	helpers_128bit::multiply_by_rational,
41	traits::{Zero, Saturating, Bounded, SaturatedConversion},
42};
43
44#[cfg(test)]
45mod mock;
46#[cfg(test)]
47mod tests;
48#[cfg(feature = "std")]
49use serde::{Serialize, Deserialize};
50#[cfg(feature = "std")]
51use codec::{Encode, Decode};
52
53mod node;
54mod reduce;
55mod helpers;
56
57// re-export reduce stuff.
58pub use reduce::reduce;
59
60// re-export the helpers.
61pub use helpers::*;
62
63// re-export the compact macro, with the dependencies of the macro.
64#[doc(hidden)]
65pub use codec;
66#[doc(hidden)]
67pub use sp_arithmetic;
68
69// re-export the compact solution type.
70pub use sp_phragmen_compact::generate_compact_solution_type;
71
72/// A trait to limit the number of votes per voter. The generated compact type will implement this.
73pub trait VotingLimit {
74	const LIMIT: usize;
75}
76
77/// an aggregator trait for a generic type of a voter/target identifier. This usually maps to
78/// substrate's account id.
79pub trait IdentifierT: Clone + Eq + Default + Ord + Debug + codec::Codec {}
80
81impl<T: Clone + Eq + Default + Ord + Debug + codec::Codec> IdentifierT for T {}
82
83/// The errors that might occur in the this crate and compact.
84#[derive(Debug, Eq, PartialEq)]
85pub enum Error {
86	/// While going from compact to staked, the stake of all the edges has gone above the
87	/// total and the last stake cannot be assigned.
88	CompactStakeOverflow,
89	/// The compact type has a voter who's number of targets is out of bound.
90	CompactTargetOverflow,
91	/// One of the index functions returned none.
92	CompactInvalidIndex,
93}
94
95/// A type which is used in the API of this crate as a numeric weight of a vote, most often the
96/// stake of the voter. It is always converted to [`ExtendedBalance`] for computation.
97pub type VoteWeight = u64;
98
99/// A type in which performing operations on vote weights are safe.
100pub type ExtendedBalance = u128;
101
102/// The score of an assignment. This can be computed from the support map via [`evaluate_support`].
103pub type PhragmenScore = [ExtendedBalance; 3];
104
105/// A winner, with their respective approval stake.
106pub type WithApprovalOf<A> = (A, ExtendedBalance);
107
108/// The denominator used for loads. Since votes are collected as u64, the smallest ratio that we
109/// might collect is `1/approval_stake` where approval stake is the sum of votes. Hence, some number
110/// bigger than u64::max_value() is needed. For maximum accuracy we simply use u128;
111const DEN: u128 = u128::max_value();
112
113/// A candidate entity for phragmen election.
114#[derive(Clone, Default, Debug)]
115struct Candidate<AccountId> {
116	/// Identifier.
117	who: AccountId,
118	/// Intermediary value used to sort candidates.
119	score: Rational128,
120	/// Sum of the stake of this candidate based on received votes.
121	approval_stake: ExtendedBalance,
122	/// Flag for being elected.
123	elected: bool,
124}
125
126/// A voter entity.
127#[derive(Clone, Default, Debug)]
128struct Voter<AccountId> {
129	/// Identifier.
130	who: AccountId,
131	/// List of candidates proposed by this voter.
132	edges: Vec<Edge<AccountId>>,
133	/// The stake of this voter.
134	budget: ExtendedBalance,
135	/// Incremented each time a candidate that this voter voted for has been elected.
136	load: Rational128,
137}
138
139/// A candidate being backed by a voter.
140#[derive(Clone, Default, Debug)]
141struct Edge<AccountId> {
142	/// Identifier.
143	who: AccountId,
144	/// Load of this vote.
145	load: Rational128,
146	/// Index of the candidate stored in the 'candidates' vector.
147	candidate_index: usize,
148}
149
150/// Final result of the phragmen election.
151#[derive(Debug)]
152pub struct PhragmenResult<AccountId, T: PerThing> {
153	/// Just winners zipped with their approval stake. Note that the approval stake is merely the
154	/// sub of their received stake and could be used for very basic sorting and approval voting.
155	pub winners: Vec<WithApprovalOf<AccountId>>,
156	/// Individual assignments. for each tuple, the first elements is a voter and the second
157	/// is the list of candidates that it supports.
158	pub assignments: Vec<Assignment<AccountId, T>>,
159}
160
161/// A voter's stake assignment among a set of targets, represented as ratios.
162#[derive(Debug, Clone, Default)]
163#[cfg_attr(feature = "std", derive(PartialEq, Eq, Encode, Decode))]
164pub struct Assignment<AccountId, T: PerThing> {
165	/// Voter's identifier.
166	pub who: AccountId,
167	/// The distribution of the voter's stake.
168	pub distribution: Vec<(AccountId, T)>,
169}
170
171impl<AccountId, T: PerThing> Assignment<AccountId, T>
172where
173	ExtendedBalance: From<<T as PerThing>::Inner>,
174{
175	/// Convert from a ratio assignment into one with absolute values aka. [`StakedAssignment`].
176	///
177	/// It needs `stake` which is the total budget of the voter. If `fill` is set to true,
178	/// it _tries_ to ensure that all the potential rounding errors are compensated and the
179	/// distribution's sum is exactly equal to the total budget, by adding or subtracting the
180	/// remainder from the last distribution.
181	///
182	/// If an edge ratio is [`Bounded::max_value()`], it is dropped. This edge can never mean
183	/// anything useful.
184	pub fn into_staked(self, stake: ExtendedBalance, fill: bool) -> StakedAssignment<AccountId>
185	where
186		T: sp_std::ops::Mul<ExtendedBalance, Output = ExtendedBalance>,
187	{
188		let mut sum: ExtendedBalance = Bounded::min_value();
189		let mut distribution = self
190			.distribution
191			.into_iter()
192			.filter_map(|(target, p)| {
193				// if this ratio is zero, then skip it.
194				if p == Bounded::min_value() {
195					None
196				} else {
197					// NOTE: this mul impl will always round to the nearest number, so we might both
198					// overflow and underflow.
199					let distribution_stake = p * stake;
200					// defensive only. We assume that balance cannot exceed extended balance.
201					sum = sum.saturating_add(distribution_stake);
202					Some((target, distribution_stake))
203				}
204			})
205			.collect::<Vec<(AccountId, ExtendedBalance)>>();
206
207		if fill {
208			// NOTE: we can do this better.
209			// https://revs.runtime-revolution.com/getting-100-with-rounded-percentages-273ffa70252b
210			if let Some(leftover) = stake.checked_sub(sum) {
211				if let Some(last) = distribution.last_mut() {
212					last.1 = last.1.saturating_add(leftover);
213				}
214			} else if let Some(excess) = sum.checked_sub(stake) {
215				if let Some(last) = distribution.last_mut() {
216					last.1 = last.1.saturating_sub(excess);
217				}
218			}
219		}
220
221		StakedAssignment {
222			who: self.who,
223			distribution,
224		}
225	}
226}
227
228/// A voter's stake assignment among a set of targets, represented as absolute values in the scale
229/// of [`ExtendedBalance`].
230#[derive(Debug, Clone, Default)]
231#[cfg_attr(feature = "std", derive(PartialEq, Eq, Encode, Decode))]
232pub struct StakedAssignment<AccountId> {
233	/// Voter's identifier
234	pub who: AccountId,
235	/// The distribution of the voter's stake.
236	pub distribution: Vec<(AccountId, ExtendedBalance)>,
237}
238
239impl<AccountId> StakedAssignment<AccountId> {
240	/// Converts self into the normal [`Assignment`] type.
241	///
242	/// If `fill` is set to true, it _tries_ to ensure that all the potential rounding errors are
243	/// compensated and the distribution's sum is exactly equal to 100%, by adding or subtracting
244	/// the remainder from the last distribution.
245	///
246	/// NOTE: it is quite critical that this attempt always works. The data type returned here will
247	/// potentially get used to create a compact type; a compact type requires sum of ratios to be
248	/// less than 100% upon un-compacting.
249	///
250	/// If an edge stake is so small that it cannot be represented in `T`, it is ignored. This edge
251	/// can never be re-created and does not mean anything useful anymore.
252	pub fn into_assignment<T: PerThing>(self, fill: bool) -> Assignment<AccountId, T>
253	where
254		ExtendedBalance: From<<T as PerThing>::Inner>,
255	{
256		let accuracy: u128 = T::ACCURACY.saturated_into();
257		let mut sum: u128 = Zero::zero();
258		let stake = self.distribution.iter().map(|x| x.1).sum();
259		let mut distribution = self
260			.distribution
261			.into_iter()
262			.filter_map(|(target, w)| {
263				let per_thing = T::from_rational_approximation(w, stake);
264				if per_thing == Bounded::min_value() {
265					None
266				} else {
267					sum += per_thing.clone().deconstruct().saturated_into();
268					Some((target, per_thing))
269				}
270			})
271			.collect::<Vec<(AccountId, T)>>();
272
273		if fill {
274			if let Some(leftover) = accuracy.checked_sub(sum) {
275				if let Some(last) = distribution.last_mut() {
276					last.1 = last.1.saturating_add(
277						T::from_parts(leftover.saturated_into())
278					);
279				}
280			} else if let Some(excess) = sum.checked_sub(accuracy) {
281				if let Some(last) = distribution.last_mut() {
282					last.1 = last.1.saturating_sub(
283						T::from_parts(excess.saturated_into())
284					);
285				}
286			}
287		}
288
289		Assignment {
290			who: self.who,
291			distribution,
292		}
293	}
294
295	/// Get the total stake of this assignment (aka voter budget).
296	pub fn total(&self) -> ExtendedBalance {
297		self.distribution.iter().fold(Zero::zero(), |a, b| a.saturating_add(b.1))
298	}
299}
300
301/// A structure to demonstrate the phragmen result from the perspective of the candidate, i.e. how
302/// much support each candidate is receiving.
303///
304/// This complements the [`PhragmenResult`] and is needed to run the equalize post-processing.
305///
306/// This, at the current version, resembles the `Exposure` defined in the Staking pallet, yet
307/// they do not necessarily have to be the same.
308#[derive(Default, Debug)]
309#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Eq, PartialEq))]
310pub struct Support<AccountId> {
311	/// Total support.
312	pub total: ExtendedBalance,
313	/// Support from voters.
314	pub voters: Vec<(AccountId, ExtendedBalance)>,
315}
316
317/// A linkage from a candidate and its [`Support`].
318pub type SupportMap<A> = BTreeMap<A, Support<A>>;
319
320/// Perform election based on Phragmén algorithm.
321///
322/// Returns an `Option` the set of winners and their detailed support ratio from each voter if
323/// enough candidates are provided. Returns `None` otherwise.
324///
325/// * `candidate_count`: number of candidates to elect.
326/// * `minimum_candidate_count`: minimum number of candidates to elect. If less candidates exist,
327///   `None` is returned.
328/// * `initial_candidates`: candidates list to be elected from.
329/// * `initial_voters`: voters list.
330///
331/// This function does not strip out candidates who do not have any backing stake. It is the
332/// responsibility of the caller to make sure only those candidates who have a sensible economic
333/// value are passed in. From the perspective of this function, a candidate can easily be among the
334/// winner with no backing stake.
335pub fn elect<AccountId, R>(
336	candidate_count: usize,
337	minimum_candidate_count: usize,
338	initial_candidates: Vec<AccountId>,
339	initial_voters: Vec<(AccountId, VoteWeight, Vec<AccountId>)>,
340) -> Option<PhragmenResult<AccountId, R>> where
341	AccountId: Default + Ord + Clone,
342	R: PerThing,
343{
344	// return structures
345	let mut elected_candidates: Vec<(AccountId, ExtendedBalance)>;
346	let mut assigned: Vec<Assignment<AccountId, R>>;
347
348	// used to cache and access candidates index.
349	let mut c_idx_cache = BTreeMap::<AccountId, usize>::new();
350
351	// voters list.
352	let num_voters = initial_candidates.len() + initial_voters.len();
353	let mut voters: Vec<Voter<AccountId>> = Vec::with_capacity(num_voters);
354
355	// Iterate once to create a cache of candidates indexes. This could be optimized by being
356	// provided by the call site.
357	let mut candidates = initial_candidates
358		.into_iter()
359		.enumerate()
360		.map(|(idx, who)| {
361			c_idx_cache.insert(who.clone(), idx);
362			Candidate { who, ..Default::default() }
363		})
364		.collect::<Vec<Candidate<AccountId>>>();
365
366	// early return if we don't have enough candidates
367	if candidates.len() < minimum_candidate_count { return None; }
368
369	// collect voters. use `c_idx_cache` for fast access and aggregate `approval_stake` of
370	// candidates.
371	voters.extend(initial_voters.into_iter().map(|(who, voter_stake, votes)| {
372		let mut edges: Vec<Edge<AccountId>> = Vec::with_capacity(votes.len());
373		for v in votes {
374			if let Some(idx) = c_idx_cache.get(&v) {
375				// This candidate is valid + already cached.
376				candidates[*idx].approval_stake = candidates[*idx].approval_stake
377					.saturating_add(voter_stake.into());
378				edges.push(Edge { who: v.clone(), candidate_index: *idx, ..Default::default() });
379			} // else {} would be wrong votes. We don't really care about it.
380		}
381		Voter {
382			who,
383			edges: edges,
384			budget: voter_stake.into(),
385			load: Rational128::zero(),
386		}
387	}));
388
389
390	// we have already checked that we have more candidates than minimum_candidate_count.
391	// run phragmen.
392	let to_elect = candidate_count.min(candidates.len());
393	elected_candidates = Vec::with_capacity(candidate_count);
394	assigned = Vec::with_capacity(candidate_count);
395
396	// main election loop
397	for _round in 0..to_elect {
398		// loop 1: initialize score
399		for c in &mut candidates {
400			if !c.elected {
401				// 1 / approval_stake == (DEN / approval_stake) / DEN. If approval_stake is zero,
402				// then the ratio should be as large as possible, essentially `infinity`.
403				if c.approval_stake.is_zero() {
404					c.score = Rational128::from_unchecked(DEN, 0);
405				} else {
406					c.score = Rational128::from(DEN / c.approval_stake, DEN);
407				}
408			}
409		}
410
411		// loop 2: increment score
412		for n in &voters {
413			for e in &n.edges {
414				let c = &mut candidates[e.candidate_index];
415				if !c.elected && !c.approval_stake.is_zero() {
416					let temp_n = multiply_by_rational(
417						n.load.n(),
418						n.budget,
419						c.approval_stake,
420					).unwrap_or(Bounded::max_value());
421					let temp_d = n.load.d();
422					let temp = Rational128::from(temp_n, temp_d);
423					c.score = c.score.lazy_saturating_add(temp);
424				}
425			}
426		}
427
428		// loop 3: find the best
429		if let Some(winner) = candidates
430			.iter_mut()
431			.filter(|c| !c.elected)
432			.min_by_key(|c| c.score)
433		{
434			// loop 3: update voter and edge load
435			winner.elected = true;
436			for n in &mut voters {
437				for e in &mut n.edges {
438					if e.who == winner.who {
439						e.load = winner.score.lazy_saturating_sub(n.load);
440						n.load = winner.score;
441					}
442				}
443			}
444
445			elected_candidates.push((winner.who.clone(), winner.approval_stake));
446		} else {
447			break
448		}
449	} // end of all rounds
450
451	// update backing stake of candidates and voters
452	for n in &mut voters {
453		let mut assignment = Assignment {
454			who: n.who.clone(),
455			..Default::default()
456		};
457		for e in &mut n.edges {
458			if elected_candidates.iter().position(|(ref c, _)| *c == e.who).is_some() {
459				let per_bill_parts: R::Inner =
460				{
461					if n.load == e.load {
462						// Full support. No need to calculate.
463						R::ACCURACY
464					} else {
465						if e.load.d() == n.load.d() {
466							// return e.load / n.load.
467							let desired_scale: u128 = R::ACCURACY.saturated_into();
468							let parts = multiply_by_rational(
469								desired_scale,
470								e.load.n(),
471								n.load.n(),
472							)
473							// If result cannot fit in u128. Not much we can do about it.
474							.unwrap_or(Bounded::max_value());
475
476							TryFrom::try_from(parts)
477								// If the result cannot fit into R::Inner. Defensive only. This can
478								// never happen. `desired_scale * e / n`, where `e / n < 1` always
479								// yields a value smaller than `desired_scale`, which will fit into
480								// R::Inner.
481								.unwrap_or(Bounded::max_value())
482						} else {
483							// defensive only. Both edge and voter loads are built from
484							// scores, hence MUST have the same denominator.
485							Zero::zero()
486						}
487					}
488				};
489				let per_thing = R::from_parts(per_bill_parts);
490				assignment.distribution.push((e.who.clone(), per_thing));
491			}
492		}
493
494		let len = assignment.distribution.len();
495		if len > 0 {
496			// To ensure an assertion indicating: no stake from the voter going to waste,
497			// we add a minimal post-processing to equally assign all of the leftover stake ratios.
498			let vote_count: R::Inner = len.saturated_into();
499			let accuracy = R::ACCURACY;
500			let mut sum: R::Inner = Zero::zero();
501			assignment.distribution.iter().for_each(|a| sum = sum.saturating_add(a.1.deconstruct()));
502
503			let diff = accuracy.saturating_sub(sum);
504			let diff_per_vote = (diff / vote_count).min(accuracy);
505
506			if !diff_per_vote.is_zero() {
507				for i in 0..len {
508					let current_ratio = assignment.distribution[i % len].1;
509					let next_ratio = current_ratio
510						.saturating_add(R::from_parts(diff_per_vote));
511					assignment.distribution[i % len].1 = next_ratio;
512				}
513			}
514
515			// `remainder` is set to be less than maximum votes of a voter (currently 16).
516			// safe to cast it to usize.
517			let remainder = diff - diff_per_vote * vote_count;
518			for i in 0..remainder.saturated_into::<usize>() {
519				let current_ratio = assignment.distribution[i % len].1;
520				let next_ratio = current_ratio.saturating_add(R::from_parts(1u8.into()));
521				assignment.distribution[i % len].1 = next_ratio;
522			}
523			assigned.push(assignment);
524		}
525	}
526
527	Some(PhragmenResult {
528		winners: elected_candidates,
529		assignments: assigned,
530	})
531}
532
533/// Build the support map from the given phragmen result. It maps a flat structure like
534///
535/// ```nocompile
536/// assignments: vec![
537/// 	voter1, vec![(candidate1, w11), (candidate2, w12)],
538/// 	voter2, vec![(candidate1, w21), (candidate2, w22)]
539/// ]
540/// ```
541///
542/// into a mapping of candidates and their respective support:
543///
544/// ```nocompile
545///  SupportMap {
546/// 	candidate1: Support {
547/// 		own:0,
548/// 		total: w11 + w21,
549/// 		others: vec![(candidate1, w11), (candidate2, w21)]
550///		},
551/// 	candidate2: Support {
552/// 		own:0,
553/// 		total: w12 + w22,
554/// 		others: vec![(candidate1, w12), (candidate2, w22)]
555///		},
556/// }
557/// ```
558///
559/// The second returned flag indicates the number of edges who didn't corresponded to an actual
560/// winner from the given winner set. A value in this place larger than 0 indicates a potentially
561/// faulty assignment.
562///
563/// `O(E)` where `E` is the total number of edges.
564pub fn build_support_map<AccountId>(
565	winners: &[AccountId],
566	assignments: &[StakedAssignment<AccountId>],
567) -> (SupportMap<AccountId>, u32) where
568	AccountId: Default + Ord + Clone,
569{
570	let mut errors = 0;
571	// Initialize the support of each candidate.
572	let mut supports = <SupportMap<AccountId>>::new();
573	winners
574		.iter()
575		.for_each(|e| { supports.insert(e.clone(), Default::default()); });
576
577	// build support struct.
578	for StakedAssignment { who, distribution } in assignments.iter() {
579		for (c, weight_extended) in distribution.iter() {
580			if let Some(support) = supports.get_mut(c) {
581				support.total = support.total.saturating_add(*weight_extended);
582				support.voters.push((who.clone(), *weight_extended));
583			} else {
584				errors = errors.saturating_add(1);
585			}
586		}
587	}
588	(supports, errors)
589}
590
591/// Evaluate a phragmen result, given the support map. The returned tuple contains:
592///
593/// - Minimum support. This value must be **maximized**.
594/// - Sum of all supports. This value must be **maximized**.
595/// - Sum of all supports squared. This value must be **minimized**.
596///
597/// `O(E)` where `E` is the total number of edges.
598pub fn evaluate_support<AccountId>(
599	support: &SupportMap<AccountId>,
600) -> PhragmenScore {
601	let mut min_support = ExtendedBalance::max_value();
602	let mut sum: ExtendedBalance = Zero::zero();
603	// NOTE: The third element might saturate but fine for now since this will run on-chain and need
604	// to be fast.
605	let mut sum_squared: ExtendedBalance = Zero::zero();
606	for (_, support) in support.iter() {
607		sum = sum.saturating_add(support.total);
608		let squared = support.total.saturating_mul(support.total);
609		sum_squared = sum_squared.saturating_add(squared);
610		if support.total < min_support {
611			min_support = support.total;
612		}
613	}
614	[min_support, sum, sum_squared]
615}
616
617/// Compares two sets of phragmen scores based on desirability and returns true if `that` is
618/// better than `this`.
619///
620/// Evaluation is done in a lexicographic manner.
621///
622/// Note that the third component should be minimized.
623pub fn is_score_better(this: PhragmenScore, that: PhragmenScore) -> bool {
624	match that
625		.iter()
626		.enumerate()
627		.map(|(i, e)| e.cmp(&this[i]))
628		.collect::<Vec<Ordering>>()
629		.as_slice()
630	{
631		[Ordering::Greater, _, _] => true,
632		[Ordering::Equal, Ordering::Greater, _] => true,
633		[Ordering::Equal, Ordering::Equal, Ordering::Less] => true,
634		_ => false,
635	}
636}
637
638/// Performs equalize post-processing to the output of the election algorithm. This happens in
639/// rounds. The number of rounds and the maximum diff-per-round tolerance can be tuned through input
640/// parameters.
641///
642/// Returns the number of iterations that were preformed.
643///
644/// - `assignments`: exactly the same is the output of phragmen.
645/// - `supports`: mutable reference to s `SupportMap`. This parameter is updated.
646/// - `tolerance`: maximum difference that can occur before an early quite happens.
647/// - `iterations`: maximum number of iterations that will be processed.
648pub fn equalize<AccountId>(
649	assignments: &mut Vec<StakedAssignment<AccountId>>,
650	supports: &mut SupportMap<AccountId>,
651	tolerance: ExtendedBalance,
652	iterations: usize,
653) -> usize where AccountId: Ord + Clone {
654	if iterations == 0 { return 0; }
655
656	let mut i = 0 ;
657	loop {
658		let mut max_diff = 0;
659		for assignment in assignments.iter_mut() {
660			let voter_budget = assignment.total();
661			let StakedAssignment { who, distribution } =  assignment;
662			let diff = do_equalize(
663				who,
664				voter_budget,
665				distribution,
666				supports,
667				tolerance,
668			);
669			if diff > max_diff { max_diff = diff; }
670		}
671
672		i += 1;
673		if max_diff <= tolerance || i >= iterations {
674			break i;
675		}
676	}
677}
678
679/// actually perform equalize. same interface is `equalize`. Just called in loops with a check for
680/// maximum difference.
681fn do_equalize<AccountId>(
682	voter: &AccountId,
683	budget: ExtendedBalance,
684	elected_edges: &mut Vec<(AccountId, ExtendedBalance)>,
685	support_map: &mut SupportMap<AccountId>,
686	tolerance: ExtendedBalance
687) -> ExtendedBalance where AccountId: Ord + Clone {
688	// Nothing to do. This voter had nothing useful.
689	// Defensive only. Assignment list should always be populated. 1 might happen for self vote.
690	if elected_edges.is_empty() || elected_edges.len() == 1 { return 0; }
691
692	let stake_used = elected_edges
693		.iter()
694		.fold(0 as ExtendedBalance, |s, e| s.saturating_add(e.1));
695
696	let backed_stakes_iter = elected_edges
697		.iter()
698		.filter_map(|e| support_map.get(&e.0))
699		.map(|e| e.total);
700
701	let backing_backed_stake = elected_edges
702		.iter()
703		.filter(|e| e.1 > 0)
704		.filter_map(|e| support_map.get(&e.0))
705		.map(|e| e.total)
706		.collect::<Vec<ExtendedBalance>>();
707
708	let mut difference;
709	if backing_backed_stake.len() > 0 {
710		let max_stake = backing_backed_stake
711			.iter()
712			.max()
713			.expect("vector with positive length will have a max; qed");
714		let min_stake = backed_stakes_iter
715			.min()
716			.expect("iterator with positive length will have a min; qed");
717
718		difference = max_stake.saturating_sub(min_stake);
719		difference = difference.saturating_add(budget.saturating_sub(stake_used));
720		if difference < tolerance {
721			return difference;
722		}
723	} else {
724		difference = budget;
725	}
726
727	// Undo updates to support
728	elected_edges.iter_mut().for_each(|e| {
729		if let Some(support) = support_map.get_mut(&e.0) {
730			support.total = support.total.saturating_sub(e.1);
731			support.voters.retain(|i_support| i_support.0 != *voter);
732		}
733		e.1 = 0;
734	});
735
736	elected_edges.sort_unstable_by_key(|e|
737		if let Some(e) = support_map.get(&e.0) { e.total } else { Zero::zero() }
738	);
739
740	let mut cumulative_stake: ExtendedBalance = 0;
741	let mut last_index = elected_edges.len() - 1;
742	let mut idx = 0usize;
743	for e in &mut elected_edges[..] {
744		if let Some(support) = support_map.get_mut(&e.0) {
745			let stake = support.total;
746			let stake_mul = stake.saturating_mul(idx as ExtendedBalance);
747			let stake_sub = stake_mul.saturating_sub(cumulative_stake);
748			if stake_sub > budget {
749				last_index = idx.checked_sub(1).unwrap_or(0);
750				break;
751			}
752			cumulative_stake = cumulative_stake.saturating_add(stake);
753		}
754		idx += 1;
755	}
756
757	let last_stake = elected_edges[last_index].1;
758	let split_ways = last_index + 1;
759	let excess = budget
760		.saturating_add(cumulative_stake)
761		.saturating_sub(last_stake.saturating_mul(split_ways as ExtendedBalance));
762	elected_edges.iter_mut().take(split_ways).for_each(|e| {
763		if let Some(support) = support_map.get_mut(&e.0) {
764			e.1 = (excess / split_ways as ExtendedBalance)
765				.saturating_add(last_stake)
766				.saturating_sub(support.total);
767			support.total = support.total.saturating_add(e.1);
768			support.voters.push((voter.clone(), e.1));
769		}
770	});
771
772	difference
773}