Implement --numbers fixed

This commit is contained in:
RunasSudo 2021-06-04 22:05:48 +10:00
parent 441e266b17
commit ceba059c53
No known key found for this signature in database
GPG Key ID: 7234E476BF21C61A
12 changed files with 472 additions and 106 deletions

60
Cargo.lock generated
View File

@ -96,6 +96,18 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "const_fn_assert"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27d614f23f34f7b5165a77dc1591f497e2518f9cec4b4f4b92bfc4dc6cf7a190"
[[package]]
name = "convert_case"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
[[package]] [[package]]
name = "crc32fast" name = "crc32fast"
version = "1.2.1" version = "1.2.1"
@ -127,6 +139,18 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "derive_more"
version = "0.99.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cc7b9cef1e351660e5443924e4f43ab25fbbed3e9a5f052df3677deb4d6b320"
dependencies = [
"convert_case",
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "flate2" name = "flate2"
version = "1.0.20" version = "1.0.20"
@ -186,6 +210,19 @@ dependencies = [
"unicode-segmentation", "unicode-segmentation",
] ]
[[package]]
name = "ibig"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61c5022ee7f7a2feb0bd2fdc4b8ec882cd14903cebf33e7c1847e3f3a282f8b7"
dependencies = [
"cfg-if 1.0.0",
"const_fn_assert",
"num-traits",
"rand",
"static_assertions",
]
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "1.6.2" version = "1.6.2"
@ -297,8 +334,10 @@ dependencies = [
"clap", "clap",
"console_error_panic_hook", "console_error_panic_hook",
"csv", "csv",
"derive_more",
"flate2", "flate2",
"git-version", "git-version",
"ibig",
"js-sys", "js-sys",
"num-bigint", "num-bigint",
"num-rational", "num-rational",
@ -368,6 +407,21 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "rand"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
dependencies = [
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7"
[[package]] [[package]]
name = "regex-automata" name = "regex-automata"
version = "0.1.9" version = "0.1.9"
@ -400,6 +454,12 @@ version = "1.0.126"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03"
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.72" version = "1.0.72"

View File

@ -8,7 +8,9 @@ edition = "2018"
crate-type = ["lib", "cdylib"] crate-type = ["lib", "cdylib"]
[dependencies] [dependencies]
derive_more = "0.99.14"
git-version = "0.3.4" git-version = "0.3.4"
ibig = "0.3.2"
num-traits = "0.2" num-traits = "0.2"
wasm-bindgen = "0.2.74" wasm-bindgen = "0.2.74"

View File

@ -146,18 +146,16 @@
<label> <label>
Numbers: Numbers:
<select id="selNumbers"> <select id="selNumbers">
<!--<option value="native">Native</option>
<option value="rational">Rational</option> <option value="rational">Rational</option>
<option value="fixed" selected>Fixed</option> <option value="fixed" selected>Fixed</option>
<option value="gfixed">Fixed (guarded)</option>--> <!--<option value="gfixed">Fixed (guarded)</option>-->
<option value="rational">Rational</option>
<option value="float64">Float (64-bit)</option> <option value="float64">Float (64-bit)</option>
</select> </select>
</label> </label>
<!--<label> <label>
Decimal places (if Numbers = Fixed): Decimal places (if <i>Numbers</i> = <i>Fixed</i>):
<input type="number" id="txtDP" value="5" min="0" style="width: 3em;"> <input type="number" id="txtDP" value="5" min="0" style="width: 3em;">
</label>--> </label>
</div> </div>
<div class="col-12"> <div class="col-12">
<label> <label>

View File

@ -99,5 +99,6 @@ async function clickCount() {
'optsStr': optsStr, 'optsStr': optsStr,
'filePath': filePath, 'filePath': filePath,
'numbers': document.getElementById('selNumbers').value, 'numbers': document.getElementById('selNumbers').value,
'decimals': document.getElementById('txtDP').value,
}); });
} }

View File

