pgnumeric/
convert.rs

1// Copyright 2020 CoD Technologies Corp.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Numeric conversion utilities.
16
17use crate::binary::{NumericDigit, NUMERIC_NEG, NUMERIC_POS};
18use crate::data::NumericData;
19use crate::error::NumericTryFromError;
20use crate::num::{Numeric, NumericBuf};
21use crate::var::{NumericVar, NBASE};
22use std::convert::{TryFrom, TryInto};
23use std::ffi::CStr;
24use std::mem::MaybeUninit;
25
26/// Zero value.
27trait Zero: Copy + PartialEq {
28    const ZERO: Self;
29
30    #[inline]
31    fn is_zero(self) -> bool {
32        self == Self::ZERO
33    }
34}
35
36macro_rules! impl_zero {
37    ($t: ty) => {
38        impl Zero for $t {
39            const ZERO: Self = 0;
40        }
41    };
42}
43
44impl_zero!(i8);
45impl_zero!(i16);
46impl_zero!(i32);
47impl_zero!(i64);
48impl_zero!(i128);
49impl_zero!(u8);
50impl_zero!(u16);
51impl_zero!(u32);
52impl_zero!(u64);
53impl_zero!(u128);
54
55/// Unsigned integer.
56trait Unsigned: Copy + Zero {
57    const MAX_NDIGITS: usize;
58
59    fn next_digit(self) -> (NumericDigit, Self);
60
61    fn from_numeric_digit(n: NumericDigit) -> Self;
62    fn from_u64(i: u64) -> Self;
63    fn overflowing_mul(self, rhs: Self) -> (Self, bool);
64    fn overflowing_add(self, rhs: Self) -> (Self, bool);
65}
66
67macro_rules! impl_unsigned {
68    ($t: ty, $ndigits: expr) => {
69        impl Unsigned for $t {
70            const MAX_NDIGITS: usize = $ndigits;
71
72            #[inline]
73            fn next_digit(self) -> (NumericDigit, Self) {
74                let new_val = self / NBASE as Self;
75                let digit = (self - new_val * NBASE as Self) as NumericDigit;
76                (digit, new_val)
77            }
78
79            #[inline]
80            fn from_numeric_digit(n: NumericDigit) -> Self {
81                n as Self
82            }
83
84            #[inline]
85            fn from_u64(i: u64) -> Self {
86                i as Self
87            }
88
89            #[inline]
90            fn overflowing_mul(self, rhs: Self) -> (Self, bool) {
91                self.overflowing_mul(rhs)
92            }
93
94            #[inline]
95            fn overflowing_add(self, rhs: Self) -> (Self, bool) {
96                self.overflowing_add(rhs)
97            }
98        }
99    };
100}
101
102impl_unsigned!(u16, 2);
103impl_unsigned!(u32, 3);
104impl_unsigned!(u64, 5);
105impl_unsigned!(u128, 10);
106
107impl Unsigned for u8 {
108    const MAX_NDIGITS: usize = 1;
109
110    #[inline]
111    fn next_digit(self) -> (NumericDigit, Self) {
112        (self as NumericDigit, 0)
113    }
114
115    fn from_numeric_digit(_n: i16) -> Self {
116        panic!("should not use this associate function")
117    }
118
119    fn from_u64(_i: u64) -> Self {
120        panic!("should not use this associate function")
121    }
122
123    fn overflowing_mul(self, _rhs: Self) -> (Self, bool) {
124        panic!("should not use this associate function")
125    }
126
127    fn overflowing_add(self, _rhs: Self) -> (Self, bool) {
128        panic!("should not use this associate function")
129    }
130}
131
132/// Signed integer.
133trait Signed: Copy + PartialOrd + Zero {
134    type AbsType: Unsigned;
135
136    const MIN_VALUE: Self;
137
138    fn from_numeric_digit(n: NumericDigit) -> Self;
139    fn from_i64(i: i64) -> Self;
140    fn is_negative(self) -> bool;
141    fn negative(self) -> Self;
142    fn abs(self) -> Self::AbsType;
143    fn overflowing_mul(self, rhs: Self) -> (Self, bool);
144    fn overflowing_sub(self, rhs: Self) -> (Self, bool);
145}
146
147macro_rules! impl_signed {
148    ($t: ty, $abs_ty: ty) => {
149        impl Signed for $t {
150            type AbsType = $abs_ty;
151
152            const MIN_VALUE: Self = Self::min_value();
153
154            #[inline]
155            fn from_numeric_digit(n: NumericDigit) -> Self {
156                debug_assert!(n as i128 <= Self::max_value() as i128);
157                debug_assert!(n as i128 >= Self::min_value() as i128);
158                n as Self
159            }
160
161            #[inline]
162            fn from_i64(i: i64) -> Self {
163                debug_assert!(i as i128 <= Self::max_value() as i128);
164                debug_assert!(i as i128 >= Self::min_value() as i128);
165                i as Self
166            }
167
168            #[inline]
169            fn is_negative(self) -> bool {
170                self < 0
171            }
172
173            #[inline]
174            fn negative(self) -> Self {
175                -self
176            }
177
178            #[inline]
179            fn abs(self) -> $abs_ty {
180                if self >= 0 {
181                    self as $abs_ty
182                } else {
183                    -(self + 1) as $abs_ty + 1
184                }
185            }
186
187            #[inline]
188            fn overflowing_mul(self, rhs: Self) -> (Self, bool) {
189                self.overflowing_mul(rhs)
190            }
191
192            #[inline]
193            fn overflowing_sub(self, rhs: Self) -> (Self, bool) {
194                self.overflowing_sub(rhs)
195            }
196        }
197    };
198}
199
200impl_signed!(i8, u8);
201impl_signed!(i16, u16);
202impl_signed!(i32, u32);
203impl_signed!(i64, u64);
204impl_signed!(i128, u128);
205
206/// Converts a signed integer to numeric.
207#[inline]
208fn from_signed<T: Signed>(val: T) -> NumericVar<'static> {
209    if val.is_zero() {
210        return NumericVar::zero();
211    }
212
213    let sign = if val.is_negative() {
214        NUMERIC_NEG
215    } else {
216        NUMERIC_POS
217    };
218
219    unsigned_to_var(val.abs(), sign)
220}
221
222/// Converts an unsigned integer to numeric.
223#[inline]
224fn from_unsigned<T: Unsigned>(val: T) -> NumericVar<'static> {
225    if val.is_zero() {
226        return NumericVar::zero();
227    }
228
229    unsigned_to_var(val, NUMERIC_POS)
230}
231
232fn unsigned_to_var<T: Unsigned>(val: T, sign: u16) -> NumericVar<'static> {
233    let mut data = NumericData::with_ndigits(T::MAX_NDIGITS as i32);
234
235    let mut u_val = val;
236    let digits = data.digits_mut(T::MAX_NDIGITS as i32);
237    let mut ndigits = 0;
238    let mut i = digits.len();
239    loop {
240        i -= 1;
241        ndigits += 1;
242
243        let (digit, new_u_val) = u_val.next_digit();
244
245        digits[i] = digit;
246        u_val = new_u_val;
247
248        if u_val.is_zero() {
249            break;
250        }
251    }
252    data.offset_set(data.len() - ndigits as u32);
253
254    NumericVar::owned(ndigits, ndigits - 1, 0, sign, data)
255}
256
257/// Floating-point number.
258trait Floating: Copy {
259    const PRECISION: usize;
260
261    fn is_nan(self) -> bool;
262    fn is_infinite(self) -> bool;
263    fn as_f64(self) -> f64;
264}
265
266macro_rules! impl_floating {
267    ($t: ty, $precision: expr) => {
268        impl Floating for $t {
269            const PRECISION: usize = $precision;
270
271            #[inline]
272            fn is_nan(self) -> bool {
273                self.is_nan()
274            }
275
276            #[inline]
277            fn is_infinite(self) -> bool {
278                self.is_infinite()
279            }
280
281            #[inline]
282            fn as_f64(self) -> f64 {
283                self as f64
284            }
285        }
286    };
287}
288
289impl_floating!(f32, 6);
290impl_floating!(f64, 15);
291
292extern "C" {
293    // format function in libc
294    fn snprintf(dest: *mut u8, size: usize, format: *const u8, ...);
295}
296
297/// Converts a floating-point number to numeric.
298fn from_floating<T: Floating>(f: T) -> Result<NumericBuf, NumericTryFromError> {
299    if f.is_nan() {
300        return Ok(NumericBuf::nan());
301    }
302
303    if f.is_infinite() {
304        return Err(NumericTryFromError::invalid());
305    }
306
307    const BUF_SIZE: usize = 128;
308
309    unsafe {
310        let mut buf: [MaybeUninit<u8>; BUF_SIZE] = MaybeUninit::uninit().assume_init();
311        snprintf(
312            buf.as_mut_ptr() as *mut u8,
313            BUF_SIZE,
314            "%.*g\0".as_ptr(),
315            T::PRECISION,
316            f.as_f64(),
317        );
318        let s = CStr::from_ptr(buf.as_ptr() as *const i8).to_string_lossy();
319        let n = s.parse::<NumericBuf>()?;
320        Ok(n)
321    }
322}
323
324macro_rules! impl_from_signed {
325    ($t: ty) => {
326        impl From<$t> for NumericBuf {
327            #[inline]
328            fn from(val: $t) -> Self {
329                from_signed(val).make_result_no_overflow()
330            }
331        }
332    };
333}
334
335macro_rules! impl_from_unsigned {
336    ($t: ty) => {
337        impl From<$t> for NumericBuf {
338            #[inline]
339            fn from(val: $t) -> Self {
340                from_unsigned(val).make_result_no_overflow()
341            }
342        }
343    };
344}
345
346impl_from_signed!(i8);
347impl_from_signed!(i16);
348impl_from_signed!(i32);
349impl_from_signed!(i64);
350impl_from_signed!(i128);
351impl_from_unsigned!(u8);
352impl_from_unsigned!(u16);
353impl_from_unsigned!(u32);
354impl_from_unsigned!(u64);
355impl_from_unsigned!(u128);
356
357impl From<bool> for NumericBuf {
358    #[inline]
359    fn from(b: bool) -> Self {
360        let val = if b { 1u8 } else { 0u8 };
361        val.into()
362    }
363}
364
365impl From<usize> for NumericBuf {
366    #[inline]
367    fn from(u: usize) -> Self {
368        if std::mem::size_of::<usize>() == 8 {
369            (u as u64).into()
370        } else if std::mem::size_of::<usize>() == 4 {
371            (u as u32).into()
372        } else {
373            panic!("invalid usize size")
374        }
375    }
376}
377
378impl From<isize> for NumericBuf {
379    #[inline]
380    fn from(i: isize) -> Self {
381        if std::mem::size_of::<isize>() == 8 {
382            (i as i64).into()
383        } else if std::mem::size_of::<isize>() == 4 {
384            (i as i32).into()
385        } else {
386            panic!("invalid isize size")
387        }
388    }
389}
390
391impl<'a> From<i64> for NumericVar<'a> {
392    #[inline]
393    fn from(val: i64) -> Self {
394        from_signed(val)
395    }
396}
397
398macro_rules! impl_try_from_floating {
399    ($t: ty) => {
400        impl TryFrom<$t> for NumericBuf {
401            type Error = NumericTryFromError;
402
403            #[inline]
404            fn try_from(f: $t) -> Result<Self, Self::Error> {
405                from_floating(f)
406            }
407        }
408    };
409}
410
411impl_try_from_floating!(f32);
412impl_try_from_floating!(f64);
413
414/// Converts a numeric to signed integer.
415fn into_signed<T: Signed>(var: &mut NumericVar) -> Result<T, NumericTryFromError> {
416    // Ensure no overflowing happened when NumericDigit convert to T
417    debug_assert!(std::mem::size_of::<T>() >= std::mem::size_of::<NumericDigit>());
418
419    if var.is_nan() {
420        return Err(NumericTryFromError::invalid());
421    }
422
423    // Round to nearest integer
424    var.round_common(0);
425
426    // Check for zero input
427    var.strip();
428    let ndigits = var.ndigits();
429    if ndigits == 0 {
430        return Ok(T::ZERO);
431    }
432
433    // For input like 10000000000, we must treat stripped digits as real.
434    // So the loop assumes there are weight+1 digits before the decimal point.
435    let weight = var.weight();
436    debug_assert!(weight >= 0);
437    debug_assert!(ndigits <= weight + 1);
438
439    // Construct the result. To avoid issues with converting a value
440    // corresponding to INT64_MIN (which can't be represented as a positive 64
441    // bit two's complement integer), accumulate value as a negative number.
442    let digits = var.digits();
443    let mut val = T::from_numeric_digit(digits[0]).negative();
444    for i in 1..=weight {
445        let (new_val, overflowing) = val.overflowing_mul(T::from_i64(NBASE as i64));
446        if overflowing {
447            return Err(NumericTryFromError::overflow());
448        }
449        val = new_val;
450
451        if i < ndigits {
452            let (new_val, overflowing) =
453                val.overflowing_sub(T::from_numeric_digit(digits[i as usize]));
454            if overflowing {
455                return Err(NumericTryFromError::overflow());
456            }
457            val = new_val;
458        }
459    }
460
461    if !var.is_negative() {
462        if val == T::MIN_VALUE {
463            return Err(NumericTryFromError::overflow());
464        }
465
466        val = val.negative();
467    }
468
469    Ok(val)
470}
471
472/// Converts a numeric to unsigned integer.
473fn into_unsigned<T: Unsigned>(var: &mut NumericVar) -> Result<T, NumericTryFromError> {
474    // Ensure no overflowing happened when NumericDigit convert to T
475    debug_assert!(std::mem::size_of::<T>() >= std::mem::size_of::<NumericDigit>());
476
477    if var.is_nan() {
478        return Err(NumericTryFromError::invalid());
479    }
480
481    // Round to nearest integer
482    var.round_common(0);
483
484    // Check for zero input
485    var.strip();
486    let ndigits = var.ndigits();
487    if ndigits == 0 {
488        return Ok(T::ZERO);
489    }
490
491    if var.is_negative() {
492        return Err(NumericTryFromError::overflow());
493    }
494
495    // For input like 10000000000, we must treat stripped digits as real.
496    // So the loop assumes there are weight+1 digits before the decimal point.
497    let weight = var.weight();
498    debug_assert!(weight >= 0);
499    debug_assert!(ndigits <= weight + 1);
500
501    // Construct the result.
502    let digits = var.digits();
503    let mut val = T::from_numeric_digit(digits[0]);
504    for i in 1..=weight {
505        let (new_val, overflowing) = val.overflowing_mul(T::from_u64(NBASE as u64));
506        if overflowing {
507            return Err(NumericTryFromError::overflow());
508        }
509        val = new_val;
510
511        if i < ndigits {
512            let (new_val, overflowing) =
513                val.overflowing_add(T::from_numeric_digit(digits[i as usize]));
514            if overflowing {
515                return Err(NumericTryFromError::overflow());
516            }
517            val = new_val;
518        }
519    }
520
521    Ok(val)
522}
523
524macro_rules! impl_try_from_var_for_signed {
525    ($t: ty) => {
526        impl<'a> TryFrom<NumericVar<'a>> for $t {
527            type Error = NumericTryFromError;
528
529            #[inline]
530            fn try_from(mut value: NumericVar<'a>) -> Result<Self, Self::Error> {
531                into_signed(&mut value)
532            }
533        }
534    };
535}
536
537macro_rules! impl_try_from_var_for_unsigned {
538    ($t: ty) => {
539        impl<'a> TryFrom<NumericVar<'a>> for $t {
540            type Error = NumericTryFromError;
541
542            #[inline]
543            fn try_from(mut value: NumericVar) -> Result<Self, Self::Error> {
544                into_unsigned(&mut value)
545            }
546        }
547    };
548}
549
550impl_try_from_var_for_signed!(i16);
551impl_try_from_var_for_signed!(i32);
552impl_try_from_var_for_signed!(i64);
553impl_try_from_var_for_signed!(i128);
554
555impl_try_from_var_for_unsigned!(u16);
556impl_try_from_var_for_unsigned!(u32);
557impl_try_from_var_for_unsigned!(u64);
558impl_try_from_var_for_unsigned!(u128);
559
560impl<'a> TryFrom<NumericVar<'a>> for i8 {
561    type Error = NumericTryFromError;
562
563    #[inline]
564    fn try_from(value: NumericVar<'a>) -> Result<Self, Self::Error> {
565        let val = TryInto::<i16>::try_into(value)?;
566        if val > i8::max_value() as i16 || val < i8::min_value() as i16 {
567            Err(NumericTryFromError::overflow())
568        } else {
569            Ok(val as i8)
570        }
571    }
572}
573
574impl<'a> TryFrom<NumericVar<'a>> for u8 {
575    type Error = NumericTryFromError;
576
577    #[inline]
578    fn try_from(value: NumericVar<'a>) -> Result<Self, Self::Error> {
579        let val = TryInto::<u16>::try_into(value)?;
580        if val > u8::max_value() as u16 {
581            Err(NumericTryFromError::overflow())
582        } else {
583            Ok(val as u8)
584        }
585    }
586}
587
588impl<'a> TryFrom<NumericVar<'a>> for usize {
589    type Error = NumericTryFromError;
590
591    #[inline]
592    fn try_from(value: NumericVar<'a>) -> Result<Self, Self::Error> {
593        if std::mem::size_of::<usize>() == 8 {
594            let val = TryInto::<u64>::try_into(value)?;
595            Ok(val as usize)
596        } else if std::mem::size_of::<usize>() == 4 {
597            let val = TryInto::<u32>::try_into(value)?;
598            Ok(val as usize)
599        } else {
600            panic!("invalid usize size")
601        }
602    }
603}
604
605impl<'a> TryFrom<NumericVar<'a>> for isize {
606    type Error = NumericTryFromError;
607
608    #[inline]
609    fn try_from(value: NumericVar<'a>) -> Result<Self, Self::Error> {
610        if std::mem::size_of::<isize>() == 8 {
611            let val = TryInto::<i64>::try_into(value)?;
612            Ok(val as isize)
613        } else if std::mem::size_of::<isize>() == 4 {
614            let val = TryInto::<i32>::try_into(value)?;
615            Ok(val as isize)
616        } else {
617            panic!("invalid usize size")
618        }
619    }
620}
621
622impl<'a> TryFrom<&NumericVar<'a>> for f32 {
623    type Error = NumericTryFromError;
624
625    #[inline]
626    fn try_from(value: &NumericVar<'a>) -> Result<Self, Self::Error> {
627        use std::fmt::Write;
628        if value.is_nan() {
629            return Ok(std::f32::NAN);
630        }
631        let mut buf = String::with_capacity(32);
632        buf.write_fmt(format_args!("{}", value))
633            .expect("returned an error unexpectedly");
634
635        match strtod::strtod(&buf) {
636            Some(val) => {
637                let f = val as f32;
638                if (f.is_infinite() && !val.is_infinite()) || (f == 0.0 && val != 0.0) {
639                    return Err(NumericTryFromError::overflow());
640                }
641                Ok(f)
642            }
643            None => Err(NumericTryFromError::overflow()),
644        }
645    }
646}
647
648impl<'a> TryFrom<&NumericVar<'a>> for f64 {
649    type Error = NumericTryFromError;
650
651    #[inline]
652    fn try_from(value: &NumericVar<'a>) -> Result<Self, Self::Error> {
653        use std::fmt::Write;
654        if value.is_nan() {
655            return Ok(std::f64::NAN);
656        }
657        let mut buf = String::with_capacity(32);
658        buf.write_fmt(format_args!("{}", value))
659            .expect("returned an error unexpectedly");
660
661        match strtod::strtod(&buf) {
662            Some(f) => Ok(f),
663            None => Err(NumericTryFromError::overflow()),
664        }
665    }
666}
667
668macro_rules! impl_try_from_numeric_to_integer {
669    ($t: ty) => {
670        impl TryFrom<NumericBuf> for $t {
671            type Error = NumericTryFromError;
672
673            #[inline]
674            fn try_from(value: NumericBuf) -> Result<Self, Self::Error> {
675                TryFrom::try_from(value.into_var())
676            }
677        }
678        impl TryFrom<&NumericBuf> for $t {
679            type Error = NumericTryFromError;
680
681            #[inline]
682            fn try_from(value: &NumericBuf) -> Result<Self, Self::Error> {
683                let var = value.as_var().clone();
684                TryFrom::try_from(var)
685            }
686        }
687        impl TryFrom<&Numeric> for $t {
688            type Error = NumericTryFromError;
689
690            #[inline]
691            fn try_from(value: &Numeric) -> Result<Self, Self::Error> {
692                let var = value.as_var().clone();
693                TryFrom::try_from(var)
694            }
695        }
696        impl<'a> TryFrom<&NumericVar<'a>> for $t {
697            type Error = NumericTryFromError;
698
699            #[inline]
700            fn try_from(value: &NumericVar<'a>) -> Result<Self, Self::Error> {
701                let var = value.clone();
702                TryFrom::try_from(var)
703            }
704        }
705    };
706}
707
708macro_rules! impl_try_from_numeric_to_floating {
709    ($t: ty) => {
710        impl TryFrom<NumericBuf> for $t {
711            type Error = NumericTryFromError;
712
713            #[inline]
714            fn try_from(value: NumericBuf) -> Result<Self, Self::Error> {
715                TryFrom::try_from(&value.into_var())
716            }
717        }
718        impl TryFrom<&NumericBuf> for $t {
719            type Error = NumericTryFromError;
720
721            #[inline]
722            fn try_from(value: &NumericBuf) -> Result<Self, Self::Error> {
723                TryFrom::try_from(&value.as_var())
724            }
725        }
726        impl TryFrom<&Numeric> for $t {
727            type Error = NumericTryFromError;
728
729            #[inline]
730            fn try_from(value: &Numeric) -> Result<Self, Self::Error> {
731                TryFrom::try_from(&value.as_var())
732            }
733        }
734    };
735}
736
737impl_try_from_numeric_to_integer!(i8);
738impl_try_from_numeric_to_integer!(i16);
739impl_try_from_numeric_to_integer!(i32);
740impl_try_from_numeric_to_integer!(i64);
741impl_try_from_numeric_to_integer!(i128);
742impl_try_from_numeric_to_integer!(u8);
743impl_try_from_numeric_to_integer!(u16);
744impl_try_from_numeric_to_integer!(u32);
745impl_try_from_numeric_to_integer!(u64);
746impl_try_from_numeric_to_integer!(u128);
747impl_try_from_numeric_to_integer!(isize);
748impl_try_from_numeric_to_integer!(usize);
749
750impl_try_from_numeric_to_floating!(f32);
751impl_try_from_numeric_to_floating!(f64);
752
753#[cfg(test)]
754mod tests {
755    use super::*;
756    use crate::Numeric;
757    use std::convert::TryInto;
758    use std::fmt::Debug;
759    use std::ops::Deref;
760
761    // use this function to test `as_bytes` in convert functions.
762    fn transform(val: &NumericBuf) -> &Numeric {
763        val.deref()
764    }
765
766    fn assert_from<V: Into<NumericBuf>, E: AsRef<str>>(val: V, expected: E) {
767        let numeric = val.into();
768        assert_eq!(transform(&numeric).to_string(), expected.as_ref());
769    }
770
771    #[test]
772    fn from_i8() {
773        assert_from(0i8, "0");
774        assert_from(1i8, "1");
775        assert_from(-1i8, "-1");
776        assert_from(127i8, "127");
777        assert_from(-128i8, "-128");
778    }
779
780    #[test]
781    fn from_i16() {
782        assert_from(0i16, "0");
783        assert_from(1i16, "1");
784        assert_from(-1i16, "-1");
785        assert_from(32767i16, "32767");
786        assert_from(-32768i16, "-32768");
787    }
788
789    #[test]
790    fn from_i32() {
791        assert_from(0i32, "0");
792        assert_from(1i32, "1");
793        assert_from(-1i32, "-1");
794        assert_from(2147483647i32, "2147483647");
795        assert_from(-2147483647i32, "-2147483647");
796    }
797
798    #[test]
799    fn from_i64() {
800        assert_from(0i64, "0");
801        assert_from(1i64, "1");
802        assert_from(-1i64, "-1");
803        assert_from(9223372036854775807i64, "9223372036854775807");
804        assert_from(-9223372036854775808i64, "-9223372036854775808");
805    }
806
807    #[test]
808    fn from_i128() {
809        assert_from(0i128, "0");
810        assert_from(1i128, "1");
811        assert_from(-1i128, "-1");
812        assert_from(
813            170141183460469231731687303715884105727i128,
814            "170141183460469231731687303715884105727",
815        );
816        assert_from(
817            -170141183460469231731687303715884105728i128,
818            "-170141183460469231731687303715884105728",
819        );
820    }
821
822    #[test]
823    fn from_u8() {
824        assert_from(0u8, "0");
825        assert_from(1u8, "1");
826        assert_from(255u8, "255");
827    }
828
829    #[test]
830    fn from_u16() {
831        assert_from(0u16, "0");
832        assert_from(1u16, "1");
833        assert_from(65535u16, "65535");
834    }
835
836    #[test]
837    fn from_u32() {
838        assert_from(0u32, "0");
839        assert_from(1u32, "1");
840        assert_from(4294967295u32, "4294967295");
841    }
842
843    #[test]
844    fn from_u64() {
845        assert_from(0u64, "0");
846        assert_from(1u64, "1");
847        assert_from(18446744073709551615u64, "18446744073709551615");
848    }
849
850    #[test]
851    fn from_u128() {
852        assert_from(0u128, "0");
853        assert_from(1u128, "1");
854        assert_from(
855            340282366920938463463374607431768211455u128,
856            "340282366920938463463374607431768211455",
857        );
858    }
859
860    #[test]
861    fn from_bool() {
862        assert_from(true, "1");
863        assert_from(false, "0");
864    }
865
866    #[test]
867    fn from_usize() {
868        assert_from(0usize, "0");
869        assert_from(1usize, "1");
870        if std::mem::size_of::<usize>() == 8 {
871            assert_from(18446744073709551615usize, "18446744073709551615");
872        } else if std::mem::size_of::<usize>() == 4 {
873            assert_from(4294967295usize, "4294967295u32");
874        }
875    }
876
877    #[test]
878    fn from_isize() {
879        assert_from(0isize, "0");
880        assert_from(1isize, "1");
881        if std::mem::size_of::<isize>() == 8 {
882            assert_from(9223372036854775807isize, "9223372036854775807");
883            assert_from(-9223372036854775808isize, "-9223372036854775808");
884        } else if std::mem::size_of::<isize>() == 4 {
885            assert_from(2147483647isize, "2147483647");
886            assert_from(-2147483648isize, "-2147483648");
887        }
888    }
889
890    fn assert_try_from<V: TryInto<NumericBuf, Error = NumericTryFromError>, E: AsRef<str>>(
891        val: V,
892        expected: E,
893    ) {
894        let numeric = val.try_into().unwrap();
895        assert_eq!(transform(&numeric).to_string(), expected.as_ref());
896    }
897
898    fn assert_try_from_invalid<V: TryInto<NumericBuf, Error = NumericTryFromError>>(val: V) {
899        let result = val.try_into();
900        assert_eq!(result.unwrap_err(), NumericTryFromError::invalid());
901    }
902
903    #[test]
904    fn try_from_f32() {
905        assert_try_from_invalid(std::f32::INFINITY);
906        assert_try_from_invalid(std::f32::NEG_INFINITY);
907        assert_try_from(std::f32::NAN, "NaN");
908        assert_try_from(0.0f32, "0");
909        assert_try_from(-0.0f32, "0");
910        assert_try_from(0.000001f32, "0.000001");
911        assert_try_from(0.0000001f32, "0.0000001");
912        assert_try_from(0.555555f32, "0.555555");
913        assert_try_from(0.5555555f32, "0.555556");
914        assert_try_from(0.999999f32, "0.999999");
915        assert_try_from(0.9999999f32, "1");
916        assert_try_from(1.0f32, "1");
917        assert_try_from(1.00001f32, "1.00001");
918        assert_try_from(1.000001f32, "1");
919        assert_try_from(1.555555f32, "1.55555");
920        assert_try_from(1.5555555f32, "1.55556");
921        assert_try_from(1.99999f32, "1.99999");
922        assert_try_from(1.999999f32, "2");
923        assert_try_from(1e-6f32, "0.000001");
924        assert_try_from(1e-10f32, "0.0000000001");
925        assert_try_from(1.23456789e10f32, "12345700000");
926        assert_try_from(1.23456789e-10f32, "0.000000000123457");
927    }
928
929    #[test]
930    fn try_from_f64() {
931        assert_try_from_invalid(std::f64::INFINITY);
932        assert_try_from_invalid(std::f64::NEG_INFINITY);
933        assert_try_from(std::f64::NAN, "NaN");
934        assert_try_from(0.0f64, "0");
935        assert_try_from(-0.0f64, "0");
936        assert_try_from(0.000000000000001f64, "0.000000000000001");
937        assert_try_from(0.0000000000000001f64, "0.0000000000000001");
938        assert_try_from(0.555555555555555f64, "0.555555555555555");
939        assert_try_from(0.5555555555555556f64, "0.555555555555556");
940        assert_try_from(0.999999999999999f64, "0.999999999999999");
941        assert_try_from(0.9999999999999999f64, "1");
942        assert_try_from(1.0f64, "1");
943        assert_try_from(1.00000000000001f64, "1.00000000000001");
944        assert_try_from(1.000000000000001f64, "1");
945        assert_try_from(1.55555555555555f64, "1.55555555555555");
946        assert_try_from(1.555555555555556f64, "1.55555555555556");
947        assert_try_from(1.99999999999999f64, "1.99999999999999");
948        assert_try_from(1.999999999999999f64, "2");
949        assert_try_from(1e-6f64, "0.000001");
950        assert_try_from(1e-20f64, "0.00000000000000000001");
951        assert_try_from(1.234567890123456789e20f64, "123456789012346000000");
952        assert_try_from(
953            1.234567890123456789e-20f64,
954            "0.0000000000000000000123456789012346",
955        );
956    }
957
958    fn try_into<S: AsRef<str>, T: TryFrom<NumericBuf, Error = NumericTryFromError>>(s: S) -> T {
959        let n = s.as_ref().parse::<NumericBuf>().unwrap();
960        let val = TryInto::<T>::try_into(n).unwrap();
961        val
962    }
963
964    fn assert_try_into<
965        S: AsRef<str>,
966        T: TryFrom<NumericBuf, Error = NumericTryFromError> + PartialEq + Debug,
967    >(
968        s: S,
969        expected: T,
970    ) {
971        let val = try_into::<S, T>(s);
972        assert_eq!(val, expected);
973    }
974
975    fn assert_try_into_overflow<T: TryFrom<NumericBuf, Error = NumericTryFromError> + Debug>(
976        s: &str,
977    ) {
978        let n = s.parse::<NumericBuf>().unwrap();
979        let result = TryInto::<T>::try_into(n);
980        assert_eq!(result.unwrap_err(), NumericTryFromError::overflow());
981    }
982
983    fn assert_try_into_invalid<T: TryFrom<NumericBuf, Error = NumericTryFromError> + Debug>(
984        s: &str,
985    ) {
986        let n = s.parse::<NumericBuf>().unwrap();
987        let result = TryInto::<T>::try_into(n);
988        assert_eq!(result.unwrap_err(), NumericTryFromError::invalid());
989    }
990
991    #[test]
992    fn into_i8() {
993        assert_try_into("0", 0i8);
994        assert_try_into("1", 1i8);
995        assert_try_into("-1", -1i8);
996        assert_try_into("127", 127i8);
997        assert_try_into("-128", -128);
998        assert_try_into_overflow::<i8>("128");
999        assert_try_into_overflow::<i8>("-129");
1000        assert_try_into_invalid::<i8>("NaN");
1001    }
1002
1003    #[test]
1004    fn into_i16() {
1005        assert_try_into("0", 0i16);
1006        assert_try_into("1", 1i16);
1007        assert_try_into("-1", -1i16);
1008        assert_try_into("32767", 32767i16);
1009        assert_try_into("-32768", -32768i16);
1010        assert_try_into_overflow::<i16>("32768");
1011        assert_try_into_overflow::<i16>("-32769");
1012        assert_try_into_invalid::<i16>("NaN");
1013    }
1014
1015    #[test]
1016    fn into_i32() {
1017        assert_try_into("0", 0i32);
1018        assert_try_into("1", 1i32);
1019        assert_try_into("-1", -1i32);
1020        assert_try_into("2147483647", 2147483647i32);
1021        assert_try_into("-2147483648", -2147483648i32);
1022        assert_try_into_overflow::<i32>("2147483648");
1023        assert_try_into_overflow::<i32>("-2147483649");
1024        assert_try_into_invalid::<i32>("NaN");
1025    }
1026
1027    #[test]
1028    fn into_i64() {
1029        assert_try_into("0", 0i64);
1030        assert_try_into("1", 1i64);
1031        assert_try_into("-1", -1i64);
1032        assert_try_into("9223372036854775807", 9223372036854775807i64);
1033        assert_try_into("-9223372036854775808", -9223372036854775808i64);
1034        assert_try_into_overflow::<i64>("9223372036854775808");
1035        assert_try_into_overflow::<i64>("-9223372036854775809");
1036        assert_try_into_invalid::<i64>("NaN");
1037    }
1038
1039    #[test]
1040    fn into_i128() {
1041        assert_try_into("0", 0i128);
1042        assert_try_into("1", 1i128);
1043        assert_try_into("-1", -1i128);
1044        assert_try_into(
1045            "170141183460469231731687303715884105727",
1046            170141183460469231731687303715884105727i128,
1047        );
1048        assert_try_into(
1049            "-170141183460469231731687303715884105728",
1050            -170141183460469231731687303715884105728i128,
1051        );
1052        assert_try_into_overflow::<i128>("170141183460469231731687303715884105728");
1053        assert_try_into_overflow::<i128>("-170141183460469231731687303715884105729");
1054        assert_try_into_invalid::<i128>("NaN");
1055    }
1056
1057    #[test]
1058    fn into_u8() {
1059        assert_try_into("0", 0u8);
1060        assert_try_into("1", 1u8);
1061        assert_try_into("255", 255u8);
1062        assert_try_into_overflow::<u8>("256");
1063        assert_try_into_overflow::<u8>("-1");
1064        assert_try_into_invalid::<u8>("NaN");
1065    }
1066
1067    #[test]
1068    fn into_u16() {
1069        assert_try_into("0", 0u16);
1070        assert_try_into("1", 1u16);
1071        assert_try_into("65535", 65535u16);
1072        assert_try_into_overflow::<u16>("65536");
1073        assert_try_into_overflow::<u16>("-1");
1074        assert_try_into_invalid::<u16>("NaN");
1075    }
1076
1077    #[test]
1078    fn into_u32() {
1079        assert_try_into("0", 0u32);
1080        assert_try_into("1", 1u32);
1081        assert_try_into("4294967295", 4294967295u32);
1082        assert_try_into_overflow::<u32>("4294967296");
1083        assert_try_into_overflow::<u32>("-1");
1084        assert_try_into_invalid::<u32>("NaN");
1085    }
1086
1087    #[test]
1088    fn into_u64() {
1089        assert_try_into("0", 0u64);
1090        assert_try_into("1", 1u64);
1091        assert_try_into("18446744073709551615", 18446744073709551615u64);
1092        assert_try_into_overflow::<u64>("18446744073709551616");
1093        assert_try_into_overflow::<u64>("-1");
1094        assert_try_into_invalid::<u64>("NaN");
1095    }
1096
1097    #[test]
1098    fn into_u128() {
1099        assert_try_into("0", 0u128);
1100        assert_try_into("1", 1u128);
1101        assert_try_into(
1102            "340282366920938463463374607431768211455",
1103            340282366920938463463374607431768211455u128,
1104        );
1105        assert_try_into_overflow::<u128>("340282366920938463463374607431768211456");
1106        assert_try_into_overflow::<u128>("-1");
1107        assert_try_into_invalid::<u128>("NaN");
1108    }
1109
1110    #[test]
1111    fn into_usize() {
1112        assert_try_into("0", 0usize);
1113        assert_try_into("1", 1usize);
1114        if std::mem::size_of::<usize>() == 8 {
1115            assert_try_into("18446744073709551615", 18446744073709551615usize);
1116            assert_try_into_overflow::<usize>("18446744073709551616");
1117            assert_try_into_overflow::<usize>("-1");
1118        } else if std::mem::size_of::<usize>() == 4 {
1119            assert_try_into("4294967295", 4294967295usize);
1120            assert_try_into_overflow::<usize>("4294967296");
1121            assert_try_into_overflow::<usize>("-1");
1122        }
1123        assert_try_into_invalid::<usize>("NaN");
1124    }
1125
1126    #[test]
1127    fn into_isize() {
1128        assert_try_into("0", 0isize);
1129        assert_try_into("1", 1isize);
1130        assert_try_into("-1", -1isize);
1131        if std::mem::size_of::<isize>() == 8 {
1132            assert_try_into("9223372036854775807", 9223372036854775807isize);
1133            assert_try_into("-9223372036854775808", -9223372036854775808isize);
1134            assert_try_into_overflow::<isize>("9223372036854775808");
1135            assert_try_into_overflow::<isize>("-9223372036854775809");
1136        } else if std::mem::size_of::<isize>() == 4 {
1137            assert_try_into("2147483647", 2147483647isize);
1138            assert_try_into("-2147483648", -2147483648isize);
1139            assert_try_into_overflow::<isize>("2147483648");
1140            assert_try_into_overflow::<isize>("-2147483649");
1141        }
1142        assert_try_into_invalid::<isize>("NaN");
1143    }
1144
1145    #[test]
1146    fn into_f32() {
1147        assert_try_into("0", 0f32);
1148        assert_try_into("1", 1f32);
1149        assert_try_into("0.000001", 0.000001f32);
1150        assert_try_into("0.0000001", 0.0000001f32);
1151        assert_try_into("0.555555", 0.555555f32);
1152        assert_try_into("0.55555599", 0.555556f32);
1153        assert_try_into("0.999999", 0.999999f32);
1154        assert_try_into("0.99999999", 1.0f32);
1155        assert_try_into("1.00001", 1.00001f32);
1156        assert_try_into("1.00000001", 1.0f32);
1157        assert_try_into("1.23456789e10", 1.23456789e10f32);
1158        assert_try_into("1.23456789e-10", 1.23456789e-10f32);
1159        assert_try_into("3.40282347e+38", std::f32::MAX);
1160        assert_try_into("-3.40282347e+38", std::f32::MIN);
1161        assert_try_into_overflow::<f32>("1e39");
1162        assert_try_into("1.17549435e-38", std::f32::MIN_POSITIVE);
1163        assert!(try_into::<&str, f32>("NaN").is_nan());
1164    }
1165
1166    #[test]
1167    fn into_f64() {
1168        assert_try_into("0", 0f64);
1169        assert_try_into("1", 1f64);
1170        assert_try_into("0.000000000000001", 0.000000000000001f64);
1171        assert_try_into("0.555555555555555", 0.555555555555555f64);
1172        assert_try_into("0.55555555555555599", 0.555555555555556f64);
1173        assert_try_into("0.999999999999999", 0.999999999999999f64);
1174        assert_try_into("0.99999999999999999", 1.0f64);
1175        assert_try_into("1.00000000000001", 1.00000000000001f64);
1176        assert_try_into("1.0000000000000001", 1.0f64);
1177        assert_try_into("1.7976931348623157e+308", std::f64::MAX);
1178        assert_try_into("-1.7976931348623157e+308", std::f64::MIN);
1179        assert_try_into("1e309", std::f64::INFINITY);
1180        assert_try_into("2.2250738585072014e-308", std::f64::MIN_POSITIVE);
1181        assert!(try_into::<&str, f64>("NaN").is_nan());
1182    }
1183}