xsd_types/value/decimal/
mod.rs1use std::fmt;
2use std::hash::Hash;
3use std::ops::Deref;
4use std::str::FromStr;
5use std::{borrow::Borrow, collections::HashSet};
6
7use lazy_static::lazy_static;
8use num_bigint::BigInt;
9use num_rational::BigRational;
10use num_traits::{Signed, ToPrimitive, Zero};
11use once_cell::unsync::OnceCell;
12
13use crate::lexical::LexicalFormOf;
14use crate::{
15 lexical, Datatype, DecimalDatatype, Double, Float, IntDatatype, LongDatatype,
16 NonNegativeIntegerDatatype, NonPositiveIntegerDatatype, ParseXsd, ShortDatatype,
17 UnsignedIntDatatype, UnsignedLongDatatype, UnsignedShortDatatype, XsdValue,
18};
19
20pub use num_bigint::Sign;
21
22mod integer;
23
24pub use integer::*;
25
26lazy_static! {
27 static ref I64_MIN: BigInt = i64::MIN.into();
28 static ref I64_MIN_RATIO: BigRational = I64_MIN.clone().into();
29 static ref I32_MIN: BigInt = i32::MIN.into();
30 static ref I32_MIN_RATIO: BigRational = I32_MIN.clone().into();
31 static ref I16_MIN: BigInt = i16::MIN.into();
32 static ref I16_MIN_RATIO: BigRational = I16_MIN.clone().into();
33 static ref I8_MIN: BigInt = i8::MIN.into();
34 static ref I8_MIN_RATIO: BigRational = I8_MIN.clone().into();
35 static ref U64_MAX: BigInt = u64::MAX.into();
36 static ref U64_MAX_RATIO: BigRational = U64_MAX.clone().into();
37 static ref U32_MAX: BigInt = u32::MAX.into();
38 static ref U32_MAX_RATIO: BigRational = U32_MAX.clone().into();
39 static ref U16_MAX: BigInt = u16::MAX.into();
40 static ref U16_MAX_RATIO: BigRational = U16_MAX.clone().into();
41 static ref U8_MAX: BigInt = u8::MAX.into();
42 static ref U8_MAX_RATIO: BigRational = U8_MAX.clone().into();
43 static ref TEN: BigInt = 10u32.into();
44}
45
46#[derive(Clone)]
51pub struct Decimal {
52 data: BigRational,
53 lexical: OnceCell<lexical::DecimalBuf>,
54}
55
56impl PartialEq for Decimal {
57 fn eq(&self, other: &Self) -> bool {
58 self.data.eq(&other.data)
59 }
60}
61
62impl Eq for Decimal {}
63
64impl PartialOrd for Decimal {
65 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
66 Some(self.data.cmp(other))
67 }
68}
69
70impl Ord for Decimal {
71 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
72 self.data.cmp(&other.data)
73 }
74}
75
76impl Hash for Decimal {
77 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
78 self.data.hash(state)
79 }
80}
81
82impl fmt::Debug for Decimal {
83 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84 write!(f, "Decimal({:?})", self.data)
85 }
86}
87
88#[derive(Default)]
94pub struct DecimalCheck {
95 set: HashSet<BigInt>,
96}
97
98impl DecimalCheck {
99 pub fn is_decimal(&mut self, r: &BigRational) -> bool {
100 self.set.clear();
101
102 let mut rem = if *r < BigRational::zero() {
103 -r.numer()
104 } else {
105 r.numer().clone()
106 };
107
108 rem %= r.denom();
109 while !rem.is_zero() && !self.set.contains(&rem) {
110 self.set.insert(rem.clone());
111 rem = (rem * TEN.clone()) % r.denom();
112 }
113
114 rem.is_zero()
115 }
116}
117
118#[inline(always)]
120pub fn is_decimal(r: &BigRational) -> bool {
121 let mut c = DecimalCheck::default();
122 c.is_decimal(r)
123}
124
125pub fn decimal_lexical_representation(r: &BigRational) -> Option<lexical::DecimalBuf> {
128 use std::fmt::Write;
129
130 let mut fraction = String::new();
131 let mut map = std::collections::HashMap::new();
132
133 let mut rem = if r.is_negative() {
134 -r.numer()
135 } else {
136 r.numer().clone()
137 };
138
139 rem %= r.denom();
140 while !rem.is_zero() && !map.contains_key(&rem) {
141 map.insert(rem.clone(), fraction.len());
142 rem *= TEN.clone();
143 fraction.push_str(&(rem.clone() / r.denom()).to_string());
144 rem %= r.denom();
145 }
146
147 let mut output = if r.is_negative() {
148 "-".to_owned()
149 } else {
150 String::new()
151 };
152
153 output.push_str(&(r.numer() / r.denom()).to_string());
154
155 if rem.is_zero() {
156 if !fraction.is_empty() {
157 write!(output, ".{}", &fraction).unwrap();
158 }
159
160 Some(unsafe { lexical::DecimalBuf::new_unchecked(output) })
161 } else {
162 None
163 }
164}
165
166impl Decimal {
167 pub unsafe fn new_unchecked(r: BigRational) -> Self {
173 Self {
174 data: r,
175 lexical: OnceCell::new(),
176 }
177 }
178
179 #[inline(always)]
180 pub fn as_big_rational(&self) -> &BigRational {
181 &self.data
182 }
183
184 #[inline(always)]
185 pub fn into_big_rational(self) -> BigRational {
186 self.data
187 }
188
189 #[inline(always)]
190 pub fn zero() -> Self {
191 Self {
192 data: BigRational::zero(),
193 lexical: OnceCell::new(),
194 }
195 }
196
197 #[inline(always)]
198 pub fn is_zero(&self) -> bool {
199 self.data.is_zero()
200 }
201
202 #[inline(always)]
203 pub fn is_positive(&self) -> bool {
204 self.data.is_positive()
205 }
206
207 #[inline(always)]
208 pub fn is_negative(&self) -> bool {
209 self.data.is_negative()
210 }
211
212 pub fn as_integer(&self) -> Option<&Integer> {
213 if self.data.is_integer() {
214 Some(Integer::from_bigint_ref(self.data.numer()))
215 } else {
216 None
217 }
218 }
219
220 pub fn into_integer(self) -> Option<Integer> {
221 if self.data.is_integer() {
222 Some(Integer::from(self.data.numer().clone())) } else {
224 None
225 }
226 }
227
228 pub fn decimal_type(&self) -> DecimalDatatype {
229 if self.data.is_integer() {
230 if self.data >= BigRational::zero() {
231 if self.data > BigRational::zero() {
232 if self.data <= *U8_MAX_RATIO {
233 UnsignedShortDatatype::UnsignedByte.into()
234 } else if self.data <= *U16_MAX_RATIO {
235 UnsignedShortDatatype::UnsignedShort.into()
236 } else if self.data <= *U32_MAX_RATIO {
237 UnsignedIntDatatype::UnsignedInt.into()
238 } else if self.data <= *U64_MAX_RATIO {
239 UnsignedLongDatatype::UnsignedLong.into()
240 } else {
241 NonNegativeIntegerDatatype::PositiveInteger.into()
242 }
243 } else {
244 UnsignedShortDatatype::UnsignedByte.into()
245 }
246 } else if self.data >= *I8_MIN_RATIO {
247 ShortDatatype::Byte.into()
248 } else if self.data >= *I16_MIN_RATIO {
249 ShortDatatype::Short.into()
250 } else if self.data >= *I32_MIN_RATIO {
251 IntDatatype::Int.into()
252 } else if self.data >= *I64_MIN_RATIO {
253 LongDatatype::Long.into()
254 } else {
255 NonPositiveIntegerDatatype::NegativeInteger.into()
256 }
257 } else {
258 DecimalDatatype::Decimal
259 }
260 }
261
262 #[inline(always)]
263 pub fn lexical_representation(&self) -> &lexical::DecimalBuf {
264 self.lexical
265 .get_or_init(|| decimal_lexical_representation(&self.data).unwrap())
266 }
267
268 pub fn as_f64(&self) -> Option<f64> {
269 self.data.to_f64()
270 }
271
272 pub fn as_f32(&self) -> Option<f32> {
273 self.data.to_f32()
274 }
275
276 pub fn as_float(&self) -> Option<Float> {
277 self.as_f32().map(Float::from)
278 }
279
280 pub fn as_double(&self) -> Option<Double> {
281 self.as_f64().map(Double::from)
282 }
283}
284
285impl fmt::Display for Decimal {
286 #[inline(always)]
287 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
288 self.lexical_representation().fmt(f)
289 }
290}
291
292impl<'a> From<&'a lexical::Decimal> for Decimal {
293 #[inline(always)]
294 fn from(value: &'a lexical::Decimal) -> Self {
295 value.to_owned().into()
296 }
297}
298
299impl From<lexical::DecimalBuf> for Decimal {
300 #[inline(always)]
301 fn from(value: lexical::DecimalBuf) -> Self {
302 let integer_part: BigInt = value.integer_part().as_str().parse().unwrap();
303 let data = match value.fractional_part() {
304 Some(fract) => {
305 let numer = fract.as_str().parse().unwrap();
306 let mut denom = BigInt::new(Sign::Plus, vec![1u32]);
307 for _ in 0..fract.as_str().len() {
308 denom *= 10
309 }
310
311 BigRational::from(integer_part) + BigRational::new(numer, denom)
312 }
313 None => integer_part.into(),
314 };
315
316 Self {
317 data,
318 lexical: value.into(),
319 }
320 }
321}
322
323impl FromStr for Decimal {
324 type Err = lexical::InvalidDecimal;
325
326 #[inline(always)]
327 fn from_str(s: &str) -> Result<Self, Self::Err> {
328 let l = lexical::DecimalBuf::new(s.to_owned()).map_err(|(e, _)| e)?;
329 Ok(l.into())
330 }
331}
332
333macro_rules! from_int {
334 ($($ty:ident),*) => {
335 $(
336 impl From<$ty> for Decimal {
337 fn from(value: $ty) -> Self {
338 Self {
339 data: BigInt::from(value).into(),
340 lexical: OnceCell::new()
341 }
342 }
343 }
344 )*
345 };
346}
347
348from_int!(u8, u16, u32, u64, i8, i16, i32, i64, usize, isize);
349
350macro_rules! try_into_int {
351 ($($ty:ident),*) => {
352 $(
353 impl TryFrom<Decimal> for $ty {
354 type Error = FromDecimalError;
355
356 fn try_from(value: Decimal) -> Result<Self, FromDecimalError> {
357 match value.as_integer() {
358 Some(i) => {
359 i.try_into().map_err(|_| FromDecimalError)
360 }
361 None => Err(FromDecimalError)
362 }
363 }
364 }
365
366 impl<'a> TryFrom<&'a Decimal> for $ty {
367 type Error = FromDecimalError;
368
369 fn try_from(value: &'a Decimal) -> Result<Self, FromDecimalError> {
370 match value.as_integer() {
371 Some(i) => {
372 i.try_into().map_err(|_| FromDecimalError)
373 }
374 None => Err(FromDecimalError)
375 }
376 }
377 }
378 )*
379 };
380}
381
382#[derive(Debug, thiserror::Error)]
383#[error("decimal number conversion failed")]
384pub struct FromDecimalError;
385
386try_into_int!(u8, u16, u32, u64, i8, i16, i32, i64, usize, isize);
387
388impl TryFrom<Decimal> for f32 {
389 type Error = FromDecimalError;
390
391 fn try_from(value: Decimal) -> Result<Self, Self::Error> {
392 value.as_f32().ok_or(FromDecimalError)
393 }
394}
395
396impl TryFrom<Decimal> for Float {
397 type Error = FromDecimalError;
398
399 fn try_from(value: Decimal) -> Result<Self, Self::Error> {
400 value.as_float().ok_or(FromDecimalError)
401 }
402}
403
404impl TryFrom<Decimal> for f64 {
405 type Error = FromDecimalError;
406
407 fn try_from(value: Decimal) -> Result<Self, Self::Error> {
408 value.as_f64().ok_or(FromDecimalError)
409 }
410}
411
412impl TryFrom<Decimal> for Double {
413 type Error = FromDecimalError;
414
415 fn try_from(value: Decimal) -> Result<Self, Self::Error> {
416 value.as_double().ok_or(FromDecimalError)
417 }
418}
419
420impl From<BigInt> for Decimal {
421 #[inline(always)]
422 fn from(value: BigInt) -> Self {
423 Self {
424 data: value.into(),
425 lexical: OnceCell::new(),
426 }
427 }
428}
429
430impl From<Integer> for Decimal {
431 #[inline(always)]
432 fn from(value: Integer) -> Self {
433 let n: BigInt = value.into();
434 n.into()
435 }
436}
437
438impl AsRef<BigRational> for Decimal {
439 #[inline(always)]
440 fn as_ref(&self) -> &BigRational {
441 &self.data
442 }
443}
444
445impl Borrow<BigRational> for Decimal {
446 #[inline(always)]
447 fn borrow(&self) -> &BigRational {
448 &self.data
449 }
450}
451
452impl Deref for Decimal {
453 type Target = BigRational;
454
455 #[inline(always)]
456 fn deref(&self) -> &Self::Target {
457 &self.data
458 }
459}
460
461#[derive(Debug, thiserror::Error)]
464#[error("no decimal representation for rational number {0}")]
465pub struct NoDecimalRepresentation(pub BigRational);
466
467impl TryFrom<BigRational> for Decimal {
468 type Error = NoDecimalRepresentation;
469
470 #[inline(always)]
471 fn try_from(value: BigRational) -> Result<Self, Self::Error> {
472 if is_decimal(&value) {
473 Ok(unsafe { Self::new_unchecked(value) })
474 } else {
475 Err(NoDecimalRepresentation(value))
476 }
477 }
478}
479
480impl From<Decimal> for BigRational {
481 #[inline(always)]
482 fn from(value: Decimal) -> Self {
483 value.data
484 }
485}
486
487impl XsdValue for Decimal {
488 #[inline(always)]
489 fn datatype(&self) -> Datatype {
490 self.decimal_type().into()
491 }
492}
493
494impl ParseXsd for Decimal {
495 type LexicalForm = lexical::Decimal;
496}
497
498impl LexicalFormOf<Decimal> for lexical::Decimal {
499 type ValueError = std::convert::Infallible;
500
501 fn try_as_value(&self) -> Result<Decimal, Self::ValueError> {
502 Ok(self.value())
503 }
504}
505
506#[derive(Debug, thiserror::Error)]
507pub enum NonDecimalFloat {
508 #[error("float is NaN")]
509 Nan,
510
511 #[error("float is positive infinity")]
512 PositiveInfinity,
513
514 #[error("float is negative infinity")]
515 NegativeInfinity,
516}
517
518impl TryFrom<Float> for Decimal {
519 type Error = NonDecimalFloat;
520
521 fn try_from(value: Float) -> Result<Self, Self::Error> {
522 if value.is_nan() {
523 Err(NonDecimalFloat::Nan)
524 } else if value.is_infinite() {
525 if value.is_positive() {
526 Err(NonDecimalFloat::PositiveInfinity)
527 } else {
528 Err(NonDecimalFloat::NegativeInfinity)
529 }
530 } else {
531 Ok(BigRational::from_float(value.into_f32())
532 .unwrap()
533 .try_into()
534 .unwrap())
535 }
536 }
537}
538
539impl TryFrom<Double> for Decimal {
540 type Error = NonDecimalFloat;
541
542 fn try_from(value: Double) -> Result<Self, Self::Error> {
543 if value.is_nan() {
544 Err(NonDecimalFloat::Nan)
545 } else if value.is_infinite() {
546 if value.is_sign_positive() {
547 Err(NonDecimalFloat::PositiveInfinity)
548 } else {
549 Err(NonDecimalFloat::NegativeInfinity)
550 }
551 } else {
552 Ok(BigRational::from_float(*value).unwrap().try_into().unwrap())
553 }
554 }
555}