<commit_before>// extern crate euclid;

use std::collections::BTreeMap;
use std::collections::VecDeque;

use types::*;
use delta::*;
use polyomino::*;

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ShapeId(u32);

#[derive(Debug)]
pub struct Shape {
    // Gameplay:
    id: ShapeId,

    //TODO: use a faster hash table or just a vec https://github.com/servo/rust-fnv
    parent_ids: BTreeMap<ShapeId, ChildPoint>,

    pub polyomino: Polyomino,
    //     pub zoom_scale: i32, // TODO: keep constant?

    // Drawing:
    pub fill_color: [f32; 3],
    pub outline_color: [f32; 3],
}

impl Shape {
    pub fn has_position(&self, p: UPoint) -> bool {
        self.polyomino.has_position(p)
    }

    pub fn has_parent(&self, parent: &Shape) -> bool {
        self.parent_ids.contains_key(&parent.id)
    }

    pub fn first_parent_id(&self) -> ShapeId {
        let (&first_key, _) = self.parent_ids.iter().next().unwrap();
        first_key
    }

    pub fn position_on(&self, parent: &Shape) -> ChildPoint {
        self.parent_ids[&parent.id]
    }

    pub fn delta_to_parent(&self, parent: &Shape) -> Delta {
        unimplemented!();
    }

    pub fn delta_to_child(&self, child: &Shape) -> Delta {
        unimplemented!();
    }
}
impl PartialEq for Shape {
    fn eq(&self, other: &Shape) -> bool {
        self.id == other.id
    }
}

impl Eq for Shape {}

fn build_fill_mesh() -> () {}

pub struct Universe {
    pub shapes: BTreeMap<ShapeId, Shape>, // Should probably just be a Vec
}

impl Universe {
    pub fn minimal() -> Universe {
        let id1 = ShapeId(1);
        let id2 = ShapeId(2);
        let shape1 = Shape {
            id: id1,
            parent_ids: btreemap!{ id2 => ChildPoint::new(0, 0) },
            polyomino: Polyomino::monomino(),
            fill_color: [1.0, 1.0, 1.0],
            outline_color: [0.5, 0.5, 0.5],
        };
        let shape2 = Shape {
            id: id2,
            parent_ids: btreemap!{ id1 => ChildPoint::new(0, 0) },
            polyomino: Polyomino::monomino(),
            fill_color: [1.0, 0.5, 0.5],
            outline_color: [0.5, 0.25, 0.25],
        };

        let mut shapes = BTreeMap::new();
        shapes.insert(id1, shape1);
        shapes.insert(id2, shape2);

        Universe { shapes: shapes }
    }

    pub fn parents_of<'a>(&'a self, shape: &'a Shape) -> impl Iterator<Item = &'a Shape> + 'a {
        shape.parent_ids.keys().map(move |id| &self.shapes[id])
    }

    pub fn parents_of_id<'a>(&'a self, shape_id: ShapeId) -> impl Iterator<Item = &'a Shape> {
        self.parents_of(&self.shapes[&shape_id])
    }

    pub fn children_of<'a>(&'a self, parent: &'a Shape) -> impl Iterator<Item = &'a Shape> {
        self.shapes.values().filter(move |s| s.has_parent(parent))
    }
    pub fn children_of_id<'a>(&'a self, parent_id: ShapeId) -> impl Iterator<Item = &'a Shape> {
        self.shapes
            .values()
            .filter(move |s| s.parent_ids.contains_key(&parent_id))
    }

    pub fn parents_with_position_of(&self, shape: &Shape) -> Vec<(&Shape, ChildPoint)> {
        shape
            .parent_ids
            .iter()
            .map(|(id, pos)| (&self.shapes[id], *pos))
            .collect()
    }
}

pub struct Square<'a> {
    pub universe: &'a Universe,
    pub shape: &'a Shape,
    pub position: UPoint,
}

impl<'a> Square<'a> {
    fn location_on(&self, parent_id: ShapeId) -> Option<Location> {
        let pos_on = self.shape.parent_ids.get(&parent_id)?;
        let new_shape = &self.universe.shapes[&parent_id];
        let offset = self.position.to_vector() + vectors::coerce_up(pos_on.to_vector());
        let (new_pos, new_subp) = vectors::split_up(vectors::coerce_down(offset));

        Some(Location {
            square: Square {
                universe: self.universe,
                shape: new_shape,
                position: new_pos.to_point(),
            },
            subposition: new_subp,
        })
    }

    fn location(&self) -> Location {
        // TODO: This sort of sanity checking is slower, probably doesn't matter
        let mut locations: Vec<Location> = self.shape
            .parent_ids
            .keys()
            .filter_map(|s| self.location_on(*s))
            .collect();

        match locations.len() {
            0 => panic!("A square is not sitting on a location!"),
            1 => locations.pop().unwrap(),
            _ => panic!("A square is sitting on multiple locations!"),
        }
    }
}

pub struct Location<'a> {
    pub square: Square<'a>,
    pub subposition: ChildVec,
}

impl<'a> Location<'a> {
    pub fn inhabitant(&self) -> Option<Square> {
        let c = self.to_coordinate();

        let mut inhabitants: Vec<Square> = self.square
            .universe
            .parents_with_position_of(self.square.shape)
            .into_iter()
            .filter(|&(s, pos)| {
                let cpos = vectors::coerce_up(c - pos.to_vector());
                s.has_position(cpos.to_point())
            })
            .map(|(s, pos)| {
                let cpos = vectors::coerce_up(c - pos.to_vector());
                Square {
                    universe: self.square.universe,
                    shape: s,
                    position: cpos.to_point(),
                }
            })
            .collect();

        if inhabitants.len() > 1 {
            // TODO: This sort of sanity checking is probably a bit slower
            panic!("A location has multiple inhabitants!");
        } else {
            inhabitants.pop()
        }
    }

    fn to_coordinate(&self) -> ChildVec {
        vectors::shift_down(self.square.position.to_vector()) + self.subposition
    }
}

// This stores offsets of each shape in the chunk

// TODO: these offsets may be too small, need a vec of offsets, one
// for each level (that can be converted to a normal form, if we want)
#[derive(Clone)]
pub struct Chunk {
    pub origin_id: ShapeId,
    pub top_shape_ids: BTreeMap<ShapeId, UVec>,
    pub lower_shape_ids: BTreeMap<ShapeId, Delta>,
}

impl Chunk {}

type ExploreResult = BTreeMap<ShapeId, Delta>;
type ExploreQueue = VecDeque<(ShapeId, Delta)>;

impl Universe {
    fn explore_step(&self, result: &mut ExploreResult, queue: &mut ExploreQueue) -> () {
        while !queue.is_empty() {
            let (sid, delta) = queue.pop_front().unwrap();
            let s = &self.shapes[&sid];

            let needs_update: bool = match result.get(&sid) {
                None => true,
                Some(existing) => delta.zdelta < existing.zdelta,
            };

            if !needs_update {
                continue;
            }

            result.insert(sid, delta.clone());

            if delta.zdelta > 0 {
                for p in self.parents_of_id(sid) {
                    queue.push_front((p.id, &delta + &s.delta_to_parent(p)));
                }
            }

            for c in self.children_of_id(sid) {
                queue.push_back((c.id, &delta + &s.delta_to_child(c)));
            }
        }
    }

    fn explore(&self, shape_id: ShapeId) -> Chunk {
        let mut result = BTreeMap::new();
        let mut queue = VecDeque::new();

        queue.push_back((shape_id, Delta::zero()));

        self.explore_step(&mut result, &mut queue);

        let top = result
            .iter()
            .filter_map(|(s, d)| {
                if d.zdelta == 0 {
                    Some((*s, d.to_uvec()))
                } else {
                    None
                }
            })
            .collect();
        let lower = result
            .iter()
            .filter_map(
                |(s, d)| {
                    if d.zdelta > 0 {
                        Some((*s, d.clone()))
                    } else {
                        None
                    }
                }, // More copying than necessary?
            )
            .collect();

        Chunk {
            origin_id: shape_id,
            top_shape_ids: top,
            lower_shape_ids: lower,
        }
    }

    // fn explore_parent_of(&self, shape_ids: BTreeMap<ShapeId, IVec2>) -> Chunk {
    // }

    pub fn chunk_of(&self, shape: &Shape) -> Chunk {
        self.explore(shape.id)
    }

    pub fn parent_of(&self, chunk: &Chunk) -> Chunk {
        let origin_parent_id = self.shapes[&chunk.origin_id].first_parent_id();
        self.explore(origin_parent_id)
    }
}

pub struct GameState {
    pub universe: Universe,
    pub player_chunk: Chunk,
}

impl GameState {
    pub fn minimal() -> GameState {
        let u = Universe::minimal();

        GameState {
            universe: u,
            player_chunk: Chunk {
                origin_id: ShapeId(1),
                top_shape_ids: btreemap![ShapeId(1) => UVec::zero()],
                lower_shape_ids: btreemap![ShapeId(2) => Delta::zero().shift_target_down()],
            },
        }
    }
}
<commit_msg>Fix logic in shape<commit_after>// extern crate euclid;

use std::collections::BTreeMap;
use std::collections::VecDeque;

use types::*;
use delta::*;
use polyomino::*;

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ShapeId(u32);

#[derive(Debug)]
pub struct Shape {
    // Gameplay:
    id: ShapeId,

