Skip to main content

decimal_bytes/
lib.rs

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