use std::convert::From;
use super::super::vector::VectorAdd;
use super::{Position, Normalizable, Piece, CubeSymmetryIterator, Translatable, Transformable, MinimumPosition};
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct Template<T> {
positions: Vec<Position<T>>,
name: Option<String>
}
impl<T> Template<T> {
pub fn new(positions: Vec<Position<T>>) -> Template<T> {
Template { positions, name: None }
}
pub fn with_name<S>(self, name: S) -> Template<T> where S : Into<String> {
let name = Some(name.into());
Template { positions: self.positions, name }
}
}
impl<T> IntoIterator for Template<T> where T: Clone + PartialOrd + Ord + Transformable + Normalizable<T> + VectorAdd<T> {
type Item = Piece<T>;
type IntoIter = PieceIterator<T>;
fn into_iter(self) -> Self::IntoIter {
PieceIterator::new(self)
}
}
pub struct PieceIterator<T> {
symmetry_iterator: CubeSymmetryIterator,
seen_pieces: Vec<Piece<T>>,
template: Template<T>,
}
impl<T> PieceIterator<T> {
pub fn new(template: Template<T>) -> PieceIterator<T> {
PieceIterator {
symmetry_iterator: CubeSymmetryIterator::new(),
seen_pieces: vec!(),
template,
}
}
}
impl<T> Iterator for PieceIterator<T> where T: Clone + PartialEq + Eq + PartialOrd + Ord + Transformable + Normalizable<T> + VectorAdd<T> {
type Item = Piece<T>;
fn next(&mut self) -> Option<Self::Item> {
let mut symmetry_option = self.symmetry_iterator.next();
while symmetry_option.is_some() {
let piece_option = symmetry_option.map(|symmetry|{
let mut piece = Piece::from(self.template.clone());
piece.transform(&symmetry);
let minimum_position = piece.minimum_position();
let translation = minimum_position.unwrap().to_reference();
piece.translate(&translation);
piece
});
if piece_option.is_some() {
let piece = piece_option.unwrap();
let clone = piece.clone();
if !self.seen_pieces.contains(&clone) {
self.seen_pieces.push(clone);
return Some(piece)
}
}
symmetry_option = self.symmetry_iterator.next();
}
None
}
}
impl<T> From<Template<T>> for Piece<T> where T: Clone + PartialOrd + Ord + Transformable + Normalizable<T> + VectorAdd<T> {
fn from(template: Template<T>) -> Self {
if template.name.is_some() {
Piece::named(template.positions, template.name.unwrap())
} else {
Piece::new(template.positions)
}
}
}
#[cfg(test)]
mod tests {
use std::iter::Iterator;
use super::*;
#[test]
fn templates_are_equal_on_values() {
let a = Template::new(vec!(
Position::new(0,0,0),
Position::new(1,0,0),
));
let b = Template::new(vec!(
Position::new(0,0,0),
Position::new(1,0,0),
));
assert_eq!(a, b);
}
#[test]
fn templates_should_return_24_pieces_unsymmetric_templates() {
let template = Template::new(vec!(
Position::new(0, 0, 0),
Position::new(1, 0, 0),
Position::new(1, 1, 0),
Position::new(1, 1, 1),
Position::new(1, 1, 2),
));
let iterator: PieceIterator<(i8, i8, i8)> = template.into_iter();
assert_eq!(iterator.count(), 24);
}
#[test]
fn templates_should_return_less_than_24_pieces_for_symmetric_templates() {
let template = Template::new(vec!(
Position::new(0, 0, 0),
Position::new(0, 1, 0),
Position::new(1, 0, 0),
Position::new(1, 1, 0),
));
let iterator: PieceIterator<(i8, i8, i8)>= template.into_iter();
assert_eq!(iterator.count(), 3);
}
}