use crate::math::numbers::gcd::limbs::gcd_11;
use core::arch::asm;
#[allow(dead_code)]
#[path = "portable.rs"]
mod portable;
pub(super) use portable::{addmul_1, extend_row, reduce_pair};
#[inline(always)]
pub(super) fn small_q1(n: u64, d: u64) -> (u64, u64) {
let (r, q): (u64, u64);
unsafe {
asm!(
"subs {t}, {r}, {d}, lsl #2",
"csel {r}, {t}, {r}, hs",
"cset {q}, hs",
"subs {t}, {r}, {d}, lsl #1",
"csel {r}, {t}, {r}, hs",
"adc {q}, {q}, {q}",
"subs {t}, {r}, {d}",
"csel {r}, {t}, {r}, hs",
"adc {q}, {q}, {q}",
r = inout(reg) n => r, d = in(reg) d, t = out(reg) _, q = out(reg) q,
options(pure, nomem, nostack),
);
}
(q, r)
}
#[inline(always)]
pub(super) fn small_q2(n: u128, d: u128) -> (u64, u128) {
let (mut lo, mut hi) = (n as u64, (n >> 64) as u64);
let (dl, dh) = (d as u64, (d >> 64) as u64);
let q: u64;
unsafe {
asm!(
"extr {h4}, {dh}, {dl}, #62",
"lsl {l4}, {dl}, #2",
"extr {h2}, {dh}, {dl}, #63",
"lsl {l2}, {dl}, #1",
"subs {t0}, {lo}, {l4}",
"sbcs {t1}, {hi}, {h4}",
"csel {lo}, {t0}, {lo}, hs",
"csel {hi}, {t1}, {hi}, hs",
"cset {q}, hs",
"subs {t0}, {lo}, {l2}",
"sbcs {t1}, {hi}, {h2}",
"csel {lo}, {t0}, {lo}, hs",
"csel {hi}, {t1}, {hi}, hs",
"adc {q}, {q}, {q}",
"subs {t0}, {lo}, {dl}",
"sbcs {t1}, {hi}, {dh}",
"csel {lo}, {t0}, {lo}, hs",
"csel {hi}, {t1}, {hi}, hs",
"adc {q}, {q}, {q}",
lo = inout(reg) lo, hi = inout(reg) hi, dl = in(reg) dl, dh = in(reg) dh,
h4 = out(reg) _, l4 = out(reg) _, h2 = out(reg) _, l2 = out(reg) _,
t0 = out(reg) _, t1 = out(reg) _, q = out(reg) q,
options(pure, nomem, nostack),
);
}
(q, (u128::from(hi) << 64) | u128::from(lo))
}
#[inline]
pub(super) fn gcd_22(u: u128, v: u128) -> u128 {
debug_assert!(u & v & 1 == 1);
let (mut u1, mut u0, mut v1, mut v0) = ((u >> 64) as u64, u as u64, (v >> 64) as u64, v as u64);
if u1 | v1 != 0 {
unsafe {
asm!(
"2:",
"subs {t0}, {u0}, {v0}",
"sbcs {t1}, {u1}, {v1}",
"cbz {t0}, 4f",
"rbit {c}, {t0}",
"csel {v0}, {u0}, {v0}, lo",
"csel {v1}, {u1}, {v1}, lo",
"cneg {t0}, {t0}, lo",
"cinv {t1}, {t1}, lo",
"clz {c}, {c}",
"neg {n}, {c}",
"lsr {u0}, {t0}, {c}",
"lsl {x}, {t1}, {n}",
"lsr {u1}, {t1}, {c}",
"orr {u0}, {u0}, {x}",
"orr {x}, {u1}, {v1}",
"cbnz {x}, 2b",
"b 6f",
"4:",
"cbz {t1}, 5f",
"csel {v1}, {u1}, {v1}, lo",
"cneg {t1}, {t1}, lo",
"rbit {c}, {t1}",
"clz {c}, {c}",
"lsr {u0}, {t1}, {c}",
"mov {u1}, #0",
"cbnz {v1}, 2b",
"b 6f",
"5:",
"mov {v0}, {u0}",
"mov {v1}, {u1}",
"6:",
u0 = inout(reg) u0, u1 = inout(reg) u1, v0 = inout(reg) v0, v1 = inout(reg) v1,
t0 = out(reg) _, t1 = out(reg) _, c = out(reg) _, n = out(reg) _, x = out(reg) _,
options(pure, nomem, nostack),
);
}
if u1 | v1 != 0 {
return u128::from(u0) | (u128::from(u1) << 64);
}
}
u128::from(gcd_11(u0, v0))
}