2021-05-28 11:58:40 +02:00
|
|
|
/* 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/>.
|
|
|
|
*/
|
|
|
|
|
2021-06-14 12:43:36 +02:00
|
|
|
use super::{Assign, Number};
|
2021-05-28 19:01:07 +02:00
|
|
|
|
2021-05-30 15:00:28 +02:00
|
|
|
use num_traits::{Num, One, Signed, Zero};
|
2021-05-28 11:58:40 +02:00
|
|
|
|
2021-05-29 18:28:52 +02:00
|
|
|
use std::cmp::{Ord, Ordering, PartialEq, PartialOrd};
|
2021-05-28 11:58:40 +02:00
|
|
|
use std::fmt;
|
|
|
|
use std::ops;
|
|
|
|
|
2021-06-05 07:08:07 +02:00
|
|
|
type RatioBase = num_bigint::BigInt;
|
|
|
|
type RatioType = num_rational::BigRational;
|
|
|
|
|
2021-06-18 10:48:12 +02:00
|
|
|
/// Rational number
|
2021-06-16 12:28:03 +02:00
|
|
|
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
2021-06-05 07:08:07 +02:00
|
|
|
pub struct Rational(RatioType);
|
2021-05-28 11:58:40 +02:00
|
|
|
|
|
|
|
impl Number for Rational {
|
2021-06-05 07:08:07 +02:00
|
|
|
fn new() -> Self { Self(RatioType::zero()) }
|
2021-05-28 11:58:40 +02:00
|
|
|
|
2021-06-03 13:35:25 +02:00
|
|
|
fn describe() -> String { "--numbers rational".to_string() }
|
2021-06-12 08:03:31 +02:00
|
|
|
fn describe_opt() -> String { String::new() }
|
2021-06-03 13:35:25 +02:00
|
|
|
|
2021-06-01 13:20:38 +02:00
|
|
|
fn pow_assign(&mut self, exponent: i32) {
|
|
|
|
self.0 = self.0.pow(exponent);
|
|
|
|
}
|
2021-05-28 11:58:40 +02:00
|
|
|
|
2021-05-29 09:51:45 +02:00
|
|
|
fn floor_mut(&mut self, dps: usize) {
|
|
|
|
if dps == 0 {
|
2021-05-30 15:00:28 +02:00
|
|
|
self.0 = self.0.floor();
|
2021-05-29 09:51:45 +02:00
|
|
|
} else {
|
2021-06-05 07:08:07 +02:00
|
|
|
let factor = RatioType::from_integer(RatioBase::from(10)).pow(dps as i32);
|
2021-05-29 09:51:45 +02:00
|
|
|
self.0 *= &factor;
|
2021-05-30 15:00:28 +02:00
|
|
|
self.0 = self.0.floor();
|
2021-05-29 09:51:45 +02:00
|
|
|
self.0 /= factor;
|
|
|
|
}
|
|
|
|
}
|
2021-06-02 10:07:05 +02:00
|
|
|
|
|
|
|
fn ceil_mut(&mut self, dps: usize) {
|
|
|
|
if dps == 0 {
|
|
|
|
self.0 = self.0.ceil();
|
|
|
|
} else {
|
2021-06-05 07:08:07 +02:00
|
|
|
let factor = RatioType::from_integer(RatioBase::from(10)).pow(dps as i32);
|
2021-06-02 10:07:05 +02:00
|
|
|
self.0 *= &factor;
|
|
|
|
self.0 = self.0.ceil();
|
|
|
|
self.0 /= factor;
|
|
|
|
}
|
|
|
|
}
|
2021-06-18 10:48:12 +02:00
|
|
|
|
2021-08-03 10:38:45 +02:00
|
|
|
fn round_mut(&mut self, dps: usize) {
|
|
|
|
if dps == 0 {
|
|
|
|
self.0 = self.0.round();
|
|
|
|
} else {
|
|
|
|
// TODO: Streamline
|
|
|
|
let mut factor = Self::from(10);
|
|
|
|
factor.pow_assign(-(dps as i32));
|
|
|
|
factor /= Self::from(2);
|
|
|
|
|
|
|
|
*self = self.clone() - factor;
|
|
|
|
self.ceil_mut(dps);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-18 10:48:12 +02:00
|
|
|
fn parse(s: &str) -> Self {
|
|
|
|
// Parse decimal
|
|
|
|
if s.contains('.') {
|
|
|
|
let (whole, decimal) = s.split_once('.').unwrap();
|
|
|
|
let whole = match RatioBase::from_str_radix(whole, 10) {
|
|
|
|
Ok(value) => RatioType::from_integer(value),
|
|
|
|
Err(_) => panic!("Syntax Error"),
|
|
|
|
};
|
|
|
|
let decimal = match RatioBase::from_str_radix(decimal, 10) {
|
|
|
|
Ok(value) => RatioType::from_integer(value),
|
|
|
|
Err(_) => panic!("Syntax Error"),
|
|
|
|
} / RatioBase::from(10).pow(decimal.len() as u32);
|
|
|
|
return Self(whole + decimal);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse integer
|
|
|
|
if let Ok(value) = Self::from_str_radix(s, 10) {
|
|
|
|
return value;
|
|
|
|
} else {
|
|
|
|
panic!("Syntax Error");
|
|
|
|
}
|
|
|
|
}
|
2021-05-28 11:58:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Num for Rational {
|
2021-06-05 07:08:07 +02:00
|
|
|
type FromStrRadixErr = <RatioBase as Num>::FromStrRadixErr;
|
2021-05-28 11:58:40 +02:00
|
|
|
fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
|
2021-06-05 07:08:07 +02:00
|
|
|
match RatioBase::from_str_radix(str, radix) {
|
|
|
|
Ok(value) => Ok(Self(RatioType::from_integer(value))),
|
2021-05-28 11:58:40 +02:00
|
|
|
Err(err) => Err(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Assign for Rational {
|
2021-05-30 15:00:28 +02:00
|
|
|
fn assign(&mut self, src: Self) { self.0 = src.0 }
|
2021-05-28 11:58:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Assign<&Rational> for Rational {
|
2021-05-30 15:00:28 +02:00
|
|
|
fn assign(&mut self, src: &Rational) { self.0 = src.0.clone() }
|
2021-05-28 11:58:40 +02:00
|
|
|
}
|
|
|
|
|
2021-06-01 13:20:38 +02:00
|
|
|
impl From<usize> for Rational {
|
2021-06-05 07:08:07 +02:00
|
|
|
fn from(n: usize) -> Self { Self(RatioType::from_integer(RatioBase::from(n))) }
|
2021-06-01 13:20:38 +02:00
|
|
|
}
|
|
|
|
|
2021-05-28 11:58:40 +02:00
|
|
|
impl fmt::Display for Rational {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
if let Some(precision) = f.precision() {
|
|
|
|
if precision == 0 {
|
2021-05-30 15:00:28 +02:00
|
|
|
let result = self.0.round().to_integer().to_string();
|
2021-05-28 11:58:40 +02:00
|
|
|
return f.write_str(&result);
|
|
|
|
} else {
|
2021-06-05 07:08:07 +02:00
|
|
|
let base = RatioBase::from(10).pow(precision as u32);
|
2021-05-30 15:00:28 +02:00
|
|
|
let mut result = (&self.0 * base).abs().round().to_integer().to_string();
|
2021-05-29 09:51:45 +02:00
|
|
|
|
2021-06-05 07:08:07 +02:00
|
|
|
let should_add_minus = (self.0 < RatioType::zero()) && result != "0";
|
2021-05-28 11:58:40 +02:00
|
|
|
|
|
|
|
// Add leading 0s
|
|
|
|
result = format!("{0:0>1$}", result, precision + 1);
|
|
|
|
|
|
|
|
// Add the decimal point
|
|
|
|
result.insert(result.len() - precision, '.');
|
|
|
|
|
2021-05-29 09:51:45 +02:00
|
|
|
// Add the sign
|
|
|
|
if should_add_minus {
|
|
|
|
result.insert(0, '-');
|
|
|
|
}
|
|
|
|
|
2021-05-28 11:58:40 +02:00
|
|
|
return f.write_str(&result);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return self.0.fmt(f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl One for Rational {
|
2021-06-05 07:08:07 +02:00
|
|
|
fn one() -> Self { Self(RatioType::one()) }
|
2021-05-28 11:58:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Zero for Rational {
|
|
|
|
fn zero() -> Self { Self::new() }
|
2021-05-30 15:00:28 +02:00
|
|
|
fn is_zero(&self) -> bool { self.0.is_zero() }
|
2021-05-28 11:58:40 +02:00
|
|
|
}
|
|
|
|
|
2021-05-29 18:28:52 +02:00
|
|
|
impl Eq for Rational {}
|
|
|
|
impl Ord for Rational {
|
2021-05-30 15:00:28 +02:00
|
|
|
fn cmp(&self, other: &Self) -> Ordering { self.0.partial_cmp(&other.0).unwrap() }
|
2021-05-28 11:58:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ops::Neg for Rational {
|
|
|
|
type Output = Rational;
|
2021-05-28 19:01:07 +02:00
|
|
|
fn neg(self) -> Self::Output { Self(-self.0) }
|
2021-05-28 11:58:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ops::Add for Rational {
|
|
|
|
type Output = Rational;
|
2021-06-16 05:00:54 +02:00
|
|
|
fn add(self, rhs: Self) -> Self::Output { Self(self.0 + rhs.0) }
|
2021-05-28 11:58:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ops::Sub for Rational {
|
|
|
|
type Output = Rational;
|
|
|
|
fn sub(self, _rhs: Self) -> Self::Output {
|
|
|
|
todo!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ops::Mul for Rational {
|
|
|
|
type Output = Rational;
|
2021-09-10 18:43:11 +02:00
|
|
|
fn mul(self, rhs: Self) -> Self::Output { Self(self.0 * rhs.0) }
|
2021-05-28 11:58:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ops::Div for Rational {
|
|
|
|
type Output = Rational;
|
2021-06-16 05:00:54 +02:00
|
|
|
fn div(self, rhs: Self) -> Self::Output { Self(self.0 / rhs.0) }
|
2021-05-28 11:58:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2021-05-28 19:01:07 +02:00
|
|
|
fn add(self, rhs: &Rational) -> Self::Output { Self(self.0 + &rhs.0) }
|
2021-05-28 11:58:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ops::Sub<&Rational> for Rational {
|
|
|
|
type Output = Rational;
|
2021-06-07 12:52:18 +02:00
|
|
|
fn sub(self, rhs: &Rational) -> Self::Output { Self(self.0 - &rhs.0) }
|
2021-05-28 11:58:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ops::Mul<&Rational> for Rational {
|
|
|
|
type Output = Rational;
|
2021-05-30 15:00:28 +02:00
|
|
|
fn mul(self, rhs: &Rational) -> Self::Output { Rational(self.0 * &rhs.0) }
|
2021-05-28 11:58:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ops::Div<&Rational> for Rational {
|
|
|
|
type Output = Rational;
|
2021-05-30 15:00:28 +02:00
|
|
|
fn div(self, rhs: &Rational) -> Self::Output { Rational(self.0 / &rhs.0) }
|
2021-05-28 11:58:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ops::Rem<&Rational> for Rational {
|
|
|
|
type Output = Rational;
|
|
|
|
fn rem(self, _rhs: &Rational) -> Self::Output {
|
|
|
|
todo!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ops::AddAssign for Rational {
|
2021-06-05 07:08:07 +02:00
|
|
|
fn add_assign(&mut self, rhs: Self) { self.0 += rhs.0; }
|
2021-05-28 11:58:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ops::SubAssign for Rational {
|
2021-06-05 07:08:07 +02:00
|
|
|
fn sub_assign(&mut self, rhs: Self) { self.0 -= rhs.0; }
|
2021-05-28 11:58:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ops::MulAssign for Rational {
|
2021-06-05 07:08:07 +02:00
|
|
|
fn mul_assign(&mut self, rhs: Self) { self.0 *= rhs.0; }
|
2021-05-28 11:58:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ops::DivAssign for Rational {
|
2021-06-05 07:08:07 +02:00
|
|
|
fn div_assign(&mut self, rhs: Self) { self.0 /= &rhs.0; }
|
2021-05-28 11:58:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ops::RemAssign for Rational {
|
|
|
|
fn rem_assign(&mut self, _rhs: Self) {
|
|
|
|
todo!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ops::AddAssign<&Rational> for Rational {
|
2021-06-05 07:08:07 +02:00
|
|
|
fn add_assign(&mut self, rhs: &Rational) { self.0 += &rhs.0; }
|
2021-05-28 11:58:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ops::SubAssign<&Rational> for Rational {
|
2021-06-05 07:08:07 +02:00
|
|
|
fn sub_assign(&mut self, rhs: &Rational) { self.0 -= &rhs.0; }
|
2021-05-28 11:58:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ops::MulAssign<&Rational> for Rational {
|
2021-06-05 07:08:07 +02:00
|
|
|
fn mul_assign(&mut self, rhs: &Rational) { self.0 *= &rhs.0; }
|
2021-05-28 11:58:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ops::DivAssign<&Rational> for Rational {
|
2021-09-10 18:43:11 +02:00
|
|
|
fn div_assign(&mut self, rhs: &Rational) { self.0 /= &rhs.0 }
|
2021-05-28 11:58:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ops::RemAssign<&Rational> for Rational {
|
|
|
|
fn rem_assign(&mut self, _rhs: &Rational) {
|
|
|
|
todo!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-28 16:43:58 +02:00
|
|
|
impl ops::Neg for &Rational {
|
|
|
|
type Output = Rational;
|
2021-05-30 15:00:28 +02:00
|
|
|
fn neg(self) -> Self::Output { Rational(-&self.0) }
|
2021-05-28 16:43:58 +02:00
|
|
|
}
|
|
|
|
|
2021-06-09 12:09:20 +02:00
|
|
|
impl ops::Add<Self> for &Rational {
|
2021-05-28 11:58:40 +02:00
|
|
|
type Output = Rational;
|
2021-06-09 12:09:20 +02:00
|
|
|
fn add(self, rhs: &Rational) -> Self::Output { Rational(&self.0 + &rhs.0) }
|
2021-05-28 11:58:40 +02:00
|
|
|
}
|
|
|
|
|
2021-06-09 12:09:20 +02:00
|
|
|
impl ops::Sub<Self> for &Rational {
|
2021-05-28 11:58:40 +02:00
|
|
|
type Output = Rational;
|
2021-05-30 15:00:28 +02:00
|
|
|
fn sub(self, rhs: &Rational) -> Self::Output { Rational(&self.0 - &rhs.0) }
|
2021-05-28 11:58:40 +02:00
|
|
|
}
|
|
|
|
|
2021-06-09 12:09:20 +02:00
|
|
|
impl ops::Mul<Self> for &Rational {
|
2021-05-28 11:58:40 +02:00
|
|
|
type Output = Rational;
|
2021-06-16 05:00:54 +02:00
|
|
|
fn mul(self, rhs: &Rational) -> Self::Output { Rational(&self.0 * &rhs.0) }
|
2021-05-28 11:58:40 +02:00
|
|
|
}
|
|
|
|
|
2021-06-09 12:09:20 +02:00
|
|
|
impl ops::Div<Self> for &Rational {
|
2021-05-28 11:58:40 +02:00
|
|
|
type Output = Rational;
|
2021-05-30 15:00:28 +02:00
|
|
|
fn div(self, rhs: &Rational) -> Self::Output { Rational(&self.0 / &rhs.0) }
|
2021-05-28 11:58:40 +02:00
|
|
|
}
|
|
|
|
|
2021-06-09 12:09:20 +02:00
|
|
|
impl ops::Rem<Self> for &Rational {
|
2021-05-28 11:58:40 +02:00
|
|
|
type Output = Rational;
|
|
|
|
fn rem(self, _rhs: &Rational) -> Self::Output {
|
|
|
|
todo!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2021-05-30 15:00:28 +02:00
|
|
|
impl ops::Add<&&NativeFloat> for &NativeFloat {
|
2021-05-28 11:58:40 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-05-30 15:00:28 +02:00
|
|
|
impl ops::Sub<&&NativeFloat> for &NativeFloat {
|
2021-05-28 11:58:40 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-05-30 15:00:28 +02:00
|
|
|
impl ops::Mul<&&NativeFloat> for &NativeFloat {
|
2021-05-28 11:58:40 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-05-30 15:00:28 +02:00
|
|
|
impl ops::Div<&&NativeFloat> for &NativeFloat {
|
2021-05-28 11:58:40 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-05-30 15:00:28 +02:00
|
|
|
impl ops::Rem<&&NativeFloat> for &NativeFloat {
|
2021-05-28 11:58:40 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
*/
|