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
255pub(crate) fn pow_2_k_scaled_biguint(
257 n: &mut WithScale<BigUint>,
258 tmp: &mut BigUint,
259 k: u16,
260 precision_bits: u64,
261) {
262 if k == 0 {
263 return;
264 }
265 *tmp = &n.value * &n.value;
266 n.scale *= 2;
267 stdlib::mem::swap(tmp, &mut n.value);
268 while n.bits() > 2 * (precision_bits + k as u64) + 19 {
269 n.value /= 1_0000000000000000000u64;
270 n.scale -= 19;
271 }
272 pow_2_k_scaled_biguint(n, tmp, k - 1, precision_bits);
273}
274
275#[cfg(not(has_unsigned_abs))]
276fn unsigned_abs(n: i64) -> u64 {
277 if n != i64::MIN {
278 n.abs() as u64
279 } else {
280 (i64::MIN as i128).abs() as u64
281 }
282}
283
284#[cfg(has_unsigned_abs)]
285#[allow(clippy::incompatible_msrv)]
286fn unsigned_abs(n: i64) -> u64 {
287 n.unsigned_abs()
288}
289
290struct RunningPrecision {
299 min: u64,
301 margin: u64,
303 margin_per_mul: u64,
305}
306
307impl RunningPrecision {
308 fn new<'a, R: RadixPowerOfTen, E: Endianness>(
310 digits: DigitSlice<'a, R, E>,
311 exp: NonZeroU64,
312 prec: NonZeroU64,
313 ) -> Self {
314 let prec_bigdigit_count = R::divceil_digit_count(prec.get() as usize + 3) as u64;
317
318 let digit_count_f = digits.count_decimal_digits() as f64 ;
320
321 let count_squarings = 63 - exp.get().leading_zeros();
322 let count_non_squarings = exp.get().count_ones() - 1;
323 let muls = (count_non_squarings + count_squarings) as u64;
325
326 let max_partial_pow = {
328 let x = 1 << count_squarings;
329 let y = exp.get() - x;
330 (x / 2).max(y)
331 };
332
333 let max_width_digits_f = digit_count_f * max_partial_pow as f64;
335
336 let diag_sum_digit_len = 2.0 * log10(R::max().to_f64().unwrap()) + log10(max_width_digits_f);
338 let diag_sum_bigdigit_len = R::divceil_digit_count(diag_sum_digit_len.ceil() as usize) as u64;
339 let margin_per_mul = diag_sum_bigdigit_len + 1;
340
341 Self {
342 min: prec_bigdigit_count,
343 margin: (muls + 1) * margin_per_mul,
344 margin_per_mul: margin_per_mul,
345 }
346 }
347
348 fn next(&mut self) -> u64 {
350 self.margin = self.margin.saturating_sub(self.margin_per_mul);
351 self.margin + self.min
352 }
353}
354
355#[cfg(test)]
356mod test {
357 use super::*;
358
359 include!("pow.tests.rs");
360}