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