<commit_before>use self::super::super::{http, Status};
use self::super::MAP;
use base64::{decode, encode};
use serde_json;
use std::slice;

#[allow(non_snake_case)]
#[derive(Deserialize, Debug)]
struct Response {
    dad: u8,
    sad: u8,
    lenr: u16,
    response: String,
    #[serde(rename = "responseCode")]
    status: i8,
}

pub fn data(
    ctn: u16,
    dad: *mut u8,
    sad: *mut u8,
    lenc: u16,
    command: *const u8,
    lenr: *mut u16,
    response: *mut u8,
) -> Status {
    if !MAP.lock().contains_key(&ctn) {
        error!("Card terminal has not been opened.");
        return Status::ErrInvalid;
    }

    let safe_dad: &mut u8 = unsafe { &mut *dad };
    debug!("dad: {}", safe_dad);

    let safe_sad: &mut u8 = unsafe { &mut *sad };
    debug!("sad: {}", safe_sad);
    debug!("lenc: {}", lenc);

    let safe_command = unsafe { slice::from_raw_parts(command, lenc as usize) };
    debug!("command: {:?}", safe_command);

    let safe_lenr: &mut u16 = unsafe { &mut *lenr };
    debug!("lenr: {}", safe_lenr);

    let safe_response = unsafe { slice::from_raw_parts_mut(response, *safe_lenr as usize) };
    debug!("response with {} slices formed", safe_response.len());

    let pn = match MAP.lock().get(&ctn) {
        Some(pn) => pn.clone(),
        None => {
            error!("Failed to extract pn for given ctn!");
            return Status::ErrHtsi;
        }
    };
    let mut json: HashMap<&str, String> = HashMap::new();
    json.insert("dad", format!("{}", *safe_dad));
    json.insert("sad", format!("{}", *safe_sad));
    json.insert("lenc", format!("{}", lenc));
    json.insert("command", encode(safe_command));
    json.insert("lenr", format!("{}", *safe_lenr));

    let path = format!("ct_data/{}/{}", ctn, pn);

    let response = http::request(&path, Some(json));
    let res = match response {
        Ok(response) => response,
        Err(why) => {
            error!("Request failed!");
            debug!("{}", why);
            return Status::ErrHtsi;
        }
    };

    if res.status != 200 {
        error!("Request failed! Server response was not OK!");
        return Status::ErrHtsi;
    }

    let json: Response = match serde_json::from_str(&res.body) {
        Ok(json) => json,
        Err(why) => {
            error!("Failed to parse server response data!");
            debug!("{}", why);
            return Status::ErrHtsi;
        }
    };

    let status: Status = json.status.into();
    match status {
        Status::OK => {
            *safe_dad = json.dad;
            *safe_sad = json.sad;
            *safe_lenr = json.lenr;

            let decoded = match decode(&json.response) {
                Ok(content) => {
                    debug!("Decoded response field {:?}", content);
                    content
                }
                Err(why) => {
                    error!("Failed to extract response.");
                    debug!("{}", why);
                    return Status::ErrHtsi;
                }
            };

            for (place, element) in safe_response.iter_mut().zip(decoded.iter()) {
                *place = *element;
            }

            status
        }
        _ => status,
    }
}

#[cfg(test)]
mod tests {

    use super::super::MAP;
    use super::data;
    use base64::encode;
    use rand;
    use serde_json;
    use std::env;
    use std::slice;
    use std::str;
    use std::u16::MAX;
    use test_server::{HttpResponse, TestServer};

    fn rand_params() -> (*const u8, u16, *mut u8, u16, u8, u8, u16, u16) {
        let commands: [u8; 1] = [rand::random::<u8>(); 1];
        let commands_ptr: *const u8 = &commands[0];
        let lenc: u16 = commands.len() as u16;

        let mut response: [u8; MAX as usize] = [rand::random::<u8>(); MAX as usize];
        let response_ptr: *mut u8 = &mut response[0];
        let lenr: u16 = response.len() as u16;

        let dad = rand::random::<u8>();
        let sad = rand::random::<u8>();

        let ctn = rand::random::<u16>();
        let pn = rand::random::<u16>();

        (commands_ptr, lenc, response_ptr, lenr, dad, sad, ctn, pn)
    }

    #[test]
    fn returns_err_invalid_if_terminal_closed() {
        let (commands_ptr, lenc, response_ptr, mut lenr, mut dad, mut sad, ctn, _pn) =
            rand_params();

        assert_eq!(
            -1,
            data(
                ctn,
                &mut dad,
                &mut sad,
                lenc,
                commands_ptr,
                &mut lenr,
                response_ptr,
            )
        )
    }

    #[test]
    fn returns_err_htsi_if_no_server() {
        env::set_var("K2_BASE_URL", "http://127.0.0.1:65432");

        let (commands_ptr, lenc, response_ptr, mut lenr, mut dad, mut sad, ctn, pn) = rand_params();

        MAP.lock().insert(ctn, pn);

        assert_eq!(
            -128,
            data(
                ctn,
                &mut dad,
                &mut sad,
                lenc,
                commands_ptr,
                &mut lenr,
                response_ptr,
            )
        )
    }

    #[test]
    fn use_ctn_and_pn_in_request_path() {
        let server = TestServer::new(0, |_| HttpResponse::BadRequest().into());
        env::set_var("K2_BASE_URL", server.url());

        let (commands_ptr, lenc, response_ptr, mut lenr, mut dad, mut sad, ctn, pn) = rand_params();
        MAP.lock().insert(ctn, pn);

        data(
            ctn,
            &mut dad,
            &mut sad,
            lenc,
            commands_ptr,
            &mut lenr,
            response_ptr,
        );

        let path = server.received_request().unwrap().path;
        assert_eq!(path, *format!("/ct_data/{}/{}", ctn, pn));
    }

    #[test]
    #[ignore]
    fn post_body_contains_parameter() {
        let server = TestServer::new(0, |_| HttpResponse::BadRequest().into());
        env::set_var("K2_BASE_URL", server.url());

        let (command, lenc, response, mut lenr, mut dad, mut sad, ctn, pn) = rand_params();
        let exp_cmd = unsafe { encode(slice::from_raw_parts(command, lenc as usize)) };

        MAP.lock().insert(ctn, pn);

        assert_eq!(
            -128,
            data(ctn, &mut dad, &mut sad, lenc, command, &mut lenr, response)
        );

        #[derive(Deserialize, Debug)]
        struct Json {
            dad: u8,
            sad: u8,
            lenc: u16,
            command: String,
            lenr: u16,
        }

        let body = server.received_request().unwrap().body;
        let json: Json = serde_json::from_str(&body).unwrap();

        assert_eq!(dad, json.dad);
        assert_eq!(sad, json.sad);
        assert_eq!(exp_cmd, json.command);
        assert_eq!(lenc, json.lenc);
        assert_eq!(lenr, json.lenr);
    }

