Skip to main content

output_len

Function output_len 

Source
pub(super) fn output_len(a_len: usize, b_len: usize) -> usize
Examples found in repository?
crates/competitive/src/math/min_plus_convolution/convex.rs (line 40)
36pub fn min_plus_convolution_convex_merge<T>(a: &[T], b: &[T]) -> Vec<T>
37where
38    T: Signed,
39{
40    let len = output_len(a.len(), b.len());
41    if len == 0 {
42        return Vec::new();
43    }
44    assert_finite(a);
45    assert_finite(b);
46    assert!(is_convex(a) && is_convex(b), "both inputs must be convex");
47    convex_merge(a, b)
48}
49
50pub(super) fn convex_merge<T>(a: &[T], b: &[T]) -> Vec<T>
51where
52    T: Signed,
53{
54    let len = output_len(a.len(), b.len());
55    let mut a_slopes = a.windows(2).map(|window| window[1] - window[0]);
56    let mut b_slopes = b.windows(2).map(|window| window[1] - window[0]);
57    let mut next_a = a_slopes.next();
58    let mut next_b = b_slopes.next();
59    let mut current = a[0] + b[0];
60    let mut result = Vec::with_capacity(len);
61    result.push(current);
62    while next_a.is_some() || next_b.is_some() {
63        let slope = match (next_a, next_b) {
64            (Some(left), Some(right)) if left <= right => {
65                next_a = a_slopes.next();
66                left
67            }
68            (Some(_), Some(right)) => {
69                next_b = b_slopes.next();
70                right
71            }
72            (Some(left), None) => {
73                next_a = a_slopes.next();
74                left
75            }
76            (None, Some(right)) => {
77                next_b = b_slopes.next();
78                right
79            }
80            (None, None) => break,
81        };
82        current += slope;
83        result.push(current);
84    }
85    result
86}
87
88/// Computes convolution when one input is convex using monotone divide and conquer.
89///
90/// # Panics
91///
92/// Panics unless both inputs are finite and at least one is convex.
93pub fn min_plus_convolution_convex_divide_and_conquer<T>(a: &[T], b: &[T]) -> Vec<T>
94where
95    T: Signed,
96{
97    let len = output_len(a.len(), b.len());
98    if len == 0 {
99        return Vec::new();
100    }
101    assert_finite(a);
102    assert_finite(b);
103    let (arbitrary, convex) = orient_one_convex(a, b);
104    convex_divide_and_conquer(arbitrary, convex)
105}
106
107pub(super) fn convex_divide_and_conquer<T>(arbitrary: &[T], convex: &[T]) -> Vec<T>
108where
109    T: Signed,
110{
111    let len = output_len(arbitrary.len(), convex.len());
112    let mut result = vec![T::zero(); len];
113
114    fn solve<T>(
115        arbitrary: &[T],
116        convex: &[T],
117        result: &mut [T],
118        rows: Range<usize>,
119        options: RangeInclusive<usize>,
120    ) where
121        T: Signed,
122    {
123        if rows.is_empty() {
124            return;
125        }
126        let row = (rows.start + rows.end) / 2;
127        let first = (*options.start()).max(row.saturating_sub(convex.len() - 1));
128        let last = (*options.end()).min(row).min(arbitrary.len() - 1);
129        let mut best_col = first;
130        let mut best_value = arbitrary[first] + convex[row - first];
131        for col in first + 1..=last {
132            let value = arbitrary[col] + convex[row - col];
133            if value < best_value {
134                best_value = value;
135                best_col = col;
136            }
137        }
138        result[row] = best_value;
139        solve(
140            arbitrary,
141            convex,
142            result,
143            rows.start..row,
144            *options.start()..=best_col,
145        );
146        solve(
147            arbitrary,
148            convex,
149            result,
150            row + 1..rows.end,
151            best_col..=*options.end(),
152        );
153    }
154
155    solve(
156        arbitrary,
157        convex,
158        &mut result,
159        0..len,
160        0..=arbitrary.len() - 1,
161    );
162    result
163}
164
165#[derive(Clone, Copy, Eq, PartialEq)]
166enum MatrixValue<T> {
167    Finite(T),
168    Infinite,
169}
170
171impl<T> Ord for MatrixValue<T>
172where
173    T: Ord,
174{
175    fn cmp(&self, other: &Self) -> Ordering {
176        match (self, other) {
177            (Self::Finite(left), Self::Finite(right)) => left.cmp(right),
178            (Self::Finite(_), Self::Infinite) => Ordering::Less,
179            (Self::Infinite, Self::Finite(_)) => Ordering::Greater,
180            (Self::Infinite, Self::Infinite) => Ordering::Equal,
181        }
182    }
183}
184
185impl<T> PartialOrd for MatrixValue<T>
186where
187    T: Ord,
188{
189    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
190        Some(self.cmp(other))
191    }
192}
193
194fn smawk<T, F>(rows: usize, cols: usize, cost: &F) -> Vec<usize>
195where
196    T: Ord,
197    F: Fn(usize, usize) -> T,
198{
199    fn solve<T, F>(rows: &[usize], cols: &[usize], cost: &F, argmins: &mut [usize])
200    where
201        T: Ord,
202        F: Fn(usize, usize) -> T,
203    {
204        if rows.is_empty() {
205            return;
206        }
207        let mut reduced = Vec::with_capacity(rows.len().min(cols.len()));
208        for &col in cols {
209            while let Some(&previous) = reduced.last() {
210                let row = rows[reduced.len() - 1];
211                if cost(row, col) <= cost(row, previous) {
212                    reduced.pop();
213                } else {
214                    break;
215                }
216            }
217            if reduced.len() < rows.len() {
218                reduced.push(col);
219            }
220        }
221        let odd_rows: Vec<_> = rows.iter().copied().skip(1).step_by(2).collect();
222        solve(&odd_rows, &reduced, cost, argmins);
223        let mut lower = 0;
224        for row_position in (0..rows.len()).step_by(2) {
225            let upper = if row_position + 1 < rows.len() {
226                let target = argmins[rows[row_position + 1]];
227                lower
228                    + reduced[lower..]
229                        .iter()
230                        .position(|&col| col == target)
231                        .expect("SMAWK odd-row minimum must remain in the reduced columns")
232            } else {
233                reduced.len() - 1
234            };
235            let row = rows[row_position];
236            let mut best = lower;
237            for position in lower + 1..=upper {
238                if cost(row, reduced[position]) <= cost(row, reduced[best]) {
239                    best = position;
240                }
241            }
242            argmins[row] = reduced[best];
243            lower = upper;
244        }
245    }
246
247    let row_indices: Vec<_> = (0..rows).collect();
248    let col_indices: Vec<_> = (0..cols).collect();
249    let mut argmins = vec![0; rows];
250    solve(&row_indices, &col_indices, cost, &mut argmins);
251    argmins
252}
253
254/// Computes convolution when one input is convex using SMAWK in `O(n + m)`.
255///
256/// # Panics
257///
258/// Panics unless both inputs are finite and at least one is convex.
259pub fn min_plus_convolution_convex_smawk<T>(a: &[T], b: &[T]) -> Vec<T>
260where
261    T: Signed,
262{
263    let len = output_len(a.len(), b.len());
264    if len == 0 {
265        return Vec::new();
266    }
267    assert_finite(a);
268    assert_finite(b);
269    let (arbitrary, convex) = orient_one_convex(a, b);
270    convex_smawk(arbitrary, convex)
271}
272
273pub(super) fn convex_smawk<T>(arbitrary: &[T], convex: &[T]) -> Vec<T>
274where
275    T: Signed,
276{
277    let len = output_len(arbitrary.len(), convex.len());
278    let cost = |row: usize, col: usize| {
279        row.checked_sub(col)
280            .filter(|&index| index < convex.len())
281            .map_or(MatrixValue::Infinite, |index| {
282                MatrixValue::Finite(arbitrary[col] + convex[index])
283            })
284    };
285    let argmins = smawk(len, arbitrary.len(), &cost);
286    argmins
287        .into_iter()
288        .enumerate()
289        .map(|(row, col)| {
290            row.checked_sub(col)
291                .filter(|&index| index < convex.len())
292                .map(|index| arbitrary[col] + convex[index])
293                .expect("SMAWK minimum must be a valid convolution entry")
294        })
295        .collect()
296}
More examples
Hide additional examples
crates/competitive/src/math/min_plus_convolution/concave.rs (line 30)
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        // Curve i is valid on [i, i + concave.len()). The two passes insert
129        // opposite sides of each validity interval into the segment envelope.
130        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
152/// Computes convolution when one finite input is concave using offline envelopes.
153///
154/// The running time is `O((n + m) log(n + m))`. The arbitrary input may
155/// contain `T::maximum()`.
156///
157/// # Panics
158///
159/// Panics unless at least one input is finite and concave, or if the
160/// interval-tree size cannot be represented.
161pub 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
208/// Computes convolution of two concave inputs from antidiagonal endpoints.
209///
210/// The running time is `O(n + m)`.
211///
212/// # Panics
213///
214/// Panics unless both inputs are finite and concave.
215pub 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}
crates/competitive/src/math/min_plus_convolution/piecewise_linear.rs (line 131)
127pub fn min_plus_convolution_linear<T>(a: &[T], b: &[T]) -> Vec<T>
128where
129    T: Signed + TryFrom<usize>,
130{
131    let len = output_len(a.len(), b.len());
132    if len == 0 {
133        return Vec::new();
134    }
135    let a_piece = finite_pieces(a).filter(|pieces| pieces.len() == 1);
136    let b_piece = finite_pieces(b).filter(|pieces| pieces.len() == 1);
137    let (arbitrary, piece) = if let Some(pieces) = b_piece {
138        (a, pieces[0])
139    } else if let Some(pieces) = a_piece {
140        (b, pieces[0])
141    } else {
142        panic!("at least one min-plus convolution input must be finite and linear")
143    };
144    convolve_pieces(arbitrary, std::iter::once(piece), len)
145}
146
147pub(super) fn linear<T>(arbitrary: &[T], structured: &[T]) -> Vec<T>
148where
149    T: Signed + TryFrom<usize>,
150{
151    let len = output_len(arbitrary.len(), structured.len());
152    convolve_pieces(arbitrary, pieces(structured), len)
153}
154
155/// Computes convolution using the input with fewer maximal linear pieces.
156///
157/// If that input has `p` pieces, the running time is `O(p * (n + m))`.
158///
159/// # Panics
160///
161/// Panics unless at least one input is finite, or an index cannot be
162/// represented by `T`.
163pub fn min_plus_convolution_piecewise_linear<T>(a: &[T], b: &[T]) -> Vec<T>
164where
165    T: Signed + TryFrom<usize>,
166{
167    let len = output_len(a.len(), b.len());
168    if len == 0 {
169        return Vec::new();
170    }
171    let a_pieces = finite_pieces(a);
172    let b_pieces = finite_pieces(b);
173    let (arbitrary, structured) = match (a_pieces, b_pieces) {
174        (Some(a_pieces), Some(b_pieces)) if a_pieces.len() < b_pieces.len() => (b, a_pieces),
175        (Some(_), Some(b_pieces)) => (a, b_pieces),
176        (Some(a_pieces), None) => (b, a_pieces),
177        (None, Some(b_pieces)) => (a, b_pieces),
178        (None, None) => {
179            panic!("at least one min-plus convolution input must be finite")
180        }
181    };
182    convolve_pieces(arbitrary, structured, len)
183}
184
185pub(super) fn piecewise_linear<T>(arbitrary: &[T], structured: &[T]) -> Vec<T>
186where
187    T: Signed + TryFrom<usize>,
188{
189    let len = output_len(arbitrary.len(), structured.len());
190    convolve_pieces(arbitrary, pieces(structured), len)
191}
crates/competitive/src/math/min_plus_convolution/monotone.rs (line 28)
24pub fn min_plus_convolution_monotone_runs<T>(a: &[T], b: &[T]) -> Vec<T>
25where
26    T: Signed,
27{
28    let len = output_len(a.len(), b.len());
29    if len == 0 {
30        return Vec::new();
31    }
32    assert_finite(a);
33    assert_finite(b);
34    let increasing = if a.windows(2).all(|window| window[0] >= window[1])
35        && b.windows(2).all(|window| window[0] >= window[1])
36    {
37        false
38    } else if a.windows(2).all(|window| window[0] <= window[1])
39        && b.windows(2).all(|window| window[0] <= window[1])
40    {
41        true
42    } else {
43        panic!("both inputs must be monotone in the same direction")
44    };
45    monotone_runs(a, b, increasing)
46}
47
48pub(super) fn monotone_runs<T>(a: &[T], b: &[T], increasing: bool) -> Vec<T>
49where
50    T: Signed,
51{
52    monotone_runs_from_entries(
53        &run_entries(a),
54        &run_entries(b),
55        a.len(),
56        b.len(),
57        increasing,
58    )
59}
60
61pub(super) fn monotone_runs_from_entries<T>(
62    a_runs: &[(usize, T)],
63    b_runs: &[(usize, T)],
64    a_len: usize,
65    b_len: usize,
66    increasing: bool,
67) -> Vec<T>
68where
69    T: Signed,
70{
71    let normalize = |runs: &[(usize, T)], len: usize| {
72        if increasing {
73            runs.iter()
74                .enumerate()
75                .rev()
76                .map(|(index, &(_, value))| {
77                    (
78                        len - runs.get(index + 1).map_or(len, |&(start, _)| start),
79                        value,
80                    )
81                })
82                .collect()
83        } else {
84            runs.to_vec()
85        }
86    };
87    let a_runs = normalize(a_runs, a_len);
88    let b_runs = normalize(b_runs, b_len);
89    let len = output_len(a_len, b_len);
90    let mut result = vec![T::maximum(); len];
91    for &(left_start, left_value) in &a_runs {
92        for &(right_start, right_value) in &b_runs {
93            let output = left_start + right_start;
94            result[output] = result[output].min(left_value + right_value);
95        }
96    }
97    for output in 1..len {
98        result[output] = result[output].min(result[output - 1]);
99    }
100    if increasing {
101        result.reverse();
102    }
103    result
104}
crates/competitive/src/math/min_plus_convolution/mod.rs (line 60)
56pub fn min_plus_convolution_naive<T>(a: &[T], b: &[T]) -> Vec<T>
57where
58    T: Signed,
59{
60    let len = output_len(a.len(), b.len());
61    if len == 0 {
62        return Vec::new();
63    }
64    let (outer, inner) = if a.len() <= b.len() { (a, b) } else { (b, a) };
65    let inner_is_finite = !inner.iter().any(T::is_maximum);
66    let mut result = vec![T::maximum(); len];
67    for (index, &left) in outer.iter().enumerate() {
68        if left.is_maximum() {
69            continue;
70        }
71        let output = &mut result[index..index + inner.len()];
72        if inner_is_finite {
73            for (slot, &right) in output.iter_mut().zip(inner) {
74                *slot = (*slot).min(left + right);
75            }
76        } else {
77            for (slot, &right) in output.iter_mut().zip(inner) {
78                if !right.is_maximum() {
79                    *slot = (*slot).min(left + right);
80                }
81            }
82        }
83    }
84    result
85}
86
87/// Computes min-plus convolution by enumerating finite input pairs only.
88///
89/// If the inputs have `s_a` and `s_b` finite values, the running time is
90/// `O(s_a * s_b + n + m)`.
91///
92/// # Panics
93///
94/// Panics if the output length does not fit [`usize`]. Arithmetic overflow is
95/// the caller's responsibility.
96pub fn min_plus_convolution_sparse<T>(a: &[T], b: &[T]) -> Vec<T>
97where
98    T: Signed,
99{
100    let len = output_len(a.len(), b.len());
101    if len == 0 {
102        return Vec::new();
103    }
104    let a: Vec<_> = a
105        .iter()
106        .copied()
107        .enumerate()
108        .filter(|(_, value)| !value.is_maximum())
109        .collect();
110    let b: Vec<_> = b
111        .iter()
112        .copied()
113        .enumerate()
114        .filter(|(_, value)| !value.is_maximum())
115        .collect();
116    sparse(a.iter().copied(), b.iter().copied(), len)
117}
118
119fn sparse<T>(
120    a: impl IntoIterator<Item = (usize, T)>,
121    b: impl IntoIterator<Item = (usize, T)> + Clone,
122    len: usize,
123) -> Vec<T>
124where
125    T: Signed,
126{
127    let mut result = vec![T::maximum(); len];
128    for (i, left) in a {
129        for (j, right) in b.clone() {
130            result[i + j] = result[i + j].min(left + right);
131        }
132    }
133    result
134}
135
136const MAX_NTT_SIZE: usize = 1 << 23;
137
138#[derive(Clone, Copy, Debug)]
139struct BoundedRequirements<T> {
140    a_min: T,
141    b_min: T,
142    base: usize,
143    transform_len: usize,
144}
145
146fn finite_extrema<T>(values: &[T]) -> Option<(T, T)>
147where
148    T: Signed,
149{
150    let mut finite = values.iter().copied().filter(|value| !value.is_maximum());
151    let first = finite.next()?;
152    Some(finite.fold((first, first), |(minimum, maximum), value| {
153        (minimum.min(value), maximum.max(value))
154    }))
155}
156
157fn bounded_transform_len(a_len: usize, b_len: usize, base: usize) -> Option<usize> {
158    let left_len = a_len.checked_mul(base)?;
159    let right_len = b_len.checked_mul(base)?;
160    let coefficient_len = left_len
161        .checked_add(right_len)
162        .and_then(|len| len.checked_sub(1))?;
163    let transform_len = coefficient_len.checked_next_power_of_two()?;
164    (transform_len <= MAX_NTT_SIZE).then_some(transform_len)
165}
166
167fn bounded_requirements_from_extrema<T>(
168    a_len: usize,
169    b_len: usize,
170    (a_min, a_max): (T, T),
171    (b_min, b_max): (T, T),
172) -> Option<BoundedRequirements<T>>
173where
174    T: Signed,
175    T::Unsigned: TryInto<usize>,
176{
177    let a_span = a_max.abs_diff(a_min).try_into().ok()?;
178    let b_span = b_max.abs_diff(b_min).try_into().ok()?;
179    let base = a_span
180        .checked_add(b_span)
181        .and_then(|span| span.checked_add(1))?;
182    let transform_len = bounded_transform_len(a_len, b_len, base)?;
183    Some(BoundedRequirements {
184        a_min,
185        b_min,
186        base,
187        transform_len,
188    })
189}
190
191/// Computes exact min-plus convolution for small integer value spans using NTT.
192///
193/// # Panics
194///
195/// Panics if the encoded range cannot be represented or requires a transform
196/// longer than `2^23`. Arithmetic overflow is the caller's responsibility.
197pub fn min_plus_convolution_bounded_ntt<T>(a: &[T], b: &[T]) -> Vec<T>
198where
199    T: Signed + TryFrom<usize>,
200    T::Unsigned: TryInto<usize>,
201{
202    let output_len = output_len(a.len(), b.len());
203    if output_len == 0 {
204        return Vec::new();
205    }
206    let (Some(a_extrema), Some(b_extrema)) = (finite_extrema(a), finite_extrema(b)) else {
207        return vec![T::maximum(); output_len];
208    };
209    let requirements = bounded_requirements_from_extrema(a.len(), b.len(), a_extrema, b_extrema)
210        .expect("bounded min-plus convolution encoding must fit the 2^23 NTT limit");
211    let mut left = vec![MInt998244353::from(0_u32); a.len() * requirements.base];
212    let mut right = vec![MInt998244353::from(0_u32); b.len() * requirements.base];
213    for (index, &value) in a.iter().enumerate() {
214        if !value.is_maximum() {
215            let normalized: usize = value
216                .abs_diff(requirements.a_min)
217                .try_into()
218                .ok()
219                .expect("bounded min-plus convolution value span must fit usize");
220            left[index * requirements.base + normalized] = MInt998244353::from(1_u32);
221        }
222    }
223    for (index, &value) in b.iter().enumerate() {
224        if !value.is_maximum() {
225            let normalized: usize = value
226                .abs_diff(requirements.b_min)
227                .try_into()
228                .ok()
229                .expect("bounded min-plus convolution value span must fit usize");
230            right[index * requirements.base + normalized] = MInt998244353::from(1_u32);
231        }
232    }
233    let coefficients = Convolve998244353::convolve(left, right);
234    let encoded_len = output_len
235        .checked_mul(requirements.base)
236        .expect("bounded min-plus convolution encoded length must fit usize");
237    let mut result = Vec::with_capacity(output_len);
238    for chunk in coefficients[..encoded_len].chunks_exact(requirements.base) {
239        let value = if let Some(normalized) = chunk.iter().position(|&value| u32::from(value) != 0)
240        {
241            requirements.a_min
242                + requirements.b_min
243                + T::try_from(normalized)
244                    .ok()
245                    .expect("bounded min-plus convolution value must fit the output type")
246        } else {
247            T::maximum()
248        };
249        result.push(value);
250    }
251    result
252}
crates/competitive/src/math/min_plus_convolution/near_convex.rs (line 75)
65pub fn min_plus_convolution_near_convex_scan<T>(
66    a: &[T],
67    b: &[T],
68    convex_a: &[T],
69    convex_b: &[T],
70    delta: T,
71) -> Vec<T>
72where
73    T: Signed,
74{
75    let len = output_len(a.len(), b.len());
76    if len == 0 {
77        return Vec::new();
78    }
79    validate_witness(a, convex_a, delta);
80    validate_witness(b, convex_b, delta);
81    let (convex_output, witnesses) = convex_convolution_witnesses(convex_a, convex_b);
82    let tolerance = delta + delta;
83    let relevant = |output: usize, i: usize| {
84        convex_a[i] + convex_b[output - i] <= convex_output[output] + tolerance
85    };
86    let mut result = Vec::with_capacity(len);
87    for output in 0..len {
88        let first = output.saturating_sub(b.len() - 1);
89        let last = output.min(a.len() - 1);
90        let witness = witnesses[output];
91        let mut low = first;
92        let mut high = witness;
93        while low < high {
94            let middle = low + (high - low) / 2;
95            if relevant(output, middle) {
96                high = middle;
97            } else {
98                low = middle + 1;
99            }
100        }
101        let first_relevant = low;
102        low = witness;
103        high = last;
104        while low < high {
105            let middle = low + (high - low).div_ceil(2);
106            if relevant(output, middle) {
107                low = middle;
108            } else {
109                high = middle - 1;
110            }
111        }
112        let mut best = T::maximum();
113        for i in first_relevant..=low {
114            best = best.min(a[i] + b[output - i]);
115        }
116        result.push(best);
117    }
118    result
119}