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 pub fn from_i64(value: i64, scale: i32) -> Result<Self, DecimalError> {
210 if scale < 0 {
211 let divisor = 10i64.pow((-scale) as u32);
213 return Ok(Self {
214 value: value / divisor,
215 });
216 }
217
218 if scale == 0 {
219 return Ok(Self { value });
220 }
221
222 let scale_factor = 10i64.pow(scale as u32);
223 let scaled = value.checked_mul(scale_factor).ok_or_else(|| {
224 DecimalError::InvalidFormat(format!(
225 "Overflow: {} * 10^{} exceeds i64 range",
226 value, scale
227 ))
228 })?;
229
230 if !(MIN_VALUE..=MAX_VALUE).contains(&scaled) {
231 return Err(DecimalError::InvalidFormat(
232 "Value too large for Decimal64NoScale".to_string(),
233 ));
234 }
235
236 Ok(Self { value: scaled })
237 }
238
239 pub fn from_u64(value: u64, scale: i32) -> Result<Self, DecimalError> {
252 if value > i64::MAX as u64 {
253 return Err(DecimalError::InvalidFormat(format!(
254 "Value {} exceeds i64::MAX",
255 value
256 )));
257 }
258 Self::from_i64(value as i64, scale)
259 }
260
261 pub fn from_f64(value: f64, scale: i32) -> Result<Self, DecimalError> {
276 if value.is_nan() {
277 return Ok(Self::nan());
278 }
279 if value.is_infinite() {
280 return Ok(if value.is_sign_positive() {
281 Self::infinity()
282 } else {
283 Self::neg_infinity()
284 });
285 }
286 Self::new(&value.to_string(), scale)
288 }
289
290 #[inline]
294 pub const fn infinity() -> Self {
295 Self {
296 value: SENTINEL_POS_INFINITY,
297 }
298 }
299
300 #[inline]
302 pub const fn neg_infinity() -> Self {
303 Self {
304 value: SENTINEL_NEG_INFINITY,
305 }
306 }
307
308 #[inline]
312 pub const fn nan() -> Self {
313 Self {
314 value: SENTINEL_NAN,
315 }
316 }
317
318 #[inline]
325 pub const fn value(&self) -> i64 {
326 self.value
327 }
328
329 #[inline]
331 pub const fn raw(&self) -> i64 {
332 self.value
333 }
334
335 #[inline]
337 pub fn is_zero(&self) -> bool {
338 !self.is_special() && self.value == 0
339 }
340
341 #[inline]
343 pub fn is_negative(&self) -> bool {
344 !self.is_special() && self.value < 0
345 }
346
347 #[inline]
349 pub fn is_positive(&self) -> bool {
350 !self.is_special() && self.value > 0
351 }
352
353 #[inline]
355 pub fn is_pos_infinity(&self) -> bool {
356 self.value == SENTINEL_POS_INFINITY
357 }
358
359 #[inline]
361 pub fn is_neg_infinity(&self) -> bool {
362 self.value == SENTINEL_NEG_INFINITY
363 }
364
365 #[inline]
367 pub fn is_infinity(&self) -> bool {
368 self.is_pos_infinity() || self.is_neg_infinity()
369 }
370
371 #[inline]
373 pub fn is_nan(&self) -> bool {
374 self.value == SENTINEL_NAN
375 }
376
377 #[inline]
379 pub fn is_special(&self) -> bool {
380 self.value == SENTINEL_NAN
381 || self.value == SENTINEL_NEG_INFINITY
382 || self.value == SENTINEL_POS_INFINITY
383 }
384
385 #[inline]
387 pub fn is_finite(&self) -> bool {
388 !self.is_special()
389 }
390
391 pub fn to_string_with_scale(&self, scale: i32) -> String {
409 if self.is_neg_infinity() {
411 return "-Infinity".to_string();
412 }
413 if self.is_pos_infinity() {
414 return "Infinity".to_string();
415 }
416 if self.is_nan() {
417 return "NaN".to_string();
418 }
419
420 let value = self.value;
421
422 if value == 0 {
423 return "0".to_string();
424 }
425
426 let is_negative = value < 0;
427 let abs_value = value.unsigned_abs();
428
429 if scale <= 0 {
430 let result = if scale < 0 {
432 abs_value * 10u64.pow((-scale) as u32)
433 } else {
434 abs_value
435 };
436 return if is_negative {
437 format!("-{}", result)
438 } else {
439 result.to_string()
440 };
441 }
442
443 let scale_factor = 10u64.pow(scale as u32);
444 let int_part = abs_value / scale_factor;
445 let frac_part = abs_value % scale_factor;
446
447 let result = if frac_part == 0 {
448 int_part.to_string()
449 } else {
450 let frac_str = format!("{:0>width$}", frac_part, width = scale as usize);
451 let frac_str = frac_str.trim_end_matches('0');
452 format!("{}.{}", int_part, frac_str)
453 };
454
455 if is_negative {
456 format!("-{}", result)
457 } else {
458 result
459 }
460 }
461
462 #[inline]
464 pub fn to_be_bytes(&self) -> [u8; 8] {
465 self.value.to_be_bytes()
466 }
467
468 #[inline]
470 pub fn from_be_bytes(bytes: [u8; 8]) -> Self {
471 Self {
472 value: i64::from_be_bytes(bytes),
473 }
474 }
475
476 pub fn to_decimal(&self, scale: i32) -> Decimal {
480 if self.is_neg_infinity() {
481 return Decimal::neg_infinity();
482 }
483 if self.is_pos_infinity() {
484 return Decimal::infinity();
485 }
486 if self.is_nan() {
487 return Decimal::nan();
488 }
489
490 Decimal::from_str(&self.to_string_with_scale(scale))
491 .expect("Decimal64NoScale string is always valid")
492 }
493
494 pub fn from_decimal(decimal: &Decimal, scale: i32) -> Result<Self, DecimalError> {
496 if decimal.is_nan() {
497 return Ok(Self::nan());
498 }
499 if decimal.is_pos_infinity() {
500 return Ok(Self::infinity());
501 }
502 if decimal.is_neg_infinity() {
503 return Ok(Self::neg_infinity());
504 }
505
506 Self::new(&decimal.to_string(), scale)
507 }
508
509 #[inline]
511 pub const fn min_value() -> Self {
512 Self { value: MIN_VALUE }
513 }
514
515 #[inline]
517 pub const fn max_value() -> Self {
518 Self { value: MAX_VALUE }
519 }
520
521 pub fn cmp_with_scale(&self, other: &Self, self_scale: i32, other_scale: i32) -> Ordering {
528 match (self.is_special(), other.is_special()) {
530 (true, true) => {
531 if self.is_nan() && other.is_nan() {
533 return Ordering::Equal;
534 }
535 if self.is_nan() {
536 return Ordering::Greater;
537 }
538 if other.is_nan() {
539 return Ordering::Less;
540 }
541 if self.is_pos_infinity() && other.is_pos_infinity() {
542 return Ordering::Equal;
543 }
544 if self.is_neg_infinity() && other.is_neg_infinity() {
545 return Ordering::Equal;
546 }
547 if self.is_pos_infinity() {
548 return Ordering::Greater;
549 }
550 if self.is_neg_infinity() {
551 return Ordering::Less;
552 }
553 if other.is_pos_infinity() {
554 return Ordering::Less;
555 }
556 Ordering::Greater }
558 (true, false) => {
559 if self.is_neg_infinity() {
560 Ordering::Less
561 } else {
562 Ordering::Greater
563 }
564 }
565 (false, true) => {
566 if other.is_neg_infinity() {
567 Ordering::Greater
568 } else {
569 Ordering::Less
570 }
571 }
572 (false, false) => {
573 if self_scale == other_scale {
575 self.value.cmp(&other.value)
576 } else {
577 let max_scale = self_scale.max(other_scale);
578
579 let self_normalized = if self_scale < max_scale {
580 self.value
581 .saturating_mul(10i64.pow((max_scale - self_scale) as u32))
582 } else {
583 self.value
584 };
585
586 let other_normalized = if other_scale < max_scale {
587 other
588 .value
589 .saturating_mul(10i64.pow((max_scale - other_scale) as u32))
590 } else {
591 other.value
592 };
593
594 self_normalized.cmp(&other_normalized)
595 }
596 }
597 }
598 }
599
600 fn compute_scaled_value(
603 int_part: &str,
604 frac_part: &str,
605 is_negative: bool,
606 scale: i32,
607 ) -> Result<i64, DecimalError> {
608 if scale < 0 {
609 let round_digits = (-scale) as usize;
613 let int_value: i64 = int_part.parse().unwrap_or(0);
614
615 if int_part.len() <= round_digits {
616 return Ok(0);
617 }
618
619 let divisor = 10i64.pow(round_digits as u32);
620 let rounded = (int_value + divisor / 2) / divisor;
622 return Ok(if is_negative { -rounded } else { rounded });
623 }
624
625 let scale_u = scale as usize;
626
627 let int_value: i64 = if int_part == "0" || int_part.is_empty() {
629 0
630 } else {
631 int_part.parse().map_err(|_| {
632 DecimalError::InvalidFormat(format!("Invalid integer part: {}", int_part))
633 })?
634 };
635
636 let scale_factor = 10i64.pow(scale as u32);
638 let scaled_int = int_value.checked_mul(scale_factor).ok_or_else(|| {
639 DecimalError::InvalidFormat("Value too large for Decimal64NoScale".to_string())
640 })?;
641
642 let frac_value: i64 = if frac_part.is_empty() {
644 0
645 } else if frac_part.len() <= scale_u {
646 let padded = format!("{:0<width$}", frac_part, width = scale_u);
648 padded.parse().unwrap_or(0)
649 } else {
650 let truncated = &frac_part[..scale_u];
652 let next_digit = frac_part.chars().nth(scale_u).unwrap_or('0');
653 let mut value: i64 = truncated.parse().unwrap_or(0);
654 if next_digit >= '5' {
655 value += 1;
656 }
657 value
658 };
659
660 let result = scaled_int + frac_value;
661 Ok(if is_negative && result != 0 {
662 -result
663 } else {
664 result
665 })
666 }
667}
668
669impl PartialOrd for Decimal64NoScale {
672 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
673 Some(self.cmp(other))
674 }
675}
676
677impl Ord for Decimal64NoScale {
678 fn cmp(&self, other: &Self) -> Ordering {
682 match (self.is_special(), other.is_special()) {
684 (true, true) => {
685 if self.is_nan() && other.is_nan() {
686 Ordering::Equal
687 } else if self.is_nan() {
688 Ordering::Greater
689 } else if other.is_nan() {
690 Ordering::Less
691 } else if (self.is_pos_infinity() && other.is_pos_infinity())
692 || (self.is_neg_infinity() && other.is_neg_infinity())
693 {
694 Ordering::Equal
695 } else if self.is_neg_infinity() {
696 Ordering::Less
697 } else if self.is_pos_infinity() || other.is_neg_infinity() {
698 Ordering::Greater
699 } else {
700 Ordering::Less
701 }
702 }
703 (true, false) => {
704 if self.is_neg_infinity() {
705 Ordering::Less
706 } else {
707 Ordering::Greater
708 }
709 }
710 (false, true) => {
711 if other.is_neg_infinity() {
712 Ordering::Greater
713 } else {
714 Ordering::Less
715 }
716 }
717 (false, false) => self.value.cmp(&other.value),
718 }
719 }
720}
721
722impl fmt::Debug for Decimal64NoScale {
723 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
724 if self.is_nan() {
725 write!(f, "Decimal64NoScale(NaN)")
726 } else if self.is_pos_infinity() {
727 write!(f, "Decimal64NoScale(Infinity)")
728 } else if self.is_neg_infinity() {
729 write!(f, "Decimal64NoScale(-Infinity)")
730 } else {
731 f.debug_struct("Decimal64NoScale")
732 .field("value", &self.value)
733 .finish()
734 }
735 }
736}
737
738impl fmt::Display for Decimal64NoScale {
739 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
743 if self.is_nan() {
744 write!(f, "NaN")
745 } else if self.is_pos_infinity() {
746 write!(f, "Infinity")
747 } else if self.is_neg_infinity() {
748 write!(f, "-Infinity")
749 } else {
750 write!(f, "{}", self.value)
751 }
752 }
753}
754
755impl From<i64> for Decimal64NoScale {
756 fn from(value: i64) -> Self {
757 Self { value }
758 }
759}
760
761impl From<i32> for Decimal64NoScale {
762 fn from(value: i32) -> Self {
763 Self {
764 value: value as i64,
765 }
766 }
767}
768
769impl Serialize for Decimal64NoScale {
771 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
772 where
773 S: Serializer,
774 {
775 serializer.serialize_i64(self.value)
776 }
777}
778
779impl<'de> Deserialize<'de> for Decimal64NoScale {
780 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
781 where
782 D: Deserializer<'de>,
783 {
784 let value = i64::deserialize(deserializer)?;
785 Ok(Self::from_raw(value))
786 }
787}
788
789#[cfg(test)]
790mod tests {
791 use super::*;
792
793 #[test]
794 fn test_new_basic() {
795 let d = Decimal64NoScale::new("123.45", 2).unwrap();
796 assert_eq!(d.value(), 12345);
797 assert_eq!(d.to_string_with_scale(2), "123.45");
798
799 let d = Decimal64NoScale::new("100", 0).unwrap();
800 assert_eq!(d.value(), 100);
801 assert_eq!(d.to_string_with_scale(0), "100");
802
803 let d = Decimal64NoScale::new("-50.5", 1).unwrap();
804 assert_eq!(d.value(), -505);
805 assert_eq!(d.to_string_with_scale(1), "-50.5");
806 }
807
808 #[test]
809 fn test_18_digit_precision() {
810 let d = Decimal64NoScale::new("123456789012345678", 0).unwrap();
812 assert_eq!(d.value(), 123456789012345678);
813 assert_eq!(d.to_string_with_scale(0), "123456789012345678");
814
815 let d = Decimal64NoScale::new("1234567890123456.78", 2).unwrap();
817 assert_eq!(d.value(), 123456789012345678);
818 assert_eq!(d.to_string_with_scale(2), "1234567890123456.78");
819 }
820
821 #[test]
822 fn test_aggregates_work() {
823 let scale = 2;
825 let a = Decimal64NoScale::new("100.50", scale).unwrap();
826 let b = Decimal64NoScale::new("200.25", scale).unwrap();
827 let c = Decimal64NoScale::new("300.75", scale).unwrap();
828
829 let sum = a.value() + b.value() + c.value();
831 assert_eq!(sum, 60150); let result = Decimal64NoScale::from_raw(sum);
835 assert_eq!(result.to_string_with_scale(scale), "601.5");
836
837 let values = [a.value(), b.value(), c.value()];
839 let min = *values.iter().min().unwrap();
840 let max = *values.iter().max().unwrap();
841 assert_eq!(
842 Decimal64NoScale::from_raw(min).to_string_with_scale(scale),
843 "100.5"
844 );
845 assert_eq!(
846 Decimal64NoScale::from_raw(max).to_string_with_scale(scale),
847 "300.75"
848 );
849 }
850
851 #[test]
852 fn test_special_values() {
853 let nan = Decimal64NoScale::nan();
854 assert!(nan.is_nan());
855 assert!(nan.is_special());
856 assert_eq!(nan.to_string_with_scale(2), "NaN");
857
858 let inf = Decimal64NoScale::infinity();
859 assert!(inf.is_pos_infinity());
860 assert_eq!(inf.to_string_with_scale(2), "Infinity");
861
862 let neg_inf = Decimal64NoScale::neg_infinity();
863 assert!(neg_inf.is_neg_infinity());
864 assert_eq!(neg_inf.to_string_with_scale(2), "-Infinity");
865 }
866
867 #[test]
868 fn test_ordering() {
869 let neg_inf = Decimal64NoScale::neg_infinity();
870 let neg = Decimal64NoScale::from_raw(-1000);
871 let zero = Decimal64NoScale::from_raw(0);
872 let pos = Decimal64NoScale::from_raw(1000);
873 let inf = Decimal64NoScale::infinity();
874 let nan = Decimal64NoScale::nan();
875
876 assert!(neg_inf < neg);
877 assert!(neg < zero);
878 assert!(zero < pos);
879 assert!(pos < inf);
880 assert!(inf < nan);
881 }
882
883 #[test]
884 fn test_from_str_special() {
885 assert!(Decimal64NoScale::new("Infinity", 0)
886 .unwrap()
887 .is_pos_infinity());
888 assert!(Decimal64NoScale::new("-Infinity", 0)
889 .unwrap()
890 .is_neg_infinity());
891 assert!(Decimal64NoScale::new("NaN", 0).unwrap().is_nan());
892 }
893
894 #[test]
895 fn test_roundtrip() {
896 let scale = 4;
897 let values = ["0", "123.4567", "-99.9999", "1000000", "-1"];
898
899 for s in values {
900 let d = Decimal64NoScale::new(s, scale).unwrap();
901 let raw = d.value();
902 let restored = Decimal64NoScale::from_raw(raw);
903 assert_eq!(d.value(), restored.value(), "Roundtrip failed for {}", s);
904 }
905 }
906
907 #[test]
908 fn test_byte_roundtrip() {
909 let d = Decimal64NoScale::new("123.45", 2).unwrap();
910 let bytes = d.to_be_bytes();
911 let restored = Decimal64NoScale::from_be_bytes(bytes);
912 assert_eq!(d, restored);
913 }
914
915 #[test]
916 fn test_zero() {
917 let d = Decimal64NoScale::new("0", 0).unwrap();
918 assert!(d.is_zero());
919 assert!(!d.is_negative());
920 assert!(!d.is_positive());
921 assert!(d.is_finite());
922 }
923
924 #[test]
925 fn test_negative_scale() {
926 let d = Decimal64NoScale::new("12345", -2).unwrap();
927 assert_eq!(d.to_string_with_scale(-2), "12300");
928 }
929
930 #[test]
931 fn test_max_precision() {
932 let max = Decimal64NoScale::max_value();
934 assert!(max.is_finite());
935 assert!(max.value() > 0);
936
937 let min = Decimal64NoScale::min_value();
939 assert!(min.is_finite());
940 assert!(min.value() < 0);
941 }
942
943 #[test]
944 fn test_from_i64() {
945 let d = Decimal64NoScale::from_i64(123, 2).unwrap();
947 assert_eq!(d.value(), 12300);
948 assert_eq!(d.to_string_with_scale(2), "123");
949
950 let d = Decimal64NoScale::from_i64(123, 0).unwrap();
952 assert_eq!(d.value(), 123);
953
954 let d = Decimal64NoScale::from_i64(-50, 2).unwrap();
956 assert_eq!(d.value(), -5000);
957
958 let d = Decimal64NoScale::from_i64(12345, -2).unwrap();
960 assert_eq!(d.value(), 123);
961 }
962
963 #[test]
964 fn test_from_u64() {
965 let d = Decimal64NoScale::from_u64(123, 2).unwrap();
966 assert_eq!(d.value(), 12300);
967
968 assert!(Decimal64NoScale::from_u64(u64::MAX, 0).is_err());
970 }
971
972 #[test]
973 fn test_from_f64() {
974 let d = Decimal64NoScale::from_f64(123.45, 2).unwrap();
976 assert_eq!(d.value(), 12345);
977 assert_eq!(d.to_string_with_scale(2), "123.45");
978
979 assert!(Decimal64NoScale::from_f64(f64::NAN, 2).unwrap().is_nan());
981 assert!(Decimal64NoScale::from_f64(f64::INFINITY, 2)
982 .unwrap()
983 .is_pos_infinity());
984 assert!(Decimal64NoScale::from_f64(f64::NEG_INFINITY, 2)
985 .unwrap()
986 .is_neg_infinity());
987
988 let d = Decimal64NoScale::from_f64(-99.99, 2).unwrap();
990 assert_eq!(d.value(), -9999);
991 }
992}