#![doc(hidden)]
#![unstable(feature = "core_private_bignum",
            reason = "internal routines only exposed for testing",
            issue = "0")]
#![macro_use]
use mem;
use intrinsics;
pub trait FullOps: Sized {
    
    
    fn full_add(self, other: Self, carry: bool) -> (bool , Self);
    
    
    fn full_mul(self, other: Self, carry: Self) -> (Self , Self);
    
    
    fn full_mul_add(self, other: Self, other2: Self, carry: Self) -> (Self , Self);
    
    
    fn full_div_rem(self,
                    other: Self,
                    borrow: Self)
                    -> (Self , Self );
}
macro_rules! impl_full_ops {
    ($($ty:ty: add($addfn:path), mul/div($bigty:ident);)*) => (
        $(
            impl FullOps for $ty {
                fn full_add(self, other: $ty, carry: bool) -> (bool, $ty) {
                    
                    
                    let (v, carry1) = intrinsics::add_with_overflow(self, other);
                    let (v, carry2) = intrinsics::add_with_overflow(v, if carry {1} else {0});
                    (carry1 || carry2, v)
                }
                fn full_mul(self, other: $ty, carry: $ty) -> ($ty, $ty) {
                    
                    
                    
                    let nbits = mem::size_of::<$ty>() * 8;
                    let v = (self as $bigty) * (other as $bigty) + (carry as $bigty);
                    ((v >> nbits) as $ty, v as $ty)
                }
                fn full_mul_add(self, other: $ty, other2: $ty, carry: $ty) -> ($ty, $ty) {
                    
                    
                    let nbits = mem::size_of::<$ty>() * 8;
                    let v = (self as $bigty) * (other as $bigty) + (other2 as $bigty) +
                            (carry as $bigty);
                    ((v >> nbits) as $ty, v as $ty)
                }
                fn full_div_rem(self, other: $ty, borrow: $ty) -> ($ty, $ty) {
                    debug_assert!(borrow < other);
                    
                    let nbits = mem::size_of::<$ty>() * 8;
                    let lhs = ((borrow as $bigty) << nbits) | (self as $bigty);
                    let rhs = other as $bigty;
                    ((lhs / rhs) as $ty, (lhs % rhs) as $ty)
                }
            }
        )*
    )
}
impl_full_ops! {
    u8:  add(intrinsics::u8_add_with_overflow),  mul/div(u16);
    u16: add(intrinsics::u16_add_with_overflow), mul/div(u32);
    u32: add(intrinsics::u32_add_with_overflow), mul/div(u64);
    
    
}
const SMALL_POW5: [(u64, usize); 3] = [(125, 3), (15625, 6), (1_220_703_125, 13)];
macro_rules! define_bignum {
    ($name:ident: type=$ty:ty, n=$n:expr) => (
        
        
        
        
        
        
        
        
        
        pub struct $name {
            
            
            
            size: usize,
            
            
            base: [$ty; $n]
        }
        impl $name {
            
            pub fn from_small(v: $ty) -> $name {
                let mut base = [0; $n];
                base[0] = v;
                $name { size: 1, base: base }
            }
            
            pub fn from_u64(mut v: u64) -> $name {
                use mem;
                let mut base = [0; $n];
                let mut sz = 0;
                while v > 0 {
                    base[sz] = v as $ty;
                    v >>= mem::size_of::<$ty>() * 8;
                    sz += 1;
                }
                $name { size: sz, base: base }
            }
            
            
            
            pub fn digits(&self) -> &[$ty] {
                &self.base[..self.size]
            }
            
            
            pub fn get_bit(&self, i: usize) -> u8 {
                use mem;
                let digitbits = mem::size_of::<$ty>() * 8;
                let d = i / digitbits;
                let b = i % digitbits;
                ((self.base[d] >> b) & 1) as u8
            }
            
            pub fn is_zero(&self) -> bool {
                self.digits().iter().all(|&v| v == 0)
            }
            
            
            pub fn bit_length(&self) -> usize {
                use mem;
                
                let digits = self.digits();
                let zeros = digits.iter().rev().take_while(|&&x| x == 0).count();
                let end = digits.len() - zeros;
                let nonzero = &digits[..end];
                if nonzero.is_empty() {
                    
                    return 0;
                }
                
                
                let digitbits = mem::size_of::<$ty>()* 8;
                let mut i = nonzero.len() * digitbits - 1;
                while self.get_bit(i) == 0 {
                    i -= 1;
                }
                i + 1
            }
            
            pub fn add<'a>(&'a mut self, other: &$name) -> &'a mut $name {
                use cmp;
                use num::bignum::FullOps;
                let mut sz = cmp::max(self.size, other.size);
                let mut carry = false;
                for (a, b) in self.base[..sz].iter_mut().zip(&other.base[..sz]) {
                    let (c, v) = (*a).full_add(*b, carry);
                    *a = v;
                    carry = c;
                }
                if carry {
                    self.base[sz] = 1;
                    sz += 1;
                }
                self.size = sz;
                self
            }
            pub fn add_small(&mut self, other: $ty) -> &mut $name {
                use num::bignum::FullOps;
                let (mut carry, v) = self.base[0].full_add(other, false);
                self.base[0] = v;
                let mut i = 1;
                while carry {
                    let (c, v) = self.base[i].full_add(0, carry);
                    self.base[i] = v;
                    carry = c;
                    i += 1;
                }
                if i > self.size {
                    self.size = i;
                }
                self
            }
            
            pub fn sub<'a>(&'a mut self, other: &$name) -> &'a mut $name {
                use cmp;
                use num::bignum::FullOps;
                let sz = cmp::max(self.size, other.size);
                let mut noborrow = true;
                for (a, b) in self.base[..sz].iter_mut().zip(&other.base[..sz]) {
                    let (c, v) = (*a).full_add(!*b, noborrow);
                    *a = v;
                    noborrow = c;
                }
                assert!(noborrow);
                self.size = sz;
                self
            }
            
            
            pub fn mul_small(&mut self, other: $ty) -> &mut $name {
                use num::bignum::FullOps;
                let mut sz = self.size;
                let mut carry = 0;
                for a in &mut self.base[..sz] {
                    let (c, v) = (*a).full_mul(other, carry);
                    *a = v;
                    carry = c;
                }
                if carry > 0 {
                    self.base[sz] = carry;
                    sz += 1;
                }
                self.size = sz;
                self
            }
            
            pub fn mul_pow2(&mut self, bits: usize) -> &mut $name {
                use mem;
                let digitbits = mem::size_of::<$ty>() * 8;
                let digits = bits / digitbits;
                let bits = bits % digitbits;
                assert!(digits < $n);
                debug_assert!(self.base[$n-digits..].iter().all(|&v| v == 0));
                debug_assert!(bits == 0 || (self.base[$n-digits-1] >> (digitbits - bits)) == 0);
                
                for i in (0..self.size).rev() {
                    self.base[i+digits] = self.base[i];
                }
                for i in 0..digits {
                    self.base[i] = 0;
                }
                
                let mut sz = self.size + digits;
                if bits > 0 {
                    let last = sz;
                    let overflow = self.base[last-1] >> (digitbits - bits);
                    if overflow > 0 {
                        self.base[last] = overflow;
                        sz += 1;
                    }
                    for i in (digits+1..last).rev() {
                        self.base[i] = (self.base[i] << bits) |
                                       (self.base[i-1] >> (digitbits - bits));
                    }
                    self.base[digits] <<= bits;
                    
                }
                self.size = sz;
                self
            }
            
            pub fn mul_pow5(&mut self, mut e: usize) -> &mut $name {
                use mem;
                use num::bignum::SMALL_POW5;
                
                
                let table_index = mem::size_of::<$ty>().trailing_zeros() as usize;
                let (small_power, small_e) = SMALL_POW5[table_index];
                let small_power = small_power as $ty;
                
                while e >= small_e {
                    self.mul_small(small_power);
                    e -= small_e;
                }
                
                let mut rest_power = 1;
                for _ in 0..e {
                    rest_power *= 5;
                }
                self.mul_small(rest_power);
                self
            }
            
            
            
            pub fn mul_digits<'a>(&'a mut self, other: &[$ty]) -> &'a mut $name {
                
                fn mul_inner(ret: &mut [$ty; $n], aa: &[$ty], bb: &[$ty]) -> usize {
                    use num::bignum::FullOps;
                    let mut retsz = 0;
                    for (i, &a) in aa.iter().enumerate() {
                        if a == 0 { continue; }
                        let mut sz = bb.len();
                        let mut carry = 0;
                        for (j, &b) in bb.iter().enumerate() {
                            let (c, v) = a.full_mul_add(b, ret[i + j], carry);
                            ret[i + j] = v;
                            carry = c;
                        }
                        if carry > 0 {
                            ret[i + sz] = carry;
                            sz += 1;
                        }
                        if retsz < i + sz {
                            retsz = i + sz;
                        }
                    }
                    retsz
                }
                let mut ret = [0; $n];
                let retsz = if self.size < other.len() {
                    mul_inner(&mut ret, &self.digits(), other)
                } else {
                    mul_inner(&mut ret, other, &self.digits())
                };
                self.base = ret;
                self.size = retsz;
                self
            }
            
            
            pub fn div_rem_small(&mut self, other: $ty) -> (&mut $name, $ty) {
                use num::bignum::FullOps;
                assert!(other > 0);
                let sz = self.size;
                let mut borrow = 0;
                for a in self.base[..sz].iter_mut().rev() {
                    let (q, r) = (*a).full_div_rem(other, borrow);
                    *a = q;
                    borrow = r;
                }
                (self, borrow)
            }
            
            
            pub fn div_rem(&self, d: &$name, q: &mut $name, r: &mut $name) {
                use mem;
                
                
                
                assert!(!d.is_zero());
                let digitbits = mem::size_of::<$ty>() * 8;
                for digit in &mut q.base[..] {
                    *digit = 0;
                }
                for digit in &mut r.base[..] {
                    *digit = 0;
                }
                r.size = d.size;
                q.size = 1;
                let mut q_is_zero = true;
                let end = self.bit_length();
                for i in (0..end).rev() {
                    r.mul_pow2(1);
                    r.base[0] |= self.get_bit(i) as $ty;
                    if &*r >= d {
                        r.sub(d);
                        
                        let digit_idx = i / digitbits;
                        let bit_idx = i % digitbits;
                        if q_is_zero {
                            q.size = digit_idx + 1;
                            q_is_zero = false;
                        }
                        q.base[digit_idx] |= 1 << bit_idx;
                    }
                }
                debug_assert!(q.base[q.size..].iter().all(|&d| d == 0));
                debug_assert!(r.base[r.size..].iter().all(|&d| d == 0));
            }
        }
        impl ::cmp::PartialEq for $name {
            fn eq(&self, other: &$name) -> bool { self.base[..] == other.base[..] }
        }
        impl ::cmp::Eq for $name {
        }
        impl ::cmp::PartialOrd for $name {
            fn partial_cmp(&self, other: &$name) -> ::option::Option<::cmp::Ordering> {
                ::option::Option::Some(self.cmp(other))
            }
        }
        impl ::cmp::Ord for $name {
            fn cmp(&self, other: &$name) -> ::cmp::Ordering {
                use cmp::max;
                let sz = max(self.size, other.size);
                let lhs = self.base[..sz].iter().cloned().rev();
                let rhs = other.base[..sz].iter().cloned().rev();
                lhs.cmp(rhs)
            }
        }
        impl ::clone::Clone for $name {
            fn clone(&self) -> $name {
                $name { size: self.size, base: self.base }
            }
        }
        impl ::fmt::Debug for $name {
            fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result {
                use mem;
                let sz = if self.size < 1 {1} else {self.size};
                let digitlen = mem::size_of::<$ty>() * 2;
                write!(f, "{:#x}", self.base[sz-1])?;
                for &v in self.base[..sz-1].iter().rev() {
                    write!(f, "_{:01$x}", v, digitlen)?;
                }
                ::result::Result::Ok(())
            }
        }
    )
}
pub type Digit32 = u32;
define_bignum!(Big32x40: type=Digit32, n=40);
#[doc(hidden)]
pub mod tests {
    define_bignum!(Big8x3: type=u8, n=3);
}