use std::collections::HashSet;
use std::fmt::Debug;
use std::hash::Hash;
use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, Not};
use crate::bitfield::BitField;
use crate::tiles::Tile;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(bound = "B: serde::Serialize + serde::de::DeserializeOwned"))]
pub struct TileSet<B: BitField> {
bitfield: B
}
impl<B: BitField> TileSet<B> {
pub fn empty() -> Self {
Self::default()
}
pub fn count(&self) -> u32 {
self.bitfield.count_ones()
}
pub fn contains(&self, t: Tile) -> bool {
!(B::tile_mask(t) & self.bitfield).is_empty()
}
pub fn is_empty(&self) -> bool {
self.bitfield.is_empty()
}
pub fn insert(&mut self, t: Tile) {
self.bitfield |= B::tile_mask(t)
}
pub fn remove(&mut self, t: Tile) {
self.bitfield &= !B::tile_mask(t)
}
pub fn clear(&mut self) {
self.bitfield.clear()
}
pub fn extend(&mut self, other: &Self) {
self.bitfield |= other.bitfield
}
pub fn union(&self, other: &Self) -> Self {
Self { bitfield: self.bitfield | other.bitfield }
}
pub fn difference(&self, other: &Self) -> Self {
Self { bitfield: self.bitfield & !other.bitfield }
}
pub fn intersection(&self, other: &Self) -> Self {
Self { bitfield: self.bitfield & other.bitfield }
}
pub fn first(&self) -> Tile {
B::bit_to_tile(self.bitfield.trailing_zeros())
}
}
impl<B: BitField> IntoIterator for TileSet<B> {
type Item = Tile;
type IntoIter = BitfieldTileIter<B>;
fn into_iter(self) -> Self::IntoIter {
BitfieldTileIter::new(self.bitfield)
}
}
impl<'a, B: BitField> IntoIterator for &'a TileSet<B> {
type Item = Tile;
type IntoIter = BitfieldTileIter<B>;
fn into_iter(self) -> Self::IntoIter {
BitfieldTileIter::new(self.bitfield)
}
}
impl<B: BitField> BitOr for TileSet<B> {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
self.union(&rhs)
}
}
impl<B: BitField> BitAnd for TileSet<B> {
type Output = Self;
fn bitand(self, rhs: Self) -> Self::Output {
self.intersection(&rhs)
}
}
impl<B: BitField> BitOrAssign for TileSet<B> {
fn bitor_assign(&mut self, rhs: Self) {
self.extend(&rhs)
}
}
impl<B: BitField> BitAndAssign for TileSet<B> {
fn bitand_assign(&mut self, rhs: Self) {
*self = self.intersection(&rhs)
}
}
impl<B: BitField> Not for TileSet<B> {
type Output = Self;
fn not(self) -> Self::Output {
Self { bitfield: !self.bitfield }
}
}
pub struct BitfieldTileIter<B: BitField> {
state: B,
i: u32,
}
impl<B: BitField> BitfieldTileIter<B> {
pub fn new(state: B) -> Self {
Self { state, i: 0 }
}
}
impl<'a, B: BitField> Iterator for BitfieldTileIter<B> {
type Item = Tile;
fn next(&mut self) -> Option<Self::Item> {
let skipped = self.state >> self.i;
if skipped.is_empty() {
return None
}
self.i += skipped.trailing_zeros() + 1;
Some(B::bit_to_tile(self.i - 1))
}
}
impl<B: BitField> From<TileSet<B>> for HashSet<Tile> {
fn from(tile_set: TileSet<B>) -> Self {
let mut set = HashSet::new();
for t in tile_set {
set.insert(t);
}
set
}
}
impl<'a, B: BitField, T: Iterator<Item=&'a Tile>> From<T> for TileSet<B> {
fn from(tiles: T) -> Self {
let mut tile_set = Self::empty();
for t in tiles {
tile_set.insert(*t);
}
tile_set
}
}
#[macro_export]
macro_rules! tileset {
($( $x: expr ),* ) => {
{
use crate::collections::tileset::TileSet;
let mut tmp = TileSet::empty();
$(
tmp.insert($x);
)*
tmp
}
};
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tiles::Tile;
#[test]
fn basic_test() {
let mut tile_set = TileSet::<u64>::empty();
assert!(tile_set.is_empty());
assert_eq!(tile_set.count(), 0);
tile_set.insert(Tile::new(1, 3));
assert!(!tile_set.is_empty());
assert_eq!(tile_set.count(), 1);
assert!(tile_set.contains(Tile::new(1, 3)));
assert!(!tile_set.contains(Tile::new(1, 4)));
let other_tile_set = TileSet::<u64>::from(vec![
&Tile::new(4, 2),
&Tile::new(1, 3),
&Tile::new(6, 1)
].into_iter());
assert_eq!(other_tile_set.count(), 3);
let intersection = tile_set.intersection(&other_tile_set);
assert!(intersection.contains(Tile::new(1, 3)));
assert!(!intersection.contains(Tile::new(4, 2)));
assert!(!intersection.contains(Tile::new(6, 1)));
}
}