OpenTally/src/numbers/rational_num.rs

322 lines
7.4 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, From, Number};
use num_bigint::{BigInt, ParseBigIntError};
use num_rational::BigRational; // TODO: Can we do Ratio<IBig> and combine with ibig?
use num_traits::{Num, One, Signed, Zero};
use std::cmp::{Ord, Ordering, PartialEq, PartialOrd};
use std::fmt;
use std::ops;
pub struct Rational(BigRational);
impl Number for Rational {
fn new() -> Self { Self(BigRational::zero()) }
fn pow_assign(&mut self, exponent: i32) {
self.0 = self.0.pow(exponent);
}
fn floor_mut(&mut self, dps: usize) {
if dps == 0 {
self.0 = self.0.floor();
} else {
let factor = BigRational::from_integer(BigInt::from(10)).pow(dps as i32);
self.0 *= &factor;
self.0 = self.0.floor();
self.0 /= factor;
}
}
fn ceil_mut(&mut self, dps: usize) {
if dps == 0 {
self.0 = self.0.ceil();
} else {
let factor = BigRational::from_integer(BigInt::from(10)).pow(dps as i32);
self.0 *= &factor;
self.0 = self.0.ceil();
self.0 /= factor;
}
}
}
impl Num for Rational {
//type FromStrRadixErr = ParseRatioError;
type FromStrRadixErr = ParseBigIntError;
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) {
Ok(value) => Ok(Self(BigRational::from_integer(value))),
Err(err) => Err(err)
}
}
}
impl Assign for Rational {
fn assign(&mut self, src: Self) { self.0 = src.0 }
}
impl Assign<&Rational> for Rational {
fn assign(&mut self, src: &Rational) { self.0 = src.0.clone() }
}
impl From<usize> for Rational {
fn from(n: usize) -> Self { Self(BigRational::from_integer(BigInt::from(n))) }
}
impl From<f64> for Rational {
fn from(n: f64) -> Self {
// FIXME: This is very broken!
return Self(BigRational::from_float(n).unwrap() * BigRational::from_integer(BigInt::from(100000)).round() / BigRational::from_integer(BigInt::from(100000)));
}
}
impl Clone for Rational {
fn clone(&self) -> Self { Self(self.0.clone()) }
}
impl fmt::Display for Rational {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(precision) = f.precision() {
if precision == 0 {
let result = self.0.round().to_integer().to_string();
return f.write_str(&result);
} else {
let base = BigInt::from(10).pow(precision as u32);
let mut result = (&self.0 * base).abs().round().to_integer().to_string();
let should_add_minus = (self.0 < BigRational::zero()) && result != "0";
// Add leading 0s
result = format!("{0:0>1$}", result, precision + 1);
// Add the decimal point
result.insert(result.len() - precision, '.');
// Add the sign
if should_add_minus {
result.insert(0, '-');
}
return f.write_str(&result);
}
} else {
return self.0.fmt(f);
}
}
}
impl One for Rational {
fn one() -> Self { Self(BigRational::one()) }
}
impl Zero for Rational {
fn zero() -> Self { Self::new() }
fn is_zero(&self) -> bool { self.0.is_zero() }
}
impl Eq for Rational {}
impl PartialEq for Rational {
fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
}
impl Ord for Rational {
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 {
type Output = Rational;
fn neg(self) -> Self::Output { Self(-self.0) }
}
impl ops::Add for Rational {
type Output = Rational;
fn add(self, _rhs: Self) -> Self::Output {
todo!()
}
}
impl ops::Sub for Rational {
type Output = Rational;
fn sub(self, _rhs: Self) -> Self::Output {
todo!()
}
}
impl ops::Mul for Rational {
type Output = Rational;
fn mul(self, _rhs: Self) -> Self::Output {
todo!()
}
}
impl ops::Div for Rational {
type Output = Rational;
fn div(self, _rhs: Self) -> Self::Output {
todo!()
}
}
impl ops::Rem for Rational {
type Output = Rational;
fn rem(self, _rhs: Self) -> Self::Output {
todo!()
}
}
impl ops::Add<&Rational> for Rational {
type Output = Rational;
fn add(self, rhs: &Rational) -> Self::Output { Self(self.0 + &rhs.0) }
}
impl ops::Sub<&Rational> for Rational {
type Output = Rational;
fn sub(self, _rhs: &Rational) -> Self::Output {
todo!()
}
}
impl ops::Mul<&Rational> for Rational {
type Output = Rational;
fn mul(self, rhs: &Rational) -> Self::Output { Rational(self.0 * &rhs.0) }
}
impl ops::Div<&Rational> for Rational {
type Output = Rational;
fn div(self, rhs: &Rational) -> Self::Output { Rational(self.0 / &rhs.0) }
}
impl ops::Rem<&Rational> for Rational {
type Output = Rational;
fn rem(self, _rhs: &Rational) -> Self::Output {
todo!()
}
}
impl ops::AddAssign for Rational {
fn add_assign(&mut self, rhs: Self) { self.0 += rhs.0 }
}
impl ops::SubAssign for Rational {
fn sub_assign(&mut self, rhs: Self) { self.0 -= rhs.0 }
}
impl ops::MulAssign for Rational {
fn mul_assign(&mut self, rhs: Self) { self.0 *= rhs.0 }
}
impl ops::DivAssign for Rational {
fn div_assign(&mut self, rhs: Self) { self.0 /= &rhs.0 }
}
impl ops::RemAssign for Rational {
fn rem_assign(&mut self, _rhs: Self) {
todo!()
}
}
impl ops::AddAssign<&Rational> for Rational {
fn add_assign(&mut self, rhs: &Rational) { self.0 += &rhs.0 }
}
impl ops::SubAssign<&Rational> for Rational {
fn sub_assign(&mut self, rhs: &Rational) { self.0 -= &rhs.0 }
}
impl ops::MulAssign<&Rational> for Rational {
fn mul_assign(&mut self, rhs: &Rational) { self.0 *= &rhs.0 }
}
impl ops::DivAssign<&Rational> for Rational {
fn div_assign(&mut self, _rhs: &Rational) {
todo!()
}
}
impl ops::RemAssign<&Rational> for Rational {
fn rem_assign(&mut self, _rhs: &Rational) {
todo!()
}
}
impl ops::Neg for &Rational {
type Output = Rational;
fn neg(self) -> Self::Output { Rational(-&self.0) }
}
impl ops::Add<&Rational> for &Rational {
type Output = Rational;
fn add(self, _rhs: &Rational) -> Self::Output {
todo!()
}
}
impl ops::Sub<&Rational> for &Rational {
type Output = Rational;
fn sub(self, rhs: &Rational) -> Self::Output { Rational(&self.0 - &rhs.0) }
}
impl ops::Mul<&Rational> for &Rational {
type Output = Rational;
fn mul(self, _rhs: &Rational) -> Self::Output {
todo!()
}
}
impl ops::Div<&Rational> for &Rational {
type Output = Rational;
fn div(self, rhs: &Rational) -> Self::Output { Rational(&self.0 / &rhs.0) }
}
impl ops::Rem<&Rational> for &Rational {
type Output = Rational;
fn rem(self, _rhs: &Rational) -> Self::Output {
todo!()
}
}
/*
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 {
}
*/