Merge pull request #2609 from jfinkels/seq-negative-zero-start

seq: print negative zero at start of integer sequence
This commit is contained in:
Sylvestre Ledru 2021-08-28 12:32:50 +02:00 committed by GitHub
commit 85c7178b10
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 75 additions and 15 deletions

View file

@ -38,6 +38,8 @@ struct SeqOptions {
}
enum Number {
/// Negative zero, as if it were an integer.
MinusZero,
BigInt(BigInt),
F64(f64),
}
@ -45,6 +47,7 @@ enum Number {
impl Number {
fn is_zero(&self) -> bool {
match self {
Number::MinusZero => true,
Number::BigInt(n) => n.is_zero(),
Number::F64(n) => n.is_zero(),
}
@ -52,6 +55,7 @@ impl Number {
fn into_f64(self) -> f64 {
match self {
Number::MinusZero => -0.,
// BigInt::to_f64() can not return None.
Number::BigInt(n) => n.to_f64().unwrap(),
Number::F64(n) => n,
@ -67,7 +71,16 @@ impl FromStr for Number {
}
match s.parse::<BigInt>() {
Ok(n) => Ok(Number::BigInt(n)),
Ok(n) => {
// If `s` is '-0', then `parse()` returns
// `BigInt::zero()`, but we need to return
// `Number::MinusZero` instead.
if n == BigInt::zero() && s.starts_with('-') {
Ok(Number::MinusZero)
} else {
Ok(Number::BigInt(n))
}
}
Err(_) => match s.parse::<f64>() {
Ok(value) if value.is_nan() => Err(format!(
"invalid 'not-a-number' argument: '{}'\nTry '{} --help' for more information.",
@ -85,6 +98,16 @@ impl FromStr for Number {
}
}
/// A range of integers.
///
/// The elements are (first, increment, last).
type RangeInt = (BigInt, BigInt, BigInt);
/// A range of f64.
///
/// The elements are (first, increment, last).
type RangeF64 = (f64, f64, f64);
pub fn uumain(args: impl uucore::Args) -> i32 {
let usage = usage();
let matches = uu_app().usage(&usage[..]).get_matches_from(args);
@ -137,21 +160,26 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
}
match (first, last, increment) {
(Number::MinusZero, Number::BigInt(last), Number::BigInt(increment)) => print_seq_integers(
(BigInt::zero(), increment, last),
options.separator,
options.terminator,
options.widths,
padding,
true,
),
(Number::BigInt(first), Number::BigInt(last), Number::BigInt(increment)) => {
print_seq_integers(
first,
increment,
last,
(first, increment, last),
options.separator,
options.terminator,
options.widths,
padding,
false,
)
}
(first, last, increment) => print_seq(
first.into_f64(),
increment.into_f64(),
last.into_f64(),
(first.into_f64(), increment.into_f64(), last.into_f64()),
largest_dec,
options.separator,
options.terminator,
@ -208,17 +236,15 @@ fn done_printing<T: Num + PartialOrd>(next: &T, increment: &T, last: &T) -> bool
}
/// Floating point based code path
#[allow(clippy::too_many_arguments)]
fn print_seq(
first: f64,
increment: f64,
last: f64,
range: RangeF64,
largest_dec: usize,
separator: String,
terminator: String,
pad: bool,
padding: usize,
) {
let (first, increment, last) = range;
let mut i = 0isize;
let mut value = first + i as f64 * increment;
while !done_printing(&value, &increment, &last) {
@ -243,22 +269,38 @@ fn print_seq(
crash_if_err!(1, stdout().flush());
}
/// BigInt based code path
/// Print an integer sequence.
///
/// This function prints a sequence of integers defined by `range`,
/// which defines the first integer, last integer, and increment of the
/// range. The `separator` is inserted between each integer and
/// `terminator` is inserted at the end.
///
/// The `pad` parameter indicates whether to pad numbers to the width
/// given in `padding`.
///
/// If `is_first_minus_zero` is `true`, then the `first` parameter is
/// printed as if it were negative zero, even though no such number
/// exists as an integer (negative zero only exists for floating point
/// numbers). Only set this to `true` if `first` is actually zero.
fn print_seq_integers(
first: BigInt,
increment: BigInt,
last: BigInt,
range: RangeInt,
separator: String,
terminator: String,
pad: bool,
padding: usize,
is_first_minus_zero: bool,
) {
let (first, increment, last) = range;
let mut value = first;
let mut is_first_iteration = true;
while !done_printing(&value, &increment, &last) {
if !is_first_iteration {
print!("{}", separator);
}
if is_first_iteration && is_first_minus_zero {
print!("-");
}
is_first_iteration = false;
if pad {
print!("{number:>0width$}", number = value, width = padding);

View file

@ -140,3 +140,21 @@ fn test_seq_wrong_arg_floats() {
fn test_zero_step_floats() {
new_ucmd!().args(&["10.0", "0", "32"]).fails();
}
#[test]
fn test_preserve_negative_zero_start() {
new_ucmd!()
.args(&["-0", "1"])
.succeeds()
.stdout_is("-0\n1\n")
.no_stderr();
}
#[test]
fn test_drop_negative_zero_end() {
new_ucmd!()
.args(&["1", "-1", "-0"])
.succeeds()
.stdout_is("1\n0\n")
.no_stderr();
}