fixed_decimal 0.5.2

An API for representing numbers in a human-readable form
Documentation
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).

use smallvec::SmallVec;

use core::cmp;
use core::cmp::Ordering;
use core::convert::TryFrom;
use core::fmt;
use core::ops::RangeInclusive;

use core::str::FromStr;

use crate::uint_iterator::IntIterator;

use crate::Error;

// FixedDecimal assumes usize (digits.len()) is at least as big as a u16
#[cfg(not(any(
    target_pointer_width = "16",
    target_pointer_width = "32",
    target_pointer_width = "64"
)))]
compile_error!("The fixed_decimal crate only works if usizes are at least the size of a u16");

/// A struct containing decimal digits with efficient iteration and manipulation by magnitude
/// (power of 10). Supports a mantissa of non-zero digits and a number of leading and trailing
/// zeros, as well as an optional sign; used for formatting and plural selection.
///
/// # Data Types
///
/// The following types can be converted to a `FixedDecimal`:
///
/// - Integers, signed and unsigned
/// - Strings representing an arbitrary-precision decimal
///
/// To create a [`FixedDecimal`] with fraction digits, either create it from an integer and then
/// call [`FixedDecimal::multiplied_pow10`], or create it from a string.
///
/// Floating point numbers will be supported pending a resolution to
/// [#166](https://github.com/unicode-org/icu4x/issues/166). In the mean time, a third-party
/// float-to-string library may be used.
///
/// # Magnitude and Position
///
/// Each digit in a `FixedDecimal` is indexed by a *magnitude*, or the digit's power of 10.
/// Illustration for the number "12.34":
///
/// | Magnitude | Digit | Description      |
/// |-----------|-------|------------------|
/// | 1         | 1     | Tens place       |
/// | 0         | 2     | Ones place       |
/// | -1        | 3     | Tenths place     |
/// | -2        | 4     | Hundredths place |
///
/// Some functions deal with a *position* for the purpose of padding, truncating, or rounding a
/// number. In these cases, the position is the index on the right side of the digit of the
/// corresponding magnitude. Illustration:
///
/// ```text
/// Position:   2   0  -2
/// Number:     |1|2.3|4|
/// Position:     1  -1
/// ```
///
/// Expected output of various operations, all with input "12.34":
///
/// | Operation       | Position  | Expected Result |
/// |-----------------|-----------|-----------------|
/// | Truncate Left   | 1         | 10              |
/// | Truncate Right  | -1        | 12.3            |
/// | Pad Left        | 4         | 0012.34         |
/// | Pad Right       | -4        | 12.3400         |
///
/// # Examples
///
/// ```
/// use fixed_decimal::FixedDecimal;
///
/// let mut dec = FixedDecimal::from(250);
/// assert_eq!("250", dec.to_string());
///
/// dec.multiply_pow10(-2);
/// assert_eq!("2.50", dec.to_string());
/// ```
#[derive(Debug, Clone, PartialEq)]
pub struct FixedDecimal {
    /// List of digits; digits\[0\] is the most significant.
    ///
    /// Invariants:
    /// - Must not include leading or trailing zeros
    /// - Length must not exceed (magnitude - lower_magnitude + 1)
    // TODO: Consider using a nibble array
    digits: SmallVec<[u8; 8]>,

    /// Power of 10 of digits\[0\].
    ///
    /// Invariants:
    /// - <= upper_magnitude
    /// - >= lower_magnitude
    magnitude: i16,

    /// Power of 10 of the most significant digit, which may be zero.
    ///
    /// Invariants:
    /// - >= 0
    /// - >= magnitude
    upper_magnitude: i16,

    /// Power of 10 of the least significant digit, which may be zero.
    ///
    /// Invariants:
    /// - <= 0
    /// - <= magnitude
    lower_magnitude: i16,

    /// The sign; note that a positive value may be represented by either
    /// `Sign::Positive` (corresponding to a prefix +) or `Sign::None`
    /// (corresponding to the absence of a prefix sign).
    sign: Sign,
}

/// A specification of the sign used when formatting a number.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[allow(clippy::exhaustive_enums)]
// There are only 3 sign values, and they correspond to the low-level data model of FixedDecimal and UTS 35.
pub enum Sign {
    /// No sign (implicitly positive, e.g., 1729).
    None,
    /// A negative sign, e.g., -1729.
    Negative,
    /// An explicit positive sign, e.g., +1729.
    Positive,
}

/// Configuration for when to render the minus sign or plus sign.
#[non_exhaustive]
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum SignDisplay {
    /// Render the sign according to locale preferences. In most cases, this means a minus sign
    /// will be shown on negative numbers, and no sign will be shown on positive numbers.
    Auto,

    /// Do not display the sign. Positive and negative numbers are indistinguishable.
    Never,

    /// Show a minus sign on negative numbers and a plus sign on positive numbers, including zero.
    Always,

    /// Show a minus sign on negative numbers and a plus sign on positive numbers, except do not
    /// show any sign on positive or negative zero.
    ExceptZero,

    /// Show a minus sign on strictly negative numbers. Do not show a sign on positive numbers or
    /// on positive or negative zero.
    ///
    /// This differs from [`Auto`](SignDisplay::Auto) in that it does not render a sign on negative zero.
    Negative,
}

impl Default for FixedDecimal {
    /// Returns a `FixedDecimal` representing zero.
    fn default() -> Self {
        Self {
            digits: SmallVec::new(),
            magnitude: 0,
            upper_magnitude: 0,
            lower_magnitude: 0,
            sign: Sign::None,
        }
    }
}

macro_rules! impl_from_signed_integer_type {
    ($itype:ident, $utype: ident) => {
        impl From<$itype> for FixedDecimal {
            fn from(value: $itype) -> Self {
                let int_iterator: IntIterator<$utype> = value.into();
                let sign = if int_iterator.is_negative {
                    Sign::Negative
                } else {
                    Sign::None
                };
                let mut result = Self::from_ascending(int_iterator)
                    .expect("All built-in integer types should fit");
                result.sign = sign;
                result
            }
        }
    };
}

macro_rules! impl_from_unsigned_integer_type {
    ($utype: ident) => {
        impl From<$utype> for FixedDecimal {
            fn from(value: $utype) -> Self {
                let int_iterator: IntIterator<$utype> = value.into();
                Self::from_ascending(int_iterator).expect("All built-in integer types should fit")
            }
        }
    };
}

impl_from_signed_integer_type!(isize, usize);
impl_from_signed_integer_type!(i128, u128);
impl_from_signed_integer_type!(i64, u64);
impl_from_signed_integer_type!(i32, u32);
impl_from_signed_integer_type!(i16, u16);
impl_from_signed_integer_type!(i8, u8);

impl_from_unsigned_integer_type!(usize);
impl_from_unsigned_integer_type!(u128);
impl_from_unsigned_integer_type!(u64);
impl_from_unsigned_integer_type!(u32);
impl_from_unsigned_integer_type!(u16);
impl_from_unsigned_integer_type!(u8);

impl FixedDecimal {
    /// Initialize a `FixedDecimal` with an iterator of digits in ascending
    /// order of magnitude, starting with the digit at magnitude 0.
    ///
    /// This method is not public; use `TryFrom::<isize>` instead.
    fn from_ascending<T>(digits_iter: T) -> Result<Self, Error>
    where
        T: Iterator<Item = u8>,
    {
        // TODO: make X a usize generic to customize the size of this array
        // https://github.com/rust-lang/rust/issues/44580
        // NOTE: 39 is the size required for u128: ceil(log10(u128::MAX)) == 39.
        const X: usize = 39;
        // A temporary structure to allow the digits in the iterator to be reversed.
        // The digits are inserted started from the end, and then a slice is copied
        // into its final destination (result.digits).
        let mut mem: [u8; X] = [0u8; X];
        let mut trailing_zeros: usize = 0;
        let mut i: usize = 0;
        for (x, d) in digits_iter.enumerate() {
            // Take only up to core::i16::MAX values so that we have enough capacity
            if x > core::i16::MAX as usize {
                return Err(Error::Limit);
            }
            // TODO: Should we check here that `d` is between 0 and 9?
            // That should always be the case if IntIterator is used.
            if i != 0 || d != 0 {
                i += 1;
                match X.checked_sub(i) {
                    #[allow(clippy::indexing_slicing)] // X - i < X
                    Some(v) => mem[v] = d,
                    // This error should be obsolete after X is made generic
                    None => return Err(Error::Limit),
                }
            } else {
                trailing_zeros += 1;
            }
        }
        let mut result: Self = Default::default();
        if i != 0 {
            let magnitude = trailing_zeros + i - 1;
            debug_assert!(magnitude <= core::i16::MAX as usize);
            result.magnitude = magnitude as i16;
            result.upper_magnitude = result.magnitude;
            debug_assert!(i <= X);
            #[allow(clippy::indexing_slicing)] // X - i < X
            result.digits.extend_from_slice(&mem[(X - i)..]);
        }
        #[cfg(debug_assertions)]
        result.check_invariants();
        Ok(result)
    }

    /// Gets the digit at the specified order of magnitude. Returns 0 if the magnitude is out of
    /// range of the currently visible digits.
    ///
    /// # Examples
    ///
    /// ```
    /// use fixed_decimal::FixedDecimal;
    ///
    /// let dec = FixedDecimal::from(945);
    /// assert_eq!(0, dec.digit_at(-1));
    /// assert_eq!(5, dec.digit_at(0));
    /// assert_eq!(4, dec.digit_at(1));
    /// assert_eq!(9, dec.digit_at(2));
    /// assert_eq!(0, dec.digit_at(3));
    /// ```
    pub fn digit_at(&self, magnitude: i16) -> u8 {
        if magnitude > self.magnitude {
            0 // Leading zero
        } else {
            // The following line can't fail: magnitude <= self.magnitude, by
            // the if statement above, and u16::MAX == i16::MAX - i16::MIN, and
            // usize is asserted to be at least as big as u16.
            let j = crate::ops::i16_abs_sub(self.magnitude, magnitude) as usize;
            match self.digits.get(j) {
                Some(v) => *v,
                None => 0, // Trailing zero
            }
        }
    }

    /// Gets the digit at the specified order of next lower magnitude (magnitude - 1).
    /// Returns 0 if the next lower magnitued is out of range of currently visible digits or the magnitude equal `i16::min`.
    fn digit_at_next_positon(&self, magnitude: i16) -> u8 {
        if magnitude == i16::MIN {
            0
        } else {
            self.digit_at(magnitude - 1)
        }
    }

    /// Gets the visible range of digit magnitudes, in ascending order of magnitude. Call `.rev()`
    /// on the return value to get the range in descending order. Magnitude 0 is always included,
    /// even if the number has leading or trailing zeros.
    ///
    /// # Examples
    ///
    /// ```
    /// use fixed_decimal::FixedDecimal;
    ///
    /// let dec: FixedDecimal = "012.340".parse().expect("valid syntax");
    /// assert_eq!(-3..=2, dec.magnitude_range());
    /// ```
    pub const fn magnitude_range(&self) -> RangeInclusive<i16> {
        self.lower_magnitude..=self.upper_magnitude
    }

    /// Gets the magnitude of the largest nonzero digit. If the number is zero, 0 is returned.
    ///
    /// # Examples
    ///
    /// ```
    /// use fixed_decimal::FixedDecimal;
    ///
    /// let dec: FixedDecimal = "012.340".parse().expect("valid syntax");
    /// assert_eq!(1, dec.nonzero_magnitude_start());
    ///
    /// assert_eq!(0, FixedDecimal::from(0).nonzero_magnitude_start());
    /// ```
    pub fn nonzero_magnitude_start(&self) -> i16 {
        self.magnitude
    }

    /// Gets the magnitude of the smallest nonzero digit. If the number is zero, 0 is returned.
    ///
    /// # Examples
    ///
    /// ```
    /// use fixed_decimal::FixedDecimal;
    ///
    /// let dec: FixedDecimal = "012.340".parse().expect("valid syntax");
    /// assert_eq!(-2, dec.nonzero_magnitude_end());
    ///
    /// assert_eq!(0, FixedDecimal::from(0).nonzero_magnitude_end());
    /// ```
    pub fn nonzero_magnitude_end(&self) -> i16 {
        if self.is_zero() {
            0
        } else {
            crate::ops::i16_sub_unsigned(self.magnitude, self.digits.len() as u16 - 1)
        }
    }

    /// Returns whether the number has a numeric value of zero.
    ///
    /// # Examples
    ///
    /// ```
    /// use fixed_decimal::FixedDecimal;
    ///
    /// let dec: FixedDecimal = "000.000".parse().expect("valid syntax");
    /// assert!(dec.is_zero());
    /// ```
    #[inline]
    pub fn is_zero(&self) -> bool {
        self.digits.is_empty()
    }

    /// Clears all the fields and sets the number to zero.
    fn clear(&mut self) {
        self.upper_magnitude = 0;
        self.lower_magnitude = 0;
        self.magnitude = 0;
        self.digits.clear();
        self.sign = Sign::None;

        #[cfg(debug_assertions)]
        self.check_invariants();
    }

    /// Shift the digits by a power of 10, modifying self.
    ///
    /// Leading or trailing zeros may be added to keep the digit at magnitude 0 (the last digit
    /// before the decimal separator) visible.
    ///
    /// NOTE: if the operation causes overflow, the number will be set to zero.
    ///
    /// # Examples
    ///
    /// ```
    /// use fixed_decimal::FixedDecimal;
    ///
    /// let mut dec = FixedDecimal::from(42);
    /// assert_eq!("42", dec.to_string());
    ///
    /// dec.multiply_pow10(3);
    /// assert_eq!("42000", dec.to_string());
    /// ```
    pub fn multiply_pow10(&mut self, delta: i16) {
        match delta.cmp(&0) {
            Ordering::Greater => {
                let upper_magnitude = self.upper_magnitude.checked_add(delta);
                match upper_magnitude {
                    Some(upper_magnitude) => {
                        self.upper_magnitude = upper_magnitude;
                        // If we get here, then the magnitude change is in-bounds.
                        let lower_magnitude = self.lower_magnitude + delta;
                        self.lower_magnitude = cmp::min(0, lower_magnitude);
                    }
                    None => {
                        // there is an overflow
                        self.clear();
                    }
                }
            }
            Ordering::Less => {
                let lower_magnitude = self.lower_magnitude.checked_add(delta);
                match lower_magnitude {
                    Some(lower_magnitude) => {
                        self.lower_magnitude = lower_magnitude;
                        // If we get here, then the magnitude change is in-bounds.
                        let upper_magnitude = self.upper_magnitude + delta;
                        self.upper_magnitude = cmp::max(0, upper_magnitude);
                    }
                    None => {
                        // there is an overflow
                        self.clear();
                    }
                }
            }
            Ordering::Equal => {}
        };
        if !self.is_zero() {
            self.magnitude += delta;
        }
        #[cfg(debug_assertions)]
        self.check_invariants();
    }