    #[test]
    fn response_is_mapped_to_parameter() {
        let server = TestServer::new(0, |_| {
            HttpResponse::Ok()
                .body(r#"{"dad":39,"sad":63,"lenr":5,"response":"AQIDBAU=","responseCode":0}"#)
        });
        env::set_var("K2_BASE_URL", server.url());

        let (command, lenc, response, mut lenr, mut dad, mut sad, ctn, pn) = rand_params();
        MAP.lock().insert(ctn, pn);

        data(ctn, &mut dad, &mut sad, lenc, command, &mut lenr, response);

        assert_eq!(dad, 39);
        assert_eq!(sad, 63);
        assert_eq!(5, lenr);

        let slice = unsafe { slice::from_raw_parts(response, lenr as usize) };
        assert_eq!([1, 2, 3, 4, 5], slice);
    }

    #[test]
    fn returns_err_htsi_if_server_response_is_not_200() {
        let server = TestServer::new(0, |_| HttpResponse::BadRequest().into());
        env::set_var("K2_BASE_URL", server.url());

        let (commands_ptr, lenc, response_ptr, mut lenr, mut dad, mut sad, ctn, pn) = rand_params();
        MAP.lock().insert(ctn, pn);

        assert_eq!(
            -128,
            data(
                ctn,
                &mut dad,
                &mut sad,
                lenc,
                commands_ptr,
                &mut lenr,
                response_ptr,
            )
        );
    }

    #[test]
    fn returns_err_htsi_if_server_response_not_contains_response_struct_as_json() {
        let server = TestServer::new(0, |_| HttpResponse::Ok().body("hello world"));
        env::set_var("K2_BASE_URL", server.url());

        let (commands_ptr, lenc, response_ptr, mut lenr, mut dad, mut sad, ctn, pn) = rand_params();
        MAP.lock().insert(ctn, pn);

        assert_eq!(
            -128,
            data(
                ctn,
                &mut dad,
                &mut sad,
                lenc,
                commands_ptr,
                &mut lenr,
                response_ptr,
            )
        );
    }

    #[test]
    fn returns_response_status_from_valid_json_response_struct() {
        let server = TestServer::new(0, |_| {
            HttpResponse::Ok()
                .body(r#"{"dad":1,"sad":1,"lenr":1,"response":"a=","responseCode":-11}"#)
        });
        env::set_var("K2_BASE_URL", server.url());

        let (commands_ptr, lenc, response_ptr, mut lenr, mut dad, mut sad, ctn, pn) = rand_params();
        MAP.lock().insert(ctn, pn);

        assert_eq!(
            -11,
            data(
                ctn,
                &mut dad,
                &mut sad,
                lenc,
                commands_ptr,
                &mut lenr,
                response_ptr,
            )
        );
    }
}
<commit_msg>adjust test server api<commit_after>use self::super::super::{http, Status};
use self::super::MAP;
use base64::{decode, encode};
use serde_json;
use std::collections::HashMap;
use std::slice;

#[allow(non_snake_case)]
#[derive(Deserialize, Debug)]
struct Response {
    dad: u8,
    sad: u8,
    lenr: u16,
    response: String,
    #[serde(rename = "responseCode")]
    status: i8,
}

pub fn data(
    ctn: u16,
    dad: *mut u8,
    sad: *mut u8,
    lenc: u16,
    command: *const u8,
    lenr: *mut u16,
    response: *mut u8,
) -> Status {
    if !MAP.lock().contains_key(&ctn) {
        error!("Card terminal has not been opened.");
        return Status::ErrInvalid;
    }

    let pn = match MAP.lock().get(&ctn) {
        Some(pn) => *pn,
        None => {
            error!("Failed to extract pn for given ctn!");
            return Status::ErrHtsi;
        }
    };

    let safe_dad: &mut u8 = unsafe { &mut *dad };
    debug!("dad: {}", safe_dad);

    let safe_sad: &mut u8 = unsafe { &mut *sad };
    debug!("sad: {}", safe_sad);
    debug!("lenc: {}", lenc);

    let safe_command = unsafe { slice::from_raw_parts(command, lenc as usize) };
    debug!("command: {:?}", safe_command);

    let safe_lenr: &mut u16 = unsafe { &mut *lenr };
    debug!("lenr: {}", safe_lenr);

    let safe_response = unsafe { slice::from_raw_parts_mut(response, *safe_lenr as usize) };
    debug!("response with {} slices formed", safe_response.len());

    let mut json: HashMap<&str, String> = HashMap::new();
    json.insert("dad", format!("{}", *safe_dad));
    json.insert("sad", format!("{}", *safe_sad));
    json.insert("lenc", format!("{}", lenc));
    json.insert("command", encode(safe_command));
    json.insert("lenr", format!("{}", *safe_lenr));

    let path = format!("ct_data/{}/{}", ctn, pn);
    let response = http::request(&path, Some(json));
    let res = match response {
        Ok(response) => response,
        Err(why) => {
            error!("Request failed!");
            debug!("{}", why);
            return Status::ErrHtsi;
        }
    };

    if res.status != 200 {
        error!("Request failed! Server response was not OK!");
        return Status::ErrHtsi;
    }

    let json: Response = match serde_json::from_str(&res.body) {
        Ok(json) => json,
        Err(why) => {
            error!("Failed to parse server response data!");
            debug!("{}", why);
            return Status::ErrHtsi;
        }
    };

    let status: Status = json.status.into();
    match status {
        Status::OK => {
            *safe_dad = json.dad;
            *safe_sad = json.sad;
            *safe_lenr = json.lenr;

            let decoded = match decode(&json.response) {
                Ok(content) => {
                    debug!("Decoded response field {:?}", content);
                    content
                }
                Err(why) => {
                    error!("Failed to extract response.");
                    debug!("{}", why);
                    return Status::ErrHtsi;
                }
            };

            for (place, element) in safe_response.iter_mut().zip(decoded.iter()) {
                *place = *element;
            }

            status
        }
        _ => status,
    }
}

#[cfg(test)]
mod tests {

    use super::super::MAP;
    use super::data;
    use base64::encode;
    use rand;
    use serde_json::{self, Value};
    use std::env;
    use std::slice;
    use std::u16::MAX;
    use test_server::{self, HttpResponse};

    #[test]
    fn returns_err_invalid_if_terminal_closed() {
        let (commands_ptr, lenc, response_ptr, mut lenr, mut dad, mut sad, ctn, _pn) =
            rand_params();

        assert_eq!(
            -1,
            data(
                ctn,
                &mut dad,
                &mut sad,
                lenc,
                commands_ptr,
                &mut lenr,
                response_ptr,
            )
        )
    }

    #[test]
    fn returns_err_htsi_if_no_server() {
        env::set_var("K2_BASE_URL", "http://127.0.0.1:65432");

        let (commands_ptr, lenc, response_ptr, mut lenr, mut dad, mut sad, ctn, pn) = rand_params();

        MAP.lock().insert(ctn, pn);

        assert_eq!(
            -128,
            data(
                ctn,
                &mut dad,
                &mut sad,
                lenc,
                commands_ptr,
                &mut lenr,
                response_ptr,
            )
        )
    }

    #[test]
    fn use_ctn_and_pn_in_request_path() {
        let server = test_server::new(0, |_| HttpResponse::BadRequest().into());
        env::set_var("K2_BASE_URL", server.url());

        let (commands_ptr, lenc, response_ptr, mut lenr, mut dad, mut sad, ctn, pn) = rand_params();
        MAP.lock().insert(ctn, pn);

        data(
            ctn,
            &mut dad,
            &mut sad,
            lenc,
            commands_ptr,
            &mut lenr,
            response_ptr,
        );

        let path = server.requests.next().unwrap().path;
        assert_eq!(path, *format!("/ct_data/{}/{}", ctn, pn));
    }

    #[test]
    fn post_body_contains_parameter() {
        let server = test_server::new(0, |_| HttpResponse::BadRequest().into());
        env::set_var("K2_BASE_URL", server.url());

        let (command, lenc, response, mut lenr, mut dad, mut sad, ctn, pn) = rand_params();
        let exp_cmd = unsafe { encode(slice::from_raw_parts(command, lenc as usize)) };

        MAP.lock().insert(ctn, pn);

        assert_eq!(
            -128,
            data(ctn, &mut dad, &mut sad, lenc, command, &mut lenr, response)
        );

        let body = server.requests.next().unwrap().body;
        let json: Value = serde_json::from_str(&body).unwrap();

        assert_eq!(*json.get("dad").unwrap(), json!(format!("{}", dad)));
        assert_eq!(*json.get("sad").unwrap(), json!(format!("{}", sad)));
        assert_eq!(*json.get("command").unwrap(), json!(format!("{}", exp_cmd)));
        assert_eq!(*json.get("lenc").unwrap(), json!(format!("{}", lenc)));
        assert_eq!(*json.get("lenr").unwrap(), json!(format!("{}", lenr)));
    }

    #[test]
    fn response_is_mapped_to_parameter() {
        let server = test_server::new(0, |_| {
            HttpResponse::Ok()
                .body(r#"{"dad":39,"sad":63,"lenr":5,"response":"AQIDBAU=","responseCode":0}"#)
        });
        env::set_var("K2_BASE_URL", server.url());

        let (command, lenc, response, mut lenr, mut dad, mut sad, ctn, pn) = rand_params();
        MAP.lock().insert(ctn, pn);

        data(ctn, &mut dad, &mut sad, lenc, command, &mut lenr, response);

        assert_eq!(dad, 39);
        assert_eq!(sad, 63);
        assert_eq!(5, lenr);

        let slice = unsafe { slice::from_raw_parts(response, lenr as usize) };
        assert_eq!([1, 2, 3, 4, 5], slice);
    }

    #[test]
    fn returns_err_htsi_if_server_response_is_not_200() {
        let server = test_server::new(0, |_| HttpResponse::BadRequest().into());
        env::set_var("K2_BASE_URL", server.url());

        let (commands_ptr, lenc, response_ptr, mut lenr, mut dad, mut sad, ctn, pn) = rand_params();
        MAP.lock().insert(ctn, pn);

        assert_eq!(
            -128,
            data(
                ctn,
                &mut dad,
                &mut sad,
                lenc,
                commands_ptr,
                &mut lenr,
                response_ptr,
            )
        );
    }

    #[test]
    fn returns_err_htsi_if_server_response_not_contains_response_struct_as_json() {
        let server = test_server::new(0, |_| HttpResponse::Ok().body("hello world"));
        env::set_var("K2_BASE_URL", server.url());

        let (commands_ptr, lenc, response_ptr, mut lenr, mut dad, mut sad, ctn, pn) = rand_params();
        MAP.lock().insert(ctn, pn);

        assert_eq!(
            -128,
            data(
                ctn,
                &mut dad,
                &mut sad,
                lenc,
                commands_ptr,
                &mut lenr,
                response_ptr,
            )
        );
    }

    #[test]
    fn returns_response_status_from_valid_json_response_struct() {
        let server = test_server::new(0, |_| {
            HttpResponse::Ok()
                .body(r#"{"dad":1,"sad":1,"lenr":1,"response":"a=","responseCode":-11}"#)
        });
        env::set_var("K2_BASE_URL", server.url());

        let (commands_ptr, lenc, response_ptr, mut lenr, mut dad, mut sad, ctn, pn) = rand_params();
        MAP.lock().insert(ctn, pn);

        assert_eq!(
            -11,
            data(
                ctn,
                &mut dad,
                &mut sad,
                lenc,
                commands_ptr,
                &mut lenr,
                response_ptr,
            )
        );
    }

    fn rand_params() -> (*const u8, u16, *mut u8, u16, u8, u8, u16, u16) {
        let commands: [u8; 1] = [rand::random::<u8>(); 1];
        let commands_ptr: *const u8 = &commands[0];
        let lenc: u16 = commands.len() as u16;

        let mut response: [u8; MAX as usize] = [rand::random::<u8>(); MAX as usize];
        let response_ptr: *mut u8 = &mut response[0];
        let lenr: u16 = response.len() as u16;

        let dad = rand::random::<u8>();
        let sad = rand::random::<u8>();

        let ctn = rand::random::<u16>();
        let pn = rand::random::<u16>();

        (commands_ptr, lenc, response_ptr, lenr, dad, sad, ctn, pn)
    }
}
<|endoftext|>


<commit_before>//! A type that can be treated as a difference.
//!
//! Differential dataflow most commonly tracks the counts associated with records in a multiset, but it
//! generalizes to tracking any map from the records to an Abelian group. The most common generalization
//! is when we maintain both a count and another accumulation, for example height. The differential
//! dataflow collections would then track for each record the total of counts and heights, which allows
//! us to track something like the average.

use std::ops::{AddAssign, Neg, Mul};

use ::Data;

pub use self::Abelian as Diff;

/// A type that can be treated as a difference.
///
/// The mathematical requirements are, I believe, an Abelian group, in that we require addition, inverses,
/// and almost certainly use commutativity somewhere (it isn't clear if it is a requirement, as it isn't
/// clear that there are semantics other than "we accumulate your differences"; I suspect we don't always
/// accumulate them in the right order, so commutativity is important until we conclude otherwise).
pub trait Monoid : for<'a> AddAssign<&'a Self> + ::std::marker::Sized + Data + Clone {
	/// Returns true if the element is the additive identity.
	///
	/// This is primarily used by differential dataflow to know when it is safe to delete an update.
	/// When a difference accumulates to zero, the difference has no effect on any accumulation and can
	/// be removed.
	#[inline(always)]
	fn is_zero(&self) -> bool { self.eq(&Self::zero()) }
	/// The additive identity.
	///
	/// This method is primarily used by differential dataflow internals as part of consolidation, when
	/// one value is accumulated elsewhere and must be replaced by valid but harmless value.
	fn zero() -> Self;
}

/// A commutative monoid with negation.
///
/// This trait represents a commutative group, here a commutative monoid with
/// the additional support for subtraction and negation. An identity subtracted
/// from itself or added to its negation should be the zero element from the
/// underlying monoid.
pub trait Abelian : Monoid + Neg<Output=Self> { }
impl<T: Monoid + Neg<Output=Self>> Abelian for T { }

impl Monoid for isize {
	#[inline(always)] fn zero() -> Self { 0 }
}

impl Monoid for i64 {
	#[inline(always)] fn zero() -> Self { 0 }
}

impl Monoid for i32 {
	#[inline(always)] fn zero() -> Self { 0 }
}

/// The difference defined by a pair of difference elements.
///
/// This type is essentially a "pair", though in Rust the tuple types do not derive the numeric
/// traits we require, and so we need to emulate the types ourselves. In the interest of ergonomics,
/// we may eventually replace the numeric traits with our own, so that we can implement them for
/// tuples and allow users to ignore details like these.
#[derive(Abomonation, Copy, Ord, PartialOrd, Eq, PartialEq, Debug, Clone, Serialize, Deserialize)]
pub struct DiffPair<R1, R2> {
	/// The first element in the pair.
	pub element1: R1,
	/// The second element in the pair.
	pub element2: R2,
}

impl<R1, R2> DiffPair<R1, R2> {
	/// Creates a new Diff pair from two elements.
	#[inline(always)] pub fn new(elt1: R1, elt2: R2) -> Self {
		DiffPair {
			element1: elt1,
			element2: elt2,
		}
	}
}

impl<R1: Monoid, R2: Monoid> Monoid for DiffPair<R1, R2> {
	#[inline(always)] fn zero() -> Self {
		DiffPair {
			element1: R1::zero(),
			element2: R2::zero(),
		}
	}
}

impl<'a, R1: AddAssign<&'a R1>, R2: AddAssign<&'a R2>> AddAssign<&'a DiffPair<R1, R2>> for DiffPair<R1, R2> {
	#[inline(always)] fn add_assign(&mut self, rhs: &'a Self) {
		self.element1 += &rhs.element1;
		self.element2 += &rhs.element2;
	}
}

impl<R1: Neg, R2: Neg> Neg for DiffPair<R1, R2> {
	type Output = DiffPair<<R1 as Neg>::Output, <R2 as Neg>::Output>;
	#[inline(always)] fn neg(self) -> Self::Output {
		DiffPair {
			element1: -self.element1,
			element2: -self.element2,
		}
	}
}

