1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-08-02 14:07:46 +00:00

seq: simplify and use new printf implementation

This commit is contained in:
Terts Diepraam 2023-11-13 17:37:25 +01:00
parent 6481d63ea4
commit e7d58f673f
8 changed files with 104 additions and 569 deletions

View file

@ -25,13 +25,8 @@ use std::fmt::Display;
use std::ops::Add; use std::ops::Add;
use bigdecimal::BigDecimal; use bigdecimal::BigDecimal;
use num_bigint::BigInt;
use num_bigint::ToBigInt;
use num_traits::One;
use num_traits::Zero; use num_traits::Zero;
use crate::extendedbigint::ExtendedBigInt;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum ExtendedBigDecimal { pub enum ExtendedBigDecimal {
/// Arbitrary precision floating point number. /// Arbitrary precision floating point number.
@ -72,53 +67,14 @@ pub enum ExtendedBigDecimal {
Nan, Nan,
} }
/// The smallest integer greater than or equal to this number.
fn ceil(x: BigDecimal) -> BigInt {
if x.is_integer() {
// Unwrapping the Option because it always returns Some
x.to_bigint().unwrap()
} else {
(x + BigDecimal::one().half()).round(0).to_bigint().unwrap()
}
}
/// The largest integer less than or equal to this number.
fn floor(x: BigDecimal) -> BigInt {
if x.is_integer() {
// Unwrapping the Option because it always returns Some
x.to_bigint().unwrap()
} else {
(x - BigDecimal::one().half()).round(0).to_bigint().unwrap()
}
}
impl ExtendedBigDecimal { impl ExtendedBigDecimal {
/// The smallest integer greater than or equal to this number. #[cfg(test)]
pub fn ceil(self) -> ExtendedBigInt { pub fn zero() -> Self {
match self { Self::BigDecimal(1.into())
Self::BigDecimal(x) => ExtendedBigInt::BigInt(ceil(x)),
other => From::from(other),
}
} }
/// The largest integer less than or equal to this number. pub fn one() -> Self {
pub fn floor(self) -> ExtendedBigInt { Self::BigDecimal(1.into())
match self {
Self::BigDecimal(x) => ExtendedBigInt::BigInt(floor(x)),
other => From::from(other),
}
}
}
impl From<ExtendedBigInt> for ExtendedBigDecimal {
fn from(big_int: ExtendedBigInt) -> Self {
match big_int {
ExtendedBigInt::BigInt(n) => Self::BigDecimal(BigDecimal::from(n)),
ExtendedBigInt::Infinity => Self::Infinity,
ExtendedBigInt::MinusInfinity => Self::MinusInfinity,
ExtendedBigInt::MinusZero => Self::MinusZero,
ExtendedBigInt::Nan => Self::Nan,
}
} }
} }

View file

