1use super::helpers::impl_try_from;
7#[cfg(feature = "_float_f64")]
8#[allow(unused_imports)]
9use crate::ExtFloat;
10#[cfg(feature = "alloc")]
11#[allow(unused_imports)]
12use crate::{vec_ as vec, Vec};
13
14#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
18#[non_exhaustive]
19pub enum UnitBi {
20 Yobi = 80,
22 Zebi = 70,
24 Exbi = 60,
26 Pebi = 50,
28 Tebi = 40,
30 Gibi = 30,
32 Mebi = 20,
34 Kibi = 10,
36 #[default]
38 None = 0,
39}
40
41#[allow(non_upper_case_globals)]
43impl UnitBi {
44 pub const Yi: Self = Self::Yobi;
46 pub const Y: Self = Self::Yobi;
48 pub const Zi: Self = Self::Zebi;
50 pub const Z: Self = Self::Zebi;
52 pub const Ei: Self = Self::Exbi;
54 pub const E: Self = Self::Exbi;
56 pub const Pi: Self = Self::Pebi;
58 pub const P: Self = Self::Pebi;
60 pub const Ti: Self = Self::Tebi;
62 pub const T: Self = Self::Tebi;
64 pub const Gi: Self = Self::Gibi;
66 pub const G: Self = Self::Gibi;
68 pub const Mi: Self = Self::Mebi;
70 pub const M: Self = Self::Mebi;
72 pub const Ki: Self = Self::Kibi;
74 pub const k: Self = Self::Kibi;
76 pub const K: Self = Self::Kibi;
78}
79
80impl UnitBi {
81 #[must_use]
89 pub const fn symbol(&self) -> &str {
90 match self {
91 UnitBi::Yobi => "Yi",
92 UnitBi::Zebi => "Zi",
93 UnitBi::Exbi => "Ei",
94 UnitBi::Pebi => "Pi",
95 UnitBi::Tebi => "Ti",
96 UnitBi::Gibi => "Gi",
97 UnitBi::Mebi => "Mi",
98 UnitBi::Kibi => "Ki",
99 UnitBi::None => "",
100 }
101 }
102 #[must_use]
104 pub const fn symbol_ascii(&self) -> &str {
105 self.symbol()
106 }
107
108 #[must_use]
110 pub const fn name(&self) -> &str {
111 match self {
112 UnitBi::Yobi => "yobi",
113 UnitBi::Zebi => "zebi",
114 UnitBi::Exbi => "exbi",
115 UnitBi::Pebi => "pebi",
116 UnitBi::Tebi => "tibi",
117 UnitBi::Gibi => "gibi",
118 UnitBi::Mebi => "mibi",
119 UnitBi::Kibi => "kibi",
120 UnitBi::None => "",
121 }
122 }
123
124 pub const BASE: i32 = 2;
126
127 #[must_use]
132 pub const fn exp(&self) -> i32 {
133 match self {
134 UnitBi::Yobi => 80,
135 UnitBi::Zebi => 70,
136 UnitBi::Exbi => 60,
137 UnitBi::Pebi => 50,
138 UnitBi::Tebi => 40,
139 UnitBi::Gibi => 30,
140 UnitBi::Mebi => 20,
141 UnitBi::Kibi => 10,
142 UnitBi::None => 0,
143 }
144 }
145
146 #[must_use]
148 pub const fn factor(&self) -> f64 {
149 match self {
150 UnitBi::Yobi => 1_208_925_819_614_629_174_706_176.,
151 UnitBi::Zebi => 1_180_591_620_717_411_303_424.,
152 UnitBi::Exbi => 1_152_921_504_606_846_976.,
153 UnitBi::Pebi => 1_125_899_906_842_624.,
154 UnitBi::Tebi => 1_099_511_627_776.,
155 UnitBi::Gibi => 1_073_741_824.,
156 UnitBi::Mebi => 1_048_576.,
157 UnitBi::Kibi => 1_024.,
158 UnitBi::None => 1.,
159 }
160 }
161
162 #[must_use]
166 pub const fn factor_i64_checked(&self) -> Option<i64> {
167 match self {
168 UnitBi::Exbi => Some(1_152_921_504_606_846_976),
169 UnitBi::Pebi => Some(1_125_899_906_842_624),
170 UnitBi::Tebi => Some(1_099_511_627_776),
171 UnitBi::Gibi => Some(1_073_741_824),
172 UnitBi::Mebi => Some(1_048_576),
173 UnitBi::Kibi => Some(1_024),
174 UnitBi::None => Some(1),
175 _ => None,
176 }
177 }
178
179 #[must_use]
183 pub const fn factor_i64(&self) -> i64 {
184 match self {
185 UnitBi::Exbi => 1_152_921_504_606_846_976,
186 UnitBi::Pebi => 1_125_899_906_842_624,
187 UnitBi::Tebi => 1_099_511_627_776,
188 UnitBi::Gibi => 1_073_741_824,
189 UnitBi::Mebi => 1_048_576,
190 UnitBi::Kibi => 1_024,
191 UnitBi::None => 1,
192 _ => 0,
193 }
194 }
195
196 pub const fn factor_i128(&self) -> i128 {
198 match self {
199 UnitBi::Yobi => 1_208_925_819_614_629_174_706_176,
200 UnitBi::Zebi => 1_180_591_620_717_411_303_424,
201 _ => self.factor_i64() as i128,
202 }
203 }
204
205 #[must_use]
208 pub fn convert(value: f64, from: Self, to: Self) -> f64 {
209 if from == to {
210 return value;
211 }
212 let (from_factor, to_factor) = (from.factor(), to.factor());
213 value * (from_factor / to_factor)
214 }
215
216 #[must_use]
219 pub fn convert_i64(value: i64, from: Self, to: Self) -> (i64, i64) {
220 if from == to {
221 return (value, 0);
222 }
223 let (from_factor, to_factor) = (from.factor_i64(), to.factor_i64());
224 let converted = value * from_factor / to_factor;
225 let remainder = value * from_factor % to_factor;
226 (converted, remainder)
227 }
228
229 #[must_use]
232 pub fn convert_i128(value: i128, from: Self, to: Self) -> (i128, i128) {
233 if from == to {
234 return (value, 0);
235 }
236 let (from_factor, to_factor) = (from.factor_i128(), to.factor_i128());
237 let converted = value * from_factor / to_factor;
238 let remainder = value * from_factor % to_factor;
239 (converted, remainder)
240 }
241
242 #[must_use]
251 #[cfg(any(feature = "std", feature = "_float_f64"))]
252 #[cfg_attr(feature = "nightly_doc", doc(cfg(any(feature = "std", feature = "_float_f64"))))]
253 pub fn reduce(value: f64) -> (f64, Self) {
254 match value.abs() {
255 value if value >= UnitBi::Yobi.factor() => {
256 (value / UnitBi::Yobi.factor(), UnitBi::Yobi)
257 }
258 value if value >= UnitBi::Zebi.factor() => {
259 (value / UnitBi::Zebi.factor(), UnitBi::Zebi)
260 }
261 value if value >= UnitBi::Exbi.factor() => {
262 (value / UnitBi::Exbi.factor(), UnitBi::Exbi)
263 }
264 value if value >= UnitBi::Pebi.factor() => {
265 (value / UnitBi::Pebi.factor(), UnitBi::Pebi)
266 }
267 value if value >= UnitBi::Tebi.factor() => {
268 (value / UnitBi::Tebi.factor(), UnitBi::Tebi)
269 }
270 value if value >= UnitBi::Gibi.factor() => {
271 (value / UnitBi::Gibi.factor(), UnitBi::Gibi)
272 }
273 value if value >= UnitBi::Mebi.factor() => {
274 (value / UnitBi::Mebi.factor(), UnitBi::Mebi)
275 }
276 value if value >= UnitBi::Kibi.factor() => {
277 (value / UnitBi::Kibi.factor(), UnitBi::Kibi)
278 }
279 _ => (value, UnitBi::None),
280 }
281 }
282
283 #[must_use]
292 pub const fn reduce_i64(value: i64) -> (i64, Self, i64) {
293 match value {
294 value if value >= UnitBi::Exbi.factor_i64() => {
295 (value / UnitBi::Exbi.factor_i64(), UnitBi::Exbi, value % UnitBi::Exbi.factor_i64())
296 }
297 value if value >= UnitBi::Pebi.factor_i64() => {
298 (value / UnitBi::Pebi.factor_i64(), UnitBi::Pebi, value % UnitBi::Pebi.factor_i64())
299 }
300 value if value >= UnitBi::Tebi.factor_i64() => {
301 (value / UnitBi::Tebi.factor_i64(), UnitBi::Tebi, value % UnitBi::Tebi.factor_i64())
302 }
303 value if value >= UnitBi::Gibi.factor_i64() => {
304 (value / UnitBi::Gibi.factor_i64(), UnitBi::Gibi, value % UnitBi::Gibi.factor_i64())
305 }
306 value if value >= UnitBi::Mebi.factor_i64() => {
307 (value / UnitBi::Mebi.factor_i64(), UnitBi::Mebi, value % UnitBi::Mebi.factor_i64())
308 }
309 value if value >= UnitBi::Kibi.factor_i64() => {
310 (value / UnitBi::Kibi.factor_i64(), UnitBi::Kibi, value % UnitBi::Kibi.factor_i64())
311 }
312 _ => (value, UnitBi::None, 0),
313 }
314 }
315
316 #[must_use]
325 pub const fn reduce_i128(value: i128) -> (i128, Self, i128) {
326 match value {
327 value if value >= UnitBi::Yobi.factor_i128() => (
328 value / UnitBi::Yobi.factor_i128(),
329 UnitBi::Yobi,
330 value % UnitBi::Yobi.factor_i128(),
331 ),
332 value if value >= UnitBi::Zebi.factor_i128() => (
333 value / UnitBi::Zebi.factor_i128(),
334 UnitBi::Zebi,
335 value % UnitBi::Zebi.factor_i128(),
336 ),
337 value if value >= UnitBi::Exbi.factor_i128() => (
338 value / UnitBi::Exbi.factor_i128(),
339 UnitBi::Exbi,
340 value % UnitBi::Exbi.factor_i128(),
341 ),
342 _ => {
343 let (v, p, r) = Self::reduce_i64(value as i64);
344 (v as i128, p, r as i128)
345 }
346 }
347 }
348
349 #[must_use]
352 #[cfg(any(feature = "std", all(feature = "alloc", feature = "_float_f64")))]
353 #[cfg_attr(
354 feature = "nightly_doc",
355 doc(cfg(any(feature = "std", all(feature = "alloc", feature = "_float_f64"))))
356 )]
357 pub fn reduce_chain(value: f64, threshold: f64) -> Vec<(f64, Self)> {
358 if value == 0.0 {
359 return vec![(0.0, UnitBi::None)];
360 }
361
362 let mut result = Vec::new();
363 let mut remainder = value;
364
365 let effective_threshold =
368 if threshold <= 0.0 { crate::ExtFloatConst::MEDIUM_MARGIN } else { threshold };
369
370 while remainder.abs() > effective_threshold {
371 let (size, unit) = Self::reduce(remainder);
372 let integer_part = size.trunc();
373 let fractional_part = size - integer_part;
374 result.push((integer_part, unit));
375 remainder = fractional_part * unit.factor();
376
377 if remainder.abs() < effective_threshold {
378 break;
379 }
380 }
381 if remainder.abs() >= effective_threshold {
382 result.push((remainder, UnitBi::None));
383 }
384 result
385 }
386
387 #[must_use]
390 #[cfg(feature = "alloc")]
391 #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = "alloc")))]
392 pub fn reduce_chain_i64(value: i64, threshold: i64) -> Vec<(i64, Self)> {
393 let mut result = Vec::new();
394 let mut remainder = value;
395
396 while remainder > threshold {
397 let (size, unit, new_remainder) = Self::reduce_i64(remainder);
398 result.push((size, unit));
399 remainder = new_remainder;
400
401 if remainder < threshold {
402 break;
403 }
404 }
405 if remainder >= threshold {
406 result.push((remainder, UnitBi::None));
407 }
408 result
409 }
410
411 #[must_use]
414 #[cfg(feature = "alloc")]
415 #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = "alloc")))]
416 pub fn reduce_chain_i128(value: i128, threshold: i128) -> Vec<(i128, Self)> {
417 let mut result = Vec::new();
418 let mut remainder = value;
419
420 while remainder > threshold {
421 let (size, unit, new_remainder) = Self::reduce_i128(remainder);
422 result.push((size, unit));
423 remainder = new_remainder;
424
425 if remainder < threshold {
426 break;
427 }
428 }
429 if remainder > threshold {
430 result.push((remainder, UnitBi::None));
431 }
432 result
433 }
434}
435
436impl UnitBi {
437 pub fn asc_iter() -> impl Iterator<Item = Self> {
439 const UNITS: [UnitBi; 9] = [
440 UnitBi::None,
441 UnitBi::Kibi,
442 UnitBi::Mebi,
443 UnitBi::Gibi,
444 UnitBi::Tebi,
445 UnitBi::Pebi,
446 UnitBi::Exbi,
447 UnitBi::Zebi,
448 UnitBi::Yobi,
449 ];
450 UNITS.iter().copied()
451 }
452
453 pub fn desc_iter() -> impl Iterator<Item = Self> {
455 const UNITS: [UnitBi; 9] = [
456 UnitBi::Yobi,
457 UnitBi::Zebi,
458 UnitBi::Exbi,
459 UnitBi::Pebi,
460 UnitBi::Tebi,
461 UnitBi::Gibi,
462 UnitBi::Mebi,
463 UnitBi::Kibi,
464 UnitBi::None,
465 ];
466 UNITS.iter().copied()
467 }
468}
469
470impl From<UnitBi> for f32 {
471 fn from(from: UnitBi) -> f32 {
472 from.factor() as f32
473 }
474}
475impl From<UnitBi> for f64 {
476 fn from(from: UnitBi) -> f64 {
477 from.factor()
478 }
479}
480impl From<UnitBi> for i64 {
481 fn from(from: UnitBi) -> i64 {
482 from.factor_i64()
483 }
484}
485impl From<UnitBi> for i128 {
486 fn from(from: UnitBi) -> i128 {
487 from.factor_i128()
488 }
489}
490impl_try_from![UnitBi, i64 => i32, i16, u64, u32, u16];
491impl_try_from![UnitBi, i128 => u128];
492
493#[cfg(test)]
494mod tests {
495 use super::{
496 UnitBi,
497 UnitBi::{Exbi, Gibi, Kibi, Mebi, Yobi, Zebi},
498 };
499 #[cfg(any(feature = "std", all(feature = "alloc", feature = "_float_f64")))]
500 use crate::vec_ as vec;
501 #[allow(unused_imports)]
502 use crate::ExtFloatConst;
503
504 #[test]
507 fn unit_bi_reduce_i64() {
508 let value = i64::from(Exbi);
509 let (reduced_value, unit, remainder) = UnitBi::reduce_i64(value);
510 assert_eq!(reduced_value, 1);
511 assert_eq!(unit, Exbi);
512 assert_eq!(remainder, 0);
513
514 let value = 2_000_000_000; let (reduced_value, unit, remainder) = UnitBi::reduce_i64(value);
516 assert_eq!(reduced_value, 1);
517 assert_eq!(unit, Gibi);
518 assert_eq!(remainder, 926_258_176); let value = 2 * i64::from(Kibi) + 512;
521 let (reduced_value, unit, remainder) = UnitBi::reduce_i64(value);
522 assert_eq!(reduced_value, 2);
523 assert_eq!(unit, Kibi);
524 assert_eq!(remainder, 512);
525 }
526
527 #[test]
528 fn unit_bi_reduce_i128() {
529 let value = Yobi.factor_i128();
530 let (reduced_value, unit, remainder) = UnitBi::reduce_i128(value);
531 assert_eq!(reduced_value, 1);
532 assert_eq!(unit, Yobi);
533 assert_eq!(remainder, 0);
534
535 let value = i128::from(Zebi) + i128::from(Mebi);
536 let (reduced_value, unit, remainder) = UnitBi::reduce_i128(value);
537 assert_eq!(reduced_value, 1);
538 assert_eq!(unit, Zebi);
539 assert_eq!(remainder, Mebi.factor_i128());
540 }
541
542 #[test]
545 #[cfg(any(feature = "std", all(feature = "alloc", feature = "_float_f64")))]
546 #[cfg_attr(
547 feature = "nightly_doc",
548 doc(cfg(any(feature = "std", all(feature = "alloc", feature = "_float_f64"))))
549 )]
550 fn unit_bi_reduce_chain() {
551 let margin = f64::MEDIUM_MARGIN;
552
553 assert_eq![
554 UnitBi::reduce_chain(Gibi.factor(), margin),
556 vec![(1.0, Gibi)]
557 ];
558 assert_eq![
559 UnitBi::reduce_chain(1.5 * Gibi.factor(), margin),
561 vec![(1.0, Gibi), (512.0, Mebi)]
562 ];
563 assert_eq![
564 UnitBi::reduce_chain(Gibi.factor() + Kibi.factor(), margin),
566 vec![(1., Gibi), (1., Kibi)]
567 ];
568 assert_eq![
569 UnitBi::reduce_chain(Mebi.factor() / 2., margin),
571 vec![(512., Kibi)]
572 ];
573 assert_eq![
574 UnitBi::reduce_chain(3. * Yobi.factor() + 2. * Zebi.factor() + Gibi.factor(), margin),
576 vec![(3.0, Yobi), (2.0, Zebi), (1.0, Gibi)]
577 ];
578 assert_eq![
579 UnitBi::reduce_chain(0.0, margin),
581 vec![(0.0, UnitBi::None)]
582 ];
583 assert_eq![
584 UnitBi::reduce_chain(Gibi.factor() / 2., margin),
586 vec![(512., Mebi)]
587 ];
588 }
589}