<commit_before>use chrono::{DateTime, Duration, TimeZone, UTC};
use std::fmt;
use std::str::FromStr;

use {Error, Result};

#[derive(Clone, Debug, PartialEq)]
pub struct Date {
    inner: DateTime<UTC>,
}

impl Date {
    pub fn from_seconds_since_plist_epoch(timestamp: f64) -> Result<Date> {
        // Seconds since 1/1/2001 00:00:00.

        let millis = timestamp * 1_000.0;
        // Chrono's Duration can only millisecond values between ::std::i64::MIN and
        // ::std::i64::MAX.
        if millis > ::std::i64::MAX as f64 || millis < ::std::i64::MIN as f64 {
            return Err(Error::InvalidData);
        }

        let whole_millis = millis.floor();
        let submilli_nanos = ((millis - whole_millis) * 1_000_000.0).floor();

        let dur = Duration::milliseconds(whole_millis as i64);
        let dur = dur + Duration::nanoseconds(submilli_nanos as i64);

        let plist_epoch = UTC.ymd(2001, 1, 1).and_hms(0, 0, 0);
        let date = plist_epoch.checked_add_signed(dur).ok_or(Error::InvalidData)?;

        Ok(Date { inner: date })
    }
}

impl From<DateTime<UTC>> for Date {
    fn from(date: DateTime<UTC>) -> Self {
        Date { inner: date }
    }
}

impl Into<DateTime<UTC>> for Date {
    fn into(self) -> DateTime<UTC> {
        self.inner
    }
}

impl fmt::Display for Date {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}", self.inner)
    }
}

impl FromStr for Date {
    type Err = ();

    fn from_str(s: &str) -> ::std::result::Result<Self, Self::Err> {
        let date = DateTime::parse_from_rfc3339(&s).map_err(|_| ())?;
        Ok(Date { inner: date.with_timezone(&UTC) })
    }
}

#[cfg(feature = "serde")]
mod serde_impls {
    use serde_base::de::{Deserialize, Deserializer, Error, Visitor, Unexpected};
    use serde_base::ser::{Serialize, Serializer};
    use std::fmt;
    use std::str::FromStr;

    use Date;

    impl Serialize for Date {
        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
            where S: Serializer
        {
            let date_str = self.to_string();
            serializer.serialize_newtype_struct("PLIST-DATE", &date_str)
        }
    }

    struct DateNewtypeVisitor;

    impl<'de> Visitor<'de> for DateNewtypeVisitor {
        type Value = Date;

        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
            formatter.write_str("a plist date newtype")
        }

        fn visit_newtype_struct<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
            where D: Deserializer<'de>
        {
            deserializer.deserialize_str(DateStrVisitor)
        }
    }

    struct DateStrVisitor;

    impl<'de> Visitor<'de> for DateStrVisitor {
        type Value = Date;

        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
            formatter.write_str("a plist date string")
        }

        fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
            where E: Error
        {
            Date::from_str(v).map_err(|_| E::invalid_value(Unexpected::Str(v), &self))
        }
    }

    impl<'de> Deserialize<'de> for Date {
        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
            where D: Deserializer<'de>
        {
            deserializer.deserialize_newtype_struct("PLIST-DATE", DateNewtypeVisitor)
        }
    }
}
<commit_msg>Unsure whether this method should be public.<commit_after>use chrono::{DateTime, Duration, TimeZone, UTC};
use std::fmt;
use std::str::FromStr;

use {Error, Result};

#[derive(Clone, Debug, PartialEq)]
pub struct Date {
    inner: DateTime<UTC>,
}

impl Date {
    #[doc(hidden)]
    pub fn from_seconds_since_plist_epoch(timestamp: f64) -> Result<Date> {
        // Seconds since 1/1/2001 00:00:00.

        let millis = timestamp * 1_000.0;
        // Chrono's Duration can only millisecond values between ::std::i64::MIN and
        // ::std::i64::MAX.
        if millis > ::std::i64::MAX as f64 || millis < ::std::i64::MIN as f64 {
            return Err(Error::InvalidData);
        }

        let whole_millis = millis.floor();
        let submilli_nanos = ((millis - whole_millis) * 1_000_000.0).floor();

        let dur = Duration::milliseconds(whole_millis as i64);
        let dur = dur + Duration::nanoseconds(submilli_nanos as i64);

        let plist_epoch = UTC.ymd(2001, 1, 1).and_hms(0, 0, 0);
        let date = plist_epoch.checked_add_signed(dur).ok_or(Error::InvalidData)?;

        Ok(Date { inner: date })
    }
}

impl From<DateTime<UTC>> for Date {
    fn from(date: DateTime<UTC>) -> Self {
        Date { inner: date }
    }
}

impl Into<DateTime<UTC>> for Date {
    fn into(self) -> DateTime<UTC> {
        self.inner
    }
}

impl fmt::Display for Date {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}", self.inner)
    }
}

impl FromStr for Date {
    type Err = ();

    fn from_str(s: &str) -> ::std::result::Result<Self, Self::Err> {
        let date = DateTime::parse_from_rfc3339(&s).map_err(|_| ())?;
        Ok(Date { inner: date.with_timezone(&UTC) })
    }
}

#[cfg(feature = "serde")]
mod serde_impls {
    use serde_base::de::{Deserialize, Deserializer, Error, Visitor, Unexpected};
    use serde_base::ser::{Serialize, Serializer};
    use std::fmt;
    use std::str::FromStr;

    use Date;

    impl Serialize for Date {
        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
            where S: Serializer
        {
            let date_str = self.to_string();
            serializer.serialize_newtype_struct("PLIST-DATE", &date_str)
        }
    }

    struct DateNewtypeVisitor;

    impl<'de> Visitor<'de> for DateNewtypeVisitor {
        type Value = Date;

        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
            formatter.write_str("a plist date newtype")
        }

        fn visit_newtype_struct<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
            where D: Deserializer<'de>
        {
            deserializer.deserialize_str(DateStrVisitor)
        }
    }

    struct DateStrVisitor;

    impl<'de> Visitor<'de> for DateStrVisitor {
        type Value = Date;

        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
            formatter.write_str("a plist date string")
        }

        fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
            where E: Error
        {
            Date::from_str(v).map_err(|_| E::invalid_value(Unexpected::Str(v), &self))
        }
    }

    impl<'de> Deserialize<'de> for Date {
        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
            where D: Deserializer<'de>
        {
            deserializer.deserialize_newtype_struct("PLIST-DATE", DateNewtypeVisitor)
        }
    }
}
<|endoftext|>


<commit_before>use rand;
use rand::{Rng};

use card::{Suit, Value, Card};

pub struct Deck {
    count_dealt: usize,
    cards: [u16; 52],
}

pub enum DeckError {
    NotEnoughCards
}

impl Deck {
    //TODO: multiple decks?

    pub fn new_unshuffled() -> Deck {
        let mut d = Deck {
            count_dealt: 0,
            cards: [0; 52],
        };

        let mut value = 0;
        for x in d.cards.iter_mut() {
            *x = value;
            value += 1;
        }
        d
    }

    pub fn new_shuffled() -> Deck {
        let mut d = Deck::new_unshuffled();
        d.shuffle();
        d
    }

    pub fn reset_unshuffled(&mut self) {
        self.count_dealt = 0;
    }

    pub fn reset_shuffled(&mut self) {
        self.count_dealt = 0;
        self.shuffle();
    }

    fn shuffle(&mut self) {
        let mut rng = rand::thread_rng();
        rng.shuffle(&mut self.cards);
    }

    fn create_card_for_value(&self, value: &u16) -> Card {
        let suit = match value/13 {
            0 => Suit::Spades,
            1 => Suit::Hearts,
            2 => Suit::Diamonds,
            3 => Suit::Clubs,
            _ => panic!("Unexpected suit conversion number")
        };

        let value = match value%13 {
            0 => Value::Two,
            1 => Value::Three,
            2 => Value::Four,
            3 => Value::Five,
            4 => Value::Six,
            5 => Value::Seven,
            6 => Value::Eight,
            7 => Value::Nine,
            8 => Value::Ten,
            9 => Value::Jack,
            10 => Value::Queen,
            11 => Value::King,
            12 => Value::Ace,
            _ => panic!("Unexpected value conversion number")
        };

        Card(value, suit)
    }

    pub fn draw(&mut self) -> Result<Card, DeckError> {
        if (self.count_dealt + 1) > 52 {
            Err(DeckError::NotEnoughCards)
        } else {
            let value = &self.cards[self.count_dealt];
            self.count_dealt+=1;

            let card = self.create_card_for_value(value);
            Ok(card)
        }
    }

    pub fn draw_n(&mut self, n: &usize) -> Result<Vec<Card>, DeckError> {
        if (self.count_dealt + n) > 52 {
            Err(DeckError::NotEnoughCards)
        } else {
            let mut cards = Vec::new();

            for _ in 0..*n {
                cards.push(self.draw().ok().unwrap());
            }

            Ok(cards)
        }
    }
}

<commit_msg>That doesn't need to be an impl function.<commit_after>use rand;
use rand::{Rng};

use card::{Suit, Value, Card};

pub struct Deck {
    count_dealt: usize,
    cards: [u16; 52],
}

pub enum DeckError {
    NotEnoughCards
}

fn create_card_for_value(value: &u16) -> Card {
    let suit = match value/13 {
        0 => Suit::Spades,
        1 => Suit::Hearts,
        2 => Suit::Diamonds,
        3 => Suit::Clubs,
        _ => panic!("Unexpected suit conversion number")
    };

    let value = match value%13 {
        0 => Value::Two,
        1 => Value::Three,
        2 => Value::Four,
        3 => Value::Five,
        4 => Value::Six,
        5 => Value::Seven,
        6 => Value::Eight,
        7 => Value::Nine,
        8 => Value::Ten,
        9 => Value::Jack,
        10 => Value::Queen,
        11 => Value::King,
        12 => Value::Ace,
        _ => panic!("Unexpected value conversion number")
    };

    Card(value, suit)
}

impl Deck {
    //TODO: a deck containing multiple sets of cards? When 52*3 is needed.

    pub fn new_unshuffled() -> Deck {
        let mut d = Deck {
            count_dealt: 0,
            cards: [0; 52],
        };

        let mut value = 0;
        for x in d.cards.iter_mut() {
            *x = value;
            value += 1;
        }
        d
    }

    pub fn new_shuffled() -> Deck {
        let mut d = Deck::new_unshuffled();
        d.shuffle();
        d
    }

    pub fn reset_unshuffled(&mut self) {
        self.count_dealt = 0;
    }

    pub fn reset_shuffled(&mut self) {
        self.count_dealt = 0;
        self.shuffle();
    }

    fn shuffle(&mut self) {
        let mut rng = rand::thread_rng();
        rng.shuffle(&mut self.cards);
    }

    pub fn draw(&mut self) -> Result<Card, DeckError> {
        if (self.count_dealt + 1) > 52 {
            Err(DeckError::NotEnoughCards)
        } else {
            let value = &self.cards[self.count_dealt];
            self.count_dealt+=1;

            let card = create_card_for_value(value);
            Ok(card)
        }
    }

    pub fn draw_n(&mut self, n: &usize) -> Result<Vec<Card>, DeckError> {
        if (self.count_dealt + n) > 52 {
            Err(DeckError::NotEnoughCards)
        } else {
            let mut cards = Vec::new();

            for _ in 0..*n {
                cards.push(self.draw().ok().unwrap());
            }

            Ok(cards)
        }
    }
}

<|endoftext|>


<commit_before>use encoding::types::EncodingRef;

use error::Error;
use history::{Cursor, History};
use buffer::Buffer;
use parser::{parse, ParseError, ParseSuccess};
use instr;

#[derive(Copy,Clone)]
pub enum EditMode {
    Emacs,
    Vi,
}

enum ViMode {
    Insert,
    Normal,
}

pub struct EditCtx<'a> {
    buf: Buffer,
    history_cursor: Cursor<'a>,
    prompt: &'a str,
    seq: Vec<u8>,
    enc: EncodingRef,
    mode: EditMode,
    vi_mode: ViMode,
}

impl<'a> EditCtx<'a> {

    pub fn new(prompt: &'a str, history: &'a History, enc: EncodingRef, mode: EditMode) -> Self {
        EditCtx {
            buf: Buffer::new(),
            history_cursor: Cursor::new(history),
            prompt: prompt,
            seq: Vec::new(),
            enc: enc,
            mode: mode,
            // always start in insert mode
            vi_mode: ViMode::Insert,
        }
    }

    pub fn fill(&mut self, byte: u8) {
        self.seq.push(byte)
    }

}

pub enum EditResult<C> {
    Cont(C),
    Halt(Result<String, Error>)
}