    /// Shift the digits by a power of 10, consuming self and returning a new object if successful.
    ///
    /// Leading or trailing zeros may be added to keep the digit at magnitude 0 (the last digit
    /// before the decimal separator) visible.
    ///
    /// NOTE: if the operation causes overflow, the returned number will be zero.
    ///
    /// # Examples
    ///
    /// ```
    /// use fixed_decimal::FixedDecimal;
    ///
    /// let dec = FixedDecimal::from(42).multiplied_pow10(3);
    /// assert_eq!("42000", dec.to_string());
    /// ```
    pub fn multiplied_pow10(mut self, delta: i16) -> Self {
        self.multiply_pow10(delta);
        self
    }

    /// Returns the sign.
    /// # Examples
    /// ```
    /// use fixed_decimal::FixedDecimal;
    /// use fixed_decimal::Sign;
    /// # use std::str::FromStr;
    ///
    /// assert_eq!(FixedDecimal::from_str("1729").unwrap().sign(), Sign::None);
    /// assert_eq!(
    ///     FixedDecimal::from_str("-1729").unwrap().sign(),
    ///     Sign::Negative
    /// );
    /// assert_eq!(
    ///     FixedDecimal::from_str("+1729").unwrap().sign(),
    ///     Sign::Positive
    /// );
    /// ```
    pub fn sign(&self) -> Sign {
        self.sign
    }

    /// Change the sign to the one given.
    ///
    /// # Examples
    ///
    /// ```
    /// use fixed_decimal::FixedDecimal;
    /// use fixed_decimal::Sign;
    ///
    /// let mut dec = FixedDecimal::from(1729);
    /// assert_eq!("1729", dec.to_string());
    ///
    /// dec.set_sign(Sign::Negative);
    /// assert_eq!("-1729", dec.to_string());
    ///
    /// dec.set_sign(Sign::Positive);
    /// assert_eq!("+1729", dec.to_string());
    ///
    /// dec.set_sign(Sign::None);
    /// assert_eq!("1729", dec.to_string());
    /// ```
    pub fn set_sign(&mut self, sign: Sign) {
        self.sign = sign;
    }

    /// Change the sign to the one given, consuming self and returning a new object.
    ///
    /// # Examples
    ///
    /// ```
    /// use fixed_decimal::FixedDecimal;
    /// use fixed_decimal::Sign;
    ///
    /// assert_eq!(
    ///     "+1729",
    ///     FixedDecimal::from(1729)
    ///         .with_sign(Sign::Positive)
    ///         .to_string()
    /// );
    /// assert_eq!(
    ///     "1729",
    ///     FixedDecimal::from(-1729).with_sign(Sign::None).to_string()
    /// );
    /// assert_eq!(
    ///     "-1729",
    ///     FixedDecimal::from(1729)
    ///         .with_sign(Sign::Negative)
    ///         .to_string()
    /// );
    /// ```
    pub fn with_sign(mut self, sign: Sign) -> Self {
        self.set_sign(sign);
        self
    }

    /// Sets the sign according to the given sign display strategy.
    ///
    /// # Examples
    /// ```
    /// use fixed_decimal::FixedDecimal;
    /// use fixed_decimal::SignDisplay::*;
    ///
    /// let mut dec = FixedDecimal::from(1729);
    /// assert_eq!("1729", dec.to_string());
    /// dec.apply_sign_display(Always);
    /// assert_eq!("+1729", dec.to_string());
    /// ```
    pub fn apply_sign_display(&mut self, sign_display: SignDisplay) {
        use Sign::*;
        match sign_display {
            SignDisplay::Auto => {
                if self.sign != Negative {
                    self.sign = None
                }
            }
            SignDisplay::Always => {
                if self.sign != Negative {
                    self.sign = Positive
                }
            }
            SignDisplay::Never => self.sign = None,
            SignDisplay::ExceptZero => {
                if self.is_zero() {
                    self.sign = None
                } else if self.sign != Negative {
                    self.sign = Positive
                }
            }
            SignDisplay::Negative => {
                if self.sign != Negative || self.is_zero() {
                    self.sign = None
                }
            }
        }
    }

    /// Sets the sign according to the given sign display strategy, consuming
    /// self and returning a new object.
    ///
    /// # Examples
    /// ```
    /// use fixed_decimal::FixedDecimal;
    /// use fixed_decimal::SignDisplay::*;
    ///
    /// assert_eq!(
    ///     "+1729",
    ///     FixedDecimal::from(1729)
    ///         .with_sign_display(ExceptZero)
    ///         .to_string()
    /// );
    /// ```
    pub fn with_sign_display(mut self, sign_display: SignDisplay) -> Self {
        self.apply_sign_display(sign_display);
        self
    }

    /// Remove leading zeroes, consuming self and returning a new object.
    ///
    /// # Examples
    ///
    /// ```
    /// use fixed_decimal::FixedDecimal;
    ///
    /// let dec = FixedDecimal::from(123400)
    ///     .multiplied_pow10(-4)
    ///     .padded_start(4);
    /// assert_eq!("0012.3400", dec.to_string());
    ///
    /// assert_eq!("12.3400", dec.trimmed_start().to_string());
    /// ```
    pub fn trimmed_start(mut self) -> Self {
        self.trim_start();
        self
    }

    /// Remove leading zeroes, modifying self.
    ///
    /// # Examples
    ///
    /// ```
    /// use fixed_decimal::FixedDecimal;
    ///
    /// let mut dec = FixedDecimal::from(123400)
    ///     .multiplied_pow10(-4)
    ///     .padded_start(4);
    /// assert_eq!("0012.3400", dec.to_string());
    ///
    /// dec.trim_start();
    /// assert_eq!("12.3400", dec.to_string());
    /// ```
    ///
    /// There is no effect if the most significant digit has magnitude less than zero:
    ///
    /// ```
    /// # use fixed_decimal::FixedDecimal;
    /// let mut dec = FixedDecimal::from(22).multiplied_pow10(-4);
    /// assert_eq!("0.0022", dec.to_string());
    ///
    /// dec.trim_start();
    /// assert_eq!("0.0022", dec.to_string());
    /// ```
    pub fn trim_start(&mut self) {
        self.upper_magnitude = cmp::max(self.magnitude, 0);
        #[cfg(debug_assertions)]
        self.check_invariants();
    }

    /// Remove trailing zeroes, consuming self and returning a new object.
    ///
    /// # Examples
    ///
    /// ```
    /// use fixed_decimal::FixedDecimal;
    ///
    /// let dec = FixedDecimal::from(123400)
    ///     .multiplied_pow10(-4)
    ///     .padded_start(4);
    /// assert_eq!("0012.3400", dec.to_string());
    ///
    /// assert_eq!("0012.34", dec.trimmed_end().to_string());
    /// ```
    pub fn trimmed_end(mut self) -> Self {
        self.trim_end();
        self
    }

    /// Remove trailing zeroes, modifying self.
    ///
    /// # Examples
    ///
    /// ```
    /// use fixed_decimal::FixedDecimal;
    ///
    /// let mut dec = FixedDecimal::from(123400)
    ///     .multiplied_pow10(-4)
    ///     .padded_start(4);
    /// assert_eq!("0012.3400", dec.to_string());
    ///
    /// dec.trim_end();
    /// assert_eq!("0012.34", dec.to_string());
    /// ```
    ///
    /// There is no effect if the least significant digit has magnitude more than zero:
    ///
    /// ```
    /// # use fixed_decimal::FixedDecimal;
    /// let mut dec = FixedDecimal::from(2200);
    /// assert_eq!("2200", dec.to_string());
    ///
    /// dec.trim_end();
    /// assert_eq!("2200", dec.to_string());
    /// ```
    pub fn trim_end(&mut self) {
        self.lower_magnitude = cmp::min(0, self.nonzero_magnitude_end());
        #[cfg(debug_assertions)]
        self.check_invariants();
    }

    /// Zero-pad the number on the left to a particular position,
    /// returning the result.
    ///
    /// Negative numbers have no effect.
    ///
    /// Also see [`FixedDecimal::with_max_position()`].
    ///
    /// # Examples
    ///
    /// ```
    /// use fixed_decimal::FixedDecimal;
    ///
    /// let mut dec = FixedDecimal::from(42);
    /// assert_eq!("42", dec.to_string());
    /// assert_eq!("0042", dec.clone().padded_start(4).to_string());
    ///
    /// assert_eq!("042", dec.clone().padded_start(3).to_string());
    ///
    /// assert_eq!("42", dec.clone().padded_start(2).to_string());
    ///
    /// assert_eq!("42", dec.clone().padded_start(1).to_string());
    /// ```
    pub fn padded_start(mut self, position: i16) -> Self {
        self.pad_start(position);
        self
    }

    /// Zero-pad the number on the left to a particular position.
    ///
    /// Negative numbers have no effect.
    ///
    /// Also see [`FixedDecimal::set_max_position()`].
    ///
    /// # Examples
    ///
    /// ```
    /// use fixed_decimal::FixedDecimal;
    ///
    /// let mut dec = FixedDecimal::from(42);
    /// assert_eq!("42", dec.to_string());
    ///
    /// dec.pad_start(4);
    /// assert_eq!("0042", dec.to_string());
    ///
    /// dec.pad_start(3);
    /// assert_eq!("042", dec.to_string());
    ///
    /// dec.pad_start(2);
    /// assert_eq!("42", dec.to_string());
    ///
    /// dec.pad_start(1);
    /// assert_eq!("42", dec.to_string());
    /// ```
    pub fn pad_start(&mut self, position: i16) {
        if position <= 0 {
            return;
        }
        let mut magnitude = position - 1;
        // Do not truncate nonzero digits
        if magnitude <= self.magnitude {
            magnitude = self.magnitude;
        }
        self.upper_magnitude = magnitude;
        #[cfg(debug_assertions)]
        self.check_invariants();
    }

    /// Zero-pad the number on the right to a particular (negative) position. Will truncate
    /// trailing zeros if necessary, but will not truncate other digits, returning the result.
    ///
    /// Positive numbers have no effect.
    ///
    /// Also see [`FixedDecimal::trunced()`].
    ///
    /// # Examples
    ///
    /// ```
    /// use fixed_decimal::FixedDecimal;
    /// # use std::str::FromStr;
    ///
    /// let mut dec = FixedDecimal::from_str("123.456").unwrap();
    /// assert_eq!("123.456", dec.to_string());
    ///
    /// assert_eq!("123.456", dec.clone().padded_end(-1).to_string());
    ///
    /// assert_eq!("123.456", dec.clone().padded_end(-2).to_string());
    ///
    /// assert_eq!("123.456000", dec.clone().padded_end(-6).to_string());
    ///
    /// assert_eq!("123.4560", dec.clone().padded_end(-4).to_string());
    /// ```
    pub fn padded_end(mut self, position: i16) -> Self {
        self.pad_end(position);
        self
    }

    /// Zero-pad the number on the right to a particular (negative) position. Will truncate
    /// trailing zeros if necessary, but will not truncate other digits.
    ///
    /// Positive numbers have no effect.
    ///
    /// Also see [`FixedDecimal::trunc()`].
    ///
    /// # Examples
    ///
    /// ```
    /// use fixed_decimal::FixedDecimal;
    /// # use std::str::FromStr;
    ///
    /// let mut dec = FixedDecimal::from_str("123.456").unwrap();
    /// assert_eq!("123.456", dec.to_string());
    ///
    /// dec.pad_end(-1);
    /// assert_eq!("123.456", dec.to_string());
    ///
    /// dec.pad_end(-2);
    /// assert_eq!("123.456", dec.to_string());
    ///
    /// dec.pad_end(-6);
    /// assert_eq!("123.456000", dec.to_string());
    ///
    /// dec.pad_end(-4);
    /// assert_eq!("123.4560", dec.to_string());
    /// ```
    pub fn pad_end(&mut self, position: i16) {
        if position >= 0 {
            return;
        }
        let bottom_magnitude = self.nonzero_magnitude_end();
        let mut magnitude = position;
        // Do not truncate nonzero digits
        if magnitude >= bottom_magnitude {
            magnitude = bottom_magnitude;
        }
        self.lower_magnitude = magnitude;
        #[cfg(debug_assertions)]
        self.check_invariants();
    }

    /// Truncate the number on the left to a particular position, deleting
    /// digits if necessary, returning the result.
    ///
    /// Also see [`FixedDecimal::padded_start()`].
    ///
    /// # Examples
    ///
    /// ```
    /// use fixed_decimal::FixedDecimal;
    ///
    /// let mut dec = FixedDecimal::from(4235970).multiplied_pow10(-3);
    /// assert_eq!("4235.970", dec.to_string());
    ///
    /// assert_eq!("04235.970", dec.clone().with_max_position(5).to_string());
    ///
    /// assert_eq!("35.970", dec.clone().with_max_position(2).to_string());
    ///
    /// assert_eq!("5.970", dec.clone().with_max_position(1).to_string());
    ///
    /// assert_eq!("0.970", dec.clone().with_max_position(0).to_string());
    ///
    /// assert_eq!("0.070", dec.clone().with_max_position(-1).to_string());
    ///
    /// assert_eq!("0.000", dec.clone().with_max_position(-2).to_string());
    ///
    /// assert_eq!("0.0000", dec.clone().with_max_position(-4).to_string());
    /// ```
    pub fn with_max_position(mut self, position: i16) -> Self {
        self.set_max_position(position);
        self
    }

