<commit_before>extern crate poker;
use std::collections::HashSet;
use poker::winning_hands;

fn hs_from<'a>(input: &[&'a str]) -> HashSet<&'a str> {
    let mut hs = HashSet::new();
    for item in input.iter() {
        hs.insert(*item);
    }
    hs
}

/// Test that the expected output is produced from the given input
/// using the `winning_hands` function.
///
/// Note that the output can be in any order. Here, we use a HashSet to
/// abstract away the order of outputs.
fn test<'a, 'b>(input: &[&'a str], expected: &[&'b str]) {
    assert_eq!(
        hs_from(&winning_hands(input).expect(
            "This test should produce Some value",
        )),
        hs_from(expected)
    )
}

#[test]
fn test_single_hand_always_wins() {
    test(&vec!["4S 5S 7H 8D JC"], &vec!["4S 5S 7H 8D JC"])
}

#[test]
#[ignore]
fn test_highest_card_of_all_hands_wins() {
    test(
        &vec!["4D 5S 6S 8D 3C", "2S 4C 7S 9H 10H", "3S 4S 5D 6H JH"],
        &vec!["3S 4S 5D 6H JH"],
    )
}

#[test]
#[ignore]
fn test_a_tie_has_multiple_winners() {
    test(
        &vec![
            "4D 5S 6S 8D 3C",
            "2S 4C 7S 9H 10H",
            "3S 4S 5D 6H JH",
            "3H 4H 5C 6C JD",
        ],
        &vec!["3S 4S 5D 6H JH", "3H 4H 5C 6C JD"],
    )
}

#[test]
#[ignore]
fn test_high_card_can_be_low_card_in_an_otherwise_tie() {
    // multiple hands with the same high cards, tie compares next highest ranked,
    // down to last card
    test(
        &vec!["3S 5H 6S 8D 7H", "2S 5D 6D 8C 7S"],
        &vec!["3S 5H 6S 8D 7H"],
    )
}

#[test]
#[ignore]
fn test_one_pair_beats_high_card() {
    test(
        &vec!["4S 5H 6C 8D KH", "2S 4H 6S 4D JH"],
        &vec!["2S 4H 6S 4D JH"],
    )
}

#[test]
#[ignore]
fn test_highest_pair_wins() {
    test(
        &vec!["4S 2H 6S 2D JH", "2S 4H 6C 4D JD"],
        &vec!["2S 4H 6C 4D JD"],
    )
}

#[test]
#[ignore]
fn test_two_pairs_beats_one_pair() {
    test(
        &vec!["2S 8H 6S 8D JH", "4S 5H 4C 8C 5C"],
        &vec!["4S 5H 4C 8C 5C"],
    )
}

#[test]
#[ignore]
fn test_two_pair_ranks() {
    // both hands have two pairs, highest ranked pair wins
    test(
        &vec!["2S 8H 2D 8D 3H", "4S 5H 4C 8S 5D"],
        &vec!["2S 8H 2D 8D 3H"],
    )
}

#[test]
#[ignore]
fn test_two_pairs_second_pair_cascade() {
    // both hands have two pairs, with the same highest ranked pair,
    // tie goes to low pair
    test(
        &vec!["2S QS 2C QD JH", "JD QH JS 8D QC"],
        &vec!["JD QH JS 8D QC"],
    )
}

#[test]
#[ignore]
fn test_two_pairs_last_card_cascade() {
    // both hands have two identically ranked pairs,
    // tie goes to remaining card (kicker)
    test(
        &vec!["JD QH JS 8D QC", "JS QS JC 2D QD"],
        &vec!["JD QH JS 8D QC"],
    )
}

#[test]
#[ignore]
fn test_three_of_a_kind_beats_two_pair() {
    test(
        &vec!["2S 8H 2H 8D JH", "4S 5H 4C 8S 4H"],
        &vec!["4S 5H 4C 8S 4H"],
    )
}

#[test]
#[ignore]
fn test_three_of_a_kind_ranks() {
    //both hands have three of a kind, tie goes to highest ranked triplet
    test(
        &vec!["2S 2H 2C 8D JH", "4S AH AS 8C AD"],
        &vec!["4S AH AS 8C AD"],
    )
}

#[test]
#[ignore]
fn test_three_of_a_kind_cascade_ranks() {
    // with multiple decks, two players can have same three of a kind,
    // ties go to highest remaining cards
    test(
        &vec!["4S AH AS 7C AD", "4S AH AS 8C AD"],
        &vec!["4S AH AS 8C AD"],
    )
}

#[test]
#[ignore]
fn test_straight_beats_three_of_a_kind() {
    test(
        &vec!["4S 5H 4C 8D 4H", "3S 4D 2S 6D 5C"],
        &vec!["3S 4D 2S 6D 5C"],
    )
}

#[test]
#[ignore]
fn test_aces_can_end_a_straight_high() {
    // aces can end a straight (10 J Q K A)
    test(
        &vec!["4S 5H 4C 8D 4H", "10D JH QS KD AC"],
        &vec!["10D JH QS KD AC"],
    )
}

#[test]
#[ignore]
fn test_aces_can_end_a_straight_low() {
    // aces can start a straight (A 2 3 4 5)
    test(
        &vec!["4S 5H 4C 8D 4H", "4D AH 3S 2D 5C"],
        &vec!["4D AH 3S 2D 5C"],
    )
}

#[test]
#[ignore]
fn test_straight_cascade() {
    // both hands with a straight, tie goes to highest ranked card
    test(
        &vec!["4S 6C 7S 8D 5H", "5S 7H 8S 9D 6H"],
        &vec!["5S 7H 8S 9D 6H"],
    )
}

#[test]
#[ignore]
fn test_straight_scoring() {
    // even though an ace is usually high, a 5-high straight is the lowest-scoring straight
    test(
        &vec!["2H 3C 4D 5D 6H", "4S AH 3S 2D 5H"],
        &vec!["2H 3C 4D 5D 6H"],
    )
}

#[test]
#[ignore]
fn test_flush_beats_a_straight() {
    test(
        &vec!["4C 6H 7D 8D 5H", "2S 4S 5S 6S 7S"],
        &vec!["2S 4S 5S 6S 7S"],
    )
}

#[test]
#[ignore]
fn test_flush_cascade() {
    // both hands have a flush, tie goes to high card, down to the last one if necessary
    test(
        &vec!["4H 7H 8H 9H 6H", "2S 4S 5S 6S 7S"],
        &vec!["4H 7H 8H 9H 6H"],
    )
}

#[test]
#[ignore]
fn test_full_house_beats_a_flush() {
    test(
        &vec!["3H 6H 7H 8H 5H", "4S 5C 4C 5D 4H"],
        &vec!["4S 5C 4C 5D 4H"],
    )
}

#[test]
#[ignore]
fn test_full_house_ranks() {
    // both hands have a full house, tie goes to highest-ranked triplet
    test(
        &vec!["4H 4S 4D 9S 9D", "5H 5S 5D 8S 8D"],
        &vec!["5H 5S 5D 8S 8D"],
    )
}

#[test]
#[ignore]
fn test_full_house_cascade() {
    // with multiple decks, both hands have a full house with the same triplet, tie goes to the pair
    test(
        &vec!["5H 5S 5D 9S 9D", "5H 5S 5D 8S 8D"],
        &vec!["5H 5S 5D 9S 9D"],
    )
}

#[test]
#[ignore]
fn test_four_of_a_kind_beats_full_house() {
    test(
        &vec!["4S 5H 4D 5D 4H", "3S 3H 2S 3D 3C"],
        &vec!["3S 3H 2S 3D 3C"],
    )
}

#[test]
#[ignore]
fn test_four_of_a_kind_ranks() {
    // both hands have four of a kind, tie goes to high quad
    test(
        &vec!["2S 2H 2C 8D 2D", "4S 5H 5S 5D 5C"],
        &vec!["4S 5H 5S 5D 5C"],
    )
}

#[test]
#[ignore]
fn test_four_of_a_kind_cascade() {
    // with multiple decks, both hands with identical four of a kind, tie determined by kicker
    test(
        &vec!["3S 3H 2S 3D 3C", "3S 3H 4S 3D 3C"],
        &vec!["3S 3H 4S 3D 3C"],
    )
}

#[test]
#[ignore]
fn test_straight_flush_beats_four_of_a_kind() {
    test(
        &vec!["4S 5H 5S 5D 5C", "7S 8S 9S 6S 10S"],
        &vec!["7S 8S 9S 6S 10S"],
    )
}

#[test]
#[ignore]
fn test_straight_flush_ranks() {
    // both hands have straight flush, tie goes to highest-ranked card
    test(
        &vec!["4H 6H 7H 8H 5H", "5S 7S 8S 9S 6S"],
        &vec!["5S 7S 8S 9S 6S"],
    )
}
<commit_msg>poker: Use `&[...]` instead of `&vec![...]`<commit_after>extern crate poker;
use std::collections::HashSet;
use poker::winning_hands;

fn hs_from<'a>(input: &[&'a str]) -> HashSet<&'a str> {
    let mut hs = HashSet::new();
    for item in input.iter() {
        hs.insert(*item);
    }
    hs
}

/// Test that the expected output is produced from the given input
/// using the `winning_hands` function.
///
/// Note that the output can be in any order. Here, we use a HashSet to
/// abstract away the order of outputs.
fn test<'a, 'b>(input: &[&'a str], expected: &[&'b str]) {
    assert_eq!(
        hs_from(&winning_hands(input).expect(
            "This test should produce Some value",
        )),
        hs_from(expected)
    )
}

#[test]
fn test_single_hand_always_wins() {
    test(&["4S 5S 7H 8D JC"], &["4S 5S 7H 8D JC"])
}

