1use oxinum_core::{OxiNumError, OxiNumResult, Sign};
21
22use super::float::{BigFloat, RoundingMode};
23
24impl BigFloat {
29 pub fn pow(&self, exp: &BigFloat, prec: u32, mode: RoundingMode) -> OxiNumResult<BigFloat> {
56 assert!(prec > 0, "BigFloat precision must be > 0");
57
58 if exp.is_zero() {
60 return Ok(BigFloat::from_i64(1, prec, mode));
61 }
62
63 if self.is_nan() || exp.is_nan() {
66 return Ok(BigFloat::nan(prec));
67 }
68 if self.is_infinite() {
70 if exp.signum() > 0 {
71 return Ok(BigFloat::infinity(prec));
73 } else {
74 return Ok(BigFloat::zero(prec));
76 }
77 }
78 if exp.is_infinite() {
80 let one = BigFloat::from_i64(1, prec, mode);
82 if self == &one {
83 return Ok(one);
84 }
85 if self.signum() <= 0 {
89 return Ok(BigFloat::nan(prec));
90 }
91 let abs_f64 = self.abs().to_f64();
92 let is_gt_one = abs_f64 > 1.0;
93 let is_pos_inf = exp.sign == Sign::Positive;
94 return if is_gt_one == is_pos_inf {
95 Ok(BigFloat::infinity(prec))
96 } else {
97 Ok(BigFloat::zero(prec))
98 };
99 }
100
101 if self.is_zero() {
103 if exp.signum() > 0 {
104 return Ok(BigFloat::zero(prec));
105 } else {
106 return Err(OxiNumError::Domain(
107 "0^negative_exponent is undefined".into(),
108 ));
109 }
110 }
111
112 if exp.exponent >= 0 {
116 let exp_i64_opt = exact_integer_to_i64(exp);
119 if let Some(n) = exp_i64_opt {
120 return self.pow_int(n, prec, mode);
121 }
122 }
126
127 if self.signum() <= 0 {
129 return Err(OxiNumError::Domain(
130 "pow with fractional exponent requires a strictly positive base".into(),
131 ));
132 }
133
134 let guard = 16u32;
135 let work_prec = prec.saturating_add(guard);
136
137 let ln_self = self.ln(work_prec, mode)?;
138 let exp_wp = exp.clone().with_precision(work_prec, mode);
139 let product = ln_self
140 .mul_ref_with_mode(&exp_wp, mode)
141 .with_precision(work_prec, mode);
142 let result = product.exp(work_prec, mode)?;
143
144 Ok(result.with_precision(prec, mode))
145 }
146
147 fn pow_int(&self, n: i64, prec: u32, mode: RoundingMode) -> OxiNumResult<BigFloat> {
149 if n == 0 {
150 return Ok(BigFloat::from_i64(1, prec, mode));
151 }
152
153 if n < 0 {
154 let mag = (n as i128).unsigned_abs() as u64;
157 let positive_pow = self.pow_uint(mag, prec, mode);
158 let one = BigFloat::from_i64(1, prec, mode);
159 return one.div_ref_with_mode(&positive_pow, mode);
160 }
161
162 Ok(self.pow_uint(n as u64, prec, mode))
163 }
164
165 fn pow_uint(&self, mut exp_u: u64, prec: u32, mode: RoundingMode) -> BigFloat {
167 let mut result = BigFloat::from_i64(1, prec, mode);
168 let mut base = self.clone().with_precision(prec, mode);
169
170 while exp_u > 0 {
171 if exp_u & 1 == 1 {
172 result = result
173 .mul_ref_with_mode(&base, mode)
174 .with_precision(prec, mode);
175 }
176 base = base
177 .mul_ref_with_mode(&base.clone(), mode)
178 .with_precision(prec, mode);
179 exp_u >>= 1;
180 }
181
182 result
183 }
184}
185
186impl BigFloat {
191 pub fn log(&self, base: &BigFloat, prec: u32, mode: RoundingMode) -> OxiNumResult<BigFloat> {
211 assert!(prec > 0, "BigFloat precision must be > 0");
212
213 if self.is_zero() || self.signum() < 0 {
214 return Err(OxiNumError::Domain(
215 "log of non-positive number is undefined".into(),
216 ));
217 }
218 if base.is_zero() || base.signum() < 0 {
219 return Err(OxiNumError::Domain(
220 "log with non-positive base is undefined".into(),
221 ));
222 }
223
224 let one = BigFloat::from_i64(1, prec, mode);
226 if base == &one {
227 return Err(OxiNumError::Domain("log base 1 is undefined".into()));
228 }
229
230 let guard = 16u32;
231 let work_prec = prec.saturating_add(guard);
232
233 let ln_self = self.ln(work_prec, mode)?;
234 let ln_base = base.ln(work_prec, mode)?;
235
236 let result = ln_self.div_ref_with_mode(&ln_base, mode)?;
240
241 Ok(result.with_precision(prec, mode))
242 }
243}
244
245fn exact_integer_to_i64(f: &BigFloat) -> Option<i64> {
255 let bit_len = f.mantissa().bit_length();
260 let total_bits = bit_len.saturating_add(f.exponent() as u64);
261 if total_bits > 63 {
262 return None;
263 }
264 let mantissa_u64 = f.mantissa().to_u64()?;
266 let exp = f.exponent() as u64;
268 let value = mantissa_u64.checked_shl(exp as u32)?;
269 if f.sign() == oxinum_core::Sign::Negative {
271 if value > i64::MAX as u64 {
274 return None;
275 }
276 Some(-(value as i64))
277 } else {
278 if value > i64::MAX as u64 {
279 return None;
280 }
281 Some(value as i64)
282 }
283}
284
285#[cfg(test)]
290mod tests {
291 use super::*;
292 use crate::native::RoundingMode;
293
294 const MODE: RoundingMode = RoundingMode::HalfEven;
295
296 fn mk(n: i64, prec: u32) -> BigFloat {
297 BigFloat::from_i64(n, prec, MODE)
298 }
299
300 fn flt(x: f64, prec: u32) -> BigFloat {
301 BigFloat::from_f64(x, prec).expect("from_f64")
302 }
303
304 #[test]
307 fn pow_two_to_ten() {
308 let two = mk(2, 100);
309 let ten = mk(10, 100);
310 let result = two.pow(&ten, 100, MODE).expect("2^10");
311 assert!(
312 (result.to_f64() - 1024.0).abs() < 1e-10,
313 "2^10 = {}",
314 result.to_f64()
315 );
316 }
317
318 #[test]
319 fn pow_zero_exponent_is_one() {
320 let zero_exp = mk(0, 64);
321 for x_f in [2.0f64, 0.5, 10.0, 0.001, -3.0] {
322 let x = flt(x_f, 64);
323 let result = x.pow(&zero_exp, 64, MODE).expect("x^0");
324 assert!(
325 (result.to_f64() - 1.0).abs() < 1e-14,
326 "{}^0 should be 1, got {}",
327 x_f,
328 result.to_f64()
329 );
330 }
331 }
332
333 #[test]
334 fn pow_zero_base_positive_exp() {
335 let z = mk(0, 64);
336 let pos = mk(3, 64);
337 let result = z.pow(&pos, 64, MODE).expect("0^3");
338 assert!(result.is_zero(), "0^3 should be 0");
339 }
340
341 #[test]
342 fn pow_zero_base_negative_exp_is_error() {
343 let z = mk(0, 64);
344 let neg = mk(-1, 64);
345 assert!(z.pow(&neg, 64, MODE).is_err());
346 }
347
348 #[test]
349 fn pow_negative_base_fractional_exp_is_error() {
350 let neg = mk(-2, 64);
351 let half = flt(1.5, 64);
352 assert!(neg.pow(&half, 64, MODE).is_err());
353 }
354
355 #[test]
356 fn pow_inverse_property() {
357 let prec = 100u32;
359 for x_f in [2.0f64, 3.0, 7.5, 0.5] {
360 let x = flt(x_f, prec);
361 let y = flt(1.5, prec);
362 let neg_y = y.neg();
363 let xy = x.pow(&y, prec, MODE).expect("x^y");
364 let xny = x.pow(&neg_y, prec, MODE).expect("x^(-y)");
365 let product = xy.mul_ref_with_mode(&xny, MODE).with_precision(prec, MODE);
366 assert!(
367 (product.to_f64() - 1.0).abs() < 1e-13,
368 "x^y * x^(-y) ≠ 1 for x={}, got {}",
369 x_f,
370 product.to_f64()
371 );
372 }
373 }
374
375 #[test]
376 fn pow_negative_integer_exponent() {
377 let two = mk(2, 100);
379 let neg_one = mk(-1, 100);
380 let result = two.pow(&neg_one, 100, MODE).expect("2^(-1)");
381 assert!(
382 (result.to_f64() - 0.5).abs() < 1e-14,
383 "2^-1 = {}",
384 result.to_f64()
385 );
386 }
387
388 #[test]
389 fn pow_large_integer_exponent_cross_val() {
390 let three = mk(3, 100);
392 let twenty = mk(20, 100);
393 let result = three.pow(&twenty, 100, MODE).expect("3^20");
394 assert!(
395 (result.to_f64() - 3_486_784_401.0_f64).abs() < 1.0,
396 "3^20 ≈ {}, expected 3486784401",
397 result.to_f64()
398 );
399 }
400
401 #[test]
402 fn pow_fractional_exp_sqrt() {
403 let prec = 100u32;
405 for x_f in [4.0f64, 9.0, 2.0, 0.25] {
406 let x = flt(x_f, prec);
407 let half = flt(0.5, prec);
408 let result = x.pow(&half, prec, MODE).expect("x^0.5");
409 let expected = x_f.sqrt();
410 assert!(
411 (result.to_f64() - expected).abs() < 1e-13,
412 "{}^0.5 ≈ {}, expected {}",
413 x_f,
414 result.to_f64(),
415 expected
416 );
417 }
418 }
419
420 #[test]
423 fn log_base_10_of_100() {
424 let hundred = mk(100, 100);
425 let ten = mk(10, 100);
426 let result = hundred.log(&ten, 100, MODE).expect("log_10(100)");
427 assert!(
428 (result.to_f64() - 2.0).abs() < 1e-14,
429 "log_10(100) = {}, expected 2",
430 result.to_f64()
431 );
432 }
433
434 #[test]
435 fn log_base_x_of_x_is_one() {
436 let prec = 100u32;
437 for x_f in [2.0f64, std::f64::consts::E, 10.0, 100.0] {
438 let x = flt(x_f, prec);
439 let result = x.log(&x.clone(), prec, MODE).expect("log_x(x)");
440 assert!(
441 (result.to_f64() - 1.0).abs() < 1e-12,
442 "log_{}({}) ≠ 1, got {}",
443 x_f,
444 x_f,
445 result.to_f64()
446 );
447 }
448 }
449
450 #[test]
451 fn log_base_10_of_1000() {
452 let val = mk(1000, 100);
453 let ten = mk(10, 100);
454 let result = val.log(&ten, 100, MODE).expect("log_10(1000)");
455 assert!(
456 (result.to_f64() - 3.0).abs() < 1e-14,
457 "log_10(1000) = {}, expected 3",
458 result.to_f64()
459 );
460 }
461
462 #[test]
463 fn log_base_non_positive_is_error() {
464 let pos = mk(8, 64);
465 let zero_base = mk(0, 64);
466 let neg_base = mk(-2, 64);
467 assert!(pos.log(&zero_base, 64, MODE).is_err());
468 assert!(pos.log(&neg_base, 64, MODE).is_err());
469 }
470
471 #[test]
472 fn log_of_non_positive_is_error() {
473 let ten = mk(10, 64);
474 let zero_val = mk(0, 64);
475 let neg_val = mk(-1, 64);
476 assert!(zero_val.log(&ten, 64, MODE).is_err());
477 assert!(neg_val.log(&ten, 64, MODE).is_err());
478 }
479
480 #[test]
481 fn log_base_1_is_error() {
482 let val = mk(5, 64);
483 let one = mk(1, 64);
484 assert!(val.log(&one, 64, MODE).is_err());
485 }
486}