use super::{Double, DoubleBuf, Float, FloatBuf};
use std::borrow::{Borrow, ToOwned};
use std::cmp::Ordering;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::ops::Deref;
use std::str::FromStr;
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
pub enum Sign {
Negative,
Zero,
Positive,
}
impl Sign {
pub fn is_positive(&self) -> bool {
matches!(self, Self::Positive)
}
pub fn is_negative(&self) -> bool {
matches!(self, Self::Negative)
}
pub fn is_zero(&self) -> bool {
matches!(self, Self::Zero)
}
}
pub struct Overflow;
mod integer;
pub use integer::*;
#[derive(Debug)]
pub struct InvalidDecimal;
pub struct Decimal([u8]);
impl Decimal {
#[inline(always)]
pub fn new<S: ?Sized + AsRef<[u8]>>(s: &S) -> Result<&Self, InvalidDecimal> {
if check(s.as_ref().iter().cloned()) {
Ok(unsafe { Self::new_unchecked(s) })
} else {
Err(InvalidDecimal)
}
}
#[inline(always)]
pub unsafe fn new_unchecked<S: ?Sized + AsRef<[u8]>>(s: &S) -> &Self {
std::mem::transmute(s.as_ref())
}
#[inline(always)]
pub fn as_str(&self) -> &str {
unsafe { core::str::from_utf8_unchecked(&self.0) }
}
#[inline(always)]
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
#[inline(always)]
pub fn as_float(&self) -> &Float {
self.into()
}
#[inline(always)]
pub fn as_double(&self) -> &Double {
self.into()
}
pub fn is_positive(&self) -> bool {
let mut sign_positive = true;
for c in &self.0 {
match c {
b'+' | b'0' | b'.' => (),
b'-' => sign_positive = false,
_ => return sign_positive,
}
}
false
}
pub fn is_negative(&self) -> bool {
let mut sign_negative = true;
for c in &self.0 {
match c {
b'-' | b'0' | b'.' => (),
b'+' => sign_negative = false,
_ => return sign_negative,
}
}
false
}
pub fn is_zero(&self) -> bool {
for c in &self.0 {
if !matches!(c, b'+' | b'-' | b'0' | b'.') {
return false;
}
}
true
}
pub fn sign(&self) -> Sign {
let mut sign_positive = true;
for c in &self.0 {
match c {
b'+' | b'0' | b'.' => (),
b'-' => sign_positive = false,
_ => {
if sign_positive {
return Sign::Positive;
} else {
return Sign::Negative;
}
}
}
}
Sign::Zero
}
#[inline(always)]
pub fn integer_part(&self) -> &Integer {
match self.split_once('.') {
Some((integer_part, _)) => unsafe { Integer::new_unchecked(integer_part) },
None => unsafe { Integer::new_unchecked(self) },
}
}
#[inline(always)]
pub fn fractional_part(&self) -> Option<&FractionalPart> {
self.split_once('.')
.map(|(_, fractional_part)| unsafe { FractionalPart::new_unchecked(fractional_part) })
}
#[inline(always)]
pub fn trimmed_fractional_part(&self) -> Option<&FractionalPart> {
self.split_once('.').and_then(|(_, fractional_part)| {
let f = unsafe { FractionalPart::new_unchecked(fractional_part) }.trimmed();
if f.is_empty() {
None
} else {
Some(f)
}
})
}
#[inline(always)]
pub fn parts(&self) -> (&Integer, Option<&FractionalPart>) {
match self.split_once('.') {
Some((i, f)) => unsafe {
(
Integer::new_unchecked(i),
Some(FractionalPart::new_unchecked(f)),
)
},
None => unsafe { (Integer::new_unchecked(self), None) },
}
}
}
impl PartialEq for Decimal {
fn eq(&self, other: &Self) -> bool {
self.integer_part() == other.integer_part()
&& self.fractional_part() == other.fractional_part()
}
}
impl Eq for Decimal {}
impl Hash for Decimal {
fn hash<H: Hasher>(&self, h: &mut H) {
self.integer_part().hash(h);
match self.fractional_part() {
Some(f) => f.hash(h),
None => FractionalPart::empty().hash(h),
}
}
}
impl Ord for Decimal {
fn cmp(&self, other: &Self) -> Ordering {
let sign = self.sign();
match sign.cmp(&other.sign()) {
Ordering::Equal => {
let (integer_part, fractional_part) = self.parts();
let (other_integer_part, other_fractional_part) = other.parts();
match integer_part.cmp(other_integer_part) {
Ordering::Equal => {
let fractional_part = fractional_part.unwrap_or_else(FractionalPart::empty);
let other_fractional_part =
other_fractional_part.unwrap_or_else(FractionalPart::empty);
if sign.is_negative() {
fractional_part.cmp(other_fractional_part).reverse()
} else {
fractional_part.cmp(other_fractional_part)
}
}
other => {
if sign.is_negative() {
other.reverse()
} else {
other
}
}
}
}
other => other,
}
}
}
impl PartialOrd for Decimal {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl AsRef<[u8]> for Decimal {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl AsRef<str> for Decimal {
fn as_ref(&self) -> &str {
self.as_str()
}
}
macro_rules! integer_conversion {
{ $($ty:ty),* } => {
$(
impl From<$ty> for DecimalBuf {
fn from(i: $ty) -> Self {
unsafe { DecimalBuf::new_unchecked(i.to_string()) }
}
}
impl<'a> TryFrom<&'a Decimal> for $ty {
type Error = Overflow;
fn try_from(i: &'a Decimal) -> Result<Self, Overflow> {
i.as_str().parse().map_err(|_| Overflow)
}
}
impl TryFrom<DecimalBuf> for $ty {
type Error = Overflow;
fn try_from(i: DecimalBuf) -> Result<Self, Overflow> {
i.as_str().parse().map_err(|_| Overflow)
}
}
)*
};
}
integer_conversion! {
u8,
i8,
u16,
i16,
u32,
i32,
u64,
i64,
usize,
isize
}
const DTOA_CONFIG: pretty_dtoa::FmtFloatConfig =
pretty_dtoa::FmtFloatConfig::default().force_no_e_notation();
impl From<f32> for DecimalBuf {
fn from(i: f32) -> Self {
unsafe { DecimalBuf::new_unchecked(pretty_dtoa::ftoa(i, DTOA_CONFIG)) }
}
}
impl<'a> TryFrom<&'a Decimal> for f32 {
type Error = <f32 as std::str::FromStr>::Err;
fn try_from(i: &'a Decimal) -> Result<Self, Self::Error> {
i.as_str().parse()
}
}
impl TryFrom<DecimalBuf> for f32 {
type Error = <f32 as std::str::FromStr>::Err;
fn try_from(i: DecimalBuf) -> Result<Self, Self::Error> {
i.as_str().parse()
}
}
impl From<f64> for DecimalBuf {
fn from(i: f64) -> Self {
unsafe { DecimalBuf::new_unchecked(pretty_dtoa::dtoa(i, DTOA_CONFIG)) }
}
}
impl<'a> TryFrom<&'a Decimal> for f64 {
type Error = <f64 as std::str::FromStr>::Err;
fn try_from(i: &'a Decimal) -> Result<Self, Self::Error> {
i.as_str().parse()
}
}
impl TryFrom<DecimalBuf> for f64 {
type Error = <f64 as std::str::FromStr>::Err;
fn try_from(i: DecimalBuf) -> Result<Self, Self::Error> {
i.as_str().parse()
}
}
impl Deref for Decimal {
type Target = str;
#[inline(always)]
fn deref(&self) -> &str {
self.as_str()
}
}
impl ToOwned for Decimal {
type Owned = DecimalBuf;
#[inline(always)]
fn to_owned(&self) -> DecimalBuf {
unsafe { DecimalBuf::new_unchecked(self.as_str().to_owned()) }
}
}
impl fmt::Display for Decimal {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.as_str().fmt(f)
}
}
impl fmt::Debug for Decimal {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
impl AsRef<Float> for Decimal {
#[inline(always)]
fn as_ref(&self) -> &Float {
self.as_float()
}
}
impl AsRef<Double> for Decimal {
#[inline(always)]
fn as_ref(&self) -> &Double {
self.as_double()
}
}
impl<'a> From<&'a Integer> for &'a Decimal {
#[inline(always)]
fn from(d: &'a Integer) -> Self {
unsafe { Decimal::new_unchecked(d) }
}
}
impl<'a> From<&'a IntegerBuf> for &'a Decimal {
#[inline(always)]
fn from(d: &'a IntegerBuf) -> Self {
d.as_ref()
}
}
impl<'a> From<&'a NonNegativeInteger> for &'a Decimal {
#[inline(always)]
fn from(d: &'a NonNegativeInteger) -> Self {
unsafe { Decimal::new_unchecked(d) }
}
}
impl<'a> From<&'a NonNegativeIntegerBuf> for &'a Decimal {
#[inline(always)]
fn from(d: &'a NonNegativeIntegerBuf) -> Self {
d.as_ref()
}
}
impl<'a> From<&'a NonPositiveInteger> for &'a Decimal {
#[inline(always)]
fn from(d: &'a NonPositiveInteger) -> Self {
unsafe { Decimal::new_unchecked(d) }
}
}
impl<'a> From<&'a NonPositiveIntegerBuf> for &'a Decimal {
#[inline(always)]
fn from(d: &'a NonPositiveIntegerBuf) -> Self {
d.as_ref()
}
}
impl<'a> TryFrom<&'a Float> for &'a Decimal {
type Error = InvalidDecimal;
#[inline(always)]
fn try_from(i: &'a Float) -> Result<Self, Self::Error> {
Decimal::new(i.as_str())
}
}
impl<'a> TryFrom<&'a FloatBuf> for &'a Decimal {
type Error = InvalidDecimal;
#[inline(always)]
fn try_from(i: &'a FloatBuf) -> Result<Self, Self::Error> {
Decimal::new(i.as_str())
}
}
impl<'a> TryFrom<&'a Double> for &'a Decimal {
type Error = InvalidDecimal;
#[inline(always)]
fn try_from(i: &'a Double) -> Result<Self, Self::Error> {
Decimal::new(i.as_str())
}
}
impl<'a> TryFrom<&'a DoubleBuf> for &'a Decimal {
type Error = InvalidDecimal;
#[inline(always)]
fn try_from(i: &'a DoubleBuf) -> Result<Self, Self::Error> {
Decimal::new(i.as_str())
}
}
pub struct FractionalPart([u8]);
impl FractionalPart {
#[inline(always)]
pub unsafe fn new_unchecked<S: ?Sized + AsRef<[u8]>>(s: &S) -> &Self {
std::mem::transmute(s.as_ref())
}
#[inline(always)]
pub fn empty<'a>() -> &'a Self {
unsafe { Self::new_unchecked(b"") }
}
#[inline(always)]
pub fn as_str(&self) -> &str {
unsafe { core::str::from_utf8_unchecked(&self.0) }
}
#[inline(always)]
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn trimmed(&self) -> &FractionalPart {
let mut end = 0;
for (i, &c) in self.0.iter().enumerate() {
if c != b'0' {
end = i + 1
}
}
unsafe { Self::new_unchecked(&self.0[0..end]) }
}
}
impl PartialEq for FractionalPart {
fn eq(&self, other: &Self) -> bool {
self.trimmed().0 == other.trimmed().0
}
}
impl Eq for FractionalPart {}
impl Hash for FractionalPart {
fn hash<H: Hasher>(&self, h: &mut H) {
self.trimmed().0.hash(h)
}
}
impl Ord for FractionalPart {
fn cmp(&self, other: &Self) -> Ordering {
self.trimmed().0.cmp(&other.trimmed().0)
}
}
impl PartialOrd for FractionalPart {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl AsRef<[u8]> for FractionalPart {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl AsRef<str> for FractionalPart {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct DecimalBuf(Vec<u8>);
impl DecimalBuf {
#[inline(always)]
pub fn new<S: AsRef<[u8]> + Into<Vec<u8>>>(s: S) -> Result<Self, (InvalidDecimal, S)> {
if check(s.as_ref().iter().cloned()) {
Ok(unsafe { Self::new_unchecked(s) })
} else {
Err((InvalidDecimal, s))
}
}
#[inline(always)]
pub unsafe fn new_unchecked(s: impl Into<Vec<u8>>) -> Self {
std::mem::transmute(s.into())
}
#[inline(always)]
pub fn as_decimal(&self) -> &Decimal {
unsafe { Decimal::new_unchecked(&self.0) }
}
#[inline(always)]
pub fn into_string(mut self) -> String {
let buf = self.0.as_mut_ptr();
let len = self.0.len();
let capacity = self.0.capacity();
core::mem::forget(self);
unsafe { String::from_raw_parts(buf, len, capacity) }
}
}
impl FromStr for DecimalBuf {
type Err = InvalidDecimal;
#[inline(always)]
fn from_str(s: &str) -> Result<Self, InvalidDecimal> {
Self::new(s.to_owned()).map_err(|(e, _)| e)
}
}
impl TryFrom<FloatBuf> for DecimalBuf {
type Error = (InvalidDecimal, FloatBuf);
#[inline(always)]
fn try_from(i: FloatBuf) -> Result<Self, Self::Error> {
match Self::new(i.into_string()) {
Ok(d) => Ok(d),
Err((e, s)) => Err((e, unsafe { FloatBuf::new_unchecked(s) })),
}
}
}
impl TryFrom<DoubleBuf> for DecimalBuf {
type Error = (InvalidDecimal, DoubleBuf);
#[inline(always)]
fn try_from(i: DoubleBuf) -> Result<Self, Self::Error> {
match Self::new(i.into_string()) {
Ok(d) => Ok(d),
Err((e, s)) => Err((e, unsafe { DoubleBuf::new_unchecked(s) })),
}
}
}
impl Deref for DecimalBuf {
type Target = Decimal;
#[inline(always)]
fn deref(&self) -> &Decimal {
unsafe { Decimal::new_unchecked(&self.0) }
}
}
impl AsRef<Decimal> for DecimalBuf {
#[inline(always)]
fn as_ref(&self) -> &Decimal {
self.as_decimal()
}
}
impl AsRef<Float> for DecimalBuf {
#[inline(always)]
fn as_ref(&self) -> &Float {
Decimal::as_ref(self)
}
}
impl AsRef<Double> for DecimalBuf {
#[inline(always)]
fn as_ref(&self) -> &Double {
Decimal::as_ref(self)
}
}
impl Borrow<Decimal> for DecimalBuf {
#[inline(always)]
fn borrow(&self) -> &Decimal {
unsafe { Decimal::new_unchecked(&self.0) }
}
}
impl<'a> From<&'a DecimalBuf> for &'a Decimal {
#[inline(always)]
fn from(b: &'a DecimalBuf) -> Self {
b.as_ref()
}
}
impl fmt::Display for DecimalBuf {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.as_str().fmt(f)
}
}
impl fmt::Debug for DecimalBuf {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.as_str().fmt(f)
}
}
fn check<C: Iterator<Item = u8>>(mut chars: C) -> bool {
enum State {
Initial,
NonEmptyInteger,
Integer,
NonEmptyDecimal,
Decimal,
}
let mut state = State::Initial;
loop {
state = match state {
State::Initial => match chars.next() {
Some(b'+') => State::NonEmptyInteger,
Some(b'-') => State::NonEmptyInteger,
Some(b'.') => State::NonEmptyDecimal,
Some(b'0'..=b'9') => State::Integer,
_ => break false,
},
State::NonEmptyInteger => match chars.next() {
Some(b'0'..=b'9') => State::Integer,
Some(b'.') => State::Decimal,
_ => break false,
},
State::Integer => match chars.next() {
Some(b'0'..=b'9') => State::Integer,
Some(b'.') => State::Decimal,
Some(_) => break false,
None => break true,
},
State::NonEmptyDecimal => match chars.next() {
Some(b'0'..=b'9') => State::Decimal,
_ => break false,
},
State::Decimal => match chars.next() {
Some(b'0'..=b'9') => State::Decimal,
Some(_) => break false,
None => break true,
},
}
}
}
macro_rules! partial_eq {
{ $($ty:ty),* } => {
$(
impl PartialEq<$ty> for Decimal {
#[inline(always)]
fn eq(&self, other: &$ty) -> bool {
self == other.as_decimal()
}
}
impl PartialEq<$ty> for DecimalBuf {
#[inline(always)]
fn eq(&self, other: &$ty) -> bool {
self.as_decimal() == other.as_decimal()
}
}
impl PartialEq<Decimal> for $ty {
#[inline(always)]
fn eq(&self, other: &Decimal) -> bool {
self.as_decimal() == other
}
}
impl PartialEq<DecimalBuf> for $ty {
#[inline(always)]
fn eq(&self, other: &DecimalBuf) -> bool {
self.as_decimal() == other.as_decimal()
}
}
)*
};
}
partial_eq! {
Integer
}
impl PartialEq<Decimal> for DecimalBuf {
#[inline(always)]
fn eq(&self, other: &Decimal) -> bool {
self.as_decimal() == other
}
}
impl<'a> PartialEq<&'a Decimal> for DecimalBuf {
#[inline(always)]
fn eq(&self, other: &&'a Decimal) -> bool {
self.as_decimal() == *other
}
}
impl PartialEq<DecimalBuf> for Decimal {
#[inline(always)]
fn eq(&self, other: &DecimalBuf) -> bool {
self == other.as_decimal()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_01() {
Decimal::new("0").unwrap();
}
#[test]
#[should_panic]
fn parse_02() {
Decimal::new("+").unwrap();
}
#[test]
#[should_panic]
fn parse_03() {
Decimal::new("-").unwrap();
}
#[test]
#[should_panic]
fn parse_04() {
Decimal::new("012+").unwrap();
}
#[test]
fn parse_05() {
Decimal::new("+42").unwrap();
}
#[test]
fn parse_06() {
Decimal::new("-42").unwrap();
}
#[test]
#[should_panic]
fn parse_07() {
Decimal::new(".").unwrap();
}
#[test]
fn parse_08() {
Decimal::new(".0").unwrap();
}
#[test]
fn parse_09() {
Decimal::new("0.").unwrap();
}
#[test]
fn parse_10() {
Decimal::new("42.0").unwrap();
}
#[test]
fn format_01() {
assert_eq!(DecimalBuf::from(1.0e10f32).to_string(), "10000000000.0")
}
#[test]
fn cmp_01() {
assert!(Decimal::new("0.123").unwrap() < Decimal::new("1.123").unwrap())
}
#[test]
fn cmp_02() {
assert!(Decimal::new("0.123").unwrap() < Decimal::new("0.1234").unwrap())
}
#[test]
fn cmp_03() {
assert!(Decimal::new("0.123").unwrap() > Decimal::new("-0.123").unwrap())
}
#[test]
fn cmp_04() {
assert!(Decimal::new("-0.123").unwrap() > Decimal::new("-0.1234").unwrap())
}
}