1use std::cmp::Ordering;
52use std::fmt;
53use std::hash::Hash;
54use std::str::FromStr;
55
56use serde::{Deserialize, Deserializer, Serialize, Serializer};
57
58use crate::encoding::DecimalError;
59use crate::Decimal;
60
61pub const MAX_DECIMAL64_NO_SCALE_PRECISION: u32 = 18;
64
65pub const MAX_DECIMAL64_NO_SCALE_SCALE: i32 = 18;
67
68const SENTINEL_NAN: i64 = i64::MIN;
70const SENTINEL_NEG_INFINITY: i64 = i64::MIN + 1;
71const SENTINEL_POS_INFINITY: i64 = i64::MAX;
72
73const MIN_VALUE: i64 = -999_999_999_999_999_999i64;
75const MAX_VALUE: i64 = 999_999_999_999_999_999i64;
76
77#[derive(Clone, Copy, Default, Eq, PartialEq, Hash)]
102pub struct Decimal64NoScale {
103 value: i64,
105}
106
107impl Decimal64NoScale {
108 pub fn new(s: &str, scale: i32) -> Result<Self, DecimalError> {
126 let s = s.trim();
127
128 let lower = s.to_lowercase();
130 match lower.as_str() {
131 "nan" | "-nan" | "+nan" => return Ok(Self::nan()),
132 "infinity" | "inf" | "+infinity" | "+inf" => return Ok(Self::infinity()),
133 "-infinity" | "-inf" => return Ok(Self::neg_infinity()),
134 _ => {}
135 }
136
137 if scale.abs() > MAX_DECIMAL64_NO_SCALE_SCALE {
139 return Err(DecimalError::InvalidFormat(format!(
140 "Scale {} exceeds maximum {} for Decimal64NoScale",
141 scale.abs(),
142 MAX_DECIMAL64_NO_SCALE_SCALE
143 )));
144 }
145
146 let (is_negative, s) = if let Some(rest) = s.strip_prefix('-') {
148 (true, rest)
149 } else if let Some(rest) = s.strip_prefix('+') {
150 (false, rest)
151 } else {
152 (false, s)
153 };
154
155 let (int_part, frac_part) = if let Some(dot_pos) = s.find('.') {
157 (&s[..dot_pos], &s[dot_pos + 1..])
158 } else {
159 (s, "")
160 };
161
162 let int_part = int_part.trim_start_matches('0');
164 let int_part = if int_part.is_empty() { "0" } else { int_part };
165
166 let value = Self::compute_scaled_value(int_part, frac_part, is_negative, scale)?;
168
169 if !(MIN_VALUE..=MAX_VALUE).contains(&value) {
170 return Err(DecimalError::InvalidFormat(
171 "Value too large for Decimal64NoScale".to_string(),
172 ));
173 }
174
175 Ok(Self { value })
176 }
177
178 #[inline]
192 pub const fn from_raw(value: i64) -> Self {
193 Self { value }
194 }
195
196 #[inline]
200 pub const fn infinity() -> Self {
201 Self {
202 value: SENTINEL_POS_INFINITY,
203 }
204 }
205
206 #[inline]
208 pub const fn neg_infinity() -> Self {
209 Self {
210 value: SENTINEL_NEG_INFINITY,
211 }
212 }
213
214 #[inline]
218 pub const fn nan() -> Self {
219 Self {
220 value: SENTINEL_NAN,
221 }
222 }
223
224 #[inline]
231 pub const fn value(&self) -> i64 {
232 self.value
233 }
234
235 #[inline]
237 pub const fn raw(&self) -> i64 {
238 self.value
239 }
240
241 #[inline]
243 pub fn is_zero(&self) -> bool {
244 !self.is_special() && self.value == 0
245 }
246
247 #[inline]
249 pub fn is_negative(&self) -> bool {
250 !self.is_special() && self.value < 0
251 }
252
253 #[inline]
255 pub fn is_positive(&self) -> bool {
256 !self.is_special() && self.value > 0
257 }
258
259 #[inline]
261 pub fn is_pos_infinity(&self) -> bool {
262 self.value == SENTINEL_POS_INFINITY
263 }
264
265 #[inline]
267 pub fn is_neg_infinity(&self) -> bool {
268 self.value == SENTINEL_NEG_INFINITY
269 }
270
271 #[inline]
273 pub fn is_infinity(&self) -> bool {
274 self.is_pos_infinity() || self.is_neg_infinity()
275 }
276
277 #[inline]
279 pub fn is_nan(&self) -> bool {
280 self.value == SENTINEL_NAN
281 }
282
283 #[inline]
285 pub fn is_special(&self) -> bool {
286 self.value == SENTINEL_NAN
287 || self.value == SENTINEL_NEG_INFINITY
288 || self.value == SENTINEL_POS_INFINITY
289 }
290
291 #[inline]
293 pub fn is_finite(&self) -> bool {
294 !self.is_special()
295 }
296
297 pub fn to_string_with_scale(&self, scale: i32) -> String {
315 if self.is_neg_infinity() {
317 return "-Infinity".to_string();
318 }
319 if self.is_pos_infinity() {
320 return "Infinity".to_string();
321 }
322 if self.is_nan() {
323 return "NaN".to_string();
324 }
325
326 let value = self.value;
327
328 if value == 0 {
329 return "0".to_string();
330 }
331
332 let is_negative = value < 0;
333 let abs_value = value.unsigned_abs();
334
335 if scale <= 0 {
336 let result = if scale < 0 {
338 abs_value * 10u64.pow((-scale) as u32)
339 } else {
340 abs_value
341 };
342 return if is_negative {
343 format!("-{}", result)
344 } else {
345 result.to_string()
346 };
347 }
348
349 let scale_factor = 10u64.pow(scale as u32);
350 let int_part = abs_value / scale_factor;
351 let frac_part = abs_value % scale_factor;
352
353 let result = if frac_part == 0 {
354 int_part.to_string()
355 } else {
356 let frac_str = format!("{:0>width$}", frac_part, width = scale as usize);
357 let frac_str = frac_str.trim_end_matches('0');
358 format!("{}.{}", int_part, frac_str)
359 };
360
361 if is_negative {
362 format!("-{}", result)
363 } else {
364 result
365 }
366 }
367
368 #[inline]
370 pub fn to_be_bytes(&self) -> [u8; 8] {
371 self.value.to_be_bytes()
372 }
373
374 #[inline]
376 pub fn from_be_bytes(bytes: [u8; 8]) -> Self {
377 Self {
378 value: i64::from_be_bytes(bytes),
379 }
380 }
381
382 pub fn to_decimal(&self, scale: i32) -> Decimal {
386 if self.is_neg_infinity() {
387 return Decimal::neg_infinity();
388 }
389 if self.is_pos_infinity() {
390 return Decimal::infinity();
391 }
392 if self.is_nan() {
393 return Decimal::nan();
394 }
395
396 Decimal::from_str(&self.to_string_with_scale(scale))
397 .expect("Decimal64NoScale string is always valid")
398 }
399
400 pub fn from_decimal(decimal: &Decimal, scale: i32) -> Result<Self, DecimalError> {
402 if decimal.is_nan() {
403 return Ok(Self::nan());
404 }
405 if decimal.is_pos_infinity() {
406 return Ok(Self::infinity());
407 }
408 if decimal.is_neg_infinity() {
409 return Ok(Self::neg_infinity());
410 }
411
412 Self::new(&decimal.to_string(), scale)
413 }
414
415 #[inline]
417 pub const fn min_value() -> Self {
418 Self { value: MIN_VALUE }
419 }
420
421 #[inline]
423 pub const fn max_value() -> Self {
424 Self { value: MAX_VALUE }
425 }
426
427 pub fn cmp_with_scale(&self, other: &Self, self_scale: i32, other_scale: i32) -> Ordering {
434 match (self.is_special(), other.is_special()) {
436 (true, true) => {
437 if self.is_nan() && other.is_nan() {
439 return Ordering::Equal;
440 }
441 if self.is_nan() {
442 return Ordering::Greater;
443 }
444 if other.is_nan() {
445 return Ordering::Less;
446 }
447 if self.is_pos_infinity() && other.is_pos_infinity() {
448 return Ordering::Equal;
449 }
450 if self.is_neg_infinity() && other.is_neg_infinity() {
451 return Ordering::Equal;
452 }
453 if self.is_pos_infinity() {
454 return Ordering::Greater;
455 }
456 if self.is_neg_infinity() {
457 return Ordering::Less;
458 }
459 if other.is_pos_infinity() {
460 return Ordering::Less;
461 }
462 Ordering::Greater }
464 (true, false) => {
465 if self.is_neg_infinity() {
466 Ordering::Less
467 } else {
468 Ordering::Greater
469 }
470 }
471 (false, true) => {
472 if other.is_neg_infinity() {
473 Ordering::Greater
474 } else {
475 Ordering::Less
476 }
477 }
478 (false, false) => {
479 if self_scale == other_scale {
481 self.value.cmp(&other.value)
482 } else {
483 let max_scale = self_scale.max(other_scale);
484
485 let self_normalized = if self_scale < max_scale {
486 self.value
487 .saturating_mul(10i64.pow((max_scale - self_scale) as u32))
488 } else {
489 self.value
490 };
491
492 let other_normalized = if other_scale < max_scale {
493 other
494 .value
495 .saturating_mul(10i64.pow((max_scale - other_scale) as u32))
496 } else {
497 other.value
498 };
499
500 self_normalized.cmp(&other_normalized)
501 }
502 }
503 }
504 }
505
506 fn compute_scaled_value(
509 int_part: &str,
510 frac_part: &str,
511 is_negative: bool,
512 scale: i32,
513 ) -> Result<i64, DecimalError> {
514 if scale < 0 {
515 let round_digits = (-scale) as usize;
519 let int_value: i64 = int_part.parse().unwrap_or(0);
520
521 if int_part.len() <= round_digits {
522 return Ok(0);
523 }
524
525 let divisor = 10i64.pow(round_digits as u32);
526 let rounded = (int_value + divisor / 2) / divisor;
528 return Ok(if is_negative { -rounded } else { rounded });
529 }
530
531 let scale_u = scale as usize;
532
533 let int_value: i64 = if int_part == "0" || int_part.is_empty() {
535 0
536 } else {
537 int_part.parse().map_err(|_| {
538 DecimalError::InvalidFormat(format!("Invalid integer part: {}", int_part))
539 })?
540 };
541
542 let scale_factor = 10i64.pow(scale as u32);
544 let scaled_int = int_value.checked_mul(scale_factor).ok_or_else(|| {
545 DecimalError::InvalidFormat("Value too large for Decimal64NoScale".to_string())
546 })?;
547
548 let frac_value: i64 = if frac_part.is_empty() {
550 0
551 } else if frac_part.len() <= scale_u {
552 let padded = format!("{:0<width$}", frac_part, width = scale_u);
554 padded.parse().unwrap_or(0)
555 } else {
556 let truncated = &frac_part[..scale_u];
558 let next_digit = frac_part.chars().nth(scale_u).unwrap_or('0');
559 let mut value: i64 = truncated.parse().unwrap_or(0);
560 if next_digit >= '5' {
561 value += 1;
562 }
563 value
564 };
565
566 let result = scaled_int + frac_value;
567 Ok(if is_negative && result != 0 {
568 -result
569 } else {
570 result
571 })
572 }
573}
574
575impl PartialOrd for Decimal64NoScale {
578 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
579 Some(self.cmp(other))
580 }
581}
582
583impl Ord for Decimal64NoScale {
584 fn cmp(&self, other: &Self) -> Ordering {
588 match (self.is_special(), other.is_special()) {
590 (true, true) => {
591 if self.is_nan() && other.is_nan() {
592 Ordering::Equal
593 } else if self.is_nan() {
594 Ordering::Greater
595 } else if other.is_nan() {
596 Ordering::Less
597 } else if (self.is_pos_infinity() && other.is_pos_infinity())
598 || (self.is_neg_infinity() && other.is_neg_infinity())
599 {
600 Ordering::Equal
601 } else if self.is_neg_infinity() {
602 Ordering::Less
603 } else if self.is_pos_infinity() || other.is_neg_infinity() {
604 Ordering::Greater
605 } else {
606 Ordering::Less
607 }
608 }
609 (true, false) => {
610 if self.is_neg_infinity() {
611 Ordering::Less
612 } else {
613 Ordering::Greater
614 }
615 }
616 (false, true) => {
617 if other.is_neg_infinity() {
618 Ordering::Greater
619 } else {
620 Ordering::Less
621 }
622 }
623 (false, false) => self.value.cmp(&other.value),
624 }
625 }
626}
627
628impl fmt::Debug for Decimal64NoScale {
629 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
630 if self.is_nan() {
631 write!(f, "Decimal64NoScale(NaN)")
632 } else if self.is_pos_infinity() {
633 write!(f, "Decimal64NoScale(Infinity)")
634 } else if self.is_neg_infinity() {
635 write!(f, "Decimal64NoScale(-Infinity)")
636 } else {
637 f.debug_struct("Decimal64NoScale")
638 .field("value", &self.value)
639 .finish()
640 }
641 }
642}
643
644impl fmt::Display for Decimal64NoScale {
645 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
649 if self.is_nan() {
650 write!(f, "NaN")
651 } else if self.is_pos_infinity() {
652 write!(f, "Infinity")
653 } else if self.is_neg_infinity() {
654 write!(f, "-Infinity")
655 } else {
656 write!(f, "{}", self.value)
657 }
658 }
659}
660
661impl From<i64> for Decimal64NoScale {
662 fn from(value: i64) -> Self {
663 Self { value }
664 }
665}
666
667impl From<i32> for Decimal64NoScale {
668 fn from(value: i32) -> Self {
669 Self {
670 value: value as i64,
671 }
672 }
673}
674
675impl Serialize for Decimal64NoScale {
677 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
678 where
679 S: Serializer,
680 {
681 serializer.serialize_i64(self.value)
682 }
683}
684
685impl<'de> Deserialize<'de> for Decimal64NoScale {
686 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
687 where
688 D: Deserializer<'de>,
689 {
690 let value = i64::deserialize(deserializer)?;
691 Ok(Self::from_raw(value))
692 }
693}
694
695#[cfg(test)]
696mod tests {
697 use super::*;
698
699 #[test]
700 fn test_new_basic() {
701 let d = Decimal64NoScale::new("123.45", 2).unwrap();
702 assert_eq!(d.value(), 12345);
703 assert_eq!(d.to_string_with_scale(2), "123.45");
704
705 let d = Decimal64NoScale::new("100", 0).unwrap();
706 assert_eq!(d.value(), 100);
707 assert_eq!(d.to_string_with_scale(0), "100");
708
709 let d = Decimal64NoScale::new("-50.5", 1).unwrap();
710 assert_eq!(d.value(), -505);
711 assert_eq!(d.to_string_with_scale(1), "-50.5");
712 }
713
714 #[test]
715 fn test_18_digit_precision() {
716 let d = Decimal64NoScale::new("123456789012345678", 0).unwrap();
718 assert_eq!(d.value(), 123456789012345678);
719 assert_eq!(d.to_string_with_scale(0), "123456789012345678");
720
721 let d = Decimal64NoScale::new("1234567890123456.78", 2).unwrap();
723 assert_eq!(d.value(), 123456789012345678);
724 assert_eq!(d.to_string_with_scale(2), "1234567890123456.78");
725 }
726
727 #[test]
728 fn test_aggregates_work() {
729 let scale = 2;
731 let a = Decimal64NoScale::new("100.50", scale).unwrap();
732 let b = Decimal64NoScale::new("200.25", scale).unwrap();
733 let c = Decimal64NoScale::new("300.75", scale).unwrap();
734
735 let sum = a.value() + b.value() + c.value();
737 assert_eq!(sum, 60150); let result = Decimal64NoScale::from_raw(sum);
741 assert_eq!(result.to_string_with_scale(scale), "601.5");
742
743 let values = [a.value(), b.value(), c.value()];
745 let min = *values.iter().min().unwrap();
746 let max = *values.iter().max().unwrap();
747 assert_eq!(
748 Decimal64NoScale::from_raw(min).to_string_with_scale(scale),
749 "100.5"
750 );
751 assert_eq!(
752 Decimal64NoScale::from_raw(max).to_string_with_scale(scale),
753 "300.75"
754 );
755 }
756
757 #[test]
758 fn test_special_values() {
759 let nan = Decimal64NoScale::nan();
760 assert!(nan.is_nan());
761 assert!(nan.is_special());
762 assert_eq!(nan.to_string_with_scale(2), "NaN");
763
764 let inf = Decimal64NoScale::infinity();
765 assert!(inf.is_pos_infinity());
766 assert_eq!(inf.to_string_with_scale(2), "Infinity");
767
768 let neg_inf = Decimal64NoScale::neg_infinity();
769 assert!(neg_inf.is_neg_infinity());
770 assert_eq!(neg_inf.to_string_with_scale(2), "-Infinity");
771 }
772
773 #[test]
774 fn test_ordering() {
775 let neg_inf = Decimal64NoScale::neg_infinity();
776 let neg = Decimal64NoScale::from_raw(-1000);
777 let zero = Decimal64NoScale::from_raw(0);
778 let pos = Decimal64NoScale::from_raw(1000);
779 let inf = Decimal64NoScale::infinity();
780 let nan = Decimal64NoScale::nan();
781
782 assert!(neg_inf < neg);
783 assert!(neg < zero);
784 assert!(zero < pos);
785 assert!(pos < inf);
786 assert!(inf < nan);
787 }
788
789 #[test]
790 fn test_from_str_special() {
791 assert!(Decimal64NoScale::new("Infinity", 0)
792 .unwrap()
793 .is_pos_infinity());
794 assert!(Decimal64NoScale::new("-Infinity", 0)
795 .unwrap()
796 .is_neg_infinity());
797 assert!(Decimal64NoScale::new("NaN", 0).unwrap().is_nan());
798 }
799
800 #[test]
801 fn test_roundtrip() {
802 let scale = 4;
803 let values = ["0", "123.4567", "-99.9999", "1000000", "-1"];
804
805 for s in values {
806 let d = Decimal64NoScale::new(s, scale).unwrap();
807 let raw = d.value();
808 let restored = Decimal64NoScale::from_raw(raw);
809 assert_eq!(d.value(), restored.value(), "Roundtrip failed for {}", s);
810 }
811 }
812
813 #[test]
814 fn test_byte_roundtrip() {
815 let d = Decimal64NoScale::new("123.45", 2).unwrap();
816 let bytes = d.to_be_bytes();
817 let restored = Decimal64NoScale::from_be_bytes(bytes);
818 assert_eq!(d, restored);
819 }
820
821 #[test]
822 fn test_zero() {
823 let d = Decimal64NoScale::new("0", 0).unwrap();
824 assert!(d.is_zero());
825 assert!(!d.is_negative());
826 assert!(!d.is_positive());
827 assert!(d.is_finite());
828 }
829
830 #[test]
831 fn test_negative_scale() {
832 let d = Decimal64NoScale::new("12345", -2).unwrap();
833 assert_eq!(d.to_string_with_scale(-2), "12300");
834 }
835
836 #[test]
837 fn test_max_precision() {
838 let max = Decimal64NoScale::max_value();
840 assert!(max.is_finite());
841 assert!(max.value() > 0);
842
843 let min = Decimal64NoScale::min_value();
845 assert!(min.is_finite());
846 assert!(min.value() < 0);
847 }
848}