Skip to content

Commit d993878

Browse files
committed
Try to fit into grid with fewer lines first and return early
1 parent 6a6e4a2 commit d993878

File tree

1 file changed

+25
-70
lines changed

1 file changed

+25
-70
lines changed

src/lib.rs

Lines changed: 25 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,8 @@ impl Dimensions {
248248
pub struct Grid {
249249
options: GridOptions,
250250
cells: Vec<Cell>,
251-
widest_cell_length: Width,
251+
widest_cell_width: Width,
252+
narrowest_cell_width: Width,
252253
width_sum: Width,
253254
cell_count: usize,
254255
}
@@ -258,7 +259,8 @@ impl Grid {
258259
/// Creates a new grid view with the given options.
259260
pub fn new(options: GridOptions) -> Self {
260261
let cells = Vec::new();
261-
Self { options, cells, widest_cell_length: 0,
262+
Self { options, cells,
263+
widest_cell_width: 0, narrowest_cell_width: usize::MAX,
262264
width_sum: 0, cell_count: 0 }
263265
}
264266

@@ -270,8 +272,11 @@ impl Grid {
270272

271273
/// Adds another cell onto the vector.
272274
pub fn add(&mut self, cell: Cell) {
273-
if cell.width > self.widest_cell_length {
274-
self.widest_cell_length = cell.width;
275+
if cell.width > self.widest_cell_width {
276+
self.widest_cell_width = cell.width;
277+
}
278+
if cell.width < self.narrowest_cell_width {
279+
self.narrowest_cell_width = cell.width;
275280
}
276281
self.width_sum += cell.width;
277282
self.cell_count += 1;
@@ -322,36 +327,8 @@ impl Grid {
322327
Dimensions { num_lines, widths }
323328
}
324329

325-
fn theoretical_max_num_lines(&self, maximum_width: usize) -> usize {
326-
// TODO: Make code readable / efficient.
327-
let mut theoretical_min_num_cols = 0;
328-
let mut col_total_width_so_far = 0;
329-
330-
let mut cells = self.cells.clone();
331-
cells.sort_unstable_by(|a, b| b.width.cmp(&a.width)); // Sort in reverse order
332-
333-
for cell in &cells {
334-
if cell.width + col_total_width_so_far <= maximum_width {
335-
theoretical_min_num_cols += 1;
336-
col_total_width_so_far += cell.width;
337-
} else {
338-
let mut theoretical_max_num_lines = self.cell_count / theoretical_min_num_cols;
339-
if self.cell_count % theoretical_min_num_cols != 0 {
340-
theoretical_max_num_lines += 1;
341-
}
342-
return theoretical_max_num_lines;
343-
}
344-
col_total_width_so_far += self.options.filling.width()
345-
}
346-
347-
// If we make it to this point, we have exhausted all cells before
348-
// reaching the maximum width; the theoretical max number of lines
349-
// needed to display all cells is 1.
350-
1
351-
}
352-
353330
fn width_dimensions(&self, maximum_width: Width) -> Option<Dimensions> {
354-
if self.widest_cell_length > maximum_width {
331+
if self.widest_cell_width > maximum_width {
355332
// Largest cell is wider than maximum width; it is impossible to fit.
356333
return None;
357334
}
@@ -370,51 +347,29 @@ impl Grid {
370347
return None;
371348
}
372349

373-
let theoretical_max_num_lines = self.theoretical_max_num_lines(maximum_width);
374-
if theoretical_max_num_lines == 1 {
375-
// This if—statement is neccesary for the function to work correctly
376-
// for small inputs.
377-
return Some(Dimensions {
378-
num_lines: 1,
379-
// I clone self.cells twice. Once here, and once in
380-
// self.theoretical_max_num_lines. Perhaps not the best for
381-
// performance?
382-
widths: self.cells.clone().into_iter().map(|cell| cell.width).collect()
383-
});
384-
}
385-
// Instead of numbers of columns, try to find the fewest number of *lines*
386-
// that the output will fit in.
387-
let mut smallest_dimensions_yet = None;
388-
for num_lines in (1 ..= theoretical_max_num_lines).rev() {
389-
// The number of columns is the number of cells divided by the number
390-
// of lines, *rounded up*.
391-
let num_columns = if self.cell_count % num_lines == 0 {
392-
self.cell_count / num_lines
393-
} else {
394-
self.cell_count / num_lines + 1
395-
};
350+
let max_column_count = self.theoretical_max_column_count(maximum_width);
351+
for num_columns in (2..=max_column_count).rev() {
352+
let potential_dimensions = self.columns_dimensions(num_columns);
396353

397-
// Early abort: if there are so many columns that the width of the
398-
// *column separators* is bigger than the width of the screen, then
399-
// don’t even try to tabulate it.
400-
// This is actually a necessary check, because the width is stored as
401-
// a usize, and making it go negative makes it huge instead, but it
402-
// also serves as a speed-up.
403354
let total_separator_width = (num_columns - 1) * self.options.filling.width();
404-
if maximum_width < total_separator_width {
405-
continue;
406-
}
407-
408-
// Remove the separator width from the available space.
409355
let adjusted_width = maximum_width - total_separator_width;
410356

411-
let potential_dimensions = self.column_widths(num_lines, num_columns);
412357
if potential_dimensions.widths.iter().sum::<Width>() < adjusted_width {
413358
return Some(potential_dimensions);
414359
}
415360
}
416361

417-
smallest_dimensions_yet
362+
Some(self.columns_dimensions(1))
363+
}
364+
365+
fn theoretical_max_column_count(&self, maximum_width: Width) -> usize {
366+
// Best case: every column is of narrowest width, except the column with the widest cell
367+
let max_column_count = ((maximum_width - self.widest_cell_width) /
368+
// let’s see how many filling + narrowest cells we can fit
369+
(self.narrowest_cell_width + self.options.filling.width())
370+
) + 1; // we add one since we substracted self.widest_cell_width at the beginning
371+
372+
return usize::min(max_column_count, self.cell_count);
418373
}
419374
}
420375

@@ -766,7 +721,7 @@ mod test {
766721
grid.add(Cell::from(*s));
767722
}
768723

769-
let bits = "test1||test2||test3||test4 ||test5 ||test6\ntest7||test8||test9||test10||test11||\n";
724+
let bits = "test1 ||test2 ||test3||test4||test5||test6||test7||test8||test9\ntest10||test11||\n";
770725
assert_eq!(grid.fit_into_width(69).unwrap().to_string(), bits);
771726
assert_eq!(grid.fit_into_width(69).unwrap().row_count(), 2);
772727
}

0 commit comments

Comments
 (0)