mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-08-02 05:57:46 +00:00
seq: simplify and use new printf implementation
This commit is contained in:
parent
6481d63ea4
commit
e7d58f673f
8 changed files with 104 additions and 569 deletions
|
@ -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,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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).
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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(())
|
|
||||||
}
|
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue