pub fn pack_distribution(total_revisions: u64) -> Vec<u64> {
if total_revisions == 0 {
return vec![0];
}
let digits = total_revisions.to_string();
let mut buckets = Vec::new();
for (i, ch) in digits.bytes().rev().enumerate() {
let count = (ch - b'0') as usize;
let value = 10u64.pow(i as u32);
for _ in 0..count {
buckets.push(value);
}
}
buckets.reverse();
buckets
}
pub fn plan_autopack_combinations(pack_revision_counts: &[u64]) -> Vec<usize> {
let distribution = {
let total: u64 = pack_revision_counts.iter().sum();
pack_distribution(total)
};
if pack_revision_counts.len() <= distribution.len() {
return Vec::new();
}
let mut order: Vec<usize> = (0..pack_revision_counts.len()).collect();
order.sort_by(|&a, &b| pack_revision_counts[b].cmp(&pack_revision_counts[a]));
let mut dist: std::collections::VecDeque<i64> =
distribution.iter().map(|&v| v as i64).collect();
let mut selected = Vec::new();
let mut pending_op_revs: i64 = 0;
for &pack in &order {
let mut rev_count = pack_revision_counts[pack] as i64;
if dist.front().is_some_and(|&head| rev_count >= head) {
while rev_count > 0 {
let head = *dist.front().expect("distribution exhausted");
rev_count -= head;
if rev_count >= 0 {
dist.pop_front();
} else {
*dist.front_mut().unwrap() = -rev_count;
}
}
} else {
pending_op_revs += rev_count;
selected.push(pack);
if dist.front().is_some_and(|&head| pending_op_revs >= head) {
dist.pop_front();
pending_op_revs = 0;
}
}
}
if selected.len() < 2 {
return Vec::new();
}
selected
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pack_distribution_powers_of_ten() {
assert_eq!(pack_distribution(0), vec![0]);
assert_eq!(pack_distribution(1), vec![1]);
assert_eq!(pack_distribution(9), vec![1, 1, 1, 1, 1, 1, 1, 1, 1]);
assert_eq!(pack_distribution(10), vec![10]);
assert_eq!(
pack_distribution(1234),
vec![1000, 100, 100, 10, 10, 10, 1, 1, 1, 1]
);
}
#[test]
fn plan_does_nothing_when_within_distribution() {
assert!(plan_autopack_combinations(&[1000]).is_empty());
assert!(plan_autopack_combinations(&[10, 1]).is_empty());
}
#[test]
fn plan_combines_many_small_packs() {
assert!(plan_autopack_combinations(&[1, 1, 1, 1, 1]).is_empty());
assert!(plan_autopack_combinations(&[1, 1, 1, 1, 1, 1]).is_empty());
let plan = plan_autopack_combinations(&[1; 11]);
assert_eq!(plan.len(), 10);
}
#[test]
fn plan_leaves_a_large_pack_alone() {
assert!(plan_autopack_combinations(&[1000, 1, 1, 1]).is_empty());
assert!(plan_autopack_combinations(&[1000, 1, 1, 1, 1]).is_empty());
}
#[test]
fn plan_gathers_small_packs_around_a_medium_one() {
assert_eq!(
plan_autopack_combinations(&[5, 1, 1, 1, 1, 1, 1]),
vec![0, 1, 2, 3, 4, 5]
);
}
#[test]
fn plan_partially_consumes_a_bucket_for_an_oversized_pack() {
let mut counts = vec![15];
counts.extend(std::iter::repeat_n(1, 11));
assert_eq!(plan_autopack_combinations(&counts), vec![1, 2, 3, 4, 5]);
}
#[test]
fn plan_combines_exactly_two_packs() {
assert_eq!(plan_autopack_combinations(&[5, 5, 1, 1]), vec![0, 1]);
}
#[test]
fn plan_suppresses_single_pack_combine() {
let mut counts = vec![1000];
counts.extend(std::iter::repeat_n(1, 11));
assert_eq!(
plan_autopack_combinations(&counts),
vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
);
}
}