@ -1,214 +0,0 @@
// This file is part of the uutils coreutils package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
// spell-checker:ignore bigint extendedbigint extendedbigdecimal
//! An arbitrary precision integer that can also represent infinity, NaN, etc.
//!
//! Usually infinity, NaN, and negative zero are only represented for
//! floating point numbers. The [`ExtendedBigInt`] enumeration provides
//! a representation of those things with the set of integers. The
//! finite values are stored as [`BigInt`] instances.
//!
//! # Examples
//!
//! Addition works for [`ExtendedBigInt`] as it does for floats. For
//! example, adding infinity to any finite value results in infinity:
//!
//! ```rust,ignore
//! let summand1 = ExtendedBigInt::BigInt(BigInt::zero());
//! let summand2 = ExtendedBigInt::Infinity;
//! assert_eq!(summand1 + summand2, ExtendedBigInt::Infinity);
//! ```
use std::cmp::Ordering;
use std::fmt::Display;
use std::ops::Add;
use num_bigint::BigInt;
use num_bigint::ToBigInt;
use num_traits::One;
use num_traits::Zero;
use crate::extendedbigdecimal::ExtendedBigDecimal;
#[derive(Debug, Clone)]
pub enum ExtendedBigInt {
BigInt(BigInt),
Infinity,
MinusInfinity,
MinusZero,
Nan,
}
impl ExtendedBigInt {
/// The integer number one.
pub fn one() -> Self {
// We would like to implement `num_traits::One`, but it requires
// a multiplication implementation, and we don't want to
// implement that here.
Self::BigInt(BigInt::one())
}
}
impl From<ExtendedBigDecimal> for ExtendedBigInt {
fn from(big_decimal: ExtendedBigDecimal) -> Self {
match big_decimal {
// TODO When can this fail?
ExtendedBigDecimal::BigDecimal(x) => Self::BigInt(x.to_bigint().unwrap()),
ExtendedBigDecimal::Infinity => Self::Infinity,
ExtendedBigDecimal::MinusInfinity => Self::MinusInfinity,
ExtendedBigDecimal::MinusZero => Self::MinusZero,
ExtendedBigDecimal::Nan => Self::Nan,
}
}
}
impl Display for ExtendedBigInt {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::BigInt(n) => n.fmt(f),
Self::Infinity => f32::INFINITY.fmt(f),
Self::MinusInfinity => f32::NEG_INFINITY.fmt(f),
Self::MinusZero => "-0".fmt(f),
Self::Nan => "nan".fmt(f),
}
}
}
impl Zero for ExtendedBigInt {
fn zero() -> Self {
Self::BigInt(BigInt::zero())
}
fn is_zero(&self) -> bool {
match self {
Self::BigInt(n) => n.is_zero(),
Self::MinusZero => true,
_ => false,
}
}
}
impl Add for ExtendedBigInt {
type Output = Self;
fn add(self, other: Self) -> Self {
match (self, other) {
(Self::BigInt(m), Self::BigInt(n)) => Self::BigInt(m.add(n)),
(Self::BigInt(_), Self::MinusInfinity) => Self::MinusInfinity,
(Self::BigInt(_), Self::Infinity) => Self::Infinity,
(Self::BigInt(_), Self::Nan) => Self::Nan,
(Self::BigInt(m), Self::MinusZero) => Self::BigInt(m),
(Self::Infinity, Self::BigInt(_)) => Self::Infinity,
(Self::Infinity, Self::Infinity) => Self::Infinity,
(Self::Infinity, Self::MinusZero) => Self::Infinity,
(Self::Infinity, Self::MinusInfinity) => Self::Nan,
(Self::Infinity, Self::Nan) => Self::Nan,
(Self::MinusInfinity, Self::BigInt(_)) => Self::MinusInfinity,
(Self::MinusInfinity, Self::MinusInfinity) => Self::MinusInfinity,
(Self::MinusInfinity, Self::MinusZero) => Self::MinusInfinity,
(Self::MinusInfinity, Self::Infinity) => Self::Nan,
(Self::MinusInfinity, Self::Nan) => Self::Nan,
(Self::Nan, _) => Self::Nan,
(Self::MinusZero, other) => other,
}
}
}
impl PartialEq for ExtendedBigInt {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::BigInt(m), Self::BigInt(n)) => m.eq(n),
(Self::BigInt(_), Self::MinusInfinity) => false,
(Self::BigInt(_), Self::Infinity) => false,
(Self::BigInt(_), Self::Nan) => false,
(Self::BigInt(_), Self::MinusZero) => false,
(Self::Infinity, Self::BigInt(_)) => false,
(Self::Infinity, Self::Infinity) => true,
(Self::Infinity, Self::MinusZero) => false,
(Self::Infinity, Self::MinusInfinity) => false,
(Self::Infinity, Self::Nan) => false,
(Self::MinusInfinity, Self::BigInt(_)) => false,
(Self::MinusInfinity, Self::Infinity) => false,
(Self::MinusInfinity, Self::MinusZero) => false,
(Self::MinusInfinity, Self::MinusInfinity) => true,
(Self::MinusInfinity, Self::Nan) => false,
(Self::Nan, _) => false,
(Self::MinusZero, Self::BigInt(_)) => false,
(Self::MinusZero, Self::Infinity) => false,
(Self::MinusZero, Self::MinusZero) => true,
(Self::MinusZero, Self::MinusInfinity) => false,
(Self::MinusZero, Self::Nan) => false,
}
}
}
impl PartialOrd for ExtendedBigInt {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match (self, other) {
(Self::BigInt(m), Self::BigInt(n)) => m.partial_cmp(n),
(Self::BigInt(_), Self::MinusInfinity) => Some(Ordering::Greater),
(Self::BigInt(_), Self::Infinity) => Some(Ordering::Less),
(Self::BigInt(_), Self::Nan) => None,
(Self::BigInt(m), Self::MinusZero) => m.partial_cmp(&BigInt::zero()),
(Self::Infinity, Self::BigInt(_)) => Some(Ordering::Greater),
(Self::Infinity, Self::Infinity) => Some(Ordering::Equal),
(Self::Infinity, Self::MinusZero) => Some(Ordering::Greater),
(Self::Infinity, Self::MinusInfinity) => Some(Ordering::Greater),
(Self::Infinity, Self::Nan) => None,
(Self::MinusInfinity, Self::BigInt(_)) => Some(Ordering::Less),
(Self::MinusInfinity, Self::Infinity) => Some(Ordering::Less),
(Self::MinusInfinity, Self::MinusZero) => Some(Ordering::Less),
(Self::MinusInfinity, Self::MinusInfinity) => Some(Ordering::Equal),
(Self::MinusInfinity, Self::Nan) => None,
(Self::Nan, _) => None,
(Self::MinusZero, Self::BigInt(n)) => BigInt::zero().partial_cmp(n),
(Self::MinusZero, Self::Infinity) => Some(Ordering::Less),
(Self::MinusZero, Self::MinusZero) => Some(Ordering::Equal),
(Self::MinusZero, Self::MinusInfinity) => Some(Ordering::Greater),
(Self::MinusZero, Self::Nan) => None,
}
}
}
#[cfg(test)]
mod tests {
use num_bigint::BigInt;
use num_traits::Zero;
use crate::extendedbigint::ExtendedBigInt;
#[test]
fn test_addition_infinity() {
let summand1 = ExtendedBigInt::BigInt(BigInt::zero());
let summand2 = ExtendedBigInt::Infinity;
assert_eq!(summand1 + summand2, ExtendedBigInt::Infinity);
}
#[test]
fn test_addition_minus_infinity() {
let summand1 = ExtendedBigInt::BigInt(BigInt::zero());
let summand2 = ExtendedBigInt::MinusInfinity;
assert_eq!(summand1 + summand2, ExtendedBigInt::MinusInfinity);
}
#[test]
fn test_addition_nan() {
let summand1 = ExtendedBigInt::BigInt(BigInt::zero());
let summand2 = ExtendedBigInt::Nan;
let sum = summand1 + summand2;
match sum {
ExtendedBigInt::Nan => (),
_ => unreachable!(),
}
}
#[test]
fn test_display() {
assert_eq!(format!("{}", ExtendedBigInt::BigInt(BigInt::zero())), "0");
assert_eq!(format!("{}", ExtendedBigInt::MinusZero), "-0");
assert_eq!(format!("{}", ExtendedBigInt::Infinity), "inf");
assert_eq!(format!("{}", ExtendedBigInt::MinusInfinity), "-inf");
assert_eq!(format!("{}", ExtendedBigInt::Nan), "nan");
}
}

