1use crate::{
51 node::{Node, NodeId, NodeRef, NodeRole},
52 ExtendedBalance, IdentifierT, StakedAssignment,
53};
54use alloc::{
55 collections::btree_map::{BTreeMap, Entry::*},
56 vec,
57 vec::Vec,
58};
59use sp_arithmetic::traits::{Bounded, Zero};
60
61type Map<A> = BTreeMap<(A, A), A>;
63
64fn combinations_2<T: Clone>(input: &[T]) -> Vec<(T, T)> {
66 let n = input.len();
67 if n < 2 {
68 return Default::default();
69 }
70
71 let mut comb = Vec::with_capacity(n * (n - 1) / 2);
72 for i in 0..n {
73 for j in i + 1..n {
74 comb.push((input[i].clone(), input[j].clone()))
75 }
76 }
77 comb
78}
79
80pub(crate) fn trailing_common<T: Eq>(t1: &[T], t2: &[T]) -> usize {
82 t1.iter().rev().zip(t2.iter().rev()).take_while(|e| e.0 == e.1).count()
83}
84
85fn merge<A: IdentifierT>(voter_root_path: Vec<NodeRef<A>>, target_root_path: Vec<NodeRef<A>>) {
87 let (shorter_path, longer_path) = if voter_root_path.len() <= target_root_path.len() {
88 (voter_root_path, target_root_path)
89 } else {
90 (target_root_path, voter_root_path)
91 };
92
93 shorter_path
96 .iter()
97 .zip(shorter_path.iter().skip(1))
98 .for_each(|(voter, next)| Node::set_parent_of(next, voter));
99 Node::set_parent_of(&shorter_path[0], &longer_path[0]);
100}
101
102fn reduce_4<A: IdentifierT>(assignments: &mut Vec<StakedAssignment<A>>) -> u32 {
111 let mut combination_map: Map<A> = Map::new();
112 let mut num_changed: u32 = Zero::zero();
113
114 for assignment_index in 0..assignments.len() {
117 let who = assignments[assignment_index].who.clone();
118
119 let distribution_ids = &assignments[assignment_index]
121 .distribution
122 .iter()
123 .map(|(t, _p)| t.clone())
124 .collect::<Vec<A>>();
125 let candidate_combinations = combinations_2(distribution_ids);
126
127 for (v1, v2) in candidate_combinations {
128 match combination_map.entry((v1.clone(), v2.clone())) {
129 Vacant(entry) => {
130 entry.insert(who.clone());
131 },
132 Occupied(mut entry) => {
133 let other_who = entry.get_mut();
134
135 if assignments[assignment_index]
141 .distribution
142 .iter()
143 .filter(|(t, _)| *t == v1 || *t == v2)
144 .count() != 2
145 {
146 continue;
147 }
148
149 let maybe_other_assignments = assignments.iter().find(|a| a.who == *other_who);
151 if maybe_other_assignments.is_none() {
152 continue;
153 }
154 let other_assignment =
155 maybe_other_assignments.expect("value is checked to be 'Some'");
156
157 let mut other_cycle_votes =
159 other_assignment
160 .distribution
161 .iter()
162 .filter_map(|(t, w)| {
163 if *t == v1 || *t == v2 {
164 Some((t.clone(), *w))
165 } else {
166 None
167 }
168 })
169 .collect::<Vec<(A, ExtendedBalance)>>();
170
171 let other_votes_count = other_cycle_votes.len();
172
173 debug_assert!(other_votes_count <= 2);
177
178 if other_votes_count < 2 {
179 *other_who = who.clone();
181 continue;
182 } else if other_votes_count == 2 {
183 let mut who_cycle_votes: Vec<(A, ExtendedBalance)> = Vec::with_capacity(2);
185 assignments[assignment_index].distribution.iter().for_each(|(t, w)| {
186 if *t == v1 || *t == v2 {
187 who_cycle_votes.push((t.clone(), *w));
188 }
189 });
190
191 if who_cycle_votes.len() != 2 {
192 continue;
193 }
194
195 if other_cycle_votes[0].0 != who_cycle_votes[0].0 {
197 other_cycle_votes.swap(0, 1);
198 }
199
200 let mut min_value: ExtendedBalance = Bounded::max_value();
202 let mut min_index: usize = 0;
203 let cycle = who_cycle_votes
204 .iter()
205 .chain(other_cycle_votes.iter())
206 .enumerate()
207 .map(|(index, (t, w))| {
208 if *w <= min_value {
209 min_value = *w;
210 min_index = index;
211 }
212 (t.clone(), *w)
213 })
214 .collect::<Vec<(A, ExtendedBalance)>>();
215
216 let mut increase_indices: Vec<usize> = Vec::new();
218 let mut decrease_indices: Vec<usize> = Vec::new();
219 decrease_indices.push(min_index);
220 if min_index < 2 {
221 let sibling_index = 1 - min_index;
224 increase_indices.push(sibling_index);
225 decrease_indices.push(sibling_index + 2);
228 increase_indices.push(min_index + 2);
229 } else {
230 let sibling_index = 3 - min_index % 2;
233 increase_indices.push(sibling_index);
234 decrease_indices.push(sibling_index - 2);
237 increase_indices.push(min_index - 2);
238 }
239
240 let mut remove_indices: Vec<usize> = Vec::with_capacity(1);
242 increase_indices.into_iter().for_each(|i| {
243 let voter = if i < 2 { who.clone() } else { other_who.clone() };
244 assignments.iter_mut().filter(|a| a.who == voter).for_each(|ass| {
248 ass.distribution
249 .iter_mut()
250 .position(|(t, _)| *t == cycle[i].0)
251 .map(|idx| {
252 let next_value =
253 ass.distribution[idx].1.saturating_add(min_value);
254 ass.distribution[idx].1 = next_value;
255 });
256 });
257 });
258 decrease_indices.into_iter().for_each(|i| {
259 let voter = if i < 2 { who.clone() } else { other_who.clone() };
260 assignments.iter_mut().filter(|a| a.who == voter).for_each(|ass| {
261 ass.distribution
262 .iter_mut()
263 .position(|(t, _)| *t == cycle[i].0)
264 .map(|idx| {
265 let next_value =
266 ass.distribution[idx].1.saturating_sub(min_value);
267 if next_value.is_zero() {
268 ass.distribution.remove(idx);
269 remove_indices.push(i);
270 num_changed += 1;
271 } else {
272 ass.distribution[idx].1 = next_value;
273 }
274 });
275 });
276 });
277
278 let who_removed = remove_indices.iter().any(|i| *i < 2usize);
280 let other_removed = remove_indices.into_iter().any(|i| i >= 2usize);
281
282 match (who_removed, other_removed) {
283 (false, true) => {
284 *other_who = who.clone();
285 },
286 (true, false) => {
287 },
289 (true, true) => {
290 entry.remove();
292 },
293 (false, false) => {
294 panic!("Duplicate voter (or other corrupt input).");
296 },
297 }
298 }
299 },
300 }
301 }
302 }
303
304 num_changed
305}
306
307fn reduce_all<A: IdentifierT>(assignments: &mut Vec<StakedAssignment<A>>) -> u32 {
318 let mut num_changed: u32 = Zero::zero();
319 let mut tree: BTreeMap<NodeId<A>, NodeRef<A>> = BTreeMap::new();
320
321 for assignment_index in 0..assignments.len() {
330 let voter = assignments[assignment_index].who.clone();
331
332 let mut dist_index = 0;
333 loop {
334 let maybe_dist = assignments[assignment_index].distribution.get(dist_index);
336 if maybe_dist.is_none() {
337 break;
339 }
340 let (target, _) = maybe_dist.expect("Value checked to be some").clone();
341
342 let voter_id = NodeId::from(voter.clone(), NodeRole::Voter);
344 let target_id = NodeId::from(target.clone(), NodeRole::Target);
345 let voter_exists = tree.contains_key(&voter_id);
346 let target_exists = tree.contains_key(&target_id);
347
348 let voter_node = tree
350 .entry(voter_id.clone())
351 .or_insert_with(|| Node::new(voter_id).into_ref())
352 .clone();
353 let target_node = tree
354 .entry(target_id.clone())
355 .or_insert_with(|| Node::new(target_id).into_ref())
356 .clone();
357
358 match (voter_exists, target_exists) {
362 (false, false) => {
363 Node::set_parent_of(&target_node, &voter_node);
364 dist_index += 1;
365 continue;
366 },
367 (false, true) => {
368 Node::set_parent_of(&voter_node, &target_node);
369 dist_index += 1;
370 continue;
371 },
372 (true, false) => {
373 Node::set_parent_of(&target_node, &voter_node);
374 dist_index += 1;
375 continue;
376 },
377 (true, true) => { },
378 };
379
380 let (voter_root, voter_root_path) = Node::root(&voter_node);
381 let (target_root, target_root_path) = Node::root(&target_node);
382
383 if voter_root != target_root {
384 merge(voter_root_path, target_root_path);
386 dist_index += 1;
387 } else {
388 let common_count = trailing_common(&voter_root_path, &target_root_path);
390
391 debug_assert!(common_count > 0);
396 debug_assert!(common_count <= voter_root_path.len());
398 debug_assert!(common_count <= target_root_path.len());
399
400 let cycle = target_root_path
404 .iter()
405 .take(target_root_path.len().saturating_sub(common_count).saturating_add(1))
406 .cloned()
407 .chain(
408 voter_root_path
409 .iter()
410 .take(voter_root_path.len().saturating_sub(common_count))
411 .rev()
412 .cloned(),
413 )
414 .collect::<Vec<NodeRef<A>>>();
415
416 debug_assert_eq!(cycle.len() % 2, 0);
418
419 let mut min_value: ExtendedBalance = Bounded::max_value();
421 let mut maybe_min_target: Option<A> = None;
424 let mut maybe_min_voter: Option<A> = None;
425 let mut maybe_min_index: Option<usize> = None;
427 let mut maybe_min_direction: Option<u32> = None;
429
430 let next_index = |i| {
432 if i < (cycle.len() - 1) {
433 i + 1
434 } else {
435 0
436 }
437 };
438 let prev_index = |i| {
439 if i > 0 {
440 i - 1
441 } else {
442 cycle.len() - 1
443 }
444 };
445
446 for i in 0..cycle.len() {
447 if cycle[i].borrow().id.role == NodeRole::Voter {
448 let current = cycle[i].borrow().id.who.clone();
450 let next = cycle[next_index(i)].borrow().id.who.clone();
451 let prev = cycle[prev_index(i)].borrow().id.who.clone();
452 assignments.iter().find(|a| a.who == current).map(|ass| {
453 ass.distribution.iter().find(|d| d.0 == next).map(|(_, w)| {
454 if *w < min_value {
455 min_value = *w;
456 maybe_min_target = Some(next.clone());
457 maybe_min_voter = Some(current.clone());
458 maybe_min_index = Some(i);
459 maybe_min_direction = Some(1);
460 }
461 })
462 });
463 assignments.iter().find(|a| a.who == current).map(|ass| {
464 ass.distribution.iter().find(|d| d.0 == prev).map(|(_, w)| {
465 if *w < min_value {
466 min_value = *w;
467 maybe_min_target = Some(prev.clone());
468 maybe_min_voter = Some(current.clone());
469 maybe_min_index = Some(i);
470 maybe_min_direction = Some(0);
471 }
472 })
473 });
474 }
475 }
476
477 let (min_value, min_target, min_voter, min_index, min_direction) = match (
480 min_value,
481 maybe_min_target,
482 maybe_min_voter,
483 maybe_min_index,
484 maybe_min_direction,
485 ) {
486 (
487 min_value,
488 Some(min_target),
489 Some(min_voter),
490 Some(min_index),
491 Some(min_direction),
492 ) => (min_value, min_target, min_voter, min_index, min_direction),
493 _ => {
494 log::error!(
495 target: "runtime::npos-elections",
496 "UNREACHABLE code reached in `reduce` algorithm. This must be a bug."
497 );
498 break;
499 },
500 };
501
502 let target_chunk = target_root_path.len() - common_count;
505 let min_chain_in_voter = (min_index + min_direction as usize) > target_chunk;
506
507 let mut should_inc_counter = true;
509 let start_operation_add = ((min_index % 2) + min_direction as usize) % 2 == 1;
510 let mut additional_removed = Vec::new();
511 for i in 0..cycle.len() {
512 let current = cycle[i].borrow();
513 if current.id.role == NodeRole::Voter {
514 let prev = cycle[prev_index(i)].borrow();
515 assignments
516 .iter_mut()
517 .enumerate()
518 .filter(|(_, a)| a.who == current.id.who)
519 .for_each(|(target_ass_index, ass)| {
520 ass.distribution
521 .iter_mut()
522 .position(|(t, _)| *t == prev.id.who)
523 .map(|idx| {
524 let next_value = if i % 2 == 0 {
525 if start_operation_add {
526 ass.distribution[idx].1.saturating_add(min_value)
527 } else {
528 ass.distribution[idx].1.saturating_sub(min_value)
529 }
530 } else if start_operation_add {
531 ass.distribution[idx].1.saturating_sub(min_value)
532 } else {
533 ass.distribution[idx].1.saturating_add(min_value)
534 };
535
536 if next_value.is_zero() {
537 if target_ass_index == assignment_index {
540 should_inc_counter = false
541 }
542 ass.distribution.remove(idx);
543 num_changed += 1;
544 if !(i == min_index && min_direction == 0) {
546 additional_removed.push((
547 cycle[i].clone(),
548 cycle[prev_index(i)].clone(),
549 ));
550 }
551 } else {
552 ass.distribution[idx].1 = next_value;
553 }
554 });
555 });
556
557 let next = cycle[next_index(i)].borrow();
558 assignments
559 .iter_mut()
560 .enumerate()
561 .filter(|(_, a)| a.who == current.id.who)
562 .for_each(|(target_ass_index, ass)| {
563 ass.distribution
564 .iter_mut()
565 .position(|(t, _)| *t == next.id.who)
566 .map(|idx| {
567 let next_value = if i % 2 == 0 {
568 if start_operation_add {
569 ass.distribution[idx].1.saturating_sub(min_value)
570 } else {
571 ass.distribution[idx].1.saturating_add(min_value)
572 }
573 } else if start_operation_add {
574 ass.distribution[idx].1.saturating_add(min_value)
575 } else {
576 ass.distribution[idx].1.saturating_sub(min_value)
577 };
578
579 if next_value.is_zero() {
580 if target_ass_index == assignment_index {
583 should_inc_counter = false
584 }
585 ass.distribution.remove(idx);
586 num_changed += 1;
587 if !(i == min_index && min_direction == 1) {
588 additional_removed.push((
589 cycle[i].clone(),
590 cycle[next_index(i)].clone(),
591 ));
592 }
593 } else {
594 ass.distribution[idx].1 = next_value;
595 }
596 });
597 });
598 }
599 }
600
601 let should_reorg = !(min_index == (cycle.len() - 1) && min_direction == 1);
604
605 if should_reorg {
607 let min_edge = vec![min_voter, min_target];
608 if min_chain_in_voter {
609 for i in 0..voter_root_path.len() - 1 {
611 let current = voter_root_path[i].clone().borrow().id.who.clone();
612 let next = voter_root_path[i + 1].clone().borrow().id.who.clone();
613 if min_edge.contains(¤t) && min_edge.contains(&next) {
614 break;
615 }
616 Node::set_parent_of(&voter_root_path[i + 1], &voter_root_path[i]);
617 }
618 Node::set_parent_of(&voter_node, &target_node);
619 } else {
620 for i in 0..target_root_path.len() - 1 {
622 let current = target_root_path[i].clone().borrow().id.who.clone();
623 let next = target_root_path[i + 1].clone().borrow().id.who.clone();
624 if min_edge.contains(¤t) && min_edge.contains(&next) {
625 break;
626 }
627 Node::set_parent_of(&target_root_path[i + 1], &target_root_path[i]);
628 }
629 Node::set_parent_of(&target_node, &voter_node);
630 }
631 }
632
633 for (r1, r2) in additional_removed {
635 if Node::is_parent_of(&r1, &r2) {
636 Node::remove_parent(&r1);
637 } else if Node::is_parent_of(&r2, &r1) {
638 Node::remove_parent(&r2);
639 }
640 }
641
642 if should_inc_counter {
644 dist_index += 1;
645 }
646 }
647 }
648 }
649
650 num_changed
651}
652
653pub fn reduce<A: IdentifierT>(assignments: &mut Vec<StakedAssignment<A>>) -> u32 where {
664 let mut num_changed = reduce_4(assignments);
665 num_changed += reduce_all(assignments);
666 num_changed
667}
668
669#[cfg(test)]
670mod tests {
671 use super::*;
672
673 #[test]
674 fn merging_works() {
675 let d = Node::new(NodeId::from(1, NodeRole::Target)).into_ref();
679 let a = Node::new(NodeId::from(2, NodeRole::Target)).into_ref();
680 let b = Node::new(NodeId::from(3, NodeRole::Target)).into_ref();
681 let c = Node::new(NodeId::from(4, NodeRole::Target)).into_ref();
682 let e = Node::new(NodeId::from(5, NodeRole::Target)).into_ref();
683 let f = Node::new(NodeId::from(6, NodeRole::Target)).into_ref();
684
685 Node::set_parent_of(&c, &b);
686 Node::set_parent_of(&b, &a);
687 Node::set_parent_of(&a, &d);
688 Node::set_parent_of(&e, &f);
689
690 let path1 = vec![c.clone(), b.clone(), a.clone(), d.clone()];
691 let path2 = vec![e.clone(), f.clone()];
692
693 merge(path1, path2);
694 assert_eq!(e.borrow().clone().parent.unwrap().borrow().id.who, 4u32); }
699
700 #[test]
701 fn merge_with_len_one() {
702 let d = Node::new(NodeId::from(1, NodeRole::Target)).into_ref();
706 let a = Node::new(NodeId::from(2, NodeRole::Target)).into_ref();
707 let b = Node::new(NodeId::from(3, NodeRole::Target)).into_ref();
708 let c = Node::new(NodeId::from(4, NodeRole::Target)).into_ref();
709 let f = Node::new(NodeId::from(6, NodeRole::Target)).into_ref();
710
711 Node::set_parent_of(&c, &b);
712 Node::set_parent_of(&b, &a);
713 Node::set_parent_of(&a, &d);
714
715 let path1 = vec![c.clone(), b.clone(), a.clone(), d.clone()];
716 let path2 = vec![f.clone()];
717
718 merge(path1, path2);
719 assert_eq!(f.borrow().clone().parent.unwrap().borrow().id.who, 4u32); }
724
725 #[test]
726 fn basic_reduce_4_cycle_works() {
727 use super::*;
728
729 let assignments = vec![
730 StakedAssignment { who: 1, distribution: vec![(10, 25), (20, 75)] },
731 StakedAssignment { who: 2, distribution: vec![(10, 50), (20, 50)] },
732 ];
733
734 let mut new_assignments = assignments.clone();
735 let num_reduced = reduce_4(&mut new_assignments);
736
737 assert_eq!(num_reduced, 1);
738 assert_eq!(
739 new_assignments,
740 vec![
741 StakedAssignment { who: 1, distribution: vec![(20, 100),] },
742 StakedAssignment { who: 2, distribution: vec![(10, 75), (20, 25),] },
743 ],
744 );
745 }
746
747 #[test]
748 fn basic_reduce_all_cycles_works() {
749 let mut assignments = vec![
750 StakedAssignment { who: 1, distribution: vec![(10, 10)] },
751 StakedAssignment { who: 2, distribution: vec![(10, 15), (20, 5)] },
752 StakedAssignment { who: 3, distribution: vec![(20, 15), (40, 15)] },
753 StakedAssignment { who: 4, distribution: vec![(20, 10), (30, 10), (40, 20)] },
754 StakedAssignment { who: 5, distribution: vec![(20, 20), (30, 10), (40, 20)] },
755 ];
756
757 assert_eq!(3, reduce_all(&mut assignments));
758
759 assert_eq!(
760 assignments,
761 vec![
762 StakedAssignment { who: 1, distribution: vec![(10, 10),] },
763 StakedAssignment { who: 2, distribution: vec![(10, 15), (20, 5),] },
764 StakedAssignment { who: 3, distribution: vec![(20, 30),] },
765 StakedAssignment { who: 4, distribution: vec![(40, 40),] },
766 StakedAssignment { who: 5, distribution: vec![(20, 15), (30, 20), (40, 15),] },
767 ],
768 )
769 }
770
771 #[test]
772 fn basic_reduce_works() {
773 let mut assignments = vec![
774 StakedAssignment { who: 1, distribution: vec![(10, 10)] },
775 StakedAssignment { who: 2, distribution: vec![(10, 15), (20, 5)] },
776 StakedAssignment { who: 3, distribution: vec![(20, 15), (40, 15)] },
777 StakedAssignment { who: 4, distribution: vec![(20, 10), (30, 10), (40, 20)] },
778 StakedAssignment { who: 5, distribution: vec![(20, 20), (30, 10), (40, 20)] },
779 ];
780
781 assert_eq!(3, reduce(&mut assignments));
782
783 assert_eq!(
784 assignments,
785 vec![
786 StakedAssignment { who: 1, distribution: vec![(10, 10),] },
787 StakedAssignment { who: 2, distribution: vec![(10, 15), (20, 5),] },
788 StakedAssignment { who: 3, distribution: vec![(20, 30),] },
789 StakedAssignment { who: 4, distribution: vec![(40, 40),] },
790 StakedAssignment { who: 5, distribution: vec![(20, 15), (30, 20), (40, 15),] },
791 ],
792 )
793 }
794
795 #[test]
796 fn should_deal_with_self_vote() {
797 let mut assignments = vec![
798 StakedAssignment { who: 1, distribution: vec![(10, 10)] },
799 StakedAssignment { who: 2, distribution: vec![(10, 15), (20, 5)] },
800 StakedAssignment { who: 3, distribution: vec![(20, 15), (40, 15)] },
801 StakedAssignment { who: 4, distribution: vec![(20, 10), (30, 10), (40, 20)] },
802 StakedAssignment { who: 5, distribution: vec![(20, 20), (30, 10), (40, 20)] },
803 StakedAssignment { who: 10, distribution: vec![(10, 100)] },
805 StakedAssignment { who: 20, distribution: vec![(20, 200)] },
806 ];
807
808 assert_eq!(3, reduce(&mut assignments));
809
810 assert_eq!(
811 assignments,
812 vec![
813 StakedAssignment { who: 1, distribution: vec![(10, 10),] },
814 StakedAssignment { who: 2, distribution: vec![(10, 15), (20, 5),] },
815 StakedAssignment { who: 3, distribution: vec![(20, 30),] },
816 StakedAssignment { who: 4, distribution: vec![(40, 40),] },
817 StakedAssignment { who: 5, distribution: vec![(20, 15), (30, 20), (40, 15),] },
818 StakedAssignment { who: 10, distribution: vec![(10, 100)] },
820 StakedAssignment { who: 20, distribution: vec![(20, 200)] },
821 ],
822 )
823 }
824
825 #[test]
826 fn reduce_3_common_votes_same_weight() {
827 let mut assignments = vec![
828 StakedAssignment {
829 who: 4,
830 distribution: vec![(1000000, 100), (1000002, 100), (1000004, 100)],
831 },
832 StakedAssignment {
833 who: 5,
834 distribution: vec![(1000000, 100), (1000002, 100), (1000004, 100)],
835 },
836 ];
837
838 reduce_4(&mut assignments);
839
840 assert_eq!(
841 assignments,
842 vec![
843 StakedAssignment { who: 4, distribution: vec![(1000000, 200,), (1000004, 100,),] },
844 StakedAssignment { who: 5, distribution: vec![(1000002, 200,), (1000004, 100,),] },
845 ],
846 )
847 }
848
849 #[test]
850 #[should_panic]
851 fn reduce_panics_on_duplicate_voter() {
852 let mut assignments = vec![
853 StakedAssignment { who: 1, distribution: vec![(10, 10), (20, 10)] },
854 StakedAssignment { who: 1, distribution: vec![(10, 15), (20, 5)] },
855 StakedAssignment { who: 2, distribution: vec![(10, 15), (20, 15)] },
856 ];
857
858 reduce(&mut assignments);
859 }
860
861 #[test]
862 fn should_deal_with_duplicates_target() {
863 let mut assignments = vec![
864 StakedAssignment { who: 1, distribution: vec![(10, 15), (20, 5)] },
865 StakedAssignment {
866 who: 2,
867 distribution: vec![
868 (10, 15),
869 (20, 15),
870 (10, 1),
872 (20, 1),
874 ],
875 },
876 ];
877
878 reduce(&mut assignments);
879
880 assert_eq!(
881 assignments,
882 vec![
883 StakedAssignment { who: 1, distribution: vec![(10, 20),] },
884 StakedAssignment {
885 who: 2,
886 distribution: vec![
887 (10, 10),
888 (20, 20),
889 (10, 1),
891 (20, 1),
892 ],
893 },
894 ],
895 )
896 }
897
898 #[test]
899 fn bound_should_be_kept() {
900 let mut assignments = vec![
901 StakedAssignment {
902 who: 1,
903 distribution: vec![(103, 72), (101, 53), (100, 83), (102, 38)],
904 },
905 StakedAssignment {
906 who: 2,
907 distribution: vec![(103, 18), (101, 36), (102, 54), (100, 94)],
908 },
909 StakedAssignment {
910 who: 3,
911 distribution: vec![(100, 96), (101, 35), (102, 52), (103, 69)],
912 },
913 StakedAssignment {
914 who: 4,
915 distribution: vec![(102, 34), (100, 47), (103, 91), (101, 73)],
916 },
917 ];
918
919 let winners = vec![103, 101, 100, 102];
920
921 let n = 4;
922 let m = winners.len() as u32;
923 let num_reduced = reduce_all(&mut assignments);
924 assert!(16 - num_reduced <= n + m);
925 }
926}