1use crate::{
4 arch::word::Word,
5 buffer::Buffer,
6 ibig::IBig,
7 primitive::{self, PrimitiveSigned, PrimitiveUnsigned, DWORD_BYTES, WORD_BITS, WORD_BYTES},
8 repr::{Repr, TypedReprRef::*},
9 ubig::UBig,
10 Sign::*,
11};
12use alloc::{boxed::Box, vec::Vec};
13use core::convert::{TryFrom, TryInto};
14use dashu_base::{
15 Approximation::{self, *},
16 ConversionError, FloatEncoding, Sign,
17};
18
19impl Default for UBig {
20 #[inline]
22 fn default() -> UBig {
23 UBig::ZERO
24 }
25}
26
27impl Default for IBig {
28 #[inline]
30 fn default() -> IBig {
31 IBig::ZERO
32 }
33}
34
35pub(crate) fn words_to_le_bytes(words: &[Word]) -> Vec<u8> {
36 debug_assert!(!words.is_empty());
37
38 let n = words.len();
39 let last = words[n - 1];
40 let skip_last_bytes = last.leading_zeros() as usize / 8;
41 let mut bytes = Vec::with_capacity(n * WORD_BYTES - skip_last_bytes);
42 for word in &words[..n - 1] {
43 bytes.extend_from_slice(&word.to_le_bytes());
44 }
45 let last_bytes = last.to_le_bytes();
46 bytes.extend_from_slice(&last_bytes[..WORD_BYTES - skip_last_bytes]);
47 bytes
48}
49
50pub(crate) fn words_to_be_bytes(words: &[Word]) -> Vec<u8> {
51 debug_assert!(!words.is_empty());
52
53 let n = words.len();
54 let last = words[n - 1];
55 let skip_last_bytes = last.leading_zeros() as usize / 8;
56 let mut bytes = Vec::with_capacity(n * WORD_BYTES - skip_last_bytes);
57 let last_bytes = last.to_be_bytes();
58 bytes.extend_from_slice(&last_bytes[skip_last_bytes..]);
59 for word in words[..n - 1].iter().rev() {
60 bytes.extend_from_slice(&word.to_be_bytes());
61 }
62 bytes
63}
64
65impl Repr {
66 #[inline]
67 pub fn from_le_bytes(bytes: &[u8]) -> Repr {
68 if bytes.len() <= WORD_BYTES {
69 Self::from_word(primitive::word_from_le_bytes_partial(bytes))
71 } else if bytes.len() <= DWORD_BYTES {
72 Self::from_dword(primitive::dword_from_le_bytes_partial(bytes))
73 } else {
74 Self::from_le_bytes_large(bytes)
76 }
77 }
78
79 pub fn from_le_bytes_large(bytes: &[u8]) -> Repr {
80 debug_assert!(bytes.len() >= DWORD_BYTES);
81 let mut buffer = Buffer::allocate((bytes.len() - 1) / WORD_BYTES + 1);
82 let mut chunks = bytes.chunks_exact(WORD_BYTES);
83 for chunk in &mut chunks {
84 buffer.push(Word::from_le_bytes(chunk.try_into().unwrap()));
85 }
86 if !chunks.remainder().is_empty() {
87 buffer.push(primitive::word_from_le_bytes_partial(chunks.remainder()));
88 }
89 Repr::from_buffer(buffer)
90 }
91
92 pub fn from_be_bytes(bytes: &[u8]) -> Repr {
93 if bytes.len() <= WORD_BYTES {
94 Repr::from_word(primitive::word_from_be_bytes_partial(bytes))
96 } else if bytes.len() <= DWORD_BYTES {
97 Repr::from_dword(primitive::dword_from_be_bytes_partial(bytes))
98 } else {
99 Self::from_be_bytes_large(bytes)
101 }
102 }
103
104 pub fn from_be_bytes_large(bytes: &[u8]) -> Repr {
105 debug_assert!(bytes.len() >= DWORD_BYTES);
106 let mut buffer = Buffer::allocate((bytes.len() - 1) / WORD_BYTES + 1);
107 let mut chunks = bytes.rchunks_exact(WORD_BYTES);
108 for chunk in &mut chunks {
109 buffer.push(Word::from_be_bytes(chunk.try_into().unwrap()));
110 }
111 if !chunks.remainder().is_empty() {
112 buffer.push(primitive::word_from_be_bytes_partial(chunks.remainder()));
113 }
114 Repr::from_buffer(buffer)
115 }
116}
117
118impl UBig {
119 #[inline]
128 pub fn from_le_bytes(bytes: &[u8]) -> UBig {
129 UBig(Repr::from_le_bytes(bytes))
130 }
131
132 #[inline]
141 pub fn from_be_bytes(bytes: &[u8]) -> UBig {
142 UBig(Repr::from_be_bytes(bytes))
143 }
144
145 pub fn to_le_bytes(&self) -> Box<[u8]> {
155 match self.repr() {
156 RefSmall(x) => {
157 let bytes = x.to_le_bytes();
158 let skip_bytes = x.leading_zeros() as usize / 8;
159 bytes[..DWORD_BYTES - skip_bytes].into()
160 }
161 RefLarge(words) => words_to_le_bytes(words).into_boxed_slice(),
162 }
163 }
164
165 pub fn to_be_bytes(&self) -> Box<[u8]> {
175 match self.repr() {
176 RefSmall(x) => {
177 let bytes = x.to_be_bytes();
178 let skip_bytes = x.leading_zeros() as usize / 8;
179 bytes[skip_bytes..].into()
180 }
181 RefLarge(words) => words_to_be_bytes(words).into_boxed_slice(),
182 }
183 }
184
185 #[inline]
198 pub fn to_f32(&self) -> Approximation<f32, Sign> {
199 self.repr().to_f32()
200 }
201
202 #[inline]
215 pub fn to_f64(&self) -> Approximation<f64, Sign> {
216 self.repr().to_f64()
217 }
218
219 #[inline]
228 pub const fn as_ibig(&self) -> &IBig {
229 unsafe { core::mem::transmute(self) }
233 }
234}
235
236impl IBig {
237 #[inline]
250 pub fn to_f32(&self) -> Approximation<f32, Sign> {
251 let (sign, mag) = self.as_sign_repr();
252 match mag.to_f32() {
253 Exact(val) => Exact(sign * val),
254 Inexact(val, diff) => Inexact(sign * val, sign * diff),
255 }
256 }
257
258 #[inline]
271 pub fn to_f64(&self) -> Approximation<f64, Sign> {
272 let (sign, mag) = self.as_sign_repr();
273 match mag.to_f64() {
274 Exact(val) => Exact(sign * val),
275 Inexact(val, diff) => Inexact(sign * val, sign * diff),
276 }
277 }
278}
279
280macro_rules! ubig_unsigned_conversions {
281 ($($t:ty)*) => {$(
282 impl From<$t> for UBig {
283 #[inline]
284 fn from(value: $t) -> UBig {
285 UBig::from_unsigned(value)
286 }
287 }
288
289 impl TryFrom<UBig> for $t {
290 type Error = ConversionError;
291
292 #[inline]
293 fn try_from(value: UBig) -> Result<$t, ConversionError> {
294 value.try_to_unsigned()
295 }
296 }
297
298 impl TryFrom<&UBig> for $t {
299 type Error = ConversionError;
300
301 #[inline]
302 fn try_from(value: &UBig) -> Result<$t, ConversionError> {
303 value.try_to_unsigned()
304 }
305 }
306 )*};
307}
308ubig_unsigned_conversions!(u8 u16 u32 u64 u128 usize);
309
310impl From<bool> for UBig {
311 #[inline]
312 fn from(b: bool) -> UBig {
313 u8::from(b).into()
314 }
315}
316
317macro_rules! ubig_signed_conversions {
318 ($($t:ty)*) => {$(
319 impl TryFrom<$t> for UBig {
320 type Error = ConversionError;
321
322 #[inline]
323 fn try_from(value: $t) -> Result<UBig, ConversionError> {
324 UBig::try_from_signed(value)
325 }
326 }
327
328 impl TryFrom<UBig> for $t {
329 type Error = ConversionError;
330
331 #[inline]
332 fn try_from(value: UBig) -> Result<$t, ConversionError> {
333 value.try_to_signed()
334 }
335 }
336
337 impl TryFrom<&UBig> for $t {
338 type Error = ConversionError;
339
340 #[inline]
341 fn try_from(value: &UBig) -> Result<$t, ConversionError> {
342 value.try_to_signed()
343 }
344 }
345 )*};
346}
347ubig_signed_conversions!(i8 i16 i32 i64 i128 isize);
348
349macro_rules! ibig_unsigned_conversions {
350 ($($t:ty)*) => {$(
351 impl From<$t> for IBig {
352 #[inline]
353 fn from(value: $t) -> IBig {
354 IBig::from_unsigned(value)
355 }
356 }
357
358 impl TryFrom<IBig> for $t {
359 type Error = ConversionError;
360
361 #[inline]
362 fn try_from(value: IBig) -> Result<$t, ConversionError> {
363 value.try_to_unsigned()
364 }
365 }
366
367 impl TryFrom<&IBig> for $t {
368 type Error = ConversionError;
369
370 #[inline]
371 fn try_from(value: &IBig) -> Result<$t, ConversionError> {
372 value.try_to_unsigned()
373 }
374 }
375 )*};
376}
377
378ibig_unsigned_conversions!(u8 u16 u32 u64 u128 usize);
379
380impl From<bool> for IBig {
381 #[inline]
382 fn from(b: bool) -> IBig {
383 u8::from(b).into()
384 }
385}
386
387macro_rules! ibig_signed_conversions {
388 ($($t:ty)*) => {$(
389 impl From<$t> for IBig {
390 #[inline]
391 fn from(value: $t) -> IBig {
392 IBig::from_signed(value)
393 }
394 }
395
396 impl TryFrom<IBig> for $t {
397 type Error = ConversionError;
398
399 #[inline]
400 fn try_from(value: IBig) -> Result<$t, ConversionError> {
401 value.try_to_signed()
402 }
403 }
404
405 impl TryFrom<&IBig> for $t {
406 type Error = ConversionError;
407
408 #[inline]
409 fn try_from(value: &IBig) -> Result<$t, ConversionError> {
410 value.try_to_signed()
411 }
412 }
413 )*};
414}
415ibig_signed_conversions!(i8 i16 i32 i64 i128 isize);
416
417macro_rules! ubig_float_conversions {
418 ($($t:ty)*) => {$(
419 impl TryFrom<$t> for UBig {
420 type Error = ConversionError;
421
422 fn try_from(value: $t) -> Result<Self, Self::Error> {
423 let (man, exp) = value.decode().map_err(|_| ConversionError::OutOfBounds)?;
424 let mut result: UBig = man.try_into()?;
425 if exp >= 0 {
426 result <<= exp as usize;
427 } else {
428 result >>= (-exp) as usize;
429 }
430 Ok(result)
431 }
432 }
433 )*};
434}
435ubig_float_conversions!(f32 f64);
436
437macro_rules! ibig_float_conversions {
438 ($($t:ty)*) => {$(
439 impl TryFrom<$t> for IBig {
440 type Error = ConversionError;
441
442 fn try_from(value: $t) -> Result<Self, Self::Error> {
443 let (man, exp) = value.decode().map_err(|_| ConversionError::OutOfBounds)?;
444 let mut result: IBig = man.into();
445 if exp >= 0 {
446 result <<= exp as usize;
447 } else {
448 result >>= (-exp) as usize;
449 }
450 Ok(result)
451 }
452 }
453 )*};
454}
455ibig_float_conversions!(f32 f64);
456
457impl From<UBig> for IBig {
458 #[inline]
459 fn from(x: UBig) -> IBig {
460 IBig(x.0.with_sign(Positive))
461 }
462}
463
464impl TryFrom<IBig> for UBig {
465 type Error = ConversionError;
466
467 #[inline]
468 fn try_from(x: IBig) -> Result<UBig, ConversionError> {
469 match x.sign() {
470 Positive => Ok(UBig(x.0)),
471 Negative => Err(ConversionError::OutOfBounds),
472 }
473 }
474}
475
476impl UBig {
477 #[inline]
479 pub(crate) fn from_unsigned<T>(x: T) -> UBig
480 where
481 T: PrimitiveUnsigned,
482 {
483 UBig(Repr::from_unsigned(x))
484 }
485
486 #[inline]
488 fn try_from_signed<T>(x: T) -> Result<UBig, ConversionError>
489 where
490 T: PrimitiveSigned,
491 {
492 let (sign, mag) = x.to_sign_magnitude();
493 match sign {
494 Sign::Positive => Ok(UBig(Repr::from_unsigned(mag))),
495 Sign::Negative => Err(ConversionError::OutOfBounds),
496 }
497 }
498
499 #[inline]
501 pub(crate) fn try_to_unsigned<T>(&self) -> Result<T, ConversionError>
502 where
503 T: PrimitiveUnsigned,
504 {
505 self.repr().try_to_unsigned()
506 }
507
508 #[inline]
510 fn try_to_signed<T>(&self) -> Result<T, ConversionError>
511 where
512 T: PrimitiveSigned,
513 {
514 T::try_from_sign_magnitude(Sign::Positive, self.repr().try_to_unsigned()?)
515 }
516}
517
518impl IBig {
519 #[inline]
521 pub(crate) fn from_unsigned<T: PrimitiveUnsigned>(x: T) -> IBig {
522 IBig(Repr::from_unsigned(x))
523 }
524
525 #[inline]
527 pub(crate) fn from_signed<T: PrimitiveSigned>(x: T) -> IBig {
528 let (sign, mag) = x.to_sign_magnitude();
529 IBig(Repr::from_unsigned(mag).with_sign(sign))
530 }
531
532 #[inline]
534 pub(crate) fn try_to_unsigned<T: PrimitiveUnsigned>(&self) -> Result<T, ConversionError> {
535 let (sign, mag) = self.as_sign_repr();
536 match sign {
537 Positive => mag.try_to_unsigned(),
538 Negative => Err(ConversionError::OutOfBounds),
539 }
540 }
541
542 #[inline]
544 pub(crate) fn try_to_signed<T: PrimitiveSigned>(&self) -> Result<T, ConversionError> {
545 let (sign, mag) = self.as_sign_repr();
546 T::try_from_sign_magnitude(sign, mag.try_to_unsigned()?)
547 }
548}
549
550mod repr {
551 use core::cmp::Ordering;
552
553 use static_assertions::const_assert;
554
555 use super::*;
556 use crate::repr::TypedReprRef;
557
558 fn unsigned_from_words<T>(words: &[Word]) -> Result<T, ConversionError>
560 where
561 T: PrimitiveUnsigned,
562 {
563 debug_assert!(words.len() >= 2);
564 let t_words = T::BYTE_SIZE / WORD_BYTES;
565 if t_words <= 1 || words.len() > t_words {
566 Err(ConversionError::OutOfBounds)
567 } else {
568 assert!(
569 T::BIT_SIZE % WORD_BITS == 0,
570 "A large primitive type not a multiple of word size."
571 );
572 let mut repr = T::default().to_le_bytes();
573 let bytes: &mut [u8] = repr.as_mut();
574 for (idx, w) in words.iter().enumerate() {
575 let pos = idx * WORD_BYTES;
576 bytes[pos..pos + WORD_BYTES].copy_from_slice(&w.to_le_bytes());
577 }
578 Ok(T::from_le_bytes(repr))
579 }
580 }
581
582 impl Repr {
583 #[inline]
584 pub fn from_unsigned<T>(x: T) -> Self
585 where
586 T: PrimitiveUnsigned,
587 {
588 if let Ok(w) = x.try_into() {
589 Self::from_word(w)
590 } else if let Ok(dw) = x.try_into() {
591 Self::from_dword(dw)
592 } else {
593 let repr = x.to_le_bytes();
594 Self::from_le_bytes_large(repr.as_ref())
595 }
596 }
597 }
598
599 impl<'a> TypedReprRef<'a> {
600 #[inline]
601 pub fn try_to_unsigned<T>(self) -> Result<T, ConversionError>
602 where
603 T: PrimitiveUnsigned,
604 {
605 match self {
606 RefSmall(dw) => T::try_from(dw).map_err(|_| ConversionError::OutOfBounds),
607 RefLarge(words) => unsigned_from_words(words),
608 }
609 }
610
611 #[inline]
612 #[allow(clippy::unnecessary_cast)] pub fn to_f32(self) -> Approximation<f32, Sign> {
614 match self {
615 RefSmall(dword) => to_f32_small(dword as u128),
616 RefLarge(_) => match self.try_to_unsigned::<u128>() {
617 Ok(val) => to_f32_small(val as u128),
618 Err(_) => self.to_f32_nontrivial(),
619 },
620 }
621 }
622
623 #[inline]
624 fn to_f32_nontrivial(self) -> Approximation<f32, Sign> {
625 let n = self.bit_len();
626 debug_assert!(n > 32);
627
628 if n > 128 {
629 Inexact(f32::INFINITY, Positive)
630 } else {
631 let top_u31: u32 = (self >> (n - 31)).as_typed().try_to_unsigned().unwrap();
632 let extra_bit = self.are_low_bits_nonzero(n - 31) as u32;
633 f32::encode((top_u31 | extra_bit) as i32, (n - 31) as i16)
634 }
635 }
636
637 #[inline]
638 #[allow(clippy::unnecessary_cast)] pub fn to_f64(self) -> Approximation<f64, Sign> {
640 match self {
641 RefSmall(dword) => to_f64_small(dword as u128),
642 RefLarge(_) => match self.try_to_unsigned::<u128>() {
643 Ok(val) => to_f64_small(val as u128),
644 Err(_) => self.to_f64_nontrivial(),
645 },
646 }
647 }
648
649 #[inline]
650 fn to_f64_nontrivial(self) -> Approximation<f64, Sign> {
651 let n = self.bit_len();
652 debug_assert!(n > 64);
653
654 if n > 1024 {
655 Inexact(f64::INFINITY, Positive)
656 } else {
657 let top_u63: u64 = (self >> (n - 63)).as_typed().try_to_unsigned().unwrap();
658 let extra_bit = self.are_low_bits_nonzero(n - 63) as u64;
659 f64::encode((top_u63 | extra_bit) as i64, (n - 63) as i16)
660 }
661 }
662 }
663
664 fn to_f32_small(dword: u128) -> Approximation<f32, Sign> {
665 let f = dword as f32;
666 if f.is_infinite() {
667 return Inexact(f, Sign::Positive);
668 }
669
670 let back = f as u128;
671 match back.partial_cmp(&dword).unwrap() {
672 Ordering::Greater => Inexact(f, Sign::Positive),
673 Ordering::Equal => Exact(f),
674 Ordering::Less => Inexact(f, Sign::Negative),
675 }
676 }
677
678 fn to_f64_small(dword: u128) -> Approximation<f64, Sign> {
679 const_assert!((u128::MAX as f64) < f64::MAX);
680 let f = dword as f64;
681 let back = f as u128;
682
683 match back.partial_cmp(&dword).unwrap() {
684 Ordering::Greater => Inexact(f, Sign::Positive),
685 Ordering::Equal => Exact(f),
686 Ordering::Less => Inexact(f, Sign::Negative),
687 }
688 }
689}
690
691