/// Concurrent div/mod operations for a number, so that you don't
/// have to do them separately.
pub trait DivMod
 where Self: Sized
{
    /// Compute the quotient and remainder of a and b.
    fn divmod(&self, rhs: &Self) -> (Self, Self);
}

pub fn get_number_size(v: &[u64]) -> Option<usize>
{
    for (idx, val) in v.iter().enumerate().rev() {
        if *val != 0 {
            return Some(idx);
        }
    }
    None
}

macro_rules! safesubidx
{
    ($array: expr, $index: expr, $amt: expr) => ({
        let idx = $index;
        let amt = $amt;

        if idx < amt {
            0
        } else {
            $array[idx-amt]
        }
    })
}

macro_rules! div_impls
{
    ($name: ident, $dbl: ident) => {
        impl DivMod for $name {
            fn divmod(&self, rhs: &$name) -> ($name, $name) {
                // if the divisor is larger, then the answer is pretty simple
                if rhs > self {
                    return ($name::zero(), self.clone());
                }
                // compute the basic number sizes
                let mut n = match get_number_size(&self.value) {
                                None    => 0,
                                Some(v) => v
                            };
                let t = match get_number_size(&rhs.value) {
                            None    => panic!("Division by zero!"),
                            Some(v) => v
                        };
                assert!(t <= n);
                // now generate mutable versions we can mess with
                let mut x = $dbl::from(self);
                let mut y = $dbl::from(rhs);
                // If we want this to perform reasonable, it's useful if the
                // value of y[t] is shifted so that the high bit is set.
                let lambda_shift = y.value[t].leading_zeros() as usize;
                if lambda_shift != 0 {
                    let dupx = x.value.clone();
                    let dupy = y.value.clone();

                    shiftl(&mut x.value, &dupx, lambda_shift);
                    shiftl(&mut y.value, &dupy, lambda_shift);
                    n = get_number_size(&x.value).unwrap();
                }
                // now go!
                // 1. For j from 0 to (n-t) do: q[j] = 0;
                //      [NB: I take some liberties with this concept]
                let mut q = $dbl::zero();
                // 2. While (x >= y * b^(n-t)) do the following:
                let mut ybnt = $dbl::zero();
                for i in 0..self.value.len() {
                    ybnt.value[(n-t)+i] = y.value[i];
                    if (n-t)+i >= ybnt.value.len() {
                        break;
                    }
                }
                while x > ybnt {
                    q.value[n - t] += 1;
                    x -= &ybnt;
                }
                // 3. For i from n down to t+1 do the following:
                let mut i = n;
                while i >= (t + 1) {
                    // 3.1. If x[i] = y[t]
                    if x.value[i] == y.value[t] {
                        // ... then set q[i-t-1] = b - 1
                        q.value[i-t-1] = 0xFFFFFFFFFFFFFFFF;
                    } else {
                        // ... otherwise set q[i-t-1] =
                        //        floor((x[i] * b + x[i-1]) / y[t])
                        let xib         = (x.value[i] as u128) << 64;
                        let xi1         = safesubidx!(x.value,i,1) as u128;
                        let yt          = y.value[t] as u128;
                        let qit1        = (xib + xi1) / yt;
                        q.value[i-t-1] = qit1 as u64;
                    }
                    // 3.2. While q[i-t-1] * (y[t]*b + y[t-1]) >
                    //                  (x[i] * b^2 + x[i-1] * b + x[i-2])
                    loop {
                        // three is very close to 2.
                        let     qit1    = U192::from(safesubidx!(q.value,i-t,1));
                        let mut ybits   = U192::zero();
                        ybits.value[0]  = safesubidx!(y.value, t, 1);
                        ybits.value[1]  = y.value[t];
                        let       qiybs = &qit1 * &ybits;
                        let mut   xbits = U384::zero();
                        xbits.value[0]  = safesubidx!(x.value,i,2);
                        xbits.value[1]  = safesubidx!(x.value,i,1);
                        xbits.value[2]  = x.value[i];

                        if !(&qiybs > &xbits) {
                            break;
                        }

                        // ... do q[i-t-1] = q[i-t-1] - 1
                        q.value[i-t-1] -= 1;
                    }
                    // 3.3. x = x - q[i-t-1] * y * b^(i-t-1)
                    // 3.4. If x < 0
                    //      then set x = x + y * b^(i-t-1) and
                    //               q[i-t-1] = q[i-t-1] - 1
                    let mut qbit1 = $name::zero();
                    qbit1.value[i-t-1] = q.value[i-t-1];
                    let smallery = $name::from(&y);
                    let mut subpart = &smallery * &qbit1;
                    if subpart > x {
                        let mut addback = $dbl::zero();
                        for (idx, val) in y.value.iter().enumerate() {
                            let dest = idx + (i - t - 1);
                            if dest < addback.value.len() {
                                addback.value[dest] = *val;
                            }
                        }
                        q.value[i-t-1] -= 1;
                        subpart -= &addback;
                    }
                    assert!(subpart <= x);
                    x -= &subpart;
                    i -= 1;
                }
                // 4. r = x ... sort of. Remember, we potentially did a bit of shifting
                // around at the very beginning, which we now need to account for. On the
                // bright side, we only need to account for this in the remainder.
                let mut r = $name::from(&x);
                let dupr = r.value.clone();
                shiftr(&mut r.value, &dupr, lambda_shift);
                // 5. Return (q,r)
                let resq = $name::from(&q);
                (resq, r)
            }
        }

        impl DivAssign for $name {
            fn div_assign(&mut self, rhs: $name) {
                self.div_assign(&rhs);
            }
        }

        impl<'a> DivAssign<&'a $name> for $name {
            fn div_assign(&mut self, rhs: &$name) {
                let (res, _) = self.divmod(&rhs);
                self.value.copy_from_slice(&res.value);
            }
        }

        impl Div for $name {
            type Output = $name;

            fn div(self, rhs: $name) -> $name {
                let (res, _) = self.divmod(&rhs);
                res
            }
        }

        impl<'a> Div<&'a $name> for $name {
            type Output = $name;

            fn div(self, rhs: &$name) -> $name {
                let (res, _) = self.divmod(rhs);
                res
            }
        }

        impl<'a> Div<$name> for &'a $name {
            type Output = $name;

            fn div(self, rhs: $name) -> $name {
                let (res, _) = self.divmod(&rhs);
                res
            }
        }
        impl<'a,'b> Div<&'a $name> for &'b $name {
            type Output = $name;

            fn div(self, rhs: &$name) -> $name {
                let (res, _) = self.divmod(rhs);
                res
            }
        }

        impl RemAssign for $name {
            fn rem_assign(&mut self, rhs: $name) {
                self.rem_assign(&rhs);
            }
        }

        impl<'a> RemAssign<&'a $name> for $name {
            fn rem_assign(&mut self, rhs: &$name) {
                let (_, res) = self.divmod(rhs);
                self.value.copy_from_slice(&res.value);
            }
        }

        impl Rem for $name {
            type Output = $name;

            fn rem(self, rhs: $name) -> $name {
                let (_, res) = self.divmod(&rhs);
                res
            }
        }

        impl<'a> Rem<&'a $name> for $name {
            type Output = $name;

            fn rem(self, rhs: &$name) -> $name {
                let (_, res) = self.divmod(&rhs);
                res
            }
        }

        impl<'a> Rem<$name> for &'a $name {
            type Output = $name;

            fn rem(self, rhs: $name) -> $name {
                let (_, res) = self.divmod(&rhs);
                res
            }
        }

        impl<'a,'b> Rem<&'a $name> for &'b $name {
            type Output = $name;

            fn rem(self, rhs: &$name) -> $name {
                let (_, res) = self.divmod(&rhs);
                res
            }
        }
    }
}

