1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
//! # `#[bit]` Attribute
//!
//! The `#[bit]` attribute is composed of three parts, two of which are optional
//! in some cases. The components can be provided in any order.
//!
//! ## Type:
//!
//! The type used to represent individual bits. This part is required.
//!
//! ```txt
//! #[bit(Type, ...)]
//! ```
//!
//! ## Mask:
//! A mask indicating the valid bits of the collection. This should be a
//! constant expression.
//!
//! If not provided, the mask is assumed to have all bits set (i.e. `!0`).
//!
//! `BitCollection::full()` returns this value.
//!
//! ```txt
//! #[bit(..., mask = "0b11", ...)]
//! ```
//!
//! ## Retriever:
//! The suffix for retrieving the inner integer value of the bit type. It
//! expands to `$value.$retr`. Because of this, the provided retriever must be
//! visible where the derive is located.
//!
//! If not provided, the bit type is assumed to be an `enum` that can be
//! casted to an integer.
//!
//! ```txt
//! #[bit(..., retr = "inner", ...)]
//! ```
//!
//! # Examples
//!
//! In computer chess, one popular way of representing the occupants of a board
//! is through a [`Bitboard`][bitboard] type. In this type, each individual bit
//! is a square on a chess board.
//!
//! ```
//! # #[cfg(not(feature = "std"))]
//! # extern crate core;
//! #[macro_use]
//! extern crate bit_collection;
//! use bit_collection::BitCollection;
//!
//! #[derive(Copy, Clone)]
//! pub struct Square(u8);
//!
//! /// A set of sixty-four `Square`s.
//! #[bit(Square, mask = "!0", retr = "0")]
//! #[derive(BitCollection)]
//! pub struct Bitboard(u64);
//!
//! # fn main() {}
//! ```
//!
//! We can also represent castle rights this way.
//!
//! ```
//! # #[cfg(not(feature = "std"))]
//! # extern crate core;
//! # #[macro_use]
//! # extern crate bit_collection;
//! # use bit_collection::BitCollection;
//! #[derive(Copy, Clone)]
//! pub enum CastleRight {
//!     WhiteKingside,
//!     BlackKingside,
//!     WhiteQueenside,
//!     BlackQueenside,
//! }
//!
//! /// A set of `CastleRight`s.
//! #[bit(CastleRight, mask = "0b1111")]
//! #[derive(BitCollection)]
//! pub struct CastleRights {
//!     bits: u8
//! }
//!
//! fn iterate_over(rights: CastleRights) {
//!     for right in rights {
//!         match right {
//!             CastleRight::WhiteKingside  => { /* ... */ },
//!             CastleRight::BlackKingside  => { /* ... */ },
//!             CastleRight::WhiteQueenside => { /* ... */ },
//!             CastleRight::BlackQueenside => { /* ... */ },
//!         }
//!     }
//! }
//!
//! # fn main() {}
//! ```
//!
//! [bitboard]: https://chessprogramming.wikispaces.com/Bitboards

#![cfg_attr(not(feature = "std"), no_std)]

// Reexport derive macro.
#[allow(unused_imports)]
#[macro_use]
extern crate bit_collection_derive;
#[doc(hidden)]
pub use bit_collection_derive::*;

/// A type that represents a collection of bits that can be iterated over.
pub trait BitCollection: DoubleEndedIterator + ExactSizeIterator {
    /// Returns a full instance with all bits set.
    fn full() -> Self;

    /// Returns an empty instance with no bits set.
    fn empty() -> Self;

    /// Returns whether `self` is empty.
    fn is_empty(&self) -> bool;

    /// Returns the least significant bit in `self` if `self` is not empty.
    #[inline]
    fn lsb(&self) -> Option<Self::Item> {
        if BitCollection::is_empty(self) {
            None
        } else {
            unsafe { Some(self.lsb_unchecked()) }
        }
    }

    /// Returns the most significant bit in `self` if `self` is not empty.
    #[inline]
    fn msb(&self) -> Option<Self::Item> {
        if BitCollection::is_empty(self) {
            None
        } else {
            unsafe { Some(self.msb_unchecked()) }
        }
    }

    /// Returns the least significant bit in `self` without checking whether
    /// `self` is empty.
    unsafe fn lsb_unchecked(&self) -> Self::Item;

    /// Returns the most significant bit in `self` without checking whether
    /// `self` is empty.
    unsafe fn msb_unchecked(&self) -> Self::Item;

    /// Removes the least significant bit from `self`.
    fn remove_lsb(&mut self);

    /// Removes the most significant bit from `self`.
    fn remove_msb(&mut self);

    /// Removes the least significant bit from `self` and returns it.
    fn pop_lsb(&mut self) -> Option<Self::Item>;

    /// Removes the most significant bit from `self` and returns it.
    fn pop_msb(&mut self) -> Option<Self::Item>;

    /// Returns whether `self` contains the value.
    fn contains<T: Into<Self>>(&self, T) -> bool where Self: Sized;

    /// Returns the result of removing the value from `self`.
    fn removing<T: Into<Self>>(self, T) -> Self where Self: Sized;

    /// Returns the result of inserting the value into `self`.
    fn inserting<T: Into<Self>>(self, T) -> Self where Self: Sized;

    /// Returns the result of toggling the bits of the value in `self`.
    fn toggling<T: Into<Self>>(self, T) -> Self where Self: Sized;

    /// Returns the result of intersecting the bits of the value with `self`.
    fn intersecting<T: Into<Self>>(self, T) -> Self where Self: Sized;

    /// Returns the result of setting the bits of the value in `self` based on
    /// `condition`.
    #[inline]
    fn setting<T: Into<Self>>(self, x: T, condition: bool) -> Self where Self: Sized {
        if condition {
            self.inserting(x)
        } else {
            self.removing(x)
        }
    }

    /// Removes the value from `self`.
    fn remove<T: Into<Self>>(&mut self, T) where Self: Sized;

    /// Inserts the value into `self`.
    fn insert<T: Into<Self>>(&mut self, T) where Self: Sized;

    /// Toggles bits of the value in `self`.
    fn toggle<T: Into<Self>>(&mut self, T) where Self: Sized;

    /// Intersects the bits of the value with `self`.
    fn intersect<T: Into<Self>>(&mut self, T) where Self: Sized;

    /// Sets the bits of the value in `self` based on `condition`.
    #[inline]
    fn set<T: Into<Self>>(&mut self, x: T, condition: bool) where Self: Sized {
        if condition {
            self.insert(x);
        } else {
            self.remove(x);
        }
    }
}