<commit_before>use std::error::Error;
use std::fmt::{Debug, Display, Formatter};
use std::fmt;

#[derive(Debug)]
pub enum FileHeaderSpec {
    Null,
    Bool,
    Integer,
    UInteger,
    Float,
    Text,
    Key { name: String, value_type: Box<FileHeaderSpec> },
    Array { allowed_types: Box<Vec<FileHeaderSpec>> },
}

#[derive(Debug)]
pub enum FileHeaderData {
    Null,
    Bool(bool),
    Integer(i64),
    UInteger(u64),
    Float(f64),
    Text(String),
    Key { name: String, value: Box<FileHeaderData> },
    Array { values: Box<Vec<FileHeaderData>> },
}

pub trait FileData : Sized {
    fn get_fulltext(&self) -> String;
    fn get_abbrev(&self) -> String;
}

impl Display for FileHeaderSpec {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        match self {
            &FileHeaderSpec::Null       => write!(fmt, "NULL"),
            &FileHeaderSpec::Bool       => write!(fmt, "Bool"),
            &FileHeaderSpec::Integer    => write!(fmt, "Integer"),
            &FileHeaderSpec::UInteger   => write!(fmt, "UInteger"),
            &FileHeaderSpec::Float      => write!(fmt, "Float"),
            &FileHeaderSpec::Text       => write!(fmt, "Text"),
            &FileHeaderSpec::Key{name: ref n, value_type: ref vt} => {
                write!(fmt, "Key({:?}) -> {:?}", n, vt)
            }
            &FileHeaderSpec::Array{allowed_types: ref at}  => {
                write!(fmt, "Array({:?})", at)
            }
        }
    }

}

pub struct MatchError<'a> {
    summary: String,
    path: Vec<FileHeaderSpec>,
    expected: &'a FileHeaderSpec,
    found: &'a FileHeaderData
}

impl<'a> MatchError<'a> {

    pub fn new(s: String,
               path: Vec<FileHeaderSpec>,
               ex: &'a FileHeaderSpec,
               found: &'a FileHeaderData) -> MatchError<'a> {
        MatchError {
            summary: s,
            path: path,
            expected: ex,
            found: found,
        }
    }

    pub fn format(&self) -> String {
        format!("MatchError: {:?}\n\nHaving: {:?}\nExpected: {:?}\nFound: {:?}\n",
               self.summary, self.path, self.expected, self.found)
    }
}

impl<'a> Error for MatchError<'a> {

    fn description(&self) -> &str {
        &self.summary[..]
    }

    fn cause(&self) -> Option<&Error> {
        None
    }

}

impl<'a> Debug for MatchError<'a> {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt, "{}", self.format());
        Ok(())
    }

}

impl<'a> Display for MatchError<'a> {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt, "{}", self.format());
        Ok(())
    }

}

pub fn match_header_spec<'a>(spec: &'a FileHeaderSpec, data: &'a FileHeaderData)
    -> Option<MatchError<'a>>
{
    match (spec, data) {
        (&FileHeaderSpec::Null,     &FileHeaderData::Null)           => { }
        (&FileHeaderSpec::Bool,     &FileHeaderData::Bool(_))        => { }
        (&FileHeaderSpec::Integer,  &FileHeaderData::Integer(_))     => { }
        (&FileHeaderSpec::UInteger, &FileHeaderData::UInteger(_))    => { }
        (&FileHeaderSpec::Float,    &FileHeaderData::Float(_))       => { }
        (&FileHeaderSpec::Text,     &FileHeaderData::Text(_))        => { }

        (
            &FileHeaderSpec::Key{name: ref kname, value_type: ref vtype},
            &FileHeaderData::Key{name: ref n, value: ref val}
        ) => {
            if kname != n {
                // error
            }
            return match_header_spec(&*vtype, &*val);
        }

        (
            &FileHeaderSpec::Array{allowed_types: ref vtypes},
            &FileHeaderData::Array{values: ref vs}
        ) => {
            for (t, v) in vtypes.iter().zip(vs.iter()) {
                let res = match_header_spec(t, v);
                if res.is_some() {
                    return res;
                }
            }
        }

        (k, v) => {
            return Some(MatchError::new(String::from("Expected type does not match found type"),
                                 vec![],
                                 k, v
                                 ))
        }
    }
    None
}

<commit_msg>Remove path member from MatchError<commit_after>use std::error::Error;
use std::fmt::{Debug, Display, Formatter};
use std::fmt;

#[derive(Debug)]
pub enum FileHeaderSpec {
    Null,
    Bool,
    Integer,
    UInteger,
    Float,
    Text,
    Key { name: String, value_type: Box<FileHeaderSpec> },
    Array { allowed_types: Box<Vec<FileHeaderSpec>> },
}

#[derive(Debug)]
pub enum FileHeaderData {
    Null,
    Bool(bool),
    Integer(i64),
    UInteger(u64),
    Float(f64),
    Text(String),
    Key { name: String, value: Box<FileHeaderData> },
    Array { values: Box<Vec<FileHeaderData>> },
}

pub trait FileData : Sized {
    fn get_fulltext(&self) -> String;
    fn get_abbrev(&self) -> String;
}

impl Display for FileHeaderSpec {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        match self {
            &FileHeaderSpec::Null       => write!(fmt, "NULL"),
            &FileHeaderSpec::Bool       => write!(fmt, "Bool"),
            &FileHeaderSpec::Integer    => write!(fmt, "Integer"),
            &FileHeaderSpec::UInteger   => write!(fmt, "UInteger"),
            &FileHeaderSpec::Float      => write!(fmt, "Float"),
            &FileHeaderSpec::Text       => write!(fmt, "Text"),
            &FileHeaderSpec::Key{name: ref n, value_type: ref vt} => {
                write!(fmt, "Key({:?}) -> {:?}", n, vt)
            }
            &FileHeaderSpec::Array{allowed_types: ref at}  => {
                write!(fmt, "Array({:?})", at)
            }
        }
    }

}

pub struct MatchError<'a> {
    summary: String,
    expected: &'a FileHeaderSpec,
    found: &'a FileHeaderData
}

impl<'a> MatchError<'a> {

    pub fn new(s: String,
               ex: &'a FileHeaderSpec,
               found: &'a FileHeaderData) -> MatchError<'a> {
        MatchError {
            summary: s,
            expected: ex,
            found: found,
        }
    }

    pub fn format(&self) -> String {
        format!("MatchError: {:?}\nExpected: {:?}\nFound: {:?}\n",
               self.summary, self.expected, self.found)
    }
}

impl<'a> Error for MatchError<'a> {

    fn description(&self) -> &str {
        &self.summary[..]
    }

    fn cause(&self) -> Option<&Error> {
        None
    }

}

impl<'a> Debug for MatchError<'a> {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt, "{}", self.format());
        Ok(())
    }

}

impl<'a> Display for MatchError<'a> {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt, "{}", self.format());
        Ok(())
    }

}

pub fn match_header_spec<'a>(spec: &'a FileHeaderSpec, data: &'a FileHeaderData)
    -> Option<MatchError<'a>>
{
    match (spec, data) {
        (&FileHeaderSpec::Null,     &FileHeaderData::Null)           => { }
        (&FileHeaderSpec::Bool,     &FileHeaderData::Bool(_))        => { }
        (&FileHeaderSpec::Integer,  &FileHeaderData::Integer(_))     => { }
        (&FileHeaderSpec::UInteger, &FileHeaderData::UInteger(_))    => { }
        (&FileHeaderSpec::Float,    &FileHeaderData::Float(_))       => { }
        (&FileHeaderSpec::Text,     &FileHeaderData::Text(_))        => { }

        (
            &FileHeaderSpec::Key{name: ref kname, value_type: ref vtype},
            &FileHeaderData::Key{name: ref n, value: ref val}
        ) => {
            if kname != n {
                // error
            }
            return match_header_spec(&*vtype, &*val);
        }

        (
            &FileHeaderSpec::Array{allowed_types: ref vtypes},
            &FileHeaderData::Array{values: ref vs}
        ) => {
            for (t, v) in vtypes.iter().zip(vs.iter()) {
                let res = match_header_spec(t, v);
                if res.is_some() {
                    return res;
                }
            }
        }

        (k, v) => {
            return Some(MatchError::new(String::from("Expected type does not match found type"),
                                 k, v
                                 ))
        }
    }
    None
}

<|endoftext|>


<commit_before>extern crate hyper;
extern crate websocket;

use self::hyper::http::h1::Incoming;
use self::hyper::method::Method;
use self::hyper::uri::RequestUri;
use self::hyper::uri::RequestUri::AbsolutePath;

use self::websocket::WebSocketError;
use futures::future::{Future,err};
use futures::stream::Stream;

use std::cell::RefCell;
use std::rc::Rc;

use std::fs::File;

use self::websocket::server::upgrade::async::IntoWs;
use super::trivial_peer::get_literal_peer_now;

use super::readdebt::{DebtHandling, ReadDebt};
use super::ws_peer::{Mode1, PeerForWs, WsReadWrapper, WsWriteWrapper};
use super::{box_up_err, io_other_error, BoxedNewPeerFuture, Peer};
use super::{ConstructParams, PeerConstructor, Specifier};
use super::options::StaticFile;

#[derive(Debug)]
pub struct WsServer<T: Specifier>(pub T);
impl<T: Specifier> Specifier for WsServer<T> {
    fn construct(&self, cp: ConstructParams) -> PeerConstructor {
        let mode1 = if cp.program_options.websocket_text_mode {
            Mode1::Text
        } else {
            Mode1::Binary
        };
        let restrict_uri = Rc::new(cp.program_options.restrict_uri.clone());
        let serve_static_files = Rc::new(cp.program_options.serve_static_files.clone());
        let inner = self.0.construct(cp.clone());
        inner.map(move |p| {
            ws_upgrade_peer(
                p,
                mode1,
                cp.program_options.read_debt_handling,
                restrict_uri.clone(),
                serve_static_files.clone(),
            )
        })
    }
    specifier_boilerplate!(typ=WebSocket noglobalstate has_subspec);
    self_0_is_subspecifier!(proxy_is_multiconnect);
}
specifier_class!(
    name = WsServerClass,
    target = WsServer,
    prefixes = ["ws-upgrade:", "upgrade-ws:", "ws-u:", "u-ws:"],
    arg_handling = subspec,
    overlay = true,
    MessageOriented,
    MulticonnectnessDependsOnInnerType,
    help = r#"
WebSocket upgrader / raw server. Specify your own protocol instead of usual TCP. [A]

All other WebSocket server modes actually use this overlay under the hood.

Example: serve incoming connection from socat

    socat tcp-l:1234,fork,reuseaddr exec:'websocat -t ws-u\:stdio\: mirror\:'
"#
);

specifier_alias!(
    name = WsTcpServerClass,
    prefixes = ["ws-listen:", "ws-l:", "l-ws:", "listen-ws:"],
    alias = "ws-u:tcp-l:",
    help = r#"
WebSocket server. Argument is host and port to listen.

Example: Dump all incoming websocket data to console

    websocat ws-l:127.0.0.1:8808 -

Example: the same, but more verbose:

    websocat ws-l:tcp-l:127.0.0.1:8808 reuse:-
"#
);

specifier_alias!(
    name = WsInetdServerClass,
    prefixes = ["inetd-ws:", "ws-inetd:"],
    alias = "ws-u:inetd:",
    help = r#"
WebSocket inetd server. [A]

TODO: transfer the example here
"#
);

