<commit_before>// Copyright 2014 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use super::{gl, Texture, Sampler};
use super::gl::types::{GLenum, GLuint, GLint, GLfloat, GLsizei, GLvoid};
use tex::*;
use Blob;

fn kind_to_gl(t: ::tex::TextureKind) -> GLenum {
    match t {
        Texture1D => gl::TEXTURE_1D,
        Texture1DArray => gl::TEXTURE_1D_ARRAY,
        Texture2D => gl::TEXTURE_2D,
        Texture2DArray => gl::TEXTURE_2D_ARRAY,
        TextureCube => gl::TEXTURE_CUBE_MAP,
        Texture3D => gl::TEXTURE_3D,
    }
}

fn format_to_gl(t: ::tex::TextureFormat) -> GLenum {
    match t {
        RGB8 => gl::RGB8,
        RGBA8 => gl::RGBA8,
    }
}

fn format_to_glpixel(t: ::tex::TextureFormat) -> GLenum {
    match t {
        RGB8 => gl::RGB,
        RGBA8 => gl::RGBA
    }
}

fn format_to_gltype(t: ::tex::TextureFormat) -> GLenum {
    match t {
        RGB8 | RGBA8 => gl::UNSIGNED_BYTE,
    }
}

fn miplevel1(w: u16) -> u8 {
    ((w as f32).log2() + 1.0) as u8
}

fn miplevel2(w: u16, h: u16) -> u8 {
    ((w as f32).max(h as f32).log2() + 1.0) as u8
}

fn miplevel3(w: u16, h: u16, d: u16) -> u8 {
    ((w as f32).max(h as f32).max(d as f32).log2() + 1.0) as u8
}

fn min(a: u8, b: u8) -> GLint {
    ::std::cmp::min(a, b) as GLint
}

/// Create a texture, assuming TexStorage* isn't available.
pub fn make_without_storage(info: ::tex::TextureInfo) -> Texture {
    let name = make_texture(info);

    let fmt = format_to_gl(info.format) as GLint;
    let pix = format_to_glpixel(info.format);
    let typ = format_to_gltype(info.format);

    let kind = kind_to_gl(info.kind);

    unsafe {
        match info.kind {
            Texture1D => {
                gl::TexImage1D(
                    kind,
                    min(info.mipmap_range.val1(), miplevel1(info.width)),
                    fmt,
                    info.width as GLsizei,
                    0,
                    pix,
                    typ,
                    ::std::ptr::null(),
                );
            },
            Texture1DArray => {
                gl::TexImage2D(
                    kind,
                    min(info.mipmap_range.val1(), miplevel1(info.width)),
                    fmt,
                    info.width as GLsizei,
                    info.height as GLsizei,
                    0,
                    pix,
                    typ,
                    ::std::ptr::null(),
                );
            },
            Texture2D => {
                gl::TexImage2D(
                    kind,
                    min(info.mipmap_range.val1(), miplevel2(info.width, info.height)),
                    fmt,
                    info.width as GLsizei,
                    info.height as GLsizei,
                    0,
                    pix,
                    typ,
                    ::std::ptr::null(),
                );
            },
            TextureCube => unimplemented!(),
            Texture2DArray => {
                gl::TexImage3D(
                    kind,
                    min(info.mipmap_range.val1(), miplevel2(info.width, info.height)),
                    fmt,
                    info.width as GLsizei,
                    info.height as GLsizei,
                    info.depth as GLsizei,
                    0,
                    pix,
                    typ,
                    ::std::ptr::null(),
                );
            },
            Texture3D => {
                gl::TexImage3D(
                    kind,
                    min(info.mipmap_range.val1(), miplevel3(info.width, info.height, info.depth)),
                    fmt,
                    info.width as GLsizei,
                    info.height as GLsizei,
                    info.depth as GLsizei,
                    0,
                    pix,
                    typ,
                    ::std::ptr::null(),
                );
            },
        }
    }

    name
}

/// Create a texture, assuming TexStorage is available.
pub fn make_with_storage(info: ::tex::TextureInfo) -> Texture {
    let name = make_texture(info);

    let fmt = format_to_gl(info.format);
    let kind = kind_to_gl(info.kind);

    match info.kind {
        Texture1D => {
            gl::TexStorage1D(
                kind,
                min(info.mipmap_range.val1(), miplevel1(info.width)),
                fmt,
                info.width as GLsizei
            );
        },
        Texture1DArray => {
            gl::TexStorage2D(
                kind,
                min(info.mipmap_range.val1(), miplevel1(info.width)),
                fmt,
                info.width as GLsizei,
                info.height as GLsizei,
            );
        },
        Texture2D => {
            gl::TexStorage2D(
                kind,
                min(info.mipmap_range.val1(), miplevel2(info.width, info.height)),
                fmt,
                info.width as GLsizei,
                info.height as GLsizei,
            );
        },
        TextureCube => unimplemented!(),
        Texture2DArray => {
            gl::TexStorage3D(
                kind,
                min(info.mipmap_range.val1(), miplevel2(info.width, info.height)),
                fmt,
                info.width as GLsizei,
                info.height as GLsizei,
                info.depth as GLsizei,
            );
        },
        Texture3D => {
            gl::TexStorage3D(
                kind,
                min(info.mipmap_range.val1(), miplevel3(info.width, info.height, info.depth)),
                fmt,
                info.width as GLsizei,
                info.height as GLsizei,
                info.depth as GLsizei,
            );
        },
    }

    name
}

/// Bind a texture + sampler to a given slot.
pub fn bind_texture(loc: GLuint, tex: Texture, sam: Sampler, info: ::tex::TextureInfo) {
    gl::ActiveTexture(gl::TEXTURE0 + loc as GLenum);
    gl::BindSampler(loc, sam);
    gl::BindTexture(kind_to_gl(info.kind), tex);
}

pub fn update_texture(tex: Texture, img: ::tex::ImageInfo, tex_info: ::tex::TextureInfo,
                      data: Box<Blob + Send>) {
    let data = data.get_address() as *const GLvoid;
    let pix = format_to_glpixel(tex_info.format);
    let typ = format_to_gltype(tex_info.format);

    gl::BindTexture(kind_to_gl(tex_info.kind), tex);

    unsafe {
        match tex_info.kind {
            Texture1D => {
                gl::TexSubImage1D(
                    kind_to_gl(tex_info.kind),
                    img.mipmap as GLint,
                    img.xoffset as GLint,
                    img.width as GLint,
                    pix,
                    typ,
                    data,
                );
            },
            Texture1DArray | Texture2D => {
                gl::TexSubImage2D(
                    kind_to_gl(tex_info.kind),
                    img.mipmap as GLint,
                    img.xoffset as GLint,
                    img.yoffset as GLint,
                    img.width as GLint,
                    img.height as GLint,
                    pix,
                    typ,
                    data,
                );
            },
            TextureCube => unimplemented!(),
            Texture2DArray | Texture3D => {
                gl::TexSubImage3D(
                    kind_to_gl(tex_info.kind),
                    img.mipmap as GLint,
                    img.xoffset as GLint,
                    img.yoffset as GLint,
                    img.zoffset as GLint,
                    img.width as GLint,
                    img.height as GLint,
                    img.depth as GLint,
                    pix,
                    typ,
                    data,
                );
            }
        }
    }
}

/// Common texture creation routine, just binds and sets mipmap ranges.
fn make_texture(info: ::tex::TextureInfo) -> Texture {
    let mut name = 0 as Texture;
    unsafe {
        gl::GenTextures(1, &mut name);
    }

    let k = kind_to_gl(info.kind);
    gl::BindTexture(k, name);

    let (base, max) = info.mipmap_range;
    gl::TexParameteri(k, gl::TEXTURE_BASE_LEVEL, base as GLint);
    gl::TexParameteri(k, gl::TEXTURE_MAX_LEVEL, max as GLint);

    name
}

fn wrap_to_gl(w: WrapMode) -> GLenum {
    match w {
        Tile => gl::REPEAT,
        Mirror => gl::MIRRORED_REPEAT,
        Clamp => gl::CLAMP_TO_EDGE
    }
}

pub fn make_sampler(info: ::tex::SamplerInfo) -> Sampler {
    let mut name = 0 as Sampler;
    unsafe {
        gl::GenSamplers(1, &mut name);
    }

    fn tup<T: Copy>(v: T) -> (T, T) { (v, v) }

    let (min, mag) = match info.filtering {
        Scale => tup(gl::NEAREST),
        Mipmap => tup(gl::NEAREST_MIPMAP_NEAREST),
        Bilinear => tup(gl::LINEAR_MIPMAP_NEAREST),
        Trilinear => tup(gl::LINEAR_MIPMAP_LINEAR),
        Anisotropic(fac) => {
            gl::SamplerParameterf(name, gl::TEXTURE_MAX_ANISOTROPY_EXT, fac as GLfloat);
            tup(gl::LINEAR_MIPMAP_LINEAR)
        }
    };

    gl::SamplerParameteri(name, gl::TEXTURE_MIN_FILTER, min as GLint);
    gl::SamplerParameteri(name, gl::TEXTURE_MAG_FILTER, mag as GLint);

    let (s, t, r) = info.wrap_mode;
    gl::SamplerParameteri(name, gl::TEXTURE_WRAP_S, wrap_to_gl(s) as GLint);
    gl::SamplerParameteri(name, gl::TEXTURE_WRAP_T, wrap_to_gl(t) as GLint);
    gl::SamplerParameteri(name, gl::TEXTURE_WRAP_R, wrap_to_gl(r) as GLint);

    gl::SamplerParameteri(name, gl::TEXTURE_LOD_BIAS, info.lod_bias as GLint);

    let (base, max) = info.mipmap_range;
    gl::SamplerParameteri(name, gl::TEXTURE_BASE_LEVEL, base as GLint);
    gl::SamplerParameteri(name, gl::TEXTURE_MAX_LEVEL, max as GLint);

    name
}
<commit_msg>Clean up mipmap handling<commit_after>// Copyright 2014 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use super::{gl, Texture, Sampler};
use super::gl::types::{GLenum, GLuint, GLint, GLfloat, GLsizei, GLvoid};
use tex::*;
use Blob;