    //TODO: use a faster hash table or just a vec https://github.com/servo/rust-fnv
    parent_ids: BTreeMap<ShapeId, ChildPoint>,

    pub polyomino: Polyomino,

    // Drawing:
    pub fill_color: [f32; 3],
    pub outline_color: [f32; 3],
}

impl Shape {
    pub fn has_position(&self, p: UPoint) -> bool {
        self.polyomino.has_position(p)
    }

    pub fn has_parent(&self, parent: &Shape) -> bool {
        self.parent_ids.contains_key(&parent.id)
    }

    pub fn first_parent_id(&self) -> ShapeId {
        let (&first_key, _) = self.parent_ids.iter().next().unwrap();
        first_key
    }

    pub fn position_on(&self, parent: &Shape) -> ChildPoint {
        self.parent_ids[&parent.id]
    }

    pub fn delta_from_parent(&self, parent: &Shape) -> Delta {
        Delta::from(self.position_on(parent).to_vector())
    }

    pub fn delta_to_child(&self, child: &Shape) -> Delta {
        child.delta_from_parent(self)
    }
}

impl PartialEq for Shape {
    fn eq(&self, other: &Shape) -> bool {
        self.id == other.id
    }
}

impl Eq for Shape {}

fn build_fill_mesh() -> () {}

pub struct Universe {
    pub shapes: BTreeMap<ShapeId, Shape>, // Should probably just be a Vec
}

impl Universe {
    pub fn minimal() -> Universe {
        let id1 = ShapeId(1);
        let id2 = ShapeId(2);
        let shape1 = Shape {
            id: id1,
            parent_ids: btreemap!{ id2 => ChildPoint::new(0, 0) },
            polyomino: Polyomino::monomino(),
            fill_color: [1.0, 1.0, 1.0],
            outline_color: [0.5, 0.5, 0.5],
        };
        let shape2 = Shape {
            id: id2,
            parent_ids: btreemap!{ id1 => ChildPoint::new(0, 0) },
            polyomino: Polyomino::monomino(),
            fill_color: [1.0, 0.5, 0.5],
            outline_color: [0.5, 0.25, 0.25],
        };

        let mut shapes = BTreeMap::new();
        shapes.insert(id1, shape1);
        shapes.insert(id2, shape2);

        Universe { shapes: shapes }
    }

    pub fn parents_of<'a>(&'a self, shape: &'a Shape) -> impl Iterator<Item = &'a Shape> + 'a {
        shape.parent_ids.keys().map(move |id| &self.shapes[id])
    }

    pub fn parents_of_id<'a>(&'a self, shape_id: ShapeId) -> impl Iterator<Item = &'a Shape> {
        self.parents_of(&self.shapes[&shape_id])
    }

    pub fn children_of<'a>(&'a self, parent: &'a Shape) -> impl Iterator<Item = &'a Shape> {
        self.shapes.values().filter(move |s| s.has_parent(parent))
    }
    pub fn children_of_id<'a>(&'a self, parent_id: ShapeId) -> impl Iterator<Item = &'a Shape> {
        self.shapes
            .values()
            .filter(move |s| s.parent_ids.contains_key(&parent_id))
    }

    pub fn parents_with_position_of(&self, shape: &Shape) -> Vec<(&Shape, ChildPoint)> {
        shape
            .parent_ids
            .iter()
            .map(|(id, pos)| (&self.shapes[id], *pos))
            .collect()
    }
}

pub struct Square<'a> {
    pub universe: &'a Universe,
    pub shape: &'a Shape,
    pub position: UPoint,
}

impl<'a> Square<'a> {
    fn location_on(&self, parent_id: ShapeId) -> Option<Location> {
        let pos_on = self.shape.parent_ids.get(&parent_id)?;
        let new_shape = &self.universe.shapes[&parent_id];
        let offset = self.position.to_vector() + vectors::coerce_up(pos_on.to_vector());
        let (new_pos, new_subp) = vectors::split_up(vectors::coerce_down(offset));

        Some(Location {
            square: Square {
                universe: self.universe,
                shape: new_shape,
                position: new_pos.to_point(),
            },
            subposition: new_subp,
        })
    }

    fn location(&self) -> Location {
        // TODO: This sort of sanity checking is slower, probably doesn't matter
        let mut locations: Vec<Location> = self.shape
            .parent_ids
            .keys()
            .filter_map(|s| self.location_on(*s))
            .collect();

        match locations.len() {
            0 => panic!("A square is not sitting on a location!"),
            1 => locations.pop().unwrap(),
            _ => panic!("A square is sitting on multiple locations!"),
        }
    }
}

pub struct Location<'a> {
    pub square: Square<'a>,
    pub subposition: ChildVec,
}

impl<'a> Location<'a> {
    pub fn inhabitant(&self) -> Option<Square> {
        let c = self.to_coordinate();

        let mut inhabitants: Vec<Square> = self.square
            .universe
            .parents_with_position_of(self.square.shape)
            .into_iter()
            .filter(|&(s, pos)| {
                let cpos = vectors::coerce_up(c - pos.to_vector());
                s.has_position(cpos.to_point())
            })
            .map(|(s, pos)| {
                let cpos = vectors::coerce_up(c - pos.to_vector());
                Square {
                    universe: self.square.universe,
                    shape: s,
                    position: cpos.to_point(),
                }
            })
            .collect();

        if inhabitants.len() > 1 {
            // TODO: This sort of sanity checking is probably a bit slower
            panic!("A location has multiple inhabitants!");
        } else {
            inhabitants.pop()
        }
    }

    fn to_coordinate(&self) -> ChildVec {
        vectors::shift_down(self.square.position.to_vector()) + self.subposition
    }
}

// This stores offsets of each shape in the chunk

// TODO: these offsets may be too small, need a vec of offsets, one
// for each level (that can be converted to a normal form, if we want)
#[derive(Clone)]
pub struct TotalChunk {
    pub origin_id: ShapeId,
    pub top_shape_ids: BTreeMap<ShapeId, UVec>,
    pub lower_shape_ids: BTreeMap<ShapeId, Delta>,
}

impl TotalChunk {}

type ExploreResult = BTreeMap<ShapeId, Delta>;
type ExploreQueue = VecDeque<(ShapeId, Delta)>;

impl Universe {
    fn explore_step(&self, result: &mut ExploreResult, queue: &mut ExploreQueue) -> () {
        while !queue.is_empty() {
            let (sid, delta) = queue.pop_front().unwrap();
            let s = &self.shapes[&sid];

            let needs_update: bool = match result.get(&sid) {
                None => true,
                Some(existing) => delta.zdelta < existing.zdelta,
            };

            if !needs_update {
                continue;
            }

            result.insert(sid, delta.clone());

            if delta.zdelta > 0 {
                for p in self.parents_of_id(sid) {
                    queue.push_front((p.id, delta.revert(&s.delta_from_parent(p)))); // TODO: check this logic
                }
            }

            for c in self.children_of_id(sid) {
                queue.push_back((c.id, delta.append(&s.delta_to_child(c))));
            }
        }
    }

    fn explore(&self, shape_id: ShapeId) -> TotalChunk {
        let mut result = BTreeMap::new();
        let mut queue = VecDeque::new();

        queue.push_back((shape_id, Delta::zero()));

        self.explore_step(&mut result, &mut queue);

        let top = result
            .iter()
            .filter_map(|(s, d)| {
                if d.zdelta == 0 {
                    Some((*s, d.to_uvec()))
                } else {
                    None
                }
            })
            .collect();
        let lower = result
            .iter()
            .filter_map(
                |(s, d)| {
                    if d.zdelta > 0 {
                        Some((*s, d.clone()))
                    } else {
                        None
                    }
                }, // More copying than necessary?
            )
            .collect();

        TotalChunk {
            origin_id: shape_id,
            top_shape_ids: top,
            lower_shape_ids: lower,
        }
    }

    // fn explore_parent_of(&self, shape_ids: BTreeMap<ShapeId, IVec2>) -> Chunk {
    // }

    pub fn chunk_of(&self, shape: &Shape) -> TotalChunk {
        self.explore(shape.id)
    }

    pub fn parent_of(&self, chunk: &TotalChunk) -> TotalChunk {
        let origin_parent_id = self.shapes[&chunk.origin_id].first_parent_id();
        self.explore(origin_parent_id)
    }
}

pub struct TopChunk {
    pub origin_id: ShapeId,
    pub top_shape_ids: BTreeMap<ShapeId, UVec>,
}

impl TopChunk {}

impl From<TotalChunk> for TopChunk {
    fn from(t: TotalChunk) -> TopChunk {
        let TotalChunk { origin_id, top_shape_ids, .. } = t;
        TopChunk { origin_id, top_shape_ids }
    }
}

pub struct GameState {
    pub universe: Universe,
    pub player_chunk: TotalChunk,
}

impl GameState {
    pub fn minimal() -> GameState {
        let u = Universe::minimal();

        GameState {
            universe: u,
            player_chunk: TotalChunk {
                origin_id: ShapeId(1),
                top_shape_ids: btreemap![ShapeId(1) => UVec::zero()],
                lower_shape_ids: btreemap![ShapeId(2) => Delta::zero().shift_target_down()],
            },
        }
    }
}
<|endoftext|>


<commit_before>//! Solve linear problems

use ndarray::*;

use super::convert::*;
use super::error::*;
use super::lapack_traits::*;
use super::layout::*;

pub use lapack_traits::{Pivot, Transpose};