impl<T: Copy, R1: Mul<T>, R2: Mul<T>> Mul<T> for DiffPair<R1,R2> {
	type Output = DiffPair<<R1 as Mul<T>>::Output, <R2 as Mul<T>>::Output>;
	fn mul(self, other: T) -> Self::Output {
		DiffPair::new(
			self.element1 * other,
			self.element2 * other,
		)
	}
}

// // TODO: This currently causes rustc to trip a recursion limit, because who knows why.
// impl<R1: Diff, R2: Diff> Mul<DiffPair<R1,R2>> for isize
// where isize: Mul<R1>, isize: Mul<R2>, <isize as Mul<R1>>::Output: Diff, <isize as Mul<R2>>::Output: Diff {
// 	type Output = DiffPair<<isize as Mul<R1>>::Output, <isize as Mul<R2>>::Output>;
// 	fn mul(self, other: DiffPair<R1,R2>) -> Self::Output {
// 		DiffPair::new(
// 			self * other.element1,
// 			self * other.element2,
// 		)
// 	}
// }

/// A variable number of accumulable updates.
#[derive(Abomonation, Ord, PartialOrd, Eq, PartialEq, Debug, Clone, Serialize, Deserialize)]
pub struct DiffVector<R> {
	buffer: Vec<R>,
}

impl<R: Monoid> Monoid for DiffVector<R> {
	#[inline(always)] fn is_zero(&self) -> bool {
		self.buffer.iter().all(|x| x.is_zero())
	}
	#[inline(always)] fn zero() -> Self {
		Self { buffer: Vec::new() }
	}
}

impl<'a, R: AddAssign<&'a R>+Clone> AddAssign<&'a DiffVector<R>> for DiffVector<R> {
	#[inline(always)]
	fn add_assign(&mut self, rhs: &'a Self) {

		// Ensure sufficient length to receive addition.
		while self.buffer.len() < rhs.buffer.len() {
			let element = &rhs.buffer[self.buffer.len()];
			self.buffer.push(element.clone());
		}

		// As other is not longer, apply updates without tests.
		for (index, update) in rhs.buffer.iter().enumerate() {
			self.buffer[index] += update;
		}
	}
}

impl<R: Neg<Output=R>+Clone> Neg for DiffVector<R> {
	type Output = DiffVector<<R as Neg>::Output>;
	#[inline(always)]
	fn neg(mut self) -> Self::Output {
		for update in self.buffer.iter_mut() {
			*update = -update.clone();
		}
		self
	}
}

impl<T: Copy, R: Mul<T>> Mul<T> for DiffVector<R> {
	type Output = DiffVector<<R as Mul<T>>::Output>;
	fn mul(self, other: T) -> Self::Output {
		let buffer =
		self.buffer
			.into_iter()
			.map(|x| x * other)
			.collect();

		DiffVector { buffer }
	}
}<commit_msg>Implement new() for DiffVector<commit_after>//! A type that can be treated as a difference.
//!
//! Differential dataflow most commonly tracks the counts associated with records in a multiset, but it
//! generalizes to tracking any map from the records to an Abelian group. The most common generalization
//! is when we maintain both a count and another accumulation, for example height. The differential
//! dataflow collections would then track for each record the total of counts and heights, which allows
//! us to track something like the average.

use std::ops::{AddAssign, Neg, Mul};

use ::Data;

pub use self::Abelian as Diff;

/// A type that can be treated as a difference.
///
/// The mathematical requirements are, I believe, an Abelian group, in that we require addition, inverses,
/// and almost certainly use commutativity somewhere (it isn't clear if it is a requirement, as it isn't
/// clear that there are semantics other than "we accumulate your differences"; I suspect we don't always
/// accumulate them in the right order, so commutativity is important until we conclude otherwise).
pub trait Monoid : for<'a> AddAssign<&'a Self> + ::std::marker::Sized + Data + Clone {
	/// Returns true if the element is the additive identity.
	///
	/// This is primarily used by differential dataflow to know when it is safe to delete an update.
	/// When a difference accumulates to zero, the difference has no effect on any accumulation and can
	/// be removed.
	#[inline(always)]
	fn is_zero(&self) -> bool { self.eq(&Self::zero()) }
	/// The additive identity.
	///
	/// This method is primarily used by differential dataflow internals as part of consolidation, when
	/// one value is accumulated elsewhere and must be replaced by valid but harmless value.
	fn zero() -> Self;
}

/// A commutative monoid with negation.
///
/// This trait represents a commutative group, here a commutative monoid with
/// the additional support for subtraction and negation. An identity subtracted
/// from itself or added to its negation should be the zero element from the
/// underlying monoid.
pub trait Abelian : Monoid + Neg<Output=Self> { }
impl<T: Monoid + Neg<Output=Self>> Abelian for T { }

impl Monoid for isize {
	#[inline(always)] fn zero() -> Self { 0 }
}

impl Monoid for i64 {
	#[inline(always)] fn zero() -> Self { 0 }
}

impl Monoid for i32 {
	#[inline(always)] fn zero() -> Self { 0 }
}

/// The difference defined by a pair of difference elements.
///
/// This type is essentially a "pair", though in Rust the tuple types do not derive the numeric
/// traits we require, and so we need to emulate the types ourselves. In the interest of ergonomics,
/// we may eventually replace the numeric traits with our own, so that we can implement them for
/// tuples and allow users to ignore details like these.
#[derive(Abomonation, Copy, Ord, PartialOrd, Eq, PartialEq, Debug, Clone, Serialize, Deserialize)]
pub struct DiffPair<R1, R2> {
	/// The first element in the pair.
	pub element1: R1,
	/// The second element in the pair.
	pub element2: R2,
}

impl<R1, R2> DiffPair<R1, R2> {
	/// Creates a new Diff pair from two elements.
	#[inline(always)] pub fn new(elt1: R1, elt2: R2) -> Self {
		DiffPair {
			element1: elt1,
			element2: elt2,
		}
	}
}

impl<R1: Monoid, R2: Monoid> Monoid for DiffPair<R1, R2> {
	#[inline(always)] fn zero() -> Self {
		DiffPair {
			element1: R1::zero(),
			element2: R2::zero(),
		}
	}
}

impl<'a, R1: AddAssign<&'a R1>, R2: AddAssign<&'a R2>> AddAssign<&'a DiffPair<R1, R2>> for DiffPair<R1, R2> {
	#[inline(always)] fn add_assign(&mut self, rhs: &'a Self) {
		self.element1 += &rhs.element1;
		self.element2 += &rhs.element2;
	}
}

impl<R1: Neg, R2: Neg> Neg for DiffPair<R1, R2> {
	type Output = DiffPair<<R1 as Neg>::Output, <R2 as Neg>::Output>;
	#[inline(always)] fn neg(self) -> Self::Output {
		DiffPair {
			element1: -self.element1,
			element2: -self.element2,
		}
	}
}

impl<T: Copy, R1: Mul<T>, R2: Mul<T>> Mul<T> for DiffPair<R1,R2> {
	type Output = DiffPair<<R1 as Mul<T>>::Output, <R2 as Mul<T>>::Output>;
	fn mul(self, other: T) -> Self::Output {
		DiffPair::new(
			self.element1 * other,
			self.element2 * other,
		)
	}
}

// // TODO: This currently causes rustc to trip a recursion limit, because who knows why.
// impl<R1: Diff, R2: Diff> Mul<DiffPair<R1,R2>> for isize
// where isize: Mul<R1>, isize: Mul<R2>, <isize as Mul<R1>>::Output: Diff, <isize as Mul<R2>>::Output: Diff {
// 	type Output = DiffPair<<isize as Mul<R1>>::Output, <isize as Mul<R2>>::Output>;
// 	fn mul(self, other: DiffPair<R1,R2>) -> Self::Output {
// 		DiffPair::new(
// 			self * other.element1,
// 			self * other.element2,
// 		)
// 	}
// }

/// A variable number of accumulable updates.
#[derive(Abomonation, Ord, PartialOrd, Eq, PartialEq, Debug, Clone, Serialize, Deserialize)]
pub struct DiffVector<R> {
	buffer: Vec<R>,
}

impl<R> DiffVector<R> {
    /// Create new DiffVector from Vec
    #[inline(always)]
    pub fn new(vec: Vec<R>) -> DiffVector<R> {
        DiffVector { buffer: vec }
    }
}

impl<R: Monoid> Monoid for DiffVector<R> {
	#[inline(always)] fn is_zero(&self) -> bool {
		self.buffer.iter().all(|x| x.is_zero())
	}
	#[inline(always)] fn zero() -> Self {
		Self { buffer: Vec::new() }
	}
}

