#[cfg(feature = "std")]
use serde::{Deserialize, Serialize};
use crate::traits::{
	BaseArithmetic, Bounded, CheckedAdd, CheckedMul, CheckedSub, One, SaturatedConversion,
	Saturating, UniqueSaturatedInto, Unsigned, Zero,
};
use codec::{CompactAs, Encode};
use num_traits::{Pow, SaturatingAdd, SaturatingSub};
use sp_std::{
	fmt, ops,
	ops::{Add, AddAssign, Div, Rem, Sub},
	prelude::*,
};
pub type InnerOf<P> = <P as PerThing>::Inner;
pub type UpperOf<P> = <P as PerThing>::Upper;
pub trait RationalArg:
	Clone
	+ Ord
	+ ops::Div<Self, Output = Self>
	+ ops::Rem<Self, Output = Self>
	+ ops::Add<Self, Output = Self>
	+ ops::AddAssign<Self>
	+ Unsigned
	+ Zero
	+ One
{
}
impl<
		T: Clone
			+ Ord
			+ ops::Div<Self, Output = Self>
			+ ops::Rem<Self, Output = Self>
			+ ops::Add<Self, Output = Self>
			+ ops::AddAssign<Self>
			+ Unsigned
			+ Zero
			+ One,
	> RationalArg for T
{
}
pub trait MultiplyArg:
	Clone
	+ ops::Rem<Self, Output = Self>
	+ ops::Div<Self, Output = Self>
	+ ops::Mul<Self, Output = Self>
	+ ops::Add<Self, Output = Self>
	+ Unsigned
{
}
impl<
		T: Clone
			+ ops::Rem<Self, Output = Self>
			+ ops::Div<Self, Output = Self>
			+ ops::Mul<Self, Output = Self>
			+ ops::Add<Self, Output = Self>
			+ Unsigned,
	> MultiplyArg for T
{
}
pub trait ReciprocalArg: MultiplyArg + Saturating {}
impl<T: MultiplyArg + Saturating> ReciprocalArg for T {}
pub trait PerThing:
	Sized
	+ Saturating
	+ Copy
	+ Default
	+ Eq
	+ PartialEq
	+ Ord
	+ PartialOrd
	+ Bounded
	+ fmt::Debug
	+ ops::Div<Output = Self>
	+ ops::Mul<Output = Self>
	+ Pow<usize, Output = Self>
{
	type Inner: BaseArithmetic + Unsigned + Copy + Into<u128> + fmt::Debug;
	type Upper: BaseArithmetic
		+ Copy
		+ From<Self::Inner>
		+ TryInto<Self::Inner>
		+ UniqueSaturatedInto<Self::Inner>
		+ Unsigned
		+ fmt::Debug;
	const ACCURACY: Self::Inner;
	fn zero() -> Self {
		Self::from_parts(Self::Inner::zero())
	}
	fn is_zero(&self) -> bool {
		self.deconstruct() == Self::Inner::zero()
	}
	fn one() -> Self {
		Self::from_parts(Self::ACCURACY)
	}
	fn is_one(&self) -> bool {
		self.deconstruct() == Self::ACCURACY
	}
	fn less_epsilon(self) -> Self {
		if self.is_zero() {
			return self
		}
		Self::from_parts(self.deconstruct() - One::one())
	}
	fn try_less_epsilon(self) -> Result<Self, Self> {
		if self.is_zero() {
			return Err(self)
		}
		Ok(Self::from_parts(self.deconstruct() - One::one()))
	}
	fn plus_epsilon(self) -> Self {
		if self.is_one() {
			return self
		}
		Self::from_parts(self.deconstruct() + One::one())
	}
	fn try_plus_epsilon(self) -> Result<Self, Self> {
		if self.is_one() {
			return Err(self)
		}
		Ok(Self::from_parts(self.deconstruct() + One::one()))
	}
	fn from_percent(x: Self::Inner) -> Self {
		let a: Self::Inner = x.min(100.into());
		let b: Self::Inner = 100.into();
		Self::from_rational::<Self::Inner>(a, b)
	}
	fn square(self) -> Self {
		let p = Self::Upper::from(self.deconstruct());
		let q = Self::Upper::from(Self::ACCURACY);
		Self::from_rational::<Self::Upper>(p * p, q * q)
	}
	fn left_from_one(self) -> Self {
		Self::one().saturating_sub(self)
	}
	fn mul_floor<N>(self, b: N) -> N
	where
		N: MultiplyArg + UniqueSaturatedInto<Self::Inner>,
		Self::Inner: Into<N>,
	{
		overflow_prune_mul::<N, Self>(b, self.deconstruct(), Rounding::Down)
	}
	fn mul_ceil<N>(self, b: N) -> N
	where
		N: MultiplyArg + UniqueSaturatedInto<Self::Inner>,
		Self::Inner: Into<N>,
	{
		overflow_prune_mul::<N, Self>(b, self.deconstruct(), Rounding::Up)
	}
	fn saturating_reciprocal_mul<N>(self, b: N) -> N
	where
		N: ReciprocalArg + UniqueSaturatedInto<Self::Inner>,
		Self::Inner: Into<N>,
	{
		saturating_reciprocal_mul::<N, Self>(b, self.deconstruct(), Rounding::NearestPrefUp)
	}
	fn saturating_reciprocal_mul_floor<N>(self, b: N) -> N
	where
		N: ReciprocalArg + UniqueSaturatedInto<Self::Inner>,
		Self::Inner: Into<N>,
	{
		saturating_reciprocal_mul::<N, Self>(b, self.deconstruct(), Rounding::Down)
	}
	fn saturating_reciprocal_mul_ceil<N>(self, b: N) -> N
	where
		N: ReciprocalArg + UniqueSaturatedInto<Self::Inner>,
		Self::Inner: Into<N>,
	{
		saturating_reciprocal_mul::<N, Self>(b, self.deconstruct(), Rounding::Up)
	}
	fn deconstruct(self) -> Self::Inner;
	fn from_parts(parts: Self::Inner) -> Self;
	#[cfg(feature = "std")]
	fn from_float(x: f64) -> Self;
	#[deprecated = "Use from_float instead"]
	#[cfg(feature = "std")]
	fn from_fraction(x: f64) -> Self {
		Self::from_float(x)
	}
	fn from_rational<N>(p: N, q: N) -> Self
	where
		N: RationalArg + TryInto<Self::Inner> + TryInto<Self::Upper>,
		Self::Inner: Into<N>,
	{
		Self::from_rational_with_rounding(p, q, Rounding::Down).unwrap_or_else(|_| Self::one())
	}
	fn from_rational_with_rounding<N>(p: N, q: N, rounding: Rounding) -> Result<Self, ()>
	where
		N: RationalArg + TryInto<Self::Inner> + TryInto<Self::Upper>,
		Self::Inner: Into<N>;
	#[deprecated = "Use from_rational instead"]
	fn from_rational_approximation<N>(p: N, q: N) -> Self
	where
		N: RationalArg + TryInto<Self::Inner> + TryInto<Self::Upper>,
		Self::Inner: Into<N>,
	{
		Self::from_rational(p, q)
	}
}
#[derive(Copy, Clone, sp_std::fmt::Debug)]
pub enum Rounding {
	Up,
	Down,
	NearestPrefUp,
	NearestPrefDown,
}
#[derive(Copy, Clone, sp_std::fmt::Debug)]
pub enum SignedRounding {
	High,
	Low,
	NearestPrefHigh,
	NearestPrefLow,
	Major,
	Minor,
	NearestPrefMajor,
	NearestPrefMinor,
}
impl Rounding {
	pub const fn from_signed(rounding: SignedRounding, negative: bool) -> Self {
		use Rounding::*;
		use SignedRounding::*;
		match (rounding, negative) {
			(Low, true) | (Major, _) | (High, false) => Up,
			(High, true) | (Minor, _) | (Low, false) => Down,
			(NearestPrefMajor, _) | (NearestPrefHigh, false) | (NearestPrefLow, true) =>
				NearestPrefUp,
			(NearestPrefMinor, _) | (NearestPrefLow, false) | (NearestPrefHigh, true) =>
				NearestPrefDown,
		}
	}
}
fn saturating_reciprocal_mul<N, P>(x: N, part: P::Inner, rounding: Rounding) -> N
where
	N: Clone
		+ UniqueSaturatedInto<P::Inner>
		+ ops::Div<N, Output = N>
		+ ops::Mul<N, Output = N>
		+ ops::Add<N, Output = N>
		+ ops::Rem<N, Output = N>
		+ Saturating
		+ Unsigned,
	P: PerThing,
	P::Inner: Into<N>,
{
	let maximum: N = P::ACCURACY.into();
	let c = rational_mul_correction::<N, P>(x.clone(), P::ACCURACY, part, rounding);
	(x / part.into()).saturating_mul(maximum).saturating_add(c)
}
fn overflow_prune_mul<N, P>(x: N, part: P::Inner, rounding: Rounding) -> N
where
	N: MultiplyArg + UniqueSaturatedInto<P::Inner>,
	P: PerThing,
	P::Inner: Into<N>,
{
	let maximum: N = P::ACCURACY.into();
	let part_n: N = part.into();
	let c = rational_mul_correction::<N, P>(x.clone(), part, P::ACCURACY, rounding);
	(x / maximum) * part_n + c
}
fn rational_mul_correction<N, P>(x: N, numer: P::Inner, denom: P::Inner, rounding: Rounding) -> N
where
	N: MultiplyArg + UniqueSaturatedInto<P::Inner>,
	P: PerThing,
	P::Inner: Into<N>,
{
	let numer_upper = P::Upper::from(numer);
	let denom_n: N = denom.into();
	let denom_upper = P::Upper::from(denom);
	let rem = x.rem(denom_n);
	let rem_inner = rem.saturated_into::<P::Inner>();
	let rem_mul_upper = P::Upper::from(rem_inner) * numer_upper;
	let mut rem_mul_div_inner = (rem_mul_upper / denom_upper).saturated_into::<P::Inner>();
	match rounding {
		Rounding::Down => {},
		Rounding::Up => {
			if rem_mul_upper % denom_upper > 0.into() {
				rem_mul_div_inner += 1.into();
			}
		},
		Rounding::NearestPrefDown =>
			if rem_mul_upper % denom_upper > denom_upper / 2.into() {
				rem_mul_div_inner += 1.into();
			},
		Rounding::NearestPrefUp =>
			if rem_mul_upper % denom_upper >= denom_upper / 2.into() + denom_upper % 2.into() {
				rem_mul_div_inner += 1.into();
			},
	}
	rem_mul_div_inner.into()
}
fn div_rounded<N>(n: N, d: N, r: Rounding) -> N
where
	N: Clone
		+ Eq
		+ Ord
		+ Zero
		+ One
		+ AddAssign
		+ Add<Output = N>
		+ Rem<Output = N>
		+ Div<Output = N>,
{
	let mut o = n.clone() / d.clone();
	use Rounding::*;
	let two = || N::one() + N::one();
	if match r {
		Up => !((n % d).is_zero()),
		NearestPrefDown => {
			let rem = n % d.clone();
			rem > d / two()
		},
		NearestPrefUp => {
			let rem = n % d.clone();
			rem >= d.clone() / two() + d % two()
		},
		Down => false,
	} {
		o += N::one()
	}
	o
}
macro_rules! implement_per_thing {
	(
		$name:ident,
		$test_mod:ident,
		[$($test_units:tt),+],
		$max:tt,
		$type:ty,
		$upper_type:ty,
		$title:expr $(,)?
	) => {
		#[doc = $title]
		#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
		#[derive(Encode, Copy, Clone, PartialEq, Eq, codec::MaxEncodedLen, PartialOrd, Ord, scale_info::TypeInfo)]
		pub struct $name($type);
		impl CompactAs for $name {
			type As = $type;
			fn encode_as(&self) -> &Self::As {
				&self.0
			}
			fn decode_from(x: Self::As) -> Result<Self, codec::Error> {
				Ok(Self::from_parts(x))
			}
		}
		impl From<codec::Compact<$name>> for $name {
			fn from(x: codec::Compact<$name>) -> $name {
				x.0
			}
		}
		#[cfg(feature = "std")]
		impl sp_std::fmt::Debug for $name {
			fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
				if $max == <$type>::max_value() {
					let pc = (self.0 as f64) / (self.0 as f64) * 100f64;
					write!(fmt, "{:.2}% ({}/{})", pc, self.0, $max)
				} else {
					let divisor = $max / 100;
					let units = self.0 / divisor;
					let rest = self.0 % divisor;
					write!(fmt, "{}", units)?;
					if rest > 0 {
						write!(fmt, ".")?;
						let mut m = $max / 100;
						while rest % m > 0 {
							m /= 10;
							write!(fmt, "{:01}", rest / m % 10)?;
						}
					}
					write!(fmt, "%")
				}
			}
		}
		#[cfg(not(feature = "std"))]
		impl sp_std::fmt::Debug for $name {
			fn fmt(&self, fmt: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {
				if $max == <$type>::max_value() {
					write!(fmt, "{}/{}", self.0, $max)
				} else {
					let units = self.0 / ($max / 100);
					let rest = self.0 % ($max / 100);
					write!(fmt, "{}", units)?;
					if rest > 0 {
						write!(fmt, ".")?;
						let mut m = $max / 100;
						while rest % m > 0 {
							m /= 10;
							write!(fmt, "{:01}", rest / m % 10)?;
						}
					}
					write!(fmt, "%")
				}
			}
		}
		impl PerThing for $name {
			type Inner = $type;
			type Upper = $upper_type;
			const ACCURACY: Self::Inner = $max;
			fn deconstruct(self) -> Self::Inner { self.0 }
			fn from_parts(parts: Self::Inner) -> Self { Self(parts.min($max)) }
			#[cfg(feature = "std")]
			fn from_float(x: f64) -> Self {
				Self::from_parts((x.max(0.).min(1.) * $max as f64) as Self::Inner)
			}
			fn from_rational_with_rounding<N>(p: N, q: N, r: Rounding) -> Result<Self, ()>
			where
				N: Clone
					+ Ord
					+ TryInto<Self::Inner>
					+ TryInto<Self::Upper>
					+ ops::Div<N, Output = N>
					+ ops::Rem<N, Output = N>
					+ ops::Add<N, Output = N>
					+ ops::AddAssign<N>
					+ Unsigned
					+ Zero
					+ One,
				Self::Inner: Into<N>
			{
				if q.is_zero() { return Err(()) }
				if p > q { return Err(()) }
				let factor = div_rounded::<N>(q.clone(), $max.into(), Rounding::Up).max(One::one());
				let q_reduce: $type = div_rounded(q, factor.clone(), r)
					.try_into()
					.map_err(|_| "Failed to convert")
					.expect(
						"`q / ceil(q/$max) < $max`; macro prevents any type being created that \
						does not satisfy this; qed"
					);
				let p_reduce: $type = div_rounded(p, factor, r)
					.try_into()
					.map_err(|_| "Failed to convert")
					.expect(
						"`p / ceil(p/$max) < $max`; macro prevents any type being created that \
						does not satisfy this; qed"
					);
				let n = p_reduce as $upper_type * <$upper_type>::from($max);
				let d = q_reduce as $upper_type;
				let part = div_rounded(n, d, r);
				Ok($name(part as Self::Inner))
			}
		}
		impl $name {
			#[allow(unused_comparisons)]
			pub const fn from_parts(parts: $type) -> Self {
				Self([parts, $max][(parts > $max) as usize])
			}
			pub const fn from_percent(x: $type) -> Self {
				Self(([x, 100][(x > 100) as usize] as $upper_type * $max as $upper_type / 100) as $type)
			}
			pub const fn one() -> Self {
				Self::from_parts($max)
			}
			pub fn is_one(&self) -> bool {
				PerThing::is_one(self)
			}
			pub const fn zero() -> Self {
				Self::from_parts(0)
			}
			pub fn is_zero(&self) -> bool {
				PerThing::is_zero(self)
			}
			pub const fn deconstruct(self) -> $type {
				self.0
			}
			pub fn square(self) -> Self {
				PerThing::square(self)
			}
			#[cfg(feature = "std")]
			pub fn from_float(x: f64) -> Self {
				<Self as PerThing>::from_float(x)
			}
			#[deprecated = "Use `PerThing::from_rational` instead"]
			pub fn from_rational_approximation<N>(p: N, q: N) -> Self
			where
				N: RationalArg+ TryInto<$type> + TryInto<$upper_type>,
				$type: Into<N>
			{
				<Self as PerThing>::from_rational(p, q)
			}
			pub fn from_rational<N>(p: N, q: N) -> Self
			where
				N: RationalArg+ TryInto<$type> + TryInto<$upper_type>,
				$type: Into<N>
			{
				<Self as PerThing>::from_rational(p, q)
			}
			pub fn int_mul(self, b: $type) -> Self {
				PerThing::from_parts(self.0.saturating_mul(b))
			}
			pub fn int_div(self, b: Self) -> $type {
				self.0 / b.0
			}
			pub fn mul_floor<N>(self, b: N) -> N
				where
					N: MultiplyArg + UniqueSaturatedInto<$type>,
					$type: Into<N>,
			{
				PerThing::mul_floor(self, b)
			}
			pub fn mul_ceil<N>(self, b: N) -> N
				where
					N: MultiplyArg + UniqueSaturatedInto<$type>,
					$type: Into<N>,
			{
				PerThing::mul_ceil(self, b)
			}
			pub fn saturating_reciprocal_mul<N>(self, b: N) -> N
				where
					N: ReciprocalArg + UniqueSaturatedInto<$type>,
					$type: Into<N>,
			{
				PerThing::saturating_reciprocal_mul(self, b)
			}
			pub fn saturating_reciprocal_mul_floor<N>(self, b: N) -> N
				where
					N: ReciprocalArg + UniqueSaturatedInto<$type>,
					$type: Into<N>,
			{
				PerThing::saturating_reciprocal_mul_floor(self, b)
			}
			pub fn saturating_reciprocal_mul_ceil<N>(self, b: N) -> N
				where
					N: ReciprocalArg + UniqueSaturatedInto<$type>,
					$type: Into<N>,
			{
				PerThing::saturating_reciprocal_mul_ceil(self, b)
			}
			pub fn saturating_div(self, rhs: Self, r: Rounding) -> Self {
				let p = self.0;
				let q = rhs.0;
				Self::from_rational_with_rounding(p, q, r).unwrap_or_else(|_| Self::one())
			}
		}
		impl Saturating for $name {
			fn saturating_add(self, rhs: Self) -> Self {
				Self::from_parts(self.0.saturating_add(rhs.0))
			}
			fn saturating_sub(self, rhs: Self) -> Self {
				Self::from_parts(self.0.saturating_sub(rhs.0))
			}
			fn saturating_mul(self, rhs: Self) -> Self {
				self * rhs
			}
			fn saturating_pow(self, exp: usize) -> Self {
				self.pow(exp)
			}
		}
		impl codec::Decode for $name {
			fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
				let inner = <$type as codec::Decode>::decode(input)?;
				if inner <= <Self as PerThing>::ACCURACY {
					Ok(Self(inner))
				} else {
					Err("Value is greater than allowed maximum!".into())
				}
			}
		}
		impl Bounded for $name {
			fn min_value() -> Self {
				<Self as PerThing>::zero()
			}
			fn max_value() -> Self {
				<Self as PerThing>::one()
			}
		}
		impl ops::Mul for $name {
			type Output = Self;
			fn mul(self, rhs: Self) -> Self::Output {
				let a = self.0 as $upper_type;
				let b = rhs.0 as $upper_type;
				let m = <$upper_type>::from($max);
				let parts = a * b / m;
				Self::from_parts(parts as $type)
			}
		}
		impl Pow<usize> for $name {
			type Output = Self;
			fn pow(mut self, exp: usize) -> Self::Output {
				if exp == 0 || self.is_one() {
					return Self::one()
				}
				let mut result = self;
				let mut exp = exp - 1;
				while exp > 0 && !result.is_zero() {
					if exp % 2 != 0 {
						result = result * self;
						exp -= 1;
					}
					self = self.square();
					exp /= 2;
				}
				result
			}
		}
		impl ops::Div for $name {
			type Output = Self;
			fn div(self, rhs: Self) -> Self::Output {
				let p = self.0;
				let q = rhs.0;
				Self::from_rational(p, q)
			}
		}
		impl Default for $name {
			fn default() -> Self {
				<Self as PerThing>::zero()
			}
		}
		impl<N> ops::Mul<N> for $name
		where
			N: Clone + UniqueSaturatedInto<$type> + ops::Rem<N, Output=N>
				+ ops::Div<N, Output=N> + ops::Mul<N, Output=N> + ops::Add<N, Output=N> + Unsigned,
			$type: Into<N>,
		{
			type Output = N;
			fn mul(self, b: N) -> Self::Output {
				overflow_prune_mul::<N, Self>(b, self.deconstruct(), Rounding::NearestPrefDown)
			}
		}
		impl<N> ops::Div<N> for $name where $type: TryFrom<N> {
			type Output = Self;
			fn div(self, b: N) -> Self::Output {
				<$type>::try_from(b).map_or(Self::zero(), |d| Self::from_parts(self.0 / d))
			}
		}
		impl Add<Self> for $name {
			type Output = $name;
			#[allow(unused_comparisons)]
			#[inline]
			fn add(self, rhs: Self) -> Self::Output {
				let inner = self.deconstruct().add(rhs.deconstruct());
				debug_assert!(inner <= $max);
				$name::from_parts(inner)
			}
		}
		impl CheckedAdd for $name {
			#[allow(unused_comparisons)]
			#[inline]
			fn checked_add(&self, rhs: &Self) -> Option<Self> {
				self.deconstruct()
					.checked_add(rhs.deconstruct())
					.map(|inner| if inner > $max { None } else { Some($name::from_parts(inner)) })
					.flatten()
			}
		}
		impl Sub<Self> for $name {
			type Output = $name;
			#[inline]
			fn sub(self, rhs: Self) -> Self::Output {
				$name::from_parts(self.deconstruct().sub(rhs.deconstruct()))
			}
		}
		impl CheckedSub for $name {
			#[inline]
			fn checked_sub(&self, v: &Self) -> Option<Self> {
				self.deconstruct().checked_sub(v.deconstruct()).map($name::from_parts)
			}
		}
		impl SaturatingAdd for $name {
			#[inline]
			fn saturating_add(&self, v: &Self) -> Self {
				$name::from_parts(self.deconstruct().saturating_add(v.deconstruct()))
			}
		}
		impl SaturatingSub for $name {
			#[inline]
			fn saturating_sub(&self, v: &Self) -> Self {
				$name::from_parts(self.deconstruct().saturating_sub(v.deconstruct()))
			}
		}
		impl CheckedMul for $name {
			#[inline]
			fn checked_mul(&self, rhs: &Self) -> Option<Self> {
				Some(*self * *rhs)
			}
		}
		impl $crate::traits::Zero for $name {
			fn zero() -> Self {
				Self::zero()
			}
			fn is_zero(&self) -> bool {
				self == &Self::zero()
			}
		}
		impl $crate::traits::One for $name {
			fn one() -> Self {
				Self::one()
			}
		}
		#[cfg(test)]
		mod $test_mod {
			use codec::{Encode, Decode};
			use super::{$name, Saturating, PerThing};
			use crate::traits::Zero;
			#[test]
			fn macro_expanded_correctly() {
				assert!(2 * ($max as $upper_type) < <$upper_type>::max_value());
				assert!(<$upper_type>::from($max) < <$upper_type>::max_value());
				assert!((<$type>::max_value() as $upper_type) <= <$upper_type>::max_value());
				assert!(<$upper_type>::from($max).checked_mul($max.into()).is_some());
				assert!(<$upper_type>::from($max) * <$upper_type>::from($max) < <$upper_type>::max_value());
			}
			#[derive(Encode, Decode, PartialEq, Eq, Debug)]
			struct WithCompact<T: codec::HasCompact> {
				data: T,
			}
			#[test]
			fn has_compact() {
				let data = WithCompact { data: $name(1) };
				let encoded = data.encode();
				assert_eq!(data, WithCompact::<$name>::decode(&mut &encoded[..]).unwrap());
			}
			#[test]
			fn compact_encoding() {
				let tests = [
					(0 as $type, 1usize),
					(1 as $type, 1usize),
					(63, 1),
					(64, 2),
					(65, 2),
					];
				for &(n, l) in &tests {
					let compact: codec::Compact<$name> = $name(n).into();
					let encoded = compact.encode();
					assert_eq!(encoded.len(), l);
					let decoded = <codec::Compact<$name>>::decode(&mut & encoded[..])
						.unwrap();
					let per_thingy: $name = decoded.into();
					assert_eq!(per_thingy, $name(n));
				}
			}
			#[test]
			fn from_parts_cannot_overflow() {
				assert_eq!(<$name>::from_parts($max.saturating_add(1)), <$name>::one());
			}
			#[test]
			fn has_max_encoded_len() {
				struct AsMaxEncodedLen<T: codec::MaxEncodedLen> {
					_data: T,
				}
				let _ = AsMaxEncodedLen { _data: $name(1) };
			}
			#[test]
			fn fail_on_invalid_encoded_value() {
				let value = <$upper_type>::from($max) * 2;
				let casted = value as $type;
				let encoded = casted.encode();
				if <$upper_type>::from(casted) == value {
					assert_eq!(
						$name::decode(&mut &encoded[..]),
						Err("Value is greater than allowed maximum!".into()),
					);
				}
			}
			#[test]
			fn per_thing_api_works() {
				assert_eq!($name::zero(), $name::from_parts(Zero::zero()));
				assert_eq!($name::one(), $name::from_parts($max));
				assert_eq!($name::ACCURACY, $max);
				assert_eq!($name::from_percent(0), $name::from_parts(Zero::zero()));
				assert_eq!($name::from_percent(10), $name::from_parts($max / 10));
				assert_eq!($name::from_percent(50), $name::from_parts($max / 2));
				assert_eq!($name::from_percent(100), $name::from_parts($max));
				assert_eq!($name::from_percent(200), $name::from_parts($max));
				assert_eq!($name::from_float(0.0), $name::from_parts(Zero::zero()));
				assert_eq!($name::from_float(0.1), $name::from_parts($max / 10));
				assert_eq!($name::from_float(1.0), $name::from_parts($max));
				assert_eq!($name::from_float(2.0), $name::from_parts($max));
				assert_eq!($name::from_float(-1.0), $name::from_parts(Zero::zero()));
			}
			#[test]
			fn percent_trait_impl_works() {
				assert_eq!(<$name as PerThing>::from_percent(0), $name::from_parts(Zero::zero()));
				assert_eq!(<$name as PerThing>::from_percent(10), $name::from_parts($max / 10));
				assert_eq!(<$name as PerThing>::from_percent(50), $name::from_parts($max / 2));
				assert_eq!(<$name as PerThing>::from_percent(100), $name::from_parts($max));
				assert_eq!(<$name as PerThing>::from_percent(200), $name::from_parts($max));
			}
			macro_rules! u256ify {
				($val:expr) => {
					Into::<U256>::into($val)
				};
			}
			macro_rules! per_thing_mul_test {
				($num_type:tt) => {
					assert_eq!(
						$name::from_float(1.0) * $num_type::max_value(),
						$num_type::max_value()
					);
					if $max % 100 == 0 {
						assert_eq_error_rate!(
							$name::from_percent(99) * $num_type::max_value(),
							((Into::<U256>::into($num_type::max_value()) * 99u32) / 100u32).as_u128() as $num_type,
							1,
						);
						assert_eq!(
							$name::from_float(0.5) * $num_type::max_value(),
							$num_type::max_value() / 2,
						);
						assert_eq_error_rate!(
							$name::from_percent(1) * $num_type::max_value(),
							$num_type::max_value() / 100,
							1,
						);
					} else {
						assert_eq!(
							$name::from_float(0.99) * <$num_type>::max_value(),
							(
								(
									u256ify!($name::from_float(0.99).0) *
									u256ify!(<$num_type>::max_value()) /
									u256ify!($max)
								).as_u128()
							) as $num_type,
						);
						assert_eq!(
							$name::from_float(0.50) * <$num_type>::max_value(),
							(
								(
									u256ify!($name::from_float(0.50).0) *
									u256ify!(<$num_type>::max_value()) /
									u256ify!($max)
								).as_u128()
							) as $num_type,
						);
						assert_eq!(
							$name::from_float(0.01) * <$num_type>::max_value(),
							(
								(
									u256ify!($name::from_float(0.01).0) *
									u256ify!(<$num_type>::max_value()) /
									u256ify!($max)
								).as_u128()
							) as $num_type,
						);
					}
					assert_eq!($name::from_float(0.0) * $num_type::max_value(), 0);
					assert_eq!($name::one() * $num_type::max_value(), $num_type::max_value());
					assert_eq!($name::zero() * $num_type::max_value(), 0);
				}
			}
			#[test]
			fn per_thing_mul_works() {
				use primitive_types::U256;
				assert_eq!(
					$name::from_rational(1 as $type, 3) * 30 as $type,
					10,
				);
				$(per_thing_mul_test!($test_units);)*
			}
			#[test]
			fn per_thing_mul_rounds_to_nearest_number() {
				assert_eq!($name::from_percent(33) * 10u64, 3);
				assert_eq!($name::from_percent(34) * 10u64, 3);
				assert_eq!($name::from_percent(35) * 10u64, 3);
				assert_eq!($name::from_percent(36) * 10u64, 4);
			}
			#[test]
			fn per_thing_multiplication_with_large_number() {
				use primitive_types::U256;
				let max_minus_one = $max - 1;
				assert_eq_error_rate!(
					$name::from_parts(max_minus_one) * std::u128::MAX,
					((Into::<U256>::into(std::u128::MAX) * max_minus_one) / $max).as_u128(),
					1,
				);
			}
			macro_rules! per_thing_from_rationale_approx_test {
				($num_type:tt) => {
					assert_eq!(
						$name::from_rational(1 as $num_type, 0),
						$name::one(),
					);
					assert_eq!(
						$name::from_rational(1 as $num_type, 1),
						$name::one(),
					);
					assert_eq_error_rate!(
						$name::from_rational(1 as $num_type, 3).0,
						$name::from_parts($max / 3).0,
						2
					);
					assert_eq!(
						$name::from_rational(1 as $num_type, 10),
						$name::from_float(0.10),
					);
					assert_eq!(
						$name::from_rational(1 as $num_type, 4),
						$name::from_float(0.25),
					);
					assert_eq!(
						$name::from_rational(1 as $num_type, 4),
						$name::from_rational(2 as $num_type, 8),
					);
					assert_eq_error_rate!(
						$name::from_rational(
							$num_type::max_value() - 1,
							$num_type::max_value()
						).0 as $upper_type,
						$name::one().0 as $upper_type,
						2,
					);
					assert_eq_error_rate!(
						$name::from_rational(
							$num_type::max_value() / 3,
							$num_type::max_value()
						).0 as $upper_type,
						$name::from_parts($max / 3).0 as $upper_type,
						2,
					);
					assert_eq!(
						$name::from_rational(1, $num_type::max_value()),
						$name::zero(),
					);
				};
			}
			#[test]
			fn per_thing_from_rationale_approx_works() {
				let max_value = <$upper_type>::from($max);
				assert_eq!(
					$name::from_rational(max_value - 1, max_value + 1),
					$name::from_parts($max - 2),
				);
				assert_eq!(
					$name::from_rational(1, $max - 1),
					$name::from_parts(1),
				);
				assert_eq!(
					$name::from_rational(1, $max),
					$name::from_parts(1),
				);
				assert_eq!(
					$name::from_rational(2, 2 * max_value - 1),
					$name::from_parts(1),
				);
				assert_eq!(
					$name::from_rational(1, max_value + 1),
					$name::zero(),
				);
				assert_eq!(
					$name::from_rational(3 * max_value / 2, 3 * max_value),
					$name::from_float(0.5),
				);
				$(per_thing_from_rationale_approx_test!($test_units);)*
			}
			#[test]
			fn per_things_mul_operates_in_output_type() {
				assert_eq!($name::from_float(0.5) * 100u64, 50u64);
				assert_eq!($name::from_float(0.5) * 100u128, 50u128);
			}
			#[test]
			fn per_thing_saturating_op_works() {
				assert_eq_error_rate!(
					$name::from_float(0.5).saturating_add($name::from_float(0.4)).0 as $upper_type,
					$name::from_float(0.9).0 as $upper_type,
					2,
				);
				assert_eq_error_rate!(
					$name::from_float(0.5).saturating_add($name::from_float(0.5)).0 as $upper_type,
					$name::one().0 as $upper_type,
					2,
				);
				assert_eq!(
					$name::from_float(0.6).saturating_add($name::from_float(0.5)),
					$name::one(),
				);
				assert_eq_error_rate!(
					$name::from_float(0.6).saturating_sub($name::from_float(0.5)).0 as $upper_type,
					$name::from_float(0.1).0 as $upper_type,
					2,
				);
				assert_eq!(
					$name::from_float(0.6).saturating_sub($name::from_float(0.6)),
					$name::from_float(0.0),
				);
				assert_eq!(
					$name::from_float(0.6).saturating_sub($name::from_float(0.7)),
					$name::from_float(0.0),
				);
				assert_eq_error_rate!(
					$name::from_float(0.5).saturating_mul($name::from_float(0.5)).0 as $upper_type,
					$name::from_float(0.25).0 as $upper_type,
					2,
				);
				assert_eq_error_rate!(
					$name::from_float(0.2).saturating_mul($name::from_float(0.2)).0 as $upper_type,
					$name::from_float(0.04).0 as $upper_type,
					2,
				);
				assert_eq_error_rate!(
					$name::from_float(0.1).saturating_mul($name::from_float(0.1)).0 as $upper_type,
					$name::from_float(0.01).0 as $upper_type,
					1,
				);
			}
			#[test]
			fn per_thing_square_works() {
				assert_eq!($name::from_float(1.0).square(), $name::from_float(1.0));
				assert_eq!($name::from_float(0.5).square(), $name::from_float(0.25));
				assert_eq!($name::from_float(0.1).square(), $name::from_float(0.01));
				assert_eq!(
					$name::from_float(0.02).square(),
					$name::from_parts((4 * <$upper_type>::from($max) / 100 / 100) as $type)
				);
			}
			#[test]
			fn per_things_div_works() {
				assert_eq_error_rate!(
					($name::from_float(0.1) / $name::from_float(0.20)).0 as $upper_type,
					$name::from_float(0.50).0 as $upper_type,
					2,
				);
				assert_eq_error_rate!(
					($name::from_float(0.1) / $name::from_float(0.10)).0 as $upper_type,
					$name::from_float(1.0).0 as $upper_type,
					2,
				);
				assert_eq_error_rate!(
					($name::from_float(0.1) / $name::from_float(0.0)).0 as $upper_type,
					$name::from_float(1.0).0 as $upper_type,
					2,
				);
				assert_eq_error_rate!(
					($name::from_float(0.10) / $name::from_float(0.05)).0 as $upper_type,
					$name::from_float(1.0).0 as $upper_type,
					2,
				);
				assert_eq_error_rate!(
					($name::from_float(1.0) / $name::from_float(0.5)).0 as $upper_type,
					$name::from_float(1.0).0 as $upper_type,
					2,
				);
			}
			#[test]
			fn saturating_pow_works() {
				assert_eq!(
					$name::from_parts($max / 2).saturating_pow(0),
					$name::from_parts($max),
				);
				assert_eq!(
					$name::from_parts($max / 2).saturating_pow(1),
					$name::from_parts($max / 2),
				);
				assert_eq!(
					$name::from_parts($max / 2).saturating_pow(2),
					$name::from_parts($max / 2).square(),
				);
				for n in 1..=16 {
					assert_eq!(
						$name::from_parts($max / 2).saturating_pow(n),
						$name::from_parts(($max as u128 / 2u128.pow(n as u32)) as $type),
					);
				}
				assert_eq!(
					$name::from_parts(0).saturating_pow(3),
					$name::from_parts(0),
				);
				assert_eq!(
					$name::from_parts($max).saturating_pow(3),
					$name::from_parts($max),
				);
				assert_eq!(
					$name::from_parts($max / 2).saturating_pow(2usize.pow(31)),
					$name::from_parts(0),
				);
			}
			#[test]
			fn saturating_reciprocal_mul_works() {
				assert_eq!(
					$name::from_parts($max).saturating_reciprocal_mul(<$type>::from(10u8)),
					10,
				);
				assert_eq!(
					$name::from_parts($max / 2).saturating_reciprocal_mul(<$type>::from(10u8)),
					20,
				);
				assert_eq!(
					$name::from_parts(1).saturating_reciprocal_mul($max),
					<$type>::max_value(),
				);
				assert_eq!(
					$name::from_percent(60).saturating_reciprocal_mul(<$type>::from(10u8)),
					17,
				);
				assert_eq!(
					$name::from_percent(60).saturating_reciprocal_mul_floor(<$type>::from(10u8)),
					16,
				);
				assert_eq!(
					$name::from_percent(61).saturating_reciprocal_mul(<$type>::from(10u8)),
					16,
				);
				assert_eq!(
					$name::from_percent(61).saturating_reciprocal_mul_ceil(<$type>::from(10u8)),
					17,
				);
			}
			#[test]
			fn saturating_truncating_mul_works() {
				assert_eq!(
					$name::from_percent(49).mul_floor(10 as $type),
					4,
				);
				let a: $upper_type = $name::from_percent(50).mul_floor(($max as $upper_type).pow(2));
				let b: $upper_type = ($max as $upper_type).pow(2) / 2;
				if $max % 2 == 0 {
					assert_eq!(a, b);
				} else {
					assert!(b - a < ($max as $upper_type).pow(2) / 100 as $upper_type);
				}
			}
			#[test]
			fn rational_mul_correction_works() {
				assert_eq!(
					super::rational_mul_correction::<$type, $name>(
						<$type>::max_value(),
						<$type>::max_value(),
						<$type>::max_value(),
						super::Rounding::NearestPrefDown,
					),
					0,
				);
				assert_eq!(
					super::rational_mul_correction::<$type, $name>(
						<$type>::max_value() - 1,
						<$type>::max_value(),
						<$type>::max_value(),
						super::Rounding::NearestPrefDown,
					),
					<$type>::max_value() - 1,
				);
				assert_eq!(
					super::rational_mul_correction::<$upper_type, $name>(
						((<$type>::max_value() - 1) as $upper_type).pow(2),
						<$type>::max_value(),
						<$type>::max_value(),
						super::Rounding::NearestPrefDown,
					),
					1,
				);
				assert_eq!(
					super::rational_mul_correction::<$upper_type, $name>(
						(<$type>::max_value() as $upper_type).pow(2) - 1,
						<$type>::max_value(),
						<$type>::max_value(),
						super::Rounding::NearestPrefDown,
					),
					<$upper_type>::from((<$type>::max_value() - 1)),
				);
				assert_eq!(
					super::rational_mul_correction::<$upper_type, $name>(
						(<$type>::max_value() as $upper_type).pow(2),
						<$type>::max_value(),
						2 as $type,
						super::Rounding::NearestPrefDown,
					),
					<$type>::max_value() as $upper_type / 2,
				);
				assert_eq!(
					super::rational_mul_correction::<$upper_type, $name>(
						(<$type>::max_value() as $upper_type).pow(2) - 1,
						2 as $type,
						<$type>::max_value(),
						super::Rounding::NearestPrefDown,
					),
					2,
				);
				assert_eq!(
					super::rational_mul_correction::<$upper_type, $name>(
						(<$type>::max_value() as $upper_type).pow(2) - 1,
						2 as $type,
						<$type>::max_value(),
						super::Rounding::Down,
					),
					1,
				);
			}
			#[test]
			#[allow(unused)]
			fn const_fns_work() {
				const C1: $name = $name::from_percent(50);
				const C2: $name = $name::one();
				const C3: $name = $name::zero();
				const C4: $name = $name::from_parts(1);
				const C5: bool = C1.deconstruct() == 0;
			}
			#[test]
			fn compact_decoding_saturate_when_beyond_accuracy() {
				use num_traits::Bounded;
				use codec::Compact;
				let p = Compact::<$name>::decode(&mut &Compact(<$type>::max_value()).encode()[..])
					.unwrap();
				assert_eq!((p.0).0, $max);
				assert_eq!($name::from(p), $name::max_value());
			}
			#[allow(unused_imports)]
			use super::*;
			#[test]
			fn test_add_basic() {
				assert_eq!($name::from_parts(1) + $name::from_parts(1), $name::from_parts(2));
				assert_eq!($name::from_parts(10) + $name::from_parts(10), $name::from_parts(20));
			}
			#[test]
			fn test_basic_checked_add() {
				assert_eq!(
					$name::from_parts(1).checked_add(&$name::from_parts(1)),
					Some($name::from_parts(2))
				);
				assert_eq!(
					$name::from_parts(10).checked_add(&$name::from_parts(10)),
					Some($name::from_parts(20))
				);
				assert_eq!(
					$name::from_parts(<$type>::MAX).checked_add(&$name::from_parts(<$type>::MAX)),
					None
				);
				assert_eq!(
					$name::from_parts($max).checked_add(&$name::from_parts(1)),
					None
				);
			}
			#[test]
			fn test_basic_saturating_add() {
				assert_eq!(
					$name::from_parts(1).saturating_add($name::from_parts(1)),
					$name::from_parts(2)
				);
				assert_eq!(
					$name::from_parts(10).saturating_add($name::from_parts(10)),
					$name::from_parts(20)
				);
				assert_eq!(
					$name::from_parts(<$type>::MAX).saturating_add($name::from_parts(<$type>::MAX)),
					$name::from_parts(<$type>::MAX)
				);
			}
			#[test]
			fn test_basic_sub() {
				assert_eq!($name::from_parts(2) - $name::from_parts(1), $name::from_parts(1));
				assert_eq!($name::from_parts(20) - $name::from_parts(10), $name::from_parts(10));
			}
			#[test]
			fn test_basic_checked_sub() {
				assert_eq!(
					$name::from_parts(2).checked_sub(&$name::from_parts(1)),
					Some($name::from_parts(1))
				);
				assert_eq!(
					$name::from_parts(20).checked_sub(&$name::from_parts(10)),
					Some($name::from_parts(10))
				);
				assert_eq!($name::from_parts(0).checked_sub(&$name::from_parts(1)), None);
			}
			#[test]
			fn test_basic_saturating_sub() {
				assert_eq!(
					$name::from_parts(2).saturating_sub($name::from_parts(1)),
					$name::from_parts(1)
				);
				assert_eq!(
					$name::from_parts(20).saturating_sub($name::from_parts(10)),
					$name::from_parts(10)
				);
				assert_eq!(
					$name::from_parts(0).saturating_sub($name::from_parts(1)),
					$name::from_parts(0)
				);
			}
			#[test]
			fn test_basic_checked_mul() {
				assert_eq!(
					$name::from_parts($max).checked_mul(&$name::from_parts($max)),
					Some($name::from_percent(100))
				);
				assert_eq!(
					$name::from_percent(100).checked_mul(&$name::from_percent(100)),
					Some($name::from_percent(100))
				);
				assert_eq!(
					$name::from_percent(50).checked_mul(&$name::from_percent(26)),
					Some($name::from_percent(13))
				);
				assert_eq!(
					$name::from_percent(0).checked_mul(&$name::from_percent(0)),
					Some($name::from_percent(0))
				);
			}
		}
	};
}
macro_rules! implement_per_thing_with_perthousand {
	(
		$name:ident,
		$test_mod:ident,
		$pt_test_mod:ident,
		[$($test_units:tt),+],
		$max:tt,
		$type:ty,
		$upper_type:ty,
		$title:expr $(,)?
	) => {
		implement_per_thing! {
			$name, $test_mod, [ $( $test_units ),+ ], $max, $type, $upper_type, $title,
		}
		impl $name {
			pub const fn from_perthousand(x: $type) -> Self {
				Self(([x, 1000][(x > 1000) as usize] as $upper_type * $max as $upper_type / 1000) as $type)
			}
		}
		#[cfg(test)]
		mod $pt_test_mod {
			use super::$name;
			use crate::traits::Zero;
			#[test]
			fn from_perthousand_works() {
				assert_eq!($name::from_perthousand(00), $name::from_parts(Zero::zero()));
				assert_eq!($name::from_perthousand(100), $name::from_parts($max / 10));
				assert_eq!($name::from_perthousand(1000), $name::from_parts($max));
				assert_eq!($name::from_perthousand(2000), $name::from_parts($max));
			}
			#[test]
			#[allow(unused)]
			fn const_fns_work() {
				const C1: $name = $name::from_perthousand(500);
			}
		}
	}
}
#[test]
fn from_rational_with_rounding_works_in_extreme_case() {
	use Rounding::*;
	for &r in [Down, NearestPrefDown, NearestPrefUp, Up].iter() {
		Percent::from_rational_with_rounding(1, u64::max_value(), r).unwrap();
		Percent::from_rational_with_rounding(1, u32::max_value(), r).unwrap();
		Percent::from_rational_with_rounding(1, u16::max_value(), r).unwrap();
		Percent::from_rational_with_rounding(u64::max_value() - 1, u64::max_value(), r).unwrap();
		Percent::from_rational_with_rounding(u32::max_value() - 1, u32::max_value(), r).unwrap();
		Percent::from_rational_with_rounding(u16::max_value() - 1, u16::max_value(), r).unwrap();
		PerU16::from_rational_with_rounding(1, u64::max_value(), r).unwrap();
		PerU16::from_rational_with_rounding(1, u32::max_value(), r).unwrap();
		PerU16::from_rational_with_rounding(1, u16::max_value(), r).unwrap();
		PerU16::from_rational_with_rounding(u64::max_value() - 1, u64::max_value(), r).unwrap();
		PerU16::from_rational_with_rounding(u32::max_value() - 1, u32::max_value(), r).unwrap();
		PerU16::from_rational_with_rounding(u16::max_value() - 1, u16::max_value(), r).unwrap();
		Permill::from_rational_with_rounding(1, u64::max_value(), r).unwrap();
		Permill::from_rational_with_rounding(1, u32::max_value(), r).unwrap();
		Permill::from_rational_with_rounding(u64::max_value() - 1, u64::max_value(), r).unwrap();
		Permill::from_rational_with_rounding(u32::max_value() - 1, u32::max_value(), r).unwrap();
		Perbill::from_rational_with_rounding(1, u64::max_value(), r).unwrap();
		Perbill::from_rational_with_rounding(1, u32::max_value(), r).unwrap();
		Perbill::from_rational_with_rounding(u64::max_value() - 1, u64::max_value(), r).unwrap();
		Perbill::from_rational_with_rounding(u32::max_value() - 1, u32::max_value(), r).unwrap();
	}
}
implement_per_thing!(Percent, test_per_cent, [u32, u64, u128], 100u8, u8, u16, "_Percent_",);
implement_per_thing_with_perthousand!(
	PerU16,
	test_peru16,
	test_peru16_extra,
	[u32, u64, u128],
	65535_u16,
	u16,
	u32,
	"_Parts per 65535_",
);
implement_per_thing_with_perthousand!(
	Permill,
	test_permill,
	test_permill_extra,
	[u32, u64, u128],
	1_000_000u32,
	u32,
	u64,
	"_Parts per Million_",
);
implement_per_thing_with_perthousand!(
	Perbill,
	test_perbill,
	test_perbill_extra,
	[u32, u64, u128],
	1_000_000_000u32,
	u32,
	u64,
	"_Parts per Billion_",
);
implement_per_thing_with_perthousand!(
	Perquintill,
	test_perquintill,
	test_perquintill_extra,
	[u64, u128],
	1_000_000_000_000_000_000u64,
	u64,
	u128,
	"_Parts per Quintillion_",
);