Skip to content

Commit

Permalink
Merge branch 'feat/cutoff'
Browse files Browse the repository at this point in the history
  • Loading branch information
edugzlez committed Sep 14, 2024
2 parents 0e670cc + f76d2e4 commit 8b522b1
Show file tree
Hide file tree
Showing 5 changed files with 213 additions and 55 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
/target
lcov.info
35 changes: 17 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,28 +21,27 @@ cargo add electosim

## Usage

### With SimpleElection
### Using macro `election!`

```rust
use electosim::methods::Method;
use electosim::models::Candidacy;

use electosim::SimpleElection;
use electosim::{ methods::Method, models::Candidacy, electosim::election };

fn main() {
let mut election = SimpleElection {
results: vec![
Candidacy::new(2010, 9),
Candidacy::new(1018, 4),
Candidacy::new(86, 0),
Candidacy::new(77, 0),
],
seats: 13,
method: Method::HAGENBASCHBISCHOFF,
};

election.compute().expect("Can not compute method");
election.results.iter().for_each(|c| println!("{:?}", c));
let candidacies = vec![
Candidacy::new(2010, 9),
Candidacy::new(1018, 4),
Candidacy::new(86, 0),
Candidacy::new(77, 0),
];

let seats = 13;
let method = Method::HAGENBASCHBISCHOFF;
let cutoff = 0.1;

let mut ele = election!(candidacies, seats, method, cutoff);

ele.compute().expect("Can not compute method");
ele.results.iter().for_each(|c| println!("{:?}", c));
}
```

Expand Down
55 changes: 55 additions & 0 deletions src/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,58 @@ pub trait WithSeats {
self.set_seats(actual_seats + n);
}
}

impl<T> WithVotes for Box<&mut T>
where
T: WithVotes,
{
fn get_votes(&self) -> u32 {
(**self).get_votes()
}

fn set_votes(&mut self, votes: u32) {
(**self).set_votes(votes);
}
}

impl<T> WithSeats for Box<&mut T>
where
T: WithSeats,
{
fn get_seats(&self) -> u16 {
(**self).get_seats()
}

fn set_seats(&mut self, seats: u16) {
(**self).set_seats(seats);
}
}

#[cfg(test)]
mod tests {
use super::*;

struct Candidate {
votes: u32,
}

impl WithVotes for Candidate {
fn get_votes(&self) -> u32 {
self.votes
}

fn set_votes(&mut self, votes: u32) {
self.votes = votes;
}
}

#[test]
fn test_with_votes() {
let mut candidate = Candidate { votes: 1000 };
let mut boxed_candidate = Box::new(&mut candidate);

assert_eq!(boxed_candidate.get_votes(), 1000);
boxed_candidate.set_votes(2000);
assert_eq!(candidate.get_votes(), 2000);
}
}
125 changes: 112 additions & 13 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,19 @@
//! ```rust
//! use electosim::methods::Method;
//! use electosim::models::Candidacy;
//!
//! use electosim::SimpleElection;
//! use electosim::election;
//!
//! fn main() {
//! let mut election = SimpleElection {
//! results: vec![
//! let mut election = election!(
//! vec![
//! Candidacy::new(2010, 9),
//! Candidacy::new(1018, 4),
//! Candidacy::new(86, 0),
//! Candidacy::new(77, 0),
//! ],
//! seats: 13,
//! method: Method::HAGENBASCHBISCHOFF,
//! };
//! 13,
//! Method::HAGENBASCHBISCHOFF
//! );
//!
//! election.compute().expect("Can not compute method");
//! election.results.iter().for_each(|c| println!("{:?}", c));
Expand Down Expand Up @@ -69,8 +68,10 @@ pub mod methods;
pub mod models;
pub mod utils;

use interface::WithVotes;
use methods::{get_method_function, Method};
use models::Candidacy;
use utils::clear_results;