pub fn edit<'a>(ctx: &mut EditCtx<'a>) -> EditResult<Vec<u8>> {
    use self::EditResult::*;

    let res = match parse(&ctx.seq, ctx.enc) {
        Err(ParseError::Error(len)) => {
            for _ in (0..len) {
                ctx.seq.remove(0);
            };
            Cont(false)
        },
        Err(ParseError::Incomplete) => Cont(false),
        Ok(ParseSuccess(token, len)) => {
            let res = match instr::interpret_token(token) {
                instr::Instr::Done => {
                    Halt(Ok(ctx.buf.drain()))
                },
                instr::Instr::DeleteCharLeftOfCursor => {
                    ctx.buf.delete_char_left_of_cursor();
                    Cont(false)
                },
                instr::Instr::DeleteCharRightOfCursor => {
                    ctx.buf.delete_char_right_of_cursor();
                    Cont(false)
                },
                instr::Instr::DeleteCharRightOfCursorOrEOF => {
                    if !ctx.buf.delete_char_right_of_cursor() {
                        Halt(Err(Error::EndOfFile))
                    } else {
                        Cont(false)
                    }
                },
                instr::Instr::MoveCursorLeft => {
                    ctx.buf.move_left();
                    Cont(false)
                },
                instr::Instr::MoveCursorRight => {
                    ctx.buf.move_right();
                    Cont(false)
                },
                instr::Instr::MoveCursorStart => {
                    ctx.buf.move_start();
                    Cont(false)
                },
                instr::Instr::MoveCursorEnd => {
                    ctx.buf.move_end();
                    Cont(false)
                },
                instr::Instr::HistoryPrev => {
                    if ctx.history_cursor.incr() {
                        ctx.buf.swap()
                    }
                    ctx.history_cursor.get().map(|s| ctx.buf.replace(s));
                    Cont(false)
                },
                instr::Instr::HistoryNext => {
                    if ctx.history_cursor.decr() {
                        ctx.buf.swap()
                    }
                    ctx.history_cursor.get().map(|s| ctx.buf.replace(s));
                    Cont(false)
                },
                instr::Instr::Noop => {
                    Cont(false)
                },
                instr::Instr::Cancel => Halt(Err(Error::Cancel)),
                instr::Instr::Clear => {
                    Cont(true)
                },
                instr::Instr::InsertAtCursor(text) => {
                    ctx.buf.insert_chars_at_cursor(text);
                    Cont(false)
                }
            };
            for _ in (0..len) {
                ctx.seq.remove(0);
            };
            res
        }
    };
    match res {
        Cont(clear) => EditResult::Cont(ctx.buf.get_line(ctx.prompt, clear)),
        Halt(res) => EditResult::Halt(res)
    }
}
<commit_msg>make ViMode enum copyable<commit_after>use encoding::types::EncodingRef;

use error::Error;
use history::{Cursor, History};
use buffer::Buffer;
use parser::{parse, ParseError, ParseSuccess};
use instr;

#[derive(Copy,Clone)]
pub enum EditMode {
    Emacs,
    Vi,
}

#[derive(Copy, Clone)]
pub enum ViMode {
    Insert,
    Normal,
}

pub struct EditCtx<'a> {
    buf: Buffer,
    history_cursor: Cursor<'a>,
    prompt: &'a str,
    seq: Vec<u8>,
    enc: EncodingRef,
    mode: EditMode,
    vi_mode: ViMode,
}

impl<'a> EditCtx<'a> {

    pub fn new(prompt: &'a str, history: &'a History, enc: EncodingRef, mode: EditMode) -> Self {
        EditCtx {
            buf: Buffer::new(),
            history_cursor: Cursor::new(history),
            prompt: prompt,
            seq: Vec::new(),
            enc: enc,
            mode: mode,
            // always start in insert mode
            vi_mode: ViMode::Insert,
        }
    }

    pub fn fill(&mut self, byte: u8) {
        self.seq.push(byte)
    }

}

pub enum EditResult<C> {
    Cont(C),
    Halt(Result<String, Error>)
}

pub fn edit<'a>(ctx: &mut EditCtx<'a>) -> EditResult<Vec<u8>> {
    use self::EditResult::*;

    let res = match parse(&ctx.seq, ctx.enc) {
        Err(ParseError::Error(len)) => {
            for _ in (0..len) {
                ctx.seq.remove(0);
            };
            Cont(false)
        },
        Err(ParseError::Incomplete) => Cont(false),
        Ok(ParseSuccess(token, len)) => {
            let res = match instr::interpret_token(token) {
                instr::Instr::Done => {
                    Halt(Ok(ctx.buf.drain()))
                },
                instr::Instr::DeleteCharLeftOfCursor => {
                    ctx.buf.delete_char_left_of_cursor();
                    Cont(false)
                },
                instr::Instr::DeleteCharRightOfCursor => {
                    ctx.buf.delete_char_right_of_cursor();
                    Cont(false)
                },
                instr::Instr::DeleteCharRightOfCursorOrEOF => {
                    if !ctx.buf.delete_char_right_of_cursor() {
                        Halt(Err(Error::EndOfFile))
                    } else {
                        Cont(false)
                    }
                },
                instr::Instr::MoveCursorLeft => {
                    ctx.buf.move_left();
                    Cont(false)
                },
                instr::Instr::MoveCursorRight => {
                    ctx.buf.move_right();
                    Cont(false)
                },
                instr::Instr::MoveCursorStart => {
                    ctx.buf.move_start();
                    Cont(false)
                },
                instr::Instr::MoveCursorEnd => {
                    ctx.buf.move_end();
                    Cont(false)
                },
                instr::Instr::HistoryPrev => {
                    if ctx.history_cursor.incr() {
                        ctx.buf.swap()
                    }
                    ctx.history_cursor.get().map(|s| ctx.buf.replace(s));
                    Cont(false)
                },
                instr::Instr::HistoryNext => {
                    if ctx.history_cursor.decr() {
                        ctx.buf.swap()
                    }
                    ctx.history_cursor.get().map(|s| ctx.buf.replace(s));
                    Cont(false)
                },
                instr::Instr::Noop => {
                    Cont(false)
                },
                instr::Instr::Cancel => Halt(Err(Error::Cancel)),
                instr::Instr::Clear => {
                    Cont(true)
                },
                instr::Instr::InsertAtCursor(text) => {
                    ctx.buf.insert_chars_at_cursor(text);
                    Cont(false)
                }
            };
            for _ in (0..len) {
                ctx.seq.remove(0);
            };
            res
        }
    };
    match res {
        Cont(clear) => EditResult::Cont(ctx.buf.get_line(ctx.prompt, clear)),
        Halt(res) => EditResult::Halt(res)
    }
}
<|endoftext|>


<commit_before>use piston_window::*;

use board::Board;
use randomizer::Randomizer;
use tetromino::*;
use settings::*;
use stats::*;

enum GameState {
    Playing,
    Paused
}

pub trait Game {
    fn on_input(&mut self, input: Input);
    fn on_update(&mut self, update_args: UpdateArgs);
    fn render(&mut self, context: Context, graphics: &mut G2d);
}

pub struct Rustris {
    board: Board,
    randomizer: Randomizer,
    current_piece: Piece,
    next_piece: Piece,
    stats: GameStats,
    time_since_moved: f64,
    time_per_tick: f64
    // state: GameState
}
impl Rustris {
    pub fn new() -> Rustris {
        let mut randomizer = Randomizer::new();
        let current_piece = randomizer.create_piece();
        let next_piece = randomizer.create_piece();
        Rustris {
            board: Board::new(),
            randomizer: randomizer,
            current_piece: current_piece,
            next_piece: next_piece,
            stats: GameStats::new(),
            time_since_moved: 0.0,
            time_per_tick: 1.0  //For testing
        }
    }

    pub fn reset(&mut self) {

    }

    pub fn get_game_stats(&self) -> &GameStats {
        &self.stats
    }

    pub fn set_current_piece(&mut self, piece: Piece) {
        self.current_piece = piece;
    }

    fn is_valid_board_position(&self, piece: &Piece) -> bool {
        // TODO: Add tests for this
        piece.blocks_iter().all(|block| {
            (block.x >= 0 && block.x < WIDTH_IN_BLOCKS &&
             block.y >= 0 && block.y < HEIGHT_IN_BLOCKS &&
             !self.board.is_space_occupied(block))
        })
    }

    fn should_lock(&self, piece: &Piece) -> bool {
        // TODO: Add tests for this
        piece.blocks_iter().any(|block| {
            block.y >= HEIGHT_IN_BLOCKS ||
            self.board.is_space_occupied(block)
        })
    }

    fn lock_current_piece(&mut self) {
        self.board.set_piece(&self.current_piece);
    }

    fn remove_completed_lines(&mut self) {
        let number_removed = self.board.remove_completed_rows();
        self.stats.score_completed_lines(number_removed);
    }

    fn calculate_ghost_piece(&self) -> Piece {
        let mut ghost = self.current_piece;
        ghost.y = HEIGHT_IN_BLOCKS - 1;
        while !self.is_valid_board_position(&ghost) {
            ghost.y -= 1;
        }
        ghost
    }

    fn get_new_piece(&mut self) {
        let next = self.next_piece;
        self.set_current_piece(next);
        self.next_piece = self.randomizer.create_piece();
    }

    fn update(&mut self) {
        let moved = self.current_piece.moved(Direction::Down);
        if self.should_lock(&moved) {
            self.lock_current_piece();
            self.remove_completed_lines();
            self.get_new_piece();
        } else {
            self.set_current_piece(moved);
        }
    }
}
impl Game for Rustris {
    fn on_input(&mut self, input: Input) {
        let mut moved: Option<Piece> = None;
        match input {
            Input::Press(key) => {
                match key {
                    Button::Keyboard(Key::Up) => {
                        moved = Some(self.current_piece.rotated());
                    }
                    Button::Keyboard(Key::Down) => {
                        let move_down = self.current_piece.moved(Direction::Down);
                        if self.is_valid_board_position(&move_down) {
                            self.time_since_moved = 0.0;
                            self.stats.score_soft_drop();
                            self.set_current_piece(move_down);
                        }
                    }
                    Button::Keyboard(Key::Left) => {
                        moved = Some(self.current_piece.moved(Direction::Left));
                    }
                    Button::Keyboard(Key::Right) => {
                        moved = Some(self.current_piece.moved(Direction::Right));
                    }
                    Button::Keyboard(Key::Space) => {
                        let ghost = self.calculate_ghost_piece();
                        let rows_dropped = (ghost.y - self.current_piece.y) as u32;
                        self.stats.score_hard_drop(rows_dropped);
                        self.set_current_piece(ghost);
                        self.lock_current_piece();
                        self.remove_completed_lines();
                        self.get_new_piece();
                    }
                    _ => {}
                }
            }
            _ => {}
        }
        if let Some(piece) = moved {
            if self.is_valid_board_position(&piece) {
                self.set_current_piece(piece);
            }
        }
    }

    fn on_update(&mut self, update_args: UpdateArgs) {
        self.time_since_moved += update_args.dt;
        // TODO: The threshold needs to be able to be updated
        // as the game progresses
        if self.time_since_moved >= self.time_per_tick {
            self.time_since_moved -= self.time_per_tick;
            self.update();
        }
    }

    fn render(&mut self, context: Context, graphics: &mut G2d) {
        self.board.render(context, graphics);
        self.calculate_ghost_piece().render_ghost(context, graphics);
        self.current_piece.render_normal(context, graphics);
    }
}

// TODO: Move this into its own module
pub fn render_square_in_grid(x: i32, y: i32, rect: Rectangle, context: Context, graphics: &mut G2d) {
    let square = rectangle::square(
        0.0, 0.0, BLOCK_SIZE - (2.0 * GRID_LINE_WIDTH)
    );
    let transform = context.transform
        // Translate the origin of the transform based on where the grid starts
        .trans(GRID_X_OFFSET, GRID_Y_OFFSET)
        // Translate the square into the first grid cell (off of the bounding box)
        .trans(GRID_LINE_WIDTH, GRID_LINE_WIDTH)
        // Translate the square into the proper grid cell based on the grid x and y position
        .trans((x as f64)*BLOCK_SIZE, (y as f64)*BLOCK_SIZE);
    rect.draw(square, &Default::default(), transform, graphics);
}
<commit_msg>remove the should_lock method since it's extraneous and moved duplicate method calls into lock_current_piece<commit_after>use piston_window::*;

use board::Board;
use randomizer::Randomizer;
use tetromino::*;
use settings::*;
use stats::*;

enum GameState {
    Playing,
    Paused
}

pub trait Game {
    fn on_input(&mut self, input: Input);
    fn on_update(&mut self, update_args: UpdateArgs);
    fn render(&mut self, context: Context, graphics: &mut G2d);
}

pub struct Rustris {
    board: Board,
    randomizer: Randomizer,
    current_piece: Piece,
    next_piece: Piece,
    stats: GameStats,
    time_since_moved: f64,
    time_per_tick: f64
    // state: GameState
}
impl Rustris {
    pub fn new() -> Rustris {
        let mut randomizer = Randomizer::new();
        let current_piece = randomizer.create_piece();
        let next_piece = randomizer.create_piece();
        Rustris {
            board: Board::new(),
            randomizer: randomizer,
            current_piece: current_piece,
            next_piece: next_piece,
            stats: GameStats::new(),
            time_since_moved: 0.0,
            time_per_tick: 1.0  //For testing
        }
    }

