openpql_prelude/card/
suit4.rs1use super::{
2 BitAnd, BitOr, Card64, CardCount, Idx, Suit, Suit4Inner, fmt, ops,
3};
4
5#[macro_export]
6macro_rules! s4 {
7 ($s:expr) => {
8 $crate::Suit4::from(
9 $s.chars()
10 .filter(|c| !c.is_whitespace())
11 .map(|c| $crate::Suit::from_char(c).unwrap())
12 .collect::<Vec<_>>()
13 .as_ref(),
14 )
15 };
16}
17
18#[derive(
28 Copy, Clone, derive_more::Debug, PartialEq, Eq, BitAnd, BitOr, Default,
29)]
30#[debug("Suit4({})", self)]
31pub struct Suit4(pub(crate) Suit4Inner);
32
33impl Suit4 {
34 pub const ALL: Self = Self(0b1111);
36
37 pub const fn is_empty(self) -> bool {
39 self.0 == 0
40 }
41
42 #[inline]
44 pub const fn set(&mut self, s: Suit) {
45 self.0 |= 1 << s as Idx;
46 }
47
48 #[inline]
50 pub const fn unset(&mut self, s: Suit) {
51 self.0 &= !(1 << s as Idx);
52 }
53
54 #[must_use]
56 #[inline]
57 pub const fn contains_suit(self, s: Suit) -> bool {
58 let v = 1 << (s as Idx);
59 v == v & self.0
60 }
61
62 #[must_use]
64 #[inline]
65 pub const fn count(&self) -> CardCount {
66 self.0.count_ones().to_le_bytes()[0]
67 }
68}
69
70impl fmt::Display for Suit4 {
71 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72 let suits: String = Suit::ARR_ALL
73 .iter()
74 .filter(|&s| self.contains_suit(*s))
75 .map(|s| s.to_char())
76 .collect();
77
78 write!(f, "{suits}")
79 }
80}
81
82impl From<Suit> for Suit4 {
83 fn from(s: Suit) -> Self {
84 Self(1 << s as Idx)
85 }
86}
87
88impl FromIterator<Suit> for Suit4 {
89 fn from_iter<T: IntoIterator<Item = Suit>>(iter: T) -> Self {
90 let mut res = Self::default();
91
92 for suit in iter {
93 res.set(suit);
94 }
95
96 res
97 }
98}
99
100impl From<&[Suit]> for Suit4 {
101 fn from(suits: &[Suit]) -> Self {
102 suits.iter().copied().collect()
103 }
104}
105
106impl ops::BitOrAssign<Suit> for Suit4 {
107 fn bitor_assign(&mut self, rhs: Suit) {
108 self.set(rhs);
109 }
110}
111
112impl From<Card64> for Suit4 {
113 fn from(c: Card64) -> Self {
114 let [s, h, d, c] = Suit::ARR_ALL.map(|s| c.count_by_suit(s) > 0);
115
116 Self(
117 Suit4Inner::from(s)
118 | Suit4Inner::from(h) << 1
119 | Suit4Inner::from(d) << 2
120 | Suit4Inner::from(c) << 3,
121 )
122 }
123}
124
125impl From<Suit4Inner> for Suit4 {
126 fn from(i: Suit4Inner) -> Self {
127 Self(i & Self::ALL.0)
128 }
129}
130
131impl From<Suit4> for Suit4Inner {
132 fn from(v: Suit4) -> Self {
133 v.0
134 }
135}
136
137#[cfg(any(test, feature = "quickcheck"))]
138impl quickcheck::Arbitrary for Suit4 {
139 fn arbitrary(g: &mut quickcheck::Gen) -> Self {
140 let inner = Suit4Inner::arbitrary(g);
141
142 Self(Self::ALL.0 & inner)
143 }
144}
145
146#[cfg(test)]
147#[cfg_attr(coverage_nightly, coverage(off))]
148mod tests {
149 use super::*;
150 use crate::*;
151
152 #[test]
153 fn test_all() {
154 assert_eq!(Suit4::ALL, Suit4(0b1111));
155 }
156
157 #[test]
158 fn test_empty() {
159 assert!(Suit4::default().is_empty());
160 assert!(!Suit4(1).is_empty());
161 }
162
163 #[quickcheck]
164 fn test_set(suit: Suit) {
165 let mut res = Suit4::default();
166 res.set(suit);
167
168 assert!(res.contains_suit(suit));
169 }
170
171 #[quickcheck]
172 fn test_unset(suit: Suit) {
173 let mut res = Suit4::ALL;
174 res.unset(suit);
175
176 assert!(!res.contains_suit(suit));
177 }
178
179 #[quickcheck]
180 #[allow(clippy::cast_possible_truncation)]
181 fn test_count(s4: Suit4) {
182 assert_eq!(
183 s4.count(),
184 Suit::ARR_ALL
185 .iter()
186 .filter(|&s| s4.contains_suit(*s))
187 .count() as CardCount
188 );
189 }
190
191 #[test]
192 fn test_display() {
193 assert_eq!(format!("{:?}", s4!("ds")), "Suit4(sd)");
194 }
195
196 #[quickcheck]
197 fn test_bit_and(s1: Suit, s2: Suit) {
198 let a = Suit4::from(s1);
199 let b = Suit4::from(s2);
200
201 assert_eq!((a & b).is_empty(), s1 != s2);
202 }
203
204 #[quickcheck]
205 fn test_bit_or(s1: Suit, s2: Suit) {
206 let a = Suit4::from(s1);
207 let b = Suit4::from(s2);
208
209 assert!((a | b).contains_suit(s1));
210 assert!((a | b).contains_suit(s2));
211
212 let mut ab = Suit4::default();
213 ab |= s1;
214 ab |= s2;
215 assert_eq!(ab, a | b);
216 }
217
218 #[quickcheck]
219 fn test_from_suit(s1: Suit, s2: Suit) {
220 let suits = Suit4::from(s1);
221
222 assert!(suits.contains_suit(s1));
223
224 let suits = Suit4::from([s1, s2].as_ref());
225
226 assert!(suits.contains_suit(s1));
227 assert!(suits.contains_suit(s2));
228 }
229
230 #[quickcheck]
231 fn test_from_card64(cards: Vec<Card>) {
232 let mut suits = Suit4::default();
233
234 for i in 0..cards.len() {
235 suits.set(cards[i].suit);
236
237 let c64: Card64 = cards[0..=i].into();
238
239 assert_eq!(Suit4::from(c64), suits);
240 }
241 }
242
243 #[quickcheck]
244 fn test_from_and_to_int(i: Suit4Inner) {
245 let obj = Suit4::from(i);
246 let mask = Suit4::ALL.0;
247
248 assert_eq!(obj.0, i & mask);
249 assert_eq!(i & mask, Suit4Inner::from(obj));
250 }
251}