    /// Truncate the number on the left to a particular position, deleting
    /// digits if necessary.
    ///
    /// Also see [`FixedDecimal::pad_start()`].
    ///
    /// # Examples
    ///
    /// ```
    /// use fixed_decimal::FixedDecimal;
    ///
    /// let mut dec = FixedDecimal::from(4235970).multiplied_pow10(-3);
    /// assert_eq!("4235.970", dec.to_string());
    ///
    /// dec.set_max_position(5);
    /// assert_eq!("04235.970", dec.to_string());
    ///
    /// dec.set_max_position(2);
    /// assert_eq!("35.970", dec.to_string());
    ///
    /// dec.set_max_position(1);
    /// assert_eq!("5.970", dec.to_string());
    ///
    /// dec.set_max_position(0);
    /// assert_eq!("0.970", dec.to_string());
    ///
    /// dec.set_max_position(-1);
    /// assert_eq!("0.070", dec.to_string());
    ///
    /// dec.set_max_position(-2);
    /// assert_eq!("0.000", dec.to_string());
    ///
    /// dec.set_max_position(-4);
    /// assert_eq!("0.0000", dec.to_string());
    /// ```
    pub fn set_max_position(&mut self, position: i16) {
        self.lower_magnitude = cmp::min(self.lower_magnitude, position);
        self.upper_magnitude = if position <= 0 { 0 } else { position - 1 };
        if position <= self.nonzero_magnitude_end() {
            self.digits.clear();
            self.magnitude = 0;
            #[cfg(debug_assertions)]
            self.check_invariants();
            return;
        }
        let magnitude = position - 1;
        if self.magnitude >= magnitude {
            let cut = crate::ops::i16_abs_sub(self.magnitude, magnitude) as usize;
            let _ = self.digits.drain(0..cut).count();
            // Count number of leading zeroes
            let extra_zeroes = self.digits.iter().position(|x| *x != 0).unwrap_or(0);
            let _ = self.digits.drain(0..extra_zeroes).count();
            debug_assert!(!self.digits.is_empty());
            self.magnitude = crate::ops::i16_sub_unsigned(magnitude, extra_zeroes as u16);
        }
        #[cfg(debug_assertions)]
        self.check_invariants();
    }

    /// Increments the digits by 1. if the digits are empty, it will add
    /// an element with value 1. If there are some trailing zeros,
    /// it will be reomved from `self.digits`.
    fn increment_abs_by_one(&mut self) -> Result<(), Error> {
        for (zero_count, digit) in self.digits.iter_mut().rev().enumerate() {
            *digit += 1;
            if *digit < 10 {
                self.digits.truncate(self.digits.len() - zero_count);
                #[cfg(debug_assertions)]
                self.check_invariants();
                return Ok(());
            }
        }

        self.digits.clear();

        if self.magnitude == i16::MAX {
            self.magnitude = 0;

            #[cfg(debug_assertions)]
            self.check_invariants();
            return Err(Error::Limit);
        }

        // Still a carry, carry one to the next magnitude.
        self.digits.push(1);
        self.magnitude += 1;

        if self.upper_magnitude < self.magnitude {
            self.upper_magnitude = self.magnitude;
        }

        #[cfg(debug_assertions)]
        self.check_invariants();
        Ok(())
    }

    /// Removes the trailing zeros in `self.digits`
    fn remove_trailing_zeros_from_digits_list(&mut self) {
        let no_of_trailing_zeros = self
            .digits
            .iter()
            .rev()
            .take_while(|&digit| *digit == 0)
            .count();

        self.digits
            .truncate(self.digits.len() - no_of_trailing_zeros);

        if self.digits.is_empty() {
            self.magnitude = 0;
        }

        #[cfg(debug_assertions)]
        self.check_invariants();
    }

    /// Truncate the number on the right to a particular position, deleting
    /// digits if necessary.
    ///
    /// Also see [`FixedDecimal::padded_end()`].
    ///
    /// # Examples
    ///
    /// ```
    /// use fixed_decimal::FixedDecimal;
    /// # use std::str::FromStr;
    ///
    /// let dec = FixedDecimal::from_str("-1.5").unwrap();
    /// assert_eq!("-1", dec.trunced(0).to_string());
    /// let dec = FixedDecimal::from_str("0.4").unwrap();
    /// assert_eq!("0", dec.trunced(0).to_string());
    /// let dec = FixedDecimal::from_str("0.5").unwrap();
    /// assert_eq!("0", dec.trunced(0).to_string());
    /// let dec = FixedDecimal::from_str("0.6").unwrap();
    /// assert_eq!("0", dec.trunced(0).to_string());
    /// let dec = FixedDecimal::from_str("1.5").unwrap();
    /// assert_eq!("1", dec.trunced(0).to_string());
    /// ```
    pub fn trunced(mut self, position: i16) -> Self {
        self.trunc(position);
        self
    }

    /// Truncates the number on the right to a particular position, deleting
    /// digits if necessary.
    ///
    /// Also see [`FixedDecimal::pad_end()`].
    ///
    /// # Examples
    ///
    /// ```
    /// use fixed_decimal::FixedDecimal;
    /// # use std::str::FromStr;
    ///
    /// let mut dec = FixedDecimal::from_str("-1.5").unwrap();
    /// dec.trunc(0);
    /// assert_eq!("-1", dec.to_string());
    /// let mut dec = FixedDecimal::from_str("0.4").unwrap();
    /// dec.trunc(0);
    /// assert_eq!("0", dec.to_string());
    /// let mut dec = FixedDecimal::from_str("0.5").unwrap();
    /// dec.trunc(0);
    /// assert_eq!("0", dec.to_string());
    /// let mut dec = FixedDecimal::from_str("0.6").unwrap();
    /// dec.trunc(0);
    /// assert_eq!("0", dec.to_string());
    /// let mut dec = FixedDecimal::from_str("1.5").unwrap();
    /// dec.trunc(0);
    /// assert_eq!("1", dec.to_string());
    /// ```
    pub fn trunc(&mut self, position: i16) {
        self.lower_magnitude = cmp::min(position, 0);
        if position == i16::MIN {
            // Nothing more to do
            #[cfg(debug_assertions)]
            self.check_invariants();
            return;
        }
        let magnitude = position - 1;
        self.upper_magnitude = cmp::max(self.upper_magnitude, magnitude);

        if magnitude <= self.magnitude {
            self.digits
                .truncate(crate::ops::i16_abs_sub(self.magnitude, magnitude) as usize);
            self.remove_trailing_zeros_from_digits_list();
        } else {
            self.digits.clear();
            self.magnitude = 0;
        }

        #[cfg(debug_assertions)]
        self.check_invariants();
    }

    /// Half Truncates the number on the right to a particular position, deleting
    /// digits if necessary.
    ///
    /// # Examples
    ///
    /// ```
    /// use fixed_decimal::FixedDecimal;
    /// # use std::str::FromStr;
    ///
    /// let mut dec = FixedDecimal::from_str("-1.5").unwrap();
    /// dec.half_trunc(0);
    /// assert_eq!("-1", dec.to_string());
    /// let mut dec = FixedDecimal::from_str("0.4").unwrap();
    /// dec.half_trunc(0);
    /// assert_eq!("0", dec.to_string());
    /// let mut dec = FixedDecimal::from_str("0.5").unwrap();
    /// dec.half_trunc(0);
    /// assert_eq!("0", dec.to_string());
    /// let mut dec = FixedDecimal::from_str("0.6").unwrap();
    /// dec.half_trunc(0);
    /// assert_eq!("1", dec.to_string());
    /// let mut dec = FixedDecimal::from_str("1.5").unwrap();
    /// dec.half_trunc(0);
    /// assert_eq!("1", dec.to_string());
    /// let mut dec = FixedDecimal::from_str("3.954").unwrap();
    /// dec.half_trunc(0);
    /// assert_eq!("4", dec.to_string());
    /// ```
    pub fn half_trunc(&mut self, position: i16) {
        let digit_after_position = self.digit_at_next_positon(position);
        let should_expand = match digit_after_position.cmp(&5) {
            Ordering::Less => false,
            Ordering::Greater => true,
            Ordering::Equal =>
            // NOTE: `digit_after_position` equals 5, this means, position does not equal to `i16::MIN`.
            {
                self.nonzero_magnitude_end() < position - 1
            }
        };

        if should_expand {
            self.expand(position);
        } else {
            self.trunc(position);
        }
    }

    /// Half Truncates the number on the right to a particular position, deleting
    /// digits if necessary.
    ///
    /// # Examples
    ///
    /// ```
    /// use fixed_decimal::FixedDecimal;
    /// # use std::str::FromStr;
    ///
    /// let dec = FixedDecimal::from_str("-1.5").unwrap();
    /// assert_eq!("-1", dec.half_trunced(0).to_string());
    /// let dec = FixedDecimal::from_str("0.4").unwrap();
    /// assert_eq!("0", dec.half_trunced(0).to_string());
    /// let dec = FixedDecimal::from_str("0.5").unwrap();
    /// assert_eq!("0", dec.half_trunced(0).to_string());
    /// let dec = FixedDecimal::from_str("0.6").unwrap();
    /// assert_eq!("1", dec.half_trunced(0).to_string());
    /// let dec = FixedDecimal::from_str("1.5").unwrap();
    /// assert_eq!("1", dec.half_trunced(0).to_string());
    /// let dec = FixedDecimal::from_str("3.954").unwrap();
    /// assert_eq!("4", dec.half_trunced(0).to_string());
    /// ```
    pub fn half_trunced(mut self, position: i16) -> Self {
        self.half_trunc(position);
        self
    }

    /// Take the expand of the number at a particular position.
    ///
    /// # Examples
    ///
    /// ```
    /// use fixed_decimal::FixedDecimal;
    /// # use std::str::FromStr;
    ///
    /// let mut dec = FixedDecimal::from_str("-1.5").unwrap();
    /// dec.expand(0);
    /// assert_eq!("-2", dec.to_string());
    /// let mut dec = FixedDecimal::from_str("0.4").unwrap();
    /// dec.expand(0);
    /// assert_eq!("1", dec.to_string());
    /// let mut dec = FixedDecimal::from_str("0.5").unwrap();
    /// dec.expand(0);
    /// assert_eq!("1", dec.to_string());
    /// let mut dec = FixedDecimal::from_str("0.6").unwrap();
    /// dec.expand(0);
    /// assert_eq!("1", dec.to_string());
    /// let mut dec = FixedDecimal::from_str("1.5").unwrap();
    /// dec.expand(0);
    /// assert_eq!("2", dec.to_string());
    /// ```
    pub fn expand(&mut self, position: i16) {
        let before_truncate_is_zero = self.is_zero();
        let before_truncate_bottom_magnitude = self.nonzero_magnitude_end();
        let before_truncate_magnitude = self.magnitude;
        self.trunc(position);

        if before_truncate_is_zero || position <= before_truncate_bottom_magnitude {
            #[cfg(debug_assertions)]
            self.check_invariants();
            return;
        }

        if position <= before_truncate_magnitude {
            let result = self.increment_abs_by_one();
            if result.is_err() {
                // Do nothing for now.
            }

            #[cfg(debug_assertions)]
            self.check_invariants();
            return;
        }

        debug_assert!(self.digits.is_empty());
        self.digits.push(1);
        self.magnitude = position;
        self.upper_magnitude = cmp::max(self.upper_magnitude, position);

        #[cfg(debug_assertions)]
        self.check_invariants();
    }

    /// Take the expand of the number at a particular position.
    ///
    /// # Examples
    ///
    /// ```
    /// use fixed_decimal::FixedDecimal;
    /// # use std::str::FromStr;
    ///
    /// let dec = FixedDecimal::from_str("-1.5").unwrap();
    /// assert_eq!("-2", dec.expanded(0).to_string());
    /// let dec = FixedDecimal::from_str("0.4").unwrap();
    /// assert_eq!("1", dec.expanded(0).to_string());
    /// let dec = FixedDecimal::from_str("0.5").unwrap();
    /// assert_eq!("1", dec.expanded(0).to_string());
    /// let dec = FixedDecimal::from_str("0.6").unwrap();
    /// assert_eq!("1", dec.expanded(0).to_string());
    /// let dec = FixedDecimal::from_str("1.5").unwrap();
    /// assert_eq!("2", dec.expanded(0).to_string());
    /// ```
    pub fn expanded(mut self, position: i16) -> Self {
        self.expand(position);
        self
    }

    /// Take the half expand of the number at a particular position.
    ///
    /// # Examples
    ///
    /// ```
    /// use fixed_decimal::FixedDecimal;
    /// # use std::str::FromStr;
    ///
    /// let mut dec = FixedDecimal::from_str("-1.5").unwrap();
    /// dec.half_expand(0);
    /// assert_eq!("-2", dec.to_string());
    /// let mut dec = FixedDecimal::from_str("0.4").unwrap();
    /// dec.half_expand(0);
    /// assert_eq!("0", dec.to_string());
    /// let mut dec = FixedDecimal::from_str("0.5").unwrap();
    /// dec.half_expand(0);
    /// assert_eq!("1", dec.to_string());
    /// let mut dec = FixedDecimal::from_str("0.6").unwrap();
    /// dec.half_expand(0);
    /// assert_eq!("1", dec.to_string());
    /// let mut dec = FixedDecimal::from_str("1.5").unwrap();
    /// dec.half_expand(0);
    /// assert_eq!("2", dec.to_string());
    /// ```
    pub fn half_expand(&mut self, position: i16) {
        let digit_after_position = self.digit_at_next_positon(position);

        if digit_after_position >= 5 {
            self.expand(position);
        } else {
            self.trunc(position);
        }
    }

