/* 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 . */ use super::{Assign, Fixed, GuardedFixed, NativeFloat64, Number, Rational}; use num_traits::{Num, One, Zero}; //use wasm_bindgen::prelude::wasm_bindgen; use std::cell::Cell; use std::cmp::{Ord, Ordering}; use std::fmt; use std::mem::ManuallyDrop; use std::ops::{self, Deref, DerefMut}; /// Represents the underlying implementation for [DynNum]s //#[wasm_bindgen] #[derive(Copy, Clone)] pub enum NumKind { /// See [crate::numbers::Fixed] Fixed, /// See [crate::numbers::GuardedFixed] GuardedFixed, /// See [crate::numbers::NativeFloat64] NativeFloat64, /// See [crate::numbers::Rational] Rational, } thread_local! { /// Determines which underlying implementation to use static KIND: Cell = Cell::new(NumKind::Fixed); } /// Returns which underlying implementation is in use #[inline] fn get_kind() -> NumKind { return KIND.with(|kind_cell| kind_cell.get()); } /// A wrapper for different numeric types using dynamic dispatch pub union DynNum { fixed: ManuallyDrop, gfixed: ManuallyDrop, float64: NativeFloat64, rational: ManuallyDrop, } impl DynNum { /// Set which underlying implementation to use pub fn set_kind(kind: NumKind) { KIND.with(|kind_cell| { kind_cell.set(kind); }); } } macro_rules! impl_1arg_nowrap { ($self:expr, $arg:expr, $func:ident) => { // Safety: Access only correct union field unsafe { match get_kind() { NumKind::Fixed => { $self.fixed.$func($arg) } NumKind::GuardedFixed => { $self.gfixed.$func($arg) } NumKind::NativeFloat64 => { $self.float64.$func($arg) } NumKind::Rational => { $self.rational.$func($arg) } } } } } macro_rules! impl_assoc_nowrap { ($func:ident) => { match get_kind() { NumKind::Fixed => { Fixed::$func() } NumKind::GuardedFixed => { GuardedFixed::$func() } NumKind::NativeFloat64 => { NativeFloat64::$func() } NumKind::Rational => { Rational::$func() } } } } impl Number for DynNum { fn new() -> Self { match get_kind() { NumKind::Fixed => { DynNum { fixed: ManuallyDrop::new(Fixed::new()) } } NumKind::GuardedFixed => { DynNum { gfixed: ManuallyDrop::new(GuardedFixed::new()) } } NumKind::NativeFloat64 => { DynNum { float64: NativeFloat64::new() } } NumKind::Rational => { DynNum { rational: ManuallyDrop::new(Rational::new()) } } } } fn parse(str: &str) -> Self { // Separate implementation required as e.g. Fixed from_str_radix does not support decimals match get_kind() { NumKind::Fixed => { DynNum { fixed: ManuallyDrop::new(Fixed::parse(str)) } } NumKind::GuardedFixed => { DynNum { gfixed: ManuallyDrop::new(GuardedFixed::parse(str)) } } NumKind::NativeFloat64 => { DynNum { float64: NativeFloat64::parse(str) } } NumKind::Rational => { DynNum { rational: ManuallyDrop::new(Rational::parse(str)) } } } } fn describe() -> String { impl_assoc_nowrap!(describe) } fn pow_assign(&mut self, exponent: i32) { impl_1arg_nowrap!(self, exponent, pow_assign) } fn floor_mut(&mut self, dps: usize) { impl_1arg_nowrap!(self, dps, floor_mut) } fn ceil_mut(&mut self, dps: usize) { impl_1arg_nowrap!(self, dps, ceil_mut) } fn round_mut(&mut self, dps: usize) { impl_1arg_nowrap!(self, dps, round_mut) } } #[test] fn rounding() { // Must specify scope so references are dropped at the correct time, before KIND is changed { DynNum::set_kind(NumKind::Fixed); Fixed::set_dps(5); let mut x = DynNum::parse("55.557"); x.floor_mut(2); assert_eq!(x, DynNum::parse("55.55")); let mut x = DynNum::parse("55.557"); x.ceil_mut(2); assert_eq!(x, DynNum::parse("55.56")); let mut x = DynNum::parse("55.557"); x.round_mut(2); assert_eq!(x, DynNum::parse("55.56")); } { DynNum::set_kind(NumKind::GuardedFixed); GuardedFixed::set_dps(5); let mut x = DynNum::parse("55.557"); x.floor_mut(2); assert_eq!(x, DynNum::parse("55.55")); let mut x = DynNum::parse("55.557"); x.ceil_mut(2); assert_eq!(x, DynNum::parse("55.56")); let mut x = DynNum::parse("55.557"); x.round_mut(2); assert_eq!(x, DynNum::parse("55.56")); } { DynNum::set_kind(NumKind::NativeFloat64); let mut x = DynNum::parse("55.557"); x.floor_mut(2); assert_eq!(x, DynNum::parse("55.55")); let mut x = DynNum::parse("55.557"); x.ceil_mut(2); assert_eq!(x, DynNum::parse("55.56")); let mut x = DynNum::parse("55.557"); x.round_mut(2); assert_eq!(x, DynNum::parse("55.56")); } { DynNum::set_kind(NumKind::Rational); let mut x = DynNum::parse("55.557"); x.floor_mut(2); assert_eq!(x, DynNum::parse("55.55")); let mut x = DynNum::parse("55.557"); x.ceil_mut(2); assert_eq!(x, DynNum::parse("55.56")); let mut x = DynNum::parse("55.557"); x.round_mut(2); assert_eq!(x, DynNum::parse("55.56")); } } impl Drop for DynNum { fn drop(&mut self) { // Safety: Access only correct union field unsafe { match get_kind() { NumKind::Fixed => { ManuallyDrop::drop(&mut self.fixed); } NumKind::GuardedFixed => { ManuallyDrop::drop(&mut self.gfixed); } NumKind::NativeFloat64 => {} NumKind::Rational => { ManuallyDrop::drop(&mut self.rational); } } } } } macro_rules! impl_0arg_wrap { ($self:expr, $func:ident) => { // Safety: Access only correct union field unsafe { match get_kind() { NumKind::Fixed => { DynNum { fixed: ManuallyDrop::new($self.fixed.deref().$func()) } } NumKind::GuardedFixed => { DynNum { gfixed: ManuallyDrop::new($self.gfixed.deref().$func()) } } NumKind::NativeFloat64 => { DynNum { float64: $self.float64.$func() } } NumKind::Rational => { DynNum { rational: ManuallyDrop::new($self.rational.deref().$func()) } } } } } } impl Clone for DynNum { fn clone(&self) -> Self { impl_0arg_wrap!(self, clone) } } impl Num for DynNum { type FromStrRadixErr = Self; // TODO fn from_str_radix(str: &str, radix: u32) -> Result { match get_kind() { NumKind::Fixed => { Ok(DynNum { fixed: ManuallyDrop::new(Fixed::from_str_radix(str, radix).unwrap()) }) } NumKind::GuardedFixed => { Ok(DynNum { gfixed: ManuallyDrop::new(GuardedFixed::from_str_radix(str, radix).unwrap()) }) } NumKind::NativeFloat64 => { Ok(DynNum { float64: NativeFloat64::from_str_radix(str, radix).unwrap() }) } NumKind::Rational => { Ok(DynNum { rational: ManuallyDrop::new(Rational::from_str_radix(str, radix).unwrap()) }) } } } } macro_rules! impl_1other_nowrap { ($self:expr, $rhs:expr, $func:ident) => { // Safety: Access only correct union field unsafe { match get_kind() { NumKind::Fixed => { $self.fixed.deref().$func($rhs.fixed.deref()) } NumKind::GuardedFixed => { $self.gfixed.deref().$func($rhs.gfixed.deref()) } NumKind::NativeFloat64 => { $self.float64.$func(&$rhs.float64) } NumKind::Rational => { $self.rational.deref().$func($rhs.rational.deref()) } } } } } macro_rules! impl_1other_nowrap_mut { ($self:expr, $rhs:expr, $func:ident) => { // Safety: Access only correct union field unsafe { match get_kind() { NumKind::Fixed => { $self.fixed.deref_mut().$func($rhs.fixed.deref()) } NumKind::GuardedFixed => { $self.gfixed.deref_mut().$func($rhs.gfixed.deref()) } NumKind::NativeFloat64 => { $self.float64.$func(&$rhs.float64) } NumKind::Rational => { $self.rational.deref_mut().$func($rhs.rational.deref()) } } } } } impl Assign for DynNum { fn assign(&mut self, src: Self) { impl_1other_nowrap_mut!(self, src, assign) } } impl Assign<&Self> for DynNum { fn assign(&mut self, src: &Self) { impl_1other_nowrap_mut!(self, src, assign) } } #[test] fn assign() { { DynNum::set_kind(NumKind::Fixed); Fixed::set_dps(2); let a = DynNum::parse("123.45"); let b = DynNum::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); } { DynNum::set_kind(NumKind::GuardedFixed); GuardedFixed::set_dps(2); let a = DynNum::parse("123.45"); let b = DynNum::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); } { DynNum::set_kind(NumKind::NativeFloat64); let a = DynNum::parse("123.45"); let b = DynNum::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); } { DynNum::set_kind(NumKind::Rational); let a = DynNum::parse("123.45"); let b = DynNum::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 for DynNum { fn from(n: usize) -> Self { match get_kind() { NumKind::Fixed => { DynNum { fixed: ManuallyDrop::new(Fixed::from(n)) } } NumKind::GuardedFixed => { DynNum { gfixed: ManuallyDrop::new(GuardedFixed::from(n)) } } NumKind::NativeFloat64 => { DynNum { float64: NativeFloat64::from(n) } } NumKind::Rational => { DynNum { rational: ManuallyDrop::new(Rational::from(n)) } } } } } impl fmt::Display for DynNum { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { impl_1arg_nowrap!(self, f, fmt) } } impl fmt::Debug for DynNum { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // Safety: Access only correct union field unsafe { match get_kind() { NumKind::Fixed => { self.fixed.deref().fmt(f) } NumKind::GuardedFixed => { self.gfixed.deref().fmt(f) } NumKind::NativeFloat64 => { self.float64.fmt(f) } NumKind::Rational => { self.rational.deref().fmt(f) } } } } } #[test] fn display_debug() { { DynNum::set_kind(NumKind::Fixed); Fixed::set_dps(2); let x = DynNum::parse("123.4"); assert_eq!(format!("{}", x), "123.40"); let x = DynNum::parse("123.4"); assert_eq!(format!("{:?}", x), "Fixed(12340)"); } { DynNum::set_kind(NumKind::GuardedFixed); GuardedFixed::set_dps(2); let x = DynNum::parse("123.4"); assert_eq!(format!("{}", x), "123.40"); let x = DynNum::parse("123.4"); assert_eq!(format!("{:?}", x), "GuardedFixed(1234000)"); } { DynNum::set_kind(NumKind::NativeFloat64); let x = DynNum::parse("123.4"); assert_eq!(format!("{}", x), format!("{}", 123.40_f64)); let x = DynNum::parse("123.4"); assert_eq!(format!("{:?}", x), format!("NativeFloat64({})", 123.40_f64)); } { DynNum::set_kind(NumKind::Rational); let x = DynNum::parse("123.4"); assert_eq!(format!("{}", x), "617/5"); let x = DynNum::parse("123.4"); assert_eq!(format!("{:.2}", x), "123.40"); let x = DynNum::parse("123.4"); assert_eq!(format!("{:?}", x), "Rational(617/5)"); } } impl PartialEq for DynNum { fn eq(&self, other: &Self) -> bool { impl_1other_nowrap!(self, other, eq) } } impl Eq for DynNum {} impl PartialOrd for DynNum { fn partial_cmp(&self, other: &Self) -> Option { impl_1other_nowrap!(self, other, partial_cmp) } } impl Ord for DynNum { fn cmp(&self, other: &Self) -> Ordering { impl_1other_nowrap!(self, other, cmp) } } macro_rules! impl_assoc_wrap { ($func:ident) => { match get_kind() { NumKind::Fixed => { DynNum { fixed: ManuallyDrop::new(Fixed::$func()) } } NumKind::GuardedFixed => { DynNum { gfixed: ManuallyDrop::new(GuardedFixed::$func()) } } NumKind::NativeFloat64 => { DynNum { float64: NativeFloat64::$func() } } NumKind::Rational => { DynNum { rational: ManuallyDrop::new(Rational::$func()) } } } } } impl One for DynNum { fn one() -> Self { impl_assoc_wrap!(one) } } macro_rules! impl_0arg_nowrap { ($self:expr, $func:ident) => { // Safety: Access only correct union field unsafe { match get_kind() { NumKind::Fixed => { $self.fixed.deref().$func() } NumKind::GuardedFixed => { $self.gfixed.deref().$func() } NumKind::NativeFloat64 => { $self.float64.$func() } NumKind::Rational => { $self.rational.deref().$func() } } } } } impl Zero for DynNum { fn zero() -> Self { impl_assoc_wrap!(zero) } fn is_zero(&self) -> bool { impl_0arg_nowrap!(self, is_zero) } } impl ops::Neg for DynNum { type Output = Self; fn neg(self) -> Self::Output { impl_0arg_wrap!(self, neg) } } macro_rules! impl_1other_wrap { ($self:expr, $rhs:expr, $func:ident) => { // Safety: Access only correct union field unsafe { match get_kind() { NumKind::Fixed => { DynNum { fixed: ManuallyDrop::new($self.fixed.deref().$func($rhs.fixed.deref())) } } NumKind::GuardedFixed => { DynNum { gfixed: ManuallyDrop::new($self.gfixed.deref().$func($rhs.gfixed.deref())) } } NumKind::NativeFloat64 => { DynNum { float64: $self.float64.$func($rhs.float64) } } NumKind::Rational => { DynNum { rational: ManuallyDrop::new($self.rational.deref().$func($rhs.rational.deref())) } } } } } } impl ops::Add for DynNum { type Output = Self; fn add(self, rhs: Self) -> Self::Output { impl_1other_wrap!(self, rhs, add) } } impl ops::Sub for DynNum { type Output = Self; fn sub(self, rhs: Self) -> Self::Output { impl_1other_wrap!(self, rhs, sub) } } impl ops::Mul for DynNum { type Output = Self; fn mul(self, rhs: Self) -> Self::Output { impl_1other_wrap!(self, rhs, mul) } } impl ops::Div for DynNum { type Output = Self; fn div(self, rhs: Self) -> Self::Output { impl_1other_wrap!(self, rhs, div) } } impl ops::Rem for DynNum { type Output = Self; fn rem(self, rhs: Self) -> Self::Output { impl_1other_wrap!(self, rhs, rem) } } #[test] fn arith_owned_owned() { { DynNum::set_kind(NumKind::Fixed); Fixed::set_dps(2); let a = DynNum::parse("123.45"); let b = DynNum::parse("678.90"); assert_eq!(a.clone() + b.clone(), DynNum::parse("802.35")); assert_eq!(a.clone() - b.clone(), DynNum::parse("-555.45")); assert_eq!(a.clone() * b.clone(), DynNum::parse("83810.20")); // = 83810.205 rounds to 83810.20 assert_eq!(a.clone() / b.clone(), DynNum::parse("0.18")); assert_eq!(b.clone() % a.clone(), DynNum::parse("61.65")); } { DynNum::set_kind(NumKind::GuardedFixed); GuardedFixed::set_dps(2); let a = DynNum::parse("123.45"); let b = DynNum::parse("678.90"); assert_eq!(a.clone() + b.clone(), DynNum::parse("802.35")); assert_eq!(a.clone() - b.clone(), DynNum::parse("-555.45")); assert_eq!(a.clone() * b.clone(), DynNum::parse("83810.205")); // Must compare to 3 d.p.s as doesn't meet FACTOR_CMP assert_eq!(a.clone() / b.clone(), DynNum::parse("0.18")); // Meets FACTOR_CMP so compare only 2 d.p.s assert_eq!(b.clone() % a.clone(), DynNum::parse("61.65")); } { DynNum::set_kind(NumKind::NativeFloat64); let a = DynNum::parse("123.45"); let b = DynNum::parse("678.90"); assert_eq!(a.clone() + b.clone(), DynNum { float64: NativeFloat64::from(123.45_f64 + 678.90_f64) }); assert_eq!(a.clone() - b.clone(), DynNum { float64: NativeFloat64::from(123.45_f64 - 678.90_f64) }); assert_eq!(a.clone() * b.clone(), DynNum { float64: NativeFloat64::from(123.45_f64 * 678.90_f64) }); assert_eq!(a.clone() / b.clone(), DynNum { float64: NativeFloat64::from(123.45_f64 / 678.90_f64) }); assert_eq!(b.clone() % a.clone(), DynNum { float64: NativeFloat64::from(678.90_f64 % 123.45_f64) }); } { DynNum::set_kind(NumKind::Rational); let a = DynNum::parse("123.45"); let b = DynNum::parse("678.90"); assert_eq!(a.clone() + b.clone(), DynNum::parse("802.35")); assert_eq!(a.clone() - b.clone(), DynNum::parse("-555.45")); assert_eq!(a.clone() * b.clone(), DynNum::parse("83810.205")); assert_eq!((a.clone() / b.clone()) * b.clone(), a); assert_eq!(b.clone() % a.clone(), DynNum::parse("61.65")); } } impl ops::Add<&Self> for DynNum { type Output = Self; fn add(self, rhs: &Self) -> Self::Output { impl_1other_wrap!(self, rhs, add) } } impl ops::Sub<&Self> for DynNum { type Output = Self; fn sub(self, rhs: &Self) -> Self::Output { impl_1other_wrap!(self, rhs, sub) } } impl ops::Mul<&Self> for DynNum { type Output = Self; fn mul(self, rhs: &Self) -> Self::Output { impl_1other_wrap!(self, rhs, mul) } } impl ops::Div<&Self> for DynNum { type Output = Self; fn div(self, rhs: &Self) -> Self::Output { impl_1other_wrap!(self, rhs, div) } } impl ops::Rem<&Self> for DynNum { type Output = Self; fn rem(self, rhs: &Self) -> Self::Output { impl_1other_wrap!(self, rhs, rem) } } #[test] fn arith_owned_ref() { { DynNum::set_kind(NumKind::Fixed); Fixed::set_dps(2); let a = DynNum::parse("123.45"); let b = DynNum::parse("678.90"); assert_eq!(a.clone() + &b, DynNum::parse("802.35")); assert_eq!(a.clone() - &b, DynNum::parse("-555.45")); assert_eq!(a.clone() * &b, DynNum::parse("83810.20")); assert_eq!(a.clone() / &b, DynNum::parse("0.18")); assert_eq!(b.clone() % &a, DynNum::parse("61.65")); } { DynNum::set_kind(NumKind::GuardedFixed); GuardedFixed::set_dps(2); let a = DynNum::parse("123.45"); let b = DynNum::parse("678.90"); assert_eq!(a.clone() + &b, DynNum::parse("802.35")); assert_eq!(a.clone() - &b, DynNum::parse("-555.45")); assert_eq!(a.clone() * &b, DynNum::parse("83810.205")); assert_eq!(a.clone() / &b, DynNum::parse("0.18")); assert_eq!(b.clone() % &a, DynNum::parse("61.65")); } { DynNum::set_kind(NumKind::NativeFloat64); let a = DynNum::parse("123.45"); let b = DynNum::parse("678.90"); assert_eq!(a.clone() + &b, DynNum { float64: NativeFloat64::from(123.45_f64 + 678.90_f64) }); assert_eq!(a.clone() - &b, DynNum { float64: NativeFloat64::from(123.45_f64 - 678.90_f64) }); assert_eq!(a.clone() * &b, DynNum { float64: NativeFloat64::from(123.45_f64 * 678.90_f64) }); assert_eq!(a.clone() / &b, DynNum { float64: NativeFloat64::from(123.45_f64 / 678.90_f64) }); assert_eq!(b.clone() % &a, DynNum { float64: NativeFloat64::from(678.90_f64 % 123.45_f64) }); } { DynNum::set_kind(NumKind::Rational); let a = DynNum::parse("123.45"); let b = DynNum::parse("678.90"); assert_eq!(a.clone() + &b, DynNum::parse("802.35")); assert_eq!(a.clone() - &b, DynNum::parse("-555.45")); assert_eq!(a.clone() * &b, DynNum::parse("83810.205")); assert_eq!((a.clone() / &b) * &b, a); assert_eq!(b.clone() % &a, DynNum::parse("61.65")); } } impl ops::AddAssign for DynNum { fn add_assign(&mut self, rhs: Self) { impl_1other_nowrap_mut!(self, rhs, add_assign) } } impl ops::SubAssign for DynNum { fn sub_assign(&mut self, rhs: Self) { impl_1other_nowrap_mut!(self, rhs, sub_assign) } } impl ops::MulAssign for DynNum { fn mul_assign(&mut self, rhs: Self) { impl_1other_nowrap_mut!(self, rhs, mul_assign) } } impl ops::DivAssign for DynNum { fn div_assign(&mut self, rhs: Self) { impl_1other_nowrap_mut!(self, rhs, div_assign) } } impl ops::RemAssign for DynNum { fn rem_assign(&mut self, rhs: Self) { impl_1other_nowrap_mut!(self, rhs, rem_assign) } } #[test] fn arithassign_owned() { { DynNum::set_kind(NumKind::Fixed); Fixed::set_dps(2); let a = DynNum::parse("123.45"); let b = DynNum::parse("678.90"); let mut x = a.clone(); x += b.clone(); assert_eq!(x, DynNum::parse("802.35")); let mut x = a.clone(); x -= b.clone(); assert_eq!(x, DynNum::parse("-555.45")); let mut x = a.clone(); x *= b.clone(); assert_eq!(x, DynNum::parse("83810.20")); let mut x = a.clone(); x /= b.clone(); assert_eq!(x, DynNum::parse("0.18")); let mut x = b.clone(); x %= a.clone(); assert_eq!(x, DynNum::parse("61.65")); } { DynNum::set_kind(NumKind::GuardedFixed); GuardedFixed::set_dps(2); let a = DynNum::parse("123.45"); let b = DynNum::parse("678.90"); let mut x = a.clone(); x += b.clone(); assert_eq!(x, DynNum::parse("802.35")); let mut x = a.clone(); x -= b.clone(); assert_eq!(x, DynNum::parse("-555.45")); let mut x = a.clone(); x *= b.clone(); assert_eq!(x, DynNum::parse("83810.205")); let mut x = a.clone(); x /= b.clone(); assert_eq!(x, DynNum::parse("0.18")); let mut x = b.clone(); x %= a.clone(); assert_eq!(x, DynNum::parse("61.65")); } { DynNum::set_kind(NumKind::NativeFloat64); let a = DynNum::parse("123.45"); let b = DynNum::parse("678.90"); let mut x = a.clone(); x += b.clone(); assert_eq!(x, DynNum { float64: NativeFloat64::from(123.45_f64 + 678.90_f64) }); let mut x = a.clone(); x -= b.clone(); assert_eq!(x, DynNum { float64: NativeFloat64::from(123.45_f64 - 678.90_f64) }); let mut x = a.clone(); x *= b.clone(); assert_eq!(x, DynNum { float64: NativeFloat64::from(123.45_f64 * 678.90_f64) }); let mut x = a.clone(); x /= b.clone(); assert_eq!(x, DynNum { float64: NativeFloat64::from(123.45_f64 / 678.90_f64) }); let mut x = b.clone(); x %= a.clone(); assert_eq!(x, DynNum { float64: NativeFloat64::from(678.90_f64 % 123.45_f64) }); } { DynNum::set_kind(NumKind::Rational); let a = DynNum::parse("123.45"); let b = DynNum::parse("678.90"); let mut x = a.clone(); x += b.clone(); assert_eq!(x, DynNum::parse("802.35")); let mut x = a.clone(); x -= b.clone(); assert_eq!(x, DynNum::parse("-555.45")); let mut x = a.clone(); x *= b.clone(); assert_eq!(x, DynNum::parse("83810.205")); let mut x = a.clone(); x /= b.clone(); x *= &b; assert_eq!(x, a); let mut x = b.clone(); x %= a.clone(); assert_eq!(x, DynNum::parse("61.65")); } } impl ops::AddAssign<&Self> for DynNum { fn add_assign(&mut self, rhs: &Self) { impl_1other_nowrap_mut!(self, rhs, add_assign) } } impl ops::SubAssign<&Self> for DynNum { fn sub_assign(&mut self, rhs: &Self) { impl_1other_nowrap_mut!(self, rhs, sub_assign) } } impl ops::MulAssign<&Self> for DynNum { fn mul_assign(&mut self, rhs: &Self) { impl_1other_nowrap_mut!(self, rhs, mul_assign) } } impl ops::DivAssign<&Self> for DynNum { fn div_assign(&mut self, rhs: &Self) { impl_1other_nowrap_mut!(self, rhs, div_assign) } } impl ops::RemAssign<&Self> for DynNum { fn rem_assign(&mut self, rhs: &Self) { impl_1other_nowrap_mut!(self, rhs, rem_assign) } } #[test] fn arithassign_ref() { { DynNum::set_kind(NumKind::Fixed); Fixed::set_dps(2); let a = DynNum::parse("123.45"); let b = DynNum::parse("678.90"); let mut x = a.clone(); x += &b; assert_eq!(x, DynNum::parse("802.35")); let mut x = a.clone(); x -= &b; assert_eq!(x, DynNum::parse("-555.45")); let mut x = a.clone(); x *= &b; assert_eq!(x, DynNum::parse("83810.20")); let mut x = a.clone(); x /= &b; assert_eq!(x, DynNum::parse("0.18")); let mut x = b.clone(); x %= &a; assert_eq!(x, DynNum::parse("61.65")); } { DynNum::set_kind(NumKind::GuardedFixed); GuardedFixed::set_dps(2); let a = DynNum::parse("123.45"); let b = DynNum::parse("678.90"); let mut x = a.clone(); x += &b; assert_eq!(x, DynNum::parse("802.35")); let mut x = a.clone(); x -= &b; assert_eq!(x, DynNum::parse("-555.45")); let mut x = a.clone(); x *= &b; assert_eq!(x, DynNum::parse("83810.205")); let mut x = a.clone(); x /= &b; assert_eq!(x, DynNum::parse("0.18")); let mut x = b.clone(); x %= &a; assert_eq!(x, DynNum::parse("61.65")); } { DynNum::set_kind(NumKind::NativeFloat64); let a = DynNum::parse("123.45"); let b = DynNum::parse("678.90"); let mut x = a.clone(); x += &b; assert_eq!(x, DynNum { float64: NativeFloat64::from(123.45_f64 + 678.90_f64) }); let mut x = a.clone(); x -= &b; assert_eq!(x, DynNum { float64: NativeFloat64::from(123.45_f64 - 678.90_f64) }); let mut x = a.clone(); x *= &b; assert_eq!(x, DynNum { float64: NativeFloat64::from(123.45_f64 * 678.90_f64) }); let mut x = a.clone(); x /= &b; assert_eq!(x, DynNum { float64: NativeFloat64::from(123.45_f64 / 678.90_f64) }); let mut x = b.clone(); x %= &a; assert_eq!(x, DynNum { float64: NativeFloat64::from(678.90_f64 % 123.45_f64) }); } { DynNum::set_kind(NumKind::Rational); let a = DynNum::parse("123.45"); let b = DynNum::parse("678.90"); let mut x = a.clone(); x += &b; assert_eq!(x, DynNum::parse("802.35")); let mut x = a.clone(); x -= &b; assert_eq!(x, DynNum::parse("-555.45")); let mut x = a.clone(); x *= &b; assert_eq!(x, DynNum::parse("83810.205")); let mut x = a.clone(); x /= &b; x *= &b; assert_eq!(x, a); let mut x = b.clone(); x %= &a; assert_eq!(x, DynNum::parse("61.65")); } } impl ops::Neg for &DynNum { type Output = DynNum; fn neg(self) -> Self::Output { impl_0arg_wrap!(self, neg) } } impl ops::Add for &DynNum { type Output = DynNum; fn add(self, rhs: Self) -> Self::Output { impl_1other_wrap!(self, rhs, add) } } impl ops::Sub for &DynNum { type Output = DynNum; fn sub(self, rhs: Self) -> Self::Output { impl_1other_wrap!(self, rhs, sub) } } impl ops::Mul for &DynNum { type Output = DynNum; fn mul(self, rhs: Self) -> Self::Output { impl_1other_wrap!(self, rhs, mul) } } impl ops::Div for &DynNum { type Output = DynNum; fn div(self, rhs: Self) -> Self::Output { impl_1other_wrap!(self, rhs, div) } } impl ops::Rem for &DynNum { type Output = DynNum; fn rem(self, rhs: Self) -> Self::Output { impl_1other_wrap!(self, rhs, rem) } } #[test] fn arith_ref_ref() { { DynNum::set_kind(NumKind::Fixed); Fixed::set_dps(2); let a = DynNum::parse("123.45"); let b = DynNum::parse("678.90"); assert_eq!(&a + &b, DynNum::parse("802.35")); assert_eq!(&a - &b, DynNum::parse("-555.45")); assert_eq!(&a * &b, DynNum::parse("83810.20")); assert_eq!(&a / &b, DynNum::parse("0.18")); assert_eq!(&b % &a, DynNum::parse("61.65")); } { DynNum::set_kind(NumKind::GuardedFixed); GuardedFixed::set_dps(2); let a = DynNum::parse("123.45"); let b = DynNum::parse("678.90"); assert_eq!(&a + &b, DynNum::parse("802.35")); assert_eq!(&a - &b, DynNum::parse("-555.45")); assert_eq!(&a * &b, DynNum::parse("83810.205")); assert_eq!(&a / &b, DynNum::parse("0.18")); assert_eq!(&b % &a, DynNum::parse("61.65")); } { DynNum::set_kind(NumKind::NativeFloat64); let a = DynNum::parse("123.45"); let b = DynNum::parse("678.90"); assert_eq!(&a + &b, DynNum { float64: NativeFloat64::from(123.45_f64 + 678.90_f64) }); assert_eq!(&a - &b, DynNum { float64: NativeFloat64::from(123.45_f64 - 678.90_f64) }); assert_eq!(&a * &b, DynNum { float64: NativeFloat64::from(123.45_f64 * 678.90_f64) }); assert_eq!(&a / &b, DynNum { float64: NativeFloat64::from(123.45_f64 / 678.90_f64) }); assert_eq!(&b % &a, DynNum { float64: NativeFloat64::from(678.90_f64 % 123.45_f64) }); } { DynNum::set_kind(NumKind::Rational); let a = DynNum::parse("123.45"); let b = DynNum::parse("678.90"); assert_eq!(&a + &b, DynNum::parse("802.35")); assert_eq!(&a - &b, DynNum::parse("-555.45")); assert_eq!(&a * &b, DynNum::parse("83810.205")); assert_eq!((&a / &b) * &b, a); assert_eq!(&b % &a, DynNum::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 { } */