use std::cmp::Ordering;
use std::convert::TryFrom;
use std::ffi::{CStr, CString};
use std::fmt;
use std::marker::PhantomData;
use std::mem::MaybeUninit;
use std::str::FromStr;
use libc::c_char;
use crate::context::{Class, Context};
use crate::decimal128::Decimal128;
use crate::decimal32::Decimal32;
use crate::decimal64::Decimal64;
use crate::error::{InvalidExponentError, InvalidPrecisionError, ParseDecimalError};
fn validate_n(n: usize) {
if n < 12 || n > 999_999_999 {
panic!("Decimal<N>:: N is not in the range [12, 999999999]");
}
}
#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary-precision")))]
#[repr(C)]
#[derive(Clone)]
pub struct Decimal<const N: usize> {
digits: u32,
exponent: i32,
bits: u8,
lsu: [u16; N],
}
impl<const N: usize> Decimal<N> {
pub(crate) fn as_ptr(&self) -> *const decnumber_sys::decNumber {
self as *const Decimal<N> as *const decnumber_sys::decNumber
}
pub(crate) fn as_mut_ptr(&mut self) -> *mut decnumber_sys::decNumber {
self as *mut Decimal<N> as *mut decnumber_sys::decNumber
}
pub fn zero() -> Decimal<N> {
Decimal::default()
}
pub fn digits(&self) -> u32 {
self.digits
}
pub fn exponent(&self) -> i32 {
self.exponent
}
pub fn is_finite(&self) -> bool {
(self.bits & decnumber_sys::DECSPECIAL) == 0
}
pub fn is_infinite(&self) -> bool {
(self.bits & decnumber_sys::DECINF) != 0
}
pub fn is_nan(&self) -> bool {
(self.bits & (decnumber_sys::DECNAN | decnumber_sys::DECSNAN)) != 0
}
pub fn is_negative(&self) -> bool {
(self.bits & decnumber_sys::DECNEG) != 0
}
pub fn is_quiet_nan(&self) -> bool {
(self.bits & decnumber_sys::DECNAN) != 0
}
pub fn is_signaling_nan(&self) -> bool {
(self.bits & decnumber_sys::DECSNAN) != 0
}
pub fn is_special(&self) -> bool {
(self.bits & decnumber_sys::DECSPECIAL) != 0
}
pub fn is_zero(&self) -> bool {
self.is_finite() && self.lsu[0] == 0 && self.digits == 1
}
pub fn quantum_matches(&self, rhs: &Decimal<N>) -> bool {
let mut d = MaybeUninit::<Decimal<N>>::uninit();
let d = unsafe {
decnumber_sys::decNumberSameQuantum(
d.as_mut_ptr() as *mut decnumber_sys::decNumber,
self.as_ptr(),
rhs.as_ptr(),
);
d.assume_init()
};
if d.is_zero() {
false
} else {
debug_assert!(!d.is_special());
true
}
}
pub fn to_decimal32(&self) -> Decimal32 {
Context::<Decimal32>::default().from_decimal(self)
}
pub fn to_decimal64(&self) -> Decimal64 {
Context::<Decimal64>::default().from_decimal(self)
}
pub fn to_decimal128(&self) -> Decimal128 {
Context::<Decimal128>::default().from_decimal(self)
}
pub fn to_raw_parts(&self) -> (u32, i32, u8, [u16; N]) {
(self.digits, self.exponent, self.bits, self.lsu)
}
}
impl<const N: usize> Default for Decimal<N> {
fn default() -> Decimal<N> {
validate_n(N);
let mut d = MaybeUninit::<Decimal<N>>::uninit();
unsafe {
decnumber_sys::decNumberZero(d.as_mut_ptr() as *mut decnumber_sys::decNumber);
d.assume_init()
}
}
}
impl<const N: usize> fmt::Debug for Decimal<N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl<const N: usize> fmt::Display for Decimal<N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut buf = Vec::with_capacity(self.digits as usize + 14);
let c_str = unsafe {
if f.alternate() {
decnumber_sys::decNumberToEngString(self.as_ptr(), buf.as_mut_ptr() as *mut c_char);
} else {
decnumber_sys::decNumberToString(self.as_ptr(), 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("decNumberToString yields valid UTF-8"),
)
}
}
impl<const N: usize> FromStr for Decimal<N> {
type Err = ParseDecimalError;
fn from_str(s: &str) -> Result<Decimal<N>, ParseDecimalError> {
Context::<Decimal<N>>::default().parse(s)
}
}
impl<const N: usize> From<Decimal32> for Decimal<N> {
fn from(n: Decimal32) -> Decimal<N> {
validate_n(N);
let mut d = MaybeUninit::<Decimal<N>>::uninit();
unsafe {
decnumber_sys::decimal32ToNumber(
&n.inner,
d.as_mut_ptr() as *mut decnumber_sys::decNumber,
);
d.assume_init()
}
}
}
impl<const N: usize> From<Decimal64> for Decimal<N> {
fn from(n: Decimal64) -> Decimal<N> {
validate_n(N);
let mut d = MaybeUninit::<Decimal<N>>::uninit();
unsafe {
decnumber_sys::decimal64ToNumber(
&n.inner,
d.as_mut_ptr() as *mut decnumber_sys::decNumber,
);
d.assume_init()
}
}
}
impl<const N: usize> From<Decimal128> for Decimal<N> {
fn from(n: Decimal128) -> Decimal<N> {
validate_n(N);
let mut d = MaybeUninit::<Decimal<N>>::uninit();
unsafe {
decnumber_sys::decimal128ToNumber(
&n.inner,
d.as_mut_ptr() as *mut decnumber_sys::decNumber,
);
d.assume_init()
}
}
}
impl<const N: usize> Default for Context<Decimal<N>> {
fn default() -> Context<Decimal<N>> {
let mut ctx = MaybeUninit::<decnumber_sys::decContext>::uninit();
let mut ctx = unsafe {
decnumber_sys::decContextDefault(ctx.as_mut_ptr(), decnumber_sys::DEC_INIT_BASE);
ctx.assume_init()
};
ctx.traps = 0;
ctx.digits = i32::try_from(N * decnumber_sys::DECDPUN)
.expect("decimal digit count does not fit into i32");
Context {
inner: ctx,
_phantom: PhantomData,
}
}
}
impl<const N: usize> Context<Decimal<N>> {
pub fn precision(&self) -> usize {
usize::try_from(self.inner.digits).expect("context digit count does not fit into usize")
}
pub fn set_precision(&mut self, precision: usize) -> Result<(), InvalidPrecisionError> {
if precision < 1 || precision > N * decnumber_sys::DECDPUN {
return Err(InvalidPrecisionError);
}
self.inner.digits = i32::try_from(precision).map_err(|_| InvalidPrecisionError)?;
Ok(())
}
pub fn clamp(&self) -> bool {
self.inner.clamp != 0
}
pub fn set_clamp(&mut self, clamp: bool) {
self.inner.clamp = u8::from(clamp)
}
pub fn max_exponent(&self) -> isize {
isize::try_from(self.inner.emax).expect("context max exponent does not fit into isize")
}
pub fn set_max_exponent(&mut self, e: isize) -> Result<(), InvalidExponentError> {
if e < 0 || e > 999999999 {
return Err(InvalidExponentError);
}
self.inner.emax = i32::try_from(e).map_err(|_| InvalidExponentError)?;
Ok(())
}
pub fn min_exponent(&self) -> isize {
isize::try_from(self.inner.emin).expect("context min exponent does not fit into isize")
}
pub fn set_min_exponent(&mut self, e: isize) -> Result<(), InvalidExponentError> {
if e > 0 || e < -999999999 {
return Err(InvalidExponentError);
}
self.inner.emin = i32::try_from(e).map_err(|_| InvalidExponentError)?;
Ok(())
}
pub fn parse<S>(&mut self, s: S) -> Result<Decimal<N>, ParseDecimalError>
where
S: Into<Vec<u8>>,
{
validate_n(N);
let c_string = CString::new(s).map_err(|_| ParseDecimalError)?;
let mut d = MaybeUninit::<Decimal<N>>::uninit();
let d = unsafe {
decnumber_sys::decNumberFromString(
d.as_mut_ptr() as *mut decnumber_sys::decNumber,
c_string.as_ptr(),
&mut self.inner,
);
d.assume_init()
};
if (self.inner.status & decnumber_sys::DEC_Conversion_syntax) != 0 {
Err(ParseDecimalError)
} else {
Ok(d)
}
}
pub fn class(&mut self, n: &Decimal<N>) -> Class {
Class::from_c(unsafe { decnumber_sys::decNumberClass(n.as_ptr(), &mut self.inner) })
}
pub fn abs(&mut self, n: &mut Decimal<N>) {
unsafe {
decnumber_sys::decNumberAbs(n.as_mut_ptr(), n.as_ptr(), &mut self.inner);
}
}
pub fn add(&mut self, lhs: &mut Decimal<N>, rhs: &Decimal<N>) {
unsafe {
decnumber_sys::decNumberAdd(
lhs.as_mut_ptr(),
lhs.as_ptr(),
rhs.as_ptr(),
&mut self.inner,
);
}
}
pub fn and(&mut self, lhs: &mut Decimal<N>, rhs: &Decimal<N>) {
unsafe {
decnumber_sys::decNumberAnd(
lhs.as_mut_ptr(),
lhs.as_ptr(),
rhs.as_ptr(),
&mut self.inner,
);
}
}
pub fn div(&mut self, lhs: &mut Decimal<N>, rhs: &Decimal<N>) {
unsafe {
decnumber_sys::decNumberDivide(
lhs.as_mut_ptr(),
lhs.as_ptr(),
rhs.as_ptr(),
&mut self.inner,
);
}
}
pub fn div_integer(&mut self, lhs: &mut Decimal<N>, rhs: &Decimal<N>) {
unsafe {
decnumber_sys::decNumberDivideInteger(
lhs.as_mut_ptr(),
lhs.as_ptr(),
rhs.as_ptr(),
&mut self.inner,
);
}
}
pub fn exp(&mut self, n: &mut Decimal<N>) {
unsafe {
decnumber_sys::decNumberExp(n.as_mut_ptr(), n.as_ptr(), &mut self.inner);
}
}
pub fn fma(&mut self, x: &mut Decimal<N>, y: &Decimal<N>, z: &Decimal<N>) {
unsafe {
decnumber_sys::decNumberFMA(
x.as_mut_ptr(),
x.as_ptr(),
y.as_ptr(),
z.as_ptr(),
&mut self.inner,
);
}
}
pub fn invert(&mut self, n: &mut Decimal<N>) {
unsafe {
decnumber_sys::decNumberInvert(n.as_mut_ptr(), n.as_ptr(), &mut self.inner);
}
}
pub fn ln(&mut self, n: &mut Decimal<N>) {
unsafe {
decnumber_sys::decNumberLn(n.as_mut_ptr(), n.as_ptr(), &mut self.inner);
}
}
pub fn log10(&mut self, n: &mut Decimal<N>) {
unsafe {
decnumber_sys::decNumberLog10(n.as_mut_ptr(), n.as_ptr(), &mut self.inner);
}
}
pub fn logb(&mut self, n: &mut Decimal<N>) {
unsafe {
decnumber_sys::decNumberLogB(n.as_mut_ptr(), n.as_ptr(), &mut self.inner);
}
}
pub fn max(&mut self, lhs: &mut Decimal<N>, rhs: &Decimal<N>) {
unsafe {
decnumber_sys::decNumberMax(
lhs.as_mut_ptr(),
lhs.as_ptr(),
rhs.as_ptr(),
&mut self.inner,
);
}
}
pub fn max_abs(&mut self, lhs: &mut Decimal<N>, rhs: &Decimal<N>) {
unsafe {
decnumber_sys::decNumberMaxMag(
lhs.as_mut_ptr(),
lhs.as_ptr(),
rhs.as_ptr(),
&mut self.inner,
);
}
}
pub fn min(&mut self, lhs: &mut Decimal<N>, rhs: &Decimal<N>) {
unsafe {
decnumber_sys::decNumberMin(
lhs.as_mut_ptr(),
lhs.as_ptr(),
rhs.as_ptr(),
&mut self.inner,
);
}
}
pub fn min_abs(&mut self, lhs: &mut Decimal<N>, rhs: &Decimal<N>) {
unsafe {
decnumber_sys::decNumberMinMag(
lhs.as_mut_ptr(),
lhs.as_ptr(),
rhs.as_ptr(),
&mut self.inner,
);
}
}
pub fn minus(&mut self, n: &mut Decimal<N>) {
unsafe {
decnumber_sys::decNumberMinus(n.as_mut_ptr(), n.as_ptr(), &mut self.inner);
}
}
pub fn mul(&mut self, lhs: &mut Decimal<N>, rhs: &Decimal<N>) {
unsafe {
decnumber_sys::decNumberMultiply(
lhs.as_mut_ptr(),
lhs.as_ptr(),
rhs.as_ptr(),
&mut self.inner,
);
}
}
pub fn next_minus(&mut self, n: &mut Decimal<N>) {
unsafe {
decnumber_sys::decNumberNextMinus(n.as_mut_ptr(), n.as_ptr(), &mut self.inner);
}
}
pub fn next_plus(&mut self, n: &mut Decimal<N>) {
unsafe {
decnumber_sys::decNumberNextPlus(n.as_mut_ptr(), n.as_ptr(), &mut self.inner);
}
}
pub fn next_toward(&mut self, x: &mut Decimal<N>, y: &Decimal<N>) {
unsafe {
decnumber_sys::decNumberNextToward(
x.as_mut_ptr(),
x.as_ptr(),
y.as_ptr(),
&mut self.inner,
);
}
}
pub fn or(&mut self, lhs: &mut Decimal<N>, rhs: &Decimal<N>) {
unsafe {
decnumber_sys::decNumberOr(
lhs.as_mut_ptr(),
lhs.as_ptr(),
rhs.as_ptr(),
&mut self.inner,
);
}
}
pub fn partial_cmp(&mut self, lhs: &Decimal<N>, rhs: &Decimal<N>) -> Option<Ordering> {
validate_n(N);
let mut d = MaybeUninit::<Decimal<N>>::uninit();
let d = unsafe {
decnumber_sys::decNumberCompare(
d.as_mut_ptr() as *mut decnumber_sys::decNumber,
lhs.as_ptr(),
rhs.as_ptr(),
&mut self.inner,
);
d.assume_init()
};
if d.is_nan() {
None
} else if d.is_negative() {
Some(Ordering::Less)
} else if d.is_zero() {
Some(Ordering::Equal)
} else {
debug_assert!(!d.is_special());
Some(Ordering::Greater)
}
}
pub fn plus(&mut self, n: &mut Decimal<N>) {
unsafe {
decnumber_sys::decNumberPlus(n.as_mut_ptr(), n.as_ptr(), &mut self.inner);
}
}
pub fn pow(&mut self, x: &mut Decimal<N>, y: &Decimal<N>) {
unsafe {
decnumber_sys::decNumberPower(x.as_mut_ptr(), x.as_ptr(), y.as_ptr(), &mut self.inner);
}
}
pub fn quantize(&mut self, lhs: &mut Decimal<N>, rhs: &Decimal<N>) {
unsafe {
decnumber_sys::decNumberQuantize(
lhs.as_mut_ptr(),
lhs.as_ptr(),
rhs.as_ptr(),
&mut self.inner,
);
}
}
pub fn reduce(&mut self, n: &mut Decimal<N>) {
unsafe {
decnumber_sys::decNumberReduce(n.as_mut_ptr(), n.as_ptr(), &mut self.inner);
}
}
pub fn rem(&mut self, lhs: &mut Decimal<N>, rhs: &Decimal<N>) {
unsafe {
decnumber_sys::decNumberRemainder(
lhs.as_mut_ptr(),
lhs.as_ptr(),
rhs.as_ptr(),
&mut self.inner,
);
}
}
pub fn rem_near(&mut self, lhs: &mut Decimal<N>, rhs: &Decimal<N>) {
unsafe {
decnumber_sys::decNumberRemainderNear(
lhs.as_mut_ptr(),
lhs.as_ptr(),
rhs.as_ptr(),
&mut self.inner,
);
}
}
pub fn shift(&mut self, lhs: &mut Decimal<N>, rhs: &Decimal<N>) {
unsafe {
decnumber_sys::decNumberShift(
lhs.as_mut_ptr(),
lhs.as_ptr(),
rhs.as_ptr(),
&mut self.inner,
);
}
}
pub fn rotate(&mut self, lhs: &mut Decimal<N>, rhs: &Decimal<N>) {
unsafe {
decnumber_sys::decNumberRotate(
lhs.as_mut_ptr(),
lhs.as_ptr(),
rhs.as_ptr(),
&mut self.inner,
);
}
}
pub fn scaleb(&mut self, x: &mut Decimal<N>, y: &Decimal<N>) {
unsafe {
decnumber_sys::decNumberScaleB(x.as_mut_ptr(), x.as_ptr(), y.as_ptr(), &mut self.inner);
}
}
pub fn sqrt(&mut self, n: &mut Decimal<N>) {
unsafe {
decnumber_sys::decNumberSquareRoot(n.as_mut_ptr(), n.as_ptr(), &mut self.inner);
}
}
pub fn sub(&mut self, lhs: &mut Decimal<N>, rhs: &Decimal<N>) {
unsafe {
decnumber_sys::decNumberSubtract(
lhs.as_mut_ptr(),
lhs.as_ptr(),
rhs.as_ptr(),
&mut self.inner,
);
}
}
pub fn total_cmp(&mut self, lhs: &Decimal<N>, rhs: &Decimal<N>) -> Ordering {
validate_n(N);
let mut d = MaybeUninit::<Decimal<N>>::uninit();
let d = unsafe {
decnumber_sys::decNumberCompareTotal(
d.as_mut_ptr() as *mut decnumber_sys::decNumber,
lhs.as_ptr(),
rhs.as_ptr(),
&mut self.inner,
);
d.assume_init()
};
debug_assert!(!d.is_special());
if d.is_negative() {
Ordering::Less
} else if d.is_zero() {
Ordering::Equal
} else {
Ordering::Greater
}
}
pub fn xor(&mut self, lhs: &mut Decimal<N>, rhs: &Decimal<N>) {
unsafe {
decnumber_sys::decNumberXor(
lhs.as_mut_ptr(),
lhs.as_ptr(),
rhs.as_ptr(),
&mut self.inner,
);
}
}
}