impl<'a, R: AddAssign<&'a R>+Clone> AddAssign<&'a DiffVector<R>> for DiffVector<R> {
	#[inline(always)]
	fn add_assign(&mut self, rhs: &'a Self) {

		// Ensure sufficient length to receive addition.
		while self.buffer.len() < rhs.buffer.len() {
			let element = &rhs.buffer[self.buffer.len()];
			self.buffer.push(element.clone());
		}

		// As other is not longer, apply updates without tests.
		for (index, update) in rhs.buffer.iter().enumerate() {
			self.buffer[index] += update;
		}
	}
}

impl<R: Neg<Output=R>+Clone> Neg for DiffVector<R> {
	type Output = DiffVector<<R as Neg>::Output>;
	#[inline(always)]
	fn neg(mut self) -> Self::Output {
		for update in self.buffer.iter_mut() {
			*update = -update.clone();
		}
		self
	}
}

impl<T: Copy, R: Mul<T>> Mul<T> for DiffVector<R> {
	type Output = DiffVector<<R as Mul<T>>::Output>;
	fn mul(self, other: T) -> Self::Output {
		let buffer =
		self.buffer
			.into_iter()
			.map(|x| x * other)
			.collect();

		DiffVector { buffer }
	}
}<|endoftext|>


<commit_before>pub mod search;

use std::thread;
use std::cmp::min;
use std::sync::Arc;
use std::sync::mpsc::{channel, Sender, Receiver};
use std::time::SystemTime;
use basetypes::*;
use uci::{UciEngine, UciEngineFactory, EngineReply, OptionName, OptionDescription};
use position::Position;
use chess_move::*;
use tt::TranspositionTable;

// use rand;
// use rand::distributions::{Sample, Range};
// let mut rng = rand::thread_rng();
// let mut between = Range::new(0, 10);
// let x = between.sample(&mut rng);

const VERSION: &'static str = "0.1";
const MAX_DEPTH: u8 = 126;

/// Implements `UciEngine` trait.
pub struct Engine {
    position: Position,
    pondering_is_allowed: bool,
    is_thinking: bool,
    is_pondering: bool,
    replies: Vec<EngineReply>,
    tt: Arc<TranspositionTable>,
    commands: Sender<search::Command>,
    reports: Receiver<search::Report>,
    thinking_since: SystemTime,
    searched_nodes: NodeCount,
    searched_time: u64, // milliseconds
    searched_depth: u8,
    stop_when: TimeManagement,
}

impl Engine {
    /// Creates a new instance.
    ///
    /// `tt_size_mb` is the preferred size of the transposition
    /// table in Mbytes.
    pub fn new(tt_size_mb: usize) -> Engine {
        let mut tt = TranspositionTable::new();
        tt.resize(tt_size_mb);
        let tt = Arc::new(tt);
        let (commands_tx, commands_rx) = channel();
        let (reports_tx, reports_rx) = channel();
        let tt2 = tt.clone();
        thread::spawn(move || {
            search::run_deepening(tt2, commands_rx, reports_tx);
        });

        Engine {
            position: Position::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QKqk - 0 \
                                          1")
                          .ok()
                          .unwrap(),
            pondering_is_allowed: false,
            is_thinking: false,
            is_pondering: false,
            replies: vec![],
            tt: tt,
            commands: commands_tx,
            reports: reports_rx,
            thinking_since: SystemTime::now(),
            searched_nodes: 0,
            searched_time: 0,
            searched_depth: 0,
            stop_when: TimeManagement::Infinite,
        }
    }
}

impl UciEngine for Engine {
    fn set_option(&mut self, name: &str, value: &str) {
        match name {
            // Tells the engine that it will be allowed to ponder.
            // This option is needed because the engine might change
            // its time management algorithm when pondering is
            // allowed.
            "Ponder" => {
                self.pondering_is_allowed = value == "true";
            }

            // An invalid option. Notice that we do not support
            // re-sizing of the transposition table once the engine
            // had started.
            _ => (),
        }
    }

    fn new_game(&mut self) {
        // Clearing the transposition table would not change anything,
        // so we do nothing.
    }

    fn position(&mut self, fen: &str, moves: &mut Iterator<Item = &str>) {
        if !self.is_thinking {
            if let Ok(p) = Position::from_history(fen, moves) {
                self.position = p;
                self.tt.new_search();
            }
        }
    }

    #[allow(unused_variables)]
    fn go(&mut self,
          searchmoves: Option<Vec<String>>,
          ponder: bool,
          wtime: Option<u64>,
          btime: Option<u64>,
          winc: Option<u64>,
          binc: Option<u64>,
          movestogo: Option<u64>,
          depth: Option<u64>,
          nodes: Option<u64>,
          mate: Option<u64>,
          movetime: Option<u64>,
          infinite: bool) {
        if !self.is_thinking {
            // Note: We ignore the "searchmoves" parameter.

            self.is_thinking = true;
            self.is_pondering = ponder;
            self.thinking_since = SystemTime::now();
            self.searched_nodes = 0;
            self.searched_time = 0;
            self.searched_depth = 0;

            // Figure out when we should stop thinking.
            let (time, inc) = if self.position.board().to_move() == WHITE {
                (wtime, winc.unwrap_or(0))
            } else {
                (btime, binc.unwrap_or(0))
            };
            self.stop_when = if infinite {
                TimeManagement::Infinite
            } else if movetime.is_some() {
                TimeManagement::MoveTime(movetime.unwrap())
            } else if nodes.is_some() {
                TimeManagement::Nodes(nodes.unwrap())
            } else if depth.is_some() {
                TimeManagement::Depth(min(depth.unwrap(), MAX_DEPTH as u64) as u8)
            } else {
                // TODO: Robert Hyatt proposes this:
                //
                // nMoves =  min( numberOfMovesOutOfBook, 10 )
                // factor = 2 - nMoves / 10
                // target = timeLeft / numberOfMovesUntilNextTimeControl
                // time   = factor * target
                let time = time.unwrap_or(0);
                let movestogo = movestogo.unwrap_or(40);
                let movetime = (time + inc * movestogo) / movestogo;
                TimeManagement::MoveTimeHint(min(movetime, time / 2))
            };

            // Start a search with the maximum possible depth.
            self.commands
                .send(search::Command::Search {
                    search_id: 0,
                    position: self.position.clone(),
                    depth: MAX_DEPTH,
                    lower_bound: -20000,
                    upper_bound: 20000,
                })
                .unwrap();
        }
    }

    fn ponder_hit(&mut self) {
        if self.is_thinking && self.is_pondering {
            self.is_pondering = false;
        }
    }

    fn stop(&mut self) {
        if self.is_thinking {
            self.commands.send(search::Command::Stop).unwrap();
            let best_move = self.get_best_move();
            self.replies.push(EngineReply::BestMove {
                best_move: best_move,
                ponder_move: None,
            });
            self.is_thinking = false;
        }
    }

    fn is_thinking(&self) -> bool {
        self.is_thinking
    }

    fn get_reply(&mut self) -> Option<EngineReply> {
        // TODO: implement panic mode for MoveTimeHint management.
        if self.is_thinking && !self.is_pondering {
            if match self.stop_when {
                TimeManagement::MoveTime(t) => self.searched_time >= t,
                TimeManagement::MoveTimeHint(t) => self.searched_time >= t,
                TimeManagement::Nodes(n) => self.searched_nodes >= n,
                TimeManagement::Depth(d) => self.searched_depth >= d,
                TimeManagement::Infinite => false,
            } {
                self.stop();
            }
        }

        // Empty the reports queue.
        while let Ok(report) = self.reports.try_recv() {
            // let rep_type = match report {
            //     search::Report::Progress { searched_nodes, .. } => format!("progress {}", searched_nodes),
            //     search::Report::Done { .. } => "done".to_string(),
            // };
            // self.replies.push(EngineReply::Info(vec![
            //     ("string".to_string(), rep_type),
            // ]));
            if self.is_thinking {
                match report {
                    search::Report::Progress { searched_nodes, depth, .. } => {
                        self.searched_nodes = searched_nodes;
                        let duration = self.thinking_since.elapsed().unwrap();
                        self.searched_time = 1000 * duration.as_secs() +
                                             (duration.subsec_nanos() / 1000000) as u64;
                        if self.searched_depth < depth {
                            self.searched_depth = depth;
                            let mut p = self.position.clone();
                            let mut v = MoveStack::new();
                            let mut pv = String::from("");
                            let mut pv_length = 0;
                            let mut value = None;
                            'depthloop: for _ in 0..depth {
                                let m = match self.tt.probe(p.hash()) {
                                    Some(entry) => {
                                        if value.is_none() {
                                            value = Some(entry.value());
                                        }
                                        entry.move16()
                                    }
                                    None => 0,
                                };
                                if m != 0 {
                                    p.generate_moves(&mut v);
                                    while let Some(x) = v.pop() {
                                        if x.digest() == m && p.do_move(x) {
                                            pv_length += 1;
                                            pv.push_str(&x.notation());
                                            pv.push(' ');
                                            v.clear();
                                            continue 'depthloop;
                                        }
                                    }
                                }
                                break;
                            }
                            if searched_nodes > 0 && pv_length >= depth {
                                self.replies.push(EngineReply::Info(vec![
                                    ("depth".to_string(), format!("{}", depth)),
                                    ("score".to_string(), format!("cp {}", value.unwrap_or(666))),
                                    ("nodes".to_string(), format!("{}", searched_nodes)),
                                    ("time".to_string(), format!("{}", self.searched_time)),
                                    ("pv".to_string(), format!("{}", pv)),
                                ]));
                            }
                        }
                    }
                    _ => (),
                }
            }
        }

        if self.replies.len() > 0 {
            Some(self.replies.remove(0))
        } else {
            None
        }
    }
}

impl Engine {
    fn get_best_move(&mut self) -> String {
        let mut m = match self.tt.probe(self.position.hash()) {
            Some(entry) => entry.move16(),
            None => 0,
        };
        if m == 0 {
            // Pick the first legal move.
            let mut first_legal_move = Move::invalid();
            let mut v = MoveStack::new();
            self.position.generate_moves(&mut v);
            while let Some(x) = v.pop() {
                if self.position.do_move(x) {
                    self.position.undo_move();
                    first_legal_move = x;
                    break;
                }
            }
            m = first_legal_move.digest();
        }
        if m != 0 {
            let move_type = move_type(m);
            let orig_square = orig_square(m);
            let dest_square = dest_square(m);
            let promoted_piece = match aux_data(m) {
                0 => "q",
                1 => "r",
                2 => "b",
                3 => "n",
                _ => panic!("invalid promoted piece code"),
            };
            format!("{}{}{}",
                    notation(orig_square),
                    notation(dest_square),
                    if move_type == MOVE_PROMOTION {
                        promoted_piece
                    } else {
                        ""
                    })

        } else {
            "0000".to_string()
        }
    }
}

