#![allow(missing_docs)]
#![cfg_attr(not(test), no_std)]
extern crate alloc;
use core::mem::size_of;
pub const MAX_VERTICES_PER_CORE: usize = 256;
pub const MAX_EDGES_PER_CORE: usize = 384;
pub type CompactVertexId = u16;
pub type CompactEdgeId = u16;
#[derive(Clone, Copy, Default)]
#[repr(C, align(32))]
pub struct BitSet256 {
pub bits: [u64; 4], }
impl BitSet256 {
pub const fn new() -> Self {
Self { bits: [0; 4] }
}
#[inline(always)]
pub fn insert(&mut self, v: CompactVertexId) {
let idx = (v / 64) as usize;
let bit = v % 64;
if idx < 4 {
self.bits[idx] |= 1u64 << bit;
}
}
#[inline(always)]
pub fn contains(&self, v: CompactVertexId) -> bool {
let idx = (v / 64) as usize;
let bit = v % 64;
idx < 4 && (self.bits[idx] & (1u64 << bit)) != 0
}
#[inline(always)]
pub fn remove(&mut self, v: CompactVertexId) {
let idx = (v / 64) as usize;
let bit = v % 64;
if idx < 4 {
self.bits[idx] &= !(1u64 << bit);
}
}
#[inline(always)]
pub fn count(&self) -> u32 {
self.bits.iter().map(|b| b.count_ones()).sum()
}
#[inline(always)]
pub fn union(&self, other: &Self) -> Self {
Self {
bits: [
self.bits[0] | other.bits[0],
self.bits[1] | other.bits[1],
self.bits[2] | other.bits[2],
self.bits[3] | other.bits[3],
],
}
}
#[inline(always)]
pub fn intersection(&self, other: &Self) -> Self {
Self {
bits: [
self.bits[0] & other.bits[0],
self.bits[1] & other.bits[1],
self.bits[2] & other.bits[2],
self.bits[3] & other.bits[3],
],
}
}
#[inline(always)]
pub fn xor(&self, other: &Self) -> Self {
Self {
bits: [
self.bits[0] ^ other.bits[0],
self.bits[1] ^ other.bits[1],
self.bits[2] ^ other.bits[2],
self.bits[3] ^ other.bits[3],
],
}
}
pub fn iter(&self) -> BitSet256Iter {
BitSet256Iter {
set: self,
current: self.bits[0],
word_idx: 0,
}
}
}
pub struct BitSet256Iter<'a> {
set: &'a BitSet256,
current: u64,
word_idx: usize,
}
impl<'a> Iterator for BitSet256Iter<'a> {
type Item = CompactVertexId;
fn next(&mut self) -> Option<Self::Item> {
loop {
if self.current != 0 {
let bit = self.current.trailing_zeros();
self.current &= self.current - 1; return Some((self.word_idx as u16 * 64) + bit as u16);
}
self.word_idx += 1;
if self.word_idx >= 4 {
return None;
}
self.current = self.set.bits[self.word_idx];
}
}
}
#[derive(Clone, Copy, Default)]
#[repr(C, packed)]
pub struct CompactEdge {
pub source: CompactVertexId, pub target: CompactVertexId, pub weight: u16, pub flags: u16, }
impl CompactEdge {
pub const FLAG_ACTIVE: u16 = 0x0001;
pub const FLAG_IN_CUT: u16 = 0x0002;
pub const FLAG_TREE_EDGE: u16 = 0x0004;
#[inline(always)]
pub fn is_active(&self) -> bool {
self.flags & Self::FLAG_ACTIVE != 0
}
#[inline(always)]
pub fn is_in_cut(&self) -> bool {
self.flags & Self::FLAG_IN_CUT != 0
}
}
#[derive(Clone, Copy, Default)]
#[repr(C)]
pub struct CompactWitness {
pub membership: BitSet256, pub seed: CompactVertexId, pub boundary_size: u16, pub cardinality: u16, pub hash: u16, }
impl CompactWitness {
pub fn new(seed: CompactVertexId, membership: BitSet256, boundary: u16) -> Self {
let cardinality = membership.count() as u16;
let hash = Self::compute_hash(seed, &membership);
Self {
membership,
seed,
boundary_size: boundary,
cardinality,
hash,
}
}
fn compute_hash(seed: CompactVertexId, membership: &BitSet256) -> u16 {
let mut h = seed as u32;
for &word in &membership.bits {
h = h.wrapping_mul(31).wrapping_add(word as u32);
}
(h & 0xFFFF) as u16
}
#[inline(always)]
pub fn contains(&self, v: CompactVertexId) -> bool {
self.membership.contains(v)
}
}
#[derive(Clone)]
#[repr(C)]
pub struct CompactAdjacency {
pub offsets: [u16; MAX_VERTICES_PER_CORE + 1], pub neighbors: [(CompactVertexId, CompactEdgeId); MAX_EDGES_PER_CORE * 2], }
impl Default for CompactAdjacency {
fn default() -> Self {
Self {
offsets: [0; MAX_VERTICES_PER_CORE + 1],
neighbors: [(0, 0); MAX_EDGES_PER_CORE * 2],
}
}
}
impl CompactAdjacency {
pub fn neighbors(&self, v: CompactVertexId) -> &[(CompactVertexId, CompactEdgeId)] {
let start = self.offsets[v as usize] as usize;
let end = self.offsets[v as usize + 1] as usize;
&self.neighbors[start..end]
}
pub fn degree(&self, v: CompactVertexId) -> u16 {
self.offsets[v as usize + 1] - self.offsets[v as usize]
}
}
#[repr(C)]
pub struct CompactCoreState {
pub adjacency: CompactAdjacency,
pub edges: [CompactEdge; MAX_EDGES_PER_CORE],
pub num_vertices: u16,
pub num_edges: u16,
pub min_cut: u16,
pub best_witness: CompactWitness,
pub lambda_min: u16,
pub lambda_max: u16,
pub core_id: u8,
pub status: u8,
}
impl CompactCoreState {
pub const STATUS_IDLE: u8 = 0;
pub const STATUS_PROCESSING: u8 = 1;
pub const STATUS_DONE: u8 = 2;
pub const STATUS_ERROR: u8 = 3;
pub const fn size() -> usize {
size_of::<Self>()
}
}
const _: () = assert!(
CompactCoreState::size() <= 8192,
"CompactCoreState exceeds 8KB"
);
#[derive(Clone, Copy, Default)]
#[repr(C)]
pub struct CoreResult {
pub core_id: u8,
pub status: u8,
pub min_cut: u16,
pub witness_hash: u16,
pub witness_seed: CompactVertexId,
pub witness_cardinality: u16,
pub witness_boundary: u16,
pub padding: [u8; 4],
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bitset256_basic() {
let mut bs = BitSet256::new();
assert!(!bs.contains(0));
bs.insert(0);
bs.insert(100);
bs.insert(255);
assert!(bs.contains(0));
assert!(bs.contains(100));
assert!(bs.contains(255));
assert!(!bs.contains(50));
assert_eq!(bs.count(), 3);
}
#[test]
fn test_bitset256_iter() {
let mut bs = BitSet256::new();
bs.insert(1);
bs.insert(64);
bs.insert(200);
let collected: alloc::vec::Vec<_> = bs.iter().collect();
assert_eq!(collected, alloc::vec![1, 64, 200]);
}
#[test]
fn test_compact_witness_size() {
assert_eq!(size_of::<CompactWitness>(), 64);
}
#[test]
fn test_compact_edge_size() {
assert_eq!(size_of::<CompactEdge>(), 8);
}
#[test]
fn test_core_state_fits_8kb() {
assert!(CompactCoreState::size() <= 8192);
}
#[test]
fn test_bitset_operations() {
let mut a = BitSet256::new();
let mut b = BitSet256::new();
a.insert(1);
a.insert(2);
b.insert(2);
b.insert(3);
let union = a.union(&b);
assert!(union.contains(1));
assert!(union.contains(2));
assert!(union.contains(3));
let inter = a.intersection(&b);
assert!(!inter.contains(1));
assert!(inter.contains(2));
assert!(!inter.contains(3));
}
}