meta_language/translation/
decimal.rs1use std::cmp::Ordering;
7use std::fmt::Write as _;
8
9#[derive(Clone, Debug, PartialEq, Eq)]
11pub struct Decimal {
12 pub negative: bool,
13 pub digits: String,
15}
16
17impl Decimal {
18 #[must_use]
21 pub fn parse(text: &str) -> Option<Self> {
22 let text = text.trim();
23 let (negative, body) = text
24 .strip_prefix('-')
25 .map_or((false, text), |rest| (true, rest));
26 let lower = body.to_ascii_lowercase();
27 let (radix, digits) = [("0x", 16), ("0o", 8), ("0b", 2)]
28 .into_iter()
29 .find_map(|(prefix, radix)| lower.strip_prefix(prefix).map(|rest| (radix, rest)))
30 .unwrap_or((10, lower.as_str()));
31 if digits.is_empty() || (radix != 10 && negative) {
32 return None;
33 }
34 let mut limbs: Vec<u64> = vec![0];
36 for ch in digits.chars() {
37 let digit = u64::from(ch.to_digit(radix)?);
38 let mut carry = digit;
39 for limb in &mut limbs {
40 let value = *limb * u64::from(radix) + carry;
41 *limb = value % 1_000_000_000;
42 carry = value / 1_000_000_000;
43 }
44 while carry > 0 {
45 limbs.push(carry % 1_000_000_000);
46 carry /= 1_000_000_000;
47 }
48 }
49 let mut out = String::new();
50 for (index, limb) in limbs.iter().rev().enumerate() {
51 if index == 0 {
52 out.push_str(&limb.to_string());
53 } else {
54 let _ = write!(out, "{limb:09}");
55 }
56 }
57 let zero = out == "0";
58 Some(Self {
59 negative: negative && !zero,
60 digits: out,
61 })
62 }
63
64 #[must_use]
65 pub fn from_i128(value: i128) -> Self {
66 Self {
67 negative: value < 0,
68 digits: value.unsigned_abs().to_string(),
69 }
70 }
71
72 #[must_use]
73 pub fn from_u128(value: u128) -> Self {
74 Self {
75 negative: false,
76 digits: value.to_string(),
77 }
78 }
79
80 #[must_use]
81 pub fn negate(mut self) -> Self {
82 if self.digits != "0" {
83 self.negative = !self.negative;
84 }
85 self
86 }
87
88 #[must_use]
89 pub const fn is_negative(&self) -> bool {
90 self.negative
91 }
92
93 #[must_use]
95 pub fn to_i128(&self) -> Option<i128> {
96 let text = self.to_string();
97 text.parse().ok()
98 }
99}
100
101impl std::fmt::Display for Decimal {
102 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103 if self.negative {
104 formatter.write_str("-")?;
105 }
106 formatter.write_str(&self.digits)
107 }
108}
109
110fn magnitude(left: &str, right: &str) -> Ordering {
111 left.len().cmp(&right.len()).then_with(|| left.cmp(right))
112}
113
114impl Ord for Decimal {
115 fn cmp(&self, other: &Self) -> Ordering {
116 match (self.negative, other.negative) {
117 (false, true) => Ordering::Greater,
118 (true, false) => Ordering::Less,
119 (false, false) => magnitude(&self.digits, &other.digits),
120 (true, true) => magnitude(&other.digits, &self.digits),
121 }
122 }
123}
124
125impl PartialOrd for Decimal {
126 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
127 Some(self.cmp(other))
128 }
129}