Skip to main content

rupg_types/
numeric.rs

1//! `numeric`: the value in the layout of PostgreSQL, its text and binary forms, and the typmod.
2//!
3//! [`Numeric`] holds what `NumericVar` holds in `src/backend/utils/adt/numeric.c`: a sign, a weight, a display scale and base-10000 digits. The binary format has the same fields, so the receive and send functions copy them. The input, the output, the rounding and the typmod check are ports of `numeric_in`, `get_str_from_var`, `round_var` and `apply_typmod`, with the same errors in the same order: a syntax error comes before a typmod error, and a typmod error comes before the check that the weight and the scale fit the format.
4//!
5//! A `numeric(p, s)` column with p up to 38 is a scaled integer in the engine. [`decimal_out`] and [`decimal_send`] write that integer as PostgreSQL writes the same value, with no allocation, and [`Numeric::from_decimal`] and [`Numeric::to_decimal`] convert between the two forms.
6//!
7//! Lifted from `crates/rudb-pgtypes/src/numeric.rs` of tamnd/rudb at f5f7065a (spec/04 section 4.9).
8
9use rupg_common::SqlState;
10
11use crate::binary::Recv;
12use crate::error::TypeError;
13use crate::number::{digit, is_space, u64_out};
14use crate::typmod::numeric_precision_scale;
15
16mod math;
17
18const NBASE: i32 = 10000;
19const HALF_NBASE: i32 = 5000;
20const DEC_DIGITS: i64 = 4;
21/// `NUMERIC_DSCALE_MAX`, the largest display scale that the format can hold.
22const DSCALE_MAX: i64 = 0x3fff;
23/// `NUMERIC_MAX_RESULT_SCALE`: the most digits that `round` and `trunc` keep after the point.
24const NUMERIC_MAX_RESULT_SCALE: i32 = 2000;
25/// `round_powers` in `numeric.c`.
26const ROUND_POWERS: [i32; 4] = [0, 1000, 100, 10];
27/// The most base-10000 digits that a scaled `i128` needs: 39 decimal digits, split at the point.
28const DECIMAL_DIGITS_MAX: usize = 12;
29
30/// The sign of a value, which also marks the special values.
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub enum NumericSign {
33    Positive,
34    Negative,
35    NaN,
36    Infinity,
37    NegativeInfinity,
38}
39
40impl NumericSign {
41    /// `NUMERIC_POS`, `NUMERIC_NEG`, `NUMERIC_NAN`, `NUMERIC_PINF` and `NUMERIC_NINF`, the codes of the binary format.
42    pub const fn code(self) -> u16 {
43        match self {
44            NumericSign::Positive => 0x0000,
45            NumericSign::Negative => 0x4000,
46            NumericSign::NaN => 0xc000,
47            NumericSign::Infinity => 0xd000,
48            NumericSign::NegativeInfinity => 0xf000,
49        }
50    }
51
52    pub const fn from_code(code: u16) -> Option<NumericSign> {
53        Some(match code {
54            0x0000 => NumericSign::Positive,
55            0x4000 => NumericSign::Negative,
56            0xc000 => NumericSign::NaN,
57            0xd000 => NumericSign::Infinity,
58            0xf000 => NumericSign::NegativeInfinity,
59            _ => return None,
60        })
61    }
62
63    const fn is_finite(self) -> bool {
64        matches!(self, NumericSign::Positive | NumericSign::Negative)
65    }
66}
67
68/// A `numeric` value as PostgreSQL stores it. The digits have no zero at either end. A zero has no digits, a weight of 0 and a positive sign, and it keeps its display scale. A special value has only its sign.
69#[derive(Debug, Clone, PartialEq, Eq)]
70pub struct Numeric {
71    sign: NumericSign,
72    weight: i16,
73    dscale: u16,
74    digits: Vec<i16>,
75}
76
77impl Numeric {
78    pub const NAN: Numeric = Numeric::special(NumericSign::NaN);
79    pub const INFINITY: Numeric = Numeric::special(NumericSign::Infinity);
80    pub const NEGATIVE_INFINITY: Numeric = Numeric::special(NumericSign::NegativeInfinity);
81
82    const fn special(sign: NumericSign) -> Numeric {
83        Numeric { sign, weight: 0, dscale: 0, digits: Vec::new() }
84    }
85
86    pub fn sign(&self) -> NumericSign {
87        self.sign
88    }
89
90    /// The power of 10000 of the first digit.
91    pub fn weight(&self) -> i16 {
92        self.weight
93    }
94
95    /// The number of decimal digits that the output shows after the point.
96    pub fn dscale(&self) -> u16 {
97        self.dscale
98    }
99
100    /// The base-10000 digits, the most significant first.
101    pub fn digits(&self) -> &[i16] {
102        &self.digits
103    }
104
105    /// The value `value / 10^scale`, with `scale` as its display scale, as a cast from a `numeric(p, s)` column of the engine gives it.
106    pub fn from_decimal(value: i128, scale: u32) -> Numeric {
107        let mut buf = [0; DECIMAL_DIGITS_MAX];
108        let (digits, weight) = decimal_digits(value.unsigned_abs(), scale, &mut buf);
109        let sign = if value < 0 { NumericSign::Negative } else { NumericSign::Positive };
110        Numeric { sign, weight, dscale: scale as u16, digits: digits.to_vec() }
111    }
112
113    /// The value as an integer count of `10^-scale`, rounded half away from zero as `round_var` rounds. `None` for a special value or a value that does not fit in an `i128`.
114    pub fn to_decimal(&self, scale: u32) -> Option<i128> {
115        if !self.sign.is_finite() {
116            return None;
117        }
118        let mut var = Var::from(self);
119        var.round(i64::from(scale));
120        // The power of ten of the last digit, in units of 10^-scale. Rounding can leave up to three decimal zeros below the scale in the last digit, and they are dropped before they can overflow the accumulator.
121        let exp = (var.weight + 1 - var.digits.len() as i64) * DEC_DIGITS + i64::from(scale);
122        let last = var.digits.len().saturating_sub(1);
123        let mut acc: i128 = 0;
124        for (k, &d) in var.digits.iter().enumerate() {
125            let (mul, d) = match exp {
126                -3..0 if k == last => (10i128.pow((4 + exp) as u32), d / 10i32.pow(-exp as u32)),
127                _ => (i128::from(NBASE), d),
128            };
129            acc = acc.checked_mul(mul)?.checked_add(i128::from(d))?;
130        }
131        if exp > 0 && acc != 0 {
132            acc = acc.checked_mul(10i128.checked_pow(u32::try_from(exp).ok()?)?)?;
133        }
134        Some(if var.sign == NumericSign::Negative { -acc } else { acc })
135    }
136}
137
138impl Numeric {
139    /// The value of bytes in the layout of [`crate::numeric_layout`], which is how a vector holds a value of the type `NUMERIC`.
140    pub fn from_bytes(bytes: &[u8]) -> Numeric {
141        let unpacked = crate::numeric_layout::decode(bytes);
142        let sign = NumericSign::from_code(unpacked.sign).unwrap_or(NumericSign::NaN);
143        if !sign.is_finite() {
144            return Numeric::special(sign);
145        }
146        let sign = if unpacked.digits.is_empty() { NumericSign::Positive } else { sign };
147        Numeric { sign, weight: unpacked.weight, dscale: unpacked.dscale, digits: unpacked.digits }
148    }
149
150    /// The bytes of the value in the layout of [`crate::numeric_layout`].
151    pub fn to_bytes(&self) -> Vec<u8> {
152        crate::numeric_layout::encode(self.sign.code(), self.weight, self.dscale, &self.digits)
153    }
154
155    /// The value of an integer, with a display scale of 0, as `int8_numeric` gives it.
156    pub fn from_integer(value: i128) -> Numeric {
157        Numeric::from_decimal(value, 0)
158    }
159
160    /// `float8_numeric`: the value of the double printed with 15 significant digits, so that `0.1::float8::numeric` is `0.1`.
161    pub fn from_f64(value: f64) -> Numeric {
162        if value.is_nan() {
163            return Numeric::NAN;
164        }
165        if value.is_infinite() {
166            return if value > 0.0 { Numeric::INFINITY } else { Numeric::NEGATIVE_INFINITY };
167        }
168        // `%.15g` with the trailing zeros dropped from the digits. The exponent form gives the same value and the same display scale as the plain form does.
169        let text = format!("{value:.14e}");
170        let (mantissa, exponent) = text.split_once('e').unwrap_or((&text, "0"));
171        let mantissa = match mantissa.contains('.') {
172            true => mantissa.trim_end_matches('0').trim_end_matches('.'),
173            false => mantissa,
174        };
175        numeric_in(&format!("{mantissa}e{exponent}"), -1).unwrap_or(Numeric::NAN)
176    }
177
178    /// `numeric_float8`: the double nearest to the text of the value.
179    pub fn to_f64(&self) -> f64 {
180        match self.sign {
181            NumericSign::NaN => f64::NAN,
182            NumericSign::Infinity => f64::INFINITY,
183            NumericSign::NegativeInfinity => f64::NEG_INFINITY,
184            _ => {
185                let mut out = Vec::new();
186                numeric_out(self, &mut out);
187                std::str::from_utf8(&out)
188                    .ok()
189                    .and_then(|text| text.parse().ok())
190                    .unwrap_or(f64::NAN)
191            }
192        }
193    }
194
195    /// The value rounded half away from zero to an integer, as the casts to the integer types round. The error names the type, as `numeric_int4` and `numeric_int8` do.
196    pub fn to_integer(&self, type_name: &str) -> Result<i128, TypeError> {
197        let range = || {
198            TypeError::new(
199                SqlState::NUMERIC_VALUE_OUT_OF_RANGE,
200                format!("{type_name} out of range"),
201            )
202        };
203        match self.sign {
204            NumericSign::NaN => {
205                return Err(TypeError::new(
206                    SqlState::FEATURE_NOT_SUPPORTED,
207                    format!("cannot convert NaN to {type_name}"),
208                ));
209            }
210            NumericSign::Infinity | NumericSign::NegativeInfinity => {
211                return Err(TypeError::new(
212                    SqlState::FEATURE_NOT_SUPPORTED,
213                    format!("cannot convert infinity to {type_name}"),
214                ));
215            }
216            _ => {}
217        }
218        self.to_decimal(0).ok_or_else(range)
219    }
220
221    /// The value with `typmod` applied, as a cast to `numeric(p, s)` gives it.
222    pub fn with_typmod(&self, typmod: i32) -> Result<Numeric, TypeError> {
223        if !self.sign.is_finite() {
224            apply_typmod_special(self.sign, typmod)?;
225            return Ok(self.clone());
226        }
227        let mut var = Var::from(self);
228        var.apply_typmod(typmod)?;
229        var.make()
230    }
231
232    /// `numeric_round`: half away from zero to `scale` digits after the point, or before it when the scale is negative.
233    pub fn round(&self, scale: i32) -> Result<Numeric, TypeError> {
234        self.rescaled(scale, Var::round)
235    }
236
237    /// `numeric_trunc`: the digits after `scale` digits after the point dropped.
238    pub fn trunc(&self, scale: i32) -> Result<Numeric, TypeError> {
239        self.rescaled(scale, Var::trunc)
240    }
241
242    /// `numeric_round` and `numeric_trunc`, which differ in how they drop the digits.
243    fn rescaled(&self, scale: i32, drop: fn(&mut Var, i64)) -> Result<Numeric, TypeError> {
244        if !self.sign.is_finite() {
245            return Ok(self.clone());
246        }
247        let scale = i64::from(scale.clamp(-NUMERIC_MAX_RESULT_SCALE, NUMERIC_MAX_RESULT_SCALE));
248        let mut var = Var::from(self);
249        drop(&mut var, scale);
250        if scale < 0 {
251            var.dscale = 0;
252        }
253        var.make()
254    }
255
256    /// `numeric_ceil` when `up` and `numeric_floor` when not: the nearest whole number on that side.
257    pub fn whole(&self, up: bool) -> Result<Numeric, TypeError> {
258        let truncated = self.trunc(0)?;
259        let toward = if up { NumericSign::Positive } else { NumericSign::Negative };
260        if self.sign != toward || truncated.compare(self).is_eq() {
261            return Ok(truncated);
262        }
263        let one = Numeric::from_integer(if up { 1 } else { -1 });
264        truncated.add(&one)
265    }
266
267    /// `numeric_abs`.
268    pub fn abs(&self) -> Numeric {
269        match self.sign {
270            NumericSign::Negative => self.negate(),
271            NumericSign::NegativeInfinity => self.negate(),
272            _ => self.clone(),
273        }
274    }
275
276    /// `numeric_uminus`.
277    pub fn negate(&self) -> Numeric {
278        let sign = match self.sign {
279            NumericSign::Positive if !self.digits.is_empty() => NumericSign::Negative,
280            NumericSign::Negative => NumericSign::Positive,
281            NumericSign::Infinity => NumericSign::NegativeInfinity,
282            NumericSign::NegativeInfinity => NumericSign::Infinity,
283            sign => sign,
284        };
285        Numeric { sign, ..self.clone() }
286    }
287
288    /// `numeric_add`: the display scale is the larger of the two.
289    pub fn add(&self, other: &Numeric) -> Result<Numeric, TypeError> {
290        if let Some(special) = special_sum(self.sign, other.sign) {
291            return Ok(special);
292        }
293        Var::from(self).add(&Var::from(other)).make()
294    }
295
296    /// `numeric_sub`.
297    pub fn sub(&self, other: &Numeric) -> Result<Numeric, TypeError> {
298        self.add(&other.negate())
299    }
300
301    /// `numeric_mul`: the display scale is the sum of the two.
302    pub fn mul(&self, other: &Numeric) -> Result<Numeric, TypeError> {
303        use NumericSign::{Infinity, NaN, Negative, NegativeInfinity};
304        let (a, b) = (self.sign, other.sign);
305        if !a.is_finite() || !b.is_finite() {
306            if a == NaN || b == NaN {
307                return Ok(Numeric::NAN);
308            }
309            let zero = (a.is_finite() && self.digits.is_empty())
310                || (b.is_finite() && other.digits.is_empty());
311            if zero {
312                return Ok(Numeric::NAN);
313            }
314            let negative = |sign| matches!(sign, Negative | NegativeInfinity);
315            return Ok(if negative(a) != negative(b) {
316                Numeric::NEGATIVE_INFINITY
317            } else {
318                Numeric::special(Infinity)
319            });
320        }
321        let rscale = (i64::from(self.dscale) + i64::from(other.dscale)).min(DSCALE_MAX);
322        let mut product = Var::from(self).mul(&Var::from(other));
323        product.round(rscale);
324        product.make()
325    }
326
327    /// `numeric_div`: the result scale is the one `select_div_scale` picks, and the last digit is rounded half away from zero.
328    pub fn div(&self, other: &Numeric) -> Result<Numeric, TypeError> {
329        use NumericSign::{Infinity, NaN, Negative, NegativeInfinity, Positive};
330        let (a, b) = (self.sign, other.sign);
331        let zero = || TypeError::new(SqlState::DIVISION_BY_ZERO, "division by zero".to_string());
332        if !a.is_finite() || !b.is_finite() {
333            if a == NaN || b == NaN {
334                return Ok(Numeric::NAN);
335            }
336            if a.is_finite() {
337                // A number over an infinity is zero.
338                return Ok(Numeric { sign: Positive, weight: 0, dscale: 0, digits: Vec::new() });
339            }
340            if !b.is_finite() {
341                return Ok(Numeric::NAN);
342            }
343            if other.digits.is_empty() {
344                return Err(zero());
345            }
346            let negative = (a == NegativeInfinity) != (b == Negative);
347            return Ok(Numeric::special(if negative { NegativeInfinity } else { Infinity }));
348        }
349        let (x, y) = (Var::from(self), Var::from(other));
350        let rscale = select_div_scale(&x, &y);
351        if y.digits.is_empty() {
352            return Err(zero());
353        }
354        x.div(&y, rscale, true).make()
355    }
356
357    /// `numeric_mod`: the remainder of the quotient cut to an integer, with the sign of `self`.
358    pub fn modulo(&self, other: &Numeric) -> Result<Numeric, TypeError> {
359        let (a, b) = (self.sign, other.sign);
360        if !a.is_finite() || !b.is_finite() {
361            if a == NumericSign::NaN || b == NumericSign::NaN || !a.is_finite() {
362                return Ok(Numeric::NAN);
363            }
364            // A number modulo an infinity is the number.
365            return Ok(self.clone());
366        }
367        if other.digits.is_empty() {
368            return Err(TypeError::new(SqlState::DIVISION_BY_ZERO, "division by zero".to_string()));
369        }
370        Var::from(self).rem(&Var::from(other)).make()
371    }
372
373    /// `cmp_numerics`: `NaN` is equal to itself and above every other value, and the display scale does not count.
374    pub fn compare(&self, other: &Numeric) -> std::cmp::Ordering {
375        let left = self.to_bytes();
376        let right = other.to_bytes();
377        crate::numeric_layout::key(&left).cmp(crate::numeric_layout::key(&right))
378    }
379}
380
381/// The sum when one side is not a finite number, or `None` when both are.
382fn special_sum(a: NumericSign, b: NumericSign) -> Option<Numeric> {
383    use NumericSign::{Infinity, NaN, NegativeInfinity};
384    match (a, b) {
385        _ if a.is_finite() && b.is_finite() => None,
386        (NaN, _) | (_, NaN) => Some(Numeric::NAN),
387        (Infinity, NegativeInfinity) | (NegativeInfinity, Infinity) => Some(Numeric::NAN),
388        (Infinity, _) | (_, Infinity) => Some(Numeric::INFINITY),
389        _ => Some(Numeric::NEGATIVE_INFINITY),
390    }
391}
392
393/// `NUMERIC_MIN_SIG_DIGITS`, the fewest significant digits that a quotient has.
394const MIN_SIG_DIGITS: i64 = 16;
395
396/// `select_div_scale`: enough digits after the point for 16 significant digits, and no fewer than the display scale of either side.
397fn select_div_scale(x: &Var, y: &Var) -> i64 {
398    let first = |v: &Var| match v.digits.iter().position(|&d| d != 0) {
399        Some(at) => (v.weight - at as i64, v.digits[at]),
400        None => (0, 0),
401    };
402    let (weight1, first1) = first(x);
403    let (weight2, first2) = first(y);
404    let mut qweight = weight1 - weight2;
405    if first1 <= first2 {
406        qweight -= 1;
407    }
408    let rscale = MIN_SIG_DIGITS - qweight * DEC_DIGITS;
409    rscale.max(x.dscale).max(y.dscale).clamp(0, 1000)
410}
411
412/// `NumericVar`: the working form. The weight and the scale can go out of the range of the format here, and [`Var::make`] checks them at the end, as `make_result` does.
413#[derive(Debug, Clone)]
414struct Var {
415    sign: NumericSign,
416    weight: i64,
417    dscale: i64,
418    digits: Vec<i32>,
419}
420
421impl From<&Numeric> for Var {
422    fn from(v: &Numeric) -> Var {
423        Var {
424            sign: v.sign,
425            weight: i64::from(v.weight),
426            dscale: i64::from(v.dscale),
427            digits: v.digits.iter().map(|&d| i32::from(d)).collect(),
428        }
429    }
430}
431
432impl Var {
433    /// The end of `set_var_from_str`: the value of the decimal digits `dec`, where the first digit has the power of ten `dweight`.
434    fn from_decimal_digits(dec: &[u8], dweight: i64, dscale: i64) -> Var {
435        let weight = if dweight >= 0 {
436            (dweight + DEC_DIGITS) / DEC_DIGITS - 1
437        } else {
438            -((-dweight - 1) / DEC_DIGITS + 1)
439        };
440        // The number of decimal zeros before the first digit that align it in its NBASE digit.
441        let offset = (weight + 1) * DEC_DIGITS - (dweight + 1);
442        let ndigits = (dec.len() as i64 + offset + DEC_DIGITS - 1) / DEC_DIGITS;
443        let at = |k: i64| match usize::try_from(k - offset) {
444            Ok(k) if k < dec.len() => i32::from(dec[k]),
445            _ => 0,
446        };
447        let digits = (0..ndigits)
448            .map(|n| {
449                let k = n * DEC_DIGITS;
450                ((at(k) * 10 + at(k + 1)) * 10 + at(k + 2)) * 10 + at(k + 3)
451            })
452            .collect();
453        let mut var = Var { sign: NumericSign::Positive, weight, dscale, digits };
454        var.strip();
455        var
456    }
457
458    /// `strip_var`.
459    fn strip(&mut self) {
460        let lead = self.digits.iter().take_while(|&&d| d == 0).count();
461        self.digits.drain(..lead);
462        self.weight -= lead as i64;
463        while self.digits.last() == Some(&0) {
464            self.digits.pop();
465        }
466        if self.digits.is_empty() {
467            self.sign = NumericSign::Positive;
468            self.weight = 0;
469        }
470    }
471
472    /// `round_var`: rounds half away from zero to `rscale` digits after the point. A negative `rscale` rounds before the point.
473    fn round(&mut self, rscale: i64) {
474        self.dscale = rscale;
475        let di = (self.weight + 1) * DEC_DIGITS + rscale;
476        if di < 0 {
477            self.digits.clear();
478            self.weight = 0;
479            self.sign = NumericSign::Positive;
480            return;
481        }
482        let n = ((di + DEC_DIGITS - 1) / DEC_DIGITS) as usize;
483        let di = (di % DEC_DIGITS) as usize;
484        if n > self.digits.len() || (n == self.digits.len() && di == 0) {
485            return;
486        }
487        let (mut i, mut carry) = if di == 0 {
488            (n, i32::from(self.digits[n] >= HALF_NBASE))
489        } else {
490            // Round inside the last NBASE digit.
491            let i = n - 1;
492            let pow10 = ROUND_POWERS[di];
493            let extra = self.digits[i] % pow10;
494            self.digits[i] -= extra;
495            let mut carry = 0;
496            if extra >= pow10 / 2 {
497                self.digits[i] += pow10;
498                if self.digits[i] >= NBASE {
499                    self.digits[i] -= NBASE;
500                    carry = 1;
501                }
502            }
503            (i, carry)
504        };
505        self.digits.truncate(n);
506        while carry != 0 {
507            if i == 0 {
508                self.digits.insert(0, carry);
509                self.weight += 1;
510                break;
511            }
512            i -= 1;
513            self.digits[i] += carry;
514            carry = 0;
515            if self.digits[i] >= NBASE {
516                self.digits[i] -= NBASE;
517                carry = 1;
518            }
519        }
520    }
521
522    /// `trunc_var`: drops the digits after `rscale` digits after the point.
523    fn trunc(&mut self, rscale: i64) {
524        self.dscale = rscale;
525        let di = (self.weight + 1) * DEC_DIGITS + rscale;
526        if di <= 0 {
527            self.digits.clear();
528            self.weight = 0;
529            self.sign = NumericSign::Positive;
530            return;
531        }
532        let n = ((di + DEC_DIGITS - 1) / DEC_DIGITS) as usize;
533        if n <= self.digits.len() {
534            self.digits.truncate(n);
535            let di = (di % DEC_DIGITS) as usize;
536            if di > 0 {
537                self.digits[n - 1] -= self.digits[n - 1] % ROUND_POWERS[di];
538            }
539        }
540    }
541
542    /// `apply_typmod`: rounds to the scale of the typmod and checks the digits before the point.
543    fn apply_typmod(&mut self, typmod: i32) -> Result<(), TypeError> {
544        let Some((precision, scale)) = numeric_precision_scale(typmod) else {
545            return Ok(());
546        };
547        let maxdigits = i64::from(precision - scale);
548        self.round(i64::from(scale));
549        self.dscale = self.dscale.max(0);
550        // The weight can count leading zero digits that `make` strips later, and a zero has no weight that means anything, so find the first digit that is not zero.
551        let mut ddigits = (self.weight + 1) * DEC_DIGITS;
552        if ddigits > maxdigits {
553            for &dig in &self.digits {
554                if dig != 0 {
555                    ddigits -= match dig {
556                        0..10 => 3,
557                        10..100 => 2,
558                        100..1000 => 1,
559                        _ => 0,
560                    };
561                    if ddigits > maxdigits {
562                        let limit = match maxdigits {
563                            0 => "1".to_string(),
564                            n => format!("10^{n}"),
565                        };
566                        return Err(field_overflow(format!(
567                            "A field with precision {precision}, scale {scale} must round to an absolute value less than {limit}."
568                        )));
569                    }
570                    break;
571                }
572                ddigits -= DEC_DIGITS;
573            }
574        }
575        Ok(())
576    }
577
578    /// `make_result`: strips the zeros and checks that the weight and the scale fit the format.
579    fn make(mut self) -> Result<Numeric, TypeError> {
580        if !self.sign.is_finite() {
581            return Ok(Numeric::special(self.sign));
582        }
583        self.strip();
584        match (i16::try_from(self.weight), u16::try_from(self.dscale)) {
585            (Ok(weight), Ok(dscale)) if self.dscale <= DSCALE_MAX => Ok(Numeric {
586                sign: self.sign,
587                weight,
588                dscale,
589                digits: self.digits.iter().map(|&d| d as i16).collect(),
590            }),
591            _ => Err(overflow()),
592        }
593    }
594}
595
596impl Var {
597    /// The digit with the power of 10000 `weight`, which is 0 outside the digits.
598    fn digit(&self, weight: i64) -> i32 {
599        usize::try_from(self.weight - weight)
600            .ok()
601            .and_then(|at| self.digits.get(at))
602            .copied()
603            .unwrap_or(0)
604    }
605
606    /// The power of 10000 of the last digit.
607    fn low(&self) -> i64 {
608        self.weight + 1 - self.digits.len() as i64
609    }
610
611    /// `cmp_abs`.
612    fn cmp_abs(&self, other: &Var) -> std::cmp::Ordering {
613        let high = self.weight.max(other.weight);
614        let low = self.low().min(other.low());
615        for weight in (low..=high).rev() {
616            match self.digit(weight).cmp(&other.digit(weight)) {
617                std::cmp::Ordering::Equal => {}
618                order => return order,
619            }
620        }
621        std::cmp::Ordering::Equal
622    }
623
624    /// `add_abs` and `sub_abs` in one: the sum of the two magnitudes, or the difference when `subtract` is set, which needs the magnitude of `self` to be the larger one.
625    fn add_abs(&self, other: &Var, subtract: bool, sign: NumericSign) -> Var {
626        let weight = self.weight.max(other.weight) + 1;
627        let low = self.low().min(other.low());
628        let mut digits = vec![0; (weight - low + 1) as usize];
629        let mut carry = 0;
630        for (at, w) in (low..=weight).enumerate() {
631            let other = other.digit(w);
632            let mut d = self.digit(w) + carry + if subtract { -other } else { other };
633            carry = 0;
634            if d >= NBASE {
635                d -= NBASE;
636                carry = 1;
637            } else if d < 0 {
638                d += NBASE;
639                carry = -1;
640            }
641            let end = digits.len() - 1 - at;
642            digits[end] = d;
643        }
644        let mut var = Var { sign, weight, dscale: self.dscale.max(other.dscale), digits };
645        var.strip();
646        var
647    }
648
649    /// `add_var`.
650    fn add(&self, other: &Var) -> Var {
651        if self.sign == other.sign {
652            return self.add_abs(other, false, self.sign);
653        }
654        match self.cmp_abs(other) {
655            std::cmp::Ordering::Less => other.add_abs(self, true, other.sign),
656            _ => self.add_abs(other, true, self.sign),
657        }
658    }
659
660    /// `mod_var`: the remainder of the quotient cut to an integer, with the sign of `self`. The divisor is not zero.
661    fn rem(&self, other: &Var) -> Var {
662        let mut product = other.mul(&self.div(other, 0, false));
663        product.dscale = other.dscale;
664        product.sign = match product.sign {
665            NumericSign::Positive if !product.digits.is_empty() => NumericSign::Negative,
666            _ => NumericSign::Positive,
667        };
668        self.add(&product)
669    }
670
671    /// The exact product, with no rounding. The display scale is set by the caller.
672    fn mul(&self, other: &Var) -> Var {
673        let sign =
674            if self.sign == other.sign { NumericSign::Positive } else { NumericSign::Negative };
675        if self.digits.is_empty() || other.digits.is_empty() {
676            return Var { sign: NumericSign::Positive, weight: 0, dscale: 0, digits: Vec::new() };
677        }
678        let mut acc = vec![0i64; self.digits.len() + other.digits.len()];
679        for (i, &a) in self.digits.iter().enumerate() {
680            for (j, &b) in other.digits.iter().enumerate() {
681                acc[i + j + 1] += i64::from(a) * i64::from(b);
682            }
683        }
684        let mut carry = 0;
685        for slot in acc.iter_mut().rev() {
686            let total = *slot + carry;
687            *slot = total % i64::from(NBASE);
688            carry = total / i64::from(NBASE);
689        }
690        let digits = acc.into_iter().map(|d| d as i32).collect();
691        let weight = self.weight + other.weight + 1;
692        let mut var = Var { sign, weight, dscale: self.dscale + other.dscale, digits };
693        var.strip();
694        var
695    }
696
697    /// `div_var` with `exact` set: the quotient to `rscale` digits after the point, rounded half away from zero when `round` is set and cut when it is not. The divisor is not zero.
698    fn div(&self, other: &Var, rscale: i64, round: bool) -> Var {
699        let sign =
700            if self.sign == other.sign { NumericSign::Positive } else { NumericSign::Negative };
701        // The quotient is cut one digit of 10000 below the scale and then rounded. The cut keeps every digit above it, so the rounding sees the same digit as for the exact quotient.
702        let low = -((rscale + DEC_DIGITS - 1) / DEC_DIGITS + 1);
703        let shift = self.low() - other.low() - low;
704        let mut dividend: Vec<i64> = self.digits.iter().map(|&d| i64::from(d)).collect();
705        if shift >= 0 {
706            dividend.resize(dividend.len() + shift as usize, 0);
707        } else {
708            let keep = dividend.len().saturating_sub((-shift) as usize);
709            dividend.truncate(keep);
710        }
711        let divisor: Vec<i64> = other.digits.iter().map(|&d| i64::from(d)).collect();
712        let quotient = divide(&dividend, &divisor);
713        let weight = low + quotient.len() as i64 - 1;
714        let digits = quotient.into_iter().map(|d| d as i32).collect();
715        let mut var = Var { sign, weight, dscale: rscale, digits };
716        var.strip();
717        if round {
718            var.round(rscale);
719        } else {
720            var.trunc(rscale);
721        }
722        var.strip();
723        if var.digits.is_empty() {
724            var.sign = NumericSign::Positive;
725        }
726        var
727    }
728}
729
730/// The integer quotient of two numbers in base 10000, the most significant digit first, by algorithm D of Knuth (TAOCP volume 2, section 4.3.1). The divisor has no leading zero.
731fn divide(dividend: &[i64], divisor: &[i64]) -> Vec<i64> {
732    let base = i64::from(NBASE);
733    let n = divisor.len();
734    if dividend.len() < n {
735        return Vec::new();
736    }
737    if n == 1 {
738        let d = divisor[0];
739        let mut rest = 0;
740        return dividend
741            .iter()
742            .map(|&digit| {
743                let current = rest * base + digit;
744                rest = current % d;
745                current / d
746            })
747            .collect();
748    }
749    // Scale both so that the first digit of the divisor is at least half the base, which keeps each trial quotient at most 2 above the true digit.
750    let scale = base / (divisor[0] + 1);
751    let times = |digits: &[i64]| -> Vec<i64> {
752        let mut out = vec![0; digits.len() + 1];
753        let mut carry = 0;
754        for (at, &digit) in digits.iter().enumerate().rev() {
755            let total = digit * scale + carry;
756            out[at + 1] = total % base;
757            carry = total / base;
758        }
759        out[0] = carry;
760        out
761    };
762    let mut u = times(dividend);
763    let v = times(divisor);
764    let v = &v[1..];
765    let m = dividend.len() - n;
766    let mut q = vec![0; m + 1];
767    for j in 0..=m {
768        let top = u[j] * base + u[j + 1];
769        let mut qhat = top / v[0];
770        let mut rhat = top % v[0];
771        while qhat >= base || qhat * v[1] > rhat * base + u[j + 2] {
772            qhat -= 1;
773            rhat += v[0];
774            if rhat >= base {
775                break;
776            }
777        }
778        // Take qhat times the divisor away from the window of the dividend.
779        let mut borrow = 0;
780        let mut carry = 0;
781        for i in (0..n).rev() {
782            let product = qhat * v[i] + carry;
783            carry = product / base;
784            let mut d = u[j + i + 1] - product % base - borrow;
785            borrow = 0;
786            if d < 0 {
787                d += base;
788                borrow = 1;
789            }
790            u[j + i + 1] = d;
791        }
792        let d = u[j] - carry - borrow;
793        if d < 0 {
794            // The trial digit was one too large: add the divisor back.
795            qhat -= 1;
796            let mut carry = 0;
797            for i in (0..n).rev() {
798                let total = u[j + i + 1] + v[i] + carry;
799                u[j + i + 1] = total % base;
800                carry = total / base;
801            }
802            u[j] = d + carry;
803        } else {
804            u[j] = d;
805        }
806        q[j] = qhat;
807    }
808    q
809}
810
811fn overflow() -> TypeError {
812    TypeError::new(
813        SqlState::NUMERIC_VALUE_OUT_OF_RANGE,
814        "value overflows numeric format".to_string(),
815    )
816}
817
818fn field_overflow(detail: String) -> TypeError {
819    let mut error =
820        TypeError::new(SqlState::NUMERIC_VALUE_OUT_OF_RANGE, "numeric field overflow".to_string());
821    error.detail = Some(detail);
822    error
823}
824
825/// `apply_typmod_special`: `NaN` fits any typmod and an infinity fits none.
826fn apply_typmod_special(sign: NumericSign, typmod: i32) -> Result<(), TypeError> {
827    match numeric_precision_scale(typmod) {
828        Some((precision, scale)) if sign != NumericSign::NaN => Err(field_overflow(format!(
829            "A field with precision {precision}, scale {scale} cannot hold an infinite value."
830        ))),
831        _ => Ok(()),
832    }
833}
834
835/// The text input of `numeric` with the typmod of the target, -1 for none.
836pub fn numeric_in(s: &str, typmod: i32) -> Result<Numeric, TypeError> {
837    let b = s.as_bytes();
838    let at = |i: usize| b.get(i).copied().unwrap_or(0);
839    let syntax = || TypeError::syntax("numeric", s);
840    let rest_is_space = |mut i: usize| {
841        while i < b.len() {
842            if !is_space(b[i]) {
843                return Err(syntax());
844            }
845            i += 1;
846        }
847        Ok(())
848    };
849
850    let mut i = 0;
851    while is_space(at(i)) {
852        i += 1;
853    }
854    let start = i;
855    let negative = at(i) == b'-';
856    if negative || at(i) == b'+' {
857        i += 1;
858    }
859
860    // Each other input has a digit or a point after the sign. `NaN` cannot have a sign.
861    if !at(i).is_ascii_digit() && at(i) != b'.' {
862        let word = |from: usize, word: &str| {
863            b.get(from..from + word.len()).is_some_and(|w| w.eq_ignore_ascii_case(word.as_bytes()))
864        };
865        let infinity = if negative { NumericSign::NegativeInfinity } else { NumericSign::Infinity };
866        let (sign, end) = if word(start, "nan") {
867            (NumericSign::NaN, start + 3)
868        } else if word(i, "infinity") {
869            (infinity, i + 8)
870        } else if word(i, "inf") {
871            (infinity, i + 3)
872        } else {
873            return Err(syntax());
874        };
875        rest_is_space(end)?;
876        apply_typmod_special(sign, typmod)?;
877        return Ok(Numeric::special(sign));
878    }
879
880    let base = match (at(i), at(i + 1)) {
881        (b'0', b'x' | b'X') => 16,
882        (b'0', b'o' | b'O') => 8,
883        (b'0', b'b' | b'B') => 2,
884        _ => 10,
885    };
886    let (mut var, end) = match base {
887        10 => decimal(b, i, s)?,
888        _ => non_decimal(b, i + 2, base, s)?,
889    };
890    if negative {
891        var.sign = NumericSign::Negative;
892    }
893    rest_is_space(end)?;
894    var.apply_typmod(typmod)?;
895    var.make()
896}
897
898/// `set_var_from_str` from the first byte after the sign.
899fn decimal(b: &[u8], mut i: usize, input: &str) -> Result<(Var, usize), TypeError> {
900    let at = |i: usize| b.get(i).copied().unwrap_or(0);
901    let syntax = || TypeError::syntax("numeric", input);
902    let mut have_point = at(i) == b'.';
903    if have_point {
904        i += 1;
905    }
906    if !at(i).is_ascii_digit() {
907        return Err(syntax());
908    }
909    let mut dec = Vec::with_capacity(b.len() - i);
910    let mut dweight: i64 = -1;
911    let mut dscale: i64 = 0;
912    loop {
913        match at(i) {
914            c @ b'0'..=b'9' => {
915                dec.push(c - b'0');
916                i += 1;
917                if have_point {
918                    dscale += 1;
919                } else {
920                    dweight += 1;
921                }
922            }
923            b'.' => {
924                // A second point, or a point with an underscore after it, is an error.
925                if have_point || at(i + 1) == b'_' {
926                    return Err(syntax());
927                }
928                have_point = true;
929                i += 1;
930            }
931            b'_' => {
932                i += 1;
933                if !at(i).is_ascii_digit() {
934                    return Err(syntax());
935                }
936            }
937            _ => break,
938        }
939    }
940
941    if matches!(at(i), b'e' | b'E') {
942        i += 1;
943        let negative = at(i) == b'-';
944        if negative || at(i) == b'+' {
945            i += 1;
946        }
947        if !at(i).is_ascii_digit() {
948            return Err(syntax());
949        }
950        let mut exponent: i64 = 0;
951        loop {
952            match at(i) {
953                c @ b'0'..=b'9' => {
954                    exponent = exponent * 10 + i64::from(c - b'0');
955                    i += 1;
956                    if exponent > i64::from(i32::MAX / 2) {
957                        return Err(overflow());
958                    }
959                }
960                b'_' => {
961                    i += 1;
962                    if !at(i).is_ascii_digit() {
963                        return Err(syntax());
964                    }
965                }
966                _ => break,
967            }
968        }
969        if negative {
970            exponent = -exponent;
971        }
972        dweight += exponent;
973        dscale = (dscale - exponent).max(0);
974    }
975    Ok((Var::from_decimal_digits(&dec, dweight, dscale), i))
976}
977
978/// `set_var_from_non_decimal_integer_str` from the first digit after the prefix. The digits go in groups that fit in an `i64`, and the weight is checked after each group, as PostgreSQL does, so a value that is too large fails before a syntax error after it.
979fn non_decimal(b: &[u8], mut i: usize, base: u64, input: &str) -> Result<(Var, usize), TypeError> {
980    let at = |i: usize| b.get(i).copied().unwrap_or(0);
981    let syntax = || TypeError::syntax("numeric", input);
982    // The value so far in base-10000 digits, the least significant first.
983    let mut value: Vec<i32> = Vec::new();
984    let add_group = |value: &mut Vec<i32>, tmp: u64, mul: u64| {
985        let mut carry = u128::from(tmp);
986        for d in value.iter_mut() {
987            let v = *d as u128 * u128::from(mul) + carry;
988            *d = (v % NBASE as u128) as i32;
989            carry = v / NBASE as u128;
990        }
991        while carry > 0 {
992            value.push((carry % NBASE as u128) as i32);
993            carry /= NBASE as u128;
994        }
995        if value.len() > i16::MAX as usize + 1 { Err(overflow()) } else { Ok(()) }
996    };
997    let first = i;
998    let (mut tmp, mut mul) = (0u64, 1u64);
999    loop {
1000        if let Some(d) = digit(at(i), base) {
1001            if mul > i64::MAX as u64 / base {
1002                add_group(&mut value, tmp, mul)?;
1003                (tmp, mul) = (0, 1);
1004            }
1005            tmp = tmp * base + d;
1006            mul *= base;
1007            i += 1;
1008        } else if at(i) == b'_' {
1009            i += 1;
1010            if digit(at(i), base).is_none() {
1011                return Err(syntax());
1012            }
1013        } else {
1014            break;
1015        }
1016    }
1017    if i == first {
1018        return Err(syntax());
1019    }
1020    add_group(&mut value, tmp, mul)?;
1021    value.reverse();
1022    let mut var = Var {
1023        sign: NumericSign::Positive,
1024        weight: value.len() as i64 - 1,
1025        dscale: 0,
1026        digits: value,
1027    };
1028    var.strip();
1029    Ok((var, i))
1030}
1031
1032/// Writes four decimal digits.
1033fn four(dig: i32, out: &mut Vec<u8>) {
1034    let dig = dig as u16;
1035    out.extend_from_slice(&[
1036        b'0' + (dig / 1000) as u8,
1037        b'0' + (dig / 100 % 10) as u8,
1038        b'0' + (dig / 10 % 10) as u8,
1039        b'0' + (dig % 10) as u8,
1040    ]);
1041}
1042
1043/// The text output of `numeric`: the digits with exactly the display scale after the point.
1044pub fn numeric_out(v: &Numeric, out: &mut Vec<u8>) {
1045    match v.sign {
1046        NumericSign::NaN => return out.extend_from_slice(b"NaN"),
1047        NumericSign::Infinity => return out.extend_from_slice(b"Infinity"),
1048        NumericSign::NegativeInfinity => return out.extend_from_slice(b"-Infinity"),
1049        NumericSign::Negative => out.push(b'-'),
1050        NumericSign::Positive => {}
1051    }
1052    let at = |d: i32| match usize::try_from(d) {
1053        Ok(d) if d < v.digits.len() => i32::from(v.digits[d]),
1054        _ => 0,
1055    };
1056    let weight = i32::from(v.weight);
1057    if weight < 0 {
1058        out.push(b'0');
1059    } else {
1060        // The first digit has no leading zeros.
1061        u64_out(at(0) as u64, out);
1062        for d in 1..=weight {
1063            four(at(d), out);
1064        }
1065    }
1066    let dscale = usize::from(v.dscale);
1067    if dscale > 0 {
1068        out.push(b'.');
1069        let end = out.len() + dscale;
1070        let mut d = weight + 1;
1071        while out.len() < end {
1072            four(at(d), out);
1073            d += 1;
1074        }
1075        out.truncate(end);
1076    }
1077}
1078
1079/// `numeric_out_sci`: the value as a significand with `scale` digits after the point, `e`, a sign and at least two digits of the exponent, as `to_char` with `EEEE` writes it. The exponent is the power of ten of the first digit. The significand is the value divided by that power and rounded half away from zero, so it can be `10.0` after the rounding, as in `get_str_from_var_sci`.
1080pub fn numeric_out_sci(v: &Numeric, scale: i32) -> String {
1081    match v.sign {
1082        NumericSign::NaN => return "NaN".to_string(),
1083        NumericSign::Infinity => return "Infinity".to_string(),
1084        NumericSign::NegativeInfinity => return "-Infinity".to_string(),
1085        _ => {}
1086    }
1087    let rscale = i64::from(scale.max(0));
1088    let (exponent, significand) = match v.digits.first() {
1089        Some(&first) => {
1090            let exponent = i64::from(v.weight) * DEC_DIGITS + i64::from(first.ilog10());
1091            let power = Var::from_decimal_digits(&[1], exponent, 0);
1092            (exponent, Var::from(v).div(&power, rscale, true))
1093        }
1094        None => {
1095            (0, Var { sign: NumericSign::Positive, weight: 0, dscale: rscale, digits: Vec::new() })
1096        }
1097    };
1098    let significand = Numeric {
1099        sign: significand.sign,
1100        weight: significand.weight as i16,
1101        dscale: significand.dscale as u16,
1102        digits: significand.digits.iter().map(|&d| d as i16).collect(),
1103    };
1104    let mut out = Vec::new();
1105    numeric_out(&significand, &mut out);
1106    format!("{}e{exponent:+03}", String::from_utf8_lossy(&out))
1107}
1108
1109/// `numeric_send`. PostgreSQL reads the display scale of an infinity from bits of the header that the short format uses for the scale, so it sends 32 there. Clients do not look at it, but the bytes are the same.
1110pub fn numeric_send(v: &Numeric, out: &mut Vec<u8>) {
1111    let dscale = match v.sign {
1112        NumericSign::Infinity | NumericSign::NegativeInfinity => 32,
1113        _ => v.dscale,
1114    };
1115    write_binary(v.sign, v.weight, dscale, &v.digits, out);
1116}
1117
1118fn write_binary(sign: NumericSign, weight: i16, dscale: u16, digits: &[i16], out: &mut Vec<u8>) {
1119    out.reserve(8 + 2 * digits.len());
1120    out.extend_from_slice(&(digits.len() as u16).to_be_bytes());
1121    out.extend_from_slice(&weight.to_be_bytes());
1122    out.extend_from_slice(&sign.code().to_be_bytes());
1123    out.extend_from_slice(&dscale.to_be_bytes());
1124    for &d in digits {
1125        out.extend_from_slice(&d.to_be_bytes());
1126    }
1127}
1128
1129/// `numeric_recv`. Digits hidden by the display scale are dropped, and the typmod is applied.
1130pub fn numeric_recv(recv: &mut Recv<'_>, typmod: i32) -> Result<Numeric, TypeError> {
1131    let bad = |what: &str| {
1132        TypeError::new(
1133            SqlState::INVALID_BINARY_REPRESENTATION,
1134            format!("invalid {what} in external \"numeric\" value"),
1135        )
1136    };
1137    let len = recv.i16()? as u16;
1138    let weight = recv.i16()?;
1139    let sign = NumericSign::from_code(recv.i16()? as u16).ok_or_else(|| bad("sign"))?;
1140    let dscale = recv.i16()? as u16;
1141    if i64::from(dscale) > DSCALE_MAX {
1142        return Err(bad("scale"));
1143    }
1144    let mut digits = Vec::new();
1145    for _ in 0..len {
1146        let d = recv.i16()?;
1147        if !(0..NBASE as i16).contains(&d) {
1148            return Err(bad("digit"));
1149        }
1150        digits.push(i32::from(d));
1151    }
1152    if !sign.is_finite() {
1153        apply_typmod_special(sign, typmod)?;
1154        return Ok(Numeric::special(sign));
1155    }
1156    let mut var = Var { sign, weight: i64::from(weight), dscale: i64::from(dscale), digits };
1157    var.trunc(i64::from(dscale));
1158    var.apply_typmod(typmod)?;
1159    var.make()
1160}
1161
1162/// The base-10000 digits of `abs / 10^scale` with no zero at either end, and the weight of the first one. The digits are aligned to the decimal point, so the last group of the fraction has zeros after its last decimal digit.
1163fn decimal_digits(abs: u128, scale: u32, buf: &mut [i16; DECIMAL_DIGITS_MAX]) -> (&[i16], i16) {
1164    assert!(scale <= 38, "a decimal has at most 38 digits after the point");
1165    let pow = 10u128.pow(scale);
1166    let (mut int, mut frac) = (abs / pow, abs % pow);
1167    let mut at = buf.len();
1168    let partial = scale % 4;
1169    if partial != 0 {
1170        let q = 10u128.pow(partial);
1171        at -= 1;
1172        buf[at] = ((frac % q) * 10u128.pow(4 - partial)) as i16;
1173        frac /= q;
1174    }
1175    for _ in 0..scale / 4 {
1176        at -= 1;
1177        buf[at] = (frac % NBASE as u128) as i16;
1178        frac /= NBASE as u128;
1179    }
1180    let point = at;
1181    while int > 0 {
1182        at -= 1;
1183        buf[at] = (int % NBASE as u128) as i16;
1184        int /= NBASE as u128;
1185    }
1186    let mut weight = (point - at) as i16 - 1;
1187    while at < buf.len() && buf[at] == 0 {
1188        at += 1;
1189        weight -= 1;
1190    }
1191    let mut end = buf.len();
1192    while end > at && buf[end - 1] == 0 {
1193        end -= 1;
1194    }
1195    if at == end {
1196        weight = 0;
1197    }
1198    (&buf[at..end], weight)
1199}
1200
1201/// Writes `n` in decimal.
1202fn u128_out(n: u128, out: &mut Vec<u8>) {
1203    if let Ok(n) = u64::try_from(n) {
1204        return u64_out(n, out);
1205    }
1206    let mut buf = [0u8; 39];
1207    let mut at = buf.len();
1208    let mut n = n;
1209    while n > 0 {
1210        at -= 1;
1211        buf[at] = b'0' + (n % 10) as u8;
1212        n /= 10;
1213    }
1214    out.extend_from_slice(&buf[at..]);
1215}
1216
1217/// The text output of `value / 10^scale` from a `numeric(p, s)` column of the engine. It is the text that [`numeric_out`] gives for the same value with `scale` as its display scale.
1218pub fn decimal_out(value: i128, scale: u32, out: &mut Vec<u8>) {
1219    if value < 0 {
1220        out.push(b'-');
1221    }
1222    let abs = value.unsigned_abs();
1223    if scale == 0 {
1224        return u128_out(abs, out);
1225    }
1226    let pow = 10u128.pow(scale);
1227    u128_out(abs / pow, out);
1228    out.push(b'.');
1229    let mut frac = abs % pow;
1230    let start = out.len();
1231    out.resize(start + scale as usize, b'0');
1232    for at in out[start..].iter_mut().rev() {
1233        *at = b'0' + (frac % 10) as u8;
1234        frac /= 10;
1235    }
1236}
1237
1238/// The binary output of `value / 10^scale`. It is the bytes that [`numeric_send`] gives for the same value with `scale` as its display scale.
1239pub fn decimal_send(value: i128, scale: u32, out: &mut Vec<u8>) {
1240    let mut buf = [0; DECIMAL_DIGITS_MAX];
1241    let (digits, weight) = decimal_digits(value.unsigned_abs(), scale, &mut buf);
1242    let sign = if value < 0 { NumericSign::Negative } else { NumericSign::Positive };
1243    write_binary(sign, weight, scale as u16, digits, out);
1244}
1245
1246#[cfg(test)]
1247mod tests {
1248    use super::*;
1249
1250    fn text(v: &Numeric) -> String {
1251        let mut out = Vec::new();
1252        numeric_out(v, &mut out);
1253        String::from_utf8(out).unwrap()
1254    }
1255
1256    fn round_trip(input: &str, typmod: i32) -> String {
1257        numeric_in(input, typmod).map(|v| text(&v)).unwrap_or_else(|e| e.message)
1258    }
1259
1260    fn value(text: &str) -> Numeric {
1261        numeric_in(text, -1).unwrap()
1262    }
1263
1264    #[test]
1265    fn the_arithmetic_keeps_the_scales_of_postgres() {
1266        // The texts are the ones PostgreSQL 19 prints.
1267        for (a, op, b, out) in [
1268            ("1.0", '/', "3", "0.33333333333333333333"),
1269            ("10.0", '/', "4", "2.5000000000000000"),
1270            ("1e20", '/', "3", "33333333333333333333"),
1271            ("0.000001", '/', "3", "0.000000333333333333333333"),
1272            ("2", '/', "3", "0.66666666666666666667"),
1273            ("-2", '/', "3", "-0.66666666666666666667"),
1274            ("123456789012345678901234567890", '/', "7", "17636684144620811271604938270"),
1275            ("1", '/', "98765432109876543210", "0.000000000000000000010124999998860938"),
1276            ("99999999", '/', "0.0001", "999999990000.00000000"),
1277            ("0", '/', "5", "0.00000000000000000000"),
1278            ("1.50", '+', "2.125", "3.625"),
1279            ("1.50", '-', "2.125", "-0.625"),
1280            ("1.5", '-', "1.5", "0.0"),
1281            ("1.50", '*', "2.125", "3.18750"),
1282            ("-0.01", '*', "0", "0.00"),
1283            ("9999.9999", '+', "0.0001", "10000.0000"),
1284            ("NaN", '+', "1", "NaN"),
1285            ("Infinity", '-', "Infinity", "NaN"),
1286            ("Infinity", '*', "-2", "-Infinity"),
1287            ("Infinity", '*', "0", "NaN"),
1288            ("1", '/', "Infinity", "0"),
1289            ("7.5", '%', "2", "1.5"),
1290            ("-7.5", '%', "2", "-1.5"),
1291            ("7", '%', "-0.25", "0.00"),
1292            ("10", '%', "Infinity", "10"),
1293            ("-Infinity", '/', "-3", "Infinity"),
1294        ] {
1295            let (a, b) = (value(a), value(b));
1296            let result = match op {
1297                '+' => a.add(&b),
1298                '-' => a.sub(&b),
1299                '*' => a.mul(&b),
1300                '%' => a.modulo(&b),
1301                _ => a.div(&b),
1302            };
1303            assert_eq!(text(&result.unwrap()), out, "{} {op} {}", text(&a), text(&b));
1304        }
1305        assert_eq!(value("1").div(&value("0")).unwrap_err().message, "division by zero");
1306        assert_eq!(value("Infinity").div(&value("0")).unwrap_err().message, "division by zero");
1307    }
1308
1309    #[test]
1310    fn the_long_division_agrees_with_the_integers() {
1311        let mut seed = 0x2545_f491_4f6c_dd1du64;
1312        let mut next = || {
1313            seed ^= seed << 13;
1314            seed ^= seed >> 7;
1315            seed ^= seed << 17;
1316            seed
1317        };
1318        for _ in 0..2000 {
1319            let a = i128::from(next() >> (next() % 60));
1320            let b = i128::from(next() >> (next() % 63)).max(1);
1321            let quotient = Numeric::from_integer(a).div(&Numeric::from_integer(b)).unwrap();
1322            assert_eq!(
1323                quotient.with_typmod(-1).unwrap().to_decimal(0),
1324                Some((2 * a + b) / (2 * b)),
1325                "{a} / {b}"
1326            );
1327            let back = quotient.mul(&Numeric::from_integer(b)).unwrap();
1328            let error = back.sub(&Numeric::from_integer(a)).unwrap().to_f64().abs();
1329            assert!(error <= b as f64, "{a} / {b} = {}", text(&quotient));
1330        }
1331    }
1332
1333    #[test]
1334    fn a_double_converts_with_fifteen_digits() {
1335        for (input, out) in [
1336            (0.1, "0.1"),
1337            (1.0 / 3.0, "0.333333333333333"),
1338            (1e20, "100000000000000000000"),
1339            (1.5e-7, "0.00000015"),
1340            (-2.0, "-2"),
1341            (123456.789, "123456.789"),
1342        ] {
1343            assert_eq!(text(&Numeric::from_f64(input)), out);
1344        }
1345        assert_eq!(value("0.33333333333333333333").to_f64(), 1.0 / 3.0);
1346        assert_eq!(value("2.5").to_integer("integer").unwrap(), 3);
1347        assert_eq!(value("-2.5").to_integer("integer").unwrap(), -3);
1348        assert_eq!(
1349            value("NaN").to_integer("integer").unwrap_err().message,
1350            "cannot convert NaN to integer"
1351        );
1352    }
1353
1354    #[test]
1355    fn the_bytes_hold_the_value_and_order_it() {
1356        for input in ["0.00", "-123456.78", "1e-30", "NaN", "-Infinity", "33333333333333333333.3"] {
1357            let v = value(input);
1358            assert_eq!(Numeric::from_bytes(&v.to_bytes()), v);
1359        }
1360        assert_eq!(value("1.0").compare(&value("1.000")), std::cmp::Ordering::Equal);
1361        assert_eq!(value("-1.1").compare(&value("-1.01")), std::cmp::Ordering::Less);
1362    }
1363
1364    #[test]
1365    fn the_input_keeps_the_scale_and_strips_the_zeros() {
1366        let v = numeric_in(" -0012.3400 ", -1).unwrap();
1367        assert_eq!(
1368            (v.sign(), v.weight(), v.dscale(), v.digits()),
1369            (NumericSign::Negative, 0, 4, &[12, 3400][..])
1370        );
1371        assert_eq!(text(&v), "-12.3400");
1372        let zero = numeric_in("-0.00", -1).unwrap();
1373        assert_eq!(
1374            (zero.sign(), zero.weight(), zero.dscale(), zero.digits()),
1375            (NumericSign::Positive, 0, 2, &[][..])
1376        );
1377        assert_eq!(round_trip("1e3", -1), "1000");
1378        assert_eq!(round_trip("1.5e-3", -1), "0.0015");
1379        assert_eq!(round_trip("0x_1F", -1), "31");
1380        assert_eq!(round_trip("-0b1_01", -1), "-5");
1381        assert_eq!(round_trip("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", -1), u128::MAX.to_string());
1382        assert_eq!(round_trip("1e-16384", -1), "value overflows numeric format");
1383        assert_eq!(round_trip("1e2147483647", -1), "value overflows numeric format");
1384    }
1385
1386    #[test]
1387    fn round_trunc_ceil_and_floor_keep_the_digits() {
1388        let text = |value: Numeric| {
1389            let mut out = Vec::new();
1390            numeric_out(&value, &mut out);
1391            String::from_utf8(out).unwrap()
1392        };
1393        let value = numeric_in("-123456789012345678901.555", -1).unwrap();
1394        assert_eq!(text(value.round(2).unwrap()), "-123456789012345678901.56");
1395        assert_eq!(text(value.round(-1).unwrap()), "-123456789012345678900");
1396        assert_eq!(text(value.trunc(1).unwrap()), "-123456789012345678901.5");
1397        assert_eq!(text(value.whole(true).unwrap()), "-123456789012345678901");
1398        assert_eq!(text(value.whole(false).unwrap()), "-123456789012345678902");
1399        assert_eq!(text(value.abs()), "123456789012345678901.555");
1400        let small = numeric_in("-0.5", -1).unwrap();
1401        assert_eq!(text(small.whole(true).unwrap()), "0");
1402        assert_eq!(text(small.whole(false).unwrap()), "-1");
1403        assert_eq!(text(numeric_in("9.99", -1).unwrap().round(1).unwrap()), "10.0");
1404        assert_eq!(text(numeric_in("5", -1).unwrap().round(1).unwrap()), "5.0");
1405        let nan = numeric_in("NaN", -1).unwrap();
1406        assert_eq!(text(nan.round(2).unwrap()), "NaN");
1407    }
1408
1409    #[test]
1410    fn rounding_carries_into_a_new_digit() {
1411        let typmod = crate::typmod::numeric_typmod;
1412        assert_eq!(round_trip("9999.99995", typmod(10, 4)), "10000.0000");
1413        assert_eq!(round_trip("0.6", typmod(1, 0)), "1");
1414        assert_eq!(round_trip("-0.004", typmod(5, 2)), "0.00");
1415        assert_eq!(round_trip("15", typmod(3, -1)), "20");
1416        assert_eq!(round_trip("-5", typmod(3, -1)), "-10");
1417        let error = numeric_in("999.995", typmod(5, 2)).unwrap_err();
1418        assert_eq!(
1419            (error.sqlstate.as_str(), error.message.as_str()),
1420            ("22003", "numeric field overflow")
1421        );
1422        assert_eq!(
1423            error.detail.as_deref(),
1424            Some(
1425                "A field with precision 5, scale 2 must round to an absolute value less than 10^3."
1426            )
1427        );
1428        let error = numeric_in("0.95", typmod(1, 1)).unwrap_err();
1429        assert_eq!(
1430            error.detail.as_deref(),
1431            Some("A field with precision 1, scale 1 must round to an absolute value less than 1.")
1432        );
1433        let error = numeric_in("-inf", typmod(5, 2)).unwrap_err();
1434        assert_eq!(
1435            error.detail.as_deref(),
1436            Some("A field with precision 5, scale 2 cannot hold an infinite value.")
1437        );
1438        assert_eq!(numeric_in("NaN", typmod(5, 2)), Ok(Numeric::NAN));
1439    }
1440
1441    #[test]
1442    fn the_binary_form_is_the_fields() {
1443        let mut out = Vec::new();
1444        numeric_send(&numeric_in("-12.3400", -1).unwrap(), &mut out);
1445        assert_eq!(out, [0, 2, 0, 0, 0x40, 0, 0, 4, 0, 12, 0x0d, 0x48]);
1446        let back = numeric_recv(&mut Recv::new(&out), -1).unwrap();
1447        assert_eq!(text(&back), "-12.3400");
1448
1449        // Digits that the scale hides are dropped, as `trunc_var` drops them.
1450        let hidden = [0, 2, 0, 0, 0, 0, 0, 1, 0, 1, 0x13, 0x88];
1451        assert_eq!(text(&numeric_recv(&mut Recv::new(&hidden), -1).unwrap()), "1.5");
1452
1453        let bad = |bytes: &[u8]| numeric_recv(&mut Recv::new(bytes), -1).unwrap_err().message;
1454        assert_eq!(
1455            bad(&[0, 0, 0, 0, 0x12, 0x34, 0, 0]),
1456            "invalid sign in external \"numeric\" value"
1457        );
1458        assert_eq!(
1459            bad(&[0, 0, 0, 0, 0, 0, 0x40, 0]),
1460            "invalid scale in external \"numeric\" value"
1461        );
1462        assert_eq!(
1463            bad(&[0, 1, 0, 0, 0, 0, 0, 0, 0x27, 0x10]),
1464            "invalid digit in external \"numeric\" value"
1465        );
1466        assert_eq!(bad(&[0, 1, 0, 0, 0, 0, 0, 0]), "insufficient data left in message");
1467        let mut out = Vec::new();
1468        numeric_send(&Numeric::NEGATIVE_INFINITY, &mut out);
1469        assert_eq!(out, [0, 0, 0, 0, 0xf0, 0, 0, 32]);
1470    }
1471
1472    #[test]
1473    fn a_decimal_of_the_engine_writes_what_numeric_writes() {
1474        let values = [
1475            0i128,
1476            1,
1477            -1,
1478            5,
1479            -5,
1480            9999,
1481            10000,
1482            123456789,
1483            -100000000,
1484            i64::MAX as i128,
1485            i128::MAX,
1486            i128::MIN + 1,
1487        ];
1488        for value in values {
1489            for scale in [0, 1, 2, 3, 4, 5, 8, 9, 18, 19, 37, 38] {
1490                let v = Numeric::from_decimal(value, scale);
1491                let mut out = Vec::new();
1492                decimal_out(value, scale, &mut out);
1493                let decimal = String::from_utf8(out).unwrap();
1494                assert_eq!(decimal, text(&v), "{value} {scale}");
1495                assert_eq!(numeric_in(&decimal, -1), Ok(v.clone()), "{value} {scale}");
1496                let (mut sent, mut expected) = (Vec::new(), Vec::new());
1497                decimal_send(value, scale, &mut sent);
1498                numeric_send(&v, &mut expected);
1499                assert_eq!(sent, expected, "{value} {scale}");
1500                assert_eq!(v.to_decimal(scale), Some(value), "{value} {scale}");
1501            }
1502        }
1503        let v = numeric_in("-1.005", -1).unwrap();
1504        assert_eq!(
1505            (v.to_decimal(2), v.to_decimal(0), v.to_decimal(5)),
1506            (Some(-101), Some(-1), Some(-100500))
1507        );
1508        assert_eq!(numeric_in("1e40", -1).unwrap().to_decimal(0), None);
1509        assert_eq!(Numeric::NAN.to_decimal(0), None);
1510    }
1511}