mirror of
https://github.com/uutils/coreutils
synced 2024-11-16 09:48:03 +00:00
Merge pull request #2609 from jfinkels/seq-negative-zero-start
seq: print negative zero at start of integer sequence
This commit is contained in:
commit
85c7178b10
2 changed files with 75 additions and 15 deletions
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue