use super::classes::Classes;
use super::poly::{Poly, Sym};
use crate::BitVec;
use crate::facts::known::{bv_and, low_mask};
use crate::ops::BinOp;
pub(crate) const MAX_SUPPORT: usize = 12;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub(crate) enum BitOp {
And,
Or,
Xor,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub(crate) struct Bits {
pub(crate) support: Vec<u32>,
pub(crate) tables: Vec<Vec<u64>>,
}
fn words(s: usize) -> usize {
(1usize << s).div_ceil(64)
}
pub(crate) fn get(t: &[u64], p: usize) -> bool {
t[p / 64] >> (p % 64) & 1 == 1
}
fn set(t: &mut [u64], p: usize) {
t[p / 64] |= 1u64 << (p % 64);
}
fn trim(t: &mut [u64], s: usize) {
if s < 6 {
t[0] &= (1u64 << (1usize << s)) - 1;
}
}
impl Bits {
pub(crate) fn constant(bits: &[bool]) -> Bits {
Bits {
support: Vec::new(),
tables: bits.iter().map(|&b| vec![u64::from(b)]).collect(),
}
}
pub(crate) fn atom(classes: usize, a: u32) -> Bits {
Bits {
support: vec![a],
tables: vec![vec![0b10]; classes],
}
}
pub(crate) fn not(&self) -> Bits {
let s = self.support.len();
Bits {
support: self.support.clone(),
tables: self
.tables
.iter()
.map(|t| {
let mut n: Vec<u64> = t.iter().map(|w| !w).collect();
trim(&mut n, s);
n
})
.collect(),
}
}
fn expand(&self, to: &[u32]) -> Vec<Vec<u64>> {
let pos: Vec<usize> = self
.support
.iter()
.map(|a| to.iter().position(|b| b == a).unwrap_or(0))
.collect();
let n = to.len();
self.tables
.iter()
.map(|t| {
let mut out = vec![0u64; words(n)];
for q in 0..1usize << n {
let p = pos
.iter()
.enumerate()
.fold(0usize, |p, (k, &j)| p | ((q >> j) & 1) << k);
if get(t, p) {
set(&mut out, q);
}
}
out
})
.collect()
}
pub(crate) fn bin(op: BitOp, a: &Bits, b: &Bits) -> Option<Bits> {
let mut support: Vec<u32> = a.support.iter().chain(&b.support).copied().collect();
support.sort_unstable();
support.dedup();
if support.len() > MAX_SUPPORT {
return None;
}
let (ta, tb) = (a.expand(&support), b.expand(&support));
let tables = ta
.iter()
.zip(&tb)
.map(|(x, y)| {
x.iter()
.zip(y)
.map(|(u, v)| match op {
BitOp::And => u & v,
BitOp::Or => u | v,
BitOp::Xor => u ^ v,
})
.collect()
})
.collect();
Some(Bits { support, tables }.prune())
}
pub(crate) fn prune(self) -> Bits {
let s = self.support.len();
let keep: Vec<u32> = (0..s)
.filter(|&k| {
self.tables.iter().any(|t| {
(0..1usize << s).any(|p| p >> k & 1 == 0 && get(t, p) != get(t, p | 1 << k))
})
})
.map(|k| self.support[k])
.collect();
if keep.len() == s {
return self;
}
let pos: Vec<usize> = keep
.iter()
.map(|a| self.support.iter().position(|b| b == a).unwrap_or(0))
.collect();
let tables = self
.tables
.iter()
.map(|t| {
let mut out = vec![0u64; words(keep.len())];
for q in 0..1usize << keep.len() {
let p = pos
.iter()
.enumerate()
.fold(0usize, |p, (k, &j)| p | ((q >> k) & 1) << j);
if get(t, p) {
set(&mut out, q);
}
}
out
})
.collect();
Bits {
support: keep,
tables,
}
}
pub(crate) fn uniform(&self) -> bool {
self.tables.windows(2).all(|w| w[0] == w[1])
}
pub(crate) fn to_poly(&self, classes: &Classes) -> Poly {
let w = classes.width();
let s = self.support.len();
let mut out = Poly::zero(w);
for (c, t) in self.tables.iter().enumerate() {
let a = mobius(t, s);
for (q, &k) in a.iter().enumerate() {
if k == 0 {
continue;
}
let kv = BitVec::wrapping_from_i128(w, i128::from(k));
if q == 0 {
out.add_term(
Vec::new(),
&BitVec::bin_unchecked(BinOp::Mul, &kv, classes.mask(c)),
);
continue;
}
let set = (0..s)
.filter(|&j| q >> j & 1 == 1)
.fold(0u64, |m, j| m | 1u64 << self.support[j]);
let sym = Sym {
set,
class: c as u16,
};
out.add_term(vec![(sym, 1)], &kv);
}
}
out
}
pub(crate) fn from_linear(p: &Poly, classes: &Classes) -> Option<Bits> {
let (support, deltas) = corner_sums(p, classes)?;
let w = classes.width();
let s = support.len();
let konst = p.konst();
let mut tables = Vec::with_capacity(deltas.len());
for (c, g) in deltas.iter().enumerate() {
let inside = bv_and(&konst, classes.mask(c));
let k = if inside.is_zero() {
BitVec::zero(w)
} else if inside == *classes.mask(c) {
BitVec::one(w)
} else {
return None;
};
let prec = u32::from(w.bits()) - u32::from(classes.low(c));
let lm = low_mask(w, prec);
let mut t = vec![0u64; words(s)];
for (q, d) in g.iter().enumerate() {
let v = bv_and(&BitVec::bin_unchecked(BinOp::Add, &k, d), &lm);
if v == BitVec::one(w) {
set(&mut t, q);
} else if !v.is_zero() {
return None;
}
}
tables.push(t);
}
Some(Bits { support, tables }.prune())
}
pub(crate) fn zero_values(p: &Poly, classes: &Classes) -> Option<Vec<([bool; 2], bool)>> {
let (_, deltas) = corner_sums(p, classes)?;
let w = classes.width();
let one = BitVec::one(w);
let mut out = Vec::with_capacity(deltas.len());
for (c, g) in deltas.iter().enumerate() {
let lm = low_mask(w, u32::from(w.bits()) - u32::from(classes.low(c)));
let mut fits = [true, true];
let mut moved = false;
for d in g {
let d = bv_and(d, &lm);
moved |= !d.is_zero();
fits[0] &= d.is_zero() || d == one;
let v = bv_and(&BitVec::bin_unchecked(BinOp::Add, &one, &d), &lm);
fits[1] &= v.is_zero() || v == one;
}
if fits == [false, false] {
return None;
}
out.push((fits, moved));
}
Some(out)
}
}
fn corner_sums(p: &Poly, classes: &Classes) -> Option<(Vec<u32>, Vec<Vec<BitVec>>)> {
if p.degree() > 1 {
return None;
}
let w = classes.width();
let atoms = p.atoms();
let support: Vec<u32> = (0..64).filter(|&a| atoms >> a & 1 == 1).collect();
if support.len() > MAX_SUPPORT {
return None;
}
let s = support.len();
let mut gamma: Vec<Vec<BitVec>> = vec![vec![BitVec::zero(w); 1 << s]; classes.len()];
for (m, c) in p.terms() {
let Some(&(sym, _)) = m.first() else {
continue;
};
let q = support
.iter()
.enumerate()
.filter(|&(_, &a)| sym.set >> a & 1 == 1)
.fold(0usize, |q, (j, _)| q | 1 << j);
let slot = gamma.get_mut(usize::from(sym.class))?.get_mut(q)?;
*slot = BitVec::bin_unchecked(BinOp::Add, slot, c);
}
for g in gamma.iter_mut() {
for b in 0..s {
for q in 0..1usize << s {
if q >> b & 1 == 1 {
g[q] = BitVec::bin_unchecked(BinOp::Add, &g[q], &g[q ^ 1 << b]);
}
}
}
}
Some((support, gamma))
}
pub(crate) fn mobius(t: &[u64], s: usize) -> Vec<i64> {
let mut a: Vec<i64> = (0..1usize << s).map(|p| i64::from(get(t, p))).collect();
for b in 0..s {
for p in 0..1usize << s {
if p >> b & 1 == 1 {
a[p] -= a[p ^ 1 << b];
}
}
}
a
}
pub(crate) fn anf(t: &[u64], s: usize) -> Vec<usize> {
let mut a: Vec<bool> = (0..1usize << s).map(|p| get(t, p)).collect();
for b in 0..s {
for p in 0..1usize << s {
if p >> b & 1 == 1 {
a[p] ^= a[p ^ 1 << b];
}
}
}
(0..1usize << s).filter(|&p| a[p]).collect()
}
pub(crate) fn table8(t: &[u64], s: usize) -> Option<u8> {
if s > 3 {
return None;
}
let mut out = 0u8;
for i in 0..8usize {
let p = i & ((1 << s) - 1);
if get(t, p) {
out |= 1 << i;
}
}
Some(out)
}