#[test]
#[ignore]
fn test_highest_card_of_all_hands_wins() {
    test(
        &["4D 5S 6S 8D 3C", "2S 4C 7S 9H 10H", "3S 4S 5D 6H JH"],
        &["3S 4S 5D 6H JH"],
    )
}

#[test]
#[ignore]
fn test_a_tie_has_multiple_winners() {
    test(
        &[
            "4D 5S 6S 8D 3C",
            "2S 4C 7S 9H 10H",
            "3S 4S 5D 6H JH",
            "3H 4H 5C 6C JD",
        ],
        &["3S 4S 5D 6H JH", "3H 4H 5C 6C JD"],
    )
}

#[test]
#[ignore]
fn test_high_card_can_be_low_card_in_an_otherwise_tie() {
    // multiple hands with the same high cards, tie compares next highest ranked,
    // down to last card
    test(
        &["3S 5H 6S 8D 7H", "2S 5D 6D 8C 7S"],
        &["3S 5H 6S 8D 7H"],
    )
}

#[test]
#[ignore]
fn test_one_pair_beats_high_card() {
    test(
        &["4S 5H 6C 8D KH", "2S 4H 6S 4D JH"],
        &["2S 4H 6S 4D JH"],
    )
}

#[test]
#[ignore]
fn test_highest_pair_wins() {
    test(
        &["4S 2H 6S 2D JH", "2S 4H 6C 4D JD"],
        &["2S 4H 6C 4D JD"],
    )
}

#[test]
#[ignore]
fn test_two_pairs_beats_one_pair() {
    test(
        &["2S 8H 6S 8D JH", "4S 5H 4C 8C 5C"],
        &["4S 5H 4C 8C 5C"],
    )
}

#[test]
#[ignore]
fn test_two_pair_ranks() {
    // both hands have two pairs, highest ranked pair wins
    test(
        &["2S 8H 2D 8D 3H", "4S 5H 4C 8S 5D"],
        &["2S 8H 2D 8D 3H"],
    )
}

#[test]
#[ignore]
fn test_two_pairs_second_pair_cascade() {
    // both hands have two pairs, with the same highest ranked pair,
    // tie goes to low pair
    test(
        &["2S QS 2C QD JH", "JD QH JS 8D QC"],
        &["JD QH JS 8D QC"],
    )
}

#[test]
#[ignore]
fn test_two_pairs_last_card_cascade() {
    // both hands have two identically ranked pairs,
    // tie goes to remaining card (kicker)
    test(
        &["JD QH JS 8D QC", "JS QS JC 2D QD"],
        &["JD QH JS 8D QC"],
    )
}

#[test]
#[ignore]
fn test_three_of_a_kind_beats_two_pair() {
    test(
        &["2S 8H 2H 8D JH", "4S 5H 4C 8S 4H"],
        &["4S 5H 4C 8S 4H"],
    )
}

#[test]
#[ignore]
fn test_three_of_a_kind_ranks() {
    //both hands have three of a kind, tie goes to highest ranked triplet
    test(
        &["2S 2H 2C 8D JH", "4S AH AS 8C AD"],
        &["4S AH AS 8C AD"],
    )
}

#[test]
#[ignore]
fn test_three_of_a_kind_cascade_ranks() {
    // with multiple decks, two players can have same three of a kind,
    // ties go to highest remaining cards
    test(
        &["4S AH AS 7C AD", "4S AH AS 8C AD"],
        &["4S AH AS 8C AD"],
    )
}

#[test]
#[ignore]
fn test_straight_beats_three_of_a_kind() {
    test(
        &["4S 5H 4C 8D 4H", "3S 4D 2S 6D 5C"],
        &["3S 4D 2S 6D 5C"],
    )
}

#[test]
#[ignore]
fn test_aces_can_end_a_straight_high() {
    // aces can end a straight (10 J Q K A)
    test(
        &["4S 5H 4C 8D 4H", "10D JH QS KD AC"],
        &["10D JH QS KD AC"],
    )
}

#[test]
#[ignore]
fn test_aces_can_end_a_straight_low() {
    // aces can start a straight (A 2 3 4 5)
    test(
        &["4S 5H 4C 8D 4H", "4D AH 3S 2D 5C"],
        &["4D AH 3S 2D 5C"],
    )
}

#[test]
#[ignore]
fn test_straight_cascade() {
    // both hands with a straight, tie goes to highest ranked card
    test(
        &["4S 6C 7S 8D 5H", "5S 7H 8S 9D 6H"],
        &["5S 7H 8S 9D 6H"],
    )
}

#[test]
#[ignore]
fn test_straight_scoring() {
    // even though an ace is usually high, a 5-high straight is the lowest-scoring straight
    test(
        &["2H 3C 4D 5D 6H", "4S AH 3S 2D 5H"],
        &["2H 3C 4D 5D 6H"],
    )
}

#[test]
#[ignore]
fn test_flush_beats_a_straight() {
    test(
        &["4C 6H 7D 8D 5H", "2S 4S 5S 6S 7S"],
        &["2S 4S 5S 6S 7S"],
    )
}

#[test]
#[ignore]
fn test_flush_cascade() {
    // both hands have a flush, tie goes to high card, down to the last one if necessary
    test(
        &["4H 7H 8H 9H 6H", "2S 4S 5S 6S 7S"],
        &["4H 7H 8H 9H 6H"],
    )
}

#[test]
#[ignore]
fn test_full_house_beats_a_flush() {
    test(
        &["3H 6H 7H 8H 5H", "4S 5C 4C 5D 4H"],
        &["4S 5C 4C 5D 4H"],
    )
}

#[test]
#[ignore]
fn test_full_house_ranks() {
    // both hands have a full house, tie goes to highest-ranked triplet
    test(
        &["4H 4S 4D 9S 9D", "5H 5S 5D 8S 8D"],
        &["5H 5S 5D 8S 8D"],
    )
}

#[test]
#[ignore]
fn test_full_house_cascade() {
    // with multiple decks, both hands have a full house with the same triplet, tie goes to the pair
    test(
        &["5H 5S 5D 9S 9D", "5H 5S 5D 8S 8D"],
        &["5H 5S 5D 9S 9D"],
    )
}

#[test]
#[ignore]
fn test_four_of_a_kind_beats_full_house() {
    test(
        &["4S 5H 4D 5D 4H", "3S 3H 2S 3D 3C"],
        &["3S 3H 2S 3D 3C"],
    )
}

#[test]
#[ignore]
fn test_four_of_a_kind_ranks() {
    // both hands have four of a kind, tie goes to high quad
    test(
        &["2S 2H 2C 8D 2D", "4S 5H 5S 5D 5C"],
        &["4S 5H 5S 5D 5C"],
    )
}

#[test]
#[ignore]
fn test_four_of_a_kind_cascade() {
    // with multiple decks, both hands with identical four of a kind, tie determined by kicker
    test(
        &["3S 3H 2S 3D 3C", "3S 3H 4S 3D 3C"],
        &["3S 3H 4S 3D 3C"],
    )
}

#[test]
#[ignore]
fn test_straight_flush_beats_four_of_a_kind() {
    test(
        &["4S 5H 5S 5D 5C", "7S 8S 9S 6S 10S"],
        &["7S 8S 9S 6S 10S"],
    )
}

#[test]
#[ignore]
fn test_straight_flush_ranks() {
    // both hands have straight flush, tie goes to highest-ranked card
    test(
        &["4H 6H 7H 8H 5H", "5S 7S 8S 9S 6S"],
        &["5S 7S 8S 9S 6S"],
    )
}
<|endoftext|>


<commit_before>extern crate iron;
extern crate router;
extern crate mount;
extern crate staticfile;
extern crate uuid;
extern crate time;
extern crate ws;

use std::env;
use std::thread;
use std::path::Path;
use std::sync::{Arc,Mutex};
use std::collections::HashMap;

use uuid::Uuid;

use iron::prelude::*;

use mount::Mount;
use staticfile::Static;

#[derive(Debug, Clone, Copy)]
pub struct Connection {
    uuid: Uuid,
}

impl std::fmt::Display for Connection {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "conn{{{}}}", self.uuid)
    }
}

fn main() {
    let connections = Arc::new(Mutex::new(HashMap::new()));

    struct WSServer {
        out: ws::Sender,
        connections: Arc<Mutex<HashMap<Uuid, Connection>>>
    }

    impl ws::Handler for WSServer {
        fn on_request(&mut self, req: &ws::Request) -> ws::Result<ws::Response> {
            let mut resp = ws::Response::from_request(req).unwrap();

            let mut conn_map = self.connections.lock().unwrap();

            println!("ws:req[{}]", time::precise_time_ns());

            let new_uuid = Uuid::new_v4();
            let new_conn = Connection {
                uuid: new_uuid
            };
            conn_map.insert(new_conn.uuid, new_conn);
            println!("wsconnect: new user with uuid {}", new_uuid);
            println!("{} connected users", conn_map.len());

            println!("wsconnect: putting cookie: bzwf_anon_wstx={}", new_conn);
            let new_bzwf_cookie_str = format!("bzwf_anon_wstx={}", new_conn);
            {
                let headers = resp.headers_mut();
                headers.push((
                    String::from("Set-Cookie"),
                    new_bzwf_cookie_str.as_bytes().to_vec()
                ));
            }

            Ok(resp)
        }

        fn on_message(&mut self, msg: ws::Message) -> ws::Result<()> {
            // TODO
            println!("ws:rcv[{}]: user with uuid {} sent ws msg {}",
                     time::precise_time_ns(),
                     "[[[ we don't have uuids in ws yet ]]]",
                     msg);
            self.out.send("I hear you loud and clear").unwrap();
            Ok(())
        }

        fn on_open(&mut self, _: ws::Handshake) -> ws::Result<()> {
            // TODO
            println!("ws:opn[{}]: user with uuid {} opened ws cxn",
                     time::precise_time_ns(),
                     "[[[ we don't have uuids in ws yet ]]]");
            Ok(())
        }

        fn on_close(&mut self, code: ws::CloseCode, reason: &str) {
            println!("ws:cls[{}]: user with uuid {} closed ws cxn\n\tCode [{:?}] reason: {}",
                     time::precise_time_ns(),
                     "[[[ we don't have uuids in ws yet ]]]",
                     code,
                     reason);
            // TODO
        }

        fn on_error(&mut self, err: ws::Error) {
            println!("ws:err[{}]: user with uuid {} got error {}",
                     time::precise_time_ns(),
                     "[[[ we don't have uuids in ws yet ]]]",
                     err);
            // TODO
        }
    }

    {
        let domain : String;

        match env::var("DOMAIN") {
            Ok(val) => domain = val,
            Err(_)  => {
                println!("DOMAIN not found; using localhost for default.");
                domain = String::from("localhost")
            }
        }

        let web_domain = domain.clone();
        let webserver_thread = thread::spawn(move || {
            let web_addr = format!("{}:3000", web_domain);
            println!("TCP Web server listening on {}", web_addr);
            println!("\tGo to: http://{}/client/html", web_addr);

            let mut assets_mount = Mount::new();
            assets_mount
                .mount("/client", Static::new(Path::new("../client")));

            Iron::new(assets_mount).http(web_addr.clone()).unwrap()
        });

        let ws_domain = domain.clone();
        let websocket_thread = thread::spawn(move || {
            let ws_addr = format!("{}:3001", ws_domain);
            println!("WS WebSockets server listening on {}", ws_addr);
            ws::listen(ws_addr.clone(), |out| WSServer {
                out: out,
                connections: connections.clone()
            }).unwrap();
        });

        webserver_thread.join().unwrap();
        websocket_thread.join().unwrap();
    }
}

