1use super::*;
2
3#[cfg(any(test, feature = "benchmark"))]
4#[macro_export]
5macro_rules! s4 {
6 ($s:expr) => {
7 $crate::Suit4::from(
8 $s.chars()
9 .filter(|c| !c.is_whitespace())
10 .map(|c| $crate::Suit::try_from(c).unwrap())
11 .collect::<Vec<_>>()
12 .as_ref(),
13 )
14 };
15}
16
17#[derive(Copy, Clone, PartialEq, Eq, BitAnd, BitOr)]
23pub struct Suit4(u8);
24
25impl Suit4 {
26 #[must_use]
39 #[inline]
40 pub const fn from_u8(v: u8) -> Self {
41 Self(v)
42 }
43
44 #[must_use]
57 #[inline]
58 pub const fn to_u8(self) -> u8 {
59 self.0
60 }
61
62 #[inline]
63 const fn from_suit(s: Suit) -> Self {
64 Self(1 << s as u8)
65 }
66
67 #[must_use]
79 #[inline]
80 pub const fn empty() -> Self {
81 Self(0)
82 }
83
84 pub const fn is_empty(self) -> bool {
98 self.0 == 0
99 }
100
101 #[inline]
114 pub fn set(&mut self, s: Suit) {
115 self.0 |= 1 << s as u8;
116 }
117
118 #[inline]
131 pub fn unset(&mut self, s: Suit) {
132 self.0 &= !(1 << s as u8);
133 }
134
135 #[must_use]
148 #[inline]
149 pub const fn contains_suit(self, s: Suit) -> bool {
150 let v = 1u8 << (s as u8);
151 v == v & self.0
152 }
153
154 #[must_use]
166 #[inline]
167 pub const fn count(&self) -> PQLCardCount {
168 self.0.count_ones().to_le_bytes()[0]
169 }
170}
171
172impl From<Suit> for Suit4 {
173 fn from(s: Suit) -> Self {
174 Self::from_suit(s)
175 }
176}
177
178impl From<&[Suit]> for Suit4 {
179 fn from(ss: &[Suit]) -> Self {
180 let mut res = Self::empty();
181
182 for s in ss {
183 res.set(*s);
184 }
185
186 res
187 }
188}
189
190pub fn u8_to_suit_str(v: u8) -> String {
191 let to_c = |i: u8| {
192 if v & 1 << i == 0 {
193 '\0'
194 } else {
195 SUIT_NAMES[i as usize]
196 }
197 };
198
199 (0..N_SUITS)
200 .map(to_c)
201 .filter(|c| c.is_alphanumeric())
202 .collect::<String>()
203}
204
205impl fmt::Debug for Suit4 {
206 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
207 f.debug_tuple("Suit4")
208 .field(&format_args!("{}", u8_to_suit_str(self.0)))
209 .finish()
210 }
211}
212
213impl From<Card64> for Suit4 {
214 fn from(c: Card64) -> Self {
215 let [s, h, d, c] = Suit::ARR_ALL.map(|s| c.count_by_suit(s) > 0);
218
219 Self(
220 u8::from(s)
221 | u8::from(h) << 1
222 | u8::from(d) << 2
223 | u8::from(c) << 3,
224 )
225 }
226}
227
228#[cfg(test)]
229mod tests {
230 use super::*;
231
232 #[test]
233 fn test_empty() {
234 assert_eq!(Suit4::empty(), Suit4(0));
235 assert!(Suit4::empty().is_empty());
236 assert!(!Suit4(1).is_empty());
237 }
238
239 #[quickcheck]
240 fn test_set_and_contains(s: Suit) {
241 let mut suits = Suit4::empty();
242
243 suits.set(s);
244
245 assert!(!suits.is_empty());
246 assert!(suits.contains_suit(s));
247
248 suits.unset(s);
249
250 assert!(suits.is_empty());
251 assert!(!suits.contains_suit(s));
252 }
253
254 #[quickcheck]
255 fn test_u8(i: u8) -> TestResult {
256 if i > 0b1111 {
257 return TestResult::discard();
258 }
259
260 assert_eq!(Suit4(i), Suit4::from_u8(i));
261 assert_eq!(i, Suit4(i).to_u8());
262
263 TestResult::passed()
264 }
265
266 #[quickcheck]
267 fn test_from_suit(s1: Suit, s2: Suit) {
268 let suits = Suit4::from(s1);
269
270 assert!(suits.contains_suit(s1));
271
272 let suits = Suit4::from([s1, s2].as_ref());
273
274 assert!(suits.contains_suit(s1));
275 assert!(suits.contains_suit(s2));
276 }
277
278 #[quickcheck]
279 fn test_bit_and(s1: Suit, s2: Suit) {
280 let a = Suit4::from(s1);
281 let b = Suit4::from(s2);
282
283 assert_eq!((a & b).is_empty(), s1 != s2);
284 }
285
286 #[quickcheck]
287 fn test_bit_or(s1: Suit, s2: Suit) {
288 let a = Suit4::from(s1);
289 let b = Suit4::from(s2);
290
291 assert!((a | b).contains_suit(s1));
292 assert!((a | b).contains_suit(s2));
293 }
294
295 #[quickcheck]
296 fn test_count(s1: Suit, s2: Suit) {
297 let suits = Suit4::from([s1, s2].as_ref());
298
299 let count = if s1 == s2 { 1 } else { 2 };
300
301 assert_eq!(count, suits.count());
302 }
303
304 #[quickcheck]
305 fn test_from_card64(cards: Vec<Card>) -> TestResult {
306 let mut suits = Suit4::empty();
307
308 for i in 0..cards.len() {
309 suits.set(cards[i].s);
310
311 let c64: Card64 = cards[0..=i].into();
312
313 assert_eq!(Suit4::from(c64), suits);
314 }
315
316 TestResult::passed()
317 }
318
319 #[test]
320 fn test_debug() {
321 let s = format!("{:?}", s4!("s") | s4!("d"));
322 assert_eq!(s, "Suit4(sd)");
323 }
324}