/*  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::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::gfixed]
	GuardedFixed,
	/// See [crate::numbers::native]
	NativeFloat64,
	/// See [crate::numbers::rational_rug] or [crate::numbers::rational_num]
	Rational,
}

thread_local! {
	/// Determines which underlying implementation to use
	static KIND: Cell<NumKind> = 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<Fixed>,
	gfixed: ManuallyDrop<GuardedFixed>,
	float64: NativeFloat64,
	rational: ManuallyDrop<Rational>,
}

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<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) }
}

#[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<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 {
		// 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<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) }
}

#[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<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) }
}

#[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 {
	
}
*/