    pub fn reset(&mut self) {

    }

    pub fn get_game_stats(&self) -> &GameStats {
        &self.stats
    }

    pub fn set_current_piece(&mut self, piece: Piece) {
        self.current_piece = piece;
    }

    fn is_valid_board_position(&self, piece: &Piece) -> bool {
        // TODO: Add tests for this
        piece.blocks_iter().all(|block| {
            (block.x >= 0 && block.x < WIDTH_IN_BLOCKS &&
             block.y >= 0 && block.y < HEIGHT_IN_BLOCKS &&
             !self.board.is_space_occupied(block))
        })
    }

    fn lock_current_piece(&mut self) {
        self.board.set_piece(&self.current_piece);
        self.remove_completed_lines();
        self.get_new_piece();
    }

    fn remove_completed_lines(&mut self) {
        let number_removed = self.board.remove_completed_rows();
        self.stats.score_completed_lines(number_removed);
    }

    fn calculate_ghost_piece(&self) -> Piece {
        let mut ghost = self.current_piece;
        ghost.y = HEIGHT_IN_BLOCKS - 1;
        while !self.is_valid_board_position(&ghost) {
            ghost.y -= 1;
        }
        ghost
    }

    fn get_new_piece(&mut self) {
        let next = self.next_piece;
        self.set_current_piece(next);
        self.next_piece = self.randomizer.create_piece();
    }

    fn update(&mut self) {
        let moved = self.current_piece.moved(Direction::Down);
        if !self.is_valid_board_position(&moved) {
            self.lock_current_piece();
        } else {
            self.set_current_piece(moved);
        }
    }
}
impl Game for Rustris {
    fn on_input(&mut self, input: Input) {
        let mut moved: Option<Piece> = None;
        match input {
            Input::Press(key) => {
                match key {
                    Button::Keyboard(Key::Up) => {
                        moved = Some(self.current_piece.rotated());
                    }
                    Button::Keyboard(Key::Down) => {
                        let move_down = self.current_piece.moved(Direction::Down);
                        if self.is_valid_board_position(&move_down) {
                            self.time_since_moved = 0.0;
                            self.stats.score_soft_drop();
                            self.set_current_piece(move_down);
                        }
                    }
                    Button::Keyboard(Key::Left) => {
                        moved = Some(self.current_piece.moved(Direction::Left));
                    }
                    Button::Keyboard(Key::Right) => {
                        moved = Some(self.current_piece.moved(Direction::Right));
                    }
                    Button::Keyboard(Key::Space) => {
                        let ghost = self.calculate_ghost_piece();
                        let rows_dropped = (ghost.y - self.current_piece.y) as u32;
                        self.stats.score_hard_drop(rows_dropped);
                        self.set_current_piece(ghost);
                        self.lock_current_piece();
                    }
                    _ => {}
                }
            }
            _ => {}
        }
        if let Some(piece) = moved {
            if self.is_valid_board_position(&piece) {
                self.set_current_piece(piece);
            }
        }
    }

    fn on_update(&mut self, update_args: UpdateArgs) {
        self.time_since_moved += update_args.dt;
        // TODO: The threshold needs to be able to be updated
        // as the game progresses
        if self.time_since_moved >= self.time_per_tick {
            self.time_since_moved -= self.time_per_tick;
            self.update();
        }
    }

    fn render(&mut self, context: Context, graphics: &mut G2d) {
        self.board.render(context, graphics);
        self.calculate_ghost_piece().render_ghost(context, graphics);
        self.current_piece.render_normal(context, graphics);
    }
}

// TODO: Move this into its own module
pub fn render_square_in_grid(x: i32, y: i32, rect: Rectangle, context: Context, graphics: &mut G2d) {
    let square = rectangle::square(
        0.0, 0.0, BLOCK_SIZE - (2.0 * GRID_LINE_WIDTH)
    );
    let transform = context.transform
        // Translate the origin of the transform based on where the grid starts
        .trans(GRID_X_OFFSET, GRID_Y_OFFSET)
        // Translate the square into the first grid cell (off of the bounding box)
        .trans(GRID_LINE_WIDTH, GRID_LINE_WIDTH)
        // Translate the square into the proper grid cell based on the grid x and y position
        .trans((x as f64)*BLOCK_SIZE, (y as f64)*BLOCK_SIZE);
    rect.draw(square, &Default::default(), transform, graphics);
}
<|endoftext|>


<commit_before>
#![crate_name = "geoip"]
#![crate_type = "rlib"]

#![warn(non_camel_case_types,
        non_upper_case_globals,
        unused_qualifications)]
#![allow(unstable)]

extern crate libc;
extern crate "rustc-serialize" as rustc_serialize;
extern crate "geoip-sys" as geoip_sys;

use libc::{c_char, c_int, c_ulong};
use std::ffi;
use std::fmt;
use std::old_io::net::ip::{IpAddr,Ipv4Addr,Ipv6Addr};

#[cfg(test)]
use std::str::FromStr;

enum Charset {
    UTF8 = 1
}

pub enum Options {
    Standard = 0,
    MemoryCache = 1,
    CheckCache = 2,
    IndexCache = 4,
    MmapCache = 8
}

impl Copy for Options { }

pub enum DBType {
    CountryEdition = 1,
    RegionEditionRev0 = 7,
    CityEditionRev0 = 6,
    ORGEdition = 5,
    ISPEdition = 4,
    CityEditionRev1 = 2,
    RegionEditionRev1 = 3,
    ProxyEdition = 8,
    ASNUMEdition = 9,
    NetSpeedEdition = 10,
    DomainEdition = 11,
    CountryEditionV6 = 12,
    LocationAEdition = 13,
    AccuracyRadiusEdition = 14,
    LargeCountryEdition = 17,
    LargeCountryEditionV6 = 18,
    ASNumEditionV6 = 21,
    ISPEditionV6 = 22,
    ORGEditionV6 = 23,
    DomainEditionV6 = 24,
    LoctionAEditionV6 = 25,
    RegistrarEdition = 26,
    RegistrarEditionV6 = 27,
    UserTypeEdition = 28,
    UserTypeEditionV6 = 29,
    CityEditionRev1V6 = 30,
    CityEditionRev0V6 = 31,
    NetSpeedEditionRev1 = 32,
    NetSpeedEditionRev1V6 = 33,
    CountryConfEdition = 34,
    CityConfEdition = 35,
    RegionConfEdition = 36,
    PostalConfEdition = 37,
    AccuracyRadiusEditionV6 = 38
}

impl Copy for DBType { }

pub struct GeoIp {
    db: geoip_sys::RawGeoIp
}

#[derive(RustcDecodable, RustcEncodable)]
pub struct ASInfo {
    pub asn: u32,
    pub name: String,
    pub netmask: u32
}

#[derive(RustcDecodable, RustcEncodable)]
pub struct CityInfo {
    pub country_code: Option<String>,
    pub country_code3: Option<String>,
    pub country_name: Option<String>,
    pub region: Option<String>,
    pub city: Option<String>,
    pub postal_code: Option<String>,
    pub latitude: f32,
    pub longitude: f32,
    pub dma_code: u32,
    pub area_code: u32,
    pub charset: u32,
    pub continent_code: Option<String>,
    pub netmask: u32
}

fn maybe_string(c_str: *const c_char) -> Option<String> {
    if c_str.is_null() {
        None
    } else {
        String::from_utf8(unsafe { ffi::c_str_to_bytes(&c_str) }.to_vec()).ok()
    }
}

impl CityInfo {
    unsafe fn from_geoiprecord(res: &geoip_sys::GeoIpRecord) -> CityInfo {
        CityInfo {
            country_code: maybe_string(res.country_code),
            country_code3: maybe_string(res.country_code3),
            country_name: maybe_string(res.country_name),
            region: maybe_string(res.region),
            city: maybe_string(res.city),
            postal_code: maybe_string(res.postal_code),
            latitude: res.latitude as f32,
            longitude: res.longitude as f32,
            dma_code: res.dma_code as u32,
            area_code: res.area_code as u32,
            charset: res.charset as u32,
            continent_code: maybe_string(res.continent_code),
            netmask: res.netmask as u32
        }
    }
}

impl fmt::Debug for ASInfo {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}\t{}", self.asn, self.name)
    }
}

enum CNetworkIp {
    V4(c_ulong),
    V6(geoip_sys::In6Addr)
}

impl CNetworkIp {
    fn new(ip: IpAddr) -> CNetworkIp {
        match ip {
            Ipv4Addr(a, b, c, d) => {
                CNetworkIp::V4(((a as c_ulong) << 24) | ((b as c_ulong) << 16) |
                               ((c as c_ulong) << 8)  | ((d as c_ulong)))
            },
            Ipv6Addr(a, b, c, d, e, f, g, h) => {
                CNetworkIp::V6([(a >> 8) as u8, a as u8,
                                (b >> 8) as u8, b as u8,
                                (c >> 8) as u8, c as u8,
                                (d >> 8) as u8, d as u8,
                                (e >> 8) as u8, e as u8,
                                (f >> 8) as u8, f as u8,
                                (g >> 8) as u8, g as u8,
                                (h >> 8) as u8, h as u8])
            }
        }
    }
}

impl GeoIp {
    pub fn open(path: &Path, options: Options) -> Result<GeoIp, String> {
        let file = match path.as_str() {
            None => return Err(format!("Invalid path {}", path.display())),
            Some(file) => file
        };
        let db = unsafe {
            geoip_sys::GeoIP_open(ffi::CString::from_slice(file.as_bytes()).as_ptr(),
                                  options as c_int)
        };
        if db.is_null() {
            return Err(format!("Can't open {}", file));
        }
        if unsafe { geoip_sys::GeoIP_set_charset(db, Charset::UTF8 as c_int)
        } != 0 {
            return Err("Can't set charset to UTF8".to_string());
        }
        Ok(GeoIp { db: db })
    }

    pub fn city_info_by_ip(&self, ip: IpAddr) -> Option<CityInfo> {
        let cres = match CNetworkIp::new(ip) {
            CNetworkIp::V4(ip) => unsafe {
                geoip_sys::GeoIP_record_by_ipnum(self.db, ip) },
            CNetworkIp::V6(ip) => unsafe {
                geoip_sys::GeoIP_record_by_ipnum_v6(self.db, ip) }
        };

        if cres.is_null() { return None; }

        unsafe {
            let city_info = CityInfo::from_geoiprecord(&*cres);
            geoip_sys::GeoIPRecord_delete(cres);
            std::mem::forget(cres);
            Some(city_info)
        }
    }

    pub fn as_info_by_ip(&self, ip: IpAddr) -> Option<ASInfo> {
        let mut gl = geoip_sys::GeoIpLookup::new();
        let cres = match CNetworkIp::new(ip) {
            CNetworkIp::V4(ip) => unsafe {
                geoip_sys::GeoIP_name_by_ipnum_gl(self.db, ip, &mut gl) },
            CNetworkIp::V6(ip) => unsafe {
                geoip_sys::GeoIP_name_by_ipnum_v6_gl(self.db, ip, &mut gl) }
        };

        if cres.is_null() {
            return None;
        }
        let description = match maybe_string(cres) {
            None => return None,
            Some(description) => description
        };
        let mut di = description.splitn(1, ' ');
        let asn = match di.next() {
            None => return None,
            Some(asn) => {
                if ! asn.starts_with("AS") {
                    return None
                } else {
                    asn[2..].parse::<u32>().unwrap()
                }
            }
        };
        let name = di.next().unwrap_or("(none)");
        let as_info = ASInfo {
            asn: asn,
            name: name.to_string(),
            netmask: gl.netmask as u32
        };
        Some(as_info)
    }
}

impl Drop for GeoIp {
    fn drop(&mut self) {
        unsafe {
            geoip_sys::GeoIP_delete(self.db);
        }
    }
}

#[test]
fn geoip_test_basic() {
    let geoip = match GeoIp::open(&Path::new("/opt/geoip/GeoIPASNum.dat"), Options::MemoryCache) {
        Err(err) => panic!(err),
        Ok(geoip) => geoip
    };
    let ip = FromStr::from_str("91.203.184.192").unwrap();
    let res = geoip.as_info_by_ip(ip).unwrap();
    assert!(res.asn == 41064);
    assert!(res.name.as_slice().contains("Telefun"));
    assert!(res.netmask == 22);
}

#[test]
fn geoip_test_city() {
    let geoip = match GeoIp::open(&Path::new("/opt/geoip/GeoLiteCity.dat"), Options::MemoryCache) {
        Err(err) => panic!(err),
        Ok(geoip) => geoip
    };
    let ip = FromStr::from_str("8.8.8.8").unwrap();
    let res = geoip.city_info_by_ip(ip).unwrap();
    assert!(res.city.unwrap().as_slice() == "Mountain View");
}
<commit_msg>Some required #![feature]<commit_after>
#![crate_name = "geoip"]
#![crate_type = "rlib"]

#![warn(non_camel_case_types,
        non_upper_case_globals,
        unused_qualifications)]