fn kind_to_gl(t: ::tex::TextureKind) -> GLenum {
    match t {
        Texture1D => gl::TEXTURE_1D,
        Texture1DArray => gl::TEXTURE_1D_ARRAY,
        Texture2D => gl::TEXTURE_2D,
        Texture2DArray => gl::TEXTURE_2D_ARRAY,
        TextureCube => gl::TEXTURE_CUBE_MAP,
        Texture3D => gl::TEXTURE_3D,
    }
}

fn format_to_gl(t: ::tex::TextureFormat) -> GLenum {
    match t {
        RGB8 => gl::RGB8,
        RGBA8 => gl::RGBA8,
    }
}

fn format_to_glpixel(t: ::tex::TextureFormat) -> GLenum {
    match t {
        RGB8 => gl::RGB,
        RGBA8 => gl::RGBA
    }
}

fn format_to_gltype(t: ::tex::TextureFormat) -> GLenum {
    match t {
        RGB8 | RGBA8 => gl::UNSIGNED_BYTE,
    }
}

/// Create a texture, assuming TexStorage* isn't available.
pub fn make_without_storage(info: ::tex::TextureInfo) -> Texture {
    let name = make_texture(info);

    let fmt = format_to_gl(info.format) as GLint;
    let pix = format_to_glpixel(info.format);
    let typ = format_to_gltype(info.format);

    let kind = kind_to_gl(info.kind);

    unsafe {
        match info.kind {
            Texture1D => {
                gl::TexImage1D(
                    kind,
                    0,
                    fmt,
                    info.width as GLsizei,
                    0,
                    pix,
                    typ,
                    ::std::ptr::null(),
                );
            },
            Texture1DArray => {
                gl::TexImage2D(
                    kind,
                    0,
                    fmt,
                    info.width as GLsizei,
                    info.height as GLsizei,
                    0,
                    pix,
                    typ,
                    ::std::ptr::null(),
                );
            },
            Texture2D => {
                gl::TexImage2D(
                    kind,
                    0,
                    fmt,
                    info.width as GLsizei,
                    info.height as GLsizei,
                    0,
                    pix,
                    typ,
                    ::std::ptr::null(),
                );
            },
            TextureCube => unimplemented!(),
            Texture2DArray => {
                gl::TexImage3D(
                    kind,
                    0,
                    fmt,
                    info.width as GLsizei,
                    info.height as GLsizei,
                    info.depth as GLsizei,
                    0,
                    pix,
                    typ,
                    ::std::ptr::null(),
                );
            },
            Texture3D => {
                gl::TexImage3D(
                    kind,
                    0,
                    fmt,
                    info.width as GLsizei,
                    info.height as GLsizei,
                    info.depth as GLsizei,
                    0,
                    pix,
                    typ,
                    ::std::ptr::null(),
                );
            },
        }
    }

    name
}

/// Create a texture, assuming TexStorage is available.
pub fn make_with_storage(info: ::tex::TextureInfo) -> Texture {
    use std::cmp::max;

    fn min(a: u8, b: u8) -> GLint {
        ::std::cmp::min(a, b) as GLint
    }

    fn mip_level1(w: u16) -> u8 {
        ((w as f32).log2() + 1.0) as u8
    }

    fn mip_level2(w: u16, h: u16) -> u8 {
        ((max(w, h) as f32).log2() + 1.0) as u8
    }

    fn mip_level3(w: u16, h: u16, d: u16) -> u8 {
        ((max(w, max(h, d)) as f32).log2() + 1.0) as u8
    }

    let name = make_texture(info);

    let fmt = format_to_gl(info.format);
    let kind = kind_to_gl(info.kind);

    match info.kind {
        Texture1D => {
            gl::TexStorage1D(
                kind,
                min(info.mipmap_range.val1(), mip_level1(info.width)),
                fmt,
                info.width as GLsizei
            );
        },
        Texture1DArray => {
            gl::TexStorage2D(
                kind,
                min(info.mipmap_range.val1(), mip_level1(info.width)),
                fmt,
                info.width as GLsizei,
                info.height as GLsizei,
            );
        },
        Texture2D => {
            gl::TexStorage2D(
                kind,
                min(info.mipmap_range.val1(), mip_level2(info.width, info.height)),
                fmt,
                info.width as GLsizei,
                info.height as GLsizei,
            );
        },
        TextureCube => unimplemented!(),
        Texture2DArray => {
            gl::TexStorage3D(
                kind,
                min(info.mipmap_range.val1(), mip_level2(info.width, info.height)),
                fmt,
                info.width as GLsizei,
                info.height as GLsizei,
                info.depth as GLsizei,
            );
        },
        Texture3D => {
            gl::TexStorage3D(
                kind,
                min(info.mipmap_range.val1(), mip_level3(info.width, info.height, info.depth)),
                fmt,
                info.width as GLsizei,
                info.height as GLsizei,
                info.depth as GLsizei,
            );
        },
    }

    name
}

/// Bind a texture + sampler to a given slot.
pub fn bind_texture(loc: GLuint, tex: Texture, sam: Sampler, info: ::tex::TextureInfo) {
    gl::ActiveTexture(gl::TEXTURE0 + loc as GLenum);
    gl::BindSampler(loc, sam);
    gl::BindTexture(kind_to_gl(info.kind), tex);
}

pub fn update_texture(tex: Texture, img: ::tex::ImageInfo, tex_info: ::tex::TextureInfo,
                      data: Box<Blob + Send>) {
    let data = data.get_address() as *const GLvoid;
    let pix = format_to_glpixel(tex_info.format);
    let typ = format_to_gltype(tex_info.format);

    gl::BindTexture(kind_to_gl(tex_info.kind), tex);

    unsafe {
        match tex_info.kind {
            Texture1D => {
                gl::TexSubImage1D(
                    kind_to_gl(tex_info.kind),
                    img.mipmap as GLint,
                    img.xoffset as GLint,
                    img.width as GLint,
                    pix,
                    typ,
                    data,
                );
            },
            Texture1DArray | Texture2D => {
                gl::TexSubImage2D(
                    kind_to_gl(tex_info.kind),
                    img.mipmap as GLint,
                    img.xoffset as GLint,
                    img.yoffset as GLint,
                    img.width as GLint,
                    img.height as GLint,
                    pix,
                    typ,
                    data,
                );
            },
            TextureCube => unimplemented!(),
            Texture2DArray | Texture3D => {
                gl::TexSubImage3D(
                    kind_to_gl(tex_info.kind),
                    img.mipmap as GLint,
                    img.xoffset as GLint,
                    img.yoffset as GLint,
                    img.zoffset as GLint,
                    img.width as GLint,
                    img.height as GLint,
                    img.depth as GLint,
                    pix,
                    typ,
                    data,
                );
            }
        }
    }
}

/// Common texture creation routine, just binds and sets mipmap ranges.
fn make_texture(info: ::tex::TextureInfo) -> Texture {
    let mut name = 0 as Texture;
    unsafe {
        gl::GenTextures(1, &mut name);
    }

    let k = kind_to_gl(info.kind);
    gl::BindTexture(k, name);

    let (base, max) = info.mipmap_range;
    gl::TexParameteri(k, gl::TEXTURE_BASE_LEVEL, base as GLint);
    gl::TexParameteri(k, gl::TEXTURE_MAX_LEVEL, max as GLint);

    name
}

fn wrap_to_gl(w: WrapMode) -> GLenum {
    match w {
        Tile => gl::REPEAT,
        Mirror => gl::MIRRORED_REPEAT,
        Clamp => gl::CLAMP_TO_EDGE
    }
}

pub fn make_sampler(info: ::tex::SamplerInfo) -> Sampler {
    let mut name = 0 as Sampler;
    unsafe {
        gl::GenSamplers(1, &mut name);
    }

    fn tup<T: Copy>(v: T) -> (T, T) { (v, v) }

    let (min, mag) = match info.filtering {
        Scale => tup(gl::NEAREST),
        Mipmap => tup(gl::NEAREST_MIPMAP_NEAREST),
        Bilinear => tup(gl::LINEAR_MIPMAP_NEAREST),
        Trilinear => tup(gl::LINEAR_MIPMAP_LINEAR),
        Anisotropic(fac) => {
            gl::SamplerParameterf(name, gl::TEXTURE_MAX_ANISOTROPY_EXT, fac as GLfloat);
            tup(gl::LINEAR_MIPMAP_LINEAR)
        }
    };

    gl::SamplerParameteri(name, gl::TEXTURE_MIN_FILTER, min as GLint);
    gl::SamplerParameteri(name, gl::TEXTURE_MAG_FILTER, mag as GLint);

    let (s, t, r) = info.wrap_mode;
    gl::SamplerParameteri(name, gl::TEXTURE_WRAP_S, wrap_to_gl(s) as GLint);
    gl::SamplerParameteri(name, gl::TEXTURE_WRAP_T, wrap_to_gl(t) as GLint);
    gl::SamplerParameteri(name, gl::TEXTURE_WRAP_R, wrap_to_gl(r) as GLint);

    gl::SamplerParameteri(name, gl::TEXTURE_LOD_BIAS, info.lod_bias as GLint);

    let (base, max) = info.mipmap_range;
    gl::SamplerParameteri(name, gl::TEXTURE_BASE_LEVEL, base as GLint);
    gl::SamplerParameteri(name, gl::TEXTURE_MAX_LEVEL, max as GLint);

    name
}
<|endoftext|>