    /// Take the half expand of the number at a particular position.
    ///
    /// # Examples
    ///
    /// ```
    /// use fixed_decimal::FixedDecimal;
    /// # use std::str::FromStr;
    ///
    /// let dec = FixedDecimal::from_str("-1.5").unwrap();
    /// assert_eq!("-2", dec.half_expanded(0).to_string());
    /// let dec = FixedDecimal::from_str("0.4").unwrap();
    /// assert_eq!("0", dec.half_expanded(0).to_string());
    /// let dec = FixedDecimal::from_str("0.5").unwrap();
    /// assert_eq!("1", dec.half_expanded(0).to_string());
    /// let dec = FixedDecimal::from_str("0.6").unwrap();
    /// assert_eq!("1", dec.half_expanded(0).to_string());
    /// let dec = FixedDecimal::from_str("1.5").unwrap();
    /// assert_eq!("2", dec.half_expanded(0).to_string());
    /// ```
    pub fn half_expanded(mut self, position: i16) -> Self {
        self.half_expand(position);
        self
    }

    /// Take the ceiling of the number at a particular position.
    ///
    /// # Examples
    ///
    /// ```
    /// use fixed_decimal::FixedDecimal;
    /// # use std::str::FromStr;
    ///
    /// let mut dec = FixedDecimal::from_str("-1.5").unwrap();
    /// dec.ceil(0);
    /// assert_eq!("-1", dec.to_string());
    /// let mut dec = FixedDecimal::from_str("0.4").unwrap();
    /// dec.ceil(0);
    /// assert_eq!("1", dec.to_string());
    /// let mut dec = FixedDecimal::from_str("0.5").unwrap();
    /// dec.ceil(0);
    /// assert_eq!("1", dec.to_string());
    /// let mut dec = FixedDecimal::from_str("0.6").unwrap();
    /// dec.ceil(0);
    /// assert_eq!("1", dec.to_string());
    /// let mut dec = FixedDecimal::from_str("1.5").unwrap();
    /// dec.ceil(0);
    /// assert_eq!("2", dec.to_string());
    /// ```
    pub fn ceil(&mut self, position: i16) {
        if self.sign == Sign::Negative {
            self.trunc(position);
            return;
        }

        self.expand(position);
    }

    /// Take the ceiling of the number at a particular position.
    ///
    /// # Examples
    ///
    /// ```
    /// use fixed_decimal::FixedDecimal;
    /// # use std::str::FromStr;
    ///
    /// let dec = FixedDecimal::from_str("-1.5").unwrap();
    /// assert_eq!("-1", dec.ceiled(0).to_string());
    /// let dec = FixedDecimal::from_str("0.4").unwrap();
    /// assert_eq!("1", dec.ceiled(0).to_string());
    /// let dec = FixedDecimal::from_str("0.5").unwrap();
    /// assert_eq!("1", dec.ceiled(0).to_string());
    /// let dec = FixedDecimal::from_str("0.6").unwrap();
    /// assert_eq!("1", dec.ceiled(0).to_string());
    /// let dec = FixedDecimal::from_str("1.5").unwrap();
    /// assert_eq!("2", dec.ceiled(0).to_string());
    /// ```
    pub fn ceiled(mut self, position: i16) -> Self {
        self.ceil(position);
        self
    }

    /// Take the half ceiling of the number at a particular position.
    ///
    /// # Examples
    ///
    /// ```
    /// use fixed_decimal::FixedDecimal;
    /// # use std::str::FromStr;
    ///
    /// let mut dec = FixedDecimal::from_str("-1.5").unwrap();
    /// dec.half_ceil(0);
    /// assert_eq!("-1", dec.to_string());
    /// let mut dec = FixedDecimal::from_str("0.4").unwrap();
    /// dec.half_ceil(0);
    /// assert_eq!("0", dec.to_string());
    /// let mut dec = FixedDecimal::from_str("0.5").unwrap();
    /// dec.half_ceil(0);
    /// assert_eq!("1", dec.to_string());
    /// let mut dec = FixedDecimal::from_str("0.6").unwrap();
    /// dec.half_ceil(0);
    /// assert_eq!("1", dec.to_string());
    /// let mut dec = FixedDecimal::from_str("1.5").unwrap();
    /// dec.half_ceil(0);
    /// assert_eq!("2", dec.to_string());
    /// ```
    pub fn half_ceil(&mut self, position: i16) {
        if self.sign == Sign::Negative {
            self.half_trunc(position);
            return;
        }

        self.half_expand(position);
    }

    /// Take the half ceiling of the number at a particular position.
    ///
    /// # Examples
    ///
    /// ```
    /// use fixed_decimal::FixedDecimal;
    /// # use std::str::FromStr;
    ///
    /// let dec = FixedDecimal::from_str("-1.5").unwrap();
    /// assert_eq!("-1", dec.half_ceiled(0).to_string());
    /// let dec = FixedDecimal::from_str("0.4").unwrap();
    /// assert_eq!("0", dec.half_ceiled(0).to_string());
    /// let dec = FixedDecimal::from_str("0.5").unwrap();
    /// assert_eq!("1", dec.half_ceiled(0).to_string());
    /// let dec = FixedDecimal::from_str("0.6").unwrap();
    /// assert_eq!("1", dec.half_ceiled(0).to_string());
    /// let dec = FixedDecimal::from_str("1.5").unwrap();
    /// assert_eq!("2", dec.half_ceiled(0).to_string());
    /// ```
    pub fn half_ceiled(mut self, position: i16) -> Self {
        self.half_ceil(position);
        self
    }

    /// Take the floor of the number at a particular position.
    ///
    /// # Examples
    ///
    /// ```
    /// use fixed_decimal::FixedDecimal;
    /// # use std::str::FromStr;
    ///
    /// let mut dec = FixedDecimal::from_str("-1.5").unwrap();
    /// dec.floor(0);
    /// assert_eq!("-2", dec.to_string());
    /// let mut dec = FixedDecimal::from_str("0.4").unwrap();
    /// dec.floor(0);
    /// assert_eq!("0", dec.to_string());
    /// let mut dec = FixedDecimal::from_str("0.5").unwrap();
    /// dec.floor(0);
    /// assert_eq!("0", dec.to_string());
    /// let mut dec = FixedDecimal::from_str("0.6").unwrap();
    /// dec.floor(0);
    /// assert_eq!("0", dec.to_string());
    /// let mut dec = FixedDecimal::from_str("1.5").unwrap();
    /// dec.floor(0);
    /// assert_eq!("1", dec.to_string());
    /// ```
    pub fn floor(&mut self, position: i16) {
        if self.sign == Sign::Negative {
            self.expand(position);
            return;
        }

        self.trunc(position);
    }

    /// Take the floor of the number at a particular position.
    ///
    /// # Examples
    ///
    /// ```
    /// use fixed_decimal::FixedDecimal;
    /// # use std::str::FromStr;
    ///
    /// let dec = FixedDecimal::from_str("-1.5").unwrap();
    /// assert_eq!("-2", dec.floored(0).to_string());
    /// let dec = FixedDecimal::from_str("0.4").unwrap();
    /// assert_eq!("0", dec.floored(0).to_string());
    /// let dec = FixedDecimal::from_str("0.5").unwrap();
    /// assert_eq!("0", dec.floored(0).to_string());
    /// let dec = FixedDecimal::from_str("0.6").unwrap();
    /// assert_eq!("0", dec.floored(0).to_string());
    /// let dec = FixedDecimal::from_str("1.5").unwrap();
    /// assert_eq!("1", dec.floored(0).to_string());
    /// ```
    pub fn floored(mut self, position: i16) -> Self {
        self.floor(position);
        self
    }

    /// Take the half floor of the number at a particular position.
    ///
    /// # Examples
    ///
    /// ```
    /// use fixed_decimal::FixedDecimal;
    /// # use std::str::FromStr;
    ///
    /// let mut dec = FixedDecimal::from_str("-1.5").unwrap();
    /// dec.half_floor(0);
    /// assert_eq!("-2", dec.to_string());
    /// let mut dec = FixedDecimal::from_str("0.4").unwrap();
    /// dec.half_floor(0);
    /// assert_eq!("0", dec.to_string());
    /// let mut dec = FixedDecimal::from_str("0.5").unwrap();
    /// dec.half_floor(0);
    /// assert_eq!("0", dec.to_string());
    /// let mut dec = FixedDecimal::from_str("0.6").unwrap();
    /// dec.half_floor(0);
    /// assert_eq!("1", dec.to_string());
    /// let mut dec = FixedDecimal::from_str("1.5").unwrap();
    /// dec.half_floor(0);
    /// assert_eq!("1", dec.to_string());
    /// ```
    pub fn half_floor(&mut self, position: i16) {
        if self.sign == Sign::Negative {
            self.half_expand(position);
            return;
        }

        self.half_trunc(position);
    }

    /// Take the half floor of the number at a particular position.
    ///
    /// # Examples
    ///
    /// ```
    /// use fixed_decimal::FixedDecimal;
    /// # use std::str::FromStr;
    ///
    /// let dec = FixedDecimal::from_str("-1.5").unwrap();
    /// assert_eq!("-2", dec.half_floored(0).to_string());
    /// let dec = FixedDecimal::from_str("0.4").unwrap();
    /// assert_eq!("0", dec.half_floored(0).to_string());
    /// let dec = FixedDecimal::from_str("0.5").unwrap();
    /// assert_eq!("0", dec.half_floored(0).to_string());
    /// let dec = FixedDecimal::from_str("0.6").unwrap();
    /// assert_eq!("1", dec.half_floored(0).to_string());
    /// let dec = FixedDecimal::from_str("1.5").unwrap();
    /// assert_eq!("1", dec.half_floored(0).to_string());
    /// ```
    pub fn half_floored(mut self, position: i16) -> Self {
        self.half_floor(position);
        self
    }

    /// Take the half even of the number at a particular position.
    ///
    /// # Examples
    ///
    /// ```
    /// use fixed_decimal::FixedDecimal;
    /// # use std::str::FromStr;
    ///
    /// let mut dec = FixedDecimal::from_str("-1.5").unwrap();
    /// dec.half_even(0);
    /// assert_eq!("-2", dec.to_string());
    /// let mut dec = FixedDecimal::from_str("0.4").unwrap();
    /// dec.half_even(0);
    /// assert_eq!("0", dec.to_string());
    /// let mut dec = FixedDecimal::from_str("0.5").unwrap();
    /// dec.half_even(0);
    /// assert_eq!("0", dec.to_string());
    /// let mut dec = FixedDecimal::from_str("0.6").unwrap();
    /// dec.half_even(0);
    /// assert_eq!("1", dec.to_string());
    /// let mut dec = FixedDecimal::from_str("1.5").unwrap();
    /// dec.half_even(0);
    /// assert_eq!("2", dec.to_string());
    /// ```
    pub fn half_even(&mut self, position: i16) {
        let digit_after_position = self.digit_at_next_positon(position);
        let should_expand = match digit_after_position.cmp(&5) {
            Ordering::Less => false,
            Ordering::Greater => true,
            Ordering::Equal => {
                // NOTE: `digit_after_position` equals to 5, this means that positon does not equal i16::MIN.
                if self.nonzero_magnitude_end() < position - 1 {
                    true
                } else {
                    self.digit_at(position) % 2 != 0
                }
            }
        };

        if should_expand {
            self.expand(position);
        } else {
            self.trunc(position);
        }
    }

    /// Take the half even of the number at a particular position.
    ///
    /// # Examples
    ///
    /// ```
    /// use fixed_decimal::FixedDecimal;
    /// # use std::str::FromStr;
    ///
    /// let dec = FixedDecimal::from_str("-1.5").unwrap();
    /// assert_eq!("-2", dec.half_evened(0).to_string());
    /// let dec = FixedDecimal::from_str("0.4").unwrap();
    /// assert_eq!("0", dec.half_evened(0).to_string());
    /// let dec = FixedDecimal::from_str("0.5").unwrap();
    /// assert_eq!("0", dec.half_evened(0).to_string());
    /// let dec = FixedDecimal::from_str("0.6").unwrap();
    /// assert_eq!("1", dec.half_evened(0).to_string());
    /// let dec = FixedDecimal::from_str("1.5").unwrap();
    /// assert_eq!("2", dec.half_evened(0).to_string());
    /// ```
    pub fn half_evened(mut self, position: i16) -> Self {
        self.half_even(position);
        self
    }

    /// Concatenate another `FixedDecimal` into the end of this `FixedDecimal`.
    ///
    /// All nonzero digits in `other` must have lower magnitude than nonzero digits in `self`.
    /// If the two decimals represent overlapping ranges of magnitudes, an `Err` is returned,
    /// passing ownership of `other` back to the caller.
    ///
    /// The magnitude range of `self` will be increased if `other` covers a larger range.
    ///
    /// # Examples
    ///
    /// ```
    /// use fixed_decimal::FixedDecimal;
    ///
    /// let integer = FixedDecimal::from(123);
    /// let fraction = FixedDecimal::from(456).multiplied_pow10(-3);
    ///
    /// let result = integer.concatenated_end(fraction).expect("nonoverlapping");
    ///
    /// assert_eq!("123.456", result.to_string());
    /// ```
    pub fn concatenated_end(mut self, other: FixedDecimal) -> Result<Self, FixedDecimal> {
        match self.concatenate_end(other) {
            Ok(()) => Ok(self),
            Err(err) => Err(err),
        }
    }