/// Implements `UciEngineFactory` trait.
pub struct EngineFactory;

impl UciEngineFactory<Engine> for EngineFactory {
    fn name(&self) -> String {
        format!("Alcibiades {}", VERSION)
    }

    fn author(&self) -> String {
        "Evgeni Pandurski".to_string()
    }

    fn options(&self) -> Vec<(OptionName, OptionDescription)> {
        vec![
            // TODO: Calculate a sane limit for the hash size.
            ("Hash".to_string(), OptionDescription::Spin { min: 1, max: 2048, default: 16 }),
            ("Ponder".to_string(), OptionDescription::Check { default: false }),
        ]
    }

    fn create(&self, hash_size_mb: Option<usize>) -> Engine {
        Engine::new(hash_size_mb.unwrap_or(16))
    }
}

enum TimeManagement {
    MoveTime(u64),
    MoveTimeHint(u64),
    Nodes(NodeCount),
    Depth(u8),
    Infinite,
}
<commit_msg>bring multipv=1 back<commit_after>pub mod search;

use std::thread;
use std::cmp::min;
use std::sync::Arc;
use std::sync::mpsc::{channel, Sender, Receiver};
use std::time::SystemTime;
use basetypes::*;
use uci::{UciEngine, UciEngineFactory, EngineReply, OptionName, OptionDescription};
use position::Position;
use chess_move::*;
use tt::TranspositionTable;

// use rand;
// use rand::distributions::{Sample, Range};
// let mut rng = rand::thread_rng();
// let mut between = Range::new(0, 10);
// let x = between.sample(&mut rng);

const VERSION: &'static str = "0.1";
const MAX_DEPTH: u8 = 126;

/// Implements `UciEngine` trait.
pub struct Engine {
    position: Position,
    pondering_is_allowed: bool,
    is_thinking: bool,
    is_pondering: bool,
    replies: Vec<EngineReply>,
    tt: Arc<TranspositionTable>,
    commands: Sender<search::Command>,
    reports: Receiver<search::Report>,
    thinking_since: SystemTime,
    searched_nodes: NodeCount,
    searched_time: u64, // milliseconds
    searched_depth: u8,
    stop_when: TimeManagement,
}

impl Engine {
    /// Creates a new instance.
    ///
    /// `tt_size_mb` is the preferred size of the transposition
    /// table in Mbytes.
    pub fn new(tt_size_mb: usize) -> Engine {
        let mut tt = TranspositionTable::new();
        tt.resize(tt_size_mb);
        let tt = Arc::new(tt);
        let (commands_tx, commands_rx) = channel();
        let (reports_tx, reports_rx) = channel();
        let tt2 = tt.clone();
        thread::spawn(move || {
            search::run_deepening(tt2, commands_rx, reports_tx);
        });

        Engine {
            position: Position::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QKqk - 0 \
                                          1")
                          .ok()
                          .unwrap(),
            pondering_is_allowed: false,
            is_thinking: false,
            is_pondering: false,
            replies: vec![],
            tt: tt,
            commands: commands_tx,
            reports: reports_rx,
            thinking_since: SystemTime::now(),
            searched_nodes: 0,
            searched_time: 0,
            searched_depth: 0,
            stop_when: TimeManagement::Infinite,
        }
    }
}

impl UciEngine for Engine {
    fn set_option(&mut self, name: &str, value: &str) {
        match name {
            // Tells the engine that it will be allowed to ponder.
            // This option is needed because the engine might change
            // its time management algorithm when pondering is
            // allowed.
            "Ponder" => {
                self.pondering_is_allowed = value == "true";
            }

            // An invalid option. Notice that we do not support
            // re-sizing of the transposition table once the engine
            // had started.
            _ => (),
        }
    }

    fn new_game(&mut self) {
        // Clearing the transposition table would not change anything,
        // so we do nothing.
    }

    fn position(&mut self, fen: &str, moves: &mut Iterator<Item = &str>) {
        if !self.is_thinking {
            if let Ok(p) = Position::from_history(fen, moves) {
                self.position = p;
                self.tt.new_search();
            }
        }
    }

    #[allow(unused_variables)]
    fn go(&mut self,
          searchmoves: Option<Vec<String>>,
          ponder: bool,
          wtime: Option<u64>,
          btime: Option<u64>,
          winc: Option<u64>,
          binc: Option<u64>,
          movestogo: Option<u64>,
          depth: Option<u64>,
          nodes: Option<u64>,
          mate: Option<u64>,
          movetime: Option<u64>,
          infinite: bool) {
        if !self.is_thinking {
            // Note: We ignore the "searchmoves" parameter.

            self.is_thinking = true;
            self.is_pondering = ponder;
            self.thinking_since = SystemTime::now();
            self.searched_nodes = 0;
            self.searched_time = 0;
            self.searched_depth = 0;

            // Figure out when we should stop thinking.
            let (time, inc) = if self.position.board().to_move() == WHITE {
                (wtime, winc.unwrap_or(0))
            } else {
                (btime, binc.unwrap_or(0))
            };
            self.stop_when = if infinite {
                TimeManagement::Infinite
            } else if movetime.is_some() {
                TimeManagement::MoveTime(movetime.unwrap())
            } else if nodes.is_some() {
                TimeManagement::Nodes(nodes.unwrap())
            } else if depth.is_some() {
                TimeManagement::Depth(min(depth.unwrap(), MAX_DEPTH as u64) as u8)
            } else {
                // TODO: Robert Hyatt proposes this:
                //
                // nMoves =  min( numberOfMovesOutOfBook, 10 )
                // factor = 2 - nMoves / 10
                // target = timeLeft / numberOfMovesUntilNextTimeControl
                // time   = factor * target
                let time = time.unwrap_or(0);
                let movestogo = movestogo.unwrap_or(40);
                let movetime = (time + inc * movestogo) / movestogo;
                TimeManagement::MoveTimeHint(min(movetime, time / 2))
            };

            // Start a search with the maximum possible depth.
            self.commands
                .send(search::Command::Search {
                    search_id: 0,
                    position: self.position.clone(),
                    depth: MAX_DEPTH,
                    lower_bound: -20000,
                    upper_bound: 20000,
                })
                .unwrap();
        }
    }

    fn ponder_hit(&mut self) {
        if self.is_thinking && self.is_pondering {
            self.is_pondering = false;
        }
    }

    fn stop(&mut self) {
        if self.is_thinking {
            self.commands.send(search::Command::Stop).unwrap();
            let best_move = self.get_best_move();
            self.replies.push(EngineReply::BestMove {
                best_move: best_move,
                ponder_move: None,
            });
            self.is_thinking = false;
        }
    }

    fn is_thinking(&self) -> bool {
        self.is_thinking
    }

    fn get_reply(&mut self) -> Option<EngineReply> {
        // TODO: implement panic mode for MoveTimeHint management.
        if self.is_thinking && !self.is_pondering {
            if match self.stop_when {
                TimeManagement::MoveTime(t) => self.searched_time >= t,
                TimeManagement::MoveTimeHint(t) => self.searched_time >= t,
                TimeManagement::Nodes(n) => self.searched_nodes >= n,
                TimeManagement::Depth(d) => self.searched_depth >= d,
                TimeManagement::Infinite => false,
            } {
                self.stop();
            }
        }

        // Empty the reports queue.
        while let Ok(report) = self.reports.try_recv() {
            // let rep_type = match report {
            //     search::Report::Progress { searched_nodes, .. } => format!("progress {}", searched_nodes),
            //     search::Report::Done { .. } => "done".to_string(),
            // };
            // self.replies.push(EngineReply::Info(vec![
            //     ("string".to_string(), rep_type),
            // ]));
            if self.is_thinking {
                match report {
                    search::Report::Progress { searched_nodes, depth, .. } => {
                        let thinking_duration = self.thinking_since.elapsed().unwrap();
                        self.searched_nodes = searched_nodes;
                        self.searched_time = 1000 * thinking_duration.as_secs() +
                                             (thinking_duration.subsec_nanos() / 1000000) as u64;
                        if self.searched_depth < depth {
                            self.searched_depth = depth;
                            let mut p = self.position.clone();
                            let mut v = MoveStack::new();
                            let mut pv = String::from("");
                            let mut pv_length = 0;
                            let mut value = None;
                            'depthloop: for _ in 0..depth {
                                let m = match self.tt.probe(p.hash()) {
                                    Some(entry) => {
                                        if value.is_none() {
                                            value = Some(entry.value());
                                        }
                                        entry.move16()
                                    }
                                    None => 0,
                                };
                                if m != 0 {
                                    p.generate_moves(&mut v);
                                    while let Some(x) = v.pop() {
                                        if x.digest() == m && p.do_move(x) {
                                            pv_length += 1;
                                            pv.push_str(&x.notation());
                                            pv.push(' ');
                                            v.clear();
                                            continue 'depthloop;
                                        }
                                    }
                                }
                                break;
                            }
                            if searched_nodes > 0 && pv_length >= depth {
                                self.replies.push(EngineReply::Info(vec![
                                    ("depth".to_string(), format!("{}", depth)),
                                    ("score".to_string(), format!("cp {}", value.unwrap_or(666))),
                                    ("nodes".to_string(), format!("{}", searched_nodes)),
                                    ("time".to_string(), format!("{}", self.searched_time)),
                                    ("multipv".to_string(), "1".to_string()),
                                    ("pv".to_string(), format!("{}", pv)),
                                ]));
                            }
                        }
                    }
                    _ => (),
                }
            }
        }

        if self.replies.len() > 0 {
            Some(self.replies.remove(0))
        } else {
            None
        }
    }
}

impl Engine {
    fn get_best_move(&mut self) -> String {
        let mut m = match self.tt.probe(self.position.hash()) {
            Some(entry) => entry.move16(),
            None => 0,
        };
        if m == 0 {
            // Pick the first legal move.
            let mut first_legal_move = Move::invalid();
            let mut v = MoveStack::new();
            self.position.generate_moves(&mut v);
            while let Some(x) = v.pop() {
                if self.position.do_move(x) {
                    self.position.undo_move();
                    first_legal_move = x;
                    break;
                }
            }
            m = first_legal_move.digest();
        }
        if m != 0 {
            let move_type = move_type(m);
            let orig_square = orig_square(m);
            let dest_square = dest_square(m);
            let promoted_piece = match aux_data(m) {
                0 => "q",
                1 => "r",
                2 => "b",
                3 => "n",
                _ => panic!("invalid promoted piece code"),
            };
            format!("{}{}{}",
                    notation(orig_square),
                    notation(dest_square),
                    if move_type == MOVE_PROMOTION {
                        promoted_piece
                    } else {
                        ""
                    })

        } else {
            "0000".to_string()
        }
    }
}

