<commit_before>use crate::{texture, Document};

pub use json::material::AlphaMode;

lazy_static! {
    static ref DEFAULT_MATERIAL: json::material::Material = Default::default();
}

/// The material appearance of a primitive.
pub struct Material<'a> {
    /// The parent `Document` struct.
    document: &'a Document,

    /// The corresponding JSON index - `None` when the default material.
    index: Option<usize>,

    /// The corresponding JSON struct.
    json: &'a json::material::Material,
}

impl<'a> Material<'a> {
    /// Constructs a `Material`.
    pub(crate) fn new(
        document: &'a Document,
        index: usize,
        json: &'a json::material::Material,
    ) -> Self {
        Self {
            document: document,
            index: Some(index),
            json: json,
        }
    }

    /// Constructs the default `Material`.
    pub(crate) fn default(document: &'a Document) -> Self {
        Self {
            document: document,
            index: None,
            json: &DEFAULT_MATERIAL,
        }
    }

    /// Returns the internal JSON index if this `Material` was explicity defined.
    ///
    /// This function returns `None` if the `Material` is the default material.
    pub fn index(&self) -> Option<usize> {
        self.index
    }

    ///  The alpha cutoff value of the material.
    pub fn alpha_cutoff(&self) -> f32 {
        self.json.alpha_cutoff.0
    }

    /// The alpha rendering mode of the material.  The material's alpha rendering
    /// mode enumeration specifying the interpretation of the alpha value of the main
    /// factor and texture.
    ///
    /// * In `Opaque` mode (default) the alpha value is ignored
    ///   and the rendered output is fully opaque.
    /// * In `Mask` mode, the rendered
    ///   output is either fully opaque or fully transparent depending on the alpha
    ///   value and the specified alpha cutoff value.
    /// * In `Blend` mode, the alpha value is used to composite the source and
    ///   destination areas and the rendered output is combined with the background
    ///   using the normal painting operation (i.e. the Porter and Duff over
    ///   operator).
    pub fn alpha_mode(&self) -> AlphaMode {
        self.json.alpha_mode.unwrap()
    }

    /// Specifies whether the material is double-sided.
    ///
    /// * When this value is false, back-face culling is enabled.
    /// * When this value is true, back-face culling is disabled and double sided
    ///   lighting is enabled.  The back-face must have its normals reversed before
    ///   the lighting equation is evaluated.
    pub fn double_sided(&self) -> bool {
        self.json.double_sided
    }

    /// Optional user-defined name for this object.
    #[cfg(feature = "names")]
    pub fn name(&self) -> Option<&str> {
        self.json.name.as_ref().map(String::as_str)
    }

    /// Parameter values that define the metallic-roughness material model from
    /// Physically-Based Rendering (PBR) methodology.
    pub fn pbr_metallic_roughness(&self) -> PbrMetallicRoughness<'a> {
        PbrMetallicRoughness::new(self.document, &self.json.pbr_metallic_roughness)
    }

    /// Parameter values that define the specular-glossiness material model from
    /// Physically-Based Rendering (PBR) methodology.
    #[cfg(feature = "KHR_materials_pbrSpecularGlossiness")]
    pub fn pbr_specular_glossiness(&self) -> Option<PbrSpecularGlossiness<'a>> {
        self.json.extensions
            .as_ref()?
            .pbr_specular_glossiness.as_ref()
            .map(|x| PbrSpecularGlossiness::new(self.document, x))
    }

    /// A tangent space normal map.
    ///
    /// The texture contains RGB components in linear space. Each texel represents
    /// the XYZ components of a normal vector in tangent space.
    ///
    /// * Red [0 to 255] maps to X [-1 to 1].
    /// * Green [0 to 255] maps to Y [-1 to 1].
    /// * Blue [128 to 255] maps to Z [1/255 to 1].
    ///
    /// The normal vectors use OpenGL conventions where +X is right, +Y is up, and
    /// +Z points toward the viewer.
    pub fn normal_texture(&self) -> Option<NormalTexture<'a>> {
        self.json.normal_texture.as_ref().map(|json| {
            let texture = self.document.textures().nth(json.index.value()).unwrap();
            NormalTexture::new(texture, json)
        })
    }

    /// The occlusion map texture.
    ///
    /// The occlusion values are sampled from the R channel. Higher values indicate
    /// areas that should receive full indirect lighting and lower values indicate
    /// no indirect lighting. These values are linear.
    ///
    /// If other channels are present (GBA), they are ignored for occlusion
    /// calculations.
    pub fn occlusion_texture(&self) -> Option<OcclusionTexture<'a>> {
        self.json.occlusion_texture.as_ref().map(|json| {
            let texture = self.document.textures().nth(json.index.value()).unwrap();
            OcclusionTexture::new(texture, json)
        })
    }

    /// The emissive map texture.
    ///
    /// The emissive map controls the color and intensity of the light being
    /// emitted by the material.
    ///
    /// This texture contains RGB components in sRGB color space. If a fourth
    /// component (A) is present, it is ignored.
    pub fn emissive_texture(&self) -> Option<texture::Info<'a>> {
        self.json.emissive_texture.as_ref().map(|json| {
            let texture = self.document.textures().nth(json.index.value()).unwrap();
            texture::Info::new(texture, json)
        })
    }

    /// The emissive color of the material.
    ///
    /// The default value is `[0.0, 0.0, 0.0]`.
    pub fn emissive_factor(&self) -> [f32; 3] {
        self.json.emissive_factor.0
    }

    /// Optional application specific data.
    pub fn extras(&self) -> &json::Extras {
        &self.json.extras
    }
}

/// A set of parameter values that are used to define the metallic-roughness
/// material model from Physically-Based Rendering (PBR) methodology.
pub struct PbrMetallicRoughness<'a> {
    /// The parent `Document` struct.
    document: &'a Document,

    /// The corresponding JSON struct.
    json: &'a json::material::PbrMetallicRoughness,
}

impl<'a> PbrMetallicRoughness<'a> {
    /// Constructs `PbrMetallicRoughness`.
    pub(crate) fn new(
        document: &'a Document,
        json: &'a json::material::PbrMetallicRoughness,
    ) -> Self {
        Self {
            document: document,
            json: json,
        }
    }

    /// Returns the material's base color factor.
    ///
    /// The default value is `[1.0, 1.0, 1.0, 1.0]`.
    pub fn base_color_factor(&self) -> [f32; 4] {
        self.json.base_color_factor.0
    }

    /// Returns the base color texture. The texture contains RGB(A) components
    /// in sRGB color space.
    pub fn base_color_texture(&self) -> Option<texture::Info<'a>> {
        self.json.base_color_texture.as_ref().map(|json| {
            let texture = self.document.textures().nth(json.index.value()).unwrap();
            texture::Info::new(texture, json)
        })
    }

    /// Returns the metalness factor of the material.
    ///
    /// The default value is `1.0`.
    pub fn metallic_factor(&self) -> f32 {
        self.json.metallic_factor.0
    }

    /// Returns the roughness factor of the material.
    ///
    /// * A value of 1.0 means the material is completely rough.
    /// * A value of 0.0 means the material is completely smooth.
    ///
    /// The default value is `1.0`.
    pub fn roughness_factor(&self) -> f32 {
        self.json.roughness_factor.0
    }

    /// The metallic-roughness texture.
    ///
    /// The metalness values are sampled from the B channel.
    /// The roughness values are sampled from the G channel.
    /// These values are linear. If other channels are present (R or A),
    /// they are ignored for metallic-roughness calculations.
    pub fn metallic_roughness_texture(&self) -> Option<texture::Info<'a>> {
        self.json.metallic_roughness_texture.as_ref().map(|json| {
            let texture = self.document.textures().nth(json.index.value()).unwrap();
            texture::Info::new(texture, json)
        })
    }

    /// Optional application specific data.
    pub fn extras(&self) -> &json::Extras {
        &self.json.extras
    }
}

/// A set of parameter values that are used to define the specular-glossiness
/// material model from Physically-Based Rendering (PBR) methodology.
#[cfg(feature = "KHR_materials_pbrSpecularGlossiness")]
pub struct PbrSpecularGlossiness<'a> {
    /// The parent `Document` struct.
    document: &'a Document,

    /// The corresponding JSON struct.
    json: &'a json::extensions::material::PbrSpecularGlossiness,
}

#[cfg(feature = "KHR_materials_pbrSpecularGlossiness")]
impl<'a> PbrSpecularGlossiness<'a> {
    /// Constructs `PbrSpecularGlossiness`.
    pub(crate) fn new(
        document: &'a Document,
        json: &'a json::extensions::material::PbrSpecularGlossiness,
    ) -> Self {
        Self {
            document: document,
            json: json,
        }
    }

    /// Returns the material's base color factor.
    ///
    /// The default value is `[1.0, 1.0, 1.0, 1.0]`.
    pub fn diffuse_factor(&self) -> [f32; 4] {
        self.json.diffuse_factor.0
    }

    /// Returns the base color texture.
    pub fn diffuse_texture(&self) -> Option<texture::Info<'a>> {
        self.json.diffuse_texture.as_ref().map(|json| {
            let texture = self.document.textures().nth(json.index.value()).unwrap();
            texture::Info::new(texture, json)
        })
    }

    /// Returns the specular factor of the material.
    ///
    /// The default value is `[1.0, 1.0, 1.0]`.
    pub fn specular_factor(&self) -> [f32; 3] {
        self.json.specular_factor.0
    }

    /// Returns the glossiness factor of the material.
    ///
    /// A value of 1.0 means the material has full glossiness or is perfectly
    /// smooth. A value of 0.0 means the material has no glossiness or is
    /// completely rough. This value is linear.
    ///
    /// The default value is `1.0`.
    pub fn glossiness_factor(&self) -> f32 {
        self.json.glossiness_factor.0
    }

    /// The specular-glossiness texture.
    ///
    /// A RGBA texture, containing the specular color of the material (RGB
    /// components) and its glossiness (A component). The color values are in
    /// sRGB space.
    pub fn specular_glossiness_texture(&self) -> Option<texture::Info<'a>> {
        self.json.specular_glossiness_texture.as_ref().map(|json| {
            let texture = self.document.textures().nth(json.index.value()).unwrap();
            texture::Info::new(texture, json)
        })
    }

    /// Optional application specific data.
    pub fn extras(&self) -> &json::Extras {
        &self.json.extras
    }
}

/// Defines the normal texture of a material.
pub struct NormalTexture<'a> {
    /// The parent `Texture` struct.
    texture: texture::Texture<'a>,

    /// The corresponding JSON struct.
    json: &'a json::material::NormalTexture,
}