<commit_before>use std::f64;
use rand::Rng;
use error::StatsError;
use result::Result;
use super::{Distribution, Univariate, Continuous};

#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Exponential {
    rate: f64,
}

impl Exponential {
    pub fn new(rate: f64) -> Result<Exponential> {
        if rate.is_nan() || rate <= 0.0 {
            Err(StatsError::BadParams)
        } else {
            Ok(Exponential { rate: rate })
        }
    }

    pub fn rate(&self) -> f64 {
        self.rate
    }
}

impl Distribution for Exponential {
    fn sample<R: Rng>(&self, r: &mut R) -> f64 {
        let mut x = r.next_f64();
        while x == 0.0 {
            x = r.next_f64();
        }
        -x.ln() / self.rate
    }
}

impl Univariate for Exponential {
    fn mean(&self) -> f64 {
        1.0 / self.rate
    }

    fn variance(&self) -> f64 {
        1.0 / (self.rate * self.rate)
    }

    fn std_dev(&self) -> f64 {
        1.0 / self.rate
    }

    fn entropy(&self) -> f64 {
        1.0 - self.rate.ln()
    }

    fn skewness(&self) -> f64 {
        2.0
    }

    fn median(&self) -> f64 {
        f64::consts::LN_2 / self.rate
    }

    fn cdf(&self, x: f64) -> f64 {
        if x < 0.0 {
            0.0
        } else {
            1.0 - (-self.rate * x).exp()
        }
    }
}

impl Continuous for Exponential {
    fn mode(&self) -> f64 {
        0.0
    }

    fn min(&self) -> f64 {
        0.0
    }

    fn max(&self) -> f64 {
        f64::INFINITY
    }

    fn pdf(&self, x: f64) -> f64 {
        if x < 0.0 {
            0.0
        } else {
            self.rate * (-self.rate * x).exp()
        }
    }

    fn ln_pdf(&self, x: f64) -> f64 {
        self.rate.ln() - self.rate * x
    }
}

#[cfg_attr(rustfmt, rustfmt_skip)]
#[cfg(test)]
mod test {
    use std::f64;
    use distribution::{Univariate, Continuous};
    use prec;
    use super::Exponential;

    fn try_create(rate: f64) -> Exponential {
        let n = Exponential::new(rate);
        assert!(n.is_ok());
        n.unwrap()
    }

    fn create_case(rate: f64) {
        let n = try_create(rate);
        assert_eq!(rate, n.rate());
    }

    fn bad_create_case(rate: f64) {
        let n = Exponential::new(rate);
        assert!(n.is_err());
    }

    fn test_case<F>(rate: f64, expected: f64, eval: F)
        where F: Fn(Exponential) -> f64
    {
        let n = try_create(rate);
        let x = eval(n);
        assert_eq!(expected, x);
    }

    fn test_almost<F>(rate: f64, expected: f64, acc: f64, eval: F)
        where F: Fn(Exponential) -> f64
    {
        let n = try_create(rate);
        let x = eval(n);
        assert!(prec::almost_eq(expected, x, acc));
    }

    fn test_is_nan<F>(rate: f64, eval: F)
        where F : Fn(Exponential) -> f64
    {
        let n = try_create(rate);
        let x = eval(n);
        assert!(x.is_nan());
    }

    #[test]
    fn test_create() {
        create_case(0.1);
        create_case(1.0);
        create_case(10.0);
    }

    #[test]
    fn test_bad_create() {
        bad_create_case(f64::NAN);
        bad_create_case(0.0);
        bad_create_case(-1.0);
        bad_create_case(-10.0);
    }

    #[test]
    fn test_mean() {
        test_case(0.1, 10.0, |x| x.mean());
        test_case(1.0, 1.0, |x| x.mean());
        test_case(10.0, 0.1, |x| x.mean());
    }

    #[test]
    fn test_variance() {
        test_almost(0.1, 100.0, 1e-13, |x| x.variance());
        test_case(1.0, 1.0, |x| x.variance());
        test_case(10.0, 0.01, |x| x.variance());
    }

    #[test]
    fn test_std_dev() {
        test_case(0.1, 10.0, |x| x.std_dev());
        test_case(1.0, 1.0, |x| x.std_dev());
        test_case(10.0, 0.1, |x| x.std_dev());
    }

    #[test]
    fn test_entropy() {
        test_almost(0.1, 3.302585092994045684018, 1e-15, |x| x.entropy());
        test_case(1.0, 1.0, |x| x.entropy());
        test_almost(10.0, -1.302585092994045684018, 1e-15, |x| x.entropy());
    }

    #[test]
    fn test_skewness() {
        test_case(0.1, 2.0, |x| x.skewness());
        test_case(1.0, 2.0, |x| x.skewness());
        test_case(10.0, 2.0, |x| x.skewness());
    }

    #[test]
    fn test_median() {
        test_almost(0.1, 6.931471805599453094172, 1e-15, |x| x.median());
        test_case(1.0, f64::consts::LN_2, |x| x.median());
        test_case(10.0, 0.06931471805599453094172, |x| x.median());
    }

    #[test]
    fn test_mode() {
        test_case(0.1, 0.0, |x| x.mode());
        test_case(1.0, 0.0, |x| x.mode());
        test_case(10.0, 0.0, |x| x.mode());
    }

    #[test]
    fn test_min_max() {
        test_case(0.1, 0.0, |x| x.min());
        test_case(1.0, 0.0, |x| x.min());
        test_case(10.0, 0.0, |x| x.min());
        test_case(0.1, f64::INFINITY, |x| x.max());
        test_case(1.0, f64::INFINITY, |x| x.max());
        test_case(10.0, f64::INFINITY, |x| x.max());
    }

    #[test]
    fn test_pdf() {
        test_case(0.1, 0.1, |x| x.pdf(0.0));
        test_case(1.0, 1.0, |x| x.pdf(0.0));
        test_case(10.0, 10.0, |x| x.pdf(0.0));
        test_is_nan(f64::INFINITY, |x| x.pdf(0.0));
        test_case(0.1, 0.09900498337491680535739, |x| x.pdf(0.1));
        test_almost(1.0, 0.9048374180359595731642, 1e-15, |x| x.pdf(0.1));
        test_case(10.0, 3.678794411714423215955, |x| x.pdf(0.1));
        test_is_nan(f64::INFINITY, |x| x.pdf(0.1));
        test_case(0.1, 0.09048374180359595731642, |x| x.pdf(1.0));
        test_case(1.0, 0.3678794411714423215955, |x| x.pdf(1.0));
        test_almost(10.0, 4.539992976248485153559e-4, 1e-19, |x| x.pdf(1.0));
        test_is_nan(f64::INFINITY, |x| x.pdf(1.0));
        test_case(0.1, 0.0, |x| x.pdf(f64::INFINITY));
        test_case(1.0, 0.0, |x| x.pdf(f64::INFINITY));
        test_case(10.0, 0.0, |x| x.pdf(f64::INFINITY));
        test_is_nan(f64::INFINITY, |x| x.pdf(f64::INFINITY));
    }
}<commit_msg>complete tests for exponential distribution<commit_after>use std::f64;
use rand::Rng;
use error::StatsError;
use result::Result;
use super::{Distribution, Univariate, Continuous};

#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Exponential {
    rate: f64,
}

impl Exponential {
    pub fn new(rate: f64) -> Result<Exponential> {
        if rate.is_nan() || rate <= 0.0 {
            Err(StatsError::BadParams)
        } else {
            Ok(Exponential { rate: rate })
        }
    }

    pub fn rate(&self) -> f64 {
        self.rate
    }
}

impl Distribution for Exponential {
    fn sample<R: Rng>(&self, r: &mut R) -> f64 {
        let mut x = r.next_f64();
        while x == 0.0 {
            x = r.next_f64();
        }
        -x.ln() / self.rate
    }
}

impl Univariate for Exponential {
    fn mean(&self) -> f64 {
        1.0 / self.rate
    }

    fn variance(&self) -> f64 {
        1.0 / (self.rate * self.rate)
    }

    fn std_dev(&self) -> f64 {
        1.0 / self.rate
    }

    fn entropy(&self) -> f64 {
        1.0 - self.rate.ln()
    }

    fn skewness(&self) -> f64 {
        2.0
    }

    fn median(&self) -> f64 {
        f64::consts::LN_2 / self.rate
    }

    fn cdf(&self, x: f64) -> f64 {
        if x < 0.0 {
            0.0
        } else {
            1.0 - (-self.rate * x).exp()
        }
    }
}

impl Continuous for Exponential {
    fn mode(&self) -> f64 {
        0.0
    }

    fn min(&self) -> f64 {
        0.0
    }

    fn max(&self) -> f64 {
        f64::INFINITY
    }

    fn pdf(&self, x: f64) -> f64 {
        if x < 0.0 {
            0.0
        } else {
            self.rate * (-self.rate * x).exp()
        }
    }

    fn ln_pdf(&self, x: f64) -> f64 {
        self.rate.ln() - self.rate * x
    }
}

#[cfg_attr(rustfmt, rustfmt_skip)]
#[cfg(test)]
mod test {
    use std::f64;
    use distribution::{Univariate, Continuous};
    use prec;
    use super::Exponential;

    fn try_create(rate: f64) -> Exponential {
        let n = Exponential::new(rate);
        assert!(n.is_ok());
        n.unwrap()
    }

    fn create_case(rate: f64) {
        let n = try_create(rate);
        assert_eq!(rate, n.rate());
    }

    fn bad_create_case(rate: f64) {
        let n = Exponential::new(rate);
        assert!(n.is_err());
    }

