open_pql/base/
rank16.rs

1use super::{
2    BitAnd, BitAndAssign, BitOr, BitOrAssign, Card, Card64, Hash, MASK16_RANKS,
3    N_RANKS, Not, PQLCardCount, RANK_NAMES, Rank, Suit, U16_LEADING_ONE, fmt,
4};
5
6#[cfg(any(test, feature = "benchmark"))]
7#[macro_export]
8macro_rules! r16 {
9    ($s:expr) => {
10        $crate::Rank16::from(
11            $s.chars()
12                .filter(|c| !c.is_whitespace())
13                .map(|c| $crate::Rank::try_from(c).unwrap())
14                .collect::<Vec<_>>()
15                .as_ref() as &[Rank],
16        )
17    };
18}
19
20/// Rank Set
21///
22/// A compact bit-set representation for storing multiple ranks using a single u16.
23/// Each bit represents whether a specific rank is present in the set.
24/// Supports efficient set operations like union, intersection, and membership testing.
25///
26/// # Memory Layout:
27/// ```text
28/// [15, 0]:   xxxAKQJT 98765432  // x: unused
29/// ```
30///
31/// # Examples
32///
33/// ```
34/// use open_pql::{Rank::*, Rank16};
35///
36/// let mut ranks = Rank16::default();
37/// ranks.set(RA);
38/// ranks.set(RK);
39///
40/// assert!(ranks.contains_rank(RA));
41/// assert_eq!(ranks.count(), 2);
42/// ```
43#[derive(
44    Copy,
45    Clone,
46    PartialEq,
47    Eq,
48    BitAnd,
49    BitOr,
50    PartialOrd,
51    Ord,
52    Hash,
53    Default,
54    BitOrAssign,
55    BitAndAssign,
56)]
57pub struct Rank16(u16);
58
59impl Rank16 {
60    /// Constructs [Rank16] from [u16]
61    ///
62    /// # Examples
63    ///
64    /// ```
65    /// use open_pql::{Rank, Rank16};
66    ///
67    /// let i: u16 = 0b0011;
68    /// let ranks: Rank16 = Rank16::from_u16(i);
69    ///
70    /// assert_eq!(ranks, Rank16::from([Rank::R2, Rank::R3].as_ref()));
71    /// ```
72    #[must_use]
73    #[inline]
74    pub const fn from_u16(v: u16) -> Self {
75        Self(v)
76    }
77
78    /// Returns the inner [u16]
79    ///
80    /// # Examples
81    ///
82    /// ```
83    /// use open_pql::Rank16;
84    ///
85    /// let i: u16 = 0b0011;
86    /// let ranks: Rank16 = Rank16::from_u16(i);
87    ///
88    /// assert_eq!(i, ranks.to_u16());
89    /// ```
90    #[must_use]
91    #[inline]
92    pub const fn to_u16(self) -> u16 {
93        self.0
94    }
95
96    #[inline]
97    const fn from_rank(r: Rank) -> Self {
98        Self(1 << r as u8)
99    }
100
101    /// Checks whether all rank masks are unset
102    ///
103    /// # Examples
104    ///
105    /// ```
106    /// use open_pql::{Rank, Rank16};
107    ///
108    /// let ranks: Rank16 = Rank16::from(Rank::RA);
109    ///
110    /// assert!(!ranks.is_empty());
111    /// ```
112    #[must_use]
113    #[inline]
114    pub const fn is_empty(self) -> bool {
115        self.0 == 0
116    }
117
118    /// Mark a [Rank]
119    ///
120    /// # Examples
121    ///
122    /// ```
123    /// use open_pql::{Rank, Rank16};
124    ///
125    /// let mut ranks: Rank16 = Rank16::default();
126    /// ranks.set(Rank::RA);
127    ///
128    /// assert_eq!(ranks, Rank16::from(Rank::RA));
129    /// ```
130    #[inline]
131    pub const fn set(&mut self, r: Rank) {
132        self.0 |= 1 << r as i8;
133    }
134
135    /// Unmark a [Rank]
136    ///
137    /// # Examples
138    ///
139    /// ```
140    /// use open_pql::{Rank, Rank16};
141    ///
142    /// let mut ranks: Rank16 = Rank16::from(Rank::RA);
143    /// ranks.unset(Rank::RA);
144    ///
145    /// assert_eq!(ranks, Rank16::default());
146    /// ```
147    #[inline]
148    pub const fn unset(&mut self, r: Rank) {
149        self.0 &= !(1 << r as i8);
150    }
151
152    /// Checks whether a [Rank] is marked
153    ///
154    /// # Examples
155    ///
156    /// ```
157    /// use open_pql::{Rank, Rank16};
158    ///
159    /// let ranks: Rank16 = Rank16::from(Rank::RA);
160    ///
161    /// assert!(ranks.contains_rank(Rank::RA));
162    /// assert!(!ranks.contains_rank(Rank::RK));
163    /// ```
164    #[must_use]
165    #[inline]
166    pub const fn contains_rank(self, r: Rank) -> bool {
167        let v = 1u16 << (r as usize);
168        v == v & self.0
169    }
170
171    /// Returns the number of marked ranks
172    ///
173    /// # Examples
174    ///
175    /// ```
176    /// use open_pql::{Rank, Rank16};
177    ///
178    /// let ranks: Rank16 = Rank16::from(Rank::RA);
179    ///
180    /// assert_eq!(ranks.count(), 1);
181    /// ```
182    #[must_use]
183    #[inline]
184    pub const fn count(self) -> PQLCardCount {
185        self.0.count_ones().to_le_bytes()[0]
186    }
187
188    /// Returns smallest Rank in the set
189    #[must_use]
190    #[inline]
191    pub const fn min_rank(self) -> Option<Rank> {
192        match self.0.trailing_zeros() {
193            0 => Some(Rank::R2),
194            1 => Some(Rank::R3),
195            2 => Some(Rank::R4),
196            3 => Some(Rank::R5),
197            4 => Some(Rank::R6),
198            5 => Some(Rank::R7),
199            6 => Some(Rank::R8),
200            7 => Some(Rank::R9),
201            8 => Some(Rank::RT),
202            9 => Some(Rank::RJ),
203            10 => Some(Rank::RQ),
204            11 => Some(Rank::RK),
205            12 => Some(Rank::RA),
206            _ => None,
207        }
208    }
209
210    /// Returns largest Rank in the set
211    #[must_use]
212    #[inline]
213    pub const fn max_rank(self) -> Option<Rank> {
214        match (MASK16_RANKS & self.0).leading_zeros() {
215            15 => Some(Rank::R2),
216            14 => Some(Rank::R3),
217            13 => Some(Rank::R4),
218            12 => Some(Rank::R5),
219            11 => Some(Rank::R6),
220            10 => Some(Rank::R7),
221            9 => Some(Rank::R8),
222            8 => Some(Rank::R9),
223            7 => Some(Rank::RT),
224            6 => Some(Rank::RJ),
225            5 => Some(Rank::RQ),
226            4 => Some(Rank::RK),
227            3 => Some(Rank::RA),
228            _ => None,
229        }
230    }
231
232    /// Returns n-th Rank in the set
233    #[must_use]
234    #[inline]
235    pub fn nth_rank(self, mut n: u8) -> Option<Rank> {
236        for rank in Rank::ARR_ALL.iter().rev() {
237            if self.contains_rank(*rank) {
238                if n == 1 {
239                    return Some(*rank);
240                } else if n == 0 {
241                    return None;
242                }
243
244                n -= 1;
245            }
246        }
247
248        None
249    }
250
251    /// Returns all higher ranks than the max rank in the set
252    #[must_use]
253    #[inline]
254    pub const fn higher_of(r: Self) -> Self {
255        if r.is_empty() {
256            Self(MASK16_RANKS)
257        } else {
258            let i = r.to_u16().leading_zeros();
259
260            Self(!((U16_LEADING_ONE >> i) * 2 - 1) & MASK16_RANKS)
261        }
262    }
263}
264
265impl Not for Rank16 {
266    type Output = Self;
267
268    fn not(self) -> Self::Output {
269        Self(!self.0 & MASK16_RANKS)
270    }
271}
272
273impl From<Rank> for Rank16 {
274    fn from(r: Rank) -> Self {
275        Self::from_rank(r)
276    }
277}
278
279impl From<&[Rank]> for Rank16 {
280    fn from(rs: &[Rank]) -> Self {
281        let mut res = Self::default();
282
283        for r in rs {
284            res.set(*r);
285        }
286
287        res
288    }
289}
290
291impl From<&[Card]> for Rank16 {
292    fn from(cs: &[Card]) -> Self {
293        let mut res = Self::default();
294
295        for c in cs {
296            res.set(c.rank);
297        }
298
299        res
300    }
301}
302
303impl From<Card64> for Rank16 {
304    fn from(c: Card64) -> Self {
305        c.ranks_by_suit(Suit::S)
306            | c.ranks_by_suit(Suit::H)
307            | c.ranks_by_suit(Suit::D)
308            | c.ranks_by_suit(Suit::C)
309    }
310}
311
312pub fn u16_to_rank_str(v: u16) -> String {
313    let to_c = |i: u8| {
314        if v & 1 << i == 0 {
315            '\0'
316        } else {
317            RANK_NAMES[i as usize]
318        }
319    };
320
321    (0..N_RANKS)
322        .map(to_c)
323        .filter(|c| c.is_alphanumeric())
324        .collect::<String>()
325}
326
327impl fmt::Debug for Rank16 {
328    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
329        f.write_str(&format!("Rank16<{}>", u16_to_rank_str(self.0)))
330    }
331}
332
333#[cfg(test)]
334mod tests {
335    use super::*;
336    use crate::*;
337
338    impl Arbitrary for Rank16 {
339        fn arbitrary(g: &mut quickcheck::Gen) -> Self {
340            let inner = u16::arbitrary(g);
341
342            Self(MASK16_RANKS & inner)
343        }
344    }
345
346    #[test]
347    fn test_empty() {
348        assert_eq!(Rank16::default(), Rank16(0));
349        assert!(Rank16::default().is_empty());
350        assert!(!Rank16(1).is_empty());
351    }
352
353    #[quickcheck]
354    fn test_set_and_contains(r: Rank) {
355        let mut ranks = Rank16::default();
356
357        ranks.set(r);
358
359        assert!(!ranks.is_empty());
360        assert!(ranks.contains_rank(r));
361
362        ranks.unset(r);
363
364        assert!(ranks.is_empty());
365        assert!(!ranks.contains_rank(r));
366    }
367
368    #[quickcheck]
369    fn test_u16(i: u16) -> TestResult {
370        if i > 0b1_1111_1111_1111 {
371            return TestResult::discard();
372        }
373
374        assert_eq!(Rank16(i), Rank16::from_u16(i));
375        assert_eq!(i, Rank16(i).to_u16());
376
377        TestResult::passed()
378    }
379
380    #[quickcheck]
381    fn test_from_rank(r1: Rank, r2: Rank) {
382        let ranks = Rank16::from(r1);
383
384        assert!(ranks.contains_rank(r1));
385
386        let ranks = Rank16::from([r1, r2].as_ref());
387
388        assert!(ranks.contains_rank(r1));
389        assert!(ranks.contains_rank(r2));
390    }
391
392    #[test]
393    fn test_bit_not() {
394        assert_eq!((!Rank16::default()).to_u16(), MASK16_RANKS);
395    }
396
397    #[quickcheck]
398    fn test_bit_and(r1: Rank, r2: Rank) {
399        let a = Rank16::from(r1);
400        let b = Rank16::from(r2);
401
402        assert_eq!((a & b).is_empty(), r1 != r2);
403    }
404
405    #[quickcheck]
406    fn test_bit_or(r1: Rank, r2: Rank) {
407        let a = Rank16::from(r1);
408        let b = Rank16::from(r2);
409
410        assert!((a | b).contains_rank(r1));
411        assert!((a | b).contains_rank(r2));
412    }
413
414    #[quickcheck]
415    fn test_count(r1: Rank, r2: Rank) {
416        let ranks = Rank16::from([r1, r2].as_ref());
417
418        let count = if r1 == r2 { 1 } else { 2 };
419
420        assert_eq!(count, ranks.count());
421    }
422
423    #[quickcheck]
424    fn test_min_max_rank(cards: CardN<3>) {
425        let ranks = Rank16::from(cards.as_ref());
426
427        let min_r = ranks.min_rank().unwrap();
428        let max_r = ranks.max_rank().unwrap();
429
430        let min_i = cards
431            .as_ref()
432            .iter()
433            .map(|c| c.rank as usize)
434            .min()
435            .unwrap();
436        let max_i = cards
437            .as_ref()
438            .iter()
439            .map(|c| c.rank as usize)
440            .max()
441            .unwrap();
442
443        for c in cards {
444            let r = Rank16::from(&[c] as &[Card]);
445            assert_eq!(c.rank, r.min_rank().unwrap());
446            assert_eq!(c.rank, r.max_rank().unwrap());
447        }
448
449        assert_eq!(min_i, min_r as usize);
450        assert_eq!(max_i, max_r as usize);
451        assert_eq!(None, Rank16::default().min_rank());
452        assert_eq!(None, Rank16::default().max_rank());
453    }
454
455    #[test]
456    fn test_nth_rank() {
457        let ranks = r16!("256KA");
458
459        assert_eq!(ranks.nth_rank(0), None);
460        assert_eq!(ranks.nth_rank(1), Some(Rank::RA));
461        assert_eq!(ranks.nth_rank(2), Some(Rank::RK));
462        assert_eq!(ranks.nth_rank(3), Some(Rank::R6));
463        assert_eq!(ranks.nth_rank(4), Some(Rank::R5));
464        assert_eq!(ranks.nth_rank(5), Some(Rank::R2));
465        assert_eq!(ranks.nth_rank(6), None);
466    }
467
468    #[test]
469    fn test_higher_of() {
470        assert_eq!(r16!("KA"), Rank16::higher_of(r16!("2Q")));
471        assert_eq!(!Rank16::default(), Rank16::higher_of(r16!("")));
472        assert_eq!(Rank16::default(), Rank16::higher_of(r16!("A")));
473    }
474
475    #[quickcheck]
476    fn test_from_card64(cards: Vec<Card>) -> TestResult {
477        let mut ranks = Rank16::default();
478
479        for i in 0..cards.len() {
480            ranks.set(cards[i].rank);
481
482            let c64: Card64 = cards[0..=i].into();
483
484            assert_eq!(Rank16::from(c64), ranks);
485        }
486
487        TestResult::passed()
488    }
489
490    #[test]
491    fn test_u16_to_rank_str() {
492        assert_eq!(u16_to_rank_str(0), "");
493        assert_eq!(u16_to_rank_str(1), "2");
494        assert_eq!(u16_to_rank_str(0b1), "2");
495        assert_eq!(u16_to_rank_str(0b10), "3");
496        assert_eq!(u16_to_rank_str(0b11), "23");
497        assert_eq!(u16_to_rank_str(0b1_0000_0000_0000), "A");
498        assert_eq!(u16_to_rank_str(0b1_0000_0000_0001), "2A");
499        assert_eq!(u16_to_rank_str(0b1_1111_1111_1111), "23456789TJQKA");
500    }
501
502    #[test]
503    fn test_debug() {
504        let s = format!("{:?}", r16!("J") | r16!("9"));
505        assert_eq!(s, "Rank16<9J>");
506    }
507}