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                    Decimal::from_str(&val.to_string()).expect("Integer is always valid")
470                }
471            }
472        )*
473    };
474}
475
476impl_from_int!(i8, i16, i32, i64, i128, u8, u16, u32, u64, u128);
477
478impl Default for Decimal {
479    fn default() -> Self {
480        Decimal {
481            bytes: vec![encoding::SIGN_ZERO],
482        }
483    }
484}
485
486// ============================================================================
487// Optional: rust_decimal interop (enabled with "rust_decimal" feature)
488// ============================================================================
489
490#[cfg(feature = "rust_decimal")]
491mod rust_decimal_interop {
492    use super::{Decimal, DecimalError};
493    use std::str::FromStr;
494
495    impl TryFrom<rust_decimal::Decimal> for Decimal {
496        type Error = DecimalError;
497
498        /// Converts a `rust_decimal::Decimal` to a `decimal_bytes::Decimal`.
499        ///
500        /// # Example
501        ///
502        /// ```ignore
503        /// use rust_decimal::Decimal as RustDecimal;
504        /// use decimal_bytes::Decimal;
505        ///
506        /// let rd = RustDecimal::new(12345, 2); // 123.45
507        /// let d: Decimal = rd.try_into().unwrap();
508        /// assert_eq!(d.to_string(), "123.45");
509        /// ```
510        fn try_from(value: rust_decimal::Decimal) -> Result<Self, Self::Error> {
511            Decimal::from_str(&value.to_string())
512        }
513    }
514
515    impl TryFrom<&Decimal> for rust_decimal::Decimal {
516        type Error = rust_decimal::Error;
517
518        /// Converts a `decimal_bytes::Decimal` to a `rust_decimal::Decimal`.
519        ///
520        /// Note: This may fail if the decimal exceeds rust_decimal's precision limits
521        /// (28-29 significant digits) or if it's a special value (Infinity, NaN).
522        ///
523        /// # Example
524        ///
525        /// ```ignore
526        /// use rust_decimal::Decimal as RustDecimal;
527        /// use decimal_bytes::Decimal;
528        ///
529        /// let d = Decimal::from_str("123.45").unwrap();
530        /// let rd: RustDecimal = (&d).try_into().unwrap();
531        /// assert_eq!(rd.to_string(), "123.45");
532        /// ```
533        fn try_from(value: &Decimal) -> Result<Self, Self::Error> {
534            use std::str::FromStr;
535            rust_decimal::Decimal::from_str(&value.to_string())
536        }
537    }
538
539    impl TryFrom<Decimal> for rust_decimal::Decimal {
540        type Error = rust_decimal::Error;
541
542        fn try_from(value: Decimal) -> Result<Self, Self::Error> {
543            rust_decimal::Decimal::try_from(&value)
544        }
545    }
546}
547
548// The rust_decimal_interop module only contains trait implementations,
549// so there's nothing to re-export. The TryFrom impls are automatically available.
550
551// ============================================================================
552// Optional: bigdecimal interop (enabled with "bigdecimal" feature)
553// ============================================================================
554
555#[cfg(feature = "bigdecimal")]
556mod bigdecimal_interop {
557    use super::{Decimal, DecimalError};
558    use std::str::FromStr;
559
560    impl TryFrom<bigdecimal::BigDecimal> for Decimal {
561        type Error = DecimalError;
562
563        /// Converts a `bigdecimal::BigDecimal` to a `decimal_bytes::Decimal`.
564        fn try_from(value: bigdecimal::BigDecimal) -> Result<Self, Self::Error> {
565            Decimal::from_str(&value.to_string())
566        }
567    }
568
569    impl TryFrom<&bigdecimal::BigDecimal> for Decimal {
570        type Error = DecimalError;
571
572        fn try_from(value: &bigdecimal::BigDecimal) -> Result<Self, Self::Error> {
573            Decimal::from_str(&value.to_string())
574        }
575    }
576
577    impl TryFrom<&Decimal> for bigdecimal::BigDecimal {
578        type Error = bigdecimal::ParseBigDecimalError;
579
580        /// Converts a `decimal_bytes::Decimal` to a `bigdecimal::BigDecimal`.
581        ///
582        /// Note: This may fail for special values (Infinity, NaN) which
583        /// bigdecimal doesn't support.
584        fn try_from(value: &Decimal) -> Result<Self, Self::Error> {
585            use std::str::FromStr;
586            bigdecimal::BigDecimal::from_str(&value.to_string())
587        }
588    }
589
590    impl TryFrom<Decimal> for bigdecimal::BigDecimal {
591        type Error = bigdecimal::ParseBigDecimalError;
592
593        fn try_from(value: Decimal) -> Result<Self, Self::Error> {
594            bigdecimal::BigDecimal::try_from(&value)
595        }
596    }
597}
598
599// The bigdecimal_interop module only contains trait implementations,
600// so there's nothing to re-export. The TryFrom impls are automatically available.
601
602#[cfg(test)]
603mod tests {
604    use super::*;
605
606    #[test]
607    fn test_from_str() {
608        let d = Decimal::from_str("123.456").unwrap();
609        assert_eq!(d.to_string(), "123.456");
610    }
611
612    #[test]
613    fn test_zero() {
614        let d = Decimal::from_str("0").unwrap();
615        assert!(d.is_zero());
616        assert!(!d.is_negative());
617        assert!(!d.is_positive());
618        assert!(d.is_finite());
619        assert!(!d.is_special());
620    }
621
622    #[test]
623    fn test_negative() {
624        let d = Decimal::from_str("-123.456").unwrap();
625        assert!(d.is_negative());
626        assert!(!d.is_zero());
627        assert!(!d.is_positive());
628        assert!(d.is_finite());
629    }
630
631    #[test]
632    fn test_positive() {
633        let d = Decimal::from_str("123.456").unwrap();
634        assert!(d.is_positive());
635        assert!(!d.is_zero());
636        assert!(!d.is_negative());
637        assert!(d.is_finite());
638    }
639
640    #[test]
641    fn test_ordering() {
642        let values = vec!["-100", "-10", "-1", "-0.1", "0", "0.1", "1", "10", "100"];
643        let decimals: Vec<Decimal> = values
644            .iter()
645            .map(|s| Decimal::from_str(s).unwrap())
646            .collect();
647
648        // Check that ordering is correct
649        for i in 0..decimals.len() - 1 {
650            assert!(
651                decimals[i] < decimals[i + 1],
652                "{} should be < {}",
653                values[i],
654                values[i + 1]
655            );
656        }
657
658        // Check that byte ordering matches
659        for i in 0..decimals.len() - 1 {
660            assert!(
661                decimals[i].as_bytes() < decimals[i + 1].as_bytes(),
662                "bytes of {} should be < bytes of {}",
663                values[i],
664                values[i + 1]
665            );
666        }
667    }
668
669    #[test]
670    fn test_roundtrip() {
671        let values = vec![
672            "0", "1", "-1", "123.456", "-123.456", "0.001", "0.1", "10", "100", "1000000",
673            "-1000000",
674        ];
675
676        for s in values {
677            let d = Decimal::from_str(s).unwrap();
678            let bytes = d.as_bytes();
679            let restored = Decimal::from_bytes(bytes).unwrap();
680            assert_eq!(d, restored, "Roundtrip failed for {}", s);
681        }
682    }
683
684    #[test]
685    fn test_precision_scale() {
686        // Round to 2 decimal places
687        let d = Decimal::with_precision_scale("123.456", Some(10), Some(2)).unwrap();
688        assert_eq!(d.to_string(), "123.46");
689
690        // When precision is exceeded, least significant integer digits are kept
691        let d = Decimal::with_precision_scale("12345.67", Some(5), Some(2)).unwrap();
692        assert_eq!(d.to_string(), "345.67"); // 5 digits total, 2 after decimal = 3 integer digits max
693
694        // Rounding within precision limits
695        let d = Decimal::with_precision_scale("99.999", Some(5), Some(2)).unwrap();
696        assert_eq!(d.to_string(), "100"); // Rounds up, fits in precision
697    }
698
699    #[test]
700    fn test_from_integer() {
701        let d = Decimal::from(42i64);
702        assert_eq!(d.to_string(), "42");
703
704        let d = Decimal::from(-100i32);
705        assert_eq!(d.to_string(), "-100");
706    }
707
708    #[test]
709    fn test_serialization() {
710        let d = Decimal::from_str("123.456").unwrap();
711        let json = serde_json::to_string(&d).unwrap();
712        assert_eq!(json, "\"123.456\"");
713
714        let restored: Decimal = serde_json::from_str(&json).unwrap();
715        assert_eq!(d, restored);
716    }
717
718    #[test]
719    fn test_byte_efficiency() {
720        // Check that storage is reasonably efficient
721        let d = Decimal::from_str("123456789").unwrap();
722        // 1 byte sign + ~2 bytes exponent + ~5 bytes mantissa (9 digits / 2)
723        assert!(
724            d.byte_len() <= 10,
725            "Expected <= 10 bytes, got {}",
726            d.byte_len()
727        );
728
729        let d = Decimal::from_str("0.000001").unwrap();
730        // Should be compact for small numbers too
731        assert!(
732            d.byte_len() <= 6,
733            "Expected <= 6 bytes, got {}",
734            d.byte_len()
735        );
736    }
737
738    // ==================== Special Values Tests ====================
739
740    #[test]
741    fn test_infinity_creation() {
742        let pos_inf = Decimal::infinity();
743        assert!(pos_inf.is_pos_infinity());
744        assert!(pos_inf.is_infinity());
745        assert!(!pos_inf.is_neg_infinity());
746        assert!(!pos_inf.is_nan());
747        assert!(pos_inf.is_special());
748        assert!(!pos_inf.is_finite());
749        assert_eq!(pos_inf.to_string(), "Infinity");
750
751        let neg_inf = Decimal::neg_infinity();
752        assert!(neg_inf.is_neg_infinity());
753        assert!(neg_inf.is_infinity());
754        assert!(!neg_inf.is_pos_infinity());
755        assert!(!neg_inf.is_nan());
756        assert!(neg_inf.is_special());
757        assert!(!neg_inf.is_finite());
758        assert_eq!(neg_inf.to_string(), "-Infinity");
759    }
760
761    #[test]
762    fn test_nan_creation() {
763        let nan = Decimal::nan();
764        assert!(nan.is_nan());
765        assert!(nan.is_special());
766        assert!(!nan.is_finite());
767        assert!(!nan.is_infinity());
768        assert!(!nan.is_zero());
769        assert_eq!(nan.to_string(), "NaN");
770    }
771
772    #[test]
773    fn test_special_value_from_str() {
774        let pos_inf = Decimal::from_str("Infinity").unwrap();
775        assert!(pos_inf.is_pos_infinity());
776
777        let neg_inf = Decimal::from_str("-Infinity").unwrap();
778        assert!(neg_inf.is_neg_infinity());
779
780        let nan = Decimal::from_str("NaN").unwrap();
781        assert!(nan.is_nan());
782
783        // Case-insensitive
784        let inf = Decimal::from_str("infinity").unwrap();
785        assert!(inf.is_pos_infinity());
786
787        let inf = Decimal::from_str("INF").unwrap();
788        assert!(inf.is_pos_infinity());
789    }
790
791    #[test]
792    fn test_special_value_ordering() {
793        // PostgreSQL order: -Infinity < negatives < zero < positives < Infinity < NaN
794        let neg_inf = Decimal::neg_infinity();
795        let neg_num = Decimal::from_str("-1000").unwrap();
796        let zero = Decimal::from_str("0").unwrap();
797        let pos_num = Decimal::from_str("1000").unwrap();
798        let pos_inf = Decimal::infinity();
799        let nan = Decimal::nan();
800
801        assert!(neg_inf < neg_num);
802        assert!(neg_num < zero);
803        assert!(zero < pos_num);
804        assert!(pos_num < pos_inf);
805        assert!(pos_inf < nan);
806
807        // Verify byte ordering matches
808        assert!(neg_inf.as_bytes() < neg_num.as_bytes());
809        assert!(neg_num.as_bytes() < zero.as_bytes());
810        assert!(zero.as_bytes() < pos_num.as_bytes());
811        assert!(pos_num.as_bytes() < pos_inf.as_bytes());
812        assert!(pos_inf.as_bytes() < nan.as_bytes());
813    }
814
815    #[test]
816    fn test_special_value_equality() {
817        // All NaNs are equal (PostgreSQL semantics)
818        let nan1 = Decimal::from_str("NaN").unwrap();
819        let nan2 = Decimal::from_str("nan").unwrap();
820        let nan3 = Decimal::nan();
821        assert_eq!(nan1, nan2);
822        assert_eq!(nan2, nan3);
823
824        // Infinities are equal to themselves
825        let inf1 = Decimal::infinity();
826        let inf2 = Decimal::from_str("Infinity").unwrap();
827        assert_eq!(inf1, inf2);
828
829        let neg_inf1 = Decimal::neg_infinity();
830        let neg_inf2 = Decimal::from_str("-Infinity").unwrap();
831        assert_eq!(neg_inf1, neg_inf2);
832    }
833
834    #[test]
835    fn test_special_value_serialization() {
836        let inf = Decimal::infinity();
837        let json = serde_json::to_string(&inf).unwrap();
838        assert_eq!(json, "\"Infinity\"");
839        let restored: Decimal = serde_json::from_str(&json).unwrap();
840        assert_eq!(inf, restored);
841
842        let nan = Decimal::nan();
843        let json = serde_json::to_string(&nan).unwrap();
844        assert_eq!(json, "\"NaN\"");
845        let restored: Decimal = serde_json::from_str(&json).unwrap();
846        assert_eq!(nan, restored);
847    }
848
849    #[test]
850    fn test_special_value_byte_efficiency() {
851        // Special values should be compact (3 bytes each)
852        assert_eq!(Decimal::infinity().byte_len(), 3);
853        assert_eq!(Decimal::neg_infinity().byte_len(), 3);
854        assert_eq!(Decimal::nan().byte_len(), 3);
855    }
856
857    // ==================== Negative Scale Tests ====================
858
859    #[test]
860    fn test_negative_scale() {
861        // Round to nearest 1000
862        let d = Decimal::with_precision_scale("12345", Some(10), Some(-3)).unwrap();
863        assert_eq!(d.to_string(), "12000");
864
865        // Round up
866        let d = Decimal::with_precision_scale("12500", Some(10), Some(-3)).unwrap();
867        assert_eq!(d.to_string(), "13000");
868
869        // Round to nearest 100
870        let d = Decimal::with_precision_scale("1234", Some(10), Some(-2)).unwrap();
871        assert_eq!(d.to_string(), "1200");
872    }
873
874    #[test]
875    fn test_negative_scale_with_precision() {
876        // NUMERIC(2, -3): 2 significant digits, round to nearest 1000
877        let d = Decimal::with_precision_scale("12345", Some(2), Some(-3)).unwrap();
878        assert_eq!(d.to_string(), "12000");
879    }
880
881    // ==================== Error Handling Tests ====================
882
883    #[test]
884    fn test_invalid_format_errors() {
885        // Multiple decimal points
886        let result = Decimal::from_str("1.2.3");
887        assert!(result.is_err());
888        assert!(matches!(
889            result.unwrap_err(),
890            DecimalError::InvalidFormat(_)
891        ));
892
893        // Invalid characters
894        let result = Decimal::from_str("12abc");
895        assert!(result.is_err());
896
897        // Missing exponent after 'e'
898        let result = Decimal::from_str("1e");
899        assert!(result.is_err());
900        assert!(matches!(
901            result.unwrap_err(),
902            DecimalError::InvalidFormat(_)
903        ));
904
905        // Invalid exponent (non-numeric after 'e')
906        let result = Decimal::from_str("1eabc");
907        assert!(result.is_err());
908
909        // Empty string gives zero
910        let d = Decimal::from_str("").unwrap();
911        assert!(d.is_zero());
912
913        // Just a sign with no digits
914        let result = Decimal::from_str("-");
915        assert!(result.is_ok()); // Parses as zero
916    }
917
918    #[test]
919    fn test_leading_plus_sign() {
920        let d = Decimal::from_str("+123.456").unwrap();
921        assert_eq!(d.to_string(), "123.456");
922        assert!(d.is_positive());
923    }
924
925    #[test]
926    fn test_scientific_notation() {
927        let d = Decimal::from_str("1.5e10").unwrap();
928        assert_eq!(d.to_string(), "15000000000");
929
930        let d = Decimal::from_str("1.5E-3").unwrap();
931        assert_eq!(d.to_string(), "0.0015");
932
933        let d = Decimal::from_str("1e+5").unwrap();
934        assert_eq!(d.to_string(), "100000");
935    }
936
937    #[test]
938    fn test_leading_decimal_point() {
939        // ".5" should be parsed as "0.5"
940        let d = Decimal::from_str(".5").unwrap();
941        assert_eq!(d.to_string(), "0.5");
942
943        let d = Decimal::from_str("-.25").unwrap();
944        assert_eq!(d.to_string(), "-0.25");
945    }
946
947    #[test]
948    fn test_trailing_zeros() {
949        let d = Decimal::from_str("100").unwrap();
950        assert_eq!(d.to_string(), "100");
951
952        let d = Decimal::from_str("1.500").unwrap();
953        assert_eq!(d.to_string(), "1.5");
954    }
955
956    #[test]
957    fn test_leading_zeros() {
958        let d = Decimal::from_str("007").unwrap();
959        assert_eq!(d.to_string(), "7");
960
961        let d = Decimal::from_str("00.123").unwrap();
962        assert_eq!(d.to_string(), "0.123");
963    }
964
965    // ==================== Additional Trait Tests ====================
966
967    #[test]
968    fn test_into_bytes() {
969        let d = Decimal::from_str("123.456").unwrap();
970        let bytes_ref = d.as_bytes().to_vec();
971        let bytes_owned = d.into_bytes();
972        assert_eq!(bytes_ref, bytes_owned);
973    }
974
975    #[test]
976    fn test_clone() {
977        let d1 = Decimal::from_str("123.456").unwrap();
978        let d2 = d1.clone();
979        assert_eq!(d1, d2);
980        assert_eq!(d1.as_bytes(), d2.as_bytes());
981    }
982
983    #[test]
984    fn test_hash() {
985        use std::collections::HashSet;
986
987        let mut set = HashSet::new();
988        set.insert(Decimal::from_str("123.456").unwrap());
989        set.insert(Decimal::from_str("123.456").unwrap()); // Duplicate
990        set.insert(Decimal::from_str("789.012").unwrap());
991
992        assert_eq!(set.len(), 2);
993        assert!(set.contains(&Decimal::from_str("123.456").unwrap()));
994    }
995
996    #[test]
997    fn test_debug_format() {
998        let d = Decimal::from_str("123.456").unwrap();
999        let debug_str = format!("{:?}", d);
1000        assert!(debug_str.contains("Decimal"));
1001        assert!(debug_str.contains("123.456"));
1002    }
1003
1004    #[test]
1005    fn test_ord_trait() {
1006        use std::cmp::Ordering;
1007
1008        let a = Decimal::from_str("1").unwrap();
1009        let b = Decimal::from_str("2").unwrap();
1010        let c = Decimal::from_str("1").unwrap();
1011
1012        assert_eq!(a.cmp(&b), Ordering::Less);
1013        assert_eq!(b.cmp(&a), Ordering::Greater);
1014        assert_eq!(a.cmp(&c), Ordering::Equal);
1015    }
1016
1017    #[test]
1018    fn test_from_bytes_invalid() {
1019        // Empty bytes should fail
1020        let result = Decimal::from_bytes(&[]);
1021        assert!(result.is_err());
1022
1023        // Single invalid sign byte
1024        let result = Decimal::from_bytes(&[0x00]);
1025        assert!(result.is_err());
1026    }
1027
1028    #[test]
1029    fn test_deserialize_from_string_number() {
1030        // JSON string numbers deserialize correctly
1031        let d: Decimal = serde_json::from_str("\"42\"").unwrap();
1032        assert_eq!(d.to_string(), "42");
1033
1034        let d: Decimal = serde_json::from_str("\"-100\"").unwrap();
1035        assert_eq!(d.to_string(), "-100");
1036
1037        let d: Decimal = serde_json::from_str("\"1.5e10\"").unwrap();
1038        assert_eq!(d.to_string(), "15000000000");
1039    }
1040
1041    #[test]
1042    fn test_from_various_integer_types() {
1043        assert_eq!(Decimal::from(0i32).to_string(), "0");
1044        assert_eq!(Decimal::from(i32::MAX).to_string(), "2147483647");
1045        assert_eq!(Decimal::from(i32::MIN).to_string(), "-2147483648");
1046        assert_eq!(Decimal::from(i64::MAX).to_string(), "9223372036854775807");
1047        assert_eq!(Decimal::from(i64::MIN).to_string(), "-9223372036854775808");
1048    }
1049
1050    #[test]
1051    fn test_precision_overflow() {
1052        // Exponent too large
1053        let result = Decimal::from_str("1e20000");
1054        assert!(result.is_err());
1055        assert!(matches!(
1056            result.unwrap_err(),
1057            DecimalError::PrecisionOverflow
1058        ));
1059
1060        // Exponent too small (negative)
1061        let result = Decimal::from_str("1e-20000");
1062        assert!(result.is_err());
1063        assert!(matches!(
1064            result.unwrap_err(),
1065            DecimalError::PrecisionOverflow
1066        ));
1067    }
1068
1069    #[test]
1070    fn test_all_zeros_variations() {
1071        let d = Decimal::from_str("0").unwrap();
1072        assert!(d.is_zero());
1073
1074        let d = Decimal::from_str("0.0").unwrap();
1075        assert!(d.is_zero());
1076
1077        let d = Decimal::from_str("00.00").unwrap();
1078        assert!(d.is_zero());
1079
1080        let d = Decimal::from_str("-0").unwrap();
1081        assert!(d.is_zero());
1082        // Note: -0 normalizes to 0
1083    }
1084
1085    // ==================== Additional Edge Case Tests ====================
1086
1087    #[test]
1088    fn test_rounding_all_nines() {
1089        // Rounding 99.999 with scale 2 should become 100.00 -> 100
1090        let d = Decimal::with_precision_scale("99.999", Some(10), Some(2)).unwrap();
1091        assert_eq!(d.to_string(), "100");
1092
1093        // Rounding 9.99 with scale 1 should become 10.0 -> 10
1094        let d = Decimal::with_precision_scale("9.99", Some(10), Some(1)).unwrap();
1095        assert_eq!(d.to_string(), "10");
1096
1097        // 999 rounding to nearest 10 (scale -1) should become 1000
1098        let d = Decimal::with_precision_scale("999", Some(10), Some(-1)).unwrap();
1099        assert_eq!(d.to_string(), "1000");
1100    }
1101
1102    #[test]
1103    fn test_negative_scale_small_number() {
1104        // Number smaller than rounding unit, rounds to 0
1105        let d = Decimal::with_precision_scale("4", Some(10), Some(-1)).unwrap();
1106        assert_eq!(d.to_string(), "0");
1107
1108        // Number >= half the rounding unit, rounds up
1109        let d = Decimal::with_precision_scale("5", Some(10), Some(-1)).unwrap();
1110        assert_eq!(d.to_string(), "10");
1111
1112        // Negative number smaller than rounding unit
1113        let d = Decimal::with_precision_scale("-4", Some(10), Some(-1)).unwrap();
1114        assert_eq!(d.to_string(), "0");
1115
1116        // Negative number >= half unit
1117        let d = Decimal::with_precision_scale("-5", Some(10), Some(-1)).unwrap();
1118        assert_eq!(d.to_string(), "-10");
1119    }
1120
1121    #[test]
1122    fn test_precision_truncation() {
1123        // When precision is exceeded, truncate from left
1124        let d = Decimal::with_precision_scale("123456", Some(3), Some(0)).unwrap();
1125        assert_eq!(d.to_string(), "456");
1126
1127        // With decimal places
1128        let d = Decimal::with_precision_scale("12345.67", Some(4), Some(2)).unwrap();
1129        assert_eq!(d.to_string(), "45.67");
1130    }
1131
1132    #[test]
1133    fn test_very_small_numbers() {
1134        let d = Decimal::from_str("0.000000001").unwrap();
1135        assert_eq!(d.to_string(), "0.000000001");
1136        assert!(d.is_positive());
1137
1138        let d = Decimal::from_str("-0.000000001").unwrap();
1139        assert_eq!(d.to_string(), "-0.000000001");
1140        assert!(d.is_negative());
1141    }
1142
1143    #[test]
1144    fn test_very_large_numbers() {
1145        let d = Decimal::from_str("999999999999999999999999999999").unwrap();
1146        assert_eq!(d.to_string(), "999999999999999999999999999999");
1147
1148        let d = Decimal::from_str("-999999999999999999999999999999").unwrap();
1149        assert_eq!(d.to_string(), "-999999999999999999999999999999");
1150    }
1151
1152    #[test]
1153    fn test_max_exponent_boundary() {
1154        // Just under max exponent should work
1155        let d = Decimal::from_str("1e16000").unwrap();
1156        assert!(d.is_positive());
1157
1158        // Just over should fail
1159        let result = Decimal::from_str("1e17000");
1160        assert!(result.is_err());
1161    }
1162
1163    #[test]
1164    fn test_min_exponent_boundary() {
1165        // Just above min exponent should work
1166        let d = Decimal::from_str("1e-16000").unwrap();
1167        assert!(d.is_positive());
1168
1169        // Just below should fail
1170        let result = Decimal::from_str("1e-17000");
1171        assert!(result.is_err());
1172    }
1173
1174    #[test]
1175    fn test_odd_digit_count() {
1176        // Odd number of digits (tests BCD padding)
1177        let d = Decimal::from_str("12345").unwrap();
1178        assert_eq!(d.to_string(), "12345");
1179
1180        let d = Decimal::from_str("1").unwrap();
1181        assert_eq!(d.to_string(), "1");
1182
1183        let d = Decimal::from_str("123").unwrap();
1184        assert_eq!(d.to_string(), "123");
1185    }
1186
1187    #[test]
1188    fn test_negative_number_ordering() {
1189        // Verify negative number byte ordering is correct
1190        let a = Decimal::from_str("-100").unwrap();
1191        let b = Decimal::from_str("-10").unwrap();
1192        let c = Decimal::from_str("-1").unwrap();
1193
1194        // -100 < -10 < -1 numerically
1195        assert!(a < b);
1196        assert!(b < c);
1197
1198        // Byte ordering should match
1199        assert!(a.as_bytes() < b.as_bytes());
1200        assert!(b.as_bytes() < c.as_bytes());
1201    }
1202
1203    #[test]
1204    fn test_from_bytes_unchecked_roundtrip() {
1205        let original = Decimal::from_str("123.456").unwrap();
1206        let bytes = original.as_bytes().to_vec();
1207        let restored = Decimal::from_bytes_unchecked(bytes);
1208        assert_eq!(original, restored);
1209    }
1210
1211    #[test]
1212    fn test_special_value_checks() {
1213        let d = Decimal::from_str("123.456").unwrap();
1214        assert!(!d.is_nan());
1215        assert!(!d.is_infinity());
1216        assert!(!d.is_pos_infinity());
1217        assert!(!d.is_neg_infinity());
1218        assert!(!d.is_special());
1219        assert!(d.is_finite());
1220    }
1221
1222    #[test]
1223    fn test_equality_and_hash_consistency() {
1224        use std::collections::HashMap;
1225
1226        let d1 = Decimal::from_str("123.456").unwrap();
1227        let d2 = Decimal::from_str("123.456").unwrap();
1228        let d3 = Decimal::from_str("123.457").unwrap();
1229
1230        // Equal values should be equal
1231        assert_eq!(d1, d2);
1232        assert_ne!(d1, d3);
1233
1234        // Equal values should have same hash (can be used as map keys)
1235        let mut map = HashMap::new();
1236        map.insert(d1.clone(), "first");
1237        map.insert(d2.clone(), "second"); // Should overwrite
1238        assert_eq!(map.len(), 1);
1239        assert_eq!(map.get(&d1), Some(&"second"));
1240    }
1241
1242    #[test]
1243    fn test_scale_zero() {
1244        // Scale 0 should keep integer part only
1245        let d = Decimal::with_precision_scale("123.999", Some(10), Some(0)).unwrap();
1246        assert_eq!(d.to_string(), "124"); // Rounds up
1247    }
1248
1249    #[test]
1250    fn test_only_fractional_with_precision_scale() {
1251        let d = Decimal::with_precision_scale(".5", Some(10), Some(2)).unwrap();
1252        assert_eq!(d.to_string(), "0.5");
1253    }
1254
1255    #[test]
1256    fn test_default_impl() {
1257        let d = Decimal::default();
1258        assert!(d.is_zero());
1259        assert_eq!(d.to_string(), "0");
1260    }
1261
1262    #[test]
1263    fn test_precision_zero_integer_digits() {
1264        // When precision equals scale, no integer digits are allowed
1265        // This triggers the max_integer_digits == 0 branch
1266        let d = Decimal::with_precision_scale("123.456", Some(2), Some(2)).unwrap();
1267        assert_eq!(d.to_string(), "0.46");
1268    }
1269
1270    #[test]
1271    fn test_negative_with_precision_truncation() {
1272        // Negative number that gets truncated by precision constraints
1273        let d = Decimal::with_precision_scale("-123.456", Some(3), Some(2)).unwrap();
1274        assert_eq!(d.to_string(), "-3.46");
1275    }
1276
1277    #[test]
1278    fn test_invalid_sign_byte() {
1279        // Sign bytes: SIGN_NEGATIVE=0x00, SIGN_ZERO=0x80, SIGN_POSITIVE=0xFF
1280        // Any other sign byte is invalid
1281
1282        // Sign byte 0x01 is invalid
1283        let result = Decimal::from_bytes(&[0x01, 0x40, 0x00, 0x12]);
1284        assert!(result.is_err());
1285
1286        // Sign byte 0x7F is also invalid
1287        let result = Decimal::from_bytes(&[0x7F, 0x40, 0x00, 0x12]);
1288        assert!(result.is_err());
1289
1290        // Sign byte 0xFE is also invalid
1291        let result = Decimal::from_bytes(&[0xFE, 0x40, 0x00, 0x12]);
1292        assert!(result.is_err());
1293    }
1294
1295    #[test]
1296    fn test_invalid_bcd_encoding() {
1297        // Construct bytes that would decode to invalid BCD (digit > 9)
1298        // SIGN_POSITIVE = 0xFF, with valid exponent, but invalid BCD mantissa
1299        // A valid BCD digit must have each nibble 0-9. 0xAB has A=10, B=11 (both > 9)
1300        let invalid_bytes = vec![
1301            0xFF, // SIGN_POSITIVE
1302            0x80, 0x00, // Valid exponent (middle of range)
1303            0xAB, // Invalid BCD: high nibble = 10, low nibble = 11
1304        ];
1305        let result = Decimal::from_bytes(&invalid_bytes);
1306        assert!(result.is_err());
1307
1308        // Also test with just high nibble invalid
1309        let invalid_bytes = vec![
1310            0xFF, // SIGN_POSITIVE
1311            0x80, 0x00, // Valid exponent
1312            0xA1, // Invalid BCD: high nibble = 10, low nibble = 1
1313        ];
1314        let result = Decimal::from_bytes(&invalid_bytes);
1315        assert!(result.is_err());
1316
1317        // Also test with just low nibble invalid
1318        let invalid_bytes = vec![
1319            0xFF, // SIGN_POSITIVE
1320            0x80, 0x00, // Valid exponent
1321            0x1B, // Invalid BCD: high nibble = 1, low nibble = 11
1322        ];
1323        let result = Decimal::from_bytes(&invalid_bytes);
1324        assert!(result.is_err());
1325    }
1326
1327    #[test]
1328    fn test_reserved_exponent_positive() {
1329        // SIGN_POSITIVE = 0xFF
1330        // Reserved exponents: 0xFFFE (Infinity), 0xFFFF (NaN)
1331        // If we have more than 3 bytes, special value check is skipped
1332        // but decode_exponent will catch the reserved value
1333
1334        // Reserved NaN exponent (0xFFFF) with extra mantissa byte
1335        let bytes_with_reserved_exp = vec![
1336            0xFF, // SIGN_POSITIVE
1337            0xFF, 0xFF, // Reserved for NaN
1338            0x12, // Some mantissa (makes it 4 bytes, not 3)
1339        ];
1340        let result = Decimal::from_bytes(&bytes_with_reserved_exp);
1341        assert!(result.is_err());
1342
1343        // Reserved Infinity exponent (0xFFFE) with extra mantissa byte
1344        let bytes_with_reserved_exp = vec![
1345            0xFF, // SIGN_POSITIVE
1346            0xFF, 0xFE, // Reserved for Infinity
1347            0x12, // Some mantissa (makes it 4 bytes, not 3)
1348        ];
1349        let result = Decimal::from_bytes(&bytes_with_reserved_exp);
1350        assert!(result.is_err());
1351    }
1352
1353    #[test]
1354    fn test_reserved_exponent_negative() {
1355        // SIGN_NEGATIVE = 0x00
1356        // Reserved exponent for -Infinity: 0x0000
1357        // If we have more than 3 bytes, special value check is skipped
1358
1359        let bytes_with_reserved_exp = vec![
1360            0x00, // SIGN_NEGATIVE
1361            0x00, 0x00, // Reserved for -Infinity
1362            0x12, // Some mantissa (makes it 4 bytes, not 3)
1363        ];
1364        let result = Decimal::from_bytes(&bytes_with_reserved_exp);
1365        assert!(result.is_err());
1366    }
1367
1368    #[test]
1369    fn test_empty_mantissa_bytes() {
1370        // Construct bytes with valid sign and exponent but no mantissa
1371        // This should decode to 0 via the empty digits path in format_decimal
1372        let bytes_no_mantissa = vec![
1373            0xFF, // SIGN_POSITIVE
1374            0x80, 0x00, // Valid exponent
1375                  // No mantissa bytes
1376        ];
1377        let d = Decimal::from_bytes(&bytes_no_mantissa).unwrap();
1378        assert_eq!(d.to_string(), "0");
1379    }
1380
1381    // ==================== rust_decimal Interop Tests ====================
1382
1383    #[cfg(feature = "rust_decimal")]
1384    mod rust_decimal_tests {
1385        use super::*;
1386
1387        #[test]
1388        fn test_from_rust_decimal() {
1389            use rust_decimal::Decimal as RustDecimal;
1390
1391            let rd = RustDecimal::new(12345, 2); // 123.45
1392            let d: Decimal = rd.try_into().unwrap();
1393            assert_eq!(d.to_string(), "123.45");
1394        }
1395
1396        #[test]
1397        fn test_to_rust_decimal() {
1398            use rust_decimal::Decimal as RustDecimal;
1399
1400            let d = Decimal::from_str("123.45").unwrap();
1401            let rd: RustDecimal = (&d).try_into().unwrap();
1402            assert_eq!(rd.to_string(), "123.45");
1403        }
1404
1405        #[test]
1406        fn test_rust_decimal_roundtrip() {
1407            use rust_decimal::Decimal as RustDecimal;
1408
1409            let values = vec!["0", "1", "-1", "123.456", "-999.999", "0.001"];
1410
1411            for s in values {
1412                let d = Decimal::from_str(s).unwrap();
1413                let rd: RustDecimal = (&d).try_into().unwrap();
1414                let d2: Decimal = rd.try_into().unwrap();
1415                assert_eq!(d, d2, "Roundtrip failed for {}", s);
1416            }
1417        }
1418
1419        #[test]
1420        fn test_rust_decimal_arithmetic() {
1421            use rust_decimal::Decimal as RustDecimal;
1422
1423            // Start with decimal-bytes values
1424            let a = Decimal::from_str("100.50").unwrap();
1425            let b = Decimal::from_str("25.25").unwrap();
1426
1427            // Convert to rust_decimal for arithmetic
1428            let ra: RustDecimal = (&a).try_into().unwrap();
1429            let rb: RustDecimal = (&b).try_into().unwrap();
1430            let sum = ra + rb;
1431
1432            // Convert back to decimal-bytes for storage
1433            let result: Decimal = sum.try_into().unwrap();
1434            assert_eq!(result.to_string(), "125.75");
1435        }
1436
1437        #[test]
1438        fn test_rust_decimal_from_owned() {
1439            use rust_decimal::Decimal as RustDecimal;
1440
1441            // Test TryFrom<Decimal> (owned) for RustDecimal
1442            let d = Decimal::from_str("456.789").unwrap();
1443            let rd: RustDecimal = d.try_into().unwrap();
1444            assert_eq!(rd.to_string(), "456.789");
1445        }
1446
1447        #[test]
1448        fn test_rust_decimal_special_values_fail() {
1449            use rust_decimal::Decimal as RustDecimal;
1450
1451            // Infinity cannot convert to rust_decimal
1452            let inf = Decimal::infinity();
1453            let result: Result<RustDecimal, _> = (&inf).try_into();
1454            assert!(result.is_err());
1455
1456            // NaN cannot convert to rust_decimal
1457            let nan = Decimal::nan();
1458            let result: Result<RustDecimal, _> = (&nan).try_into();
1459            assert!(result.is_err());
1460        }
1461    }
1462
1463    // ==================== bigdecimal Interop Tests ====================
1464
1465    #[cfg(feature = "bigdecimal")]
1466    mod bigdecimal_tests {
1467        use super::*;
1468
1469        #[test]
1470        fn test_from_bigdecimal() {
1471            use bigdecimal::BigDecimal;
1472            use std::str::FromStr;
1473
1474            let bd = BigDecimal::from_str("123.45").unwrap();
1475            let d: Decimal = bd.try_into().unwrap();
1476            assert_eq!(d.to_string(), "123.45");
1477        }
1478
1479        #[test]
1480        fn test_to_bigdecimal() {
1481            use bigdecimal::BigDecimal;
1482
1483            let d = Decimal::from_str("123.45").unwrap();
1484            let bd: BigDecimal = (&d).try_into().unwrap();
1485            assert_eq!(bd.to_string(), "123.45");
1486        }
1487
1488        #[test]
1489        fn test_bigdecimal_roundtrip() {
1490            use bigdecimal::BigDecimal;
1491
1492            let values = vec!["0", "1", "-1", "123.456", "-999.999", "0.001"];
1493
1494            for s in values {
1495                let d = Decimal::from_str(s).unwrap();
1496                let bd: BigDecimal = (&d).try_into().unwrap();
1497                let d2: Decimal = bd.try_into().unwrap();
1498                assert_eq!(d, d2, "Roundtrip failed for {}", s);
1499            }
1500        }
1501
1502        #[test]
1503        fn test_bigdecimal_from_owned() {
1504            use bigdecimal::BigDecimal;
1505
1506            // Test TryFrom<Decimal> (owned) for BigDecimal
1507            let d = Decimal::from_str("456.789").unwrap();
1508            let bd: BigDecimal = d.try_into().unwrap();
1509            assert_eq!(bd.to_string(), "456.789");
1510        }
1511
1512        #[test]
1513        fn test_bigdecimal_from_ref() {
1514            use bigdecimal::BigDecimal;
1515            use std::str::FromStr;
1516
1517            // Test TryFrom<&BigDecimal> for Decimal
1518            let bd = BigDecimal::from_str("789.012").unwrap();
1519            let d: Decimal = (&bd).try_into().unwrap();
1520            assert_eq!(d.to_string(), "789.012");
1521        }
1522
1523        #[test]
1524        fn test_bigdecimal_special_values_fail() {
1525            use bigdecimal::BigDecimal;
1526
1527            // Infinity cannot convert to BigDecimal
1528            let inf = Decimal::infinity();
1529            let result: Result<BigDecimal, _> = (&inf).try_into();
1530            assert!(result.is_err());
1531
1532            // NaN cannot convert to BigDecimal
1533            let nan = Decimal::nan();
1534            let result: Result<BigDecimal, _> = (&nan).try_into();
1535            assert!(result.is_err());
1536        }
1537    }
1538}