    /// Concatenate another `FixedDecimal` into the end of this `FixedDecimal`.
    ///
    /// All nonzero digits in `other` must have lower magnitude than nonzero digits in `self`.
    /// If the two decimals represent overlapping ranges of magnitudes, an `Err` is returned,
    /// passing ownership of `other` back to the caller.
    ///
    /// The magnitude range of `self` will be increased if `other` covers a larger range.
    ///
    /// # Examples
    ///
    /// ```
    /// use fixed_decimal::FixedDecimal;
    ///
    /// let mut integer = FixedDecimal::from(123);
    /// let fraction = FixedDecimal::from(456).multiplied_pow10(-3);
    ///
    /// integer.concatenate_end(fraction);
    ///
    /// assert_eq!("123.456", integer.to_string());
    /// ```
    pub fn concatenate_end(&mut self, other: FixedDecimal) -> Result<(), FixedDecimal> {
        let self_right = self.nonzero_magnitude_end();
        let other_left = other.nonzero_magnitude_start();
        if self.is_zero() {
            // Operation will succeed. We can move the digits into self.
            self.digits = other.digits;
            self.magnitude = other.magnitude;
        } else if other.is_zero() {
            // No changes to the digits are necessary.
        } else if self_right <= other_left {
            // Illegal: `other` is not to the right of `self`
            return Err(other);
        } else {
            // Append the digits from other to the end of self
            let inner_zeroes = crate::ops::i16_abs_sub(self_right, other_left) as usize - 1;
            self.append_digits(inner_zeroes, &other.digits);
        }
        self.upper_magnitude = cmp::max(self.upper_magnitude, other.upper_magnitude);
        self.lower_magnitude = cmp::min(self.lower_magnitude, other.lower_magnitude);
        #[cfg(debug_assertions)]
        self.check_invariants();
        Ok(())
    }

    /// Appends a slice of digits to the end of `self.digits` with optional inner zeroes.
    ///
    /// This function does not check invariants.
    fn append_digits(&mut self, inner_zeroes: usize, new_digits: &[u8]) {
        let new_len = self.digits.len() + inner_zeroes;
        self.digits.resize_with(new_len, || 0);
        self.digits.extend_from_slice(new_digits);
    }

    /// Assert that the invariants among struct fields are enforced. Returns true if all are okay.
    /// Call this in any method that mutates the struct fields.
    ///
    /// Example: `debug_assert!(self.check_invariants())`
    #[cfg(debug_assertions)]
    #[allow(clippy::indexing_slicing)]
    fn check_invariants(&self) {
        // magnitude invariants:
        debug_assert!(
            self.upper_magnitude >= self.magnitude,
            "Upper magnitude too small {:?}",
            self
        );
        debug_assert!(
            self.lower_magnitude <= self.magnitude,
            "Lower magnitude too large {:?}",
            self
        );
        debug_assert!(
            self.upper_magnitude >= 0,
            "Upper magnitude below zero {:?}",
            self
        );
        debug_assert!(
            self.lower_magnitude <= 0,
            "Lower magnitude above zero {:?}",
            self
        );

        // digits invariants:
        debug_assert!(
            self.digits.len() <= (self.magnitude as i32 - self.lower_magnitude as i32 + 1) as usize,
            "{:?}",
            self
        );
        if !self.digits.is_empty() {
            debug_assert_ne!(self.digits[0], 0, "Starts with a zero {self:?}");
            debug_assert_ne!(
                self.digits[self.digits.len() - 1],
                0,
                "Ends with a zero {self:?}",
            );
        } else {
            debug_assert_eq!(self.magnitude, 0);
        }
    }
}

/// Render the `FixedDecimal` as a string of ASCII digits with a possible decimal point.
///
/// # Examples
///
/// ```
/// # use fixed_decimal::FixedDecimal;
/// # use writeable::assert_writeable_eq;
/// #
/// assert_writeable_eq!(FixedDecimal::from(42), "42");
/// ```
impl writeable::Writeable for FixedDecimal {
    fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
        match self.sign {
            Sign::Negative => sink.write_char('-')?,
            Sign::Positive => sink.write_char('+')?,
            Sign::None => (),
        }
        for m in self.magnitude_range().rev() {
            if m == -1 {
                sink.write_char('.')?;
            }
            let d = self.digit_at(m);
            sink.write_char((b'0' + d) as char)?;
        }
        Ok(())
    }

    fn writeable_length_hint(&self) -> writeable::LengthHint {
        writeable::LengthHint::exact(1)
            + ((self.upper_magnitude as i32 - self.lower_magnitude as i32) as usize)
            + (if self.sign == Sign::None { 0 } else { 1 })
            + (if self.lower_magnitude < 0 { 1 } else { 0 })
    }
}

writeable::impl_display_with_writeable!(FixedDecimal);

impl FromStr for FixedDecimal {
    type Err = Error;
    fn from_str(input_str: &str) -> Result<Self, Self::Err> {
        Self::try_from(input_str.as_bytes())
    }
}

impl TryFrom<&[u8]> for FixedDecimal {
    type Error = Error;
    fn try_from(input_str: &[u8]) -> Result<Self, Self::Error> {
        // input_str: the input string
        // no_sign_str: the input string when the sign is removed from it
        if input_str.is_empty() {
            return Err(Error::Syntax);
        }
        #[allow(clippy::indexing_slicing)] // The string is not empty.
        let sign = match input_str[0] {
            b'-' => Sign::Negative,
            b'+' => Sign::Positive,
            _ => Sign::None,
        };
        #[allow(clippy::indexing_slicing)] // The string is not empty.
        let no_sign_str = if sign == Sign::None {
            input_str
        } else {
            &input_str[1..]
        };
        if no_sign_str.is_empty() {
            return Err(Error::Syntax);
        }
        // Compute length of each string once and store it, so if you use that multiple times,
        // you don't compute it multiple times
        // has_dot: shows if your input has dot in it
        // has_exponent: shows if your input has an exponent in it
        // dot_index: gives the index of dot (after removing the sign) -- equal to length of
        // the no_sign_str if there is no dot
        // exponent_index: gives the index of exponent (after removing the sign) -- equal to length of
        // the no_sign_str if there is no dot
        let mut has_dot = false;
        let mut has_exponent = false;
        let mut dot_index = no_sign_str.len();
        let mut exponent_index = no_sign_str.len();
        // The following loop computes has_dot, dot_index, and also checks to see if all
        // characters are digits and if you have at most one dot
        // Note: Input of format 111_123 is detected as syntax error here
        // Note: Input starting or ending with a dot is detected as syntax error here (Ex: .123, 123.)
        for (i, c) in no_sign_str.iter().enumerate() {
            if *c == b'.' {
                if has_dot || has_exponent {
                    // multiple dots or dots after the exponent
                    return Err(Error::Syntax);
                }
                dot_index = i;
                has_dot = true;
                // We do support omitting the leading zero,
                // but not trailing decimal points
                if i == no_sign_str.len() - 1 {
                    return Err(Error::Syntax);
                }
            } else if *c == b'e' || *c == b'E' {
                if has_exponent {
                    // multiple exponents
                    return Err(Error::Syntax);
                }
                exponent_index = i;
                has_exponent = true;
                if i == 0 || i == no_sign_str.len() - 1 {
                    return Err(Error::Syntax);
                }
            } else if *c == b'-' {
                // Allow a single minus sign after the exponent
                if has_exponent && exponent_index == i - 1 {
                    continue;
                } else {
                    return Err(Error::Syntax);
                }
            } else if *c < b'0' || *c > b'9' {
                return Err(Error::Syntax);
            }
        }

        // The string without the exponent (or sign)
        // We do the bulk of the calculation on this string,
        // and extract the exponent at the end
        #[allow(clippy::indexing_slicing)] // exponent_index comes from enumerate
        let no_exponent_str = &no_sign_str[..exponent_index];

        // If there was no dot, truncate the dot index
        if dot_index > exponent_index {
            dot_index = exponent_index;
        }

        // defining the output dec here and set its sign
        let mut dec = Self {
            sign,
            ..Default::default()
        };

        // no_dot_str_len: shows length of the string after removing the dot
        let mut no_dot_str_len = no_exponent_str.len();
        if has_dot {
            no_dot_str_len -= 1;
        }

        // Computing DecimalFixed.upper_magnitude
        // We support strings like `0.x` and `.x`. The upper magnitude
        // is always one less than the position of the dot, except in the case where
        // the 0 is omitted; when dot_index = 0. We use saturating_sub to set
        // magnitude to 0 in that case.
        let temp_upper_magnitude = dot_index.saturating_sub(1);
        if temp_upper_magnitude > i16::MAX as usize {
            return Err(Error::Limit);
        }
        dec.upper_magnitude = temp_upper_magnitude as i16;

        // Computing DecimalFixed.lower_magnitude
        // Note: ((i16::MIN as u16) as usize) == 32768
        let temp_lower_magnitude = no_dot_str_len - dot_index;
        if temp_lower_magnitude > (i16::MIN as u16) as usize {
            return Err(Error::Limit);
        }
        dec.lower_magnitude = (temp_lower_magnitude as i16).wrapping_neg();

        // leftmost_digit: index of the first non-zero digit
        // rightmost_digit: index of the first element after the last non-zero digit
        // Example:
        //     input string    leftmost_digit     rightmost_digit
        //     00123000              2                  5
        //     0.0123000             3                  6
        //     001.23000             2                  6
        //     001230.00             2                  5
        // Compute leftmost_digit
        let leftmost_digit = no_exponent_str
            .iter()
            .position(|c| *c != b'.' && *c != b'0');

        // If the input only has zeros (like 000, 00.0, -00.000) we handle the situation here
        // by returning the dec and don't running the rest of the code
        let leftmost_digit = if let Some(leftmost_digit) = leftmost_digit {
            leftmost_digit
        } else {
            return Ok(dec);
        };

        // Else if the input is not all zeros, we compute its magnitude:
        // Note that we can cast with "as" here because lower and upper magnitude have been checked already
        let mut temp_magnitude = ((dot_index as i32) - (leftmost_digit as i32) - 1i32) as i16;
        if dot_index < leftmost_digit {
            temp_magnitude += 1;
        }
        dec.magnitude = temp_magnitude;

        // Compute the index where the rightmost_digit ends
        let rightmost_digit_end = no_exponent_str
            .iter()
            .rposition(|c| *c != b'.' && *c != b'0')
            .map(|p| p + 1)
            .unwrap_or(no_exponent_str.len());

        // digits_str_len: shows the length of digits (Ex. 0012.8900 --> 4)
        let mut digits_str_len = rightmost_digit_end - leftmost_digit;
        if leftmost_digit < dot_index && dot_index < rightmost_digit_end {
            digits_str_len -= 1;
        }

        // Constructing DecimalFixed.digits
        #[allow(clippy::indexing_slicing)]
        // leftmost_digit  and rightmost_digit_end come from Iterator::position and Iterator::rposition.
        let v: SmallVec<[u8; 8]> = no_exponent_str[leftmost_digit..rightmost_digit_end]
            .iter()
            .filter(|c| **c != b'.')
            .map(|c| c - b'0')
            .collect();

        let v_len = v.len();
        debug_assert_eq!(v_len, digits_str_len);
        dec.digits = v;

        // Extract the exponent part
        if has_exponent {
            let mut pow = 0;
            let mut pos_neg = 1;
            #[allow(clippy::indexing_slicing)]
            // exponent_index is exist, then exponent_index + 1 will equal at most no_sign_str.len().
            for digit in &no_sign_str[exponent_index + 1..] {
                if *digit == b'-' {
                    pos_neg = -1;
                    continue;
                }
                pow *= 10;
                pow += (digit - b'0') as i16;
            }

            dec.multiply_pow10(pos_neg * pow);

            // Clean up magnitude after multiplication
            if dec.magnitude > 0 {
                dec.upper_magnitude = dec.magnitude;
            }
            let neg_mag = dec.magnitude - dec.digits.len() as i16 + 1;
            if neg_mag < 0 {
                dec.lower_magnitude = neg_mag;
            }
        }

        Ok(dec)
    }
}

/// Specifies the precision of a floating point value when constructing a FixedDecimal.
///
/// IEEE 754 is a representation of a point on the number line. On the other hand, FixedDecimal
/// specifies not only the point on the number line but also the precision of the number to a
/// specific power of 10. This enum augments a floating-point value with the additional
/// information required by FixedDecimal.
#[non_exhaustive]
#[cfg(feature = "ryu")]
#[derive(Debug, Clone, Copy)]
pub enum DoublePrecision {
    /// Specify that the floating point number is integer-valued.
    ///
    /// If the floating point is not actually integer-valued, an error will be returned.
    Integer,

    /// Specify that the floating point number is precise to a specific power of 10.
    /// The number may be rounded or trailing zeros may be added as necessary.
    Magnitude(i16),

    /// Specify that the floating point number is precise to a specific number of significant digits.
    /// The number may be rounded or trailing zeros may be added as necessary.
    ///
    /// The number requested may not be zero
    SignificantDigits(u8),

    /// Specify that the floating point number is precise to the maximum representable by IEEE.
    ///
    /// This results in a FixedDecimal having enough digits to recover the original floating point
    /// value, with no trailing zeros.
    Floating,
}