#[cfg(test)]
mod tests {
    // NOTE: bring into scope everything defined above
    use super::*;

    #[test]
    fn it_works() {
        // TODO: write a test
    }
}
<commit_msg>Minor: cleaner log messages<commit_after>extern crate iron;
extern crate router;
extern crate mount;
extern crate staticfile;
extern crate uuid;
extern crate time;
extern crate ws;

use std::env;
use std::thread;
use std::path::Path;
use std::sync::{Arc,Mutex};
use std::collections::HashMap;

use uuid::Uuid;

use iron::prelude::*;

use mount::Mount;
use staticfile::Static;

#[derive(Debug, Clone, Copy)]
pub struct Connection {
    uuid: Uuid,
}

impl std::fmt::Display for Connection {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "conn{{{}}}", self.uuid)
    }
}

fn main() {
    let connections = Arc::new(Mutex::new(HashMap::new()));

    struct WSServer {
        out: ws::Sender,
        connections: Arc<Mutex<HashMap<Uuid, Connection>>>
    }

    impl ws::Handler for WSServer {
        fn on_request(&mut self, req: &ws::Request) -> ws::Result<ws::Response> {
            let mut resp = ws::Response::from_request(req).unwrap();

            let mut conn_map = self.connections.lock().unwrap();

            println!("ws:req[{}]", time::precise_time_ns());

            let new_uuid = Uuid::new_v4();
            let new_conn = Connection {
                uuid: new_uuid
            };
            conn_map.insert(new_conn.uuid, new_conn);
            println!("wsconnect: new user with uuid {}", new_uuid);
            println!("{} connected users", conn_map.len());

            println!("wsconnect: putting cookie: bzwf_anon_wstx={}", new_conn);
            let new_bzwf_cookie_str = format!("bzwf_anon_wstx={}", new_conn);
            {
                let headers = resp.headers_mut();
                headers.push((
                    String::from("Set-Cookie"),
                    new_bzwf_cookie_str.as_bytes().to_vec()
                ));
            }

            Ok(resp)
        }

        fn on_message(&mut self, msg: ws::Message) -> ws::Result<()> {
            // TODO
            println!("ws:rcv[{}]: user with uuid {} sent ws msg {}",
                     time::precise_time_ns(),
                     "[[[ we don't have uuids in ws yet ]]]",
                     msg);
            self.out.send("I hear you loud and clear").unwrap();
            Ok(())
        }

        fn on_open(&mut self, _: ws::Handshake) -> ws::Result<()> {
            // TODO
            println!("ws:opn[{}]: user with uuid {} opened ws cxn",
                     time::precise_time_ns(),
                     "[[[ we don't have uuids in ws yet ]]]");
            Ok(())
        }

        fn on_close(&mut self, code: ws::CloseCode, reason: &str) {
            println!("ws:cls[{}]: user with uuid {} closed ws cxn\n\tCode [{:?}] reason: {}",
                     time::precise_time_ns(),
                     "[[[ we don't have uuids in ws yet ]]]",
                     code,
                     reason);
            // TODO
        }

        fn on_error(&mut self, err: ws::Error) {
            println!("ws:err[{}]: user with uuid {} got error {}",
                     time::precise_time_ns(),
                     "[[[ we don't have uuids in ws yet ]]]",
                     err);
            // TODO
        }
    }

    {
        let domain : String;

        match env::var("DOMAIN") {
            Ok(val) => domain = val,
            Err(_)  => {
                println!("* DOMAIN not set; using localhost.");
                domain = String::from("localhost")
            }
        }

        let web_domain = domain.clone();
        let webserver_thread = thread::spawn(move || {
            let web_addr = format!("{}:3000", web_domain);
            println!("TCP server listening on {}", web_addr);
            println!("\tGo to: http://{}/client/html", web_addr);

            let mut assets_mount = Mount::new();
            assets_mount
                .mount("/client", Static::new(Path::new("../client")));

            Iron::new(assets_mount).http(web_addr.clone()).unwrap()
        });

        let ws_domain = domain.clone();
        let websocket_thread = thread::spawn(move || {
            let ws_addr = format!("{}:3001", ws_domain);
            println!("WebSockets server listening on {}", ws_addr);
            ws::listen(ws_addr.clone(), |out| WSServer {
                out: out,
                connections: connections.clone()
            }).unwrap();
        });

        webserver_thread.join().unwrap();
        websocket_thread.join().unwrap();
    }
}

#[cfg(test)]
mod tests {
    // NOTE: bring into scope everything defined above
    use super::*;

    #[test]
    fn it_works() {
        // TODO: write a test
    }
}
<|endoftext|>


<commit_before>#![feature(test)]

extern crate parity_wasm;
extern crate time;

use std::fs;

fn rate(file_name: &'static str, iterations: u64) {
	let file_size = fs::metadata(file_name).expect(&format!("{} to exist", file_name)).len();
	let mut total_ms = 0;

	for _ in 0..iterations {
		let start = time::PreciseTime::now();
		let _module = parity_wasm::deserialize_file(file_name);
		let end = time::PreciseTime::now();

		total_ms += start.to(end).num_milliseconds();
	}

	println!("Rate for {}: {} MB/s", file_name,
		(file_size as f64 * iterations as f64 / (1024*1024) as f64) /  // total work megabytes
		(total_ms as f64 / 1000f64)									   // total seconds
	);
}

fn main() {
	rate("./res/cases/v1/clang.wasm", 10);
	rate("./res/cases/v1/hello.wasm", 100);
	rate("./res/cases/v1/with_names.wasm", 100);
}<commit_msg>remove test feature<commit_after>extern crate parity_wasm;
extern crate time;

use std::fs;

fn rate(file_name: &'static str, iterations: u64) {
	let file_size = fs::metadata(file_name).expect(&format!("{} to exist", file_name)).len();
	let mut total_ms = 0;

	for _ in 0..iterations {
		let start = time::PreciseTime::now();
		let _module = parity_wasm::deserialize_file(file_name);
		let end = time::PreciseTime::now();

		total_ms += start.to(end).num_milliseconds();
	}

	println!("Rate for {}: {} MB/s", file_name,
		(file_size as f64 * iterations as f64 / (1024*1024) as f64) /  // total work megabytes
		(total_ms as f64 / 1000f64)									   // total seconds
	);
}

fn main() {
	rate("./res/cases/v1/clang.wasm", 10);
	rate("./res/cases/v1/hello.wasm", 100);
	rate("./res/cases/v1/with_names.wasm", 100);
}<|endoftext|>


<commit_before>use std::cmp::{max, min};
use std::mem;

use rustbox::keyboard::Key;
use rustbox::{RustBox};

use trie::Trie;
use tui::style;
use tui::termbox;
use tui::widget::{WidgetRet, Widget};
use utils;

// TODO: Make these settings
const SCROLLOFF : i32 = 5;
const HIST_SIZE : usize = 30;

pub struct TextField {
    /// The message that's currently being edited (not yet sent)
    buffer : Vec<char>,

    /// Cursor in currently shown line
    cursor : i32,

    /// Horizontal scroll
    scroll : i32,

    /// Width of the widget
    width : i32,

    /// A history of sent messages/commands. Once added messages are never
    /// modified. A modification attempt should result in a new buffer with a
    /// copy of the vector in history. (old contents of the buffer will be lost)
    history : Vec<Vec<char>>,

    mode : Mode,
}

enum Mode {
    /// Editing the buffer
    Edit,

    /// Browsing history
    History(i32),

    /// Auto-completing a nick in channel
    Autocomplete {
        original_buffer    : Vec<char>,
        insertion_point    : usize,
        word_starts        : usize,
        completions        : Vec<String>,
        current_completion : usize,
    }
}

impl TextField {
    pub fn new(width : i32) -> TextField {
        TextField {
            buffer: Vec::with_capacity(512),
            cursor: 0,
            scroll: 0,
            width: width,
            history: Vec::with_capacity(HIST_SIZE),
            mode: Mode::Edit,
        }
    }

    pub fn resize_(&mut self, width : i32, _ : i32) {
        self.width = width;
        self.move_cursor_to_end();
    }