specifier_alias!(
    name = WsUnixServerClass,
    prefixes = ["l-ws-unix:"],
    alias = "ws-u:unix-l:",
    help = r#"
WebSocket UNIX socket-based server. [A]
"#
);

specifier_alias!(
    name = WsAbstractUnixServerClass,
    prefixes = ["l-ws-abstract:"],
    alias = "ws-l:abstract-l:",
    help = r#"
WebSocket abstract-namespaced UNIX socket server. [A]
"#
);

/*

     if x == "" {
                Err("Specify underlying protocol for ws-l:")?;
            }
            if let Some(c) = x.chars().next() {
                if c.is_numeric() || c == '[' {
                    // Assuming user uses old format like ws-l:127.0.0.1:8080
                    return spec(&("ws-l:tcp-l:".to_owned() + x));
                }
            }
            boxup(super::ws_server_peer::WsUpgrade(spec(x)?))
*/

const BAD_REQUEST :&'static [u8] = b"HTTP/1.1 400 Bad Reqeust\r\nServer: websocat\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nOnly WebSocket connections are welcome here\n";

const NOT_FOUND: &'static [u8] = b"HTTP/1.1 404 Not Found\r\nServer: websocat\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nURI does not match any -F option and is not a WebSocket connection.\n";

const NOT_FOUND2: &'static [u8] = b"HTTP/1.1 500 Not Found\r\nServer: websocat\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nFailed to open the file on server side.\n";

const BAD_METHOD :&'static [u8] = b"HTTP/1.1 400 Bad Reqeust\r\nServer: websocat\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nHTTP method should be GET\n";

const BAD_URI_FORMAT :&'static [u8] = b"HTTP/1.1 400 Bad Reqeust\r\nServer: websocat\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nURI should be an absolute path\n";

fn get_static_file_reply(len: Option<u64>, ct: &str) -> Vec<u8> {
    let mut q = Vec::with_capacity(256);
    q.extend_from_slice(b"HTTP/1.1 200 OK\r\nServer: websocat\r\nContent-Type: ");
    q.extend_from_slice(ct.as_bytes());
    q.extend_from_slice(b"\r\n");
    if let Some(x) = len {
        q.extend_from_slice(b"Content-Length: ");
        q.extend_from_slice(format!("{}",x).as_bytes());
        q.extend_from_slice(b"\r\n");
    }
    q.extend_from_slice(b"\r\n\r\n");
    q
}

fn http_serve(
        p:Peer,
        incoming:Option<Incoming<(Method, RequestUri)>>,
        serve_static_files: Rc<Vec<StaticFile>>,
) -> Box<Future<Item=(), Error=()>> {
    info!("HTTP-serving {:?}", incoming);
    let mut serve_file = None;
    let content = if serve_static_files.is_empty() {
        BAD_REQUEST.to_vec()
    } else {
        if let Some(inc) = incoming {
            if inc.subject.0 == Method::Get {
                match inc.subject.1 {
                    AbsolutePath(x) => {
                        let mut reply = None;
                        for sf in &*serve_static_files {
                            if sf.uri == x {
                                match File::open(&sf.file) {
                                    Ok(f) => {
                                        let fs = match f.metadata() {
                                            Err(_) => None,
                                            Ok(x) => Some(x.len()),
                                        };
                                        reply = Some(get_static_file_reply(
                                            fs, &sf.content_type));
                                        serve_file = Some(f);
                                    },
                                    Err(_) => {
                                        reply = Some(NOT_FOUND2.to_vec());
                                    },
                                }
                            }
                        }
                        reply.unwrap_or(NOT_FOUND.to_vec())
                    },
                    _ => BAD_URI_FORMAT.to_vec(),
                }
            } else {
                BAD_METHOD.to_vec()
            }
        } else {
            BAD_REQUEST.to_vec()
        }
    };
    let reply = get_literal_peer_now(content);

    let co = super::my_copy::CopyOptions {
        buffer_size: 1024,
        once: false,
        stop_on_reader_zero_read: true,
    };

    if let Some(f) = serve_file {
        Box::new(
        super::my_copy::copy(reply, p.1, co)
        .map_err(drop)
        .and_then(move |(_len,_,conn)| {
            let co2 = super::my_copy::CopyOptions {
                buffer_size: 65536,
                once: false,
                stop_on_reader_zero_read: true,
            };
            let wr = super::file_peer::ReadFileWrapper(f);
            super::my_copy::copy(wr, conn, co2).map(|_|()).map_err(drop)
        }))
    } else {
        Box::new(super::my_copy::copy(reply, p.1, co).map(|_|()).map_err(drop))
    }
}

pub fn ws_upgrade_peer(
    inner_peer: Peer,
    mode1: Mode1,
    ws_read_debt_handling: DebtHandling,
    restrict_uri: Rc<Option<String>>,
    serve_static_files: Rc<Vec<StaticFile>>,
) -> BoxedNewPeerFuture {
    let step1 = PeerForWs(inner_peer);
    let step2: Box<
        Future<Item = self::websocket::server::upgrade::async::Upgrade<_>, Error = _>,
    > = step1.into_ws();
    let step3 = step2
        .or_else(|(innerpeer, hyper_incoming, _bytesmut, e)| {
            info!("bm: {:?}", _bytesmut);
            http_serve(innerpeer.0, hyper_incoming, serve_static_files)
            .then(|_|
                err(WebSocketError::IoError(io_other_error(e)))
            )
        })
        .and_then(
            move |x| -> Box<Future<Item = Peer, Error = websocket::WebSocketError>> {
                info!("Incoming connection to websocket: {}", x.request.subject.1);
                debug!("{:?}", x.request);
                debug!("{:?}", x.headers);
                if let Some(ref restrict_uri) = *restrict_uri {
                    let check_passed = match x.request.subject.1 {
                        AbsolutePath(ref x) if x == restrict_uri => true,
                        _ => false,
                    };
                    if !check_passed {
                        return Box::new(
                            x.reject()
                                .and_then(|_| {
                                    warn!("Incoming request URI doesn't match the --restrict-uri value");
                                    ::futures::future::err(::util::simple_err(
                                        "Request URI doesn't match --restrict-uri parameter"
                                            .to_string(),
                                    ))
                                })
                                .map_err(|e| websocket::WebSocketError::IoError(io_other_error(e))),
                        )
                            as Box<Future<Item = Peer, Error = websocket::WebSocketError>>;
                    }
                };
                Box::new(x.accept().map(move |(y, headers)| {
                    debug!("{:?}", headers);
                    info!("Upgraded");
                    let (sink, stream) = y.split();
                    let mpsink = Rc::new(RefCell::new(sink));

                    let ws_str = WsReadWrapper {
                        s: stream,
                        pingreply: mpsink.clone(),
                        debt: ReadDebt(Default::default(), ws_read_debt_handling),
                    };
                    let ws_sin =
                        WsWriteWrapper(mpsink, mode1, true /* send Close on shutdown */);

                    Peer::new(ws_str, ws_sin)
                })) as Box<Future<Item = Peer, Error = websocket::WebSocketError>>
            },
        );
    let step4 = step3.map_err(box_up_err);
    Box::new(step4) as BoxedNewPeerFuture
}
<commit_msg>Minor adjustments for -F<commit_after>extern crate hyper;
extern crate websocket;

use self::hyper::http::h1::Incoming;
use self::hyper::method::Method;
use self::hyper::uri::RequestUri;
use self::hyper::uri::RequestUri::AbsolutePath;

use self::websocket::WebSocketError;
use futures::future::{Future,err};
use futures::stream::Stream;

use std::cell::RefCell;
use std::rc::Rc;

use std::fs::File;

use self::websocket::server::upgrade::async::IntoWs;
use super::trivial_peer::get_literal_peer_now;

use super::readdebt::{DebtHandling, ReadDebt};
use super::ws_peer::{Mode1, PeerForWs, WsReadWrapper, WsWriteWrapper};
use super::{box_up_err, io_other_error, BoxedNewPeerFuture, Peer};
use super::{ConstructParams, PeerConstructor, Specifier};
use super::options::StaticFile;

#[derive(Debug)]
pub struct WsServer<T: Specifier>(pub T);
impl<T: Specifier> Specifier for WsServer<T> {
    fn construct(&self, cp: ConstructParams) -> PeerConstructor {
        let mode1 = if cp.program_options.websocket_text_mode {
            Mode1::Text
        } else {
            Mode1::Binary
        };
        let restrict_uri = Rc::new(cp.program_options.restrict_uri.clone());
        let serve_static_files = Rc::new(cp.program_options.serve_static_files.clone());
        let inner = self.0.construct(cp.clone());
        inner.map(move |p| {
            ws_upgrade_peer(
                p,
                mode1,
                cp.program_options.read_debt_handling,
                restrict_uri.clone(),
                serve_static_files.clone(),
            )
        })
    }
    specifier_boilerplate!(typ=WebSocket noglobalstate has_subspec);
    self_0_is_subspecifier!(proxy_is_multiconnect);
}
specifier_class!(
    name = WsServerClass,
    target = WsServer,
    prefixes = ["ws-upgrade:", "upgrade-ws:", "ws-u:", "u-ws:"],
    arg_handling = subspec,
    overlay = true,
    MessageOriented,
    MulticonnectnessDependsOnInnerType,
    help = r#"
WebSocket upgrader / raw server. Specify your own protocol instead of usual TCP. [A]

All other WebSocket server modes actually use this overlay under the hood.

Example: serve incoming connection from socat

    socat tcp-l:1234,fork,reuseaddr exec:'websocat -t ws-u\:stdio\: mirror\:'
"#
);

specifier_alias!(
    name = WsTcpServerClass,
    prefixes = ["ws-listen:", "ws-l:", "l-ws:", "listen-ws:"],
    alias = "ws-u:tcp-l:",
    help = r#"
WebSocket server. Argument is host and port to listen.

Example: Dump all incoming websocket data to console

    websocat ws-l:127.0.0.1:8808 -

Example: the same, but more verbose:

    websocat ws-l:tcp-l:127.0.0.1:8808 reuse:-
"#
);

specifier_alias!(
    name = WsInetdServerClass,
    prefixes = ["inetd-ws:", "ws-inetd:"],
    alias = "ws-u:inetd:",
    help = r#"
WebSocket inetd server. [A]

TODO: transfer the example here
"#
);

specifier_alias!(
    name = WsUnixServerClass,
    prefixes = ["l-ws-unix:"],
    alias = "ws-u:unix-l:",
    help = r#"
WebSocket UNIX socket-based server. [A]
"#
);

specifier_alias!(
    name = WsAbstractUnixServerClass,
    prefixes = ["l-ws-abstract:"],
    alias = "ws-l:abstract-l:",
    help = r#"
WebSocket abstract-namespaced UNIX socket server. [A]
"#
);

/*

     if x == "" {
                Err("Specify underlying protocol for ws-l:")?;
            }
            if let Some(c) = x.chars().next() {
                if c.is_numeric() || c == '[' {
                    // Assuming user uses old format like ws-l:127.0.0.1:8080
                    return spec(&("ws-l:tcp-l:".to_owned() + x));
                }
            }
            boxup(super::ws_server_peer::WsUpgrade(spec(x)?))
*/

const BAD_REQUEST :&'static [u8] = b"HTTP/1.1 400 Bad Reqeust\r\nServer: websocat\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nOnly WebSocket connections are welcome here\n";

