competitive/math/min_plus_convolution/
concave.rs1use super::{Signed, assert_finite, output_len};
2
3pub(super) fn is_concave<T>(values: &[T]) -> bool
4where
5 T: Signed,
6{
7 values
8 .windows(3)
9 .all(|window| window[1] - window[0] >= window[2] - window[1])
10}
11
12fn bit_width(value: usize) -> u32 {
13 value.checked_ilog2().map_or(0, |log| log + 1)
14}
15
16struct ConcaveEnvelope<'a, T> {
17 arbitrary: &'a [T],
18 concave: &'a [T],
19 leaf_count: usize,
20 query_root: usize,
21 node_curves: Vec<Option<usize>>,
22 result: Vec<T>,
23}
24
25impl<'a, T> ConcaveEnvelope<'a, T>
26where
27 T: Signed,
28{
29 fn new(arbitrary: &'a [T], concave: &'a [T]) -> Self {
30 let output_len = output_len(arbitrary.len(), concave.len());
31 let leaf_count = 1_usize
32 .checked_shl(bit_width(output_len))
33 .expect("min-plus convolution envelope size must fit usize");
34 ConcaveEnvelope {
35 arbitrary,
36 concave,
37 leaf_count,
38 query_root: leaf_count >> bit_width(concave.len() - 1),
39 node_curves: vec![None; leaf_count],
40 result: vec![T::maximum(); output_len],
41 }
42 }
43
44 #[inline]
45 fn value(&self, curve: usize, output: usize) -> T {
46 self.arbitrary[curve] + self.concave[output - curve]
47 }
48
49 #[inline]
50 fn query(&mut self, output: usize) {
51 let mut best = self.result[output];
52 let mut node = (output + self.leaf_count) >> 1;
53 while node >= self.query_root {
54 if let Some(curve) = self.node_curves[node] {
55 best = best.min(self.value(curve, output));
56 }
57 node >>= 1;
58 }
59 self.result[output] = best;
60 }
61
62 #[inline]
63 fn insert_from_left(&mut self, left: usize) {
64 let mut right = left + self.concave.len();
65 let block = 1_usize << (left ^ right).ilog2();
66 right &= !(block - 1);
67 let mut depth = bit_width(right - left - 1);
68 let mut node = (self.leaf_count + left) >> depth;
69 let mut pending = (!self.arbitrary[left].is_maximum()).then_some(left);
70 while depth != 0 {
71 let Some(curve) = pending else {
72 break;
73 };
74 depth -= 1;
75 let middle = ((node << 1 | 1) << depth) - self.leaf_count - 1;
76 if middle < left {
77 node = node << 1 | 1;
78 } else if self.node_curves[node]
79 .is_some_and(|old| self.value(old, middle) < self.value(curve, middle))
80 {
81 node <<= 1;
82 } else {
83 std::mem::swap(&mut self.node_curves[node], &mut pending);
84 node = node << 1 | 1;
85 }
86 }
87 if let Some(curve) = pending {
88 let output = node - self.leaf_count;
89 self.result[output] = self.result[output].min(self.value(curve, output));
90 }
91 }
92
93 #[inline]
94 fn insert_from_right(&mut self, right: usize) {
95 let curve = right - self.concave.len();
96 let block = 1_usize << (curve ^ right).ilog2();
97 let left = right & !(block - 1);
98 if left == right {
99 return;
100 }
101 let mut depth = bit_width(right - left - 1);
102 let mut node = (self.leaf_count + left) >> depth;
103 let mut pending = (!self.arbitrary[curve].is_maximum()).then_some(curve);
104 while depth != 0 {
105 let Some(curve) = pending else {
106 break;
107 };
108 depth -= 1;
109 let middle = ((node << 1 | 1) << depth) - self.leaf_count;
110 if middle >= right {
111 node <<= 1;
112 } else if self.node_curves[node]
113 .is_some_and(|old| self.value(old, middle) < self.value(curve, middle))
114 {
115 node = node << 1 | 1;
116 } else {
117 std::mem::swap(&mut self.node_curves[node], &mut pending);
118 node <<= 1;
119 }
120 }
121 if let Some(curve) = pending {
122 let output = node - self.leaf_count;
123 self.result[output] = self.result[output].min(self.value(curve, output));
124 }
125 }
126
127 fn convolve(mut self) -> Vec<T> {
128 for left in 0..self.arbitrary.len() {
131 self.insert_from_left(left);
132 self.query(left);
133 }
134 for output in self.arbitrary.len()..self.result.len() {
135 self.query(output);
136 }
137
138 self.node_curves.fill(None);
139 let mut right = self.result.len();
140 while right >= self.concave.len() {
141 self.insert_from_right(right);
142 right -= 1;
143 self.query(right);
144 }
145 for output in 0..self.concave.len() {
146 self.query(output);
147 }
148 self.result
149 }
150}
151
152pub fn min_plus_convolution_concave_envelope<T>(a: &[T], b: &[T]) -> Vec<T>
162where
163 T: Signed,
164{
165 let len = output_len(a.len(), b.len());
166 if len == 0 {
167 return Vec::new();
168 }
169 let a_is_concave = !a.iter().any(T::is_maximum) && is_concave(a);
170 let b_is_concave = !b.iter().any(T::is_maximum) && is_concave(b);
171 let (arbitrary, concave) = if b_is_concave {
172 (a, b)
173 } else if a_is_concave {
174 (b, a)
175 } else {
176 panic!("at least one min-plus convolution input must be finite and concave")
177 };
178 concave_envelope(arbitrary, concave)
179}
180
181pub(super) fn concave_envelope<T>(arbitrary: &[T], concave: &[T]) -> Vec<T>
182where
183 T: Signed,
184{
185 if concave.len() == 1 {
186 return arbitrary
187 .iter()
188 .map(|&value| {
189 if value.is_maximum() {
190 T::maximum()
191 } else {
192 value + concave[0]
193 }
194 })
195 .collect();
196 }
197 if arbitrary.len() == 1 {
198 return if arbitrary[0].is_maximum() {
199 vec![T::maximum(); concave.len()]
200 } else {
201 concave.iter().map(|&value| arbitrary[0] + value).collect()
202 };
203 }
204
205 ConcaveEnvelope::new(arbitrary, concave).convolve()
206}
207
208pub fn min_plus_convolution_concave_both<T>(a: &[T], b: &[T]) -> Vec<T>
216where
217 T: Signed,
218{
219 let len = output_len(a.len(), b.len());
220 if len == 0 {
221 return Vec::new();
222 }
223 assert_finite(a);
224 assert_finite(b);
225 assert!(
226 is_concave(a) && is_concave(b),
227 "both inputs must be concave"
228 );
229 concave_both(a, b)
230}
231
232pub(super) fn concave_both<T>(a: &[T], b: &[T]) -> Vec<T>
233where
234 T: Signed,
235{
236 let len = output_len(a.len(), b.len());
237 let mut result = Vec::with_capacity(len);
238 for output in 0..len {
239 let first = output.saturating_sub(b.len() - 1);
240 let last = output.min(a.len() - 1);
241 result.push((a[first] + b[output - first]).min(a[last] + b[output - last]));
242 }
243 result
244}
245
246#[cfg(test)]
247mod tests {
248 use super::*;
249 use crate::{math::min_plus_convolution::min_plus_convolution_naive, tools::Xorshift};
250
251 #[test]
252 fn test_concave_algorithms_randomly() {
253 let mut rng = Xorshift::default();
254 for _ in 0..1_000 {
255 let arbitrary_len = rng.random(0..=32);
256 let concave_len: usize = rng.random(0..=32);
257 let arbitrary: Vec<_> = (0..arbitrary_len)
258 .map(|_| {
259 if rng.random(0_u64..8) == 0 {
260 i64::MAX
261 } else {
262 rng.random(-50_i64..=50)
263 }
264 })
265 .collect();
266 let mut slopes: Vec<_> = rng
267 .random_iter(-20_i64..=20)
268 .take(concave_len.saturating_sub(1))
269 .collect();
270 slopes.sort_unstable_by(|a, b| b.cmp(a));
271 let mut concave = Vec::with_capacity(concave_len);
272 if concave_len != 0 {
273 concave.push(rng.random(-50_i64..=50));
274 }
275 for slope in slopes {
276 concave.push(concave[concave.len() - 1] + slope);
277 }
278 let other_len: usize = rng.random(0..=32);
279 let mut slopes: Vec<_> = rng
280 .random_iter(-20_i64..=20)
281 .take(other_len.saturating_sub(1))
282 .collect();
283 slopes.sort_unstable_by(|a, b| b.cmp(a));
284 let mut other = Vec::with_capacity(other_len);
285 if other_len != 0 {
286 other.push(rng.random(-50_i64..=50));
287 }
288 for slope in slopes {
289 other.push(other[other.len() - 1] + slope);
290 }
291 assert_eq!(
292 min_plus_convolution_concave_envelope(&arbitrary, &concave),
293 min_plus_convolution_naive(&arbitrary, &concave)
294 );
295 assert_eq!(
296 min_plus_convolution_concave_both(&concave, &other),
297 min_plus_convolution_naive(&concave, &other)
298 );
299 }
300 }
301
302 #[test]
303 fn test_concave_envelope_exhaustively() {
304 let values = [-2_i64, 0, 3, i64::MAX];
305 let mut inputs = vec![Vec::new()];
306 for _ in 0..3 {
307 let prefixes = inputs.clone();
308 for prefix in prefixes {
309 for &value in &values {
310 let mut input = prefix.clone();
311 input.push(value);
312 inputs.push(input);
313 }
314 }
315 }
316 inputs.sort();
317 inputs.dedup();
318 let concave: Vec<_> = inputs
319 .iter()
320 .filter(|input| !input.contains(&i64::MAX) && is_concave(input))
321 .collect();
322 for arbitrary in &inputs {
323 for &structured in &concave {
324 assert_eq!(
325 min_plus_convolution_concave_envelope(arbitrary, structured),
326 min_plus_convolution_naive(arbitrary, structured)
327 );
328 }
329 }
330 }
331}