#![feature(libc, path, std_misc)]

extern crate libc;
extern crate "rustc-serialize" as rustc_serialize;
extern crate "geoip-sys" as geoip_sys;

use libc::{c_char, c_int, c_ulong};
use std::ffi;
use std::fmt;
use std::old_io::net::ip::{IpAddr,Ipv4Addr,Ipv6Addr};

#[cfg(test)]
use std::str::FromStr;

enum Charset {
    UTF8 = 1
}

pub enum Options {
    Standard = 0,
    MemoryCache = 1,
    CheckCache = 2,
    IndexCache = 4,
    MmapCache = 8
}

impl Copy for Options { }

pub enum DBType {
    CountryEdition = 1,
    RegionEditionRev0 = 7,
    CityEditionRev0 = 6,
    ORGEdition = 5,
    ISPEdition = 4,
    CityEditionRev1 = 2,
    RegionEditionRev1 = 3,
    ProxyEdition = 8,
    ASNUMEdition = 9,
    NetSpeedEdition = 10,
    DomainEdition = 11,
    CountryEditionV6 = 12,
    LocationAEdition = 13,
    AccuracyRadiusEdition = 14,
    LargeCountryEdition = 17,
    LargeCountryEditionV6 = 18,
    ASNumEditionV6 = 21,
    ISPEditionV6 = 22,
    ORGEditionV6 = 23,
    DomainEditionV6 = 24,
    LoctionAEditionV6 = 25,
    RegistrarEdition = 26,
    RegistrarEditionV6 = 27,
    UserTypeEdition = 28,
    UserTypeEditionV6 = 29,
    CityEditionRev1V6 = 30,
    CityEditionRev0V6 = 31,
    NetSpeedEditionRev1 = 32,
    NetSpeedEditionRev1V6 = 33,
    CountryConfEdition = 34,
    CityConfEdition = 35,
    RegionConfEdition = 36,
    PostalConfEdition = 37,
    AccuracyRadiusEditionV6 = 38
}

impl Copy for DBType { }

pub struct GeoIp {
    db: geoip_sys::RawGeoIp
}

#[derive(RustcDecodable, RustcEncodable)]
pub struct ASInfo {
    pub asn: u32,
    pub name: String,
    pub netmask: u32
}

#[derive(RustcDecodable, RustcEncodable)]
pub struct CityInfo {
    pub country_code: Option<String>,
    pub country_code3: Option<String>,
    pub country_name: Option<String>,
    pub region: Option<String>,
    pub city: Option<String>,
    pub postal_code: Option<String>,
    pub latitude: f32,
    pub longitude: f32,
    pub dma_code: u32,
    pub area_code: u32,
    pub charset: u32,
    pub continent_code: Option<String>,
    pub netmask: u32
}

fn maybe_string(c_str: *const c_char) -> Option<String> {
    if c_str.is_null() {
        None
    } else {
        String::from_utf8(unsafe { ffi::c_str_to_bytes(&c_str) }.to_vec()).ok()
    }
}

impl CityInfo {
    unsafe fn from_geoiprecord(res: &geoip_sys::GeoIpRecord) -> CityInfo {
        CityInfo {
            country_code: maybe_string(res.country_code),
            country_code3: maybe_string(res.country_code3),
            country_name: maybe_string(res.country_name),
            region: maybe_string(res.region),
            city: maybe_string(res.city),
            postal_code: maybe_string(res.postal_code),
            latitude: res.latitude as f32,
            longitude: res.longitude as f32,
            dma_code: res.dma_code as u32,
            area_code: res.area_code as u32,
            charset: res.charset as u32,
            continent_code: maybe_string(res.continent_code),
            netmask: res.netmask as u32
        }
    }
}

impl fmt::Debug for ASInfo {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}\t{}", self.asn, self.name)
    }
}

enum CNetworkIp {
    V4(c_ulong),
    V6(geoip_sys::In6Addr)
}

impl CNetworkIp {
    fn new(ip: IpAddr) -> CNetworkIp {
        match ip {
            Ipv4Addr(a, b, c, d) => {
                CNetworkIp::V4(((a as c_ulong) << 24) | ((b as c_ulong) << 16) |
                               ((c as c_ulong) << 8)  | ((d as c_ulong)))
            },
            Ipv6Addr(a, b, c, d, e, f, g, h) => {
                CNetworkIp::V6([(a >> 8) as u8, a as u8,
                                (b >> 8) as u8, b as u8,
                                (c >> 8) as u8, c as u8,
                                (d >> 8) as u8, d as u8,
                                (e >> 8) as u8, e as u8,
                                (f >> 8) as u8, f as u8,
                                (g >> 8) as u8, g as u8,
                                (h >> 8) as u8, h as u8])
            }
        }
    }
}

impl GeoIp {
    pub fn open(path: &Path, options: Options) -> Result<GeoIp, String> {
        let file = match path.as_str() {
            None => return Err(format!("Invalid path {}", path.display())),
            Some(file) => file
        };
        let db = unsafe {
            geoip_sys::GeoIP_open(ffi::CString::from_slice(file.as_bytes()).as_ptr(),
                                  options as c_int)
        };
        if db.is_null() {
            return Err(format!("Can't open {}", file));
        }
        if unsafe { geoip_sys::GeoIP_set_charset(db, Charset::UTF8 as c_int)
        } != 0 {
            return Err("Can't set charset to UTF8".to_string());
        }
        Ok(GeoIp { db: db })
    }

    pub fn city_info_by_ip(&self, ip: IpAddr) -> Option<CityInfo> {
        let cres = match CNetworkIp::new(ip) {
            CNetworkIp::V4(ip) => unsafe {
                geoip_sys::GeoIP_record_by_ipnum(self.db, ip) },
            CNetworkIp::V6(ip) => unsafe {
                geoip_sys::GeoIP_record_by_ipnum_v6(self.db, ip) }
        };

        if cres.is_null() { return None; }

        unsafe {
            let city_info = CityInfo::from_geoiprecord(&*cres);
            geoip_sys::GeoIPRecord_delete(cres);
            std::mem::forget(cres);
            Some(city_info)
        }
    }

    pub fn as_info_by_ip(&self, ip: IpAddr) -> Option<ASInfo> {
        let mut gl = geoip_sys::GeoIpLookup::new();
        let cres = match CNetworkIp::new(ip) {
            CNetworkIp::V4(ip) => unsafe {
                geoip_sys::GeoIP_name_by_ipnum_gl(self.db, ip, &mut gl) },
            CNetworkIp::V6(ip) => unsafe {
                geoip_sys::GeoIP_name_by_ipnum_v6_gl(self.db, ip, &mut gl) }
        };

        if cres.is_null() {
            return None;
        }
        let description = match maybe_string(cres) {
            None => return None,
            Some(description) => description
        };
        let mut di = description.splitn(1, ' ');
        let asn = match di.next() {
            None => return None,
            Some(asn) => {
                if ! asn.starts_with("AS") {
                    return None
                } else {
                    asn[2..].parse::<u32>().unwrap()
                }
            }
        };
        let name = di.next().unwrap_or("(none)");
        let as_info = ASInfo {
            asn: asn,
            name: name.to_string(),
            netmask: gl.netmask as u32
        };
        Some(as_info)
    }
}

impl Drop for GeoIp {
    fn drop(&mut self) {
        unsafe {
            geoip_sys::GeoIP_delete(self.db);
        }
    }
}

#[test]
fn geoip_test_basic() {
    let geoip = match GeoIp::open(&Path::new("/opt/geoip/GeoIPASNum.dat"), Options::MemoryCache) {
        Err(err) => panic!(err),
        Ok(geoip) => geoip
    };
    let ip = FromStr::from_str("91.203.184.192").unwrap();
    let res = geoip.as_info_by_ip(ip).unwrap();
    assert!(res.asn == 41064);
    assert!(res.name.as_slice().contains("Telefun"));
    assert!(res.netmask == 22);
}

#[test]
fn geoip_test_city() {
    let geoip = match GeoIp::open(&Path::new("/opt/geoip/GeoLiteCity.dat"), Options::MemoryCache) {
        Err(err) => panic!(err),
        Ok(geoip) => geoip
    };
    let ip = FromStr::from_str("8.8.8.8").unwrap();
    let res = geoip.city_info_by_ip(ip).unwrap();
    assert!(res.city.unwrap().as_slice() == "Mountain View");
}
<|endoftext|>


<commit_before>//! Functions for creating and evaluating [Haar-like features](https://en.wikipedia.org/wiki/Haar-like_features).

use image::{
    GenericImage,
    Luma
};
use itertools::Itertools;
use std::collections::HashMap;
use std::ops::Mul;

/// A Haar filter whose value on an integral image is the weighted sum
/// of the values of the integral image at the given points.
// TODO: these structs are pretty big. Look into instead just storing
// TODO: the offsets between sample points. We should only need 10 bytes/filter,
// TODO: meaning we could fit a typical cascade in L1 cache.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct HaarFilter {
    points: [u32; 18],
    weights: [i8; 9],
    count: usize
}

/// Whether the top left region in a Haar filter is counted
/// with positive or negative sign.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Sign { Positive, Negative }

impl HaarFilter {

    /// Returns the following feature (with signs reversed if Sign == Sign::Negative).
    ///

    ///     A   B   C
    ///       +   -
    ///     D   E   F
    ///

    /// A = (top, left), B.x = left + dx1, C.x = B.x + dx2 and D.y = A.y + dy.
    /// Given an integral image I, the value of this feature is
    /// sign * (I(A) - 2I(B) + I(C) - I(D) + 2I(E) - I(F)).
    pub fn two_region_horizontal(top: u32, left: u32, dx1: u32, dx2: u32, dy: u32, sign: Sign)
        -> HaarFilter {

        combine_alternating(&[
            eval_points(top, left,       dx1, dy),
            eval_points(top, left + dx1, dx2, dy)]) * multiplier(sign)
    }

    /// If Sign is Positive then returns the following Haar feature.
    ///

    ///     A   B
    ///       +
    ///     C   D
    ///       -
    ///     E   F
    ///

    /// If Sign is Negative then the + and - signs are reversed.
    /// The distance between A and B is dx, between A and C is dy1,
    /// and between C and E is dy2.
    ///
    /// Given an integral image I, the value of this feature is
    /// I(A) - I(B) - 2I(C) + 2I(D) + I(E) - I(F) multipled by sign.
    pub fn two_region_vertical(top: u32, left: u32, dx: u32, dy1: u32, dy2: u32, sign: Sign)
        -> HaarFilter {

        combine_alternating(&[
            eval_points(top,       left, dx, dy1),
            eval_points(top + dy1, left, dx, dy2)]) * multiplier(sign)
    }

    /// If Sign is Positive then returns the following Haar feature.
    ///

    ///     A   B   C   D
    ///       +   -   +
    ///     E   F   G   H
    ///

    /// If Sign is Negative then the + and - signs are reversed.
    /// The distance between A and B is dx1, between B and C is dx2, between
    /// C and D is dx3, and between A and E is dy.
    ///
    /// Given an integral image I, the value of this feature is
    /// I(A) - 2I(B) + 2I(C) - I(D) - I(E) + 2I(F) - 2I(G) + I(H) multipled by sign.
    pub fn three_region_horizontal(
        top: u32, left: u32, dx1: u32, dx2: u32, dx3: u32, dy: u32, sign: Sign)
            -> HaarFilter {

        combine_alternating(&[
            eval_points(top, left,             dx1, dy),
            eval_points(top, left + dx1,       dx2, dy),
            eval_points(top, left + dx1 + dx2, dx3, dy),
            ]) * multiplier(sign)
    }

    /// If Sign is Positive then returns the following Haar feature.
    ///

    ///     A   B
    ///       +
    ///     C   D
    ///       -
    ///     E   F
    ///       +
    ///     G   H
    ///

    /// If Sign is Negative then the + and - signs are reversed.
    /// The distance between A and B is dx, between A and C is dy1, between
    /// C and E is dy2, and between E and G is dy3.
    ///
    /// Given an integral image I, the value of this feature is
    /// I(A) - I(B) - 2I(C) + 2I(D) + 2I(E) - 2I(F) - I(G) + I(H) multiplied by sign.
    pub fn three_region_vertical(
        top: u32, left: u32, dx: u32, dy1: u32, dy2: u32, dy3: u32, sign: Sign)
            -> HaarFilter {

        combine_alternating(&[
            eval_points(top,             left, dx, dy1),
            eval_points(top + dy1,       left, dx, dy2),
            eval_points(top + dy1 + dy2, left, dx, dy3),
            ]) * multiplier(sign)
    }

    /// If Sign is Positive then returns the following Haar feature.
    ///

    ///     A   B   C
    ///       +   -
    ///     D   E   F
    ///       -   +
    ///     G   H   I
    ///

