Skip to main content

rupg_types/numeric/
math.rs

1//! The square root, the exponential, the logarithms and the powers of `numeric`. These are ports of `sqrt_var`, `exp_var`, `ln_var`, `log_var`, `power_var` and `power_var_int`, with the multiplication and the division that they use: `mul_var` keeps only the digits that the result scale needs, and `div_var` finds the quotient digits from an estimate in floating point when it does not have to be exact. The digits that these two drop change the last digits of a result, so the ports drop the same digits.
2
3use std::cmp::Ordering;
4
5use rupg_common::SqlState;
6
7use super::{
8    DEC_DIGITS, MIN_SIG_DIGITS, NBASE, Numeric, NumericSign, Var, overflow, select_div_scale,
9};
10use crate::error::TypeError;
11use crate::prng::Prng;
12
13/// `NBASE * NBASE`: the base of the digit pairs of `mul_var` and `div_var`.
14const NBASE_SQR: i64 = 100_000_000;
15/// `MUL_GUARD_DIGITS`: the digits that `mul_var` computes after the result scale.
16const MUL_GUARD_DIGITS: i64 = 2;
17/// `DIV_GUARD_DIGITS`: the digits that `div_var` computes after the result scale when the quotient need not be exact.
18const DIV_GUARD_DIGITS: i64 = 4;
19/// `NUMERIC_MAX_DISPLAY_SCALE`.
20const MAX_DISPLAY_SCALE: i64 = 1000;
21/// `NUMERIC_WEIGHT_MAX`.
22const WEIGHT_MAX: i64 = i16::MAX as i64;
23/// `NUMERIC_MAX_RESULT_SCALE * 3`: `exp_var` gives zero or an overflow for an argument with a larger magnitude.
24const EXP_LIMIT: f64 = 6000.0;
25// The estimates of the scales use these values with 15 digits, as numeric.c has them, and not the constants of the standard library.
26/// `log10(e)`.
27#[allow(clippy::approx_constant)]
28const LOG10_E: f64 = 0.434294481903252;
29/// `log10(2)`.
30#[allow(clippy::approx_constant)]
31const LOG10_2: f64 = 0.301029995663981;
32/// `ln(10)`.
33#[allow(clippy::approx_constant)]
34const LN_10: f64 = 2.302585092994046;
35
36fn log_error(message: &str) -> TypeError {
37    TypeError::new(SqlState::INVALID_ARGUMENT_FOR_LOG, message.to_string())
38}
39
40fn power_error(message: &str) -> TypeError {
41    TypeError::new(SqlState::INVALID_ARGUMENT_FOR_POWER_FUNCTION, message.to_string())
42}
43
44fn division_by_zero() -> TypeError {
45    TypeError::new(SqlState::DIVISION_BY_ZERO, "division by zero".to_string())
46}
47
48/// The display scale for a result with `MIN_SIG_DIGITS` significant digits, no less than the scales of the arguments.
49fn result_scale(dweight: i64, scales: &[i64]) -> i64 {
50    let rscale = scales.iter().fold(MIN_SIG_DIGITS - dweight, |r, &s| r.max(s));
51    rscale.clamp(0, MAX_DISPLAY_SCALE)
52}
53
54impl Var {
55    fn zero(dscale: i64) -> Var {
56        Var { sign: NumericSign::Positive, weight: 0, dscale, digits: Vec::new() }
57    }
58
59    /// A var of a small integer, as `int64_to_numericvar` gives it.
60    fn int(value: i64) -> Var {
61        let sign = if value < 0 { NumericSign::Negative } else { NumericSign::Positive };
62        let mut rest = value.unsigned_abs();
63        let mut digits = Vec::new();
64        while rest > 0 {
65            digits.insert(0, (rest % NBASE as u64) as i32);
66            rest /= NBASE as u64;
67        }
68        let weight = digits.len() as i64 - 1;
69        let mut var = Var { sign, weight, dscale: 0, digits };
70        var.strip();
71        var
72    }
73
74    /// `const_zero_point_nine`.
75    fn point_nine() -> Var {
76        Var { sign: NumericSign::Positive, weight: -1, dscale: 1, digits: vec![9000] }
77    }
78
79    /// `const_one_point_one`.
80    fn one_point_one() -> Var {
81        Var { sign: NumericSign::Positive, weight: 0, dscale: 1, digits: vec![1, 1000] }
82    }
83
84    fn is_zero(&self) -> bool {
85        self.digits.is_empty()
86    }
87
88    /// `cmp_var`: the order of two values with their signs.
89    fn cmp(&self, other: &Var) -> Ordering {
90        let negative = |v: &Var| !v.is_zero() && v.sign == NumericSign::Negative;
91        let positive = |v: &Var| !v.is_zero() && v.sign == NumericSign::Positive;
92        match (negative(self), negative(other)) {
93            (true, false) => Ordering::Less,
94            (false, true) => Ordering::Greater,
95            (true, true) => other.cmp_abs(self),
96            (false, false) if positive(self) || positive(other) => self.cmp_abs(other),
97            (false, false) => Ordering::Equal,
98        }
99    }
100
101    /// `sub_var`.
102    fn sub(&self, other: &Var) -> Var {
103        let mut negated = other.clone();
104        negated.sign = match other.sign {
105            NumericSign::Positive if !other.is_zero() => NumericSign::Negative,
106            _ => NumericSign::Positive,
107        };
108        self.add(&negated)
109    }
110
111    /// `numericvar_to_double_no_overflow`: the double nearest to the text of the value.
112    fn to_f64(&self) -> f64 {
113        let mut text = String::new();
114        if self.sign == NumericSign::Negative {
115            text.push('-');
116        }
117        if self.weight < 0 {
118            text.push('0');
119        } else {
120            text.push_str(&self.digit(self.weight).to_string());
121            for w in (0..self.weight).rev() {
122                text.push_str(&format!("{:04}", self.digit(w)));
123            }
124        }
125        if self.dscale > 0 {
126            text.push('.');
127            let end = text.len() + self.dscale as usize;
128            let mut w = -1;
129            while text.len() < end {
130                text.push_str(&format!("{:04}", self.digit(w)));
131                w -= 1;
132            }
133            text.truncate(end);
134        }
135        text.parse().unwrap_or(f64::NAN)
136    }
137
138    /// `numericvar_to_int64` for an integer: `None` when the value does not fit.
139    fn to_i64(&self) -> Option<i64> {
140        let mut value: i64 = 0;
141        for w in (0..=self.weight).rev() {
142            value = value.checked_mul(i64::from(NBASE))?.checked_add(i64::from(self.digit(w)))?;
143        }
144        Some(if self.sign == NumericSign::Negative { -value } else { value })
145    }
146
147    /// The digit pair `i` of the digits: two digits as one digit of base `NBASE_SQR`. The last pair of an odd number of digits has a zero after its digit.
148    fn pair(&self, i: usize) -> i64 {
149        let high = i64::from(self.digits[2 * i]) * i64::from(NBASE);
150        high + self.digits.get(2 * i + 1).map_or(0, |&d| i64::from(d))
151    }
152
153    /// `mul_var`: the product rounded to `rscale` digits after the point. Only the digit pairs that touch the first `rscale` digits and the guard digits go into the product.
154    fn mul_round(&self, other: &Var, rscale: i64) -> Var {
155        let (a, b) =
156            if self.digits.len() > other.digits.len() { (other, self) } else { (self, other) };
157        if a.is_zero() {
158            return Var::zero(rscale);
159        }
160        if a.digits.len() <= 6 && rscale == a.dscale + b.dscale {
161            // `mul_var_short` gives the exact product.
162            let mut product = a.mul(b);
163            product.dscale = rscale;
164            return product;
165        }
166        let sign = if a.sign == b.sign { NumericSign::Positive } else { NumericSign::Negative };
167        let (n1, n2) = (a.digits.len() as i64, b.digits.len() as i64);
168        let (mut pairs1, mut pairs2) = ((n1 + 1) / 2, (n2 + 1) / 2);
169        let res_ndigits = n1 + n2;
170        let mut res_pairs = res_ndigits / 2 + 1;
171        let pair_offset = res_pairs - pairs1 - pairs2 + 1;
172        let res_weight =
173            a.weight + b.weight + 1 + 2 * res_pairs - res_ndigits - (n1 & 1) - (n2 & 1);
174        let maxdigits = res_weight + 1 + (rscale + DEC_DIGITS - 1) / DEC_DIGITS + MUL_GUARD_DIGITS;
175        res_pairs = res_pairs.min(maxdigits / 2 + 1);
176        if res_pairs <= pair_offset {
177            return Var::zero(rscale);
178        }
179        pairs1 = pairs1.min(res_pairs - pair_offset);
180        pairs2 = pairs2.min(res_pairs - pair_offset);
181        let right: Vec<u128> = (0..pairs2 as usize).map(|i| b.pair(i) as u128).collect();
182        let mut dig = vec![0u128; res_pairs as usize];
183        for i1 in 0..pairs1 {
184            let left = a.pair(i1 as usize) as u128;
185            if left == 0 {
186                continue;
187            }
188            let limit = pairs2.min(res_pairs - i1 - pair_offset);
189            let at = (i1 + pair_offset) as usize;
190            for (slot, &r) in dig[at..].iter_mut().zip(&right[..limit as usize]) {
191                *slot += left * r;
192            }
193        }
194        let mut digits = vec![0; 2 * res_pairs as usize];
195        let mut carry = 0u128;
196        for (i, &d) in dig.iter().enumerate().rev() {
197            let total = d + carry;
198            let low = (total % NBASE_SQR as u128) as i64;
199            carry = total / NBASE_SQR as u128;
200            digits[2 * i] = (low / i64::from(NBASE)) as i32;
201            digits[2 * i + 1] = (low % i64::from(NBASE)) as i32;
202        }
203        let mut product = Var { sign, weight: res_weight, dscale: 0, digits };
204        product.round(rscale);
205        product.strip();
206        product
207    }
208
209    /// `div_var`: the quotient to `rscale` digits after the point, rounded when `round` is set and cut when it is not. A divisor of up to 12 digits, or a call with `exact` set, gives the exact quotient. A longer divisor gives the quotient that the estimates of `div_var` give.
210    fn div_round(
211        &self,
212        other: &Var,
213        rscale: i64,
214        round: bool,
215        exact: bool,
216    ) -> Result<Var, TypeError> {
217        if other.is_zero() || other.digits[0] == 0 {
218            return Err(division_by_zero());
219        }
220        if self.is_zero() {
221            return Ok(Var::zero(rscale));
222        }
223        if exact || other.digits.len() as i64 <= 2 * (DIV_GUARD_DIGITS + 2) {
224            return Ok(self.div(other, rscale, round));
225        }
226        Ok(self.div_estimate(other, rscale, round))
227    }
228
229    /// The part of `div_var` that does not correct the quotient: each digit pair of the quotient is the estimate from the first two pairs of the dividend and the divisor in floating point.
230    fn div_estimate(&self, other: &Var, rscale: i64, round: bool) -> Var {
231        let sign =
232            if self.sign == other.sign { NumericSign::Positive } else { NumericSign::Negative };
233        let res_weight = self.weight - other.weight + 1;
234        let mut res_ndigits = (res_weight + 1 + (rscale + DEC_DIGITS - 1) / DEC_DIGITS).max(1);
235        if round {
236            res_ndigits += 1;
237        }
238        res_ndigits += DIV_GUARD_DIGITS;
239        let res_pairs = ((res_ndigits + 1) / 2) as usize;
240        let div_pairs = res_pairs;
241        let pairs1 = self.digits.len().div_ceil(2).min(div_pairs);
242        let pairs2 = other.digits.len().div_ceil(2).min(div_pairs);
243        let mut dividend = vec![0i64; div_pairs + 1];
244        for (i, slot) in dividend.iter_mut().enumerate().take(pairs1) {
245            *slot = self.pair(i);
246        }
247        let divisor: Vec<i64> = (0..pairs2).map(|i| other.pair(i)).collect();
248        let mut fdivisor = divisor[0] as f64 * NBASE_SQR as f64;
249        if pairs2 > 1 {
250            fdivisor += divisor[1] as f64;
251        }
252        let inverse = 1.0 / fdivisor;
253        let estimate = |dividend: &[i64], qi: usize| -> i32 {
254            let fdividend = dividend[qi] as f64 * NBASE_SQR as f64 + dividend[qi + 1] as f64;
255            let quotient = fdividend * inverse;
256            if quotient >= 0.0 { quotient as i32 } else { quotient as i32 - 1 }
257        };
258        let limit = (i64::MAX - i64::MAX / NBASE_SQR - 1) / (NBASE_SQR - 1);
259        let mut maxdiv: i64 = 1;
260        for qi in 0..res_pairs {
261            let mut qdigit = estimate(&dividend, qi);
262            if qdigit != 0 {
263                maxdiv += i64::from(qdigit).abs();
264                if maxdiv > limit {
265                    // Carry the digits that earlier steps changed, so that no digit overflows.
266                    let mut carry = 0;
267                    let mut i = (qi + pairs2 - 2).min(div_pairs - 1);
268                    while i > qi {
269                        let total = dividend[i] + carry;
270                        carry = total.div_euclid(NBASE_SQR);
271                        dividend[i] = total.rem_euclid(NBASE_SQR);
272                        i -= 1;
273                    }
274                    dividend[qi] += carry;
275                    maxdiv = 1;
276                    qdigit = estimate(&dividend, qi);
277                    maxdiv += i64::from(qdigit).abs();
278                }
279                if qdigit != 0 {
280                    let stop = pairs2.min(div_pairs - qi);
281                    for (slot, &d) in dividend[qi..qi + stop].iter_mut().zip(&divisor) {
282                        *slot -= i64::from(qdigit) * d;
283                    }
284                }
285            }
286            // The digit can still hold a remainder: fold it into the next one. The product can overflow, and the sum then overflows back, as in C.
287            dividend[qi + 1] = dividend[qi + 1].wrapping_add(dividend[qi].wrapping_mul(NBASE_SQR));
288            dividend[qi] = i64::from(qdigit);
289        }
290        let mut digits = vec![0; 2 * res_pairs];
291        let mut carry = 0;
292        for i in (0..res_pairs).rev() {
293            let total = dividend[i] + carry;
294            carry = total.div_euclid(NBASE_SQR);
295            let low = total.rem_euclid(NBASE_SQR);
296            digits[2 * i] = (low / i64::from(NBASE)) as i32;
297            digits[2 * i + 1] = (low % i64::from(NBASE)) as i32;
298        }
299        let mut quotient = Var { sign, weight: res_weight, dscale: 0, digits };
300        if round {
301            quotient.round(rscale);
302        } else {
303            quotient.trunc(rscale);
304        }
305        quotient.strip();
306        quotient
307    }
308
309    /// `div_var_int`: the quotient by a small integer, which is exact before the rounding.
310    fn div_int(&self, divisor: i64, rscale: i64) -> Var {
311        self.div(&Var::int(divisor), rscale, true)
312    }
313
314    /// `sqrt_var` for a value above zero: the integer square root of the digits that the result scale needs, rounded to `rscale` digits after the point. `rscale` can be below zero.
315    fn sqrt(&self, rscale: i64) -> Var {
316        if self.is_zero() {
317            return Var::zero(rscale);
318        }
319        let res_weight = self.weight.div_euclid(2);
320        let res_ndigits = if rscale + 1 >= 0 {
321            res_weight + 1 + (rscale + DEC_DIGITS) / DEC_DIGITS
322        } else {
323            res_weight + 1 - (-rscale - 1) / DEC_DIGITS
324        }
325        .max(1);
326        let src_ndigits = (self.weight + 1 + (res_ndigits - res_weight - 1) * 2).max(1);
327        let digits: Vec<i32> =
328            (0..src_ndigits as usize).map(|i| self.digits.get(i).copied().unwrap_or(0)).collect();
329        let mut source =
330            Var { sign: NumericSign::Positive, weight: src_ndigits - 1, dscale: 0, digits };
331        source.strip();
332        let mut root = isqrt(&source);
333        root.weight = res_weight;
334        root.round(rscale);
335        root.strip();
336        root
337    }
338
339    /// `exp_var`: e to the power of the value, to `rscale` digits after the point.
340    fn exp(&self, rscale: i64) -> Result<Var, TypeError> {
341        let mut x = self.clone();
342        let mut val = x.to_f64();
343        if val.abs() >= EXP_LIMIT {
344            if val > 0.0 {
345                return Err(overflow());
346            }
347            return Ok(Var::zero(rscale));
348        }
349        let dweight = (val * LOG10_E) as i64;
350        // Bring the argument near zero, so that the series converges fast.
351        let mut ndiv2 = 0;
352        if val.abs() > 0.01 {
353            ndiv2 = 1;
354            val /= 2.0;
355            while val.abs() > 0.01 {
356                ndiv2 += 1;
357                val /= 2.0;
358            }
359            let local_rscale = x.dscale + ndiv2;
360            x = x.div_int(1 << ndiv2, local_rscale);
361        }
362        let sig_digits = (1 + dweight + rscale + (ndiv2 as f64 * LOG10_2) as i64).max(0) + 8;
363        let local_rscale = sig_digits - 1;
364        let mut result = Var::int(1).add(&x);
365        let mut elem = x.mul_round(&x, local_rscale);
366        let mut ni = 2;
367        elem = elem.div_int(ni, local_rscale);
368        while !elem.is_zero() {
369            result = result.add(&elem);
370            elem = elem.mul_round(&x, local_rscale);
371            ni += 1;
372            elem = elem.div_int(ni, local_rscale);
373        }
374        for _ in 0..ndiv2 {
375            let local_rscale = (sig_digits - result.weight * 2 * DEC_DIGITS).max(0);
376            result = result.mul_round(&result, local_rscale);
377        }
378        result.round(rscale);
379        Ok(result)
380    }
381
382    /// `estimate_ln_dweight`: the power of ten of the first digit of the natural logarithm. It is 0 for a value that has no logarithm.
383    fn estimate_ln_dweight(&self) -> i64 {
384        if self.sign != NumericSign::Positive {
385            return 0;
386        }
387        if self.cmp(&Var::point_nine()) != Ordering::Less
388            && self.cmp(&Var::one_point_one()) != Ordering::Greater
389        {
390            // ln(1 + x) is near x.
391            let x = self.sub(&Var::int(1));
392            return match x.digits.first() {
393                Some(&first) => x.weight * DEC_DIGITS + f64::from(first).log10() as i64,
394                None => 0,
395            };
396        }
397        let Some(&first) = self.digits.first() else { return 0 };
398        let mut digits = first;
399        let mut dweight = self.weight * DEC_DIGITS;
400        if let Some(&second) = self.digits.get(1) {
401            digits = digits * NBASE + second;
402            dweight -= DEC_DIGITS;
403        }
404        let ln = f64::from(digits).ln() + dweight as f64 * LN_10;
405        ln.abs().log10() as i64
406    }
407
408    /// `ln_var`: the natural logarithm, to `rscale` digits after the point.
409    fn ln(&self, rscale: i64) -> Result<Var, TypeError> {
410        match self.cmp(&Var::zero(0)) {
411            Ordering::Equal => return Err(log_error("cannot take logarithm of zero")),
412            Ordering::Less => return Err(log_error("cannot take logarithm of a negative number")),
413            Ordering::Greater => {}
414        }
415        let two = Var::int(2);
416        let mut x = self.clone();
417        let mut fact = two.clone();
418        let mut nsqrt = 0;
419        // Take square roots until the value is between 0.9 and 1.1.
420        while x.cmp(&Var::point_nine()) != Ordering::Greater {
421            x = x.sqrt(rscale - x.weight * DEC_DIGITS / 2 + 8);
422            fact = fact.mul_round(&two, 0);
423            nsqrt += 1;
424        }
425        while x.cmp(&Var::one_point_one()) != Ordering::Less {
426            x = x.sqrt(rscale - x.weight * DEC_DIGITS / 2 + 8);
427            fact = fact.mul_round(&two, 0);
428            nsqrt += 1;
429        }
430        // The series of 0.5 * ln((1 + z) / (1 - z)) with z = (x - 1) / (x + 1).
431        let local_rscale = rscale + ((nsqrt + 1) as f64 * LOG10_2) as i64 + 8;
432        let one = Var::int(1);
433        let mut result = x.sub(&one).div_round(&x.add(&one), local_rscale, true, false)?;
434        let mut xx = result.clone();
435        let x = result.mul_round(&result, local_rscale);
436        let mut ni = 1;
437        loop {
438            ni += 2;
439            xx = xx.mul_round(&x, local_rscale);
440            let elem = xx.div_int(ni, local_rscale);
441            if elem.is_zero() {
442                break;
443            }
444            result = result.add(&elem);
445            if elem.weight < result.weight - local_rscale * 2 / DEC_DIGITS {
446                break;
447            }
448        }
449        Ok(result.mul_round(&fact, rscale))
450    }
451
452    /// `log_var`: the logarithm of `num` in the base `self`, with the scale that `log_var` picks.
453    fn log(&self, num: &Var) -> Result<Var, TypeError> {
454        let ln_base_dweight = self.estimate_ln_dweight();
455        let ln_num_dweight = num.estimate_ln_dweight();
456        let result_dweight = ln_num_dweight - ln_base_dweight;
457        let rscale = result_scale(result_dweight, &[self.dscale, num.dscale]);
458        let ln_base_rscale = (rscale + result_dweight - ln_base_dweight + 8).max(0);
459        let ln_num_rscale = (rscale + result_dweight - ln_num_dweight + 8).max(0);
460        let ln_base = self.ln(ln_base_rscale)?;
461        let ln_num = num.ln(ln_num_rscale)?;
462        ln_num.div_round(&ln_base, rscale, true, false)
463    }
464
465    /// Whether the value has no digits after the point.
466    fn is_integral(&self) -> bool {
467        self.digits.len() as i64 <= self.weight + 1
468    }
469
470    /// `power_var`: `self` to the power of `exp`, with the scale that `power_var` picks.
471    fn power(&self, exp: &Var) -> Result<Var, TypeError> {
472        if exp.is_integral()
473            && let Some(value) = exp.to_i64()
474            && let Ok(value) = i32::try_from(value)
475        {
476            return self.power_int(value, exp.dscale);
477        }
478        if self.is_zero() {
479            return Ok(Var::zero(MIN_SIG_DIGITS));
480        }
481        let mut base = self.clone();
482        let mut negative = false;
483        if self.sign == NumericSign::Negative {
484            if !exp.is_integral() {
485                return Err(power_error(
486                    "a negative number raised to a non-integer power yields a complex result",
487                ));
488            }
489            negative = exp.digits.len() as i64 == exp.weight + 1
490                && exp.digits.last().is_some_and(|d| d & 1 == 1);
491            base.sign = NumericSign::Positive;
492        }
493        // A first pass with about 8 digits of ln(base) estimates the weight of the result.
494        let ln_dweight = base.estimate_ln_dweight();
495        let local_rscale = (8 - ln_dweight).max(0);
496        let ln_base = base.ln(local_rscale)?;
497        let ln_num = ln_base.mul_round(exp, local_rscale);
498        let mut val = ln_num.to_f64();
499        if val.abs() > 2000.0 * 3.01 {
500            if val > 0.0 {
501                return Err(overflow());
502            }
503            return Ok(Var::zero(MAX_DISPLAY_SCALE));
504        }
505        val *= LOG10_E;
506        let rscale = result_scale(val as i64, &[base.dscale, exp.dscale]);
507        let sig_digits = (rscale + val as i64).max(0);
508        let local_rscale = (sig_digits - ln_dweight + 8).max(0);
509        let ln_base = base.ln(local_rscale)?;
510        let ln_num = ln_base.mul_round(exp, local_rscale);
511        let mut result = ln_num.exp(rscale)?;
512        if negative && !result.is_zero() {
513            result.sign = NumericSign::Negative;
514        }
515        Ok(result)
516    }
517
518    /// `power_var_int`: `self` to an integer power, by repeated squares.
519    fn power_int(&self, exp: i32, exp_dscale: i64) -> Result<Var, TypeError> {
520        // An estimate of the power of ten of the result, from the first 16 digits.
521        let f = match self.digits.first() {
522            Some(&first) => {
523                let mut f = f64::from(first);
524                let mut p = self.weight * DEC_DIGITS;
525                for &d in self.digits.iter().skip(1).take(3) {
526                    f = f * f64::from(NBASE) + f64::from(d);
527                    p -= DEC_DIGITS;
528                }
529                f64::from(exp) * (f.log10() + p as f64)
530            }
531            None => 0.0,
532        };
533        if f > ((WEIGHT_MAX + 1) * DEC_DIGITS) as f64 {
534            return Err(overflow());
535        }
536        if f + 1.0 < -(MAX_DISPLAY_SCALE as f64) {
537            return Ok(Var::zero(MAX_DISPLAY_SCALE));
538        }
539        let rscale = result_scale(f as i64, &[self.dscale, exp_dscale]);
540        match exp {
541            0 => {
542                let mut one = Var::int(1);
543                one.dscale = rscale;
544                return Ok(one);
545            }
546            1 => {
547                let mut result = self.clone();
548                result.round(rscale);
549                return Ok(result);
550            }
551            -1 => return Var::int(1).div_round(self, rscale, true, true),
552            2 => return Ok(self.mul_round(self, rscale)),
553            _ => {}
554        }
555        if self.is_zero() {
556            if exp < 0 {
557                return Err(division_by_zero());
558            }
559            return Ok(Var::zero(rscale));
560        }
561        let sig_digits = 1 + rscale + f as i64 + f64::from(exp).abs().ln() as i64 + 8;
562        let mut negative = exp < 0;
563        let mut mask = exp.unsigned_abs();
564        let mut base_prod = self.clone();
565        let mut result = if mask & 1 == 1 { self.clone() } else { Var::int(1) };
566        loop {
567            mask >>= 1;
568            if mask == 0 {
569                break;
570            }
571            let local_rscale =
572                (sig_digits - 2 * base_prod.weight * DEC_DIGITS).min(2 * base_prod.dscale).max(0);
573            base_prod = base_prod.mul_round(&base_prod, local_rscale);
574            if mask & 1 == 1 {
575                let local_rscale = (sig_digits - (base_prod.weight + result.weight) * DEC_DIGITS)
576                    .min(base_prod.dscale + result.dscale)
577                    .max(0);
578                result = base_prod.mul_round(&result, local_rscale);
579            }
580            // Past this weight the result overflows, or is zero for a negative power.
581            if base_prod.weight > WEIGHT_MAX || result.weight > WEIGHT_MAX {
582                if !negative {
583                    return Err(overflow());
584                }
585                result = Var::zero(0);
586                negative = false;
587                break;
588            }
589        }
590        if negative {
591            return Var::int(1).div_round(&result, rscale, true, false);
592        }
593        result.round(rscale);
594        Ok(result)
595    }
596}
597
598/// The integer square root of an integer above zero, by the steps of Newton from a value above the root.
599fn isqrt(n: &Var) -> Var {
600    let len = n.weight + 1;
601    let (top, shift) = if len % 2 == 1 {
602        (i64::from(n.digit(n.weight)), (len - 1) / 2)
603    } else {
604        (
605            i64::from(n.digit(n.weight)) * i64::from(NBASE) + i64::from(n.digit(n.weight - 1)),
606            (len - 2) / 2,
607        )
608    };
609    let mut x = Var::int((top as f64).sqrt() as i64 + 1);
610    x.weight += shift;
611    let two = Var::int(2);
612    loop {
613        let y = x.add(&n.div(&x, 0, false)).div(&two, 0, false);
614        if y.cmp_abs(&x) != Ordering::Less {
615            return x;
616        }
617        x = y;
618    }
619}
620
621impl Numeric {
622    fn var(&self) -> Var {
623        Var::from(self)
624    }
625
626    fn is_zero(&self) -> bool {
627        self.sign.is_finite() && self.digits.is_empty()
628    }
629
630    /// The sign as `numeric_sign_internal` gives it: -1, 0 or 1. `NaN` is not given here.
631    fn signum(&self) -> i32 {
632        match self.sign {
633            NumericSign::Negative | NumericSign::NegativeInfinity => -1,
634            _ if self.is_zero() => 0,
635            _ => 1,
636        }
637    }
638
639    /// `numeric_sqrt`.
640    pub fn sqrt(&self) -> Result<Numeric, TypeError> {
641        match self.sign {
642            NumericSign::NegativeInfinity => {
643                return Err(power_error("cannot take square root of a negative number"));
644            }
645            NumericSign::NaN | NumericSign::Infinity => return Ok(self.clone()),
646            _ => {}
647        }
648        let arg = self.var();
649        if arg.cmp(&Var::zero(0)) == Ordering::Less {
650            return Err(power_error("cannot take square root of a negative number"));
651        }
652        let sweight = arg.weight * DEC_DIGITS / 2 + 1;
653        let rscale = result_scale(sweight, &[arg.dscale]);
654        arg.sqrt(rscale).make()
655    }
656
657    /// The end of `numeric_stddev_internal` for finite values: the variance or, when `root` is set, the standard deviation of `n` values with the sum `sum` and the sum of squares `squares`. The variance of a sample divides by `n * (n - 1)` and the variance of the population by `n * n`. The caller makes sure that `n` is above 0, and above 1 for a sample.
658    pub fn moment(
659        n: i64,
660        sum: &Numeric,
661        squares: &Numeric,
662        sample: bool,
663        root: bool,
664    ) -> Result<Numeric, TypeError> {
665        let count = Var::int(n);
666        let sum = sum.var();
667        // The products are exact, because the scale of the squares is twice the scale of the values.
668        let rscale = sum.dscale * 2;
669        let mut sum_sq = sum.mul(&sum);
670        sum_sq.dscale = rscale;
671        let mut scaled = count.mul(&squares.var());
672        scaled.dscale = rscale;
673        let numerator = scaled.sub(&sum_sq);
674        if numerator.cmp(&Var::zero(0)) != Ordering::Greater {
675            // The rounding of the inputs can make the numerator negative.
676            return Ok(Numeric::from_integer(0));
677        }
678        let mut denominator = count.mul(&Var::int(if sample { n - 1 } else { n }));
679        denominator.dscale = 0;
680        let rscale = select_div_scale(&numerator, &denominator);
681        let variance = numerator.div(&denominator, rscale, true);
682        if root { variance.sqrt(rscale).make() } else { variance.make() }
683    }
684
685    /// `numeric_exp`.
686    pub fn exp(&self) -> Result<Numeric, TypeError> {
687        match self.sign {
688            NumericSign::NegativeInfinity => return Var::zero(0).make(),
689            NumericSign::NaN | NumericSign::Infinity => return Ok(self.clone()),
690            _ => {}
691        }
692        let arg = self.var();
693        let val = (arg.to_f64() * LOG10_E).clamp(-2000.0, 2000.0);
694        let rscale = result_scale(val as i64, &[arg.dscale]);
695        arg.exp(rscale)?.make()
696    }
697
698    /// `numeric_ln`.
699    pub fn ln(&self) -> Result<Numeric, TypeError> {
700        match self.sign {
701            NumericSign::NegativeInfinity => {
702                return Err(log_error("cannot take logarithm of a negative number"));
703            }
704            NumericSign::NaN | NumericSign::Infinity => return Ok(self.clone()),
705            _ => {}
706        }
707        let arg = self.var();
708        let rscale = result_scale(arg.estimate_ln_dweight(), &[arg.dscale]);
709        arg.ln(rscale)?.make()
710    }
711
712    /// `numeric_log`: the logarithm of `num` in the base `self`.
713    pub fn log(&self, num: &Numeric) -> Result<Numeric, TypeError> {
714        if !self.sign.is_finite() || !num.sign.is_finite() {
715            if self.sign == NumericSign::NaN || num.sign == NumericSign::NaN {
716                return Ok(Numeric::NAN);
717            }
718            if self.signum() < 0 || num.signum() < 0 {
719                return Err(log_error("cannot take logarithm of a negative number"));
720            }
721            if self.signum() == 0 || num.signum() == 0 {
722                return Err(log_error("cannot take logarithm of zero"));
723            }
724            if self.sign == NumericSign::Infinity {
725                if num.sign == NumericSign::Infinity {
726                    return Ok(Numeric::NAN);
727                }
728                return Var::zero(0).make();
729            }
730            return Ok(Numeric::INFINITY);
731        }
732        self.var().log(&num.var())?.make()
733    }
734
735    /// `numeric_power`: the rules of POSIX `pow` for `NaN` and the infinities.
736    pub fn power(&self, exp: &Numeric) -> Result<Numeric, TypeError> {
737        use NumericSign::{Infinity, NaN, NegativeInfinity};
738        let one = || Var::int(1).make();
739        let zero = || Var::zero(0).make();
740        if !self.sign.is_finite() || !exp.sign.is_finite() {
741            if self.sign == NaN {
742                if exp.is_zero() {
743                    return one();
744                }
745                return Ok(Numeric::NAN);
746            }
747            if exp.sign == NaN {
748                if self.sign.is_finite() && self.var().cmp(&Var::int(1)) == Ordering::Equal {
749                    return one();
750                }
751                return Ok(Numeric::NAN);
752            }
753            let (sign1, sign2) = (self.signum(), exp.signum());
754            if sign1 == 0 && sign2 < 0 {
755                return Err(power_error("zero raised to a negative power is undefined"));
756            }
757            if sign1 < 0 && exp.sign.is_finite() && !exp.var().is_integral() {
758                return Err(power_error(
759                    "a negative number raised to a non-integer power yields a complex result",
760                ));
761            }
762            if self.sign.is_finite() && self.var().cmp(&Var::int(1)) == Ordering::Equal {
763                return one();
764            }
765            if sign2 == 0 {
766                return one();
767            }
768            if sign1 == 0 && sign2 > 0 {
769                return zero();
770            }
771            if !exp.sign.is_finite() {
772                let above_one = if self.sign.is_finite() {
773                    let base = self.var();
774                    if base.cmp(&Var::int(-1)) == Ordering::Equal {
775                        return one();
776                    }
777                    base.cmp_abs(&Var::int(1)) == Ordering::Greater
778                } else {
779                    true
780                };
781                return if above_one == (sign2 > 0) { Ok(Numeric::INFINITY) } else { zero() };
782            }
783            if self.sign == Infinity {
784                return if sign2 > 0 { Ok(Numeric::INFINITY) } else { zero() };
785            }
786            debug_assert_eq!(self.sign, NegativeInfinity);
787            if sign2 < 0 {
788                return zero();
789            }
790            let e = exp.var();
791            let odd = e.digits.len() as i64 == e.weight + 1
792                && e.digits.last().is_some_and(|d| d & 1 == 1);
793            return Ok(if odd { Numeric::NEGATIVE_INFINITY } else { Numeric::INFINITY });
794        }
795        if self.signum() == 0 && exp.signum() < 0 {
796            return Err(power_error("zero raised to a negative power is undefined"));
797        }
798        self.var().power(&exp.var())?.make()
799    }
800}
801
802/// `numeric_fac` fails above this number, whose factorial has the most digits that the format holds.
803const FACTORIAL_MAX: i64 = 32177;
804
805impl Numeric {
806    /// `numeric_div_trunc`: the quotient cut to an integer.
807    pub fn div_trunc(&self, other: &Numeric) -> Result<Numeric, TypeError> {
808        use NumericSign::{Infinity, NaN, NegativeInfinity};
809        if !self.sign.is_finite() || !other.sign.is_finite() {
810            if self.sign == NaN || other.sign == NaN {
811                return Ok(Numeric::NAN);
812            }
813            if !self.sign.is_finite() {
814                if !other.sign.is_finite() {
815                    return Ok(Numeric::NAN);
816                }
817                let negative = (self.sign == NegativeInfinity) != (other.signum() < 0);
818                return match other.signum() {
819                    0 => Err(division_by_zero()),
820                    _ if negative => Ok(Numeric::NEGATIVE_INFINITY),
821                    _ => Ok(Numeric::special(Infinity)),
822                };
823            }
824            return Var::zero(0).make();
825        }
826        self.var().div_round(&other.var(), 0, false, true)?.make()
827    }
828
829    /// `numeric_gcd`: the greatest common divisor, with the larger of the two display scales. `NaN` and the infinities give `NaN`.
830    pub fn gcd(&self, other: &Numeric) -> Result<Numeric, TypeError> {
831        if !self.sign.is_finite() || !other.sign.is_finite() {
832            return Ok(Numeric::NAN);
833        }
834        gcd(&self.var(), &other.var()).make()
835    }
836
837    /// `numeric_lcm`: the least common multiple, which is zero when one of the two is zero.
838    pub fn lcm(&self, other: &Numeric) -> Result<Numeric, TypeError> {
839        if !self.sign.is_finite() || !other.sign.is_finite() {
840            return Ok(Numeric::NAN);
841        }
842        let (x, y) = (self.var(), other.var());
843        let mut result = if x.is_zero() || y.is_zero() {
844            Var::zero(0)
845        } else {
846            let mut result = y.mul(&x.div(&gcd(&x, &y), 0, false));
847            result.sign = NumericSign::Positive;
848            result
849        };
850        result.dscale = x.dscale.max(y.dscale);
851        result.make()
852    }
853
854    /// `numeric_fac`: the factorial of an integer.
855    pub fn factorial(n: i64) -> Result<Numeric, TypeError> {
856        if n < 0 {
857            return Err(TypeError::new(
858                SqlState::NUMERIC_VALUE_OUT_OF_RANGE,
859                "factorial of a negative number is undefined".to_string(),
860            ));
861        }
862        if n > FACTORIAL_MAX {
863            return Err(overflow());
864        }
865        let mut result = Var::int(n.max(1));
866        for k in (2..n).rev() {
867            result = result.mul(&Var::int(k));
868        }
869        result.dscale = 0;
870        result.make()
871    }
872
873    /// `numeric_scale`: the display scale, or `None` for `NaN` and the infinities.
874    pub fn scale(&self) -> Option<i32> {
875        self.sign.is_finite().then_some(i32::from(self.dscale))
876    }
877
878    /// `numeric_min_scale`: the fewest digits after the point that hold the value, or `None` for `NaN` and the infinities.
879    pub fn min_scale(&self) -> Option<i32> {
880        self.sign.is_finite().then(|| min_scale(&self.var()) as i32)
881    }
882
883    /// `numeric_trim_scale`: the value with the display scale of `numeric_min_scale`.
884    pub fn trim_scale(&self) -> Numeric {
885        if !self.sign.is_finite() {
886            return self.clone();
887        }
888        let mut var = self.var();
889        var.dscale = min_scale(&var);
890        var.make().unwrap_or_else(|_| self.clone())
891    }
892
893    /// `width_bucket_numeric`: the bucket of `self` among `count` buckets of the same width from `low` to `high`.
894    pub fn width_bucket(
895        &self,
896        low: &Numeric,
897        high: &Numeric,
898        count: i32,
899    ) -> Result<i32, TypeError> {
900        let bucket_error = |message: &str| {
901            TypeError::new(
902                SqlState::INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION,
903                message.to_string(),
904            )
905        };
906        if count <= 0 {
907            return Err(bucket_error("count must be greater than zero"));
908        }
909        if !low.sign.is_finite() || !high.sign.is_finite() {
910            if low.sign == NumericSign::NaN || high.sign == NumericSign::NaN {
911                return Err(bucket_error("lower and upper bounds cannot be NaN"));
912            }
913            return Err(bucket_error("lower and upper bounds must be finite"));
914        }
915        let past = i64::from(count) + 1;
916        let bucket = match low.compare(high) {
917            Ordering::Equal => return Err(bucket_error("lower bound cannot equal upper bound")),
918            Ordering::Less if self.compare(low) == Ordering::Less => 0,
919            Ordering::Less if self.compare(high) != Ordering::Less => past,
920            Ordering::Greater if self.compare(low) == Ordering::Greater => 0,
921            Ordering::Greater if self.compare(high) != Ordering::Greater => past,
922            _ => {
923                // Multiply by the count before the division, so that the bucket is exact.
924                let (x, a, b) = (self.var(), low.var(), high.var());
925                let offset = x.sub(&a).mul(&Var::int(i64::from(count)));
926                let bucket = offset.div(&b.sub(&a), 0, false).add(&Var::int(1));
927                bucket.to_i64().unwrap_or(i64::MAX)
928            }
929        };
930        i32::try_from(bucket).map_err(|_| {
931            TypeError::new(SqlState::NUMERIC_VALUE_OUT_OF_RANGE, "integer out of range".to_string())
932        })
933    }
934}
935
936fn parameter_error(message: &str) -> TypeError {
937    TypeError::new(SqlState::INVALID_PARAMETER_VALUE, message.to_string())
938}
939
940impl Numeric {
941    /// `random_numeric`: a value from `rmin` to `rmax` with the larger of the two display scales, from the generator `prng`.
942    pub fn random(prng: &mut Prng, rmin: &Numeric, rmax: &Numeric) -> Result<Numeric, TypeError> {
943        for (bound, name) in [(rmin, "lower"), (rmax, "upper")] {
944            match bound.sign {
945                NumericSign::NaN => {
946                    return Err(parameter_error(&format!("{name} bound cannot be NaN")));
947                }
948                NumericSign::Infinity | NumericSign::NegativeInfinity => {
949                    return Err(parameter_error(&format!("{name} bound cannot be infinity")));
950                }
951                _ => {}
952            }
953        }
954        random_var(prng, &rmin.var(), &rmax.var())?.make()
955    }
956}
957
958/// `random_var`: a random value from `rmin` to `rmax`. It takes a value from 0 to `rmax - rmin` and adds `rmin`.
959#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss, clippy::cast_possible_wrap)]
960fn random_var(prng: &mut Prng, rmin: &Var, rmax: &Var) -> Result<Var, TypeError> {
961    let rscale = rmin.dscale.max(rmax.dscale);
962    let rlen = rmax.sub(rmin);
963    if !rlen.is_zero() && rlen.sign == NumericSign::Negative {
964        return Err(parameter_error("lower bound must be less than or equal to upper bound"));
965    }
966    if rlen.is_zero() {
967        let mut result = rmin.clone();
968        result.dscale = rscale;
969        return Ok(result);
970    }
971    let frac_ndigits = ((rscale + DEC_DIGITS - 1) / DEC_DIGITS) as usize;
972    let res_ndigits = (rlen.weight + 1 + frac_ndigits as i64) as usize;
973    // The last digit is a multiple of `pow10` to give the scale `rscale`.
974    let pow10 = 10u64.pow((frac_ndigits as i64 * DEC_DIGITS - rscale) as u32);
975    let nbase = NBASE as u64;
976    // The first four digits of `rlen`. The range from 0 to `rlen64` followed by nines is a little larger than the range from 0 to `rlen`, and a value above `rlen` is taken again.
977    let mut rlen64 = rlen.digits[0] as u64;
978    let mut rlen64_ndigits = 1;
979    while rlen64_ndigits < res_ndigits && rlen64_ndigits < 4 {
980        rlen64 *= nbase;
981        if let Some(&d) = rlen.digits.get(rlen64_ndigits) {
982            rlen64 += d as u64;
983        }
984        rlen64_ndigits += 1;
985    }
986    let whole_ndigits = if pow10 == 1 { res_ndigits } else { res_ndigits - 1 };
987    loop {
988        let mut digits = vec![0i32; res_ndigits];
989        let mut rand = if rlen64_ndigits == res_ndigits && pow10 != 1 {
990            prng.uint64_range(0, rlen64 / pow10) * pow10
991        } else {
992            prng.uint64_range(0, rlen64)
993        };
994        for d in digits[..rlen64_ndigits].iter_mut().rev() {
995            *d = (rand % nbase) as i32;
996            rand /= nbase;
997        }
998        let mut i = rlen64_ndigits;
999        while i + 3 < whole_ndigits {
1000            let mut rand = prng.uint64_range(0, nbase * nbase * nbase * nbase - 1);
1001            for d in &mut digits[i..i + 4] {
1002                *d = (rand % nbase) as i32;
1003                rand /= nbase;
1004            }
1005            i += 4;
1006        }
1007        while i < whole_ndigits {
1008            digits[i] = prng.uint64_range(0, nbase - 1) as i32;
1009            i += 1;
1010        }
1011        if i < res_ndigits {
1012            digits[i] = (prng.uint64_range(0, nbase / pow10 - 1) * pow10) as i32;
1013        }
1014        let mut result =
1015            Var { sign: NumericSign::Positive, weight: rlen.weight, dscale: rscale, digits };
1016        result.strip();
1017        if result.cmp(&rlen) != Ordering::Greater {
1018            return Ok(result.add(rmin));
1019        }
1020    }
1021}
1022
1023/// `gcd_var`: the greatest common divisor by the algorithm of Euclid, with the larger of the two display scales.
1024fn gcd(x: &Var, y: &Var) -> Var {
1025    let dscale = x.dscale.max(y.dscale);
1026    let (mut a, mut b) = if x.cmp_abs(y) == Ordering::Less {
1027        (y.clone(), x.clone())
1028    } else {
1029        (x.clone(), y.clone())
1030    };
1031    while !b.is_zero() && a.cmp_abs(&b) != Ordering::Equal {
1032        let rest = a.rem(&b);
1033        a = b;
1034        b = rest;
1035    }
1036    a.sign = NumericSign::Positive;
1037    a.dscale = dscale;
1038    a
1039}
1040
1041/// `get_min_scale`: the digits after the point up to the last digit that is not zero.
1042fn min_scale(var: &Var) -> i64 {
1043    let Some(last) = var.digits.iter().rposition(|&d| d != 0) else { return 0 };
1044    let mut scale = (last as i64 - var.weight) * DEC_DIGITS;
1045    if scale <= 0 {
1046        return 0;
1047    }
1048    let mut digit = var.digits[last];
1049    while digit % 10 == 0 {
1050        scale -= 1;
1051        digit /= 10;
1052    }
1053    scale
1054}