OpenTally/src/numbers/native.rs

336 lines
11 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 derive_more::Display;
use num_traits::{Num, One, ParseFloatError, Zero};
use std::cmp::{Ord, Ordering, PartialEq, PartialOrd};
use std::ops;
type ImplType = f64;
/// Native 64-bit floating-point number
#[derive(Clone, Debug, Display, PartialEq, PartialOrd)]
pub struct NativeFloat64(ImplType);
impl Number for NativeFloat64 {
fn new() -> Self { Self(0.0) }
fn describe() -> String { "--numbers float64".to_string() }
fn pow_assign(&mut self, exponent: i32) {
self.0 = self.0.powi(exponent);
}
fn floor_mut(&mut self, dps: usize) {
let factor = 10.0_f64.powi(dps as i32);
self.0 = (self.0 * factor).floor() / factor;
}
fn ceil_mut(&mut self, dps: usize) {
let factor = 10.0_f64.powi(dps as i32);
self.0 = (self.0 * factor).ceil() / factor;
}
fn round_mut(&mut self, dps: usize) {
let factor = 10.0_f64.powi(dps as i32);
self.0 = (self.0 * factor).round() / factor;
}
}
#[test]
fn display_debug() {
let x = NativeFloat64::parse("123.4"); assert_eq!(format!("{}", x), format!("{}", 123.40_f64));
let x = NativeFloat64::parse("123.4"); assert_eq!(format!("{:?}", x), format!("NativeFloat64({})", 123.40_f64));
}
#[test]
fn rounding() {
let mut x = NativeFloat64::parse("55.550"); x.floor_mut(2); assert_eq!(x, NativeFloat64::parse("55.55"));
let mut x = NativeFloat64::parse("55.552"); x.floor_mut(2); assert_eq!(x, NativeFloat64::parse("55.55"));
let mut x = NativeFloat64::parse("55.555"); x.floor_mut(2); assert_eq!(x, NativeFloat64::parse("55.55"));
let mut x = NativeFloat64::parse("55.557"); x.floor_mut(2); assert_eq!(x, NativeFloat64::parse("55.55"));
let mut x = NativeFloat64::parse("55.550"); x.ceil_mut(2); assert_eq!(x, NativeFloat64::parse("55.55"));
let mut x = NativeFloat64::parse("55.552"); x.ceil_mut(2); assert_eq!(x, NativeFloat64::parse("55.56"));
let mut x = NativeFloat64::parse("55.555"); x.ceil_mut(2); assert_eq!(x, NativeFloat64::parse("55.56"));
let mut x = NativeFloat64::parse("55.557"); x.ceil_mut(2); assert_eq!(x, NativeFloat64::parse("55.56"));
let mut x = NativeFloat64::parse("55.550"); x.round_mut(2); assert_eq!(x, NativeFloat64::parse("55.55"));
let mut x = NativeFloat64::parse("55.552"); x.round_mut(2); assert_eq!(x, NativeFloat64::parse("55.55"));
let mut x = NativeFloat64::parse("55.555"); x.round_mut(2); assert_eq!(x, NativeFloat64::parse("55.56"));
let mut x = NativeFloat64::parse("55.557"); x.round_mut(2); assert_eq!(x, NativeFloat64::parse("55.56"));
}
impl Num for NativeFloat64 {
type FromStrRadixErr = ParseFloatError;
fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
match f64::from_str_radix(str, radix) {
Ok(value) => Ok(Self(value as ImplType)),
Err(err) => Err(err)
}
}
}
impl Assign for NativeFloat64 {
fn assign(&mut self, src: Self) { self.0 = src.0; }
}
impl Assign<&NativeFloat64> for NativeFloat64 {
fn assign(&mut self, src: &NativeFloat64) { self.0 = src.0; }
}
#[test]
fn assign() {
let a = NativeFloat64::parse("123.45");
let b = NativeFloat64::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 NativeFloat64 {
fn from(n: usize) -> Self { Self(n as ImplType) }
}
impl From<f64> for NativeFloat64 {
fn from(n: f64) -> Self { Self(n as ImplType) }
}
impl One for NativeFloat64 {
fn one() -> Self { Self(1.0) }
}
impl Zero for NativeFloat64 {
fn zero() -> Self { Self::new() }
fn is_zero(&self) -> bool { self.0.is_zero() }
}
impl Eq for NativeFloat64 {}
impl Ord for NativeFloat64 {
fn cmp(&self, other: &Self) -> Ordering { self.0.partial_cmp(&other.0).unwrap() }
}
impl ops::Neg for NativeFloat64 {
type Output = NativeFloat64;
fn neg(self) -> Self::Output { Self(-self.0) }
}
impl ops::Add for NativeFloat64 {
type Output = NativeFloat64;
fn add(self, rhs: Self) -> Self::Output { Self(self.0 + rhs.0) }
}
impl ops::Sub for NativeFloat64 {
type Output = NativeFloat64;
fn sub(self, rhs: Self) -> Self::Output { Self(self.0 - rhs.0) }
}
impl ops::Mul for NativeFloat64 {
type Output = NativeFloat64;
fn mul(self, rhs: Self) -> Self::Output { Self(self.0 * rhs.0) }
}
impl ops::Div for NativeFloat64 {
type Output = NativeFloat64;
fn div(self, rhs: Self) -> Self::Output { Self(self.0 / rhs.0) }
}
impl ops::Rem for NativeFloat64 {
type Output = NativeFloat64;
fn rem(self, rhs: Self) -> Self::Output { Self(self.0 % rhs.0) }
}
#[test]
fn arith_owned_owned() {
let a = NativeFloat64::parse("123.45");
let b = NativeFloat64::parse("678.90");
assert_eq!(a.clone() + b.clone(), NativeFloat64::from(123.45_f64 + 678.90_f64));
assert_eq!(a.clone() - b.clone(), NativeFloat64::from(123.45_f64 - 678.90_f64));
assert_eq!(a.clone() * b.clone(), NativeFloat64::from(123.45_f64 * 678.90_f64));
assert_eq!(a.clone() / b.clone(), NativeFloat64::from(123.45_f64 / 678.90_f64));
assert_eq!(b.clone() % a.clone(), NativeFloat64::from(678.90_f64 % 123.45_f64));
}
impl ops::Add<&NativeFloat64> for NativeFloat64 {
type Output = NativeFloat64;
fn add(self, rhs: &NativeFloat64) -> Self::Output { Self(self.0 + rhs.0) }
}
impl ops::Sub<&NativeFloat64> for NativeFloat64 {
type Output = NativeFloat64;
fn sub(self, rhs: &NativeFloat64) -> Self::Output { Self(self.0 - rhs.0) }
}
impl ops::Mul<&NativeFloat64> for NativeFloat64 {
type Output = NativeFloat64;
fn mul(self, rhs: &NativeFloat64) -> Self::Output { NativeFloat64(self.0 * rhs.0) }
}
impl ops::Div<&NativeFloat64> for NativeFloat64 {
type Output = NativeFloat64;
fn div(self, rhs: &NativeFloat64) -> Self::Output { NativeFloat64(self.0 / rhs.0) }
}
impl ops::Rem<&NativeFloat64> for NativeFloat64 {
type Output = NativeFloat64;
fn rem(self, rhs: &NativeFloat64) -> Self::Output { NativeFloat64(self.0 % rhs.0) }
}
#[test]
fn arith_owned_ref() {
let a = NativeFloat64::parse("123.45");
let b = NativeFloat64::parse("678.90");
assert_eq!(a.clone() + &b, NativeFloat64::from(123.45_f64 + 678.90_f64));
assert_eq!(a.clone() - &b, NativeFloat64::from(123.45_f64 - 678.90_f64));
assert_eq!(a.clone() * &b, NativeFloat64::from(123.45_f64 * 678.90_f64));
assert_eq!(a.clone() / &b, NativeFloat64::from(123.45_f64 / 678.90_f64));
assert_eq!(b.clone() % &a, NativeFloat64::from(678.90_f64 % 123.45_f64));
}
impl ops::AddAssign for NativeFloat64 {
fn add_assign(&mut self, rhs: Self) { self.0 += rhs.0; }
}
impl ops::SubAssign for NativeFloat64 {
fn sub_assign(&mut self, rhs: Self) { self.0 -= rhs.0; }
}
impl ops::MulAssign for NativeFloat64 {
fn mul_assign(&mut self, rhs: Self) { self.0 *= rhs.0; }
}
impl ops::DivAssign for NativeFloat64 {
fn div_assign(&mut self, rhs: Self) { self.0 /= rhs.0; }
}
impl ops::RemAssign for NativeFloat64 {
fn rem_assign(&mut self, rhs: Self) { self.0 %= rhs.0; }
}
#[test]
fn arithassign_owned() {
let a = NativeFloat64::parse("123.45");
let b = NativeFloat64::parse("678.90");
let mut x = a.clone(); x += b.clone(); assert_eq!(x, NativeFloat64::from(123.45_f64 + 678.90_f64));
let mut x = a.clone(); x -= b.clone(); assert_eq!(x, NativeFloat64::from(123.45_f64 - 678.90_f64));
let mut x = a.clone(); x *= b.clone(); assert_eq!(x, NativeFloat64::from(123.45_f64 * 678.90_f64));
let mut x = a.clone(); x /= b.clone(); assert_eq!(x, NativeFloat64::from(123.45_f64 / 678.90_f64));
let mut x = b.clone(); x %= a.clone(); assert_eq!(x, NativeFloat64::from(678.90_f64 % 123.45_f64));
}
impl ops::AddAssign<&NativeFloat64> for NativeFloat64 {
fn add_assign(&mut self, rhs: &NativeFloat64) { self.0 += rhs.0; }
}
impl ops::SubAssign<&NativeFloat64> for NativeFloat64 {
fn sub_assign(&mut self, rhs: &NativeFloat64) { self.0 -= rhs.0; }
}
impl ops::MulAssign<&NativeFloat64> for NativeFloat64 {
fn mul_assign(&mut self, rhs: &NativeFloat64) { self.0 *= rhs.0; }
}
impl ops::DivAssign<&NativeFloat64> for NativeFloat64 {
fn div_assign(&mut self, rhs: &NativeFloat64) { self.0 /= rhs.0; }
}
impl ops::RemAssign<&NativeFloat64> for NativeFloat64 {
fn rem_assign(&mut self, rhs: &NativeFloat64) { self.0 %= rhs.0; }
}
#[test]
fn arithassign_ref() {
let a = NativeFloat64::parse("123.45");
let b = NativeFloat64::parse("678.90");
let mut x = a.clone(); x += &b; assert_eq!(x, NativeFloat64::from(123.45_f64 + 678.90_f64));
let mut x = a.clone(); x -= &b; assert_eq!(x, NativeFloat64::from(123.45_f64 - 678.90_f64));
let mut x = a.clone(); x *= &b; assert_eq!(x, NativeFloat64::from(123.45_f64 * 678.90_f64));
let mut x = a.clone(); x /= &b; assert_eq!(x, NativeFloat64::from(123.45_f64 / 678.90_f64));
let mut x = b.clone(); x %= &a; assert_eq!(x, NativeFloat64::from(678.90_f64 % 123.45_f64));
}
impl ops::Neg for &NativeFloat64 {
type Output = NativeFloat64;
fn neg(self) -> Self::Output { NativeFloat64(-self.0) }
}
impl ops::Add<Self> for &NativeFloat64 {
type Output = NativeFloat64;
fn add(self, rhs: &NativeFloat64) -> Self::Output { NativeFloat64(self.0 + rhs.0) }
}
impl ops::Sub<Self> for &NativeFloat64 {
type Output = NativeFloat64;
fn sub(self, rhs: &NativeFloat64) -> Self::Output { NativeFloat64(self.0 - rhs.0) }
}
impl ops::Mul<Self> for &NativeFloat64 {
type Output = NativeFloat64;
fn mul(self, rhs: &NativeFloat64) -> Self::Output { NativeFloat64(self.0 * rhs.0) }
}
impl ops::Div<Self> for &NativeFloat64 {
type Output = NativeFloat64;
fn div(self, rhs: &NativeFloat64) -> Self::Output { NativeFloat64(self.0 / rhs.0) }
}
impl ops::Rem<Self> for &NativeFloat64 {
type Output = NativeFloat64;
fn rem(self, rhs: &NativeFloat64) -> Self::Output { NativeFloat64(self.0 % rhs.0) }
}
#[test]
fn arith_ref_ref() {
let a = NativeFloat64::parse("123.45");
let b = NativeFloat64::parse("678.90");
assert_eq!(&a + &b, NativeFloat64::from(123.45_f64 + 678.90_f64));
assert_eq!(&a - &b, NativeFloat64::from(123.45_f64 - 678.90_f64));
assert_eq!(&a * &b, NativeFloat64::from(123.45_f64 * 678.90_f64));
assert_eq!(&a / &b, NativeFloat64::from(123.45_f64 / 678.90_f64));
assert_eq!(&b % &a, NativeFloat64::from(678.90_f64 % 123.45_f64));
}
/*
impl ops::Add<&&NativeFloat> for &NativeFloat {
}
impl ops::Sub<&&NativeFloat> for &NativeFloat {
}
impl ops::Mul<&&NativeFloat> for &NativeFloat {
}
impl ops::Div<&&NativeFloat> for &NativeFloat {
}
impl ops::Rem<&&NativeFloat> for &NativeFloat {
}
*/