    fn test_case<F>(rate: f64, expected: f64, eval: F)
        where F: Fn(Exponential) -> f64
    {
        let n = try_create(rate);
        let x = eval(n);
        assert_eq!(expected, x);
    }

    fn test_almost<F>(rate: f64, expected: f64, acc: f64, eval: F)
        where F: Fn(Exponential) -> f64
    {
        let n = try_create(rate);
        let x = eval(n);
        assert!(prec::almost_eq(expected, x, acc));
    }

    fn test_is_nan<F>(rate: f64, eval: F)
        where F : Fn(Exponential) -> f64
    {
        let n = try_create(rate);
        let x = eval(n);
        assert!(x.is_nan());
    }

    #[test]
    fn test_create() {
        create_case(0.1);
        create_case(1.0);
        create_case(10.0);
    }

    #[test]
    fn test_bad_create() {
        bad_create_case(f64::NAN);
        bad_create_case(0.0);
        bad_create_case(-1.0);
        bad_create_case(-10.0);
    }

    #[test]
    fn test_mean() {
        test_case(0.1, 10.0, |x| x.mean());
        test_case(1.0, 1.0, |x| x.mean());
        test_case(10.0, 0.1, |x| x.mean());
    }

    #[test]
    fn test_variance() {
        test_almost(0.1, 100.0, 1e-13, |x| x.variance());
        test_case(1.0, 1.0, |x| x.variance());
        test_case(10.0, 0.01, |x| x.variance());
    }

    #[test]
    fn test_std_dev() {
        test_case(0.1, 10.0, |x| x.std_dev());
        test_case(1.0, 1.0, |x| x.std_dev());
        test_case(10.0, 0.1, |x| x.std_dev());
    }

    #[test]
    fn test_entropy() {
        test_almost(0.1, 3.302585092994045684018, 1e-15, |x| x.entropy());
        test_case(1.0, 1.0, |x| x.entropy());
        test_almost(10.0, -1.302585092994045684018, 1e-15, |x| x.entropy());
    }

    #[test]
    fn test_skewness() {
        test_case(0.1, 2.0, |x| x.skewness());
        test_case(1.0, 2.0, |x| x.skewness());
        test_case(10.0, 2.0, |x| x.skewness());
    }

    #[test]
    fn test_median() {
        test_almost(0.1, 6.931471805599453094172, 1e-15, |x| x.median());
        test_case(1.0, f64::consts::LN_2, |x| x.median());
        test_case(10.0, 0.06931471805599453094172, |x| x.median());
    }

    #[test]
    fn test_mode() {
        test_case(0.1, 0.0, |x| x.mode());
        test_case(1.0, 0.0, |x| x.mode());
        test_case(10.0, 0.0, |x| x.mode());
    }

    #[test]
    fn test_min_max() {
        test_case(0.1, 0.0, |x| x.min());
        test_case(1.0, 0.0, |x| x.min());
        test_case(10.0, 0.0, |x| x.min());
        test_case(0.1, f64::INFINITY, |x| x.max());
        test_case(1.0, f64::INFINITY, |x| x.max());
        test_case(10.0, f64::INFINITY, |x| x.max());
    }

    #[test]
    fn test_pdf() {
        test_case(0.1, 0.1, |x| x.pdf(0.0));
        test_case(1.0, 1.0, |x| x.pdf(0.0));
        test_case(10.0, 10.0, |x| x.pdf(0.0));
        test_is_nan(f64::INFINITY, |x| x.pdf(0.0));
        test_case(0.1, 0.09900498337491680535739, |x| x.pdf(0.1));
        test_almost(1.0, 0.9048374180359595731642, 1e-15, |x| x.pdf(0.1));
        test_case(10.0, 3.678794411714423215955, |x| x.pdf(0.1));
        test_is_nan(f64::INFINITY, |x| x.pdf(0.1));
        test_case(0.1, 0.09048374180359595731642, |x| x.pdf(1.0));
        test_case(1.0, 0.3678794411714423215955, |x| x.pdf(1.0));
        test_almost(10.0, 4.539992976248485153559e-4, 1e-19, |x| x.pdf(1.0));
        test_is_nan(f64::INFINITY, |x| x.pdf(1.0));
        test_case(0.1, 0.0, |x| x.pdf(f64::INFINITY));
        test_case(1.0, 0.0, |x| x.pdf(f64::INFINITY));
        test_case(10.0, 0.0, |x| x.pdf(f64::INFINITY));
        test_is_nan(f64::INFINITY, |x| x.pdf(f64::INFINITY));
    }

    #[test]
    fn test_ln_pdf() {
        test_almost(0.1, -2.302585092994045684018, 1e-15, |x| x.ln_pdf(0.0));
        test_case(1.0, 0.0, |x| x.ln_pdf(0.0));
        test_case(10.0, 2.302585092994045684018, |x| x.ln_pdf(0.0));
        test_is_nan(f64::INFINITY, |x| x.ln_pdf(0.0));
        test_almost(0.1, -2.312585092994045684018, 1e-15, |x| x.ln_pdf(0.1));
        test_case(1.0, -0.1, |x| x.ln_pdf(0.1));
        test_almost(10.0, 1.302585092994045684018, 1e-15, |x| x.ln_pdf(0.1));
        test_is_nan(f64::INFINITY, |x| x.ln_pdf(0.1));
        test_case(0.1, -2.402585092994045684018, |x| x.ln_pdf(1.0));
        test_case(1.0, -1.0, |x| x.ln_pdf(1.0));
        test_case(10.0, -7.697414907005954315982, |x| x.ln_pdf(1.0));
        test_is_nan(f64::INFINITY, |x| x.ln_pdf(1.0));
        test_case(0.1, f64::NEG_INFINITY, |x| x.ln_pdf(f64::INFINITY));
        test_case(1.0, f64::NEG_INFINITY, |x| x.ln_pdf(f64::INFINITY));
        test_case(10.0, f64::NEG_INFINITY, |x| x.ln_pdf(f64::INFINITY));
        test_is_nan(f64::INFINITY, |x| x.ln_pdf(f64::INFINITY));
    }

    #[test]
    fn test_cdf() {
        test_case(0.1, 0.0, |x| x.cdf(0.0));
        test_case(1.0, 0.0, |x| x.cdf(0.0));
        test_case(10.0, 0.0, |x| x.cdf(0.0));
        test_is_nan(f64::INFINITY, |x| x.cdf(0.0));
        test_almost(0.1, 0.009950166250831946426094, 1e-16, |x| x.cdf(0.1));
        test_almost(1.0, 0.0951625819640404268358, 1e-16, |x| x.cdf(0.1));
        test_case(10.0, 0.6321205588285576784045, |x| x.cdf(0.1));
        test_case(f64::INFINITY, 1.0, |x| x.cdf(0.1));
        test_almost(0.1, 0.0951625819640404268358, 1e-16, |x| x.cdf(1.0));
        test_case(1.0, 0.6321205588285576784045, |x| x.cdf(1.0));
        test_case(10.0, 0.9999546000702375151485, |x| x.cdf(1.0));
        test_case(f64::INFINITY, 1.0, |x| x.cdf(1.0));
        test_case(0.1, 1.0, |x| x.cdf(f64::INFINITY));
        test_case(1.0, 1.0, |x| x.cdf(f64::INFINITY));
        test_case(10.0, 1.0, |x| x.cdf(f64::INFINITY));
        test_case(f64::INFINITY, 1.0, |x| x.cdf(f64::INFINITY));
    }
}<|endoftext|>


<commit_before>use core::RigidBody;
use maths::{ Vector, Quaternion };
use dynamics::Dynamics;
use collisions::{ Contact, Constraint, Collisions };

/// Contains the simplest implementation for a time marching scheme.
pub struct SimpleDynamics {
    gravity: Vector,
}

impl SimpleDynamics {
    /// Instantiates a new `SimpleDynamics` object.
    pub fn new() -> SimpleDynamics {
        SimpleDynamics {
            gravity: Vector::new_zero(),
        }
    }

    #[allow(non_snake_case)]
    fn solve_for_contact(&mut self, body_0: &RigidBody, body_1: &RigidBody, contact: &Contact) -> ((Vector, Vector), (Vector, Vector)) {
        // TODO compute dynamically
        let epsilon = 0.9;
        // body masses
        let M = [body_0.mass(), body_1.mass()];
        let Jinv = [body_0.inertia().inverse(), body_1.inertia().inverse()];
        // body velocities
        let v = [body_0.velocity(), body_1.velocity()];
        // body angular velocities
        let w = [body_0.angular_velocity(), body_1.angular_velocity()];
        // relative vector from position to contact center
        let to_contact_center = [
            contact.center - body_0.position(),
            contact.center - body_1.position(),
        ];
        // axis of rotation for the impulse introduced by the contact. The axis
        // has been scaled by the distance to the contact.
        let k_scaled = [
            to_contact_center[0].cross(contact.normal),
            to_contact_center[1].cross(contact.normal),
        ];

        let impulse = - (1.0 + epsilon) *
            (contact.normal.dot(v[0] - v[1]) + w[0].dot(k_scaled[0]) - w[1].dot(k_scaled[1])) /
            (1.0/M[0] + 1.0/M[1] + k_scaled[0].dot(Jinv[0]*k_scaled[0]) + k_scaled[1].dot(Jinv[1]*k_scaled[1]));

        let velocity_change = contact.normal * impulse;
        let angular_velocity_change_0 = Jinv[0]*to_contact_center[0].cross( velocity_change);
        let angular_velocity_change_1 = Jinv[1]*to_contact_center[1].cross(-velocity_change);

        return ((velocity_change, angular_velocity_change_0), (-velocity_change, angular_velocity_change_1));

        // self.accumulator.add_impulse(body_0,  impulse_vector, contact.center);
        // self.accumulator.add_impulse(body_1, -impulse_vector, contact.center);
    }

