1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
//! BigFloatNumber formatting.

use crate::Sign;
use crate::defs::Radix;
use crate::defs::Error;
use crate::defs::RoundingMode;
use crate::parser;
use crate::num::BigFloatNumber;
use std::fmt::Write;


const DIGIT_CHARS: [char; 16] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', ];

impl BigFloatNumber {

    /// Parses the number from the string `s` using radix `rdx`, precision `p`, and rounding mode `rm`.
    /// Note, since hexadecimal digits include the character "e", the exponent part is separated 
    /// from the mantissa by "_". 
    /// For example, a number with mantissa `123abcdef` and exponent `123` would be formatted as `123abcdef_e+123`.
    /// 
    /// ## Errors
    /// 
    ///  - InvalidArgument: failed to parse input.
    ///  - MemoryAllocation: failed to allocate memory for mantissa.
    ///  - ExponentOverflow: the resulting exponent becomes greater than the maximum allowed value for the exponent.
    pub fn parse(s: &str, rdx: Radix, p: usize, rm: RoundingMode) -> Result<Self, Error> {

        let ps = parser::parse(s, rdx);

        if ps.is_valid() {
            let (m, s, e) = ps.raw_parts();
            BigFloatNumber::convert_from_radix(s, m, e, rdx, p, rm)
        } else {
            Err(Error::InvalidArgument)
        }
    }

    /// Formats the number using radix `rdx` and rounding mode `rm`.
    /// Note, since hexadecimal digits include the character "e", the exponent part is separated 
    /// from the mantissa by "_". 
    /// For example, a number with mantissa `123abcdef` and exponent `123` would be formatted as `123abcdef_e+123`.
    /// 
    /// ## Errors
    /// 
    ///  - MemoryAllocation: failed to allocate memory for mantissa.
    ///  - ExponentOverflow: the resulting exponent becomes greater than the maximum allowed value for the exponent.
    pub fn format(&self, rdx: Radix, rm: RoundingMode) -> Result<String, Error> {

        let (s, m, e) = self.convert_to_radix(rdx, rm)?;

        let mut mstr = if s == Sign::Neg {
            "-".to_owned()
        } else {
            String::new()
        };

        if m.is_empty() {

            mstr.push_str("0.0");

        } else {

            mstr.push(DIGIT_CHARS[m[0] as usize]);
            mstr.push('.');
            mstr.push_str(&m.iter().skip(1).map(|d| { DIGIT_CHARS[*d as usize] }).collect::<String>());

            if rdx == Radix::Hex {
                let _ = write!(mstr, "_");
            }

            if e < 1 {
                let _ = match rdx {
                    Radix::Bin => write!(mstr, "e-{:b}", (e - 1).unsigned_abs()),
                    Radix::Oct => write!(mstr, "e-{:o}", (e - 1).unsigned_abs()),
                    Radix::Dec => write!(mstr, "e-{}", (e - 1).unsigned_abs()),
                    Radix::Hex => write!(mstr, "e-{:x}", (e - 1).unsigned_abs()),
                };    
            } else {
                let _ = match rdx {
                    Radix::Bin => write!(mstr, "e+{:b}", e - 1),
                    Radix::Oct => write!(mstr, "e+{:o}", e - 1),
                    Radix::Dec => write!(mstr, "e+{}", e - 1),
                    Radix::Hex => write!(mstr, "e+{:x}", e - 1),
                };
            };
        }

        Ok(mstr)
    }
}


#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn test_strop() {

        let mut eps = BigFloatNumber::from_word(1, 192).unwrap();

        for _ in 0..10000 {
            for rdx in [Radix::Bin, Radix::Oct, Radix::Hex, Radix::Dec] {

                let n = BigFloatNumber::random_normal(192, -2, -2).unwrap();
                let s = n.format(rdx, RoundingMode::ToEven).unwrap();
                let d = BigFloatNumber::parse(&s, rdx, 200, RoundingMode::ToEven).unwrap();

                if rdx == Radix::Dec {
                    //println!("\n{:?}\n{:?}\n{:?}\n{:?}\n{:?}", n, s, d, n.format(Radix::Hex, RoundingMode::ToEven), d.format(Radix::Hex, RoundingMode::ToEven));
                    eps.set_exponent(n.get_exponent() - 160);
                    assert!(d.sub(&n, RoundingMode::ToEven).unwrap().abs().unwrap().cmp(&eps) < 0);
                } else {
                    assert!(d.cmp(&n) == 0);
                }
            }
        }
    }
}