1use alloc::vec::Vec;
15
16use crate::bounds::{Bounds, union_all};
17
18pub trait SplitParameters {
26 const MAX: usize;
28 const MIN: usize;
30
31 fn split(entries: &[Bounds]) -> (Vec<usize>, Vec<usize>);
34}
35
36#[derive(Debug, Clone, Copy, Default)]
43pub struct Quadratic<const MAX: usize = 8, const MIN: usize = 3>;
44
45impl<const MAX: usize, const MIN: usize> SplitParameters for Quadratic<MAX, MIN> {
46 const MAX: usize = MAX;
47 const MIN: usize = MIN;
48
49 fn split(entries: &[Bounds]) -> (Vec<usize>, Vec<usize>) {
50 let n = entries.len();
51 let (s1, s2) = quadratic_seeds(entries);
52
53 let mut g1 = Vec::from([s1]);
54 let mut g2 = Vec::from([s2]);
55 let mut b1 = entries[s1];
56 let mut b2 = entries[s2];
57
58 let mut remaining: Vec<usize> = (0..n).filter(|&i| i != s1 && i != s2).collect();
59
60 while let Some(idx) = remaining.pop() {
61 if g1.len() + remaining.len() + 1 == MIN {
64 g1.push(idx);
65 b1 = b1.union(&entries[idx]);
66 continue;
67 }
68 if g2.len() + remaining.len() + 1 == MIN {
69 g2.push(idx);
70 b2 = b2.union(&entries[idx]);
71 continue;
72 }
73 let e1 = b1.enlargement(&entries[idx]);
75 let e2 = b2.enlargement(&entries[idx]);
76 if e1 <= e2 {
77 g1.push(idx);
78 b1 = b1.union(&entries[idx]);
79 } else {
80 g2.push(idx);
81 b2 = b2.union(&entries[idx]);
82 }
83 }
84
85 (g1, g2)
86 }
87}
88
89#[derive(Debug, Clone, Copy, Default)]
96pub struct Linear<const MAX: usize = 8, const MIN: usize = 3>;
97
98impl<const MAX: usize, const MIN: usize> SplitParameters for Linear<MAX, MIN> {
99 const MAX: usize = MAX;
100 const MIN: usize = MIN;
101
102 fn split(entries: &[Bounds]) -> (Vec<usize>, Vec<usize>) {
103 let n = entries.len();
104 let (s1, s2) = linear_seeds(entries);
105
106 let mut g1 = Vec::from([s1]);
107 let mut g2 = Vec::from([s2]);
108 let mut b1 = entries[s1];
109 let mut b2 = entries[s2];
110
111 let mut remaining: Vec<usize> = (0..n).filter(|&i| i != s1 && i != s2).collect();
112 while let Some(idx) = remaining.pop() {
113 if g1.len() + remaining.len() + 1 == MIN {
114 g1.push(idx);
115 b1 = b1.union(&entries[idx]);
116 continue;
117 }
118 if g2.len() + remaining.len() + 1 == MIN {
119 g2.push(idx);
120 b2 = b2.union(&entries[idx]);
121 continue;
122 }
123 let e1 = b1.enlargement(&entries[idx]);
124 let e2 = b2.enlargement(&entries[idx]);
125 if e1 <= e2 {
126 g1.push(idx);
127 b1 = b1.union(&entries[idx]);
128 } else {
129 g2.push(idx);
130 b2 = b2.union(&entries[idx]);
131 }
132 }
133 (g1, g2)
134 }
135}
136
137fn quadratic_seeds(entries: &[Bounds]) -> (usize, usize) {
140 let mut worst = (0, 1, f64::NEG_INFINITY);
141 for i in 0..entries.len() {
142 for j in (i + 1)..entries.len() {
143 let combined = entries[i].union(&entries[j]).area();
144 let waste = combined - entries[i].area() - entries[j].area();
145 if waste > worst.2 {
146 worst = (i, j, waste);
147 }
148 }
149 }
150 (worst.0, worst.1)
151}
152
153fn linear_seeds(entries: &[Bounds]) -> (usize, usize) {
156 let all = union_all(entries);
157 let width = [all.max[0] - all.min[0], all.max[1] - all.min[1]];
158 let axis = usize::from(width[1] > width[0]);
159
160 let mut lo_idx = 0;
162 let mut hi_idx = 0;
163 let mut max_low = f64::NEG_INFINITY;
164 let mut min_high = f64::INFINITY;
165 for (i, b) in entries.iter().enumerate() {
166 if b.min[axis] > max_low {
167 max_low = b.min[axis];
168 hi_idx = i;
169 }
170 if b.max[axis] < min_high {
171 min_high = b.max[axis];
172 lo_idx = i;
173 }
174 }
175 if lo_idx == hi_idx {
176 (0, usize::from(entries.len() > 1))
178 } else {
179 (lo_idx, hi_idx)
180 }
181}
182
183#[cfg(test)]
184#[allow(
185 clippy::cast_precision_loss,
186 reason = "small test indices convert exactly to f64"
187)]
188mod tests {
189 use super::{Linear, Quadratic, SplitParameters};
190 use crate::bounds::Bounds;
191
192 fn line_of_boxes(n: usize) -> Vec<Bounds> {
193 (0..n)
194 .map(|i| Bounds::point([i as f64, 0.0]))
195 .collect::<Vec<_>>()
196 }
197
198 #[test]
199 fn quadratic_splits_into_two_min_sized_groups() {
200 let entries = line_of_boxes(9);
201 let (g1, g2) = <Quadratic<8, 3>>::split(&entries);
202 assert!(g1.len() >= 3 && g2.len() >= 3);
203 assert_eq!(g1.len() + g2.len(), 9);
204 }
205
206 #[test]
207 fn linear_splits_into_two_min_sized_groups() {
208 let entries = line_of_boxes(9);
209 let (g1, g2) = <Linear<8, 3>>::split(&entries);
210 assert!(g1.len() >= 3 && g2.len() >= 3);
211 assert_eq!(g1.len() + g2.len(), 9);
212 }
213
214 #[test]
215 fn every_index_assigned_exactly_once() {
216 let entries = line_of_boxes(9);
217 let (mut g1, g2) = <Quadratic<8, 3>>::split(&entries);
218 g1.extend(g2);
219 g1.sort_unstable();
220 assert_eq!(g1, (0..9).collect::<Vec<_>>());
221 }
222}