#![allow(clippy::type_repetition_in_bounds)]
use super::{Cast, Digits, Exponent, Fix, Mantissa, Radix};
use core::ops::{
Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign,
};
use typenum::{Diff, Max, Maximum, Min, Minimum, Sum, P1};
pub trait Add1: Add<P1> {}
impl<T: Add<P1>> Add1 for T {}
type Sum1<T> = <T as Add<P1>>::Output;
impl<R, B, E> Neg for Fix<R, B, E>
where
R: Radix<B>,
B: Digits,
E: Exponent,
Mantissa<R, B>: Neg<Output = Mantissa<R, B>>,
{
type Output = Self;
fn neg(self) -> Self {
Self::new(-self.bits)
}
}
type SumE<E1, E2> = Minimum<E1, E2>;
type SumBovf<B1, E1, B2, E2> = Diff<Maximum<Sum<B1, E1>, Sum<B2, E2>>, SumE<E1, E2>>;
type SumB<B1, E1, B2, E2> = Sum1<SumBovf<B1, E1, B2, E2>>;
type SumT<R, B1, E1, B2, E2> = Fix<R, SumB<B1, E1, B2, E2>, SumE<E1, E2>>;
impl<R, B1, E1, B2, E2> Add<Fix<R, B2, E2>> for Fix<R, B1, E1>
where
R: Radix<B1> + Radix<B2> + Radix<SumB<B1, E1, B2, E2>>,
B1: Digits + Add<E1>,
E1: Exponent + Min<E2>,
B2: Digits + Add<E2>,
E2: Exponent,
Sum<B1, E1>: Max<Sum<B2, E2>>,
SumBovf<B1, E1, B2, E2>: Add1,
SumB<B1, E1, B2, E2>: Digits,
SumE<E1, E2>: Exponent,
Maximum<Sum<B1, E1>, Sum<B2, E2>>: Sub<Minimum<E1, E2>>,
Mantissa<R, SumB<B1, E1, B2, E2>>: Cast<Mantissa<R, B1>>
+ Cast<Mantissa<R, B2>>
+ Add<Output = Mantissa<R, SumB<B1, E1, B2, E2>>>,
{
type Output = SumT<R, B1, E1, B2, E2>;
fn add(self, other: Fix<R, B2, E2>) -> Self::Output {
let a: SumT<R, B1, E1, B2, E2> = self.convert();
let b: SumT<R, B1, E1, B2, E2> = other.convert();
Self::Output::new(a.bits + b.bits)
}
}
impl<R, B1, E1, B2, E2> Sub<Fix<R, B2, E2>> for Fix<R, B1, E1>
where
R: Radix<B1> + Radix<B2> + Radix<SumB<B1, E1, B2, E2>>,
B1: Digits + Add<E1>,
E1: Exponent + Min<E2>,
B2: Digits + Add<E2>,
E2: Exponent,
Sum<B1, E1>: Max<Sum<B2, E2>>,
SumBovf<B1, E1, B2, E2>: Add1,
SumB<B1, E1, B2, E2>: Digits,
SumE<E1, E2>: Exponent,
Maximum<Sum<B1, E1>, Sum<B2, E2>>: Sub<Minimum<E1, E2>>,
Mantissa<R, SumB<B1, E1, B2, E2>>: Cast<Mantissa<R, B1>>
+ Cast<Mantissa<R, B2>>
+ Sub<Output = Mantissa<R, SumB<B1, E1, B2, E2>>>,
{
type Output = SumT<R, B1, E1, B2, E2>;
fn sub(self, other: Fix<R, B2, E2>) -> Self::Output {
let a: SumT<R, B1, E1, B2, E2> = self.convert();
let b: SumT<R, B1, E1, B2, E2> = other.convert();
Self::Output::new(a.bits - b.bits)
}
}
type ProdB<B1, B2> = Sum<B1, B2>;
type ProdE<E1, E2> = Sum<E1, E2>;
type ProdM<R, B1, B2> = Mantissa<R, ProdB<B1, B2>>;
type ProdT<R, B1, E1, B2, E2> = Fix<R, ProdB<B1, B2>, ProdE<E1, E2>>;
impl<R, B1, E1, B2, E2> Mul<Fix<R, B2, E2>> for Fix<R, B1, E1>
where
R: Radix<B1> + Radix<B2> + Radix<ProdB<B1, B2>>,
B1: Digits + Add<B2>,
E1: Exponent + Add<E2>,
B2: Digits,
E2: Exponent,
ProdB<B1, B2>: Digits,
ProdE<E1, E2>: Exponent,
ProdM<R, B1, B2>:
Cast<Mantissa<R, B1>> + Cast<Mantissa<R, B2>> + Mul<Output = ProdM<R, B1, B2>>,
{
type Output = ProdT<R, B1, E1, B2, E2>;
fn mul(self, other: Fix<R, B2, E2>) -> Self::Output {
let a = ProdM::<R, B1, B2>::cast(self.bits);
let b = ProdM::<R, B1, B2>::cast(other.bits);
Self::Output::new(a * b)
}
}
type QuotB<B1, B2> = Diff<B1, B2>;
type QuotE<E1, E2> = Diff<E1, E2>;
type QuotM<R, B1, B2> = Mantissa<R, QuotB<B1, B2>>;
type QuotT<R, B1, E1, B2, E2> = Fix<R, QuotB<B1, B2>, QuotE<E1, E2>>;
impl<R, B1, E1, B2, E2> Div<Fix<R, B2, E2>> for Fix<R, B1, E1>
where
R: Radix<B1> + Radix<B2> + Radix<QuotB<B1, B2>>,
B1: Digits + Sub<B2>,
E1: Exponent + Sub<E2>,
B2: Digits,
E2: Exponent,
QuotB<B1, B2>: Digits,
QuotE<E1, E2>: Exponent,
QuotM<R, B1, B2>: Cast<Mantissa<R, B1>>,
Mantissa<R, B1>: Cast<Mantissa<R, B2>> + Div<Output = Mantissa<R, B1>>,
{
type Output = QuotT<R, B1, E1, B2, E2>;
fn div(self, other: Fix<R, B2, E2>) -> Self::Output {
let a = self.bits;
let b = Mantissa::<R, B1>::cast(other.bits);
let c = QuotM::<R, B1, B2>::cast(a / b);
Self::Output::new(c)
}
}
impl<R, B, E> Rem for Fix<R, B, E>
where
R: Radix<B>,
B: Digits,
E: Exponent,
Mantissa<R, B>: Rem<Output = Mantissa<R, B>>,
{
type Output = Self;
fn rem(self, rhs: Self) -> Self {
Self::new(self.bits % rhs.bits)
}
}
impl<R, B, E, T> AddAssign<T> for Fix<R, B, E>
where
R: Radix<B>,
Mantissa<R, B>: AddAssign,
Fix<R, B, E>: Cast<T>,
{
fn add_assign(&mut self, other: T) {
let b = Fix::<R, B, E>::cast(other);
self.bits += b.bits;
}
}
impl<R, B, E, T> SubAssign<T> for Fix<R, B, E>
where
R: Radix<B>,
Mantissa<R, B>: SubAssign,
Fix<R, B, E>: Cast<T>,
{
fn sub_assign(&mut self, other: T) {
let b = Fix::<R, B, E>::cast(other);
self.bits -= b.bits;
}
}
impl<R, B, E, T> MulAssign<T> for Fix<R, B, E>
where
R: Radix<B>,
Mantissa<R, B>: MulAssign<T>,
{
fn mul_assign(&mut self, other: T) {
self.bits *= other;
}
}
impl<R, B, E, T> DivAssign<T> for Fix<R, B, E>
where
R: Radix<B>,
Mantissa<R, B>: DivAssign<T>,
{
fn div_assign(&mut self, other: T) {
self.bits /= other;
}
}
impl<R, B, E1, E2> RemAssign<Fix<R, B, E2>> for Fix<R, B, E1>
where
R: Radix<B>,
Mantissa<R, B>: RemAssign,
{
fn rem_assign(&mut self, other: Fix<R, B, E2>) {
self.bits %= other.bits;
}
}
#[cfg(test)]
mod tests {
use super::super::si::{Centi, Kilo, Milli, UCenti, UKilo, Unit};
use typenum::*;
#[test]
fn convert_milli_to_kilo() {
assert_eq!(Kilo::<P2>::new(15), Milli::<P8>::new(15_000_000).convert());
}
#[test]
fn convert_kilo_to_milli() {
assert_eq!(Milli::<P8>::new(15_000_000), Kilo::<P2>::new(15).convert());
}
#[test]
fn cmp_unsigned() {
assert!(UKilo::<P1>::new(1) < UKilo::new(2));
}
#[test]
fn cmp_signed() {
assert!(Kilo::<P1>::new(1) < Kilo::new(2));
assert!(Kilo::<P1>::new(-2) < Kilo::new(-1));
}
#[test]
fn neg_signed() {
assert_eq!(-Kilo::<P1>::new(1), Kilo::new(-1));
assert_eq!(-Kilo::<P1>::new(-1), Kilo::new(1));
}
#[test]
fn add_signed() {
assert_eq!(Kilo::<P2>::new(3), Kilo::<P1>::new(1) + Kilo::<P1>::new(2));
assert_eq!(
Centi::<P4>::new(0_30),
Centi::<P3>::new(0_10) + Centi::<P3>::new(0_20)
);
}
#[test]
fn add_unsigned() {
assert_eq!(
UKilo::<P1>::new(1) + UKilo::<P1>::new(2),
UKilo::<P2>::new(3)
);
assert_eq!(
UCenti::<P3>::new(0_10) + UCenti::<P3>::new(0_20),
UCenti::<P4>::new(0_30)
);
}
#[test]
fn sub_signed() {
assert_eq!(Kilo::<P2>::new(1), Kilo::<P1>::new(3) - Kilo::<P1>::new(2));
}
#[test]
fn mul_signed() {
assert_eq!(Unit::new(6), Kilo::<P1>::new(2) * Milli::<P1>::new(3));
}
#[test]
fn div_signed() {
assert_eq!(Unit::new(3), Kilo::<P2>::new(6) / Kilo::<P1>::new(2));
}
#[test]
fn rem_signed() {
assert_eq!(Kilo::<P1>::new(1), Kilo::new(6) % Kilo::new(5));
}
#[test]
fn add_assign_signed() {
let mut a = Kilo::<P5>::new(1);
a += Kilo::<P5>::new(2);
assert_eq!(Kilo::new(3), a);
}
#[test]
fn sub_assign_signed() {
let mut a = Kilo::<P5>::new(3);
a -= Kilo::<P5>::new(2);
assert_eq!(Kilo::new(1), a);
}
#[test]
fn mul_assign_signed_bits() {
let mut a = Kilo::<P5>::new(2);
a *= 3;
assert_eq!(Kilo::new(6), a);
}
#[test]
fn div_assign_signed_bits() {
let mut a = Kilo::<P9>::new(6);
a /= 2;
assert_eq!(Kilo::new(3), a);
}
#[test]
fn rem_assign_signed() {
let mut a = Kilo::<P9>::new(6);
a %= Milli::new(5);
assert_eq!(Kilo::new(1), a);
}
}