1use super::{One, Scan, ScanSource, Zero};
2use std::{cmp::Ordering, ops::Neg};
3
4pub mod addsub;
5pub mod convert;
6
7#[derive(PartialEq, PartialOrd, Eq, Ord, Copy, Clone, Debug, Hash)]
8enum Sign {
9 Minus,
10 Zero,
11 Plus,
12}
13
14impl Neg for Sign {
15 type Output = Self;
16
17 fn neg(self) -> Self::Output {
18 match self {
19 Sign::Minus => Sign::Plus,
20 Sign::Zero => Sign::Zero,
21 Sign::Plus => Sign::Minus,
22 }
23 }
24}
25
26const ZERO: Decimal = Decimal {
27 sign: Sign::Zero,
28 integer: Vec::new(),
29 decimal: Vec::new(),
30};
31
32const POW10: [u64; RADIX_LEN + 1] = [
33 1,
34 10,
35 100,
36 1_000,
37 10_000,
38 100_000,
39 1_000_000,
40 10_000_000,
41 100_000_000,
42 1_000_000_000,
43 10_000_000_000,
44 100_000_000_000,
45 1_000_000_000_000,
46 10_000_000_000_000,
47 100_000_000_000_000,
48 1_000_000_000_000_000,
49 10_000_000_000_000_000,
50 100_000_000_000_000_000,
51 1_000_000_000_000_000_000,
52];
53
54const RADIX: u64 = POW10[RADIX_LEN];
55const RADIX_LEN: usize = 18;
56
57#[derive(Clone, Debug, PartialEq, Eq, Hash)]
58pub struct Decimal {
59 sign: Sign,
60 integer: Vec<u64>,
61 decimal: Vec<u64>,
62}
63
64impl Default for Decimal {
65 fn default() -> Self {
66 Decimal::zero()
67 }
68}
69
70impl Zero for Decimal {
71 fn zero() -> Self {
72 ZERO
73 }
74
75 fn is_zero(&self) -> bool {
76 self.sign == Sign::Zero
77 }
78}
79
80impl One for Decimal {
81 fn one() -> Self {
82 Decimal {
83 sign: Sign::Plus,
84 integer: vec![1],
85 decimal: Vec::new(),
86 }
87 }
88}
89
90impl PartialOrd for Decimal {
91 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
92 Some(self.cmp(other))
93 }
94}
95
96impl Ord for Decimal {
97 fn cmp(&self, other: &Self) -> Ordering {
98 self.sign.cmp(&other.sign).then_with(|| match self.sign {
99 Sign::Minus => other.cmp_absolute_parts(self),
100 Sign::Zero => Ordering::Equal,
101 Sign::Plus => self.cmp_absolute_parts(other),
102 })
103 }
104}
105
106impl Neg for Decimal {
107 type Output = Self;
108
109 fn neg(self) -> Self::Output {
110 Self {
111 sign: -self.sign,
112 integer: self.integer,
113 decimal: self.decimal,
114 }
115 }
116}
117
118impl Decimal {
119 fn cmp_absolute_parts(&self, other: &Self) -> Ordering {
120 self.integer
121 .len()
122 .cmp(&other.integer.len())
123 .then_with(|| self.integer.iter().rev().cmp(other.integer.iter().rev()))
124 .then_with(|| self.decimal.iter().cmp(other.decimal.iter()))
125 }
126 fn normalize(&mut self) {
127 if let Some(&0) = self.decimal.last() {
128 let len = self
129 .decimal
130 .iter()
131 .rposition(|&d| d != 0)
132 .map_or(0, |i| i + 1);
133 self.decimal.truncate(len);
134 }
135 if self.decimal.len() < self.decimal.capacity() / 4 {
136 self.decimal.shrink_to_fit();
137 }
138 if let Some(&0) = self.integer.last() {
139 let len = self
140 .integer
141 .iter()
142 .rposition(|&d| d != 0)
143 .map_or(0, |i| i + 1);
144 self.integer.truncate(len);
145 }
146 if self.integer.len() < self.integer.capacity() / 4 {
147 self.integer.shrink_to_fit();
148 }
149 if self.integer.is_empty() && self.decimal.is_empty() {
150 self.sign = Sign::Zero;
151 }
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158 use crate::tools::Xorshift;
159 use crate::tools::testutil::integer_boundary_values;
160
161 #[test]
162 fn test_decimal_arithmetic() {
163 let mut rng = Xorshift::default();
164 let mut cases: Vec<_> = (-10..=10)
165 .flat_map(|a| (-10..=10).flat_map(move |b| (-4..=4).map(move |scale| (a, b, scale))))
166 .collect();
167 for _ in 0..10_000 {
168 let digits = rng.random(0..=35u32);
169 let bound = 10i128.pow(digits);
170 let a = rng.random(-bound..=bound);
171 let b = match rng.random(0..4) {
172 0 => a,
173 1 => -a,
174 _ => rng.random(-bound..=bound),
175 };
176 let scale = rng.random(-80..=80i32);
177 cases.push((a, b, scale));
178 }
179 for (a, b, scale) in cases {
180 let format = |x: i128| {
181 if x == 0 {
182 return "0".to_owned();
183 }
184 let mut s = x.abs().to_string();
185 if scale > 0 {
186 let places = scale as usize;
187 if s.len() <= places {
188 s = "0".repeat(places + 1 - s.len()) + &s;
189 }
190 s.insert(s.len() - places, '.');
191 s = s.trim_end_matches('0').trim_end_matches('.').to_owned();
192 } else {
193 s.push_str(&"0".repeat((-scale) as usize));
194 }
195 if x < 0 {
196 s.insert(0, '-');
197 }
198 s
199 };
200 let x: Decimal = format(a).parse().unwrap();
201 let y: Decimal = format(b).parse().unwrap();
202 assert_eq!(x.to_string(), format(a));
203 assert_eq!(x.cmp(&y), a.cmp(&b));
204 assert_eq!(x.partial_cmp(&y), Some(a.cmp(&b)));
205 assert_eq!((-x.clone()).to_string(), format(-a));
206 assert_eq!((x.clone() + y.clone()).to_string(), format(a + b));
207 assert_eq!((&x + &y).to_string(), format(a + b));
208 assert_eq!((x.clone() - y.clone()).to_string(), format(a - b));
209 assert_eq!((&x - &y).to_string(), format(a - b));
210 let mut z = x.clone();
211 z += &y;
212 assert_eq!(z.to_string(), format(a + b));
213 z -= &y;
214 assert_eq!(z, x);
215 assert_eq!(x.is_zero(), a == 0);
216 }
217 }
218
219 #[test]
220 fn test_decimal_conversion() {
221 let mut rng = Xorshift::default();
222 macro_rules! check_integer {
223 ($($t:ty),*) => {$(
224 for x in integer_boundary_values!($t).into_iter().chain(rng.random_iter(..).take(1000)) {
225 let decimal = Decimal::from(x);
226 assert_eq!(decimal.to_string(), x.to_string());
227 assert_eq!(x.to_string().parse::<Decimal>().unwrap(), decimal);
228 }
229 )*};
230 }
231 check_integer!(
232 u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize
233 );
234 for _ in 0..1000 {
235 let x = rng.random(-1_000_000..=1_000_000) as f64 / 10f64.powi(rng.random(0..=12));
236 assert_eq!(Decimal::from(x).to_string(), x.to_string());
237 let x = x as f32;
238 assert_eq!(Decimal::from(x).to_string(), x.to_string());
239 let n = rng.random(0..=100);
240 let digits: String = rng
241 .random_iter(b'0'..=b'9')
242 .take(n)
243 .map(char::from)
244 .collect();
245 let sign = if rng.random(0..2) == 0 { "+" } else { "-" };
246 let s = format!("{}00{}.{}00", sign, digits, digits);
247 let decimal: Decimal = s.parse().unwrap();
248 assert_eq!(decimal.to_string().parse::<Decimal>().unwrap(), decimal);
249 let mut invalid = s;
250 invalid.insert(
251 rng.random(0..=invalid.len()),
252 char::from(rng.random(b'a'..=b'z')),
253 );
254 assert!(invalid.parse::<Decimal>().is_err(), "{invalid}");
255 }
256 }
257}