use super::P8E0;
use crate::WithSign;
use core::{mem, ops};
impl ops::Neg for P8E0 {
type Output = Self;
#[inline]
fn neg(self) -> Self {
Self::new(self.0.wrapping_neg())
}
}
impl ops::AddAssign for P8E0 {
#[inline]
fn add_assign(&mut self, other: Self) {
*self = *self + other
}
}
impl ops::SubAssign for P8E0 {
#[inline]
fn sub_assign(&mut self, other: Self) {
*self = *self - other
}
}
impl ops::MulAssign for P8E0 {
#[inline]
fn mul_assign(&mut self, other: Self) {
*self = *self * other
}
}
impl ops::DivAssign for P8E0 {
#[inline]
fn div_assign(&mut self, other: Self) {
*self = *self / other
}
}
impl ops::RemAssign for P8E0 {
#[inline]
fn rem_assign(&mut self, other: Self) {
*self = *self % other
}
}
impl ops::Add for P8E0 {
type Output = Self;
#[inline]
fn add(self, other: Self) -> Self {
let ui_a = self.to_bits();
let ui_b = other.to_bits();
if self.is_zero() || other.is_zero() {
Self::from_bits(ui_a | ui_b)
} else if self.is_nar() || other.is_nar() {
Self::NAR
} else {
if Self::sign_ui(ui_a ^ ui_b) {
Self::sub_mags(ui_a, ui_b)
} else {
Self::add_mags(ui_a, ui_b)
}
}
}
}
impl ops::Sub for P8E0 {
type Output = Self;
#[inline]
fn sub(self, other: Self) -> Self {
let ui_a = self.to_bits();
let ui_b = other.to_bits();
if self.is_nar() || other.is_nar() {
Self::NAR
} else if self.is_zero() || other.is_zero() {
Self::from_bits(ui_a | ui_b.wrapping_neg())
} else {
if Self::sign_ui(ui_a ^ ui_b) {
Self::add_mags(ui_a, ui_b.wrapping_neg())
} else {
Self::sub_mags(ui_a, ui_b.wrapping_neg())
}
}
}
}
impl ops::Div for P8E0 {
type Output = Self;
#[inline]
fn div(self, other: Self) -> Self {
let mut ui_a = self.to_bits();
let mut ui_b = other.to_bits();
if self.is_nar() || other.is_nar() || other.is_zero() {
return Self::NAR;
} else if self.is_zero() {
return Self::ZERO;
}
let sign_a = Self::sign_ui(ui_a);
let sign_b = Self::sign_ui(ui_b);
let sign_z = sign_a ^ sign_b;
if sign_a {
ui_a = ui_a.wrapping_neg();
}
if sign_b {
ui_b = ui_b.wrapping_neg();
}
let (mut k_a, frac_a) = Self::separate_bits(ui_a);
let (k_b, frac_b) = Self::separate_bits(ui_b);
k_a -= k_b;
let frac16_a = (frac_a as u16) << 7;
let (quot, rem) = crate::div(frac16_a as i32, frac_b as i32);
let mut frac16 = quot as u16;
if frac16 != 0 {
let rcarry = (frac16 >> 7) != 0; if !rcarry {
k_a -= 1;
frac16 <<= 1;
}
}
let (regime, reg_sa, reg_len) = Self::calculate_regime(k_a);
let u_z = if reg_len > 6 {
if reg_sa {
0x7F
} else {
0x1
}
} else {
frac16 &= 0x7F;
let frac_a = (frac16 >> (reg_len + 1)) as u8;
let bit_n_plus_one = (0x1 & (frac16 >> reg_len)) != 0;
let mut u_z = Self::pack_to_ui(regime, frac_a);
if bit_n_plus_one {
let bits_more = if rem != 0 {
true
} else {
(((1 << reg_len) - 1) & frac16) != 0
};
u_z += (u_z & 1) | (bits_more as u8);
}
u_z
};
Self::from_bits(u_z.with_sign(sign_z))
}
}
impl P8E0 {
#[inline]
fn calc_ui(k: i8, mut frac16: u16) -> u8 {
let (regime, reg_s, reg_len) = Self::calculate_regime(k);
if reg_len > 6 {
if reg_s {
0x7F
} else {
0x1
}
} else {
frac16 = (frac16 & 0x3FFF) >> reg_len;
let frac = (frac16 >> 8) as u8;
let bit_n_plus_one = (frac16 & 0x80) != 0;
let mut u_z = Self::pack_to_ui(regime, frac);
if bit_n_plus_one {
let bits_more = (frac16 & 0x7F) != 0;
u_z += (u_z & 1) | (bits_more as u8);
}
u_z
}
}
}
impl ops::Mul for P8E0 {
type Output = Self;
#[inline]
fn mul(self, other: Self) -> Self {
let mut ui_a = self.to_bits();
let mut ui_b = other.to_bits();
if self.is_nar() || other.is_nar() {
return Self::NAR;
} else if self.is_zero() || other.is_zero() {
return Self::ZERO;
}
let sign_a = Self::sign_ui(ui_a);
let sign_b = Self::sign_ui(ui_b);
let sign_z = sign_a ^ sign_b;
if sign_a {
ui_a = ui_a.wrapping_neg();
}
if sign_b {
ui_b = ui_b.wrapping_neg();
}
let (mut k_a, frac_a) = Self::separate_bits(ui_a);
let (k_b, frac_b) = Self::separate_bits(ui_b);
k_a += k_b;
let mut frac16 = (frac_a as u16) * (frac_b as u16);
let rcarry = (frac16 & 0x_8000) != 0; if rcarry {
k_a += 1;
frac16 >>= 1;
}
let u_z = Self::calc_ui(k_a, frac16);
Self::from_bits(u_z.with_sign(sign_z))
}
}
impl P8E0 {
#[inline]
fn add_mags(mut ui_a: u8, mut ui_b: u8) -> Self {
let sign = Self::sign_ui(ui_a);
if sign {
ui_a = ui_a.wrapping_neg();
ui_b = ui_b.wrapping_neg();
}
if (ui_a as i8) < (ui_b as i8) {
mem::swap(&mut ui_a, &mut ui_b);
}
let (mut k_a, frac_a) = Self::separate_bits(ui_a);
let mut frac16_a = (frac_a as u16) << 7;
let (k_b, frac_b) = Self::separate_bits(ui_b);
let shift_right = (k_a as i16) - (k_b as i16);
frac16_a += (frac_b as u16)
.checked_shl((7 - shift_right) as u32)
.unwrap_or(0);
let rcarry = (0x8000 & frac16_a) != 0; if rcarry {
k_a += 1;
frac16_a >>= 1;
}
let u_z = Self::calc_ui(k_a, frac16_a);
Self::from_bits(u_z.with_sign(sign))
}
#[inline]
fn sub_mags(mut ui_a: u8, mut ui_b: u8) -> Self {
let mut sign = Self::sign_ui(ui_a);
if sign {
ui_a = ui_a.wrapping_neg();
} else {
ui_b = ui_b.wrapping_neg();
}
if ui_a == ui_b {
return Self::ZERO;
}
if ui_a < ui_b {
mem::swap(&mut ui_a, &mut ui_b);
sign = !sign; }
let (mut k_a, frac_a) = Self::separate_bits(ui_a);
let mut frac16_a = (frac_a as u16) << 7;
let (k_b, frac_b) = Self::separate_bits(ui_b);
let shift_right = (k_a as i16) - (k_b as i16);
let mut frac16_b = (frac_b as u16) << 7;
if shift_right >= 14 {
return Self::from_bits(ui_a.with_sign(sign));
} else {
frac16_b >>= shift_right;
}
frac16_a -= frac16_b;
while (frac16_a >> 14) == 0 {
k_a -= 1;
frac16_a <<= 1;
}
let ecarry = ((0x4000 & frac16_a) >> 14) != 0;
if !ecarry {
k_a -= 1;
frac16_a <<= 1;
}
let u_z = Self::calc_ui(k_a, frac16_a);
Self::from_bits(u_z.with_sign(sign))
}
}
impl ops::Rem for P8E0 {
type Output = Self;
fn rem(self, other: Self) -> Self {
self - (self / other).trunc() * other
}
}
#[cfg(test)]
fn test_ops(fun: fn(P8E0, P8E0, f64, f64) -> (P8E0, f64)) {
use rand::Rng;
let mut rng = rand::thread_rng();
for _ in 0..crate::NTESTS8 {
let p_a: P8E0 = rng.gen();
let p_b: P8E0 = rng.gen();
let f_a = f64::from(p_a);
let f_b = f64::from(p_b);
let (p, f) = fun(p_a, p_b, f_a, f_b);
assert_eq!(p, P8E0::from(f));
}
}
#[test]
fn add() {
test_ops(|p_a, p_b, f_a, f_b| (p_a + p_b, f_a + f_b));
}
#[test]
fn sub() {
test_ops(|p_a, p_b, f_a, f_b| (p_a - p_b, f_a - f_b));
}
#[test]
fn mul() {
test_ops(|p_a, p_b, f_a, f_b| (p_a * p_b, f_a * f_b));
}
#[test]
fn div() {
test_ops(|p_a, p_b, f_a, f_b| (p_a / p_b, f_a / f_b));
}