1use super::helpers::impl_try_from;
7#[allow(unused_imports)]
8use crate::ExtFloat;
9#[cfg(feature = "alloc")]
10#[allow(unused_imports)]
11use crate::{Vec, vec_ as vec};
12
13#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
17#[non_exhaustive]
18pub enum UnitBi {
19 Yobi = 80,
21 Zebi = 70,
23 Exbi = 60,
25 Pebi = 50,
27 Tebi = 40,
29 Gibi = 30,
31 Mebi = 20,
33 Kibi = 10,
35 #[default]
37 None = 0,
38}
39
40#[allow(non_upper_case_globals)]
42impl UnitBi {
43 pub const Yi: Self = Self::Yobi;
45 pub const Y: Self = Self::Yobi;
47 pub const Zi: Self = Self::Zebi;
49 pub const Z: Self = Self::Zebi;
51 pub const Ei: Self = Self::Exbi;
53 pub const E: Self = Self::Exbi;
55 pub const Pi: Self = Self::Pebi;
57 pub const P: Self = Self::Pebi;
59 pub const Ti: Self = Self::Tebi;
61 pub const T: Self = Self::Tebi;
63 pub const Gi: Self = Self::Gibi;
65 pub const G: Self = Self::Gibi;
67 pub const Mi: Self = Self::Mebi;
69 pub const M: Self = Self::Mebi;
71 pub const Ki: Self = Self::Kibi;
73 pub const k: Self = Self::Kibi;
75 pub const K: Self = Self::Kibi;
77}
78
79impl UnitBi {
80 #[must_use]
88 pub const fn symbol(&self) -> &str {
89 match self {
90 UnitBi::Yobi => "Yi",
91 UnitBi::Zebi => "Zi",
92 UnitBi::Exbi => "Ei",
93 UnitBi::Pebi => "Pi",
94 UnitBi::Tebi => "Ti",
95 UnitBi::Gibi => "Gi",
96 UnitBi::Mebi => "Mi",
97 UnitBi::Kibi => "Ki",
98 UnitBi::None => "",
99 }
100 }
101 #[must_use]
103 pub const fn symbol_ascii(&self) -> &str {
104 self.symbol()
105 }
106
107 #[must_use]
109 pub const fn name(&self) -> &str {
110 match self {
111 UnitBi::Yobi => "yobi",
112 UnitBi::Zebi => "zebi",
113 UnitBi::Exbi => "exbi",
114 UnitBi::Pebi => "pebi",
115 UnitBi::Tebi => "tibi",
116 UnitBi::Gibi => "gibi",
117 UnitBi::Mebi => "mibi",
118 UnitBi::Kibi => "kibi",
119 UnitBi::None => "",
120 }
121 }
122
123 pub const BASE: i32 = 2;
125
126 #[must_use]
131 pub const fn exp(&self) -> i32 {
132 match self {
133 UnitBi::Yobi => 80,
134 UnitBi::Zebi => 70,
135 UnitBi::Exbi => 60,
136 UnitBi::Pebi => 50,
137 UnitBi::Tebi => 40,
138 UnitBi::Gibi => 30,
139 UnitBi::Mebi => 20,
140 UnitBi::Kibi => 10,
141 UnitBi::None => 0,
142 }
143 }
144
145 #[must_use]
147 pub const fn factor(&self) -> f64 {
148 match self {
149 UnitBi::Yobi => 1_208_925_819_614_629_174_706_176.,
150 UnitBi::Zebi => 1_180_591_620_717_411_303_424.,
151 UnitBi::Exbi => 1_152_921_504_606_846_976.,
152 UnitBi::Pebi => 1_125_899_906_842_624.,
153 UnitBi::Tebi => 1_099_511_627_776.,
154 UnitBi::Gibi => 1_073_741_824.,
155 UnitBi::Mebi => 1_048_576.,
156 UnitBi::Kibi => 1_024.,
157 UnitBi::None => 1.,
158 }
159 }
160
161 #[must_use]
165 pub const fn factor_i64_checked(&self) -> Option<i64> {
166 match self {
167 UnitBi::Exbi => Some(1_152_921_504_606_846_976),
168 UnitBi::Pebi => Some(1_125_899_906_842_624),
169 UnitBi::Tebi => Some(1_099_511_627_776),
170 UnitBi::Gibi => Some(1_073_741_824),
171 UnitBi::Mebi => Some(1_048_576),
172 UnitBi::Kibi => Some(1_024),
173 UnitBi::None => Some(1),
174 _ => None,
175 }
176 }
177
178 #[must_use]
182 pub const fn factor_i64(&self) -> i64 {
183 match self {
184 UnitBi::Exbi => 1_152_921_504_606_846_976,
185 UnitBi::Pebi => 1_125_899_906_842_624,
186 UnitBi::Tebi => 1_099_511_627_776,
187 UnitBi::Gibi => 1_073_741_824,
188 UnitBi::Mebi => 1_048_576,
189 UnitBi::Kibi => 1_024,
190 UnitBi::None => 1,
191 _ => 0,
192 }
193 }
194
195 pub const fn factor_i128(&self) -> i128 {
197 match self {
198 UnitBi::Yobi => 1_208_925_819_614_629_174_706_176,
199 UnitBi::Zebi => 1_180_591_620_717_411_303_424,
200 _ => self.factor_i64() as i128,
201 }
202 }
203
204 #[must_use]
207 pub fn convert(value: f64, from: Self, to: Self) -> f64 {
208 if from == to {
209 return value;
210 }
211 let (from_factor, to_factor) = (from.factor(), to.factor());
212 value * (from_factor / to_factor)
213 }
214
215 #[must_use]
218 pub fn convert_i64(value: i64, from: Self, to: Self) -> (i64, i64) {
219 if from == to {
220 return (value, 0);
221 }
222 let (from_factor, to_factor) = (from.factor_i64(), to.factor_i64());
223 let converted = value * from_factor / to_factor;
224 let remainder = value * from_factor % to_factor;
225 (converted, remainder)
226 }
227
228 #[must_use]
231 pub fn convert_i128(value: i128, from: Self, to: Self) -> (i128, i128) {
232 if from == to {
233 return (value, 0);
234 }
235 let (from_factor, to_factor) = (from.factor_i128(), to.factor_i128());
236 let converted = value * from_factor / to_factor;
237 let remainder = value * from_factor % to_factor;
238 (converted, remainder)
239 }
240
241 #[must_use]
250 pub fn reduce(value: f64) -> (f64, Self) {
251 match value.abs() {
252 value if value >= UnitBi::Yobi.factor() => {
253 (value / UnitBi::Yobi.factor(), UnitBi::Yobi)
254 }
255 value if value >= UnitBi::Zebi.factor() => {
256 (value / UnitBi::Zebi.factor(), UnitBi::Zebi)
257 }
258 value if value >= UnitBi::Exbi.factor() => {
259 (value / UnitBi::Exbi.factor(), UnitBi::Exbi)
260 }
261 value if value >= UnitBi::Pebi.factor() => {
262 (value / UnitBi::Pebi.factor(), UnitBi::Pebi)
263 }
264 value if value >= UnitBi::Tebi.factor() => {
265 (value / UnitBi::Tebi.factor(), UnitBi::Tebi)
266 }
267 value if value >= UnitBi::Gibi.factor() => {
268 (value / UnitBi::Gibi.factor(), UnitBi::Gibi)
269 }
270 value if value >= UnitBi::Mebi.factor() => {
271 (value / UnitBi::Mebi.factor(), UnitBi::Mebi)
272 }
273 value if value >= UnitBi::Kibi.factor() => {
274 (value / UnitBi::Kibi.factor(), UnitBi::Kibi)
275 }
276 _ => (value, UnitBi::None),
277 }
278 }
279
280 #[must_use]
289 pub const fn reduce_i64(value: i64) -> (i64, Self, i64) {
290 match value {
291 value if value >= UnitBi::Exbi.factor_i64() => {
292 (value / UnitBi::Exbi.factor_i64(), UnitBi::Exbi, value % UnitBi::Exbi.factor_i64())
293 }
294 value if value >= UnitBi::Pebi.factor_i64() => {
295 (value / UnitBi::Pebi.factor_i64(), UnitBi::Pebi, value % UnitBi::Pebi.factor_i64())
296 }
297 value if value >= UnitBi::Tebi.factor_i64() => {
298 (value / UnitBi::Tebi.factor_i64(), UnitBi::Tebi, value % UnitBi::Tebi.factor_i64())
299 }
300 value if value >= UnitBi::Gibi.factor_i64() => {
301 (value / UnitBi::Gibi.factor_i64(), UnitBi::Gibi, value % UnitBi::Gibi.factor_i64())
302 }
303 value if value >= UnitBi::Mebi.factor_i64() => {
304 (value / UnitBi::Mebi.factor_i64(), UnitBi::Mebi, value % UnitBi::Mebi.factor_i64())
305 }
306 value if value >= UnitBi::Kibi.factor_i64() => {
307 (value / UnitBi::Kibi.factor_i64(), UnitBi::Kibi, value % UnitBi::Kibi.factor_i64())
308 }
309 _ => (value, UnitBi::None, 0),
310 }
311 }
312
313 #[must_use]
322 pub const fn reduce_i128(value: i128) -> (i128, Self, i128) {
323 match value {
324 value if value >= UnitBi::Yobi.factor_i128() => (
325 value / UnitBi::Yobi.factor_i128(),
326 UnitBi::Yobi,
327 value % UnitBi::Yobi.factor_i128(),
328 ),
329 value if value >= UnitBi::Zebi.factor_i128() => (
330 value / UnitBi::Zebi.factor_i128(),
331 UnitBi::Zebi,
332 value % UnitBi::Zebi.factor_i128(),
333 ),
334 value if value >= UnitBi::Exbi.factor_i128() => (
335 value / UnitBi::Exbi.factor_i128(),
336 UnitBi::Exbi,
337 value % UnitBi::Exbi.factor_i128(),
338 ),
339 _ => {
340 let (v, p, r) = Self::reduce_i64(value as i64);
341 (v as i128, p, r as i128)
342 }
343 }
344 }
345
346 #[must_use]
349 #[cfg(feature = "alloc")]
350 #[cfg_attr(nightly_doc, doc(cfg(feature = "alloc")))]
351 pub fn reduce_chain(value: f64, threshold: f64) -> Vec<(f64, Self)> {
352 if value == 0.0 {
353 return vec![(0.0, UnitBi::None)];
354 }
355
356 let mut result = Vec::new();
357 let mut remainder = value;
358
359 let effective_threshold =
362 if threshold <= 0.0 { crate::FloatConst::MEDIUM_MARGIN } else { threshold };
363
364 while remainder.abs() > effective_threshold {
365 let (size, unit) = Self::reduce(remainder);
366 let integer_part = size.trunc();
367 let fractional_part = size - integer_part;
368 result.push((integer_part, unit));
369 remainder = fractional_part * unit.factor();
370
371 if remainder.abs() < effective_threshold {
372 break;
373 }
374 }
375 if remainder.abs() >= effective_threshold {
376 result.push((remainder, UnitBi::None));
377 }
378 result
379 }
380
381 #[must_use]
384 #[cfg(feature = "alloc")]
385 #[cfg_attr(nightly_doc, doc(cfg(feature = "alloc")))]
386 pub fn reduce_chain_i64(value: i64, threshold: i64) -> Vec<(i64, Self)> {
387 let mut result = Vec::new();
388 let mut remainder = value;
389
390 while remainder > threshold {
391 let (size, unit, new_remainder) = Self::reduce_i64(remainder);
392 result.push((size, unit));
393 remainder = new_remainder;
394
395 if remainder < threshold {
396 break;
397 }
398 }
399 if remainder >= threshold {
400 result.push((remainder, UnitBi::None));
401 }
402 result
403 }
404
405 #[must_use]
408 #[cfg(feature = "alloc")]
409 #[cfg_attr(nightly_doc, doc(cfg(feature = "alloc")))]
410 pub fn reduce_chain_i128(value: i128, threshold: i128) -> Vec<(i128, Self)> {
411 let mut result = Vec::new();
412 let mut remainder = value;
413
414 while remainder > threshold {
415 let (size, unit, new_remainder) = Self::reduce_i128(remainder);
416 result.push((size, unit));
417 remainder = new_remainder;
418
419 if remainder < threshold {
420 break;
421 }
422 }
423 if remainder > threshold {
424 result.push((remainder, UnitBi::None));
425 }
426 result
427 }
428}
429
430impl UnitBi {
431 pub fn asc_iter() -> impl Iterator<Item = Self> {
433 const UNITS: [UnitBi; 9] = [
434 UnitBi::None,
435 UnitBi::Kibi,
436 UnitBi::Mebi,
437 UnitBi::Gibi,
438 UnitBi::Tebi,
439 UnitBi::Pebi,
440 UnitBi::Exbi,
441 UnitBi::Zebi,
442 UnitBi::Yobi,
443 ];
444 UNITS.iter().copied()
445 }
446
447 pub fn desc_iter() -> impl Iterator<Item = Self> {
449 const UNITS: [UnitBi; 9] = [
450 UnitBi::Yobi,
451 UnitBi::Zebi,
452 UnitBi::Exbi,
453 UnitBi::Pebi,
454 UnitBi::Tebi,
455 UnitBi::Gibi,
456 UnitBi::Mebi,
457 UnitBi::Kibi,
458 UnitBi::None,
459 ];
460 UNITS.iter().copied()
461 }
462}
463
464impl From<UnitBi> for f32 {
465 fn from(from: UnitBi) -> f32 {
466 from.factor() as f32
467 }
468}
469impl From<UnitBi> for f64 {
470 fn from(from: UnitBi) -> f64 {
471 from.factor()
472 }
473}
474impl From<UnitBi> for i64 {
475 fn from(from: UnitBi) -> i64 {
476 from.factor_i64()
477 }
478}
479impl From<UnitBi> for i128 {
480 fn from(from: UnitBi) -> i128 {
481 from.factor_i128()
482 }
483}
484impl_try_from![UnitBi, i64 => i32, i16, u64, u32, u16];
485impl_try_from![UnitBi, i128 => u128];
486
487#[cfg(test)]
488mod tests {
489 use super::{
490 UnitBi,
491 UnitBi::{Exbi, Gibi, Kibi, Mebi, Yobi, Zebi},
492 };
493 #[allow(unused_imports)]
494 use crate::FloatConst;
495 #[cfg(feature = "alloc")]
496 use crate::vec_ as vec;
497
498 #[test]
501 fn unit_bi_reduce_i64() {
502 let value = i64::from(Exbi);
503 let (reduced_value, unit, remainder) = UnitBi::reduce_i64(value);
504 assert_eq!(reduced_value, 1);
505 assert_eq!(unit, Exbi);
506 assert_eq!(remainder, 0);
507
508 let value = 2_000_000_000; let (reduced_value, unit, remainder) = UnitBi::reduce_i64(value);
510 assert_eq!(reduced_value, 1);
511 assert_eq!(unit, Gibi);
512 assert_eq!(remainder, 926_258_176); let value = 2 * i64::from(Kibi) + 512;
515 let (reduced_value, unit, remainder) = UnitBi::reduce_i64(value);
516 assert_eq!(reduced_value, 2);
517 assert_eq!(unit, Kibi);
518 assert_eq!(remainder, 512);
519 }
520
521 #[test]
522 fn unit_bi_reduce_i128() {
523 let value = Yobi.factor_i128();
524 let (reduced_value, unit, remainder) = UnitBi::reduce_i128(value);
525 assert_eq!(reduced_value, 1);
526 assert_eq!(unit, Yobi);
527 assert_eq!(remainder, 0);
528
529 let value = i128::from(Zebi) + i128::from(Mebi);
530 let (reduced_value, unit, remainder) = UnitBi::reduce_i128(value);
531 assert_eq!(reduced_value, 1);
532 assert_eq!(unit, Zebi);
533 assert_eq!(remainder, Mebi.factor_i128());
534 }
535
536 #[test]
539 #[cfg(feature = "alloc")]
540 #[cfg_attr(nightly_doc, doc(cfg(feature = "alloc")))]
541 fn unit_bi_reduce_chain() {
542 let margin = f64::MEDIUM_MARGIN;
543
544 assert_eq![
545 UnitBi::reduce_chain(Gibi.factor(), margin),
547 vec![(1.0, Gibi)]
548 ];
549 assert_eq![
550 UnitBi::reduce_chain(1.5 * Gibi.factor(), margin),
552 vec![(1.0, Gibi), (512.0, Mebi)]
553 ];
554 assert_eq![
555 UnitBi::reduce_chain(Gibi.factor() + Kibi.factor(), margin),
557 vec![(1., Gibi), (1., Kibi)]
558 ];
559 assert_eq![
560 UnitBi::reduce_chain(Mebi.factor() / 2., margin),
562 vec![(512., Kibi)]
563 ];
564 assert_eq![
565 UnitBi::reduce_chain(3. * Yobi.factor() + 2. * Zebi.factor() + Gibi.factor(), margin),
567 vec![(3.0, Yobi), (2.0, Zebi), (1.0, Gibi)]
568 ];
569 assert_eq![
570 UnitBi::reduce_chain(0.0, margin),
572 vec![(0.0, UnitBi::None)]
573 ];
574 assert_eq![
575 UnitBi::reduce_chain(Gibi.factor() / 2., margin),
577 vec![(512., Mebi)]
578 ];
579 }
580}