    pub fn draw_(&self, _ : &RustBox, pos_x : i32, pos_y : i32) {
        match self.mode {
            Mode::Edit => {
                draw_line(&self.buffer, pos_x, pos_y, self.scroll, self.width, self.cursor);
            },
            Mode::History(hist_curs) => {
                draw_line(&self.history[hist_curs as usize],
                          pos_x, pos_y, self.scroll, self.width, self.cursor);
            },
            Mode::Autocomplete {
                ref original_buffer, insertion_point, word_starts,
                ref completions, current_completion
            } => {
                // draw a placeholder for the cursor
                termbox::print_char(pos_x + self.cursor - self.scroll, pos_y,
                                    style::USER_MSG.fg, style::USER_MSG.bg,
                                    ' ');

                let completion : &str = &completions[current_completion];

                let mut orig_buf_iter = original_buffer.iter().cloned();
                let mut completion_iter = completion.chars();

                let iter : utils::InsertIterator<char> =
                    utils::insert_iter(&mut orig_buf_iter, &mut completion_iter, insertion_point);

                for (char_idx, char) in iter.enumerate() {
                    if char_idx >= ((self.scroll + self.width) as usize) {
                        break;
                    }

                    if char_idx >= self.scroll as usize {
                        if char_idx >= word_starts &&
                                char_idx < insertion_point + completion.len() {
                            termbox::print_char(pos_x + (char_idx as i32) - self.scroll, pos_y,
                                                style::COMPLETION.fg, style::COMPLETION.bg,
                                                char);
                        } else {
                            termbox::print_char(pos_x + (char_idx as i32) - self.scroll, pos_y,
                                                style::USER_MSG.fg, style::USER_MSG.bg,
                                                char);
                        }

                    }
                }

                termbox::set_cursor(pos_x + self.cursor - self.scroll, pos_y);
            },
        }
    }

    pub fn keypressed_(&mut self, key : Key) -> WidgetRet {
        match key {
            Key::Char(ch) => {
                self.modify();
                self.buffer.insert(self.cursor as usize, ch);
                self.inc_cursor();
                WidgetRet::KeyHandled
            },

            Key::Backspace => {
                if self.cursor > 0 {
                    self.modify();
                    self.buffer.remove(self.cursor as usize - 1);
                    self.dec_cursor();
                }
                WidgetRet::KeyHandled
            },

            Key::Ctrl(ch) => {
                if ch == 'a' {
                    self.move_cursor(0);
                    WidgetRet::KeyHandled
                }

                else if ch == 'e' {
                    self.move_cursor_to_end();
                    WidgetRet::KeyHandled
                }

                else if ch == 'k' {
                    if self.cursor != self.line_len() {
                        self.modify();
                        self.buffer.drain(self.cursor as usize ..);
                    }
                    WidgetRet::KeyHandled
                }

                else if ch == 'w' {
                    self.consume_word_before_curs();
                    WidgetRet::KeyHandled
                }

                else {
                    WidgetRet::KeyIgnored
                }
            },

            Key::Left => {
                self.dec_cursor();
                WidgetRet::KeyHandled
            },

            Key::Right => {
                self.inc_cursor();
                WidgetRet::KeyHandled
            },

            Key::Enter => {
                if self.line_len() > 0 {
                    self.modify();

                    let ret = mem::replace(&mut self.buffer, Vec::new());
                    if self.history.len() == HIST_SIZE {
                        let mut reuse = self.history.remove(0);
                        reuse.clear();
                        reuse.extend_from_slice(&ret);
                        self.history.push(reuse);
                    } else {
                        self.history.push(ret.clone());
                    }

                    self.move_cursor(0);

                    WidgetRet::Input(ret)
                } else {
                    WidgetRet::KeyHandled
                }
            },

            ////////////////////////////////////////////////////////////////////
            // Scrolling in history or autocompletion list

            Key::Up => {
                let mode = mem::replace(&mut self.mode, Mode::Edit);

                match mode {
                    Mode::Edit => {},
                    Mode::History(hist_curs) => {
                        if hist_curs != (self.history.len() - 1) as i32 {
                            self.mode = Mode::History(hist_curs + 1);
                        } else {
                            self.mode = Mode::Edit;
                        }
                        self.move_cursor_to_end();
                    },
                    Mode::Autocomplete {
                        original_buffer, insertion_point,
                        word_starts, completions, current_completion, ..
                    } => {
                        let current_completion =
                            if current_completion != completions.len() - 1 {
                                current_completion + 1
                            } else {
                                current_completion
                            };

                        let cursor = (insertion_point + completions[current_completion].len()) as i32;

                        self.mode = Mode::Autocomplete {
                            original_buffer: original_buffer,
                            insertion_point: insertion_point,
                            word_starts: word_starts,
                            completions: completions,
                            current_completion: current_completion,
                        };

                        self.move_cursor(cursor);
                    },
                }

                WidgetRet::KeyHandled
            },

            Key::Down => {
                let mode = mem::replace(&mut self.mode, Mode::Edit);

                match mode {
                    Mode::Edit => {
                        if !self.history.is_empty() {
                            self.mode = Mode::History((self.history.len() as i32) - 1);
                            self.move_cursor_to_end();
                        }
                    },
                    Mode::History(hist_curs) => {
                        self.mode = Mode::History(
                            if hist_curs > 0 { hist_curs - 1 } else { hist_curs });
                        self.move_cursor_to_end();
                    },
                    Mode::Autocomplete {
                        original_buffer, insertion_point,
                        word_starts, completions, current_completion, ..
                    } => {
                        let current_completion =
                            if current_completion > 0 {
                                current_completion - 1
                            } else {
                                current_completion
                            };

                        let cursor = (insertion_point + completions[current_completion].len()) as i32;

                        self.mode = Mode::Autocomplete {
                            original_buffer: original_buffer,
                            insertion_point: insertion_point,
                            word_starts: word_starts,
                            completions: completions,
                            current_completion: current_completion,
                        };

                        self.move_cursor(cursor);
                    },
                }

                WidgetRet::KeyHandled
            },

            ////////////////////////////////////////////////////////////////////

            _ => WidgetRet::KeyIgnored,
        }
    }

    fn consume_word_before_curs(&mut self) {
        // No modifications can happen if the scroll is at the beginning
        if self.cursor == 0 {
            return;
        }

        self.modify();

        let char = self.buffer[(self.cursor - 1) as usize];

        // Try to imitate vim's behaviour here.
        if char.is_whitespace() {
            self.consume_before(|c| c.is_whitespace());
        }

        else if char.is_alphanumeric() {
            self.consume_before(|c| c.is_alphanumeric());
        }

        else {
            self.consume_before(|c| !c.is_alphanumeric());
        }
    }

    fn consume_before<F>(&mut self, f : F) where F : Fn(char) -> bool {
        let end_range = self.cursor as usize;
        let mut begin_range = self.cursor - 1;
        while begin_range >= 0 && f(self.buffer[begin_range as usize]) {
            begin_range -= 1;
        }
        self.buffer.drain(((begin_range + 1) as usize) .. end_range);
        self.move_cursor(begin_range + 1);
    }

    // Ignoring auto-completions
    fn shown_line(&self) -> &Vec<char> {
        match self.mode {
            Mode::Edit | Mode::Autocomplete { .. } => &self.buffer,
            Mode::History(hist_curs) => &self.history[hist_curs as usize],
        }
    }

    fn line_len(&self) -> i32 {
        match self.mode {
            Mode::Edit => {
                self.buffer.len() as i32
            },
            Mode::History(hist_curs) => {
                self.history[hist_curs as usize].len() as i32
            },
            Mode::Autocomplete { ref original_buffer, ref completions, current_completion, .. } => {
                (original_buffer.len() + completions[current_completion].len()) as i32
            },
        }
    }

    ////////////////////////////////////////////////////////////////////////////

    fn in_autocomplete(&self) -> bool {
        match self.mode {
            Mode::Autocomplete { .. } => true,
            _ => false
        }
    }

    fn modify(&mut self) {
        match self.mode {
            Mode::Edit => {},
            Mode::History(hist_idx) => {
                self.buffer.clear();
                self.buffer.extend_from_slice(&self.history[hist_idx as usize]);
            },
            Mode::Autocomplete {
                ref mut original_buffer,
                mut insertion_point,
                ref mut completions,
                current_completion,
                ..
            } => {
                let mut buffer  : Vec<char>   = mem::replace(original_buffer, vec![]);
                let completions : Vec<String> = mem::replace(completions, vec![]);
                let word = &completions[current_completion];

                // FIXME: This is inefficient
                for char in word.chars() {
                    buffer.insert(insertion_point, char);
                    insertion_point += 1;
                }

                self.buffer = buffer;
            }
        }

        self.mode = Mode::Edit;
    }

    ////////////////////////////////////////////////////////////////////////////
    // Manipulating cursor

    fn inc_cursor(&mut self) {
        let cur = min(self.line_len(), self.cursor + 1);
        self.move_cursor(cur);
    }

    fn dec_cursor(&mut self) {
        let cur = max(0, self.cursor - 1);
        self.move_cursor(cur);
    }

    fn move_cursor_to_end(&mut self) {
        let cursor = self.line_len();
        self.move_cursor(cursor);
    }

    fn move_cursor(&mut self, cursor : i32) {
        assert!(cursor >= 0 && cursor <= self.line_len());
        self.cursor = cursor;

        if self.line_len() + 1 < self.width {
            self.scroll = 0;
        } else {
            let scrolloff = { if self.width < 2 * SCROLLOFF + 1 { 0 } else { SCROLLOFF } };

            let left_end  = self.scroll;
            let right_end = self.scroll + self.width;

            if cursor - scrolloff < left_end {
                self.scroll = max(0, cursor - scrolloff);
            } else if cursor + scrolloff >= right_end {
                self.scroll = min(// +1 because cursor should be visible, i.e.
                                  // right_end > cursor should hold after this
                                  max(0, cursor + 1 + scrolloff - self.width),
                                  // +1 because cursor goes one more character
                                  // after the buffer, to be able to add chars
                                  max(0, self.line_len() + 1 - self.width));
            }
        }
    }
}

