open_pql/vm/
stack_value_num.rs1use super::*;
2
3#[derive(Debug, Clone, Copy, From, TryInto)]
4pub enum VmStackValueNum {
5 Double(PQLDouble),
6 Long(PQLLong),
7 CardCount(PQLCardCount),
8}
9
10impl VmStackValueNum {
11 #[allow(clippy::cast_precision_loss)]
12 pub fn cast_dbl(self) -> PQLDouble {
13 match self {
14 Self::Double(v) => v,
15 Self::Long(v) => v as PQLDouble,
16 Self::CardCount(v) => PQLDouble::from(v),
17 }
18 }
19
20 #[allow(clippy::cast_possible_truncation)]
21 pub fn cast_int(self) -> PQLLong {
22 match self {
23 Self::Double(v) => v.floor() as PQLLong,
24 Self::Long(v) => v,
25 Self::CardCount(v) => PQLLong::from(v),
26 }
27 }
28}
29
30const EPSILON: PQLDouble = 1e-6;
31
32fn float_eq(l: PQLDouble, r: PQLDouble) -> bool {
33 (l - r).abs() <= EPSILON
34}
35
36impl PartialEq for VmStackValueNum {
37 fn eq(&self, other: &Self) -> bool {
38 match (self, other) {
39 (_, Self::Double(_)) | (Self::Double(_), _) => {
40 float_eq(self.cast_dbl(), other.cast_dbl())
41 }
42 _ => self.cast_int() == other.cast_int(),
43 }
44 }
45}
46
47impl PartialOrd for VmStackValueNum {
48 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
49 if self == other {
50 return Some(Ordering::Equal);
51 }
52
53 match (self, other) {
54 (_, Self::Double(_)) | (Self::Double(_), _) => {
55 self.cast_dbl().partial_cmp(&other.cast_dbl())
56 }
57 _ => self.cast_int().partial_cmp(&other.cast_int()),
58 }
59 }
60}
61
62macro_rules! impl_from {
64 ($ty:ty) => {
65 impl From<$ty> for VmStackValue {
66 fn from(v: $ty) -> Self {
67 VmStackValueNum::from(v).into()
68 }
69 }
70 };
71}
72
73impl_from!(PQLCardCount);
74impl_from!(PQLDouble);
75impl_from!(PQLLong);
76
77macro_rules! impl_try_into {
78 ($ty:ty) => {
79 impl TryFrom<VmStackValue> for $ty {
80 type Error = PQLError;
81
82 fn try_from(v: VmStackValue) -> Result<Self, Self::Error> {
83 if let VmStackValue::Num(v) = v {
84 if let Ok(v) = v.try_into() {
85 return Ok(v);
86 }
87 }
88
89 Err(InternalError::BrokenStack.into())
90 }
91 }
92 };
93}
94
95impl_try_into!(PQLCardCount);
96impl_try_into!(PQLDouble);
97impl_try_into!(PQLLong);
98
99macro_rules! impl_op {
100 ($ty:ty, $name:ident, $op:tt) => {
101 impl $ty for VmStackValueNum {
102 type Output = Self;
103
104 fn $name(self, rhs: Self) -> Self::Output {
105 match (self, rhs) {
106 (_, Self::Double(_)) | (Self::Double(_), _) => {
107 Self::from(self.cast_dbl() $op rhs.cast_dbl())
108 }
109 _ => Self::from(self.cast_int() $op rhs.cast_int()),
110 }
111 }
112 }
113 };
114}
115
116impl Div for VmStackValueNum {
117 type Output = Self;
118
119 fn div(self, rhs: Self) -> Self::Output {
120 Self::from(self.cast_dbl() / rhs.cast_dbl())
121 }
122}
123
124impl_op!(Add, add, +);
125impl_op!(Sub, sub, -);
126impl_op!(Mul, mul, *);
127
128#[cfg(test)]
129mod tests {
130 use VmStackValueNum::*;
131
132 use super::*;
133 use crate::*;
134
135 impl Arbitrary for VmStackValueNum {
136 #[allow(clippy::cast_precision_loss)]
137 fn arbitrary(g: &mut quickcheck::Gen) -> Self {
138 match g.choose(&[0, 1, 2]).unwrap() {
139 0 => Double(PQLLong::arbitrary(g) as PQLDouble / 10.0), 1 => Long(i32::arbitrary(g).into()), _ => CardCount(PQLCardCount::arbitrary(g)),
142 }
143 }
144 }
145
146 #[test]
147 fn test_eq() {
148 let v = Double(100.0 + EPSILON / 10.0);
149
150 assert_eq!(Double(1.0).cast_int(), 1);
151
152 assert_eq!(v, Double(100.0));
153 assert_eq!(v, Long(100));
154 assert_eq!(v, CardCount(100));
155
156 assert!(v <= Double(100.0));
157 assert!(v <= Long(100));
158 assert!(v <= CardCount(100));
159 assert!(v >= Double(100.0));
160 assert!(v >= Long(100));
161 assert!(v >= CardCount(100));
162 }
163
164 #[test]
165 fn test_cmp() {
166 let v = Double(100.0);
167
168 assert!(v >= Long(99));
169 assert!(v > CardCount(99));
170 assert!(v <= Long(101));
171 assert!(v < CardCount(101));
172 assert!(Long(99) < CardCount(101));
173 }
174
175 #[test]
176 fn test_from_and_into() {
177 assert_eq!(VmStackValue::Num(Long(0)), PQLLong::from(0).into());
178 assert_eq!(Ok(0), PQLLong::try_from(VmStackValue::Num(Long(0))));
179 assert!(PQLLong::try_from(VmStackValue::Rank(Some(Rank::R2))).is_err());
180 assert!(PQLLong::try_from(VmStackValue::Num(Double(0.0))).is_err());
181 }
182}