impl<'a> NormalTexture<'a> {
    /// Constructs a `NormalTexture`.
    pub(crate) fn new(
        texture: texture::Texture<'a>,
        json: &'a json::material::NormalTexture,
    ) -> Self {
        Self {
            texture: texture,
            json: json,
        }
    }

    /// Returns the scalar multiplier applied to each normal vector of the texture.
    pub fn scale(&self) -> f32 {
        self.json.scale
    }

    /// The set index of the texture's `TEXCOORD` attribute.
    pub fn tex_coord(&self) -> u32 {
        self.json.tex_coord
    }

    /// Returns the referenced texture.
    pub fn texture(&self) -> texture::Texture<'a> {
        self.texture.clone()
    }

    /// Optional application specific data.
    pub fn extras(&self) -> &json::Extras {
        &self.json.extras
    }
}

/// Defines the occlusion texture of a material.
pub struct OcclusionTexture<'a> {
    /// The parent `Texture` struct.
    texture: texture::Texture<'a>,

    /// The corresponding JSON struct.
    json: &'a json::material::OcclusionTexture,
}

impl<'a> OcclusionTexture<'a> {
    /// Constructs a `OcclusionTexture`.
    pub(crate) fn new(
        texture: texture::Texture<'a>,
        json: &'a json::material::OcclusionTexture,
    ) -> Self {
        Self {
            texture: texture,
            json: json,
        }
    }

    /// Returns the scalar multiplier controlling the amount of occlusion applied.
    pub fn strength(&self) -> f32 {
        self.json.strength.0
    }

    /// Returns the set index of the texture's `TEXCOORD` attribute.
    pub fn tex_coord(&self) -> u32 {
        self.json.tex_coord
    }

    /// Returns the referenced texture.
    pub fn texture(&self) -> texture::Texture<'a> {
        self.texture.clone()
    }

    /// Optional application specific data.
    pub fn extras(&self) -> &json::Extras {
        &self.json.extras
    }
}

impl<'a> AsRef<texture::Texture<'a>> for NormalTexture<'a> {
    fn as_ref(&self) -> &texture::Texture<'a> {
        &self.texture
    }
}

impl<'a> AsRef<texture::Texture<'a>> for OcclusionTexture<'a> {
    fn as_ref(&self) -> &texture::Texture<'a> {
        &self.texture
    }
}
<commit_msg>Give gltf::material::Material Clone & Debug derives.<commit_after>use crate::{texture, Document};

pub use json::material::AlphaMode;

lazy_static! {
    static ref DEFAULT_MATERIAL: json::material::Material = Default::default();
}

/// The material appearance of a primitive.
#[derive(Clone, Debug)]
pub struct Material<'a> {
    /// The parent `Document` struct.
    document: &'a Document,

    /// The corresponding JSON index - `None` when the default material.
    index: Option<usize>,

    /// The corresponding JSON struct.
    json: &'a json::material::Material,
}

impl<'a> Material<'a> {
    /// Constructs a `Material`.
    pub(crate) fn new(
        document: &'a Document,
        index: usize,
        json: &'a json::material::Material,
    ) -> Self {
        Self {
            document: document,
            index: Some(index),
            json: json,
        }
    }

    /// Constructs the default `Material`.
    pub(crate) fn default(document: &'a Document) -> Self {
        Self {
            document: document,
            index: None,
            json: &DEFAULT_MATERIAL,
        }
    }

    /// Returns the internal JSON index if this `Material` was explicity defined.
    ///
    /// This function returns `None` if the `Material` is the default material.
    pub fn index(&self) -> Option<usize> {
        self.index
    }

    ///  The alpha cutoff value of the material.
    pub fn alpha_cutoff(&self) -> f32 {
        self.json.alpha_cutoff.0
    }

    /// The alpha rendering mode of the material.  The material's alpha rendering
    /// mode enumeration specifying the interpretation of the alpha value of the main
    /// factor and texture.
    ///
    /// * In `Opaque` mode (default) the alpha value is ignored
    ///   and the rendered output is fully opaque.
    /// * In `Mask` mode, the rendered
    ///   output is either fully opaque or fully transparent depending on the alpha
    ///   value and the specified alpha cutoff value.
    /// * In `Blend` mode, the alpha value is used to composite the source and
    ///   destination areas and the rendered output is combined with the background
    ///   using the normal painting operation (i.e. the Porter and Duff over
    ///   operator).
    pub fn alpha_mode(&self) -> AlphaMode {
        self.json.alpha_mode.unwrap()
    }

    /// Specifies whether the material is double-sided.
    ///
    /// * When this value is false, back-face culling is enabled.
    /// * When this value is true, back-face culling is disabled and double sided
    ///   lighting is enabled.  The back-face must have its normals reversed before
    ///   the lighting equation is evaluated.
    pub fn double_sided(&self) -> bool {
        self.json.double_sided
    }

    /// Optional user-defined name for this object.
    #[cfg(feature = "names")]
    pub fn name(&self) -> Option<&str> {
        self.json.name.as_ref().map(String::as_str)
    }

    /// Parameter values that define the metallic-roughness material model from
    /// Physically-Based Rendering (PBR) methodology.
    pub fn pbr_metallic_roughness(&self) -> PbrMetallicRoughness<'a> {
        PbrMetallicRoughness::new(self.document, &self.json.pbr_metallic_roughness)
    }

    /// Parameter values that define the specular-glossiness material model from
    /// Physically-Based Rendering (PBR) methodology.
    #[cfg(feature = "KHR_materials_pbrSpecularGlossiness")]
    pub fn pbr_specular_glossiness(&self) -> Option<PbrSpecularGlossiness<'a>> {
        self.json.extensions
            .as_ref()?
            .pbr_specular_glossiness.as_ref()
            .map(|x| PbrSpecularGlossiness::new(self.document, x))
    }

    /// A tangent space normal map.
    ///
    /// The texture contains RGB components in linear space. Each texel represents
    /// the XYZ components of a normal vector in tangent space.
    ///
    /// * Red [0 to 255] maps to X [-1 to 1].
    /// * Green [0 to 255] maps to Y [-1 to 1].
    /// * Blue [128 to 255] maps to Z [1/255 to 1].
    ///
    /// The normal vectors use OpenGL conventions where +X is right, +Y is up, and
    /// +Z points toward the viewer.
    pub fn normal_texture(&self) -> Option<NormalTexture<'a>> {
        self.json.normal_texture.as_ref().map(|json| {
            let texture = self.document.textures().nth(json.index.value()).unwrap();
            NormalTexture::new(texture, json)
        })
    }

    /// The occlusion map texture.
    ///
    /// The occlusion values are sampled from the R channel. Higher values indicate
    /// areas that should receive full indirect lighting and lower values indicate
    /// no indirect lighting. These values are linear.
    ///
    /// If other channels are present (GBA), they are ignored for occlusion
    /// calculations.
    pub fn occlusion_texture(&self) -> Option<OcclusionTexture<'a>> {
        self.json.occlusion_texture.as_ref().map(|json| {
            let texture = self.document.textures().nth(json.index.value()).unwrap();
            OcclusionTexture::new(texture, json)
        })
    }

    /// The emissive map texture.
    ///
    /// The emissive map controls the color and intensity of the light being
    /// emitted by the material.
    ///
    /// This texture contains RGB components in sRGB color space. If a fourth
    /// component (A) is present, it is ignored.
    pub fn emissive_texture(&self) -> Option<texture::Info<'a>> {
        self.json.emissive_texture.as_ref().map(|json| {
            let texture = self.document.textures().nth(json.index.value()).unwrap();
            texture::Info::new(texture, json)
        })
    }

    /// The emissive color of the material.
    ///
    /// The default value is `[0.0, 0.0, 0.0]`.
    pub fn emissive_factor(&self) -> [f32; 3] {
        self.json.emissive_factor.0
    }

    /// Optional application specific data.
    pub fn extras(&self) -> &json::Extras {
        &self.json.extras
    }
}

/// A set of parameter values that are used to define the metallic-roughness
/// material model from Physically-Based Rendering (PBR) methodology.
pub struct PbrMetallicRoughness<'a> {
    /// The parent `Document` struct.
    document: &'a Document,

    /// The corresponding JSON struct.
    json: &'a json::material::PbrMetallicRoughness,
}

impl<'a> PbrMetallicRoughness<'a> {
    /// Constructs `PbrMetallicRoughness`.
    pub(crate) fn new(
        document: &'a Document,
        json: &'a json::material::PbrMetallicRoughness,
    ) -> Self {
        Self {
            document: document,
            json: json,
        }
    }

    /// Returns the material's base color factor.
    ///
    /// The default value is `[1.0, 1.0, 1.0, 1.0]`.
    pub fn base_color_factor(&self) -> [f32; 4] {
        self.json.base_color_factor.0
    }

    /// Returns the base color texture. The texture contains RGB(A) components
    /// in sRGB color space.
    pub fn base_color_texture(&self) -> Option<texture::Info<'a>> {
        self.json.base_color_texture.as_ref().map(|json| {
            let texture = self.document.textures().nth(json.index.value()).unwrap();
            texture::Info::new(texture, json)
        })
    }

    /// Returns the metalness factor of the material.
    ///
    /// The default value is `1.0`.
    pub fn metallic_factor(&self) -> f32 {
        self.json.metallic_factor.0
    }

    /// Returns the roughness factor of the material.
    ///
    /// * A value of 1.0 means the material is completely rough.
    /// * A value of 0.0 means the material is completely smooth.
    ///
    /// The default value is `1.0`.
    pub fn roughness_factor(&self) -> f32 {
        self.json.roughness_factor.0
    }

    /// The metallic-roughness texture.
    ///
    /// The metalness values are sampled from the B channel.
    /// The roughness values are sampled from the G channel.
    /// These values are linear. If other channels are present (R or A),
    /// they are ignored for metallic-roughness calculations.
    pub fn metallic_roughness_texture(&self) -> Option<texture::Info<'a>> {
        self.json.metallic_roughness_texture.as_ref().map(|json| {
            let texture = self.document.textures().nth(json.index.value()).unwrap();
            texture::Info::new(texture, json)
        })
    }

    /// Optional application specific data.
    pub fn extras(&self) -> &json::Extras {
        &self.json.extras
    }
}

/// A set of parameter values that are used to define the specular-glossiness
/// material model from Physically-Based Rendering (PBR) methodology.
#[cfg(feature = "KHR_materials_pbrSpecularGlossiness")]
pub struct PbrSpecularGlossiness<'a> {
    /// The parent `Document` struct.
    document: &'a Document,

    /// The corresponding JSON struct.
    json: &'a json::extensions::material::PbrSpecularGlossiness,
}