pub struct Factorized<S: Data> {
    pub a: ArrayBase<S, Ix2>,
    pub ipiv: Pivot,
}

impl<A, S> Factorized<S>
where
    A: LapackScalar,
    S: Data<Elem = A>,
{
    pub fn solve<Sb>(&self, t: Transpose, mut rhs: ArrayBase<Sb, Ix1>) -> Result<ArrayBase<Sb, Ix1>>
    where
        Sb: DataMut<Elem = A>,
    {
        A::solve(
            self.a.square_layout()?,
            t,
            self.a.as_allocated()?,
            &self.ipiv,
            rhs.as_slice_mut().unwrap(),
        )?;
        Ok(rhs)
    }
}

impl<A, S> Factorized<S>
where
    A: LapackScalar,
    S: DataMut<Elem = A>,
{
    pub fn into_inverse(mut self) -> Result<ArrayBase<S, Ix2>> {
        A::inv(
            self.a.square_layout()?,
            self.a.as_allocated_mut()?,
            &self.ipiv,
        )?;
        Ok(self.a)
    }
}

pub trait Factorize<S: Data> {
    fn factorize(self) -> Result<Factorized<S>>;
}

impl<A, S> Factorize<S> for ArrayBase<S, Ix2>
where
    A: LapackScalar,
    S: DataMut<Elem = A>,
{
    fn factorize(mut self) -> Result<Factorized<S>> {
        let ipiv = A::lu(self.layout()?, self.as_allocated_mut()?)?;
        Ok(Factorized {
            a: self,
            ipiv: ipiv,
        })
    }
}

impl<'a, A, Si, So> Factorize<So> for &'a ArrayBase<Si, Ix2>
where
    A: LapackScalar + Copy,
    Si: Data<Elem = A>,
    So: DataOwned<Elem = A> + DataMut,
{
    fn factorize(self) -> Result<Factorized<So>> {
        let mut a: ArrayBase<So, Ix2> = replicate(self);
        let ipiv = A::lu(a.layout()?, a.as_allocated_mut()?)?;
        Ok(Factorized { a: a, ipiv: ipiv })
    }
}

pub trait Inverse<Inv> {
    fn inv(self) -> Result<Inv>;
}

impl<A, S> Inverse<ArrayBase<S, Ix2>> for ArrayBase<S, Ix2>
where
    A: LapackScalar,
    S: DataMut<Elem = A>,
{
    fn inv(self) -> Result<ArrayBase<S, Ix2>> {
        let f = self.factorize()?;
        f.into_inverse()
    }
}

impl<'a, A, Si, So> Inverse<ArrayBase<So, Ix2>> for &'a ArrayBase<Si, Ix2>
where
    A: LapackScalar + Copy,
    Si: Data<Elem = A>,
    So: DataOwned<Elem = A> + DataMut,
{
    fn inv(self) -> Result<ArrayBase<So, Ix2>> {
        let f = self.factorize()?;
        f.into_inverse()
    }
}
<commit_msg>Simplify solve<commit_after>//! Solve linear problems

use ndarray::*;

use super::convert::*;
use super::error::*;
use super::lapack_traits::*;
use super::layout::*;
use super::types::*;

pub use lapack_traits::{Pivot, Transpose};

pub struct Factorized<S: Data> {
    pub a: ArrayBase<S, Ix2>,
    pub ipiv: Pivot,
}

impl<A, S> Factorized<S>
where
    A: LapackScalar,
    S: Data<Elem = A>,
{
    pub fn solve<Sb>(&self, t: Transpose, mut rhs: ArrayBase<Sb, Ix1>) -> Result<ArrayBase<Sb, Ix1>>
    where
        Sb: DataMut<Elem = A>,
    {
        A::solve(
            self.a.square_layout()?,
            t,
            self.a.as_allocated()?,
            &self.ipiv,
            rhs.as_slice_mut().unwrap(),
        )?;
        Ok(rhs)
    }
}

impl<A, S> Factorized<S>
where
    A: LapackScalar,
    S: DataMut<Elem = A>,
{
    pub fn into_inverse(mut self) -> Result<ArrayBase<S, Ix2>> {
        A::inv(
            self.a.square_layout()?,
            self.a.as_allocated_mut()?,
            &self.ipiv,
        )?;
        Ok(self.a)
    }
}

pub trait Factorize<S: Data> {
    fn factorize(&self) -> Result<Factorized<S>>;
}

pub trait FactorizeInto<S: Data> {
    fn factorize_into(self) -> Result<Factorized<S>>;
}

impl<A, S> FactorizeInto<S> for ArrayBase<S, Ix2>
where
    A: Scalar,
    S: DataMut<Elem = A>,
{
    fn factorize_into(mut self) -> Result<Factorized<S>> {
        let ipiv = A::lu(self.layout()?, self.as_allocated_mut()?)?;
        Ok(Factorized {
            a: self,
            ipiv: ipiv,
        })
    }
}

impl<A, Si, So> Factorize<So> for ArrayBase<Si, Ix2>
where
    A: Scalar,
    Si: Data<Elem = A>,
    So: DataOwned<Elem = A> + DataMut,
{
    fn factorize(&self) -> Result<Factorized<So>> {
        let mut a: ArrayBase<So, Ix2> = replicate(self);
        let ipiv = A::lu(a.layout()?, a.as_allocated_mut()?)?;
        Ok(Factorized { a: a, ipiv: ipiv })
    }
}

pub trait Inverse {
    type Output;
    fn inv(&self) -> Result<Self::Output>;
}

pub trait InverseInto {
    type Output;
    fn inv_into(self) -> Result<Self::Output>;
}

impl<A, S> InverseInto for ArrayBase<S, Ix2>
where
    A: Scalar,
    S: DataMut<Elem = A>,
{
    type Output = Self;

    fn inv_into(self) -> Result<Self::Output> {
        let f = self.factorize_into()?;
        f.into_inverse()
    }
}

impl<A, Si> Inverse for ArrayBase<Si, Ix2>
where
    A: Scalar,
    Si: Data<Elem = A>,
{
    type Output = Array2<A>;

    fn inv(&self) -> Result<Self::Output> {
        let f = self.factorize()?;
        f.into_inverse()
    }
}
<|endoftext|>


<commit_before>#[derive(Eq,PartialEq,Debug,Copy,Clone,Hash,RustcEncodable,RustcDecodable)]
pub struct Locale {
    rank: u8,
    file: u8,
}

pub const ORANGE_FIGUREHEAD_START: Locale = Locale { rank: 0, file: 4 };
pub const BLUE_FIGUREHEAD_START: Locale = Locale { rank: 7, file: 4 };

static INDEX_TO_FILE_NAME: [char; 8] = [
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'
];

lazy_static! {
    static ref LOCALE_STASH: [Locale; 64] = {
        let mut m: [Locale; 64] = [Locale {rank: 0, file: 0}; 64];
        for rank in 0..8 {
            for file in 0..8 {
                let idx = (rank*8 + file) as usize;

                m[idx] = Locale { rank, file };
            }
        }
        m
    };
}

impl Locale {
    pub fn new(rank: u8, file: u8) -> Self {
        let idx = (rank*8 + file) as usize;
        LOCALE_STASH[idx]
    }

    pub fn to_algebraic(&self) -> String {
        format!("{}{}", INDEX_TO_FILE_NAME[self.file as usize], self.rank() + 1)
    }

    pub fn from_algebraic(notation: &str) -> Self {
        let mut notation_pieces = notation.chars();
        let file_note = notation_pieces.next()
            .expect("expected a first character");
        let rank_note = notation_pieces.next()
            .expect("expected a second character");
        Locale::new(
            (rank_note as u8) - 49, // 49 == '1'
            (file_note as u8) - 97, // 97 == 'a'
        )
    }

    pub fn pindex(&self) -> u32 {
        (8u32 * u32::from(self.rank())) + u32::from(self.file)
    }

    pub fn pinpoint(&self) -> Pinfield {
        Pinfield(1u64 << self.pindex())
    }

    pub fn is_legal(&self) -> bool {
        self.rank() < 8 && self.file < 8
    }

    pub fn displace(&self, offset: (i8, i8)) -> Option<Self> {
        let (rank_offset, file_offset) = offset;
        // note: when constructing possibly-illegal Locales, do not use ::new
        let potential_locale = Locale {
            // XXX: it won't happen with the arguments we expect to
            // give it in this program, but in the interests of Safety,
            // this is an overflow bug (-1i8 as u8 == 255u8)
            rank: (self.rank() as i8 + rank_offset) as u8,
            file: (self.file() as i8 + file_offset) as u8,
        };
        if potential_locale.is_legal() {
            Some(potential_locale)
        } else {
            None
        }
    }

    pub fn multidisplace(&self, offset: (i8, i8), factor: i8) -> Option<Self> {
        let (rank_offset, file_offset) = offset;
        let (real_rank, real_file) = (factor * rank_offset,
                                      factor * file_offset);

        // note: when constructing possibly-illegal Locales, do not use ::new
        let potential_locale = Locale {
            // XXX: could overflow given unrealistic arguments
            rank: (self.rank() as i8 + real_rank) as u8,
            file: (self.file() as i8 + real_file) as u8,
        };
        if potential_locale.is_legal() {
            Some(potential_locale)
        } else {
            None
        }
    }

    pub fn rank(&self) -> u8 {
        self.rank
    }

    pub fn file(&self) -> u8 {
        self.file
    }
}

