use crate::bid_decimal_data::{BID_ESTIMATE_DECIMAL_DIGITS, BID_POWER10_TABLE_128, BID_RECIP_SCALE, BID_RECIPROCALS10_128, BID_ROUND_CONST_TABLE_128};
use crate::bid_internal::*;
use crate::d128::{_IDEC_flags, StatusFlags, RoundingMode};
pub (crate) fn bid128_quantize(x: &BID_UINT128, y: &BID_UINT128, rnd_mode: RoundingMode, pfpsf: &mut _IDEC_flags) -> BID_UINT128 {
let CT: BID_UINT256;
let mut CX: BID_UINT128 = Default::default();
let mut CY: BID_UINT128 = Default::default();
let T: &BID_UINT128;
let mut CX2: BID_UINT128 = Default::default();
let mut CR: BID_UINT128 = Default::default();
let mut Stemp: BID_UINT128 = Default::default();
let mut res: BID_UINT128 = Default::default();
let mut REM_H: BID_UINT128 = Default::default();
let mut C2N: BID_UINT128 = Default::default();
let mut sign_x: BID_UINT64 = 0;
let mut sign_y: BID_UINT64 = 0;
let remainder_h: BID_UINT64;
let carry: BID_UINT64;
let CY64: BID_UINT64;
let valid_x: BID_UINT64;
let mut tempx: BID_UI32FLOAT = Default::default();
let mut exponent_x: i32 = 0;
let mut exponent_y: i32 = 0;
let mut digits_x: i32;
let extra_digits: i32;
let amount: i32;
let expon_diff: i32;
let total_digits: i32;
let bin_expon_cx: i32;
let mut rmode: RoundingMode;
let mut status: _IDEC_flags;
valid_x = unpack_BID128_value(&mut sign_x, &mut exponent_x, &mut CX, x);
if unpack_BID128_value(&mut sign_y, &mut exponent_y, &mut CY, y) == 0 {
if (x.w[1] & SNAN_MASK64) == SNAN_MASK64 { __set_status_flags(pfpsf, StatusFlags::BID_INVALID_EXCEPTION);
}
if (y.w[1] & 0x7c00000000000000u64) == 0x7c00000000000000u64 {
if (y.w[1] & 0x7e00000000000000u64) == 0x7e00000000000000u64 {
__set_status_flags(pfpsf, StatusFlags::BID_INVALID_EXCEPTION);
}
if (x.w[1] & 0x7c00000000000000u64) != 0x7c00000000000000u64 {
res.w[1] = CY.w[1] & QUIET_MASK64;
res.w[0] = CY.w[0];
} else {
res.w[1] = CX.w[1] & QUIET_MASK64;
res.w[0] = CX.w[0];
}
return res;
}
if (y.w[1] & 0x7800000000000000u64) == 0x7800000000000000u64 {
if (x.w[1] & 0x7c00000000000000u64) < 0x7800000000000000u64 {
__set_status_flags(pfpsf, StatusFlags::BID_INVALID_EXCEPTION);
res.w[1] = 0x7c00000000000000u64;
res.w[0] = 0;
return res;
} else if (x.w[1] & 0x7c00000000000000u64) <= 0x7800000000000000u64 {
res.w[1] = CX.w[1] & QUIET_MASK64;
res.w[0] = CX.w[0];
return res;
}
}
}
if valid_x == 0 {
if (x.w[1] & 0x7c00000000000000u64) == 0x7800000000000000u64 {
__set_status_flags(pfpsf, StatusFlags::BID_INVALID_EXCEPTION);
res.w[1] = 0x7c00000000000000u64;
res.w[0] = 0;
return res;
} else if (x.w[1] & 0x7c00000000000000u64) == 0x7c00000000000000u64 {
if (x.w[1] & 0x7e00000000000000u64) == 0x7e00000000000000u64 {
__set_status_flags(pfpsf, StatusFlags::BID_INVALID_EXCEPTION);
}
res.w[1] = CX.w[1] & QUIET_MASK64;
res.w[0] = CX.w[0];
return res;
}
if CX.w[1] == 0 && CX.w[0] == 0 {
res = bid_get_BID128_very_fast(sign_x, exponent_y, &CX);
return res;
}
}
unsafe {
if CX.w[1] != 0 {
tempx.d = CX.w[1] as f32;
bin_expon_cx = (((tempx.ui32 >> 23) & 0xff) - 0x7f + 64) as i32;
} else {
tempx.d = CX.w[0] as f32;
bin_expon_cx = (((tempx.ui32 >> 23) & 0xff) - 0x7f) as i32;
}
}
digits_x = BID_ESTIMATE_DECIMAL_DIGITS[bin_expon_cx as usize];
if CX.w[1] > BID_POWER10_TABLE_128[digits_x as usize].w[1]
|| (CX.w[1] == BID_POWER10_TABLE_128[digits_x as usize].w[1]
&& CX.w[0] >= BID_POWER10_TABLE_128[digits_x as usize].w[0]) {
digits_x += 1;
}
expon_diff = exponent_x - exponent_y;
total_digits = digits_x + expon_diff;
if (total_digits as BID_UINT32) <= 34 {
if expon_diff >= 0 {
T = &BID_POWER10_TABLE_128[expon_diff as usize];
CX2 = __mul_128x128_low(T, &CX);
res = bid_get_BID128_very_fast(sign_x, exponent_y, &CX2);
return res;
}
rmode = rnd_mode;
if sign_x != 0 && ((rmode as u32 - 1u32) < 2) {
rmode = RoundingMode::from(3 - (rmode as u32));
}
extra_digits = -expon_diff;
CX = __add_128_128(&CX, &BID_ROUND_CONST_TABLE_128[rmode as usize][extra_digits as usize]);
CT = __mul_128x128_to_256(&CX, &BID_RECIPROCALS10_128[extra_digits as usize]);
amount = BID_RECIP_SCALE[extra_digits as usize];
CX2.w[0] = CT.w[2];
CX2.w[1] = CT.w[3];
if amount >= 64 {
CR.w[1] = 0;
CR.w[0] = CX2.w[1] >> (amount - 64);
} else {
CR = __shr_128(&CX2, amount);
}
if rnd_mode == RoundingMode::NearestEven && (CR.w[0] & 1) == 1 {
remainder_h = if amount >= 64 {
CX2.w[0] | (CX2.w[1] << (128 - amount))
} else {
CX2.w[0] << (64 - amount)
};
if remainder_h == 0
&& (CT.w[1] < BID_RECIPROCALS10_128[extra_digits as usize].w[1]
|| (CT.w[1] == BID_RECIPROCALS10_128[extra_digits as usize].w[1]
&& CT.w[0] < BID_RECIPROCALS10_128[extra_digits as usize].w[0])) {
CR.w[0] -= 1;
}
}
status = StatusFlags::BID_INEXACT_EXCEPTION;
if amount >= 64 {
REM_H.w[1] = CX2.w[1] << (128 - amount);
REM_H.w[0] = CX2.w[0];
} else {
REM_H.w[1] = CX2.w[0] << (64 - amount);
REM_H.w[0] = 0;
}
match rmode {
RoundingMode::NearestEven | RoundingMode::NearestAway => {
if REM_H.w[1] == 0x8000000000000000u64 && REM_H.w[0] == 0
&& (CT.w[1] < BID_RECIPROCALS10_128[extra_digits as usize].w[1]
|| (CT.w[1] == BID_RECIPROCALS10_128[extra_digits as usize].w[1]
&& CT.w[0] < BID_RECIPROCALS10_128[extra_digits as usize].w[0])) {
status = StatusFlags::BID_EXACT_STATUS;
}
},
RoundingMode::Downward | RoundingMode::TowardZero => {
if (REM_H.w[1] | REM_H.w[0]) == 0
&& (CT.w[1] < BID_RECIPROCALS10_128[extra_digits as usize].w[1]
|| (CT.w[1] == BID_RECIPROCALS10_128[extra_digits as usize].w[1]
&& CT.w[0] < BID_RECIPROCALS10_128[extra_digits as usize].w[0])) {
status = StatusFlags::BID_EXACT_STATUS;
}
},
_ => {
(Stemp.w[0], CY64) = __add_carry_out(CT.w[0], BID_RECIPROCALS10_128[extra_digits as usize].w[0]);
(Stemp.w[1], carry) = __add_carry_in_out(CT.w[1], BID_RECIPROCALS10_128[extra_digits as usize].w[1], CY64);
if amount < 64 {
C2N.w[1] = 0;
C2N.w[0] = (1 as BID_UINT64) << amount;
REM_H.w[0] = REM_H.w[1] >> (64 - amount);
REM_H.w[1] = 0;
} else {
C2N.w[1] = (1 as BID_UINT64) << (amount - 64);
C2N.w[0] = 0;
REM_H.w[1] >>= 128 - amount;
}
REM_H.w[0] += carry;
if REM_H.w[0] < carry {
REM_H.w[1] += 1;
}
if __unsigned_compare_ge_128(&REM_H, &C2N) {
status = StatusFlags::BID_EXACT_STATUS;
}
}
}
__set_status_flags (pfpsf, status);
res = bid_get_BID128_very_fast(sign_x, exponent_y, &CR);
return res;
}
if total_digits < 0 {
CR.w[1] = 0;
CR.w[0] = 0;
rmode = rnd_mode;
if sign_x != 0 && ((rmode as u32 - 1u32) < 2) {
rmode = RoundingMode::from(3 - (rmode as u32));
}
if rmode == RoundingMode::Upward {
CR.w[0] = 1;
}
__set_status_flags(pfpsf, StatusFlags::BID_INEXACT_EXCEPTION);
res = bid_get_BID128_very_fast(sign_x, exponent_y, &CR);
return res;
}
__set_status_flags(pfpsf, StatusFlags::BID_INVALID_EXCEPTION);
res.w[1] = 0x7c00000000000000u64;
res.w[0] = 0;
res
}