fn bounded_requirements_from_extrema<T>(
a_len: usize,
b_len: usize,
(a_min, a_max): (T, T),
(b_min, b_max): (T, T),
) -> Option<BoundedRequirements<T>>Examples found in repository?
crates/competitive/src/math/min_plus_convolution/mod.rs (line 209)
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}More examples
crates/competitive/src/math/min_plus_convolution/selector.rs (line 191)
161fn select_algorithm<T>(
162 a_len: usize,
163 b_len: usize,
164 a_characteristics: &InputCharacteristics<T>,
165 b_characteristics: &InputCharacteristics<T>,
166) -> Algorithm
167where
168 T: Signed + TryFrom<usize>,
169 T::Unsigned: TryInto<usize>,
170{
171 let output = a_len.saturating_add(b_len).saturating_sub(1) as u128;
172 let mut selected = ((a_len as u128) * (b_len as u128), Algorithm::Naive);
173 let mut consider = |work: u128, algorithm| {
174 if work < selected.0 {
175 selected = (work, algorithm);
176 }
177 };
178
179 consider(
180 // min_plus/sparse: 50% finite inputs are already over 10% faster than
181 // the INF-skipping naive scan, while dense pair enumeration loses.
182 scaled_work(
183 3,
184 (a_characteristics.finite_count as u128) * (b_characteristics.finite_count as u128),
185 ),
186 Algorithm::Sparse,
187 );
188 if let (Some(a_extrema), Some(b_extrema)) =
189 (a_characteristics.extrema, b_characteristics.extrema)
190 && let Some(requirements) =
191 bounded_requirements_from_extrema(a_len, b_len, a_extrema, b_extrema)
192 {
193 // min_plus/bounded: small transforms need a wider margin for encoding
194 // overhead; at 2^20 and above the NTT wins from a lower work ratio.
195 consider(
196 scaled_work(
197 if requirements.transform_len < 1 << 20 {
198 8
199 } else {
200 6
201 },
202 (requirements.transform_len as u128) * (requirements.transform_len.ilog2() as u128),
203 ),
204 Algorithm::BoundedNtt,
205 );
206 }
207
208 if a_characteristics.is_convex && b_characteristics.is_convex {
209 consider(output, Algorithm::ConvexMerge);
210 } else if a_characteristics.is_convex {
211 consider(
212 scaled_work(2, output),
213 Algorithm::ConvexDivideAndConquerLeft,
214 );
215 } else if b_characteristics.is_convex {
216 consider(
217 scaled_work(2, output),
218 Algorithm::ConvexDivideAndConquerRight,
219 );
220 }
221 if a_characteristics.is_concave && b_characteristics.is_concave {
222 consider(output, Algorithm::ConcaveBoth);
223 } else if a_characteristics.is_concave || b_characteristics.is_concave {
224 consider(
225 scaled_work(8, output.saturating_mul(output.max(1).ilog2() as u128 + 1)),
226 if a_characteristics.is_concave {
227 Algorithm::ConcaveEnvelopeLeft
228 } else {
229 Algorithm::ConcaveEnvelopeRight
230 },
231 );
232 }
233 let increasing = a_characteristics.is_nondecreasing && b_characteristics.is_nondecreasing;
234 let decreasing = a_characteristics.is_nonincreasing && b_characteristics.is_nonincreasing;
235 if increasing || decreasing {
236 consider(
237 scaled_work(
238 4,
239 a_characteristics
240 .run_count
241 .saturating_mul(b_characteristics.run_count) as u128,
242 ) + output,
243 if decreasing {
244 Algorithm::MonotoneRunsDecreasing
245 } else {
246 Algorithm::MonotoneRunsIncreasing
247 },
248 );
249 }
250 let piecewise = match (
251 a_characteristics.finite_count == a_len,
252 b_characteristics.finite_count == b_len,
253 ) {
254 (true, true) if a_characteristics.piece_count < b_characteristics.piece_count => {
255 Some((a_characteristics.piece_count, true))
256 }
257 (true, true) => Some((b_characteristics.piece_count, false)),
258 (true, false) => Some((a_characteristics.piece_count, true)),
259 (false, true) => Some((b_characteristics.piece_count, false)),
260 (false, false) => None,
261 };
262 if let Some((pieces, structured_is_left)) = piecewise {
263 if pieces == 1 {
264 consider(
265 output,
266 if structured_is_left {
267 Algorithm::LinearLeft
268 } else {
269 Algorithm::LinearRight
270 },
271 );
272 } else {
273 consider(
274 scaled_work(4, (pieces as u128).saturating_mul(output)),
275 if structured_is_left {
276 Algorithm::PiecewiseLinearLeft
277 } else {
278 Algorithm::PiecewiseLinearRight
279 },
280 );
281 }
282 }
283 selected.1
284}