1use stdlib::num::NonZeroU64;
4
5use crate::*;
6
7use super::log10;
8use super::multiplication::{
9 multiply_decimals_with_context,
10 multiply_slices_with_prec_into_p19_z,
11};
12
13use bigdigit::digitvec::{DigitVec, DigitSlice};
14use bigdigit::radix::{RadixType, RadixPowerOfTen, RADIX_u64, RADIX_10p19_u64};
15use bigdigit::endian::{Endianness, LittleEndian};
16
17use arithmetic::multiplication::mul_scaled_slices_truncating_into;
18
19
20pub(crate) fn impl_powi_with_context<'a>(
25 bd: impl Into<BigDecimalRef<'a>>,
26 exp: i64,
27 ctx: &Context,
28) -> BigDecimal {
29 powi_with_context(bd.into(), exp, ctx)
30}
31
32fn powi_with_context(
35 bd: BigDecimalRef,
36 exp: i64,
37 ctx: &Context,
38) -> BigDecimal {
39 match exp.cmp(&0) {
40 Ordering::Equal => BigDecimal::one(),
41 Ordering::Greater => pow_u64_with_context(bd, exp as u64, ctx),
42 Ordering::Less => {
43 if exp == -1 {
44 return bd.inverse_with_context(&ctx);
45 }
46 let inv_ctx = Context::new_truncating(
48 (ctx.precision().get() * 2)
49 .max((bd.digits.bits() as f64 * LOG10_2 * 2.0).ceil() as u64)
50 .max(38) );
52 let inverted_bd = bd.inverse_with_context(&inv_ctx);
53 pow_u64_with_context(inverted_bd.to_ref(), unsigned_abs(exp), &ctx)
55 }
56 }
57}
58
59
60fn pow_u64_with_context(
66 bd: BigDecimalRef,
67 exp: u64,
68 ctx: &Context,
69) -> BigDecimal {
70 type R = RADIX_10p19_u64;
71
72 if true {
match (&exp, &0) {
(left_val, right_val) => {
if *left_val == *right_val {
let kind = ::core::panicking::AssertKind::Ne;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
};
};debug_assert_ne!(exp, 0);
73
74 if exp == 1 {
75 return ctx.round_decimal_ref(bd);
76 }
77
78 if (bd.digits.bits() as f64 * exp as f64) < (ctx.precision().get() as f64 * LOG2_10) {
80 return pow_u64_no_context(bd, exp);
81 }
82
83 let mut tmp = Vec::new();
84 let bd_as_base10p19 = DigitVec::from_biguint_using_tmp(bd.digits, &mut tmp);
85 if true {
match (&tmp.len(), &0) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
};
};debug_assert_eq!(tmp.len(), 0);
86
87 let mut prec = RunningPrecision::new(
88 bd_as_base10p19.as_digit_slice(),
89 NonZeroU64::new(exp).unwrap(),
90 ctx.precision(),
91 );
92
93 let mut n = exp;
96
97 let final_x_pow = 1 << (63 - n.leading_zeros() as u64);
100 let final_y_pow = n - final_x_pow;
101
102 let final_x_times_y = final_y_pow != 0;
106
107 let mut y_vec = Vec::new();
108 if final_x_times_y {
109 y_vec.reserve(bd_as_base10p19.len());
110 y_vec.push(1);
111 } else {
112 n >>= 1;
115 }
116
117 let mut digits_x = WithScale {
118 value: bd_as_base10p19,
119 scale: 0,
120 };
121 let mut digits_y = WithScale {
122 value: DigitVec::from_vec(y_vec),
123 scale: 0,
124 };
125 let mut prod = WithScale {
127 value: DigitVec::from_vec(tmp),
128 scale: 0,
129 };
130
131 let mut trailing_zeros = true;
133
134 while n > 1 {
135 if n % 2 == 1 {
136 let skipped_bigdigit_count = mul_scaled_slices_truncating_into(
138 &mut prod,
139 digits_y.as_digit_slice(),
140 digits_x.as_digit_slice(),
141 prec.next(),
142 );
143
144 trailing_zeros = trailing_zeros
145 && {
146 let skipped = skipped_bigdigit_count.saturating_sub(digits_y.value.len() - 1);
147 digits_x.value.least_n_are_zero(skipped)
148 }
149 && {
150 let skipped = skipped_bigdigit_count.saturating_sub(digits_x.value.len() - 1);
151 digits_y.value.least_n_are_zero(skipped)
152 };
153
154 stdlib::mem::swap(&mut prod, &mut digits_y);
156 }
157 let skipped_bigdigits = mul_scaled_slices_truncating_into(
159 &mut prod,
160 digits_x.as_digit_slice(),
161 digits_x.as_digit_slice(),
162 prec.next(),
163 );
164 trailing_zeros = trailing_zeros
166 && {
167 let skipped = skipped_bigdigits.saturating_sub(digits_x.value.len() - 1);
168 digits_x.value.least_n_are_zero(skipped)
169 };
170
171 stdlib::mem::swap(&mut prod, &mut digits_x);
173
174 n >>= 1;
176 }
177
178 let sign = if exp % 2 == 0 {
179 Sign::Plus
180 } else {
181 bd.sign()
182 };
183 let rounding = crate::rounding::NonDigitRoundingData {
184 mode: ctx.rounding_mode(),
185 sign: sign,
186 };
187
188 prod.value.clear();
189 prod.scale = 0;
190
191 let x_slice = digits_x.value.as_digit_slice();
192
193 let y_slice = if final_x_times_y {
194 digits_y.value.as_digit_slice()
195 } else {
196 if true {
match (&digits_y.value.digits.capacity(), &0) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
};
};debug_assert_eq!(digits_y.value.digits.capacity(), 0);
199 digits_y.scale = digits_x.scale;
200 x_slice
201 };
202
203 multiply_slices_with_prec_into_p19_z(
204 &mut prod,
205 x_slice,
206 y_slice,
207 ctx.precision(),
208 rounding,
209 trailing_zeros,
210 );
211
212 let mut scale = bd.scale * exp as i64 + prod.scale;
213 scale += (digits_x.scale + digits_y.scale) * R::DIGITS as i64;
214
215 let int_val = BigInt::from_biguint(sign, prod.value.into_biguint());
216
217 BigDecimal::new(int_val, scale)
218}
219
220fn pow_u64_no_context(bd: BigDecimalRef, exp: u64) -> BigDecimal {
223 if true {
match (&exp, &0) {
(left_val, right_val) => {
if *left_val == *right_val {
let kind = ::core::panicking::AssertKind::Ne;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
};
};debug_assert_ne!(exp, 0);
224 if exp == 1 {
225 return bd.to_owned();
226 }
227
228 let mut x = bd.digits.clone();
229 let mut y: BigUint = 1u8.into();
230
231 let mut n = exp;
232 while n > 1 {
233 if n % 2 == 1 {
234 y *= &x;
235 }
236 x = x.pow(2u8);
237 n >>= 1;
238 }
239
240 let p = x * y;
242
243 let sign = if exp % 2 == 0 {
244 Sign::Plus
245 } else {
246 bd.sign()
247 };
248
249 let scale = bd.scale * exp as i64;
250 let int_val = BigInt::from_biguint(sign, p);
251
252 BigDecimal::new(int_val, scale)
253}
254
255#[cfg(not(has_unsigned_abs))]
256fn unsigned_abs(n: i64) -> u64 {
257 if n != i64::MIN {
258 n.abs() as u64
259 } else {
260 (i64::MIN as i128).abs() as u64
261 }
262}
263
264#[cfg(has_unsigned_abs)]
265#[allow(clippy::incompatible_msrv)]
266fn unsigned_abs(n: i64) -> u64 {
267 n.unsigned_abs()
268}
269
270struct RunningPrecision {
279 min: u64,
281 margin: u64,
283 margin_per_mul: u64,
285}
286
287impl RunningPrecision {
288 fn new<'a, R: RadixPowerOfTen, E: Endianness>(
290 digits: DigitSlice<'a, R, E>,
291 exp: NonZeroU64,
292 prec: NonZeroU64,
293 ) -> Self {
294 let prec_bigdigit_count = R::divceil_digit_count(prec.get() as usize + 3) as u64;
297
298 let digit_count_f = digits.count_decimal_digits() as f64 ;
300
301 let count_squarings = 63 - exp.get().leading_zeros();
302 let count_non_squarings = exp.get().count_ones() - 1;
303 let muls = (count_non_squarings + count_squarings) as u64;
305
306 let max_partial_pow = {
308 let x = 1 << count_squarings;
309 let y = exp.get() - x;
310 (x / 2).max(y)
311 };
312
313 let max_width_digits_f = digit_count_f * max_partial_pow as f64;
315
316 let diag_sum_digit_len = 2.0 * log10(R::max().to_f64().unwrap()) + log10(max_width_digits_f);
318 let diag_sum_bigdigit_len = R::divceil_digit_count(diag_sum_digit_len.ceil() as usize) as u64;
319 let margin_per_mul = diag_sum_bigdigit_len + 1;
320
321 Self {
322 min: prec_bigdigit_count,
323 margin: (muls + 1) * margin_per_mul,
324 margin_per_mul: margin_per_mul,
325 }
326 }
327
328 fn next(&mut self) -> u64 {
330 self.margin = self.margin.saturating_sub(self.margin_per_mul);
331 self.margin + self.min
332 }
333}
334
335#[cfg(test)]
336mod test {
337 use super::*;
338
339 include!("pow.tests.rs");
340}