open_pql/base/
card64.rs

1use super::{
2    BitAnd, BitAndAssign, BitOr, BitOrAssign, Card, CardIter, Hash, MASK64_2,
3    MASK64_ALL, Not, OFFSET_C, OFFSET_D, OFFSET_H, OFFSET_S, OFFSET_SUIT,
4    PQLCardCount, RANK_NAMES, Rank, Rank16, Rank16Iter, SUIT_NAMES, Suit, fmt,
5    prim, u16_to_rank_str,
6};
7
8#[cfg(any(test, feature = "benchmark"))]
9#[macro_export]
10macro_rules! c64 {
11    ($s:expr) => {
12        $crate::Card64::from($crate::cards![$s].as_ref())
13    };
14}
15
16/// A 64-bit representation of a set of playing cards using bitwise operations.
17///
18/// Card64 efficiently stores multiple cards in a single u64 value, where each bit
19/// represents a specific card. This allows for fast set operations like union,
20/// intersection, and membership testing using bitwise operations.
21///
22/// Card Set
23/// # Memory Layout:
24/// ```text
25/// [63, 48]:  xxxAKQJT 98765432  // Club
26/// [47, 32]:  xxxAKQJT 98765432  // Diamond
27/// [31, 16]:  xxxAKQJT 98765432  // Heart
28/// [15, 0]:   xxxAKQJT 98765432  // Spade, x: unused
29/// ```
30#[derive(
31    Copy,
32    Clone,
33    PartialEq,
34    Eq,
35    Hash,
36    BitAnd,
37    BitAndAssign,
38    BitOr,
39    BitOrAssign,
40    Default,
41)]
42pub struct Card64(u64);
43
44impl Card64 {
45    /// Constructs [Card64] from [u64]
46    ///
47    /// # Examples
48    ///
49    /// ```
50    /// use open_pql::{Card, Card64, Rank::*, Suit::*};
51    ///
52    /// let i: u64 = 0b11;
53    /// let c64: Card64 = Card64::from_u64(i);
54    ///
55    /// assert_eq!(
56    ///     c64,
57    ///     Card64::from([Card::new(R2, S), Card::new(R3, S)].as_ref())
58    /// );
59    /// ```
60    #[must_use]
61    #[inline]
62    pub const fn from_u64(v: u64) -> Self {
63        Self(v)
64    }
65
66    /// Returns the inner [u64]
67    ///
68    /// # Examples
69    ///
70    /// ```
71    /// use open_pql::{Card, Card64, Rank::*, Suit::*};
72    ///
73    /// let i: u64 = 0b11;
74    /// let c64: Card64 = Card64::from_u64(i);
75    ///
76    /// assert_eq!(i, c64.to_u64());
77    /// ```
78    #[must_use]
79    #[inline]
80    pub const fn to_u64(self) -> u64 {
81        self.0
82    }
83
84    /// Checks whether all rank masks are unset
85    ///
86    /// # Examples
87    ///
88    /// ```
89    /// use open_pql::{Card, Card64, Rank::*, Suit::*};
90    ///
91    /// let c64: Card64 = Card64::from(Card::new(R2, S));
92    ///
93    /// assert!(!c64.is_empty());
94    /// ```
95    #[must_use]
96    #[inline]
97    pub const fn is_empty(self) -> bool {
98        self.0 == 0
99    }
100
101    /// Constructs [Card64] as the set of all 52 cards
102    ///
103    /// # Examples
104    ///
105    /// ```
106    /// use open_pql::{Card, Card64, Rank::*, Suit::*};
107    ///
108    /// let c64: Card64 = Card64::all();
109    ///
110    /// assert_eq!(c64.count(), 52);
111    /// ```
112    #[must_use]
113    #[inline]
114    pub const fn all() -> Self {
115        Self(MASK64_ALL)
116    }
117
118    /// checks whether another [Card64] is a subset
119    ///
120    /// # Examples
121    ///
122    /// ```
123    /// use open_pql::{Card, Card64, Rank::*, Suit::*};
124    ///
125    /// let c64_2s: Card64 = Card64::from(Card::new(R2, S));
126    /// let c64_2h: Card64 = Card64::from(Card::new(R2, H));
127    /// let c64_2s_2h: Card64 = c64_2s | c64_2h;
128    ///
129    /// assert!(c64_2s_2h.contains(c64_2h));
130    /// assert!(!c64_2s.contains(c64_2h));
131    /// ```
132    #[must_use]
133    #[inline]
134    pub fn contains(self, other: Self) -> bool {
135        other & self == other
136    }
137
138    /// checks whether a [Card] is in the set
139    ///
140    /// # Examples
141    ///
142    /// ```
143    /// use open_pql::{Card, Card64, Rank::*, Suit::*};
144    ///
145    /// let c64: Card64 = Card64::from(Card::new(R2, S));
146    ///
147    /// assert!(c64.contains_card(Card::new(R2, S)));
148    /// assert!(!c64.contains_card(Card::new(R2, H)));
149    /// ```
150    #[must_use]
151    #[inline]
152    pub const fn contains_card(self, c: Card) -> bool {
153        let v = Self::u64_from_ranksuit_i8(c.rank as i8, c.suit as i8);
154        v & self.0 == v
155    }
156
157    /// Marks a [Card]
158    ///
159    /// # Examples
160    ///
161    /// ```
162    /// use open_pql::{Card, Card64, Rank::*, Suit::*};
163    ///
164    /// let mut c64: Card64 = Card64::default();
165    /// c64.set(Card::new(R2, S));
166    ///
167    /// assert_eq!(c64, Card64::from(Card::new(R2, S)));
168    /// ```
169    #[inline]
170    pub const fn set(&mut self, c: Card) {
171        self.0 |= Self::u64_from_ranksuit_i8(c.rank as i8, c.suit as i8);
172    }
173
174    /// Unmarks a [Card]
175    ///
176    /// # Examples
177    ///
178    /// ```
179    /// use open_pql::{Card, Card64, Rank::*, Suit::*};
180    ///
181    /// let mut c64: Card64 = Card64::from(Card::new(R2, S));
182    /// c64.unset(Card::new(R2, S));
183    ///
184    /// assert_eq!(c64, Card64::default());
185    /// ```
186    #[inline]
187    pub const fn unset(&mut self, c: Card) {
188        self.0 &= !Self::u64_from_ranksuit_i8(c.rank as i8, c.suit as i8);
189    }
190
191    // /// Marks three Flop [Card]s
192    // #[inline]
193    // pub fn set_flop(&mut self, cs: Flop2) {
194    //     todo!()
195    // }
196
197    // /// Marks five Board [Card]s
198    // #[inline]
199    // pub fn set_board(&mut self, b: Board2) {
200    //     todo!()
201    // }
202
203    /// Returns the number of marked cards
204    ///
205    /// # Examples
206    ///
207    /// ```
208    /// use open_pql::{Card, Card64, Rank::*, Suit::*};
209    ///
210    /// let c64: Card64 = Card64::from(Card::new(R2, S));
211    ///
212    /// assert_eq!(c64.count(), 1);
213    /// ```
214    #[must_use]
215    #[inline]
216    pub const fn count(&self) -> PQLCardCount {
217        self.0.count_ones().to_le_bytes()[0]
218    }
219
220    /// Returns the number of marked cards of rank r
221    ///
222    /// # Examples
223    ///
224    /// ```
225    /// use open_pql::{Card, Card64, Rank::*, Suit::*};
226    ///
227    /// let c64: Card64 = Card64::all();
228    ///
229    /// assert_eq!(c64.count_by_rank(RA), 4);
230    /// ```
231    pub const fn count_by_rank(self, r: Rank) -> PQLCardCount {
232        (self.0 & MASK64_2 << r as u8).count_ones().to_le_bytes()[0]
233    }
234
235    /// Returns the number of marked cards of suit s
236    ///
237    /// # Examples
238    ///
239    /// ```
240    /// use open_pql::{Card, Card64, Rank::*, Suit::*};
241    ///
242    /// let c64: Card64 = Card64::all();
243    ///
244    /// assert_eq!(c64.count_by_suit(D), 13);
245    /// ```
246    pub const fn count_by_suit(self, s: Suit) -> PQLCardCount {
247        #[inline]
248        const fn count_ones(v: u8) -> u8 {
249            v.count_ones().to_le_bytes()[0]
250        }
251
252        let bytes = self.to_u64().to_le_bytes();
253
254        match s {
255            Suit::S => count_ones(bytes[0]) + count_ones(bytes[1]),
256            Suit::H => count_ones(bytes[2]) + count_ones(bytes[3]),
257            Suit::D => count_ones(bytes[4]) + count_ones(bytes[5]),
258            Suit::C => count_ones(bytes[6]) + count_ones(bytes[7]),
259        }
260    }
261
262    pub(crate) const fn u64_from_ranksuit_i8(r: i8, s: i8) -> u64 {
263        1 << r << (s * OFFSET_SUIT)
264    }
265
266    /// Attempts to mark a card of rank r in the order S, H, D, C
267    #[allow(unused)]
268    #[inline]
269    pub(crate) fn set_available_card_by_rank(&mut self, r: Rank) {
270        for s in Suit::ARR_ALL {
271            let c = Card::new(r, s);
272
273            if !self.contains_card(c) {
274                return self.set(c);
275            }
276        }
277    }
278
279    /// Normalize u64 so that each u16 indicates rank count
280    #[allow(unused)]
281    #[inline]
282    pub(crate) const fn normalize(&mut self) {
283        self.0 = u64::from_le_bytes(prim::normalize_u64(self.0));
284    }
285
286    #[inline]
287    pub(crate) const fn ranks_by_suit(self, s: Suit) -> Rank16 {
288        let bytes = self.0.to_le_bytes();
289
290        match s {
291            Suit::S => {
292                Rank16::from_u16(u16::from_le_bytes([bytes[0], bytes[1]]))
293            }
294            Suit::H => {
295                Rank16::from_u16(u16::from_le_bytes([bytes[2], bytes[3]]))
296            }
297            Suit::D => {
298                Rank16::from_u16(u16::from_le_bytes([bytes[4], bytes[5]]))
299            }
300            Suit::C => {
301                Rank16::from_u16(u16::from_le_bytes([bytes[6], bytes[7]]))
302            }
303        }
304    }
305
306    /// Creates a Card64 with all four cards of each rank in the given Rank16.
307    ///
308    /// # Examples
309    ///
310    /// ```
311    /// use open_pql::{Card64, Rank::*, Rank16};
312    ///
313    /// let ranks = Rank16::from([RA, RK].as_ref());
314    /// let c64 = Card64::from_ranks(ranks);
315    ///
316    /// assert_eq!(c64.count_by_rank(RA), 4);
317    /// assert_eq!(c64.count_by_rank(RK), 4);
318    /// assert_eq!(c64.count_by_rank(RQ), 0);
319    /// ```
320    #[inline]
321    #[must_use]
322    pub const fn from_ranks(rs: Rank16) -> Self {
323        let v = rs.to_u16() as u64;
324
325        Self(v << OFFSET_S | v << OFFSET_H | v << OFFSET_D | v << OFFSET_C)
326    }
327
328    /// Returns a Rank16 containing all ranks that have at least one card marked.
329    ///
330    /// # Examples
331    ///
332    /// ```
333    /// use open_pql::{Card, Card64, Rank::*, Suit::*};
334    ///
335    /// let c64 = Card64::from([Card::new(RA, S), Card::new(RK, H)].as_ref());
336    /// let ranks = c64.ranks();
337    ///
338    /// assert!(ranks.contains_rank(RA));
339    /// assert!(ranks.contains_rank(RK));
340    /// assert!(!ranks.contains_rank(RQ));
341    /// ```
342    #[inline]
343    #[must_use]
344    pub const fn ranks(self) -> Rank16 {
345        let arr = self.0.to_le_bytes();
346
347        let lo = arr[0] | arr[2] | arr[4] | arr[6];
348        let hi = arr[1] | arr[3] | arr[5] | arr[7];
349
350        Rank16::from_u16(u16::from_le_bytes([lo, hi]))
351    }
352
353    /// Returns an iterator over all cards in this set.
354    ///
355    /// # Examples
356    ///
357    /// ```
358    /// use open_pql::{Card, Card64, Rank::*, Suit::*};
359    ///
360    /// let c64 = Card64::from([Card::new(RA, S), Card::new(RK, H)].as_ref());
361    /// let cards: Vec<Card> = c64.iter().collect();
362    ///
363    /// assert_eq!(cards.len(), 2);
364    /// ```
365    pub const fn iter(self) -> CardIter {
366        CardIter::new(self)
367    }
368
369    /// Returns an iterator over the ranks in each suit.
370    ///
371    /// The iterator yields tuples of (Rank16, Suit) for each suit in the order S, H, D, C.
372    ///
373    /// # Examples
374    ///
375    /// ```
376    /// use open_pql::{Card, Card64, Rank::*, Suit::*};
377    ///
378    /// let c64 = Card64::from([Card::new(RA, S), Card::new(RK, H)].as_ref());
379    /// let ranks: Vec<_> = c64.iter_ranks().collect();
380    ///
381    /// assert_eq!(ranks.len(), 4);
382    /// assert!(ranks[0].0.contains_rank(RA)); // Spades
383    /// assert!(ranks[1].0.contains_rank(RK)); // Hearts
384    /// ```
385    pub const fn iter_ranks(self) -> Rank16Iter {
386        Rank16Iter::new(self)
387    }
388}
389
390impl Not for Card64 {
391    type Output = Self;
392
393    fn not(self) -> Self::Output {
394        Self(!self.0 & MASK64_ALL)
395    }
396}
397
398impl fmt::Debug for Card64 {
399    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
400        #[inline]
401        fn to_s(v: u16) -> String {
402            let s = u16_to_rank_str(v);
403            if s.is_empty() { "_".into() } else { s }
404        }
405
406        #[inline]
407        const fn truncate_i8(v: usize) -> i8 {
408            i8::from_le_bytes([v.to_le_bytes()[0]])
409        }
410
411        let n = self.0.count_ones();
412
413        if n == 1 {
414            for (sv, s) in SUIT_NAMES.iter().enumerate() {
415                for (rv, r) in RANK_NAMES.iter().enumerate() {
416                    if self.0
417                        == Self::u64_from_ranksuit_i8(
418                            truncate_i8(rv),
419                            truncate_i8(sv),
420                        )
421                    {
422                        return f.write_str(&format!("Card64<{r}{s}>"));
423                    }
424                }
425            }
426        }
427
428        let bs = self.0.to_le_bytes();
429
430        f.debug_tuple("Card64")
431            .field(&format_args!(
432                "{}",
433                to_s(u16::from_le_bytes([bs[0], bs[1]]))
434            ))
435            .field(&format_args!(
436                "{}",
437                to_s(u16::from_le_bytes([bs[2], bs[3]]))
438            ))
439            .field(&format_args!(
440                "{}",
441                to_s(u16::from_le_bytes([bs[4], bs[5]]))
442            ))
443            .field(&format_args!(
444                "{}",
445                to_s(u16::from_le_bytes([bs[6], bs[7]]))
446            ))
447            .finish()
448    }
449}
450
451impl From<&[Card]> for Card64 {
452    fn from(cs: &[Card]) -> Self {
453        let mut res = Self::default();
454
455        for c in cs {
456            res.0 |= Self::u64_from_ranksuit_i8(c.rank as i8, c.suit as i8);
457        }
458
459        res
460    }
461}
462
463impl From<Card> for Card64 {
464    fn from(c: Card) -> Self {
465        Self::from_u64(Self::u64_from_ranksuit_i8(c.rank as i8, c.suit as i8))
466    }
467}
468
469#[cfg(test)]
470mod tests {
471    use super::*;
472    use crate::*;
473
474    impl Arbitrary for Card64 {
475        fn arbitrary(g: &mut quickcheck::Gen) -> Self {
476            let inner = u64::arbitrary(g);
477
478            Self(MASK64_ALL & inner)
479        }
480    }
481
482    #[test]
483    fn test_empty() {
484        assert_eq!(Card64::default(), Card64(0));
485        assert!(Card64::default().is_empty());
486        assert!(!Card64(1).is_empty());
487    }
488
489    #[quickcheck]
490    fn test_all(c: Card) {
491        let all = Card64::all();
492
493        assert!(all.contains_card(c));
494    }
495
496    #[quickcheck]
497    fn test_u64(i: u64) -> TestResult {
498        if i & MASK64_ALL != i {
499            return TestResult::discard();
500        }
501
502        assert_eq!(Card64(i), Card64::from_u64(i));
503        assert_eq!(i, Card64(i).to_u64());
504
505        TestResult::passed()
506    }
507
508    #[quickcheck]
509    fn test_set_and_contains_card(c1: Card, c2: Card) {
510        let mut c64 = Card64::default();
511        c64.set(c1);
512        c64.set(c2);
513
514        assert!(c64.contains_card(c1));
515        assert!(c64.contains_card(c2));
516
517        c64.unset(c1);
518
519        assert!(!c64.contains_card(c1));
520        assert_eq!(c64.contains_card(c2), c2 != c1);
521    }
522
523    //#[quickcheck]
524    //fn test_set_flop_and_board(b: Board2) -> TestResult {
525    //if !board_distinct(b) {
526    //    return TestResult::discard();
527    //}
528
529    //let mut flop = Card64::default();
530
531    //flop.set_flop(b.0);
532    //assert!(flop.contains_card(b.0 .0));
533    //assert!(flop.contains_card(b.0 .1));
534    //assert!(flop.contains_card(b.0 .2));
535
536    //let mut board = Card64::default();
537
538    //board.set_board(b);
539    //assert!(board.contains_card(b.0 .0));
540    //assert!(board.contains_card(b.0 .1));
541    //assert!(board.contains_card(b.0 .2));
542    //assert!(board.contains_card(b.1));
543    //assert!(board.contains_card(b.2));
544
545    //assert!(board.contains(flop));
546
547    //TestResult::passed()
548    //}
549
550    //#[quickcheck]
551    //fn test_set_cards(cards: (Card, Card, Card, Card)) {
552    //    let mut lhs = Card64::default();
553    //    lhs.set_cards([cards.0, cards.1, cards.2, cards.3].as_ref());
554
555    //    let mut rhs = Card64::default();
556    //    rhs.set(cards.0);
557    //    rhs.set(cards.1);
558    //    rhs.set(cards.2);
559    //    rhs.set(cards.3);
560
561    //    assert_eq!(lhs, rhs);
562    //}
563
564    #[quickcheck]
565    fn test_from_card(c1: Card, c2: Card) {
566        let cards = Card64::from(c1);
567
568        assert!(cards.contains_card(c1));
569
570        let cards = Card64::from([c1, c2].as_ref());
571
572        assert!(cards.contains_card(c1));
573        assert!(cards.contains_card(c2));
574    }
575
576    #[quickcheck]
577    fn test_bit_not(c: Card) {
578        let c64 = Card64::from(c);
579        let c64_complement = !c64;
580
581        assert!(c64.contains_card(c));
582        assert!(!c64_complement.contains_card(c));
583        assert_eq!(c64 | c64_complement, Card64::all());
584        assert_eq!(c64, !c64_complement);
585    }
586
587    #[quickcheck]
588    fn test_bit_and(c1: Card, c2: Card) {
589        let mut a = Card64::from(c1);
590        let b = Card64::from(c2);
591
592        assert_eq!((a & b).is_empty(), c1 != c2);
593
594        a &= Card64::default();
595
596        assert_eq!(a, Card64::default());
597    }
598
599    #[quickcheck]
600    fn test_bit_or(c1: Card, c2: Card) {
601        let mut a = Card64::from(c1);
602        let b = Card64::from(c2);
603
604        assert!((a | b).contains_card(c1));
605        assert!((a | b).contains_card(c2));
606
607        a |= Card64::all();
608
609        assert_eq!(a, Card64::all());
610    }
611
612    #[quickcheck]
613    fn test_count(c1: Card, c2: Card) {
614        let c = Card64::from([c1, c2].as_ref());
615
616        let count = if c1 == c2 { 1 } else { 2 };
617
618        assert_eq!(count, c.count());
619    }
620
621    #[quickcheck]
622    fn test_count_by_rank(cards: CardN<20>) -> TestResult {
623        let c: Card64 = cards.clone().into();
624
625        for r in Rank::ARR_ALL {
626            let count = cards.as_ref().iter().filter(|c| c.rank == r).count();
627
628            assert_eq!(count, c.count_by_rank(r) as usize);
629        }
630
631        TestResult::passed()
632    }
633
634    #[quickcheck]
635    fn test_count_by_suit(cards: CardN<5>) -> TestResult {
636        let c: Card64 = cards.clone().into();
637
638        for s in Suit::ARR_ALL {
639            let count = cards.as_ref().iter().filter(|c| c.suit == s).count();
640
641            assert_eq!(count, c.count_by_suit(s) as usize);
642        }
643
644        TestResult::passed()
645    }
646
647    #[quickcheck]
648    fn test_set_available_card_by_rank(mut c64: Card64, r: Rank) -> TestResult {
649        let n = c64.count_by_rank(r);
650
651        c64.set_available_card_by_rank(r);
652
653        let m = c64.count_by_rank(r);
654
655        TestResult::from_bool(m == n + 1 || n == 4 && m == 4)
656    }
657
658    #[quickcheck]
659    fn test_normalize(mut c64: Card64) {
660        let rank_count = Rank::ARR_ALL
661            .into_iter()
662            .map(|r| (r, c64.count_by_rank(r)))
663            .collect::<Vec<_>>();
664
665        c64.normalize();
666
667        for (r, count) in rank_count {
668            assert_eq!(c64.contains_card(Card::new(r, Suit::S)), count > 0);
669            assert_eq!(c64.contains_card(Card::new(r, Suit::H)), count > 1);
670            assert_eq!(c64.contains_card(Card::new(r, Suit::D)), count > 2);
671            assert_eq!(c64.contains_card(Card::new(r, Suit::C)), count > 3);
672        }
673    }
674
675    #[quickcheck]
676    fn test_from_ranks_and_ranks(ranks: Rank16) {
677        let c = Card64::from_ranks(ranks);
678
679        for r in Rank::ARR_ALL {
680            if ranks.contains_rank(r) {
681                assert_eq!(4, c.count_by_rank(r));
682            } else {
683                assert_eq!(0, c.count_by_rank(r));
684            }
685        }
686
687        assert_eq!(ranks, c.ranks());
688    }
689
690    #[quickcheck]
691    fn test_contains(c1: Card64, c2: Card64) -> TestResult {
692        let combined = c1 | c2;
693
694        assert!(combined.contains(c1));
695        assert!(combined.contains(c2));
696        assert!(Card64::all().contains(c1));
697
698        if (c1 & c2).is_empty() && !c2.is_empty() {
699            assert!(!c1.contains(c2));
700        }
701
702        TestResult::passed()
703    }
704
705    #[test]
706    fn test_iter() {
707        let empty = Card64::default();
708        assert_eq!(empty.iter().count(), 0);
709
710        let single = c64!("As");
711        let cards: Vec<Card> = single.iter().collect();
712        assert_eq!(cards.len(), 1);
713        assert_eq!(cards[0], cards!("As")[0]);
714
715        let multiple = c64!("As Kh 2d");
716        let cards: Vec<Card> = multiple.iter().collect();
717        assert_eq!(cards.len(), 3);
718        assert!(cards.contains(&cards!("As")[0]));
719        assert!(cards.contains(&cards!("Kh")[0]));
720        assert!(cards.contains(&cards!("2d")[0]));
721
722        let all = Card64::all();
723        assert_eq!(all.iter().count(), constants::N_CARDS as usize);
724    }
725
726    #[test]
727    fn test_iter_ranks() {
728        let empty = Card64::default();
729        assert_eq!(empty.iter_ranks().count(), 4);
730
731        let single = c64!("As");
732        let ranks: Vec<(Rank16, Suit)> = single.iter_ranks().collect();
733        assert_eq!(ranks.len(), 4);
734        assert!(ranks[0].0.contains_rank(Rank::RA));
735        assert_eq!(ranks[0].1, Suit::S);
736
737        let same_rank = c64!("As Ah Ad");
738        let ranks: Vec<(Rank16, Suit)> = same_rank.iter_ranks().collect();
739        assert_eq!(ranks.len(), 4);
740        assert!(ranks[0].0.contains_rank(Rank::RA));
741        assert!(ranks[1].0.contains_rank(Rank::RA));
742        assert!(ranks[2].0.contains_rank(Rank::RA));
743        assert!(!ranks[3].0.contains_rank(Rank::RA));
744
745        let multiple = c64!("As Kh 2d");
746        let ranks: Vec<(Rank16, Suit)> = multiple.iter_ranks().collect();
747        assert_eq!(ranks.len(), 4);
748
749        let (spade_ranks, spade) = ranks[0];
750        assert_eq!(spade, Suit::S);
751        assert!(spade_ranks.contains_rank(Rank::RA));
752        assert!(!spade_ranks.contains_rank(Rank::RK));
753        assert!(!spade_ranks.contains_rank(Rank::R2));
754
755        let (heart_ranks, heart) = ranks[1];
756        assert_eq!(heart, Suit::H);
757        assert!(!heart_ranks.contains_rank(Rank::RA));
758        assert!(heart_ranks.contains_rank(Rank::RK));
759        assert!(!heart_ranks.contains_rank(Rank::R2));
760
761        let (diamond_ranks, diamond) = ranks[2];
762        assert_eq!(diamond, Suit::D);
763        assert!(!diamond_ranks.contains_rank(Rank::RA));
764        assert!(!diamond_ranks.contains_rank(Rank::RK));
765        assert!(diamond_ranks.contains_rank(Rank::R2));
766    }
767
768    #[test]
769    fn test_debug() {
770        let s = format!("{:?}", c64!("As"));
771        assert_eq!(s, "Card64<As>");
772
773        let s = format!("{:?}", c64!("As 9h"));
774        assert_eq!(s, "Card64(A, 9, _, _)");
775    }
776}