devela/num/int/wrapper/
impl_base.rs

1// devela::num::int::wrapper::impl_base
2//
3/// Implements base-related methods for [`Int`].
4//
5// TOC
6// - signed|unsigned:
7//   - digits
8//   - digits_sign
9//   - digits_base
10//   - digits_base_sign
11//   - digital_root
12//   - digital_root_base
13use crate::{iif, paste, Int};
14
15/// Implements base-related methods for [`Int`].
16///
17/// # Args
18/// $t:   the integer primitive input/output type, and the niche inner type
19/// $cap: the capability feature that enables the given implementation. E.g "_int_i8"
20///
21/// $d:   the doclink suffix for the method name
22macro_rules! impl_base {
23    () => {
24        impl_base![signed
25            i8    :"_int_i8"    |"",
26            i16   :"_int_i16"   |"-1",
27            i32   :"_int_i32"   |"-2",
28            i64   :"_int_i64"   |"-3",
29            i128  :"_int_i128"  |"-4",
30            isize :"_int_isize" |"-5"
31        ];
32        impl_base![unsigned
33            u8    :"_int_u8"    |"-6",
34            u16   :"_int_u16"   |"-7",
35            u32   :"_int_u32"   |"-8",
36            u64   :"_int_u64"   |"-9",
37            u128  :"_int_u128"  |"-10",
38            usize :"_int_usize" |"-11"
39        ];
40    };
41    (signed $( $t:ty : $cap:literal | $d:literal ),+) => {
42        $( impl_base![@signed $t:$cap | $d]; )+
43    };
44    (unsigned $( $t:ty : $cap:literal | $d:literal ),+) => {
45        $( impl_base![@unsigned $t:$cap | $d]; )+
46    };
47    (
48    // implements ops on signed primitives
49    @signed $t:ty : $cap:literal | $d:literal) => { paste! {
50        #[doc = crate::doc_availability!(feature = $cap)]
51        ///
52        #[doc = "# Integer base related methods for `" $t "`\n\n"]
53        #[doc = "- [digits](#method.digits" $d ")"]
54        #[doc = "- [digits_sign](#method.digits_sign" $d ")"]
55        #[doc = "- [digits_base](#method.digits_base" $d ")"]
56        #[doc = "- [digits_base_sign](#method.digits_base_sign" $d ")"]
57        #[doc = "- [digital_root](#method.digital_root" $d ")"]
58        #[doc = "- [digital_root_base](#method.digital_root_base" $d ")"]
59        ///
60        #[cfg(feature = $cap )]
61        impl Int<$t> {
62            /// Returns the number of digits in base 10.
63            /// # Examples
64            /// ```
65            /// # use devela::Int;
66            #[doc = "assert_eq![Int(1), Int(0_" $t ").digits()];"]
67            #[doc = "assert_eq![Int(1), Int(-1_" $t ").digits()];"]
68            #[doc = "assert_eq![Int(3), Int(127_" $t ").digits()];"]
69            #[doc = "assert_eq![Int(3), Int(-128_" $t ").digits()];"]
70            /// ```
71            #[must_use]
72            pub const fn digits(self) -> Int<$t> {
73                let a = self.0; let n = iif![a == $t::MIN; $t::MAX; a.abs()];
74                iif![let Some(c) = n.checked_ilog10(); Int(c as $t + 1); Int(1)]
75            }
76
77            /// Returns the number of digits in base 10, including the negative sign.
78            /// # Examples
79            /// ```
80            /// # use devela::Int;
81            #[doc = "assert_eq![Int(1), Int(0_" $t ").digits_sign()];"]
82            #[doc = "assert_eq![Int(2), Int(-1_" $t ").digits_sign()];"]
83            #[doc = "assert_eq![Int(3), Int(127_" $t ").digits_sign()];"]
84            #[doc = "assert_eq![Int(4), Int(-128_" $t ").digits_sign()];"]
85            /// ```
86            #[must_use]
87            pub const fn digits_sign(self) -> Int<$t> {
88                let a = self.0; let mut res = (a < 0) as $t;
89                let n = iif![a == $t::MIN; $t::MAX; a.abs()];
90                res += iif![let Some(c) = n.checked_ilog10(); c as $t + 1; 1];
91                Int(res)
92            }
93
94            /// Returns the number of digits in the given absolute `base`.
95            ///
96            /// If the base is 0, it returns 0.
97            /// # Examples
98            /// ```
99            /// # use devela::Int;
100            #[doc = "assert_eq![Int(2), Int(3_" $t ").digits_base(2)];"]
101            #[doc = "assert_eq![Int(2), Int(127_" $t ").digits_base(16)];"]
102            #[doc = "assert_eq![Int(2), Int(-128_" $t ").digits_base(16)];"]
103            #[doc = "assert_eq![Int(2), Int(-128_" $t ").digits_base(-16)];"]
104            #[doc = "assert_eq![Int(0), Int(100_" $t ").digits_base(0)];"]
105            #[doc = "assert_eq![Int(1), Int(0_" $t ").digits_base(100)];"]
106            /// ```
107            #[must_use]
108            pub const fn digits_base(self, mut base: $t) -> Int<$t> {
109                let mut a = self.0;
110                iif![base == 0; return Int(0)];
111                base = base.abs();
112                a = iif![a == $t::MIN; $t::MAX; a.abs()];
113                iif![let Some(c) = a.checked_ilog(base); Int(c as $t + 1); Int(1)]
114            }
115
116            /// Returns the number of digits in the given absolute `base`,
117            /// including the negative sign.
118            ///
119            /// If the base is 0, it returns 0.
120            /// # Examples
121            /// ```
122            /// # use devela::Int;
123            #[doc = "assert_eq![Int(2), Int(3_" $t ").digits_base_sign(2)];"]
124            #[doc = "assert_eq![Int(2), Int(127_" $t ").digits_base_sign(16)];"]
125            #[doc = "assert_eq![Int(3), Int(-128_" $t ").digits_base_sign(16)];"]
126            #[doc = "assert_eq![Int(3), Int(-128_" $t ").digits_base_sign(-16)];"]
127            #[doc = "assert_eq![Int(0), Int(100_" $t ").digits_base_sign(0)];"]
128            #[doc = "assert_eq![Int(1), Int(0_" $t ").digits_base_sign(100)];"]
129            /// ```
130            #[must_use]
131            pub const fn digits_base_sign(self, mut base: $t) -> Int<$t> {
132                let mut a = self.0;
133                iif![base == 0; return Int(0)];
134                base = base.abs();
135                let mut res = (a < 0) as $t;
136                a = iif![a == $t::MIN; $t::MAX; a.abs()];
137                res += iif![let Some(c) = a.checked_ilog(base); c as $t + 1; 1];
138                Int(res)
139            }
140
141            /* signed digital_root */
142
143            /// Returns the digital root in base 10.
144            /// # Examples
145            /// ```
146            /// # use devela::Int;
147            #[doc = "assert_eq![Int(1), Int(127_" $t ").digital_root()];"]
148            #[doc = "assert_eq![Int(1), Int(-127_" $t ").digital_root()];"]
149            #[doc = "assert_eq![Int(9), Int(126_" $t ").digital_root()];"]
150            /// ```
151            #[must_use]
152            pub const fn digital_root(self) -> Int<$t> {
153                let mut n = self.0.abs();
154                let mut sum = 0;
155                while n > 0 {
156                    sum += n % 10;
157                    n /= 10;
158                    iif![n == 0 && sum >= 10; { n = sum; sum = 0; }];
159                }
160                Int(sum)
161            }
162
163            /// Returns the digital root in in the given absolute `base`.
164            /// # Examples
165            /// ```
166            /// # use devela::Int;
167            #[doc = "assert_eq![Int(1), Int(127_" $t ").digital_root_base(10)];"]
168            #[doc = "assert_eq![Int(1), Int(127_" $t ").digital_root_base(-10)];"]
169            #[doc = "assert_eq![Int(1), Int(-127_" $t ").digital_root_base(-10)];"]
170            #[doc = "assert_eq![Int(9), Int(-126_" $t ").digital_root_base(10)];"]
171            #[doc = "assert_eq![Int(3), Int(-33_" $t ").digital_root_base(16)];"]
172            /// ```
173            #[must_use]
174            pub const fn digital_root_base(self, base: $t) -> Int<$t> {
175                let (mut n, base) = (self.0.abs(), base.abs());
176                let mut sum = 0;
177                while n > 0 {
178                    sum += n % base;
179                    n /= base;
180                    iif![n == 0 && sum >= base; { n = sum; sum = 0; }];
181                }
182                Int(sum)
183            }
184        }
185    }};
186    (
187    // implements ops on unsigned primitives
188    @unsigned $t:ty : $cap:literal | $d:literal) => { paste! {
189        #[doc = crate::doc_availability!(feature = $cap)]
190        ///
191        #[doc = "# Integer base related methods for `" $t "`\n\n"]
192        #[doc = "- [digits](#method.digits" $d ")"]
193        #[doc = "- [digits_sign](#method.digits_sign" $d ")"]
194        #[doc = "- [digits_base](#method.digits_base" $d ")"]
195        #[doc = "- [digits_base_sign](#method.digits_base_sign" $d ")"]
196        #[doc = "- [digital_root](#method.digital_root" $d ")"]
197        #[doc = "- [digital_root_base](#method.digital_root_base" $d ")"]
198        ///
199        #[cfg(feature = $cap )]
200        impl Int<$t> {
201            /* unsigned digits */
202
203            /// Returns the number of digits in base 10.
204            /// # Examples
205            /// ```
206            /// # use devela::Int;
207            #[doc = "assert_eq![Int(1), Int(0_" $t ").digits()];"]
208            #[doc = "assert_eq![Int(3), Int(127_" $t ").digits()];"]
209            /// ```
210            #[must_use]
211            pub const fn digits(self) -> Int<$t> {
212                iif![let Some(c) = self.0.checked_ilog10(); Int(c as $t + 1); Int(1)]
213            }
214
215            /// An alias of [`digits`][Self#digits].
216            #[must_use]
217            pub const fn digits_sign(self) -> Int<$t> { self.digits() }
218
219            /// Returns the number of digits in the given `base`.
220            ///
221            /// If the base is 0, it returns 0.
222            /// # Examples
223            /// ```
224            /// # use devela::Int;
225            #[doc = "assert_eq![Int(2), Int(3_" $t ").digits_base(2)];"]
226            #[doc = "assert_eq![Int(2), Int(127_" $t ").digits_base(16)];"]
227            #[doc = "assert_eq![Int(0), Int(100_" $t ").digits_base(0)];"]
228            #[doc = "assert_eq![Int(1), Int(0_" $t ").digits_base(100)];"]
229            /// ```
230            #[must_use]
231            pub const fn digits_base(self, base: $t) -> Int<$t> {
232                let a = self.0; iif![base == 0; return Int(0)];
233                iif![let Some(c) = a.checked_ilog(base); Int(c as $t + 1); Int(1)]
234            }
235
236            /// An alias of [`digits_base`][Self#digits_base].
237            #[must_use]
238            pub const fn digits_base_sign(self, base: $t) -> Int<$t> { self.digits_base(base) }
239
240            /* unsigned digital_root */
241
242            /// Returns the digital root in base 10.
243            /// # Examples
244            /// ```
245            /// # use devela::Int;
246            #[doc = "assert_eq![Int(1), Int(127_" $t ").digital_root()];"]
247            #[doc = "assert_eq![Int(9), Int(126_" $t ").digital_root()];"]
248            /// ```
249            #[must_use]
250            pub const fn digital_root(self) -> Int<$t> {
251                let [mut a, mut sum] = [self.0, 0];
252                while a > 0 {
253                    sum += a % 10;
254                    a /= 10;
255                    iif![a == 0 && sum >= 10; { a = sum; sum = 0; }];
256                }
257                Int(sum)
258            }
259
260            /// Returns the digital root in in the given absolute `base`.
261            /// # Examples
262            /// ```
263            /// # use devela::Int;
264            #[doc = "assert_eq![Int(1), Int(127_" $t ").digital_root_base(10)];"]
265            #[doc = "assert_eq![Int(3), Int(33_" $t ").digital_root_base(16)];"]
266            /// ```
267            #[must_use]
268            pub const fn digital_root_base(self, base: $t) -> Int<$t> {
269                let [mut a, mut sum] = [self.0, 0];
270                while a > 0 {
271                    sum += a % base;
272                    a /= base;
273                    iif![a == 0 && sum >= base; { a = sum; sum = 0; }];
274                }
275                Int(sum)
276            }
277        }
278    }};
279}
280impl_base!();