#[cfg(test)]
macro_rules! generate_div_tests {
    ($name: ident, $lname: ident) => {
        #[test]
        fn $lname() {
            generate_div_tests!(body $name, $lname);
        }
    };
    (ignore $name: ident, $lname: ident) => {
        #[test]
        #[ignore]
        fn $lname() {
            generate_div_tests!(body $name, $lname);
        }
    };
    (body $name: ident, $lname: ident) => {
        let fname = build_test_path("div", stringify!($name));
        run_test(fname.to_string(), 4, |case| {
            let (neg0, abytes) = case.get("a").unwrap();
            let (neg1, bbytes) = case.get("b").unwrap();
            let (neg2, qbytes) = case.get("q").unwrap();
            let (neg3, rbytes) = case.get("r").unwrap();
            assert!(!neg0 && !neg1 && !neg2 && !neg3);

            let a = $name::from_bytes(abytes);
            let b = $name::from_bytes(bbytes);
            let q = $name::from_bytes(qbytes);
            let r = $name::from_bytes(rbytes);
            let (myq, myr) = a.divmod(&b);
            assert_eq!(q, myq);
            assert_eq!(r, myr);
            let mut a2 = a.clone();
            a2 /= &b;
            assert_eq!(a2, q);
            let mut a3 = a.clone();
            a3 %= &b;
            assert_eq!(a3, r);
        });
    };
}