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