Implement dynamic dispatch for different number types

This commit is contained in:
RunasSudo 2021-07-14 16:36:37 +10:00
parent 5a53574366
commit 414a1482c3
Signed by: RunasSudo
GPG Key ID: 7234E476BF21C61A
6 changed files with 531 additions and 5 deletions

513
src/numbers/dynnum.rs Normal file
View File

@ -0,0 +1,513 @@
/* 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, Fixed, GuardedFixed, NativeFloat64, Number, Rational};
use num_traits::{Num, One, Zero};
//use wasm_bindgen::prelude::wasm_bindgen;
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]
pub enum NumKind {
/// See [crate::numbers::fixed]
Fixed,
/// See [crate::numbers::gfixed]
GuardedFixed,
/// See [crate::numbers::native]
NativeFloat64,
/// See [crate::numbers::rational_rug] or [crate::numbers::rational_num]
Rational,
}
/// Determines which underlying implementation to use
static mut KIND: NumKind = NumKind::Fixed;
/// Returns which underlying implementation is in use
#[inline]
fn get_kind() -> &'static NumKind {
unsafe {
return &KIND;
}
}
/// A wrapper for different numeric types using dynamic dispatch
pub union DynNum {
fixed: ManuallyDrop<Fixed>,
gfixed: ManuallyDrop<GuardedFixed>,
float64: NativeFloat64,
rational: ManuallyDrop<Rational>,
}
impl DynNum {
/// Set which underlying implementation to use
pub fn set_kind(kind: NumKind) {
unsafe {
KIND = 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 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) }
}
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<Self, Self::FromStrRadixErr> {
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) }
}
impl From<usize> 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 { impl_1arg_nowrap!(self, f, fmt) }
}
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<Ordering> { 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) }
}
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) }
}
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) }
}
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) }
}
impl ops::Neg for &DynNum {
type Output = DynNum;
fn neg(self) -> Self::Output { impl_0arg_wrap!(self, neg) }
}
impl ops::Add<Self> for &DynNum {
type Output = DynNum;
fn add(self, rhs: Self) -> Self::Output { impl_1other_wrap!(self, rhs, add) }
}
impl ops::Sub<Self> for &DynNum {
type Output = DynNum;
fn sub(self, rhs: Self) -> Self::Output { impl_1other_wrap!(self, rhs, sub) }
}
impl ops::Mul<Self> for &DynNum {
type Output = DynNum;
fn mul(self, rhs: Self) -> Self::Output { impl_1other_wrap!(self, rhs, mul) }
}
impl ops::Div<Self> for &DynNum {
type Output = DynNum;
fn div(self, rhs: Self) -> Self::Output { impl_1other_wrap!(self, rhs, div) }
}
impl ops::Rem<Self> for &DynNum {
type Output = DynNum;
fn rem(self, rhs: Self) -> Self::Output { impl_1other_wrap!(self, rhs, rem) }
}
/*
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 {
}
*/

View File

@ -187,7 +187,7 @@ impl Num for GuardedFixed {
} }
impl PartialEq for GuardedFixed { impl PartialEq for GuardedFixed {
fn eq(&self, other: &GuardedFixed) -> bool { fn eq(&self, other: &Self) -> bool {
if &(&self.0 - &other.0).abs() < get_factor_cmp() { if &(&self.0 - &other.0).abs() < get_factor_cmp() {
return true; return true;
} else { } else {
@ -197,13 +197,13 @@ impl PartialEq for GuardedFixed {
} }
impl PartialOrd for GuardedFixed { impl PartialOrd for GuardedFixed {
fn partial_cmp(&self, other: &GuardedFixed) -> Option<Ordering> { fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
return Some(self.cmp(other)); return Some(self.cmp(other));
} }
} }
impl Ord for GuardedFixed { impl Ord for GuardedFixed {
fn cmp(&self, other: &GuardedFixed) -> Ordering { fn cmp(&self, other: &Self) -> Ordering {
if self.eq(other) { if self.eq(other) {
return Ordering::Equal; return Ordering::Equal;
} else { } else {

View File

@ -30,6 +30,9 @@ mod rational_rug;
//#[cfg(target_arch = "wasm32")] //#[cfg(target_arch = "wasm32")]
mod rational_num; mod rational_num;
//#[cfg(target_arch = "wasm32")]
mod dynnum;
use num_traits::{NumAssignRef, NumRef}; use num_traits::{NumAssignRef, NumRef};
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
@ -153,6 +156,8 @@ impl<N: Number, D: Fallible + ?Sized> DeserializeWith<Archived<String>, Option<N
} }
} }
} }
pub use self::dynnum::NumKind;
pub use self::dynnum::DynNum;
pub use self::fixed::Fixed; pub use self::fixed::Fixed;
pub use self::gfixed::GuardedFixed; pub use self::gfixed::GuardedFixed;

View File

@ -26,7 +26,7 @@ use std::ops;
type ImplType = f64; type ImplType = f64;
/// Native 64-bit floating-point number /// Native 64-bit floating-point number
#[derive(Clone, Debug, Display, PartialEq, PartialOrd)] #[derive(Copy, Clone, Debug, Display, PartialEq, PartialOrd)]
pub struct NativeFloat64(ImplType); pub struct NativeFloat64(ImplType);
impl Number for NativeFloat64 { impl Number for NativeFloat64 {

View File

@ -350,7 +350,7 @@ impl ops::MulAssign<&Self> for Rational {
} }
impl ops::DivAssign<&Self> for Rational { impl ops::DivAssign<&Self> for Rational {
fn div_assign(&mut self, rhs: &Self) { self.0 /= &rhs.0 } fn div_assign(&mut self, rhs: &Self) { self.0 /= &rhs.0; }
} }
impl ops::RemAssign<&Self> for Rational { impl ops::RemAssign<&Self> for Rational {

View File

@ -20,6 +20,7 @@
use crate::constraints::Constraints; use crate::constraints::Constraints;
use crate::election::{CandidateState, CountState, Election, StageKind}; use crate::election::{CandidateState, CountState, Election, StageKind};
//use crate::numbers::{DynNum, Fixed, GuardedFixed, NativeFloat64, Number, NumKind, Rational};
use crate::numbers::{Fixed, GuardedFixed, NativeFloat64, Number, Rational}; use crate::numbers::{Fixed, GuardedFixed, NativeFloat64, Number, Rational};
use crate::parser::blt; use crate::parser::blt;
use crate::stv; use crate::stv;
@ -49,6 +50,12 @@ macro_rules! wasm_error {
// Init // Init
/// Wrapper for [DynNum::set_kind]
//#[wasm_bindgen]
//pub fn dynnum_set_kind(kind: NumKind) {
// DynNum::set_kind(kind);
//}
/// Wrapper for [Fixed::set_dps] /// Wrapper for [Fixed::set_dps]
#[cfg_attr(feature = "wasm", wasm_bindgen)] #[cfg_attr(feature = "wasm", wasm_bindgen)]
pub fn fixed_set_dps(dps: usize) { pub fn fixed_set_dps(dps: usize) {
@ -209,6 +216,7 @@ macro_rules! impl_type {
}} }}
} }
//impl_type!(DynNum);
impl_type!(Fixed); impl_type!(Fixed);
impl_type!(GuardedFixed); impl_type!(GuardedFixed);
impl_type!(NativeFloat64); impl_type!(NativeFloat64);