decimal_bytes/
lib.rs

1//! # decimal-bytes
2//!
3//! Arbitrary precision decimals with lexicographically sortable byte encoding.
4//!
5//! This crate provides a `Decimal` type that stores decimal numbers as bytes
6//! in a format that preserves numerical ordering when compared lexicographically.
7//! This makes it ideal for use in databases and search engines where efficient
8//! range queries on decimal values are needed.
9//!
10//! ## Features
11//!
12//! - **Bytes-first storage**: The primary representation is a compact byte array
13//! - **Lexicographic ordering**: Byte comparison matches numerical comparison
14//! - **Arbitrary precision**: Supports up to 131,072 digits before and 16,383 after decimal
15//! - **PostgreSQL NUMERIC compatibility**: Full support for precision, scale, and special values
16//! - **Special values**: Infinity, -Infinity, and NaN with correct PostgreSQL sort order
17//!
18//! ## Example
19//!
20//! ```
21//! use decimal_bytes::Decimal;
22//! use std::str::FromStr;
23//!
24//! // Create decimals
25//! let a = Decimal::from_str("123.456").unwrap();
26//! let b = Decimal::from_str("123.457").unwrap();
27//!
28//! // Byte comparison matches numerical comparison
29//! assert!(a.as_bytes() < b.as_bytes());
30//! assert!(a < b);
31//!
32//! // Display the value
33//! assert_eq!(a.to_string(), "123.456");
34//!
35//! // Special values (PostgreSQL compatible)
36//! let inf = Decimal::infinity();
37//! let nan = Decimal::nan();
38//! assert!(a < inf);
39//! assert!(inf < nan);
40//! ```
41//!
42//! ## Sort Order
43//!
44//! The lexicographic byte order matches PostgreSQL NUMERIC:
45//!
46//! ```text
47//! -Infinity < negative numbers < zero < positive numbers < +Infinity < NaN
48//! ```
49//!
50//! ## Special Value Semantics (PostgreSQL vs IEEE 754)
51//!
52//! This library follows **PostgreSQL semantics** for special values, which differ
53//! from IEEE 754 floating-point:
54//!
55//! | Behavior | PostgreSQL / decimal-bytes | IEEE 754 float |
56//! |----------|---------------------------|----------------|
57//! | `NaN == NaN` | `true` | `false` |
58//! | `NaN` ordering | Greatest value (> Infinity) | Unordered |
59//! | `Infinity == Infinity` | `true` | `true` |
60//!
61//! ```
62//! use decimal_bytes::Decimal;
63//!
64//! let nan1 = Decimal::nan();
65//! let nan2 = Decimal::nan();
66//! let inf = Decimal::infinity();
67//!
68//! // NaN equals itself (PostgreSQL behavior, unlike IEEE 754)
69//! assert_eq!(nan1, nan2);
70//!
71//! // NaN is greater than everything, including Infinity
72//! assert!(nan1 > inf);
73//! ```
74//!
75//! This makes `Decimal` suitable for use in indexes, sorting, and deduplication
76//! where consistent ordering and equality semantics are required.
77
78mod encoding;
79
80use std::cmp::Ordering;
81use std::fmt;
82use std::hash::{Hash, Hasher};
83use std::str::FromStr;
84
85use serde::{Deserialize, Deserializer, Serialize, Serializer};
86
87pub use encoding::DecimalError;
88pub use encoding::SpecialValue;
89use encoding::{
90    decode_special_value, decode_to_string, encode_decimal, encode_decimal_with_constraints,
91    encode_special_value, ENCODING_NAN, ENCODING_NEG_INFINITY, ENCODING_POS_INFINITY,
92};
93
94/// An arbitrary precision decimal number stored as sortable bytes.
95///
96/// The internal byte representation is designed to be lexicographically sortable,
97/// meaning that comparing the bytes directly yields the same result as comparing
98/// the numerical values. This enables efficient range queries in databases.
99///
100/// # Storage Efficiency
101///
102/// The encoding uses:
103/// - 1 byte for the sign
104/// - Variable bytes for the exponent (typically 1-3 bytes)
105/// - 4 bits per decimal digit (BCD encoding, 2 digits per byte)
106#[derive(Clone)]
107pub struct Decimal {
108    bytes: Vec<u8>,
109}
110
111impl Decimal {
112    /// Creates a new Decimal with precision and scale constraints.
113    ///
114    /// Values that exceed the constraints are truncated/rounded to fit.
115    /// This is compatible with SQL NUMERIC(precision, scale) semantics.
116    ///
117    /// - `precision`: Maximum total number of significant digits (None = unlimited)
118    /// - `scale`: Digits after decimal point; negative values round to left of decimal
119    ///
120    /// # PostgreSQL Compatibility
121    ///
122    /// Supports negative scale (rounds to powers of 10):
123    /// - `scale = -3` rounds to nearest 1000
124    /// - `NUMERIC(2, -3)` allows values like -99000 to 99000
125    ///
126    /// # Examples
127    ///
128    /// ```
129    /// use decimal_bytes::Decimal;
130    ///
131    /// // NUMERIC(5, 2) - up to 5 digits total, 2 after decimal
132    /// let d = Decimal::with_precision_scale("123.456", Some(5), Some(2)).unwrap();
133    /// assert_eq!(d.to_string(), "123.46"); // Rounded to 2 decimal places
134    ///
135    /// // NUMERIC(2, -3) - rounds to nearest 1000, max 2 significant digits
136    /// let d = Decimal::with_precision_scale("12345", Some(2), Some(-3)).unwrap();
137    /// assert_eq!(d.to_string(), "12000"); // Rounded to nearest 1000
138    /// ```
139    pub fn with_precision_scale(
140        s: &str,
141        precision: Option<u32>,
142        scale: Option<i32>,
143    ) -> Result<Self, DecimalError> {
144        let bytes = encode_decimal_with_constraints(s, precision, scale)?;
145        Ok(Self { bytes })
146    }
147
148    /// Creates a Decimal from raw bytes.
149    ///
150    /// The bytes must be a valid encoding produced by `as_bytes()`.
151    /// Returns an error if the bytes are invalid.
152    ///
153    /// # Examples
154    ///
155    /// ```
156    /// use decimal_bytes::Decimal;
157    /// use std::str::FromStr;
158    ///
159    /// let original = Decimal::from_str("123.456").unwrap();
160    /// let bytes = original.as_bytes();
161    /// let restored = Decimal::from_bytes(bytes).unwrap();
162    /// assert_eq!(original, restored);
163    /// ```
164    pub fn from_bytes(bytes: &[u8]) -> Result<Self, DecimalError> {
165        // Validate by attempting to decode
166        let _ = decode_to_string(bytes)?;
167        Ok(Self {
168            bytes: bytes.to_vec(),
169        })
170    }
171
172    /// Creates a Decimal from raw bytes without validation.
173    ///
174    /// # Safety
175    ///
176    /// The caller must ensure the bytes are a valid encoding.
177    /// Using invalid bytes may cause panics or incorrect results.
178    #[inline]
179    pub fn from_bytes_unchecked(bytes: Vec<u8>) -> Self {
180        Self { bytes }
181    }
182
183    /// Returns the raw byte representation.
184    ///
185    /// These bytes are lexicographically sortable - comparing them directly
186    /// yields the same result as comparing the numerical values.
187    #[inline]
188    pub fn as_bytes(&self) -> &[u8] {
189        &self.bytes
190    }
191
192    /// Consumes the Decimal and returns the underlying bytes.
193    #[inline]
194    pub fn into_bytes(self) -> Vec<u8> {
195        self.bytes
196    }
197
198    /// Returns true if this decimal represents zero.
199    pub fn is_zero(&self) -> bool {
200        self.bytes.len() == 1 && self.bytes[0] == encoding::SIGN_ZERO
201    }
202
203    /// Returns true if this decimal is negative.
204    pub fn is_negative(&self) -> bool {
205        !self.bytes.is_empty() && self.bytes[0] == encoding::SIGN_NEGATIVE
206    }
207
208    /// Returns true if this decimal is positive (and not zero).
209    pub fn is_positive(&self) -> bool {
210        !self.bytes.is_empty() && self.bytes[0] == encoding::SIGN_POSITIVE
211    }
212
213    /// Returns true if this decimal represents positive infinity.
214    pub fn is_pos_infinity(&self) -> bool {
215        self.bytes.as_slice() == ENCODING_POS_INFINITY
216    }
217
218    /// Returns true if this decimal represents negative infinity.
219    pub fn is_neg_infinity(&self) -> bool {
220        self.bytes.as_slice() == ENCODING_NEG_INFINITY
221    }
222
223    /// Returns true if this decimal represents positive or negative infinity.
224    pub fn is_infinity(&self) -> bool {
225        self.is_pos_infinity() || self.is_neg_infinity()
226    }
227
228    /// Returns true if this decimal represents NaN (Not a Number).
229    pub fn is_nan(&self) -> bool {
230        self.bytes.as_slice() == ENCODING_NAN
231    }
232
233    /// Returns true if this decimal is a special value (Infinity or NaN).
234    pub fn is_special(&self) -> bool {
235        decode_special_value(&self.bytes).is_some()
236    }
237
238    /// Returns true if this decimal is a finite number (not Infinity or NaN).
239    pub fn is_finite(&self) -> bool {
240        !self.is_special()
241    }
242
243    /// Returns the number of bytes used to store this decimal.
244    #[inline]
245    pub fn byte_len(&self) -> usize {
246        self.bytes.len()
247    }
248
249    /// Creates positive infinity.
250    ///
251    /// Infinity is greater than all finite numbers but less than NaN.
252    /// Two positive infinities are equal to each other.
253    ///
254    /// # Example
255    ///
256    /// ```
257    /// use decimal_bytes::Decimal;
258    /// use std::str::FromStr;
259    ///
260    /// let inf = Decimal::infinity();
261    /// let big = Decimal::from_str("999999999999").unwrap();
262    /// assert!(inf > big);
263    /// assert_eq!(inf, Decimal::infinity());
264    /// ```
265    pub fn infinity() -> Self {
266        Self {
267            bytes: encode_special_value(SpecialValue::Infinity),
268        }
269    }
270
271    /// Creates negative infinity.
272    ///
273    /// Negative infinity is less than all finite numbers and positive infinity.
274    /// Two negative infinities are equal to each other.
275    ///
276    /// # Example
277    ///
278    /// ```
279    /// use decimal_bytes::Decimal;
280    /// use std::str::FromStr;
281    ///
282    /// let neg_inf = Decimal::neg_infinity();
283    /// let small = Decimal::from_str("-999999999999").unwrap();
284    /// assert!(neg_inf < small);
285    /// assert_eq!(neg_inf, Decimal::neg_infinity());
286    /// ```
287    pub fn neg_infinity() -> Self {
288        Self {
289            bytes: encode_special_value(SpecialValue::NegInfinity),
290        }
291    }
292
293    /// Creates NaN (Not a Number).
294    ///
295    /// # PostgreSQL Semantics
296    ///
297    /// Unlike IEEE 754 floating-point where `NaN != NaN`, this follows PostgreSQL
298    /// semantics where:
299    /// - `NaN == NaN` is `true`
300    /// - `NaN` is the greatest value (greater than positive infinity)
301    /// - All NaN values are equal regardless of how they were created
302    ///
303    /// This makes NaN usable in sorting, indexing, and deduplication.
304    ///
305    /// # Example
306    ///
307    /// ```
308    /// use decimal_bytes::Decimal;
309    /// use std::str::FromStr;
310    ///
311    /// let nan1 = Decimal::nan();
312    /// let nan2 = Decimal::from_str("NaN").unwrap();
313    /// let inf = Decimal::infinity();
314    ///
315    /// // NaN equals itself (PostgreSQL behavior)
316    /// assert_eq!(nan1, nan2);
317    ///
318    /// // NaN is greater than everything
319    /// assert!(nan1 > inf);
320    /// ```
321    pub fn nan() -> Self {
322        Self {
323            bytes: encode_special_value(SpecialValue::NaN),
324        }
325    }
326}
327
328impl FromStr for Decimal {
329    type Err = DecimalError;
330
331    /// Creates a new Decimal from a string representation.
332    ///
333    /// # Examples
334    ///
335    /// ```
336    /// use decimal_bytes::Decimal;
337    /// use std::str::FromStr;
338    ///
339    /// let d = Decimal::from_str("123.456").unwrap();
340    /// let d = Decimal::from_str("-0.001").unwrap();
341    /// let d = Decimal::from_str("1e10").unwrap();
342    /// // Or use parse:
343    /// let d: Decimal = "42.5".parse().unwrap();
344    /// ```
345    fn from_str(s: &str) -> Result<Self, Self::Err> {
346        let bytes = encode_decimal(s)?;
347        Ok(Self { bytes })
348    }
349}
350
351impl fmt::Display for Decimal {
352    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
353        let s = decode_to_string(&self.bytes).expect("Decimal contains valid bytes");
354        write!(f, "{}", s)
355    }
356}
357
358impl fmt::Debug for Decimal {
359    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
360        let value = decode_to_string(&self.bytes).expect("Decimal contains valid bytes");
361        f.debug_struct("Decimal")
362            .field("value", &value)
363            .field("bytes", &self.bytes)
364            .finish()
365    }
366}
367
368impl PartialEq for Decimal {
369    fn eq(&self, other: &Self) -> bool {
370        self.bytes == other.bytes
371    }
372}
373
374impl Eq for Decimal {}
375
376impl PartialOrd for Decimal {
377    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
378        Some(self.cmp(other))
379    }
380}
381
382impl Ord for Decimal {
383    fn cmp(&self, other: &Self) -> Ordering {
384        // Byte comparison is equivalent to numerical comparison
385        self.bytes.cmp(&other.bytes)
386    }
387}
388
389impl Hash for Decimal {
390    fn hash<H: Hasher>(&self, state: &mut H) {
391        self.bytes.hash(state);
392    }
393}
394
395impl Serialize for Decimal {
396    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
397    where
398        S: Serializer,
399    {
400        // Serialize as string for human readability in JSON
401        serializer.serialize_str(&self.to_string())
402    }
403}
404
405impl<'de> Deserialize<'de> for Decimal {
406    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
407    where
408        D: Deserializer<'de>,
409    {
410        let s = String::deserialize(deserializer)?;
411        Decimal::from_str(&s).map_err(serde::de::Error::custom)
412    }
413}
414
415// Conversion from integer types
416macro_rules! impl_from_int {
417    ($($t:ty),*) => {
418        $(
419            impl From<$t> for Decimal {
420                fn from(val: $t) -> Self {
421                    Decimal::from_str(&val.to_string()).expect("Integer is always valid")
422                }
423            }
424        )*
425    };
426}
427
428impl_from_int!(i8, i16, i32, i64, i128, u8, u16, u32, u64, u128);
429
430impl Default for Decimal {
431    fn default() -> Self {
432        Decimal {
433            bytes: vec![encoding::SIGN_ZERO],
434        }
435    }
436}
437
438// ============================================================================
439// Optional: rust_decimal interop (enabled with "rust_decimal" feature)
440// ============================================================================
441
442#[cfg(feature = "rust_decimal")]
443mod rust_decimal_interop {
444    use super::{Decimal, DecimalError};
445    use std::str::FromStr;
446
447    impl TryFrom<rust_decimal::Decimal> for Decimal {
448        type Error = DecimalError;
449
450        /// Converts a `rust_decimal::Decimal` to a `decimal_bytes::Decimal`.
451        ///
452        /// # Example
453        ///
454        /// ```ignore
455        /// use rust_decimal::Decimal as RustDecimal;
456        /// use decimal_bytes::Decimal;
457        ///
458        /// let rd = RustDecimal::new(12345, 2); // 123.45
459        /// let d: Decimal = rd.try_into().unwrap();
460        /// assert_eq!(d.to_string(), "123.45");
461        /// ```
462        fn try_from(value: rust_decimal::Decimal) -> Result<Self, Self::Error> {
463            Decimal::from_str(&value.to_string())
464        }
465    }
466
467    impl TryFrom<&Decimal> for rust_decimal::Decimal {
468        type Error = rust_decimal::Error;
469
470        /// Converts a `decimal_bytes::Decimal` to a `rust_decimal::Decimal`.
471        ///
472        /// Note: This may fail if the decimal exceeds rust_decimal's precision limits
473        /// (28-29 significant digits) or if it's a special value (Infinity, NaN).
474        ///
475        /// # Example
476        ///
477        /// ```ignore
478        /// use rust_decimal::Decimal as RustDecimal;
479        /// use decimal_bytes::Decimal;
480        ///
481        /// let d = Decimal::from_str("123.45").unwrap();
482        /// let rd: RustDecimal = (&d).try_into().unwrap();
483        /// assert_eq!(rd.to_string(), "123.45");
484        /// ```
485        fn try_from(value: &Decimal) -> Result<Self, Self::Error> {
486            use std::str::FromStr;
487            rust_decimal::Decimal::from_str(&value.to_string())
488        }
489    }
490
491    impl TryFrom<Decimal> for rust_decimal::Decimal {
492        type Error = rust_decimal::Error;
493
494        fn try_from(value: Decimal) -> Result<Self, Self::Error> {
495            rust_decimal::Decimal::try_from(&value)
496        }
497    }
498}
499
500// The rust_decimal_interop module only contains trait implementations,
501// so there's nothing to re-export. The TryFrom impls are automatically available.
502
503// ============================================================================
504// Optional: bigdecimal interop (enabled with "bigdecimal" feature)
505// ============================================================================
506
507#[cfg(feature = "bigdecimal")]
508mod bigdecimal_interop {
509    use super::{Decimal, DecimalError};
510    use std::str::FromStr;
511
512    impl TryFrom<bigdecimal::BigDecimal> for Decimal {
513        type Error = DecimalError;
514
515        /// Converts a `bigdecimal::BigDecimal` to a `decimal_bytes::Decimal`.
516        fn try_from(value: bigdecimal::BigDecimal) -> Result<Self, Self::Error> {
517            Decimal::from_str(&value.to_string())
518        }
519    }
520
521    impl TryFrom<&bigdecimal::BigDecimal> for Decimal {
522        type Error = DecimalError;
523
524        fn try_from(value: &bigdecimal::BigDecimal) -> Result<Self, Self::Error> {
525            Decimal::from_str(&value.to_string())
526        }
527    }
528
529    impl TryFrom<&Decimal> for bigdecimal::BigDecimal {
530        type Error = bigdecimal::ParseBigDecimalError;
531
532        /// Converts a `decimal_bytes::Decimal` to a `bigdecimal::BigDecimal`.
533        ///
534        /// Note: This may fail for special values (Infinity, NaN) which
535        /// bigdecimal doesn't support.
536        fn try_from(value: &Decimal) -> Result<Self, Self::Error> {
537            use std::str::FromStr;
538            bigdecimal::BigDecimal::from_str(&value.to_string())
539        }
540    }
541
542    impl TryFrom<Decimal> for bigdecimal::BigDecimal {
543        type Error = bigdecimal::ParseBigDecimalError;
544
545        fn try_from(value: Decimal) -> Result<Self, Self::Error> {
546            bigdecimal::BigDecimal::try_from(&value)
547        }
548    }
549}
550
551// The bigdecimal_interop module only contains trait implementations,
552// so there's nothing to re-export. The TryFrom impls are automatically available.
553
554#[cfg(test)]
555mod tests {
556    use super::*;
557
558    #[test]
559    fn test_from_str() {
560        let d = Decimal::from_str("123.456").unwrap();
561        assert_eq!(d.to_string(), "123.456");
562    }
563
564    #[test]
565    fn test_zero() {
566        let d = Decimal::from_str("0").unwrap();
567        assert!(d.is_zero());
568        assert!(!d.is_negative());
569        assert!(!d.is_positive());
570        assert!(d.is_finite());
571        assert!(!d.is_special());
572    }
573
574    #[test]
575    fn test_negative() {
576        let d = Decimal::from_str("-123.456").unwrap();
577        assert!(d.is_negative());
578        assert!(!d.is_zero());
579        assert!(!d.is_positive());
580        assert!(d.is_finite());
581    }
582
583    #[test]
584    fn test_positive() {
585        let d = Decimal::from_str("123.456").unwrap();
586        assert!(d.is_positive());
587        assert!(!d.is_zero());
588        assert!(!d.is_negative());
589        assert!(d.is_finite());
590    }
591
592    #[test]
593    fn test_ordering() {
594        let values = vec!["-100", "-10", "-1", "-0.1", "0", "0.1", "1", "10", "100"];
595        let decimals: Vec<Decimal> = values
596            .iter()
597            .map(|s| Decimal::from_str(s).unwrap())
598            .collect();
599
600        // Check that ordering is correct
601        for i in 0..decimals.len() - 1 {
602            assert!(
603                decimals[i] < decimals[i + 1],
604                "{} should be < {}",
605                values[i],
606                values[i + 1]
607            );
608        }
609
610        // Check that byte ordering matches
611        for i in 0..decimals.len() - 1 {
612            assert!(
613                decimals[i].as_bytes() < decimals[i + 1].as_bytes(),
614                "bytes of {} should be < bytes of {}",
615                values[i],
616                values[i + 1]
617            );
618        }
619    }
620
621    #[test]
622    fn test_roundtrip() {
623        let values = vec![
624            "0", "1", "-1", "123.456", "-123.456", "0.001", "0.1", "10", "100", "1000000",
625            "-1000000",
626        ];
627
628        for s in values {
629            let d = Decimal::from_str(s).unwrap();
630            let bytes = d.as_bytes();
631            let restored = Decimal::from_bytes(bytes).unwrap();
632            assert_eq!(d, restored, "Roundtrip failed for {}", s);
633        }
634    }
635
636    #[test]
637    fn test_precision_scale() {
638        // Round to 2 decimal places
639        let d = Decimal::with_precision_scale("123.456", Some(10), Some(2)).unwrap();
640        assert_eq!(d.to_string(), "123.46");
641
642        // When precision is exceeded, least significant integer digits are kept
643        let d = Decimal::with_precision_scale("12345.67", Some(5), Some(2)).unwrap();
644        assert_eq!(d.to_string(), "345.67"); // 5 digits total, 2 after decimal = 3 integer digits max
645
646        // Rounding within precision limits
647        let d = Decimal::with_precision_scale("99.999", Some(5), Some(2)).unwrap();
648        assert_eq!(d.to_string(), "100"); // Rounds up, fits in precision
649    }
650
651    #[test]
652    fn test_from_integer() {
653        let d = Decimal::from(42i64);
654        assert_eq!(d.to_string(), "42");
655
656        let d = Decimal::from(-100i32);
657        assert_eq!(d.to_string(), "-100");
658    }
659
660    #[test]
661    fn test_serialization() {
662        let d = Decimal::from_str("123.456").unwrap();
663        let json = serde_json::to_string(&d).unwrap();
664        assert_eq!(json, "\"123.456\"");
665
666        let restored: Decimal = serde_json::from_str(&json).unwrap();
667        assert_eq!(d, restored);
668    }
669
670    #[test]
671    fn test_byte_efficiency() {
672        // Check that storage is reasonably efficient
673        let d = Decimal::from_str("123456789").unwrap();
674        // 1 byte sign + ~2 bytes exponent + ~5 bytes mantissa (9 digits / 2)
675        assert!(
676            d.byte_len() <= 10,
677            "Expected <= 10 bytes, got {}",
678            d.byte_len()
679        );
680
681        let d = Decimal::from_str("0.000001").unwrap();
682        // Should be compact for small numbers too
683        assert!(
684            d.byte_len() <= 6,
685            "Expected <= 6 bytes, got {}",
686            d.byte_len()
687        );
688    }
689
690    // ==================== Special Values Tests ====================
691
692    #[test]
693    fn test_infinity_creation() {
694        let pos_inf = Decimal::infinity();
695        assert!(pos_inf.is_pos_infinity());
696        assert!(pos_inf.is_infinity());
697        assert!(!pos_inf.is_neg_infinity());
698        assert!(!pos_inf.is_nan());
699        assert!(pos_inf.is_special());
700        assert!(!pos_inf.is_finite());
701        assert_eq!(pos_inf.to_string(), "Infinity");
702
703        let neg_inf = Decimal::neg_infinity();
704        assert!(neg_inf.is_neg_infinity());
705        assert!(neg_inf.is_infinity());
706        assert!(!neg_inf.is_pos_infinity());
707        assert!(!neg_inf.is_nan());
708        assert!(neg_inf.is_special());
709        assert!(!neg_inf.is_finite());
710        assert_eq!(neg_inf.to_string(), "-Infinity");
711    }
712
713    #[test]
714    fn test_nan_creation() {
715        let nan = Decimal::nan();
716        assert!(nan.is_nan());
717        assert!(nan.is_special());
718        assert!(!nan.is_finite());
719        assert!(!nan.is_infinity());
720        assert!(!nan.is_zero());
721        assert_eq!(nan.to_string(), "NaN");
722    }
723
724    #[test]
725    fn test_special_value_from_str() {
726        let pos_inf = Decimal::from_str("Infinity").unwrap();
727        assert!(pos_inf.is_pos_infinity());
728
729        let neg_inf = Decimal::from_str("-Infinity").unwrap();
730        assert!(neg_inf.is_neg_infinity());
731
732        let nan = Decimal::from_str("NaN").unwrap();
733        assert!(nan.is_nan());
734
735        // Case-insensitive
736        let inf = Decimal::from_str("infinity").unwrap();
737        assert!(inf.is_pos_infinity());
738
739        let inf = Decimal::from_str("INF").unwrap();
740        assert!(inf.is_pos_infinity());
741    }
742
743    #[test]
744    fn test_special_value_ordering() {
745        // PostgreSQL order: -Infinity < negatives < zero < positives < Infinity < NaN
746        let neg_inf = Decimal::neg_infinity();
747        let neg_num = Decimal::from_str("-1000").unwrap();
748        let zero = Decimal::from_str("0").unwrap();
749        let pos_num = Decimal::from_str("1000").unwrap();
750        let pos_inf = Decimal::infinity();
751        let nan = Decimal::nan();
752
753        assert!(neg_inf < neg_num);
754        assert!(neg_num < zero);
755        assert!(zero < pos_num);
756        assert!(pos_num < pos_inf);
757        assert!(pos_inf < nan);
758
759        // Verify byte ordering matches
760        assert!(neg_inf.as_bytes() < neg_num.as_bytes());
761        assert!(neg_num.as_bytes() < zero.as_bytes());
762        assert!(zero.as_bytes() < pos_num.as_bytes());
763        assert!(pos_num.as_bytes() < pos_inf.as_bytes());
764        assert!(pos_inf.as_bytes() < nan.as_bytes());
765    }
766
767    #[test]
768    fn test_special_value_equality() {
769        // All NaNs are equal (PostgreSQL semantics)
770        let nan1 = Decimal::from_str("NaN").unwrap();
771        let nan2 = Decimal::from_str("nan").unwrap();
772        let nan3 = Decimal::nan();
773        assert_eq!(nan1, nan2);
774        assert_eq!(nan2, nan3);
775
776        // Infinities are equal to themselves
777        let inf1 = Decimal::infinity();
778        let inf2 = Decimal::from_str("Infinity").unwrap();
779        assert_eq!(inf1, inf2);
780
781        let neg_inf1 = Decimal::neg_infinity();
782        let neg_inf2 = Decimal::from_str("-Infinity").unwrap();
783        assert_eq!(neg_inf1, neg_inf2);
784    }
785
786    #[test]
787    fn test_special_value_serialization() {
788        let inf = Decimal::infinity();
789        let json = serde_json::to_string(&inf).unwrap();
790        assert_eq!(json, "\"Infinity\"");
791        let restored: Decimal = serde_json::from_str(&json).unwrap();
792        assert_eq!(inf, restored);
793
794        let nan = Decimal::nan();
795        let json = serde_json::to_string(&nan).unwrap();
796        assert_eq!(json, "\"NaN\"");
797        let restored: Decimal = serde_json::from_str(&json).unwrap();
798        assert_eq!(nan, restored);
799    }
800
801    #[test]
802    fn test_special_value_byte_efficiency() {
803        // Special values should be compact (3 bytes each)
804        assert_eq!(Decimal::infinity().byte_len(), 3);
805        assert_eq!(Decimal::neg_infinity().byte_len(), 3);
806        assert_eq!(Decimal::nan().byte_len(), 3);
807    }
808
809    // ==================== Negative Scale Tests ====================
810
811    #[test]
812    fn test_negative_scale() {
813        // Round to nearest 1000
814        let d = Decimal::with_precision_scale("12345", Some(10), Some(-3)).unwrap();
815        assert_eq!(d.to_string(), "12000");
816
817        // Round up
818        let d = Decimal::with_precision_scale("12500", Some(10), Some(-3)).unwrap();
819        assert_eq!(d.to_string(), "13000");
820
821        // Round to nearest 100
822        let d = Decimal::with_precision_scale("1234", Some(10), Some(-2)).unwrap();
823        assert_eq!(d.to_string(), "1200");
824    }
825
826    #[test]
827    fn test_negative_scale_with_precision() {
828        // NUMERIC(2, -3): 2 significant digits, round to nearest 1000
829        let d = Decimal::with_precision_scale("12345", Some(2), Some(-3)).unwrap();
830        assert_eq!(d.to_string(), "12000");
831    }
832
833    // ==================== Error Handling Tests ====================
834
835    #[test]
836    fn test_invalid_format_errors() {
837        // Multiple decimal points
838        let result = Decimal::from_str("1.2.3");
839        assert!(result.is_err());
840        assert!(matches!(
841            result.unwrap_err(),
842            DecimalError::InvalidFormat(_)
843        ));
844
845        // Invalid characters
846        let result = Decimal::from_str("12abc");
847        assert!(result.is_err());
848
849        // Missing exponent after 'e'
850        let result = Decimal::from_str("1e");
851        assert!(result.is_err());
852        assert!(matches!(
853            result.unwrap_err(),
854            DecimalError::InvalidFormat(_)
855        ));
856
857        // Invalid exponent (non-numeric after 'e')
858        let result = Decimal::from_str("1eabc");
859        assert!(result.is_err());
860
861        // Empty string gives zero
862        let d = Decimal::from_str("").unwrap();
863        assert!(d.is_zero());
864
865        // Just a sign with no digits
866        let result = Decimal::from_str("-");
867        assert!(result.is_ok()); // Parses as zero
868    }
869
870    #[test]
871    fn test_leading_plus_sign() {
872        let d = Decimal::from_str("+123.456").unwrap();
873        assert_eq!(d.to_string(), "123.456");
874        assert!(d.is_positive());
875    }
876
877    #[test]
878    fn test_scientific_notation() {
879        let d = Decimal::from_str("1.5e10").unwrap();
880        assert_eq!(d.to_string(), "15000000000");
881
882        let d = Decimal::from_str("1.5E-3").unwrap();
883        assert_eq!(d.to_string(), "0.0015");
884
885        let d = Decimal::from_str("1e+5").unwrap();
886        assert_eq!(d.to_string(), "100000");
887    }
888
889    #[test]
890    fn test_leading_decimal_point() {
891        // ".5" should be parsed as "0.5"
892        let d = Decimal::from_str(".5").unwrap();
893        assert_eq!(d.to_string(), "0.5");
894
895        let d = Decimal::from_str("-.25").unwrap();
896        assert_eq!(d.to_string(), "-0.25");
897    }
898
899    #[test]
900    fn test_trailing_zeros() {
901        let d = Decimal::from_str("100").unwrap();
902        assert_eq!(d.to_string(), "100");
903
904        let d = Decimal::from_str("1.500").unwrap();
905        assert_eq!(d.to_string(), "1.5");
906    }
907
908    #[test]
909    fn test_leading_zeros() {
910        let d = Decimal::from_str("007").unwrap();
911        assert_eq!(d.to_string(), "7");
912
913        let d = Decimal::from_str("00.123").unwrap();
914        assert_eq!(d.to_string(), "0.123");
915    }
916
917    // ==================== Additional Trait Tests ====================
918
919    #[test]
920    fn test_into_bytes() {
921        let d = Decimal::from_str("123.456").unwrap();
922        let bytes_ref = d.as_bytes().to_vec();
923        let bytes_owned = d.into_bytes();
924        assert_eq!(bytes_ref, bytes_owned);
925    }
926
927    #[test]
928    fn test_clone() {
929        let d1 = Decimal::from_str("123.456").unwrap();
930        let d2 = d1.clone();
931        assert_eq!(d1, d2);
932        assert_eq!(d1.as_bytes(), d2.as_bytes());
933    }
934
935    #[test]
936    fn test_hash() {
937        use std::collections::HashSet;
938
939        let mut set = HashSet::new();
940        set.insert(Decimal::from_str("123.456").unwrap());
941        set.insert(Decimal::from_str("123.456").unwrap()); // Duplicate
942        set.insert(Decimal::from_str("789.012").unwrap());
943
944        assert_eq!(set.len(), 2);
945        assert!(set.contains(&Decimal::from_str("123.456").unwrap()));
946    }
947
948    #[test]
949    fn test_debug_format() {
950        let d = Decimal::from_str("123.456").unwrap();
951        let debug_str = format!("{:?}", d);
952        assert!(debug_str.contains("Decimal"));
953        assert!(debug_str.contains("123.456"));
954    }
955
956    #[test]
957    fn test_ord_trait() {
958        use std::cmp::Ordering;
959
960        let a = Decimal::from_str("1").unwrap();
961        let b = Decimal::from_str("2").unwrap();
962        let c = Decimal::from_str("1").unwrap();
963
964        assert_eq!(a.cmp(&b), Ordering::Less);
965        assert_eq!(b.cmp(&a), Ordering::Greater);
966        assert_eq!(a.cmp(&c), Ordering::Equal);
967    }
968
969    #[test]
970    fn test_from_bytes_invalid() {
971        // Empty bytes should fail
972        let result = Decimal::from_bytes(&[]);
973        assert!(result.is_err());
974
975        // Single invalid sign byte
976        let result = Decimal::from_bytes(&[0x00]);
977        assert!(result.is_err());
978    }
979
980    #[test]
981    fn test_deserialize_from_string_number() {
982        // JSON string numbers deserialize correctly
983        let d: Decimal = serde_json::from_str("\"42\"").unwrap();
984        assert_eq!(d.to_string(), "42");
985
986        let d: Decimal = serde_json::from_str("\"-100\"").unwrap();
987        assert_eq!(d.to_string(), "-100");
988
989        let d: Decimal = serde_json::from_str("\"1.5e10\"").unwrap();
990        assert_eq!(d.to_string(), "15000000000");
991    }
992
993    #[test]
994    fn test_from_various_integer_types() {
995        assert_eq!(Decimal::from(0i32).to_string(), "0");
996        assert_eq!(Decimal::from(i32::MAX).to_string(), "2147483647");
997        assert_eq!(Decimal::from(i32::MIN).to_string(), "-2147483648");
998        assert_eq!(Decimal::from(i64::MAX).to_string(), "9223372036854775807");
999        assert_eq!(Decimal::from(i64::MIN).to_string(), "-9223372036854775808");
1000    }
1001
1002    #[test]
1003    fn test_precision_overflow() {
1004        // Exponent too large
1005        let result = Decimal::from_str("1e20000");
1006        assert!(result.is_err());
1007        assert!(matches!(
1008            result.unwrap_err(),
1009            DecimalError::PrecisionOverflow
1010        ));
1011
1012        // Exponent too small (negative)
1013        let result = Decimal::from_str("1e-20000");
1014        assert!(result.is_err());
1015        assert!(matches!(
1016            result.unwrap_err(),
1017            DecimalError::PrecisionOverflow
1018        ));
1019    }
1020
1021    #[test]
1022    fn test_all_zeros_variations() {
1023        let d = Decimal::from_str("0").unwrap();
1024        assert!(d.is_zero());
1025
1026        let d = Decimal::from_str("0.0").unwrap();
1027        assert!(d.is_zero());
1028
1029        let d = Decimal::from_str("00.00").unwrap();
1030        assert!(d.is_zero());
1031
1032        let d = Decimal::from_str("-0").unwrap();
1033        assert!(d.is_zero());
1034        // Note: -0 normalizes to 0
1035    }
1036
1037    // ==================== Additional Edge Case Tests ====================
1038
1039    #[test]
1040    fn test_rounding_all_nines() {
1041        // Rounding 99.999 with scale 2 should become 100.00 -> 100
1042        let d = Decimal::with_precision_scale("99.999", Some(10), Some(2)).unwrap();
1043        assert_eq!(d.to_string(), "100");
1044
1045        // Rounding 9.99 with scale 1 should become 10.0 -> 10
1046        let d = Decimal::with_precision_scale("9.99", Some(10), Some(1)).unwrap();
1047        assert_eq!(d.to_string(), "10");
1048
1049        // 999 rounding to nearest 10 (scale -1) should become 1000
1050        let d = Decimal::with_precision_scale("999", Some(10), Some(-1)).unwrap();
1051        assert_eq!(d.to_string(), "1000");
1052    }
1053
1054    #[test]
1055    fn test_negative_scale_small_number() {
1056        // Number smaller than rounding unit, rounds to 0
1057        let d = Decimal::with_precision_scale("4", Some(10), Some(-1)).unwrap();
1058        assert_eq!(d.to_string(), "0");
1059
1060        // Number >= half the rounding unit, rounds up
1061        let d = Decimal::with_precision_scale("5", Some(10), Some(-1)).unwrap();
1062        assert_eq!(d.to_string(), "10");
1063
1064        // Negative number smaller than rounding unit
1065        let d = Decimal::with_precision_scale("-4", Some(10), Some(-1)).unwrap();
1066        assert_eq!(d.to_string(), "0");
1067
1068        // Negative number >= half unit
1069        let d = Decimal::with_precision_scale("-5", Some(10), Some(-1)).unwrap();
1070        assert_eq!(d.to_string(), "-10");
1071    }
1072
1073    #[test]
1074    fn test_precision_truncation() {
1075        // When precision is exceeded, truncate from left
1076        let d = Decimal::with_precision_scale("123456", Some(3), Some(0)).unwrap();
1077        assert_eq!(d.to_string(), "456");
1078
1079        // With decimal places
1080        let d = Decimal::with_precision_scale("12345.67", Some(4), Some(2)).unwrap();
1081        assert_eq!(d.to_string(), "45.67");
1082    }
1083
1084    #[test]
1085    fn test_very_small_numbers() {
1086        let d = Decimal::from_str("0.000000001").unwrap();
1087        assert_eq!(d.to_string(), "0.000000001");
1088        assert!(d.is_positive());
1089
1090        let d = Decimal::from_str("-0.000000001").unwrap();
1091        assert_eq!(d.to_string(), "-0.000000001");
1092        assert!(d.is_negative());
1093    }
1094
1095    #[test]
1096    fn test_very_large_numbers() {
1097        let d = Decimal::from_str("999999999999999999999999999999").unwrap();
1098        assert_eq!(d.to_string(), "999999999999999999999999999999");
1099
1100        let d = Decimal::from_str("-999999999999999999999999999999").unwrap();
1101        assert_eq!(d.to_string(), "-999999999999999999999999999999");
1102    }
1103
1104    #[test]
1105    fn test_max_exponent_boundary() {
1106        // Just under max exponent should work
1107        let d = Decimal::from_str("1e16000").unwrap();
1108        assert!(d.is_positive());
1109
1110        // Just over should fail
1111        let result = Decimal::from_str("1e17000");
1112        assert!(result.is_err());
1113    }
1114
1115    #[test]
1116    fn test_min_exponent_boundary() {
1117        // Just above min exponent should work
1118        let d = Decimal::from_str("1e-16000").unwrap();
1119        assert!(d.is_positive());
1120
1121        // Just below should fail
1122        let result = Decimal::from_str("1e-17000");
1123        assert!(result.is_err());
1124    }
1125
1126    #[test]
1127    fn test_odd_digit_count() {
1128        // Odd number of digits (tests BCD padding)
1129        let d = Decimal::from_str("12345").unwrap();
1130        assert_eq!(d.to_string(), "12345");
1131
1132        let d = Decimal::from_str("1").unwrap();
1133        assert_eq!(d.to_string(), "1");
1134
1135        let d = Decimal::from_str("123").unwrap();
1136        assert_eq!(d.to_string(), "123");
1137    }
1138
1139    #[test]
1140    fn test_negative_number_ordering() {
1141        // Verify negative number byte ordering is correct
1142        let a = Decimal::from_str("-100").unwrap();
1143        let b = Decimal::from_str("-10").unwrap();
1144        let c = Decimal::from_str("-1").unwrap();
1145
1146        // -100 < -10 < -1 numerically
1147        assert!(a < b);
1148        assert!(b < c);
1149
1150        // Byte ordering should match
1151        assert!(a.as_bytes() < b.as_bytes());
1152        assert!(b.as_bytes() < c.as_bytes());
1153    }
1154
1155    #[test]
1156    fn test_from_bytes_unchecked_roundtrip() {
1157        let original = Decimal::from_str("123.456").unwrap();
1158        let bytes = original.as_bytes().to_vec();
1159        let restored = Decimal::from_bytes_unchecked(bytes);
1160        assert_eq!(original, restored);
1161    }
1162
1163    #[test]
1164    fn test_special_value_checks() {
1165        let d = Decimal::from_str("123.456").unwrap();
1166        assert!(!d.is_nan());
1167        assert!(!d.is_infinity());
1168        assert!(!d.is_pos_infinity());
1169        assert!(!d.is_neg_infinity());
1170        assert!(!d.is_special());
1171        assert!(d.is_finite());
1172    }
1173
1174    #[test]
1175    fn test_equality_and_hash_consistency() {
1176        use std::collections::HashMap;
1177
1178        let d1 = Decimal::from_str("123.456").unwrap();
1179        let d2 = Decimal::from_str("123.456").unwrap();
1180        let d3 = Decimal::from_str("123.457").unwrap();
1181
1182        // Equal values should be equal
1183        assert_eq!(d1, d2);
1184        assert_ne!(d1, d3);
1185
1186        // Equal values should have same hash (can be used as map keys)
1187        let mut map = HashMap::new();
1188        map.insert(d1.clone(), "first");
1189        map.insert(d2.clone(), "second"); // Should overwrite
1190        assert_eq!(map.len(), 1);
1191        assert_eq!(map.get(&d1), Some(&"second"));
1192    }
1193
1194    #[test]
1195    fn test_scale_zero() {
1196        // Scale 0 should keep integer part only
1197        let d = Decimal::with_precision_scale("123.999", Some(10), Some(0)).unwrap();
1198        assert_eq!(d.to_string(), "124"); // Rounds up
1199    }
1200
1201    #[test]
1202    fn test_only_fractional_with_precision_scale() {
1203        let d = Decimal::with_precision_scale(".5", Some(10), Some(2)).unwrap();
1204        assert_eq!(d.to_string(), "0.5");
1205    }
1206
1207    #[test]
1208    fn test_default_impl() {
1209        let d = Decimal::default();
1210        assert!(d.is_zero());
1211        assert_eq!(d.to_string(), "0");
1212    }
1213
1214    #[test]
1215    fn test_precision_zero_integer_digits() {
1216        // When precision equals scale, no integer digits are allowed
1217        // This triggers the max_integer_digits == 0 branch
1218        let d = Decimal::with_precision_scale("123.456", Some(2), Some(2)).unwrap();
1219        assert_eq!(d.to_string(), "0.46");
1220    }
1221
1222    #[test]
1223    fn test_negative_with_precision_truncation() {
1224        // Negative number that gets truncated by precision constraints
1225        let d = Decimal::with_precision_scale("-123.456", Some(3), Some(2)).unwrap();
1226        assert_eq!(d.to_string(), "-3.46");
1227    }
1228
1229    #[test]
1230    fn test_invalid_sign_byte() {
1231        // Sign bytes: SIGN_NEGATIVE=0x00, SIGN_ZERO=0x80, SIGN_POSITIVE=0xFF
1232        // Any other sign byte is invalid
1233
1234        // Sign byte 0x01 is invalid
1235        let result = Decimal::from_bytes(&[0x01, 0x40, 0x00, 0x12]);
1236        assert!(result.is_err());
1237
1238        // Sign byte 0x7F is also invalid
1239        let result = Decimal::from_bytes(&[0x7F, 0x40, 0x00, 0x12]);
1240        assert!(result.is_err());
1241
1242        // Sign byte 0xFE is also invalid
1243        let result = Decimal::from_bytes(&[0xFE, 0x40, 0x00, 0x12]);
1244        assert!(result.is_err());
1245    }
1246
1247    #[test]
1248    fn test_invalid_bcd_encoding() {
1249        // Construct bytes that would decode to invalid BCD (digit > 9)
1250        // SIGN_POSITIVE = 0xFF, with valid exponent, but invalid BCD mantissa
1251        // A valid BCD digit must have each nibble 0-9. 0xAB has A=10, B=11 (both > 9)
1252        let invalid_bytes = vec![
1253            0xFF, // SIGN_POSITIVE
1254            0x80, 0x00, // Valid exponent (middle of range)
1255            0xAB, // Invalid BCD: high nibble = 10, low nibble = 11
1256        ];
1257        let result = Decimal::from_bytes(&invalid_bytes);
1258        assert!(result.is_err());
1259
1260        // Also test with just high nibble invalid
1261        let invalid_bytes = vec![
1262            0xFF, // SIGN_POSITIVE
1263            0x80, 0x00, // Valid exponent
1264            0xA1, // Invalid BCD: high nibble = 10, low nibble = 1
1265        ];
1266        let result = Decimal::from_bytes(&invalid_bytes);
1267        assert!(result.is_err());
1268
1269        // Also test with just low nibble invalid
1270        let invalid_bytes = vec![
1271            0xFF, // SIGN_POSITIVE
1272            0x80, 0x00, // Valid exponent
1273            0x1B, // Invalid BCD: high nibble = 1, low nibble = 11
1274        ];
1275        let result = Decimal::from_bytes(&invalid_bytes);
1276        assert!(result.is_err());
1277    }
1278
1279    #[test]
1280    fn test_reserved_exponent_positive() {
1281        // SIGN_POSITIVE = 0xFF
1282        // Reserved exponents: 0xFFFE (Infinity), 0xFFFF (NaN)
1283        // If we have more than 3 bytes, special value check is skipped
1284        // but decode_exponent will catch the reserved value
1285
1286        // Reserved NaN exponent (0xFFFF) with extra mantissa byte
1287        let bytes_with_reserved_exp = vec![
1288            0xFF, // SIGN_POSITIVE
1289            0xFF, 0xFF, // Reserved for NaN
1290            0x12, // Some mantissa (makes it 4 bytes, not 3)
1291        ];
1292        let result = Decimal::from_bytes(&bytes_with_reserved_exp);
1293        assert!(result.is_err());
1294
1295        // Reserved Infinity exponent (0xFFFE) with extra mantissa byte
1296        let bytes_with_reserved_exp = vec![
1297            0xFF, // SIGN_POSITIVE
1298            0xFF, 0xFE, // Reserved for Infinity
1299            0x12, // Some mantissa (makes it 4 bytes, not 3)
1300        ];
1301        let result = Decimal::from_bytes(&bytes_with_reserved_exp);
1302        assert!(result.is_err());
1303    }
1304
1305    #[test]
1306    fn test_reserved_exponent_negative() {
1307        // SIGN_NEGATIVE = 0x00
1308        // Reserved exponent for -Infinity: 0x0000
1309        // If we have more than 3 bytes, special value check is skipped
1310
1311        let bytes_with_reserved_exp = vec![
1312            0x00, // SIGN_NEGATIVE
1313            0x00, 0x00, // Reserved for -Infinity
1314            0x12, // Some mantissa (makes it 4 bytes, not 3)
1315        ];
1316        let result = Decimal::from_bytes(&bytes_with_reserved_exp);
1317        assert!(result.is_err());
1318    }
1319
1320    #[test]
1321    fn test_empty_mantissa_bytes() {
1322        // Construct bytes with valid sign and exponent but no mantissa
1323        // This should decode to 0 via the empty digits path in format_decimal
1324        let bytes_no_mantissa = vec![
1325            0xFF, // SIGN_POSITIVE
1326            0x80, 0x00, // Valid exponent
1327                  // No mantissa bytes
1328        ];
1329        let d = Decimal::from_bytes(&bytes_no_mantissa).unwrap();
1330        assert_eq!(d.to_string(), "0");
1331    }
1332
1333    // ==================== rust_decimal Interop Tests ====================
1334
1335    #[cfg(feature = "rust_decimal")]
1336    mod rust_decimal_tests {
1337        use super::*;
1338
1339        #[test]
1340        fn test_from_rust_decimal() {
1341            use rust_decimal::Decimal as RustDecimal;
1342
1343            let rd = RustDecimal::new(12345, 2); // 123.45
1344            let d: Decimal = rd.try_into().unwrap();
1345            assert_eq!(d.to_string(), "123.45");
1346        }
1347
1348        #[test]
1349        fn test_to_rust_decimal() {
1350            use rust_decimal::Decimal as RustDecimal;
1351
1352            let d = Decimal::from_str("123.45").unwrap();
1353            let rd: RustDecimal = (&d).try_into().unwrap();
1354            assert_eq!(rd.to_string(), "123.45");
1355        }
1356
1357        #[test]
1358        fn test_rust_decimal_roundtrip() {
1359            use rust_decimal::Decimal as RustDecimal;
1360
1361            let values = vec!["0", "1", "-1", "123.456", "-999.999", "0.001"];
1362
1363            for s in values {
1364                let d = Decimal::from_str(s).unwrap();
1365                let rd: RustDecimal = (&d).try_into().unwrap();
1366                let d2: Decimal = rd.try_into().unwrap();
1367                assert_eq!(d, d2, "Roundtrip failed for {}", s);
1368            }
1369        }
1370
1371        #[test]
1372        fn test_rust_decimal_arithmetic() {
1373            use rust_decimal::Decimal as RustDecimal;
1374
1375            // Start with decimal-bytes values
1376            let a = Decimal::from_str("100.50").unwrap();
1377            let b = Decimal::from_str("25.25").unwrap();
1378
1379            // Convert to rust_decimal for arithmetic
1380            let ra: RustDecimal = (&a).try_into().unwrap();
1381            let rb: RustDecimal = (&b).try_into().unwrap();
1382            let sum = ra + rb;
1383
1384            // Convert back to decimal-bytes for storage
1385            let result: Decimal = sum.try_into().unwrap();
1386            assert_eq!(result.to_string(), "125.75");
1387        }
1388
1389        #[test]
1390        fn test_rust_decimal_from_owned() {
1391            use rust_decimal::Decimal as RustDecimal;
1392
1393            // Test TryFrom<Decimal> (owned) for RustDecimal
1394            let d = Decimal::from_str("456.789").unwrap();
1395            let rd: RustDecimal = d.try_into().unwrap();
1396            assert_eq!(rd.to_string(), "456.789");
1397        }
1398
1399        #[test]
1400        fn test_rust_decimal_special_values_fail() {
1401            use rust_decimal::Decimal as RustDecimal;
1402
1403            // Infinity cannot convert to rust_decimal
1404            let inf = Decimal::infinity();
1405            let result: Result<RustDecimal, _> = (&inf).try_into();
1406            assert!(result.is_err());
1407
1408            // NaN cannot convert to rust_decimal
1409            let nan = Decimal::nan();
1410            let result: Result<RustDecimal, _> = (&nan).try_into();
1411            assert!(result.is_err());
1412        }
1413    }
1414
1415    // ==================== bigdecimal Interop Tests ====================
1416
1417    #[cfg(feature = "bigdecimal")]
1418    mod bigdecimal_tests {
1419        use super::*;
1420
1421        #[test]
1422        fn test_from_bigdecimal() {
1423            use bigdecimal::BigDecimal;
1424            use std::str::FromStr;
1425
1426            let bd = BigDecimal::from_str("123.45").unwrap();
1427            let d: Decimal = bd.try_into().unwrap();
1428            assert_eq!(d.to_string(), "123.45");
1429        }
1430
1431        #[test]
1432        fn test_to_bigdecimal() {
1433            use bigdecimal::BigDecimal;
1434
1435            let d = Decimal::from_str("123.45").unwrap();
1436            let bd: BigDecimal = (&d).try_into().unwrap();
1437            assert_eq!(bd.to_string(), "123.45");
1438        }
1439
1440        #[test]
1441        fn test_bigdecimal_roundtrip() {
1442            use bigdecimal::BigDecimal;
1443
1444            let values = vec!["0", "1", "-1", "123.456", "-999.999", "0.001"];
1445
1446            for s in values {
1447                let d = Decimal::from_str(s).unwrap();
1448                let bd: BigDecimal = (&d).try_into().unwrap();
1449                let d2: Decimal = bd.try_into().unwrap();
1450                assert_eq!(d, d2, "Roundtrip failed for {}", s);
1451            }
1452        }
1453
1454        #[test]
1455        fn test_bigdecimal_from_owned() {
1456            use bigdecimal::BigDecimal;
1457
1458            // Test TryFrom<Decimal> (owned) for BigDecimal
1459            let d = Decimal::from_str("456.789").unwrap();
1460            let bd: BigDecimal = d.try_into().unwrap();
1461            assert_eq!(bd.to_string(), "456.789");
1462        }
1463
1464        #[test]
1465        fn test_bigdecimal_from_ref() {
1466            use bigdecimal::BigDecimal;
1467            use std::str::FromStr;
1468
1469            // Test TryFrom<&BigDecimal> for Decimal
1470            let bd = BigDecimal::from_str("789.012").unwrap();
1471            let d: Decimal = (&bd).try_into().unwrap();
1472            assert_eq!(d.to_string(), "789.012");
1473        }
1474
1475        #[test]
1476        fn test_bigdecimal_special_values_fail() {
1477            use bigdecimal::BigDecimal;
1478
1479            // Infinity cannot convert to BigDecimal
1480            let inf = Decimal::infinity();
1481            let result: Result<BigDecimal, _> = (&inf).try_into();
1482            assert!(result.is_err());
1483
1484            // NaN cannot convert to BigDecimal
1485            let nan = Decimal::nan();
1486            let result: Result<BigDecimal, _> = (&nan).try_into();
1487            assert!(result.is_err());
1488        }
1489    }
1490}