OpenTally/src/numbers/gfixed.rs

514 lines
14 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, Signed, Zero};
use std::cell::{Cell, UnsafeCell};
use std::cmp::{Ord, Ordering, PartialEq, PartialOrd};
use std::ops;
use std::fmt;
thread_local! {
static DPS: Cell<usize> = Cell::new(5);
static FACTOR: UnsafeCell<IBig> = UnsafeCell::new(IBig::from(10).pow(10));
static FACTOR_CMP: UnsafeCell<IBig> = UnsafeCell::new(IBig::from(10).pow(5) / IBig::from(2));
}
pub fn get_dps() -> usize {
return DPS.with(|dps_cell| dps_cell.get());
}
fn get_factor<'a>() -> &'a IBig {
FACTOR.with(|factor_cell| {
let factor_ptr = factor_cell.get();
// SAFETY: Safe if requirements of GuardedFixed::set_dps met
let factor_ref = unsafe { &*factor_ptr };
return factor_ref;
})
}
fn get_factor_cmp<'a>() -> &'a IBig {
FACTOR_CMP.with(|factor_cmp_cell| {
let factor_cmp_ptr = factor_cmp_cell.get();
// SAFETY: Safe if requirements of Fixed::set_dps met
let factor_cmp_ref = unsafe { &*factor_cmp_ptr };
return factor_cmp_ref;
})
}
/// Guarded fixed-point number
#[derive(Clone, Debug, Eq)]
pub struct GuardedFixed(IBig);
impl GuardedFixed {
/// Set the number of decimal places to compute results to
///
/// SAFETY: This must be called before, and never after, any operations on [GuardedFixed].
pub fn set_dps(dps: usize) {
DPS.with(|dps_cell| {
dps_cell.set(dps);
});
FACTOR.with(|factor_cell| {
let factor = IBig::from(10).pow(dps * 2);
let factor_ptr = factor_cell.get();
// SAFETY: Safe if requirements above met
unsafe {
*factor_ptr = factor;
}
});
FACTOR_CMP.with(|factor_cmp_cell| {
let factor_cmp = IBig::from(10).pow(dps) / IBig::from(2);
let factor_cmp_ptr = factor_cmp_cell.get();
// SAFETY: Safe if requirements above met
unsafe {
*factor_cmp_ptr = factor_cmp;
}
});
}
}
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) {
if exponent < 0 {
todo!();
}
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;
}
}
fn round_mut(&mut self, dps: usize) {
// Only do something if truncating
if dps < get_dps() * 2 {
let mut factor = IBig::from(10).pow(get_dps() * 2 - dps);
factor /= IBig::from(2);
self.0 += factor;
self.floor_mut(dps);
}
}
fn parse(s: &str) -> Self {
// Parse decimal
if s.contains('.') {
let (whole, decimal) = s.split_once('.').unwrap();
let whole = match IBig::from_str_radix(whole, 10) {
Ok(value) => value,
Err(_) => panic!("Syntax Error"),
} * get_factor();
let decimal = match IBig::from_str_radix(decimal, 10) {
Ok(value) => value,
Err(_) => panic!("Syntax Error"),
} * get_factor() / IBig::from(10).pow(decimal.len());
if whole.is_negative() {
return Self(whole - decimal);
} else {
return Self(whole + decimal);
}
}
// Parse integer
if let Ok(value) = Self::from_str_radix(s, 10) {
return value;
} else {
panic!("Syntax Error");
}
}
}
#[test]
fn rounding() {
GuardedFixed::set_dps(5);
let mut x = GuardedFixed::parse("55.550"); x.floor_mut(2); assert_eq!(x, GuardedFixed::parse("55.55"));
let mut x = GuardedFixed::parse("55.552"); x.floor_mut(2); assert_eq!(x, GuardedFixed::parse("55.55"));
let mut x = GuardedFixed::parse("55.555"); x.floor_mut(2); assert_eq!(x, GuardedFixed::parse("55.55"));
let mut x = GuardedFixed::parse("55.557"); x.floor_mut(2); assert_eq!(x, GuardedFixed::parse("55.55"));
let mut x = GuardedFixed::parse("55.550"); x.ceil_mut(2); assert_eq!(x, GuardedFixed::parse("55.55"));
let mut x = GuardedFixed::parse("55.552"); x.ceil_mut(2); assert_eq!(x, GuardedFixed::parse("55.56"));
let mut x = GuardedFixed::parse("55.555"); x.ceil_mut(2); assert_eq!(x, GuardedFixed::parse("55.56"));
let mut x = GuardedFixed::parse("55.557"); x.ceil_mut(2); assert_eq!(x, GuardedFixed::parse("55.56"));
let mut x = GuardedFixed::parse("55.550"); x.round_mut(2); assert_eq!(x, GuardedFixed::parse("55.55"));
let mut x = GuardedFixed::parse("55.552"); x.round_mut(2); assert_eq!(x, GuardedFixed::parse("55.55"));
let mut x = GuardedFixed::parse("55.555"); x.round_mut(2); assert_eq!(x, GuardedFixed::parse("55.56"));
let mut x = GuardedFixed::parse("55.557"); x.round_mut(2); assert_eq!(x, GuardedFixed::parse("55.56"));
}
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: &Self) -> bool {
if &(&self.0 - &other.0).abs() < get_factor_cmp() {
return true;
} else {
return false;
}
}
}
impl PartialOrd for GuardedFixed {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
return Some(self.cmp(other));
}
}
impl Ord for GuardedFixed {
fn cmp(&self, other: &Self) -> 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() }
}
#[test]
fn assign() {
GuardedFixed::set_dps(2);
let a = GuardedFixed::parse("123.45");
let b = GuardedFixed::parse("678.90");
let mut x = a.clone(); x.assign(b.clone()); assert_eq!(x, b);
let mut x = a.clone(); x.assign(&b); assert_eq!(x, b);
}
impl From<usize> for GuardedFixed {
fn from(n: usize) -> Self { Self(IBig::from(n) * get_factor()) }
}
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 mut result;
if dps == get_dps() * 2 {
result = self.0.clone().abs().to_string();
} else {
// Rounding required
let factor = IBig::from(10).pow(get_dps() * 2 - dps - 1);
let mut rounded = (&self.0 / factor).abs() + IBig::from(5); // Add 5 to force round-to-nearest
rounded /= IBig::from(10); // Remove rounding digit
result = rounded.to_string();
}
//let should_add_minus = (self.0 < IBig::zero()) && result != "0";
let should_add_minus = self.0 < IBig::zero();
// 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);
}
}
#[test]
fn display_debug() {
GuardedFixed::set_dps(2);
let x = GuardedFixed::parse("123.4"); assert_eq!(format!("{}", x), "123.40");
let x = GuardedFixed::parse("123.4"); assert_eq!(format!("{:?}", x), "GuardedFixed(1234000)");
}
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 { Self(self.0 + rhs.0) }
}
impl ops::Sub for GuardedFixed {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output { Self(self.0 - rhs.0) }
}
impl ops::Mul for GuardedFixed {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output { Self(self.0 * rhs.0 / get_factor())}
}
impl ops::Div for GuardedFixed {
type Output = Self;
fn div(self, rhs: Self) -> Self::Output { Self(self.0 * get_factor() / rhs.0) }
}
impl ops::Rem for GuardedFixed {
type Output = Self;
fn rem(self, rhs: Self) -> Self::Output { Self(self.0 % rhs.0) }
}
#[test]
fn arith_owned_owned() {
GuardedFixed::set_dps(2);
let a = GuardedFixed::parse("123.45");
let b = GuardedFixed::parse("678.90");
assert_eq!(a.clone() + b.clone(), GuardedFixed::parse("802.35"));
assert_eq!(a.clone() - b.clone(), GuardedFixed::parse("-555.45"));
assert_eq!(a.clone() * b.clone(), GuardedFixed::parse("83810.205")); // Must compare to 3 d.p.s as doesn't meet FACTOR_CMP
assert_eq!(a.clone() / b.clone(), GuardedFixed::parse("0.18")); // Meets FACTOR_CMP so compare only 2 d.p.s
assert_eq!(b.clone() % a.clone(), GuardedFixed::parse("61.65"));
}
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 { Self(self.0 % &rhs.0) }
}
#[test]
fn arith_owned_ref() {
GuardedFixed::set_dps(2);
let a = GuardedFixed::parse("123.45");
let b = GuardedFixed::parse("678.90");
assert_eq!(a.clone() + &b, GuardedFixed::parse("802.35"));
assert_eq!(a.clone() - &b, GuardedFixed::parse("-555.45"));
assert_eq!(a.clone() * &b, GuardedFixed::parse("83810.205"));
assert_eq!(a.clone() / &b, GuardedFixed::parse("0.18"));
assert_eq!(b.clone() % &a, GuardedFixed::parse("61.65"));
}
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) { self.0 %= rhs.0; }
}
#[test]
fn arithassign_owned() {
GuardedFixed::set_dps(2);
let a = GuardedFixed::parse("123.45");
let b = GuardedFixed::parse("678.90");
let mut x = a.clone(); x += b.clone(); assert_eq!(x, GuardedFixed::parse("802.35"));
let mut x = a.clone(); x -= b.clone(); assert_eq!(x, GuardedFixed::parse("-555.45"));
let mut x = a.clone(); x *= b.clone(); assert_eq!(x, GuardedFixed::parse("83810.205"));
let mut x = a.clone(); x /= b.clone(); assert_eq!(x, GuardedFixed::parse("0.18"));
let mut x = b.clone(); x %= a.clone(); assert_eq!(x, GuardedFixed::parse("61.65"));
}
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) {
self.0 *= get_factor();
self.0 /= &rhs.0;
}
}
impl ops::RemAssign<&Self> for GuardedFixed {
fn rem_assign(&mut self, rhs: &Self) { self.0 %= &rhs.0; }
}
#[test]
fn arithassign_ref() {
GuardedFixed::set_dps(2);
let a = GuardedFixed::parse("123.45");
let b = GuardedFixed::parse("678.90");
let mut x = a.clone(); x += &b; assert_eq!(x, GuardedFixed::parse("802.35"));
let mut x = a.clone(); x -= &b; assert_eq!(x, GuardedFixed::parse("-555.45"));
let mut x = a.clone(); x *= &b; assert_eq!(x, GuardedFixed::parse("83810.205"));
let mut x = a.clone(); x /= &b; assert_eq!(x, GuardedFixed::parse("0.18"));
let mut x = b.clone(); x %= &a; assert_eq!(x, GuardedFixed::parse("61.65"));
}
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 { GuardedFixed(&self.0 * &rhs.0 / get_factor()) }
}
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 { GuardedFixed(&self.0 % &rhs.0) }
}
#[test]
fn arith_ref_ref() {
GuardedFixed::set_dps(2);
let a = GuardedFixed::parse("123.45");
let b = GuardedFixed::parse("678.90");
assert_eq!(&a + &b, GuardedFixed::parse("802.35"));
assert_eq!(&a - &b, GuardedFixed::parse("-555.45"));
assert_eq!(&a * &b, GuardedFixed::parse("83810.205"));
assert_eq!(&a / &b, GuardedFixed::parse("0.18"));
assert_eq!(&b % &a, GuardedFixed::parse("61.65"));
}
/*
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 {
}
*/