devboy_format_pipeline/trim/
mod.rs1pub mod greedy;
10pub mod head_tail;
11pub mod knapsack;
12pub mod wfq;
13
14use crate::tree::TrimNode;
15
16pub fn trim(tree: &mut TrimNode, budget: usize) {
21 if tree.total_weight() <= budget {
23 return;
24 }
25
26 let count = tree.count_nodes();
27 match count {
28 0..=99 => knapsack::solve(tree, budget),
29 100..=999 => greedy::solve(tree, budget),
30 1000..=9999 => wfq::solve(tree, budget),
31 _ => head_tail::solve(tree, budget),
32 }
33}
34
35#[cfg(test)]
36mod tests {
37 use super::*;
38 use crate::tree::NodeKind;
39
40 fn make_tree(item_weights: &[usize], values: &[f64]) -> TrimNode {
41 let mut root = TrimNode::new(0, NodeKind::Root, 0);
42 for (i, (&w, &v)) in item_weights.iter().zip(values.iter()).enumerate() {
43 let mut node = TrimNode::new(i + 1, NodeKind::Item { index: i }, w);
44 node.value = v;
45 root.children.push(node);
46 }
47 root
48 }
49
50 #[test]
51 fn test_trim_no_op_when_fits() {
52 let mut tree = make_tree(&[10, 20, 30], &[1.0, 1.0, 1.0]);
53 trim(&mut tree, 100);
54 assert_eq!(tree.included_items_count(), 3);
55 }
56
57 #[test]
58 fn test_trim_reduces_weight() {
59 let mut tree = make_tree(&[100, 200, 300], &[1.0, 0.5, 0.2]);
60 let budget = 250;
61 trim(&mut tree, budget);
62 assert!(tree.total_weight() <= budget);
63 assert!(tree.included_items_count() >= 1);
65 }
66
67 #[test]
68 fn test_trim_prefers_high_value() {
69 let mut tree = make_tree(&[100, 100, 100], &[0.1, 0.9, 0.5]);
70 trim(&mut tree, 150);
71 let included = tree.included_item_indices();
73 assert!(included.contains(&1), "High-value item should be kept");
74 }
75
76 #[test]
77 fn test_trim_selects_correct_algorithm() {
78 let mut small = make_tree(&[10; 5], &[1.0; 5]);
80 trim(&mut small, 30);
81 assert!(small.total_weight() <= 30);
82
83 let weights: Vec<usize> = (0..120).map(|_| 10).collect();
85 let values: Vec<f64> = (0..120).map(|i| 1.0 - i as f64 * 0.005).collect();
86 let mut large = make_tree(&weights, &values);
87 trim(&mut large, 500);
88 assert!(large.total_weight() <= 500);
89 }
90
91 #[test]
92 fn test_trim_wfq_range() {
93 let weights: Vec<usize> = (0..1500).map(|_| 5).collect();
95 let values: Vec<f64> = (0..1500).map(|i| 1.0 - i as f64 * 0.0005).collect();
96 let mut tree = make_tree(&weights, &values);
97 let budget = 2000;
98 trim(&mut tree, budget);
99 assert!(tree.total_weight() <= budget);
100 assert!(tree.included_items_count() > 0);
101 }
102
103 #[test]
104 fn test_trim_head_tail_range() {
105 let weights: Vec<usize> = (0..10500).map(|_| 1).collect();
107 let values: Vec<f64> = (0..10500).map(|_| 1.0).collect();
108 let mut tree = make_tree(&weights, &values);
109 let budget = 500;
110 trim(&mut tree, budget);
111 assert!(tree.total_weight() <= budget);
112 }
113}