@ -11,10 +11,15 @@ initWasm();
onmessage = function(evt) { onmessage = function(evt) {
if (evt.data.type === 'countElection') { if (evt.data.type === 'countElection') {
let numbers; let numbers;
if (evt.data.numbers === 'rational') { if (evt.data.numbers === 'fixed') {
numbers = 'Rational'; numbers = 'Fixed';
wasm.fixed_set_dps(evt.data.decimals);
} else if (evt.data.numbers === 'float64') { } else if (evt.data.numbers === 'float64') {
numbers = 'NativeFloat64'; numbers = 'NativeFloat64';
} else if (evt.data.numbers === 'rational') {
numbers = 'Rational';
} else {
throw 'Unknown --numbers';
} }
// Init election // Init election

View File

@ -17,7 +17,7 @@
use opentally::stv; use opentally::stv;
use opentally::election::{Candidate, CandidateState, CountCard, CountState, CountStateOrRef, Election, StageResult}; use opentally::election::{Candidate, CandidateState, CountCard, CountState, CountStateOrRef, Election, StageResult};
use opentally::numbers::{NativeFloat64, Number, Rational}; use opentally::numbers::{Fixed, NativeFloat64, Number, Rational};
use clap::{AppSettings, Clap}; use clap::{AppSettings, Clap};
@ -52,9 +52,13 @@ struct STV {
// -- Numbers settings -- // -- Numbers settings --
/// Numbers mode /// Numbers mode
#[clap(help_heading=Some("NUMBERS"), short, long, possible_values=&["rational", "float64"], default_value="rational", value_name="mode")] #[clap(help_heading=Some("NUMBERS"), short, long, possible_values=&["rational", "float64", "fixed"], default_value="rational", value_name="mode")]
numbers: String, numbers: String,
/// Decimal places if --numbers fixed
#[clap(help_heading=Some("NUMBERS"), long, default_value="5", value_name="dps")]
decimals: usize,
// ----------------------- // -----------------------
// -- Rounding settings -- // -- Rounding settings --
@ -135,6 +139,10 @@ fn main() {
} else if cmd_opts.numbers == "float64" { } else if cmd_opts.numbers == "float64" {
let election: Election<NativeFloat64> = Election::from_blt(lines.map(|r| r.expect("IO Error").to_string()).into_iter()); let election: Election<NativeFloat64> = Election::from_blt(lines.map(|r| r.expect("IO Error").to_string()).into_iter());
count_election(election, cmd_opts); count_election(election, cmd_opts);
} else if cmd_opts.numbers == "fixed" {
Fixed::set_dps(cmd_opts.decimals);
let election: Election<Fixed> = Election::from_blt(lines.map(|r| r.expect("IO Error").to_string()).into_iter());
count_election(election, cmd_opts);
} }
} }

327
src/numbers/fixed.rs Normal file
View File

@ -0,0 +1,327 @@
/* OpenTally: Open-source election vote counting
* Copyright © 2021 Lee Yingtong Li (RunasSudo)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use super::{Assign, From, Number};
use ibig::{IBig, ops::Abs};
use num_traits::{Num, One, Zero};
use std::cmp::{Ord, PartialEq, PartialOrd};
use std::ops;
use std::fmt;
static mut DPS: Option<usize> = None;
static mut FACTOR: Option<IBig> = None;
#[inline]
pub fn get_dps() -> usize {
unsafe { DPS.unwrap() }
}
#[inline]
fn get_factor() -> &'static IBig {
unsafe { FACTOR.as_ref().unwrap() }
}
#[derive(Clone, Eq, Ord, PartialEq, PartialOrd)]
pub struct Fixed(IBig);
impl Fixed {
pub fn set_dps(dps: usize) {
unsafe {
DPS = Some(dps);
FACTOR = Some(IBig::from(10).pow(dps));
}
}
}
impl Number for Fixed {
fn new() -> Self { Self(IBig::zero()) }
fn describe() -> String { format!("--numbers fixed --decimals {}", get_dps()) }
fn pow_assign(&mut self, exponent: i32) {
self.0 = self.0.pow(exponent as usize) * get_factor() / get_factor().pow(exponent as usize);
}
fn floor_mut(&mut self, dps: usize) {
// Only do something if truncating
if dps < get_dps() {
let factor = IBig::from(10).pow(get_dps() - dps);
self.0 /= &factor;
self.0 *= factor;
}
}
fn ceil_mut(&mut self, dps: usize) {
// Only do something if truncating
if dps < get_dps() {
self.0 += IBig::one();
let factor = IBig::from(10).pow(get_dps() - dps);
self.0 /= &factor;
self.0 *= factor;
}
}
}
impl Num for Fixed {
type FromStrRadixErr = ibig::error::ParseError;
fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
match IBig::from_str_radix(str, radix) {
Ok(value) => Ok(Self(value * get_factor())),
Err(err) => Err(err)
}
}
}
impl Assign for Fixed {
fn assign(&mut self, src: Self) { self.0 = src.0 }
}
impl Assign<&Self> for Fixed {
fn assign(&mut self, src: &Self) { self.0 = src.0.clone() }
}
impl From<usize> for Fixed {
fn from(n: usize) -> Self { Self(IBig::from(n) * get_factor()) }
}
impl From<f64> for Fixed {
fn from(n: f64) -> Self {
return Self(IBig::from((n * 10_f64.powi(get_dps() as i32)) as u32))
}
}
impl fmt::Display for Fixed {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let dps = match f.precision() {
Some(precision) => if precision < get_dps() { precision } else { get_dps() },
None => get_dps(),
};
let factor = IBig::from(10).pow(get_dps() - dps);
let mut result = (&self.0 / factor).abs().to_string();
let should_add_minus = (self.0 < IBig::zero()) && result != "0";
// Add leading 0s
result = format!("{0:0>1$}", result, dps + 1);
// Add the decimal point
if dps > 0 {
result.insert(result.len() - dps, '.');
}
// Add the sign
if should_add_minus {
result.insert(0, '-');
}
return f.write_str(&result);
}
}
impl One for Fixed {
fn one() -> Self { Self(get_factor().clone()) }
}
impl Zero for Fixed {
fn zero() -> Self { Self::new() }
fn is_zero(&self) -> bool { self.0.is_zero() }
}
impl ops::Neg for Fixed {
type Output = Self;
fn neg(self) -> Self::Output { Self(-self.0) }
}
impl ops::Add for Fixed {
type Output = Self;
fn add(self, _rhs: Self) -> Self::Output {
todo!()
}
}
impl ops::Sub for Fixed {
type Output = Self;
fn sub(self, _rhs: Self) -> Self::Output {
todo!()
}
}
impl ops::Mul for Fixed {
type Output = Self;
fn mul(self, _rhs: Self) -> Self::Output {
todo!()
}
}
impl ops::Div for Fixed {
type Output = Self;
fn div(self, _rhs: Self) -> Self::Output {
todo!()
}
}
impl ops::Rem for Fixed {
type Output = Self;
fn rem(self, _rhs: Self) -> Self::Output {
todo!()
}
}
impl ops::Add<&Self> for Fixed {
type Output = Self;
fn add(self, rhs: &Self) -> Self::Output { Self(self.0 + &rhs.0) }
}
impl ops::Sub<&Self> for Fixed {
type Output = Self;
fn sub(self, _rhs: &Self) -> Self::Output {
todo!()
}
}
impl ops::Mul<&Self> for Fixed {
type Output = Self;
fn mul(self, rhs: &Self) -> Self::Output { Self(self.0 * &rhs.0 / get_factor()) }
}
impl ops::Div<&Self> for Fixed {
type Output = Self;
fn div(self, rhs: &Self) -> Self::Output { Self(self.0 * get_factor() / &rhs.0) }
}
impl ops::Rem<&Self> for Fixed {
type Output = Self;
fn rem(self, _rhs: &Self) -> Self::Output {
todo!()
}
}
impl ops::AddAssign for Fixed {
fn add_assign(&mut self, rhs: Self) { self.0 += rhs.0; }
}
impl ops::SubAssign for Fixed {
fn sub_assign(&mut self, rhs: Self) { self.0 -= rhs.0; }
}
impl ops::MulAssign for Fixed {
fn mul_assign(&mut self, rhs: Self) {
self.0 *= rhs.0;
self.0 /= get_factor();
}
}
impl ops::DivAssign for Fixed {
fn div_assign(&mut self, rhs: Self) {
self.0 *= get_factor();
self.0 /= rhs.0;
}
}
impl ops::RemAssign for Fixed {
fn rem_assign(&mut self, _rhs: Self) {
todo!()
}
}
impl ops::AddAssign<&Self> for Fixed {
fn add_assign(&mut self, rhs: &Self) { self.0 += &rhs.0; }
}
impl ops::SubAssign<&Self> for Fixed {
fn sub_assign(&mut self, rhs: &Self) { self.0 -= &rhs.0; }
}
impl ops::MulAssign<&Self> for Fixed {
fn mul_assign(&mut self, rhs: &Self) {
self.0 *= &rhs.0;
self.0 /= get_factor();
}
}
impl ops::DivAssign<&Self> for Fixed {
fn div_assign(&mut self, _rhs: &Self) {
todo!()
}
}
impl ops::RemAssign<&Self> for Fixed {
fn rem_assign(&mut self, _rhs: &Self) {
todo!()
}
}
impl ops::Neg for &Fixed {
type Output = Fixed;
fn neg(self) -> Self::Output { Fixed(-&self.0) }
}
impl ops::Add<Self> for &Fixed {
type Output = Fixed;
fn add(self, _rhs: Self) -> Self::Output {
todo!()
}
}
impl ops::Sub<Self> for &Fixed {
type Output = Fixed;
fn sub(self, rhs: Self) -> Self::Output { Fixed(&self.0 - &rhs.0) }
}
impl ops::Mul<Self> for &Fixed {
type Output = Fixed;
fn mul(self, _rhs: Self) -> Self::Output {
todo!()
}
}
impl ops::Div<Self> for &Fixed {
type Output = Fixed;
fn div(self, rhs: Self) -> Self::Output { Fixed(&self.0 * get_factor() / &rhs.0) }
}
impl ops::Rem<Self> for &Fixed {
type Output = Fixed;
fn rem(self, _rhs: Self) -> Self::Output {
todo!()
}
}
/*
impl ops::Add<&&Rational> for &Rational {
}
impl ops::Sub<&&Rational> for &Rational {
}
impl ops::Mul<&&Rational> for &Rational {
}
impl ops::Div<&&Rational> for &Rational {
}
impl ops::Rem<&&Rational> for &Rational {
}
*/

View File

@ -15,6 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
mod fixed;
mod native; mod native;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
@ -60,6 +61,7 @@ where
} }
} }
pub use self::fixed::Fixed;
pub use self::native::NativeFloat64; pub use self::native::NativeFloat64;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]

