open_pql/vm/
stack_value.rs

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            // TODO: fix this
58            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            // TODO: fix this
83            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            // TODO: fix this
108            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::Fraction,         VmStackValue::Double(0.0));
282        //assert_def_is(PQLType::Numeric,          VmStackValue::Double(0.0));
283
284        //assert_def_is(PQLType::HandRanking,      VmStackValue::Double(0.0));
285        //assert_def_is(PQLType::LoRating,         VmStackValue::Double(0.0));
286
287        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}