padded_number/
arithmetic.rs

1use std::ops::{Add, Sub};
2
3use crate::*;
4
5impl<const A: u8, const B: u8> PaddedNumber<A, B> {
6    /// Wrapping addition with u64 as right-hand side
7    ///
8    /// Used within the `impl Add<u64> for PaddedNumber` implementation.
9    ///
10    /// ```rust
11    /// # use padded_number_macros::*;
12    /// assert_eq!(padded_number!("0") + 1, padded_number!("0").wrapping_add(1));
13    ///
14    /// // Within bounds
15    /// assert_eq!(padded_number!("9") + 1, padded_number!("00"));
16    /// assert_eq!(padded_number!("80") + 11, padded_number!("91"));
17    ///
18    /// // Wrapped
19    /// assert_eq!(
20    ///     bound_padded_number!(2, 3, "999") + 2,
21    ///     bound_padded_number!(2, 3, "01")
22    /// );
23    /// ```
24    pub fn wrapping_add(self, rhs: u64) -> Self {
25        self.add_impl(rhs, |new_number, max_for_length_number| {
26            let start = Self::min_number_for_min_length();
27
28            match B == 0 {
29                true => start,
30                // -1 because '0' counts as a step
31                false => start.wrapping_add(new_number - max_for_length_number - 1),
32            }
33        })
34    }
35
36    /// Saturating addition with u64 as right-hand side
37    ///
38    /// ```rust
39    /// # use padded_number_macros::*;
40    /// assert_eq!(
41    ///     bound_padded_number!(2, 3, "990").saturating_add(1000),
42    ///     bound_padded_number!(2, 3, "999") // saturated
43    /// );
44    /// ```
45    ///
46    /// Addition within bounds behaves the same as in [`Self::wrapping_add`].
47    pub fn saturating_add(self, rhs: u64) -> Self {
48        self.add_impl(rhs, |_new_number, max_for_length_number| Self {
49            leading_zeros: 0,
50            number: max_for_length_number,
51        })
52    }
53
54    /// Wrapping subtraction with u64 as right-hand side
55    ///
56    /// Used within the `impl Sub<u64> for PaddedNumber` implementation.
57    ///
58    /// ```rust
59    /// # use padded_number_macros::*;
60    /// assert_eq!(padded_number!("9") - 1, padded_number!("9").wrapping_sub(1));
61    ///
62    /// // Within bounds
63    /// assert_eq!(padded_number!("9") + 1, padded_number!("00"));
64    /// assert_eq!(padded_number!("80") + 11, padded_number!("91"));
65    ///
66    /// // Wrapped
67    /// assert_eq!(
68    ///     bound_padded_number!(2, 3, "999") + 2,
69    ///     bound_padded_number!(2, 3, "01")
70    /// );
71    /// ```
72    pub fn wrapping_sub(self, rhs: u64) -> Self {
73        self.sub_impl(rhs, |remaining_difference| {
74            Self::max_number_for_max_length().wrapping_sub(remaining_difference)
75        })
76    }
77
78    /// Saturating subtraction with u64 as right-hand side
79    ///
80    /// ```rust
81    /// # use padded_number_macros::*;
82    /// assert_eq!(
83    ///     bound_padded_number!(1, 2, "99").saturating_sub(1000),
84    ///     bound_padded_number!(1, 2, "0") // saturated
85    /// );
86    /// ```
87    ///
88    /// Subtraction within bounds behaves the same as in [`Self::wrapping_sub`].
89    pub fn saturating_sub(self, rhs: u64) -> Self {
90        self.sub_impl(rhs, |_| Self::min_number_for_min_length())
91    }
92
93    // can't yet be const due to Rust currently missing const closures
94    fn add_impl(self, rhs: u64, overflow_fn: fn(u64, u64) -> Self) -> Self {
95        if rhs == 0 {
96            return self;
97        }
98
99        let new_number = self.number + rhs;
100
101        let max_for_length = self.max_number_for_current_length();
102        let max_for_length_number = max_for_length.number;
103
104        // check for overflow
105        if new_number > max_for_length_number {
106            // handle overflow
107            if self.len() == B {
108                overflow_fn(new_number, max_for_length_number)
109            }
110            // recursively add one leading zero
111            else {
112                let next_number = Self { leading_zeros: self.len() + 1, number: 0 };
113
114                let diff_to_next_increase = max_for_length_number - self.number;
115                let rhs_next = rhs - diff_to_next_increase - 1;
116
117                next_number.add_impl(rhs_next, overflow_fn)
118            }
119        }
120        // no overflow, preserve number of digits
121        else {
122            let new_number_digits = digit_length(new_number);
123            let current_digits = digit_length(self.number);
124            let leading_zeroes_to_remove = new_number_digits - current_digits;
125
126            Self {
127                leading_zeros: self.leading_zeros - leading_zeroes_to_remove,
128                number: new_number,
129            }
130        }
131    }
132
133    fn sub_impl(self, rhs: u64, overflow_fn: impl FnOnce(u64) -> Self) -> Self {
134        if rhs == 0 || self.is_empty() {
135            return self;
136        }
137
138        match self.number.checked_sub(rhs) {
139            // within does not overflow, preserve length
140            Some(new_number) => {
141                let current_digits = digit_length(self.number);
142                let new_number_digits = digit_length(new_number);
143                let leading_zeroes_to_add = current_digits - new_number_digits;
144
145                Self {
146                    leading_zeros: self.leading_zeros + leading_zeroes_to_add,
147                    number: new_number,
148                }
149            }
150            // within overflows
151            None => {
152                let length = self.len();
153
154                if length == A {
155                    // -1 because '0' counts as a step
156                    overflow_fn(rhs - self.number - 1)
157                } else {
158                    let next_number =
159                        PaddedNumber { leading_zeros: 0, number: Self::max_number_for_length_impl(length - 1) };
160
161                    let overflow_diff = rhs - self.number;
162                    let next_rhs = overflow_diff - 1;
163
164                    next_number.sub_impl(next_rhs, overflow_fn)
165                }
166            }
167        }
168    }
169
170    const fn max_number_for_current_length(&self) -> PaddedNumber<A, B> {
171        PaddedNumber { leading_zeros: 0, number: Self::max_number_for_length_impl(self.len()) }
172    }
173
174    const fn max_number_for_max_length() -> PaddedNumber<A, B> {
175        PaddedNumber { leading_zeros: 0, number: Self::max_number_for_length_impl(B) }
176    }
177
178    const fn min_number_for_min_length() -> Self {
179        Self { leading_zeros: A, number: 0 }
180    }
181
182    const fn max_number_for_length_impl(length: u8) -> u64 {
183        (10_u64.pow(length as u32)) - 1
184    }
185}
186
187impl<const A: u8, const B: u8> Add<u64> for PaddedNumber<A, B> {
188    type Output = Self;
189
190    fn add(self, rhs: u64) -> Self::Output {
191        Self::wrapping_add(self, rhs)
192    }
193}
194
195impl<const A: u8, const B: u8> Sub<u64> for PaddedNumber<A, B> {
196    type Output = Self;
197
198    fn sub(self, rhs: u64) -> Self::Output {
199        Self::wrapping_sub(self, rhs)
200    }
201}
202
203const fn digit_length(n: u64) -> u8 {
204    if n == 0 {
205        0
206    } else {
207        let mut count = 1;
208        let mut current = n;
209
210        while current >= 10 {
211            count += 1;
212            current /= 10
213        }
214
215        count
216    }
217}
218
219#[cfg(test)]
220mod tests {
221    use crate::{tests::mock_from_str, *};
222
223    #[test]
224    fn max_number_for_current_length() {
225        assert_impl("", "");
226        assert_impl("9", "0");
227        assert_impl("99", "00");
228        assert_impl("999", "001");
229        assert_impl("9999", "1234");
230
231        fn assert_impl(max_number_str: &str, number_str: &str) {
232            let expected_max_number = mock_from_str::<0, 10>(max_number_str);
233            let actual_max_number = mock_from_str::<0, 10>(number_str).max_number_for_current_length();
234            assert_eq!(expected_max_number, actual_max_number);
235        }
236    }
237
238    #[test]
239    fn max_number_for_max_length() {
240        assert_impl::<0>("");
241        assert_impl::<1>("9");
242        assert_impl::<2>("99");
243
244        fn assert_impl<const B: u8>(expected_max_number_str: &str) {
245            let expected_max_number = mock_from_str::<0, B>(expected_max_number_str);
246            let actual_max_number = PaddedNumber::<0, B>::max_number_for_max_length();
247            assert_eq!(expected_max_number, actual_max_number);
248        }
249    }
250
251    #[test]
252    fn non_overflowing_add() {
253        // zero is no-op
254        assert_non_overflowing_add("00", ("00", 0));
255
256        // from empty
257        assert_non_overflowing_add("0", ("", 1));
258
259        // adds leading zero on increment
260        assert_non_overflowing_add("00", ("9", 1));
261        assert_non_overflowing_add("0000", ("999", 1));
262        assert_non_overflowing_add("000", ("9", 101));
263
264        // inner decimal increment does not add leading zero
265        assert_non_overflowing_add("99", ("00", 99));
266        assert_non_overflowing_add("001", ("000", 1));
267        assert_non_overflowing_add("100", ("099", 1));
268        assert_non_overflowing_add("1099", ("0099", 1000));
269
270        fn assert_non_overflowing_add(expected: &str, (lhs, rhs): (&str, u64)) {
271            assert_arithmetic::<0, 10>(expected, (lhs, rhs), |lhs, rhs| {
272                lhs.add_impl(rhs, |_, _| panic!("overflow occurred when testing non overflows"))
273            });
274        }
275    }
276
277    #[test]
278    fn wrapping_add() {
279        // wrappes at next
280        assert_wrapping_add::<0, 10>("", ("9999999999", 1));
281        // wrappes at next to min for length
282        assert_wrapping_add::<2, 10>("00", ("9999999999", 1));
283        // wrappes past last
284        assert_wrapping_add::<1, 10>("01", ("9999999900", 111));
285        // wrappes at empty
286        assert_wrapping_add::<0, 0>("", ("", 10));
287
288        fn assert_wrapping_add<const A: u8, const B: u8>(expected: &str, (lhs, rhs): (&str, u64)) {
289            assert_arithmetic(expected, (lhs, rhs), PaddedNumber::<A, B>::wrapping_add);
290        }
291    }
292
293    #[test]
294    fn saturating_add() {
295        // saturates at next
296        assert_saturating_add::<0, 10>("9999999999", ("9999999999", 1));
297        // saturates until last
298        assert_saturating_add::<0, 10>("9999999999", ("9999999900", 1000));
299        // saturates at empty
300        assert_saturating_add::<0, 0>("", ("", 10));
301
302        // doesn't saturate just because of max length
303        assert_saturating_add::<0, 10>("9999999992", ("9999999990", 2));
304
305        fn assert_saturating_add<const A: u8, const B: u8>(expected: &str, (lhs, rhs): (&str, u64)) {
306            assert_arithmetic(expected, (lhs, rhs), PaddedNumber::<A, B>::saturating_add);
307        }
308    }
309
310    #[test]
311    fn non_overflowing_sub() {
312        // zero is no-op
313        assert_non_overflowing_sub("00", ("00", 0));
314
315        // within does not overflow, digit count is preserved
316        assert_non_overflowing_sub("0", ("9", 9));
317        assert_non_overflowing_sub("0000", ("0100", 100));
318        assert_non_overflowing_sub("099", ("100", 1));
319        assert_non_overflowing_sub("050", ("100", 50));
320
321        // within overflows, decrease digit count
322        assert_non_overflowing_sub("", ("0", 1));
323        assert_non_overflowing_sub("9", ("00", 1));
324        assert_non_overflowing_sub("09", ("090", 181));
325        assert_non_overflowing_sub("999", ("0100", 101));
326        assert_non_overflowing_sub("995", ("0100", 105));
327
328        fn assert_non_overflowing_sub(expected: &str, (lhs, rhs): (&str, u64)) {
329            assert_arithmetic::<0, 10>(expected, (lhs, rhs), |lhs, rhs| {
330                lhs.sub_impl(rhs, |_| panic!("overflow occurred when testing non overflows"))
331            });
332        }
333    }
334
335    #[test]
336    fn wrapping_sub() {
337        // wrappes at next
338        assert_wrapping_sub::<1, 1>("9", ("0", 1));
339        // wrappes to max digit count
340        assert_wrapping_sub::<1, 3>("999", ("0", 1));
341        // wrappes past last
342        assert_wrapping_sub::<1, 3>("995", ("01", 16));
343        // wrappes at empty
344        assert_wrapping_sub::<0, 0>("", ("", 10));
345
346        fn assert_wrapping_sub<const A: u8, const B: u8>(expected: &str, (lhs, rhs): (&str, u64)) {
347            assert_arithmetic(expected, (lhs, rhs), PaddedNumber::<A, B>::wrapping_sub);
348        }
349    }
350
351    #[test]
352    fn saturating_sub() {
353        // saturates at next
354        assert_saturating_sub::<1, 1>("0", ("0", 1));
355        assert_saturating_sub::<2, 2>("00", ("00", 1));
356        // saturates until last
357        assert_saturating_sub::<0, 2>("", ("00", 1000));
358        assert_saturating_sub::<1, 2>("0", ("00", 1000));
359        assert_saturating_sub::<1, 2>("0", ("90", 1000));
360        assert_saturating_sub::<1, 2>("0", ("99", 1000));
361
362        fn assert_saturating_sub<const A: u8, const B: u8>(expected: &str, (lhs, rhs): (&str, u64)) {
363            assert_arithmetic(expected, (lhs, rhs), PaddedNumber::<A, B>::saturating_sub);
364        }
365    }
366
367    fn assert_arithmetic<const A: u8, const B: u8>(
368        expected: &str,
369        (lhs, rhs): (&str, u64),
370        operator: impl Fn(PaddedNumber<A, B>, u64) -> PaddedNumber<A, B>,
371    ) {
372        let expected = mock_from_str::<A, B>(expected);
373        let actual = operator(mock_from_str::<A, B>(lhs), rhs);
374        assert_eq!(
375            expected, actual,
376            "failed to operate on '{}' with {} to in the end expect '{}'",
377            lhs, rhs, expected
378        );
379    }
380}