Skip to content

Commit

Permalink
Introduce OverflowError
Browse files Browse the repository at this point in the history
  • Loading branch information
mgeisler committed Jan 4, 2022
1 parent f01811b commit dbcc245
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 15 deletions.
2 changes: 1 addition & 1 deletion src/wrap_algorithms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down
46 changes: 32 additions & 14 deletions src/wrap_algorithms/optimal_fit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<Vec<&'a [T]>, 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);
Expand Down Expand Up @@ -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 {
Expand All @@ -387,7 +398,7 @@ pub fn wrap_optimal_fit<'a, 'b, T: Fragment>(
}

lines.reverse();
lines
Ok(lines)
}

#[cfg(test)]
Expand All @@ -405,24 +416,31 @@ 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]
fn wrap_fragments_with_large_widths() {
// 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)][..]])
);
}
}

0 comments on commit dbcc245

Please sign in to comment.