1use super::{
2 BitAnd, BitAndAssign, BitOr, BitOrAssign, Card, Card64Inner, CardCount,
3 CardIdx, CardIter, Hash, Idx, Not, Rank, Rank16, Rank16Inner, Suit, fmt,
4 ops,
5};
6
7#[macro_export]
8macro_rules! c64 {
9 ($s:expr) => {
10 $crate::Card64::from($crate::cards![$s].as_ref())
11 };
12}
13
14#[derive(
27 Copy,
28 Clone,
29 PartialEq,
30 Eq,
31 Hash,
32 BitAnd,
33 BitAndAssign,
34 BitOr,
35 BitOrAssign,
36 Default,
37)]
38pub struct Card64(pub(crate) Card64Inner);
39
40impl Card64 {
41 const OFFSET_SUIT: Idx = 16;
42
43 pub(crate) const EMPTY: Self = Self(0);
44
45 const ALL: Self = sealed::all::<false>();
46 const ALL_SD: Self = sealed::all::<true>();
47
48 #[must_use]
49 #[inline]
50 pub const fn all<const SD: bool>() -> Self {
51 const { if SD { Self::ALL_SD } else { Self::ALL } }
52 }
53
54 #[must_use]
56 #[inline]
57 pub const fn is_empty(self) -> bool {
58 self.0 == 0
59 }
60
61 #[inline]
63 pub const fn set(&mut self, card: Card) {
64 self.0 |= Self::from_card(card).0;
65 }
66
67 #[inline]
69 pub const fn unset(&mut self, card: Card) {
70 self.0 &= !Self::from_card(card).0;
71 }
72
73 #[must_use]
75 #[inline]
76 pub fn contains(self, other: Self) -> bool {
77 other & self == other
78 }
79
80 #[must_use]
82 #[inline]
83 pub const fn contains_card(self, card: Card) -> bool {
84 let v = Self::from_card(card).0;
85 v & self.0 == v
86 }
87
88 #[must_use]
90 #[inline]
91 #[allow(clippy::cast_possible_truncation)]
92 pub const fn count(&self) -> CardCount {
93 self.0.count_ones() as CardCount
94 }
95
96 #[must_use]
98 #[inline]
99 pub const fn count_by_rank(self, rank: Rank) -> CardCount {
100 Self(self.0 & Self::from_ranks(Rank16::from_rank(rank)).0).count()
101 }
102
103 #[must_use]
105 #[inline]
106 pub const fn count_by_suit(self, suit: Suit) -> CardCount {
107 self.ranks_by_suit(suit).count()
108 }
109
110 #[inline]
111 #[allow(clippy::cast_possible_truncation)]
112 pub(crate) const fn ranks_by_suit(self, suit: Suit) -> Rank16 {
113 Rank16((self.0 >> (Self::OFFSET_SUIT * suit as Idx)) as Rank16Inner)
114 }
115
116 #[inline]
118 #[must_use]
119 pub const fn from_ranks(rs: Rank16) -> Self {
120 let [l, h] = rs.0.to_le_bytes();
121
122 Self(Card64Inner::from_le_bytes([l, h, l, h, l, h, l, h]))
123 }
124
125 #[inline]
127 #[must_use]
128 pub const fn from_suit(suit: Suit) -> Self {
129 let v = Rank16::all::<false>().0 as Card64Inner;
130
131 Self(v << (Self::OFFSET_SUIT * (suit as Idx)))
132 }
133
134 #[inline]
136 #[must_use]
137 pub const fn ranks(self) -> Rank16 {
138 Rank16(
139 self.ranks_by_suit(Suit::S).0
140 | self.ranks_by_suit(Suit::H).0
141 | self.ranks_by_suit(Suit::D).0
142 | self.ranks_by_suit(Suit::C).0,
143 )
144 }
145
146 pub const fn iter(self) -> CardIter {
148 CardIter::new(self)
149 }
150
151 const fn from_indices(r: Idx, s: Idx) -> Self {
152 Self(1 << r << (s * Self::OFFSET_SUIT))
153 }
154
155 const fn from_card(card: Card) -> Self {
156 Self::from_indices(card.rank as Idx, card.suit as Idx)
157 }
158}
159
160#[allow(clippy::cast_possible_wrap)]
162#[cfg_attr(coverage_nightly, coverage(off))]
163mod sealed {
164 use super::{Card, Card64, CardIdx, Idx};
165
166 pub(super) const fn all<const SD: bool>() -> Card64 {
167 let start_idx = if SD {
168 Card::N_CARDS - Card::N_CARDS_SD
169 } else {
170 0
171 };
172
173 let mut res = Card64::EMPTY;
174 let mut i = 0;
175
176 while i < Card::N_CARDS {
177 if i >= start_idx {
178 res.set(CardIdx(i as Idx).to_card().unwrap());
179 }
180 i += 1;
181 }
182
183 res
184 }
185}
186
187impl Not for Card64 {
188 type Output = Self;
189
190 fn not(self) -> Self::Output {
191 Self(!self.0 & Self::ALL.0)
192 }
193}
194
195impl fmt::Debug for Card64 {
196 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197 fn ranks_str(r16: Rank16) -> String {
198 if r16.is_empty() {
199 "_".to_string()
200 } else {
201 r16.to_string()
202 }
203 }
204
205 if self.count() == 1 {
206 write!(f, "Card64<{}>", self.iter().next().unwrap())
207 } else {
208 f.debug_tuple("Card64")
209 .field(&format_args!(
210 "{}",
211 ranks_str(self.ranks_by_suit(Suit::S))
212 ))
213 .field(&format_args!(
214 "{}",
215 ranks_str(self.ranks_by_suit(Suit::H))
216 ))
217 .field(&format_args!(
218 "{}",
219 ranks_str(self.ranks_by_suit(Suit::D))
220 ))
221 .field(&format_args!(
222 "{}",
223 ranks_str(self.ranks_by_suit(Suit::C))
224 ))
225 .finish()
226 }
227 }
228}
229
230impl From<Card> for Card64 {
231 fn from(c: Card) -> Self {
232 Self::from_card(c)
233 }
234}
235
236impl From<Card64Inner> for Card64 {
237 fn from(i: Card64Inner) -> Self {
238 Self(i & Self::ALL.0)
239 }
240}
241
242impl From<Card64> for Card64Inner {
243 fn from(v: Card64) -> Self {
244 v.0
245 }
246}
247
248impl FromIterator<Card> for Card64 {
249 fn from_iter<T: IntoIterator<Item = Card>>(iter: T) -> Self {
250 let mut res = Self::default();
251
252 for card in iter {
253 res.set(card);
254 }
255
256 res
257 }
258}
259
260impl From<&[Card]> for Card64 {
261 fn from(cards: &[Card]) -> Self {
262 cards.iter().copied().collect()
263 }
264}
265
266impl ops::BitOrAssign<Card> for Card64 {
267 fn bitor_assign(&mut self, rhs: Card) {
268 self.set(rhs);
269 }
270}
271
272#[cfg(any(test, feature = "quickcheck"))]
273impl quickcheck::Arbitrary for Card64 {
274 fn arbitrary(g: &mut quickcheck::Gen) -> Self {
275 let inner = Card64Inner::arbitrary(g);
276
277 Self(Self::ALL.0 & inner)
278 }
279}
280
281#[cfg(test)]
282#[cfg_attr(coverage_nightly, coverage(off))]
283mod tests {
284 use super::*;
285 use crate::*;
286
287 #[test]
288 fn test_all() {
289 for c in Card::all::<false>() {
290 if c.rank >= Rank::R6 {
291 assert!(Card64::all::<true>().contains_card(*c));
292 }
293 assert!(Card64::all::<false>().contains_card(*c));
294 }
295 }
296
297 #[test]
298 fn test_empty() {
299 assert!(Card64::default().is_empty());
300 assert!(!Card64::all::<false>().is_empty());
301 }
302
303 #[quickcheck]
304 fn test_set(card: Card) {
305 let mut res = Card64::default();
306 res.set(card);
307
308 assert!(res.contains_card(card));
309 }
310
311 #[quickcheck]
312 fn test_unset(card: Card) {
313 let mut res = Card64::all::<false>();
314 res.unset(card);
315
316 assert!(!res.contains_card(card));
317 }
318
319 #[quickcheck]
320 fn test_contains(cards: Vec<Card>) {
321 let all = Card64::from(cards.as_slice());
322 let half = Card64::from(&cards[..cards.len() / 2]);
323
324 assert!(all.contains(half));
325 }
326
327 #[quickcheck]
328 #[allow(clippy::cast_possible_truncation)]
329 fn test_count(c64: Card64) {
330 assert_eq!(
331 c64.count(),
332 Card::all::<false>()
333 .iter()
334 .filter(|&c| c64.contains_card(*c))
335 .count() as CardCount
336 );
337 }
338
339 #[quickcheck]
340 #[allow(clippy::cast_possible_truncation)]
341 fn test_count_by_rank(c64: Card64, rank: Rank) {
342 assert_eq!(
343 c64.count_by_rank(rank),
344 Card::all::<false>()
345 .iter()
346 .filter(|&c| c64.contains_card(*c) && c.rank == rank)
347 .count() as CardCount
348 );
349 }
350
351 #[quickcheck]
352 #[allow(clippy::cast_possible_truncation)]
353 fn test_count_by_suit(c64: Card64, suit: Suit) {
354 assert_eq!(
355 c64.count_by_suit(suit),
356 Card::all::<false>()
357 .iter()
358 .filter(|&c| c64.contains_card(*c) && c.suit == suit)
359 .count() as CardCount
360 );
361 }
362
363 #[quickcheck]
364 fn test_ranks_by_suit(c64: Card64, suit: Suit) {
365 let expected = Rank::all::<false>()
366 .iter()
367 .filter(|&&r| c64.contains_card(Card::new(r, suit)))
368 .copied()
369 .collect();
370
371 assert_eq!(c64.ranks_by_suit(suit), expected);
372 }
373
374 #[quickcheck]
375 fn test_from_ranks(r16: Rank16) {
376 let expected = Card::all::<false>()
377 .iter()
378 .filter(|c| r16.contains_rank(c.rank))
379 .copied()
380 .collect();
381
382 assert_eq!(Card64::from_ranks(r16), expected);
383 }
384
385 #[quickcheck]
386 fn test_from_suit(suit: Suit) {
387 let expected = Card::all::<false>()
388 .iter()
389 .filter(|c| c.suit == suit)
390 .copied()
391 .collect();
392
393 assert_eq!(Card64::from_suit(suit), expected);
394 }
395
396 #[quickcheck]
397 fn test_ranks(c64: Card64) {
398 let expected = Card::all::<false>()
399 .iter()
400 .filter(|&&c| c64.contains_card(c))
401 .map(|c| c.rank)
402 .collect();
403
404 assert_eq!(c64.ranks(), expected);
405 }
406
407 #[test]
408 fn test_iter() {
409 fn assert_iter(s: &str) {
410 let card64 = c64!(s);
411 let v = cards!(s);
412
413 for c in card64.iter() {
414 assert!(v.contains(&c));
415 }
416
417 assert_eq!(card64.iter().count(), v.len());
418 }
419
420 assert_iter("");
421 assert_iter("As");
422 assert_iter("As Kh 2d");
423 }
424
425 #[test]
426 fn test_display() {
427 assert_eq!(format!("{:?}", c64!("As")), "Card64<As>");
428 assert_eq!(format!("{:?}", c64!("As 9h")), "Card64(A, 9, _, _)");
429 }
430
431 #[quickcheck]
432 fn test_bit_not(c64: Card64) {
433 assert_eq!((!c64).0, Card64::ALL.0 & !(c64.0));
434 }
435
436 #[quickcheck]
437 fn test_bit_and(c1: Card, c2: Card) {
438 let a = Card64::from(c1);
439 let b = Card64::from(c2);
440
441 assert_eq!((a & b).is_empty(), c1 != c2);
442 }
443
444 #[quickcheck]
445 fn test_bit_or(c1: Card, c2: Card) {
446 let a = Card64::from(c1);
447 let b = Card64::from(c2);
448
449 assert!((a | b).contains_card(c1));
450 assert!((a | b).contains_card(c2));
451
452 let mut ab = Card64::default();
453 ab |= c1;
454 ab |= c2;
455 assert_eq!(ab, a | b);
456 }
457
458 #[quickcheck]
459 fn test_from_and_to_int(i: Card64Inner) {
460 let obj = Card64::from(i);
461 let mask = Card64::ALL.0;
462
463 assert_eq!(obj.0, i & mask);
464 assert_eq!(i & mask, Card64Inner::from(obj));
465 }
466}