fn draw_line(line : &[char], pos_x : i32, pos_y : i32, scroll : i32, width : i32, cursor : i32) {
    let slice : &[char] = &line[ scroll as usize .. min(line.len(), (scroll + width) as usize) ];

    termbox::print_chars(pos_x, pos_y, style::USER_MSG.fg, style::USER_MSG.bg, slice);

    // On my terminal the cursor is only shown when there's a character
    // under it.
    if cursor as usize >= line.len() {
        termbox::print_char(pos_x + cursor - scroll, pos_y,
                            style::USER_MSG.fg, style::USER_MSG.bg,
                            ' ');
    }
    termbox::set_cursor(pos_x + cursor - scroll, pos_y);
}

impl Widget for TextField {
    fn resize(&mut self, width : i32, height : i32) {
        self.resize_(width, height);
    }

    fn draw(&self, rustbox : &RustBox, pos_x : i32, pos_y : i32) {
        self.draw_(rustbox, pos_x, pos_y);
    }

    fn keypressed(&mut self, key : Key) -> WidgetRet {
        self.keypressed_(key)
    }

    fn autocomplete(&mut self, dict : &Trie) {
        if self.in_autocomplete() {
            // AWFUL CODE YO
            self.keypressed(Key::Up);
            return;
        }

        let cursor_right = max(0, self.cursor - 1);
        let mut cursor_left = cursor_right;

        let completions = {
            let line = self.shown_line();

            while cursor_left >= 0
                    && line.get(cursor_left as usize).map(|c| c.is_alphanumeric()).unwrap_or(false) {
                cursor_left -= 1;
            }

            let word = {
                if cursor_left == cursor_right {
                    &[]
                } else {
                    &line[ ((cursor_left + 1) as usize) .. (cursor_right as usize) ]
                }
            };

            dict.drop_pfx(&mut word.iter().cloned())
        };

        if !completions.is_empty() {
            let completion_len = completions[0].len();
            self.mode = Mode::Autocomplete {
                original_buffer: self.shown_line().to_owned(),
                insertion_point: self.cursor as usize,
                word_starts: (cursor_left + 1) as usize,
                completions: completions,
                current_completion: 0,
            };
            let cursor = self.cursor;
            self.move_cursor(cursor + completion_len as i32);
        }
    }
}
<commit_msg>another off-by-one error<commit_after>use std::cmp::{max, min};
use std::mem;

use rustbox::keyboard::Key;
use rustbox::{RustBox};

use trie::Trie;
use tui::style;
use tui::termbox;
use tui::widget::{WidgetRet, Widget};
use utils;

// TODO: Make these settings
const SCROLLOFF : i32 = 5;
const HIST_SIZE : usize = 30;

pub struct TextField {
    /// The message that's currently being edited (not yet sent)
    buffer : Vec<char>,

    /// Cursor in currently shown line
    cursor : i32,

    /// Horizontal scroll
    scroll : i32,

    /// Width of the widget
    width : i32,

    /// A history of sent messages/commands. Once added messages are never
    /// modified. A modification attempt should result in a new buffer with a
    /// copy of the vector in history. (old contents of the buffer will be lost)
    history : Vec<Vec<char>>,

    mode : Mode,
}

enum Mode {
    /// Editing the buffer
    Edit,

    /// Browsing history
    History(i32),

    /// Auto-completing a nick in channel
    Autocomplete {
        original_buffer    : Vec<char>,
        insertion_point    : usize,
        word_starts        : usize,
        completions        : Vec<String>,
        current_completion : usize,
    }
}

impl TextField {
    pub fn new(width : i32) -> TextField {
        TextField {
            buffer: Vec::with_capacity(512),
            cursor: 0,
            scroll: 0,
            width: width,
            history: Vec::with_capacity(HIST_SIZE),
            mode: Mode::Edit,
        }
    }

    pub fn resize_(&mut self, width : i32, _ : i32) {
        self.width = width;
        self.move_cursor_to_end();
    }

    pub fn draw_(&self, _ : &RustBox, pos_x : i32, pos_y : i32) {
        match self.mode {
            Mode::Edit => {
                draw_line(&self.buffer, pos_x, pos_y, self.scroll, self.width, self.cursor);
            },
            Mode::History(hist_curs) => {
                draw_line(&self.history[hist_curs as usize],
                          pos_x, pos_y, self.scroll, self.width, self.cursor);
            },
            Mode::Autocomplete {
                ref original_buffer, insertion_point, word_starts,
                ref completions, current_completion
            } => {
                // draw a placeholder for the cursor
                termbox::print_char(pos_x + self.cursor - self.scroll, pos_y,
                                    style::USER_MSG.fg, style::USER_MSG.bg,
                                    ' ');

                let completion : &str = &completions[current_completion];

                let mut orig_buf_iter = original_buffer.iter().cloned();
                let mut completion_iter = completion.chars();

                let iter : utils::InsertIterator<char> =
                    utils::insert_iter(&mut orig_buf_iter, &mut completion_iter, insertion_point);

                for (char_idx, char) in iter.enumerate() {
                    if char_idx >= ((self.scroll + self.width) as usize) {
                        break;
                    }

                    if char_idx >= self.scroll as usize {
                        if char_idx >= word_starts &&
                                char_idx < insertion_point + completion.len() {
                            termbox::print_char(pos_x + (char_idx as i32) - self.scroll, pos_y,
                                                style::COMPLETION.fg, style::COMPLETION.bg,
                                                char);
                        } else {
                            termbox::print_char(pos_x + (char_idx as i32) - self.scroll, pos_y,
                                                style::USER_MSG.fg, style::USER_MSG.bg,
                                                char);
                        }

                    }
                }

                termbox::set_cursor(pos_x + self.cursor - self.scroll, pos_y);
            },
        }
    }

    pub fn keypressed_(&mut self, key : Key) -> WidgetRet {
        match key {
            Key::Char(ch) => {
                self.modify();
                self.buffer.insert(self.cursor as usize, ch);
                self.inc_cursor();
                WidgetRet::KeyHandled
            },

            Key::Backspace => {
                if self.cursor > 0 {
                    self.modify();
                    self.buffer.remove(self.cursor as usize - 1);
                    self.dec_cursor();
                }
                WidgetRet::KeyHandled
            },

            Key::Ctrl(ch) => {
                if ch == 'a' {
                    self.move_cursor(0);
                    WidgetRet::KeyHandled
                }

                else if ch == 'e' {
                    self.move_cursor_to_end();
                    WidgetRet::KeyHandled
                }

                else if ch == 'k' {
                    if self.cursor != self.line_len() {
                        self.modify();
                        self.buffer.drain(self.cursor as usize ..);
                    }
                    WidgetRet::KeyHandled
                }

                else if ch == 'w' {
                    self.consume_word_before_curs();
                    WidgetRet::KeyHandled
                }

                else {
                    WidgetRet::KeyIgnored
                }
            },

            Key::Left => {
                self.dec_cursor();
                WidgetRet::KeyHandled
            },

            Key::Right => {
                self.inc_cursor();
                WidgetRet::KeyHandled
            },

            Key::Enter => {
                if self.line_len() > 0 {
                    self.modify();

                    let ret = mem::replace(&mut self.buffer, Vec::new());
                    if self.history.len() == HIST_SIZE {
                        let mut reuse = self.history.remove(0);
                        reuse.clear();
                        reuse.extend_from_slice(&ret);
                        self.history.push(reuse);
                    } else {
                        self.history.push(ret.clone());
                    }

                    self.move_cursor(0);

                    WidgetRet::Input(ret)
                } else {
                    WidgetRet::KeyHandled
                }
            },

            ////////////////////////////////////////////////////////////////////
            // Scrolling in history or autocompletion list

            Key::Up => {
                let mode = mem::replace(&mut self.mode, Mode::Edit);

                match mode {
                    Mode::Edit => {},
                    Mode::History(hist_curs) => {
                        if hist_curs != (self.history.len() - 1) as i32 {
                            self.mode = Mode::History(hist_curs + 1);
                        } else {
                            self.mode = Mode::Edit;
                        }
                        self.move_cursor_to_end();
                    },
                    Mode::Autocomplete {
                        original_buffer, insertion_point,
                        word_starts, completions, current_completion, ..
                    } => {
                        let current_completion =
                            if current_completion != completions.len() - 1 {
                                current_completion + 1
                            } else {
                                current_completion
                            };

                        let cursor = (insertion_point + completions[current_completion].len()) as i32;

                        self.mode = Mode::Autocomplete {
                            original_buffer: original_buffer,
                            insertion_point: insertion_point,
                            word_starts: word_starts,
                            completions: completions,
                            current_completion: current_completion,
                        };

                        self.move_cursor(cursor);
                    },
                }

                WidgetRet::KeyHandled
            },

            Key::Down => {
                let mode = mem::replace(&mut self.mode, Mode::Edit);

                match mode {
                    Mode::Edit => {
                        if !self.history.is_empty() {
                            self.mode = Mode::History((self.history.len() as i32) - 1);
                            self.move_cursor_to_end();
                        }
                    },
                    Mode::History(hist_curs) => {
                        self.mode = Mode::History(
                            if hist_curs > 0 { hist_curs - 1 } else { hist_curs });
                        self.move_cursor_to_end();
                    },
                    Mode::Autocomplete {
                        original_buffer, insertion_point,
                        word_starts, completions, current_completion, ..
                    } => {
                        let current_completion =
                            if current_completion > 0 {
                                current_completion - 1
                            } else {
                                current_completion
                            };

                        let cursor = (insertion_point + completions[current_completion].len()) as i32;

                        self.mode = Mode::Autocomplete {
                            original_buffer: original_buffer,
                            insertion_point: insertion_point,
                            word_starts: word_starts,
                            completions: completions,
                            current_completion: current_completion,
                        };

                        self.move_cursor(cursor);
                    },
                }

                WidgetRet::KeyHandled
            },

            ////////////////////////////////////////////////////////////////////

            _ => WidgetRet::KeyIgnored,
        }
    }

