use clap::{Parser, ValueEnum};
use mem_dbg::*;
use rand::{RngExt, SeedableRng, rngs::SmallRng};
use sux::{
bits::BitVec,
rank_sel::{Rank9, RankSmall, Select9, SelectAdapt},
traits::*,
};
#[derive(Parser)]
#[command(about = "Prints the memory layout of rank and select structures.", long_about = None, next_line_help = true, max_term_width = 100)]
struct Args {
len: usize,
density: f64,
#[arg(short, long)]
non_uniform: bool,
#[arg(value_enum)]
sel_type: StructType,
}
trait Struct {
fn build(bits: BitVec<Vec<u64>>) -> Self;
}
impl Struct for SelectAdapt<AddNumBits<BitVec<Vec<u64>>>> {
fn build(bits: BitVec<Vec<u64>>) -> Self {
SelectAdapt::new(bits.into())
}
}
impl Struct for Select9<Rank9<BitVec<Vec<u64>>>> {
fn build(bits: BitVec<Vec<u64>>) -> Self {
Select9::new(Rank9::<BitVec<Vec<u64>>>::new(bits))
}
}
impl Struct for Rank9<BitVec<Vec<u64>>> {
fn build(bits: BitVec<Vec<u64>>) -> Self {
Rank9::<BitVec<Vec<u64>>>::new(bits)
}
}
impl Struct for RankSmall<64, 2, 9, BitVec<Vec<u64>>> {
fn build(bits: BitVec<Vec<u64>>) -> Self {
RankSmall::<64, 2, 9, BitVec<Vec<u64>>>::new(bits)
}
}
impl Struct for RankSmall<64, 1, 9, BitVec<Vec<u64>>> {
fn build(bits: BitVec<Vec<u64>>) -> Self {
RankSmall::<64, 1, 9, BitVec<Vec<u64>>>::new(bits)
}
}
impl Struct for RankSmall<64, 1, 10, BitVec<Vec<u64>>> {
fn build(bits: BitVec<Vec<u64>>) -> Self {
RankSmall::<64, 1, 10, BitVec<Vec<u64>>>::new(bits)
}
}
impl Struct for RankSmall<64, 1, 11, BitVec<Vec<u64>>> {
fn build(bits: BitVec<Vec<u64>>) -> Self {
RankSmall::<64, 1, 11, BitVec<Vec<u64>>>::new(bits)
}
}
impl Struct for RankSmall<64, 3, 13, BitVec<Vec<u64>>> {
fn build(bits: BitVec<Vec<u64>>) -> Self {
RankSmall::<64, 3, 13, BitVec<Vec<u64>>>::new(bits)
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug)]
enum StructType {
Rank9,
RankSmall0,
RankSmall1,
RankSmall2,
RankSmall3,
RankSmall4,
SelectAdapt,
Select9,
}
fn mem_usage<S: Struct + MemSize + MemDbg + BitLength>(
len: usize,
density: f64,
uniform: bool,
name: &str,
) {
let mut rng = SmallRng::seed_from_u64(0);
let (density0, density1) = if uniform {
(density, density)
} else {
(density * 0.01, density * 0.99)
};
let first_half = loop {
let b = (0..len / 2)
.map(|_| rng.random_bool(density0))
.collect::<BitVec<Vec<u64>>>();
if b.count_ones() > 0 {
break b;
}
};
let second_half = (0..len / 2)
.map(|_| rng.random_bool(density1))
.collect::<BitVec<Vec<u64>>>();
let bits = first_half
.into_iter()
.chain(&second_half)
.collect::<BitVec<Vec<u64>>>();
let s = S::build(bits);
let mem_cost = mem_cost(&s);
println!(
"BitVec with length: {}, density: {}, uniform: {}",
len, density, uniform
);
println!("Memory cost of {}: {}%", name, mem_cost);
s.mem_dbg(DbgFlags::PERCENTAGE).unwrap();
}
fn mem_cost<S: Struct + MemSize + MemDbg + BitLength>(s: &S) -> f64 {
(((s.mem_size(SizeFlags::default()) * 8 - s.len()) * 100) as f64) / (s.len() as f64)
}
fn main() {
let args = Args::parse();
let uniform = !args.non_uniform;
match args.sel_type {
StructType::SelectAdapt => {
mem_usage::<SelectAdapt<AddNumBits<BitVec<Vec<u64>>>>>(
args.len,
args.density,
uniform,
"SelectAdapt",
);
}
StructType::Select9 => {
mem_usage::<Select9<Rank9<BitVec<Vec<u64>>>>>(
args.len,
args.density,
uniform,
"Select9",
);
}
StructType::Rank9 => {
mem_usage::<Rank9<BitVec<Vec<u64>>>>(args.len, args.density, uniform, "Rank9");
}
StructType::RankSmall0 => {
mem_usage::<RankSmall<64, 2, 9, BitVec<Vec<u64>>>>(
args.len,
args.density,
uniform,
"RankSmall0",
);
}
StructType::RankSmall1 => {
mem_usage::<RankSmall<64, 1, 9, BitVec<Vec<u64>>>>(
args.len,
args.density,
uniform,
"RankSmall1",
);
}
StructType::RankSmall2 => {
mem_usage::<RankSmall<64, 1, 10, BitVec<Vec<u64>>>>(
args.len,
args.density,
uniform,
"RankSmall2",
);
}
StructType::RankSmall3 => {
mem_usage::<RankSmall<64, 1, 11, BitVec<Vec<u64>>>>(
args.len,
args.density,
uniform,
"RankSmall3",
);
}
StructType::RankSmall4 => {
mem_usage::<RankSmall<64, 3, 13, BitVec<Vec<u64>>>>(
args.len,
args.density,
uniform,
"RankSmall4",
);
}
}
}