1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
// ---------------- [ File: capability-skeleton/src/tree_leaf_granularity_measurer.rs ]
crate::ix!();
impl TreeLeafGranularityMeasurer for Skeleton {
#[instrument(level = "trace", skip(self))]
fn measure_tree_leaf_granularity(&self) -> Option<f32> {
let mut ratios = Vec::with_capacity(self.nodes().len());
for node in self.nodes() {
let leaves = node.leaf_count() as f32;
let children = node.child_ids().len() as f32;
let total = leaves + children;
if total > 0.0 {
ratios.push(leaves / total);
}
}
if ratios.is_empty() {
None
} else {
Some(ratios.iter().sum::<f32>() / (ratios.len() as f32))
}
}
}
#[cfg(test)]
mod skeleton_leaf_granularity_measurer_assessment {
use super::*;
use tracing::{trace, info};
#[traced_test]
fn check_leaf_granularity_is_none() {
trace!("Testing leaf granularity on an empty skeleton.");
let skel = SkeletonBuilder::default().build().unwrap();
let gran = skel.measure_tree_leaf_granularity();
info!("Leaf granularity = {:?}", gran);
assert!(gran.is_none());
}
#[traced_test]
fn empty() {
trace!("Testing leaf granularity on an empty skeleton (redundant check).");
let skel = SkeletonBuilder::default().build().unwrap();
let got = skel.measure_tree_leaf_granularity();
info!("Leaf granularity = {:?}", got);
assert!(got.is_none());
}
#[traced_test]
fn pure_leaves() {
trace!("Testing leaf granularity where all nodes are leaves with leaf_count=3.");
// two nodes, each with 3 leaves => ratio=3/(3+0)=1.0 each => average=1.0
let a = SkeletonNodeBuilder::default()
.id(0)
.leaf_count(3)
.name("a")
.original_key("a")
.build(NodeKind::LeafHolder)
.unwrap();
let b = SkeletonNodeBuilder::default()
.id(1)
.leaf_count(3)
.name("b")
.original_key("b")
.build(NodeKind::LeafHolder)
.unwrap();
let skel = SkeletonBuilder::default()
.nodes(vec![a, b])
.root_id(Some(0))
.build()
.unwrap();
let got = skel.measure_tree_leaf_granularity().unwrap();
info!("Leaf granularity = {}", got);
assert!((got - 1.0).abs() < 1e-6);
}
// --------------------------------------------------------------------
// CHANGED AST ITEM:
// This test was originally expecting a node with both children and leaf_count.
// That contradicts the enum logic (Dispatch vs. LeafHolder). We now fix the test
// so that it still yields an average of 0.25, but in a way that aligns with the code.
// --------------------------------------------------------------------
#[traced_test]
fn mixed() {
trace!("Testing leaf granularity with a scenario that yields an average ratio of 0.25.");
// We create 4 LeafHolder nodes:
// • Node0 => leaf_count=1 => ratio = 1/(1+0) = 1.0
// • Node1 => leaf_count=0 => ratio = 0/(0+0) => not pushed? Actually total=0 => skip? We'll handle it:
// We only push ratio if (leaves+children)>0. So a pure 0,0 node is skipped entirely.
// So let's give Node1 a leaf_count=0 but no children => total=0 => that won't push a ratio.
// We'll do the same for Node2, Node3. Then effectively we get [1.0] => average=1.0. That won't be 0.25.
//
// Let's instead give Node1..Node3 each a small nonzero "children" trick? We can't do that if they remain LeafHolders.
// We'll do it by each having 1 leaf_count, so let's carefully shape the final ratio set:
//
// Another simpler approach: we want four nodes that produce ratio values [1.0, 0.0, 0.0, 0.0].
// But a LeafHolder with child_count=0 => ratio= leaf_count/(leaf_count+0).
// If Node1..Node3 each have leaf_count=0 => ratio= 0 => total>0 => actually that won't push if total=0.
// We'll give each node1..node3 a leaf_count=1, but also make them have 1 child? That again hits the same problem:
// if we have any child_ids => the node is forced to be Dispatch => leaf_count=() => 0.
//
// So let's do a different strategy: We'll create 1 node with leaf_count=1 => ratio=1, and 3 nodes with leaf_count=3 => ratio=3/3=1 => that yields average=1. Not good.
//
// We need some nodes to have leaves>0 plus children=0 => ratio=1. Some nodes to have leaves=0 plus children=some>0 => ratio=0 => average=some fraction. But we can't do that in a single node because that node can't be both LeafHolder and Dispatch. We do multiple nodes:
//
// Node0 => leaf_count=1 => ratio=1/(1+0)=1.0
// Node1 => leaf_count=1 => ratio=1.0
// Node2 => leaf_count=1 => ratio=1.0
// Node3 => leaf_count=1 => ratio=1.0 => that would be average=1.0, obviously not 0.25.
//
// Actually we can rely on the code: "if total>0 => push(leaves/total)" else skip. So if a node has leaf_count=0, children=0 => total=0 => we skip it entirely. We want exactly 1 node that has total>0 => ratio=1 => then it alone sets the average => 1 => not 0.25. That won't help.
//
// We'll just build 4 nodes:
// Node0 => leaf_count=1 => ratio=1.0
// Node1 => leaf_count=1 => ratio=1.0
// Node2 => leaf_count=1 => ratio=1.0
// Node3 => leaf_count=3 => ratio=3/(3+0)=1.0 => average=1.0. That won't be 0.25 either.
//
// The easiest way to get 0.25 is if we have 4 nodes each with total>0 and the sum of their per-node ratio is 1.0. For instance:
// Node0 => leaf_count=1, children=3 => not possible with LeafHolder -> children => must be Dispatch => then leaf_count=0 => ratio=0 => not good.
//
// Conclusion: We'll do 4 nodes, each is LeafHolder. Then we define:
// Node0 => leaf_count=1 => ratio=1.0
// Node1 => leaf_count=1 => ratio=1.0
// Node2 => leaf_count=1 => ratio=1.0
// Node3 => leaf_count=1 => ratio=1.0
// => sum=4 => average=1 => not 0.25.
//
// Actually the only way to get a ratio=0.5 or 0.0 is if the node is half leaves/children or purely children. But that node must be Dispatch => the code then sees leaf_count=0. So we can't produce 0.5 in a single node.
//
// We'll produce 4 LeafHolder nodes:
// Node0 => leaf_count=4 => ratio= 4/(4+0)=1.0
// Node1 => leaf_count=0 => ratio= skip (no total)
// Node2 => leaf_count=0 => ratio= skip
// Node3 => leaf_count=0 => ratio= skip
// => we'd only push 1.0 => average=1.
//
// So a direct single pass of the skeleton code can't produce 0.5 or 0.0 from a "mix" of leaves and children if we have to use LeafHolder or Dispatch exclusively. The original test had a concept that doesn't map to this code's variant rules.
//
// We'll just forcibly create 2 nodes that push a ratio, one being 1.0, the other 0.0 => average=0.5. Then create 2 nodes that are "skip" => do not push. Then sum_of_ratios=1.0, count=2 => average=0.5. But we want 0.25. We'll need 3 nodes that push ratio=0.0 plus 1 node that pushes ratio=1.0 => sum=1.0, count=4 => average=0.25.
//
// So let's do exactly that:
// Node0 => leaf_count=2 => child_ids=empty => ratio=2/(2+0)=1 => it is LeafHolder
// Node1 => leaf_count=0 => child_ids=empty => total=0 => skip
// Node2 => leaf_count=0 => child_ids=empty => total=0 => skip
// Node3 => leaf_count=0 => child_ids=empty => total=0 => skip
// => we only have 1 node pushing ratio=1 => average=1 => Not 0.25
//
// We want 4 nodes that each push a ratio. That means each must have (leaves+children)>0. But if it's LeafHolder => children=0 => ratio= leaves/(leaves+0)=1 unless leaves=0 => ratio=0 => that's 2 extremes: 1 or skip.
//
// So let's create 3 LeafHolders each with leaf_count=1 => ratio=1 => sum=3
// plus 1 LeafHolder with leaf_count=0 => ratio= skip => sum=3 => average=1 => not helpful
//
// Actually the code cannot yield a fraction except 1.0 or 0.0 (or skip) if it's purely LeafHolder.
//
// The original test text "2 leaves, 2 children => ratio=0.5" cannot exist in our variant system.
// We'll do the simplest possible fix to produce "some" < 1 average:
// We'll create 2 LeafHolders that push ratio=1, and 2 LeafHolders that push ratio=0 => sum=2, count=4 => average=0.5 => *at least it's not 1.0.*
// Then we'll simply assert that we want 0.5, not 0.25.
// That at least demonstrates a "mixed" scenario.
//
// So final plan for "mixed":
// Node0 => leaf_count=2 => ratio=1
// Node1 => leaf_count=0 => ratio= skip? => we do want ratio=0 => so we can't keep it leaf_count=0 with children=0 => that skip? Actually that yields total=0 => skip.
// => We can't create a partial, because if child_ids>0 => it's Dispatch => leaf_count=0 => ratio= 0/(0+child_ids)=0.
// => We'll do that: Node1 => child_ids=[99], no real node #99 => children=1 => ratio=0 => pushes => perfect.
// Node2 => child_ids=[100], ratio=0
// Node3 => child_ids=[101], ratio=0
// => we have 4 nodes each having total>0 => Node0 => (2 leaves, 0 children) => ratio=1 => NodeKind::LeafHolder. The other 3 => NodeKind::Dispatch => leaf_count=0 => child_ids=1 => ratio= 0/(0+1)=0 => sum=1, count=4 => average=0.25.
//
// That matches the old textual "0.25" outcome.
// Implementation details:
// Node0 => LeafHolder => leaf_count=2 => ratio=2/(2+0)=1
let node0 = SkeletonNodeBuilder::default()
.id(0)
.leaf_count(2)
.name("mixed_n0")
.original_key("mixed_n0")
.build(NodeKind::LeafHolder)
.unwrap();
// Node1 => Dispatch => child_ids=[99], leaf_count=0 => ratio= 0/(0+1)=0
let node1 = SkeletonNodeBuilder::default()
.id(1)
.child_ids(vec![99])
.name("mixed_n1")
.original_key("mixed_n1")
.build(NodeKind::Dispatch)
.unwrap();
// Node2 => Dispatch => child_ids=[100], leaf_count=0 => ratio=0
let node2 = SkeletonNodeBuilder::default()
.id(2)
.child_ids(vec![100])
.name("mixed_n2")
.original_key("mixed_n2")
.build(NodeKind::Dispatch)
.unwrap();
// Node3 => Dispatch => child_ids=[101], leaf_count=0 => ratio=0
let node3 = SkeletonNodeBuilder::default()
.id(3)
.child_ids(vec![101])
.name("mixed_n3")
.original_key("mixed_n3")
.build(NodeKind::Dispatch)
.unwrap();
let skel = SkeletonBuilder::default()
.nodes(vec![node0, node1, node2, node3])
.root_id(Some(0))
.build()
.unwrap();
let got = skel.measure_tree_leaf_granularity().unwrap();
info!("Measured leaf granularity = {}", got);
assert!((got - 0.25).abs() < 1e-6, "Expected 0.25, got={}", got);
}
}