Skip to main content

bounded_requirements_from_extrema

Function bounded_requirements_from_extrema 

Source
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>>
where T: Signed, T::Unsigned: TryInto<usize>,
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
Hide additional 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}