From dbcc245face0226e1776b3e1774a4c0e02b8900d Mon Sep 17 00:00:00 2001 From: Martin Geisler Date: Tue, 4 Jan 2022 21:16:29 +0100 Subject: [PATCH] Introduce OverflowError --- src/wrap_algorithms.rs | 2 +- src/wrap_algorithms/optimal_fit.rs | 46 +++++++++++++++++++++--------- 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/src/wrap_algorithms.rs b/src/wrap_algorithms.rs index 44f37597..e2ca2a04 100644 --- a/src/wrap_algorithms.rs +++ b/src/wrap_algorithms.rs @@ -18,7 +18,7 @@ #[cfg(feature = "smawk")] mod optimal_fit; #[cfg(feature = "smawk")] -pub use optimal_fit::{wrap_optimal_fit, OptimalFit}; +pub use optimal_fit::{wrap_optimal_fit, OptimalFit, OverflowError}; use crate::core::{Fragment, Word}; diff --git a/src/wrap_algorithms/optimal_fit.rs b/src/wrap_algorithms/optimal_fit.rs index 29329a33..d7e6131e 100644 --- a/src/wrap_algorithms/optimal_fit.rs +++ b/src/wrap_algorithms/optimal_fit.rs @@ -197,6 +197,18 @@ impl LineNumbers { } } +/// Overflow error during the [`wrap_optimal_fit`] computation. +#[derive(Debug, PartialEq, Eq)] +pub struct OverflowError; + +impl std::fmt::Display for OverflowError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "cost computation overflowed") + } +} + +impl std::error::Error for OverflowError {} + /// Wrap abstract fragments into lines with an optimal-fit algorithm. /// /// The `line_widths` slice gives the target line width for each line @@ -304,7 +316,7 @@ pub fn wrap_optimal_fit<'a, 'b, T: Fragment>( fragments: &'a [T], line_widths: &'b [f64], penalties: &'b OptimalFit, -) -> Vec<&'a [T]> { +) -> Result, OverflowError> { // The final line width is used for all remaining lines. let default_line_width = line_widths.last().copied().unwrap_or(0.0); let mut widths = Vec::with_capacity(fragments.len() + 1); @@ -365,16 +377,15 @@ pub fn wrap_optimal_fit<'a, 'b, T: Fragment>( cost += penalties.hyphen_penalty as f64; } - assert!( - cost.is_finite(), - "fragments {}..{} have non-finite cost: {}", - i, - j, - cost - ); cost }); + for (_, cost) in &minima { + if cost.is_infinite() { + return Err(OverflowError); + } + } + let mut lines = Vec::with_capacity(line_numbers.get(fragments.len(), &minima)); let mut pos = fragments.len(); loop { @@ -387,7 +398,7 @@ pub fn wrap_optimal_fit<'a, 'b, T: Fragment>( } lines.reverse(); - lines + Ok(lines) } #[cfg(test)] @@ -405,17 +416,21 @@ mod tests { } #[test] - #[should_panic] fn wrap_fragments_with_infinite_widths() { let words = vec![Word(f64::INFINITY)]; - wrap_optimal_fit(&words, &[0.0], &OptimalFit::default()); + assert_eq!( + wrap_optimal_fit(&words, &[0.0], &OptimalFit::default()), + Err(OverflowError) + ); } #[test] - #[should_panic] fn wrap_fragments_with_huge_widths() { let words = vec![Word(1e200), Word(1e250), Word(1e300)]; - wrap_optimal_fit(&words, &[1e300], &OptimalFit::default()); + assert_eq!( + wrap_optimal_fit(&words, &[1e300], &OptimalFit::default()), + Err(OverflowError) + ); } #[test] @@ -423,6 +438,9 @@ mod tests { // The gaps will be of the sizes between 1e25 and 1e75. This // makes the `gap * gap` cost fit comfortably in a f64. let words = vec![Word(1e25), Word(1e50), Word(1e75)]; - wrap_optimal_fit(&words, &[1e100], &OptimalFit::default()); + assert_eq!( + wrap_optimal_fit(&words, &[1e100], &OptimalFit::default()), + Ok(vec![&vec![Word(1e25), Word(1e50), Word(1e75)][..]]) + ); } }