1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
// 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 ).

//! This file contains the implementation of the rounding algorithm and the related functions & types.

// Adapters to convert runtime dispatched calls into const-inlined methods.
// This allows reducing the codesize for the common case of no increment.

#[derive(Copy, Clone, PartialEq)]
pub(crate) struct NoIncrement;

pub(crate) trait IncrementLike: Copy + Sized + PartialEq {
    const MULTIPLES_OF_1: Option<Self>;
    const MULTIPLES_OF_2: Option<Self>;
    const MULTIPLES_OF_5: Option<Self>;
    const MULTIPLES_OF_25: Option<Self>;
}

impl IncrementLike for RoundingIncrement {
    const MULTIPLES_OF_1: Option<Self> = Some(Self::MultiplesOf1);

    const MULTIPLES_OF_2: Option<Self> = Some(Self::MultiplesOf2);

    const MULTIPLES_OF_5: Option<Self> = Some(Self::MultiplesOf5);

    const MULTIPLES_OF_25: Option<Self> = Some(Self::MultiplesOf25);
}

impl IncrementLike for NoIncrement {
    const MULTIPLES_OF_1: Option<Self> = Some(Self);

    const MULTIPLES_OF_2: Option<Self> = None;

    const MULTIPLES_OF_5: Option<Self> = None;

    const MULTIPLES_OF_25: Option<Self> = None;
}

/// Mode used in a unsigned rounding operations.
///
/// # Comparative table of all the rounding modes, including the signed and unsigned ones.
///
/// | Value | Ceil | Expand | Floor | Trunc | HalfCeil | HalfExpand | HalfFloor | HalfTrunc | HalfEven |
/// |:-----:|:----:|:------:|:-----:|:-----:|:--------:|:----------:|:---------:|:---------:|:--------:|
/// |  +1.8 |  +2  |   +2   |   +1  |   +1  |    +2    |     +2     |     +2    |     +2    |    +2    |
/// |  +1.5 |   "  |    "   |   "   |   "   |     "    |      "     |     +1    |     +1    |     "    |
/// |  +1.2 |   "  |    "   |   "   |   "   |    +1    |     +1     |     "     |     "     |    +1    |
/// |  +0.8 |  +1  |   +1   |   0   |   0   |     "    |      "     |     "     |     "     |     "    |
/// |  +0.5 |   "  |    "   |   "   |   "   |     "    |      "     |     0     |     0     |     0    |
/// |  +0.2 |   "  |    "   |   "   |   "   |     0    |      0     |     "     |     "     |     "    |
/// |  -0.2 |   0  |   -1   |   -1  |   "   |     "    |      "     |     "     |     "     |     "    |
/// |  -0.5 |   "  |    "   |   "   |   "   |     "    |     -1     |     -1    |     "     |     "    |
/// |  -0.8 |   "  |    "   |   "   |   "   |    -1    |      "     |     "     |     -1    |    -1    |
/// |  -1.2 |  -1  |   -2   |   -2  |   -1  |     "    |      "     |     "     |     "     |     "    |
/// |  -1.5 |   "  |    "   |   "   |   "   |     "    |     -2     |     -2    |     "     |    -2    |
/// |  -1.8 |   "  |    "   |   "   |   "   |    -2    |      "     |     "     |     -2    |     "    |
///
/// NOTE:
///   - Ceil, Floor, HalfCeil and HalfFloor are part of the [`SignedRoundingMode`] enum.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[non_exhaustive]
pub enum UnsignedRoundingMode {
    Expand,
    Trunc,
    HalfExpand,
    HalfTrunc,
    HalfEven,
}

/// Mode used in a signed rounding operations.
///
/// NOTE:
///   - You can find the comparative table of all the rounding modes in the [`UnsignedRoundingMode`] documentation.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[non_exhaustive]
pub enum SignedRoundingMode {
    Unsigned(UnsignedRoundingMode),
    Ceil,
    Floor,
    HalfCeil,
    HalfFloor,
}

/// Increment used in a rounding operation.
///
/// Forces a rounding operation to round to only multiples of the specified increment.
///
/// # Example
///
/// ```
/// use fixed_decimal::{SignedFixedDecimal, RoundingIncrement, SignedRoundingMode, UnsignedRoundingMode};
/// use writeable::assert_writeable_eq;
/// # use std::str::FromStr;
/// let dec = SignedFixedDecimal::from_str("-7.266").unwrap();
/// let mode = SignedRoundingMode::Unsigned(UnsignedRoundingMode::Expand);
/// let increments = [
///     // .266 normally expands to .27 when rounding on position -2...
///     (RoundingIncrement::MultiplesOf1, "-7.27"),
///     // ...however, when rounding to multiples of 2, .266 expands to .28, since the next multiple
///     // of 2 bigger than the least significant digit of the rounded value (7) is 8.
///     (RoundingIncrement::MultiplesOf2, "-7.28"),
///     // .266 expands to .30, since the next multiple of 5 bigger than 7 is 10.
///     (RoundingIncrement::MultiplesOf5, "-7.30"),
///     // .266 expands to .50, since the next multiple of 25 bigger than 27 is 50.
///     // Note how we compare against 27 instead of only 7, because the increment applies to
///     // the two least significant digits of the rounded value instead of only the least
///     // significant digit.
///     (RoundingIncrement::MultiplesOf25, "-7.50"),
/// ];
///
/// for (increment, expected) in increments {
///     assert_writeable_eq!(
///         dec.clone().rounded_with_mode_and_increment(
///             -2,
///             mode,
///             increment
///         ),
///         expected
///     );
/// }
/// ```
#[derive(Debug, Eq, PartialEq, Clone, Copy, Default)]
#[non_exhaustive]
pub enum RoundingIncrement {
    /// Round the least significant digit to any digit (0-9).
    ///
    /// This is the default rounding increment for all the methods that don't take a
    /// `RoundingIncrement` as an argument.
    #[default]
    MultiplesOf1,
    /// Round the least significant digit to multiples of two (0, 2, 4, 6, 8).
    MultiplesOf2,
    /// Round the least significant digit to multiples of five (0, 5).
    MultiplesOf5,
    /// Round the two least significant digits to multiples of twenty-five (0, 25, 50, 75).
    ///
    /// With this increment, the rounding position index will match the least significant digit
    /// of the multiple of 25; e.g. the number .264 expanded at position -2 using increments of 25
    /// will give .50 as a result, since the next multiple of 25 bigger than 26 is 50.
    MultiplesOf25,
}

/// 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 FloatPrecision {
    /// 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.
    RoundTrip,
}