    /// If Sign is Negative then the + and - signs are reversed.
    /// The distance between A and B is dx1, between B and C is dx1, between
    /// A and D is dy1, and between D and G is dy2.
    ///
    /// Given an integral image I, the value of this feature is
    /// I(A) - 2I(B) + I(C) - 2I(D) + 4I(E) - 2I(F) + I(G) - 2I(H) + I(I) multiplied by sign.
    pub fn four_region(
        top: u32, left: u32, dx1: u32, dx2: u32, dy1: u32, dy2: u32, sign: Sign)
            -> HaarFilter {

        combine_alternating(&[
            eval_points(top,       left,       dx1, dy1),
            eval_points(top,       left + dx1, dx2, dy1),
            eval_points(top + dy1, left,       dx1, dy2),
            eval_points(top + dy1, left + dx1, dx2, dy2),
            ]) * multiplier(sign)
    }

    /// Evaluates the Haar filter on an integral image.
    pub fn evaluate(&self, integral: &I ) -> i32
        where I: GenericImage<Pixel=Luma<u32>> {

        let mut sum = 0i32;
        for i in 0..self.count {
            let p = integral.get_pixel(self.points[2 * i], self.points[2 * i + 1])[0];
            sum += p as i32 * self.weights[i] as i32;
        }
        sum
    }
}

/// See comment on eval_points.
struct EvalPoints {
    points: [(u32, u32); 4],
    weights: [i8; 4]
}

impl EvalPoints {
    fn new(points: [(u32, u32); 4], weights: [i8; 4]) -> EvalPoints {
        EvalPoints { points: points, weights: weights }
    }
}

impl Mul<i8> for HaarFilter {
    type Output = HaarFilter;

    fn mul(self, rhs: i8) -> HaarFilter {
        let mut copy = self;
        for i in 0..copy.weights.len() {
            copy.weights[i] *= rhs;
        }
        copy
    }
}

/// Points at which to evaluate an integral image to produce the sum of the
/// pixel intensities of all points within a rectangle. Only valid when the
/// rectangle is wholly contained in the image boundaries. The only non-trivial
/// cases are when the rectangle touches the left or top image boundaries. In this
/// case we need to evaluate fewer than four points, and return weights of zero
/// for the other points.
fn eval_points(top: u32, left: u32, width: u32, height: u32) -> EvalPoints {

    let mut points = [
        (0u32, 0u32),
        (0u32, 0u32),
        (0u32, 0u32),
        (left + width - 1, top + height - 1)];

    let mut weights = [0i8, 0i8, 0i8, 1i8];

    if top > 0 && left > 0 {
        points[0] = (left - 1, top - 1);
        weights[0] = 1i8;
    }
    if top > 0 {
        points[1] = (left + width - 1, top - 1);
        weights[1] = -1i8;
    }
    if left > 0 {
        points[2] = (left - 1, top + height - 1);
        weights[2] = -1i8;
    }

    EvalPoints::new(points, weights)
}

/// Combine sets of evaluation points with alternating signs.
/// The first entry of rects is counted with positive sign.
// TODO: check that we don't have too many distinct points. This
// TODO: function isn't exported, so we just need to check the HaarFilter uses
// TODO: haven't messed anything up.
fn combine_alternating(rects: &[EvalPoints]) -> HaarFilter {

    // Aggregate weights of all points, remove any with zero weight, and
    // order lexicographically by location.
    let mut sign = 1i8;
    let sorted_points = rects
        .iter()
        .fold(HashMap::new(), |mut acc, rect| {
            for i in 0..4 {
                *acc.entry(rect.points[i]).or_insert(0) += sign * rect.weights[i];
            }
            sign *= -1i8;
            acc
            })
        .into_iter()
        .filter(|kv| kv.1 != 0)
        .sorted_by(|a, b| Ord::cmp(&((a.0).1, (a.0).0), &((b.0).1, (b.0).0)));

    let mut count = 0;
    let mut points = [0u32; 18];
    let mut weights = [0i8; 9];

    for pw in sorted_points {
        points[2 * count] = (pw.0).0;
        points[2 * count + 1] = (pw.0).1;
        weights[count] = pw.1;
        count += 1;
    }

    HaarFilter {
        points: points,
        weights: weights,
        count: count }
}

fn multiplier(sign: Sign) -> i8 {
    if sign == Sign::Positive {1} else {-1}
}

#[cfg(test)]
mod test {

    use super::{
        combine_alternating,
        EvalPoints,
        HaarFilter,
        Sign
    };
    use image::{
        ImageBuffer
    };
    use integralimage::{
        integral_image
    };

    #[test]
    fn test_combine_alternating() {
        // Three region horizontally aligned filter:
        // A   B   C   D
        //   +   -   +
        // E   F   G   H
        // I(A) - 2I(B) + 2I(C) - I(D) - I(E) + 2I(F) - 2I(G) + I(H).

        let a = (0, 0);
        let b = (1, 0);
        let c = (2, 0);
        let d = (3, 0);
        let e = (0, 1);
        let f = (1, 1);
        let g = (2, 1);
        let h = (3, 1);

        let left  = EvalPoints::new([a, b, e, f], [1, -1, -1, 1]);
        let mid   = EvalPoints::new([b, c, f, g], [1, -1, -1, 1]);
        let right = EvalPoints::new([c, d, g, h], [1, -1, -1, 1]);

        let filter = combine_alternating(&[left, mid, right]);
        let expected = HaarFilter {
            points: [0, 0, 1, 0, 2, 0, 3, 0, 0, 1, 1, 1, 2, 1, 3, 1, 0, 0],
            weights: [1, -2, 2, -1, -1, 2, -2, 1, 0],
            count: 8
        };

        assert_eq!(filter, expected);
    }

    #[test]
    fn test_two_region_horizontal() {
        // Two region horizontally aligned filter:
        // A   B   C
        //   +   -
        // D   E   F
        // I(A) - 2I(B) + I(C) - I(D) + 2I(E) - I(F)
        let image = ImageBuffer::from_raw(5, 5, vec![
            1u8,     2u8, 3u8,     4u8,     5u8,
                 /***+++++++++*****-----***/
            6u8, /**/7u8, 8u8,/**/ 9u8, /**/0u8,
            9u8, /**/8u8, 7u8,/**/ 6u8, /**/5u8,
            4u8, /**/3u8, 2u8,/**/ 1u8, /**/0u8,
                 /***+++++++++*****-----***/
            6u8, 5u8, 4u8, 2u8, 1u8]).unwrap();

        let integral = integral_image(&image);

        let filter = HaarFilter::two_region_horizontal(
            1, 1, 2u32, 1u32, 3u32, Sign::Positive);

        let value = filter.evaluate(&integral);

        assert_eq!(value, 19i32);
    }
}
<commit_msg>Improve Haar doc comments<commit_after>//! Functions for creating and evaluating [Haar-like features](https://en.wikipedia.org/wiki/Haar-like_features).

use image::{
    GenericImage,
    Luma
};
use itertools::Itertools;
use std::collections::HashMap;
use std::ops::Mul;

/// Whether the top left region in a Haar filter is counted
/// with positive or negative sign.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Sign { Positive, Negative }

/// A Haar filter whose value on an integral image is the weighted sum
/// of the values of the integral image at the given points.
// TODO: these structs are pretty big. Look into instead just storing
// TODO: the offsets between sample points. We should only need 10 bytes/filter,
// TODO: meaning we could fit a typical cascade in L1 cache.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct HaarFilter {
    points: [u32; 18],
    weights: [i8; 9],
    count: usize
}

impl HaarFilter {

    /// Evaluates the Haar filter on an integral image.
    pub fn evaluate(&self, integral: &I ) -> i32
        where I: GenericImage<Pixel=Luma<u32>> {

        let mut sum = 0i32;
        for i in 0..self.count {
            let p = integral.get_pixel(self.points[2 * i], self.points[2 * i + 1])[0];
            sum += p as i32 * self.weights[i] as i32;
        }
        sum
    }

    /// Returns the following feature (with signs reversed if Sign == Sign::Negative).
    ///

    ///     A   B   C
    ///       +   -
    ///     D   E   F
    ///

    /// A = (top, left), B.x = left + dx1, C.x = B.x + dx2, and D.y = A.y + dy.
    pub fn two_region_horizontal(top: u32, left: u32, dx1: u32, dx2: u32, dy: u32, sign: Sign)
        -> HaarFilter {

        combine_alternating(&[
            eval_points(top, left,       dx1, dy),
            eval_points(top, left + dx1, dx2, dy)]) * multiplier(sign)
    }

    /// Returns the following feature (with signs reversed if Sign == Sign::Negative).
    ///

    ///     A   B
    ///       +
    ///     C   D
    ///       -
    ///     E   F
    ///

    /// A = (top, left), B.x = left + dx, C.y = top + dy1, and E.y = C.y + dy2.
    pub fn two_region_vertical(top: u32, left: u32, dx: u32, dy1: u32, dy2: u32, sign: Sign)
        -> HaarFilter {

        combine_alternating(&[
            eval_points(top,       left, dx, dy1),
            eval_points(top + dy1, left, dx, dy2)]) * multiplier(sign)
    }

    /// Returns the following feature (with signs reversed if Sign == Sign::Negative).
    ///

    ///     A   B   C   D
    ///       +   -   +
    ///     E   F   G   H
    ///

    /// A = (top, left), B.x = left + dx1, C.x = B.x + dx2, D.x = C.x + dx3, and E.y = top + dy.
    pub fn three_region_horizontal(
        top: u32, left: u32, dx1: u32, dx2: u32, dx3: u32, dy: u32, sign: Sign)
            -> HaarFilter {

        combine_alternating(&[
            eval_points(top, left,             dx1, dy),
            eval_points(top, left + dx1,       dx2, dy),
            eval_points(top, left + dx1 + dx2, dx3, dy),
            ]) * multiplier(sign)
    }

    /// Returns the following feature (with signs reversed if Sign == Sign::Negative).
    ///

    ///     A   B
    ///       +
    ///     C   D
    ///       -
    ///     E   F
    ///       +
    ///     G   H
    ///

    /// A = (top, left), B.x = left + dx, C.y = top + dy1, E.y = C.y + dy2, and G.y = E.y + dy3.
    pub fn three_region_vertical(
        top: u32, left: u32, dx: u32, dy1: u32, dy2: u32, dy3: u32, sign: Sign)
            -> HaarFilter {

        combine_alternating(&[
            eval_points(top,             left, dx, dy1),
            eval_points(top + dy1,       left, dx, dy2),
            eval_points(top + dy1 + dy2, left, dx, dy3),
            ]) * multiplier(sign)
    }

    /// Returns the following feature (with signs reversed if Sign == Sign::Negative).
    ///

    ///     A   B   C
    ///       +   -
    ///     D   E   F
    ///       -   +
    ///     G   H   I
    ///

    /// A = (top, left), B.x = left + dx1, C.x = B.x + dx2, D.y = top + dy1, and G.y = D.y + dy2.
    pub fn four_region(
        top: u32, left: u32, dx1: u32, dx2: u32, dy1: u32, dy2: u32, sign: Sign)
            -> HaarFilter {

        combine_alternating(&[
            eval_points(top,       left,       dx1, dy1),
            eval_points(top,       left + dx1, dx2, dy1),
            eval_points(top + dy1, left,       dx1, dy2),
            eval_points(top + dy1, left + dx1, dx2, dy2),
            ]) * multiplier(sign)
    }
}

/// See comment on eval_points.
struct EvalPoints {
    points: [(u32, u32); 4],
    weights: [i8; 4]
}

impl EvalPoints {
    fn new(points: [(u32, u32); 4], weights: [i8; 4]) -> EvalPoints {
        EvalPoints { points: points, weights: weights }
    }
}

impl Mul<i8> for HaarFilter {
    type Output = HaarFilter;

    fn mul(self, rhs: i8) -> HaarFilter {
        let mut copy = self;
        for i in 0..copy.weights.len() {
            copy.weights[i] *= rhs;
        }
        copy
    }
}

/// Points at which to evaluate an integral image to produce the sum of the
/// pixel intensities of all points within a rectangle. Only valid when the
/// rectangle is wholly contained in the image boundaries. The only non-trivial
/// cases are when the rectangle touches the left or top image boundaries. In this
/// case we need to evaluate fewer than four points, and return weights of zero
/// for the other points.
fn eval_points(top: u32, left: u32, width: u32, height: u32) -> EvalPoints {

    let mut points = [
        (0u32, 0u32),
        (0u32, 0u32),
        (0u32, 0u32),
        (left + width - 1, top + height - 1)];

    let mut weights = [0i8, 0i8, 0i8, 1i8];

    if top > 0 && left > 0 {
        points[0] = (left - 1, top - 1);
        weights[0] = 1i8;
    }
    if top > 0 {
        points[1] = (left + width - 1, top - 1);
        weights[1] = -1i8;
    }
    if left > 0 {
        points[2] = (left - 1, top + height - 1);
        weights[2] = -1i8;
    }

    EvalPoints::new(points, weights)
}