const NOT_FOUND: &'static [u8] = b"HTTP/1.1 404 Not Found\r\nServer: websocat\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nURI does not match any -F option and is not a WebSocket connection.\n";

const NOT_FOUND2: &'static [u8] = b"HTTP/1.1 500 Not Found\r\nServer: websocat\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nFailed to open the file on server side.\n";

const BAD_METHOD :&'static [u8] = b"HTTP/1.1 400 Bad Reqeust\r\nServer: websocat\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nHTTP method should be GET\n";

const BAD_URI_FORMAT :&'static [u8] = b"HTTP/1.1 400 Bad Reqeust\r\nServer: websocat\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nURI should be an absolute path\n";

fn get_static_file_reply(len: Option<u64>, ct: &str) -> Vec<u8> {
    let mut q = Vec::with_capacity(256);
    q.extend_from_slice(b"HTTP/1.1 200 OK\r\nServer: websocat\r\nContent-Type: ");
    q.extend_from_slice(ct.as_bytes());
    q.extend_from_slice(b"\r\n");
    if let Some(x) = len {
        q.extend_from_slice(b"Content-Length: ");
        q.extend_from_slice(format!("{}",x).as_bytes());
        q.extend_from_slice(b"\r\n");
    }
    q.extend_from_slice(b"\r\n");
    q
}

fn http_serve(
        p:Peer,
        incoming:Option<Incoming<(Method, RequestUri)>>,
        serve_static_files: Rc<Vec<StaticFile>>,
) -> Box<Future<Item=(), Error=()>> {
    let mut serve_file = None;
    let content = if serve_static_files.is_empty() {
        BAD_REQUEST.to_vec()
    } else {
        if let Some(inc) = incoming {
            info!("HTTP-serving {:?}", inc.subject);
            if inc.subject.0 == Method::Get {
                match inc.subject.1 {
                    AbsolutePath(x) => {
                        let mut reply = None;
                        for sf in &*serve_static_files {
                            if sf.uri == x {
                                match File::open(&sf.file) {
                                    Ok(f) => {
                                        let fs = match f.metadata() {
                                            Err(_) => None,
                                            Ok(x) => Some(x.len()),
                                        };
                                        reply = Some(get_static_file_reply(
                                            fs, &sf.content_type));
                                        serve_file = Some(f);
                                    },
                                    Err(_) => {
                                        reply = Some(NOT_FOUND2.to_vec());
                                    },
                                }
                            }
                        }
                        reply.unwrap_or(NOT_FOUND.to_vec())
                    },
                    _ => BAD_URI_FORMAT.to_vec(),
                }
            } else {
                BAD_METHOD.to_vec()
            }
        } else {
            BAD_REQUEST.to_vec()
        }
    };
    let reply = get_literal_peer_now(content);

    let co = super::my_copy::CopyOptions {
        buffer_size: 1024,
        once: false,
        stop_on_reader_zero_read: true,
    };

    if let Some(f) = serve_file {
        Box::new(
        super::my_copy::copy(reply, p.1, co)
        .map_err(drop)
        .and_then(move |(_len,_,conn)| {
            let co2 = super::my_copy::CopyOptions {
                buffer_size: 65536,
                once: false,
                stop_on_reader_zero_read: true,
            };
            let wr = super::file_peer::ReadFileWrapper(f);
            super::my_copy::copy(wr, conn, co2).map(|_|()).map_err(drop)
        }))
    } else {
        Box::new(super::my_copy::copy(reply, p.1, co).map(|_|()).map_err(drop))
    }
}

pub fn ws_upgrade_peer(
    inner_peer: Peer,
    mode1: Mode1,
    ws_read_debt_handling: DebtHandling,
    restrict_uri: Rc<Option<String>>,
    serve_static_files: Rc<Vec<StaticFile>>,
) -> BoxedNewPeerFuture {
    let step1 = PeerForWs(inner_peer);
    let step2: Box<
        Future<Item = self::websocket::server::upgrade::async::Upgrade<_>, Error = _>,
    > = step1.into_ws();
    let step3 = step2
        .or_else(|(innerpeer, hyper_incoming, _bytesmut, e)| {
            http_serve(innerpeer.0, hyper_incoming, serve_static_files)
            .then(|_|
                err(WebSocketError::IoError(io_other_error(e)))
            )
        })
        .and_then(
            move |x| -> Box<Future<Item = Peer, Error = websocket::WebSocketError>> {
                info!("Incoming connection to websocket: {}", x.request.subject.1);
                debug!("{:?}", x.request);
                debug!("{:?}", x.headers);
                if let Some(ref restrict_uri) = *restrict_uri {
                    let check_passed = match x.request.subject.1 {
                        AbsolutePath(ref x) if x == restrict_uri => true,
                        _ => false,
                    };
                    if !check_passed {
                        return Box::new(
                            x.reject()
                                .and_then(|_| {
                                    warn!("Incoming request URI doesn't match the --restrict-uri value");
                                    ::futures::future::err(::util::simple_err(
                                        "Request URI doesn't match --restrict-uri parameter"
                                            .to_string(),
                                    ))
                                })
                                .map_err(|e| websocket::WebSocketError::IoError(io_other_error(e))),
                        )
                            as Box<Future<Item = Peer, Error = websocket::WebSocketError>>;
                    }
                };
                Box::new(x.accept().map(move |(y, headers)| {
                    debug!("{:?}", headers);
                    info!("Upgraded");
                    let (sink, stream) = y.split();
                    let mpsink = Rc::new(RefCell::new(sink));

                    let ws_str = WsReadWrapper {
                        s: stream,
                        pingreply: mpsink.clone(),
                        debt: ReadDebt(Default::default(), ws_read_debt_handling),
                    };
                    let ws_sin =
                        WsWriteWrapper(mpsink, mode1, true /* send Close on shutdown */);

                    Peer::new(ws_str, ws_sin)
                })) as Box<Future<Item = Peer, Error = websocket::WebSocketError>>
            },
        );
    let step4 = step3.map_err(box_up_err);
    Box::new(step4) as BoxedNewPeerFuture
}
<|endoftext|>


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

//! Note that this is NOT Ken Perlin's simplex noise, as that is patent encumbered.
//! Instead, these functions use the OpenSimplex algorithm, as detailed here:
//! http://uniblock.tumblr.com/post/97868843242/noise

use num::Float;

use {gradient, math, Seed};

const STRETCH_CONSTANT_2D: f64 = -0.211324865405187; //(1/sqrt(2+1)-1)/2;
const SQUISH_CONSTANT_2D: f64 = 0.366025403784439; //(sqrt(2+1)-1)/2;
const STRETCH_CONSTANT_3D: f64 = -1.0 / 6.0; //(1/Math.sqrt(3+1)-1)/3;
const SQUISH_CONSTANT_3D: f64 = 1.0 / 3.0; //(Math.sqrt(3+1)-1)/3;

const NORM_CONSTANT_2D: f32 = 1.0 / 14.0;
const NORM_CONSTANT_3D: f32 = 1.0 / 14.0;

/// 2-dimensional [OpenSimplex Noise](http://uniblock.tumblr.com/post/97868843242/noise)
///
/// This is a slower but higher quality form of gradient noise than `noise::perlin2`.
pub fn open_simplex2<T: Float>(seed: &Seed, point: &::Point2<T>) -> T {
    fn gradient<T: Float>(seed: &Seed, xs_floor: T, ys_floor: T, dx: T, dy: T) -> T {
        let zero: T = math::cast(0);

        let attn = math::cast::<_, T>(2.0_f64) - dx * dx - dy * dy;
        if attn > zero {
            let index = seed.get2::<isize>([math::cast(xs_floor), math::cast(ys_floor)]);
            let vec = gradient::get2::<T>(index);
            math::pow4(attn) * (dx * vec[0] + dy * vec[1])
        } else {
            zero
        }
    }

    let zero: T = math::cast(0);
    let one: T = math::cast(1);
    let squish_constant: T = math::cast(SQUISH_CONSTANT_2D);

    // Place input coordinates onto grid.
    let stretch_offset = (point[0] + point[1]) * math::cast(STRETCH_CONSTANT_2D);
    let xs = point[0] + stretch_offset;
    let ys = point[1] + stretch_offset;

    // Floor to get grid coordinates of rhombus (stretched square) cell origin.
    let mut xs_floor = xs.floor();
    let mut ys_floor = ys.floor();

    // Skew out to get actual coordinates of rhombus origin. We'll need these later.
    let squish_offset = (xs_floor + ys_floor) * squish_constant;
    let x_floor = xs_floor + squish_offset;
    let y_floor = ys_floor + squish_offset;

    // Compute grid coordinates relative to rhombus origin.
    let xs_frac = xs - xs_floor;
    let ys_frac = ys - ys_floor;

    // Sum those together to get a value that determines which region we're in.
    let frac_sum = xs_frac + ys_frac;

    // Positions relative to origin point.
    let mut dx0 = point[0] - x_floor;
    let mut dy0 = point[1] - y_floor;

    let mut value: T = zero;

    // (0, 0) --- (1, 0)
    // |   A     /     |
    // |       /       |
    // |     /     B   |
    // (0, 1) --- (1, 1)

    // Point (1, 0)
    let dx1 = dx0 - one - squish_constant;
    let dy1 = dy0 - squish_constant;
    value = value + gradient(seed, xs_floor + one, ys_floor, dx1, dy1);

    // Point (0, 1)
    let dx2 = dx1 + one;
    let dy2 = dy1 - one;
    value = value + gradient(seed, xs_floor, ys_floor + one, dx2, dy2);

    // See the graph for an intuitive explanation; the sum of `x` and `y` is
    // only greater than `1` if we're on Region B.
    if frac_sum > one {
        // Point (1, 1)
        xs_floor = xs_floor + one;
        ys_floor = ys_floor + one;
        // We are moving across the diagonal `/`, so we'll need to add by the
        // squish constant
        dx0 = dx1 - squish_constant;
        dy0 = dy2 - squish_constant;
    }

    // Point (0, 0) or (1, 1)
    value = value + gradient(seed, xs_floor, ys_floor, dx0, dy0);

    value * math::cast(NORM_CONSTANT_2D)
}

