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 {
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 {
685 self.value.cmp(&other.value)
686 }
687}
688
689impl fmt::Debug for Decimal64NoScale {
690 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
691 if self.is_nan() {
692 write!(f, "Decimal64NoScale(NaN)")
693 } else if self.is_pos_infinity() {
694 write!(f, "Decimal64NoScale(Infinity)")
695 } else if self.is_neg_infinity() {
696 write!(f, "Decimal64NoScale(-Infinity)")
697 } else {
698 f.debug_struct("Decimal64NoScale")
699 .field("value", &self.value)
700 .finish()
701 }
702 }
703}
704
705impl fmt::Display for Decimal64NoScale {
706 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
710 if self.is_nan() {
711 write!(f, "NaN")
712 } else if self.is_pos_infinity() {
713 write!(f, "Infinity")
714 } else if self.is_neg_infinity() {
715 write!(f, "-Infinity")
716 } else {
717 write!(f, "{}", self.value)
718 }
719 }
720}
721
722impl From<i64> for Decimal64NoScale {
723 fn from(value: i64) -> Self {
724 Self { value }
725 }
726}
727
728impl From<i32> for Decimal64NoScale {
729 fn from(value: i32) -> Self {
730 Self {
731 value: value as i64,
732 }
733 }
734}
735
736impl Serialize for Decimal64NoScale {
738 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
739 where
740 S: Serializer,
741 {
742 serializer.serialize_i64(self.value)
743 }
744}
745
746impl<'de> Deserialize<'de> for Decimal64NoScale {
747 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
748 where
749 D: Deserializer<'de>,
750 {
751 let value = i64::deserialize(deserializer)?;
752 Ok(Self::from_raw(value))
753 }
754}
755
756#[cfg(test)]
757mod tests {
758 use super::*;
759
760 #[test]
761 fn test_new_basic() {
762 let d = Decimal64NoScale::new("123.45", 2).unwrap();
763 assert_eq!(d.value(), 12345);
764 assert_eq!(d.to_string_with_scale(2), "123.45");
765
766 let d = Decimal64NoScale::new("100", 0).unwrap();
767 assert_eq!(d.value(), 100);
768 assert_eq!(d.to_string_with_scale(0), "100");
769
770 let d = Decimal64NoScale::new("-50.5", 1).unwrap();
771 assert_eq!(d.value(), -505);
772 assert_eq!(d.to_string_with_scale(1), "-50.5");
773 }
774
775 #[test]
776 fn test_18_digit_precision() {
777 let d = Decimal64NoScale::new("123456789012345678", 0).unwrap();
779 assert_eq!(d.value(), 123456789012345678);
780 assert_eq!(d.to_string_with_scale(0), "123456789012345678");
781
782 let d = Decimal64NoScale::new("1234567890123456.78", 2).unwrap();
784 assert_eq!(d.value(), 123456789012345678);
785 assert_eq!(d.to_string_with_scale(2), "1234567890123456.78");
786 }
787
788 #[test]
789 fn test_aggregates_work() {
790 let scale = 2;
792 let a = Decimal64NoScale::new("100.50", scale).unwrap();
793 let b = Decimal64NoScale::new("200.25", scale).unwrap();
794 let c = Decimal64NoScale::new("300.75", scale).unwrap();
795
796 let sum = a.value() + b.value() + c.value();
798 assert_eq!(sum, 60150); let result = Decimal64NoScale::from_raw(sum);
802 assert_eq!(result.to_string_with_scale(scale), "601.5");
803
804 let values = [a.value(), b.value(), c.value()];
806 let min = *values.iter().min().unwrap();
807 let max = *values.iter().max().unwrap();
808 assert_eq!(
809 Decimal64NoScale::from_raw(min).to_string_with_scale(scale),
810 "100.5"
811 );
812 assert_eq!(
813 Decimal64NoScale::from_raw(max).to_string_with_scale(scale),
814 "300.75"
815 );
816 }
817
818 #[test]
819 fn test_special_values() {
820 let nan = Decimal64NoScale::nan();
821 assert!(nan.is_nan());
822 assert!(nan.is_special());
823 assert_eq!(nan.to_string_with_scale(2), "NaN");
824
825 let inf = Decimal64NoScale::infinity();
826 assert!(inf.is_pos_infinity());
827 assert_eq!(inf.to_string_with_scale(2), "Infinity");
828
829 let neg_inf = Decimal64NoScale::neg_infinity();
830 assert!(neg_inf.is_neg_infinity());
831 assert_eq!(neg_inf.to_string_with_scale(2), "-Infinity");
832 }
833
834 #[test]
835 fn test_ordering() {
836 let neg_inf = Decimal64NoScale::neg_infinity();
837 let neg = Decimal64NoScale::from_raw(-1000);
838 let zero = Decimal64NoScale::from_raw(0);
839 let pos = Decimal64NoScale::from_raw(1000);
840 let inf = Decimal64NoScale::infinity();
841 let nan = Decimal64NoScale::nan();
842
843 assert!(neg_inf < neg);
844 assert!(neg < zero);
845 assert!(zero < pos);
846 assert!(pos < inf);
847 assert!(inf < nan);
848 }
849
850 #[test]
851 fn test_from_str_special() {
852 assert!(Decimal64NoScale::new("Infinity", 0)
853 .unwrap()
854 .is_pos_infinity());
855 assert!(Decimal64NoScale::new("-Infinity", 0)
856 .unwrap()
857 .is_neg_infinity());
858 assert!(Decimal64NoScale::new("NaN", 0).unwrap().is_nan());
859 }
860
861 #[test]
862 fn test_roundtrip() {
863 let scale = 4;
864 let values = ["0", "123.4567", "-99.9999", "1000000", "-1"];
865
866 for s in values {
867 let d = Decimal64NoScale::new(s, scale).unwrap();
868 let raw = d.value();
869 let restored = Decimal64NoScale::from_raw(raw);
870 assert_eq!(d.value(), restored.value(), "Roundtrip failed for {}", s);
871 }
872 }
873
874 #[test]
875 fn test_byte_roundtrip() {
876 let d = Decimal64NoScale::new("123.45", 2).unwrap();
877 let bytes = d.to_be_bytes();
878 let restored = Decimal64NoScale::from_be_bytes(bytes);
879 assert_eq!(d, restored);
880 }
881
882 #[test]
883 fn test_zero() {
884 let d = Decimal64NoScale::new("0", 0).unwrap();
885 assert!(d.is_zero());
886 assert!(!d.is_negative());
887 assert!(!d.is_positive());
888 assert!(d.is_finite());
889 }
890
891 #[test]
892 fn test_negative_scale() {
893 let d = Decimal64NoScale::new("12345", -2).unwrap();
894 assert_eq!(d.to_string_with_scale(-2), "12300");
895 }
896
897 #[test]
898 fn test_max_precision() {
899 let max = Decimal64NoScale::max_value();
901 assert!(max.is_finite());
902 assert!(max.value() > 0);
903
904 let min = Decimal64NoScale::min_value();
906 assert!(min.is_finite());
907 assert!(min.value() < 0);
908 }
909
910 #[test]
911 fn test_from_i64() {
912 let d = Decimal64NoScale::from_i64(123, 2).unwrap();
914 assert_eq!(d.value(), 12300);
915 assert_eq!(d.to_string_with_scale(2), "123");
916
917 let d = Decimal64NoScale::from_i64(123, 0).unwrap();
919 assert_eq!(d.value(), 123);
920
921 let d = Decimal64NoScale::from_i64(-50, 2).unwrap();
923 assert_eq!(d.value(), -5000);
924
925 let d = Decimal64NoScale::from_i64(12345, -2).unwrap();
927 assert_eq!(d.value(), 123);
928 }
929
930 #[test]
931 fn test_from_u64() {
932 let d = Decimal64NoScale::from_u64(123, 2).unwrap();
933 assert_eq!(d.value(), 12300);
934
935 assert!(Decimal64NoScale::from_u64(u64::MAX, 0).is_err());
937 }
938
939 #[test]
940 fn test_from_f64() {
941 let d = Decimal64NoScale::from_f64(123.45, 2).unwrap();
943 assert_eq!(d.value(), 12345);
944 assert_eq!(d.to_string_with_scale(2), "123.45");
945
946 assert!(Decimal64NoScale::from_f64(f64::NAN, 2).unwrap().is_nan());
948 assert!(Decimal64NoScale::from_f64(f64::INFINITY, 2)
949 .unwrap()
950 .is_pos_infinity());
951 assert!(Decimal64NoScale::from_f64(f64::NEG_INFINITY, 2)
952 .unwrap()
953 .is_neg_infinity());
954
955 let d = Decimal64NoScale::from_f64(-99.99, 2).unwrap();
957 assert_eq!(d.value(), -9999);
958 }
959}