#[derive(Eq, PartialEq, Debug, Copy, Clone, Hash, Default)]
pub struct Pinfield(pub u64);

impl Pinfield {
    pub fn new() -> Self {
        Pinfield(0)
    }

    pub fn init(starters: &[Locale]) -> Pinfield {
        let mut board = Pinfield::new();
        for &starter in starters.iter() {
            board = board.alight(starter);
        }
        board
    }

    pub fn union(&self, other: Pinfield) -> Pinfield {
        let Pinfield(our_bits) = *self;
        let Pinfield(their_bits) = other;
        Pinfield(our_bits | their_bits)
    }

    pub fn intersection(&self, other: Pinfield) -> Pinfield {
        let Pinfield(our_bits) = *self;
        let Pinfield(their_bits) = other;
        Pinfield(our_bits & their_bits)
    }

    pub fn invert(&self) -> Pinfield {
        let Pinfield(our_bits) = *self;
        Pinfield(!our_bits)
    }

    pub fn alight(&self, station: Locale) -> Pinfield {
        // classic `|= (1 << n)` would probably be more efficient, huh &c.
        self.union(station.pinpoint())
    }

    pub fn quench(&self, station: Locale) -> Pinfield {
        self.intersection(station.pinpoint().invert())
    }

    pub fn transit(&self, departure: Locale, destination: Locale) -> Pinfield {
        // empirically, pulling function calls out of this and replacing
        // them with bit manipulation directly doesnt help.
        self.quench(departure).alight(destination)
    }

    pub fn query(&self, station: Locale) -> bool {
        let Pinfield(our_bits) = *self;
        let Pinfield(beacon_bits) = station.pinpoint();
        (our_bits & beacon_bits) != 0
    }

    pub fn to_locales(&self) -> Vec<Locale> {
        let mut locales = Vec::with_capacity(8);
        let Pinfield(bits) = *self;
        let mut bitfield = 1u64;
        for rank in 0..8 {
            for file in 0..8 {
                if bitfield & bits != 0 {
                    let locale = Locale::new(rank, file);
                    locales.push(locale);
                }
                bitfield <<= 1;
            }
        }
        locales
    }

    pub fn pincount(&self) -> u8 {
        let Pinfield(bits) = *self;
        bits.count_ones() as u8
    }

    // TODO: convert to Display::fmt
    #[allow(dead_code)]
    pub fn display(&self) {
        for rank in 0..8 {
            for file in 0..8 {
                if self.query(Locale::new(rank, file)) {
                    print!("• ");
                } else {
                    print!("_ ");
                }
            }
            println!();
        }
    }
}

#[cfg(test)]
mod tests {
    extern crate test;
    extern crate rand;
    use self::test::{Bencher, black_box};
    use super::{Locale, Pinfield};
    use fnv;
    use twox_hash::XxHash;
    use std::hash::Hash;
    use std::collections::hash_map;
    use space::tests::rand::prelude::*;

    static ALGEBRAICS: [&'static str; 64] = [
        "a1", "b1", "c1", "d1", "e1", "f1", "g1", "h1",
        "a2", "b2", "c2", "d2", "e2", "f2", "g2", "h2",
        "a3", "b3", "c3", "d3", "e3", "f3", "g3", "h3",
        "a4", "b4", "c4", "d4", "e4", "f4", "g4", "h4",
        "a5", "b5", "c5", "d5", "e5", "f5", "g5", "h5",
        "a6", "b6", "c6", "d6", "e6", "f6", "g6", "h6",
        "a7", "b7", "c7", "d7", "e7", "f7", "g7", "h7",
        "a8", "b8", "c8", "d8", "e8", "f8", "g8", "h8"
    ];

    #[bench]
    fn benchmark_hashing_tuple_fnv(b: &mut Bencher) {
        let mut hasher = fnv::FnvHasher::default();
        let t: (u8, u8) = (1, 4);

        b.iter(|| {
            for _ in 0..1000 {
                t.hash(&mut hasher);
            }
        });
    }

    #[bench]
    fn benchmark_hashing_tuple_xx(b: &mut Bencher) {
        let mut hasher = XxHash::default();
        let t: (u8, u8) = (1, 4);

        b.iter(|| {
            for _ in 0..1000 {
                t.hash(&mut hasher);
            }
        });
    }

    #[bench]
    fn benchmark_hashing_tuple_sip(b: &mut Bencher) {
        let mut hasher = hash_map::DefaultHasher::new();
        let t: (u8, u8) = (1, 4);

        b.iter(|| {
            for _ in 0..1000 {
                t.hash(&mut hasher);
            }
        });
    }

    #[bench]
    fn benchmark_locale_lookup(b: &mut Bencher) {
        let mut args = Vec::with_capacity(64);
        for rank in 0..8 {
            for file in 0..8 {
                args.push((rank, file));
            }
        }
        let mut rng = thread_rng();
        args.shuffle(&mut rng);
        b.iter(|| {
            for (r, f) in &args {
                black_box(Locale::new(*r, *f));
            }
        });

    }

    #[bench]
    fn benchmark_to_locales_servantlike(b: &mut Bencher) {

        let mut stage = Pinfield(0);
        for r in 0..8 {
            stage = stage.alight(Locale::new(1, r));
        }

        b.iter(|| {
            for _ in 0..100 {
                black_box(stage.to_locales());
            }
        });
    }

    #[bench]
    fn benchmark_to_locales_scholarlike(b: &mut Bencher) {
        let mut stage = Pinfield(0);
        stage = stage.alight(Locale::new(0, 2));
        stage = stage.alight(Locale::new(0, 5));

        b.iter(|| {
            for _ in 0..100 {
                black_box(stage.to_locales());
            }
        });
    }

    #[bench]
    fn benchmark_transit(b: &mut Bencher) {
        let mut stage = Pinfield(0);
        let from = Locale::new(1, 3);
        let to = Locale::new(2, 5);
        stage = stage.alight(from);

        b.iter(|| {
            for _ in 0..10000 {
                black_box(stage.transit(from, to));
            }
        });
    }

    #[test]
    fn concerning_converting_to_algebraic() {
        let actual = iproduct!(0..8, 0..8)
                         .map(|t| Locale::new(t.0, t.1))
                         .map(|l| l.to_algebraic());
        for (expectation, actuality) in ALGEBRAICS.iter().zip(actual) {
            // TODO: it's more elegant if the conversion happens in
            // the iterator rather than the body of this
            // assertion-iteration
            assert_eq!(expectation.to_owned(), actuality);
        }
    }

    #[test]
    fn concerning_converting_from_algebraic() {
        let expected = iproduct!(0..8, 0..8).map(|t| Locale::new(t.0, t.1));
        for (expectation, actuality) in expected.zip(ALGEBRAICS.iter()) {
            assert_eq!(expectation,
                       // TODO: again, conversion in iterator
                       Locale::from_algebraic(*actuality));
        }
    }

    #[test]
    fn test_locale() {
        let a1 = Locale::new(0, 0);
        assert_eq!(Pinfield(1u64), a1.pinpoint());

        let c4 = Locale::new(2, 3);
        assert_eq!(Pinfield(524288u64), c4.pinpoint());
    }

    #[test]
    fn test_displace_locale() {
        assert_eq!(
            Some(Locale::new(3, 5)),
            Locale::new(4, 4).displace((-1, 1))
        );
        assert_eq!(
            None,
            Locale::new(0, 0).displace((-1, 1))
        )
    }

    #[test]
    fn concerning_multidisplacement() {
        for i in 0..8 {
            assert_eq!(
                Some(Locale::new(i as u8, i as u8 )),
                Locale::new(0, 0).multidisplace((1, 1), i)
            )
        }
        assert_eq!(None, Locale::new(0, 0).multidisplace((1, 1), 8));
    }

    #[test]
    fn test_alight_and_quench() {
        let mut stage = Pinfield(0);
        let b5 = Locale::new(1, 4);
        stage = stage.alight(b5);
        assert!(stage.query(b5));
        stage = stage.quench(b5);
        assert!(!stage.query(b5));
    }

    #[test]
    fn test_init_pinfield() {
        let starters = vec![Locale::new(1, 2),
                            Locale::new(3, 4),
                            Locale::new(5, 6)];
        let stage = Pinfield::init(&starters);
        for &starter in &starters {
            assert!(stage.query(starter));
        }
    }

    #[test]
    fn concerning_pincount() {
        let starters = vec![Locale::new(1, 2),
                            Locale::new(3, 4),
                            Locale::new(5, 6)];
        let stage = Pinfield::init(&starters);
        assert_eq!(3, stage.pincount());
    }
}
<commit_msg>Save some space in the Locale<commit_after>#[derive(Eq,PartialEq,Debug,Copy,Clone,Hash,RustcEncodable,RustcDecodable)]
pub struct Locale {
    rank_and_file: u8,
}

pub const ORANGE_FIGUREHEAD_START: Locale = Locale { rank_and_file: (0 << 4) | 4 };
pub const BLUE_FIGUREHEAD_START: Locale = Locale { rank_and_file: (7 << 4) | 4 };

static INDEX_TO_FILE_NAME: [char; 8] = [
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'
];

lazy_static! {
    static ref LOCALE_STASH: [Locale; 64] = {
        let mut m: [Locale; 64] = [Locale {rank_and_file: 0}; 64];
        for rank in 0..8 {
            for file in 0..8 {
                let idx = (rank*8 + file) as usize;

                let rank_and_file = (rank << 4) | file;
                m[idx] = Locale { rank_and_file }
            }
        }
        m
    };
}