/// 3-dimensional [OpenSimplex Noise](http://uniblock.tumblr.com/post/97868843242/noise)
///
/// This is a slower but higher quality form of gradient noise than `noise::perlin3`.
pub fn open_simplex3<T: Float>(seed: &Seed, point: &::Point3<T>) -> T {
    fn gradient<T: Float>(seed: &Seed, xs_floor: T, ys_floor: T, zs_floor: T, dx: T, dy: T, dz: T) -> T {
        let zero: T = math::cast(0);

        let attn = math::cast::<_, T>(2.0_f64) - dx * dx - dy * dy - dz * dz;
        if attn > zero {
            let index = seed.get3::<isize>([math::cast(xs_floor), math::cast(ys_floor), math::cast(zs_floor)]);
            let vec = gradient::get3::<T>(index);
            math::pow4(attn) * (dx * vec[0] + dy * vec[1] + dz * vec[2])
        } else {
            zero
        }
    }

    let zero: T = math::cast(0);
    let one: T = math::cast(1);
    let two: T = math::cast(2);
    let squish_constant: T = math::cast(SQUISH_CONSTANT_3D);

    //Place input coordinates on simplectic honeycomb.
    let stretch_offset = (point[0] + point[1] + point[2]) * math::cast(STRETCH_CONSTANT_3D);
    let xs = point[0] + stretch_offset;
    let ys = point[1] + stretch_offset;
    let zs = point[2] + stretch_offset;

    //Floor to get simplectic honeycomb coordinates of rhombohedron (stretched cube) super-cell origin.
    let xsb = xs.floor();
    let ysb = ys.floor();
    let zsb = zs.floor();

    //Skew out to get actual coordinates of rhombohedron origin. We'll need these later.
    let squish_offset = (xsb + ysb + zsb) * squish_constant;
    let xb = xsb + squish_offset;
    let yb = ysb + squish_offset;
    let zb = zsb + squish_offset;

    //Compute simplectic honeycomb coordinates relative to rhombohedral origin.
    let xins = xs - xsb;
    let yins = ys - ysb;
    let zins = zs - zsb;

    //Sum those together to get a value that determines which region we're in.
    let in_sum = xins + yins + zins;

    //Positions relative to origin point.
    let mut dx0 = point[0] - xb;
    let mut dy0 = point[1] - yb;
    let mut dz0 = point[2] - zb;

    let mut value = zero;
    let c0 = one + two * squish_constant;

    if in_sum <= one { //We're inside the tetrahedron (3-Simplex) at (0,0,0)
        //Contribution (0,0,0)
        value = value + gradient(seed, xsb, ysb, zsb, dx0, dy0, dz0);

        //Contribution (1,0,0)
        let dx1 = dx0 - one - squish_constant;
        let dy1 = dy0 - squish_constant;
        let dz1 = dz0 - squish_constant;
        value = value + gradient(seed, xsb + one, ysb, zsb, dx1, dy1, dz1);

        //Contribution (0,1,0)
        let dx2 = dx0 - squish_constant;
        let dy2 = dy1 - one;
        let dz2 = dz1;
        value = value + gradient(seed, xsb, ysb + one, zsb, dx2, dy2, dz2);

        //Contribution (0,0,1)
        let dx3 = dx2;
        let dy3 = dy1;
        let dz3 = dz1 - one;
        value = value + gradient(seed, xsb, ysb, zsb + one, dx3, dy3, dz3);
    } else if in_sum >= two { //We're inside the tetrahedron (3-Simplex) at (1,11)
        //Contribution (1,1,0)
        let dx3 = dx0 - c0;
        let dy3 = dy0 - c0;
        let dz3 = dz0 - c0 + one;
        value = value + gradient(seed, xsb + one, ysb + one, zsb, dx3, dy3, dz3);

        //Contribution (1,0,1)
        let dx2 = dx3;
        let dy2 = dy3 + one;
        let dz2 = dz3 - one;
        value = value + gradient(seed, xsb + one, ysb, zsb + one, dx2, dy2, dz2);

        //Contribution (0,1,1)
        let dx1 = dx3 + one;
        let dy1 = dy3;
        let dz1 = dz2;
        value = value + gradient(seed, xsb, ysb + one, zsb + one, dx1, dy1, dz1);

        //Contribution (1,1,1)
        dx0 = dx3 - squish_constant;
        dy0 = dy3 - squish_constant;
        dz0 = dz2 - squish_constant;
        value = value + gradient(seed, xsb + one, ysb + one, zsb + one, dx0, dy0, dz0);
    } else { //We're inside the octahedron (Rectified 3-Simplex) in between.
        //Contribution (1,0,0)
        let dx1 = dx0 - one - squish_constant;
        let dy1 = dy0 - squish_constant;
        let dz1 = dz0 - squish_constant;
        value = value + gradient(seed, xsb + one, ysb, zsb, dx1, dy1, dz1);

        //Contribution (0,1,0)
        let dx2 = dx1 + one;
        let dy2 = dy1 - one;
        let dz2 = dz1;
        value = value + gradient(seed, xsb, ysb + one, zsb, dx2, dy2, dz2);

        //Contribution (0,0,1)
        let dx3 = dx2;
        let dy3 = dy1;
        let dz3 = dz1 - one;
        value = value + gradient(seed, xsb, ysb, zsb + one, dx3, dy3, dz3);

        //Contribution (1,1,0)
        let dx4 = dx0 - c0;
        let dy4 = dy0 - c0;
        let dz4 = dz1 - squish_constant;
        value = value + gradient(seed, xsb + one, ysb + one, zsb, dx4, dy4, dz4);

        //Contribution (1,0,1)
        let dx5 = dx4;
        let dy5 = dy4 + one;
        let dz5 = dz4 - one;
        value = value + gradient(seed, xsb + one, ysb, zsb + one, dx5, dy5, dz5);

        //Contribution (0,1,1)
        let dx6 = dx4 + one;
        let dy6 = dy4;
        let dz6 = dz5;
        value = value + gradient(seed, xsb, ysb + one, zsb + one, dx6, dy6, dz6);
    }

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

//! Note that this is NOT Ken Perlin's simplex noise, as that is patent encumbered.
//! Instead, these functions use the OpenSimplex algorithm, as detailed here:
//! http://uniblock.tumblr.com/post/97868843242/noise

use num::Float;

use {gradient, math, Seed};

const STRETCH_CONSTANT_2D: f64 = -0.211324865405187; //(1/sqrt(2+1)-1)/2;
const SQUISH_CONSTANT_2D: f64 = 0.366025403784439; //(sqrt(2+1)-1)/2;
const STRETCH_CONSTANT_3D: f64 = -1.0 / 6.0; //(1/Math.sqrt(3+1)-1)/3;
const SQUISH_CONSTANT_3D: f64 = 1.0 / 3.0; //(Math.sqrt(3+1)-1)/3;

const NORM_CONSTANT_2D: f32 = 1.0 / 14.0;
const NORM_CONSTANT_3D: f32 = 1.0 / 14.0;

/// 2-dimensional [OpenSimplex Noise](http://uniblock.tumblr.com/post/97868843242/noise)
///
/// This is a slower but higher quality form of gradient noise than `noise::perlin2`.
pub fn open_simplex2<T: Float>(seed: &Seed, point: &::Point2<T>) -> T {
    fn gradient<T: Float>(seed: &Seed, xs_floor: T, ys_floor: T, dx: T, dy: T) -> T {
        let zero: T = math::cast(0);

        let attn = math::cast::<_, T>(2.0_f64) - dx * dx - dy * dy;
        if attn > zero {
            let index = seed.get2::<isize>([math::cast(xs_floor), math::cast(ys_floor)]);
            let vec = gradient::get2::<T>(index);
            math::pow4(attn) * (dx * vec[0] + dy * vec[1])
        } else {
            zero
        }
    }

    let zero: T = math::cast(0);
    let one: T = math::cast(1);
    let squish_constant: T = math::cast(SQUISH_CONSTANT_2D);

    // Place input coordinates onto grid.
    let stretch_offset = (point[0] + point[1]) * math::cast(STRETCH_CONSTANT_2D);
    let xs = point[0] + stretch_offset;
    let ys = point[1] + stretch_offset;

    // Floor to get grid coordinates of rhombus (stretched square) cell origin.
    let mut xs_floor = xs.floor();
    let mut ys_floor = ys.floor();

    // Skew out to get actual coordinates of rhombus origin. We'll need these later.
    let squish_offset = (xs_floor + ys_floor) * squish_constant;
    let x_floor = xs_floor + squish_offset;
    let y_floor = ys_floor + squish_offset;

    // Compute grid coordinates relative to rhombus origin.
    let xs_frac = xs - xs_floor;
    let ys_frac = ys - ys_floor;

    // Sum those together to get a value that determines which region we're in.
    let frac_sum = xs_frac + ys_frac;

    // Positions relative to origin point (0, 0).
    let mut dx0 = point[0] - x_floor;
    let mut dy0 = point[1] - y_floor;

    let mut value: T = zero;

    // (0, 0) --- (1, 0)
    // |   A     /     |
    // |       /       |
    // |     /     B   |
    // (0, 1) --- (1, 1)

    // Point (1, 0)
    let dx1 = dx0 - one - squish_constant;
    let dy1 = dy0 - squish_constant;
    value = value + gradient(seed, xs_floor + one, ys_floor, dx1, dy1);

    // Point (0, 1)
    let dx2 = dx1 + one;
    let dy2 = dy1 - one;
    value = value + gradient(seed, xs_floor, ys_floor + one, dx2, dy2);

    // See the graph for an intuitive explanation; the sum of `x` and `y` is
    // only greater than `1` if we're on Region B.
    if frac_sum > one {
        // Point (1, 1)
        xs_floor = xs_floor + one;
        ys_floor = ys_floor + one;
        // We are moving across the diagonal `/`, so we'll need to add by the
        // squish constant
        dx0 = dx1 - squish_constant;
        dy0 = dy2 - squish_constant;
    }

    // Point (0, 0) or (1, 1)
    value = value + gradient(seed, xs_floor, ys_floor, dx0, dy0);

    value * math::cast(NORM_CONSTANT_2D)
}

/// 3-dimensional [OpenSimplex Noise](http://uniblock.tumblr.com/post/97868843242/noise)
///
/// This is a slower but higher quality form of gradient noise than `noise::perlin3`.
pub fn open_simplex3<T: Float>(seed: &Seed, point: &::Point3<T>) -> T {
    fn gradient<T: Float>(seed: &Seed, xs_floor: T, ys_floor: T, zs_floor: T, dx: T, dy: T, dz: T) -> T {
        let zero: T = math::cast(0);

        let attn = math::cast::<_, T>(2.0_f64) - dx * dx - dy * dy - dz * dz;
        if attn > zero {
            let index = seed.get3::<isize>([math::cast(xs_floor), math::cast(ys_floor), math::cast(zs_floor)]);
            let vec = gradient::get3::<T>(index);
            math::pow4(attn) * (dx * vec[0] + dy * vec[1] + dz * vec[2])
        } else {
            zero
        }
    }

    let zero: T = math::cast(0);
    let one: T = math::cast(1);
    let two: T = math::cast(2);
    let squish_constant: T = math::cast(SQUISH_CONSTANT_3D);

    //Place input coordinates on simplectic honeycomb.
    let stretch_offset = (point[0] + point[1] + point[2]) * math::cast(STRETCH_CONSTANT_3D);
    let xs = point[0] + stretch_offset;
    let ys = point[1] + stretch_offset;
    let zs = point[2] + stretch_offset;

    //Floor to get simplectic honeycomb coordinates of rhombohedron (stretched cube) super-cell origin.
    let xsb = xs.floor();
    let ysb = ys.floor();
    let zsb = zs.floor();

    //Skew out to get actual coordinates of rhombohedron origin. We'll need these later.
    let squish_offset = (xsb + ysb + zsb) * squish_constant;
    let xb = xsb + squish_offset;
    let yb = ysb + squish_offset;
    let zb = zsb + squish_offset;

    //Compute simplectic honeycomb coordinates relative to rhombohedral origin.
    let xins = xs - xsb;
    let yins = ys - ysb;
    let zins = zs - zsb;

    //Sum those together to get a value that determines which region we're in.
    let in_sum = xins + yins + zins;

    //Positions relative to origin point.
    let mut dx0 = point[0] - xb;
    let mut dy0 = point[1] - yb;
    let mut dz0 = point[2] - zb;

    let mut value = zero;
    let c0 = one + two * squish_constant;

    if in_sum <= one { //We're inside the tetrahedron (3-Simplex) at (0,0,0)
        //Contribution (0,0,0)
        value = value + gradient(seed, xsb, ysb, zsb, dx0, dy0, dz0);

        //Contribution (1,0,0)
        let dx1 = dx0 - one - squish_constant;
        let dy1 = dy0 - squish_constant;
        let dz1 = dz0 - squish_constant;
        value = value + gradient(seed, xsb + one, ysb, zsb, dx1, dy1, dz1);

        //Contribution (0,1,0)
        let dx2 = dx0 - squish_constant;
        let dy2 = dy1 - one;
        let dz2 = dz1;
        value = value + gradient(seed, xsb, ysb + one, zsb, dx2, dy2, dz2);

        //Contribution (0,0,1)
        let dx3 = dx2;
        let dy3 = dy1;
        let dz3 = dz1 - one;
        value = value + gradient(seed, xsb, ysb, zsb + one, dx3, dy3, dz3);
    } else if in_sum >= two { //We're inside the tetrahedron (3-Simplex) at (1,11)
        //Contribution (1,1,0)
        let dx3 = dx0 - c0;
        let dy3 = dy0 - c0;
        let dz3 = dz0 - c0 + one;
        value = value + gradient(seed, xsb + one, ysb + one, zsb, dx3, dy3, dz3);

        //Contribution (1,0,1)
        let dx2 = dx3;
        let dy2 = dy3 + one;
        let dz2 = dz3 - one;
        value = value + gradient(seed, xsb + one, ysb, zsb + one, dx2, dy2, dz2);

        //Contribution (0,1,1)
        let dx1 = dx3 + one;
        let dy1 = dy3;
        let dz1 = dz2;
        value = value + gradient(seed, xsb, ysb + one, zsb + one, dx1, dy1, dz1);

        //Contribution (1,1,1)
        dx0 = dx3 - squish_constant;
        dy0 = dy3 - squish_constant;
        dz0 = dz2 - squish_constant;
        value = value + gradient(seed, xsb + one, ysb + one, zsb + one, dx0, dy0, dz0);
    } else { //We're inside the octahedron (Rectified 3-Simplex) in between.
        //Contribution (1,0,0)
        let dx1 = dx0 - one - squish_constant;
        let dy1 = dy0 - squish_constant;
        let dz1 = dz0 - squish_constant;
        value = value + gradient(seed, xsb + one, ysb, zsb, dx1, dy1, dz1);

        //Contribution (0,1,0)
        let dx2 = dx1 + one;
        let dy2 = dy1 - one;
        let dz2 = dz1;
        value = value + gradient(seed, xsb, ysb + one, zsb, dx2, dy2, dz2);

        //Contribution (0,0,1)
        let dx3 = dx2;
        let dy3 = dy1;
        let dz3 = dz1 - one;
        value = value + gradient(seed, xsb, ysb, zsb + one, dx3, dy3, dz3);

        //Contribution (1,1,0)
        let dx4 = dx0 - c0;
        let dy4 = dy0 - c0;
        let dz4 = dz1 - squish_constant;
        value = value + gradient(seed, xsb + one, ysb + one, zsb, dx4, dy4, dz4);

        //Contribution (1,0,1)
        let dx5 = dx4;
        let dy5 = dy4 + one;
        let dz5 = dz4 - one;
        value = value + gradient(seed, xsb + one, ysb, zsb + one, dx5, dy5, dz5);

        //Contribution (0,1,1)
        let dx6 = dx4 + one;
        let dy6 = dy4;
        let dz6 = dz5;
        value = value + gradient(seed, xsb, ysb + one, zsb + one, dx6, dy6, dz6);
    }

    return value * math::cast(NORM_CONSTANT_3D);
}
<|endoftext|>


<commit_before>// This file is part of rust-web/twig
//
// For the copyright and license information, please view the LICENSE
// file that was distributed with this source code.

/// Twig library for rust
///
/// @author NAME <EMAIL>

/////////////
// imports //
/////////////

/////////////
// exports //
/////////////
<commit_msg>rust-template: simplified header<commit_after>// This file is part of rust-web/twig
//
// For the copyright and license information, please view the LICENSE
// file that was distributed with this source code.

/// Twig library for rust
<|endoftext|>


<commit_before>use combine::{satisfy, skip_many, none_of, token, between, optional, many, many1, choice, try,
              Parser, Stream, ParseResult};
use combine::char::{string, spaces, digit, char};

#[derive(Clone, Debug, PartialEq)]
pub enum Tokens {
    ACCELERATOR,
    ACTIVETRANSFORM,
    ALL,
    AREALIGHTSOURCE,
    ATTRIBUTEBEGIN,
    ATTRIBUTEEND,
    CAMERA,
    CONCATTRANSFORM,
    COORDINATESYSTEM,
    COORDSYSTRANSFORM,
    ENDTIME,
    FILM,
    IDENTITY,
    INCLUDE,
    LIGHTSOURCE,
    LOOKAT,
    MAKENAMEDMEDIUM,
    MAKENAMEDMATERIAL,
    MATERIAL,
    MEDIUMINTERFACE,
    NAMEDMATERIAL,
    OBJECTBEGIN,
    OBJECTEND,
    OBJECTINSTANCE,
    PIXELFILTER,
    REVERSEORIENTATION,
    ROTATE,
    SAMPLE,
    SCALE,
    SHAPE,
    STARTTIME,
    INTEGRATOR,
    TEXTURE,
    TRANSFORMBEGIN,
    TRANSFORMEND,
    TRANSFORMTIMES,
    TRANSFORM,
    TRANSLATE,
    WORLDBEGIN,
    WORLDEND,
    STR(String),
    NUMBER(f32),
    LBRACK,
    RBRACK,
    COMMENT,
}

pub fn tokenize<I: Stream<Item = char>>(input: I) -> ParseResult<Vec<Tokens>, I> {

    // parsers for keywords
    let mut parsers = vec![token_parser("Accelerator", Tokens::ACCELERATOR),
                           token_parser("ActiveTransform", Tokens::ACTIVETRANSFORM),
                           token_parser("All", Tokens::ALL),
                           token_parser("AreaLightSource", Tokens::AREALIGHTSOURCE),
                           token_parser("AttributeBegin", Tokens::ATTRIBUTEBEGIN),
                           token_parser("AttributeEnd", Tokens::ATTRIBUTEEND),
                           token_parser("Camera", Tokens::CAMERA),
                           token_parser("ConcatTransform", Tokens::CONCATTRANSFORM),
                           token_parser("CoordinateSystem", Tokens::COORDINATESYSTEM),
                           token_parser("CoordSysTransform", Tokens::COORDSYSTRANSFORM),
                           token_parser("EndTime", Tokens::ENDTIME),
                           token_parser("Film", Tokens::FILM),
                           token_parser("Identity", Tokens::IDENTITY),
                           token_parser("Include", Tokens::INCLUDE),
                           token_parser("LightSource", Tokens::LIGHTSOURCE),
                           token_parser("LookAt", Tokens::LOOKAT),
                           token_parser("MakeNamedMedium", Tokens::MAKENAMEDMEDIUM),
                           token_parser("MakeNamedMaterial", Tokens::MAKENAMEDMATERIAL),
                           token_parser("Material", Tokens::MATERIAL),
                           token_parser("MediumInterface", Tokens::MEDIUMINTERFACE),
                           token_parser("NamedMaterial", Tokens::NAMEDMATERIAL),
                           token_parser("ObjectBegin", Tokens::OBJECTBEGIN),
                           token_parser("ObjectEnd", Tokens::OBJECTEND),
                           token_parser("ObjectInstance", Tokens::OBJECTINSTANCE),
                           token_parser("PixelFilter", Tokens::PIXELFILTER),
                           token_parser("ReverseOrientation", Tokens::REVERSEORIENTATION),
                           token_parser("Rotate", Tokens::ROTATE),
                           token_parser("Sample", Tokens::SAMPLE),
                           token_parser("Scale", Tokens::SCALE),
                           token_parser("Shape", Tokens::SHAPE),
                           token_parser("StartTime", Tokens::STARTTIME),
                           token_parser("Integrator", Tokens::INTEGRATOR),
                           token_parser("Texture", Tokens::TEXTURE),
                           token_parser("TransformBegin", Tokens::TRANSFORMBEGIN),
                           token_parser("TransformEnd", Tokens::TRANSFORMEND),
                           token_parser("TransformTimes", Tokens::TRANSFORMTIMES),
                           token_parser("Transform", Tokens::TRANSFORM),
                           token_parser("Translate", Tokens::TRANSLATE),
                           token_parser("WorldBegin", Tokens::WORLDBEGIN),
                           token_parser("WorldEnd", Tokens::WORLDEND),
                           token_parser("[", Tokens::LBRACK),
                           token_parser("]", Tokens::RBRACK)]
            .into_iter()
            .map(|parser| try(parser))
            .collect::<Vec<_>>();

    // Add parsers from num, strings, etc...
    parsers.push(try(float_parser()));
    parsers.push(try(string_parser()));
    parsers.push(try(comment_parser()));

    spaces()
        .with(many::<Vec<_>, _>(choice(parsers)))
        .parse_stream(input)
}

fn token_parser<'a, I: Stream<Item = char> + 'a>
    (s: &'static str,
     t: Tokens)
     -> Box<Parser<Input = I, Output = Tokens> + 'a> {
    string(s).skip(spaces()).map(move |_| t.clone()).boxed()
}

