Skip to main content

dashu_int/
convert.rs

1//! Conversions between types.
2
3use crate::{
4    add,
5    arch::word::{DoubleWord, Word},
6    buffer::Buffer,
7    helper_macros::debug_assert_zero,
8    ibig::IBig,
9    math,
10    primitive::{
11        self, PrimitiveSigned, PrimitiveUnsigned, DWORD_BITS_USIZE, DWORD_BYTES, WORD_BITS,
12        WORD_BITS_USIZE, WORD_BYTES,
13    },
14    repr::{
15        Repr,
16        TypedReprRef::{self, *},
17    },
18    shift,
19    ubig::UBig,
20    Sign::*,
21};
22use alloc::{boxed::Box, vec, vec::Vec};
23use core::convert::{TryFrom, TryInto};
24use dashu_base::{
25    Approximation::{self, *},
26    BitTest, ConversionError, FloatEncoding, PowerOfTwo, Sign,
27};
28use static_assertions::const_assert;
29
30impl Default for UBig {
31    /// Default value: 0.
32    #[inline]
33    fn default() -> UBig {
34        UBig::ZERO
35    }
36}
37
38impl Default for IBig {
39    /// Default value: 0.
40    #[inline]
41    fn default() -> IBig {
42        IBig::ZERO
43    }
44}
45
46pub(crate) fn words_to_le_bytes<const FLIP: bool>(words: &[Word]) -> Vec<u8> {
47    debug_assert!(!words.is_empty());
48
49    let n = words.len();
50    let last = words[n - 1];
51    let skip_last_bytes = last.leading_zeros() as usize / 8;
52    let mut bytes = Vec::with_capacity(n * WORD_BYTES - skip_last_bytes);
53    for word in &words[..n - 1] {
54        let word = if FLIP { !*word } else { *word };
55        bytes.extend_from_slice(&word.to_le_bytes());
56    }
57    let last = if FLIP { !last } else { last };
58    let last_bytes = last.to_le_bytes();
59    bytes.extend_from_slice(&last_bytes[..WORD_BYTES - skip_last_bytes]);
60    bytes
61}
62
63fn words_to_be_bytes<const FLIP: bool>(words: &[Word]) -> Vec<u8> {
64    debug_assert!(!words.is_empty());
65
66    let n = words.len();
67    let last = words[n - 1];
68    let skip_last_bytes = last.leading_zeros() as usize / 8;
69    let mut bytes = Vec::with_capacity(n * WORD_BYTES - skip_last_bytes);
70    let last = if FLIP { !last } else { last };
71    let last_bytes = last.to_be_bytes();
72    bytes.extend_from_slice(&last_bytes[skip_last_bytes..]);
73    for word in words[..n - 1].iter().rev() {
74        let word = if FLIP { !*word } else { *word };
75        bytes.extend_from_slice(&word.to_be_bytes());
76    }
77    bytes
78}
79
80/// Convert a integer into an array of chunks by bit chunking
81///
82/// Requirements:
83/// - chunks_out.len() tightly fits all chunks.
84/// - All words in chunks_out must has enough length (i.e. ceil(chunk_bits / WORD_BITS))
85fn words_to_chunks(words: &[Word], chunks_out: &mut [&mut [Word]], chunk_bits: usize) {
86    assert!(!words.is_empty());
87
88    if chunk_bits % WORD_BITS_USIZE == 0 {
89        // shortcut for word aligned chunks
90        let words_per_chunk = chunk_bits / WORD_BITS_USIZE;
91        for (i, chunk_out) in chunks_out.iter_mut().enumerate() {
92            let start_pos = i * words_per_chunk;
93            // The last chunk may not have `words_per_chunk` source words —
94            // e.g. a 68-bit value chunked at 64 bits has `words = 3` on a
95            // 32-bit target but `words_per_chunk = 2`, so chunk index 1
96            // would otherwise index `words[2..4]` and panic. Clamp the read
97            // and leave the rest of the (zero-initialised) `chunk_out` alone.
98            let end_pos = (start_pos + words_per_chunk).min(words.len());
99            let copy_len = end_pos - start_pos;
100            chunk_out[..copy_len].copy_from_slice(&words[start_pos..end_pos]);
101        }
102    } else {
103        let bit_len =
104            words.len() * WORD_BITS_USIZE - words.last().unwrap().leading_zeros() as usize;
105        for (i, chunk_out) in chunks_out.iter_mut().enumerate() {
106            let start = i * chunk_bits;
107            let end = bit_len.min(start + chunk_bits);
108            debug_assert!(start < end); // make sure that there is no empty chunk
109
110            let (start_pos, end_pos) = (start / WORD_BITS_USIZE, end / WORD_BITS_USIZE);
111            let end_bits = (end % WORD_BITS_USIZE) as u32;
112            let len;
113            if end_bits != 0 {
114                len = end_pos - start_pos;
115                chunk_out[..=len].copy_from_slice(&words[start_pos..=end_pos]);
116                chunk_out[len] &= math::ones_word(end_bits);
117            } else {
118                len = end_pos - start_pos - 1;
119                chunk_out[..=len].copy_from_slice(&words[start_pos..end_pos]);
120            }
121            shift::shr_in_place(&mut chunk_out[..=len], (start % WORD_BITS_USIZE) as u32);
122        }
123    }
124}
125
126/// Convert chunks to a single integer by shifting and adding.
127///
128/// Requirements:
129/// - words_out must have enough length
130/// - buffer must have enough length: buffer.len() > max(chunk.len()) for chunk in chunks
131fn chunks_to_words(
132    words_out: &mut [Word],
133    chunks: &[&[Word]],
134    chunk_bits: usize,
135    buffer: &mut [Word],
136) {
137    assert!(!chunks.is_empty());
138    for (i, chunk) in chunks.iter().enumerate() {
139        let shift = i * chunk_bits;
140        buffer[..chunk.len()].copy_from_slice(chunk);
141        buffer[chunk.len()] = 0;
142        shift::shl_in_place(&mut buffer[..=chunk.len()], (shift % WORD_BITS_USIZE) as u32);
143        debug_assert_zero!(add::add_in_place(
144            &mut words_out[shift / WORD_BITS_USIZE..],
145            &buffer[..=chunk.len()]
146        ));
147    }
148}
149
150impl TypedReprRef<'_> {
151    fn to_le_bytes(self) -> Vec<u8> {
152        match self {
153            RefSmall(x) => {
154                let bytes = x.to_le_bytes();
155                let skip_bytes = x.leading_zeros() as usize / 8;
156                bytes[..DWORD_BYTES - skip_bytes].into()
157            }
158            RefLarge(words) => words_to_le_bytes::<false>(words),
159        }
160    }
161
162    fn to_signed_le_bytes(self, negate: bool) -> Vec<u8> {
163        // make sure to return empty for zero
164        if let RefSmall(v) = self {
165            if v == 0 {
166                return Vec::new();
167            }
168        }
169
170        let mut bytes = if negate {
171            match self {
172                RefSmall(x) => {
173                    let bytes = (!x + 1).to_le_bytes();
174                    let skip_bytes = x.leading_zeros() as usize / 8;
175                    bytes[..DWORD_BYTES - skip_bytes].into()
176                }
177                RefLarge(words) => {
178                    let mut buffer = Buffer::from(words);
179                    debug_assert_zero!(add::sub_one_in_place(&mut buffer));
180                    words_to_le_bytes::<true>(&buffer)
181                }
182            }
183        } else {
184            self.to_le_bytes()
185        };
186
187        let needs_sign_pad = match bytes.last() {
188            None => false, // pure zero, no bytes
189            Some(&b) => (b & 0x80 != 0) != negate,
190        };
191        if needs_sign_pad {
192            bytes.push(if negate { 0xff } else { 0 });
193        }
194
195        bytes
196    }
197
198    fn to_be_bytes(self) -> Vec<u8> {
199        match self {
200            RefSmall(x) => {
201                let bytes = x.to_be_bytes();
202                let skip_bytes = x.leading_zeros() as usize / 8;
203                bytes[skip_bytes..].into()
204            }
205            RefLarge(words) => words_to_be_bytes::<false>(words),
206        }
207    }
208
209    fn to_signed_be_bytes(self, negate: bool) -> Vec<u8> {
210        // make sure to return empty for zero
211        if let RefSmall(v) = self {
212            if v == 0 {
213                return Vec::new();
214            }
215        }
216
217        let mut bytes = if negate {
218            match self {
219                RefSmall(x) => {
220                    let bytes = (!x + 1).to_be_bytes();
221                    let skip_bytes = x.leading_zeros() as usize / 8;
222                    bytes[skip_bytes..].into()
223                }
224                RefLarge(words) => {
225                    let mut buffer = Buffer::from(words);
226                    debug_assert_zero!(add::sub_one_in_place(&mut buffer));
227                    words_to_be_bytes::<true>(&buffer)
228                }
229            }
230        } else {
231            self.to_be_bytes()
232        };
233
234        let needs_sign_pad = match bytes.first() {
235            None => false, // pure zero, no bytes
236            Some(&b) => (b & 0x80 != 0) != negate,
237        };
238        if needs_sign_pad {
239            bytes.insert(0, if negate { 0xff } else { 0 });
240        }
241
242        bytes
243    }
244
245    fn to_chunks(self, chunk_bits: usize) -> Vec<Repr> {
246        assert!(chunk_bits > 0);
247        let chunk_count = math::ceil_div(self.bit_len(), chunk_bits);
248
249        match self {
250            RefSmall(x) => match chunk_count {
251                0 => Vec::new(),
252                1 => vec![Repr::from_dword(x)],
253                n => {
254                    const_assert!(u8::MAX as usize > DWORD_BITS_USIZE);
255                    let mut buffers = Vec::with_capacity(n);
256                    let chunk_bits = chunk_bits as u8; // chunk has at most DWORD_BITS bits, otherwise n <= 1
257                    for i in 0..n as u8 {
258                        let chunk = (x >> (i * chunk_bits)) & math::ones_dword(chunk_bits as _);
259                        buffers.push(Repr::from_dword(chunk));
260                    }
261                    buffers
262                }
263            },
264            RefLarge(words) => {
265                let mut buffers = Vec::<Buffer>::new();
266                let word_per_chunk = math::ceil_div(chunk_bits, WORD_BITS_USIZE);
267                buffers.resize_with(chunk_count, || {
268                    // allocate an extra word for shifting
269                    let mut buf: Buffer = Buffer::allocate(word_per_chunk + 1);
270                    buf.push_zeros(word_per_chunk + 1);
271                    buf
272                });
273                let mut buffer_refs: Box<[&mut [Word]]> =
274                    buffers.iter_mut().map(|buf| buf.as_mut()).collect();
275                words_to_chunks(words, &mut buffer_refs, chunk_bits);
276                buffers.into_iter().map(Repr::from_buffer).collect()
277            }
278        }
279    }
280}
281
282impl Repr {
283    fn from_le_bytes(bytes: &[u8]) -> Self {
284        if bytes.len() <= DWORD_BYTES {
285            // fast path
286            Self::from_dword(primitive::dword_from_le_bytes_partial::<false>(bytes))
287        } else {
288            // slow path
289            Self::from_le_bytes_large::<false>(bytes)
290        }
291    }
292
293    fn from_signed_le_bytes(bytes: &[u8]) -> Self {
294        if let Some(v) = bytes.last() {
295            if *v < 0x80 {
296                return Self::from_le_bytes(bytes);
297            }
298        } else {
299            return Self::zero();
300        }
301
302        // negative
303        let repr = if bytes.len() <= DWORD_BYTES {
304            // fast path
305            let dword = primitive::dword_from_le_bytes_partial::<true>(bytes);
306            Self::from_dword(!dword + 1)
307        } else {
308            // slow path
309            Self::from_le_bytes_large::<true>(bytes)
310        };
311        repr.with_sign(Sign::Negative)
312    }
313
314    fn from_le_bytes_large<const NEG: bool>(bytes: &[u8]) -> Self {
315        debug_assert!(bytes.len() >= DWORD_BYTES);
316        let mut buffer = Buffer::allocate((bytes.len() - 1) / WORD_BYTES + 1);
317        let mut chunks = bytes.chunks_exact(WORD_BYTES);
318        for chunk in &mut chunks {
319            let word = Word::from_le_bytes(chunk.try_into().unwrap());
320            buffer.push(if NEG { !word } else { word });
321        }
322        if !chunks.remainder().is_empty() {
323            let word = primitive::word_from_le_bytes_partial::<NEG>(chunks.remainder());
324            buffer.push(if NEG { !word } else { word });
325        }
326        if NEG {
327            debug_assert_zero!(add::add_one_in_place(&mut buffer));
328        }
329        Self::from_buffer(buffer)
330    }
331
332    fn from_be_bytes(bytes: &[u8]) -> Self {
333        if bytes.len() <= DWORD_BYTES {
334            Self::from_dword(primitive::dword_from_be_bytes_partial::<false>(bytes))
335        } else {
336            // slow path
337            Self::from_be_bytes_large::<false>(bytes)
338        }
339    }
340
341    fn from_signed_be_bytes(bytes: &[u8]) -> Self {
342        if let Some(v) = bytes.first() {
343            if *v < 0x80 {
344                return Self::from_be_bytes(bytes);
345            }
346        } else {
347            return Self::zero();
348        }
349
350        // negative
351        let repr = if bytes.len() <= DWORD_BYTES {
352            // fast path
353            let dword = primitive::dword_from_be_bytes_partial::<true>(bytes);
354            Self::from_dword(!dword + 1)
355        } else {
356            // slow path
357            Self::from_be_bytes_large::<true>(bytes)
358        };
359        repr.with_sign(Sign::Negative)
360    }
361
362    fn from_be_bytes_large<const NEG: bool>(bytes: &[u8]) -> Self {
363        debug_assert!(bytes.len() >= DWORD_BYTES);
364        let mut buffer = Buffer::allocate((bytes.len() - 1) / WORD_BYTES + 1);
365        let mut chunks = bytes.rchunks_exact(WORD_BYTES);
366        for chunk in &mut chunks {
367            let word = Word::from_be_bytes(chunk.try_into().unwrap());
368            buffer.push(if NEG { !word } else { word });
369        }
370        if !chunks.remainder().is_empty() {
371            let word = primitive::word_from_be_bytes_partial::<NEG>(chunks.remainder());
372            buffer.push(if NEG { !word } else { word });
373        }
374        if NEG {
375            debug_assert_zero!(add::add_one_in_place(&mut buffer));
376        }
377        Self::from_buffer(buffer)
378    }
379
380    fn from_chunks(chunks: &[&[Word]], chunk_bits: usize) -> Self {
381        if let Some(max_len) = chunks.iter().map(|words| words.len()).max() {
382            // allocate an extra word for shifting
383            let result_len = max_len + (chunks.len() - 1) * chunk_bits + 1;
384            let mut result = Buffer::allocate(result_len);
385            result.push_zeros(result_len);
386            let mut buffer = Buffer::allocate_exact(max_len + 1);
387            buffer.push_zeros(max_len + 1);
388
389            chunks_to_words(&mut result, chunks, chunk_bits, &mut buffer);
390            Self::from_buffer(result)
391        } else {
392            Self::zero()
393        }
394    }
395}
396
397impl UBig {
398    /// Construct from little-endian bytes.
399    ///
400    /// # Examples
401    ///
402    /// ```
403    /// # use dashu_int::UBig;
404    /// assert_eq!(UBig::from_le_bytes(&[3, 2, 1]), UBig::from(0x010203u32));
405    /// ```
406    #[inline]
407    pub fn from_le_bytes(bytes: &[u8]) -> UBig {
408        UBig(Repr::from_le_bytes(bytes))
409    }
410
411    /// Construct from big-endian bytes.
412    ///
413    /// # Examples
414    ///
415    /// ```
416    /// # use dashu_int::UBig;
417    /// assert_eq!(UBig::from_be_bytes(&[1, 2, 3]), UBig::from(0x010203u32));
418    /// ```
419    #[inline]
420    pub fn from_be_bytes(bytes: &[u8]) -> UBig {
421        UBig(Repr::from_be_bytes(bytes))
422    }
423
424    /// Return little-endian bytes.
425    ///
426    /// # Examples
427    ///
428    /// ```
429    /// # use dashu_int::UBig;
430    /// assert!(UBig::ZERO.to_le_bytes().is_empty());
431    /// assert_eq!(*UBig::from(0x010203u32).to_le_bytes(), [3, 2, 1]);
432    /// ```
433    pub fn to_le_bytes(&self) -> Box<[u8]> {
434        self.repr().to_le_bytes().into_boxed_slice()
435    }
436
437    /// Return big-endian bytes.
438    ///
439    /// # Examples
440    ///
441    /// ```
442    /// # use dashu_int::UBig;
443    /// assert!(UBig::ZERO.to_be_bytes().is_empty());
444    /// assert_eq!(*UBig::from(0x010203u32).to_be_bytes(), [1, 2, 3]);
445    /// ```
446    pub fn to_be_bytes(&self) -> Box<[u8]> {
447        self.repr().to_be_bytes().into_boxed_slice()
448    }
449
450    /// Reconstruct an integer from a group of bit chunks.
451    ///
452    /// Denote the chunks as C_i, then this function calculates sum(C_i * 2^(i * chunk_bits))
453    /// for i from 0 to len(chunks) - 1.
454    ///
455    /// Note that it's allowed for each chunk to have more bits than chunk_bits, which is different
456    /// from the [UBig::to_chunks] method.
457    ///
458    /// # Examples
459    ///
460    /// ```
461    /// # use dashu_int::UBig;
462    /// assert_eq!(
463    ///     UBig::from_chunks([0x3u8.into(), 0x2u8.into(), 0x1u8.into()].iter(), 8),
464    ///     UBig::from(0x010203u32)
465    /// );
466    /// ```
467    ///
468    /// # Panics
469    ///
470    /// Panics if chunk_bits is zero.
471    #[inline]
472    pub fn from_chunks<'a, I: Iterator<Item = &'a UBig>>(chunks: I, chunk_bits: usize) -> Self {
473        let chunks: Box<_> = chunks.into_iter().map(|u| u.as_words()).collect();
474        Self(Repr::from_chunks(&chunks, chunk_bits))
475    }
476
477    /// Slice the integer into group of bit chunks from the least significant end to the most
478    /// significant end. Each chunk is represented as an integer, containing a subset of the
479    /// bits in the source integer.
480    ///
481    /// # Examples
482    ///
483    /// ```
484    /// # use dashu_int::UBig;
485    /// assert!(UBig::ZERO.to_chunks(1).is_empty());
486    /// assert_eq!(*UBig::from(0x010203u32).to_chunks(8),
487    ///     [0x3u8.into(), 0x2u8.into(), 0x1u8.into()]);
488    /// ```
489    ///
490    /// # Panics
491    ///
492    /// Panics if chunk_bits is zero.
493    #[inline]
494    pub fn to_chunks(&self, chunk_bits: usize) -> Box<[UBig]> {
495        self.repr()
496            .to_chunks(chunk_bits)
497            .into_iter()
498            .map(UBig)
499            .collect()
500    }
501
502    /// Convert to f32.
503    ///
504    /// Round to nearest, breaking ties to even last bit. The returned approximation
505    /// is exact if the integer is exactly representable by f32, otherwise the error
506    /// field of the approximation contains the sign of `result - self`.
507    ///
508    /// # Examples
509    ///
510    /// ```
511    /// # use dashu_int::UBig;
512    /// assert_eq!(UBig::from(134u8).to_f32().value(), 134.0f32);
513    /// ```
514    #[inline]
515    pub fn to_f32(&self) -> Approximation<f32, Sign> {
516        self.repr().to_f32()
517    }
518
519    /// Convert to f64.
520    ///
521    /// Round to nearest, breaking ties to even last bit. The returned approximation
522    /// is exact if the integer is exactly representable by f64, otherwise the error
523    /// field of the approximation contains the sign of `result - self`.
524    ///
525    /// # Examples
526    ///
527    /// ```
528    /// # use dashu_int::UBig;
529    /// assert_eq!(UBig::from(134u8).to_f64().value(), 134.0f64);
530    /// ```
531    #[inline]
532    pub fn to_f64(&self) -> Approximation<f64, Sign> {
533        self.repr().to_f64()
534    }
535
536    /// Regard the number as a [IBig] number and return a reference of [IBig] type.
537    ///
538    /// # Examples
539    ///
540    /// ```
541    /// # use dashu_int::{IBig, UBig};
542    /// assert_eq!(UBig::from(123u8).as_ibig(), &IBig::from(123));
543    /// ```
544    #[inline]
545    pub const fn as_ibig(&self) -> &IBig {
546        // SAFETY: UBig and IBig are both transparent wrapper around the Repr type.
547        //         This conversion is only available for immutable references, so that
548        //         the sign will not be messed up.
549        unsafe { core::mem::transmute(self) }
550    }
551}
552
553impl IBig {
554    /// Construct from signed little-endian bytes.
555    ///
556    /// The negative number must be represented in a two's complement format, assuming
557    /// the top bits are all ones. The number is assumed negative when the top bit of
558    /// the top byte is set.
559    ///
560    /// # Examples
561    ///
562    /// ```
563    /// # use dashu_int::IBig;
564    /// assert_eq!(IBig::from_le_bytes(&[1, 2, 0xf3]), IBig::from(0xfff30201u32 as i32));
565    /// ```
566    #[inline]
567    pub fn from_le_bytes(bytes: &[u8]) -> IBig {
568        IBig(Repr::from_signed_le_bytes(bytes))
569    }
570
571    /// Construct from big-endian bytes.
572    ///
573    /// The negative number must be represented in a two's complement format, assuming
574    /// the top bits are all ones. The number is assumed negative when the top bit of
575    /// the top byte is set.
576    ///
577    /// # Examples
578    ///
579    /// ```
580    /// # use dashu_int::IBig;
581    /// assert_eq!(IBig::from_be_bytes(&[0xf3, 2, 1]), IBig::from(0xfff30201u32 as i32));
582    /// ```
583    #[inline]
584    pub fn from_be_bytes(bytes: &[u8]) -> IBig {
585        IBig(Repr::from_signed_be_bytes(bytes))
586    }
587
588    /// Return little-endian bytes.
589    ///
590    /// The negative number will be represented in a two's complement format
591    ///
592    /// # Examples
593    ///
594    /// ```
595    /// # use dashu_int::IBig;
596    /// assert!(IBig::ZERO.to_le_bytes().is_empty());
597    /// assert_eq!(*IBig::from(0xfff30201u32 as i32).to_le_bytes(), [1, 2, 0xf3]);
598    /// ```
599    #[inline]
600    pub fn to_le_bytes(&self) -> Box<[u8]> {
601        let (sign, repr) = self.as_sign_repr();
602        repr.to_signed_le_bytes(sign.into()).into_boxed_slice()
603    }
604
605    /// Return big-endian bytes.
606    ///
607    /// The negative number will be represented in a two's complement format
608    ///
609    /// # Examples
610    ///
611    /// ```
612    /// # use dashu_int::IBig;
613    /// assert!(IBig::ZERO.to_be_bytes().is_empty());
614    /// assert_eq!(*IBig::from(0xfff30201u32 as i32).to_be_bytes(), [0xf3, 2, 1]);
615    /// ```
616    pub fn to_be_bytes(&self) -> Box<[u8]> {
617        let (sign, repr) = self.as_sign_repr();
618        repr.to_signed_be_bytes(sign.into()).into_boxed_slice()
619    }
620
621    /// Convert to f32.
622    ///
623    /// Round to nearest, breaking ties to even last bit. The returned approximation
624    /// is exact if the integer is exactly representable by f32, otherwise the error
625    /// field of the approximation contains the sign of `result - self`.
626    ///
627    /// # Examples
628    ///
629    /// ```
630    /// # use dashu_int::IBig;
631    /// assert_eq!(IBig::from(-134).to_f32().value(), -134.0f32);
632    /// ```
633    #[inline]
634    pub fn to_f32(&self) -> Approximation<f32, Sign> {
635        let (sign, mag) = self.as_sign_repr();
636        match mag.to_f32() {
637            Exact(val) => Exact(sign * val),
638            Inexact(val, diff) => Inexact(sign * val, sign * diff),
639        }
640    }
641
642    /// Convert to f64.
643    ///
644    /// Round to nearest, breaking ties to even last bit. The returned approximation
645    /// is exact if the integer is exactly representable by f64, otherwise the error
646    /// field of the approximation contains the sign of `result - self`.
647    ///
648    /// # Examples
649    ///
650    /// ```
651    /// # use dashu_int::IBig;
652    /// assert_eq!(IBig::from(-134).to_f64().value(), -134.0f64);
653    /// ```
654    #[inline]
655    pub fn to_f64(&self) -> Approximation<f64, Sign> {
656        let (sign, mag) = self.as_sign_repr();
657        match mag.to_f64() {
658            Exact(val) => Exact(sign * val),
659            Inexact(val, diff) => Inexact(sign * val, sign * diff),
660        }
661    }
662
663    /// Regard the number as a [UBig] number and return a reference of [UBig] type.
664    ///
665    /// The conversion is only successful when the number is positive
666    ///
667    /// # Examples
668    ///
669    /// ```
670    /// # use dashu_int::{IBig, UBig};
671    /// assert_eq!(IBig::from(123).as_ubig(), Some(&UBig::from(123u8)));
672    /// assert_eq!(IBig::from(-123).as_ubig(), None);
673    /// ```
674    #[inline]
675    pub const fn as_ubig(&self) -> Option<&UBig> {
676        match self.sign() {
677            Sign::Positive => {
678                // SAFETY: UBig and IBig are both transparent wrapper around the Repr type.
679                //         This conversion is only available for immutable references and
680                //         positive numbers, so that the sign will not be messed up.
681                unsafe { Some(core::mem::transmute::<&IBig, &UBig>(self)) }
682            }
683            Sign::Negative => None,
684        }
685    }
686}
687
688macro_rules! ubig_unsigned_conversions {
689    ($($t:ty)*) => {$(
690        impl From<$t> for UBig {
691            #[inline]
692            fn from(value: $t) -> UBig {
693                UBig::from_unsigned(value)
694            }
695        }
696
697        impl TryFrom<UBig> for $t {
698            type Error = ConversionError;
699
700            #[inline]
701            fn try_from(value: UBig) -> Result<$t, ConversionError> {
702                value.try_to_unsigned()
703            }
704        }
705
706        impl TryFrom<&UBig> for $t {
707            type Error = ConversionError;
708
709            #[inline]
710            fn try_from(value: &UBig) -> Result<$t, ConversionError> {
711                value.try_to_unsigned()
712            }
713        }
714    )*};
715}
716ubig_unsigned_conversions!(u8 u16 u32 u64 u128 usize);
717
718impl From<bool> for UBig {
719    #[inline]
720    fn from(b: bool) -> UBig {
721        u8::from(b).into()
722    }
723}
724
725macro_rules! ubig_signed_conversions {
726    ($($t:ty)*) => {$(
727        impl TryFrom<$t> for UBig {
728            type Error = ConversionError;
729
730            #[inline]
731            fn try_from(value: $t) -> Result<UBig, ConversionError> {
732                UBig::try_from_signed(value)
733            }
734        }
735
736        impl TryFrom<UBig> for $t {
737            type Error = ConversionError;
738
739            #[inline]
740            fn try_from(value: UBig) -> Result<$t, ConversionError> {
741                value.try_to_signed()
742            }
743        }
744
745        impl TryFrom<&UBig> for $t {
746            type Error = ConversionError;
747
748            #[inline]
749            fn try_from(value: &UBig) -> Result<$t, ConversionError> {
750                value.try_to_signed()
751            }
752        }
753    )*};
754}
755ubig_signed_conversions!(i8 i16 i32 i64 i128 isize);
756
757macro_rules! ibig_unsigned_conversions {
758    ($($t:ty)*) => {$(
759        impl From<$t> for IBig {
760            #[inline]
761            fn from(value: $t) -> IBig {
762                IBig::from_unsigned(value)
763            }
764        }
765
766        impl TryFrom<IBig> for $t {
767            type Error = ConversionError;
768
769            #[inline]
770            fn try_from(value: IBig) -> Result<$t, ConversionError> {
771                value.try_to_unsigned()
772            }
773        }
774
775        impl TryFrom<&IBig> for $t {
776            type Error = ConversionError;
777
778            #[inline]
779            fn try_from(value: &IBig) -> Result<$t, ConversionError> {
780                value.try_to_unsigned()
781            }
782        }
783    )*};
784}
785
786ibig_unsigned_conversions!(u8 u16 u32 u64 u128 usize);
787
788impl From<bool> for IBig {
789    #[inline]
790    fn from(b: bool) -> IBig {
791        u8::from(b).into()
792    }
793}
794
795macro_rules! ibig_signed_conversions {
796    ($($t:ty)*) => {$(
797        impl From<$t> for IBig {
798            #[inline]
799            fn from(value: $t) -> IBig {
800                IBig::from_signed(value)
801            }
802        }
803
804        impl TryFrom<IBig> for $t {
805            type Error = ConversionError;
806
807            #[inline]
808            fn try_from(value: IBig) -> Result<$t, ConversionError> {
809                value.try_to_signed()
810            }
811        }
812
813        impl TryFrom<&IBig> for $t {
814            type Error = ConversionError;
815
816            #[inline]
817            fn try_from(value: &IBig) -> Result<$t, ConversionError> {
818                value.try_to_signed()
819            }
820        }
821    )*};
822}
823ibig_signed_conversions!(i8 i16 i32 i64 i128 isize);
824
825macro_rules! ubig_float_conversions {
826    ($($t:ty => $i:ty;)*) => {$(
827        impl TryFrom<$t> for UBig {
828            type Error = ConversionError;
829
830            fn try_from(value: $t) -> Result<Self, Self::Error> {
831                let (man, exp) = value.decode().map_err(|_| ConversionError::OutOfBounds)?;
832                let mut result: UBig = man.try_into()?;
833                if exp >= 0 {
834                    result <<= exp as usize;
835                } else {
836                    result >>= (-exp) as usize;
837                }
838                Ok(result)
839            }
840        }
841
842        impl TryFrom<UBig> for $t {
843            type Error = ConversionError;
844
845            fn try_from(value: UBig) -> Result<Self, Self::Error> {
846                const MAX_BIT_LEN: usize = (<$t>::MANTISSA_DIGITS + 1) as usize;
847                if value.bit_len() > MAX_BIT_LEN
848                    || (value.bit_len() == MAX_BIT_LEN && !value.is_power_of_two())
849                {
850                    // precision loss occurs when the integer has more digits than what the mantissa can store
851                    Err(ConversionError::LossOfPrecision)
852                } else {
853                    Ok(<$i>::try_from(value).unwrap() as $t)
854                }
855            }
856        }
857    )*};
858}
859ubig_float_conversions!(f32 => u32; f64 => u64;);
860
861macro_rules! ibig_float_conversions {
862    ($($t:ty => $i:ty;)*) => {$(
863        impl TryFrom<$t> for IBig {
864            type Error = ConversionError;
865
866            fn try_from(value: $t) -> Result<Self, Self::Error> {
867                let (man, exp) = value.decode().map_err(|_| ConversionError::OutOfBounds)?;
868                let mut result: IBig = man.into();
869                if exp >= 0 {
870                    result <<= exp as usize;
871                } else {
872                    result >>= (-exp) as usize;
873                }
874                Ok(result)
875            }
876        }
877
878        impl TryFrom<IBig> for $t {
879            type Error = ConversionError;
880
881            fn try_from(value: IBig) -> Result<Self, Self::Error> {
882                const MAX_BIT_LEN: usize = (<$t>::MANTISSA_DIGITS + 1) as usize;
883                let (sign, value) = value.into_parts();
884                if value.bit_len() > MAX_BIT_LEN
885                    || (value.bit_len() == MAX_BIT_LEN && !value.is_power_of_two())
886                {
887                    // precision loss occurs when the integer has more digits than what the mantissa can store
888                    Err(ConversionError::LossOfPrecision)
889                } else {
890                    let float = <$i>::try_from(value).unwrap() as $t;
891                    Ok(sign * float)
892                }
893            }
894        }
895    )*};
896}
897ibig_float_conversions!(f32 => u32; f64 => u64;);
898
899impl From<UBig> for IBig {
900    #[inline]
901    fn from(x: UBig) -> IBig {
902        IBig(x.0.with_sign(Positive))
903    }
904}
905
906impl TryFrom<IBig> for UBig {
907    type Error = ConversionError;
908
909    #[inline]
910    fn try_from(x: IBig) -> Result<UBig, ConversionError> {
911        match x.sign() {
912            Positive => Ok(UBig(x.0)),
913            Negative => Err(ConversionError::OutOfBounds),
914        }
915    }
916}
917
918impl UBig {
919    /// Convert an unsigned primitive to [UBig].
920    #[inline]
921    pub(crate) fn from_unsigned<T>(x: T) -> UBig
922    where
923        T: PrimitiveUnsigned,
924    {
925        UBig(Repr::from_unsigned(x))
926    }
927
928    /// Try to convert a signed primitive to [UBig].
929    #[inline]
930    fn try_from_signed<T>(x: T) -> Result<UBig, ConversionError>
931    where
932        T: PrimitiveSigned,
933    {
934        let (sign, mag) = x.to_sign_magnitude();
935        match sign {
936            Sign::Positive => Ok(UBig(Repr::from_unsigned(mag))),
937            Sign::Negative => Err(ConversionError::OutOfBounds),
938        }
939    }
940
941    /// Try to convert [UBig] to an unsigned primitive.
942    #[inline]
943    pub(crate) fn try_to_unsigned<T>(&self) -> Result<T, ConversionError>
944    where
945        T: PrimitiveUnsigned,
946    {
947        self.repr().try_to_unsigned()
948    }
949
950    /// Try to convert [UBig] to a signed primitive.
951    #[inline]
952    fn try_to_signed<T>(&self) -> Result<T, ConversionError>
953    where
954        T: PrimitiveSigned,
955    {
956        T::try_from_sign_magnitude(Sign::Positive, self.repr().try_to_unsigned()?)
957    }
958}
959
960impl IBig {
961    /// Convert an unsigned primitive to [IBig].
962    #[inline]
963    pub(crate) fn from_unsigned<T: PrimitiveUnsigned>(x: T) -> IBig {
964        IBig(Repr::from_unsigned(x))
965    }
966
967    /// Convert a signed primitive to [IBig].
968    #[inline]
969    pub(crate) fn from_signed<T: PrimitiveSigned>(x: T) -> IBig {
970        let (sign, mag) = x.to_sign_magnitude();
971        IBig(Repr::from_unsigned(mag).with_sign(sign))
972    }
973
974    /// Try to convert [IBig] to an unsigned primitive.
975    #[inline]
976    pub(crate) fn try_to_unsigned<T: PrimitiveUnsigned>(&self) -> Result<T, ConversionError> {
977        let (sign, mag) = self.as_sign_repr();
978        match sign {
979            Positive => mag.try_to_unsigned(),
980            Negative => Err(ConversionError::OutOfBounds),
981        }
982    }
983
984    /// Try to convert [IBig] to an signed primitive.
985    #[inline]
986    pub(crate) fn try_to_signed<T: PrimitiveSigned>(&self) -> Result<T, ConversionError> {
987        let (sign, mag) = self.as_sign_repr();
988        T::try_from_sign_magnitude(sign, mag.try_to_unsigned()?)
989    }
990}
991
992mod repr {
993    use core::cmp::Ordering;
994
995    use static_assertions::const_assert;
996
997    use super::*;
998    use crate::repr::TypedReprRef;
999
1000    /// Try to convert `Word`s to an unsigned primitive.
1001    fn unsigned_from_words<T>(words: &[Word]) -> Result<T, ConversionError>
1002    where
1003        T: PrimitiveUnsigned,
1004    {
1005        debug_assert!(words.len() >= 2);
1006        let t_words = T::BYTE_SIZE / WORD_BYTES;
1007        if t_words <= 1 || words.len() > t_words {
1008            Err(ConversionError::OutOfBounds)
1009        } else {
1010            assert!(
1011                T::BIT_SIZE % WORD_BITS == 0,
1012                "A large primitive type not a multiple of word size."
1013            );
1014            let mut repr = T::default().to_le_bytes();
1015            let bytes: &mut [u8] = repr.as_mut();
1016            for (idx, w) in words.iter().enumerate() {
1017                let pos = idx * WORD_BYTES;
1018                bytes[pos..pos + WORD_BYTES].copy_from_slice(&w.to_le_bytes());
1019            }
1020            Ok(T::from_le_bytes(repr))
1021        }
1022    }
1023
1024    impl Repr {
1025        #[inline]
1026        pub fn from_unsigned<T>(x: T) -> Self
1027        where
1028            T: PrimitiveUnsigned,
1029        {
1030            if let Ok(dw) = x.try_into() {
1031                Self::from_dword(dw)
1032            } else {
1033                let repr = x.to_le_bytes();
1034                Self::from_le_bytes_large::<false>(repr.as_ref())
1035            }
1036        }
1037    }
1038
1039    impl<'a> TypedReprRef<'a> {
1040        #[inline]
1041        pub fn try_to_unsigned<T>(self) -> Result<T, ConversionError>
1042        where
1043            T: PrimitiveUnsigned,
1044        {
1045            match self {
1046                RefSmall(dw) => T::try_from(dw).map_err(|_| ConversionError::OutOfBounds),
1047                RefLarge(words) => unsigned_from_words(words),
1048            }
1049        }
1050
1051        #[inline]
1052        pub fn to_f32(self) -> Approximation<f32, Sign> {
1053            match self {
1054                RefSmall(dword) => to_f32_small(dword),
1055                RefLarge(_) => self.to_f32_nontrivial(),
1056            }
1057        }
1058
1059        #[inline]
1060        fn to_f32_nontrivial(self) -> Approximation<f32, Sign> {
1061            let n = self.bit_len();
1062            debug_assert!(n > 32);
1063
1064            if n > 128 {
1065                Inexact(f32::INFINITY, Positive)
1066            } else {
1067                let top_u31: u32 = (self >> (n - 31)).as_typed().try_to_unsigned().unwrap();
1068                let extra_bit = self.are_low_bits_nonzero(n - 31) as u32;
1069                f32::encode((top_u31 | extra_bit) as i32, (n - 31) as i16)
1070            }
1071        }
1072
1073        #[inline]
1074        pub fn to_f64(self) -> Approximation<f64, Sign> {
1075            match self {
1076                RefSmall(dword) => to_f64_small(dword as DoubleWord),
1077                RefLarge(_) => {
1078                    // On 16-bit Word targets a `RefLarge` value can have
1079                    // `bit_len()` in [33, 64], which still fits in `u64`.
1080                    // The `to_f64_nontrivial` path's shift arithmetic
1081                    // assumes the value spans more than 64 bits, so route
1082                    // small RefLarge values through the same direct cast
1083                    // that `to_f64_small` uses.
1084                    if self.bit_len() <= 64 {
1085                        let v: u64 = self.try_to_unsigned().unwrap();
1086                        let f = v as f64;
1087                        let back = f as u64;
1088                        match back.cmp(&v) {
1089                            Ordering::Greater => Inexact(f, Sign::Positive),
1090                            Ordering::Equal => Exact(f),
1091                            Ordering::Less => Inexact(f, Sign::Negative),
1092                        }
1093                    } else {
1094                        self.to_f64_nontrivial()
1095                    }
1096                }
1097            }
1098        }
1099
1100        #[inline]
1101        fn to_f64_nontrivial(self) -> Approximation<f64, Sign> {
1102            let n = self.bit_len();
1103            debug_assert!(n > 64);
1104
1105            if n > 1024 {
1106                Inexact(f64::INFINITY, Positive)
1107            } else {
1108                let top_u63: u64 = (self >> (n - 63)).as_typed().try_to_unsigned().unwrap();
1109                let extra_bit = self.are_low_bits_nonzero(n - 63) as u64;
1110                f64::encode((top_u63 | extra_bit) as i64, (n - 63) as i16)
1111            }
1112        }
1113    }
1114
1115    fn to_f32_small(dword: DoubleWord) -> Approximation<f32, Sign> {
1116        let f = dword as f32;
1117        if f.is_infinite() {
1118            return Inexact(f, Sign::Positive);
1119        }
1120
1121        let back = f as DoubleWord;
1122        match back.partial_cmp(&dword).unwrap() {
1123            Ordering::Greater => Inexact(f, Sign::Positive),
1124            Ordering::Equal => Exact(f),
1125            Ordering::Less => Inexact(f, Sign::Negative),
1126        }
1127    }
1128
1129    fn to_f64_small(dword: DoubleWord) -> Approximation<f64, Sign> {
1130        const_assert!((DoubleWord::MAX as f64) < f64::MAX);
1131        let f = dword as f64;
1132        let back = f as DoubleWord;
1133
1134        match back.partial_cmp(&dword).unwrap() {
1135            Ordering::Greater => Inexact(f, Sign::Positive),
1136            Ordering::Equal => Exact(f),
1137            Ordering::Less => Inexact(f, Sign::Negative),
1138        }
1139    }
1140}
1141
1142// TODO(v0.5): implement `to_digits` and `from_digits`, that supports base up to Word::MAX.
1143//             This method won't be optimized as much as the InRadix formatter,
1144//             because InRadix has a limit on the radix.
1145//
1146//             For small bases, we should use the same algorithm with `InRadix` formatter,
1147//             therefore we need break change here (limit the 'Digit' type to u8), and only
1148//             share algorithm for base < 256