use smallvec::SmallVec;
use super::{Cluster, LocalIdx};
pub struct Chunk {
pub nodes: SmallVec<[LocalIdx; 4]>,
pub fee: u64,
pub vsize: u64,
}
const BRUTE_FORCE_LIMIT: usize = 18;
const BITMASK_LIMIT: usize = 128;
pub fn linearize(cluster: &Cluster) -> Vec<Chunk> {
let n = cluster.nodes.len();
if n == 0 {
return Vec::new();
}
assert!(
n <= BITMASK_LIMIT,
"cluster size {} exceeds u128 capacity",
n
);
let mut parents_mask: Vec<u128> = vec![0; n];
let mut ancestor_incl: Vec<u128> = vec![0; n];
let mut order: Vec<LocalIdx> = (0..n as LocalIdx).collect();
order.sort_by_key(|&i| cluster.topo_rank[i as usize]);
for &v in &order {
let mut par = 0u128;
let mut acc = 1u128 << v;
for &p in &cluster.nodes[v as usize].parents {
par |= 1u128 << p;
acc |= ancestor_incl[p as usize];
}
parents_mask[v as usize] = par;
ancestor_incl[v as usize] = acc;
}
let fee_of: Vec<u64> = cluster.nodes.iter().map(|n| u64::from(n.fee)).collect();
let vsize_of: Vec<u64> = cluster.nodes.iter().map(|n| u64::from(n.vsize)).collect();
let all: u128 = if n == 128 { !0 } else { (1u128 << n) - 1 };
let mut chunks: Vec<Chunk> = Vec::new();
let mut remaining: u128 = all;
while remaining != 0 {
let (mask, fee, vsize) = if n <= BRUTE_FORCE_LIMIT {
best_subset(remaining, &order, &parents_mask, &fee_of, &vsize_of)
} else {
best_ancestor_union(remaining, &ancestor_incl, &fee_of, &vsize_of)
};
chunks.push(chunk_of(mask, fee, vsize));
remaining &= !mask;
}
canonicalize(chunks)
}
struct Ctx<'a> {
topo_order: &'a [LocalIdx],
parents_mask: &'a [u128],
fee_of: &'a [u64],
vsize_of: &'a [u64],
remaining: u128,
}
fn best_subset(
remaining: u128,
topo_order: &[LocalIdx],
parents_mask: &[u128],
fee_of: &[u64],
vsize_of: &[u64],
) -> (u128, u64, u64) {
let ctx = Ctx {
topo_order,
parents_mask,
fee_of,
vsize_of,
remaining,
};
let mut best = (0u128, 0u64, 1u64);
recurse(&ctx, 0, 0, 0, 0, &mut best);
best
}
fn recurse(ctx: &Ctx, idx: usize, included: u128, f: u64, v: u64, best: &mut (u128, u64, u64)) {
if idx == ctx.topo_order.len() {
if included != 0 && f as u128 * best.2 as u128 > best.1 as u128 * v as u128 {
*best = (included, f, v);
}
return;
}
let node = ctx.topo_order[idx];
let bit = 1u128 << node;
if (bit & ctx.remaining) == 0
|| (ctx.parents_mask[node as usize] & ctx.remaining & !included) != 0
{
recurse(ctx, idx + 1, included, f, v, best);
return;
}
recurse(ctx, idx + 1, included, f, v, best);
recurse(
ctx,
idx + 1,
included | bit,
f + ctx.fee_of[node as usize],
v + ctx.vsize_of[node as usize],
best,
);
}
fn best_ancestor_union(
remaining: u128,
ancestor_incl: &[u128],
fee_of: &[u64],
vsize_of: &[u64],
) -> (u128, u64, u64) {
let mut best = (0u128, 0u64, 1u64);
let mut seeds = remaining;
while seeds != 0 {
let i = seeds.trailing_zeros() as usize;
seeds &= seeds - 1;
let mut s = ancestor_incl[i] & remaining;
let (mut f, mut v) = totals(s, fee_of, vsize_of);
loop {
let mut picked: Option<(u128, u64, u64)> = None;
let mut cands = remaining & !s;
while cands != 0 {
let j = cands.trailing_zeros() as usize;
cands &= cands - 1;
let add = ancestor_incl[j] & remaining & !s;
if add == 0 {
continue;
}
let (df, dv) = totals(add, fee_of, vsize_of);
let nf = f + df;
let nv = v + dv;
if nf as u128 * v as u128 <= f as u128 * nv as u128 {
continue;
}
match picked {
None => picked = Some((add, nf, nv)),
Some((_, pf, pv)) => {
if nf as u128 * pv as u128 > pf as u128 * nv as u128 {
picked = Some((add, nf, nv));
}
}
}
}
match picked {
Some((add, nf, nv)) => {
s |= add;
f = nf;
v = nv;
}
None => break,
}
}
if f as u128 * best.2 as u128 > best.1 as u128 * v as u128 {
best = (s, f, v);
}
}
best
}
fn canonicalize(chunks: Vec<Chunk>) -> Vec<Chunk> {
let mut out: Vec<Chunk> = Vec::with_capacity(chunks.len());
for mut cur in chunks {
while let Some(top) = out.last() {
if cur.fee as u128 * top.vsize as u128 > top.fee as u128 * cur.vsize as u128 {
let mut prev = out.pop().unwrap();
prev.fee += cur.fee;
prev.vsize += cur.vsize;
prev.nodes.extend(cur.nodes);
cur = prev;
} else {
break;
}
}
out.push(cur);
}
out
}
#[inline]
fn totals(mask: u128, fee_of: &[u64], vsize_of: &[u64]) -> (u64, u64) {
let mut f = 0u64;
let mut v = 0u64;
let mut bits = mask;
while bits != 0 {
let i = bits.trailing_zeros() as usize;
f += fee_of[i];
v += vsize_of[i];
bits &= bits - 1;
}
(f, v)
}
fn chunk_of(mask: u128, fee: u64, vsize: u64) -> Chunk {
let mut nodes: SmallVec<[LocalIdx; 4]> = SmallVec::new();
let mut bits = mask;
while bits != 0 {
let i = bits.trailing_zeros();
nodes.push(i as LocalIdx);
bits &= bits - 1;
}
Chunk { nodes, fee, vsize }
}