/// Implements `UciEngineFactory` trait.
pub struct EngineFactory;

impl UciEngineFactory<Engine> for EngineFactory {
    fn name(&self) -> String {
        format!("Alcibiades {}", VERSION)
    }

    fn author(&self) -> String {
        "Evgeni Pandurski".to_string()
    }

    fn options(&self) -> Vec<(OptionName, OptionDescription)> {
        vec![
            // TODO: Calculate a sane limit for the hash size.
            ("Hash".to_string(), OptionDescription::Spin { min: 1, max: 2048, default: 16 }),
            ("Ponder".to_string(), OptionDescription::Check { default: false }),
        ]
    }

    fn create(&self, hash_size_mb: Option<usize>) -> Engine {
        Engine::new(hash_size_mb.unwrap_or(16))
    }
}

enum TimeManagement {
    MoveTime(u64),
    MoveTimeHint(u64),
    Nodes(NodeCount),
    Depth(u8),
    Infinite,
}
<|endoftext|>


<commit_before>#![macro_escape]

macro_rules! fail {
	($expr:expr) => (
		return Err(::std::error::FromError::from_error($expr));
	)
}
<commit_msg>macro_escape -> macro_use fallout<commit_after>#![macro_use]

macro_rules! fail {
	($expr:expr) => (
		return Err(::std::error::FromError::from_error($expr));
	)
}
<|endoftext|>


<commit_before>use std::collections::{BTreeMap, BTreeSet};
use std::io;
use std::error::FromError;

use rustc_serialize::json;
use rustc_serialize::json::{Json, ToJson};

#[derive(Clone, Debug)]
pub enum ChatJsonError {
    MalformedJson(json::ParserError),
    IoError(io::Error),
    InvalidObject,
    InvalidField,
    UnknownField,
    UnknownColor,
    InvalidClickEvent,
    InvalidHoverEvent
}

impl FromError<io::Error> for ChatJsonError {
    fn from_error(err: io::Error) -> ChatJsonError {
        ChatJsonError::IoError(err)
    }
}

impl FromError<json::ParserError> for ChatJsonError {
    fn from_error(err: json::ParserError) -> ChatJsonError {
        if let json::ParserError::IoError(e) = err {
            ChatJsonError::IoError(e)
        } else {
            ChatJsonError::MalformedJson(err)
        }
    }
}

#[derive(Clone, Debug)]
pub struct ChatJson {
    pub msg: Message,
    pub extra: Option<Vec<Json>>,
    pub color: Option<Color>,
    pub formats: BTreeSet<Format>,
    pub click_event: Option<ClickEvent>,
    pub hover_event: Option<HoverEvent>,
    pub insertion: Option<String>
}

impl ChatJson {
    pub fn msg(msg: String) -> ChatJson {
        ChatJson {
            msg: Message::PlainText(msg), extra: None, color: None, formats: BTreeSet::new(),
            click_event: None, hover_event: None, insertion: None
        }
    }

    pub fn from_json<T: Iterator<Item=char>>(src: T) -> Result<ChatJson, ChatJsonError> {
        let json = try!(json::Builder::new(src).build());
        if let Json::Object(map) = json {
            let mut result = ChatJson::msg("".to_string());
            for (key, value) in map {
                println!("{:?}: {:?}", key, value);
                match key.as_slice() {
                    "text" => {
                        if let Json::String(string) = value {
                            result.msg = Message::PlainText(string);
                        } else {
                            return Err(ChatJsonError::InvalidField);
                        }
                    },
                    "insertion" => {
                        if let Json::String(string) = value {
                            result.insertion = Some(string);
                        } else {
                            return Err(ChatJsonError::InvalidField);
                        }
                    },
                    "color" => {
                        if let Json::String(string) = value {
                            result.color = match Color::from_string(&string) {
                                None => return Err(ChatJsonError::UnknownColor),
                                c => c
                            };
                        } else {
                            return Err(ChatJsonError::InvalidField);
                        }
                    },
                    // Handle all of the different format strings.
                    "bold"|"italic"|"underlined"|"strikethrough"|"obfuscated"|"reset"|"random" => {
                        if let Json::Boolean(b) = value {
                            if b == true {
                                result.formats.insert(Format::from_string(&key).unwrap());
                            }
                        } else {
                            return Err(ChatJsonError::InvalidField);
                        }
                    },
                    // Handle the JSON format of click events.
                    "clickEvent" => {
                        if let Json::Object(event) = value {
                            // Get the `value` first.
                            let val: String = match event.get("value") {
                                Some(&Json::String(ref string)) => string.clone(),
                                _ => return Err(ChatJsonError::InvalidClickEvent)
                            };
                            // Handle the different click events.
                            if let Some(&Json::String(ref string)) = event.get("action") {
                                result.click_event = match string.as_slice() {
                                    "open_url" => Some(ClickEvent::OpenUrl(val)),
                                    "open_file" => Some(ClickEvent::OpenFile(val)),
                                    "run_command" => Some(ClickEvent::RunCommand(val)),
                                    "suggest_command" => Some(ClickEvent::SuggestCommand(val)),
                                    _ => return Err(ChatJsonError::InvalidClickEvent)
                                };
                            } else {
                                return Err(ChatJsonError::InvalidClickEvent);
                            }
                        }
                    },
                    // Handle the JSON format of hover events.
                    "hoverEvent" => {
                        if let Json::Object(event) = value {
                            // Get the `value` first.
                            let val: String = match event.get("value") {
                                Some(&Json::String(ref string)) => string.clone(),
                                _ => return Err(ChatJsonError::InvalidHoverEvent)
                            };
                            // Handle the different click events.
                            if let Some(&Json::String(ref string)) = event.get("action") {
                                result.hover_event = match string.as_slice() {
                                    "show_text" => Some(HoverEvent::Text(val)),
                                    "show_achievement" => Some(HoverEvent::Achievement(val)),
                                    "show_item" => Some(HoverEvent::Item(val)),
                                    _ => return Err(ChatJsonError::InvalidHoverEvent)
                                };
                            } else {
                                return Err(ChatJsonError::InvalidHoverEvent);
                            }
                        }
                    },
                    "extra" => {
                        if let Json::Array(extra) = value {
                            result.extra = Some(extra);
                        } else {
                            return Err(ChatJsonError::InvalidField);
                        }
                    },
                    // TODO: Error on unknown key when the implementation is complete.
                    _ => (),
                };
            }
            Ok(result)
        } else {
            Err(ChatJsonError::InvalidObject)
        }
    }
}

impl ToJson for ChatJson {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();

        match self.msg {
            Message::PlainText(ref text) => {
                d.insert("text".to_string(), text.to_json());
            },
            _ => unimplemented!()
        };

        if let Some(ref extra) = self.extra {
            d.insert("extra".to_string(), extra.to_json());
        }
        if let Some(ref color) = self.color {
            d.insert("color".to_string(), color.to_string().to_json());
        }
        if let Some(ref event) = self.click_event {
            d.insert("clickEvent".to_string(), event.to_json());
        }
        if let Some(ref event) = self.hover_event {
            d.insert("hoverEvent".to_string(), event.to_json());
        }
        if let Some(ref ins) = self.insertion {
            d.insert("insertion".to_string(), ins.to_json());
        }

        Json::Object(d)
    }
}

#[derive(Clone, Debug)]
pub enum Message {
    PlainText(String),
    Score(String, String),
    Translatable,
    Selector
}

#[derive(Clone, Debug)]
pub enum ClickEvent {
    OpenUrl(String),
    OpenFile(String),
    RunCommand(String),
    SuggestCommand(String)
}

impl ToJson for ClickEvent {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        match self {
            &ClickEvent::OpenUrl(ref url) => {
                d.insert("action".to_string(), "open_url".to_json());
                d.insert("value".to_string(), url.to_json());
            },
            &ClickEvent::OpenFile(ref file) => {
                d.insert("action".to_string(), "open_file".to_json());
                d.insert("value".to_string(), file.to_json());
            },
            &ClickEvent::RunCommand(ref cmd) => {
                d.insert("action".to_string(), "run_command".to_json());
                d.insert("value".to_string(), cmd.to_json());
            },
            &ClickEvent::SuggestCommand(ref cmd) => {
                d.insert("action".to_string(), "suggest_command".to_json());
                d.insert("value".to_string(), cmd.to_json());
            }
        }
        Json::Object(d)
    }
}

#[derive(Clone, Debug)]
pub enum HoverEvent {
    Text(String),
    Achievement(String),
    Item(String)
}

impl ToJson for HoverEvent {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        match self {
            &HoverEvent::Text(ref text) => {
                d.insert("action".to_string(), "show_text".to_json());
                d.insert("value".to_string(), text.to_json());
            },
            &HoverEvent::Achievement(ref ach) => {
                d.insert("action".to_string(), "show_achievement".to_json());
                d.insert("value".to_string(), ach.to_json());
            },
            &HoverEvent::Item(ref item) => {
                d.insert("action".to_string(), "show_item".to_json());
                // The string is actually a JSON object, just in the form of a
                // string.
                d.insert("value".to_string(), item.to_json());
            }
        }
        Json::Object(d)
    }
}

#[derive(Copy, Debug, Clone)]
pub enum Color {
    Black       = 0x0,
    DarkBlue    = 0x1,
    DarkGreen   = 0x2,
    DarkCyan    = 0x3,
    DarkRed     = 0x4,
    Purple      = 0x5,
    Gold        = 0x6,
    Gray        = 0x7,
    DarkGray    = 0x8,
    Blue        = 0x9,
    BrightGreen = 0xa,
    Cyan        = 0xb,
    Red         = 0xc,
    Pink        = 0xd,
    Yellow      = 0xe,
    White       = 0xf
}

impl Color {
    pub fn to_string(&self) -> String {
        match self {
            &Color::Black => "black".to_string(),
            &Color::DarkBlue => "dark_blue".to_string(),
            &Color::DarkGreen => "dark_green".to_string(),
            &Color::DarkCyan => "dark_aqua".to_string(),
            &Color::DarkRed => "dark_red".to_string(),
            &Color::Purple => "dark_purple".to_string(),
            &Color::Gold => "gold".to_string(),
            &Color::Gray => "gray".to_string(),
            &Color::DarkGray => "dark_gray".to_string(),
            &Color::Blue => "blue".to_string(),
            &Color::BrightGreen => "green".to_string(),
            &Color::Cyan => "aqua".to_string(),
            &Color::Red => "red".to_string(),
            &Color::Pink => "light_purple".to_string(),
            &Color::Yellow => "yellow".to_string(),
            &Color::White => "white".to_string()
        }
    }