    #[allow(non_snake_case)]
    fn solve_for_contact_with_static(&mut self, body_0: &RigidBody, contact: &Contact) -> (Vector, Vector) {
        // TODO compute dynamically
        let epsilon = 0.9;
        // relative vector from position to contact center
        let to_contact_center = contact.center - body_0.position();
        // axis of rotation for the impulse introduced by the contact. The axis
        // has been scaled by the distance to the contact.
        let k_scaled = to_contact_center.cross(contact.normal);

        let v = body_0.velocity();
        let w = body_0.angular_velocity();
        let Jinv = body_0.inertia().inverse();

        let impulse = - (1.0 + epsilon) *
            (contact.normal.dot(v) + w.dot(k_scaled)) /
            (1.0/body_0.mass() + k_scaled.dot(Jinv*k_scaled));

        let velocity_change = contact.normal * impulse;
        let angular_velocity_change = Jinv*to_contact_center.cross(velocity_change);

        return (velocity_change, angular_velocity_change);
    }
}

impl Dynamics for SimpleDynamics {
    fn update<C: Collisions>(&mut self, collisions: &mut C, time_step: f32) {
        if let Some(contacts) = collisions.find_contacts() {
            println!("CONTACTS FOUND ({})", contacts.len());

            let contacts: Vec<Contact> = contacts.iter().map(|a| a.clone()).collect();
            let contact = contacts[0];

            match contact.constraint {
                Constraint::RigidRigid(id_0, id_1) => {
                    let dv_0: Vector;
                    let dw_0: Vector;
                    let dv_1: Vector;
                    let dw_1: Vector;

                    {
                        let body_0 = collisions.find_body(id_0).expect("A RigidBody went missing!");
                        let body_1 = collisions.find_body(id_1).expect("A RigidBody went missing!");

                        let tup = self.solve_for_contact(body_0, body_1, &contact);

                        dv_0 = (tup.0).0;
                        dw_0 = (tup.0).1;
                        dv_1 = (tup.1).0;
                        dw_1 = (tup.1).1;
                    }

                    {
                        let body_0 = collisions.find_body_mut(id_0).unwrap();
                        let v = body_0.velocity();
                        let w = body_0.angular_velocity();
                        body_0.set_velocity_with_vector(v + dv_0);
                        body_0.set_angular_velocity_with_vector(w + dw_0);
                    }

                    {
                        let body_1 = collisions.find_body_mut(id_1).unwrap();
                        let v = body_1.velocity();
                        let w = body_1.angular_velocity();
                        body_1.set_velocity_with_vector(v + dv_1);
                        body_1.set_angular_velocity_with_vector(w + dw_1);
                    }
                },

                Constraint::RigidStatic { rigid_id, static_id: _ } => {
                    let dv: Vector;
                    let dw: Vector;

                    {
                        let rigid_body = collisions.find_body(rigid_id).expect("A RigidBody went missing!");

                        let tup = self.solve_for_contact_with_static(rigid_body, &contact);
                        dv = tup.0;
                        dw = tup.1;
                    }

                    let rigid_body = collisions.find_body_mut(rigid_id).expect("A RigidBody went missing!");
                    let v = rigid_body.velocity();
                    let w = rigid_body.angular_velocity();
                    rigid_body.set_velocity_with_vector(v + dv);
                    rigid_body.set_angular_velocity_with_vector(w + dw);
                },
            }
        }

        let scaled_gravity = self.gravity * time_step;
        for body in collisions.bodies_iter_mut() {
            // TODO deal with temporaries
            let t = time_step;
            let p = body.position();
            let v = body.velocity();
            body.set_position_with_vector(p + v * t);
            body.set_velocity_with_vector(v + scaled_gravity);

            let w = body.angular_velocity();
            let w_as_quat = Quaternion::new(0.0, w[0] * t, w[1] * t, w[2] * t);
            let q = body.rotation_quaternion();
            let new_rotation = q + w_as_quat * q * 0.5;

            body.set_rotation_with_quaternion(new_rotation.normalize());
        }
    }

    fn gravity(&self) -> Vector {
        self.gravity
    }

    fn set_gravity(&mut self, gravity: Vector) {
        self.gravity = gravity;
    }
}
<commit_msg>fixed failing tests due to a refactor<commit_after>use core::RigidBody;
use maths::{ Vector, Quaternion };
use dynamics::Dynamics;
use collisions::{ Contact, Constraint, Collisions };

/// Contains the simplest implementation for a time marching scheme.
pub struct SimpleDynamics {
    gravity: Vector,
}

impl SimpleDynamics {
    /// Instantiates a new `SimpleDynamics` object.
    pub fn new() -> SimpleDynamics {
        SimpleDynamics {
            gravity: Vector::new_zero(),
        }
    }

    #[allow(non_snake_case)]
    fn solve_for_contact(&mut self, body_0: &RigidBody, body_1: &RigidBody, contact: &Contact) -> ((Vector, Vector), (Vector, Vector)) {
        // TODO compute dynamically
        let epsilon = 1.0;
        // body masses
        let M = [body_0.mass(), body_1.mass()];
        let Jinv = [body_0.inertia().inverse(), body_1.inertia().inverse()];
        // body velocities
        let v = [body_0.velocity(), body_1.velocity()];
        // body angular velocities
        let w = [body_0.angular_velocity(), body_1.angular_velocity()];
        // relative vector from position to contact center
        let to_contact_center = [
            contact.center - body_0.position(),
            contact.center - body_1.position(),
        ];
        // axis of rotation for the impulse introduced by the contact. The axis
        // has been scaled by the distance to the contact.
        let k_scaled = [
            to_contact_center[0].cross(contact.normal),
            to_contact_center[1].cross(contact.normal),
        ];

        let impulse = - (1.0 + epsilon) *
            (contact.normal.dot(v[0] - v[1]) + w[0].dot(k_scaled[0]) - w[1].dot(k_scaled[1])) /
            (1.0/M[0] + 1.0/M[1] + k_scaled[0].dot(Jinv[0]*k_scaled[0]) + k_scaled[1].dot(Jinv[1]*k_scaled[1]));

        let velocity_change = contact.normal * impulse;
        let angular_velocity_change_0 = Jinv[0]*to_contact_center[0].cross( velocity_change);
        let angular_velocity_change_1 = Jinv[1]*to_contact_center[1].cross(-velocity_change);

        return ((velocity_change, angular_velocity_change_0), (-velocity_change, angular_velocity_change_1));

        // self.accumulator.add_impulse(body_0,  impulse_vector, contact.center);
        // self.accumulator.add_impulse(body_1, -impulse_vector, contact.center);
    }

    #[allow(non_snake_case)]
    fn solve_for_contact_with_static(&mut self, body_0: &RigidBody, contact: &Contact) -> (Vector, Vector) {
        // TODO compute dynamically
        let epsilon = 1.0;
        // relative vector from position to contact center
        let to_contact_center = contact.center - body_0.position();
        // axis of rotation for the impulse introduced by the contact. The axis
        // has been scaled by the distance to the contact.
        let k_scaled = to_contact_center.cross(contact.normal);

        let v = body_0.velocity();
        let w = body_0.angular_velocity();
        let Jinv = body_0.inertia().inverse();

        let impulse = - (1.0 + epsilon) *
            (contact.normal.dot(v) + w.dot(k_scaled)) /
            (1.0/body_0.mass() + k_scaled.dot(Jinv*k_scaled));

        let velocity_change = contact.normal * impulse;
        let angular_velocity_change = Jinv*to_contact_center.cross(velocity_change);

        return (velocity_change, angular_velocity_change);
    }
}

impl Dynamics for SimpleDynamics {
    fn update<C: Collisions>(&mut self, collisions: &mut C, time_step: f32) {
        if let Some(contacts) = collisions.find_contacts() {
            println!("CONTACTS FOUND ({})", contacts.len());

            let contacts: Vec<Contact> = contacts.iter().map(|a| a.clone()).collect();
            let contact = contacts[0];

            match contact.constraint {
                Constraint::RigidRigid(id_0, id_1) => {
                    let dv_0: Vector;
                    let dw_0: Vector;
                    let dv_1: Vector;
                    let dw_1: Vector;

                    {
                        let body_0 = collisions.find_body(id_0).expect("A RigidBody went missing!");
                        let body_1 = collisions.find_body(id_1).expect("A RigidBody went missing!");

                        let tup = self.solve_for_contact(body_0, body_1, &contact);

                        dv_0 = (tup.0).0;
                        dw_0 = (tup.0).1;
                        dv_1 = (tup.1).0;
                        dw_1 = (tup.1).1;
                    }

                    {
                        let body_0 = collisions.find_body_mut(id_0).unwrap();
                        let v = body_0.velocity();
                        let w = body_0.angular_velocity();
                        body_0.set_velocity_with_vector(v + dv_0);
                        body_0.set_angular_velocity_with_vector(w + dw_0);
                    }

                    {
                        let body_1 = collisions.find_body_mut(id_1).unwrap();
                        let v = body_1.velocity();
                        let w = body_1.angular_velocity();
                        body_1.set_velocity_with_vector(v + dv_1);
                        body_1.set_angular_velocity_with_vector(w + dw_1);
                    }
                },

                Constraint::RigidStatic { rigid_id, static_id: _ } => {
                    let dv: Vector;
                    let dw: Vector;

                    {
                        let rigid_body = collisions.find_body(rigid_id).expect("A RigidBody went missing!");

                        let tup = self.solve_for_contact_with_static(rigid_body, &contact);
                        dv = tup.0;
                        dw = tup.1;
                    }

                    let rigid_body = collisions.find_body_mut(rigid_id).expect("A RigidBody went missing!");
                    let v = rigid_body.velocity();
                    let w = rigid_body.angular_velocity();
                    rigid_body.set_velocity_with_vector(v + dv);
                    rigid_body.set_angular_velocity_with_vector(w + dw);
                },
            }
        }

        let scaled_gravity = self.gravity * time_step;
        for body in collisions.bodies_iter_mut() {
            // TODO deal with temporaries
            let t = time_step;
            let p = body.position();
            let v = body.velocity();
            let new_velocity = v + scaled_gravity;
            body.set_velocity_with_vector(new_velocity);
            body.set_position_with_vector(p + new_velocity * t);

            let w = body.angular_velocity();
            let w_as_quat = Quaternion::new(0.0, w[0] * t, w[1] * t, w[2] * t);
            let q = body.rotation_quaternion();
            let new_rotation = q + w_as_quat * q * 0.5;

            body.set_rotation_with_quaternion(new_rotation.normalize());
        }
    }