/// Combine sets of evaluation points with alternating signs.
/// The first entry of rects is counted with positive sign.
// TODO: check that we don't have too many distinct points. This
// TODO: function isn't exported, so we just need to check the HaarFilter uses
// TODO: haven't messed anything up.
fn combine_alternating(rects: &[EvalPoints]) -> HaarFilter {

    // Aggregate weights of all points, remove any with zero weight, and
    // order lexicographically by location.
    let mut sign = 1i8;
    let sorted_points = rects
        .iter()
        .fold(HashMap::new(), |mut acc, rect| {
            for i in 0..4 {
                *acc.entry(rect.points[i]).or_insert(0) += sign * rect.weights[i];
            }
            sign *= -1i8;
            acc
            })
        .into_iter()
        .filter(|kv| kv.1 != 0)
        .sorted_by(|a, b| Ord::cmp(&((a.0).1, (a.0).0), &((b.0).1, (b.0).0)));

    let mut count = 0;
    let mut points = [0u32; 18];
    let mut weights = [0i8; 9];

    for pw in sorted_points {
        points[2 * count] = (pw.0).0;
        points[2 * count + 1] = (pw.0).1;
        weights[count] = pw.1;
        count += 1;
    }

    HaarFilter {
        points: points,
        weights: weights,
        count: count }
}

fn multiplier(sign: Sign) -> i8 {
    if sign == Sign::Positive {1} else {-1}
}

#[cfg(test)]
mod test {

    use super::{
        combine_alternating,
        EvalPoints,
        HaarFilter,
        Sign
    };
    use image::{
        ImageBuffer
    };
    use integralimage::{
        integral_image
    };

    #[test]
    fn test_combine_alternating() {
        // Three region horizontally aligned filter:
        // A   B   C   D
        //   +   -   +
        // E   F   G   H
        // I(A) - 2I(B) + 2I(C) - I(D) - I(E) + 2I(F) - 2I(G) + I(H).

        let a = (0, 0);
        let b = (1, 0);
        let c = (2, 0);
        let d = (3, 0);
        let e = (0, 1);
        let f = (1, 1);
        let g = (2, 1);
        let h = (3, 1);

        let left  = EvalPoints::new([a, b, e, f], [1, -1, -1, 1]);
        let mid   = EvalPoints::new([b, c, f, g], [1, -1, -1, 1]);
        let right = EvalPoints::new([c, d, g, h], [1, -1, -1, 1]);

        let filter = combine_alternating(&[left, mid, right]);
        let expected = HaarFilter {
            points: [0, 0, 1, 0, 2, 0, 3, 0, 0, 1, 1, 1, 2, 1, 3, 1, 0, 0],
            weights: [1, -2, 2, -1, -1, 2, -2, 1, 0],
            count: 8
        };

        assert_eq!(filter, expected);
    }

    #[test]
    fn test_two_region_horizontal() {
        // Two region horizontally aligned filter:
        // A   B   C
        //   +   -
        // D   E   F
        // I(A) - 2I(B) + I(C) - I(D) + 2I(E) - I(F)
        let image = ImageBuffer::from_raw(5, 5, vec![
            1u8,     2u8, 3u8,     4u8,     5u8,
                 /***+++++++++*****-----***/
            6u8, /**/7u8, 8u8,/**/ 9u8, /**/0u8,
            9u8, /**/8u8, 7u8,/**/ 6u8, /**/5u8,
            4u8, /**/3u8, 2u8,/**/ 1u8, /**/0u8,
                 /***+++++++++*****-----***/
            6u8, 5u8, 4u8, 2u8, 1u8]).unwrap();

        let integral = integral_image(&image);

        let filter = HaarFilter::two_region_horizontal(
            1, 1, 2u32, 1u32, 3u32, Sign::Positive);

        let value = filter.evaluate(&integral);

        assert_eq!(value, 19i32);
    }
}
<|endoftext|>


<commit_before>use util::{ZOPFLI_WINDOW_MASK, ZOPFLI_MIN_MATCH, ZOPFLI_WINDOW_SIZE};

const HASH_SHIFT: i32 = 5;
const HASH_MASK: i32 = 32767;

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Which {
    Hash1,
    Hash2,
}

pub struct SmallerHashThing {
    prev: u16, /* Index to index of prev. occurrence of same hash. */
    hashval: i32, /* Index to hash value at this index. */
}

pub struct HashThing {
    head: Vec<i32>,  /* Hash value to index of its most recent occurrence. */
    prev_and_hashval: Vec<SmallerHashThing>,
    val: i32,  /* Current hash value. */
}

impl HashThing {
    fn new() -> HashThing {
        HashThing {
            head: vec![-1; 65536],
            prev_and_hashval: (0..ZOPFLI_WINDOW_SIZE as u16).map(|p| {
                SmallerHashThing {
                    prev: p,
                    hashval: -1,
                }
            }).collect(),
            val: 0,
        }
    }

    fn reset(&mut self) {
        self.val = 0;
        self.head.clear();
        self.head.resize(65536, -1);
        self.prev_and_hashval = (0..ZOPFLI_WINDOW_SIZE as u16).map(|p| {
            SmallerHashThing {
                prev: p,
                hashval: -1,
            }
        }).collect();
    }

    fn update(&mut self, hpos: usize) {
        let hashval = self.val;
        let index = self.val as usize;
        let head_index = self.head[index];
        let prev = if head_index != -1 && self.prev_and_hashval[head_index as usize].hashval == self.val {
            head_index as u16
        } else {
            hpos as u16
        };

        self.prev_and_hashval[hpos] = SmallerHashThing {
            prev: prev,
            hashval: hashval,
        };
        self.head[index] = hpos as i32;
    }
}

pub struct ZopfliHash {
    hash1: HashThing,
    hash2: HashThing,
    pub same: [u16; ZOPFLI_WINDOW_SIZE],  /* Amount of repetitions of same byte after this .*/
}

impl ZopfliHash {
    pub fn new() -> ZopfliHash {
        ZopfliHash {
            hash1: HashThing::new(),
            hash2: HashThing::new(),
            same: [0; ZOPFLI_WINDOW_SIZE],
        }
    }

    pub fn reset(&mut self) {
        self.hash1.reset();
        self.hash2.reset();
        self.same = [0; ZOPFLI_WINDOW_SIZE];
    }

    pub fn warmup(&mut self, arr: &[u8], pos: usize, end: usize) {
        let c = arr[pos];
        self.update_val(c);

        if pos + 1 < end {
            let c = arr[pos + 1];
            self.update_val(c);
        }
    }

    /// Update the sliding hash value with the given byte. All calls to this function
    /// must be made on consecutive input characters. Since the hash value exists out
    /// of multiple input bytes, a few warmups with this function are needed initially.
    fn update_val(&mut self, c: u8) {
        self.hash1.val = ((self.hash1.val << HASH_SHIFT) ^ c as i32) & HASH_MASK;
    }

    pub fn update(&mut self, array: &[u8], pos: usize) {
        let hash_value = array.get(pos + ZOPFLI_MIN_MATCH - 1).cloned().unwrap_or(0);
        self.update_val(hash_value);

        let hpos = pos & ZOPFLI_WINDOW_MASK;

        self.hash1.update(hpos);

        // Update "same".
        let mut amount = 0;
        let same_index = pos.wrapping_sub(1) & ZOPFLI_WINDOW_MASK;
        let same = self.same[same_index];
        if same > 1 {
            amount = same - 1;
        }

        self.same[hpos] = amount;

        self.hash2.val = ((amount.wrapping_sub(ZOPFLI_MIN_MATCH as u16) & 255) ^ self.hash1.val as u16) as i32;

        self.hash2.update(hpos);
    }

    pub fn head_at(&self, index: usize, which: Which) -> i32 {
        match which {
            Which::Hash1 => self.hash1.head[index],
            Which::Hash2 => self.hash2.head[index],
        }
    }

    pub fn prev_at(&self, index: usize, which: Which) -> u16 {
        match which {
            Which::Hash1 => self.hash1.prev_and_hashval[index].prev,
            Which::Hash2 => self.hash2.prev_and_hashval[index].prev,
        }
    }

    pub fn hash_val_at(&self, index: usize, which: Which) -> i32 {
        match which {
            Which::Hash1 => self.hash1.prev_and_hashval[index].hashval,
            Which::Hash2 => self.hash2.prev_and_hashval[index].hashval,
        }
    }

    pub fn val(&self, which: Which) -> i32 {
        match which {
            Which::Hash1 => self.hash1.val,
            Which::Hash2 => self.hash2.val,
        }
    }
}
<commit_msg>Try Vec::with_capacity<commit_after>use util::{ZOPFLI_WINDOW_MASK, ZOPFLI_MIN_MATCH, ZOPFLI_WINDOW_SIZE};

const HASH_SHIFT: i32 = 5;
const HASH_MASK: i32 = 32767;

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Which {
    Hash1,
    Hash2,
}

pub struct SmallerHashThing {
    prev: u16, /* Index to index of prev. occurrence of same hash. */
    hashval: i32, /* Index to hash value at this index. */
}

pub struct HashThing {
    head: Vec<i32>,  /* Hash value to index of its most recent occurrence. */
    prev_and_hashval: Vec<SmallerHashThing>,
    val: i32,  /* Current hash value. */
}

impl HashThing {
    fn new() -> HashThing {
        let mut prev_and_hashval = Vec::with_capacity(ZOPFLI_WINDOW_SIZE);
        for p in 0..ZOPFLI_WINDOW_SIZE as u16 {
            prev_and_hashval.push(SmallerHashThing {
                prev: p,
                hashval: -1,
            });
        }
        HashThing {
            head: vec![-1; 65536],
            prev_and_hashval,
            val: 0,
        }
    }

    fn reset(&mut self) {
        self.val = 0;
        self.head.clear();
        self.head.resize(65536, -1);
        self.prev_and_hashval = (0..ZOPFLI_WINDOW_SIZE as u16).map(|p| {
            SmallerHashThing {
                prev: p,
                hashval: -1,
            }
        }).collect();
    }

    fn update(&mut self, hpos: usize) {
        let hashval = self.val;
        let index = self.val as usize;
        let head_index = self.head[index];
        let prev = if head_index != -1 && self.prev_and_hashval[head_index as usize].hashval == self.val {
            head_index as u16
        } else {
            hpos as u16
        };

        self.prev_and_hashval[hpos] = SmallerHashThing {
            prev: prev,
            hashval: hashval,
        };
        self.head[index] = hpos as i32;
    }
}

pub struct ZopfliHash {
    hash1: HashThing,
    hash2: HashThing,
    pub same: [u16; ZOPFLI_WINDOW_SIZE],  /* Amount of repetitions of same byte after this .*/
}

impl ZopfliHash {
    pub fn new() -> ZopfliHash {
        ZopfliHash {
            hash1: HashThing::new(),
            hash2: HashThing::new(),
            same: [0; ZOPFLI_WINDOW_SIZE],
        }
    }

    pub fn reset(&mut self) {
        self.hash1.reset();
        self.hash2.reset();
        self.same = [0; ZOPFLI_WINDOW_SIZE];
    }

    pub fn warmup(&mut self, arr: &[u8], pos: usize, end: usize) {
        let c = arr[pos];
        self.update_val(c);

        if pos + 1 < end {
            let c = arr[pos + 1];
            self.update_val(c);
        }
    }

    /// Update the sliding hash value with the given byte. All calls to this function
    /// must be made on consecutive input characters. Since the hash value exists out
    /// of multiple input bytes, a few warmups with this function are needed initially.
    fn update_val(&mut self, c: u8) {
        self.hash1.val = ((self.hash1.val << HASH_SHIFT) ^ c as i32) & HASH_MASK;
    }

    pub fn update(&mut self, array: &[u8], pos: usize) {
        let hash_value = array.get(pos + ZOPFLI_MIN_MATCH - 1).cloned().unwrap_or(0);
        self.update_val(hash_value);

        let hpos = pos & ZOPFLI_WINDOW_MASK;

        self.hash1.update(hpos);

        // Update "same".
        let mut amount = 0;
        let same_index = pos.wrapping_sub(1) & ZOPFLI_WINDOW_MASK;
        let same = self.same[same_index];
        if same > 1 {
            amount = same - 1;
        }

        self.same[hpos] = amount;

        self.hash2.val = ((amount.wrapping_sub(ZOPFLI_MIN_MATCH as u16) & 255) ^ self.hash1.val as u16) as i32;

        self.hash2.update(hpos);
    }

    pub fn head_at(&self, index: usize, which: Which) -> i32 {
        match which {
            Which::Hash1 => self.hash1.head[index],
            Which::Hash2 => self.hash2.head[index],
        }
    }

    pub fn prev_at(&self, index: usize, which: Which) -> u16 {
        match which {
            Which::Hash1 => self.hash1.prev_and_hashval[index].prev,
            Which::Hash2 => self.hash2.prev_and_hashval[index].prev,
        }
    }

    pub fn hash_val_at(&self, index: usize, which: Which) -> i32 {
        match which {
            Which::Hash1 => self.hash1.prev_and_hashval[index].hashval,
            Which::Hash2 => self.hash2.prev_and_hashval[index].hashval,
        }
    }

    pub fn val(&self, which: Which) -> i32 {
        match which {
            Which::Hash1 => self.hash1.val,
            Which::Hash2 => self.hash2.val,
        }
    }
}
<|endoftext|>