    pub fn from_string(string: &String) -> Option<Color> {
        match string.as_slice() {
            "black"        => Some(Color::Black),
            "dark_blue"    => Some(Color::DarkBlue),
            "dark_green"   => Some(Color::DarkGreen),
            "dark_aqua"    => Some(Color::DarkCyan),
            "dark_red"     => Some(Color::DarkRed),
            "dark_purple"  => Some(Color::Purple),
            "gold"         => Some(Color::Gold),
            "gray"         => Some(Color::Gray),
            "dark_gray"    => Some(Color::DarkGray),
            "blue"         => Some(Color::Blue),
            "green"        => Some(Color::BrightGreen),
            "aqua"         => Some(Color::Cyan),
            "red"          => Some(Color::Red),
            "light_purple" => Some(Color::Pink),
            "yellow"       => Some(Color::Yellow),
            "white"        => Some(Color::White),
            _              => None
        }
    }
}

#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd)]
pub enum Format {
    Bold, Underlined, Strikethrough, Italic, Obfuscated, Random, Reset
}

impl Format {
    pub fn from_string(string: &String) -> Option<Format> {
        match string.as_slice() {
            "bold"          => Some(Format::Bold),
            "italic"        => Some(Format::Italic),
            "underlined"    => Some(Format::Underlined),
            "strikethrough" => Some(Format::Strikethrough),
            "obfuscated"    => Some(Format::Obfuscated),
            "random"        => Some(Format::Random),
            "reset"         => Some(Format::Reset),
            _               => None
        }
    }
}

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

    #[test]
    fn chat_plain() {
        let msg = ChatJson::msg("Hello, world!".to_string());
        println!("{}", msg.to_json());
    }

    #[test]
    fn chat_extra() {
        let blob = r#"{
            "text": "Hello world",
            "extra": [
                "Testing",
                {"translate":"demo.day.2"}
            ],
            "bold":true,
            "italic":false,
            "underlined": false,
            "strikethrough": true,
            "obfuscated": false,
            "color":"red",
            "clickEvent":{
                "action":"run_command",
                "value": "/time set day"
            },
            "hoverEvent": {
                "action":"show_text",
                "value": "Hello"
            },
            "insertion": "Hello world"
        }"#;

        let parsed = ChatJson::from_json(blob.chars());
        println!("{:?}", parsed);
    }
}
<commit_msg>chat: Improves the simple tests.<commit_after>use std::collections::{BTreeMap, BTreeSet};
use std::io;
use std::error::FromError;

use rustc_serialize::json;
use rustc_serialize::json::{Json, ToJson};

#[derive(Clone, Debug, PartialEq)]
pub enum ChatJsonError {
    MalformedJson(json::ParserError),
    IoError(io::Error),
    InvalidObject,
    InvalidField,
    UnknownField,
    UnknownColor,
    InvalidClickEvent,
    InvalidHoverEvent
}

impl FromError<io::Error> for ChatJsonError {
    fn from_error(err: io::Error) -> ChatJsonError {
        ChatJsonError::IoError(err)
    }
}

impl FromError<json::ParserError> for ChatJsonError {
    fn from_error(err: json::ParserError) -> ChatJsonError {
        if let json::ParserError::IoError(e) = err {
            ChatJsonError::IoError(e)
        } else {
            ChatJsonError::MalformedJson(err)
        }
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct ChatJson {
    pub msg: Message,
    pub extra: Option<Vec<Json>>,
    pub color: Option<Color>,
    pub formats: BTreeSet<Format>,
    pub click_event: Option<ClickEvent>,
    pub hover_event: Option<HoverEvent>,
    pub insertion: Option<String>
}

impl ChatJson {
    pub fn msg(msg: String) -> ChatJson {
        ChatJson {
            msg: Message::PlainText(msg), extra: None, color: None, formats: BTreeSet::new(),
            click_event: None, hover_event: None, insertion: None
        }
    }

    pub fn from_json<T: Iterator<Item=char>>(src: T) -> Result<ChatJson, ChatJsonError> {
        let json = try!(json::Builder::new(src).build());
        if let Json::Object(map) = json {
            let mut result = ChatJson::msg("".to_string());
            for (key, value) in map {
                println!("{:?}: {:?}", key, value);
                match key.as_slice() {
                    "text" => {
                        if let Json::String(string) = value {
                            result.msg = Message::PlainText(string);
                        } else {
                            return Err(ChatJsonError::InvalidField);
                        }
                    },
                    "insertion" => {
                        if let Json::String(string) = value {
                            result.insertion = Some(string);
                        } else {
                            return Err(ChatJsonError::InvalidField);
                        }
                    },
                    "color" => {
                        if let Json::String(string) = value {
                            result.color = match Color::from_string(&string) {
                                None => return Err(ChatJsonError::UnknownColor),
                                c => c
                            };
                        } else {
                            return Err(ChatJsonError::InvalidField);
                        }
                    },
                    // Handle all of the different format strings.
                    "bold"|"italic"|"underlined"|"strikethrough"|"obfuscated"|"reset"|"random" => {
                        if let Json::Boolean(b) = value {
                            if b == true {
                                result.formats.insert(Format::from_string(&key).unwrap());
                            }
                        } else {
                            return Err(ChatJsonError::InvalidField);
                        }
                    },
                    // Handle the JSON format of click events.
                    "clickEvent" => {
                        if let Json::Object(event) = value {
                            // Get the `value` first.
                            let val: String = match event.get("value") {
                                Some(&Json::String(ref string)) => string.clone(),
                                _ => return Err(ChatJsonError::InvalidClickEvent)
                            };
                            // Handle the different click events.
                            if let Some(&Json::String(ref string)) = event.get("action") {
                                result.click_event = match string.as_slice() {
                                    "open_url" => Some(ClickEvent::OpenUrl(val)),
                                    "open_file" => Some(ClickEvent::OpenFile(val)),
                                    "run_command" => Some(ClickEvent::RunCommand(val)),
                                    "suggest_command" => Some(ClickEvent::SuggestCommand(val)),
                                    _ => return Err(ChatJsonError::InvalidClickEvent)
                                };
                            } else {
                                return Err(ChatJsonError::InvalidClickEvent);
                            }
                        }
                    },
                    // Handle the JSON format of hover events.
                    "hoverEvent" => {
                        if let Json::Object(event) = value {
                            // Get the `value` first.
                            let val: String = match event.get("value") {
                                Some(&Json::String(ref string)) => string.clone(),
                                _ => return Err(ChatJsonError::InvalidHoverEvent)
                            };
                            // Handle the different click events.
                            if let Some(&Json::String(ref string)) = event.get("action") {
                                result.hover_event = match string.as_slice() {
                                    "show_text" => Some(HoverEvent::Text(val)),
                                    "show_achievement" => Some(HoverEvent::Achievement(val)),
                                    "show_item" => Some(HoverEvent::Item(val)),
                                    _ => return Err(ChatJsonError::InvalidHoverEvent)
                                };
                            } else {
                                return Err(ChatJsonError::InvalidHoverEvent);
                            }
                        }
                    },
                    "extra" => {
                        if let Json::Array(extra) = value {
                            result.extra = Some(extra);
                        } else {
                            return Err(ChatJsonError::InvalidField);
                        }
                    },
                    // TODO: Error on unknown key when the implementation is complete.
                    _ => (),
                };
            }
            Ok(result)
        } else {
            Err(ChatJsonError::InvalidObject)
        }
    }
}

impl ToJson for ChatJson {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();

        match self.msg {
            Message::PlainText(ref text) => {
                d.insert("text".to_string(), text.to_json());
            },
            _ => unimplemented!()
        };

        if let Some(ref extra) = self.extra {
            d.insert("extra".to_string(), extra.to_json());
        }
        if let Some(ref color) = self.color {
            d.insert("color".to_string(), color.to_string().to_json());
        }
        if let Some(ref event) = self.click_event {
            d.insert("clickEvent".to_string(), event.to_json());
        }
        if let Some(ref event) = self.hover_event {
            d.insert("hoverEvent".to_string(), event.to_json());
        }
        if let Some(ref ins) = self.insertion {
            d.insert("insertion".to_string(), ins.to_json());
        }

        Json::Object(d)
    }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Message {
    PlainText(String),
    Score(String, String),
    Translatable,
    Selector
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ClickEvent {
    OpenUrl(String),
    OpenFile(String),
    RunCommand(String),
    SuggestCommand(String)
}

impl ToJson for ClickEvent {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        match self {
            &ClickEvent::OpenUrl(ref url) => {
                d.insert("action".to_string(), "open_url".to_json());
                d.insert("value".to_string(), url.to_json());
            },
            &ClickEvent::OpenFile(ref file) => {
                d.insert("action".to_string(), "open_file".to_json());
                d.insert("value".to_string(), file.to_json());
            },
            &ClickEvent::RunCommand(ref cmd) => {
                d.insert("action".to_string(), "run_command".to_json());
                d.insert("value".to_string(), cmd.to_json());
            },
            &ClickEvent::SuggestCommand(ref cmd) => {
                d.insert("action".to_string(), "suggest_command".to_json());
                d.insert("value".to_string(), cmd.to_json());
            }
        }
        Json::Object(d)
    }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum HoverEvent {
    Text(String),
    Achievement(String),
    Item(String)
}

impl ToJson for HoverEvent {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        match self {
            &HoverEvent::Text(ref text) => {
                d.insert("action".to_string(), "show_text".to_json());
                d.insert("value".to_string(), text.to_json());
            },
            &HoverEvent::Achievement(ref ach) => {
                d.insert("action".to_string(), "show_achievement".to_json());
                d.insert("value".to_string(), ach.to_json());
            },
            &HoverEvent::Item(ref item) => {
                d.insert("action".to_string(), "show_item".to_json());
                // The string is actually a JSON object, just in the form of a
                // string.
                d.insert("value".to_string(), item.to_json());
            }
        }
        Json::Object(d)
    }
}

#[derive(Copy, Debug, Clone, Eq, PartialEq)]
pub enum Color {
    Black       = 0x0,
    DarkBlue    = 0x1,
    DarkGreen   = 0x2,
    DarkCyan    = 0x3,
    DarkRed     = 0x4,
    Purple      = 0x5,
    Gold        = 0x6,
    Gray        = 0x7,
    DarkGray    = 0x8,
    Blue        = 0x9,
    BrightGreen = 0xa,
    Cyan        = 0xb,
    Red         = 0xc,
    Pink        = 0xd,
    Yellow      = 0xe,
    White       = 0xf
}

impl Color {
    pub fn to_string(&self) -> String {
        match self {
            &Color::Black => "black".to_string(),
            &Color::DarkBlue => "dark_blue".to_string(),
            &Color::DarkGreen => "dark_green".to_string(),
            &Color::DarkCyan => "dark_aqua".to_string(),
            &Color::DarkRed => "dark_red".to_string(),
            &Color::Purple => "dark_purple".to_string(),
            &Color::Gold => "gold".to_string(),
            &Color::Gray => "gray".to_string(),
            &Color::DarkGray => "dark_gray".to_string(),
            &Color::Blue => "blue".to_string(),
            &Color::BrightGreen => "green".to_string(),
            &Color::Cyan => "aqua".to_string(),
            &Color::Red => "red".to_string(),
            &Color::Pink => "light_purple".to_string(),
            &Color::Yellow => "yellow".to_string(),
            &Color::White => "white".to_string()
        }
    }

    pub fn from_string(string: &String) -> Option<Color> {
        match string.as_slice() {
            "black"        => Some(Color::Black),
            "dark_blue"    => Some(Color::DarkBlue),
            "dark_green"   => Some(Color::DarkGreen),
            "dark_aqua"    => Some(Color::DarkCyan),
            "dark_red"     => Some(Color::DarkRed),
            "dark_purple"  => Some(Color::Purple),
            "gold"         => Some(Color::Gold),
            "gray"         => Some(Color::Gray),
            "dark_gray"    => Some(Color::DarkGray),
            "blue"         => Some(Color::Blue),
            "green"        => Some(Color::BrightGreen),
            "aqua"         => Some(Color::Cyan),
            "red"          => Some(Color::Red),
            "light_purple" => Some(Color::Pink),
            "yellow"       => Some(Color::Yellow),
            "white"        => Some(Color::White),
            _              => None
        }
    }
}

#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd)]
pub enum Format {
    Bold, Underlined, Strikethrough, Italic, Obfuscated, Random, Reset
}

impl Format {
    pub fn from_string(string: &String) -> Option<Format> {
        match string.as_slice() {
            "bold"          => Some(Format::Bold),
            "italic"        => Some(Format::Italic),
            "underlined"    => Some(Format::Underlined),
            "strikethrough" => Some(Format::Strikethrough),
            "obfuscated"    => Some(Format::Obfuscated),
            "random"        => Some(Format::Random),
            "reset"         => Some(Format::Reset),
            _               => None
        }
    }
}

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

    #[test]
    fn chat_plain() {
        let msg = ChatJson::msg("Hello, world!".to_string());
        let blob = r#"{
            "text": "Hello, world!"
        }"#;
        let parsed = ChatJson::from_json(blob.chars()).unwrap();
        assert_eq!(&msg, &parsed);
    }

