use ll;
use ll::limb::Limb;
use mem;
use ll::limb_ptr::{Limbs, LimbsMut};
pub unsafe fn pow(mut wp: LimbsMut, mut ap: Limbs, mut an: i32, mut exp: u32) {
debug_assert!(exp > 2);
debug_assert!(!ll::is_zero(ap, an));
let mut wn = num_pow_limbs(ap, an, exp);
debug_assert!(!ll::overlap(wp, wn, ap, an));
let mut tmp = mem::TmpAllocator::new();
ll::zero(wp, wn);
while *ap == 0 {
ap = ap.offset(1);
an -= 1;
wp = wp.offset(exp as isize);
wn -= exp as i32;
}
let trailing = (*ap).trailing_zeros() as u32;
let sz = wn as usize;
let (bp, scratch) = tmp.allocate_2(sz, sz + 1);
let mut bn = an;
if trailing > 0 {
ll::shr(bp, ap, an, trailing);
} else {
ll::copy_incr(ap, bp, an);
}
let mut shift = trailing * exp;
while shift >= Limb::BITS as u32 {
shift -= Limb::BITS as u32;
wp = wp.offset(1);
}
*wp = Limb(1);
let mut wn = 1;
loop {
if (exp & 1) == 1 {
if wn > bn {
ll::mul(scratch, wp.as_const(), wn, bp.as_const(), bn);
} else {
ll::mul(scratch, bp.as_const(), bn, wp.as_const(), wn);
}
wn = ll::normalize(scratch.as_const(), wn + bn);
ll::copy_incr(scratch.as_const(), wp, wn);
}
exp >>= 1;
if exp == 0 {
break;
}
ll::sqr(scratch, bp.as_const(), bn);
bn = ll::normalize(scratch.as_const(), bn + bn);
ll::copy_incr(scratch.as_const(), bp, bn);
}
if shift > 0 {
let v = ll::shl(wp, wp.as_const(), wn, shift);
if v > 0 {
*wp.offset(wn as isize) = v;
}
}
}
pub unsafe fn num_pow_limbs(xp: Limbs, xn: i32, exp: u32) -> i32 {
let n = xn - 1;
let high_limb = *xp.offset(n as isize);
let lg2 = Limb::BITS as u32 - high_limb.leading_zeros() as u32;
let lg2e = exp as i32 * lg2 as i32;
let elog_b = lg2e / Limb::BITS as i32;
elog_b + (exp as i32 * n) + 1
}