1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
use crate::Tally;
use codec::{Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
use serde::{Deserialize, Serialize};
use sp_runtime::traits::{IntegerSquareRoot, Zero};
use sp_std::ops::{Add, Div, Mul, Rem};
#[derive(
Serialize,
Deserialize,
Clone,
Copy,
PartialEq,
Eq,
Encode,
Decode,
sp_runtime::RuntimeDebug,
TypeInfo,
MaxEncodedLen,
)]
pub enum VoteThreshold {
SuperMajorityApprove,
SuperMajorityAgainst,
SimpleMajority,
}
pub trait Approved<Balance> {
fn approved(&self, tally: Tally<Balance>, electorate: Balance) -> bool;
}
fn compare_rationals<T: Zero + Mul<T, Output = T> + Div<T, Output = T> + Rem<T, Output = T> + Ord + Copy>(
mut n1: T,
mut d1: T,
mut n2: T,
mut d2: T,
) -> bool {
loop {
let q1 = n1 / d1;
let q2 = n2 / d2;
if q1 < q2 {
return true;
}
if q2 < q1 {
return false;
}
let r1 = n1 % d1;
let r2 = n2 % d2;
if r2.is_zero() {
return false;
}
if r1.is_zero() {
return true;
}
n1 = d2;
n2 = d1;
d1 = r2;
d2 = r1;
}
}
impl<
Balance: IntegerSquareRoot
+ Zero
+ Ord
+ Add<Balance, Output = Balance>
+ Mul<Balance, Output = Balance>
+ Div<Balance, Output = Balance>
+ Rem<Balance, Output = Balance>
+ Copy,
> Approved<Balance> for VoteThreshold
{
fn approved(&self, tally: Tally<Balance>, electorate: Balance) -> bool {
let sqrt_voters = tally.turnout.integer_sqrt();
let sqrt_electorate = electorate.max(tally.turnout).integer_sqrt();
if sqrt_voters.is_zero() || sqrt_electorate.is_zero() {
return false;
}
match *self {
VoteThreshold::SuperMajorityApprove => {
compare_rationals(tally.nays, sqrt_voters, tally.ayes, sqrt_electorate)
}
VoteThreshold::SuperMajorityAgainst => {
compare_rationals(tally.nays, sqrt_electorate, tally.ayes, sqrt_voters)
}
VoteThreshold::SimpleMajority => tally.ayes > tally.nays,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn should_work() {
assert!(!VoteThreshold::SuperMajorityApprove.approved(
Tally {
ayes: 60,
nays: 50,
turnout: 110
},
210
));
assert!(VoteThreshold::SuperMajorityApprove.approved(
Tally {
ayes: 100,
nays: 50,
turnout: 150
},
210
));
}
}