fn float_parser<'a, I: Stream<Item = char> + 'a>
    ()
    -> Box<Parser<Input = I, Output = Tokens> + 'a>
{

    (optional(char('-').or(char('+'))),
     many1::<Vec<_>, _>(digit()),
     optional(char('.').with(many::<Vec<_>, _>(digit()))),
     optional(char('e').or(char('E')).with(many1::<Vec<_>, _>(digit()))))
            .skip(spaces())
            .and_then(|(sign, int_part, frac_part, mantissa)| {
                let mut buf = String::new();
                if let Some(s) = sign {
                    buf.push(s);
                }
                for c in int_part {
                    buf.push(c);
                }
                if let Some(frac) = frac_part {
                    buf.push('.');
                    for c in frac {
                        buf.push(c);
                    }
                }
                if let Some(mant) = mantissa {
                    buf.push('e');
                    for c in mant {
                        buf.push(c);
                    }
                }

                buf.parse::<f32>().map(Tokens::NUMBER)
            })
            .boxed()
}

fn string_parser<'a, I: Stream<Item = char> + 'a>
    ()
    -> Box<Parser<Input = I, Output = Tokens> + 'a>
{
    between(token('"'),
            token('"'),
            many::<Vec<_>, _>(none_of("\"".chars())))
            .skip(spaces())
            .map(|chars| {
                     let mut buf = String::new();
                     for c in chars {
                         buf.push(c);
                     }
                     Tokens::STR(buf)
                 })
            .boxed()
}

fn comment_parser<'a, I: Stream<Item = char> + 'a>
    ()
    -> Box<Parser<Input = I, Output = Tokens> + 'a>
{
    token('#')
        .and(skip_many(satisfy(|c| c != '\n')))
        .skip(spaces())
        .map(|_| Tokens::COMMENT)
        .boxed()
}