<commit_before>use libc::{c_double, c_void};
use std::cmp::{Ordering};
use std::sync::{Once, ONCE_INIT};

static EXACTINIT: Once = ONCE_INIT;

#[repr(C)]
#[derive(Debug, Clone, PartialEq)]
pub struct Point2D {
    x: c_double,
    y: c_double,
}

impl Point2D {
    pub fn new(x: c_double, y: c_double) -> Point2D {
        Point2D { x: x, y: y }
    }
}

#[repr(C)]
#[derive(Debug, Clone, PartialEq)]
pub struct Point3D {
    x: c_double,
    y: c_double,
    z: c_double,
}

impl Point3D {
    pub fn new(x: c_double, y: c_double, z: c_double) -> Point3D {
        Point3D { x: x, y: y, z: z }
    }
}

#[link(name = "predicates")]
extern "C" {
    fn exactinit() -> c_void;
    fn orient2d(pa: *const Point2D, pb: *const Point2D, pc: *const Point2D) -> c_double;
    fn incircle(pa: *const Point2D, pb: *const Point2D, pc: *const Point2D, pd: *const Point2D) -> c_double;
    fn orient3d(pa: *const Point3D, pb: *const Point3D, pc: *const Point3D, pd: *const Point3D) -> c_double;
    fn insphere(pa: *const Point3D, pb: *const Point3D, pc: *const Point3D, pd: *const Point3D, pe: *const Point3D) -> c_double;
}

fn init_predicates() {
    EXACTINIT.call_once(|| {
        unsafe { exactinit() };
    });
}

#[derive(Debug, Eq, PartialEq)]
pub enum InCircleLocation {
    Inside,
    Outside,
    On
}

#[derive(Debug, Eq, PartialEq)]
pub enum Orientation {
    Positive,
    Negative
}

impl Orientation {
    pub fn to_f64_multiplier(&self) -> f64 {
        match *self {
            Orientation::Positive => {  1.0 },
            Orientation::Negative => { -1.0 }
        }
    }
}

pub trait InCircleTestable<P> {
    fn in_circle_test(&self, point: &P) -> Option<InCircleLocation>;
}

pub trait Orientable {
    fn orientation(&self) -> Option<Orientation>;
}

pub struct Triangle<P> {
    p1: P,
    p2: P,
    p3: P,
}

impl<P> Triangle<P> {
    pub fn new(p1: P, p2: P, p3: P) -> Triangle<P> {
        Triangle {
            p1: p1,
            p2: p2,
            p3: p3,
        }
    }
}

pub struct Tetrahedron<P> {
    p1: P,
    p2: P,
    p3: P,
    p4: P,
}

impl<P> Tetrahedron<P> {
    pub fn new(p1: P, p2: P, p3: P, p4: P) -> Tetrahedron<P> {
        Tetrahedron {
            p1: p1,
            p2: p2,
            p3: p3,
            p4: p4,
        }
    }
}

fn det_to_in_circle_location(det: f64) -> Option<InCircleLocation> {
    match 0.0.partial_cmp(&det) {
        Some(Ordering::Greater) => { Some(InCircleLocation::Inside) }
        Some(Ordering::Less) => { Some(InCircleLocation::Outside) }
        Some(Ordering::Equal) => { Some(InCircleLocation::On) }
        None => { None }
    }
}

fn det_to_orientation(det: f64) -> Option<Orientation> {
    match 0.0.partial_cmp(&det) {
        Some(Ordering::Greater) => { Some(Orientation::Positive) }
        Some(Ordering::Less) => { Some(Orientation::Negative) }
        _ => { None }
    }
}

impl Orientable for Triangle<Point2D> {
    fn orientation(&self) -> Option<Orientation> {
        init_predicates();

        let orientation_det = unsafe { orient2d(&self.p1, &self.p2, &self.p3) };

        det_to_orientation(orientation_det)
    }
}

impl InCircleTestable<Point2D> for Triangle<Point2D> {
    fn in_circle_test(&self, point: &Point2D) -> Option<InCircleLocation> {
        init_predicates();

        let orienation_multiplier: f64 = match self.orientation() {
            Some(p) => { p.to_f64_multiplier() },
            None => { return None; }
        };

        let incircle_det = unsafe { incircle(&self.p1, &self.p2, &self.p3, point) };

        det_to_in_circle_location(orienation_multiplier * incircle_det)
    }
}

impl Orientable for Tetrahedron<Point3D> {
    fn orientation(&self) -> Option<Orientation> {
        init_predicates();

        let orientation_det = unsafe { orient3d(&self.p1, &self.p2, &self.p3, &self.p4) };

        det_to_orientation(orientation_det)
    }
}

impl InCircleTestable<Point3D> for Tetrahedron<Point3D> {
    fn in_circle_test(&self, point: &Point3D) -> Option<InCircleLocation> {
        init_predicates();

        let orienation_multiplier: f64 = match self.orientation() {
            Some(p) => { p.to_f64_multiplier() },
            None => { return None; }
        };

        let incircle_det = unsafe { insphere(&self.p1, &self.p2, &self.p3, &self.p4, point) };

        det_to_in_circle_location(orienation_multiplier * incircle_det)
    }
}

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

    use std::f64;
    use std::f64::consts;
    use rand;
    use quickcheck::{TestResult, quickcheck, QuickCheck, StdGen};

    #[test]
    fn in_circle_2d() {
        let p1 = Point2D::new(0.0, 0.0);
        let p2 = Point2D::new(0.0, 1.0);
        let p3 = Point2D::new(1.0, 1.0);
        let d_inside = Point2D::new(0.5, 0.5);
        let d_outside = Point2D::new(2.0, 0.0);
        let d_on = Point2D::new(1.0, 1.0);

        let t = Triangle::new(p1, p2, p3);

        assert_eq!(t.in_circle_test(&d_inside), Some(InCircleLocation::Inside));
        assert_eq!(t.in_circle_test(&d_outside), Some(InCircleLocation::Outside));
        assert_eq!(t.in_circle_test(&d_on), Some(InCircleLocation::On));
    }

    #[test]
    fn in_circle_2d_triangle_constructed_from_circle() {
        fn in_circle_2d_triangle_constructed_from_circle(center: (f64, f64), radius: f64, angles: (f64, f64, f64)) -> TestResult {
            if radius <= 0.0 { return TestResult::discard()};
            fn to_pnt(center: (f64, f64), radius: f64, angle: f64) -> Point2D {
                let x = center.0 + radius * angle.cos();
                let y = center.1 + radius * angle.sin();
                Point2D::new(x, y)
            }
            let p1 = to_pnt(center, radius, angles.0);
            let p2 = to_pnt(center, radius, angles.1);
            let p3 = to_pnt(center, radius, angles.2);
            let p_center = Point2D::new(center.0, center.1);

            let triangle = Triangle::new(p1, p2, p3);

            TestResult::from_bool(triangle.in_circle_test(&p_center) == Some(InCircleLocation::Inside))
        }
        quickcheck(in_circle_2d_triangle_constructed_from_circle as fn(center: (f64, f64), radius: f64, angles: (f64, f64, f64)) -> TestResult)
    }

    #[test]
    fn in_circle_2d_tetrahedron_constructed_from_sphere() {
        const DOMAIN: usize = 100;
        fn in_circle_2d_tetrahedron_constructed_from_sphere(center: (f64, f64, f64), radius: f64, angle_inputs: ((f64, f64), (f64, f64), (f64, f64), (f64, f64))) -> TestResult {
            if radius <= 0.0 { return TestResult::discard()};

            /// Generate a point on the sphere given by the "spherical" input coordinates.
            /// angle_inputs should be on the interval [-DOMAIN, DOMAIN], as converting to real
            /// spherical coordinates is also done by this function.
            /// See e.g. http://mathworld.wolfram.com/SpherePointPicking.html how uniform sampling
            /// on a sphere works.
            fn to_pnt(center: (f64, f64, f64), radius: f64, angle_inputs: (f64, f64)) -> Point3D {
                let domain_f64 = DOMAIN as f64;

                // Transform input to random variable on [0, 1]
                let u = 0.5 * (angle_inputs.0 / domain_f64 + 1.0);
                // Transform other input to random variable on [-1, 1]
                let v = angle_inputs.1 / domain_f64;

                let theta = 2.0 * consts::PI * u;
                let phi = v.acos();

                let x = center.0 + radius * theta.cos() * phi.sin();
                let y = center.1 + radius * theta.sin() * phi.sin();
                let z = center.2 + radius * phi.cos();

                Point3D::new(x, y, z)
            }

            let p1 = to_pnt(center, radius, angle_inputs.0);
            let p2 = to_pnt(center, radius, angle_inputs.1);
            let p3 = to_pnt(center, radius, angle_inputs.2);
            let p4 = to_pnt(center, radius, angle_inputs.3);
            let p_center = Point3D::new(center.0, center.1, center.2);

            let tetrahedron = Tetrahedron::new(p1, p2, p3, p4);

            TestResult::from_bool(tetrahedron.in_circle_test(&p_center) == Some(InCircleLocation::Inside))
        }
        QuickCheck::new().gen(StdGen::new(rand::thread_rng(), DOMAIN)).quickcheck(
            in_circle_2d_tetrahedron_constructed_from_sphere as fn(center: (f64, f64, f64), radius: f64, angle_inputs: ((f64, f64), (f64, f64), (f64, f64), (f64, f64))) -> TestResult
        )
    }

    #[test]
    fn in_circle_2d_nan_in_test_point() {
        let p1 = Point2D::new(0.0, 0.0);
        let p2 = Point2D::new(0.0, 1.0);
        let p3 = Point2D::new(1.0, 1.0);

        let d = Point2D::new(1.0, f64::NAN);

        let t = Triangle::new(p1, p2, p3);

        assert_eq!(t.in_circle_test(&d), None);
    }

    #[test]
    fn in_circle_2d_nan_in_triangle() {
        let p1 = Point2D::new(0.0, 0.0);
        let p2 = Point2D::new(0.0, f64::NAN);
        let p3 = Point2D::new(1.0, 1.0);

        let d = Point2D::new(1.0, 1.0);

        let t = Triangle::new(p1, p2, p3);

        assert_eq!(t.in_circle_test(&d), None);
    }

    #[test]
    fn in_circle_2d_infinity_in_test_point() {
        let p1 = Point2D::new(0.0, 0.0);
        let p2 = Point2D::new(0.0, 1.0);
        let p3 = Point2D::new(1.0, 1.0);

        let d = Point2D::new(1.0, f64::INFINITY);

        let t = Triangle::new(p1, p2, p3);

        assert_eq!(t.in_circle_test(&d), None);
    }

    #[test]
    fn in_circle_2d_infinity_in_triangle() {
        let p1 = Point2D::new(0.0, 0.0);
        let p2 = Point2D::new(0.0, f64::INFINITY);
        let p3 = Point2D::new(1.0, 1.0);

        let d = Point2D::new(1.0, 1.0);

        let t = Triangle::new(p1, p2, p3);

        assert_eq!(t.in_circle_test(&d), None);
    }

    #[test]
    fn in_circle_2d_neg_infinity_in_test_point() {
        let p1 = Point2D::new(0.0, 0.0);
        let p2 = Point2D::new(0.0, 1.0);
        let p3 = Point2D::new(1.0, 1.0);

        let d = Point2D::new(1.0, f64::NEG_INFINITY);

        let t = Triangle::new(p1, p2, p3);

        assert_eq!(t.in_circle_test(&d), None);
    }

    #[test]
    fn in_circle_2d_neg_infinity_in_triangle() {
        let p1 = Point2D::new(0.0, 0.0);
        let p2 = Point2D::new(0.0, f64::NEG_INFINITY);
        let p3 = Point2D::new(1.0, 1.0);

        let d = Point2D::new(1.0, 1.0);

        let t = Triangle::new(p1, p2, p3);

        assert_eq!(t.in_circle_test(&d), None);
    }

    #[test]
    fn in_circle_3d() {
        let p1 = Point3D::new(-1.0,  1.0, -1.0);
        let p2 = Point3D::new( 1.0,  1.0, -1.0);
        let p3 = Point3D::new( 0.0, -1.0, -1.0);
        let p4 = Point3D::new( 0.0,  0.0,  1.0);
        let d_inside = Point3D::new(0.0, 0.0, 0.0);
        let d_outside = Point3D::new(10.0, 10.0, 10.0);
        let d_on = Point3D::new(0.0, 0.0, 1.0);

        let t = Tetrahedron::new(p1, p2, p3, p4);

        assert_eq!(t.in_circle_test(&d_inside), Some(InCircleLocation::Inside));
        assert_eq!(t.in_circle_test(&d_outside), Some(InCircleLocation::Outside));
        assert_eq!(t.in_circle_test(&d_on), Some(InCircleLocation::On));
    }

}
<commit_msg>Test points inside and outside of circumsphere.<commit_after>use libc::{c_double, c_void};
use std::cmp::{Ordering};
use std::sync::{Once, ONCE_INIT};