#[cfg(feature = "ryu")]
impl FixedDecimal {
    /// Construct a [`FixedDecimal`] from an f64.
    ///
    /// Since f64 values do not carry a notion of their precision, the second argument to this
    /// function specifies the type of precision associated with the f64. For more information,
    /// see [`DoublePrecision`].
    ///
    /// This function uses `ryu`, which is an efficient double-to-string algorithm, but other
    /// implementations may yield higher performance; for more details, see
    /// [icu4x#166](https://github.com/unicode-org/icu4x/issues/166).
    ///
    /// This function can be made available with the `"ryu"` Cargo feature.
    ///
    /// ```rust
    /// use fixed_decimal::{DoublePrecision, FixedDecimal};
    /// use writeable::assert_writeable_eq;
    ///
    /// let decimal =
    ///     FixedDecimal::try_from_f64(-5.1, DoublePrecision::Magnitude(-2))
    ///         .expect("Finite quantity with limited precision");
    /// assert_writeable_eq!(decimal, "-5.10");
    ///
    /// let decimal =
    ///     FixedDecimal::try_from_f64(0.012345678, DoublePrecision::Floating)
    ///         .expect("Finite quantity");
    /// assert_writeable_eq!(decimal, "0.012345678");
    ///
    /// let decimal =
    ///     FixedDecimal::try_from_f64(12345678000., DoublePrecision::Integer)
    ///         .expect("Finite, integer-valued quantity");
    /// assert_writeable_eq!(decimal, "12345678000");
    /// ```
    ///
    /// Negative zero is supported.
    ///
    /// ```rust
    /// use fixed_decimal::{DoublePrecision, FixedDecimal};
    /// use writeable::assert_writeable_eq;
    ///
    /// // IEEE 754 for floating point defines the sign bit separate
    /// // from the mantissa and exponent, allowing for -0.
    /// let negative_zero =
    ///     FixedDecimal::try_from_f64(-0.0, DoublePrecision::Integer)
    ///         .expect("Negative zero");
    /// assert_writeable_eq!(negative_zero, "-0");
    /// ```
    pub fn try_from_f64(float: f64, precision: DoublePrecision) -> Result<Self, Error> {
        let mut decimal = Self::new_from_f64_raw(float)?;
        let n_digits = decimal.digits.len();
        // magnitude of the lowest digit in self.digits
        let lowest_magnitude = decimal.magnitude - n_digits as i16 + 1;
        // ryū will usually tack on a `.0` to integers which gets included when parsing.
        // Explicitly remove it before doing anything else
        if lowest_magnitude >= 0 && decimal.lower_magnitude < 0 {
            decimal.lower_magnitude = 0;
        }
        match precision {
            DoublePrecision::Floating => (),
            DoublePrecision::Integer => {
                if lowest_magnitude < 0 {
                    return Err(Error::Limit);
                }
            }
            DoublePrecision::Magnitude(mag) => {
                decimal.half_even(mag);
            }
            DoublePrecision::SignificantDigits(sig) => {
                if sig == 0 {
                    return Err(Error::Limit);
                }

                let position = decimal.magnitude - (sig as i16) + 1;
                let old_magnitude = decimal.magnitude;
                decimal.half_even(position);

                // This means the significant digits has been increased by 1.
                if decimal.magnitude > old_magnitude {
                    decimal.lower_magnitude = cmp::min(0, position + 1);
                }
            }
        }
        #[cfg(debug_assertions)]
        decimal.check_invariants();
        Ok(decimal)
    }

    /// Internal function for parsing directly from floats using ryū
    fn new_from_f64_raw(float: f64) -> Result<Self, Error> {
        if !float.is_finite() {
            return Err(Error::Limit);
        }
        // note: this does not heap allocate
        let mut buf = ryu::Buffer::new();
        let formatted = buf.format_finite(float);
        Self::from_str(formatted)
    }
}

#[cfg(feature = "ryu")]
#[test]
fn test_float() {
    #[derive(Debug)]
    struct TestCase {
        pub input: f64,
        pub precision: DoublePrecision,
        pub expected: &'static str,
    }
    let cases = [
        TestCase {
            input: 1.234567,
            precision: DoublePrecision::Floating,
            expected: "1.234567",
        },
        TestCase {
            input: 888999.,
            precision: DoublePrecision::Floating,
            expected: "888999",
        },
        // HalfExpand tests
        TestCase {
            input: 1.234567,
            precision: DoublePrecision::Magnitude(-2),
            expected: "1.23",
        },
        TestCase {
            input: 1.235567,
            precision: DoublePrecision::Magnitude(-2),
            expected: "1.24",
        },
        TestCase {
            input: 1.2002,
            precision: DoublePrecision::Magnitude(-3),
            expected: "1.200",
        },
        TestCase {
            input: 888999.,
            precision: DoublePrecision::Magnitude(2),
            expected: "889000",
        },
        TestCase {
            input: 888999.,
            precision: DoublePrecision::Magnitude(4),
            expected: "890000",
        },
        TestCase {
            input: 0.9,
            precision: DoublePrecision::Magnitude(0),
            expected: "1",
        },
        TestCase {
            input: 0.9,
            precision: DoublePrecision::Magnitude(2),
            expected: "00",
        },
        TestCase {
            input: 0.009,
            precision: DoublePrecision::Magnitude(-2),
            expected: "0.01",
        },
        TestCase {
            input: 0.009,
            precision: DoublePrecision::Magnitude(-1),
            expected: "0.0",
        },
        TestCase {
            input: 0.009,
            precision: DoublePrecision::Magnitude(0),
            expected: "0",
        },
        TestCase {
            input: 0.0000009,
            precision: DoublePrecision::Magnitude(0),
            expected: "0",
        },
        TestCase {
            input: 0.0000009,
            precision: DoublePrecision::Magnitude(-7),
            expected: "0.0000009",
        },
        TestCase {
            input: 0.0000009,
            precision: DoublePrecision::Magnitude(-6),
            expected: "0.000001",
        },
        TestCase {
            input: 1.234567,
            precision: DoublePrecision::SignificantDigits(1),
            expected: "1",
        },
        TestCase {
            input: 1.234567,
            precision: DoublePrecision::SignificantDigits(2),
            expected: "1.2",
        },
        TestCase {
            input: 1.234567,
            precision: DoublePrecision::SignificantDigits(4),
            expected: "1.235",
        },
        TestCase {
            input: 1.234567,
            precision: DoublePrecision::SignificantDigits(10),
            expected: "1.234567000",
        },
        TestCase {
            input: 888999.,
            precision: DoublePrecision::SignificantDigits(1),
            expected: "900000",
        },
        TestCase {
            input: 888999.,
            precision: DoublePrecision::SignificantDigits(2),
            expected: "890000",
        },
        TestCase {
            input: 888999.,
            precision: DoublePrecision::SignificantDigits(4),
            expected: "889000",
        },
        TestCase {
            input: 988999.,
            precision: DoublePrecision::SignificantDigits(1),
            expected: "1000000",
        },
        TestCase {
            input: 99888.,
            precision: DoublePrecision::SignificantDigits(1),
            expected: "100000",
        },
        TestCase {
            input: 99888.,
            precision: DoublePrecision::SignificantDigits(2),
            expected: "100000",
        },
        TestCase {
            input: 99888.,
            precision: DoublePrecision::SignificantDigits(3),
            expected: "99900",
        },
        TestCase {
            input: 0.0099,
            precision: DoublePrecision::SignificantDigits(1),
            expected: "0.01",
        },
        TestCase {
            input: 9.9888,
            precision: DoublePrecision::SignificantDigits(1),
            expected: "10",
        },
        TestCase {
            input: 9.9888,
            precision: DoublePrecision::SignificantDigits(2),
            expected: "10",
        },
        TestCase {
            input: 99888.0,
            precision: DoublePrecision::SignificantDigits(1),
            expected: "100000",
        },
        TestCase {
            input: 99888.0,
            precision: DoublePrecision::SignificantDigits(2),
            expected: "100000",
        },
        TestCase {
            input: 9.9888,
            precision: DoublePrecision::SignificantDigits(3),
            expected: "9.99",
        },
    ];

    for case in &cases {
        let dec = FixedDecimal::try_from_f64(case.input, case.precision).unwrap();
        writeable::assert_writeable_eq!(dec, case.expected, "{:?}", case);
    }
}

#[test]
fn test_basic() {
    #[derive(Debug)]
    struct TestCase {
        pub input: isize,
        pub delta: i16,
        pub expected: &'static str,
    }
    let cases = [
        TestCase {
            input: 51423,
            delta: 0,
            expected: "51423",
        },
        TestCase {
            input: 51423,
            delta: -2,
            expected: "514.23",
        },
        TestCase {
            input: 51423,
            delta: -5,
            expected: "0.51423",
        },
        TestCase {
            input: 51423,
            delta: -8,
            expected: "0.00051423",
        },
        TestCase {
            input: 51423,
            delta: 3,
            expected: "51423000",
        },
        TestCase {
            input: 0,
            delta: 0,
            expected: "0",
        },
        TestCase {
            input: 0,
            delta: -2,
            expected: "0.00",
        },
        TestCase {
            input: 0,
            delta: 3,
            expected: "0000",
        },
        TestCase {
            input: 500,
            delta: 0,
            expected: "500",
        },
        TestCase {
            input: 500,
            delta: -1,
            expected: "50.0",
        },
        TestCase {
            input: 500,
            delta: -2,
            expected: "5.00",
        },
        TestCase {
            input: 500,
            delta: -3,
            expected: "0.500",
        },
        TestCase {
            input: 500,
            delta: -4,
            expected: "0.0500",
        },
        TestCase {
            input: 500,
            delta: 3,
            expected: "500000",
        },
        TestCase {
            input: -123,
            delta: 0,
            expected: "-123",
        },
        TestCase {
            input: -123,
            delta: -2,
            expected: "-1.23",
        },
        TestCase {
            input: -123,
            delta: -5,
            expected: "-0.00123",
        },
        TestCase {
            input: -123,
            delta: 3,
            expected: "-123000",
        },
    ];
    for cas in &cases {
        let mut dec: FixedDecimal = cas.input.into();
        // println!("{}", cas.input + 0.01);
        dec.multiply_pow10(cas.delta);
        writeable::assert_writeable_eq!(dec, cas.expected, "{:?}", cas);
    }
}

#[test]
fn test_from_str() {
    #[derive(Debug)]
    struct TestCase {
        pub input_str: &'static str,
        /// The output str, None for roundtrip
        pub output_str: Option<&'static str>,
        /// [upper magnitude, upper nonzero magnitude, lower nonzero magnitude, lower magnitude]
        pub magnitudes: [i16; 4],
    }
    let cases = [
        TestCase {
            input_str: "-00123400",
            output_str: None,
            magnitudes: [7, 5, 2, 0],
        },
        TestCase {
            input_str: "+00123400",
            output_str: None,
            magnitudes: [7, 5, 2, 0],
        },
        TestCase {
            input_str: "0.0123400",
            output_str: None,
            magnitudes: [0, -2, -5, -7],
        },
        TestCase {
            input_str: "-00.123400",
            output_str: None,
            magnitudes: [1, -1, -4, -6],
        },
        TestCase {
            input_str: "0012.3400",
            output_str: None,
            magnitudes: [3, 1, -2, -4],
        },
        TestCase {
            input_str: "-0012340.0",
            output_str: None,
            magnitudes: [6, 4, 1, -1],
        },
        TestCase {
            input_str: "1234",
            output_str: None,
            magnitudes: [3, 3, 0, 0],
        },
        TestCase {
            input_str: "0.000000001",
            output_str: None,
            magnitudes: [0, -9, -9, -9],
        },
        TestCase {
            input_str: "0.0000000010",
            output_str: None,
            magnitudes: [0, -9, -9, -10],
        },
        TestCase {
            input_str: "1000000",
            output_str: None,
            magnitudes: [6, 6, 6, 0],
        },
        TestCase {
            input_str: "10000001",
            output_str: None,
            magnitudes: [7, 7, 0, 0],
        },
        TestCase {
            input_str: "123",
            output_str: None,
            magnitudes: [2, 2, 0, 0],
        },
        TestCase {
            input_str: "922337203685477580898230948203840239384.9823094820384023938423424",
            output_str: None,
            magnitudes: [38, 38, -25, -25],
        },
        TestCase {
            input_str: "009223372000.003685477580898230948203840239384000",
            output_str: None,
            magnitudes: [11, 9, -33, -36],
        },
        TestCase {
            input_str: "-009223372000.003685477580898230948203840239384000",
            output_str: None,
            magnitudes: [11, 9, -33, -36],
        },
        TestCase {
            input_str: "0",
            output_str: None,
            magnitudes: [0, 0, 0, 0],
        },
        TestCase {
            input_str: "-0",
            output_str: None,
            magnitudes: [0, 0, 0, 0],
        },
        TestCase {
            input_str: "+0",
            output_str: None,
            magnitudes: [0, 0, 0, 0],
        },
        TestCase {
            input_str: "000",
            output_str: None,
            magnitudes: [2, 0, 0, 0],
        },
        TestCase {
            input_str: "-00.0",
            output_str: None,
            magnitudes: [1, 0, 0, -1],
        },
        // no leading 0 parsing
        TestCase {
            input_str: ".0123400",
            output_str: Some("0.0123400"),
            magnitudes: [0, -2, -5, -7],
        },
        TestCase {
            input_str: ".000000001",
            output_str: Some("0.000000001"),
            magnitudes: [0, -9, -9, -9],
        },
        TestCase {
            input_str: "-.123400",
            output_str: Some("-0.123400"),
            magnitudes: [0, -1, -4, -6],
        },
    ];
    for cas in &cases {
        let fd = FixedDecimal::from_str(cas.input_str).unwrap();
        assert_eq!(
            fd.magnitude_range(),
            cas.magnitudes[3]..=cas.magnitudes[0],
            "{:?}",
            cas
        );
        assert_eq!(fd.nonzero_magnitude_start(), cas.magnitudes[1], "{:?}", cas);
        assert_eq!(fd.nonzero_magnitude_end(), cas.magnitudes[2], "{:?}", cas);
        let input_str_roundtrip = fd.to_string();
        let output_str = cas.output_str.unwrap_or(cas.input_str);
        assert_eq!(output_str, input_str_roundtrip, "{:?}", cas);
    }
}

#[test]
fn test_from_str_scientific() {
    #[derive(Debug)]
    struct TestCase {
        pub input_str: &'static str,
        pub output: &'static str,
    }
    let cases = [
        TestCase {
            input_str: "-5.4e10",
            output: "-54000000000",
        },
        TestCase {
            input_str: "5.4e-2",
            output: "0.054",
        },
        TestCase {
            input_str: "54.1e-2",
            output: "0.541",
        },
        TestCase {
            input_str: "-541e-2",
            output: "-5.41",
        },
        TestCase {
            input_str: "0.009E10",
            output: "90000000",
        },
        TestCase {
            input_str: "-9000E-10",
            output: "-0.0000009",
        },
    ];
    for cas in &cases {
        let input_str_roundtrip = FixedDecimal::from_str(cas.input_str).unwrap().to_string();
        assert_eq!(cas.output, input_str_roundtrip);
    }
}

#[test]
fn test_isize_limits() {
    for num in &[core::isize::MAX, core::isize::MIN] {
        let dec: FixedDecimal = (*num).into();
        let dec_str = dec.to_string();
        assert_eq!(num.to_string(), dec_str);
        assert_eq!(dec, FixedDecimal::from_str(&dec_str).unwrap());
        writeable::assert_writeable_eq!(dec, dec_str);
    }
}