    fn gravity(&self) -> Vector {
        self.gravity
    }

    fn set_gravity(&mut self, gravity: Vector) {
        self.gravity = gravity;
    }
}
<|endoftext|>


<commit_before>use core::Float;
use maths::{ Vector, State };
use dynamics::{ Dynamics, SemiImplicitEuler };
use entities::{ RigidBody, StaticBody };
use collisions::{ ContactPair, CollisionSpace };
use collisions::narrowphase::Intersection;

/// Contains the simplest implementation for a time marching scheme.
pub struct SimpleDynamics {
    gravity: Vector,
    integrator: SemiImplicitEuler,
}

impl SimpleDynamics {
    /// Instantiates a new `SimpleDynamics` object.
    pub fn new() -> SimpleDynamics {
        SimpleDynamics {
            gravity: Vector::new_zero(),
            integrator: SemiImplicitEuler,
        }
    }

    #[allow(non_snake_case)]
    fn solve_for_contact(&mut self, rigid_body_0: &RigidBody, rigid_body_1: &RigidBody, contact_center: Vector, contact_normal: Vector) -> ((Vector, Vector), (Vector, Vector)) {
        let epsilon = rigid_body_0.coefficient_of_restitution() * rigid_body_1.coefficient_of_restitution();
        // body masses
        let M = [rigid_body_0.mass(), rigid_body_1.mass()];
        let Jinv = [rigid_body_0.inertia().inverse(), rigid_body_1.inertia().inverse()];
        // body velocities
        let v = [rigid_body_0.velocity(), rigid_body_1.velocity()];
        // body angular velocities
        let w = [rigid_body_0.angular_velocity(), rigid_body_1.angular_velocity()];
        // relative vector from position to contact center
        let to_contact_center = [
            contact_center - rigid_body_0.position(),
            contact_center - rigid_body_1.position(),
        ];
        // axis of rotation for the impulse introduced by the contact. The axis
        // has been scaled by the distance to the contact.
        let k_scaled = [
            to_contact_center[0].cross(contact_normal),
            to_contact_center[1].cross(contact_normal),
        ];

        let impulse = - (1.0 + epsilon) *
            (contact_normal.dot(v[0] - v[1]) + w[0].dot(k_scaled[0]) - w[1].dot(k_scaled[1])) /
            (1.0/M[0] + 1.0/M[1] + k_scaled[0].dot(Jinv[0]*k_scaled[0]) + k_scaled[1].dot(Jinv[1]*k_scaled[1]));

        let velocity_change = contact_normal * impulse;
        let angular_velocity_change_0 = Jinv[0]*to_contact_center[0].cross( velocity_change);
        let angular_velocity_change_1 = Jinv[1]*to_contact_center[1].cross(-velocity_change);

        return ((velocity_change / M[0], angular_velocity_change_0), (-velocity_change / M[1], angular_velocity_change_1));
    }

    #[allow(non_snake_case)]
    fn solve_for_contact_with_static(&mut self, rigid_body: &RigidBody, static_body: &StaticBody, contact_center: Vector, contact_normal: Vector) -> (Vector, Vector) {
        let epsilon = rigid_body.coefficient_of_restitution() * static_body.coefficient_of_restitution();
        // relative vector from position to contact center
        let to_contact_center = contact_center - rigid_body.position();
        // axis of rotation for the impulse introduced by the contact. The axis
        // has been scaled by the distance to the contact.
        let k_scaled = to_contact_center.cross(contact_normal);

        let m = rigid_body.mass();
        let v = rigid_body.velocity();
        let w = rigid_body.angular_velocity();
        let Jinv = rigid_body.inertia().inverse();

        let impulse = - (1.0 + epsilon) *
            (contact_normal.dot(v) + w.dot(k_scaled)) /
            (1.0/m + k_scaled.dot(Jinv*k_scaled));

        let velocity_change = contact_normal * impulse;
        let angular_velocity_change = Jinv*to_contact_center.cross(velocity_change);

        return (velocity_change / m, angular_velocity_change);
    }

    fn revert_to_time_of_contact<C: CollisionSpace>(&self, collision_space: &mut C, current_intersection: Intersection, rigid_body_0: &mut RigidBody, rigid_body_1: &mut RigidBody, time_window: Float) -> (Intersection, Float) {
        let mut last_intersection: (Intersection, Float, State, State) = (current_intersection, 0.0, rigid_body_0.state().clone(), rigid_body_1.state().clone());
        let mut did_intersect_last_step = true;
        let mut current_time = time_window;

        for i in (0..5) {
            let multiplier = if did_intersect_last_step {
                -1.0
            } else {
                1.0
            };

            let step = multiplier * time_window / ((2usize << i) as Float);
            current_time = current_time + step;

            self.integrator.integrate_in_place(rigid_body_0.state_mut(), step, self.gravity);
            self.integrator.integrate_in_place(rigid_body_1.state_mut(), step, self.gravity);

            if let Some(intersection) = collision_space.find_intersection(rigid_body_0, rigid_body_1) {
                did_intersect_last_step = true;
                last_intersection = (intersection, current_time, rigid_body_0.state().clone(), rigid_body_1.state().clone());
            } else {
                did_intersect_last_step = false;
            }
        }

        return (last_intersection.0, last_intersection.1);
    }

    fn revert_to_time_of_contact_with_static<C: CollisionSpace>(&self, collision_space: &mut C, current_intersection: Intersection, rigid_body: &mut RigidBody, static_body: &StaticBody, time_window: Float) -> (Intersection, Float) {
        // let intersection_option = collision_space.find_intersection(rigid_body, static_body);
        // debug_assert!(intersection_option.is_some(), "find_intersection returned false when there was a contact!");
        let mut last_intersection: (Intersection, Float, State) = (current_intersection, 0.0, rigid_body.state().clone());
        let mut did_intersect_last_step = true;
        let mut current_time = time_window;

        for i in (0..5) {
            let multiplier = if did_intersect_last_step {
                -1.0
            } else {
                1.0
            };

            let step = multiplier * time_window / ((2usize << i) as Float);
            current_time = current_time + step;

            self.integrator.integrate_in_place(rigid_body.state_mut(), step, self.gravity);

            if let Some(intersection) = collision_space.find_intersection(rigid_body, static_body) {
                did_intersect_last_step = true;
                last_intersection = (intersection, current_time, rigid_body.state().clone());
            } else {
                did_intersect_last_step = false;
            }
        }

        return (last_intersection.0, last_intersection.1);
    }

    fn update_rigid_body(&self, rigid_body: &mut RigidBody, change: (Vector, Vector), remaining_time: Float) {
        let v = rigid_body.velocity();
        let w = rigid_body.angular_velocity();
        rigid_body.set_velocity_with_vector(v + change.0);
        rigid_body.set_angular_velocity_with_vector(w + change.1);

        self.integrator.integrate_in_place(rigid_body.state_mut(), remaining_time, self.gravity);
    }
}

impl Dynamics for SimpleDynamics {
    fn update<C: CollisionSpace>(&mut self, collision_space: &mut C, time_step: Float) {
        for mut body in collision_space.bodies_iter_mut() {
            self.integrator.integrate_in_place(body.state_mut(), time_step, self.gravity);
        }

        if let Some(contacts) = collision_space.find_contacts() {
            println!("CONTACTS FOUND ({})", contacts.len());

            for contact in contacts.iter() {
                println!("HANDLING CONTACT {} FACING {}", contact.center, contact.normal);

                match contact.pair {
                    ContactPair::RigidRigid(ref cell_0, ref cell_1) => {
                        let rigid_body_0 = &mut cell_0.borrow_mut();
                        let rigid_body_1 = &mut cell_1.borrow_mut();
                        let current_intersection = Intersection::new(contact.center, contact.normal);

                        let (intersection, remaining_time) = self.revert_to_time_of_contact(collision_space, current_intersection, rigid_body_0, rigid_body_1, time_step);
                        let changes = self.solve_for_contact(rigid_body_0, rigid_body_1, intersection.point(), intersection.normal());

                        self.update_rigid_body(rigid_body_0, changes.0, remaining_time);
                        self.update_rigid_body(rigid_body_1, changes.1, remaining_time);
                    },

                    ContactPair::RigidStatic(ref cell_0, ref cell_1) => {
                        let rigid_body = &mut cell_0.borrow_mut();
                        let static_body = &mut cell_1.borrow();
                        let current_intersection = Intersection::new(contact.center, contact.normal);

                        let (intersection, remaining_time) = self.revert_to_time_of_contact_with_static(collision_space, current_intersection, rigid_body, static_body, time_step);
                        let change = self.solve_for_contact_with_static(rigid_body, static_body, intersection.point(), intersection.normal());

                        self.update_rigid_body(rigid_body, change, remaining_time);
                    },
                }
            }
        }
    }

