1use thiserror::Error;
29
30pub(crate) const SIGN_NEGATIVE: u8 = 0x00;
32pub(crate) const SIGN_ZERO: u8 = 0x80;
33pub(crate) const SIGN_POSITIVE: u8 = 0xFF;
34
35pub const ENCODING_NEG_INFINITY: [u8; 3] = [0x00, 0x00, 0x00];
38pub const ENCODING_POS_INFINITY: [u8; 3] = [0xFF, 0xFF, 0xFE];
40pub const ENCODING_NAN: [u8; 3] = [0xFF, 0xFF, 0xFF];
42
43const RESERVED_NEG_INFINITY_EXP: u16 = 0x0000; const RESERVED_POS_INFINITY_EXP: u16 = 0xFFFE; const RESERVED_NAN_EXP: u16 = 0xFFFF; const EXPONENT_BIAS: i32 = 16384;
50const MAX_EXPONENT: i32 = 32767 - EXPONENT_BIAS - 2; const MIN_EXPONENT: i32 = -EXPONENT_BIAS + 1; #[derive(Error, Debug, Clone, PartialEq)]
55pub enum DecimalError {
56 #[error("Invalid format: {0}")]
58 InvalidFormat(String),
59
60 #[error("Precision overflow: exponent out of range")]
62 PrecisionOverflow,
63
64 #[error("Invalid encoding")]
66 InvalidEncoding,
67}
68
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71pub enum SpecialValue {
72 Infinity,
74 NegInfinity,
76 NaN,
78}
79
80pub fn encode_decimal(value: &str) -> Result<Vec<u8>, DecimalError> {
82 if let Some(special) = parse_special_value(value) {
84 return Ok(encode_special_value(special));
85 }
86
87 let (is_negative, digits, exponent) = parse_decimal(value)?;
88
89 if digits.is_empty() {
91 return Ok(vec![SIGN_ZERO]);
92 }
93
94 let mut result = Vec::with_capacity(1 + 2 + digits.len().div_ceil(2));
95
96 result.push(if is_negative {
98 SIGN_NEGATIVE
99 } else {
100 SIGN_POSITIVE
101 });
102
103 encode_exponent(&mut result, exponent, is_negative);
105
106 encode_mantissa(&mut result, &digits, is_negative);
108
109 Ok(result)
110}
111
112fn parse_special_value(value: &str) -> Option<SpecialValue> {
114 let trimmed = value.trim();
115 let lower = trimmed.to_lowercase();
116
117 match lower.as_str() {
118 "infinity" | "inf" | "+infinity" | "+inf" => Some(SpecialValue::Infinity),
119 "-infinity" | "-inf" => Some(SpecialValue::NegInfinity),
120 "nan" | "-nan" | "+nan" => Some(SpecialValue::NaN), _ => None,
122 }
123}
124
125pub fn encode_special_value(special: SpecialValue) -> Vec<u8> {
127 match special {
128 SpecialValue::NegInfinity => ENCODING_NEG_INFINITY.to_vec(),
129 SpecialValue::Infinity => ENCODING_POS_INFINITY.to_vec(),
130 SpecialValue::NaN => ENCODING_NAN.to_vec(),
131 }
132}
133
134pub fn decode_special_value(bytes: &[u8]) -> Option<SpecialValue> {
136 if bytes.len() == 3 {
137 if bytes == ENCODING_NEG_INFINITY {
138 return Some(SpecialValue::NegInfinity);
139 }
140 if bytes == ENCODING_POS_INFINITY {
141 return Some(SpecialValue::Infinity);
142 }
143 if bytes == ENCODING_NAN {
144 return Some(SpecialValue::NaN);
145 }
146 }
147 None
148}
149
150pub fn encode_decimal_with_constraints(
162 value: &str,
163 precision: Option<u32>,
164 scale: Option<i32>,
165) -> Result<Vec<u8>, DecimalError> {
166 if parse_special_value(value).is_some() {
168 return encode_decimal(value);
169 }
170
171 let truncated = truncate_decimal(value, precision, scale)?;
172 encode_decimal(&truncated)
173}
174
175pub fn decode_to_string(bytes: &[u8]) -> Result<String, DecimalError> {
177 if bytes.is_empty() {
178 return Err(DecimalError::InvalidEncoding);
179 }
180
181 if let Some(special) = decode_special_value(bytes) {
183 return Ok(match special {
184 SpecialValue::NegInfinity => "-Infinity".to_string(),
185 SpecialValue::Infinity => "Infinity".to_string(),
186 SpecialValue::NaN => "NaN".to_string(),
187 });
188 }
189
190 let sign_byte = bytes[0];
191
192 if sign_byte == SIGN_ZERO {
194 return Ok("0".to_string());
195 }
196
197 let is_negative = sign_byte == SIGN_NEGATIVE;
198
199 if sign_byte != SIGN_NEGATIVE && sign_byte != SIGN_POSITIVE {
200 return Err(DecimalError::InvalidEncoding);
201 }
202
203 let (exponent, mantissa_start) = decode_exponent(&bytes[1..], is_negative)?;
205
206 let mantissa_bytes = &bytes[1 + mantissa_start..];
208 let digits = decode_mantissa(mantissa_bytes, is_negative)?;
209
210 format_decimal(is_negative, &digits, exponent)
212}
213
214pub fn decode_to_string_with_scale(bytes: &[u8], scale: i32) -> Result<String, DecimalError> {
229 let normalized = decode_to_string(bytes)?;
231
232 if normalized == "NaN" || normalized == "Infinity" || normalized == "-Infinity" {
234 return Ok(normalized);
235 }
236
237 if scale <= 0 {
239 return Ok(normalized);
240 }
241
242 let scale = scale as usize;
243
244 if let Some(dot_pos) = normalized.find('.') {
246 let current_decimals = normalized.len() - dot_pos - 1;
247 if current_decimals >= scale {
248 Ok(normalized)
250 } else {
251 let zeros_needed = scale - current_decimals;
253 Ok(format!("{}{}", normalized, "0".repeat(zeros_needed)))
254 }
255 } else {
256 Ok(format!("{}.{}", normalized, "0".repeat(scale)))
258 }
259}
260
261fn parse_decimal(value: &str) -> Result<(bool, Vec<u8>, i32), DecimalError> {
263 let value = value.trim();
264 let mut chars = value.chars().peekable();
265
266 let is_negative = if chars.peek() == Some(&'-') {
268 chars.next();
269 true
270 } else if chars.peek() == Some(&'+') {
271 chars.next();
272 false
273 } else {
274 false
275 };
276
277 let mut integer_part = String::new();
279 let mut fractional_part = String::new();
280 let mut seen_decimal = false;
281 let mut seen_exponent_marker = false;
282
283 while let Some(&c) = chars.peek() {
284 if c == '.' {
285 if seen_decimal {
286 return Err(DecimalError::InvalidFormat(
287 "Multiple decimal points".to_string(),
288 ));
289 }
290 seen_decimal = true;
291 chars.next();
292 } else if c.is_ascii_digit() {
293 if seen_decimal {
294 fractional_part.push(c);
295 } else {
296 integer_part.push(c);
297 }
298 chars.next();
299 } else if c == 'e' || c == 'E' {
300 seen_exponent_marker = true;
301 chars.next();
302 break;
303 } else {
304 return Err(DecimalError::InvalidFormat(format!(
305 "Invalid character: {}",
306 c
307 )));
308 }
309 }
310
311 let mut exp_offset: i32 = 0;
313 if seen_exponent_marker {
314 if chars.peek().is_none() {
315 return Err(DecimalError::InvalidFormat(
316 "Missing exponent after 'e'".to_string(),
317 ));
318 }
319 let exp_str: String = chars.collect();
320 exp_offset = exp_str
321 .parse()
322 .map_err(|_| DecimalError::InvalidFormat(format!("Invalid exponent: {}", exp_str)))?;
323 }
324
325 if integer_part.is_empty() && fractional_part.is_empty() {
327 return Ok((false, vec![], 0));
328 }
329
330 if integer_part.is_empty() {
332 integer_part.push('0');
333 }
334
335 let decimal_position = integer_part.len();
337
338 integer_part.push_str(&fractional_part);
340 let all_digits = integer_part;
341
342 let first_nonzero = all_digits.chars().position(|c| c != '0');
344 let last_nonzero = all_digits.chars().rev().position(|c| c != '0');
345
346 if first_nonzero.is_none() {
348 return Ok((false, vec![], 0));
349 }
350
351 let first_nonzero = first_nonzero.unwrap();
352 let last_nonzero = all_digits.len() - 1 - last_nonzero.unwrap();
353
354 let significant = &all_digits[first_nonzero..=last_nonzero];
356
357 let exponent = (decimal_position as i32) - (first_nonzero as i32) + exp_offset;
359
360 let digits: Vec<u8> = significant
362 .chars()
363 .map(|c| c.to_digit(10).unwrap() as u8)
364 .collect();
365
366 if !(MIN_EXPONENT..=MAX_EXPONENT).contains(&exponent) {
368 return Err(DecimalError::PrecisionOverflow);
369 }
370
371 Ok((is_negative, digits, exponent))
372}
373
374fn encode_exponent(result: &mut Vec<u8>, exponent: i32, is_negative: bool) {
376 let biased = (exponent + EXPONENT_BIAS) as u16;
379
380 let encoded = if is_negative { !biased } else { biased };
382
383 result.push((encoded >> 8) as u8);
385 result.push((encoded & 0xFF) as u8);
386}
387
388fn decode_exponent(bytes: &[u8], is_negative: bool) -> Result<(i32, usize), DecimalError> {
390 if bytes.len() < 2 {
391 return Err(DecimalError::InvalidEncoding);
392 }
393
394 let encoded = ((bytes[0] as u16) << 8) | (bytes[1] as u16);
395
396 if is_negative && encoded == RESERVED_NEG_INFINITY_EXP {
398 return Err(DecimalError::InvalidEncoding);
399 }
400 if !is_negative && (encoded == RESERVED_POS_INFINITY_EXP || encoded == RESERVED_NAN_EXP) {
401 return Err(DecimalError::InvalidEncoding);
402 }
403
404 let biased = if is_negative { !encoded } else { encoded };
405 let exponent = (biased as i32) - EXPONENT_BIAS;
406
407 Ok((exponent, 2))
408}
409
410fn encode_mantissa(result: &mut Vec<u8>, digits: &[u8], is_negative: bool) {
412 let mut i = 0;
414 while i < digits.len() {
415 let high = digits[i];
416 let low = if i + 1 < digits.len() {
417 digits[i + 1]
418 } else {
419 0 };
421
422 let byte = (high << 4) | low;
423
424 result.push(if is_negative { !byte } else { byte });
426
427 i += 2;
428 }
429}
430
431fn decode_mantissa(bytes: &[u8], is_negative: bool) -> Result<Vec<u8>, DecimalError> {
433 let mut digits = Vec::with_capacity(bytes.len() * 2);
434
435 for &byte in bytes {
436 let byte = if is_negative { !byte } else { byte };
437 let high = (byte >> 4) & 0x0F;
438 let low = byte & 0x0F;
439
440 if high > 9 || low > 9 {
441 return Err(DecimalError::InvalidEncoding);
442 }
443
444 digits.push(high);
445 digits.push(low);
446 }
447
448 while digits.last() == Some(&0) && digits.len() > 1 {
450 digits.pop();
451 }
452
453 Ok(digits)
454}
455
456fn format_decimal(is_negative: bool, digits: &[u8], exponent: i32) -> Result<String, DecimalError> {
458 if digits.is_empty() {
459 return Ok("0".to_string());
460 }
461
462 let mut result = String::new();
463
464 if is_negative {
465 result.push('-');
466 }
467
468 let num_digits = digits.len() as i32;
469
470 if exponent >= num_digits {
471 for d in digits {
473 result.push(char::from_digit(*d as u32, 10).unwrap());
474 }
475 for _ in 0..(exponent - num_digits) {
477 result.push('0');
478 }
479 } else if exponent <= 0 {
480 result.push('0');
482 result.push('.');
483 for _ in 0..(-exponent) {
484 result.push('0');
485 }
486 for d in digits {
487 result.push(char::from_digit(*d as u32, 10).unwrap());
488 }
489 } else {
490 let decimal_pos = exponent as usize;
492 for (i, d) in digits.iter().enumerate() {
493 if i == decimal_pos {
494 result.push('.');
495 }
496 result.push(char::from_digit(*d as u32, 10).unwrap());
497 }
498 }
499
500 Ok(result)
501}
502
503fn truncate_decimal(
510 value: &str,
511 precision: Option<u32>,
512 scale: Option<i32>,
513) -> Result<String, DecimalError> {
514 let value = value.trim();
516 let is_negative = value.starts_with('-');
517 let value = value.trim_start_matches(['-', '+']);
518
519 let (integer_part, fractional_part) = if let Some(dot_pos) = value.find('.') {
521 (&value[..dot_pos], &value[dot_pos + 1..])
522 } else {
523 (value, "")
524 };
525
526 let integer_part = integer_part.trim_start_matches('0');
528 let integer_part = if integer_part.is_empty() {
529 "0"
530 } else {
531 integer_part
532 };
533
534 let scale_val = scale.unwrap_or(0);
535
536 if scale_val < 0 {
538 let round_digits = (-scale_val) as usize;
539
540 let mut int_str = integer_part.to_string();
542
543 if int_str.len() <= round_digits {
544 let num_val: u64 = int_str.parse().unwrap_or(0);
547 let rounding_unit = 10u64.pow(round_digits as u32);
548 let half_unit = rounding_unit / 2;
549
550 let result = if num_val >= half_unit {
551 rounding_unit.to_string()
552 } else {
553 "0".to_string()
554 };
555
556 return if is_negative && result != "0" {
557 Ok(format!("-{}", result))
558 } else {
559 Ok(result)
560 };
561 }
562
563 let keep_len = int_str.len() - round_digits;
565 let keep_part = &int_str[..keep_len];
566 let round_part = &int_str[keep_len..];
567
568 let first_rounded_digit = round_part.chars().next().unwrap_or('0');
570 let mut result_int = keep_part.to_string();
571
572 if first_rounded_digit >= '5' {
573 result_int = add_one_to_integer(&result_int);
574 }
575
576 int_str = format!("{}{}", result_int, "0".repeat(round_digits));
578
579 if let Some(p) = precision {
581 let max_significant = p as usize;
582 let significant_len = result_int.trim_start_matches('0').len();
583 if significant_len > max_significant && max_significant > 0 {
584 let trimmed = &result_int[result_int.len().saturating_sub(max_significant)..];
586 int_str = format!("{}{}", trimmed, "0".repeat(round_digits));
587 }
588 }
589
590 return if is_negative && int_str != "0" {
591 Ok(format!("-{}", int_str))
592 } else {
593 Ok(int_str)
594 };
595 }
596
597 let scale_usize = scale_val as usize;
599
600 let (mut integer_part, fractional_part) = if fractional_part.len() > scale_usize {
602 let truncated = &fractional_part[..scale_usize];
604 let next_digit = fractional_part.chars().nth(scale_usize).unwrap_or('0');
605
606 if next_digit >= '5' {
607 if scale_usize == 0 {
609 (add_one_to_integer(integer_part), String::new())
611 } else {
612 let rounded = round_up(truncated);
613 if rounded.len() > scale_usize {
614 let new_int = add_one_to_integer(integer_part);
616 (new_int, "0".repeat(scale_usize))
617 } else {
618 (integer_part.to_string(), rounded)
619 }
620 }
621 } else {
622 (integer_part.to_string(), truncated.to_string())
623 }
624 } else {
625 (integer_part.to_string(), fractional_part.to_string())
626 };
627
628 if let Some(p) = precision {
630 let max_integer_digits = if (p as i32) > scale_val {
631 (p as i32 - scale_val) as usize
632 } else {
633 0
634 };
635
636 if integer_part.len() > max_integer_digits && max_integer_digits > 0 {
637 integer_part = integer_part[integer_part.len() - max_integer_digits..].to_string();
639 } else if max_integer_digits == 0 {
640 integer_part = "0".to_string();
641 }
642 }
643
644 let result = if fractional_part.is_empty() || fractional_part.chars().all(|c| c == '0') {
646 integer_part
647 } else {
648 format!("{}.{}", integer_part, fractional_part.trim_end_matches('0'))
649 };
650
651 if is_negative && result != "0" {
652 Ok(format!("-{}", result))
653 } else {
654 Ok(result)
655 }
656}
657
658fn add_one_to_integer(s: &str) -> String {
660 let mut chars: Vec<char> = s.chars().collect();
661 let mut carry = true;
662
663 for c in chars.iter_mut().rev() {
664 if carry {
665 if *c == '9' {
666 *c = '0';
667 } else {
668 *c = char::from_digit(c.to_digit(10).unwrap() + 1, 10).unwrap();
669 carry = false;
670 }
671 }
672 }
673
674 if carry {
675 format!("1{}", chars.iter().collect::<String>())
676 } else {
677 chars.iter().collect()
678 }
679}
680
681fn round_up(s: &str) -> String {
683 let mut chars: Vec<char> = s.chars().collect();
684 let mut carry = true;
685
686 for c in chars.iter_mut().rev() {
687 if carry {
688 if *c == '9' {
689 *c = '0';
690 } else {
691 *c = char::from_digit(c.to_digit(10).unwrap() + 1, 10).unwrap();
692 carry = false;
693 }
694 }
695 }
696
697 if carry {
698 format!("1{}", chars.iter().collect::<String>())
700 } else {
701 chars.iter().collect()
702 }
703}
704
705#[cfg(test)]
706mod tests {
707 use super::*;
708
709 #[test]
710 fn test_encode_decode_roundtrip() {
711 let values = vec![
712 "0",
713 "1",
714 "-1",
715 "123.456",
716 "-123.456",
717 "0.001",
718 "0.1",
719 "10",
720 "100",
721 "1000",
722 "-0.001",
723 "999999999999999999",
724 ];
725
726 for s in values {
727 let encoded = encode_decimal(s).unwrap();
728 let decoded = decode_to_string(&encoded).unwrap();
729 let re_encoded = encode_decimal(&decoded).unwrap();
731 assert_eq!(encoded, re_encoded, "Roundtrip failed for {}", s);
732 }
733 }
734
735 #[test]
736 fn test_lexicographic_ordering() {
737 let values = vec![
738 "-1000", "-100", "-10", "-1", "-0.1", "-0.01", "0", "0.01", "0.1", "1", "10", "100",
739 "1000",
740 ];
741
742 let encoded: Vec<Vec<u8>> = values.iter().map(|s| encode_decimal(s).unwrap()).collect();
743
744 for i in 0..encoded.len() - 1 {
746 assert!(
747 encoded[i] < encoded[i + 1],
748 "Ordering failed: {} should be < {}",
749 values[i],
750 values[i + 1]
751 );
752 }
753 }
754
755 #[test]
756 fn test_zero_encoding() {
757 let encoded = encode_decimal("0").unwrap();
758 assert_eq!(encoded, vec![SIGN_ZERO]);
759
760 let encoded = encode_decimal("0.0").unwrap();
761 assert_eq!(encoded, vec![SIGN_ZERO]);
762
763 let encoded = encode_decimal("-0").unwrap();
764 assert_eq!(encoded, vec![SIGN_ZERO]);
765 }
766
767 #[test]
768 fn test_truncate_scale() {
769 assert_eq!(
770 truncate_decimal("123.456", None, Some(2)).unwrap(),
771 "123.46"
772 );
773 assert_eq!(
774 truncate_decimal("123.454", None, Some(2)).unwrap(),
775 "123.45"
776 );
777 assert_eq!(truncate_decimal("123.995", None, Some(2)).unwrap(), "124");
778 assert_eq!(truncate_decimal("9.999", None, Some(2)).unwrap(), "10");
779 }
780
781 #[test]
782 fn test_storage_efficiency() {
783 let encoded = encode_decimal("123456789").unwrap();
785 assert!(
786 encoded.len() <= 8,
787 "Expected <= 8 bytes, got {}",
788 encoded.len()
789 );
790
791 let encoded = encode_decimal("0.1").unwrap();
793 assert!(
794 encoded.len() <= 4,
795 "Expected <= 4 bytes, got {}",
796 encoded.len()
797 );
798 }
799
800 #[test]
803 fn test_special_value_encoding() {
804 let pos_inf = encode_decimal("Infinity").unwrap();
806 assert_eq!(pos_inf, ENCODING_POS_INFINITY.to_vec());
807
808 let neg_inf = encode_decimal("-Infinity").unwrap();
809 assert_eq!(neg_inf, ENCODING_NEG_INFINITY.to_vec());
810
811 let nan = encode_decimal("NaN").unwrap();
812 assert_eq!(nan, ENCODING_NAN.to_vec());
813 }
814
815 #[test]
816 fn test_special_value_decoding() {
817 assert_eq!(
819 decode_to_string(&ENCODING_POS_INFINITY).unwrap(),
820 "Infinity"
821 );
822 assert_eq!(
823 decode_to_string(&ENCODING_NEG_INFINITY).unwrap(),
824 "-Infinity"
825 );
826 assert_eq!(decode_to_string(&ENCODING_NAN).unwrap(), "NaN");
827 }
828
829 #[test]
830 fn test_special_value_parsing_variants() {
831 let variants = vec![
833 ("infinity", "Infinity"),
834 ("Infinity", "Infinity"),
835 ("INFINITY", "Infinity"),
836 ("inf", "Infinity"),
837 ("Inf", "Infinity"),
838 ("+infinity", "Infinity"),
839 ("+inf", "Infinity"),
840 ("-infinity", "-Infinity"),
841 ("-inf", "-Infinity"),
842 ("-Infinity", "-Infinity"),
843 ("nan", "NaN"),
844 ("NaN", "NaN"),
845 ("NAN", "NaN"),
846 ("-nan", "NaN"), ("+nan", "NaN"),
848 ];
849
850 for (input, expected) in variants {
851 let encoded = encode_decimal(input).unwrap();
852 let decoded = decode_to_string(&encoded).unwrap();
853 assert_eq!(decoded, expected, "Failed for input: {}", input);
854 }
855 }
856
857 #[test]
858 fn test_special_value_ordering() {
859 let values = vec![
861 "-Infinity",
862 "-1000000",
863 "-1",
864 "-0.001",
865 "0",
866 "0.001",
867 "1",
868 "1000000",
869 "Infinity",
870 "NaN",
871 ];
872
873 let encoded: Vec<Vec<u8>> = values.iter().map(|s| encode_decimal(s).unwrap()).collect();
874
875 for i in 0..encoded.len() - 1 {
877 assert!(
878 encoded[i] < encoded[i + 1],
879 "Special value ordering failed: {} should be < {} (bytes: {:?} < {:?})",
880 values[i],
881 values[i + 1],
882 encoded[i],
883 encoded[i + 1]
884 );
885 }
886 }
887
888 #[test]
889 fn test_special_value_roundtrip() {
890 let values = vec!["Infinity", "-Infinity", "NaN"];
891
892 for s in values {
893 let encoded = encode_decimal(s).unwrap();
894 let decoded = decode_to_string(&encoded).unwrap();
895 let re_encoded = encode_decimal(&decoded).unwrap();
896 assert_eq!(
897 encoded, re_encoded,
898 "Special value roundtrip failed for {}",
899 s
900 );
901 }
902 }
903
904 #[test]
905 fn test_decode_special_value_helper() {
906 assert_eq!(
907 decode_special_value(&ENCODING_POS_INFINITY),
908 Some(SpecialValue::Infinity)
909 );
910 assert_eq!(
911 decode_special_value(&ENCODING_NEG_INFINITY),
912 Some(SpecialValue::NegInfinity)
913 );
914 assert_eq!(decode_special_value(&ENCODING_NAN), Some(SpecialValue::NaN));
915
916 let regular = encode_decimal("123.456").unwrap();
918 assert_eq!(decode_special_value(®ular), None);
919
920 let zero = encode_decimal("0").unwrap();
921 assert_eq!(decode_special_value(&zero), None);
922 }
923
924 #[test]
927 fn test_negative_scale_basic() {
928 assert_eq!(truncate_decimal("123", None, Some(-1)).unwrap(), "120");
930 assert_eq!(truncate_decimal("125", None, Some(-1)).unwrap(), "130");
931 assert_eq!(truncate_decimal("124", None, Some(-1)).unwrap(), "120");
932
933 assert_eq!(truncate_decimal("1234", None, Some(-2)).unwrap(), "1200");
935 assert_eq!(truncate_decimal("1250", None, Some(-2)).unwrap(), "1300");
936 assert_eq!(truncate_decimal("1249", None, Some(-2)).unwrap(), "1200");
937
938 assert_eq!(truncate_decimal("12345", None, Some(-3)).unwrap(), "12000");
940 assert_eq!(truncate_decimal("12500", None, Some(-3)).unwrap(), "13000");
941 }
942
943 #[test]
944 fn test_negative_scale_small_numbers() {
945 assert_eq!(truncate_decimal("499", None, Some(-3)).unwrap(), "0");
947 assert_eq!(truncate_decimal("500", None, Some(-3)).unwrap(), "1000");
948 assert_eq!(truncate_decimal("999", None, Some(-3)).unwrap(), "1000");
949
950 assert_eq!(truncate_decimal("49", None, Some(-2)).unwrap(), "0");
951 assert_eq!(truncate_decimal("50", None, Some(-2)).unwrap(), "100");
952 }
953
954 #[test]
955 fn test_negative_scale_with_precision() {
956 assert_eq!(
958 truncate_decimal("12345", Some(2), Some(-3)).unwrap(),
959 "12000"
960 );
961 assert_eq!(
966 truncate_decimal("99999", Some(2), Some(-3)).unwrap(),
967 "00000"
968 );
969 }
970
971 #[test]
972 fn test_negative_scale_negative_numbers() {
973 assert_eq!(truncate_decimal("-123", None, Some(-1)).unwrap(), "-120");
974 assert_eq!(truncate_decimal("-125", None, Some(-1)).unwrap(), "-130");
975 assert_eq!(truncate_decimal("-1234", None, Some(-2)).unwrap(), "-1200");
976 }
977
978 #[test]
979 fn test_negative_scale_with_decimal_input() {
980 assert_eq!(truncate_decimal("123.456", None, Some(-1)).unwrap(), "120");
982 assert_eq!(
983 truncate_decimal("1234.999", None, Some(-2)).unwrap(),
984 "1200"
985 );
986 }
987
988 #[test]
989 fn test_negative_scale_encoding_ordering() {
990 let values = vec!["-1000", "-100", "0", "100", "1000"];
992
993 let encoded: Vec<Vec<u8>> = values
994 .iter()
995 .map(|s| encode_decimal_with_constraints(s, None, Some(-2)).unwrap())
996 .collect();
997
998 for i in 0..encoded.len() - 1 {
999 assert!(
1000 encoded[i] < encoded[i + 1],
1001 "Negative scale ordering failed: {} should be < {}",
1002 values[i],
1003 values[i + 1]
1004 );
1005 }
1006 }
1007
1008 #[test]
1009 fn test_special_values_ignore_precision_scale() {
1010 let inf = encode_decimal_with_constraints("Infinity", Some(5), Some(2)).unwrap();
1012 assert_eq!(inf, ENCODING_POS_INFINITY.to_vec());
1013
1014 let neg_inf = encode_decimal_with_constraints("-Infinity", Some(5), Some(2)).unwrap();
1015 assert_eq!(neg_inf, ENCODING_NEG_INFINITY.to_vec());
1016
1017 let nan = encode_decimal_with_constraints("NaN", Some(5), Some(2)).unwrap();
1018 assert_eq!(nan, ENCODING_NAN.to_vec());
1019 }
1020}