    #[test]
    fn chat_invalid_field() {
        let blob = r#"{
            "text": true
        }"#;
        let parsed = ChatJson::from_json(blob.chars());
        assert_eq!(&parsed, &Err(ChatJsonError::InvalidField));
    }

    #[test]
    fn chat_extra() {
        let blob = r#"{
            "text": "Hello world",
            "extra": [
                "Testing",
                {"translate":"demo.day.2"}
            ],
            "bold":true,
            "italic":false,
            "underlined": false,
            "strikethrough": true,
            "obfuscated": false,
            "color":"red",
            "clickEvent":{
                "action":"run_command",
                "value": "/time set day"
            },
            "hoverEvent": {
                "action":"show_text",
                "value": "Hello"
            },
            "insertion": "Hello world"
        }"#;

        let parsed = ChatJson::from_json(blob.chars());
        println!("{:?}", parsed);
    }
}
<|endoftext|>


<commit_before>// 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/.

// We won't use the usual `main` function. We are going to use a different "entry point".
#![no_main]

// We won't use the standard library because it requires OS abstractions like threads and files and
// those are not available in this platform.
#![no_std]
#![feature(alloc)]

#[macro_use]
extern crate cc3200;
extern crate alloc;
extern crate freertos_rs;
extern crate freertos_alloc;

use cc3200::cc3200::{Board, Console, Utils, LedEnum, LedName};

use alloc::arc::Arc;
use freertos_rs::{CurrentTask, Duration, Task, Queue};

static VERSION: &'static str = "1.0";

// Conceptually, this is our program "entry point". It's the first thing the microcontroller will
// execute when it (re)boots. (As far as the linker is concerned the entry point must be named
// `start` (by default; it can have a different name). That's why this function is `pub`lic, named
// `start` and is marked as `#[no_mangle]`.)
//
// Returning from this function is undefined because there is nothing to return to! To statically
// forbid returning from this function, we mark it as divergent, hence the `fn() -> !` signature.
#[no_mangle]
pub fn start() -> ! {

    Board::init();

    Console::init_term();
    Console::clear_term();
    println!("Welcome to CC3200 blinking leds version {}", VERSION);

    assert_eq!(1, 2);

    let queue = Arc::new(Queue::new(10).unwrap());
    let _producer = {
        let queue = queue.clone();
        Task::new()
            .name("producer")
            .start(move || {
                let msgs = ["Welcome ", "to ", "CC32xx ", "development !\n"];
                loop {
                    for msg in msgs.iter() {
                        queue.send(msg, Duration::ms(15)).unwrap();
                        CurrentTask::delay(Duration::ms(15))
                    }
                    CurrentTask::delay(Duration::ms(1000))
                }
            })
            .unwrap()
    };

    let _consumer = {
        let queue = queue.clone();
        Task::new()
            .name("consumer")
            .start(move || {
                loop {
                    let msg = queue.receive(Duration::ms(2000)).unwrap();
                    Console::message("Received: ");
                    Console::message(msg);
                    Console::message("\n");
                }
            })
            .unwrap()
    };

    let _blinky = {
        Task::new()
            .name("blinky")
            .start(|| {
                Board::led_configure(&[LedEnum::LED1, LedEnum::LED2, LedEnum::LED3]);
                Board::led_off(LedName::MCU_ALL_LED_IND);
                let mut counter = 0;
                loop {
                    Board::led_on(LedName::MCU_RED_LED_GPIO);
                    if counter & 1 != 0 {
                        Board::led_on(LedName::MCU_ORANGE_LED_GPIO);
                    } else {
                        Board::led_off(LedName::MCU_ORANGE_LED_GPIO);
                    }
                    if counter & 2 != 0 {
                        Board::led_on(LedName::MCU_GREEN_LED_GPIO);
                    } else {
                        Board::led_off(LedName::MCU_GREEN_LED_GPIO);
                    }
                    Utils::delay(1333333);
                    Board::led_off(LedName::MCU_RED_LED_GPIO);
                    Utils::delay(1333333);
                    Board::led_on(LedName::MCU_RED_LED_GPIO);
                    Utils::delay(1333333);
                    Board::led_off(LedName::MCU_RED_LED_GPIO);
                    Utils::delay(1333333 * 6);

                    counter += 1;
                }
            })
            .unwrap()
    };

    Board::start_scheduler();

    // The only reason start_scheduler should fail is if there wasn't enough
    // heap to initialize the IDLE and timer tasks

    loop {}
}
<commit_msg>Remove the test assertion...<commit_after>// 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/.

// We won't use the usual `main` function. We are going to use a different "entry point".
#![no_main]

// We won't use the standard library because it requires OS abstractions like threads and files and
// those are not available in this platform.
#![no_std]
#![feature(alloc)]

#[macro_use]
extern crate cc3200;
extern crate alloc;
extern crate freertos_rs;
extern crate freertos_alloc;

use cc3200::cc3200::{Board, Console, Utils, LedEnum, LedName};

use alloc::arc::Arc;
use freertos_rs::{CurrentTask, Duration, Task, Queue};

static VERSION: &'static str = "1.0";

// Conceptually, this is our program "entry point". It's the first thing the microcontroller will
// execute when it (re)boots. (As far as the linker is concerned the entry point must be named
// `start` (by default; it can have a different name). That's why this function is `pub`lic, named
// `start` and is marked as `#[no_mangle]`.)
//
// Returning from this function is undefined because there is nothing to return to! To statically
// forbid returning from this function, we mark it as divergent, hence the `fn() -> !` signature.
#[no_mangle]
pub fn start() -> ! {

    Board::init();

    Console::init_term();
    Console::clear_term();
    println!("Welcome to CC3200 blinking leds version {}", VERSION);

    let queue = Arc::new(Queue::new(10).unwrap());
    let _producer = {
        let queue = queue.clone();
        Task::new()
            .name("producer")
            .start(move || {
                let msgs = ["Welcome ", "to ", "CC32xx ", "development !\n"];
                loop {
                    for msg in msgs.iter() {
                        queue.send(msg, Duration::ms(15)).unwrap();
                        CurrentTask::delay(Duration::ms(15))
                    }
                    CurrentTask::delay(Duration::ms(1000))
                }
            })
            .unwrap()
    };

    let _consumer = {
        let queue = queue.clone();
        Task::new()
            .name("consumer")
            .start(move || {
                loop {
                    let msg = queue.receive(Duration::ms(2000)).unwrap();
                    Console::message("Received: ");
                    Console::message(msg);
                    Console::message("\n");
                }
            })
            .unwrap()
    };

    let _blinky = {
        Task::new()
            .name("blinky")
            .start(|| {
                Board::led_configure(&[LedEnum::LED1, LedEnum::LED2, LedEnum::LED3]);
                Board::led_off(LedName::MCU_ALL_LED_IND);
                let mut counter = 0;
                loop {
                    Board::led_on(LedName::MCU_RED_LED_GPIO);
                    if counter & 1 != 0 {
                        Board::led_on(LedName::MCU_ORANGE_LED_GPIO);
                    } else {
                        Board::led_off(LedName::MCU_ORANGE_LED_GPIO);
                    }
                    if counter & 2 != 0 {
                        Board::led_on(LedName::MCU_GREEN_LED_GPIO);
                    } else {
                        Board::led_off(LedName::MCU_GREEN_LED_GPIO);
                    }
                    Utils::delay(1333333);
                    Board::led_off(LedName::MCU_RED_LED_GPIO);
                    Utils::delay(1333333);
                    Board::led_on(LedName::MCU_RED_LED_GPIO);
                    Utils::delay(1333333);
                    Board::led_off(LedName::MCU_RED_LED_GPIO);
                    Utils::delay(1333333 * 6);

                    counter += 1;
                }
            })
            .unwrap()
    };

    Board::start_scheduler();

    // The only reason start_scheduler should fail is if there wasn't enough
    // heap to initialize the IDLE and timer tasks

    loop {}
}
<|endoftext|>