    fn gravity(&self) -> Vector {
        self.gravity
    }

    fn set_gravity(&mut self, gravity: Vector) {
        self.gravity = gravity;
    }
}
<commit_msg>ignore non-separating impulses<commit_after>use core::{ Float, TOLERANCE };
use maths::{ Vector, State };
use dynamics::{ Dynamics, SemiImplicitEuler };
use entities::{ RigidBody, StaticBody };
use collisions::{ ContactPair, CollisionSpace };
use collisions::narrowphase::Intersection;

/// Contains the simplest implementation for a time marching scheme.
pub struct SimpleDynamics {
    gravity: Vector,
    integrator: SemiImplicitEuler,
}

impl SimpleDynamics {
    /// Instantiates a new `SimpleDynamics` object.
    pub fn new() -> SimpleDynamics {
        SimpleDynamics {
            gravity: Vector::new_zero(),
            integrator: SemiImplicitEuler,
        }
    }

    #[allow(non_snake_case)]
    fn solve_for_contact(&mut self, rigid_body_0: &RigidBody, rigid_body_1: &RigidBody, contact_center: Vector, contact_normal: Vector) -> ((Vector, Vector), (Vector, Vector)) {
        let epsilon = rigid_body_0.coefficient_of_restitution() * rigid_body_1.coefficient_of_restitution();
        // body masses
        let M = [rigid_body_0.mass(), rigid_body_1.mass()];
        let Jinv = [rigid_body_0.inertia().inverse(), rigid_body_1.inertia().inverse()];
        // body velocities
        let v = [rigid_body_0.velocity(), rigid_body_1.velocity()];
        // body angular velocities
        let w = [rigid_body_0.angular_velocity(), rigid_body_1.angular_velocity()];
        // relative vector from position to contact center
        let to_contact_center = [
            contact_center - rigid_body_0.position(),
            contact_center - rigid_body_1.position(),
        ];
        // axis of rotation for the impulse introduced by the contact. The axis
        // has been scaled by the distance to the contact.
        let k_scaled = [
            to_contact_center[0].cross(contact_normal),
            to_contact_center[1].cross(contact_normal),
        ];

        let impulse = - (1.0 + epsilon) *
            (contact_normal.dot(v[0] - v[1]) + w[0].dot(k_scaled[0]) - w[1].dot(k_scaled[1])) /
            (1.0/M[0] + 1.0/M[1] + k_scaled[0].dot(Jinv[0]*k_scaled[0]) + k_scaled[1].dot(Jinv[1]*k_scaled[1]));

        let impulse = if impulse > TOLERANCE {
            println!("[WARNING] NON-SEPARATING IMPULSE! = {}", impulse);
            0.0
        } else {
            impulse
        };

        let velocity_change = contact_normal * impulse;
        let angular_velocity_change_0 = Jinv[0]*to_contact_center[0].cross( velocity_change);
        let angular_velocity_change_1 = Jinv[1]*to_contact_center[1].cross(-velocity_change);

        return ((velocity_change / M[0], angular_velocity_change_0), (-velocity_change / M[1], angular_velocity_change_1));
    }

    #[allow(non_snake_case)]
    fn solve_for_contact_with_static(&mut self, rigid_body: &RigidBody, static_body: &StaticBody, contact_center: Vector, contact_normal: Vector) -> (Vector, Vector) {
        let epsilon = rigid_body.coefficient_of_restitution() * static_body.coefficient_of_restitution();
        // relative vector from position to contact center
        let to_contact_center = contact_center - rigid_body.position();
        // axis of rotation for the impulse introduced by the contact. The axis
        // has been scaled by the distance to the contact.
        let k_scaled = to_contact_center.cross(contact_normal);

        let m = rigid_body.mass();
        let v = rigid_body.velocity();
        let w = rigid_body.angular_velocity();
        let Jinv = rigid_body.inertia().inverse();

        let impulse = - (1.0 + epsilon) *
            (contact_normal.dot(v) + w.dot(k_scaled)) /
            (1.0/m + k_scaled.dot(Jinv*k_scaled));

        let impulse = if impulse > TOLERANCE {
            println!("[WARNING] NON-SEPARATING IMPULSE! = {}", impulse);
            0.0
        } else {
            impulse
        };

        let velocity_change = contact_normal * impulse;
        let angular_velocity_change = Jinv*to_contact_center.cross(velocity_change);

        return (velocity_change / m, angular_velocity_change);
    }

    fn revert_to_time_of_contact<C: CollisionSpace>(&self, collision_space: &mut C, current_intersection: Intersection, rigid_body_0: &mut RigidBody, rigid_body_1: &mut RigidBody, time_window: Float) -> (Intersection, Float) {
        let mut last_intersection: (Intersection, Float, State, State) = (current_intersection, 0.0, rigid_body_0.state().clone(), rigid_body_1.state().clone());
        let mut did_intersect_last_step = true;
        let mut current_time = time_window;

        for i in (0..5) {
            let multiplier = if did_intersect_last_step {
                -1.0
            } else {
                1.0
            };

            let step = multiplier * time_window / ((2usize << i) as Float);
            current_time = current_time + step;

            self.integrator.integrate_in_place(rigid_body_0.state_mut(), step, self.gravity);
            self.integrator.integrate_in_place(rigid_body_1.state_mut(), step, self.gravity);

            if let Some(intersection) = collision_space.find_intersection(rigid_body_0, rigid_body_1) {
                did_intersect_last_step = true;
                last_intersection = (intersection, current_time, rigid_body_0.state().clone(), rigid_body_1.state().clone());
            } else {
                did_intersect_last_step = false;
            }
        }

        return (last_intersection.0, last_intersection.1);
    }

    fn revert_to_time_of_contact_with_static<C: CollisionSpace>(&self, collision_space: &mut C, current_intersection: Intersection, rigid_body: &mut RigidBody, static_body: &StaticBody, time_window: Float) -> (Intersection, Float) {
        // let intersection_option = collision_space.find_intersection(rigid_body, static_body);
        // debug_assert!(intersection_option.is_some(), "find_intersection returned false when there was a contact!");
        let mut last_intersection: (Intersection, Float, State) = (current_intersection, 0.0, rigid_body.state().clone());
        let mut did_intersect_last_step = true;
        let mut current_time = time_window;

        for i in (0..5) {
            let multiplier = if did_intersect_last_step {
                -1.0
            } else {
                1.0
            };

            let step = multiplier * time_window / ((2usize << i) as Float);
            current_time = current_time + step;

            self.integrator.integrate_in_place(rigid_body.state_mut(), step, self.gravity);

            if let Some(intersection) = collision_space.find_intersection(rigid_body, static_body) {
                did_intersect_last_step = true;
                last_intersection = (intersection, current_time, rigid_body.state().clone());
            } else {
                did_intersect_last_step = false;
            }
        }

        return (last_intersection.0, last_intersection.1);
    }

    fn update_rigid_body(&self, rigid_body: &mut RigidBody, change: (Vector, Vector), remaining_time: Float) {
        let v = rigid_body.velocity();
        let w = rigid_body.angular_velocity();
        rigid_body.set_velocity_with_vector(v + change.0);
        rigid_body.set_angular_velocity_with_vector(w + change.1);

        self.integrator.integrate_in_place(rigid_body.state_mut(), remaining_time, self.gravity);
    }
}

impl Dynamics for SimpleDynamics {
    fn update<C: CollisionSpace>(&mut self, collision_space: &mut C, time_step: Float) {
        for mut body in collision_space.bodies_iter_mut() {
            self.integrator.integrate_in_place(body.state_mut(), time_step, self.gravity);
        }

        if let Some(contacts) = collision_space.find_contacts() {
            println!("CONTACTS FOUND ({})", contacts.len());

            for contact in contacts.iter() {
                println!("HANDLING CONTACT {} FACING {}", contact.center, contact.normal);

                match contact.pair {
                    ContactPair::RigidRigid(ref cell_0, ref cell_1) => {
                        let rigid_body_0 = &mut cell_0.borrow_mut();
                        let rigid_body_1 = &mut cell_1.borrow_mut();
                        let current_intersection = Intersection::new(contact.center, contact.normal);

                        let (intersection, remaining_time) = self.revert_to_time_of_contact(collision_space, current_intersection, rigid_body_0, rigid_body_1, time_step);
                        let changes = self.solve_for_contact(rigid_body_0, rigid_body_1, intersection.point(), intersection.normal());

                        self.update_rigid_body(rigid_body_0, changes.0, remaining_time);
                        self.update_rigid_body(rigid_body_1, changes.1, remaining_time);
                    },

                    ContactPair::RigidStatic(ref cell_0, ref cell_1) => {
                        let rigid_body = &mut cell_0.borrow_mut();
                        let static_body = &mut cell_1.borrow();
                        let current_intersection = Intersection::new(contact.center, contact.normal);

                        let (intersection, remaining_time) = self.revert_to_time_of_contact_with_static(collision_space, current_intersection, rigid_body, static_body, time_step);
                        let change = self.solve_for_contact_with_static(rigid_body, static_body, intersection.point(), intersection.normal());

                        self.update_rigid_body(rigid_body, change, remaining_time);
                    },
                }
            }
        }
    }

    fn gravity(&self) -> Vector {
        self.gravity
    }

    fn set_gravity(&mut self, gravity: Vector) {
        self.gravity = gravity;
    }
}
<|endoftext|>


<commit_before>use tikv::raftserver::store::*;

use super::util::*;
use super::cluster::{Cluster, Simulator};
use super::node::new_node_cluster;
use super::server::new_server_cluster;

