1use crate::intrinsic::real::Real;
4use std::ops::{Div, Mul};
5use zarch::hfp::Hfp;
6use zarch::wide::U256;
7
8pub mod parse;
9
10pub fn fixed_number(negative: bool, magnitude: U256, scale: u32, integers: u32) -> String {
14 let digits = magnitude.to_u128().map_or_else(|| decimal(magnitude), |m| m.to_string());
15 let digits = format!("{digits:0>width$}", width = scale as usize + 1);
16 let (whole, fraction) = digits.split_at(digits.len() - scale as usize);
17 let kept = &whole[whole.len().saturating_sub(integers.max(1) as usize)..];
18 let trimmed = kept.trim_start_matches('0');
19 let whole = if trimmed.is_empty() { "0" } else { trimmed };
20 let zero = magnitude.is_zero() || (whole == "0" && fraction.bytes().all(|b| b == b'0'));
21 let sign = if negative && !zero { "-" } else { "" };
22 if scale == 0 { format!("{sign}{whole}") } else { format!("{sign}{whole}.{fraction}") }
23}
24
25fn decimal(mut m: U256) -> String {
26 let mut out = Vec::new();
27 let ten = U256::from_u128(10);
28 while !m.is_zero() {
29 let (q, r) = m.div_rem(ten);
30 out.push(b'0' + r.lo as u8);
31 m = q;
32 }
33 out.reverse();
34 String::from_utf8(out).unwrap_or_default()
35}
36
37pub fn float_number(value: Hfp, decimals: u32) -> String {
40 let v = Real::from_hfp(value);
41 if v.is_zero() {
42 return format!("0.{}E+00", "0".repeat(decimals as usize));
43 }
44 let magnitude = v.abs();
45 let mut exponent = magnitude.to_f64().log10().floor() as i32;
46 let significant = |e: i32| -> u128 {
47 let shift = decimals as i32 - e;
48 let scaled = if shift >= 0 { magnitude.mul(power_of_ten(shift as u32)) } else { magnitude.div(power_of_ten(shift.unsigned_abs())) };
49 scaled.round_to_integer().unwrap_or(0).unsigned_abs()
50 };
51 let low = 10u128.pow(decimals);
52 let mut digits = significant(exponent);
53 if digits >= low * 10 {
54 exponent += 1;
55 digits = significant(exponent);
56 } else if digits < low {
57 exponent -= 1;
58 digits = significant(exponent);
59 }
60 let text = digits.to_string();
61 let sign = if v.is_negative() { "-" } else { "" };
62 let exponent_sign = if exponent < 0 { '-' } else { '+' };
63 format!("{sign}{}.{}E{exponent_sign}{:02}", &text[..1], &text[1..], exponent.unsigned_abs())
64}
65
66fn power_of_ten(mut n: u32) -> Real {
67 let mut scale = Real::ONE;
68 while n > 0 {
69 let step = n.min(38);
70 scale = scale.mul(Real::from_u128(10u128.pow(step)));
71 n -= step;
72 }
73 scale
74}
75
76pub fn trimmed(text: &str, justified: bool) -> &str {
79 let t = if justified { text.trim_start_matches(' ') } else { text.trim_end_matches(' ') };
80 if t.is_empty() && !text.is_empty() { &text[..1] } else { t }
81}
82
83pub fn string(text: &str) -> String {
86 let mut out = String::with_capacity(text.len() + 2);
87 out.push('"');
88 for c in text.chars() {
89 match c {
90 '"' => out.push_str("\\\""),
91 '\\' => out.push_str("\\\\"),
92 '\u{8}' => out.push_str("\\b"),
93 '\t' => out.push_str("\\t"),
94 '\n' => out.push_str("\\n"),
95 '\u{c}' => out.push_str("\\f"),
96 '\r' => out.push_str("\\r"),
97 '\u{85}' => out.push_str("\\x"),
98 c if (c as u32) < 0x20 => out.push_str(&format!("\\u{:04X}", c as u32)),
99 c => out.push(c),
100 }
101 }
102 out.push('"');
103 out
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109 use zarch::hfp::Precision;
110
111 fn u(n: u128) -> U256 {
112 U256::from_u128(n)
113 }
114
115 #[test]
116 fn fixed_point_values_trim_as_the_language_reference_shows() {
117 assert_eq!(fixed_number(false, u(78), 1, 2), "7.8");
118 assert_eq!(fixed_number(true, u(90), 1, 2), "-9.0");
119 assert_eq!(fixed_number(true, u(12340), 3, 3), "-12.340");
120 assert_eq!(fixed_number(false, u(45), 2, 4), "0.45");
121 assert_eq!(fixed_number(false, u(13), 0, 4), "13");
122 assert_eq!(fixed_number(false, u(0), 0, 4), "0");
123 assert_eq!(fixed_number(true, u(0), 2, 3), "0.00");
124 assert_eq!(fixed_number(false, u(123_456), 0, 5), "23456");
125 }
126
127 #[test]
128 fn floating_point_values_take_the_external_float_picture() {
129 let long = |n: i128| Hfp::from_integer(n, Precision::Long);
130 assert_eq!(float_number(long(78).div(long(10), Default::default()).unwrap(), 17), "7.79999999999999982E+00");
131 assert_eq!(float_number(long(-1234), 8), "-1.23400000E+03");
132 assert_eq!(float_number(long(1).div(long(1000), Default::default()).unwrap(), 8), "1.00000000E-03");
133 assert_eq!(float_number(Hfp::zero(Precision::Short), 8), "0.00000000E+00");
134 }
135
136 #[test]
137 fn strings_are_escaped_as_ibm_escapes_them() {
138 assert_eq!(string("a\"b\\c\td\u{85}\u{1}"), "\"a\\\"b\\\\c\\td\\x\\u0001\"");
139 assert_eq!(trimmed("SX1234 ", false), "SX1234");
140 assert_eq!(trimmed(" ", false), " ");
141 assert_eq!(trimmed(" AB", true), "AB");
142 }
143}