    fn consume_word_before_curs(&mut self) {
        // No modifications can happen if the scroll is at the beginning
        if self.cursor == 0 {
            return;
        }

        self.modify();

        let char = self.buffer[(self.cursor - 1) as usize];

        // Try to imitate vim's behaviour here.
        if char.is_whitespace() {
            self.consume_before(|c| c.is_whitespace());
        }

        else if char.is_alphanumeric() {
            self.consume_before(|c| c.is_alphanumeric());
        }

        else {
            self.consume_before(|c| !c.is_alphanumeric());
        }
    }

    fn consume_before<F>(&mut self, f : F) where F : Fn(char) -> bool {
        let end_range = self.cursor as usize;
        let mut begin_range = self.cursor - 1;
        while begin_range >= 0 && f(self.buffer[begin_range as usize]) {
            begin_range -= 1;
        }
        self.buffer.drain(((begin_range + 1) as usize) .. end_range);
        self.move_cursor(begin_range + 1);
    }

    // Ignoring auto-completions
    fn shown_line(&self) -> &Vec<char> {
        match self.mode {
            Mode::Edit | Mode::Autocomplete { .. } => &self.buffer,
            Mode::History(hist_curs) => &self.history[hist_curs as usize],
        }
    }

    fn line_len(&self) -> i32 {
        match self.mode {
            Mode::Edit => {
                self.buffer.len() as i32
            },
            Mode::History(hist_curs) => {
                self.history[hist_curs as usize].len() as i32
            },
            Mode::Autocomplete { ref original_buffer, ref completions, current_completion, .. } => {
                (original_buffer.len() + completions[current_completion].len()) as i32
            },
        }
    }

    ////////////////////////////////////////////////////////////////////////////

    fn in_autocomplete(&self) -> bool {
        match self.mode {
            Mode::Autocomplete { .. } => true,
            _ => false
        }
    }

    fn modify(&mut self) {
        match self.mode {
            Mode::Edit => {},
            Mode::History(hist_idx) => {
                self.buffer.clear();
                self.buffer.extend_from_slice(&self.history[hist_idx as usize]);
            },
            Mode::Autocomplete {
                ref mut original_buffer,
                mut insertion_point,
                ref mut completions,
                current_completion,
                ..
            } => {
                let mut buffer  : Vec<char>   = mem::replace(original_buffer, vec![]);
                let completions : Vec<String> = mem::replace(completions, vec![]);
                let word = &completions[current_completion];

                // FIXME: This is inefficient
                for char in word.chars() {
                    buffer.insert(insertion_point, char);
                    insertion_point += 1;
                }

                self.buffer = buffer;
            }
        }

        self.mode = Mode::Edit;
    }

    ////////////////////////////////////////////////////////////////////////////
    // Manipulating cursor

    fn inc_cursor(&mut self) {
        let cur = min(self.line_len(), self.cursor + 1);
        self.move_cursor(cur);
    }

    fn dec_cursor(&mut self) {
        let cur = max(0, self.cursor - 1);
        self.move_cursor(cur);
    }

    fn move_cursor_to_end(&mut self) {
        let cursor = self.line_len();
        self.move_cursor(cursor);
    }

    fn move_cursor(&mut self, cursor : i32) {
        assert!(cursor >= 0 && cursor <= self.line_len());
        self.cursor = cursor;

        if self.line_len() + 1 < self.width {
            self.scroll = 0;
        } else {
            let scrolloff = { if self.width < 2 * SCROLLOFF + 1 { 0 } else { SCROLLOFF } };

            let left_end  = self.scroll;
            let right_end = self.scroll + self.width;

            if cursor - scrolloff < left_end {
                self.scroll = max(0, cursor - scrolloff);
            } else if cursor + scrolloff >= right_end {
                self.scroll = min(// +1 because cursor should be visible, i.e.
                                  // right_end > cursor should hold after this
                                  max(0, cursor + 1 + scrolloff - self.width),
                                  // +1 because cursor goes one more character
                                  // after the buffer, to be able to add chars
                                  max(0, self.line_len() + 1 - self.width));
            }
        }
    }
}

fn draw_line(line : &[char], pos_x : i32, pos_y : i32, scroll : i32, width : i32, cursor : i32) {
    let slice : &[char] = &line[ scroll as usize .. min(line.len(), (scroll + width) as usize) ];

    termbox::print_chars(pos_x, pos_y, style::USER_MSG.fg, style::USER_MSG.bg, slice);

    // On my terminal the cursor is only shown when there's a character
    // under it.
    if cursor as usize >= line.len() {
        termbox::print_char(pos_x + cursor - scroll, pos_y,
                            style::USER_MSG.fg, style::USER_MSG.bg,
                            ' ');
    }
    termbox::set_cursor(pos_x + cursor - scroll, pos_y);
}

impl Widget for TextField {
    fn resize(&mut self, width : i32, height : i32) {
        self.resize_(width, height);
    }

    fn draw(&self, rustbox : &RustBox, pos_x : i32, pos_y : i32) {
        self.draw_(rustbox, pos_x, pos_y);
    }

    fn keypressed(&mut self, key : Key) -> WidgetRet {
        self.keypressed_(key)
    }

    fn autocomplete(&mut self, dict : &Trie) {
        if self.in_autocomplete() {
            // AWFUL CODE YO
            self.keypressed(Key::Up);
            return;
        }

        let cursor_right = self.cursor;
        let mut cursor_left = max(0, cursor_right - 1);

        let completions = {
            let line = self.shown_line();

            while cursor_left >= 0
                    && line.get(cursor_left as usize).map(|c| c.is_alphanumeric()).unwrap_or(false) {
                cursor_left -= 1;
            }

            let word = {
                if cursor_left == cursor_right {
                    &[]
                } else {
                    &line[ ((cursor_left + 1) as usize) .. (cursor_right as usize) ]
                }
            };

            dict.drop_pfx(&mut word.iter().cloned())
        };

        if !completions.is_empty() {
            let completion_len = completions[0].len();
            self.mode = Mode::Autocomplete {
                original_buffer: self.shown_line().to_owned(),
                insertion_point: self.cursor as usize,
                word_starts: (cursor_left + 1) as usize,
                completions: completions,
                current_completion: 0,
            };
            let cursor = self.cursor;
            self.move_cursor(cursor + completion_len as i32);
        }
    }
}
<|endoftext|>


<commit_before>/* Cymbalum, Molecular Simulation in Rust - Copyright (C) 2015 Guillaume Fraux
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/
 */
//! Caching energy components for speeding up the energy computation in
//! Monte-Carlo simulations.
//!
//! In most of Monte-Carlo moves, only a very small subset of the system changes.
//! We can use that property to remove the need of recomputing most of the
//! energy components, by storing them and providing update callbacks.
use std::mem::swap;

use super::Universe;
use types::{Vector3D, Array2};

// Yep, this is a type ...
type UpdateCallback = Option<Box<Fn(&mut EnergyCache)>>;

/// This is a cache for energy computation.
///
/// Cache integrity is left up to the user of this structure: any function with
/// `update_` prefix must be called as needed to ensure cache consistency.
pub struct EnergyCache {
    /// 2-D array containing the pairs interactions between particles `i` and
    /// `j` at index `i, j`
    pairs_cache: Array2<f64>,
    /// Energy of all the pairs in the system
    pairs: f64,
    /// Energy of all the bonds in the system
    bonds: f64,
    /// Energy of all the angles in the system
    angles: f64,
    /// Energy of all the dihedrals angles in the system
    dihedrals: f64,
    /// Energy of coulombic interactions
    coulomb: f64,
    /// Energy of global interactions
    global: f64,

    /// TODO
    update_particles_moved: UpdateCallback
}

impl EnergyCache {
    /// Create a new empty energy cache.
    pub fn new() -> EnergyCache {
        EnergyCache {
            pairs_cache: Array2::new(),
            pairs: 0.0,
            bonds: 0.0,
            angles: 0.0,
            dihedrals: 0.0,
            coulomb: 0.0,
            global: 0.0,

            update_particles_moved: None,
        }
    }

    /// Clear all values in the cache by setting them to 0
    fn clear(&mut self) {
        self.pairs_cache.fill(0.0);
        self.pairs = 0.0;
        self.bonds = 0.0;
        self.angles = 0.0;
        self.dihedrals = 0.0;
        self.coulomb = 0.0;
        self.global = 0.0;
    }

    /// Initialize the cache to be used with `universe`. After a call to this
    /// function, the cache is only usable with the same universe. To change
    /// the associated universe, one must call this fucntion again.
    pub fn init(&mut self, universe: &Universe) {
        self.clear();
        self.pairs_cache.resize((universe.size(), universe.size()));
        let evaluator = universe.energy_evaluator();

        for i in 0..universe.size() {
            for j in (i + 1)..universe.size() {
                let r = universe.wraped_vector(i, j).norm();
                let energy = evaluator.pair(r, i, j);
                self.pairs_cache[(i, j)] = energy;
                self.pairs_cache[(j, i)] = energy;
                self.pairs += energy;
            }
        }

        self.bonds = evaluator.bonds();
        self.angles = evaluator.angles();
        self.dihedrals = evaluator.dihedrals();
        self.coulomb = evaluator.coulomb();
        self.global = evaluator.global();
    }