/// Represents a simple election.
pub struct SimpleElection {
Expand All @@ -80,9 +81,25 @@ pub struct SimpleElection {
pub seats: u16,
/// The method used for the election.
pub method: Method,
/// Electoral cutoff
pub cutoff: f32,
}

impl SimpleElection {
/// Creates a new `SimpleElection` struct.
pub fn new(results: Vec<Candidacy>, seats: u16, method: Method) -> Self {
SimpleElection {
results,
seats,
method,
cutoff: 0.0,
}
}

pub fn total_votes(&self) -> u32 {
self.results.iter().map(|c| c.get_votes()).sum()
}

/// Computes the election results using the specified method.
///
/// # Arguments
Expand All @@ -94,29 +111,111 @@ impl SimpleElection {
/// Returns `Ok(())` if the computation is successful, otherwise returns an `Err` with an error message.
pub fn compute(&mut self) -> Result<(), &str> {
let fun = get_method_function(self.method);
fun(&mut self.results, self.seats)
let total_votes = self.total_votes() as f32;
let cutoff_votes = (total_votes * self.cutoff) as u32;
// compute_with_cutoff(fun, &mut self.results, self.seats, cutoff_votes)
clear_results(self.results.as_mut());

let mut filtered_results = self
.results
.iter_mut()
.filter(|c| c.get_votes() > cutoff_votes)
.map(|c| Box::new(c))
.collect::<Vec<_>>();

fun(&mut filtered_results, self.seats).unwrap();

Ok(())
}
}

#[macro_export]
macro_rules! election {
($results:expr) => {
SimpleElection {
results: $results,
seats: 0,
method: Method::DHONDT,
cutoff: 0.0,
}
};
($results:expr, $seats:expr) => {
SimpleElection {
results: $results,
seats: $seats,
method: Method::DHONDT,
cutoff: 0.0,
}
};
($results:expr, $seats:expr, $method:expr) => {
SimpleElection {
results: $results,
seats: $seats,
method: $method,
cutoff: 0.0,
}
};
($results:expr, $seats:expr, $method:expr, $cutoff:expr) => {
SimpleElection {
results: $results,
seats: $seats,
method: $method,
cutoff: $cutoff,
}
};
}

#[cfg(test)]
mod tests {
use interface::WithSeats;

use super::*;
use crate::models::Candidacy;

#[test]
fn test_simple_election() {
let mut election = SimpleElection {
results: vec![
let mut election = election!(
vec![
Candidacy::new(2010, 9),
Candidacy::new(1018, 4),
Candidacy::new(86, 0),
Candidacy::new(77, 0),
],
seats: 13,
method: Method::DHONDT,
};
13,
Method::DHONDT
);

election.compute().unwrap();
election.results.iter().for_each(|c| println!("{:?}", c));
}

#[test]
fn test_load_new_election() {
let _ = SimpleElection::new(
vec![
Candidacy::new(2010, 9),
Candidacy::new(1018, 4),
Candidacy::new(86, 0),
Candidacy::new(77, 0),
],
13,
Method::DHONDT,
);
}

#[test]
fn test_with_cutoff() {
let mut res = election!(
vec![Candidacy::new(10, 0), Candidacy::new(1, 0),],
13,
Method::DHONDT,
0.1
);

res.compute().unwrap();

assert_eq!(res.results.len(), 2);
assert_eq!(res.results[0].get_seats(), 13);
assert_eq!(res.results[1].get_seats(), 0);
}
}
52 changes: 28 additions & 24 deletions src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,32 +89,36 @@ where
results.iter().map(|x| x.get_seats()).sum()
}

#[test]
fn listing_candidacy() {
let candidacies = vec![
crate::models::Candidacy::new(2010, 9),
crate::models::Candidacy::new(1018, 4),
crate::models::Candidacy::new(86, 0),
crate::models::Candidacy::new(77, 0),
];
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn listing_candidacy() {
let candidacies = vec![
crate::models::Candidacy::new(2010, 9),
crate::models::Candidacy::new(1018, 4),
crate::models::Candidacy::new(86, 0),
crate::models::Candidacy::new(77, 0),
];

assert_eq!(compute_total_votes(&candidacies), 3191);
assert_eq!(compute_total_seats(&candidacies), 13);
}
assert_eq!(compute_total_votes(&candidacies), 3191);
assert_eq!(compute_total_seats(&candidacies), 13);
}

#[test]
fn clearing_results() {
let mut candidacies = vec![
crate::models::Candidacy::new(2010, 9),
crate::models::Candidacy::new(1018, 4),
crate::models::Candidacy::new(86, 0),
crate::models::Candidacy::new(77, 0),
];
#[test]
fn clearing_results() {
let mut candidacies = vec![
crate::models::Candidacy::new(2010, 9),
crate::models::Candidacy::new(1018, 4),
crate::models::Candidacy::new(86, 0),
crate::models::Candidacy::new(77, 0),
];

clear_results(&mut candidacies);
clear_results(&mut candidacies);

assert_eq!(candidacies[0].get_seats(), 0);
assert_eq!(candidacies[1].get_seats(), 0);
assert_eq!(candidacies[2].get_seats(), 0);
assert_eq!(candidacies[3].get_seats(), 0);
assert_eq!(candidacies[0].get_seats(), 0);
assert_eq!(candidacies[1].get_seats(), 0);
assert_eq!(candidacies[2].get_seats(), 0);
assert_eq!(candidacies[3].get_seats(), 0);
}
}

0 comments on commit 8b522b1

Please sign in to comment.