View file

@ -12,70 +12,6 @@
use num_traits::Zero; use num_traits::Zero;
use crate::extendedbigdecimal::ExtendedBigDecimal; use crate::extendedbigdecimal::ExtendedBigDecimal;
use crate::extendedbigint::ExtendedBigInt;
/// An integral or floating point number.
#[derive(Debug, PartialEq)]
pub enum Number {
Int(ExtendedBigInt),
Float(ExtendedBigDecimal),
}
impl Number {
/// Decide whether this number is zero (either positive or negative).
pub fn is_zero(&self) -> bool {
// We would like to implement `num_traits::Zero`, but it
// requires an addition implementation, and we don't want to
// implement that here.
match self {
Self::Int(n) => n.is_zero(),
Self::Float(x) => x.is_zero(),
}
}
/// Convert this number into an `ExtendedBigDecimal`.
pub fn into_extended_big_decimal(self) -> ExtendedBigDecimal {
match self {
Self::Int(n) => ExtendedBigDecimal::from(n),
Self::Float(x) => x,
}
}
/// The integer number one.
pub fn one() -> Self {
// We would like to implement `num_traits::One`, but it requires
// a multiplication implementation, and we don't want to
// implement that here.
Self::Int(ExtendedBigInt::one())
}
/// Round this number towards the given other number.
///
/// If `other` is greater, then round up. If `other` is smaller,
/// then round down.
pub fn round_towards(self, other: &ExtendedBigInt) -> ExtendedBigInt {
match self {
// If this number is already an integer, it is already
// rounded to the nearest integer in the direction of
// `other`.
Self::Int(num) => num,
// Otherwise, if this number is a float, we need to decide
// whether `other` is larger or smaller than it, and thus
// whether to round up or round down, respectively.
Self::Float(num) => {
let other: ExtendedBigDecimal = From::from(other.clone());
if other > num {
num.ceil()
} else {
// If they are equal, then `self` is already an
// integer, so calling `floor()` does no harm and
// will just return that integer anyway.
num.floor()
}
}
}
}
}
/// A number with a specified number of integer and fractional digits. /// A number with a specified number of integer and fractional digits.
/// ///
@ -87,13 +23,13 @@ impl Number {
/// You can get an instance of this struct by calling [`str::parse`]. /// You can get an instance of this struct by calling [`str::parse`].
#[derive(Debug)] #[derive(Debug)]
pub struct PreciseNumber { pub struct PreciseNumber {
pub number: Number, pub number: ExtendedBigDecimal,
pub num_integral_digits: usize, pub num_integral_digits: usize,
pub num_fractional_digits: usize, pub num_fractional_digits: usize,
} }
impl PreciseNumber { impl PreciseNumber {
pub fn new(number: Number, num_integral_digits: usize, num_fractional_digits: usize) -> Self { pub fn new(number: ExtendedBigDecimal, num_integral_digits: usize, num_fractional_digits: usize) -> Self {
Self { Self {
number, number,
num_integral_digits, num_integral_digits,
@ -106,7 +42,7 @@ impl PreciseNumber {
// We would like to implement `num_traits::One`, but it requires // We would like to implement `num_traits::One`, but it requires
// a multiplication implementation, and we don't want to // a multiplication implementation, and we don't want to
// implement that here. // implement that here.
Self::new(Number::one(), 1, 0) Self::new(ExtendedBigDecimal::one(), 1, 0)
} }
/// Decide whether this number is zero (either positive or negative). /// Decide whether this number is zero (either positive or negative).

View file

@ -16,8 +16,6 @@ use num_traits::Num;
use num_traits::Zero; use num_traits::Zero;
use crate::extendedbigdecimal::ExtendedBigDecimal; use crate::extendedbigdecimal::ExtendedBigDecimal;
use crate::extendedbigint::ExtendedBigInt;
use crate::number::Number;
use crate::number::PreciseNumber; use crate::number::PreciseNumber;
/// An error returned when parsing a number fails. /// An error returned when parsing a number fails.
@ -29,8 +27,8 @@ pub enum ParseNumberError {
} }
/// Decide whether a given string and its parsed `BigInt` is negative zero. /// Decide whether a given string and its parsed `BigInt` is negative zero.
fn is_minus_zero_int(s: &str, n: &BigInt) -> bool { fn is_minus_zero_int(s: &str, n: &BigDecimal) -> bool {
s.starts_with('-') && n == &BigInt::zero() s.starts_with('-') && n == &BigDecimal::zero()
} }
/// Decide whether a given string and its parsed `BigDecimal` is negative zero. /// Decide whether a given string and its parsed `BigDecimal` is negative zero.
@ -53,19 +51,19 @@ fn is_minus_zero_float(s: &str, x: &BigDecimal) -> bool {
/// assert_eq!(actual, expected); /// assert_eq!(actual, expected);
/// ``` /// ```
fn parse_no_decimal_no_exponent(s: &str) -> Result<PreciseNumber, ParseNumberError> { fn parse_no_decimal_no_exponent(s: &str) -> Result<PreciseNumber, ParseNumberError> {
match s.parse::<BigInt>() { match s.parse::<BigDecimal>() {
Ok(n) => { Ok(n) => {
// If `s` is '-0', then `parse()` returns `BigInt::zero()`, // If `s` is '-0', then `parse()` returns `BigInt::zero()`,
// but we need to return `Number::MinusZeroInt` instead. // but we need to return `Number::MinusZeroInt` instead.
if is_minus_zero_int(s, &n) { if is_minus_zero_int(s, &n) {
Ok(PreciseNumber::new( Ok(PreciseNumber::new(
Number::Int(ExtendedBigInt::MinusZero), ExtendedBigDecimal::MinusZero,
s.len(), s.len(),
0, 0,
)) ))
} else { } else {
Ok(PreciseNumber::new( Ok(PreciseNumber::new(
Number::Int(ExtendedBigInt::BigInt(n)), ExtendedBigDecimal::BigDecimal(n),
s.len(), s.len(),
0, 0,
)) ))
@ -79,7 +77,7 @@ fn parse_no_decimal_no_exponent(s: &str) -> Result<PreciseNumber, ParseNumberErr
"nan" | "-nan" => return Err(ParseNumberError::Nan), "nan" | "-nan" => return Err(ParseNumberError::Nan),
_ => return Err(ParseNumberError::Float), _ => return Err(ParseNumberError::Float),
}; };
Ok(PreciseNumber::new(Number::Float(float_val), 0, 0)) Ok(PreciseNumber::new(float_val, 0, 0))
} }
} }
} }
@ -125,13 +123,13 @@ fn parse_exponent_no_decimal(s: &str, j: usize) -> Result<PreciseNumber, ParseNu
if exponent < 0 { if exponent < 0 {
if is_minus_zero_float(s, &x) { if is_minus_zero_float(s, &x) {
Ok(PreciseNumber::new( Ok(PreciseNumber::new(
Number::Float(ExtendedBigDecimal::MinusZero), ExtendedBigDecimal::MinusZero,
num_integral_digits, num_integral_digits,
num_fractional_digits, num_fractional_digits,
)) ))
} else { } else {
Ok(PreciseNumber::new( Ok(PreciseNumber::new(
Number::Float(ExtendedBigDecimal::BigDecimal(x)), ExtendedBigDecimal::BigDecimal(x),
num_integral_digits, num_integral_digits,
num_fractional_digits, num_fractional_digits,
)) ))
@ -169,13 +167,13 @@ fn parse_decimal_no_exponent(s: &str, i: usize) -> Result<PreciseNumber, ParseNu
let num_fractional_digits = s.len() - (i + 1); let num_fractional_digits = s.len() - (i + 1);
if is_minus_zero_float(s, &x) { if is_minus_zero_float(s, &x) {
Ok(PreciseNumber::new( Ok(PreciseNumber::new(
Number::Float(ExtendedBigDecimal::MinusZero), ExtendedBigDecimal::MinusZero,
num_integral_digits, num_integral_digits,
num_fractional_digits, num_fractional_digits,
)) ))
} else { } else {
Ok(PreciseNumber::new( Ok(PreciseNumber::new(
Number::Float(ExtendedBigDecimal::BigDecimal(x)), ExtendedBigDecimal::BigDecimal(x),
num_integral_digits, num_integral_digits,
num_fractional_digits, num_fractional_digits,
)) ))
@ -239,7 +237,7 @@ fn parse_decimal_and_exponent(
if num_digits_between_decimal_point_and_e <= exponent { if num_digits_between_decimal_point_and_e <= exponent {
if is_minus_zero_float(s, &val) { if is_minus_zero_float(s, &val) {
Ok(PreciseNumber::new( Ok(PreciseNumber::new(
Number::Int(ExtendedBigInt::MinusZero), ExtendedBigDecimal::MinusZero,
num_integral_digits, num_integral_digits,
num_fractional_digits, num_fractional_digits,
)) ))
@ -251,23 +249,23 @@ fn parse_decimal_and_exponent(
); );
let expanded = [&s[0..i], &s[i + 1..j], &zeros].concat(); let expanded = [&s[0..i], &s[i + 1..j], &zeros].concat();
let n = expanded let n = expanded
.parse::<BigInt>() .parse::<BigDecimal>()
.map_err(|_| ParseNumberError::Float)?; .map_err(|_| ParseNumberError::Float)?;
Ok(PreciseNumber::new( Ok(PreciseNumber::new(
Number::Int(ExtendedBigInt::BigInt(n)), ExtendedBigDecimal::BigDecimal(n),
num_integral_digits, num_integral_digits,
num_fractional_digits, num_fractional_digits,
)) ))
} }
} else if is_minus_zero_float(s, &val) { } else if is_minus_zero_float(s, &val) {
Ok(PreciseNumber::new( Ok(PreciseNumber::new(
Number::Float(ExtendedBigDecimal::MinusZero), ExtendedBigDecimal::MinusZero,
num_integral_digits, num_integral_digits,
num_fractional_digits, num_fractional_digits,
)) ))
} else { } else {
Ok(PreciseNumber::new( Ok(PreciseNumber::new(
Number::Float(ExtendedBigDecimal::BigDecimal(val)), ExtendedBigDecimal::BigDecimal(val),
num_integral_digits, num_integral_digits,
num_fractional_digits, num_fractional_digits,
)) ))
@ -303,20 +301,17 @@ fn parse_hexadecimal(s: &str) -> Result<PreciseNumber, ParseNumberError> {
} }
let num = BigInt::from_str_radix(s, 16).map_err(|_| ParseNumberError::Hex)?; let num = BigInt::from_str_radix(s, 16).map_err(|_| ParseNumberError::Hex)?;
let num = BigDecimal::from(num);
match (is_neg, num == BigInt::zero()) { match (is_neg, num == BigDecimal::zero()) {
(true, true) => Ok(PreciseNumber::new( (true, true) => Ok(PreciseNumber::new(ExtendedBigDecimal::MinusZero, 2, 0)),
Number::Int(ExtendedBigInt::MinusZero),
2,
0,
)),
(true, false) => Ok(PreciseNumber::new( (true, false) => Ok(PreciseNumber::new(
Number::Int(ExtendedBigInt::BigInt(-num)), ExtendedBigDecimal::BigDecimal(-num),
0, 0,
0, 0,
)), )),
(false, _) => Ok(PreciseNumber::new( (false, _) => Ok(PreciseNumber::new(
Number::Int(ExtendedBigInt::BigInt(num)), ExtendedBigDecimal::BigDecimal(num),
0, 0,
0, 0,
)), )),
@ -364,19 +359,14 @@ impl FromStr for PreciseNumber {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use bigdecimal::BigDecimal; use bigdecimal::BigDecimal;
use num_bigint::BigInt;
use num_traits::Zero;
use crate::extendedbigdecimal::ExtendedBigDecimal; use crate::extendedbigdecimal::ExtendedBigDecimal;
use crate::extendedbigint::ExtendedBigInt;
use crate::number::Number;
use crate::number::PreciseNumber; use crate::number::PreciseNumber;
use crate::numberparse::ParseNumberError; use crate::numberparse::ParseNumberError;
/// Convenience function for parsing a [`Number`] and unwrapping. /// Convenience function for parsing a [`Number`] and unwrapping.
fn parse(s: &str) -> Number { fn parse(s: &str) -> ExtendedBigDecimal {
s.parse::<PreciseNumber>().unwrap().number s.parse::<PreciseNumber>().unwrap().number
} }
@ -392,40 +382,37 @@ mod tests {
#[test] #[test]
fn test_parse_minus_zero_int() { fn test_parse_minus_zero_int() {
assert_eq!(parse("-0e0"), Number::Int(ExtendedBigInt::MinusZero)); assert_eq!(parse("-0e0"), ExtendedBigDecimal::MinusZero);
assert_eq!(parse("-0e-0"), Number::Int(ExtendedBigInt::MinusZero)); assert_eq!(parse("-0e-0"), ExtendedBigDecimal::MinusZero);
assert_eq!(parse("-0e1"), Number::Int(ExtendedBigInt::MinusZero)); assert_eq!(parse("-0e1"), ExtendedBigDecimal::MinusZero);
assert_eq!(parse("-0e+1"), Number::Int(ExtendedBigInt::MinusZero)); assert_eq!(parse("-0e+1"), ExtendedBigDecimal::MinusZero);
assert_eq!(parse("-0.0e1"), Number::Int(ExtendedBigInt::MinusZero)); assert_eq!(parse("-0.0e1"), ExtendedBigDecimal::MinusZero);
assert_eq!(parse("-0x0"), Number::Int(ExtendedBigInt::MinusZero)); assert_eq!(parse("-0x0"), ExtendedBigDecimal::MinusZero);
} }
#[test] #[test]
fn test_parse_minus_zero_float() { fn test_parse_minus_zero_float() {
assert_eq!(parse("-0.0"), Number::Float(ExtendedBigDecimal::MinusZero)); assert_eq!(parse("-0.0"), ExtendedBigDecimal::MinusZero);
assert_eq!(parse("-0e-1"), Number::Float(ExtendedBigDecimal::MinusZero)); assert_eq!(parse("-0e-1"), ExtendedBigDecimal::MinusZero);
assert_eq!( assert_eq!(parse("-0.0e-1"), ExtendedBigDecimal::MinusZero);
parse("-0.0e-1"),
Number::Float(ExtendedBigDecimal::MinusZero)
);
} }
#[test] #[test]
fn test_parse_big_int() { fn test_parse_big_int() {
assert_eq!(parse("0"), Number::Int(ExtendedBigInt::zero())); assert_eq!(parse("0"), ExtendedBigDecimal::zero());
assert_eq!(parse("0.1e1"), Number::Int(ExtendedBigInt::one())); assert_eq!(parse("0.1e1"), ExtendedBigDecimal::one());
assert_eq!( assert_eq!(
parse("1.0e1"), parse("1.0e1"),
Number::Int(ExtendedBigInt::BigInt("10".parse::<BigInt>().unwrap())) ExtendedBigDecimal::BigDecimal("10".parse::<BigDecimal>().unwrap())
); );
} }
#[test] #[test]
fn test_parse_hexadecimal_big_int() { fn test_parse_hexadecimal_big_int() {
assert_eq!(parse("0x0"), Number::Int(ExtendedBigInt::zero())); assert_eq!(parse("0x0"), ExtendedBigDecimal::zero());
assert_eq!( assert_eq!(
parse("0x10"), parse("0x10"),
Number::Int(ExtendedBigInt::BigInt("16".parse::<BigInt>().unwrap())) ExtendedBigDecimal::BigDecimal("16".parse::<BigDecimal>().unwrap())
); );
} }
@ -433,56 +420,34 @@ mod tests {
fn test_parse_big_decimal() { fn test_parse_big_decimal() {
assert_eq!( assert_eq!(
parse("0.0"), parse("0.0"),
Number::Float(ExtendedBigDecimal::BigDecimal( ExtendedBigDecimal::BigDecimal("0.0".parse::<BigDecimal>().unwrap())
"0.0".parse::<BigDecimal>().unwrap()
))
); );
assert_eq!( assert_eq!(
parse(".0"), parse(".0"),
Number::Float(ExtendedBigDecimal::BigDecimal( ExtendedBigDecimal::BigDecimal("0.0".parse::<BigDecimal>().unwrap())
"0.0".parse::<BigDecimal>().unwrap()
))
); );
assert_eq!( assert_eq!(
parse("1.0"), parse("1.0"),
Number::Float(ExtendedBigDecimal::BigDecimal( ExtendedBigDecimal::BigDecimal("1.0".parse::<BigDecimal>().unwrap())
"1.0".parse::<BigDecimal>().unwrap()
))
); );
assert_eq!( assert_eq!(
parse("10e-1"), parse("10e-1"),
Number::Float(ExtendedBigDecimal::BigDecimal( ExtendedBigDecimal::BigDecimal("1.0".parse::<BigDecimal>().unwrap())
"1.0".parse::<BigDecimal>().unwrap()
))
); );
assert_eq!( assert_eq!(
parse("-1e-3"), parse("-1e-3"),
Number::Float(ExtendedBigDecimal::BigDecimal( ExtendedBigDecimal::BigDecimal("-0.001".parse::<BigDecimal>().unwrap())
"-0.001".parse::<BigDecimal>().unwrap()
))
); );
} }
#[test] #[test]
fn test_parse_inf() { fn test_parse_inf() {
assert_eq!(parse("inf"), Number::Float(ExtendedBigDecimal::Infinity)); assert_eq!(parse("inf"), ExtendedBigDecimal::Infinity);
assert_eq!( assert_eq!(parse("infinity"), ExtendedBigDecimal::Infinity);
parse("infinity"), assert_eq!(parse("+inf"), ExtendedBigDecimal::Infinity);
Number::Float(ExtendedBigDecimal::Infinity) assert_eq!(parse("+infinity"), ExtendedBigDecimal::Infinity);
); assert_eq!(parse("-inf"), ExtendedBigDecimal::MinusInfinity);
assert_eq!(parse("+inf"), Number::Float(ExtendedBigDecimal::Infinity)); assert_eq!(parse("-infinity"), ExtendedBigDecimal::MinusInfinity);
assert_eq!(
parse("+infinity"),
Number::Float(ExtendedBigDecimal::Infinity)
);
assert_eq!(
parse("-inf"),
Number::Float(ExtendedBigDecimal::MinusInfinity)
);
assert_eq!(
parse("-infinity"),
Number::Float(ExtendedBigDecimal::MinusInfinity)
);
} }
#[test] #[test]

View file

@ -3,24 +3,21 @@
// For the full copyright and license information, please view the LICENSE // For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code. // file that was distributed with this source code.
// spell-checker:ignore (ToDO) istr chiter argptr ilen extendedbigdecimal extendedbigint numberparse // spell-checker:ignore (ToDO) istr chiter argptr ilen extendedbigdecimal extendedbigint numberparse
use std::io::{stdout, Write}; use std::io::{stdout, ErrorKind, Write};
use clap::{crate_version, Arg, ArgAction, Command}; use clap::{crate_version, Arg, ArgAction, Command};
use num_traits::{Zero, ToPrimitive}; use num_traits::{ToPrimitive, Zero};
use uucore::error::UResult; use uucore::error::{FromIo, UResult};
use uucore::format::{printf, FormatArgument, Format, num_format}; use uucore::format::{num_format, Format};
use uucore::{format_usage, help_about, help_usage}; use uucore::{format_usage, help_about, help_usage};
mod error; mod error;
mod extendedbigdecimal; mod extendedbigdecimal;
mod extendedbigint;
mod number; mod number;
mod numberparse; mod numberparse;
use crate::error::SeqError; use crate::error::SeqError;
use crate::extendedbigdecimal::ExtendedBigDecimal; use crate::extendedbigdecimal::ExtendedBigDecimal;
use crate::extendedbigint::ExtendedBigInt;
use crate::number::Number;
use crate::number::PreciseNumber; use crate::number::PreciseNumber;
const ABOUT: &str = help_about!("seq.md"); const ABOUT: &str = help_about!("seq.md");
@ -41,11 +38,6 @@ struct SeqOptions<'a> {
format: Option<&'a str>, format: Option<&'a str>,
} }
/// A range of integers.
///
/// The elements are (first, increment, last).
type RangeInt = (ExtendedBigInt, ExtendedBigInt, ExtendedBigInt);
/// A range of floats. /// A range of floats.
/// ///
/// The elements are (first, increment, last). /// The elements are (first, increment, last).
@ -116,53 +108,26 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
.num_fractional_digits .num_fractional_digits
.max(increment.num_fractional_digits); .max(increment.num_fractional_digits);
let result = match (first.number, increment.number, last.number) { let format = match options.format {
(Number::Int(first), Number::Int(increment), last) => { Some(f) => {
let last = last.round_towards(&first); let f = Format::<num_format::Float>::parse(f)?;
let format = match options.format { Some(f)
Some(f) => {
let f = Format::<num_format::SignedInt>::parse(f)?;
Some(f)
}
None => None,
};
print_seq_integers(
(first, increment, last),
&options.separator,
&options.terminator,
options.equal_width,
padding,
format,
)
}
(first, increment, last) => {
let format = match options.format {
Some(f) => {
let f = Format::<num_format::Float>::parse(f)?;
Some(f)
}
None => None,
};
print_seq(
(
first.into_extended_big_decimal(),
increment.into_extended_big_decimal(),
last.into_extended_big_decimal(),
),
largest_dec,
&options.separator,
&options.terminator,
options.equal_width,
padding,
format,
)
} }
None => None,
}; };
let result = print_seq(
(first.number, increment.number, last.number),
largest_dec,
&options.separator,
&options.terminator,
options.equal_width,
padding,
format,
);
match result { match result {
Ok(_) => Ok(()), Ok(_) => Ok(()),
_ => todo!(), Err(err) if err.kind() == ErrorKind::BrokenPipe => Ok(()),
// Err(err) if err.kind() == ErrorKind::BrokenPipe => Ok(()), Err(e) => Err(e.map_err_context(|| "write error".into())),
// Err(e) => Err(e.map_err_context(|| "write error".into())),
} }
} }
@ -230,28 +195,6 @@ fn write_value_float(
write!(writer, "{value_as_str}") write!(writer, "{value_as_str}")
} }
/// Write a big int formatted according to the given parameters.
fn write_value_int(
writer: &mut impl Write,
value: &ExtendedBigInt,
width: usize,
pad: bool,
) -> std::io::Result<()> {
let value_as_str = if pad {
if *value == ExtendedBigInt::MinusZero {
format!("{value:0<width$}")
} else {
format!("{value:>0width$}")
}
} else {
format!("{value}")
};
write!(writer, "{value_as_str}")
}
// TODO `print_seq()` and `print_seq_integers()` are nearly identical,
// they could be refactored into a single more general function.
/// Floating point based code path /// Floating point based code path
fn print_seq( fn print_seq(
range: RangeFloat, range: RangeFloat,
@ -261,12 +204,16 @@ fn print_seq(
pad: bool, pad: bool,
padding: usize, padding: usize,
format: Option<Format<num_format::Float>>, format: Option<Format<num_format::Float>>,
) -> UResult<()> { ) -> std::io::Result<()> {
let stdout = stdout(); let stdout = stdout();
let mut stdout = stdout.lock(); let mut stdout = stdout.lock();
let (first, increment, last) = range; let (first, increment, last) = range;
let mut value = first; let mut value = first;
let padding = if pad { padding + 1 + largest_dec } else { 0 }; let padding = if pad {
padding + if largest_dec > 0 { largest_dec + 1 } else { 0 }
} else {
0
};
let mut is_first_iteration = true; let mut is_first_iteration = true;
while !done_printing(&value, &increment, &last) { while !done_printing(&value, &increment, &last) {
if !is_first_iteration { if !is_first_iteration {
@ -307,65 +254,3 @@ fn print_seq(
stdout.flush()?; stdout.flush()?;
Ok(()) Ok(())
} }
/// 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(
range: RangeInt,
separator: &str,
terminator: &str,
pad: bool,
padding: usize,
format: Option<Format<num_format::SignedInt>>,
) -> UResult<()> {
let stdout = stdout();
let mut stdout = stdout.lock();
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 {
write!(stdout, "{separator}")?;
}
// If there was an argument `-f FORMAT`, then use that format
// template instead of the default formatting strategy.
//
// The `printf()` function takes in the template and
// the current value and writes the result to `stdout`.
//
// TODO See similar comment about formatting in `print_seq()`.
match &format {
Some(f) => {
let int = match &value {
ExtendedBigInt::BigInt(bi) => bi.to_i64().unwrap(),
ExtendedBigInt::Infinity => todo!(),
ExtendedBigInt::MinusInfinity => todo!(),
ExtendedBigInt::MinusZero => todo!(),
ExtendedBigInt::Nan => todo!(),
};
f.fmt(&mut stdout, int)?;
}
None => write_value_int(&mut stdout, &value, padding, pad)?,
}
// TODO Implement augmenting addition.
value = value + increment.clone();
is_first_iteration = false;
}
if !is_first_iteration {
write!(stdout, "{terminator}")?;
}
Ok(())
}

View file

@ -115,7 +115,7 @@ fn parse_iter(fmt: &[u8]) -> impl Iterator<Item = Result<FormatItem, FormatError
Some(_) => { Some(_) => {
let spec = match Spec::parse(&mut rest) { let spec = match Spec::parse(&mut rest) {
Some(spec) => spec, Some(spec) => spec,
None => return Some(Err(FormatError::SpecError)), None => return Some(Err(dbg!(FormatError::SpecError))),
}; };
Some(Ok(FormatItem::Spec(spec))) Some(Ok(FormatItem::Spec(spec)))
} }
@ -230,7 +230,7 @@ impl<F: Formatter> Format<F> {
for item in &mut iter { for item in &mut iter {
match item? { match item? {
FormatItem::Spec(_) => { FormatItem::Spec(_) => {
return Err(FormatError::SpecError); return Err(dbg!(FormatError::SpecError));
} }
FormatItem::Text(t) => suffix.extend_from_slice(&t), FormatItem::Text(t) => suffix.extend_from_slice(&t),
FormatItem::Char(c) => suffix.push(c), FormatItem::Char(c) => suffix.push(c),

View file

@ -13,14 +13,14 @@ pub trait Formatter {
Self: Sized; Self: Sized;
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy, Debug)]
pub enum UnsignedIntVariant { pub enum UnsignedIntVariant {
Decimal, Decimal,
Octal(Prefix), Octal(Prefix),
Hexadecimal(Case, Prefix), Hexadecimal(Case, Prefix),
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy, Debug)]
pub enum FloatVariant { pub enum FloatVariant {
Decimal, Decimal,
@ -29,32 +29,32 @@ pub enum FloatVariant {
Hexadecimal, Hexadecimal,
} }
#[derive(Clone, Copy, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Case { pub enum Case {
Lowercase, Lowercase,
Uppercase, Uppercase,
} }
#[derive(Clone, Copy, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Prefix { pub enum Prefix {
No, No,
Yes, Yes,
} }
#[derive(Clone, Copy, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ForceDecimal { pub enum ForceDecimal {
No, No,
Yes, Yes,
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy, Debug)]
pub enum PositiveSign { pub enum PositiveSign {
None, None,
Plus, Plus,
Space, Space,
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy, Debug)]
pub enum NumberAlignment { pub enum NumberAlignment {
Left, Left,
RightSpace, RightSpace,
@ -93,7 +93,7 @@ impl Formatter for SignedInt {
alignment, alignment,
} = s } = s
else { else {
return Err(FormatError::SpecError); return Err(dbg!(FormatError::SpecError));
}; };
let width = match width { let width = match width {
@ -152,7 +152,7 @@ impl Formatter for UnsignedInt {
alignment, alignment,
} = s } = s
else { else {
return Err(FormatError::SpecError); return Err(dbg!(FormatError::SpecError));
}; };
let width = match width { let width = match width {
@ -241,19 +241,19 @@ impl Formatter for Float {
precision, precision,
} = s } = s
else { else {
return Err(FormatError::SpecError); return Err(dbg!(FormatError::SpecError));
}; };
let width = match width { let width = match width {
Some(CanAsterisk::Fixed(x)) => x, Some(CanAsterisk::Fixed(x)) => x,
None => 0, None => 0,
Some(CanAsterisk::Asterisk) => return Err(FormatError::SpecError), Some(CanAsterisk::Asterisk) => return Err(dbg!(FormatError::SpecError)),
}; };
let precision = match precision { let precision = match precision {
Some(CanAsterisk::Fixed(x)) => x, Some(CanAsterisk::Fixed(x)) => x,
None => 0, None => 0,
Some(CanAsterisk::Asterisk) => return Err(FormatError::SpecError), Some(CanAsterisk::Asterisk) => return Err(dbg!(FormatError::SpecError)),
}; };
Ok(Self { Ok(Self {

View file

@ -9,6 +9,7 @@ use super::{
}; };
use std::{fmt::Display, io::Write}; use std::{fmt::Display, io::Write};
#[derive(Debug)]
pub enum Spec { pub enum Spec {
Char { Char {
width: Option<CanAsterisk<usize>>, width: Option<CanAsterisk<usize>>,
@ -41,7 +42,7 @@ pub enum Spec {
/// Precision and width specified might use an asterisk to indicate that they are /// Precision and width specified might use an asterisk to indicate that they are
/// determined by an argument. /// determined by an argument.
#[derive(Clone, Copy)] #[derive(Clone, Copy, Debug)]
pub enum CanAsterisk<T> { pub enum CanAsterisk<T> {
Fixed(T), Fixed(T),
Asterisk, Asterisk,
@ -99,6 +100,7 @@ impl Spec {
let width = eat_asterisk_or_number(rest); let width = eat_asterisk_or_number(rest);
let precision = if let Some(b'.') = rest.get(0) { let precision = if let Some(b'.') = rest.get(0) {
*rest = &rest[1..];
Some(eat_asterisk_or_number(rest).unwrap_or(CanAsterisk::Fixed(0))) Some(eat_asterisk_or_number(rest).unwrap_or(CanAsterisk::Fixed(0)))
} else { } else {
None None
@ -134,7 +136,9 @@ impl Spec {
*rest = &rest[1..]; *rest = &rest[1..];
} }
Some(match rest.get(0)? { let type_spec = rest.get(0)?;
*rest = &rest[1..];
Some(match type_spec {
b'c' => Spec::Char { b'c' => Spec::Char {
width, width,
align_left: minus, align_left: minus,
@ -208,7 +212,10 @@ impl Spec {
(false, false) => PositiveSign::None, (false, false) => PositiveSign::None,
}, },
}, },
_ => return None, x => {
dbg!("{:b}", x);
return dbg!(None)
},
}) })
} }