#[cfg(feature = "KHR_materials_pbrSpecularGlossiness")]
impl<'a> PbrSpecularGlossiness<'a> {
    /// Constructs `PbrSpecularGlossiness`.
    pub(crate) fn new(
        document: &'a Document,
        json: &'a json::extensions::material::PbrSpecularGlossiness,
    ) -> Self {
        Self {
            document: document,
            json: json,
        }
    }

    /// Returns the material's base color factor.
    ///
    /// The default value is `[1.0, 1.0, 1.0, 1.0]`.
    pub fn diffuse_factor(&self) -> [f32; 4] {
        self.json.diffuse_factor.0
    }

    /// Returns the base color texture.
    pub fn diffuse_texture(&self) -> Option<texture::Info<'a>> {
        self.json.diffuse_texture.as_ref().map(|json| {
            let texture = self.document.textures().nth(json.index.value()).unwrap();
            texture::Info::new(texture, json)
        })
    }

    /// Returns the specular factor of the material.
    ///
    /// The default value is `[1.0, 1.0, 1.0]`.
    pub fn specular_factor(&self) -> [f32; 3] {
        self.json.specular_factor.0
    }

    /// Returns the glossiness factor of the material.
    ///
    /// A value of 1.0 means the material has full glossiness or is perfectly
    /// smooth. A value of 0.0 means the material has no glossiness or is
    /// completely rough. This value is linear.
    ///
    /// The default value is `1.0`.
    pub fn glossiness_factor(&self) -> f32 {
        self.json.glossiness_factor.0
    }

    /// The specular-glossiness texture.
    ///
    /// A RGBA texture, containing the specular color of the material (RGB
    /// components) and its glossiness (A component). The color values are in
    /// sRGB space.
    pub fn specular_glossiness_texture(&self) -> Option<texture::Info<'a>> {
        self.json.specular_glossiness_texture.as_ref().map(|json| {
            let texture = self.document.textures().nth(json.index.value()).unwrap();
            texture::Info::new(texture, json)
        })
    }

    /// Optional application specific data.
    pub fn extras(&self) -> &json::Extras {
        &self.json.extras
    }
}

/// Defines the normal texture of a material.
pub struct NormalTexture<'a> {
    /// The parent `Texture` struct.
    texture: texture::Texture<'a>,

    /// The corresponding JSON struct.
    json: &'a json::material::NormalTexture,
}

impl<'a> NormalTexture<'a> {
    /// Constructs a `NormalTexture`.
    pub(crate) fn new(
        texture: texture::Texture<'a>,
        json: &'a json::material::NormalTexture,
    ) -> Self {
        Self {
            texture: texture,
            json: json,
        }
    }

    /// Returns the scalar multiplier applied to each normal vector of the texture.
    pub fn scale(&self) -> f32 {
        self.json.scale
    }

    /// The set index of the texture's `TEXCOORD` attribute.
    pub fn tex_coord(&self) -> u32 {
        self.json.tex_coord
    }

    /// Returns the referenced texture.
    pub fn texture(&self) -> texture::Texture<'a> {
        self.texture.clone()
    }

    /// Optional application specific data.
    pub fn extras(&self) -> &json::Extras {
        &self.json.extras
    }
}

/// Defines the occlusion texture of a material.
pub struct OcclusionTexture<'a> {
    /// The parent `Texture` struct.
    texture: texture::Texture<'a>,

    /// The corresponding JSON struct.
    json: &'a json::material::OcclusionTexture,
}

impl<'a> OcclusionTexture<'a> {
    /// Constructs a `OcclusionTexture`.
    pub(crate) fn new(
        texture: texture::Texture<'a>,
        json: &'a json::material::OcclusionTexture,
    ) -> Self {
        Self {
            texture: texture,
            json: json,
        }
    }

    /// Returns the scalar multiplier controlling the amount of occlusion applied.
    pub fn strength(&self) -> f32 {
        self.json.strength.0
    }

    /// Returns the set index of the texture's `TEXCOORD` attribute.
    pub fn tex_coord(&self) -> u32 {
        self.json.tex_coord
    }

    /// Returns the referenced texture.
    pub fn texture(&self) -> texture::Texture<'a> {
        self.texture.clone()
    }

    /// Optional application specific data.
    pub fn extras(&self) -> &json::Extras {
        &self.json.extras
    }
}

impl<'a> AsRef<texture::Texture<'a>> for NormalTexture<'a> {
    fn as_ref(&self) -> &texture::Texture<'a> {
        &self.texture
    }
}

impl<'a> AsRef<texture::Texture<'a>> for OcclusionTexture<'a> {
    fn as_ref(&self) -> &texture::Texture<'a> {
        &self.texture
    }
}
<|endoftext|>


<commit_before>/// A structure of values
pub struct Matrix<T> {
    /// 2 dimensional array of rows and columns
    dim: [usize; 2],
    /// `Vector` of values in the `Matrix`
    buffer: Vec<T>,
}

impl <T> Matrix<T> {
    /// Returns a new `Matrix`
    ///
    /// # Arguments
    ///
    /// - `dimensions` - 2 dimensional array of rows and columns
    /// - `buffer` - `Vector` of values
    ///
    /// # Example
    ///
    /// ```
    /// let matrix = ktensor::math::Matrix::new([2, 3], vec![0, 1, 2, 3, 4, 5]);
    /// ```
    pub fn new(dimensions: [usize; 2], buffer: Vec<T>) -> Matrix<T> {
        Matrix {
            dim: dimensions,
            buffer: buffer,
        }
    }
}
<commit_msg>implement getting values from vector buffer<commit_after>/// A pair of coordinates
pub struct Vec2(pub usize, pub usize);

/// A structure of values
pub struct Matrix<T> {
    /// 2 dimensional array of rows and columns
    dim: Vec2,
    /// `Vector` of values in the `Matrix`
    buffer: Vec<T>,
}

impl <T> Matrix<T> {
    /// Returns a new `Matrix`
    ///
    /// # Arguments
    ///
    /// - `dimensions` - 2 dimensional array of rows and columns
    /// - `buffer` - `Vector` of values
    ///
    /// # Example
    ///
    /// ```
    /// let matrix = ktensor::math::Matrix::new(ktensor::math::Vec2(2, 3), vec![0, 1, 2, 3, 4, 5]);
    /// ```
    pub fn new(dimensions: Vec2, buffer: Vec<T>) -> Matrix<T> {
        Matrix {
            dim: dimensions,
            buffer: buffer,
        }
    }

