Decouple Stkaing and Election - Part1: Support traits (#7908)

* Base features and traits.

* Fix the build

* Remove unused boxing

* Self review cleanup

* Fix build
This commit is contained in:
Kian Paimani
2021-01-18 10:24:12 +00:00
committed by GitHub
parent c58a2d9a74
commit ced107b355
23 changed files with 925 additions and 341 deletions
@@ -21,7 +21,7 @@ use crate::field_name_for;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
fn from_impl(count: usize) -> TokenStream2 {
pub(crate) fn from_impl(count: usize) -> TokenStream2 {
let from_impl_single = {
let name = field_name_for(1);
quote!(1 => compact.#name.push(
@@ -73,7 +73,7 @@ fn from_impl(count: usize) -> TokenStream2 {
)
}
fn into_impl(count: usize, per_thing: syn::Type) -> TokenStream2 {
pub(crate) fn into_impl(count: usize, per_thing: syn::Type) -> TokenStream2 {
let into_impl_single = {
let name = field_name_for(1);
quote!(
@@ -153,53 +153,3 @@ fn into_impl(count: usize, per_thing: syn::Type) -> TokenStream2 {
#into_impl_rest
)
}
pub(crate) fn assignment(
ident: syn::Ident,
voter_type: syn::Type,
target_type: syn::Type,
weight_type: syn::Type,
count: usize,
) -> TokenStream2 {
let from_impl = from_impl(count);
let into_impl = into_impl(count, weight_type.clone());
quote!(
use _npos::__OrInvalidIndex;
impl #ident {
pub fn from_assignment<FV, FT, A>(
assignments: Vec<_npos::Assignment<A, #weight_type>>,
index_of_voter: FV,
index_of_target: FT,
) -> Result<Self, _npos::Error>
where
A: _npos::IdentifierT,
for<'r> FV: Fn(&'r A) -> Option<#voter_type>,
for<'r> FT: Fn(&'r A) -> Option<#target_type>,
{
let mut compact: #ident = Default::default();
for _npos::Assignment { who, distribution } in assignments {
match distribution.len() {
0 => continue,
#from_impl
_ => {
return Err(_npos::Error::CompactTargetOverflow);
}
}
};
Ok(compact)
}
pub fn into_assignment<A: _npos::IdentifierT>(
self,
voter_at: impl Fn(#voter_type) -> Option<A>,
target_at: impl Fn(#target_type) -> Option<A>,
) -> Result<Vec<_npos::Assignment<A, #weight_type>>, _npos::Error> {
let mut assignments: Vec<_npos::Assignment<A, #weight_type>> = Default::default();
#into_impl
Ok(assignments)
}
}
)
}
@@ -95,19 +95,11 @@ pub fn generate_solution_type(item: TokenStream) -> TokenStream {
compact_encoding,
).unwrap_or_else(|e| e.to_compile_error());
let assignment_impls = assignment::assignment(
ident.clone(),
voter_type.clone(),
target_type.clone(),
weight_type.clone(),
count,
);
quote!(
#imports
#solution_struct
#assignment_impls
).into()
)
.into()
}
fn struct_def(
@@ -125,29 +117,32 @@ fn struct_def(
let singles = {
let name = field_name_for(1);
// NOTE: we use the visibility of the struct for the fields as well.. could be made better.
quote!(
#name: Vec<(#voter_type, #target_type)>,
#vis #name: Vec<(#voter_type, #target_type)>,
)
};
let doubles = {
let name = field_name_for(2);
quote!(
#name: Vec<(#voter_type, (#target_type, #weight_type), #target_type)>,
#vis #name: Vec<(#voter_type, (#target_type, #weight_type), #target_type)>,
)
};
let rest = (3..=count).map(|c| {
let field_name = field_name_for(c);
let array_len = c - 1;
quote!(
#field_name: Vec<(
#voter_type,
[(#target_type, #weight_type); #array_len],
#target_type
)>,
)
}).collect::<TokenStream2>();
let rest = (3..=count)
.map(|c| {
let field_name = field_name_for(c);
let array_len = c - 1;
quote!(
#vis #field_name: Vec<(
#voter_type,
[(#target_type, #weight_type); #array_len],
#target_type
)>,
)
})
.collect::<TokenStream2>();
let len_impl = len_impl(count);
let edge_count_impl = edge_count_impl(count);
@@ -172,40 +167,38 @@ fn struct_def(
quote!(#[derive(Default, PartialEq, Eq, Clone, Debug, _npos::codec::Encode, _npos::codec::Decode)])
};
let from_impl = assignment::from_impl(count);
let into_impl = assignment::into_impl(count, weight_type.clone());
Ok(quote! (
/// A struct to encode a election assignment in a compact way.
#derives_and_maybe_compact_encoding
#vis struct #ident { #singles #doubles #rest }
impl _npos::VotingLimit for #ident {
use _npos::__OrInvalidIndex;
impl _npos::CompactSolution for #ident {
const LIMIT: usize = #count;
}
type Voter = #voter_type;
type Target = #target_type;
type Accuracy = #weight_type;
impl #ident {
/// Get the length of all the assignments that this type is encoding. This is basically
/// the same as the number of assignments, or the number of voters in total.
pub fn len(&self) -> usize {
fn voter_count(&self) -> usize {
let mut all_len = 0usize;
#len_impl
all_len
}
/// Get the total count of edges.
pub fn edge_count(&self) -> usize {
fn edge_count(&self) -> usize {
let mut all_edges = 0usize;
#edge_count_impl
all_edges
}
/// Get the number of unique targets in the whole struct.
///
/// Once presented with a list of winners, this set and the set of winners must be
/// equal.
///
/// The resulting indices are sorted.
pub fn unique_targets(&self) -> Vec<#target_type> {
let mut all_targets: Vec<#target_type> = Vec::with_capacity(self.average_edge_count());
let mut maybe_insert_target = |t: #target_type| {
fn unique_targets(&self) -> Vec<Self::Target> {
// NOTE: this implementation returns the targets sorted, but we don't use it yet per
// se, nor is the API enforcing it.
let mut all_targets: Vec<Self::Target> = Vec::with_capacity(self.average_edge_count());
let mut maybe_insert_target = |t: Self::Target| {
match all_targets.binary_search(&t) {
Ok(_) => (),
Err(pos) => all_targets.insert(pos, t)
@@ -217,22 +210,44 @@ fn struct_def(
all_targets
}
/// Get the average edge count.
pub fn average_edge_count(&self) -> usize {
self.edge_count().checked_div(self.len()).unwrap_or(0)
}
/// Remove a certain voter.
///
/// This will only search until the first instance of `to_remove`, and return true. If
/// no instance is found (no-op), then it returns false.
///
/// In other words, if this return true, exactly one element must have been removed from
/// `self.len()`.
pub fn remove_voter(&mut self, to_remove: #voter_type) -> bool {
fn remove_voter(&mut self, to_remove: Self::Voter) -> bool {
#remove_voter_impl
return false
}
fn from_assignment<FV, FT, A>(
assignments: Vec<_npos::Assignment<A, #weight_type>>,
index_of_voter: FV,
index_of_target: FT,
) -> Result<Self, _npos::Error>
where
A: _npos::IdentifierT,
for<'r> FV: Fn(&'r A) -> Option<Self::Voter>,
for<'r> FT: Fn(&'r A) -> Option<Self::Target>,
{
let mut compact: #ident = Default::default();
for _npos::Assignment { who, distribution } in assignments {
match distribution.len() {
0 => continue,
#from_impl
_ => {
return Err(_npos::Error::CompactTargetOverflow);
}
}
};
Ok(compact)
}
fn into_assignment<A: _npos::IdentifierT>(
self,
voter_at: impl Fn(Self::Voter) -> Option<A>,
target_at: impl Fn(Self::Target) -> Option<A>,
) -> Result<Vec<_npos::Assignment<A, #weight_type>>, _npos::Error> {
let mut assignments: Vec<_npos::Assignment<A, #weight_type>> = Default::default();
#into_impl
Ok(assignments)
}
}
))
}