static EXACTINIT: Once = ONCE_INIT;

#[repr(C)]
#[derive(Debug, Clone, PartialEq)]
pub struct Point2D {
    x: c_double,
    y: c_double,
}

impl Point2D {
    pub fn new(x: c_double, y: c_double) -> Point2D {
        Point2D { x: x, y: y }
    }
}

#[repr(C)]
#[derive(Debug, Clone, PartialEq)]
pub struct Point3D {
    x: c_double,
    y: c_double,
    z: c_double,
}

impl Point3D {
    pub fn new(x: c_double, y: c_double, z: c_double) -> Point3D {
        Point3D { x: x, y: y, z: z }
    }
}

#[link(name = "predicates")]
extern "C" {
    fn exactinit() -> c_void;
    fn orient2d(pa: *const Point2D, pb: *const Point2D, pc: *const Point2D) -> c_double;
    fn incircle(pa: *const Point2D, pb: *const Point2D, pc: *const Point2D, pd: *const Point2D) -> c_double;
    fn orient3d(pa: *const Point3D, pb: *const Point3D, pc: *const Point3D, pd: *const Point3D) -> c_double;
    fn insphere(pa: *const Point3D, pb: *const Point3D, pc: *const Point3D, pd: *const Point3D, pe: *const Point3D) -> c_double;
}

fn init_predicates() {
    EXACTINIT.call_once(|| {
        unsafe { exactinit() };
    });
}

#[derive(Debug, Eq, PartialEq)]
pub enum InCircleLocation {
    Inside,
    Outside,
    On
}

#[derive(Debug, Eq, PartialEq)]
pub enum Orientation {
    Positive,
    Negative
}

impl Orientation {
    pub fn to_f64_multiplier(&self) -> f64 {
        match *self {
            Orientation::Positive => {  1.0 },
            Orientation::Negative => { -1.0 }
        }
    }
}

pub trait InCircleTestable<P> {
    fn in_circle_test(&self, point: &P) -> Option<InCircleLocation>;
}

pub trait Orientable {
    fn orientation(&self) -> Option<Orientation>;
}

pub struct Triangle<P> {
    p1: P,
    p2: P,
    p3: P,
}

impl<P> Triangle<P> {
    pub fn new(p1: P, p2: P, p3: P) -> Triangle<P> {
        Triangle {
            p1: p1,
            p2: p2,
            p3: p3,
        }
    }
}

pub struct Tetrahedron<P> {
    p1: P,
    p2: P,
    p3: P,
    p4: P,
}

impl<P> Tetrahedron<P> {
    pub fn new(p1: P, p2: P, p3: P, p4: P) -> Tetrahedron<P> {
        Tetrahedron {
            p1: p1,
            p2: p2,
            p3: p3,
            p4: p4,
        }
    }
}

fn det_to_in_circle_location(det: f64) -> Option<InCircleLocation> {
    match 0.0.partial_cmp(&det) {
        Some(Ordering::Greater) => { Some(InCircleLocation::Inside) }
        Some(Ordering::Less) => { Some(InCircleLocation::Outside) }
        Some(Ordering::Equal) => { Some(InCircleLocation::On) }
        None => { None }
    }
}

fn det_to_orientation(det: f64) -> Option<Orientation> {
    match 0.0.partial_cmp(&det) {
        Some(Ordering::Greater) => { Some(Orientation::Positive) }
        Some(Ordering::Less) => { Some(Orientation::Negative) }
        _ => { None }
    }
}

impl Orientable for Triangle<Point2D> {
    fn orientation(&self) -> Option<Orientation> {
        init_predicates();

        let orientation_det = unsafe { orient2d(&self.p1, &self.p2, &self.p3) };

        det_to_orientation(orientation_det)
    }
}

impl InCircleTestable<Point2D> for Triangle<Point2D> {
    fn in_circle_test(&self, point: &Point2D) -> Option<InCircleLocation> {
        init_predicates();

        let orienation_multiplier: f64 = match self.orientation() {
            Some(p) => { p.to_f64_multiplier() },
            None => { return None; }
        };

        let incircle_det = unsafe { incircle(&self.p1, &self.p2, &self.p3, point) };

        det_to_in_circle_location(orienation_multiplier * incircle_det)
    }
}

impl Orientable for Tetrahedron<Point3D> {
    fn orientation(&self) -> Option<Orientation> {
        init_predicates();

        let orientation_det = unsafe { orient3d(&self.p1, &self.p2, &self.p3, &self.p4) };

        det_to_orientation(orientation_det)
    }
}

impl InCircleTestable<Point3D> for Tetrahedron<Point3D> {
    fn in_circle_test(&self, point: &Point3D) -> Option<InCircleLocation> {
        init_predicates();

        let orienation_multiplier: f64 = match self.orientation() {
            Some(p) => { p.to_f64_multiplier() },
            None => { return None; }
        };

        let incircle_det = unsafe { insphere(&self.p1, &self.p2, &self.p3, &self.p4, point) };

        det_to_in_circle_location(orienation_multiplier * incircle_det)
    }
}

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

    use std::f64;
    use std::f64::consts;
    use rand;
    use quickcheck::{TestResult, quickcheck, QuickCheck, StdGen};

    #[test]
    fn in_circle_2d() {
        let p1 = Point2D::new(0.0, 0.0);
        let p2 = Point2D::new(0.0, 1.0);
        let p3 = Point2D::new(1.0, 1.0);
        let d_inside = Point2D::new(0.5, 0.5);
        let d_outside = Point2D::new(2.0, 0.0);
        let d_on = Point2D::new(1.0, 1.0);

        let t = Triangle::new(p1, p2, p3);

        assert_eq!(t.in_circle_test(&d_inside), Some(InCircleLocation::Inside));
        assert_eq!(t.in_circle_test(&d_outside), Some(InCircleLocation::Outside));
        assert_eq!(t.in_circle_test(&d_on), Some(InCircleLocation::On));
    }

    #[test]
    fn in_circle_2d_triangle_constructed_from_circle() {
        fn in_circle_2d_triangle_constructed_from_circle(center: (f64, f64), radius: f64, angles: (f64, f64, f64)) -> TestResult {
            if radius <= 0.0 { return TestResult::discard()};
            fn to_pnt(center: (f64, f64), radius: f64, angle: f64) -> Point2D {
                let x = center.0 + radius * angle.cos();
                let y = center.1 + radius * angle.sin();
                Point2D::new(x, y)
            }
            let p1 = to_pnt(center, radius, angles.0);
            let p2 = to_pnt(center, radius, angles.1);
            let p3 = to_pnt(center, radius, angles.2);
            let p_center = Point2D::new(center.0, center.1);

            let triangle = Triangle::new(p1, p2, p3);

            TestResult::from_bool(triangle.in_circle_test(&p_center) == Some(InCircleLocation::Inside))
        }
        quickcheck(in_circle_2d_triangle_constructed_from_circle as fn(center: (f64, f64), radius: f64, angles: (f64, f64, f64)) -> TestResult)
    }

    #[test]
    fn in_circle_2d_tetrahedron_constructed_from_sphere() {
        const DOMAIN: usize = 100;
        fn in_circle_2d_tetrahedron_constructed_from_sphere(
            center: (f64, f64, f64), radius: f64, test_point_radius: f64,
            angle_inputs: ((f64, f64), (f64, f64), (f64, f64), (f64, f64), (f64, f64)),
        ) -> TestResult {
            if radius <= 0.0 { return TestResult::discard() };
            if test_point_radius <= 0.0 { return TestResult::discard() };
            if radius == test_point_radius { return TestResult::discard() };

            /// Generate a point on the sphere given by the "spherical" input coordinates.
            /// angle_inputs should be on the interval [-DOMAIN, DOMAIN], as converting to real
            /// spherical coordinates is also done by this function.
            /// See e.g. http://mathworld.wolfram.com/SpherePointPicking.html how uniform sampling
            /// on a sphere works.
            fn to_pnt(center: (f64, f64, f64), radius: f64, angle_inputs: (f64, f64)) -> Point3D {
                let domain_f64 = DOMAIN as f64;

                // Transform input to random variable on [0, 1]
                let u = 0.5 * (angle_inputs.0 / domain_f64 + 1.0);
                // Transform other input to random variable on [-1, 1]
                let v = angle_inputs.1 / domain_f64;

                let theta = 2.0 * consts::PI * u;
                let phi = v.acos();

                let x = center.0 + radius * theta.cos() * phi.sin();
                let y = center.1 + radius * theta.sin() * phi.sin();
                let z = center.2 + radius * phi.cos();

                Point3D::new(x, y, z)
            }

            let p1 = to_pnt(center, radius, angle_inputs.0);
            let p2 = to_pnt(center, radius, angle_inputs.1);
            let p3 = to_pnt(center, radius, angle_inputs.2);
            let p4 = to_pnt(center, radius, angle_inputs.3);
            let test_point = to_pnt(center, test_point_radius, angle_inputs.4);

            let tetrahedron = Tetrahedron::new(p1, p2, p3, p4);

            let expected =
                if test_point_radius < radius {
                    Some(InCircleLocation::Inside) }
                else {
                    Some(InCircleLocation::Outside)
                };
            TestResult::from_bool(tetrahedron.in_circle_test(&test_point) == expected)
        }
        QuickCheck::new().gen(StdGen::new(rand::thread_rng(), DOMAIN)).quickcheck(in_circle_2d_tetrahedron_constructed_from_sphere as fn(
            center: (f64, f64, f64), radius: f64, test_point_radius: f64,
            angle_inputs: ((f64, f64), (f64, f64), (f64, f64), (f64, f64), (f64, f64)),
        ) -> TestResult)
    }

    #[test]
    fn in_circle_2d_nan_in_test_point() {
        let p1 = Point2D::new(0.0, 0.0);
        let p2 = Point2D::new(0.0, 1.0);
        let p3 = Point2D::new(1.0, 1.0);

        let d = Point2D::new(1.0, f64::NAN);

        let t = Triangle::new(p1, p2, p3);

        assert_eq!(t.in_circle_test(&d), None);
    }

    #[test]
    fn in_circle_2d_nan_in_triangle() {
        let p1 = Point2D::new(0.0, 0.0);
        let p2 = Point2D::new(0.0, f64::NAN);
        let p3 = Point2D::new(1.0, 1.0);

        let d = Point2D::new(1.0, 1.0);

        let t = Triangle::new(p1, p2, p3);

        assert_eq!(t.in_circle_test(&d), None);
    }

    #[test]
    fn in_circle_2d_infinity_in_test_point() {
        let p1 = Point2D::new(0.0, 0.0);
        let p2 = Point2D::new(0.0, 1.0);
        let p3 = Point2D::new(1.0, 1.0);

        let d = Point2D::new(1.0, f64::INFINITY);

        let t = Triangle::new(p1, p2, p3);

        assert_eq!(t.in_circle_test(&d), None);
    }

    #[test]
    fn in_circle_2d_infinity_in_triangle() {
        let p1 = Point2D::new(0.0, 0.0);
        let p2 = Point2D::new(0.0, f64::INFINITY);
        let p3 = Point2D::new(1.0, 1.0);

        let d = Point2D::new(1.0, 1.0);

        let t = Triangle::new(p1, p2, p3);

        assert_eq!(t.in_circle_test(&d), None);
    }

    #[test]
    fn in_circle_2d_neg_infinity_in_test_point() {
        let p1 = Point2D::new(0.0, 0.0);
        let p2 = Point2D::new(0.0, 1.0);
        let p3 = Point2D::new(1.0, 1.0);

        let d = Point2D::new(1.0, f64::NEG_INFINITY);

        let t = Triangle::new(p1, p2, p3);

        assert_eq!(t.in_circle_test(&d), None);
    }

    #[test]
    fn in_circle_2d_neg_infinity_in_triangle() {
        let p1 = Point2D::new(0.0, 0.0);
        let p2 = Point2D::new(0.0, f64::NEG_INFINITY);
        let p3 = Point2D::new(1.0, 1.0);

        let d = Point2D::new(1.0, 1.0);

        let t = Triangle::new(p1, p2, p3);

        assert_eq!(t.in_circle_test(&d), None);
    }

    #[test]
    fn in_circle_3d() {
        let p1 = Point3D::new(-1.0,  1.0, -1.0);
        let p2 = Point3D::new( 1.0,  1.0, -1.0);
        let p3 = Point3D::new( 0.0, -1.0, -1.0);
        let p4 = Point3D::new( 0.0,  0.0,  1.0);
        let d_inside = Point3D::new(0.0, 0.0, 0.0);
        let d_outside = Point3D::new(10.0, 10.0, 10.0);
        let d_on = Point3D::new(0.0, 0.0, 1.0);

        let t = Tetrahedron::new(p1, p2, p3, p4);

        assert_eq!(t.in_circle_test(&d_inside), Some(InCircleLocation::Inside));
        assert_eq!(t.in_circle_test(&d_outside), Some(InCircleLocation::Outside));
        assert_eq!(t.in_circle_test(&d_on), Some(InCircleLocation::On));
    }

}
<|endoftext|>