    /// Get the cached energy
    pub fn energy(&self) -> f64 {
        let mut energy = 0.0;
        energy += self.pairs;

        energy += self.bonds;
        energy += self.angles;
        energy += self.dihedrals;

        energy += self.coulomb;
        energy += self.global;
        return energy;
    }
}

impl EnergyCache {
    /// Get the cost of moving the set of particles with indexes in `idxes` to
    /// `newpos`. This function DO NOT update the cache, the
    /// `update_particles_moved` function MUST be called if the particles are
    /// effectively moved.
    pub fn move_particles_cost(&mut self, universe: &Universe, idxes: Vec<usize>, newpos: &[Vector3D]) -> f64 {
        let evaluator = universe.energy_evaluator();

        // First, go for pair interactions
        let mut new_pairs = Array2::<f64>::with_size((universe.size(), universe.size()));
        let mut pairs_delta = 0.0;
        // Interactions with the sub-system not being moved
        for (i, &part_i) in idxes.iter().enumerate() {
            for part_j in 0..universe.size() {
                // Exclude interactions inside the sub-system.
                if idxes.contains(&part_j) {continue}

                let r = universe.cell().distance(&universe[part_j].position, &newpos[i]);
                let energy = evaluator.pair(r, part_i, part_j);

                pairs_delta += energy;
                new_pairs[(part_i, part_j)] += energy;
                new_pairs[(part_j, part_i)] += energy;

                pairs_delta -= self.pairs_cache[(part_i, part_j)];
            }
        }

        // Interactions withing the sub-system being moved
        for (i, &part_i) in idxes.iter().enumerate() {
            for (j, &part_j) in idxes.iter().enumerate().skip(i + 1) {
                let r = universe.cell().distance(&newpos[i], &newpos[j]);
                let energy = evaluator.pair(r, part_i, part_j);

                pairs_delta += energy;
                new_pairs[(part_i, part_j)] += energy;
                new_pairs[(part_j, part_i)] += energy;

                pairs_delta -= self.pairs_cache[(part_i, part_j)];
            }
        }

        // Recompute everything here. One day we could store some terms and do
        // the same thing than for pair interactions.
        let mut bonds = 0.0;
        let mut angles = 0.0;
        let mut dihedrals = 0.0;
        for molecule in universe.molecules() {
            for bond in molecule.bonds() {
                let (i, j) = (bond.i(), bond.j());
                let ri = new_position(universe, i, &idxes, newpos);
                let rj = new_position(universe, j, &idxes, newpos);
                let r = universe.cell().distance(ri, rj);
                bonds += evaluator.bond(r, i, j);
            }

            for angle in molecule.angles() {
                let (i, j, k) = (angle.i(), angle.j(), angle.k());
                let ri = new_position(universe, i, &idxes, newpos);
                let rj = new_position(universe, j, &idxes, newpos);
                let rk = new_position(universe, k, &idxes, newpos);
                let theta = universe.cell().angle(ri, rj, rk);
                angles += evaluator.angle(theta, i, j, k);
            }

            for dihedral in molecule.dihedrals() {
                let (i, j, k, m) = (dihedral.i(), dihedral.j(), dihedral.k(), dihedral.m());
                let ri = new_position(universe, i, &idxes, newpos);
                let rj = new_position(universe, j, &idxes, newpos);
                let rk = new_position(universe, k, &idxes, newpos);
                let rm = new_position(universe, m, &idxes, newpos);
                let phi = universe.cell().dihedral(ri, rj, rk, rm);
                dihedrals += evaluator.dihedral(phi, i, j, k, m);
            }
        }

        let coulomb_delta = 0.0;
        if let Some(_) = *universe.coulomb_potential() {
            unimplemented!();
        }

        let global_delta = 0.0;
        if universe.global_potentials().len() != 0 {
            unimplemented!();
        }

        let cost = pairs_delta + (bonds - self.bonds)
                               + (angles - self.angles)
                               + (dihedrals - self.dihedrals)
                               + coulomb_delta + global_delta;

        self.update_particles_moved = Some(Box::new(move |cache| {
            cache.bonds = bonds;
            cache.angles = angles;
            cache.dihedrals = dihedrals;

            cache.pairs += pairs_delta;
            cache.coulomb += coulomb_delta;
            cache.global += global_delta;

            let (n, m) = new_pairs.size();
            debug_assert!(n == m);
            debug_assert!(n == cache.pairs_cache.size().0);
            debug_assert!(n == cache.pairs_cache.size().1);
            for i in 0..n {
                for j in 0..n {
                    if new_pairs[(i, j)] != 0.0 {
                        cache.pairs_cache[(i, j)] = new_pairs[(i, j)];
                    }
                }
            }
        }));
        return cost;
    }

    /// Update the cache after a corresponding call to `move_particles_cost`.
    ///
    /// # Panic
    /// If not called after `move_particles_cost`.
    pub fn update_particles_moved(&mut self) {
        let mut update = None;
        swap(&mut self.update_particles_moved, &mut update);
        if let Some(update) = update {
            update(self);
        } else {
            error!("Error in `update_particles_moved`: \
                    This function MUST be called after a call to `move_particles_cost`");
            panic!()
        }
    }
}

/// Return either the new position of a particle (from `newpos`) if its index
/// is in `idxes`, or its old position in the universe.
fn new_position<'a>(universe: &'a Universe, i: usize, idxes: &[usize], newpos: &'a[Vector3D]) -> &'a Vector3D {
    for (idx, &particle) in idxes.iter().enumerate() {
        if particle == i {
            return &newpos[idx];
        }
    }
    return &universe[i].position;
}

#[cfg(test)]
mod tests {
    use super::*;
    use universe::{Universe, UnitCell};
    use utils::universe_from_xyz;
    use input::read_interactions_string;
    use types::Vector3D;