    /// Returns reference to value at `Vec2`
    ///
    /// # Arguments
    ///
    /// - `Vec2` - coordinates of the value in the `Matrix`
    ///
    /// # Example
    ///
    /// ```
    /// let matrix = ktensor::math::Matrix::new(ktensor::math::Vec2(2, 3), vec![0, 1, 2, 3, 4, 5]);
    /// assert_eq!(*matrix.get(ktensor::math::Vec2(1, 2)), 5);
    /// ```
    pub fn get(&self, Vec2(x, y): Vec2) -> &T {
        &self.buffer[x * self.dim.1 + y]
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::fs;
use std::net::{SocketAddr, TcpListener};
use std::path::PathBuf;
use std::process::{Command, Child, Stdio};
use std::thread;
use std::io::{BufRead, BufReader};

use tempdir::TempDir;

/// A process handle that will kill the process when it goes out of scope.
pub struct ProcessHandle(Child);
impl Drop for ProcessHandle {
    fn drop(&mut self) {
        let _ = self.0.kill();
    }
}

#[allow(dead_code)]
pub struct MiniCluster {
    dir: TempDir,
    master_addrs: Vec<SocketAddr>,
    master_procs: Vec<ProcessHandle>,
    tserver_procs: Vec<ProcessHandle>,
}

impl MiniCluster {
    pub fn new(conf: &MiniClusterConfig) -> MiniCluster {
        let kudu_home = env::var("KUDU_HOME").expect("KUDU_HOME environment variable must be set");
        let mut bin_dir = PathBuf::from(&kudu_home);
        bin_dir.push("build");
        bin_dir.push("latest");
        bin_dir.push("bin");

        let master_bin = bin_dir.join("kudu-master");
        let tserver_bin = bin_dir.join("kudu-tserver");
        let dir = TempDir::new("kudu-rs-mini-cluster").expect("unable to create temp dir");

        let mut master_procs = Vec::with_capacity(conf.num_masters as usize);

        let addrs = get_unbound_addresses((conf.num_masters + conf.num_tservers) as usize);
        let (master_addrs, tserver_addrs) = addrs.split_at(conf.num_masters as usize);

        let masters = master_addrs.iter().map(ToString::to_string).collect::<Vec<_>>().join(",");

        for (i, addr) in master_addrs.iter().enumerate() {
            let path = dir.path().join(format!("master-{}", i));
            let logs = dir.path().join(format!("master-{}-logs", i));
            fs::create_dir(&logs).expect("unable to create master log directory");
            let mut command = Command::new(&master_bin);
            command.arg(format!("--fs_wal_dir={}", path.to_str().unwrap()))
                   .arg(format!("--fs_data_dirs={}", path.to_str().unwrap()))
                   .arg(format!("--log_dir={}", logs.to_str().unwrap()))
                   .arg(format!("--rpc_bind_addresses={}", addr))
                   .arg("--webserver_port=0")
                   .arg("--logtostderr");

            command.stderr(Stdio::piped());
            conf.configure(&mut command);

            if conf.num_masters > 1 {
                command.arg(format!("--master_addresses={}", masters));
            }

            let mut process = command.spawn().expect("unable to start master");
            let stderr = process.stderr.take().unwrap();
            let port = addr.port();

            thread::spawn(move || {
                let stderr = BufReader::new(stderr);
                for line in stderr.lines() {
                    debug!("(master:{}): {}", port, line.unwrap());
                }

            });

            master_procs.push(ProcessHandle(process));
        }

        let mut tserver_procs = Vec::with_capacity(conf.num_tservers as usize);
        for (i, addr) in tserver_addrs.iter().enumerate() {
            let path = dir.path().join(format!("tserver-{}", i));
            let logs = dir.path().join(format!("tserver-{}-logs", i));
            fs::create_dir(&logs).expect("unable to create tserver log directory");

            let mut command = Command::new(&tserver_bin);
            command.arg(format!("--fs_wal_dir={}", path.to_str().unwrap()))
                   .arg(format!("--fs_data_dirs={}", path.to_str().unwrap()))
                   .arg(format!("--log_dir={}", logs.to_str().unwrap()))
                   .arg(format!("--rpc_bind_addresses={}", addr))
                   .arg("--webserver_port=0")
                   .arg(format!("--tserver_master_addrs={}", masters))
                   .arg("--logtostderr");

            command.stderr(Stdio::piped());
            conf.configure(&mut command);

            let mut process = command.spawn().expect("unable to start master");
            let stderr = process.stderr.take().unwrap();
            let port = addr.port();

            thread::spawn(move || {
                let stderr = BufReader::new(stderr);
                for line in stderr.lines() {
                    debug!("(tserver:{}): {}", port, line.unwrap());
                }
            });

            tserver_procs.push(ProcessHandle(process));
        }

        MiniCluster {
            dir: dir,
            master_addrs: master_addrs.to_owned(),
            master_procs: master_procs,
            tserver_procs: tserver_procs,
        }
    }

    #[allow(dead_code)]
    pub fn master_addrs(&self) -> &[SocketAddr] {
        &self.master_addrs
    }
}

impl Default for MiniCluster {
    fn default() -> MiniCluster {
        MiniCluster::new(&MiniClusterConfig::default())
    }
}

pub fn get_unbound_address() -> SocketAddr {
    TcpListener::bind("127.0.0.1:0").unwrap().local_addr().unwrap()
}

/// Attempts to get local unbound socket addresses for testing.
fn get_unbound_addresses(count: usize) -> Vec<SocketAddr> {
    let mut listeners = Vec::with_capacity(count);
    let mut addrs = Vec::with_capacity(count);

    for _ in 0..count {
        let listener = TcpListener::bind("127.0.0.1:0").unwrap();
        let addr = listener.local_addr().unwrap();
        listeners.push(listener);
        addrs.push(addr);
    }
    addrs
}

/// Mini cluster configuration options. Unless otherwise specified, the defaults match the master
/// and tablet server defaults.
pub struct MiniClusterConfig {
    /// The number of masters in the mini cluster. Default: 1.
    num_masters: i32,
    /// The number of tablet servers in the mini cluster. Default: 1.
    num_tservers: i32,

    // Logging Options

    /// Minimum logging level.
    min_log_level: Option<LogLevel>,

    /// Show all `VLOG(m)` messages for `m < this`. Overridden by `vmodule`.
    vlog: Option<i32>,

    /// Per-module `VLOG` level. List of (<module name>, <log level>) pairs. <module name> is a
    /// glob pattern, matched against the filename base (that is, name ignoring
    /// `.cc`/`.h`/`-inl.h`). <log level> overrides any value given by `vlog`.
    vmodule: Vec<(String, i32)>,

    /// If enabled, dump traces of all RPC negotiations to the log at `INFO` level.
    log_rpc_negotiation_trace: Option<bool>,

    /// If enabled, dump traces of all RPCs to the log at `INFO` level.
    log_rpc_trace: Option<bool>,

    // Latency Injection

    /// Amount of latency in milliseconds to inject into the RPC negotiation process.
    rpc_negotiation_delay: Option<i32>,

    /// Amount of latency in milliseconds to inject in GetTableLocations RPCs.
    get_table_locations_delay: Option<i32>,

    // Miscellaneous

    /// Whether to enable failure detection of tablet leaders. If enabled, attempts will be made to
    /// elect a follower as a new leader when the leader is detected to have failed.
    leader_failure_detection: Option<bool>,

    /// Whether to enable fsync() of data blocks, metadata, and their parent directories. Disabling
    /// this flag may cause data loss in the event of a system crash. Default: false.
    data_block_fsync: bool,
}

impl MiniClusterConfig {

    pub fn num_masters(&mut self, num_masters: i32) -> &mut MiniClusterConfig {
        self.num_masters = num_masters;
        self
    }

    pub fn num_tservers(&mut self, num_tservers: i32) -> &mut MiniClusterConfig {
        self.num_tservers = num_tservers;
        self
    }

    pub fn min_log_level(&mut self, log_level: LogLevel) -> &mut MiniClusterConfig {
        self.min_log_level = Some(log_level);
        self
    }

    pub fn vlog(&mut self, vlog_level: i32) -> &mut MiniClusterConfig {
        assert!(vlog_level >= 0);
        self.vlog = Some(vlog_level);
        self
    }

    pub fn vmodule(&mut self, module: String, vlog_level: i32) -> &mut MiniClusterConfig {
        assert!(vlog_level >= 0);
        self.vmodule.push((module, vlog_level));
        self
    }

    pub fn log_rpc_negotiation_trace(&mut self, log_rpc_negotiation_trace: bool) -> &mut MiniClusterConfig {
        self.log_rpc_negotiation_trace = Some(log_rpc_negotiation_trace);
        self
    }

    pub fn log_rpc_trace(&mut self, log_rpc_trace: bool) -> &mut MiniClusterConfig {
        self.log_rpc_trace = Some(log_rpc_trace);
        self
    }

    pub fn rpc_negotiation_delay(&mut self, millis: i32) -> &mut MiniClusterConfig {
        self.rpc_negotiation_delay = Some(millis);
        self
    }

    pub fn get_table_locations_delay(&mut self, millis: i32) -> &mut MiniClusterConfig {
        self.get_table_locations_delay = Some(millis);
        self
    }

    pub fn leader_failure_detection(&mut self, leader_failure_detection: bool) -> &mut MiniClusterConfig {
        self.leader_failure_detection = Some(leader_failure_detection);
        self
    }

    pub fn data_block_fsync(&mut self, data_block_fsync: bool) -> &mut MiniClusterConfig {
        self.data_block_fsync = data_block_fsync;
        self
    }

    fn configure(&self, command: &mut Command) {

        if let Some(log_level) = self.min_log_level {
            command.arg(format!("--minloglevel={}", log_level as usize));
        }

        if let Some(vlog_level) = self.vlog {
            command.arg(format!("--v={}", vlog_level));
        }

        if !self.vmodule.is_empty() {
            let modules = self.vmodule
                              .iter()
                              .map(|&(ref module, ref level)| format!("{}={}", module, level))
                              .collect::<Vec<_>>();
            command.arg(format!("--vmodule={}", modules.join(",")));
        }

        if let Some(log_rpc_negotiation_trace) = self.log_rpc_negotiation_trace {
            command.arg(format!("--rpc_trace_negotiation={}", log_rpc_negotiation_trace));
        }

        if let Some(log_rpc_trace) = self.log_rpc_trace {
            command.arg(format!("--rpc_dump_all_traces={}", log_rpc_trace));
        }

        if let Some(rpc_negotiation_delay) = self.rpc_negotiation_delay {
            command.arg(format!("--rpc_negotiation_inject_delay_ms={}",
                                rpc_negotiation_delay));
        }

        if let Some(get_table_locations_delay) = self.get_table_locations_delay {
            command.arg(format!("--master_inject_latency_on_tablet_lookups_ms={}",
                                get_table_locations_delay));
        }

        if let Some(leader_failure_detection) = self.leader_failure_detection {
            command.arg(format!("--enable_leader_failure_detection={}",
                                leader_failure_detection));
        }

        command.arg(format!("--enable_data_block_fsync={}", self.data_block_fsync));
    }
}

impl Default for MiniClusterConfig {
    fn default() -> MiniClusterConfig {
        MiniClusterConfig {
            num_masters: 1,
            num_tservers: 1,
            min_log_level: None,
            vlog: None,
            vmodule: Vec::new(),
            log_rpc_negotiation_trace: None,
            log_rpc_trace: None,
            rpc_negotiation_delay: None,
            get_table_locations_delay: None,
            leader_failure_detection: None,
            data_block_fsync: false,
        }
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum LogLevel {
    Info = 0,
    Warning = 1,
    Error = 2,
    Fatal = 3,
}

#[cfg(test)]
mod tests {

    use env_logger;

    use super::*;

    #[test]
    fn test_spawn_mini_cluster() {
        let _ = env_logger::init();
        let _cluster = MiniCluster::default();
    }
}
<commit_msg>Add mini cluster node start/stop<commit_after>use std::collections::HashMap;
use std::env;
use std::fs;
use std::net::{SocketAddr, TcpListener};
use std::path::PathBuf;
use std::process::{Command, Child, Stdio};
use std::thread;
use std::io::{BufRead, BufReader};

use tempdir::TempDir;

/// A Kudu server node (either master or tablet server).
struct Node {
    name: String,
    bin: PathBuf,
    args: Vec<(&'static str, String)>,
    process: Option<Child>,
}

impl Node {
    fn new(name: String, bin: PathBuf, args: Vec<(&'static str, String)>) -> Node {
        Node {
            name: name,
            bin: bin,
            args: args,
            process: None,
        }

    }

    fn stop(&mut self) {
        let _ = self.process.take().expect(&format!("{} not running", &self.name)).kill();
    }

    fn start(&mut self) {
        assert!(self.process.is_none(), "{} already started", &self.name);

        let mut command = Command::new(&self.bin);

        for &(k, ref v) in &self.args {
            command.arg(format!("--{}={}", k, v));
        }

        command.stderr(Stdio::piped());
        let mut process = command.spawn().unwrap();
        let stderr = process.stderr.take().unwrap();

        let name = self.name.clone();
        thread::spawn(move || {
            let stderr = BufReader::new(stderr);
            for line in stderr.lines() {
                debug!("({}): {}", name, line.unwrap());
            }

        });
        self.process = Some(process);
    }
}

impl Drop for Node {
    fn drop(&mut self) {
        let _ = self.process.take().map(|mut process| process.kill());
    }
}

pub struct MiniCluster {
    dir: TempDir,
    master_addrs: Vec<SocketAddr>,
    nodes: HashMap<SocketAddr, Node>,
}

impl MiniCluster {
    pub fn new(conf: &MiniClusterConfig) -> MiniCluster {
        let kudu_home = env::var("KUDU_HOME").expect("KUDU_HOME environment variable must be set");
        let mut bin_dir = PathBuf::from(&kudu_home);
        bin_dir.push("build");
        bin_dir.push("latest");
        bin_dir.push("bin");

        let master_bin = bin_dir.join("kudu-master");
        let tserver_bin = bin_dir.join("kudu-tserver");
        let dir = TempDir::new("kudu-rs-mini-cluster").expect("unable to create temp dir");

        let num_nodes = (conf.num_masters + conf.num_tservers) as usize;
        let mut nodes = HashMap::with_capacity(num_nodes);

        let addrs = get_unbound_addresses(num_nodes);
        let (master_addrs, tserver_addrs) = addrs.split_at(conf.num_masters as usize);

        let master_addresses = master_addrs.iter().map(ToString::to_string).collect::<Vec<_>>().join(",");
        let conf_args = conf.args();

        for (i, addr) in master_addrs.iter().enumerate() {
            let path = dir.path().join(format!("master-{}", i));
            let logs = dir.path().join(format!("master-{}-logs", i));
            fs::create_dir(&logs).expect("unable to create master log directory");

            let name = format!("master:{}", addr.port());

            let mut args = conf_args.clone();
            args.push(("fs_wal_dir", path.to_string_lossy().into_owned()));
            args.push(("fs_data_dirs", path.to_string_lossy().into_owned()));
            args.push(("log_dir", logs.to_string_lossy().into_owned()));
            args.push(("rpc_bind_addresses", addr.to_string()));
            args.push(("webserver_port", 0.to_string()));
            args.push(("logtostderr", true.to_string()));

            if conf.num_masters > 1 {
                args.push(("master_addresses", master_addresses.clone()));
            }

            let mut node = Node::new(name, master_bin.clone(), args);
            node.start();
            nodes.insert(*addr, node);
        }

        for (i, addr) in tserver_addrs.iter().enumerate() {
            let path = dir.path().join(format!("tserver-{}", i));
            let logs = dir.path().join(format!("tserver-{}-logs", i));
            fs::create_dir(&logs).expect("unable to create tserver log directory");

            let name = format!("tserver:{}", addr.port());

            let mut args = conf_args.clone();
            args.push(("fs_wal_dir", path.to_string_lossy().into_owned()));
            args.push(("fs_data_dirs", path.to_string_lossy().into_owned()));
            args.push(("log_dir", logs.to_string_lossy().into_owned()));
            args.push(("rpc_bind_addresses", addr.to_string()));
            args.push(("webserver_port", 0.to_string()));
            args.push(("logtostderr", true.to_string()));
            args.push(("tserver_master_addrs", master_addresses.clone()));

            let mut node = Node::new(name, tserver_bin.clone(), args);
            node.start();
            nodes.insert(*addr, node);
        }

        MiniCluster {
            dir: dir,
            master_addrs: master_addrs.to_owned(),
            nodes: nodes,
        }
    }

    pub fn master_addrs(&self) -> &[SocketAddr] {
        &self.master_addrs
    }

    pub fn stop_node(&mut self, addr: &SocketAddr) {
        self.nodes.get_mut(addr).expect(&format!("no node with address {}", addr)).stop();
    }

    pub fn start_node(&mut self, addr: &SocketAddr) {
        self.nodes.get_mut(addr).expect(&format!("no node with address {}", addr)).start();
    }
}

impl Default for MiniCluster {
    fn default() -> MiniCluster {
        MiniCluster::new(&MiniClusterConfig::default())
    }
}

pub fn get_unbound_address() -> SocketAddr {
    TcpListener::bind("127.0.0.1:0").unwrap().local_addr().unwrap()
}

/// Attempts to get local unbound socket addresses for testing.
fn get_unbound_addresses(count: usize) -> Vec<SocketAddr> {
    let mut listeners = Vec::with_capacity(count);
    let mut addrs = Vec::with_capacity(count);

    for _ in 0..count {
        let listener = TcpListener::bind("127.0.0.1:0").unwrap();
        let addr = listener.local_addr().unwrap();
        listeners.push(listener);
        addrs.push(addr);
    }
    addrs
}

/// Mini cluster configuration options. Unless otherwise specified, the defaults match the master
/// and tablet server defaults.
pub struct MiniClusterConfig {
    /// The number of masters in the mini cluster. Default: 1.
    num_masters: i32,
    /// The number of tablet servers in the mini cluster. Default: 1.
    num_tservers: i32,

    // Logging Options

    /// Minimum logging level.
    min_log_level: Option<LogLevel>,

    /// Show all `VLOG(m)` messages for `m < this`. Overridden by `vmodule`.
    vlog: Option<i32>,

    /// Per-module `VLOG` level. List of (<module name>, <log level>) pairs. <module name> is a
    /// glob pattern, matched against the filename base (that is, name ignoring
    /// `.cc`/`.h`/`-inl.h`). <log level> overrides any value given by `vlog`.
    vmodule: Vec<(String, i32)>,

    /// If enabled, dump traces of all RPC negotiations to the log at `INFO` level.
    log_rpc_negotiation_trace: Option<bool>,

    /// If enabled, dump traces of all RPCs to the log at `INFO` level.
    log_rpc_trace: Option<bool>,

    // Latency Injection

    /// Amount of latency in milliseconds to inject into the RPC negotiation process.
    rpc_negotiation_delay: Option<i32>,

    /// Amount of latency in milliseconds to inject in GetTableLocations RPCs.
    get_table_locations_delay: Option<i32>,

    // Miscellaneous

    /// Whether to enable failure detection of tablet leaders. If enabled, attempts will be made to
    /// elect a follower as a new leader when the leader is detected to have failed.
    leader_failure_detection: Option<bool>,

    /// Whether to enable fsync() of data blocks, metadata, and their parent directories. Disabling
    /// this flag may cause data loss in the event of a system crash. Default: false.
    data_block_fsync: bool,
}

impl MiniClusterConfig {

    pub fn num_masters(&mut self, num_masters: i32) -> &mut MiniClusterConfig {
        self.num_masters = num_masters;
        self
    }

    pub fn num_tservers(&mut self, num_tservers: i32) -> &mut MiniClusterConfig {
        self.num_tservers = num_tservers;
        self
    }

    pub fn min_log_level(&mut self, log_level: LogLevel) -> &mut MiniClusterConfig {
        self.min_log_level = Some(log_level);
        self
    }

    pub fn vlog(&mut self, vlog_level: i32) -> &mut MiniClusterConfig {
        assert!(vlog_level >= 0);
        self.vlog = Some(vlog_level);
        self
    }

    pub fn vmodule(&mut self, module: String, vlog_level: i32) -> &mut MiniClusterConfig {
        assert!(vlog_level >= 0);
        self.vmodule.push((module, vlog_level));
        self
    }

    pub fn log_rpc_negotiation_trace(&mut self, log_rpc_negotiation_trace: bool) -> &mut MiniClusterConfig {
        self.log_rpc_negotiation_trace = Some(log_rpc_negotiation_trace);
        self
    }

    pub fn log_rpc_trace(&mut self, log_rpc_trace: bool) -> &mut MiniClusterConfig {
        self.log_rpc_trace = Some(log_rpc_trace);
        self
    }

    pub fn rpc_negotiation_delay(&mut self, millis: i32) -> &mut MiniClusterConfig {
        self.rpc_negotiation_delay = Some(millis);
        self
    }

    pub fn get_table_locations_delay(&mut self, millis: i32) -> &mut MiniClusterConfig {
        self.get_table_locations_delay = Some(millis);
        self
    }

    pub fn leader_failure_detection(&mut self, leader_failure_detection: bool) -> &mut MiniClusterConfig {
        self.leader_failure_detection = Some(leader_failure_detection);
        self
    }

    pub fn data_block_fsync(&mut self, data_block_fsync: bool) -> &mut MiniClusterConfig {
        self.data_block_fsync = data_block_fsync;
        self
    }

    fn args(&self) -> Vec<(&'static str, String)> {
        let mut args = Vec::new();

        if let Some(log_level) = self.min_log_level {
            args.push(("minloglevel", (log_level as usize).to_string()));
        }

        if let Some(vlog_level) = self.vlog {
            args.push(("v", vlog_level.to_string()));
        }

        if !self.vmodule.is_empty() {
            let modules = self.vmodule
                              .iter()
                              .map(|&(ref module, ref level)| format!("{}={}", module, level))
                              .collect::<Vec<_>>();
            args.push(("vmodule", modules.join(",")));
        }

        if let Some(log_rpc_negotiation_trace) = self.log_rpc_negotiation_trace {
            args.push(("rpc_trace_negotiation", log_rpc_negotiation_trace.to_string()));
        }

        if let Some(log_rpc_trace) = self.log_rpc_trace {
            args.push(("rpc_dump_all_traces", log_rpc_trace.to_string()));
        }

        if let Some(rpc_negotiation_delay) = self.rpc_negotiation_delay {
            args.push(("rpc_negotiation_inject_delay_ms", rpc_negotiation_delay.to_string()));
        }

        if let Some(get_table_locations_delay) = self.get_table_locations_delay {
            args.push(("master_inject_latency_on_tablet_lookup_ms",
                       get_table_locations_delay.to_string()));
        }

        if let Some(leader_failure_detection) = self.leader_failure_detection {
            args.push(("enable_leader_failure_detection",
                       leader_failure_detection.to_string()));
        }

        args.push(("enable_data_block_fsync",
                   self.data_block_fsync.to_string()));

        args
    }
}

impl Default for MiniClusterConfig {
    fn default() -> MiniClusterConfig {
        MiniClusterConfig {
            num_masters: 1,
            num_tservers: 1,
            min_log_level: None,
            vlog: None,
            vmodule: Vec::new(),
            log_rpc_negotiation_trace: None,
            log_rpc_trace: None,
            rpc_negotiation_delay: None,
            get_table_locations_delay: None,
            leader_failure_detection: None,
            data_block_fsync: false,
        }
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum LogLevel {
    Info = 0,
    Warning = 1,
    Error = 2,
    Fatal = 3,
}

#[cfg(test)]
mod tests {

    use env_logger;

    use super::*;

    #[test]
    fn test_spawn_mini_cluster() {
        let _ = env_logger::init();
        let _cluster = MiniCluster::default();
    }

    #[test]
    fn test_pause_resume_master() {
        let _ = env_logger::init();
        let mut cluster = MiniCluster::new(MiniClusterConfig::default().num_masters(3).num_tservers(0));
        let master = cluster.master_addrs()[0];

        ::std::thread::sleep_ms(1000);

        info!("PAUSING MASTER");
        cluster.stop_node(&master);
        ::std::thread::sleep_ms(1000);

        info!("RESUMING MASTER");
        cluster.start_node(&master);
        ::std::thread::sleep_ms(1000);
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 Johannes Köster.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

use std::f64;
use std::iter;
use std::slice;

use num::traits::{cast, NumCast};
use itertools::Itertools;

use bio::stats::logprobs::{self, LogProb};

/// Implementation of a cumulative distribution function.
#[derive(Debug, Clone)]
pub struct CDF<T: PartialOrd> {
    inner: Vec<(T, LogProb)>
}

impl<T: PartialOrd> CDF<T> {
    /// Create CDF from given probability mass function (PMF). The PMF may contain duplicate values
    /// the probabilities of which are summed during generation of the CDF.
    pub fn from_pmf(mut entries: Vec<(T, LogProb)>) -> Self {
        entries.sort_by(|&(ref a, _), &(ref b, _)| a.partial_cmp(b).unwrap());
        let mut inner: Vec<(T, LogProb)> = Vec::new();
        for mut e in entries.into_iter() {
            let p = logprobs::add(inner.last().map_or(f64::NEG_INFINITY, |e| e.1), e.1);
            if !inner.is_empty() && inner.last().unwrap().0 == e.0 {
                inner.last_mut().unwrap().1 = p;
            }
            else {
                e.1 = p;
                inner.push(e);
            }
        }
        let mut cdf = CDF {
            inner: inner
        };

        if relative_eq!(cdf.total_prob(), 0.0) && cdf.total_prob() > 0.0 {
            cdf.inner.last_mut().unwrap().1 = 0.0;
        }

        cdf
    }

    /// Create CDF from iterator. This can be used to replace the values of a CDF.
    pub fn from_cdf<I: Iterator<Item = (T, LogProb)>>(entries: I) -> Self {
        CDF { inner: entries.collect_vec() }
    }

    pub fn reduce(self) -> Self {
        let mut inner = Vec::new();
        let mut last = f64::NEG_INFINITY;
        for e in self.inner.into_iter() {
            if last != e.1 {
                last = e.1;
                inner.push(e);
            }
        }
        CDF { inner: inner }
    }

    pub fn sample(mut self, n: usize) -> Self {
        if self.inner.len() <= n {
            self
        }
        else {
            let s = self.inner.len() / (n - 1);
            let last = self.inner.pop().unwrap();
            let mut inner = self.inner.into_iter().step(s).collect_vec();
            inner.push(last);
            CDF {
                inner: inner
            }
        }
    }

    pub fn iter(&self) -> slice::Iter<(T, f64)>{
        self.inner.iter()
    }

    pub fn iter_pmf<'a>(&'a self) -> CDFPMFIter<'a, T> {
        fn cdf_to_pmf<'a, G>(last_prob: &mut LogProb, e: &'a (G, LogProb)) -> Option<(&'a G, LogProb)> {
            let &(ref value, cdf_prob) = e;
            let prob = logprobs::sub(cdf_prob, *last_prob);
            *last_prob = cdf_prob;
            Some((value, prob))
        }
        self.inner.iter().scan(f64::NEG_INFINITY, cdf_to_pmf)
    }

    /// Get cumulative probability for a given value. If the value is not present,
    /// return the probability of the previous value.
    pub fn get(&self, value: &T) -> Option<LogProb> {
        if self.inner.is_empty() {
            None
        }
        else {
            Some(match self.inner.binary_search_by(|e| e.0.partial_cmp(value).unwrap()) {
                Ok(i) => self.inner[i].1,
                Err(i) => if i > 0 { self.inner[i - 1].1 } else { f64::NEG_INFINITY }
            })
        }
    }

    pub fn get_pmf(&self, value: &T) -> Option<LogProb> {
        if self.inner.is_empty() {
            None
        }
        else {
            Some(match self.inner.binary_search_by(|e| e.0.partial_cmp(value).unwrap()) {
                Ok(i) => if i > 0 { logprobs::sub(self.inner[i].1, self.inner[i - 1].1) } else { self.inner[0].1 },
                Err(i) => if i > 0 { self.inner[i - 1].1 } else { f64::NEG_INFINITY }
            })
        }
    }

    pub fn total_prob(&self) -> LogProb {
        let &(_, prob) = self.inner.last().unwrap();
        prob
    }

    /// Return maximum a posteriori probability estimate (MAP).
    pub fn map(&self) -> &T {
        let mut max = self.iter_pmf().next().unwrap();
        for e in self.iter_pmf() {
            if e.1 >= max.1 {
                max = e;
            }
        }
        &max.0
    }

    pub fn credible_interval(&self) -> (&T, &T) {
        let lower = self.inner.binary_search_by(|&(_, p)| p.partial_cmp(&0.025f64.ln()).unwrap()).unwrap_or_else(|i| i);
        let upper = self.inner.binary_search_by(|&(_, p)| p.partial_cmp(&0.975f64.ln()).unwrap()).unwrap_or_else(|i| i - 1);

        (&self.inner[lower].0, &self.inner[upper].0)
    }

    pub fn len(&self) -> usize {
        self.inner.len()
    }
}

impl<T: NumCast + Clone + PartialOrd> CDF<T> {
    pub fn expected_value(&self) -> f64 {
        self.iter_pmf().map(|(value, prob)| {
            cast::<T, f64>(value.clone()).unwrap() * prob.exp()
        }).fold(0.0f64, |s, e| s + e)
    }

    pub fn variance(&self) -> f64 {
        let ev = self.expected_value();
        self.iter_pmf().map(|(value, prob)| {
                (cast::<T, f64>(value.clone()).unwrap() - ev).powi(2) * prob.exp()
        }).fold(0.0, |s, e| s + e)
    }

    pub fn standard_deviation(&self) -> f64 {
        self.variance().sqrt()
    }
}

pub type CDFPMFIter<'a, T> = iter::Scan<slice::Iter<'a, (T, LogProb)>, LogProb, fn(&mut LogProb, &'a (T, LogProb)) -> Option<(&'a T, LogProb)>>;

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_cdf() {
        let mut pmf = vec![(0.0, 0.1f64.ln())];
        for i in 0..9 {
            pmf.push((i as f64, 0.1f64.ln()));
        }
        println!("{:?}", pmf);

        let cdf = CDF::from_pmf(pmf.clone());
        println!("{:?}", cdf);
        for &(value, prob) in pmf.iter().skip(2) {
            assert_ulps_eq!(prob, cdf.get_pmf(&value).unwrap(), epsilon = 0.0000000000001);
        }
        assert_relative_eq!(cdf.total_prob(), 1.0f64.ln());
        assert_relative_eq!(cdf.get(&1.0).unwrap(), 0.3f64.ln(), epsilon = 0.00000001);
    }
}
<commit_msg>Robustness in case of narrow credible interval.<commit_after>// Copyright 2016 Johannes Köster.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

use std::f64;
use std::iter;
use std::slice;
use std::cmp;

use num::traits::{cast, NumCast};
use itertools::Itertools;

use bio::stats::logprobs::{self, LogProb};

/// Implementation of a cumulative distribution function.
#[derive(Debug, Clone)]
pub struct CDF<T: PartialOrd> {
    inner: Vec<(T, LogProb)>
}

impl<T: PartialOrd> CDF<T> {
    /// Create CDF from given probability mass function (PMF). The PMF may contain duplicate values
    /// the probabilities of which are summed during generation of the CDF.
    pub fn from_pmf(mut entries: Vec<(T, LogProb)>) -> Self {
        entries.sort_by(|&(ref a, _), &(ref b, _)| a.partial_cmp(b).unwrap());
        let mut inner: Vec<(T, LogProb)> = Vec::new();
        for mut e in entries.into_iter() {
            let p = logprobs::add(inner.last().map_or(f64::NEG_INFINITY, |e| e.1), e.1);
            if !inner.is_empty() && inner.last().unwrap().0 == e.0 {
                inner.last_mut().unwrap().1 = p;
            }
            else {
                e.1 = p;
                inner.push(e);
            }
        }
        let mut cdf = CDF {
            inner: inner
        };

        if relative_eq!(cdf.total_prob(), 0.0) && cdf.total_prob() > 0.0 {
            cdf.inner.last_mut().unwrap().1 = 0.0;
        }

        cdf
    }

    /// Create CDF from iterator. This can be used to replace the values of a CDF.
    pub fn from_cdf<I: Iterator<Item = (T, LogProb)>>(entries: I) -> Self {
        CDF { inner: entries.collect_vec() }
    }

    pub fn reduce(self) -> Self {
        let mut inner = Vec::new();
        let mut last = f64::NEG_INFINITY;
        for e in self.inner.into_iter() {
            if last != e.1 {
                last = e.1;
                inner.push(e);
            }
        }
        CDF { inner: inner }
    }

    pub fn sample(mut self, n: usize) -> Self {
        if self.inner.len() <= n {
            self
        }
        else {
            let s = self.inner.len() / (n - 1);
            let last = self.inner.pop().unwrap();
            let mut inner = self.inner.into_iter().step(s).collect_vec();
            inner.push(last);
            CDF {
                inner: inner
            }
        }
    }

    pub fn iter(&self) -> slice::Iter<(T, f64)>{
        self.inner.iter()
    }

    pub fn iter_pmf<'a>(&'a self) -> CDFPMFIter<'a, T> {
        fn cdf_to_pmf<'a, G>(last_prob: &mut LogProb, e: &'a (G, LogProb)) -> Option<(&'a G, LogProb)> {
            let &(ref value, cdf_prob) = e;
            let prob = logprobs::sub(cdf_prob, *last_prob);
            *last_prob = cdf_prob;
            Some((value, prob))
        }
        self.inner.iter().scan(f64::NEG_INFINITY, cdf_to_pmf)
    }

    /// Get cumulative probability for a given value. If the value is not present,
    /// return the probability of the previous value.
    pub fn get(&self, value: &T) -> Option<LogProb> {
        if self.inner.is_empty() {
            None
        }
        else {
            Some(match self.inner.binary_search_by(|e| e.0.partial_cmp(value).unwrap()) {
                Ok(i) => self.inner[i].1,
                Err(i) => if i > 0 { self.inner[i - 1].1 } else { f64::NEG_INFINITY }
            })
        }
    }

    pub fn get_pmf(&self, value: &T) -> Option<LogProb> {
        if self.inner.is_empty() {
            None
        }
        else {
            Some(match self.inner.binary_search_by(|e| e.0.partial_cmp(value).unwrap()) {
                Ok(i) => if i > 0 { logprobs::sub(self.inner[i].1, self.inner[i - 1].1) } else { self.inner[0].1 },
                Err(i) => if i > 0 { self.inner[i - 1].1 } else { f64::NEG_INFINITY }
            })
        }
    }

    pub fn total_prob(&self) -> LogProb {
        let &(_, prob) = self.inner.last().unwrap();
        prob
    }

    /// Return maximum a posteriori probability estimate (MAP).
    pub fn map(&self) -> &T {
        let mut max = self.iter_pmf().next().unwrap();
        for e in self.iter_pmf() {
            if e.1 >= max.1 {
                max = e;
            }
        }
        &max.0
    }

    pub fn credible_interval(&self) -> (&T, &T) {
        let lower = self.inner.binary_search_by(|&(_, p)| p.partial_cmp(&0.025f64.ln()).unwrap()).unwrap_or_else(|i| i);
        let upper = self.inner.binary_search_by(|&(_, p)| p.partial_cmp(&0.975f64.ln()).unwrap()).unwrap_or_else(|i| if i > 0 { i - 1 } else { 0 });
        let upper = cmp::max(lower, upper);

        (&self.inner[lower].0, &self.inner[upper].0)
    }

    pub fn len(&self) -> usize {
        self.inner.len()
    }
}

impl<T: NumCast + Clone + PartialOrd> CDF<T> {
    pub fn expected_value(&self) -> f64 {
        self.iter_pmf().map(|(value, prob)| {
            cast::<T, f64>(value.clone()).unwrap() * prob.exp()
        }).fold(0.0f64, |s, e| s + e)
    }

    pub fn variance(&self) -> f64 {
        let ev = self.expected_value();
        self.iter_pmf().map(|(value, prob)| {
                (cast::<T, f64>(value.clone()).unwrap() - ev).powi(2) * prob.exp()
        }).fold(0.0, |s, e| s + e)
    }

    pub fn standard_deviation(&self) -> f64 {
        self.variance().sqrt()
    }
}

pub type CDFPMFIter<'a, T> = iter::Scan<slice::Iter<'a, (T, LogProb)>, LogProb, fn(&mut LogProb, &'a (T, LogProb)) -> Option<(&'a T, LogProb)>>;

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_cdf() {
        let mut pmf = vec![(0.0, 0.1f64.ln())];
        for i in 0..9 {
            pmf.push((i as f64, 0.1f64.ln()));
        }
        println!("{:?}", pmf);

        let cdf = CDF::from_pmf(pmf.clone());
        println!("{:?}", cdf);
        for &(value, prob) in pmf.iter().skip(2) {
            assert_ulps_eq!(prob, cdf.get_pmf(&value).unwrap(), epsilon = 0.0000000000001);
        }
        assert_relative_eq!(cdf.total_prob(), 1.0f64.ln());
        assert_relative_eq!(cdf.get(&1.0).unwrap(), 0.3f64.ln(), epsilon = 0.00000001);
    }
}
<|endoftext|>


<commit_before>extern crate collenchyma;
extern crate collenchyma_nn;

use self::collenchyma::framework::IFramework;
use self::collenchyma::backend::{Backend, BackendConfig};
use self::collenchyma::frameworks::Native;
use self::collenchyma::hardware::IHardware;
use self::collenchyma::device::IDevice;
use self::collenchyma::binary::IBinary;
use self::collenchyma::memory::IMemory;
use self::collenchyma::Error;
use self::collenchyma::tensor::SharedTensor;
use self::collenchyma_nn::*;

pub struct Ctrnn {
    pub backend: Backend<Native>
}

impl Ctrnn {
    pub fn new() -> Ctrnn{
        let framework = Native::new();
        let hardwares = &framework.hardwares().to_vec();
        let backend_config = BackendConfig::new(framework, hardwares);
        let backend = Backend::new(backend_config);
        Ctrnn {
            backend: backend.unwrap()
        }
    }

    pub fn activate(&self, steps: usize, gamma: &Vec<f64>, delta_t: f64, tau: &Vec<f64>, wij: &Vec<Vec<f64>>, theta: &Vec<f64>, wik: &Vec<Vec<f64>>, i: &Vec<f64>) -> Vec<f64> {
        return vec![0.0, 0.0];
    }
}

#[cfg(test)]
mod tests {
    use neat::*;

    #[test]
    fn neural_network_activation_should_return_correct_values(){
        let gamma =         vec![0.0, 0.0, 0.0];
        let delta_t =       13.436;
        let tau =           vec![61.694, 10.149, 16.851];
        let wij =           vec![vec![-2.94737, 2.70665, -0.57046],
                            vec![-3.27553,   3.67193, 1.83218],
                            vec![2.32476,   0.24739, 0.58587]];
        let theta =         vec![-0.695126, -0.677891, -0.072129];
        let wik =           vec![vec![-0.10097, -3.04457],
                            vec![-4.86594, 1.79273],
                            vec![-3.45899, -1.27388]];
        let i =             vec![0.98856, 0.31540];

        let ctrnn = Ctrnn::new();

        assert_eq!( ctrnn.activate(1, &gamma, delta_t, &tau, &wij, &theta, &wik, &i),
            vec![-0.86998, -6.11057, -2.14179]);
        assert_eq!( ctrnn.activate(10, &gamma, delta_t, &tau, &wij, &theta, &wik, &i),
            vec![-1.7698, -4.9550, -3.1953]);
        assert_eq!( ctrnn.activate(30, &gamma, delta_t, &tau, &wij, &theta, &wik, &i),
            vec![-1.7869, -4.9408, -3.2092]);

        assert_eq!( ctrnn.activate(30, &gamma, delta_t, &tau, &wij, &theta, &wik, &i),
            ctrnn.activate(100, &gamma, delta_t, &tau, &wij, &theta, &wik, &i));
    }
}
<commit_msg>First sigmoid with tensors and collenchyma<commit_after>extern crate collenchyma;
extern crate collenchyma_nn;

use self::collenchyma::framework::IFramework;
use self::collenchyma::backend::{Backend, BackendConfig};
use self::collenchyma::frameworks::Native;
use self::collenchyma::Error;
use self::collenchyma::tensor::SharedTensor;
use self::collenchyma::DeviceType;
use self::collenchyma::MemoryType;
use self::collenchyma_nn::*;
use self::collenchyma::tensor::IntoTensorDesc;

pub struct Ctrnn {
    pub backend: Backend<Native>, //shuld replace with general backend
    pub cpu: DeviceType
}

impl Ctrnn {
    pub fn new() -> Ctrnn{
        let framework = Native::new();
        let hardwares = &framework.hardwares().to_vec();
        let backend_config = BackendConfig::new(framework, hardwares);
        let backend = Backend::new(backend_config);
        let native = Native::new();
        Ctrnn {
            backend: backend.unwrap(),
            cpu: native.new_device(native.hardwares()).unwrap()
        }
    }

    pub fn activate(&self, steps: usize, gamma_v: &Vec<f64>, delta_t: f64, tau_v: &Vec<f64>, wij_v: &Vec<Vec<f64>>, theta_v: &Vec<f64>, wik_v: &Vec<Vec<f64>>, i_v: &Vec<f64>) -> Vec<f64> {
        let mut gamma = self.vector_to_tensor(gamma_v);
        let mut tau = self.vector_to_tensor(tau_v);
        let mut wij = self.matrix_to_tensor(wij_v);
        let mut theta = self.vector_to_tensor(theta_v);
        let mut wik = self.matrix_to_tensor(wik_v);
        let mut i = self.vector_to_tensor(i_v);
        let mut result = SharedTensor::<f64>::new(self.backend.device(), wij.desc()).unwrap();
        &self.backend.sigmoid(&mut wij, &mut result).unwrap();
        //result.add_device(&cpu).unwrap();
        result.sync(&self.cpu).unwrap();
        return vec![0.0, 0.0];
    }

    fn matrix_to_tensor(&self, matrix: &Vec<Vec<f64>>) -> SharedTensor<f64> {
        let mut tensor = SharedTensor::<f64>::new(self.backend.device(), &(matrix.len(), matrix[0].len())).unwrap();
        //tensor.add_device(&self.cpu).unwrap();
        tensor.sync(&self.cpu).unwrap();
        self.write_to_memory(tensor.get_mut(&self.cpu).unwrap(), &matrix.iter().flat_map(|s| s.iter()).collect::<Vec<&f64>>());
        tensor
    }

    fn vector_to_tensor(&self, vector: &Vec<f64>) -> SharedTensor<f64> {
        self.generic_to_tensor(&vec!(vector.len()), vector)
    }

    fn generic_to_tensor(&self, desc: &Vec<usize>, data: &[f64]) -> SharedTensor<f64> {
        let mut tensor = SharedTensor::<f64>::new(self.backend.device(), desc).unwrap();
        //tensor.add_device(&self.cpu).unwrap();
        tensor.sync(&self.cpu).unwrap();
        self.write_to_memory(tensor.get_mut(&self.cpu).unwrap(), data);
        tensor
    }

    fn write_to_memory<T: Copy>(&self, mem: &mut MemoryType, data: &[T]) {
        let &mut MemoryType::Native(ref mut mem) = mem;
        let mut mem_buffer = mem.as_mut_slice::<T>();
        for (index, datum) in data.iter().enumerate() {
            mem_buffer[index] = *datum;
        }
    }
}

#[cfg(test)]
mod tests {
    use neat::*;

    #[test]
    fn neural_network_activation_should_return_correct_values(){
        let gamma =         vec![0.0, 0.0, 0.0];
        let delta_t =       13.436;
        let tau =           vec![61.694, 10.149, 16.851];
        let wij =           vec![vec![-2.94737, 2.70665, -0.57046],
                            vec![-3.27553,   3.67193, 1.83218],
                            vec![2.32476,   0.24739, 0.58587]];
        let theta =         vec![-0.695126, -0.677891, -0.072129];
        let wik =           vec![vec![-0.10097, -3.04457],
                            vec![-4.86594, 1.79273],
                            vec![-3.45899, -1.27388]];
        let i =             vec![0.98856, 0.31540];

        let ctrnn = Ctrnn::new();

        assert_eq!( ctrnn.activate(1, &gamma, delta_t, &tau, &wij, &theta, &wik, &i),
            vec![-0.86998, -6.11057, -2.14179]);
        assert_eq!( ctrnn.activate(10, &gamma, delta_t, &tau, &wij, &theta, &wik, &i),
            vec![-1.7698, -4.9550, -3.1953]);
        assert_eq!( ctrnn.activate(30, &gamma, delta_t, &tau, &wij, &theta, &wik, &i),
            vec![-1.7869, -4.9408, -3.2092]);

        assert_eq!( ctrnn.activate(30, &gamma, delta_t, &tau, &wij, &theta, &wik, &i),
            ctrnn.activate(100, &gamma, delta_t, &tau, &wij, &theta, &wik, &i));
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017-2018 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

use model::zobrist::ZobristHash;

const TABLE_SIZE: usize = 1 << 20;
const TABLE_MASK: u64 = TABLE_SIZE as u64 - 1;

// This could just by an array, but because arrays are allocated on the stack (even when
// doing Box::new(array)), we need to use a Vec
pub struct TTable {
    table: Vec<Entry>,
    age: u8,
}

impl TTable {
    pub fn new() -> Self {
        Self {
            table: vec![Default::default(); TABLE_SIZE],
            age: 0,
        }
    }
    pub fn inc_age(&mut self) {
        self.age.wrapping_add(1);
    }
    pub fn get(&self, zobrist: ZobristHash, depth: i8) -> Option<Score> {
        let hash = (zobrist & TABLE_MASK) as usize;
        let entry = self.table[hash];
        if entry.zobrist == zobrist && entry.depth >= depth {
            Some(entry.score)
        } else {
            None
        }
    }
    pub fn set(&mut self, zobrist: ZobristHash, score: Score, depth: i8) {
        let hash = (zobrist & TABLE_MASK) as usize;
        let mut entry = self.table[hash];
        let mut replace = false;
        if entry.zobrist != 0 {
            if self.age != entry.age || depth > entry.depth {
                replace = true;
            }
        } else {
            replace = true;
        }

        if replace {
            entry.score = score;
            entry.age = self.age;
            entry.depth = depth;
            entry.zobrist = zobrist;
        }
    }
}

#[derive(Clone, Copy)]
pub enum Score {
    Exact(i16),
    Beta(i16),
}

// TODO: Store best move for move ordering?
// TODO: Use lower bits of ZobristHash to save space?
#[derive(Clone, Copy)]
pub struct Entry {
    pub score: Score,
    pub age: u8,
    pub depth: i8,
    pub zobrist: ZobristHash,
}

impl Default for Entry {
    fn default() -> Self {
        Self {
            score: Score::Exact(0),
            age: 0,
            depth: 0,
            // The only field that matters for determining if this is an empty entry or not.
            // Assume (and hope) that no valid board ever hashes to 0.
            zobrist: 0,
        }
    }
}
<commit_msg>Use Entry::default instead of Default::default<commit_after>/*
 * Copyright (C) 2017-2018 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

use model::zobrist::ZobristHash;

const TABLE_SIZE: usize = 1 << 20;
const TABLE_MASK: u64 = TABLE_SIZE as u64 - 1;

// This could just by an array, but because arrays are allocated on the stack (even when
// doing Box::new(array)), we need to use a Vec
pub struct TTable {
    table: Vec<Entry>,
    age: u8,
}

impl TTable {
    pub fn new() -> Self {
        Self {
            table: vec![Entry::default(); TABLE_SIZE],
            age: 0,
        }
    }
    pub fn inc_age(&mut self) {
        self.age.wrapping_add(1);
    }
    pub fn get(&self, zobrist: ZobristHash, depth: i8) -> Option<Score> {
        let hash = (zobrist & TABLE_MASK) as usize;
        let entry = self.table[hash];
        if entry.zobrist == zobrist && entry.depth >= depth {
            Some(entry.score)
        } else {
            None
        }
    }
    pub fn set(&mut self, zobrist: ZobristHash, score: Score, depth: i8) {
        let hash = (zobrist & TABLE_MASK) as usize;
        let mut entry = self.table[hash];
        let mut replace = false;
        if entry.zobrist != 0 {
            if self.age != entry.age || depth > entry.depth {
                replace = true;
            }
        } else {
            replace = true;
        }

        if replace {
            entry.score = score;
            entry.age = self.age;
            entry.depth = depth;
            entry.zobrist = zobrist;
        }
    }
}

#[derive(Clone, Copy)]
pub enum Score {
    Exact(i16),
    Beta(i16),
}

// TODO: Store best move for move ordering?
// TODO: Use lower bits of ZobristHash to save space?
#[derive(Clone, Copy)]
pub struct Entry {
    pub score: Score,
    pub age: u8,
    pub depth: i8,
    pub zobrist: ZobristHash,
}

impl Default for Entry {
    fn default() -> Self {
        Self {
            score: Score::Exact(0),
            age: 0,
            depth: 0,
            // The only field that matters for determining if this is an empty entry or not.
            // Assume (and hope) that no valid board ever hashes to 0.
            zobrist: 0,
        }
    }
}
<|endoftext|>


<commit_before>use diesel::pg::Pg;
use diesel::prelude::*;

use crate::app::App;
use crate::github;
use crate::util::errors::{cargo_err, AppResult};

use crate::models::{Crate, Team, User};
use crate::schema::{crate_owners, users};
use crate::views::EncodableOwner;

#[derive(Insertable, Associations, Identifiable, Debug, Clone, Copy)]
#[belongs_to(Crate)]
#[belongs_to(User, foreign_key = "owner_id")]
#[belongs_to(Team, foreign_key = "owner_id")]
#[table_name = "crate_owners"]
#[primary_key(crate_id, owner_id, owner_kind)]
pub struct CrateOwner {
    pub crate_id: i32,
    pub owner_id: i32,
    pub created_by: i32,
    pub owner_kind: i32,
    pub email_notifications: bool,
}

type BoxedQuery<'a> = crate_owners::BoxedQuery<'a, Pg, crate_owners::SqlType>;

impl CrateOwner {
    /// Returns a base crate owner query filtered by the owner kind argument. This query also
    /// filters out deleted records.
    pub fn by_owner_kind(kind: OwnerKind) -> BoxedQuery<'static> {
        use self::crate_owners::dsl::*;

        crate_owners
            .filter(deleted.eq(false))
            .filter(owner_kind.eq(kind as i32))
            .into_boxed()
    }
}

#[derive(Debug, Clone, Copy)]
#[repr(u32)]
pub enum OwnerKind {
    User = 0,
    Team = 1,
}

/// Unifies the notion of a User or a Team.
#[derive(Debug)]
pub enum Owner {
    User(User),
    Team(Team),
}

impl Owner {
    /// Finds the owner by name. Always recreates teams to get the most
    /// up-to-date GitHub ID. Fails out if the user isn't found in the
    /// database, the team isn't found on GitHub, or if the user isn't a member
    /// of the team on GitHub.
    /// May be a user's GH login or a full team name. This is case
    /// sensitive.
    pub fn find_or_create_by_login(
        app: &App,
        conn: &PgConnection,
        req_user: &User,
        name: &str,
    ) -> AppResult<Owner> {
        if name.contains(':') {
            Ok(Owner::Team(Team::create_or_update(
                app, conn, name, req_user,
            )?))
        } else {
            users::table
                .filter(users::gh_login.eq(name))
                .first(conn)
                .map(Owner::User)
                .map_err(|_| cargo_err(&format_args!("could not find user with login `{}`", name)))
        }
    }

    pub fn kind(&self) -> i32 {
        match *self {
            Owner::User(_) => OwnerKind::User as i32,
            Owner::Team(_) => OwnerKind::Team as i32,
        }
    }

    pub fn login(&self) -> &str {
        match *self {
            Owner::User(ref user) => &user.gh_login,
            Owner::Team(ref team) => &team.login,
        }
    }

    pub fn id(&self) -> i32 {
        match *self {
            Owner::User(ref user) => user.id,
            Owner::Team(ref team) => team.id,
        }
    }

    pub fn encodable(self) -> EncodableOwner {
        match self {
            Owner::User(User {
                id,
                name,
                gh_login,
                gh_avatar,
                ..
            }) => {
                let url = format!("https://github.com/{}", gh_login);
                EncodableOwner {
                    id,
                    login: gh_login,
                    avatar: gh_avatar,
                    url: Some(url),
                    name,
                    kind: String::from("user"),
                }
            }
            Owner::Team(Team {
                id,
                name,
                login,
                avatar,
                ..
            }) => {
                let url = github::team_url(&login);
                EncodableOwner {
                    id,
                    login,
                    url: Some(url),
                    avatar,
                    name,
                    kind: String::from("team"),
                }
            }
        }
    }
}
<commit_msg>Exclude inactive accounts when considering which user to invite<commit_after>use diesel::pg::Pg;
use diesel::prelude::*;

use crate::app::App;
use crate::github;
use crate::util::errors::{cargo_err, AppResult};

use crate::models::{Crate, Team, User};
use crate::schema::{crate_owners, users};
use crate::views::EncodableOwner;

#[derive(Insertable, Associations, Identifiable, Debug, Clone, Copy)]
#[belongs_to(Crate)]
#[belongs_to(User, foreign_key = "owner_id")]
#[belongs_to(Team, foreign_key = "owner_id")]
#[table_name = "crate_owners"]
#[primary_key(crate_id, owner_id, owner_kind)]
pub struct CrateOwner {
    pub crate_id: i32,
    pub owner_id: i32,
    pub created_by: i32,
    pub owner_kind: i32,
    pub email_notifications: bool,
}

type BoxedQuery<'a> = crate_owners::BoxedQuery<'a, Pg, crate_owners::SqlType>;

impl CrateOwner {
    /// Returns a base crate owner query filtered by the owner kind argument. This query also
    /// filters out deleted records.
    pub fn by_owner_kind(kind: OwnerKind) -> BoxedQuery<'static> {
        use self::crate_owners::dsl::*;

        crate_owners
            .filter(deleted.eq(false))
            .filter(owner_kind.eq(kind as i32))
            .into_boxed()
    }
}

#[derive(Debug, Clone, Copy)]
#[repr(u32)]
pub enum OwnerKind {
    User = 0,
    Team = 1,
}

/// Unifies the notion of a User or a Team.
#[derive(Debug)]
pub enum Owner {
    User(User),
    Team(Team),
}

impl Owner {
    /// Finds the owner by name. Always recreates teams to get the most
    /// up-to-date GitHub ID. Fails out if the user isn't found in the
    /// database, the team isn't found on GitHub, or if the user isn't a member
    /// of the team on GitHub.
    /// May be a user's GH login or a full team name. This is case
    /// sensitive.
    pub fn find_or_create_by_login(
        app: &App,
        conn: &PgConnection,
        req_user: &User,
        name: &str,
    ) -> AppResult<Owner> {
        if name.contains(':') {
            Ok(Owner::Team(Team::create_or_update(
                app, conn, name, req_user,
            )?))
        } else {
            users::table
                .filter(users::gh_login.eq(name))
                .filter(users::gh_id.ne(-1))
                .first(conn)
                .map(Owner::User)
                .map_err(|_| cargo_err(&format_args!("could not find user with login `{}`", name)))
        }
    }

    pub fn kind(&self) -> i32 {
        match *self {
            Owner::User(_) => OwnerKind::User as i32,
            Owner::Team(_) => OwnerKind::Team as i32,
        }
    }

    pub fn login(&self) -> &str {
        match *self {
            Owner::User(ref user) => &user.gh_login,
            Owner::Team(ref team) => &team.login,
        }
    }

    pub fn id(&self) -> i32 {
        match *self {
            Owner::User(ref user) => user.id,
            Owner::Team(ref team) => team.id,
        }
    }

    pub fn encodable(self) -> EncodableOwner {
        match self {
            Owner::User(User {
                id,
                name,
                gh_login,
                gh_avatar,
                ..
            }) => {
                let url = format!("https://github.com/{}", gh_login);
                EncodableOwner {
                    id,
                    login: gh_login,
                    avatar: gh_avatar,
                    url: Some(url),
                    name,
                    kind: String::from("user"),
                }
            }
            Owner::Team(Team {
                id,
                name,
                login,
                avatar,
                ..
            }) => {
                let url = github::team_url(&login);
                EncodableOwner {
                    id,
                    login,
                    url: Some(url),
                    avatar,
                    name,
                    kind: String::from("team"),
                }
            }
        }
    }
}
<|endoftext|>