1use super::*;
2
3#[derive(Debug, Clone, Copy, From, TryInto, PartialEq, PartialOrd, Display)]
4pub enum VmStackValue {
5 #[display("TODO")]
6 Ref(VmStoreVarIdx),
7 #[display("TODO")]
8 Num(VmStackValueNum),
9 #[display("TODO")]
10 Bool(PQLBoolean),
11 #[display("TODO")]
12 Player(PQLPlayer),
13 #[display("TODO")]
14 Street(PQLStreet),
15 #[display("TODO")]
16 Card(PQLCard),
17 #[display("TODO")]
18 Rank(Option<Rank>),
19 #[display("TODO")]
20 Ranks(PQLRankSet),
21 #[display("{_0}")]
22 FlopCategory(PQLFlopHandCategory),
23 #[display("{_0}")]
24 HandType(PQLHandType),
25 #[display("TODO")]
26 Rating(PQLHiRating),
27}
28
29macro_rules! impl_arith {
30 ($fn_name:ident, $err:expr, $op:tt) => {
31 pub fn $fn_name(self, other: Self) -> Result<Self, RuntimeError> {
32 match (self, other) {
33 (Self::Num(l), Self::Num(r)) => Ok(Self::Num(l $op r)),
34 _ => Err($err),
35 }
36 }
37 };
38}
39
40impl VmStackValue {
41 impl_arith!(try_add, RuntimeError::AddFailed, +);
42 impl_arith!(try_sub, RuntimeError::AddFailed, -);
43 impl_arith!(try_mul, RuntimeError::AddFailed, *);
44 impl_arith!(try_div, RuntimeError::AddFailed, /);
45
46 pub const DBL_ZERO: Self = Self::Num(VmStackValueNum::Double(0.0));
47 pub const INT_ZERO: Self = Self::Num(VmStackValueNum::Long(0));
48 pub const INT_ONE: Self = Self::Num(VmStackValueNum::Long(1));
49
50 pub(crate) fn default_of(t: PQLType) -> Result<Self, RuntimeError> {
51 match t {
52 PQLType::Card => Ok(PQLCard::default().into()),
53 PQLType::FlopHandCategory => {
54 Ok(PQLFlopHandCategory::default().into())
55 }
56 PQLType::HandType => Ok(PQLHandType::default().into()),
57 PQLType::HiRating | PQLType::LoRating => {
59 Ok(PQLHiRating::default().into())
60 }
61 PQLType::Player => Ok(PQLPlayer::default().into()),
62 PQLType::Rank => Ok(Some(PQLRank::default()).into()),
63 PQLType::RankSet => Ok(PQLRankSet::default().into()),
64 PQLType::Street => Ok(PQLStreet::default().into()),
65
66 PQLType::Boolean => Ok(PQLBoolean::default().into()),
67
68 PQLType::PlayerCount | PQLType::CardCount => {
69 Ok(PQLCardCount::default().into())
70 }
71 PQLType::Integer | PQLType::Long => Ok(PQLLong::default().into()),
72 PQLType::Fraction
73 | PQLType::Numeric
74 | PQLType::Equity
75 | PQLType::Double => Ok(PQLDouble::default().into()),
76 _ => Err(RuntimeError::DefaultNotDefined),
77 }
78 }
79
80 pub fn min_of(t: PQLType, g: PQLGame) -> Result<Self, RuntimeError> {
81 match t {
82 PQLType::Fraction
84 | PQLType::Numeric
85 | PQLType::Double
86 | PQLType::Equity => Ok(PQLDouble::MIN.into()),
87
88 PQLType::Integer | PQLType::Long => Ok(PQLLong::MIN.into()),
89
90 PQLType::CardCount | PQLType::PlayerCount => {
91 Ok(PQLCardCount::MIN.into())
92 }
93
94 PQLType::FlopHandCategory => {
95 Ok(PQLFlopHandCategory::from((FlopHandCategory::MIN, g)).into())
96 }
97 PQLType::HandType => {
98 Ok(PQLHandType::from((HandType::MIN, g)).into())
99 }
100 PQLType::HiRating => Ok(PQLHiRating::MIN.into()),
101 _ => Err(RuntimeError::MinMaxNotDefined),
102 }
103 }
104
105 pub fn max_of(t: PQLType, g: PQLGame) -> Result<Self, RuntimeError> {
106 match t {
107 PQLType::Fraction
109 | PQLType::Numeric
110 | PQLType::Double
111 | PQLType::Equity => Ok(PQLDouble::MAX.into()),
112
113 PQLType::Integer | PQLType::Long => Ok(PQLLong::MAX.into()),
114
115 PQLType::CardCount | PQLType::PlayerCount => {
116 Ok(PQLCardCount::MAX.into())
117 }
118 PQLType::FlopHandCategory => {
119 Ok(PQLFlopHandCategory::from((FlopHandCategory::MAX, g)).into())
120 }
121 PQLType::HandType => {
122 Ok(PQLHandType::from((HandType::MAX, g)).into())
123 }
124 PQLType::HiRating => Ok(PQLHiRating::MAX.into()),
125 _ => Err(RuntimeError::MinMaxNotDefined),
126 }
127 }
128}
129
130impl From<VmStackValue> for PQLType {
131 fn from(v: VmStackValue) -> Self {
132 match v {
133 VmStackValue::Bool(_) => Self::Boolean,
134 VmStackValue::Ref(_)
135 | VmStackValue::Num(VmStackValueNum::Long(_)) => Self::Long,
136 VmStackValue::Num(VmStackValueNum::CardCount(_)) => Self::CardCount,
137 VmStackValue::Num(VmStackValueNum::Double(_)) => Self::Double,
138 VmStackValue::Player(_) => Self::Player,
139 VmStackValue::Street(_) => Self::String,
140 VmStackValue::Card(_) => Self::Card,
141 VmStackValue::Rank(_) => Self::Rank,
142 VmStackValue::Ranks(_) => Self::RankSet,
143 VmStackValue::FlopCategory(_) => Self::FlopHandCategory,
144 VmStackValue::HandType(_) => Self::HandType,
145 VmStackValue::Rating(_) => Self::HiRating,
146 }
147 }
148}
149
150impl From<PQLRank> for VmStackValue {
151 fn from(r: PQLRank) -> Self {
152 Self::Rank(Some(r))
153 }
154}
155
156#[cfg_attr(coverage_nightly, coverage(off))]
157#[cfg(test)]
158mod tests {
159 use super::*;
160 use crate::*;
161
162 impl VmStackValue {
163 pub const ONE: Self = Self::Num(VmStackValueNum::Long(1));
164
165 pub fn strict_eq(&self, other: &Self) -> bool {
166 use VmStackValue::*;
167
168 match (self, other) {
169 (Ref(l), Ref(r)) => l == r,
170 (Bool(l), Bool(r)) => l == r,
171 (Num(l), Num(r)) => l == r,
172 (Player(l), Player(r)) => l == r,
173 (Street(l), Street(r)) => l == r,
174 (Card(l), Card(r)) => l == r,
175 (Rank(l), Rank(r)) => l == r,
176 (Ranks(l), Ranks(r)) => l == r,
177 (FlopCategory(l), FlopCategory(r)) => l == r,
178 (HandType(l), HandType(r)) => l == r,
179 (Rating(l), Rating(r)) => l == r,
180 _ => false,
181 }
182 }
183 }
184
185 impl Arbitrary for VmStackValue {
186 fn arbitrary(g: &mut quickcheck::Gen) -> Self {
187 match g.choose(&(0..=10).collect::<Vec<_>>()).unwrap() {
188 0 => Self::Ref(VmStoreVarIdx::arbitrary(g)),
189 1 => Self::Bool(PQLBoolean::arbitrary(g)),
190 2 => Self::Num(VmStackValueNum::arbitrary(g)),
191 3 => Self::Player(PQLPlayer::arbitrary(g)),
192 4 => Self::Street(PQLStreet::arbitrary(g)),
193 5 => Self::Card(PQLCard::arbitrary(g)),
194 6 => Self::Rank(Some(PQLRank::arbitrary(g))),
195 7 => Self::Ranks(PQLRankSet::arbitrary(g)),
196 8 => Self::FlopCategory(PQLFlopHandCategory::arbitrary(g)),
197 9 => Self::HandType(PQLHandType::arbitrary(g)),
198 _ => Self::Rating(PQLHiRating::arbitrary(g)),
199 }
200 }
201 }
202
203 #[test]
204 fn test_partial_eq() {
205 let cs = cards!("As Ks");
206 let card_a: VmStackValue = cs[0].into();
207 let card_k: VmStackValue = cs[1].into();
208
209 assert!(card_a == card_a);
210 assert!(card_a != card_k);
211
212 assert!(
213 VmStackValue::Player(0.into()) != VmStackValue::Player(1.into())
214 );
215
216 assert!(
217 VmStackValue::Street(PQLStreet::Flop)
218 == VmStackValue::Street(PQLStreet::Flop)
219 );
220 assert!(
221 VmStackValue::Rank(Some(Rank::R2))
222 == VmStackValue::Rank(Some(Rank::R2))
223 );
224 assert!(
225 VmStackValue::Ranks(r16!("AK")) == VmStackValue::Ranks(r16!("AK"))
226 );
227
228 assert!(
229 VmStackValue::Rank(Some(Rank::R2))
230 != VmStackValue::Street(PQLStreet::Flop)
231 );
232 }
233
234 #[quickcheck]
235 fn test_arith(nl: VmStackValueNum, nr: VmStackValueNum) {
236 fn to_v(f: PQLDouble) -> VmStackValue {
237 f.into()
238 }
239 let zero = VmStackValue::default_of(PQLType::Long).unwrap();
240
241 let fl = nl.cast_dbl();
242 let fr = nr.cast_dbl();
243
244 let l: VmStackValue = nl.into();
245 let r: VmStackValue = nr.into();
246
247 assert_eq!(to_v(fl + fr), l.try_add(r).unwrap());
248 assert_eq!(to_v(fl - fr), l.try_sub(r).unwrap());
249 assert_eq!(to_v(fl * fr), l.try_mul(r).unwrap());
250
251 if r != zero {
252 assert_eq!(to_v(fl / fr), l.try_div(r).unwrap());
253 }
254 }
255
256 #[test]
257 fn test_arith_error() {
258 let l = VmStackValue::default_of(PQLType::Card).unwrap();
259 let r = VmStackValue::default_of(PQLType::Card).unwrap();
260
261 assert!(l.try_add(r).is_err());
262 assert!(l.try_sub(r).is_err());
263 assert!(l.try_mul(r).is_err());
264 assert!(l.try_div(r).is_err());
265 }
266
267 #[test]
268 fn test_default_of() {
269 fn assert_def_is(t: PQLType, v: VmStackValue) {
270 assert!(VmStackValue::default_of(t).unwrap().strict_eq(&v));
271 }
272 assert_def_is(PQLType::PlayerCount, PQLCardCount::default().into());
273 assert_def_is(PQLType::CardCount, PQLCardCount::default().into());
274
275 assert_def_is(PQLType::Long, PQLLong::default().into());
276 assert_def_is(PQLType::Integer, PQLLong::default().into());
277
278 assert_def_is(PQLType::Double, PQLDouble::default().into());
279 assert_def_is(PQLType::Equity, PQLDouble::default().into());
280
281 assert_def_is(PQLType::Boolean, bool::default().into());
288 assert_def_is(PQLType::Card, Card::default().into());
289 assert_def_is(
290 PQLType::FlopHandCategory,
291 PQLFlopHandCategory::default().into(),
292 );
293 assert_def_is(PQLType::HandType, PQLHandType::default().into());
294 assert_def_is(PQLType::HiRating, PQLHiRating::default().into());
295 assert_def_is(PQLType::Player, PQLPlayer::default().into());
296 assert_def_is(PQLType::Rank, Some(PQLRank::default()).into());
297 assert_def_is(PQLType::RankSet, PQLRankSet::default().into());
298 assert_def_is(PQLType::Street, PQLStreet::default().into());
299
300 assert!(VmStackValue::default_of(PQLType::Range).is_err());
301 assert!(VmStackValue::default_of(PQLType::BoardRange).is_err());
302 assert!(VmStackValue::default_of(PQLType::String).is_err());
303 }
304
305 #[test]
306 fn test_min_of() {
307 let g = PQLGame::default();
308
309 assert_eq!(
310 VmStackValue::min_of(PQLType::FlopHandCategory, g),
311 Ok(PQLFlopHandCategory::min(g).into())
312 );
313
314 assert_eq!(
315 VmStackValue::min_of(PQLType::HandType, g),
316 Ok(PQLHandType::min(g).into())
317 );
318
319 assert_eq!(
320 VmStackValue::min_of(PQLType::HiRating, g),
321 Ok(PQLHiRating::MIN.into())
322 );
323
324 assert!(VmStackValue::min_of(PQLType::Card, g).is_err());
325 }
326
327 #[test]
328 fn test_max_of() {
329 let g = PQLGame::default();
330
331 assert_eq!(
332 VmStackValue::max_of(PQLType::FlopHandCategory, g),
333 Ok(PQLFlopHandCategory::max(g).into())
334 );
335
336 assert_eq!(
337 VmStackValue::max_of(PQLType::HandType, g),
338 Ok(PQLHandType::max(g).into())
339 );
340
341 assert_eq!(
342 VmStackValue::max_of(PQLType::HiRating, g),
343 Ok(PQLHiRating::MAX.into())
344 );
345
346 assert!(VmStackValue::max_of(PQLType::Card, g).is_err());
347 }
348
349 #[quickcheck]
350 fn test_to_type(v: VmStackValue) {
351 let expected = match v {
352 VmStackValue::Bool(_) => PQLType::Boolean,
353 VmStackValue::Ref(_)
354 | VmStackValue::Num(VmStackValueNum::Long(_)) => PQLType::Long,
355 VmStackValue::Num(VmStackValueNum::CardCount(_)) => {
356 PQLType::CardCount
357 }
358 VmStackValue::Num(VmStackValueNum::Double(_)) => PQLType::Double,
359 VmStackValue::Player(_) => PQLType::Player,
360 VmStackValue::Street(_) => PQLType::String,
361 VmStackValue::Card(_) => PQLType::Card,
362 VmStackValue::Rank(_) => PQLType::Rank,
363 VmStackValue::Ranks(_) => PQLType::RankSet,
364 VmStackValue::FlopCategory(_) => PQLType::FlopHandCategory,
365 VmStackValue::HandType(_) => PQLType::HandType,
366 VmStackValue::Rating(_) => PQLType::HiRating,
367 };
368
369 assert_eq!(expected, PQLType::from(v));
370 }
371}