#[test]
fn test_ui128_limits() {
    for num in &[core::i128::MAX, core::i128::MIN] {
        let dec: FixedDecimal = (*num).into();
        let dec_str = dec.to_string();
        assert_eq!(num.to_string(), dec_str);
        assert_eq!(dec, FixedDecimal::from_str(&dec_str).unwrap());
        writeable::assert_writeable_eq!(dec, dec_str);
    }
    for num in &[core::u128::MAX, core::u128::MIN] {
        let dec: FixedDecimal = (*num).into();
        let dec_str = dec.to_string();
        assert_eq!(num.to_string(), dec_str);
        assert_eq!(dec, FixedDecimal::from_str(&dec_str).unwrap());
        writeable::assert_writeable_eq!(dec, dec_str);
    }
}

#[test]
fn test_upper_magnitude_bounds() {
    let mut dec: FixedDecimal = 98765.into();
    assert_eq!(dec.upper_magnitude, 4);
    dec.multiply_pow10(32763);
    assert_eq!(dec.upper_magnitude, core::i16::MAX);
    assert_eq!(dec.nonzero_magnitude_start(), core::i16::MAX);
    let dec_backup = dec.clone();
    dec.multiply_pow10(1);
    assert!(dec.is_zero());
    assert_ne!(dec, dec_backup, "Value should be unchanged on failure");

    // Checking from_str for dec (which is valid)
    let dec_roundtrip = FixedDecimal::from_str(&dec.to_string()).unwrap();
    assert_eq!(dec, dec_roundtrip);
}

#[test]
fn test_lower_magnitude_bounds() {
    let mut dec: FixedDecimal = 98765.into();
    assert_eq!(dec.lower_magnitude, 0);
    dec.multiply_pow10(-32768);
    assert_eq!(dec.lower_magnitude, core::i16::MIN);
    assert_eq!(dec.nonzero_magnitude_end(), core::i16::MIN);
    let dec_backup = dec.clone();
    dec.multiply_pow10(-1);
    assert!(dec.is_zero());
    assert_ne!(dec, dec_backup);

    // Checking from_str for dec (which is valid)
    let dec_roundtrip = FixedDecimal::from_str(&dec.to_string()).unwrap();
    assert_eq!(dec, dec_roundtrip);
}

#[test]
fn test_zero_str_bounds() {
    #[derive(Debug)]
    struct TestCase {
        pub zeros_before_dot: usize,
        pub zeros_after_dot: usize,
        pub expected_err: Option<Error>,
    }
    // Note that core::i16::MAX = 32768
    let cases = [
        TestCase {
            zeros_before_dot: 32768,
            zeros_after_dot: 0,
            expected_err: None,
        },
        TestCase {
            zeros_before_dot: 32767,
            zeros_after_dot: 0,
            expected_err: None,
        },
        TestCase {
            zeros_before_dot: 32769,
            zeros_after_dot: 0,
            expected_err: Some(Error::Limit),
        },
        TestCase {
            zeros_before_dot: 0,
            zeros_after_dot: 32769,
            expected_err: Some(Error::Limit),
        },
        TestCase {
            zeros_before_dot: 32768,
            zeros_after_dot: 32768,
            expected_err: None,
        },
        TestCase {
            zeros_before_dot: 32769,
            zeros_after_dot: 32768,
            expected_err: Some(Error::Limit),
        },
        TestCase {
            zeros_before_dot: 32768,
            zeros_after_dot: 32769,
            expected_err: Some(Error::Limit),
        },
        TestCase {
            zeros_before_dot: 32767,
            zeros_after_dot: 32769,
            expected_err: Some(Error::Limit),
        },
        TestCase {
            zeros_before_dot: 32767,
            zeros_after_dot: 32767,
            expected_err: None,
        },
        TestCase {
            zeros_before_dot: 32768,
            zeros_after_dot: 32767,
            expected_err: None,
        },
    ];
    for cas in &cases {
        let mut input_str = format!("{:0fill$}", 0, fill = cas.zeros_before_dot);
        if cas.zeros_after_dot > 0 {
            input_str.push('.');
            input_str.push_str(&format!("{:0fill$}", 0, fill = cas.zeros_after_dot));
        }
        match FixedDecimal::from_str(&input_str) {
            Ok(dec) => {
                assert_eq!(cas.expected_err, None, "{:?}", cas);
                assert_eq!(input_str, dec.to_string(), "{:?}", cas);
            }
            Err(err) => {
                assert_eq!(cas.expected_err, Some(err), "{:?}", cas);
            }
        }
    }
}

#[test]
fn test_syntax_error() {
    #[derive(Debug)]
    struct TestCase {
        pub input_str: &'static str,
        pub expected_err: Option<Error>,
    }
    let cases = [
        TestCase {
            input_str: "-12a34",
            expected_err: Some(Error::Syntax),
        },
        TestCase {
            input_str: "0.0123√400",
            expected_err: Some(Error::Syntax),
        },
        TestCase {
            input_str: "0.012.3400",
            expected_err: Some(Error::Syntax),
        },
        TestCase {
            input_str: "-0-0123400",
            expected_err: Some(Error::Syntax),
        },
        TestCase {
            input_str: "0-0123400",
            expected_err: Some(Error::Syntax),
        },
        TestCase {
            input_str: "-0.00123400",
            expected_err: None,
        },
        TestCase {
            input_str: "00123400.",
            expected_err: Some(Error::Syntax),
        },
        TestCase {
            input_str: "00123400.0",
            expected_err: None,
        },
        TestCase {
            input_str: "123_456",
            expected_err: Some(Error::Syntax),
        },
        TestCase {
            input_str: "",
            expected_err: Some(Error::Syntax),
        },
        TestCase {
            input_str: "-",
            expected_err: Some(Error::Syntax),
        },
        TestCase {
            input_str: "+",
            expected_err: Some(Error::Syntax),
        },
        TestCase {
            input_str: "-1",
            expected_err: None,
        },
    ];
    for cas in &cases {
        match FixedDecimal::from_str(cas.input_str) {
            Ok(dec) => {
                assert_eq!(cas.expected_err, None, "{:?}", cas);
                assert_eq!(cas.input_str, dec.to_string(), "{:?}", cas);
            }
            Err(err) => {
                assert_eq!(cas.expected_err, Some(err), "{:?}", cas);
            }
        }
    }
}

#[test]
fn test_pad() {
    let mut dec = FixedDecimal::from_str("-0.42").unwrap();
    assert_eq!("-0.42", dec.to_string());

    dec.pad_start(1);
    assert_eq!("-0.42", dec.to_string());

    dec.pad_start(4);
    assert_eq!("-0000.42", dec.to_string());

    dec.pad_start(2);
    assert_eq!("-00.42", dec.to_string());
}

#[test]
fn test_sign_display() {
    use SignDisplay::*;
    let positive_nonzero = FixedDecimal::from(163);
    let negative_nonzero = FixedDecimal::from(-163);
    let positive_zero = FixedDecimal::from(0);
    let negative_zero = FixedDecimal::from(0).with_sign(Sign::Negative);
    assert_eq!(
        "163",
        positive_nonzero.clone().with_sign_display(Auto).to_string()
    );
    assert_eq!(
        "-163",
        negative_nonzero.clone().with_sign_display(Auto).to_string()
    );
    assert_eq!(
        "0",
        positive_zero.clone().with_sign_display(Auto).to_string()
    );
    assert_eq!(
        "-0",
        negative_zero.clone().with_sign_display(Auto).to_string()
    );
    assert_eq!(
        "+163",
        positive_nonzero
            .clone()
            .with_sign_display(Always)
            .to_string()
    );
    assert_eq!(
        "-163",
        negative_nonzero
            .clone()
            .with_sign_display(Always)
            .to_string()
    );
    assert_eq!(
        "+0",
        positive_zero.clone().with_sign_display(Always).to_string()
    );
    assert_eq!(
        "-0",
        negative_zero.clone().with_sign_display(Always).to_string()
    );
    assert_eq!(
        "163",
        positive_nonzero
            .clone()
            .with_sign_display(Never)
            .to_string()
    );
    assert_eq!(
        "163",
        negative_nonzero
            .clone()
            .with_sign_display(Never)
            .to_string()
    );
    assert_eq!(
        "0",
        positive_zero.clone().with_sign_display(Never).to_string()
    );
    assert_eq!(
        "0",
        negative_zero.clone().with_sign_display(Never).to_string()
    );
    assert_eq!(
        "+163",
        positive_nonzero
            .clone()
            .with_sign_display(ExceptZero)
            .to_string()
    );
    assert_eq!(
        "-163",
        negative_nonzero
            .clone()
            .with_sign_display(ExceptZero)
            .to_string()
    );
    assert_eq!(
        "0",
        positive_zero
            .clone()
            .with_sign_display(ExceptZero)
            .to_string()
    );
    assert_eq!(
        "0",
        negative_zero
            .clone()
            .with_sign_display(ExceptZero)
            .to_string()
    );
    assert_eq!(
        "163",
        positive_nonzero.with_sign_display(Negative).to_string()
    );
    assert_eq!(
        "-163",
        negative_nonzero.with_sign_display(Negative).to_string()
    );
    assert_eq!("0", positive_zero.with_sign_display(Negative).to_string());
    assert_eq!("0", negative_zero.with_sign_display(Negative).to_string());
}

#[test]
fn test_set_max_position() {
    let mut dec = FixedDecimal::from(1000);
    assert_eq!("1000", dec.to_string());

    dec.set_max_position(2);
    assert_eq!("00", dec.to_string());

    dec.set_max_position(0);
    assert_eq!("0", dec.to_string());

    dec.set_max_position(3);
    assert_eq!("000", dec.to_string());

    let mut dec = FixedDecimal::from_str("0.456").unwrap();
    assert_eq!("0.456", dec.to_string());

    dec.set_max_position(0);
    assert_eq!("0.456", dec.to_string());

    dec.set_max_position(-1);
    assert_eq!("0.056", dec.to_string());

    dec.set_max_position(-2);
    assert_eq!("0.006", dec.to_string());

    dec.set_max_position(-3);
    assert_eq!("0.000", dec.to_string());

    dec.set_max_position(-4);
    assert_eq!("0.0000", dec.to_string());

    let mut dec = FixedDecimal::from_str("100.01").unwrap();
    dec.set_max_position(1);
    assert_eq!("0.01", dec.to_string());
}

#[test]
fn test_pad_start_bounds() {
    let mut dec = FixedDecimal::from_str("299792.458").unwrap();
    let max_integer_digits = core::i16::MAX as usize + 1;

    dec.pad_start(core::i16::MAX - 1);
    assert_eq!(
        max_integer_digits - 2,
        dec.to_string().split_once('.').unwrap().0.len()
    );

    dec.pad_start(core::i16::MAX);
    assert_eq!(
        max_integer_digits - 1,
        dec.to_string().split_once('.').unwrap().0.len()
    );
}

#[test]
fn test_pad_end_bounds() {
    let mut dec = FixedDecimal::from_str("299792.458").unwrap();
    let max_fractional_digits = -(core::i16::MIN as isize) as usize;

    dec.pad_end(core::i16::MIN + 1);
    assert_eq!(
        max_fractional_digits - 1,
        dec.to_string().split_once('.').unwrap().1.len()
    );

    dec.pad_end(core::i16::MIN);
    assert_eq!(
        max_fractional_digits,
        dec.to_string().split_once('.').unwrap().1.len()
    );
}

