use std::cmp::Ordering;
use std::ffi::{CStr, CString};
use std::fmt;
use std::iter::{Product, Sum};
use std::marker::PhantomData;
use std::mem::MaybeUninit;
use std::ops::{
Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign,
};
use std::str::FromStr;
use libc::c_char;
use crate::context::{Class, Context};
#[cfg(feature = "arbitrary-precision")]
use crate::decimal::Decimal;
use crate::decimal128::Decimal128;
use crate::decimal32::Decimal32;
use crate::error::ParseDecimalError;
#[derive(Clone, Copy)]
pub struct Decimal64 {
pub(crate) inner: decnumber_sys::decDouble,
}
impl Decimal64 {
pub const NAN: Decimal64 = Decimal64::from_ne_bytes(if cfg!(target_endian = "little") {
[0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7c]
} else {
[0x7c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]
});
pub const ZERO: Decimal64 = Decimal64::from_ne_bytes(if cfg!(target_endian = "little") {
[0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x38, 0x22]
} else {
[0x22, 0x38, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]
});
pub const ONE: Decimal64 = Decimal64::from_ne_bytes(if cfg!(target_endian = "little") {
[0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x38, 0x22]
} else {
[0x22, 0x38, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1]
});
pub fn from_le_bytes(mut bytes: [u8; 8]) -> Decimal64 {
if cfg!(target_endian = "big") {
bytes.reverse();
}
Decimal64::from_ne_bytes(bytes)
}
pub fn from_be_bytes(mut bytes: [u8; 8]) -> Decimal64 {
if cfg!(target_endian = "little") {
bytes.reverse();
}
Decimal64::from_ne_bytes(bytes)
}
pub const fn from_ne_bytes(bytes: [u8; 8]) -> Decimal64 {
Decimal64 {
inner: decnumber_sys::decDouble { bytes },
}
}
pub fn to_le_bytes(&self) -> [u8; 8] {
let mut bytes = self.to_ne_bytes();
if cfg!(target_endian = "big") {
bytes.reverse();
}
bytes
}
pub fn to_be_bytes(&self) -> [u8; 8] {
let mut bytes = self.to_ne_bytes();
if cfg!(target_endian = "little") {
bytes.reverse();
}
bytes
}
pub fn to_ne_bytes(&self) -> [u8; 8] {
self.inner.bytes
}
pub fn class(&self) -> Class {
Class::from_c(unsafe { decnumber_sys::decDoubleClass(&self.inner) })
}
pub fn digits(&self) -> u32 {
unsafe { decnumber_sys::decDoubleDigits(&self.inner) }
}
pub fn exponent(&self) -> i32 {
unsafe { decnumber_sys::decDoubleGetExponent(&self.inner) }
}
pub fn canonical(mut self) -> Decimal64 {
unsafe {
decnumber_sys::decDoubleCanonical(&mut self.inner, &self.inner);
}
self
}
pub fn is_canonical(&self) -> bool {
unsafe { decnumber_sys::decDoubleIsCanonical(&self.inner) != 0 }
}
pub fn is_finite(&self) -> bool {
unsafe { decnumber_sys::decDoubleIsFinite(&self.inner) != 0 }
}
pub fn is_infinite(&self) -> bool {
unsafe { decnumber_sys::decDoubleIsInfinite(&self.inner) != 0 }
}
pub fn is_integer(&self) -> bool {
unsafe { decnumber_sys::decDoubleIsInteger(&self.inner) != 0 }
}
pub fn is_logical(&self) -> bool {
unsafe { decnumber_sys::decDoubleIsInteger(&self.inner) != 0 }
}
pub fn is_nan(&self) -> bool {
unsafe { decnumber_sys::decDoubleIsNaN(&self.inner) != 0 }
}
pub fn is_negative(&self) -> bool {
unsafe { decnumber_sys::decDoubleIsNegative(&self.inner) != 0 }
}
pub fn is_normal(&self) -> bool {
unsafe { decnumber_sys::decDoubleIsNormal(&self.inner) != 0 }
}
pub fn is_positive(&self) -> bool {
unsafe { decnumber_sys::decDoubleIsPositive(&self.inner) != 0 }
}
pub fn is_signaling_nan(&self) -> bool {
unsafe { decnumber_sys::decDoubleIsSignaling(&self.inner) != 0 }
}
pub fn is_signed(&self) -> bool {
unsafe { decnumber_sys::decDoubleIsSigned(&self.inner) != 0 }
}
pub fn is_subnormal(&self) -> bool {
unsafe { decnumber_sys::decDoubleIsSubnormal(&self.inner) != 0 }
}
pub fn is_zero(&self) -> bool {
unsafe { decnumber_sys::decDoubleIsZero(&self.inner) != 0 }
}
pub fn quantum_matches(&self, rhs: &Decimal64) -> bool {
unsafe { decnumber_sys::decDoubleSameQuantum(&self.inner, &rhs.inner) != 0 }
}
pub fn total_cmp(&self, rhs: &Decimal64) -> Ordering {
let mut d = MaybeUninit::<decnumber_sys::decDouble>::uninit();
let d = Decimal64 {
inner: unsafe {
decnumber_sys::decDoubleCompareTotal(d.as_mut_ptr(), &self.inner, &rhs.inner);
d.assume_init()
},
};
if d.is_positive() {
Ordering::Greater
} else if d.is_negative() {
Ordering::Less
} else {
debug_assert!(d.is_zero());
Ordering::Equal
}
}
}
impl Default for Decimal64 {
fn default() -> Decimal64 {
Decimal64::ZERO
}
}
impl fmt::Debug for Decimal64 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl fmt::Display for Decimal64 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut buf = MaybeUninit::<[c_char; decnumber_sys::DECDOUBLE_String]>::uninit();
let c_str = unsafe {
if f.alternate() {
decnumber_sys::decDoubleToEngString(&self.inner, buf.as_mut_ptr() as *mut c_char);
} else {
decnumber_sys::decDoubleToString(&self.inner, buf.as_mut_ptr() as *mut c_char);
}
CStr::from_ptr(buf.as_ptr() as *const c_char)
};
f.write_str(
c_str
.to_str()
.expect("decDoubleToString yields valid UTF-8"),
)
}
}
impl FromStr for Decimal64 {
type Err = ParseDecimalError;
fn from_str(s: &str) -> Result<Decimal64, ParseDecimalError> {
Context::<Decimal64>::default().parse(s)
}
}
impl From<i32> for Decimal64 {
fn from(n: i32) -> Decimal64 {
let mut d = MaybeUninit::<decnumber_sys::decDouble>::uninit();
let d = unsafe {
decnumber_sys::decDoubleFromInt32(d.as_mut_ptr(), n);
d.assume_init()
};
Decimal64 { inner: d }
}
}
impl From<u32> for Decimal64 {
fn from(n: u32) -> Decimal64 {
let mut d = MaybeUninit::<decnumber_sys::decDouble>::uninit();
let d = unsafe {
decnumber_sys::decDoubleFromUInt32(d.as_mut_ptr(), n);
d.assume_init()
};
Decimal64 { inner: d }
}
}
impl From<Decimal32> for Decimal64 {
fn from(d32: Decimal32) -> Decimal64 {
let mut d64 = MaybeUninit::<decnumber_sys::decDouble>::uninit();
let d64 = unsafe {
decnumber_sys::decSingleToWider(&d32.inner, d64.as_mut_ptr());
d64.assume_init()
};
Decimal64 { inner: d64 }
}
}
impl PartialOrd for Decimal64 {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Context::<Decimal64>::default().partial_cmp(*self, *other)
}
}
impl PartialEq for Decimal64 {
fn eq(&self, other: &Self) -> bool {
self.partial_cmp(other) == Some(Ordering::Equal)
}
}
impl Neg for Decimal64 {
type Output = Decimal64;
fn neg(self) -> Decimal64 {
Context::<Decimal64>::default().minus(self)
}
}
impl Add<Decimal64> for Decimal64 {
type Output = Decimal64;
fn add(self, rhs: Decimal64) -> Decimal64 {
Context::<Decimal64>::default().add(self, rhs)
}
}
impl AddAssign<Decimal64> for Decimal64 {
fn add_assign(&mut self, rhs: Decimal64) {
*self = Context::<Decimal64>::default().add(*self, rhs);
}
}
impl Div<Decimal64> for Decimal64 {
type Output = Decimal64;
fn div(self, rhs: Decimal64) -> Decimal64 {
Context::<Decimal64>::default().div(self, rhs)
}
}
impl DivAssign<Decimal64> for Decimal64 {
fn div_assign(&mut self, rhs: Decimal64) {
*self = Context::<Decimal64>::default().div(*self, rhs);
}
}
impl Mul<Decimal64> for Decimal64 {
type Output = Decimal64;
fn mul(self, rhs: Decimal64) -> Decimal64 {
Context::<Decimal64>::default().mul(self, rhs)
}
}
impl MulAssign<Decimal64> for Decimal64 {
fn mul_assign(&mut self, rhs: Decimal64) {
*self = Context::<Decimal64>::default().mul(*self, rhs);
}
}
impl Rem<Decimal64> for Decimal64 {
type Output = Decimal64;
fn rem(self, rhs: Decimal64) -> Decimal64 {
Context::<Decimal64>::default().rem(self, rhs)
}
}
impl RemAssign<Decimal64> for Decimal64 {
fn rem_assign(&mut self, rhs: Decimal64) {
*self = Context::<Decimal64>::default().rem(*self, rhs);
}
}
impl Sub<Decimal64> for Decimal64 {
type Output = Decimal64;
fn sub(self, rhs: Decimal64) -> Decimal64 {
Context::<Decimal64>::default().sub(self, rhs)
}
}
impl SubAssign<Decimal64> for Decimal64 {
fn sub_assign(&mut self, rhs: Decimal64) {
*self = Context::<Decimal64>::default().sub(*self, rhs);
}
}
impl Sum for Decimal64 {
fn sum<I>(iter: I) -> Self
where
I: Iterator<Item = Decimal64>,
{
let mut cx = Context::<Decimal64>::default();
let mut sum = Decimal64::ZERO;
for d in iter {
sum = cx.add(sum, d);
}
sum
}
}
impl<'a> Sum<&'a Decimal64> for Decimal64 {
fn sum<I>(iter: I) -> Self
where
I: Iterator<Item = &'a Decimal64>,
{
iter.copied().sum()
}
}
impl Product for Decimal64 {
fn product<I>(iter: I) -> Self
where
I: Iterator<Item = Decimal64>,
{
let mut cx = Context::<Decimal64>::default();
let mut product = Decimal64::ONE;
for d in iter {
product = cx.mul(product, d);
}
product
}
}
impl<'a> Product<&'a Decimal64> for Decimal64 {
fn product<I>(iter: I) -> Self
where
I: Iterator<Item = &'a Decimal64>,
{
iter.copied().product()
}
}
impl Default for Context<Decimal64> {
fn default() -> Context<Decimal64> {
let mut ctx = MaybeUninit::<decnumber_sys::decContext>::uninit();
let ctx = unsafe {
decnumber_sys::decContextDefault(ctx.as_mut_ptr(), decnumber_sys::DEC_INIT_DECDOUBLE);
ctx.assume_init()
};
Context {
inner: ctx,
_phantom: PhantomData,
}
}
}
impl Context<Decimal64> {
pub fn parse<S>(&mut self, s: S) -> Result<Decimal64, ParseDecimalError>
where
S: Into<Vec<u8>>,
{
let c_string = CString::new(s).map_err(|_| ParseDecimalError)?;
let mut d = MaybeUninit::<decnumber_sys::decDouble>::uninit();
let d = unsafe {
decnumber_sys::decDoubleFromString(d.as_mut_ptr(), c_string.as_ptr(), &mut self.inner);
d.assume_init()
};
if (self.inner.status & decnumber_sys::DEC_Conversion_syntax) != 0 {
Err(ParseDecimalError)
} else {
Ok(Decimal64 { inner: d })
}
}
pub fn from_decimal128(&mut self, d128: Decimal128) -> Decimal64 {
let mut d64 = MaybeUninit::<decnumber_sys::decDouble>::uninit();
let d64 = unsafe {
decnumber_sys::decDoubleFromWider(d64.as_mut_ptr(), &d128.inner, &mut self.inner);
d64.assume_init()
};
Decimal64 { inner: d64 }
}
#[cfg(feature = "arbitrary-precision")]
pub fn from_decimal<const N: usize>(&mut self, d: &Decimal<N>) -> Decimal64 {
let mut d64 = MaybeUninit::<decnumber_sys::decDouble>::uninit();
let d64 = unsafe {
decnumber_sys::decimal64FromNumber(d64.as_mut_ptr(), d.as_ptr(), &mut self.inner);
d64.assume_init()
};
Decimal64 { inner: d64 }
}
pub fn abs(&mut self, mut n: Decimal64) -> Decimal64 {
unsafe {
decnumber_sys::decDoubleAbs(&mut n.inner, &n.inner, &mut self.inner);
}
n
}
pub fn add(&mut self, mut lhs: Decimal64, rhs: Decimal64) -> Decimal64 {
unsafe {
decnumber_sys::decDoubleAdd(&mut lhs.inner, &lhs.inner, &rhs.inner, &mut self.inner);
}
lhs
}
pub fn and(&mut self, mut lhs: Decimal64, rhs: Decimal64) -> Decimal64 {
unsafe {
decnumber_sys::decDoubleAnd(&mut lhs.inner, &lhs.inner, &rhs.inner, &mut self.inner);
}
lhs
}
pub fn div(&mut self, mut lhs: Decimal64, rhs: Decimal64) -> Decimal64 {
unsafe {
decnumber_sys::decDoubleDivide(&mut lhs.inner, &lhs.inner, &rhs.inner, &mut self.inner);
}
lhs
}
pub fn div_integer(&mut self, mut lhs: Decimal64, rhs: Decimal64) -> Decimal64 {
unsafe {
decnumber_sys::decDoubleDivideInteger(
&mut lhs.inner,
&lhs.inner,
&rhs.inner,
&mut self.inner,
);
}
lhs
}
pub fn fma(&mut self, mut x: Decimal64, y: Decimal64, z: Decimal64) -> Decimal64 {
unsafe {
decnumber_sys::decDoubleFMA(
&mut x.inner,
&x.inner,
&y.inner,
&z.inner,
&mut self.inner,
);
}
x
}
pub fn invert(&mut self, mut n: Decimal64) -> Decimal64 {
unsafe {
decnumber_sys::decDoubleInvert(&mut n.inner, &n.inner, &mut self.inner);
}
n
}
pub fn logb(&mut self, mut n: Decimal64) -> Decimal64 {
unsafe {
decnumber_sys::decDoubleLogB(&mut n.inner, &n.inner, &mut self.inner);
}
n
}
pub fn max(&mut self, mut lhs: Decimal64, rhs: Decimal64) -> Decimal64 {
unsafe {
decnumber_sys::decDoubleMax(&mut lhs.inner, &lhs.inner, &rhs.inner, &mut self.inner);
}
lhs
}
pub fn max_abs(&mut self, mut lhs: Decimal64, rhs: Decimal64) -> Decimal64 {
unsafe {
decnumber_sys::decDoubleMaxMag(&mut lhs.inner, &lhs.inner, &rhs.inner, &mut self.inner);
}
lhs
}
pub fn min(&mut self, mut lhs: Decimal64, rhs: Decimal64) -> Decimal64 {
unsafe {
decnumber_sys::decDoubleMin(&mut lhs.inner, &lhs.inner, &rhs.inner, &mut self.inner);
}
lhs
}
pub fn min_abs(&mut self, mut lhs: Decimal64, rhs: Decimal64) -> Decimal64 {
unsafe {
decnumber_sys::decDoubleMinMag(&mut lhs.inner, &lhs.inner, &rhs.inner, &mut self.inner);
}
lhs
}
pub fn minus(&mut self, mut n: Decimal64) -> Decimal64 {
unsafe {
decnumber_sys::decDoubleMinus(&mut n.inner, &n.inner, &mut self.inner);
}
n
}
pub fn mul(&mut self, mut lhs: Decimal64, rhs: Decimal64) -> Decimal64 {
unsafe {
decnumber_sys::decDoubleMultiply(
&mut lhs.inner,
&lhs.inner,
&rhs.inner,
&mut self.inner,
);
}
lhs
}
pub fn next_minus(&mut self, mut n: Decimal64) -> Decimal64 {
unsafe {
decnumber_sys::decDoubleNextMinus(&mut n.inner, &n.inner, &mut self.inner);
}
n
}
pub fn next_plus(&mut self, mut n: Decimal64) -> Decimal64 {
unsafe {
decnumber_sys::decDoubleNextPlus(&mut n.inner, &n.inner, &mut self.inner);
}
n
}
pub fn next_toward(&mut self, mut x: Decimal64, y: Decimal64) -> Decimal64 {
unsafe {
decnumber_sys::decDoubleNextToward(&mut x.inner, &x.inner, &y.inner, &mut self.inner);
}
x
}
pub fn partial_cmp(&mut self, lhs: Decimal64, rhs: Decimal64) -> Option<Ordering> {
let mut d = MaybeUninit::<decnumber_sys::decDouble>::uninit();
let d = Decimal64 {
inner: unsafe {
decnumber_sys::decDoubleCompare(
d.as_mut_ptr(),
&lhs.inner,
&rhs.inner,
&mut self.inner,
);
d.assume_init()
},
};
if d.is_positive() {
Some(Ordering::Greater)
} else if d.is_negative() {
Some(Ordering::Less)
} else if d.is_zero() {
Some(Ordering::Equal)
} else {
debug_assert!(d.is_nan());
None
}
}
pub fn plus(&mut self, mut n: Decimal64) -> Decimal64 {
unsafe {
decnumber_sys::decDoublePlus(&mut n.inner, &n.inner, &mut self.inner);
}
n
}
pub fn quantize(&mut self, mut lhs: Decimal64, rhs: Decimal64) -> Decimal64 {
unsafe {
decnumber_sys::decDoubleQuantize(
&mut lhs.inner,
&lhs.inner,
&rhs.inner,
&mut self.inner,
);
}
lhs
}
pub fn reduce(&mut self, mut n: Decimal64) -> Decimal64 {
unsafe {
decnumber_sys::decDoubleReduce(&mut n.inner, &n.inner, &mut self.inner);
}
n
}
pub fn rem(&mut self, mut lhs: Decimal64, rhs: Decimal64) -> Decimal64 {
unsafe {
decnumber_sys::decDoubleRemainder(
&mut lhs.inner,
&lhs.inner,
&rhs.inner,
&mut self.inner,
);
}
lhs
}
pub fn rem_near(&mut self, mut lhs: Decimal64, rhs: Decimal64) -> Decimal64 {
unsafe {
decnumber_sys::decDoubleRemainderNear(
&mut lhs.inner,
&lhs.inner,
&rhs.inner,
&mut self.inner,
);
}
lhs
}
pub fn rotate(&mut self, mut lhs: Decimal64, rhs: Decimal64) -> Decimal64 {
unsafe {
decnumber_sys::decDoubleRotate(&mut lhs.inner, &lhs.inner, &rhs.inner, &mut self.inner);
}
lhs
}
pub fn round(&mut self, mut n: Decimal64) -> Decimal64 {
unsafe {
decnumber_sys::decDoubleToIntegralExact(&mut n.inner, &n.inner, &mut self.inner);
}
n
}
pub fn scaleb(&mut self, mut x: Decimal64, y: Decimal64) -> Decimal64 {
unsafe {
decnumber_sys::decDoubleScaleB(&mut x.inner, &x.inner, &y.inner, &mut self.inner);
}
x
}
pub fn shift(&mut self, mut lhs: Decimal64, rhs: Decimal64) -> Decimal64 {
unsafe {
decnumber_sys::decDoubleShift(&mut lhs.inner, &lhs.inner, &rhs.inner, &mut self.inner);
}
lhs
}
pub fn sub(&mut self, mut lhs: Decimal64, rhs: Decimal64) -> Decimal64 {
unsafe {
decnumber_sys::decDoubleSubtract(
&mut lhs.inner,
&lhs.inner,
&rhs.inner,
&mut self.inner,
);
}
lhs
}
pub fn or(&mut self, mut lhs: Decimal64, rhs: Decimal64) -> Decimal64 {
unsafe {
decnumber_sys::decDoubleOr(&mut lhs.inner, &lhs.inner, &rhs.inner, &mut self.inner);
}
lhs
}
pub fn xor(&mut self, mut lhs: Decimal64, rhs: Decimal64) -> Decimal64 {
unsafe {
decnumber_sys::decDoubleXor(&mut lhs.inner, &lhs.inner, &rhs.inner, &mut self.inner);
}
lhs
}
}