#[test]
fn test_tokenize() {
    use combine::primitives::Consumed;
    let scene = r##"
LookAt 0 0 5 0 0 0 0 1 0
Camera "perspective" "float fov" [50]

Film "image" "integer xresolution" [800] "integer yresolution" [600]
    "string filename" "test-whitted.tga"

Integrator "whitted"
#Integrator "directlighting"

WorldBegin
  LightSource "distant" "point from" [0 1 5] "point to" [0 0 0]

  #Material "matte" "rgb Kd" [1.0 0.0 0.0] "float sigma" [20]
  AttributeBegin
    #Material "matte" "rgb Kd" [1.0 0.0 0.0]
    Material "plastic" "rgb Kd" [1.0 0.0 0.0] "rgb Ks" [1.0 1.0 1.0]

    Shape "sphere"
  AttributeEnd

  AttributeBegin
    Rotate -90 1 0 0
    Material "matte" "rgb Kd" [1.0 1.0 1.0]
    Shape "disk" "float radius" [20] "float height" [-1]
  AttributeEnd

  AttributeBegin
    AreaLightSource "diffuse" "rgb L" [2.0 2.0 2.0]
    Rotate 90 1 0 0
    Shape "disk" "float height" [-2] "float radius" [0.5]
  AttributeEnd
WorldEnd
        "##;
    let result = tokenize(scene);
    assert!(result.is_ok());
    let (tokens, rest) = result.unwrap();
    assert_eq!(rest, Consumed::Consumed(""));
}

#[test]
fn test_string_parser() {
    let s = "\"this is a string\"";
    let result = string_parser().parse(s);
    assert_eq!(result,
               Ok((Tokens::STR("this is a string".to_string()), "")))
}
<commit_msg>Fix typo + error handling tweaks<commit_after>use combine::{satisfy, skip_many, none_of, token, between, optional, many, many1, choice, try,
              Parser, Stream, ParseError};
use combine::char::{string, spaces, digit, char};

#[derive(Clone, Debug, PartialEq)]
pub enum Tokens {
    ACCELERATOR,
    ACTIVETRANSFORM,
    ALL,
    AREALIGHTSOURCE,
    ATTRIBUTEBEGIN,
    ATTRIBUTEEND,
    CAMERA,
    CONCATTRANSFORM,
    COORDINATESYSTEM,
    COORDSYSTRANSFORM,
    ENDTIME,
    FILM,
    IDENTITY,
    INCLUDE,
    LIGHTSOURCE,
    LOOKAT,
    MAKENAMEDMEDIUM,
    MAKENAMEDMATERIAL,
    MATERIAL,
    MEDIUMINTERFACE,
    NAMEDMATERIAL,
    OBJECTBEGIN,
    OBJECTEND,
    OBJECTINSTANCE,
    PIXELFILTER,
    REVERSEORIENTATION,
    ROTATE,
    SAMPLER,
    SCALE,
    SHAPE,
    STARTTIME,
    INTEGRATOR,
    TEXTURE,
    TRANSFORMBEGIN,
    TRANSFORMEND,
    TRANSFORMTIMES,
    TRANSFORM,
    TRANSLATE,
    WORLDBEGIN,
    WORLDEND,
    STR(String),
    NUMBER(f32),
    LBRACK,
    RBRACK,
    COMMENT,
}

pub fn tokenize<'a>(input: &'a str) -> Result<(Vec<Tokens>, &'a str), ParseError<&'a str>> {

    // parsers for keywords
    let mut parsers = vec![token_parser("Accelerator", Tokens::ACCELERATOR),
                           token_parser("ActiveTransform", Tokens::ACTIVETRANSFORM),
                           token_parser("All", Tokens::ALL),
                           token_parser("AreaLightSource", Tokens::AREALIGHTSOURCE),
                           token_parser("AttributeBegin", Tokens::ATTRIBUTEBEGIN),
                           token_parser("AttributeEnd", Tokens::ATTRIBUTEEND),
                           token_parser("Camera", Tokens::CAMERA),
                           token_parser("ConcatTransform", Tokens::CONCATTRANSFORM),
                           token_parser("CoordinateSystem", Tokens::COORDINATESYSTEM),
                           token_parser("CoordSysTransform", Tokens::COORDSYSTRANSFORM),
                           token_parser("EndTime", Tokens::ENDTIME),
                           token_parser("Film", Tokens::FILM),
                           token_parser("Identity", Tokens::IDENTITY),
                           token_parser("Include", Tokens::INCLUDE),
                           token_parser("LightSource", Tokens::LIGHTSOURCE),
                           token_parser("LookAt", Tokens::LOOKAT),
                           token_parser("MakeNamedMedium", Tokens::MAKENAMEDMEDIUM),
                           token_parser("MakeNamedMaterial", Tokens::MAKENAMEDMATERIAL),
                           token_parser("Material", Tokens::MATERIAL),
                           token_parser("MediumInterface", Tokens::MEDIUMINTERFACE),
                           token_parser("NamedMaterial", Tokens::NAMEDMATERIAL),
                           token_parser("ObjectBegin", Tokens::OBJECTBEGIN),
                           token_parser("ObjectEnd", Tokens::OBJECTEND),
                           token_parser("ObjectInstance", Tokens::OBJECTINSTANCE),
                           token_parser("PixelFilter", Tokens::PIXELFILTER),
                           token_parser("ReverseOrientation", Tokens::REVERSEORIENTATION),
                           token_parser("Rotate", Tokens::ROTATE),
                           token_parser("Sampler", Tokens::SAMPLER),
                           token_parser("Scale", Tokens::SCALE),
                           token_parser("Shape", Tokens::SHAPE),
                           token_parser("StartTime", Tokens::STARTTIME),
                           token_parser("Integrator", Tokens::INTEGRATOR),
                           token_parser("Texture", Tokens::TEXTURE),
                           token_parser("TransformBegin", Tokens::TRANSFORMBEGIN),
                           token_parser("TransformEnd", Tokens::TRANSFORMEND),
                           token_parser("TransformTimes", Tokens::TRANSFORMTIMES),
                           token_parser("Transform", Tokens::TRANSFORM),
                           token_parser("Translate", Tokens::TRANSLATE),
                           token_parser("WorldBegin", Tokens::WORLDBEGIN),
                           token_parser("WorldEnd", Tokens::WORLDEND),
                           token_parser("[", Tokens::LBRACK),
                           token_parser("]", Tokens::RBRACK)]
            .into_iter()
            .map(|parser| try(parser))
            .collect::<Vec<_>>();

    // Add parsers from num, strings, etc...
    parsers.push(try(float_parser()));
    parsers.push(try(string_parser()));
    parsers.push(try(comment_parser()));

    spaces()
        .with(many::<Vec<_>, _>(choice(parsers)))
        .parse(input)
}

fn token_parser<'a, I: Stream<Item = char> + 'a>
    (s: &'static str,
     t: Tokens)
     -> Box<Parser<Input = I, Output = Tokens> + 'a> {
    string(s).skip(spaces()).map(move |_| t.clone()).boxed()
}

fn float_parser<'a, I: Stream<Item = char> + 'a>
    ()
    -> Box<Parser<Input = I, Output = Tokens> + 'a>
{

    (optional(char('-').or(char('+'))),
     many1::<Vec<_>, _>(digit()),
     optional(char('.').with(many::<Vec<_>, _>(digit()))),
     optional(char('e').or(char('E')).with(many1::<Vec<_>, _>(digit()))))
            .skip(spaces())
            .and_then(|(sign, int_part, frac_part, mantissa)| {
                let mut buf = String::new();
                if let Some(s) = sign {
                    buf.push(s);
                }
                for c in int_part {
                    buf.push(c);
                }
                if let Some(frac) = frac_part {
                    buf.push('.');
                    for c in frac {
                        buf.push(c);
                    }
                }
                if let Some(mant) = mantissa {
                    buf.push('e');
                    for c in mant {
                        buf.push(c);
                    }
                }

                buf.parse::<f32>().map(Tokens::NUMBER)
            })
            .boxed()
}

fn string_parser<'a, I: Stream<Item = char> + 'a>
    ()
    -> Box<Parser<Input = I, Output = Tokens> + 'a>
{
    between(token('"'),
            token('"'),
            many::<Vec<_>, _>(none_of("\"".chars())))
            .skip(spaces())
            .map(|chars| {
                     let mut buf = String::new();
                     for c in chars {
                         buf.push(c);
                     }
                     Tokens::STR(buf)
                 })
            .boxed()
}

fn comment_parser<'a, I: Stream<Item = char> + 'a>
    ()
    -> Box<Parser<Input = I, Output = Tokens> + 'a>
{
    token('#')
        .and(skip_many(satisfy(|c| c != '\n')))
        .skip(spaces())
        .map(|_| Tokens::COMMENT)
        .boxed()
}

#[test]
fn test_tokenize() {
    use combine::primitives::Consumed;
    let scene = r##"
LookAt 0 0 5 0 0 0 0 1 0
Camera "perspective" "float fov" [50]

Film "image" "integer xresolution" [800] "integer yresolution" [600]
    "string filename" "test-whitted.tga"

Integrator "whitted"
#Integrator "directlighting"

WorldBegin
  LightSource "distant" "point from" [0 1 5] "point to" [0 0 0]

  #Material "matte" "rgb Kd" [1.0 0.0 0.0] "float sigma" [20]
  AttributeBegin
    #Material "matte" "rgb Kd" [1.0 0.0 0.0]
    Material "plastic" "rgb Kd" [1.0 0.0 0.0] "rgb Ks" [1.0 1.0 1.0]

    Shape "sphere"
  AttributeEnd

  AttributeBegin
    Rotate -90 1 0 0
    Material "matte" "rgb Kd" [1.0 1.0 1.0]
    Shape "disk" "float radius" [20] "float height" [-1]
  AttributeEnd

  AttributeBegin
    AreaLightSource "diffuse" "rgb L" [2.0 2.0 2.0]
    Rotate 90 1 0 0
    Shape "disk" "float height" [-2] "float radius" [0.5]
  AttributeEnd
WorldEnd
        "##;
    let result = tokenize(scene);
    assert!(result.is_ok());
    let (tokens, rest) = result.unwrap();
    assert_eq!(rest, Consumed::Consumed(""));
}

#[test]
fn test_string_parser() {
    let s = "\"this is a string\"";
    let result = string_parser().parse(s);
    assert_eq!(result,
               Ok((Tokens::STR("this is a string".to_string()), "")))
}
<|endoftext|>


<commit_before>//! Implements the rules of chess and static position evaluation
//! logic.

#![allow(dead_code)]
#![allow(unused_variables)]

pub mod board_geometry;
pub mod board;

// use notation;
use basetypes::*;
use bitsets::*;
use chess_move::*;
use self::board::{Board, IllegalBoard};

pub type Value = i16;

/// Represents an illegal possiton error.
pub struct IllegalPosition;

/// Represents a chess position.
///
/// `Position` can generate all possible moves in the current
/// position, play a selected move, and take it back. It can also
/// statically (without doing extensive tree-searching) evaluate the
/// chances of the sides, so that tree-searching algorithms can use
/// this evaluation to assign realistic game outcomes to their leaf
/// nodes. `Position` can also fabricate a "null move" that can be
/// used to aggressively prune the search tree.
pub struct Position {
    board: Board,
    halfmove_clock: u32,
    fullmove_number: u32, /* move_stack
                           * move_history (including fullmove_number?)
                           * ply
                           * hply?
                           * various hash tables
                           * first_move_index[usize; MAX_PLY]
                           * undo_move data stack */
}

impl Position {
    fn from_fen(fen: &str) -> Result<Position, IllegalBoard> {
        let parts: Vec<_> = fen.split_whitespace().collect();

        if parts.len() == 6 {
            let p = Position {
                board: try!(Board::from_fen(fen)),
                halfmove_clock: try!(parts[4].parse::<u32>().map_err(|_| IllegalBoard)),
                fullmove_number: try!(parts[5].parse::<u32>().map_err(|_| IllegalBoard)),
            };
            Ok(p)
        } else {
            Err(IllegalBoard)
        }
    }

