1use super::*;
2
3#[derive(Debug, Clone, Copy, PartialEq)]
4pub enum PQLNumeric {
5 Count(PQLCardCount),
6 Long(PQLLong),
7 Double(PQLDouble),
8 Frac(PQLFraction),
9}
10
11const fn int_add(lhs: PQLLong, rhs: PQLLong) -> Result<PQLNumeric, RuntimeError> {
12 match lhs.checked_add(rhs) {
13 Some(v) => Ok(PQLNumeric::Long(v)),
14 None => Err(RuntimeError::AddOverflow),
15 }
16}
17
18const fn int_sub(lhs: PQLLong, rhs: PQLLong) -> Result<PQLNumeric, RuntimeError> {
19 match lhs.checked_sub(rhs) {
20 Some(v) => Ok(PQLNumeric::Long(v)),
21 None => Err(RuntimeError::SubOverflow),
22 }
23}
24
25const fn int_mul(lhs: PQLLong, rhs: PQLLong) -> Result<PQLNumeric, RuntimeError> {
26 match lhs.checked_mul(rhs) {
27 Some(v) => Ok(PQLNumeric::Long(v)),
28 None => Err(RuntimeError::MulOverflow),
29 }
30}
31
32#[allow(clippy::unnecessary_wraps)]
33const fn dbl_add(lhs: PQLDouble, rhs: PQLDouble) -> Result<PQLNumeric, RuntimeError> {
34 Ok(PQLNumeric::Double(lhs + rhs))
35}
36
37#[allow(clippy::unnecessary_wraps)]
38const fn dbl_sub(lhs: PQLDouble, rhs: PQLDouble) -> Result<PQLNumeric, RuntimeError> {
39 Ok(PQLNumeric::Double(lhs - rhs))
40}
41
42#[allow(clippy::unnecessary_wraps)]
43const fn dbl_mul(lhs: PQLDouble, rhs: PQLDouble) -> Result<PQLNumeric, RuntimeError> {
44 Ok(PQLNumeric::Double(lhs * rhs))
45}
46
47#[allow(clippy::unnecessary_wraps)]
48const fn dbl_div(lhs: PQLDouble, rhs: PQLDouble) -> Result<PQLNumeric, RuntimeError> {
49 Ok(PQLNumeric::Double(lhs / rhs))
50}
51
52impl PQLNumeric {
53 pub fn try_add(self, other: Self) -> Result<Self, RuntimeError> {
54 if self.is_int() && other.is_int() {
55 int_add(self.to_int(), other.to_int())
56 } else {
57 dbl_add(self.to_dbl(), other.to_dbl())
58 }
59 }
60
61 pub fn try_sub(self, other: Self) -> Result<Self, RuntimeError> {
62 if self.is_int() && other.is_int() {
63 int_sub(self.to_int(), other.to_int())
64 } else {
65 dbl_sub(self.to_dbl(), other.to_dbl())
66 }
67 }
68
69 pub fn try_mul(self, other: Self) -> Result<Self, RuntimeError> {
70 if self.is_int() && other.is_int() {
71 int_mul(self.to_int(), other.to_int())
72 } else {
73 dbl_mul(self.to_dbl(), other.to_dbl())
74 }
75 }
76
77 pub const fn try_div(self, other: Self) -> Result<Self, RuntimeError> {
78 dbl_div(self.to_dbl(), other.to_dbl())
79 }
80
81 pub fn partial_compare(self, other: Self) -> Option<cmp::Ordering> {
82 if self.is_int() && other.is_int() {
83 Some(self.to_int().cmp(&other.to_int()))
84 } else {
85 self.to_dbl().partial_cmp(&other.to_dbl())
86 }
87 }
88
89 const fn is_int(self) -> bool {
90 matches!(self, Self::Count(_) | Self::Long(_))
91 }
92
93 fn to_int(self) -> PQLLong {
94 match self {
95 Self::Count(v) => PQLLong::from(v),
96 Self::Long(v) => v,
97 _ => unreachable!(),
98 }
99 }
100
101 #[allow(clippy::cast_lossless)]
102 #[allow(clippy::cast_precision_loss)]
103 pub const fn to_dbl(self) -> PQLDouble {
104 match self {
105 Self::Count(v) => v as PQLDouble,
106 Self::Long(v) => v as PQLDouble,
107 Self::Double(v) => v,
108 Self::Frac(v) => v.to_double(),
109 }
110 }
111}
112
113#[cfg(test)]
114#[cfg_attr(coverage_nightly, coverage(off))]
115pub mod tests {
116 use super::*;
117 use crate::*;
118
119 fn cnt_(v: PQLCardCount) -> PQLNumeric {
120 PQLNumeric::Count(v)
121 }
122
123 fn long(v: PQLLong) -> PQLNumeric {
124 PQLNumeric::Long(v)
125 }
126
127 fn dbl_(v: i8) -> PQLNumeric {
128 PQLNumeric::Double(PQLDouble::from(v))
129 }
130
131 fn frac(v: FractionInner) -> PQLNumeric {
132 PQLNumeric::Frac(PQLFraction::new(v, 1))
133 }
134
135 #[test]
136 fn test_add() {
137 let op = PQLNumeric::try_add;
138 assert_eq!(op(cnt_(1), cnt_(2)), Ok(long(3)));
139 assert_eq!(op(cnt_(1), long(2)), Ok(long(3)));
140 assert_eq!(op(cnt_(1), frac(2)), Ok(dbl_(3)));
141 assert_eq!(op(cnt_(1), dbl_(2)), Ok(dbl_(3)));
142
143 assert_eq!(op(long(1), long(2)), Ok(long(3)));
144 assert_eq!(op(long(1), frac(2)), Ok(dbl_(3)));
145 assert_eq!(op(long(1), dbl_(2)), Ok(dbl_(3)));
146
147 assert_eq!(op(frac(1), frac(2)), Ok(dbl_(3)));
148 assert_eq!(op(frac(1), dbl_(2)), Ok(dbl_(3)));
149
150 assert_eq!(op(dbl_(1), dbl_(2)), Ok(dbl_(3)));
151 }
152
153 #[test]
154 fn test_sub() {
155 let op = PQLNumeric::try_sub;
156 assert_eq!(op(cnt_(1), cnt_(2)), Ok(long(-1)));
157 assert_eq!(op(cnt_(1), long(2)), Ok(long(-1)));
158 assert_eq!(op(cnt_(1), frac(2)), Ok(dbl_(-1)));
159 assert_eq!(op(cnt_(1), dbl_(2)), Ok(dbl_(-1)));
160
161 assert_eq!(op(long(1), long(2)), Ok(long(-1)));
162 assert_eq!(op(long(1), frac(2)), Ok(dbl_(-1)));
163 assert_eq!(op(long(1), dbl_(2)), Ok(dbl_(-1)));
164
165 assert_eq!(op(frac(1), frac(2)), Ok(dbl_(-1)));
166 assert_eq!(op(frac(1), dbl_(2)), Ok(dbl_(-1)));
167
168 assert_eq!(op(dbl_(1), dbl_(2)), Ok(dbl_(-1)));
169 }
170
171 #[test]
172 fn test_mul() {
173 let op = PQLNumeric::try_mul;
174 assert_eq!(op(cnt_(1), cnt_(2)), Ok(long(2)));
175 assert_eq!(op(cnt_(1), long(2)), Ok(long(2)));
176 assert_eq!(op(cnt_(1), frac(2)), Ok(dbl_(2)));
177 assert_eq!(op(cnt_(1), dbl_(2)), Ok(dbl_(2)));
178
179 assert_eq!(op(long(1), long(2)), Ok(long(2)));
180 assert_eq!(op(long(1), frac(2)), Ok(dbl_(2)));
181 assert_eq!(op(long(1), dbl_(2)), Ok(dbl_(2)));
182
183 assert_eq!(op(frac(1), frac(2)), Ok(dbl_(2)));
184 assert_eq!(op(frac(1), dbl_(2)), Ok(dbl_(2)));
185
186 assert_eq!(op(dbl_(1), dbl_(2)), Ok(dbl_(2)));
187 }
188
189 #[test]
190 fn test_div() {
191 let op = PQLNumeric::try_div;
192 let half = PQLNumeric::Double(0.5);
193 assert_eq!(op(cnt_(1), cnt_(2)), Ok(half));
194 assert_eq!(op(cnt_(1), long(2)), Ok(half));
195 assert_eq!(op(cnt_(1), frac(2)), Ok(half));
196 assert_eq!(op(cnt_(1), dbl_(2)), Ok(half));
197
198 assert_eq!(op(long(1), long(2)), Ok(half));
199 assert_eq!(op(long(1), frac(2)), Ok(half));
200 assert_eq!(op(long(1), dbl_(2)), Ok(half));
201
202 assert_eq!(op(frac(1), frac(2)), Ok(half));
203 assert_eq!(op(frac(1), dbl_(2)), Ok(half));
204
205 assert_eq!(op(dbl_(1), dbl_(2)), Ok(half));
206 }
207
208 #[test]
209 fn test_err() {
210 assert_eq!(
211 long(PQLLong::MAX).try_add(cnt_(1)),
212 Err(RuntimeError::AddOverflow)
213 );
214
215 assert_eq!(
216 long(PQLLong::MIN).try_sub(cnt_(1)),
217 Err(RuntimeError::SubOverflow)
218 );
219
220 assert_eq!(
221 long(PQLLong::MIN).try_mul(cnt_(2)),
222 Err(RuntimeError::MulOverflow)
223 );
224 }
225
226 #[test]
227 #[should_panic(expected = "")]
228 fn test_internal() {
229 dbl_(1).to_int();
230 }
231}