#[test]
fn test_rounding() {
    pub(crate) use std::str::FromStr;

    // Test Ceil
    let mut dec = FixedDecimal::from_str("3.234").unwrap();
    dec.ceil(0);
    assert_eq!("4", dec.to_string());

    let mut dec = FixedDecimal::from_str("2.222").unwrap();
    dec.ceil(-1);
    assert_eq!("2.3", dec.to_string());

    let mut dec = FixedDecimal::from_str("22.222").unwrap();
    dec.ceil(-2);
    assert_eq!("22.23", dec.to_string());

    let mut dec = FixedDecimal::from_str("99.999").unwrap();
    dec.ceil(-2);
    assert_eq!("100.00", dec.to_string());

    let mut dec = FixedDecimal::from_str("99.999").unwrap();
    dec.ceil(-5);
    assert_eq!("99.99900", dec.to_string());

    let mut dec = FixedDecimal::from_str("-99.999").unwrap();
    dec.ceil(-5);
    assert_eq!("-99.99900", dec.to_string());

    let mut dec = FixedDecimal::from_str("-99.999").unwrap();
    dec.ceil(-2);
    assert_eq!("-99.99", dec.to_string());

    let mut dec = FixedDecimal::from_str("99.999").unwrap();
    dec.ceil(4);
    assert_eq!("10000", dec.to_string());

    let mut dec = FixedDecimal::from_str("-99.999").unwrap();
    dec.ceil(4);
    assert_eq!("-0000", dec.to_string());

    let mut dec = FixedDecimal::from_str("0.009").unwrap();
    dec.ceil(-1);
    assert_eq!("0.1", dec.to_string());

    let mut dec = FixedDecimal::from_str("-0.009").unwrap();
    dec.ceil(-1);
    assert_eq!("-0.0", dec.to_string());

    // Test Half Ceil
    let mut dec = FixedDecimal::from_str("3.234").unwrap();
    dec.half_ceil(0);
    assert_eq!("3", dec.to_string());

    let mut dec = FixedDecimal::from_str("3.534").unwrap();
    dec.half_ceil(0);
    assert_eq!("4", dec.to_string());

    let mut dec = FixedDecimal::from_str("3.934").unwrap();
    dec.half_ceil(0);
    assert_eq!("4", dec.to_string());

    let mut dec = FixedDecimal::from_str("2.222").unwrap();
    dec.half_ceil(-1);
    assert_eq!("2.2", dec.to_string());

    let mut dec = FixedDecimal::from_str("2.44").unwrap();
    dec.half_ceil(-1);
    assert_eq!("2.4", dec.to_string());

    let mut dec = FixedDecimal::from_str("2.45").unwrap();
    dec.half_ceil(-1);
    assert_eq!("2.5", dec.to_string());

    let mut dec = FixedDecimal::from_str("-2.44").unwrap();
    dec.half_ceil(-1);
    assert_eq!("-2.4", dec.to_string());

    let mut dec = FixedDecimal::from_str("-2.45").unwrap();
    dec.half_ceil(-1);
    assert_eq!("-2.4", dec.to_string());

    let mut dec = FixedDecimal::from_str("22.222").unwrap();
    dec.half_ceil(-2);
    assert_eq!("22.22", dec.to_string());

    let mut dec = FixedDecimal::from_str("99.999").unwrap();
    dec.half_ceil(-2);
    assert_eq!("100.00", dec.to_string());

    let mut dec = FixedDecimal::from_str("99.999").unwrap();
    dec.half_ceil(-5);
    assert_eq!("99.99900", dec.to_string());

    let mut dec = FixedDecimal::from_str("-99.999").unwrap();
    dec.half_ceil(-5);
    assert_eq!("-99.99900", dec.to_string());

    let mut dec = FixedDecimal::from_str("-99.999").unwrap();
    dec.half_ceil(-2);
    assert_eq!("-100.00", dec.to_string());

    let mut dec = FixedDecimal::from_str("99.999").unwrap();
    dec.half_ceil(4);
    assert_eq!("0000", dec.to_string());

    let mut dec = FixedDecimal::from_str("-99.999").unwrap();
    dec.half_ceil(4);
    assert_eq!("-0000", dec.to_string());

    let mut dec = FixedDecimal::from_str("0.009").unwrap();
    dec.half_ceil(-1);
    assert_eq!("0.0", dec.to_string());

    let mut dec = FixedDecimal::from_str("-0.009").unwrap();
    dec.half_ceil(-1);
    assert_eq!("-0.0", dec.to_string());

    // Test Floor
    let mut dec = FixedDecimal::from_str("3.234").unwrap();
    dec.floor(0);
    assert_eq!("3", dec.to_string());

    let mut dec = FixedDecimal::from_str("2.222").unwrap();
    dec.floor(-1);
    assert_eq!("2.2", dec.to_string());

    let mut dec = FixedDecimal::from_str("99.999").unwrap();
    dec.floor(-2);
    assert_eq!("99.99", dec.to_string());

    let mut dec = FixedDecimal::from_str("99.999").unwrap();
    dec.floor(-10);
    assert_eq!("99.9990000000", dec.to_string());

    let mut dec = FixedDecimal::from_str("-99.999").unwrap();
    dec.floor(-10);
    assert_eq!("-99.9990000000", dec.to_string());

    let mut dec = FixedDecimal::from_str("99.999").unwrap();
    dec.floor(10);
    assert_eq!("0000000000", dec.to_string());

    let mut dec = FixedDecimal::from_str("-99.999").unwrap();
    dec.floor(10);
    assert_eq!("-10000000000", dec.to_string());

    // Test Half Floor
    let mut dec = FixedDecimal::from_str("3.234").unwrap();
    dec.half_floor(0);
    assert_eq!("3", dec.to_string());

    let mut dec = FixedDecimal::from_str("3.534").unwrap();
    dec.half_floor(0);
    assert_eq!("4", dec.to_string());

    let mut dec = FixedDecimal::from_str("3.934").unwrap();
    dec.half_floor(0);
    assert_eq!("4", dec.to_string());

    let mut dec = FixedDecimal::from_str("2.222").unwrap();
    dec.half_floor(-1);
    assert_eq!("2.2", dec.to_string());

    let mut dec = FixedDecimal::from_str("2.44").unwrap();
    dec.half_floor(-1);
    assert_eq!("2.4", dec.to_string());

    let mut dec = FixedDecimal::from_str("2.45").unwrap();
    dec.half_floor(-1);
    assert_eq!("2.4", dec.to_string());

    let mut dec = FixedDecimal::from_str("-2.44").unwrap();
    dec.half_floor(-1);
    assert_eq!("-2.4", dec.to_string());

    let mut dec = FixedDecimal::from_str("-2.45").unwrap();
    dec.half_floor(-1);
    assert_eq!("-2.5", dec.to_string());

    let mut dec = FixedDecimal::from_str("22.222").unwrap();
    dec.half_floor(-2);
    assert_eq!("22.22", dec.to_string());

    let mut dec = FixedDecimal::from_str("99.999").unwrap();
    dec.half_floor(-2);
    assert_eq!("100.00", dec.to_string());

    let mut dec = FixedDecimal::from_str("99.999").unwrap();
    dec.half_floor(-5);
    assert_eq!("99.99900", dec.to_string());

    let mut dec = FixedDecimal::from_str("-99.999").unwrap();
    dec.half_floor(-5);
    assert_eq!("-99.99900", dec.to_string());

    let mut dec = FixedDecimal::from_str("-99.999").unwrap();
    dec.half_floor(-2);
    assert_eq!("-100.00", dec.to_string());

    let mut dec = FixedDecimal::from_str("99.999").unwrap();
    dec.half_floor(4);
    assert_eq!("0000", dec.to_string());

    let mut dec = FixedDecimal::from_str("-99.999").unwrap();
    dec.half_floor(4);
    assert_eq!("-0000", dec.to_string());

    let mut dec = FixedDecimal::from_str("0.009").unwrap();
    dec.half_floor(-1);
    assert_eq!("0.0", dec.to_string());

    let mut dec = FixedDecimal::from_str("-0.009").unwrap();
    dec.half_floor(-1);
    assert_eq!("-0.0", dec.to_string());

    // Test Truncate Right
    let mut dec = FixedDecimal::from(4235970).multiplied_pow10(-3);
    assert_eq!("4235.970", dec.to_string());

    dec.trunc(-5);
    assert_eq!("4235.97000", dec.to_string());

    dec.trunc(-1);
    assert_eq!("4235.9", dec.to_string());

    dec.trunc(0);
    assert_eq!("4235", dec.to_string());

    dec.trunc(1);
    assert_eq!("4230", dec.to_string());

    dec.trunc(5);
    assert_eq!("00000", dec.to_string());

    dec.trunc(2);
    assert_eq!("00000", dec.to_string());

    let mut dec = FixedDecimal::from_str("-99.999").unwrap();
    dec.trunc(-2);
    assert_eq!("-99.99", dec.to_string());

    let mut dec = FixedDecimal::from_str("1234.56").unwrap();
    dec.trunc(-1);
    assert_eq!("1234.5", dec.to_string());

    let mut dec = FixedDecimal::from_str("0.009").unwrap();
    dec.trunc(-1);
    assert_eq!("0.0", dec.to_string());

    // Test trunced
    let dec = FixedDecimal::from(4235970).multiplied_pow10(-3);
    assert_eq!("4235.970", dec.to_string());

    assert_eq!("4235.97000", dec.clone().trunced(-5).to_string());

    assert_eq!("4230", dec.clone().trunced(1).to_string());

    assert_eq!("4200", dec.clone().trunced(2).to_string());

    assert_eq!("00000", dec.trunced(5).to_string());

    //Test expand
    let mut dec = FixedDecimal::from_str("3.234").unwrap();
    dec.expand(0);
    assert_eq!("4", dec.to_string());

    let mut dec = FixedDecimal::from_str("2.222").unwrap();
    dec.expand(-1);
    assert_eq!("2.3", dec.to_string());

    let mut dec = FixedDecimal::from_str("22.222").unwrap();
    dec.expand(-2);
    assert_eq!("22.23", dec.to_string());

    let mut dec = FixedDecimal::from_str("99.999").unwrap();
    dec.expand(-2);
    assert_eq!("100.00", dec.to_string());

    let mut dec = FixedDecimal::from_str("99.999").unwrap();
    dec.expand(-5);
    assert_eq!("99.99900", dec.to_string());

    let mut dec = FixedDecimal::from_str("-99.999").unwrap();
    dec.expand(-5);
    assert_eq!("-99.99900", dec.to_string());

    let mut dec = FixedDecimal::from_str("-99.999").unwrap();
    dec.expand(-2);
    assert_eq!("-100.00", dec.to_string());

    let mut dec = FixedDecimal::from_str("99.999").unwrap();
    dec.expand(4);
    assert_eq!("10000", dec.to_string());

    let mut dec = FixedDecimal::from_str("-99.999").unwrap();
    dec.expand(4);
    assert_eq!("-10000", dec.to_string());

    let mut dec = FixedDecimal::from_str("0.009").unwrap();
    dec.expand(-1);
    assert_eq!("0.1", dec.to_string());

    let mut dec = FixedDecimal::from_str("-0.009").unwrap();
    dec.expand(-1);
    assert_eq!("-0.1", dec.to_string());

    let mut dec = FixedDecimal::from_str("3.954").unwrap();
    dec.expand(0);
    assert_eq!("4", dec.to_string());

    // Test half_expand
    let mut dec = FixedDecimal::from_str("3.234").unwrap();
    dec.half_expand(0);
    assert_eq!("3", dec.to_string());

    let mut dec = FixedDecimal::from_str("3.534").unwrap();
    dec.half_expand(0);
    assert_eq!("4", dec.to_string());

    let mut dec = FixedDecimal::from_str("3.934").unwrap();
    dec.half_expand(0);
    assert_eq!("4", dec.to_string());

    let mut dec = FixedDecimal::from_str("2.222").unwrap();
    dec.half_expand(-1);
    assert_eq!("2.2", dec.to_string());

    let mut dec = FixedDecimal::from_str("2.44").unwrap();
    dec.half_expand(-1);
    assert_eq!("2.4", dec.to_string());

    let mut dec = FixedDecimal::from_str("2.45").unwrap();
    dec.half_expand(-1);
    assert_eq!("2.5", dec.to_string());

    let mut dec = FixedDecimal::from_str("-2.44").unwrap();
    dec.half_expand(-1);
    assert_eq!("-2.4", dec.to_string());

    let mut dec = FixedDecimal::from_str("-2.45").unwrap();
    dec.half_expand(-1);
    assert_eq!("-2.5", dec.to_string());

    let mut dec = FixedDecimal::from_str("22.222").unwrap();
    dec.half_expand(-2);
    assert_eq!("22.22", dec.to_string());

    let mut dec = FixedDecimal::from_str("99.999").unwrap();
    dec.half_expand(-2);
    assert_eq!("100.00", dec.to_string());

    let mut dec = FixedDecimal::from_str("99.999").unwrap();
    dec.half_expand(-5);
    assert_eq!("99.99900", dec.to_string());

    let mut dec = FixedDecimal::from_str("-99.999").unwrap();
    dec.half_expand(-5);
    assert_eq!("-99.99900", dec.to_string());

    let mut dec = FixedDecimal::from_str("-99.999").unwrap();
    dec.half_expand(-2);
    assert_eq!("-100.00", dec.to_string());

    let mut dec = FixedDecimal::from_str("99.999").unwrap();
    dec.half_expand(4);
    assert_eq!("0000", dec.to_string());

    let mut dec = FixedDecimal::from_str("-99.999").unwrap();
    dec.half_expand(4);
    assert_eq!("-0000", dec.to_string());

    let mut dec = FixedDecimal::from_str("0.009").unwrap();
    dec.half_expand(-1);
    assert_eq!("0.0", dec.to_string());

    let mut dec = FixedDecimal::from_str("-0.009").unwrap();
    dec.half_expand(-1);
    assert_eq!("-0.0", dec.to_string());
}

#[test]
fn test_concatenate() {
    #[derive(Debug)]
    struct TestCase {
        pub input_1: &'static str,
        pub input_2: &'static str,
        pub expected: Option<&'static str>,
    }
    let cases = [
        TestCase {
            input_1: "123",
            input_2: "0.456",
            expected: Some("123.456"),
        },
        TestCase {
            input_1: "0.456",
            input_2: "123",
            expected: None,
        },
        TestCase {
            input_1: "123",
            input_2: "0.0456",
            expected: Some("123.0456"),
        },
        TestCase {
            input_1: "0.0456",
            input_2: "123",
            expected: None,
        },
        TestCase {
            input_1: "100",
            input_2: "0.456",
            expected: Some("100.456"),
        },
        TestCase {
            input_1: "0.456",
            input_2: "100",
            expected: None,
        },
        TestCase {
            input_1: "100",
            input_2: "0.001",
            expected: Some("100.001"),
        },
        TestCase {
            input_1: "0.001",
            input_2: "100",
            expected: None,
        },
        TestCase {
            input_1: "123000",
            input_2: "456",
            expected: Some("123456"),
        },
        TestCase {
            input_1: "456",
            input_2: "123000",
            expected: None,
        },
        TestCase {
            input_1: "5",
            input_2: "5",
            expected: None,
        },
        TestCase {
            input_1: "120",
            input_2: "25",
            expected: None,
        },
        TestCase {
            input_1: "1.1",
            input_2: "0.2",
            expected: None,
        },
        TestCase {
            input_1: "0",
            input_2: "222",
            expected: Some("222"),
        },
        TestCase {
            input_1: "222",
            input_2: "0",
            expected: Some("222"),
        },
        TestCase {
            input_1: "0",
            input_2: "0",
            expected: Some("0"),
        },
        TestCase {
            input_1: "000",
            input_2: "0",
            expected: Some("000"),
        },
        TestCase {
            input_1: "0.00",
            input_2: "0",
            expected: Some("0.00"),
        },
    ];
    for cas in &cases {
        let fd1 = FixedDecimal::from_str(cas.input_1).unwrap();
        let fd2 = FixedDecimal::from_str(cas.input_2).unwrap();
        match fd1.concatenated_end(fd2) {
            Ok(fd) => {
                assert_eq!(cas.expected, Some(fd.to_string().as_str()), "{:?}", cas);
            }
            Err(_) => {
                assert!(cas.expected.is_none(), "{:?}", cas);
            }
        }
    }
}