impl Locale {
    pub fn new(rank: u8, file: u8) -> Self {
        let idx = (rank*8 + file) as usize;
        LOCALE_STASH[idx]
    }

    pub fn to_algebraic(&self) -> String {
        format!("{}{}", INDEX_TO_FILE_NAME[self.file() as usize], self.rank() + 1)
    }

    pub fn from_algebraic(notation: &str) -> Self {
        let mut notation_pieces = notation.chars();
        let file_note = notation_pieces.next()
            .expect("expected a first character");
        let rank_note = notation_pieces.next()
            .expect("expected a second character");
        Locale::new(
            (rank_note as u8) - 49, // 49 == '1'
            (file_note as u8) - 97, // 97 == 'a'
        )
    }

    pub fn pindex(&self) -> u32 {
        (8u32 * u32::from(self.rank())) + u32::from(self.file())
    }

    pub fn pinpoint(&self) -> Pinfield {
        Pinfield(1u64 << self.pindex())
    }

    pub fn is_legal(&self) -> bool {
        self.rank() < 8 && self.file() < 8
    }

    fn build_possibly_illegal(rank: u8, file: u8) -> Self {
        let rank_and_file = (rank << 4) | file;
        Locale { rank_and_file }
    }

    pub fn displace(&self, offset: (i8, i8)) -> Option<Self> {
        let (rank_offset, file_offset) = offset;
        // note: when constructing possibly-illegal Locales, do not use ::new
        let potential_locale = Locale::build_possibly_illegal(
            // XXX: it won't happen with the arguments we expect to
            // give it in this program, but in the interests of Safety,
            // this is an overflow bug (-1i8 as u8 == 255u8)
            (self.rank() as i8 + rank_offset) as u8,
            (self.file() as i8 + file_offset) as u8,
        );
        if potential_locale.is_legal() {
            Some(potential_locale)
        } else {
            None
        }
    }

    pub fn multidisplace(&self, offset: (i8, i8), factor: i8) -> Option<Self> {
        let (rank_offset, file_offset) = offset;
        let (real_rank, real_file) = (factor * rank_offset,
                                      factor * file_offset);

        // note: when constructing possibly-illegal Locales, do not use ::new
        let potential_locale = Locale::build_possibly_illegal(
            // XXX: could overflow given unrealistic arguments
            (self.rank() as i8 + real_rank) as u8,
            (self.file() as i8 + real_file) as u8,
        );
        if potential_locale.is_legal() {
            Some(potential_locale)
        } else {
            None
        }
    }

    pub fn rank(&self) -> u8 {
        self.rank_and_file >> 4
    }

    pub fn file(&self) -> u8 {
        self.rank_and_file & (0b1111)
    }
}

#[derive(Eq, PartialEq, Debug, Copy, Clone, Hash, Default)]
pub struct Pinfield(pub u64);

impl Pinfield {
    pub fn new() -> Self {
        Pinfield(0)
    }

    pub fn init(starters: &[Locale]) -> Pinfield {
        let mut board = Pinfield::new();
        for &starter in starters.iter() {
            board = board.alight(starter);
        }
        board
    }

    pub fn union(&self, other: Pinfield) -> Pinfield {
        let Pinfield(our_bits) = *self;
        let Pinfield(their_bits) = other;
        Pinfield(our_bits | their_bits)
    }

    pub fn intersection(&self, other: Pinfield) -> Pinfield {
        let Pinfield(our_bits) = *self;
        let Pinfield(their_bits) = other;
        Pinfield(our_bits & their_bits)
    }

    pub fn invert(&self) -> Pinfield {
        let Pinfield(our_bits) = *self;
        Pinfield(!our_bits)
    }

    pub fn alight(&self, station: Locale) -> Pinfield {
        // classic `|= (1 << n)` would probably be more efficient, huh &c.
        self.union(station.pinpoint())
    }

    pub fn quench(&self, station: Locale) -> Pinfield {
        self.intersection(station.pinpoint().invert())
    }

    pub fn transit(&self, departure: Locale, destination: Locale) -> Pinfield {
        // empirically, pulling function calls out of this and replacing
        // them with bit manipulation directly doesnt help.
        self.quench(departure).alight(destination)
    }

    pub fn query(&self, station: Locale) -> bool {
        let Pinfield(our_bits) = *self;
        let Pinfield(beacon_bits) = station.pinpoint();
        (our_bits & beacon_bits) != 0
    }

    pub fn to_locales(&self) -> Vec<Locale> {
        let mut locales = Vec::with_capacity(8);
        let Pinfield(bits) = *self;
        let mut bitfield = 1u64;
        for rank in 0..8 {
            for file in 0..8 {
                if bitfield & bits != 0 {
                    let locale = Locale::new(rank, file);
                    locales.push(locale);
                }
                bitfield <<= 1;
            }
        }
        locales
    }

    pub fn pincount(&self) -> u8 {
        let Pinfield(bits) = *self;
        bits.count_ones() as u8
    }

    // TODO: convert to Display::fmt
    #[allow(dead_code)]
    pub fn display(&self) {
        for rank in 0..8 {
            for file in 0..8 {
                if self.query(Locale::new(rank, file)) {
                    print!("• ");
                } else {
                    print!("_ ");
                }
            }
            println!();
        }
    }
}

#[cfg(test)]
mod tests {
    extern crate test;
    extern crate rand;
    use self::test::{Bencher, black_box};
    use super::{Locale, Pinfield};
    use fnv;
    use twox_hash::XxHash;
    use std::hash::Hash;
    use std::collections::hash_map;
    use space::tests::rand::prelude::*;

    static ALGEBRAICS: [&'static str; 64] = [
        "a1", "b1", "c1", "d1", "e1", "f1", "g1", "h1",
        "a2", "b2", "c2", "d2", "e2", "f2", "g2", "h2",
        "a3", "b3", "c3", "d3", "e3", "f3", "g3", "h3",
        "a4", "b4", "c4", "d4", "e4", "f4", "g4", "h4",
        "a5", "b5", "c5", "d5", "e5", "f5", "g5", "h5",
        "a6", "b6", "c6", "d6", "e6", "f6", "g6", "h6",
        "a7", "b7", "c7", "d7", "e7", "f7", "g7", "h7",
        "a8", "b8", "c8", "d8", "e8", "f8", "g8", "h8"
    ];

    #[bench]
    fn benchmark_hashing_tuple_fnv(b: &mut Bencher) {
        let mut hasher = fnv::FnvHasher::default();
        let t: (u8, u8) = (1, 4);

        b.iter(|| {
            for _ in 0..1000 {
                t.hash(&mut hasher);
            }
        });
    }

    #[bench]
    fn benchmark_hashing_tuple_xx(b: &mut Bencher) {
        let mut hasher = XxHash::default();
        let t: (u8, u8) = (1, 4);

        b.iter(|| {
            for _ in 0..1000 {
                t.hash(&mut hasher);
            }
        });
    }

    #[bench]
    fn benchmark_hashing_tuple_sip(b: &mut Bencher) {
        let mut hasher = hash_map::DefaultHasher::new();
        let t: (u8, u8) = (1, 4);

        b.iter(|| {
            for _ in 0..1000 {
                t.hash(&mut hasher);
            }
        });
    }

    #[bench]
    fn benchmark_locale_lookup(b: &mut Bencher) {
        let mut args = Vec::with_capacity(64);
        for rank in 0..8 {
            for file in 0..8 {
                args.push((rank, file));
            }
        }
        let mut rng = thread_rng();
        args.shuffle(&mut rng);
        b.iter(|| {
            for (r, f) in &args {
                black_box(Locale::new(*r, *f));
            }
        });

    }

    #[bench]
    fn benchmark_to_locales_servantlike(b: &mut Bencher) {

        let mut stage = Pinfield(0);
        for r in 0..8 {
            stage = stage.alight(Locale::new(1, r));
        }

        b.iter(|| {
            for _ in 0..100 {
                black_box(stage.to_locales());
            }
        });
    }

    #[bench]
    fn benchmark_to_locales_scholarlike(b: &mut Bencher) {
        let mut stage = Pinfield(0);
        stage = stage.alight(Locale::new(0, 2));
        stage = stage.alight(Locale::new(0, 5));

        b.iter(|| {
            for _ in 0..100 {
                black_box(stage.to_locales());
            }
        });
    }

    #[bench]
    fn benchmark_transit(b: &mut Bencher) {
        let mut stage = Pinfield(0);
        let from = Locale::new(1, 3);
        let to = Locale::new(2, 5);
        stage = stage.alight(from);

        b.iter(|| {
            for _ in 0..10000 {
                black_box(stage.transit(from, to));
            }
        });
    }

    #[test]
    fn concerning_converting_to_algebraic() {
        let actual = iproduct!(0..8, 0..8)
                         .map(|t| Locale::new(t.0, t.1))
                         .map(|l| l.to_algebraic());
        for (expectation, actuality) in ALGEBRAICS.iter().zip(actual) {
            // TODO: it's more elegant if the conversion happens in
            // the iterator rather than the body of this
            // assertion-iteration
            assert_eq!(expectation.to_owned(), actuality);
        }
    }