    /// Creates a new instance.
    ///
    /// `fen` should be the Forsyth–Edwards Notation of a legal
    /// starting position. `moves` should be an iterator over all the
    /// moves that were played from that position.
    pub fn from_history(fen: &str,
                        moves: &mut Iterator<Item = &str>)
                        -> Result<Position, IllegalPosition> {
        Err(IllegalPosition)
    }

    /// Evaluates a final position.
    ///
    /// In final positions this method is guaranteed to return the
    /// correct value of the position (white wins, black wins,
    /// draw). A position is guaranteed to be final if
    /// `generate_moves` method generates no legal moves (it may
    /// generate some pseudo-legal moves, but if none of them is
    /// legal, then the position is guaranteed to be a final
    /// position).
    pub fn evaluate_final(&self) -> Value {
        0
    }

    /// Statically evaluates the position.
    ///
    /// This method considers only static material and positional
    /// properties of the position. If the position is dynamic, with
    /// pending tactical threats, this function will return a grossly
    /// incorrect evaluation. Therefore, if should be relied upon only
    /// for reasonably "quiet" positions.
    ///
    /// `lower_bound` and `upper_bound` together give the interval
    /// within which an as precise as possible evaluation is
    /// required. If during the calculation it is determined that the
    /// evaluation is outside this interval, this method may return
    /// any value outside of the interval (including the bounds), but
    /// always staying on the correct side of the interval.
    pub fn evaluate_static(&self, lower_bound: Value, upper_bound: Value) -> Value {
        // TODO: Implement a real evaluation.

        const VALUE: [Value; 6] = [10000, 975, 500, 325, 325, 100];
        let piece_type = self.board.piece_type();
        let color = self.board.color();
        let us = self.board.to_move();
        let them = 1 ^ us;
        let mut result = 0;
        for piece in QUEEN..NO_PIECE {
            result += VALUE[piece] *
                      (pop_count(piece_type[piece] & color[us]) as i16 -
                       pop_count(piece_type[piece] & color[them]) as i16);
        }
        result
    }

    /// Performs "quiescence search" and returns an evaluation.
    ///
    /// The purpose of the "quiescence search" is to only evaluate
    /// "quiet" positions (positions where there are no winning
    /// tactical moves to be made).
    ///
    /// `lower_bound` and `upper_bound` together give the interval
    /// within which an as precise as possible evaluation is
    /// required. If during the calculation it is determined that the
    /// evaluation is outside this interval, this method may return
    /// any value outside of the interval (including the bounds), but
    /// always staying on the correct side of the interval.
    ///
    /// TODO: Add more details for the algorithm used.
    pub fn evaluate(&self, lower_bound: Value, upper_bound: Value) -> Value {
        0
    }

    /// Returns an almost unique hash value for the position.
    #[inline(always)]
    pub fn hash(&self) -> u64 {
        0
    }

    /// Plays a move on the board.
    ///
    /// It verifies if the move is legal. If the move is legal, the
    /// board is updated and `true` is returned. If the move is
    /// illegal, `false` is returned without updating the board. The
    /// move passed to this method **must** have been generated by
    /// `generate_moves` or `null_move` methods for the current
    /// position on the board.
    ///
    /// Moves generated by the `null_move` method are exceptions. For
    /// them `do_move(m)` will return `false` if and only if the king
    /// is in check.
    #[inline]
    pub fn do_move(&self, m: Move) -> bool {
        false
    }

    /// Takes back the last played move.
    #[inline]
    pub fn undo_move(&self) {}

    /// Generates pseudo-legal moves and write them to `move_stack`.
    ///
    /// If all of the moves generated by this methods are illegal
    /// (this means that `do_move(m)` returns `false` for all of
    /// them), then it is guaranteed that the position is final, and
    /// `evaluate_final()` will return its correct value.
    #[inline]
    pub fn generate_moves(&self, move_stack: &mut Vec<Move>) {}

    /// Returns a null move.
    ///
    /// "Null move" is an illegal pseudo-move that changes only the
    /// side to move. For the move generated by this method,
    /// `do_move(m)` will return `false` if and only if the king is in
    /// check.
    #[inline]
    pub fn null_move(&self) -> Move {
        use castling_rights::CastlingRights;
        Move::new(WHITE,
                  0,
                  MOVE_NORMAL,
                  KING,
                  0,
                  0,
                  NO_PIECE,
                  8, // no en-passant file
                  CastlingRights::new(),
                  0)
    }
}

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

    #[test]
    fn test_fen_parsing() {
        assert!(Position::from_fen("nbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1")
                    .is_err());
        assert!(Position::from_fen("rnbqkbnr1/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1")
                    .is_err());
        assert!(Position::from_fen("rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBN b KQkq e3 0 1")
                    .is_err());
        assert!(Position::from_fen("rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR/ b KQkq e3 0 1")
                    .is_err());
        assert!(Position::from_fen("rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNRR b KQkq e3 0 1")
                    .is_err());
        assert!(Position::from_fen("rnbqkbnr/pppppppp/8/8/4P3/8/PPP01PPP/RNBQKBNR b KQkq e3 0 1")
                    .is_err());
        assert!(Position::from_fen("rnbqkbnr/pppppppp/8/8/4P3/8/PPP91PPP/RNBQKBNR b KQkq e3 0 1")
                    .is_err());
        assert!(Position::from_fen("rnbqkbnr/pppppppp/8/8/4P3/8/PPP*1PPP/RNBQKBNR b KQkq e3 0 1")
                    .is_err());
        assert!(Position::from_fen("rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 * 1")
                    .is_err());
        assert!(Position::from_fen("rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 *")
                    .is_err());
        assert!(Position::from_fen("rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b - e3 0 1")
                    .is_ok());
        assert!(Position::from_fen("rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1")
                    .is_ok());
        assert!(Position::from_fen("8/8/8/8/8/8/8/8 w - - 0 1").is_err());
        assert!(Position::from_fen("8/8/8/8/8/8/8/7K w - - 0 1").is_err());
        assert!(Position::from_fen("k7/8/8/8/8/8/8/7K w - - 0 1").is_ok());
        assert!(Position::from_fen("k7/8/8/8/8/8/8/6KK w - - 0 1").is_err());
        assert!(Position::from_fen("k7/pppppppp/p7/8/8/8/8/7K w - - 0 1").is_err());
        assert!(Position::from_fen("k7/8/8/8/8/7P/PPPPPPPP/7K w - - 0 1").is_err());
        assert!(Position::from_fen("k7/pppppppp/8/8/8/8/PPPPPPPP/7K w - - 0 1").is_ok());
        assert!(Position::from_fen("k7/1P6/8/8/8/8/8/7K w - - 0 1").is_err());
        assert!(Position::from_fen("k7/1B6/8/8/8/8/8/7K w - - 0 1").is_err());
        assert!(Position::from_fen("k7/1N6/8/8/8/8/8/7K w - - 0 1").is_ok());
        assert!(Position::from_fen("k3P3/8/8/8/8/8/8/7K w - - 0 1").is_err());
        assert!(Position::from_fen("k3p3/8/8/8/8/8/8/7K w - - 0 1").is_err());
        assert!(Position::from_fen("k7/8/8/8/8/8/8/pP5K w - - 0 1").is_err());
        assert!(Position::from_fen("r3k2r/8/8/8/8/8/8/R3K2R w KQkq - 0 1").is_ok());
        assert!(Position::from_fen("r3k2r/8/8/8/8/8/8/R3K2B w KQkq - 0 1").is_err());
        assert!(Position::from_fen("r3k2r/8/8/8/8/8/8/R3K3 w KQkq - 0 1").is_err());
        assert!(Position::from_fen("r3k2r/8/8/8/8/8/8/R3K3 w KQkq - 0 1").is_err());
        assert!(Position::from_fen("r3k2r/8/8/8/8/8/8/R3K3 w Qkq - 0 1").is_ok());
        assert!(Position::from_fen("r2k3r/8/8/8/8/8/8/R3K3 w Qkq - 0 1").is_err());
        assert!(Position::from_fen("r2k3r/8/8/8/8/8/8/R3K3 w Qk - 0 1").is_err());
        assert!(Position::from_fen("r2k3r/8/8/8/8/8/8/R3K3 w Q - 0 1").is_ok());
        assert!(Position::from_fen("k7/8/8/8/7P/8/8/7K w - h3 0 1").is_err());
        assert!(Position::from_fen("k7/8/8/8/7P/8/8/7K b - h3 0 1").is_ok());
        assert!(Position::from_fen("k7/8/8/7P/8/8/8/7K b - h4 0 1").is_err());
        assert!(Position::from_fen("k7/8/8/8/7P/7P/8/7K b - h3 0 1").is_err());
        assert!(Position::from_fen("k7/8/8/8/7P/8/7P/7K b - h3 0 1").is_err());
        assert!(Position::from_fen("k7/8/8/8/6P1/7P/8/7K b - h3 0 1").is_err());
        assert!(Position::from_fen("8/8/8/6k1/7P/8/8/7K b - h3 0 1").is_ok());
        assert!(Position::from_fen("8/8/8/6k1/7P/8/8/6RK b - h3 0 1").is_err());
        assert!(Position::from_fen("8/8/8/6k1/3P4/8/8/2B4K b - d3 0 1").is_ok());
        assert!(Position::from_fen("8/8/8/6k1/7P/4B3/8/7K b - h3 0 1").is_err());
    }

    #[test]
    fn test_evaluate_static_parsing() {
        assert_eq!(Position::from_fen("krq5/p7/8/8/8/8/8/KRQ5 w - - 0 1")
                       .ok().unwrap()
                       .evaluate_static(-1000, 1000),
                   -100);
    }
}
<commit_msg>more work on positoin.rs<commit_after>//! Implements the rules of chess and static position evaluation
//! logic.

#![allow(dead_code)]
#![allow(unused_variables)]

pub mod board_geometry;
pub mod board;

// use notation;
use std::cell::RefCell;
use basetypes::*;
use bitsets::*;
use chess_move::*;
use self::board::{Board, IllegalBoard};

const MOVE_STACK_CAPACITY: usize = 4096;

pub type Value = i16;

/// Represents an illegal possiton error.
pub struct IllegalPosition;

/// Represents a chess position.
///
/// `Position` can generate all possible moves in the current
/// position, play a selected move, and take it back. It can also
/// statically (without doing extensive tree-searching) evaluate the
/// chances of the sides, so that tree-searching algorithms can use
/// this evaluation to assign realistic game outcomes to their leaf
/// nodes. `Position` can also fabricate a "null move" that can be
/// used to aggressively prune the search tree.
pub struct Position {
    board: RefCell<Board>,
    halfmove_clock: u32,
    fullmove_number: u32, /* move_stack
                           * move_history (including fullmove_number?)
                           * ply
                           * hply?
                           * various hash tables
                           * first_move_index[usize; MAX_PLY]
                           * undo_move data stack */
}

impl Position {
    fn from_fen(fen: &str) -> Result<Position, IllegalBoard> {
        let parts: Vec<_> = fen.split_whitespace().collect();

        if parts.len() == 6 {
            let p = Position {
                board: RefCell::new(try!(Board::from_fen(fen))),
                halfmove_clock: try!(parts[4].parse::<u32>().map_err(|_| IllegalBoard)),
                fullmove_number: try!(parts[5].parse::<u32>().map_err(|_| IllegalBoard)),
            };
            Ok(p)
        } else {
            Err(IllegalBoard)
        }
    }