View File

@ -17,15 +17,16 @@
use super::{Assign, From, Number}; use super::{Assign, From, Number};
use derive_more::Display;
use num_traits::{Num, One, Zero}; use num_traits::{Num, One, Zero};
use std::cmp::{Ord, Ordering, PartialEq, PartialOrd}; use std::cmp::{Ord, Ordering, PartialEq, PartialOrd};
use std::num::ParseIntError; use std::num::ParseIntError;
use std::fmt;
use std::ops; use std::ops;
type ImplType = f64; type ImplType = f64;
#[derive(Clone, Display, PartialEq, PartialOrd)]
pub struct NativeFloat64(ImplType); pub struct NativeFloat64(ImplType);
impl Number for NativeFloat64 { impl Number for NativeFloat64 {
@ -50,7 +51,6 @@ impl Number for NativeFloat64 {
impl Num for NativeFloat64 { impl Num for NativeFloat64 {
type FromStrRadixErr = ParseIntError; type FromStrRadixErr = ParseIntError;
fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> { fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
match i64::from_str_radix(str, radix) { match i64::from_str_radix(str, radix) {
Ok(value) => Ok(Self(value as ImplType)), Ok(value) => Ok(Self(value as ImplType)),
@ -75,14 +75,6 @@ impl From<f64> for NativeFloat64 {
fn from(n: f64) -> Self { Self(n as ImplType) } fn from(n: f64) -> Self { Self(n as ImplType) }
} }
impl Clone for NativeFloat64 {
fn clone(&self) -> Self { Self(self.0) }
}
impl fmt::Display for NativeFloat64 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) }
}
impl One for NativeFloat64 { impl One for NativeFloat64 {
fn one() -> Self { Self(1.0) } fn one() -> Self { Self(1.0) }
} }
@ -93,18 +85,10 @@ impl Zero for NativeFloat64 {
} }
impl Eq for NativeFloat64 {} impl Eq for NativeFloat64 {}
impl PartialEq for NativeFloat64 {
fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
}
impl Ord for NativeFloat64 { impl Ord for NativeFloat64 {
fn cmp(&self, other: &Self) -> Ordering { self.0.partial_cmp(&other.0).unwrap() } fn cmp(&self, other: &Self) -> Ordering { self.0.partial_cmp(&other.0).unwrap() }
} }
impl PartialOrd for NativeFloat64 {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { self.0.partial_cmp(&other.0) }
}
impl ops::Neg for NativeFloat64 { impl ops::Neg for NativeFloat64 {
type Output = NativeFloat64; type Output = NativeFloat64;
fn neg(self) -> Self::Output { Self(-self.0) } fn neg(self) -> Self::Output { Self(-self.0) }
@ -140,7 +124,6 @@ impl ops::Div for NativeFloat64 {
impl ops::Rem for NativeFloat64 { impl ops::Rem for NativeFloat64 {
type Output = NativeFloat64; type Output = NativeFloat64;
fn rem(self, _rhs: Self) -> Self::Output { fn rem(self, _rhs: Self) -> Self::Output {
todo!() todo!()
} }

View File

@ -25,6 +25,7 @@ use std::cmp::{Ord, Ordering, PartialEq, PartialOrd};
use std::fmt; use std::fmt;
use std::ops; use std::ops;
#[derive(Clone, PartialEq, PartialOrd)]
pub struct Rational(BigRational); pub struct Rational(BigRational);
impl Number for Rational { impl Number for Rational {
@ -60,11 +61,8 @@ impl Number for Rational {
} }
impl Num for Rational { impl Num for Rational {
//type FromStrRadixErr = ParseRatioError;
type FromStrRadixErr = ParseBigIntError; type FromStrRadixErr = ParseBigIntError;
fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> { fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
//match BigRational::from_str_radix(str, radix) {
match BigInt::from_str_radix(str, radix) { match BigInt::from_str_radix(str, radix) {
Ok(value) => Ok(Self(BigRational::from_integer(value))), Ok(value) => Ok(Self(BigRational::from_integer(value))),
Err(err) => Err(err) Err(err) => Err(err)
@ -91,10 +89,6 @@ impl From<f64> for Rational {
} }
} }
impl Clone for Rational {
fn clone(&self) -> Self { Self(self.0.clone()) }
}
impl fmt::Display for Rational { impl fmt::Display for Rational {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(precision) = f.precision() { if let Some(precision) = f.precision() {
@ -136,18 +130,10 @@ impl Zero for Rational {
} }
impl Eq for Rational {} impl Eq for Rational {}
impl PartialEq for Rational {
fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
}
impl Ord for Rational { impl Ord for Rational {
fn cmp(&self, other: &Self) -> Ordering { self.0.partial_cmp(&other.0).unwrap() } fn cmp(&self, other: &Self) -> Ordering { self.0.partial_cmp(&other.0).unwrap() }
} }
impl PartialOrd for Rational {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { self.0.partial_cmp(&other.0) }
}
impl ops::Neg for Rational { impl ops::Neg for Rational {
type Output = Rational; type Output = Rational;
fn neg(self) -> Self::Output { Self(-self.0) } fn neg(self) -> Self::Output { Self(-self.0) }
@ -183,7 +169,6 @@ impl ops::Div for Rational {
impl ops::Rem for Rational { impl ops::Rem for Rational {
type Output = Rational; type Output = Rational;
fn rem(self, _rhs: Self) -> Self::Output { fn rem(self, _rhs: Self) -> Self::Output {
todo!() todo!()
} }

View File

@ -25,6 +25,7 @@ use std::cmp::{Ord, Ordering, PartialEq, PartialOrd};
use std::fmt; use std::fmt;
use std::ops; use std::ops;
#[derive(Clone, PartialEq, PartialOrd)]
pub struct Rational(rug::Rational); pub struct Rational(rug::Rational);
impl Number for Rational { impl Number for Rational {
@ -61,7 +62,6 @@ impl Number for Rational {
impl Num for Rational { impl Num for Rational {
type FromStrRadixErr = ParseRationalError; type FromStrRadixErr = ParseRationalError;
fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> { fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
match rug::Rational::parse_radix(str, radix as i32) { match rug::Rational::parse_radix(str, radix as i32) {
Ok(value) => Ok(Self(rug::Rational::from(value))), Ok(value) => Ok(Self(rug::Rational::from(value))),
@ -74,8 +74,8 @@ impl Assign for Rational {
fn assign(&mut self, src: Self) { self.0.assign(src.0) } fn assign(&mut self, src: Self) { self.0.assign(src.0) }
} }
impl Assign<&Rational> for Rational { impl Assign<&Self> for Rational {
fn assign(&mut self, src: &Rational) { self.0.assign(&src.0) } fn assign(&mut self, src: &Self) { self.0.assign(&src.0) }
} }
impl From<usize> for Rational { impl From<usize> for Rational {
@ -89,10 +89,6 @@ impl From<f64> for Rational {
} }
} }
impl Clone for Rational {
fn clone(&self) -> Self { Self(self.0.clone()) }
}
impl fmt::Display for Rational { impl fmt::Display for Rational {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(precision) = f.precision() { if let Some(precision) = f.precision() {
@ -134,84 +130,75 @@ impl Zero for Rational {
} }
impl Eq for Rational {} impl Eq for Rational {}
impl PartialEq for Rational {
fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
}
impl Ord for Rational { impl Ord for Rational {
fn cmp(&self, other: &Self) -> Ordering { self.0.cmp(&other.0) } fn cmp(&self, other: &Self) -> Ordering { self.0.cmp(&other.0) }
} }
impl PartialOrd for Rational {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { self.0.partial_cmp(&other.0) }
}
impl ops::Neg for Rational { impl ops::Neg for Rational {
type Output = Rational; type Output = Self;
fn neg(self) -> Self::Output { Self(-self.0) } fn neg(self) -> Self::Output { Self(-self.0) }
} }
impl ops::Add for Rational { impl ops::Add for Rational {
type Output = Rational; type Output = Self;
fn add(self, _rhs: Self) -> Self::Output { fn add(self, _rhs: Self) -> Self::Output {
todo!() todo!()
} }
} }
impl ops::Sub for Rational { impl ops::Sub for Rational {
type Output = Rational; type Output = Self;
fn sub(self, _rhs: Self) -> Self::Output { fn sub(self, _rhs: Self) -> Self::Output {
todo!() todo!()
} }
} }
impl ops::Mul for Rational { impl ops::Mul for Rational {
type Output = Rational; type Output = Self;
fn mul(self, _rhs: Self) -> Self::Output { fn mul(self, _rhs: Self) -> Self::Output {
todo!() todo!()
} }
} }
impl ops::Div for Rational { impl ops::Div for Rational {
type Output = Rational; type Output = Self;
fn div(self, _rhs: Self) -> Self::Output { fn div(self, _rhs: Self) -> Self::Output {
todo!() todo!()
} }
} }
impl ops::Rem for Rational { impl ops::Rem for Rational {
type Output = Rational; type Output = Self;
fn rem(self, _rhs: Self) -> Self::Output { fn rem(self, _rhs: Self) -> Self::Output {
todo!() todo!()
} }
} }
impl ops::Add<&Rational> for Rational { impl ops::Add<&Self> for Rational {
type Output = Rational; type Output = Self;
fn add(self, rhs: &Rational) -> Self::Output { Self(self.0 + &rhs.0) } fn add(self, rhs: &Self) -> Self::Output { Self(self.0 + &rhs.0) }
} }
impl ops::Sub<&Rational> for Rational { impl ops::Sub<&Self> for Rational {
type Output = Rational; type Output = Self;
fn sub(self, _rhs: &Rational) -> Self::Output { fn sub(self, _rhs: &Self) -> Self::Output {
todo!() todo!()
} }
} }
impl ops::Mul<&Rational> for Rational { impl ops::Mul<&Self> for Rational {
type Output = Rational; type Output = Self;
fn mul(self, rhs: &Rational) -> Self::Output { Self(self.0 * &rhs.0) } fn mul(self, rhs: &Self) -> Self::Output { Self(self.0 * &rhs.0) }
} }
impl ops::Div<&Rational> for Rational { impl ops::Div<&Self> for Rational {
type Output = Rational; type Output = Self;
fn div(self, rhs: &Rational) -> Self::Output { Self(self.0 / &rhs.0) } fn div(self, rhs: &Self) -> Self::Output { Self(self.0 / &rhs.0) }
} }
impl ops::Rem<&Rational> for Rational { impl ops::Rem<&Self> for Rational {
type Output = Rational; type Output = Self;
fn rem(self, _rhs: &Rational) -> Self::Output { fn rem(self, _rhs: &Self) -> Self::Output {
todo!() todo!()
} }
} }
@ -238,26 +225,26 @@ impl ops::RemAssign for Rational {
} }
} }
impl ops::AddAssign<&Rational> for Rational { impl ops::AddAssign<&Self> for Rational {
fn add_assign(&mut self, rhs: &Rational) { self.0 += &rhs.0 } fn add_assign(&mut self, rhs: &Self) { self.0 += &rhs.0 }
} }
impl ops::SubAssign<&Rational> for Rational { impl ops::SubAssign<&Self> for Rational {
fn sub_assign(&mut self, rhs: &Rational) { self.0 -= &rhs.0 } fn sub_assign(&mut self, rhs: &Self) { self.0 -= &rhs.0 }
} }
impl ops::MulAssign<&Rational> for Rational { impl ops::MulAssign<&Self> for Rational {
fn mul_assign(&mut self, rhs: &Rational) { self.0 *= &rhs.0 } fn mul_assign(&mut self, rhs: &Self) { self.0 *= &rhs.0 }
} }
impl ops::DivAssign<&Rational> for Rational { impl ops::DivAssign<&Self> for Rational {
fn div_assign(&mut self, _rhs: &Rational) { fn div_assign(&mut self, _rhs: &Self) {
todo!() todo!()
} }
} }
impl ops::RemAssign<&Rational> for Rational { impl ops::RemAssign<&Self> for Rational {
fn rem_assign(&mut self, _rhs: &Rational) { fn rem_assign(&mut self, _rhs: &Self) {
todo!() todo!()
} }
} }
@ -267,33 +254,33 @@ impl ops::Neg for &Rational {
fn neg(self) -> Self::Output { Rational(rug::Rational::from(-&self.0)) } fn neg(self) -> Self::Output { Rational(rug::Rational::from(-&self.0)) }
} }
impl ops::Add<&Rational> for &Rational { impl ops::Add<Self> for &Rational {
type Output = Rational; type Output = Rational;
fn add(self, _rhs: &Rational) -> Self::Output { fn add(self, _rhs: Self) -> Self::Output {
todo!() todo!()
} }
} }
impl ops::Sub<&Rational> for &Rational { impl ops::Sub<Self> for &Rational {
type Output = Rational; type Output = Rational;
fn sub(self, rhs: &Rational) -> Self::Output { Rational(rug::Rational::from(&self.0 - &rhs.0)) } fn sub(self, rhs: Self) -> Self::Output { Rational(rug::Rational::from(&self.0 - &rhs.0)) }
} }
impl ops::Mul<&Rational> for &Rational { impl ops::Mul<Self> for &Rational {
type Output = Rational; type Output = Rational;
fn mul(self, _rhs: &Rational) -> Self::Output { fn mul(self, _rhs: Self) -> Self::Output {
todo!() todo!()
} }
} }
impl ops::Div<&Rational> for &Rational { impl ops::Div<Self> for &Rational {
type Output = Rational; type Output = Rational;
fn div(self, rhs: &Rational) -> Self::Output { Rational(rug::Rational::from(&self.0 / &rhs.0)) } fn div(self, rhs: Self) -> Self::Output { Rational(rug::Rational::from(&self.0 / &rhs.0)) }
} }
impl ops::Rem<&Rational> for &Rational { impl ops::Rem<Self> for &Rational {
type Output = Rational; type Output = Rational;
fn rem(self, _rhs: &Rational) -> Self::Output { fn rem(self, _rhs: Self) -> Self::Output {
todo!() todo!()
} }
} }

View File

@ -16,7 +16,7 @@
*/ */
use crate::election::{CandidateState, CountState, Election}; use crate::election::{CandidateState, CountState, Election};
use crate::numbers::{NativeFloat64, Number, Rational}; use crate::numbers::{Fixed, NativeFloat64, Number, Rational};
use crate::stv; use crate::stv;
extern crate console_error_panic_hook; extern crate console_error_panic_hook;
@ -24,11 +24,18 @@ extern crate console_error_panic_hook;
use js_sys::Array; use js_sys::Array;
use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::prelude::wasm_bindgen;
// Init
#[wasm_bindgen]
pub fn fixed_set_dps(dps: usize) {
Fixed::set_dps(dps);
}
// Helper macros for making functions // Helper macros for making functions
macro_rules! impl_type { macro_rules! impl_type {
($type:ident) => { paste::item! { ($type:ident) => { paste::item! {
// Exported functions // Counting
#[wasm_bindgen] #[wasm_bindgen]
#[allow(non_snake_case)] #[allow(non_snake_case)]
@ -119,8 +126,9 @@ macro_rules! impl_type {
}} }}
} }
impl_type!(Rational); impl_type!(Fixed);
impl_type!(NativeFloat64); impl_type!(NativeFloat64);
impl_type!(Rational);
// Reporting // Reporting
@ -137,7 +145,7 @@ fn describe_count<N: Number>(filename: String, election: &Election<N>, opts: &st
let mut result = String::from("<p>Count computed by OpenTally (revision "); let mut result = String::from("<p>Count computed by OpenTally (revision ");
result.push_str(crate::VERSION); result.push_str(crate::VERSION);
let total_ballots = election.ballots.iter().fold(N::zero(), |acc, b| { acc + &b.orig_value }); let total_ballots = election.ballots.iter().fold(N::zero(), |acc, b| { acc + &b.orig_value });
result.push_str(&format!(r#"). Read {} ballots from &lsquo;{}&rsquo; for election &lsquo;{}&rsquo;. There are {} candidates for {} vacancies. Counting using options <span style="font-family: monospace;">{}</span>.</p>"#, total_ballots, filename, election.name, election.candidates.len(), election.seats, opts.describe::<N>())); result.push_str(&format!(r#"). Read {:.dps$} ballots from &lsquo;{}&rsquo; for election &lsquo;{}&rsquo;. There are {} candidates for {} vacancies. Counting using options <span style="font-family: monospace;">{}</span>.</p>"#, total_ballots, filename, election.name, election.candidates.len(), election.seats, opts.describe::<N>(), dps=opts.pp_decimals));
return result; return result;
} }