use crate::integer::Integer;
use crate::natural::{Natural, WIDTH_MINUS_1};
use crate::platform::{Limb, SignedLimb};
use core::cmp::max;
use core::mem::swap;
use malachite_base::num::arithmetic::traits::{
AddMul, DivMod, Parity, SubMulAssign, UnsignedAbs, WrappingSubMul,
};
use malachite_base::num::basic::traits::{NegativeOne, Zero};
use malachite_base::num::conversion::traits::ExactFrom;
use malachite_base::num::logic::traits::SignificantBits;
pub fn extended_gcd_partial(
mut r2: Natural,
mut r1: Natural,
l: &Natural,
) -> (Integer, Integer, Natural, Natural) {
let mut co2 = Integer::ZERO;
let mut co1 = Integer::NEGATIVE_ONE;
while r1 != 0u32 && r1 > *l {
let bits = max(r2.significant_bits(), r1.significant_bits())
.saturating_sub(WIDTH_MINUS_1);
let mut rr2 = SignedLimb::exact_from(Limb::exact_from(&(&r2 >> bits)));
let mut rr1 = SignedLimb::exact_from(Limb::exact_from(&(&r1 >> bits)));
let bb = SignedLimb::exact_from(Limb::exact_from(&(l >> bits)));
let mut aa2: SignedLimb = 0;
let mut aa1: SignedLimb = 1;
let mut bb2: SignedLimb = 1;
let mut bb1: SignedLimb = 0;
let mut i = 0u64;
while rr1 != 0 && rr1 > bb {
let qq = rr2 / rr1;
let t1 = rr2.wrapping_sub_mul(qq, rr1);
let t2 = aa2.wrapping_sub_mul(qq, aa1);
let t3 = bb2.wrapping_sub_mul(qq, bb1);
let stop = if i.odd() {
t1 < t3.wrapping_neg() || rr1.wrapping_sub(t1) < t2.wrapping_sub(aa1)
} else {
t1 < t2.wrapping_neg() || rr1.wrapping_sub(t1) < t3.wrapping_sub(bb1)
};
if stop {
break;
}
rr2 = rr1;
rr1 = t1;
aa2 = aa1;
aa1 = t2;
bb2 = bb1;
bb1 = t3;
i += 1;
}
if i == 0 {
let (q, r) = (&r2).div_mod(&r1);
r2 = r;
swap(&mut r2, &mut r1);
co2.sub_mul_assign(&co1, Integer::from(q));
swap(&mut co2, &mut co1);
} else {
let new_r2 = (Integer::from(&r2) * Integer::from(bb2))
.add_mul(Integer::from(&r1), Integer::from(aa2));
let new_r1 = (Integer::from(&r1) * Integer::from(aa1))
.add_mul(Integer::from(&r2), Integer::from(bb1));
let new_co2 =
(&co2 * Integer::from(bb2)).add_mul(co1.clone(), Integer::from(aa2));
let new_co1 = (&co1 * Integer::from(aa1)).add_mul(co2, Integer::from(bb1));
co1 = if new_r1 < 0u32 { -new_co1 } else { new_co1 };
r1 = new_r1.unsigned_abs();
co2 = if new_r2 < 0u32 { -new_co2 } else { new_co2 };
r2 = new_r2.unsigned_abs();
}
}
(co2, co1, r2, r1)
}