    /// Creates a new instance.
    ///
    /// `fen` should be the Forsyth–Edwards Notation of a legal
    /// starting position. `moves` should be an iterator over all the
    /// moves that were played from that position.
    pub fn from_history(fen: &str,
                        moves: &mut Iterator<Item = &str>)
                        -> Result<Position, IllegalPosition> {
        Err(IllegalPosition)
    }

    /// Evaluates a final position.
    ///
    /// In final positions this method is guaranteed to return the
    /// correct value of the position (white wins, black wins,
    /// draw). A position is guaranteed to be final if
    /// `generate_moves` method generates no legal moves (it may
    /// generate some pseudo-legal moves, but if none of them is
    /// legal, then the position is guaranteed to be a final
    /// position).
    pub fn evaluate_final(&self) -> Value {
        0
    }

    /// Statically evaluates the position.
    ///
    /// This method considers only static material and positional
    /// properties of the position. If the position is dynamic, with
    /// pending tactical threats, this function will return a grossly
    /// incorrect evaluation. Therefore, if should be relied upon only
    /// for reasonably "quiet" positions.
    ///
    /// `lower_bound` and `upper_bound` together give the interval
    /// within which an as precise as possible evaluation is
    /// required. If during the calculation it is determined that the
    /// evaluation is outside this interval, this method may return
    /// any value outside of the interval (including the bounds), but
    /// always staying on the correct side of the interval.
    pub fn evaluate_static(&self, lower_bound: Value, upper_bound: Value) -> Value {
        // TODO: Implement a real evaluation.

        const VALUE: [Value; 6] = [10000, 975, 500, 325, 325, 100];
        let board = self.board.borrow();
        let piece_type = board.piece_type();
        let color = board.color();
        let us = board.to_move();
        let them = 1 ^ us;
        let mut result = 0;
        for piece in QUEEN..NO_PIECE {
            result += VALUE[piece] *
                      (pop_count(piece_type[piece] & color[us]) as i16 -
                       pop_count(piece_type[piece] & color[them]) as i16);
        }
        result
    }

    /// Performs "quiescence search" and returns an evaluation.
    ///
    /// The purpose of the "quiescence search" is to only evaluate
    /// "quiet" positions (positions where there are no winning
    /// tactical moves to be made).
    ///
    /// `lower_bound` and `upper_bound` together give the interval
    /// within which an as precise as possible evaluation is
    /// required. If during the calculation it is determined that the
    /// evaluation is outside this interval, this method may return
    /// any value outside of the interval (including the bounds), but
    /// always staying on the correct side of the interval.
    ///
    /// TODO: Add more details for the algorithm used.
    pub fn evaluate(&self, lower_bound: Value, upper_bound: Value) -> Value {
        thread_local!(
            static MOVE_STACK: RefCell<Vec<Move>> = RefCell::new(
                Vec::with_capacity(MOVE_STACK_CAPACITY))
        );
        MOVE_STACK.with(|ref_cell| {
            let mut board = self.board.borrow_mut();
            let mut move_stack = ref_cell.borrow_mut();
            move_stack.clear();
            self.qsearch(lower_bound, upper_bound, &mut move_stack, &mut board)
        })
    }

    /// Returns an almost unique hash value for the position.
    #[inline(always)]
    pub fn hash(&self) -> u64 {
        0
    }

    /// Plays a move on the board.
    ///
    /// It verifies if the move is legal. If the move is legal, the
    /// board is updated and `true` is returned. If the move is
    /// illegal, `false` is returned without updating the board. The
    /// move passed to this method **must** have been generated by
    /// `generate_moves` or `null_move` methods for the current
    /// position on the board.
    ///
    /// Moves generated by the `null_move` method are exceptions. For
    /// them `do_move(m)` will return `false` if and only if the king
    /// is in check.
    #[inline]
    pub fn do_move(&self, m: Move) -> bool {
        false
    }

    /// Takes back the last played move.
    #[inline]
    pub fn undo_move(&self) {}

    /// Generates pseudo-legal moves and write them to `move_stack`.
    ///
    /// If all of the moves generated by this methods are illegal
    /// (this means that `do_move(m)` returns `false` for all of
    /// them), then it is guaranteed that the position is final, and
    /// `evaluate_final()` will return its correct value.
    #[inline]
    pub fn generate_moves(&self, move_stack: &mut Vec<Move>) {}

    /// Returns a null move.
    ///
    /// "Null move" is an illegal pseudo-move that changes only the
    /// side to move. For the move generated by this method,
    /// `do_move(m)` will return `false` if and only if the king is in
    /// check.
    #[inline]
    pub fn null_move(&self) -> Move {
        use castling_rights::CastlingRights;
        Move::new(WHITE,
                  0,
                  MOVE_NORMAL,
                  KING,
                  0,
                  0,
                  NO_PIECE,
                  8, // no en-passant file
                  CastlingRights::new(),
                  0)
    }

    fn qsearch(&self,
               mut lower_bound: Value,
               upper_bound: Value,
               move_stack: &mut Vec<Move>,
               board: &mut Board)
               -> Value {
        // At the beginning of quiescence, the position's evaluation
        // is used to establish a lower bound on the score
        // (`stand_pat`). This is theoretically sound because we can
        // usually assume that there is at least one move that can
        // either match or beat the lower bound.
        let stand_pat = self.evaluate_static(lower_bound, upper_bound);

        if stand_pat > upper_bound {
            return upper_bound;  // fail-high
        }

        if lower_bound < stand_pat {
            // We assume that even if none of the capturing moves can
            // improve over the stand pat, there will we at least one
            // "quiet" move that will at least preserve the stand pat
            // value.
            lower_bound = stand_pat;
        }

        let first_move = move_stack.len();
        // board.generate_moves(false, move_stack);
        for m in &move_stack[first_move..] {
            if board.do_move(*m) {
                // let value = self.qsearch(-upper_bound, -lower_bound, move_stack, board);
                board.undo_move(*m);
            }
        }
        0
    }
}

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

    #[test]
    fn test_fen_parsing() {
        assert!(Position::from_fen("nbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1")
                    .is_err());
        assert!(Position::from_fen("rnbqkbnr1/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1")
                    .is_err());
        assert!(Position::from_fen("rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBN b KQkq e3 0 1")
                    .is_err());
        assert!(Position::from_fen("rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR/ b KQkq e3 0 1")
                    .is_err());
        assert!(Position::from_fen("rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNRR b KQkq e3 0 1")
                    .is_err());
        assert!(Position::from_fen("rnbqkbnr/pppppppp/8/8/4P3/8/PPP01PPP/RNBQKBNR b KQkq e3 0 1")
                    .is_err());
        assert!(Position::from_fen("rnbqkbnr/pppppppp/8/8/4P3/8/PPP91PPP/RNBQKBNR b KQkq e3 0 1")
                    .is_err());
        assert!(Position::from_fen("rnbqkbnr/pppppppp/8/8/4P3/8/PPP*1PPP/RNBQKBNR b KQkq e3 0 1")
                    .is_err());
        assert!(Position::from_fen("rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 * 1")
                    .is_err());
        assert!(Position::from_fen("rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 *")
                    .is_err());
        assert!(Position::from_fen("rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b - e3 0 1")
                    .is_ok());
        assert!(Position::from_fen("rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1")
                    .is_ok());
        assert!(Position::from_fen("8/8/8/8/8/8/8/8 w - - 0 1").is_err());
        assert!(Position::from_fen("8/8/8/8/8/8/8/7K w - - 0 1").is_err());
        assert!(Position::from_fen("k7/8/8/8/8/8/8/7K w - - 0 1").is_ok());
        assert!(Position::from_fen("k7/8/8/8/8/8/8/6KK w - - 0 1").is_err());
        assert!(Position::from_fen("k7/pppppppp/p7/8/8/8/8/7K w - - 0 1").is_err());
        assert!(Position::from_fen("k7/8/8/8/8/7P/PPPPPPPP/7K w - - 0 1").is_err());
        assert!(Position::from_fen("k7/pppppppp/8/8/8/8/PPPPPPPP/7K w - - 0 1").is_ok());
        assert!(Position::from_fen("k7/1P6/8/8/8/8/8/7K w - - 0 1").is_err());
        assert!(Position::from_fen("k7/1B6/8/8/8/8/8/7K w - - 0 1").is_err());
        assert!(Position::from_fen("k7/1N6/8/8/8/8/8/7K w - - 0 1").is_ok());
        assert!(Position::from_fen("k3P3/8/8/8/8/8/8/7K w - - 0 1").is_err());
        assert!(Position::from_fen("k3p3/8/8/8/8/8/8/7K w - - 0 1").is_err());
        assert!(Position::from_fen("k7/8/8/8/8/8/8/pP5K w - - 0 1").is_err());
        assert!(Position::from_fen("r3k2r/8/8/8/8/8/8/R3K2R w KQkq - 0 1").is_ok());
        assert!(Position::from_fen("r3k2r/8/8/8/8/8/8/R3K2B w KQkq - 0 1").is_err());
        assert!(Position::from_fen("r3k2r/8/8/8/8/8/8/R3K3 w KQkq - 0 1").is_err());
        assert!(Position::from_fen("r3k2r/8/8/8/8/8/8/R3K3 w KQkq - 0 1").is_err());
        assert!(Position::from_fen("r3k2r/8/8/8/8/8/8/R3K3 w Qkq - 0 1").is_ok());
        assert!(Position::from_fen("r2k3r/8/8/8/8/8/8/R3K3 w Qkq - 0 1").is_err());
        assert!(Position::from_fen("r2k3r/8/8/8/8/8/8/R3K3 w Qk - 0 1").is_err());
        assert!(Position::from_fen("r2k3r/8/8/8/8/8/8/R3K3 w Q - 0 1").is_ok());
        assert!(Position::from_fen("k7/8/8/8/7P/8/8/7K w - h3 0 1").is_err());
        assert!(Position::from_fen("k7/8/8/8/7P/8/8/7K b - h3 0 1").is_ok());
        assert!(Position::from_fen("k7/8/8/7P/8/8/8/7K b - h4 0 1").is_err());
        assert!(Position::from_fen("k7/8/8/8/7P/7P/8/7K b - h3 0 1").is_err());
        assert!(Position::from_fen("k7/8/8/8/7P/8/7P/7K b - h3 0 1").is_err());
        assert!(Position::from_fen("k7/8/8/8/6P1/7P/8/7K b - h3 0 1").is_err());
        assert!(Position::from_fen("8/8/8/6k1/7P/8/8/7K b - h3 0 1").is_ok());
        assert!(Position::from_fen("8/8/8/6k1/7P/8/8/6RK b - h3 0 1").is_err());
        assert!(Position::from_fen("8/8/8/6k1/3P4/8/8/2B4K b - d3 0 1").is_ok());
        assert!(Position::from_fen("8/8/8/6k1/7P/4B3/8/7K b - h3 0 1").is_err());
    }

    #[test]
    fn test_evaluate_static_parsing() {
        assert_eq!(Position::from_fen("krq5/p7/8/8/8/8/8/KRQ5 w - - 0 1")
                       .ok()
                       .unwrap()
                       .evaluate_static(-1000, 1000),
                   -100);
    }
}
<|endoftext|>