    #[test]
    fn concerning_converting_from_algebraic() {
        let expected = iproduct!(0..8, 0..8).map(|t| Locale::new(t.0, t.1));
        for (expectation, actuality) in expected.zip(ALGEBRAICS.iter()) {
            assert_eq!(expectation,
                       // TODO: again, conversion in iterator
                       Locale::from_algebraic(*actuality));
        }
    }

    #[test]
    fn concerning_all_locales() {
        for rank in 0..8 {
            for file in 0..8 {
                let l = Locale {
                    rank_and_file: (rank << 4) | file
                };
                assert_eq!(rank, l.rank());
                assert_eq!(file, l.file());
            }
        }

    }

    #[test]
    fn test_locale() {
        let a1 = Locale::new(0, 0);
        assert_eq!(Pinfield(1u64), a1.pinpoint());

        let c4 = Locale::new(2, 3);
        assert_eq!(Pinfield(524288u64), c4.pinpoint());
    }

    #[test]
    fn test_displace_locale() {
        assert_eq!(
            Some(Locale::new(3, 5)),
            Locale::new(4, 4).displace((-1, 1))
        );
        assert_eq!(
            None,
            Locale::new(0, 0).displace((-1, 1))
        )
    }

    #[test]
    fn concerning_multidisplacement() {
        for i in 0..8 {
            assert_eq!(
                Some(Locale::new(i as u8, i as u8 )),
                Locale::new(0, 0).multidisplace((1, 1), i)
            )
        }
        assert_eq!(None, Locale::new(0, 0).multidisplace((1, 1), 8));
    }

    #[test]
    fn test_alight_and_quench() {
        let mut stage = Pinfield(0);
        let b5 = Locale::new(1, 4);
        stage = stage.alight(b5);
        assert!(stage.query(b5));
        stage = stage.quench(b5);
        assert!(!stage.query(b5));
    }

    #[test]
    fn test_init_pinfield() {
        let starters = vec![Locale::new(1, 2),
                            Locale::new(3, 4),
                            Locale::new(5, 6)];
        let stage = Pinfield::init(&starters);
        for &starter in &starters {
            assert!(stage.query(starter));
        }
    }

    #[test]
    fn concerning_pincount() {
        let starters = vec![Locale::new(1, 2),
                            Locale::new(3, 4),
                            Locale::new(5, 6)];
        let stage = Pinfield::init(&starters);
        assert_eq!(3, stage.pincount());
    }
}
<|endoftext|>


<commit_before>use eventual::Async;
use protobuf::{self, Message};

use util;
use session::Session;
use util::SpotifyId;
use util::version::version_string;
use mercury::{MercuryRequest, MercuryMethod};
use player::{Player, PlayerState};

use std::sync::{Mutex, Arc};
use std::collections::HashMap;

use protocol;
pub use protocol::spirc::{PlayStatus, MessageType};

pub struct SpircManager(Arc<Mutex<SpircInternal>>);

struct SpircInternal {
    player: Player,
    session: Session,

    seq_nr: u32,

    name: String,
    ident: String,
    device_type: u8,
    can_play: bool,

    repeat: bool,
    shuffle: bool,

    is_active: bool,
    became_active_at: i64,

    last_command_ident: String,
    last_command_msgid: u32,

    tracks: Vec<SpotifyId>,
    index: u32,

    devices: HashMap<String, String>,
}

impl SpircManager {
    pub fn new(session: Session, player: Player) -> SpircManager {
        let ident = session.device_id();
        let name = session.config().device_name.clone();

        SpircManager(Arc::new(Mutex::new(SpircInternal {
            player: player,
            session: session,

            seq_nr: 0,

            name: name,
            ident: ident,
            device_type: 5,
            can_play: true,

            repeat: false,
            shuffle: false,

            is_active: false,
            became_active_at: 0,

            last_command_ident: String::new(),
            last_command_msgid: 0,

            tracks: Vec::new(),
            index: 0,

            devices: HashMap::new(),
        })))
    }

    pub fn run(&mut self) {
        let rx = {
            let mut internal = self.0.lock().unwrap();

            let rx = internal.session.mercury_sub(internal.uri());

            internal.notify(true, None);

            // Use a weak pointer to avoid creating an Rc cycle between the player and the
            // SpircManager
            let _self = Arc::downgrade(&self.0);
            internal.player.add_observer(Box::new(move |state| {
                if let Some(_self) = _self.upgrade() {
                    let mut internal = _self.lock().unwrap();
                    internal.on_update(state);
                }
            }));

            rx
        };

        for pkt in rx {
            let data = pkt.payload.first().unwrap();
            let frame = protobuf::parse_from_bytes::<protocol::spirc::Frame>(data).unwrap();

            println!("{:?} {} {} {} {}",
                     frame.get_typ(),
                     frame.get_device_state().get_name(),
                     frame.get_ident(),
                     frame.get_seq_nr(),
                     frame.get_state_update_id());

            self.0.lock().unwrap().handle(frame);
        }
    }
}

impl SpircInternal {
    fn on_update(&mut self, player_state: &PlayerState) {
        let end_of_track = player_state.end_of_track();
        if end_of_track {
            self.index = (self.index + 1) % self.tracks.len() as u32;
            let track = self.tracks[self.index as usize];
            self.player.load(track, true, 0);
        } else {
            self.notify_with_player_state(false, None, player_state);
        }
    }

    fn handle(&mut self, mut frame: protocol::spirc::Frame) {
        if frame.get_ident() == self.ident ||
           (frame.get_recipient().len() > 0 && !frame.get_recipient().contains(&self.ident)) {
            return;
        }

        if frame.get_recipient().len() > 0 {
            self.last_command_ident = frame.get_ident().to_owned();
            self.last_command_msgid = frame.get_seq_nr();
        }

        if frame.has_ident() && !frame.has_goodbye() && frame.has_device_state() {
            self.devices.insert(frame.take_ident(), frame.take_device_state().take_name());
        }

        match frame.get_typ() {
            protocol::spirc::MessageType::kMessageTypeHello => {
                self.notify(false, Some(frame.get_ident()));
            }
            protocol::spirc::MessageType::kMessageTypeLoad => {
                if !self.is_active {
                    self.is_active = true;
                    self.became_active_at = util::now_ms();
                }

                self.reload_tracks(&frame);

                let play = frame.get_state().get_status() == PlayStatus::kPlayStatusPlay;
                let track = self.tracks[self.index as usize];
                let position = frame.get_state().get_position_ms();
                self.player.load(track, play, position);
            }
            protocol::spirc::MessageType::kMessageTypePlay => {
                self.player.play();
            }
            protocol::spirc::MessageType::kMessageTypePause => {
                self.player.pause();
            }
            protocol::spirc::MessageType::kMessageTypeNext => {
                self.index = (self.index + 1) % self.tracks.len() as u32;
                let track = self.tracks[self.index as usize];
                self.player.load(track, true, 0);
            }
            protocol::spirc::MessageType::kMessageTypePrev => {
                self.index = (self.index - 1) % self.tracks.len() as u32;
                let track = self.tracks[self.index as usize];
                self.player.load(track, true, 0);
            }
            protocol::spirc::MessageType::kMessageTypeSeek => {
                self.player.seek(frame.get_position());
            }
            protocol::spirc::MessageType::kMessageTypeReplace => {
                self.reload_tracks(&frame);
            }
            protocol::spirc::MessageType::kMessageTypeNotify => {
                if self.is_active && frame.get_device_state().get_is_active() {
                    self.is_active = false;
                    self.player.stop();
                }

                if frame.has_ident() && frame.has_goodbye() {
                    self.devices.remove(&frame.take_ident());
                }
            }
            protocol::spirc::MessageType::kMessageTypeVolume => {
                self.player.volume(frame.get_volume() as u16);
            }
            _ => (),
        }
    }

    fn reload_tracks(&mut self, ref frame: &protocol::spirc::Frame) {
        self.index = frame.get_state().get_playing_track_index();
        self.tracks = frame.get_state()
                           .get_track()
                           .iter()
                           .filter(|track| track.has_gid())
                           .map(|track| SpotifyId::from_raw(track.get_gid()))
                           .collect();
    }

    fn notify(&mut self, hello: bool, recipient: Option<&str>) {
        send_cmd(self,
                 if hello {
                     MessageType::kMessageTypeHello
                 } else {
                     MessageType::kMessageTypeNotify
                 },
                 recipient,
                 None);
    }

    fn notify_with_player_state(&mut self,
                                hello: bool,
                                recipient: Option<&str>,
                                player_state: &PlayerState) {
        send_cmd(self,
                 if hello {
                     MessageType::kMessageTypeHello
                 } else {
                     MessageType::kMessageTypeNotify
                 },
                 recipient,
                 Some(player_state));
    }

    fn spirc_state(&self, player_state: &PlayerState) -> protocol::spirc::State {
        let (position_ms, position_measured_at) = player_state.position();

        protobuf_init!(protocol::spirc::State::new(), {
            status: player_state.status(),
            position_ms: position_ms,
            position_measured_at: position_measured_at as u64,

            playing_track_index: self.index,
            track: self.tracks.iter().map(|track| {
                protobuf_init!(protocol::spirc::TrackRef::new(), {
                    gid: track.to_raw().to_vec()
                })
            }).collect(),

            shuffle: self.shuffle,
            repeat: self.repeat,

            playing_from_fallback: true,

            last_command_ident: self.last_command_ident.clone(),
            last_command_msgid: self.last_command_msgid
        })
    }

