359 lines
7.9 KiB
Rust
359 lines
7.9 KiB
Rust
|
/* 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, Number};
|
||
|
|
||
|
use ibig::{IBig, ops::Abs};
|
||
|
use num_traits::{Num, One, Zero};
|
||
|
|
||
|
use std::cmp::{Ord, Ordering, PartialEq, PartialOrd};
|
||
|
use std::ops;
|
||
|
use std::fmt;
|
||
|
|
||
|
static mut DPS: Option<usize> = None;
|
||
|
static mut FACTOR: Option<IBig> = None;
|
||
|
static mut FACTOR_CMP: Option<IBig> = None;
|
||
|
|
||
|
#[inline]
|
||
|
pub fn get_dps() -> usize {
|
||
|
unsafe { DPS.unwrap() }
|
||
|
}
|
||
|
|
||
|
#[inline]
|
||
|
fn get_factor() -> &'static IBig {
|
||
|
unsafe { FACTOR.as_ref().unwrap() }
|
||
|
}
|
||
|
|
||
|
#[inline]
|
||
|
fn get_factor_cmp() -> &'static IBig {
|
||
|
unsafe { FACTOR_CMP.as_ref().unwrap() }
|
||
|
}
|
||
|
|
||
|
/// Guarded fixed-point number
|
||
|
#[derive(Clone, Eq)]
|
||
|
pub struct GuardedFixed(IBig);
|
||
|
|
||
|
impl GuardedFixed {
|
||
|
pub fn set_dps(dps: usize) {
|
||
|
unsafe {
|
||
|
DPS = Some(dps);
|
||
|
FACTOR = Some(IBig::from(10).pow(dps * 2));
|
||
|
FACTOR_CMP = Some(IBig::from(10).pow(dps) / IBig::from(2));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Number for GuardedFixed {
|
||
|
fn new() -> Self { Self(IBig::zero()) }
|
||
|
|
||
|
fn describe() -> String { format!("--numbers gfixed --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() * 2 {
|
||
|
let factor = IBig::from(10).pow(get_dps() * 2 - dps);
|
||
|
self.0 /= &factor;
|
||
|
self.0 *= factor;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn ceil_mut(&mut self, dps: usize) {
|
||
|
// Only do something if truncating
|
||
|
if dps < get_dps() * 2 {
|
||
|
self.0 -= IBig::one();
|
||
|
let factor = IBig::from(10).pow(get_dps() * 2 - dps);
|
||
|
self.0 /= &factor;
|
||
|
self.0 += IBig::one();
|
||
|
self.0 *= factor;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Num for GuardedFixed {
|
||
|
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 PartialEq for GuardedFixed {
|
||
|
fn eq(&self, other: &GuardedFixed) -> bool {
|
||
|
if &(&self.0 - &other.0).abs() < get_factor_cmp() {
|
||
|
return true;
|
||
|
} else {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl PartialOrd for GuardedFixed {
|
||
|
fn partial_cmp(&self, other: &GuardedFixed) -> Option<Ordering> {
|
||
|
return Some(self.cmp(other));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Ord for GuardedFixed {
|
||
|
fn cmp(&self, other: &GuardedFixed) -> Ordering {
|
||
|
if self.eq(other) {
|
||
|
return Ordering::Equal;
|
||
|
} else {
|
||
|
return self.0.cmp(&other.0);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Assign for GuardedFixed {
|
||
|
fn assign(&mut self, src: Self) { self.0 = src.0 }
|
||
|
}
|
||
|
|
||
|
impl Assign<&Self> for GuardedFixed {
|
||
|
fn assign(&mut self, src: &Self) { self.0 = src.0.clone() }
|
||
|
}
|
||
|
|
||
|
impl From<usize> for GuardedFixed {
|
||
|
fn from(n: usize) -> Self { Self(IBig::from(n) * get_factor()) }
|
||
|
}
|
||
|
|
||
|
impl From<f64> for GuardedFixed {
|
||
|
fn from(n: f64) -> Self {
|
||
|
return Self(IBig::from((n * 10_f64.powi((get_dps() * 2) as i32)).round() as u32))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl fmt::Display for GuardedFixed {
|
||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||
|
let dps = match f.precision() {
|
||
|
Some(precision) => if precision < get_dps() * 2 { precision } else { get_dps() * 2 },
|
||
|
None => get_dps(),
|
||
|
};
|
||
|
|
||
|
let factor = IBig::from(10).pow(get_dps() * 2 - 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 GuardedFixed {
|
||
|
fn one() -> Self { Self(get_factor().clone()) }
|
||
|
}
|
||
|
|
||
|
impl Zero for GuardedFixed {
|
||
|
fn zero() -> Self { Self::new() }
|
||
|
fn is_zero(&self) -> bool { self.0.is_zero() }
|
||
|
}
|
||
|
|
||
|
impl ops::Neg for GuardedFixed {
|
||
|
type Output = Self;
|
||
|
fn neg(self) -> Self::Output { Self(-self.0) }
|
||
|
}
|
||
|
|
||
|
impl ops::Add for GuardedFixed {
|
||
|
type Output = Self;
|
||
|
fn add(self, _rhs: Self) -> Self::Output {
|
||
|
todo!()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl ops::Sub for GuardedFixed {
|
||
|
type Output = Self;
|
||
|
fn sub(self, _rhs: Self) -> Self::Output {
|
||
|
todo!()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl ops::Mul for GuardedFixed {
|
||
|
type Output = Self;
|
||
|
fn mul(self, _rhs: Self) -> Self::Output {
|
||
|
todo!()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl ops::Div for GuardedFixed {
|
||
|
type Output = Self;
|
||
|
fn div(self, _rhs: Self) -> Self::Output {
|
||
|
todo!()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl ops::Rem for GuardedFixed {
|
||
|
type Output = Self;
|
||
|
fn rem(self, _rhs: Self) -> Self::Output {
|
||
|
todo!()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl ops::Add<&Self> for GuardedFixed {
|
||
|
type Output = Self;
|
||
|
fn add(self, rhs: &Self) -> Self::Output { Self(self.0 + &rhs.0) }
|
||
|
}
|
||
|
|
||
|
impl ops::Sub<&Self> for GuardedFixed {
|
||
|
type Output = Self;
|
||
|
fn sub(self, rhs: &Self) -> Self::Output { Self(self.0 - &rhs.0) }
|
||
|
}
|
||
|
|
||
|
impl ops::Mul<&Self> for GuardedFixed {
|
||
|
type Output = Self;
|
||
|
fn mul(self, rhs: &Self) -> Self::Output { Self(self.0 * &rhs.0 / get_factor()) }
|
||
|
}
|
||
|
|
||
|
impl ops::Div<&Self> for GuardedFixed {
|
||
|
type Output = Self;
|
||
|
fn div(self, rhs: &Self) -> Self::Output { Self(self.0 * get_factor() / &rhs.0) }
|
||
|
}
|
||
|
|
||
|
impl ops::Rem<&Self> for GuardedFixed {
|
||
|
type Output = Self;
|
||
|
fn rem(self, _rhs: &Self) -> Self::Output {
|
||
|
todo!()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl ops::AddAssign for GuardedFixed {
|
||
|
fn add_assign(&mut self, rhs: Self) { self.0 += rhs.0; }
|
||
|
}
|
||
|
|
||
|
impl ops::SubAssign for GuardedFixed {
|
||
|
fn sub_assign(&mut self, rhs: Self) { self.0 -= rhs.0; }
|
||
|
}
|
||
|
|
||
|
impl ops::MulAssign for GuardedFixed {
|
||
|
fn mul_assign(&mut self, rhs: Self) {
|
||
|
self.0 *= rhs.0;
|
||
|
self.0 /= get_factor();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl ops::DivAssign for GuardedFixed {
|
||
|
fn div_assign(&mut self, rhs: Self) {
|
||
|
self.0 *= get_factor();
|
||
|
self.0 /= rhs.0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl ops::RemAssign for GuardedFixed {
|
||
|
fn rem_assign(&mut self, _rhs: Self) {
|
||
|
todo!()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl ops::AddAssign<&Self> for GuardedFixed {
|
||
|
fn add_assign(&mut self, rhs: &Self) { self.0 += &rhs.0; }
|
||
|
}
|
||
|
|
||
|
impl ops::SubAssign<&Self> for GuardedFixed {
|
||
|
fn sub_assign(&mut self, rhs: &Self) { self.0 -= &rhs.0; }
|
||
|
}
|
||
|
|
||
|
impl ops::MulAssign<&Self> for GuardedFixed {
|
||
|
fn mul_assign(&mut self, rhs: &Self) {
|
||
|
self.0 *= &rhs.0;
|
||
|
self.0 /= get_factor();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl ops::DivAssign<&Self> for GuardedFixed {
|
||
|
fn div_assign(&mut self, _rhs: &Self) {
|
||
|
todo!()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl ops::RemAssign<&Self> for GuardedFixed {
|
||
|
fn rem_assign(&mut self, _rhs: &Self) {
|
||
|
todo!()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl ops::Neg for &GuardedFixed {
|
||
|
type Output = GuardedFixed;
|
||
|
fn neg(self) -> Self::Output { GuardedFixed(-&self.0) }
|
||
|
}
|
||
|
|
||
|
impl ops::Add<Self> for &GuardedFixed {
|
||
|
type Output = GuardedFixed;
|
||
|
fn add(self, rhs: Self) -> Self::Output { GuardedFixed(&self.0 + &rhs.0) }
|
||
|
}
|
||
|
|
||
|
impl ops::Sub<Self> for &GuardedFixed {
|
||
|
type Output = GuardedFixed;
|
||
|
fn sub(self, rhs: Self) -> Self::Output { GuardedFixed(&self.0 - &rhs.0) }
|
||
|
}
|
||
|
|
||
|
impl ops::Mul<Self> for &GuardedFixed {
|
||
|
type Output = GuardedFixed;
|
||
|
fn mul(self, _rhs: Self) -> Self::Output {
|
||
|
todo!()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl ops::Div<Self> for &GuardedFixed {
|
||
|
type Output = GuardedFixed;
|
||
|
fn div(self, rhs: Self) -> Self::Output { GuardedFixed(&self.0 * get_factor() / &rhs.0) }
|
||
|
}
|
||
|
|
||
|
impl ops::Rem<Self> for &GuardedFixed {
|
||
|
type Output = GuardedFixed;
|
||
|
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 {
|
||
|
|
||
|
}
|
||
|
*/
|