    fn testing_universe() -> Universe {
        let mut universe = universe_from_xyz("4
        bonds
        O     0.000000     0.000000     0.000000
        O     0.000000     0.000000     1.480000
        H     0.895669     0.000000    -0.316667
        H    -0.895669     0.000000     1.796667");
        universe.set_cell(UnitCell::cubic(10.0));
        assert!(universe.molecules().len() == 1);

        read_interactions_string(&mut universe, "
        # Values are completely random, just having a bit of all the types
        pairs:
            - atoms: [H, H]
              type: LennardJones
              sigma: 3.0 A
              epsilon: 0.2 kJ/mol
            - atoms: [O, O]
              type: Null
            - atoms: [O, H]
              type: LennardJones
              sigma: 1.0 A
              epsilon: 0.33 kJ/mol
        bonds:
            - atoms: [O, H]
              type: Harmonic
              x0: 3.4 A
              k: 522 kJ/mol/A^2
        angles:
            - atoms: [O, O, H]
              type: Harmonic
              x0: 120 deg
              k: 150 kJ/mol/deg^2
        dihedrals:
            - atoms: [H, O, O, H]
              type: Harmonic
              x0: 180 deg
              k: 800 kJ/mol/deg^2
        ").unwrap();

        return universe;
    }

    #[test]
    fn cache_energy() {
        let universe = testing_universe();
        let mut cache = EnergyCache::new();
        cache.init(&universe);

        assert_approx_eq!(cache.energy(), universe.potential_energy());
    }

    #[test]
    fn move_atoms() {
        let mut universe = testing_universe();
        let mut cache = EnergyCache::new();
        let old_e = universe.potential_energy();
        cache.init(&universe);

        let idxes = vec![0, 3];
        let newpos = &[Vector3D::new(0.0, 0.0, 0.5), Vector3D::new(-0.7, 0.2, 1.5)];

        let cost = cache.move_particles_cost(&universe, idxes, newpos);

        universe[0].position = newpos[0];
        universe[3].position = newpos[1];
        let new_e = universe.potential_energy();
        assert_approx_eq!(cost, new_e - old_e);

        cache.update_particles_moved();
        assert_approx_eq!(cache.energy(), new_e);

        // Check that the cache is really updated
        let old_e = new_e;
        let idxes = vec![2, 3];
        let newpos = &[Vector3D::new(0.9, 0.2, -0.4), Vector3D::new(-0.9, 0.0, 1.8)];
        let cost = cache.move_particles_cost(&universe, idxes, newpos);
        universe[2].position = newpos[0];
        universe[3].position = newpos[1];
        let new_e = universe.potential_energy();
        assert_approx_eq!(cost, new_e - old_e);
    }
}
<commit_msg>Use a generic updater for the energy cache<commit_after>/* Cymbalum, Molecular Simulation in Rust - Copyright (C) 2015 Guillaume Fraux
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/
 */
//! Caching energy components for speeding up the energy computation in
//! Monte-Carlo simulations.
//!
//! In most of Monte-Carlo moves, only a very small subset of the system changes.
//! We can use that property to remove the need of recomputing most of the
//! energy components, by storing them and providing update callbacks.
use std::mem::swap;

use super::Universe;
use types::{Vector3D, Array2};

// Yep, this is a type ...
type UpdateCallback = Box<Fn(&mut EnergyCache)>;

/// This is a cache for energy computation.
///
/// Cache integrity is left up to the user of this structure: any function with
/// `update_` prefix must be called as needed to ensure cache consistency.
pub struct EnergyCache {
    /// 2-D array containing the pairs interactions between particles `i` and
    /// `j` at index `i, j`
    pairs_cache: Array2<f64>,
    /// Energy of all the pairs in the system
    pairs: f64,
    /// Energy of all the bonds in the system
    bonds: f64,
    /// Energy of all the angles in the system
    angles: f64,
    /// Energy of all the dihedrals angles in the system
    dihedrals: f64,
    /// Energy of coulombic interactions
    coulomb: f64,
    /// Energy of global interactions
    global: f64,
    /// Callback to be called to update the cache if the universe is modified
    updater: Option<UpdateCallback>
}

impl EnergyCache {
    /// Create a new empty energy cache.
    pub fn new() -> EnergyCache {
        EnergyCache {
            pairs_cache: Array2::new(),
            pairs: 0.0,
            bonds: 0.0,
            angles: 0.0,
            dihedrals: 0.0,
            coulomb: 0.0,
            global: 0.0,
            updater: None,
        }
    }

    /// Clear all values in the cache by setting them to 0
    fn clear(&mut self) {
        self.pairs_cache.fill(0.0);
        self.pairs = 0.0;
        self.bonds = 0.0;
        self.angles = 0.0;
        self.dihedrals = 0.0;
        self.coulomb = 0.0;
        self.global = 0.0;
    }

    /// Initialize the cache to be used with `universe`. After a call to this
    /// function, the cache is only usable with the same universe. To change
    /// the associated universe, one must call this fucntion again.
    pub fn init(&mut self, universe: &Universe) {
        self.clear();
        self.pairs_cache.resize((universe.size(), universe.size()));
        let evaluator = universe.energy_evaluator();

        for i in 0..universe.size() {
            for j in (i + 1)..universe.size() {
                let r = universe.wraped_vector(i, j).norm();
                let energy = evaluator.pair(r, i, j);
                self.pairs_cache[(i, j)] = energy;
                self.pairs_cache[(j, i)] = energy;
                self.pairs += energy;
            }
        }

        self.bonds = evaluator.bonds();
        self.angles = evaluator.angles();
        self.dihedrals = evaluator.dihedrals();
        self.coulomb = evaluator.coulomb();
        self.global = evaluator.global();
    }

    /// Get the cached energy
    pub fn energy(&self) -> f64 {
        let mut energy = 0.0;
        energy += self.pairs;

        energy += self.bonds;
        energy += self.angles;
        energy += self.dihedrals;

        energy += self.coulomb;
        energy += self.global;
        return energy;
    }

    /// Update the cache after a call to a `*_cost` function.
    pub fn update(&mut self) {
        let mut updater = None;
        swap(&mut self.updater, &mut updater);
        if let Some(updater) = updater {
            updater(self);
        } else {
            error!("Error in `EnergyCache::update`: \
                    This function MUST be called after a call to a `*_cost` function");
            panic!()
        }
    }
}

impl EnergyCache {
    /// Get the cost of moving the set of particles with indexes in `idxes` to
    /// `newpos`. This function DO NOT update the cache, the
    /// `update_particles_moved` function MUST be called if the particles are
    /// effectively moved.
    pub fn move_particles_cost(&mut self, universe: &Universe, idxes: Vec<usize>, newpos: &[Vector3D]) -> f64 {
        let evaluator = universe.energy_evaluator();

        // First, go for pair interactions
        let mut new_pairs = Array2::<f64>::with_size((universe.size(), universe.size()));
        let mut pairs_delta = 0.0;
        // Interactions with the sub-system not being moved
        for (i, &part_i) in idxes.iter().enumerate() {
            for part_j in 0..universe.size() {
                // Exclude interactions inside the sub-system.
                if idxes.contains(&part_j) {continue}

                let r = universe.cell().distance(&universe[part_j].position, &newpos[i]);
                let energy = evaluator.pair(r, part_i, part_j);

                pairs_delta += energy;
                new_pairs[(part_i, part_j)] += energy;
                new_pairs[(part_j, part_i)] += energy;

                pairs_delta -= self.pairs_cache[(part_i, part_j)];
            }
        }

        // Interactions withing the sub-system being moved
        for (i, &part_i) in idxes.iter().enumerate() {
            for (j, &part_j) in idxes.iter().enumerate().skip(i + 1) {
                let r = universe.cell().distance(&newpos[i], &newpos[j]);
                let energy = evaluator.pair(r, part_i, part_j);

                pairs_delta += energy;
                new_pairs[(part_i, part_j)] += energy;
                new_pairs[(part_j, part_i)] += energy;

                pairs_delta -= self.pairs_cache[(part_i, part_j)];
            }
        }

        // Recompute everything here. One day we could store some terms and do
        // the same thing than for pair interactions.
        let mut bonds = 0.0;
        let mut angles = 0.0;
        let mut dihedrals = 0.0;
        for molecule in universe.molecules() {
            for bond in molecule.bonds() {
                let (i, j) = (bond.i(), bond.j());
                let ri = new_position(universe, i, &idxes, newpos);
                let rj = new_position(universe, j, &idxes, newpos);
                let r = universe.cell().distance(ri, rj);
                bonds += evaluator.bond(r, i, j);
            }

            for angle in molecule.angles() {
                let (i, j, k) = (angle.i(), angle.j(), angle.k());
                let ri = new_position(universe, i, &idxes, newpos);
                let rj = new_position(universe, j, &idxes, newpos);
                let rk = new_position(universe, k, &idxes, newpos);
                let theta = universe.cell().angle(ri, rj, rk);
                angles += evaluator.angle(theta, i, j, k);
            }

            for dihedral in molecule.dihedrals() {
                let (i, j, k, m) = (dihedral.i(), dihedral.j(), dihedral.k(), dihedral.m());
                let ri = new_position(universe, i, &idxes, newpos);
                let rj = new_position(universe, j, &idxes, newpos);
                let rk = new_position(universe, k, &idxes, newpos);
                let rm = new_position(universe, m, &idxes, newpos);
                let phi = universe.cell().dihedral(ri, rj, rk, rm);
                dihedrals += evaluator.dihedral(phi, i, j, k, m);
            }
        }

        let coulomb_delta = 0.0;
        if let Some(_) = *universe.coulomb_potential() {
            unimplemented!();
        }

        let global_delta = 0.0;
        if universe.global_potentials().len() != 0 {
            unimplemented!();
        }

        let cost = pairs_delta + (bonds - self.bonds)
                               + (angles - self.angles)
                               + (dihedrals - self.dihedrals)
                               + coulomb_delta + global_delta;

        self.updater = Some(Box::new(move |cache| {
            cache.bonds = bonds;
            cache.angles = angles;
            cache.dihedrals = dihedrals;

            cache.pairs += pairs_delta;
            cache.coulomb += coulomb_delta;
            cache.global += global_delta;

            let (n, m) = new_pairs.size();
            debug_assert!(n == m);
            debug_assert!(n == cache.pairs_cache.size().0);
            debug_assert!(n == cache.pairs_cache.size().1);
            for i in 0..n {
                for j in 0..n {
                    if new_pairs[(i, j)] != 0.0 {
                        cache.pairs_cache[(i, j)] = new_pairs[(i, j)];
                    }
                }
            }
        }));
        return cost;
    }
}

/// Return either the new position of a particle (from `newpos`) if its index
/// is in `idxes`, or its old position in the universe.
fn new_position<'a>(universe: &'a Universe, i: usize, idxes: &[usize], newpos: &'a[Vector3D]) -> &'a Vector3D {
    for (idx, &particle) in idxes.iter().enumerate() {
        if particle == i {
            return &newpos[idx];
        }
    }
    return &universe[i].position;
}

#[cfg(test)]
mod tests {
    use super::*;
    use universe::{Universe, UnitCell};
    use utils::universe_from_xyz;
    use input::read_interactions_string;
    use types::Vector3D;

    fn testing_universe() -> Universe {
        let mut universe = universe_from_xyz("4
        bonds
        O     0.000000     0.000000     0.000000
        O     0.000000     0.000000     1.480000
        H     0.895669     0.000000    -0.316667
        H    -0.895669     0.000000     1.796667");
        universe.set_cell(UnitCell::cubic(10.0));
        assert!(universe.molecules().len() == 1);

        read_interactions_string(&mut universe, "
        # Values are completely random, just having a bit of all the types
        pairs:
            - atoms: [H, H]
              type: LennardJones
              sigma: 3.0 A
              epsilon: 0.2 kJ/mol
            - atoms: [O, O]
              type: Null
            - atoms: [O, H]
              type: LennardJones
              sigma: 1.0 A
              epsilon: 0.33 kJ/mol
        bonds:
            - atoms: [O, H]
              type: Harmonic
              x0: 3.4 A
              k: 522 kJ/mol/A^2
        angles:
            - atoms: [O, O, H]
              type: Harmonic
              x0: 120 deg
              k: 150 kJ/mol/deg^2
        dihedrals:
            - atoms: [H, O, O, H]
              type: Harmonic
              x0: 180 deg
              k: 800 kJ/mol/deg^2
        ").unwrap();

        return universe;
    }

    #[test]
    fn cache_energy() {
        let universe = testing_universe();
        let mut cache = EnergyCache::new();
        cache.init(&universe);

        assert_approx_eq!(cache.energy(), universe.potential_energy());
    }

    #[test]
    fn move_atoms() {
        let mut universe = testing_universe();
        let mut cache = EnergyCache::new();
        let old_e = universe.potential_energy();
        cache.init(&universe);

        let idxes = vec![0, 3];
        let newpos = &[Vector3D::new(0.0, 0.0, 0.5), Vector3D::new(-0.7, 0.2, 1.5)];

        let cost = cache.move_particles_cost(&universe, idxes, newpos);

        universe[0].position = newpos[0];
        universe[3].position = newpos[1];
        let new_e = universe.potential_energy();
        assert_approx_eq!(cost, new_e - old_e);

        cache.update();
        assert_approx_eq!(cache.energy(), new_e);

        // Check that the cache is really updated
        let old_e = new_e;
        let idxes = vec![2, 3];
        let newpos = &[Vector3D::new(0.9, 0.2, -0.4), Vector3D::new(-0.9, 0.0, 1.8)];
        let cost = cache.move_particles_cost(&universe, idxes, newpos);
        universe[2].position = newpos[0];
        universe[3].position = newpos[1];
        let new_e = universe.potential_energy();
        assert_approx_eq!(cost, new_e - old_e);
    }
}
<|endoftext|>