    fn device_state(&self, player_state: &PlayerState) -> protocol::spirc::DeviceState {
        protobuf_init!(protocol::spirc::DeviceState::new(), {
            sw_version: version_string(),
            is_active: self.is_active,
            can_play: self.can_play,
            volume: player_state.volume() as u32,
            name: self.name.clone(),
            error_code: 0,
            became_active_at: if self.is_active { self.became_active_at as i64 } else { 0 },
            capabilities => [
                @{
                    typ: protocol::spirc::CapabilityType::kCanBePlayer,
                    intValue => [0]
                },
                @{
                    typ: protocol::spirc::CapabilityType::kDeviceType,
                    intValue => [ self.device_type as i64 ]
                },
                @{
                    typ: protocol::spirc::CapabilityType::kGaiaEqConnectId,
                    intValue => [1]
                },
                @{
                    typ: protocol::spirc::CapabilityType::kSupportsLogout,
                    intValue => [0]
                },
                @{
                    typ: protocol::spirc::CapabilityType::kIsObservable,
                    intValue => [1]
                },
                @{
                    typ: protocol::spirc::CapabilityType::kVolumeSteps,
                    intValue => [10]
                },
                @{
                    typ: protocol::spirc::CapabilityType::kSupportedContexts,
                    stringValue => [
                        "album".to_owned(),
                        "playlist".to_owned(),
                        "search".to_owned(),
                        "inbox".to_owned(),
                        "toplist".to_owned(),
                        "starred".to_owned(),
                        "publishedstarred".to_owned(),
                        "track".to_owned(),
                    ]
                },
                @{
                    typ: protocol::spirc::CapabilityType::kSupportedTypes,
                    stringValue => [
                        "audio/local".to_owned(),
                        "audio/track".to_owned(),
                        "local".to_owned(),
                        "track".to_owned(),
                    ]
                }
            ],
        })
    }

    fn uri(&self) -> String {
        format!("hm://remote/user/{}", self.session.username())
    }
}

fn send_cmd(spirc_internal: &mut SpircInternal,
            cmd: protocol::spirc::MessageType,
            recipient: Option<&str>,
            player_state: Option<&PlayerState>) {
    let mut pkt = protobuf_init!(protocol::spirc::Frame::new(), {
        version: 1,
        ident: spirc_internal.ident.clone(),
        protocol_version: "2.0.0".to_owned(),
        seq_nr: { spirc_internal.seq_nr += 1; spirc_internal.seq_nr  },
        typ: cmd,
        recipient: protobuf::RepeatedField::from_vec(
            recipient.map(|r| vec![r.to_owned()] ).unwrap_or(vec![])
        ),
    });

    if let Some(s) = player_state {
        pkt.set_device_state(spirc_internal.device_state(s));
        pkt.set_state_update_id(s.update_time() as i64);
        if spirc_internal.is_active {
            pkt.set_state(spirc_internal.spirc_state(s));
        }
    } else {
        let s = &*spirc_internal.player.state();
        pkt.set_device_state(spirc_internal.device_state(s));
        pkt.set_state_update_id(s.update_time() as i64);
        if spirc_internal.is_active {
            pkt.set_state(spirc_internal.spirc_state(s));
        }
    }

    spirc_internal.session
                  .mercury(MercuryRequest {
                      method: MercuryMethod::SEND,
                      uri: spirc_internal.uri(),
                      content_type: None,
                      payload: vec![pkt.write_to_bytes().unwrap()],
                  })
                  .fire();
}
<commit_msg>Export devices.<commit_after>use eventual::Async;
use protobuf::{self, Message};

use util;
use session::Session;
use util::SpotifyId;
use util::version::version_string;
use mercury::{MercuryRequest, MercuryMethod};
use player::{Player, PlayerState};

use std::sync::{Mutex, Arc};
use std::collections::HashMap;

use protocol;
pub use protocol::spirc::{PlayStatus, MessageType};

pub struct SpircManager(Arc<Mutex<SpircInternal>>);

struct SpircInternal {
    player: Player,
    session: Session,

    seq_nr: u32,

    name: String,
    ident: String,
    device_type: u8,
    can_play: bool,

    repeat: bool,
    shuffle: bool,

    is_active: bool,
    became_active_at: i64,

    last_command_ident: String,
    last_command_msgid: u32,

    tracks: Vec<SpotifyId>,
    index: u32,

    devices: HashMap<String, String>,
}

impl SpircManager {
    pub fn new(session: Session, player: Player) -> SpircManager {
        let ident = session.device_id();
        let name = session.config().device_name.clone();

        SpircManager(Arc::new(Mutex::new(SpircInternal {
            player: player,
            session: session,

            seq_nr: 0,

            name: name,
            ident: ident,
            device_type: 5,
            can_play: true,

            repeat: false,
            shuffle: false,

            is_active: false,
            became_active_at: 0,

            last_command_ident: String::new(),
            last_command_msgid: 0,

            tracks: Vec::new(),
            index: 0,

            devices: HashMap::new(),
        })))
    }

    pub fn run(&mut self) {
        let rx = {
            let mut internal = self.0.lock().unwrap();

            let rx = internal.session.mercury_sub(internal.uri());

            internal.notify(true, None);

            // Use a weak pointer to avoid creating an Rc cycle between the player and the
            // SpircManager
            let _self = Arc::downgrade(&self.0);
            internal.player.add_observer(Box::new(move |state| {
                if let Some(_self) = _self.upgrade() {
                    let mut internal = _self.lock().unwrap();
                    internal.on_update(state);
                }
            }));

            rx
        };

        for pkt in rx {
            let data = pkt.payload.first().unwrap();
            let frame = protobuf::parse_from_bytes::<protocol::spirc::Frame>(data).unwrap();

            println!("{:?} {} {} {} {}",
                     frame.get_typ(),
                     frame.get_device_state().get_name(),
                     frame.get_ident(),
                     frame.get_seq_nr(),
                     frame.get_state_update_id());

            self.0.lock().unwrap().handle(frame);
        }
    }

    pub fn devices(&self) -> HashMap<String, String> {
        self.0.lock().unwrap().devices.clone()
    }
}

impl SpircInternal {
    fn on_update(&mut self, player_state: &PlayerState) {
        let end_of_track = player_state.end_of_track();
        if end_of_track {
            self.index = (self.index + 1) % self.tracks.len() as u32;
            let track = self.tracks[self.index as usize];
            self.player.load(track, true, 0);
        } else {
            self.notify_with_player_state(false, None, player_state);
        }
    }

    fn handle(&mut self, mut frame: protocol::spirc::Frame) {
        if frame.get_ident() == self.ident ||
           (frame.get_recipient().len() > 0 && !frame.get_recipient().contains(&self.ident)) {
            return;
        }

        if frame.get_recipient().len() > 0 {
            self.last_command_ident = frame.get_ident().to_owned();
            self.last_command_msgid = frame.get_seq_nr();
        }

        if frame.has_ident() && !frame.has_goodbye() && frame.has_device_state() {
            self.devices.insert(frame.take_ident(), frame.take_device_state().take_name());
        }

        match frame.get_typ() {
            protocol::spirc::MessageType::kMessageTypeHello => {
                self.notify(false, Some(frame.get_ident()));
            }
            protocol::spirc::MessageType::kMessageTypeLoad => {
                if !self.is_active {
                    self.is_active = true;
                    self.became_active_at = util::now_ms();
                }

                self.reload_tracks(&frame);

                let play = frame.get_state().get_status() == PlayStatus::kPlayStatusPlay;
                let track = self.tracks[self.index as usize];
                let position = frame.get_state().get_position_ms();
                self.player.load(track, play, position);
            }
            protocol::spirc::MessageType::kMessageTypePlay => {
                self.player.play();
            }
            protocol::spirc::MessageType::kMessageTypePause => {
                self.player.pause();
            }
            protocol::spirc::MessageType::kMessageTypeNext => {
                self.index = (self.index + 1) % self.tracks.len() as u32;
                let track = self.tracks[self.index as usize];
                self.player.load(track, true, 0);
            }
            protocol::spirc::MessageType::kMessageTypePrev => {
                self.index = (self.index - 1) % self.tracks.len() as u32;
                let track = self.tracks[self.index as usize];
                self.player.load(track, true, 0);
            }
            protocol::spirc::MessageType::kMessageTypeSeek => {
                self.player.seek(frame.get_position());
            }
            protocol::spirc::MessageType::kMessageTypeReplace => {
                self.reload_tracks(&frame);
            }
            protocol::spirc::MessageType::kMessageTypeNotify => {
                if self.is_active && frame.get_device_state().get_is_active() {
                    self.is_active = false;
                    self.player.stop();
                }

                if frame.has_ident() && frame.has_goodbye() {
                    self.devices.remove(&frame.take_ident());
                }
            }
            protocol::spirc::MessageType::kMessageTypeVolume => {
                self.player.volume(frame.get_volume() as u16);
            }
            _ => (),
        }
    }

    fn reload_tracks(&mut self, ref frame: &protocol::spirc::Frame) {
        self.index = frame.get_state().get_playing_track_index();
        self.tracks = frame.get_state()
                           .get_track()
                           .iter()
                           .filter(|track| track.has_gid())
                           .map(|track| SpotifyId::from_raw(track.get_gid()))
                           .collect();
    }

    fn notify(&mut self, hello: bool, recipient: Option<&str>) {
        send_cmd(self,
                 if hello {
                     MessageType::kMessageTypeHello
                 } else {
                     MessageType::kMessageTypeNotify
                 },
                 recipient,
                 None);
    }

