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(test)]
157mod tests {
158 use super::*;
159 use crate::*;
160
161 impl VmStackValue {
162 pub const ONE: Self = Self::Num(VmStackValueNum::Long(1));
163
164 pub fn strict_eq(&self, other: &Self) -> bool {
165 use VmStackValue::*;
166
167 match (self, other) {
168 (Ref(l), Ref(r)) => l == r,
169 (Bool(l), Bool(r)) => l == r,
170 (Num(l), Num(r)) => l == r,
171 (Player(l), Player(r)) => l == r,
172 (Street(l), Street(r)) => l == r,
173 (Card(l), Card(r)) => l == r,
174 (Rank(l), Rank(r)) => l == r,
175 (Ranks(l), Ranks(r)) => l == r,
176 (FlopCategory(l), FlopCategory(r)) => l == r,
177 (HandType(l), HandType(r)) => l == r,
178 (Rating(l), Rating(r)) => l == r,
179 _ => false,
180 }
181 }
182 }
183
184 impl Arbitrary for VmStackValue {
185 fn arbitrary(g: &mut quickcheck::Gen) -> Self {
186 match g.choose(&(0..=10).collect::<Vec<_>>()).unwrap() {
187 0 => Self::Ref(VmStoreVarIdx::arbitrary(g)),
188 1 => Self::Bool(PQLBoolean::arbitrary(g)),
189 2 => Self::Num(VmStackValueNum::arbitrary(g)),
190 3 => Self::Player(PQLPlayer::arbitrary(g)),
191 4 => Self::Street(PQLStreet::arbitrary(g)),
192 5 => Self::Card(PQLCard::arbitrary(g)),
193 6 => Self::Rank(Some(PQLRank::arbitrary(g))),
194 7 => Self::Ranks(PQLRankSet::arbitrary(g)),
195 8 => Self::FlopCategory(PQLFlopHandCategory::arbitrary(g)),
196 9 => Self::HandType(PQLHandType::arbitrary(g)),
197 _ => Self::Rating(PQLHiRating::arbitrary(g)),
198 }
199 }
200 }
201
202 #[test]
203 fn test_partial_eq() {
204 let cs = cards!("As Ks");
205 let card_a: VmStackValue = cs[0].into();
206 let card_k: VmStackValue = cs[1].into();
207
208 assert!(card_a == card_a);
209 assert!(card_a != card_k);
210
211 assert!(
212 VmStackValue::Player(0.into()) != VmStackValue::Player(1.into())
213 );
214
215 assert!(
216 VmStackValue::Street(PQLStreet::Flop)
217 == VmStackValue::Street(PQLStreet::Flop)
218 );
219 assert!(
220 VmStackValue::Rank(Some(Rank::R2))
221 == VmStackValue::Rank(Some(Rank::R2))
222 );
223 assert!(
224 VmStackValue::Ranks(r16!("AK")) == VmStackValue::Ranks(r16!("AK"))
225 );
226
227 assert!(
228 VmStackValue::Rank(Some(Rank::R2))
229 != VmStackValue::Street(PQLStreet::Flop)
230 );
231 }
232
233 #[quickcheck]
234 fn test_arith(nl: VmStackValueNum, nr: VmStackValueNum) {
235 fn to_v(f: PQLDouble) -> VmStackValue {
236 f.into()
237 }
238 let zero = VmStackValue::default_of(PQLType::Long).unwrap();
239
240 let fl = nl.cast_dbl();
241 let fr = nr.cast_dbl();
242
243 let l: VmStackValue = nl.into();
244 let r: VmStackValue = nr.into();
245
246 assert_eq!(to_v(fl + fr), l.try_add(r).unwrap());
247 assert_eq!(to_v(fl - fr), l.try_sub(r).unwrap());
248 assert_eq!(to_v(fl * fr), l.try_mul(r).unwrap());
249
250 if r != zero {
251 assert_eq!(to_v(fl / fr), l.try_div(r).unwrap());
252 }
253 }
254
255 #[test]
256 fn test_arith_error() {
257 let l = VmStackValue::default_of(PQLType::Card).unwrap();
258 let r = VmStackValue::default_of(PQLType::Card).unwrap();
259
260 assert!(l.try_add(r).is_err());
261 assert!(l.try_sub(r).is_err());
262 assert!(l.try_mul(r).is_err());
263 assert!(l.try_div(r).is_err());
264 }
265
266 #[test]
267 fn test_default_of() {
268 fn assert_def_is(t: PQLType, v: VmStackValue) {
269 assert!(VmStackValue::default_of(t).unwrap().strict_eq(&v));
270 }
271 assert_def_is(PQLType::PlayerCount, PQLCardCount::default().into());
272 assert_def_is(PQLType::CardCount, PQLCardCount::default().into());
273
274 assert_def_is(PQLType::Long, PQLLong::default().into());
275 assert_def_is(PQLType::Integer, PQLLong::default().into());
276
277 assert_def_is(PQLType::Double, PQLDouble::default().into());
278 assert_def_is(PQLType::Equity, PQLDouble::default().into());
279
280 assert_def_is(PQLType::Boolean, bool::default().into());
287 assert_def_is(PQLType::Card, Card::default().into());
288 assert_def_is(
289 PQLType::FlopHandCategory,
290 PQLFlopHandCategory::default().into(),
291 );
292 assert_def_is(PQLType::HandType, PQLHandType::default().into());
293 assert_def_is(PQLType::HiRating, PQLHiRating::default().into());
294 assert_def_is(PQLType::Player, PQLPlayer::default().into());
295 assert_def_is(PQLType::Rank, Some(PQLRank::default()).into());
296 assert_def_is(PQLType::RankSet, PQLRankSet::default().into());
297 assert_def_is(PQLType::Street, PQLStreet::default().into());
298
299 assert!(VmStackValue::default_of(PQLType::Range).is_err());
300 assert!(VmStackValue::default_of(PQLType::BoardRange).is_err());
301 assert!(VmStackValue::default_of(PQLType::String).is_err());
302 }
303
304 #[test]
305 fn test_min_of() {
306 let g = PQLGame::default();
307
308 assert_eq!(
309 VmStackValue::min_of(PQLType::FlopHandCategory, g),
310 Ok(PQLFlopHandCategory::min(g).into())
311 );
312
313 assert_eq!(
314 VmStackValue::min_of(PQLType::HandType, g),
315 Ok(PQLHandType::min(g).into())
316 );
317
318 assert_eq!(
319 VmStackValue::min_of(PQLType::HiRating, g),
320 Ok(PQLHiRating::MIN.into())
321 );
322
323 assert!(VmStackValue::min_of(PQLType::Card, g).is_err());
324 }
325
326 #[test]
327 fn test_max_of() {
328 let g = PQLGame::default();
329
330 assert_eq!(
331 VmStackValue::max_of(PQLType::FlopHandCategory, g),
332 Ok(PQLFlopHandCategory::max(g).into())
333 );
334
335 assert_eq!(
336 VmStackValue::max_of(PQLType::HandType, g),
337 Ok(PQLHandType::max(g).into())
338 );
339
340 assert_eq!(
341 VmStackValue::max_of(PQLType::HiRating, g),
342 Ok(PQLHiRating::MAX.into())
343 );
344
345 assert!(VmStackValue::max_of(PQLType::Card, g).is_err());
346 }
347
348 #[quickcheck]
349 fn test_to_type(v: VmStackValue) {
350 let expected = match v {
351 VmStackValue::Bool(_) => PQLType::Boolean,
352 VmStackValue::Ref(_)
353 | VmStackValue::Num(VmStackValueNum::Long(_)) => PQLType::Long,
354 VmStackValue::Num(VmStackValueNum::CardCount(_)) => {
355 PQLType::CardCount
356 }
357 VmStackValue::Num(VmStackValueNum::Double(_)) => PQLType::Double,
358 VmStackValue::Player(_) => PQLType::Player,
359 VmStackValue::Street(_) => PQLType::String,
360 VmStackValue::Card(_) => PQLType::Card,
361 VmStackValue::Rank(_) => PQLType::Rank,
362 VmStackValue::Ranks(_) => PQLType::RankSet,
363 VmStackValue::FlopCategory(_) => PQLType::FlopHandCategory,
364 VmStackValue::HandType(_) => PQLType::HandType,
365 VmStackValue::Rating(_) => PQLType::HiRating,
366 };
367
368 assert_eq!(expected, PQLType::from(v));
369 }
370}