use crate::ops::{BinOp, CmpOp};
use crate::{BitVec, Width};
pub(crate) fn ult(a: &BitVec, b: &BitVec) -> bool {
BitVec::cmp_unchecked(CmpOp::Ult, a, b)
}
pub(crate) fn ule(a: &BitVec, b: &BitVec) -> bool {
BitVec::cmp_unchecked(CmpOp::Ule, a, b)
}
pub(crate) fn slt(a: &BitVec, b: &BitVec) -> bool {
BitVec::cmp_unchecked(CmpOp::Slt, a, b)
}
pub(crate) fn sle(a: &BitVec, b: &BitVec) -> bool {
BitVec::cmp_unchecked(CmpOp::Sle, a, b)
}
fn umin(a: BitVec, b: BitVec) -> BitVec {
if ult(&a, &b) { a } else { b }
}
fn umax(a: BitVec, b: BitVec) -> BitVec {
if ult(&a, &b) { b } else { a }
}
fn smin(a: BitVec, b: BitVec) -> BitVec {
if slt(&a, &b) { a } else { b }
}
fn smax(a: BitVec, b: BitVec) -> BitVec {
if slt(&a, &b) { b } else { a }
}
pub(crate) fn rem(v: &BitVec, m: u64) -> u64 {
if m == 1 {
return 0;
}
let m = u128::from(m);
let mut r = 0u128;
for &limb in v.limbs().iter().rev() {
r = ((r << 64) | u128::from(limb)) % m;
}
r as u64
}
pub(crate) fn gcd(mut a: u64, mut b: u64) -> u64 {
while b != 0 {
(a, b) = (b, a % b);
}
a
}
fn inverse(a: u64, n: u64) -> Option<u64> {
let (mut t, mut next_t) = (0i128, 1i128);
let (mut r, mut next_r) = (i128::from(n), i128::from(a % n));
while next_r != 0 {
let q = r / next_r;
(t, next_t) = (next_t, t - q * next_t);
(r, next_r) = (next_r, r - q * next_r);
}
(r == 1).then(|| t.rem_euclid(i128::from(n)) as u64)
}
pub(crate) fn crt(r1: u64, m1: u64, r2: u64, m2: u64) -> Option<Option<(u64, u64)>> {
let (r1, r2) = (r1 % m1, r2 % m2);
let g = gcd(m1, m2);
if r1 % g != r2 % g {
return None;
}
let Ok(lcm) = u64::try_from(u128::from(m1 / g) * u128::from(m2)) else {
return Some(None);
};
let n = m2 / g;
let k = if n == 1 {
0
} else {
let d = ((i128::from(r2) - i128::from(r1)) / i128::from(g)).rem_euclid(i128::from(n));
let inv = inverse(m1 / g, n)?;
(d as u128 * u128::from(inv)) % u128::from(n)
};
let x = (u128::from(r1) + u128::from(m1) * k) % u128::from(lcm);
Some(Some((x as u64, lcm)))
}
fn add_small(v: &BitVec, d: u64) -> Option<BitVec> {
let w = v.width();
if w.bits() < 64 && d >> w.bits() != 0 {
return None;
}
let r = BitVec::bin_unchecked(BinOp::Add, v, &BitVec::wrapping_from_u64(w, d));
ule(v, &r).then_some(r)
}
fn sub_small(v: &BitVec, d: u64) -> Option<BitVec> {
let w = v.width();
if w.bits() < 64 && d >> w.bits() != 0 {
return None;
}
let r = BitVec::bin_unchecked(BinOp::Sub, v, &BitVec::wrapping_from_u64(w, d));
ule(&r, v).then_some(r)
}
fn snap_up(lo: &BitVec, r: u64, m: u64) -> Option<BitVec> {
if m <= 1 {
return Some(*lo);
}
let d = (u128::from(r % m) + u128::from(m) - u128::from(rem(lo, m))) % u128::from(m);
add_small(lo, d as u64)
}
fn snap_down(hi: &BitVec, r: u64, m: u64) -> Option<BitVec> {
if m <= 1 {
return Some(*hi);
}
let d = (u128::from(rem(hi, m)) + u128::from(m) - u128::from(r % m)) % u128::from(m);
sub_small(hi, d as u64)
}
pub(crate) fn stride_of(d: &BitVec) -> u64 {
match d.to_u64() {
Some(v) if v > 0 => v,
_ => 1 << super::known::trailing_zeros(d).min(63),
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct URange {
lo: BitVec,
hi: BitVec,
stride: u64,
}
impl URange {
pub fn full(w: Width) -> Self {
URange {
lo: BitVec::zero(w),
hi: BitVec::ones(w),
stride: 1,
}
}
pub fn constant(v: &BitVec) -> Self {
URange {
lo: *v,
hi: *v,
stride: 0,
}
}
pub fn new(lo: BitVec, hi: BitVec) -> Option<Self> {
(lo.width() == hi.width() && ule(&lo, &hi)).then(|| URange::from_parts(lo, hi, 1))
}
pub fn strided(lo: BitVec, hi: BitVec, stride: u64) -> Option<Self> {
if lo.width() != hi.width() || !ule(&lo, &hi) {
return None;
}
if lo == hi {
return Some(URange::constant(&lo));
}
let span = BitVec::bin_unchecked(BinOp::Sub, &hi, &lo);
(stride >= 1 && rem(&span, stride) == 0).then_some(URange { lo, hi, stride })
}
pub(crate) fn from_parts(lo: BitVec, hi: BitVec, stride: u64) -> Self {
debug_assert!(lo.width() == hi.width() && ule(&lo, &hi));
let stride = if lo == hi { 0 } else { stride.max(1) };
debug_assert!(
stride == 0 || rem(&BitVec::bin_unchecked(BinOp::Sub, &hi, &lo), stride) == 0,
"stride {stride} does not divide [{lo}, {hi}]"
);
URange { lo, hi, stride }
}
pub(crate) fn from_raw(lo: BitVec, hi: BitVec, stride: u64) -> Self {
debug_assert_eq!(URange::from_parts(lo, hi, stride).stride, stride);
URange { lo, hi, stride }
}
pub fn lo(&self) -> BitVec {
self.lo
}
pub fn hi(&self) -> BitVec {
self.hi
}
pub fn stride(&self) -> u64 {
self.stride
}
pub fn width(&self) -> Width {
self.lo.width()
}
pub fn contains(&self, v: &BitVec) -> bool {
v.width() == self.width()
&& ule(&self.lo, v)
&& ule(v, &self.hi)
&& (self.stride <= 1
|| rem(&BitVec::bin_unchecked(BinOp::Sub, v, &self.lo), self.stride) == 0)
}
pub(crate) fn within(&self, o: &URange) -> bool {
if !ule(&o.lo, &self.lo) || !ule(&self.hi, &o.hi) {
return false;
}
if o.stride <= 1 {
return true;
}
let offset = rem(
&BitVec::bin_unchecked(BinOp::Sub, &self.lo, &o.lo),
o.stride,
);
offset == 0 && (self.stride == 0 || self.stride.is_multiple_of(o.stride))
}
pub fn is_full(&self) -> bool {
self.lo.is_zero() && self.hi.is_ones() && self.stride == 1
}
pub fn as_constant(&self) -> Option<BitVec> {
(self.lo == self.hi).then_some(self.lo)
}
pub fn meet(&self, o: &URange) -> Option<URange> {
if self.width() != o.width() {
return None;
}
if self.stride == 0 {
return o.contains(&self.lo).then_some(*self);
}
if o.stride == 0 {
return self.contains(&o.lo).then_some(*o);
}
let (lo, hi) = (umax(self.lo, o.lo), umin(self.hi, o.hi));
if ult(&hi, &lo) {
return None;
}
let (r1, r2) = (rem(&self.lo, self.stride), rem(&o.lo, o.stride));
let (r, m) = match crt(r1, self.stride, r2, o.stride)? {
Some(class) => class,
None if self.stride >= o.stride => (r1, self.stride),
None => (r2, o.stride),
};
URange::snapped(&lo, &hi, r, m)
}
pub(crate) fn meet_class(&self, r: u64, m: u64) -> Option<URange> {
if m <= 1 {
return Some(*self);
}
if self.stride == 0 {
return (rem(&self.lo, m) == r % m).then_some(*self);
}
let own = rem(&self.lo, self.stride);
let (r, m) = crt(own, self.stride, r, m)?.unwrap_or((own, self.stride));
URange::snapped(&self.lo, &self.hi, r, m)
}
fn snapped(lo: &BitVec, hi: &BitVec, r: u64, m: u64) -> Option<URange> {
let lo = snap_up(lo, r, m)?;
let hi = snap_down(hi, r, m)?;
ule(&lo, &hi).then(|| URange::from_parts(lo, hi, m))
}
pub fn join(&self, o: &URange) -> Option<URange> {
(self.width() == o.width()).then(|| self.hull(o))
}
pub(crate) fn hull(&self, o: &URange) -> URange {
debug_assert_eq!(self.width(), o.width());
let (lo, hi) = (umin(self.lo, o.lo), umax(self.hi, o.hi));
if lo == hi {
return URange::constant(&lo);
}
let apart = BitVec::bin_unchecked(BinOp::Sub, &umax(self.lo, o.lo), &lo);
let g = gcd(self.stride, o.stride);
let stride = match g {
0 => stride_of(&apart),
_ => gcd(g, rem(&apart, g)),
};
URange::from_parts(lo, hi, stride)
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct SRange {
lo: BitVec,
hi: BitVec,
}
impl SRange {
pub fn full(w: Width) -> Self {
SRange {
lo: BitVec::smin(w),
hi: BitVec::smax(w),
}
}
pub fn constant(v: &BitVec) -> Self {
SRange { lo: *v, hi: *v }
}
pub fn new(lo: BitVec, hi: BitVec) -> Option<Self> {
(lo.width() == hi.width() && sle(&lo, &hi)).then_some(SRange { lo, hi })
}
pub(crate) fn from_bounds(lo: BitVec, hi: BitVec) -> Self {
debug_assert!(lo.width() == hi.width() && sle(&lo, &hi));
SRange { lo, hi }
}
pub fn lo(&self) -> BitVec {
self.lo
}
pub fn hi(&self) -> BitVec {
self.hi
}
pub fn width(&self) -> Width {
self.lo.width()
}
pub fn contains(&self, v: &BitVec) -> bool {
v.width() == self.width() && sle(&self.lo, v) && sle(v, &self.hi)
}
pub fn is_full(&self) -> bool {
self.lo == BitVec::smin(self.width()) && self.hi == BitVec::smax(self.width())
}
pub fn as_constant(&self) -> Option<BitVec> {
(self.lo == self.hi).then_some(self.lo)
}
pub fn meet(&self, o: &SRange) -> Option<SRange> {
if self.width() != o.width() {
return None;
}
SRange::new(smax(self.lo, o.lo), smin(self.hi, o.hi))
}
pub fn join(&self, o: &SRange) -> Option<SRange> {
(self.width() == o.width()).then(|| self.hull(o))
}
pub(crate) fn hull(&self, o: &SRange) -> SRange {
debug_assert_eq!(self.width(), o.width());
SRange {
lo: smin(self.lo, o.lo),
hi: smax(self.hi, o.hi),
}
}
}