    fn notify_with_player_state(&mut self,
                                hello: bool,
                                recipient: Option<&str>,
                                player_state: &PlayerState) {
        send_cmd(self,
                 if hello {
                     MessageType::kMessageTypeHello
                 } else {
                     MessageType::kMessageTypeNotify
                 },
                 recipient,
                 Some(player_state));
    }

    fn spirc_state(&self, player_state: &PlayerState) -> protocol::spirc::State {
        let (position_ms, position_measured_at) = player_state.position();

        protobuf_init!(protocol::spirc::State::new(), {
            status: player_state.status(),
            position_ms: position_ms,
            position_measured_at: position_measured_at as u64,

            playing_track_index: self.index,
            track: self.tracks.iter().map(|track| {
                protobuf_init!(protocol::spirc::TrackRef::new(), {
                    gid: track.to_raw().to_vec()
                })
            }).collect(),

            shuffle: self.shuffle,
            repeat: self.repeat,

            playing_from_fallback: true,

            last_command_ident: self.last_command_ident.clone(),
            last_command_msgid: self.last_command_msgid
        })
    }

    fn device_state(&self, player_state: &PlayerState) -> protocol::spirc::DeviceState {
        protobuf_init!(protocol::spirc::DeviceState::new(), {
            sw_version: version_string(),
            is_active: self.is_active,
            can_play: self.can_play,
            volume: player_state.volume() as u32,
            name: self.name.clone(),
            error_code: 0,
            became_active_at: if self.is_active { self.became_active_at as i64 } else { 0 },
            capabilities => [
                @{
                    typ: protocol::spirc::CapabilityType::kCanBePlayer,
                    intValue => [0]
                },
                @{
                    typ: protocol::spirc::CapabilityType::kDeviceType,
                    intValue => [ self.device_type as i64 ]
                },
                @{
                    typ: protocol::spirc::CapabilityType::kGaiaEqConnectId,
                    intValue => [1]
                },
                @{
                    typ: protocol::spirc::CapabilityType::kSupportsLogout,
                    intValue => [0]
                },
                @{
                    typ: protocol::spirc::CapabilityType::kIsObservable,
                    intValue => [1]
                },
                @{
                    typ: protocol::spirc::CapabilityType::kVolumeSteps,
                    intValue => [10]
                },
                @{
                    typ: protocol::spirc::CapabilityType::kSupportedContexts,
                    stringValue => [
                        "album".to_owned(),
                        "playlist".to_owned(),
                        "search".to_owned(),
                        "inbox".to_owned(),
                        "toplist".to_owned(),
                        "starred".to_owned(),
                        "publishedstarred".to_owned(),
                        "track".to_owned(),
                    ]
                },
                @{
                    typ: protocol::spirc::CapabilityType::kSupportedTypes,
                    stringValue => [
                        "audio/local".to_owned(),
                        "audio/track".to_owned(),
                        "local".to_owned(),
                        "track".to_owned(),
                    ]
                }
            ],
        })
    }

    fn uri(&self) -> String {
        format!("hm://remote/user/{}", self.session.username())
    }
}

fn send_cmd(spirc_internal: &mut SpircInternal,
            cmd: protocol::spirc::MessageType,
            recipient: Option<&str>,
            player_state: Option<&PlayerState>) {
    let mut pkt = protobuf_init!(protocol::spirc::Frame::new(), {
        version: 1,
        ident: spirc_internal.ident.clone(),
        protocol_version: "2.0.0".to_owned(),
        seq_nr: { spirc_internal.seq_nr += 1; spirc_internal.seq_nr  },
        typ: cmd,
        recipient: protobuf::RepeatedField::from_vec(
            recipient.map(|r| vec![r.to_owned()] ).unwrap_or(vec![])
        ),
    });

    if let Some(s) = player_state {
        pkt.set_device_state(spirc_internal.device_state(s));
        pkt.set_state_update_id(s.update_time() as i64);
        if spirc_internal.is_active {
            pkt.set_state(spirc_internal.spirc_state(s));
        }
    } else {
        let s = &*spirc_internal.player.state();
        pkt.set_device_state(spirc_internal.device_state(s));
        pkt.set_state_update_id(s.update_time() as i64);
        if spirc_internal.is_active {
            pkt.set_state(spirc_internal.spirc_state(s));
        }
    }

    spirc_internal.session
                  .mercury(MercuryRequest {
                      method: MercuryMethod::SEND,
                      uri: spirc_internal.uri(),
                      content_type: None,
                      payload: vec![pkt.write_to_bytes().unwrap()],
                  })
                  .fire();
}
<|endoftext|>


<commit_before>use std::path::{PathBuf, Components};
use types::{ToResult, ToError, Bookmark, Bookmarks};
use std::fs::{File};
use std::io::{BufReader, ErrorKind};
use dir::{mkdirp};

// TODO implement display
pub struct Store {
    db: PathBuf,
    // bookmarks: Cell<Bookmarks>, // Maybe?
}

impl Store {
    pub fn new(directory: PathBuf) -> Store {
        let mut db = PathBuf::from(directory);
                db.push("db.bin");

        println!("db {:?}", db);
        return Store{ db: db };
    }

    pub fn all(&self) -> ToResult<Bookmarks> {
        let collection = match self.read() {
            Ok(value) => value,
            Err(err) => panic!(err),
        };

        return Ok(collection);
    }

    pub fn put(&self, name: String, directory: PathBuf) -> ToResult<()> {
        println!("PUT: {:?} {:?}", name, directory);

        let mut collection = match self.all() {
            Ok(value) => value,
            Err(err) => panic!(err),
        };

        let key = name.clone();
        let value = Bookmark::new(name, directory);
        collection.insert(key, value);

        println!("collection {:?}", collection);

        if let Err(err) = self.write(collection) {
            print!("Error on put");
        }

        return Ok(());
    }

    fn create(&self) -> ToResult<()> {
        return Ok(());
    }

    fn read(&self) -> ToResult<Bookmarks> {
        let file = match bootstrap(&self.db) {
            Ok(value) => value,
            Err(err) => return Err(err),
        };

        // let mut reader = BufReader::new(file);

        let bookmarks = Bookmarks::new();

        return Ok(bookmarks);
    }

    fn write(&self, bookmarks: Bookmarks) -> ToResult<()> {
        return Ok(());
    }
}

fn bootstrap(db: &PathBuf) -> ToResult<File> {
    let directory = match db.parent() {
        Some(value) => value,
        None => panic!("db cannot be a idrectory."),
    };

    if let Err(err) = mkdirp(directory) {
        return Err(err);
    }

    match File::create(&db) {
        Ok(_) => {},
        Err(ref err) if err.kind() == ErrorKind::AlreadyExists => {},
        Err(err) => return Err(ToError::Io(err)),
    };

    let file = match File::open(db) {
        Ok(value) => value,
        Err(err) => return Err(ToError::Io(err)),
    };

    return Ok(file);
}

fn open(file: PathBuf) -> ToResult<File> {
    match File::open(file) {
        Ok(value) => return Ok(value),
        Err(err) => return Err(ToError::Io(err)),
    }
}
<commit_msg>Renames<commit_after>use std::path::{PathBuf, Components};
use types::{ToResult, ToError, Bookmark, Bookmarks};
use std::fs::{File};
use std::io::{BufReader, ErrorKind};
use dir::{mkdirp};

// TODO implement display
pub struct Store {
    location: PathBuf,
    // bookmarks: Cell<Bookmarks>, // Maybe?
}

impl Store {
    pub fn new(location: PathBuf) -> Store {
        let mut location = PathBuf::from(location);
                location.push("db.bin");

        println!("location {:?}", location);
        return Store{ location: location };
    }

    pub fn all(&self) -> ToResult<Bookmarks> {
        let collection = match self.read() {
            Ok(value) => value,
            Err(err) => panic!(err),
        };

        return Ok(collection);
    }

    pub fn put(&self, name: String, directory: PathBuf) -> ToResult<()> {
        println!("PUT: {:?} {:?}", name, directory);

        let mut collection = match self.all() {
            Ok(value) => value,
            Err(err) => panic!(err),
        };

        let key = name.clone();
        let value = Bookmark::new(name, directory);
        collection.insert(key, value);

        println!("collection {:?}", collection);

        if let Err(err) = self.write(collection) {
            print!("Error on put");
        }

        return Ok(());
    }

    fn read(&self) -> ToResult<Bookmarks> {
        let file = match open(&self.location) {
            Ok(value) => value,
            Err(err) => return Err(err),
        };

        // let mut reader = BufReader::new(file);

        let bookmarks = Bookmarks::new();

        return Ok(bookmarks);
    }

    fn write(&self, bookmarks: Bookmarks) -> ToResult<()> {
        return Ok(());
    }
}

fn open(location: &PathBuf) -> ToResult<File> {
    let directory = match location.parent() {
        Some(value) => value,
        None => panic!("location cannot be a idrectory."),
    };

    if let Err(err) = mkdirp(directory) {
        return Err(err);
    }

    match File::create(&location) {
        Ok(_) => {},
        Err(ref err) if err.kind() == ErrorKind::AlreadyExists => {},
        Err(err) => return Err(ToError::Io(err)),
    };

    let file = match File::open(location) {
        Ok(value) => value,
        Err(err) => return Err(ToError::Io(err)),
    };

    return Ok(file);
}
<|endoftext|>