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(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::Fraction,         VmStackValue::Double(0.0));
281        //assert_def_is(PQLType::Numeric,          VmStackValue::Double(0.0));
282
283        //assert_def_is(PQLType::HandRanking,      VmStackValue::Double(0.0));
284        //assert_def_is(PQLType::LoRating,         VmStackValue::Double(0.0));
285
286        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}