fn test_multi_base<T: Simulator>(cluster: &mut Cluster<T>) {
    // init_env_log();

    // test a cluster with five nodes [1, 5], only one region (region 1).
    // every node has a store and a peer with same id as node's.
    cluster.bootstrap_region().expect("");
    cluster.start();

    let (key, value) = (b"a1", b"v1");

    cluster.put(key, value);
    assert_eq!(cluster.get(key), Some(value.to_vec()));

    let check_res = cluster.check_quorum(|engine| {
        match engine.get_value(&keys::data_key(key)).unwrap() {
            None => false,
            Some(v) => &*v == value,
        }
    });
    assert!(check_res);

    cluster.delete(key);
    assert_eq!(cluster.get(key), None);

    let check_res = cluster.check_quorum(|engine| {
        engine.get_value(&keys::data_key(key)).unwrap().is_none()
    });
    assert!(check_res);
}

fn test_multi_leader_crash<T: Simulator>(cluster: &mut Cluster<T>) {
    cluster.bootstrap_region().expect("");
    cluster.start();

    let (key1, value1) = (b"a1", b"v1");

    cluster.put(key1, value1);

    let last_leader = cluster.leader_of_region(1).unwrap();
    cluster.stop_node(last_leader.get_node_id());

    sleep_ms(800);
    cluster.reset_leader_of_region(1);
    let new_leader = cluster.leader_of_region(1).expect("leader should be elected.");
    assert!(new_leader != last_leader);

    assert_eq!(cluster.get(key1), Some(value1.to_vec()));

    let (key2, value2) = (b"a2", b"v2");

    cluster.put(key2, value2);
    cluster.delete(key1);
    must_get_none(&cluster.engines[&last_leader.get_node_id()], key2);
    assert!(cluster.engines[&last_leader.get_node_id()]
                .get_value(&keys::data_key(key1))
                .unwrap()
                .is_some());

    // week up
    cluster.run_node(last_leader.get_node_id());

    must_get_equal(&cluster.engines[&last_leader.get_node_id()], key2, value2);
    must_get_none(&cluster.engines[&last_leader.get_node_id()], key1);
}

fn test_multi_cluster_restart<T: Simulator>(cluster: &mut Cluster<T>) {
    cluster.bootstrap_region().expect("");
    cluster.start();

    let (key, value) = (b"a1", b"v1");

    assert!(cluster.leader_of_region(1).is_some());
    assert_eq!(cluster.get(key), None);
    cluster.put(key, value);

    assert_eq!(cluster.get(key), Some(value.to_vec()));

    cluster.shutdown();
    cluster.start();

    assert!(cluster.leader_of_region(1).is_some());
    assert_eq!(cluster.get(key), Some(value.to_vec()));
}

fn test_multi_lost_majority<T: Simulator>(cluster: &mut Cluster<T>, count: usize) {
    cluster.bootstrap_region().expect("");
    cluster.start();

    let half = (count as u64 + 1) / 2;
    for i in 1..half + 1 {
        cluster.stop_node(i);
    }
    if let Some(leader) = cluster.leader_of_region(1) {
        if leader.get_node_id() >= half + 1 {
            cluster.stop_node(leader.get_node_id());
        }
    }
    cluster.reset_leader_of_region(1);
    sleep_ms(600);

    assert!(cluster.leader_of_region(1).is_none());

}

#[test]
fn test_multi_node_base() {
    let count = 5;
    let mut cluster = new_node_cluster(0, count);
    test_multi_base(&mut cluster)
}

#[test]
fn test_multi_server_base() {
    let count = 5;
    let mut cluster = new_server_cluster(0, count);
    test_multi_base(&mut cluster)
}

#[test]
fn test_multi_node_leader_crash() {
    let count = 5;
    let mut cluster = new_node_cluster(0, count);
    test_multi_leader_crash(&mut cluster)
}

#[test]
fn test_multi_server_leader_crash() {
    let count = 5;
    let mut cluster = new_server_cluster(0, count);
    test_multi_leader_crash(&mut cluster)
}

#[test]
fn test_multi_node_cluster_restart() {
    let count = 5;
    let mut cluster = new_node_cluster(0, count);
    test_multi_cluster_restart(&mut cluster)
}

#[test]
fn test_multi_server_cluster_restart() {
    let count = 5;
    let mut cluster = new_server_cluster(0, count);
    test_multi_cluster_restart(&mut cluster)
}

#[test]
fn test_multi_node_lost_majority() {
    let mut tests = vec![4, 5];
    for count in tests.drain(..) {
        let mut cluster = new_node_cluster(0, count);
        test_multi_lost_majority(&mut cluster, count)
    }
}

#[test]
fn test_multi_server_lost_majority() {
    let mut tests = vec![4, 5];
    for count in tests.drain(..) {
        let mut cluster = new_server_cluster(0, count);
        test_multi_lost_majority(&mut cluster, count)
    }
}
<commit_msg>Clean up more<commit_after>use tikv::raftserver::store::*;

use super::util::*;
use super::cluster::{Cluster, Simulator};
use super::node::new_node_cluster;
use super::server::new_server_cluster;

fn test_multi_base<T: Simulator>(cluster: &mut Cluster<T>) {
    // init_env_log();

    // test a cluster with five nodes [1, 5], only one region (region 1).
    // every node has a store and a peer with same id as node's.
    cluster.bootstrap_region().expect("");
    cluster.start();

    let (key, value) = (b"a1", b"v1");

    cluster.put(key, value);
    assert_eq!(cluster.get(key), Some(value.to_vec()));

    let check_res = cluster.check_quorum(|engine| {
        match engine.get_value(&keys::data_key(key)).unwrap() {
            None => false,
            Some(v) => &*v == value,
        }
    });
    assert!(check_res);

    cluster.delete(key);
    assert_eq!(cluster.get(key), None);

    let check_res = cluster.check_quorum(|engine| {
        engine.get_value(&keys::data_key(key)).unwrap().is_none()
    });
    assert!(check_res);
}

fn test_multi_leader_crash<T: Simulator>(cluster: &mut Cluster<T>) {
    cluster.bootstrap_region().expect("");
    cluster.start();

    let (key1, value1) = (b"a1", b"v1");

    cluster.put(key1, value1);

    let last_leader = cluster.leader_of_region(1).unwrap();
    cluster.stop_node(last_leader.get_node_id());

    sleep_ms(800);
    cluster.reset_leader_of_region(1);
    let new_leader = cluster.leader_of_region(1).expect("leader should be elected.");
    assert!(new_leader != last_leader);

    assert_eq!(cluster.get(key1), Some(value1.to_vec()));

    let (key2, value2) = (b"a2", b"v2");

    cluster.put(key2, value2);
    cluster.delete(key1);
    must_get_none(&cluster.engines[&last_leader.get_node_id()], key2);
    must_get_equal(&cluster.engines[&last_leader.get_node_id()], key1, value1);

    // week up
    cluster.run_node(last_leader.get_node_id());

    must_get_equal(&cluster.engines[&last_leader.get_node_id()], key2, value2);
    must_get_none(&cluster.engines[&last_leader.get_node_id()], key1);
}

fn test_multi_cluster_restart<T: Simulator>(cluster: &mut Cluster<T>) {
    cluster.bootstrap_region().expect("");
    cluster.start();

    let (key, value) = (b"a1", b"v1");

    assert!(cluster.leader_of_region(1).is_some());
    assert_eq!(cluster.get(key), None);
    cluster.put(key, value);

    assert_eq!(cluster.get(key), Some(value.to_vec()));

    cluster.shutdown();
    cluster.start();

    assert!(cluster.leader_of_region(1).is_some());
    assert_eq!(cluster.get(key), Some(value.to_vec()));
}

fn test_multi_lost_majority<T: Simulator>(cluster: &mut Cluster<T>, count: usize) {
    cluster.bootstrap_region().expect("");
    cluster.start();

    let half = (count as u64 + 1) / 2;
    for i in 1..half + 1 {
        cluster.stop_node(i);
    }
    if let Some(leader) = cluster.leader_of_region(1) {
        if leader.get_node_id() >= half + 1 {
            cluster.stop_node(leader.get_node_id());
        }
    }
    cluster.reset_leader_of_region(1);
    sleep_ms(600);

    assert!(cluster.leader_of_region(1).is_none());

}

#[test]
fn test_multi_node_base() {
    let count = 5;
    let mut cluster = new_node_cluster(0, count);
    test_multi_base(&mut cluster)
}

#[test]
fn test_multi_server_base() {
    let count = 5;
    let mut cluster = new_server_cluster(0, count);
    test_multi_base(&mut cluster)
}

#[test]
fn test_multi_node_leader_crash() {
    let count = 5;
    let mut cluster = new_node_cluster(0, count);
    test_multi_leader_crash(&mut cluster)
}

#[test]
fn test_multi_server_leader_crash() {
    let count = 5;
    let mut cluster = new_server_cluster(0, count);
    test_multi_leader_crash(&mut cluster)
}

#[test]
fn test_multi_node_cluster_restart() {
    let count = 5;
    let mut cluster = new_node_cluster(0, count);
    test_multi_cluster_restart(&mut cluster)
}

#[test]
fn test_multi_server_cluster_restart() {
    let count = 5;
    let mut cluster = new_server_cluster(0, count);
    test_multi_cluster_restart(&mut cluster)
}

#[test]
fn test_multi_node_lost_majority() {
    let mut tests = vec![4, 5];
    for count in tests.drain(..) {
        let mut cluster = new_node_cluster(0, count);
        test_multi_lost_majority(&mut cluster, count)
    }
}

#[test]
fn test_multi_server_lost_majority() {
    let mut tests = vec![4, 5];
    for count in tests.drain(..) {
        let mut cluster = new_server_cluster(0, count);
        test_multi_lost_majority(&mut cluster, count)
    }
}
<|endoftext|>