From 5cd55391ec1a69fb07d9a0566361543be698dcf6 Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Sat, 28 Aug 2021 16:28:38 -0400 Subject: [PATCH] seq: compute width of numbers after parsing Fix a bug in `seq` where the number of characters needed to print the number was computed incorrectly in some cases. This commit changes the computation of the width to be after parsing the number instead of before, in order to accommodate inputs like `1e3`, which requires four digits when printing the number, not three. --- src/uu/seq/src/seq.rs | 66 +++++++++++++++++++++++++++++++++++---- tests/by-util/test_seq.rs | 18 +++++++++++ 2 files changed, 78 insertions(+), 6 deletions(-) diff --git a/src/uu/seq/src/seq.rs b/src/uu/seq/src/seq.rs index f24b8be68..730c5efc7 100644 --- a/src/uu/seq/src/seq.rs +++ b/src/uu/seq/src/seq.rs @@ -61,6 +61,38 @@ impl Number { Number::F64(n) => n, } } + + /// Number of characters needed to print the integral part of the number. + /// + /// The number of characters includes one character to represent the + /// minus sign ("-") if this number is negative. + /// + /// # Examples + /// + /// ```rust,ignore + /// use num_bigint::{BigInt, Sign}; + /// + /// assert_eq!( + /// Number::BigInt(BigInt::new(Sign::Plus, vec![123])).num_digits(), + /// 3 + /// ); + /// assert_eq!( + /// Number::BigInt(BigInt::new(Sign::Minus, vec![123])).num_digits(), + /// 4 + /// ); + /// assert_eq!(Number::F64(123.45).num_digits(), 3); + /// assert_eq!(Number::MinusZero.num_digits(), 2); + /// ``` + fn num_digits(&self) -> usize { + match self { + Number::MinusZero => 2, + Number::BigInt(n) => n.to_string().len(), + Number::F64(n) => { + let s = n.to_string(); + s.find('.').unwrap_or_else(|| s.len()) + } + } + } } impl FromStr for Number { @@ -121,13 +153,11 @@ pub fn uumain(args: impl uucore::Args) -> i32 { }; let mut largest_dec = 0; - let mut padding = 0; let first = if numbers.len() > 1 { let slice = numbers[0]; let len = slice.len(); let dec = slice.find('.').unwrap_or(len); largest_dec = len - dec; - padding = dec; crash_if_err!(1, slice.parse()) } else { Number::BigInt(BigInt::one()) @@ -137,7 +167,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let len = slice.len(); let dec = slice.find('.').unwrap_or(len); largest_dec = cmp::max(largest_dec, len - dec); - padding = cmp::max(padding, dec); crash_if_err!(1, slice.parse()) } else { Number::BigInt(BigInt::one()) @@ -150,15 +179,18 @@ pub fn uumain(args: impl uucore::Args) -> i32 { ); return 1; } - let last = { + let last: Number = { let slice = numbers[numbers.len() - 1]; - padding = cmp::max(padding, slice.find('.').unwrap_or_else(|| slice.len())); crash_if_err!(1, slice.parse()) }; if largest_dec > 0 { largest_dec -= 1; } + let padding = first + .num_digits() + .max(increment.num_digits()) + .max(last.num_digits()); match (first, last, increment) { (Number::MinusZero, Number::BigInt(last), Number::BigInt(increment)) => print_seq_integers( (BigInt::zero(), increment, last), @@ -298,12 +330,14 @@ fn print_seq_integers( if !is_first_iteration { print!("{}", separator); } + let mut width = padding; if is_first_iteration && is_first_minus_zero { print!("-"); + width -= 1; } is_first_iteration = false; if pad { - print!("{number:>0width$}", number = value, width = padding); + print!("{number:>0width$}", number = value, width = width); } else { print!("{}", value); } @@ -314,3 +348,23 @@ fn print_seq_integers( print!("{}", terminator); } } + +#[cfg(test)] +mod tests { + use crate::Number; + use num_bigint::{BigInt, Sign}; + + #[test] + fn test_number_num_digits() { + assert_eq!( + Number::BigInt(BigInt::new(Sign::Plus, vec![123])).num_digits(), + 3 + ); + assert_eq!( + Number::BigInt(BigInt::new(Sign::Minus, vec![123])).num_digits(), + 4 + ); + assert_eq!(Number::F64(123.45).num_digits(), 3); + assert_eq!(Number::MinusZero.num_digits(), 2); + } +} diff --git a/tests/by-util/test_seq.rs b/tests/by-util/test_seq.rs index 48221eaf2..458769839 100644 --- a/tests/by-util/test_seq.rs +++ b/tests/by-util/test_seq.rs @@ -158,3 +158,21 @@ fn test_drop_negative_zero_end() { .stdout_is("1\n0\n") .no_stderr(); } + +#[test] +fn test_width_scientific_notation() { + new_ucmd!() + .args(&["-w", "999", "1e3"]) + .succeeds() + .stdout_is("0999\n1000\n") + .no_stderr(); +} + +#[test] +fn test_width_negative_zero() { + new_ucmd!() + .args(&["-w", "-0", "1"]) + .succeeds() + .stdout_is("-0\n01\n") + .no_stderr(); +}