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_NEG_INFINITY: i64 = i64::MIN;
70const SENTINEL_POS_INFINITY: i64 = i64::MAX - 1;
71const SENTINEL_NAN: 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 {
433 if self.is_neg_infinity() {
435 return "-Infinity".to_string();
436 }
437 if self.is_pos_infinity() {
438 return "Infinity".to_string();
439 }
440 if self.is_nan() {
441 return "NaN".to_string();
442 }
443
444 let value = self.value;
445
446 if value == 0 {
447 return if scale > 0 {
448 format!("0.{}", "0".repeat(scale as usize))
449 } else {
450 "0".to_string()
451 };
452 }
453
454 let is_negative = value < 0;
455 let abs_value = value.unsigned_abs();
456
457 if scale <= 0 {
458 let result = if scale < 0 {
460 abs_value * 10u64.pow((-scale) as u32)
461 } else {
462 abs_value
463 };
464 return if is_negative {
465 format!("-{}", result)
466 } else {
467 result.to_string()
468 };
469 }
470
471 let scale_factor = 10u64.pow(scale as u32);
472 let int_part = abs_value / scale_factor;
473 let frac_part = abs_value % scale_factor;
474
475 let frac_str = format!("{:0>width$}", frac_part, width = scale as usize);
477 let result = format!("{}.{}", int_part, frac_str);
478
479 if is_negative {
480 format!("-{}", result)
481 } else {
482 result
483 }
484 }
485
486 #[inline]
488 pub fn to_be_bytes(&self) -> [u8; 8] {
489 self.value.to_be_bytes()
490 }
491
492 #[inline]
494 pub fn from_be_bytes(bytes: [u8; 8]) -> Self {
495 Self {
496 value: i64::from_be_bytes(bytes),
497 }
498 }
499
500 pub fn to_decimal(&self, scale: i32) -> Decimal {
504 if self.is_neg_infinity() {
505 return Decimal::neg_infinity();
506 }
507 if self.is_pos_infinity() {
508 return Decimal::infinity();
509 }
510 if self.is_nan() {
511 return Decimal::nan();
512 }
513
514 Decimal::from_str(&self.to_string_with_scale(scale))
515 .expect("Decimal64NoScale string is always valid")
516 }
517
518 pub fn from_decimal(decimal: &Decimal, scale: i32) -> Result<Self, DecimalError> {
520 if decimal.is_nan() {
521 return Ok(Self::nan());
522 }
523 if decimal.is_pos_infinity() {
524 return Ok(Self::infinity());
525 }
526 if decimal.is_neg_infinity() {
527 return Ok(Self::neg_infinity());
528 }
529
530 Self::new(&decimal.to_string(), scale)
531 }
532
533 #[inline]
535 pub const fn min_value() -> Self {
536 Self { value: MIN_VALUE }
537 }
538
539 #[inline]
541 pub const fn max_value() -> Self {
542 Self { value: MAX_VALUE }
543 }
544
545 pub fn cmp_with_scale(&self, other: &Self, self_scale: i32, other_scale: i32) -> Ordering {
552 match (self.is_special(), other.is_special()) {
554 (true, true) => {
555 if self.is_nan() && other.is_nan() {
557 return Ordering::Equal;
558 }
559 if self.is_nan() {
560 return Ordering::Greater;
561 }
562 if other.is_nan() {
563 return Ordering::Less;
564 }
565 if self.is_pos_infinity() && other.is_pos_infinity() {
566 return Ordering::Equal;
567 }
568 if self.is_neg_infinity() && other.is_neg_infinity() {
569 return Ordering::Equal;
570 }
571 if self.is_pos_infinity() {
572 return Ordering::Greater;
573 }
574 if self.is_neg_infinity() {
575 return Ordering::Less;
576 }
577 if other.is_pos_infinity() {
578 return Ordering::Less;
579 }
580 Ordering::Greater }
582 (true, false) => {
583 if self.is_neg_infinity() {
584 Ordering::Less
585 } else {
586 Ordering::Greater
587 }
588 }
589 (false, true) => {
590 if other.is_neg_infinity() {
591 Ordering::Greater
592 } else {
593 Ordering::Less
594 }
595 }
596 (false, false) => {
597 if self_scale == other_scale {
599 self.value.cmp(&other.value)
600 } else {
601 let max_scale = self_scale.max(other_scale);
602
603 let self_normalized = if self_scale < max_scale {
604 self.value
605 .saturating_mul(10i64.pow((max_scale - self_scale) as u32))
606 } else {
607 self.value
608 };
609
610 let other_normalized = if other_scale < max_scale {
611 other
612 .value
613 .saturating_mul(10i64.pow((max_scale - other_scale) as u32))
614 } else {
615 other.value
616 };
617
618 self_normalized.cmp(&other_normalized)
619 }
620 }
621 }
622 }
623
624 fn compute_scaled_value(
627 int_part: &str,
628 frac_part: &str,
629 is_negative: bool,
630 scale: i32,
631 ) -> Result<i64, DecimalError> {
632 if scale < 0 {
633 let round_digits = (-scale) as usize;
637 let int_value: i64 = int_part.parse().unwrap_or(0);
638
639 if int_part.len() <= round_digits {
640 return Ok(0);
641 }
642
643 let divisor = 10i64.pow(round_digits as u32);
644 let rounded = (int_value + divisor / 2) / divisor;
646 return Ok(if is_negative { -rounded } else { rounded });
647 }
648
649 let scale_u = scale as usize;
650
651 let int_value: i64 = if int_part == "0" || int_part.is_empty() {
653 0
654 } else {
655 int_part.parse().map_err(|_| {
656 DecimalError::InvalidFormat(format!("Invalid integer part: {}", int_part))
657 })?
658 };
659
660 let scale_factor = 10i64.pow(scale as u32);
662 let scaled_int = int_value.checked_mul(scale_factor).ok_or_else(|| {
663 DecimalError::InvalidFormat("Value too large for Decimal64NoScale".to_string())
664 })?;
665
666 let frac_value: i64 = if frac_part.is_empty() {
668 0
669 } else if frac_part.len() <= scale_u {
670 let padded = format!("{:0<width$}", frac_part, width = scale_u);
672 padded.parse().unwrap_or(0)
673 } else {
674 let truncated = &frac_part[..scale_u];
676 let next_digit = frac_part.chars().nth(scale_u).unwrap_or('0');
677 let mut value: i64 = truncated.parse().unwrap_or(0);
678 if next_digit >= '5' {
679 value += 1;
680 }
681 value
682 };
683
684 let result = scaled_int + frac_value;
685 Ok(if is_negative && result != 0 {
686 -result
687 } else {
688 result
689 })
690 }
691}
692
693impl PartialOrd for Decimal64NoScale {
696 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
697 Some(self.cmp(other))
698 }
699}
700
701impl Ord for Decimal64NoScale {
702 fn cmp(&self, other: &Self) -> Ordering {
709 self.value.cmp(&other.value)
710 }
711}
712
713impl fmt::Debug for Decimal64NoScale {
714 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
715 if self.is_nan() {
716 write!(f, "Decimal64NoScale(NaN)")
717 } else if self.is_pos_infinity() {
718 write!(f, "Decimal64NoScale(Infinity)")
719 } else if self.is_neg_infinity() {
720 write!(f, "Decimal64NoScale(-Infinity)")
721 } else {
722 f.debug_struct("Decimal64NoScale")
723 .field("value", &self.value)
724 .finish()
725 }
726 }
727}
728
729impl fmt::Display for Decimal64NoScale {
730 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
734 if self.is_nan() {
735 write!(f, "NaN")
736 } else if self.is_pos_infinity() {
737 write!(f, "Infinity")
738 } else if self.is_neg_infinity() {
739 write!(f, "-Infinity")
740 } else {
741 write!(f, "{}", self.value)
742 }
743 }
744}
745
746impl From<i64> for Decimal64NoScale {
747 fn from(value: i64) -> Self {
748 Self { value }
749 }
750}
751
752impl From<i32> for Decimal64NoScale {
753 fn from(value: i32) -> Self {
754 Self {
755 value: value as i64,
756 }
757 }
758}
759
760impl Serialize for Decimal64NoScale {
762 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
763 where
764 S: Serializer,
765 {
766 serializer.serialize_i64(self.value)
767 }
768}
769
770impl<'de> Deserialize<'de> for Decimal64NoScale {
771 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
772 where
773 D: Deserializer<'de>,
774 {
775 let value = i64::deserialize(deserializer)?;
776 Ok(Self::from_raw(value))
777 }
778}
779
780#[cfg(test)]
781mod tests {
782 use super::*;
783
784 #[test]
785 fn test_new_basic() {
786 let d = Decimal64NoScale::new("123.45", 2).unwrap();
787 assert_eq!(d.value(), 12345);
788 assert_eq!(d.to_string_with_scale(2), "123.45");
789
790 let d = Decimal64NoScale::new("100", 0).unwrap();
791 assert_eq!(d.value(), 100);
792 assert_eq!(d.to_string_with_scale(0), "100");
793
794 let d = Decimal64NoScale::new("-50.5", 1).unwrap();
795 assert_eq!(d.value(), -505);
796 assert_eq!(d.to_string_with_scale(1), "-50.5");
797 }
798
799 #[test]
800 fn test_18_digit_precision() {
801 let d = Decimal64NoScale::new("123456789012345678", 0).unwrap();
803 assert_eq!(d.value(), 123456789012345678);
804 assert_eq!(d.to_string_with_scale(0), "123456789012345678");
805
806 let d = Decimal64NoScale::new("1234567890123456.78", 2).unwrap();
808 assert_eq!(d.value(), 123456789012345678);
809 assert_eq!(d.to_string_with_scale(2), "1234567890123456.78");
810 }
811
812 #[test]
813 fn test_aggregates_work() {
814 let scale = 2;
816 let a = Decimal64NoScale::new("100.50", scale).unwrap();
817 let b = Decimal64NoScale::new("200.25", scale).unwrap();
818 let c = Decimal64NoScale::new("300.75", scale).unwrap();
819
820 let sum = a.value() + b.value() + c.value();
822 assert_eq!(sum, 60150); let result = Decimal64NoScale::from_raw(sum);
826 assert_eq!(result.to_string_with_scale(scale), "601.50");
827
828 let values = [a.value(), b.value(), c.value()];
830 let min = *values.iter().min().unwrap();
831 let max = *values.iter().max().unwrap();
832 assert_eq!(
833 Decimal64NoScale::from_raw(min).to_string_with_scale(scale),
834 "100.50"
835 );
836 assert_eq!(
837 Decimal64NoScale::from_raw(max).to_string_with_scale(scale),
838 "300.75"
839 );
840 }
841
842 #[test]
843 fn test_special_values() {
844 let nan = Decimal64NoScale::nan();
845 assert!(nan.is_nan());
846 assert!(nan.is_special());
847 assert_eq!(nan.to_string_with_scale(2), "NaN");
848
849 let inf = Decimal64NoScale::infinity();
850 assert!(inf.is_pos_infinity());
851 assert_eq!(inf.to_string_with_scale(2), "Infinity");
852
853 let neg_inf = Decimal64NoScale::neg_infinity();
854 assert!(neg_inf.is_neg_infinity());
855 assert_eq!(neg_inf.to_string_with_scale(2), "-Infinity");
856 }
857
858 #[test]
859 fn test_ordering() {
860 let neg_inf = Decimal64NoScale::neg_infinity();
861 let neg = Decimal64NoScale::from_raw(-1000);
862 let zero = Decimal64NoScale::from_raw(0);
863 let pos = Decimal64NoScale::from_raw(1000);
864 let inf = Decimal64NoScale::infinity();
865 let nan = Decimal64NoScale::nan();
866
867 assert!(neg_inf < neg);
868 assert!(neg < zero);
869 assert!(zero < pos);
870 assert!(pos < inf);
871 assert!(inf < nan);
872 }
873
874 #[test]
875 fn test_from_str_special() {
876 assert!(Decimal64NoScale::new("Infinity", 0)
877 .unwrap()
878 .is_pos_infinity());
879 assert!(Decimal64NoScale::new("-Infinity", 0)
880 .unwrap()
881 .is_neg_infinity());
882 assert!(Decimal64NoScale::new("NaN", 0).unwrap().is_nan());
883 }
884
885 #[test]
886 fn test_roundtrip() {
887 let scale = 4;
888 let values = ["0", "123.4567", "-99.9999", "1000000", "-1"];
889
890 for s in values {
891 let d = Decimal64NoScale::new(s, scale).unwrap();
892 let raw = d.value();
893 let restored = Decimal64NoScale::from_raw(raw);
894 assert_eq!(d.value(), restored.value(), "Roundtrip failed for {}", s);
895 }
896 }
897
898 #[test]
899 fn test_byte_roundtrip() {
900 let d = Decimal64NoScale::new("123.45", 2).unwrap();
901 let bytes = d.to_be_bytes();
902 let restored = Decimal64NoScale::from_be_bytes(bytes);
903 assert_eq!(d, restored);
904 }
905
906 #[test]
907 fn test_zero() {
908 let d = Decimal64NoScale::new("0", 0).unwrap();
909 assert!(d.is_zero());
910 assert!(!d.is_negative());
911 assert!(!d.is_positive());
912 assert!(d.is_finite());
913 }
914
915 #[test]
916 fn test_negative_scale() {
917 let d = Decimal64NoScale::new("12345", -2).unwrap();
919 assert_eq!(d.to_string_with_scale(-2), "12300");
920
921 let d = Decimal64NoScale::from_raw(123);
923 assert_eq!(d.to_string_with_scale(-1), "1230"); assert_eq!(d.to_string_with_scale(-2), "12300"); let d = Decimal64NoScale::from_raw(12345);
928 assert_eq!(d.to_string_with_scale(0), "12345");
929
930 let zero = Decimal64NoScale::from_raw(0);
932 assert_eq!(zero.to_string_with_scale(0), "0");
933 assert_eq!(zero.to_string_with_scale(-2), "0");
934 }
935
936 #[test]
937 fn test_max_precision() {
938 let max = Decimal64NoScale::max_value();
940 assert!(max.is_finite());
941 assert!(max.value() > 0);
942
943 let min = Decimal64NoScale::min_value();
945 assert!(min.is_finite());
946 assert!(min.value() < 0);
947 }
948
949 #[test]
950 fn test_from_i64() {
951 let d = Decimal64NoScale::from_i64(123, 2).unwrap();
953 assert_eq!(d.value(), 12300);
954 assert_eq!(d.to_string_with_scale(2), "123.00");
955
956 let d = Decimal64NoScale::from_i64(123, 0).unwrap();
958 assert_eq!(d.value(), 123);
959
960 let d = Decimal64NoScale::from_i64(-50, 2).unwrap();
962 assert_eq!(d.value(), -5000);
963
964 let d = Decimal64NoScale::from_i64(12345, -2).unwrap();
966 assert_eq!(d.value(), 123);
967 }
968
969 #[test]
970 fn test_from_u64() {
971 let d = Decimal64NoScale::from_u64(123, 2).unwrap();
972 assert_eq!(d.value(), 12300);
973
974 assert!(Decimal64NoScale::from_u64(u64::MAX, 0).is_err());
976 }
977
978 #[test]
979 fn test_from_f64() {
980 let d = Decimal64NoScale::from_f64(123.45, 2).unwrap();
982 assert_eq!(d.value(), 12345);
983 assert_eq!(d.to_string_with_scale(2), "123.45");
984
985 assert!(Decimal64NoScale::from_f64(f64::NAN, 2).unwrap().is_nan());
987 assert!(Decimal64NoScale::from_f64(f64::INFINITY, 2)
988 .unwrap()
989 .is_pos_infinity());
990 assert!(Decimal64NoScale::from_f64(f64::NEG_INFINITY, 2)
991 .unwrap()
992 .is_neg_infinity());
993
994 let d = Decimal64NoScale::from_f64(-99.99, 2).unwrap();
996 assert_eq!(d.value(), -9999);
997 }
998}