<commit_before>use std::{
    collections::HashSet,
    io,
    net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
    sync::Arc,
    time::Duration,
};

use futures::future;
use log::{debug, error, info, warn};
use tokio::net::TcpListener;
use trust_dns_proto::{
    op::{header::MessageType, response_code::ResponseCode, Message, OpCode, Query},
    rr::{DNSClass, Name, RData, RecordType},
};

use crate::{
    acl::AccessControl,
    config::ConfigType,
    context::SharedContext,
    relay::{
        loadbalancing::server::{PlainPingBalancer, ServerType},
        sys::create_udp_socket,
        utils::try_timeout,
    },
};

pub mod upstream;

fn should_forward_by_ptr_name(acl: &AccessControl, name: &Name) -> bool {
    let mut iter = name.iter().rev();
    let mut next = || match iter.next() {
        Some(label) => std::str::from_utf8(label).unwrap_or("*"),
        None => "0",    // zero fill the missing labels
    };
    if !"arpa".eq_ignore_ascii_case(next()) {
        return acl.is_default_in_proxy_list();
    }
    match &next().to_ascii_lowercase()[..] {
        "in-addr" => {
            let mut octets: [u8; 4] = [0; 4];
            for octet in octets.iter_mut() {
                match next().parse() {
                    Ok(result) => *octet = result,
                    Err(_) => return acl.is_default_in_proxy_list(),
                }
            }
            acl.check_ip_in_proxy_list(&IpAddr::V4(Ipv4Addr::new(octets[0], octets[1], octets[2], octets[3])))
        }
        "ip6" => {
            let mut segments: [u16; 8] = [0; 8];
            for segment in segments.iter_mut() {
                match u16::from_str_radix(&[next(), next(), next(), next()].concat(), 16) {
                    Ok(result) => *segment = result,
                    Err(_) => return acl.is_default_in_proxy_list(),
                }
            }
            acl.check_ip_in_proxy_list(&IpAddr::V6(Ipv6Addr::new(
                segments[0], segments[1], segments[2], segments[3], segments[4], segments[5], segments[6], segments[7]
            )))
        }
        _ => acl.is_default_in_proxy_list(),
    }
}

fn check_name_in_proxy_list(acl: &AccessControl, name: &Name) -> Option<bool> {
    if name.is_fqdn() {
        // remove the last dot from FQDN
        let mut name = name.to_ascii();
        name.pop();
        acl.check_host_in_proxy_list(&name)
    } else {
        // unconditionally use default for PQDNs
        Some(acl.is_default_in_proxy_list())
    }
}

/// given the query, determine whether remote/local query should be used, or inconclusive
fn should_forward_by_query(acl: &Option<AccessControl>, query: &Query) -> Option<bool> {
    if let Some(acl) = acl {
        if query.query_class() != DNSClass::IN {
            // unconditionally use default for all non-IN queries
            Some(acl.is_default_in_proxy_list())
        } else if query.query_type() == RecordType::PTR {
            Some(should_forward_by_ptr_name(acl, query.name()))
        } else {
            let result = check_name_in_proxy_list(acl, query.name());
            if result == None && acl.is_ip_empty() && acl.is_host_empty() {
                Some(acl.is_default_in_proxy_list())
            } else {
                result
            }
        }
    } else {
        Some(true)
    }
}

/// given the local response, determine whether remote response should be used instead
fn should_forward_by_response(
    acl: &Option<AccessControl>,
    local_response: &io::Result<Message>,
    query: &Query,
) -> bool {
    if let Some(acl) = acl {
        if let Ok(ref local_response) = local_response {
            let mut names = HashSet::new();
            names.insert(query.name());
            macro_rules! examine_record {
                ($rec:ident, $is_answer:expr) => {
                    if let RData::CNAME(ref name) = $rec.rdata() {
                        if $is_answer {
                            if let Some(value) = check_name_in_proxy_list(acl, name) {
                                return value;
                            }
                        }
                        names.insert(name);
                        continue;
                    }
                    if $is_answer && !query.query_type().is_any() && $rec.record_type() != query.query_type() {
                        warn!("local DNS response has inconsistent answer type {} for query {}",
                              $rec.record_type(), query);
                        return true;
                    }
                    let forward = match $rec.rdata() {
                        RData::A(ref ip) => acl.check_ip_in_proxy_list(&IpAddr::V4(*ip)),
                        RData::AAAA(ref ip) => acl.check_ip_in_proxy_list(&IpAddr::V6(*ip)),
                        RData::PTR(_) => unreachable!(),
                        _ => acl.is_default_in_proxy_list(),
                    };
                    if !forward {
                        return false;
                    }
                };
            }
            for rec in local_response.answers() {
                if !names.contains(rec.name()) {
                    warn!("local DNS response contains unexpected name {} for query {}", rec.name(), query);
                    return true;
                }
                examine_record!(rec, true);
            }
            for rec in local_response.additionals() {
                if names.contains(rec.name()) {
                    examine_record!(rec, false);
                }
            }
        }
        true
    } else {
        unreachable!()
    }
}

struct DnsRelay<Remote: upstream::Upstream> {
    context: SharedContext,
    remote_upstream: Remote,
}

impl<Remote: upstream::Upstream> DnsRelay<Remote> {
    async fn acl_lookup(&self, query: &Query) -> (io::Result<Message>, bool) {
        let acl = self.context.acl();
        let local = self.context.local_dns();
        let remote = &self.remote_upstream;
        // Start querying name servers
        debug!(
            "attempting lookup of {:?} {} with ns {:?} and {:?}",
            query.query_type(), query.name(), local, remote
        );

        let remote_response_fut = try_timeout(remote.lookup(query), Some(Duration::new(3, 0)));
        let local_response_fut = try_timeout(local.lookup(query), Some(Duration::new(3, 0)));

        match should_forward_by_query(acl, query) {
            Some(true) => {
                let remote_response = remote_response_fut.await;
                debug!("pick remote response (query): {:?}", remote_response);
                return (remote_response, true);
            }
            Some(false) => {
                let local_response = local_response_fut.await;
                debug!("pick local response (query): {:?}", local_response);
                return (local_response, false);
            }
            None => (),
        }

        let decider = async {
            let local_response = local_response_fut.await;
            if should_forward_by_response(acl, &local_response, query) {
                None
            } else {
                Some(local_response)
            }
        };
        tokio::pin!(remote_response_fut, decider);
        let mut use_remote = false;
        let mut remote_response = None;
        loop {
            tokio::select! {
                response = &mut remote_response_fut, if remote_response.is_none() => {
                    if use_remote {
                        debug!("pick remote response (response): {:?}", response);
                        return (response, true);
                    } else {
                        remote_response = Some(response);
                    }
                }
                decision = &mut decider, if !use_remote => {
                    if let Some(local_response) = decision {
                        debug!("pick local response (response): {:?}", local_response);
                        return (local_response, false);
                    } else if let Some(remote_response) = remote_response {
                        debug!("pick remote response (response): {:?}", remote_response);
                        return (remote_response, true);
                    } else {
                        use_remote = true;
                    }
                }
                else => unreachable!(),
            }
        }
    }

    async fn resolve(&self, request: Message) -> Message {
        let mut message = Message::new();
        message.set_id(request.id());
        message.set_recursion_desired(true);
        message.set_recursion_available(true);
        message.set_message_type(MessageType::Response);
        if !request.recursion_desired() {
            message.set_recursion_desired(false);
            message.set_response_code(ResponseCode::NotImp);
        } else if request.op_code() != OpCode::Query || request.message_type() != MessageType::Query {
            message.set_response_code(ResponseCode::NotImp);
        } else if request.query_count() > 0 {
            let (r, forward) = self.acl_lookup(&request.queries()[0]).await;
            if let Ok(result) = r {
                for rec in result.answers() {
                    debug!("dns answer: {:?}", rec);
                    match rec.rdata() {
                        RData::A(ref ip) => self.context.add_to_reverse_lookup_cache(&IpAddr::V4(*ip), forward),
                        RData::AAAA(ref ip) => self.context.add_to_reverse_lookup_cache(&IpAddr::V6(*ip), forward),
                        _ => (),
                    }
                }
                message = result;
                message.set_id(request.id());
            } else {
                message.set_response_code(ResponseCode::ServFail);
            }
        }
        message
    }
}

async fn run_tcp<Remote: upstream::Upstream + Send + Sync + 'static>(
    relay: Arc<DnsRelay<Remote>>,
    bind_addr: SocketAddr
) -> io::Result<()> {
    let mut listener = TcpListener::bind(&bind_addr).await?;

    let actual_local_addr = listener.local_addr()?;
    info!("shadowsocks DNS relay listening on tcp:{}", actual_local_addr);

    loop {
        let (mut stream, src) = listener.accept().await?;
        let relay = relay.clone();
        tokio::spawn(async move {
            match upstream::read_message(&mut stream).await {
                Ok(request) => {
                    debug!("received src: {}, query: {:?}", src, request);
                    let message = relay.resolve(request).await;
                    debug!("DNS src: {}, final response: {:?}", src, message);
                    if let Err(err) = upstream::write_message(&mut stream, &message).await {
                        error!("failed to write DNS response, error: {}", err);
                    }
                }
                Err(e) => error!("failed to parse TCP query message from {}, error: {:?}", src, e),
            }
        });
    }
}

async fn run_udp<Remote: upstream::Upstream + Send + Sync + 'static>(
    relay: Arc<DnsRelay<Remote>>,
    bind_addr: SocketAddr
) -> io::Result<()> {
    let socket = create_udp_socket(&bind_addr).await?;

    let actual_local_addr = socket.local_addr()?;
    info!("shadowsocks DNS relay listening on udp:{}", actual_local_addr);

    let (mut rx, tx) = socket.split();
    let tx = Arc::new(tokio::sync::Mutex::new(tx));

    loop {
        let mut req_buffer: [u8; 512] = [0; 512];
        let (_, src) = match rx.recv_from(&mut req_buffer).await {
            Ok(x) => x,
            Err(e) => {
                error!("DNS relay read from UDP socket error: {}", e);
                continue;
            }
        };

        let request = match Message::from_vec(&req_buffer) {
            Ok(x) => x,
            Err(e) => {
                error!("failed to parse UDP query message, error: {:?}", e);
                continue;
            }
        };

        debug!("received src: {}, query: {:?}", src, request);

        let relay = relay.clone();
        let tx = tx.clone();

        tokio::spawn(async move {
            let message = relay.resolve(request).await;
            debug!("DNS src: {}, final response: {:?}", src, message);

            match message.to_vec() {
                Err(err) => {
                    error!("failed to serialize message, error: {}", err);
                }
                Ok(res_buffer) => {
                    if let Err(err) = tx.lock().await.send_to(&res_buffer, &src).await {
                        error!("DNS send back UDP error: {}", err);
                    }
                }
            }
        });
    }
}

/// Start a DNS relay local server
pub async fn run(context: SharedContext) -> io::Result<()> {
    let local_addr = match context.config().config_type {
        ConfigType::DnsLocal => {
            // Standalone server
            context.config().local_addr.as_ref().expect("local config")
        }
        c if c.is_local() => {
            // Integrated mode
            context.config().dns_local_addr.as_ref().expect("dns relay addr")
        }
        _ => {
            panic!("ConfigType must be DnsLocal");
        }
    };
    let bind_addr = local_addr.bind_addr(&context).await?;

    let config = context.config();
    // FIXME: We use TCP to send remote queries by default, which should be configuable.
    let balancer = PlainPingBalancer::new(context.clone(), ServerType::Tcp).await;
    let relay = Arc::new(DnsRelay {
        context: context.clone(),
        remote_upstream: upstream::ProxyTcpUpstream {
            context: context.clone(),
            svr_cfg: move || balancer.pick_server().server_config().clone(),
            ns: config.remote_dns_addr.clone().expect("remote query DNS address"),
        }
    });

    future::select(
        tokio::spawn(run_tcp(relay.clone(), bind_addr)),
        tokio::spawn(run_udp(relay, bind_addr)),
    ).await;
    Ok(())
}
<commit_msg>Support MX and NS records<commit_after>use std::{
    collections::HashSet,
    io,
    net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
    sync::Arc,
    time::Duration,
};

use futures::future;
use log::{debug, error, info, warn};
use tokio::net::TcpListener;
use trust_dns_proto::{
    op::{header::MessageType, response_code::ResponseCode, Message, OpCode, Query},
    rr::{DNSClass, Name, RData, RecordType},
};

use crate::{
    acl::AccessControl,
    config::ConfigType,
    context::SharedContext,
    relay::{
        loadbalancing::server::{PlainPingBalancer, ServerType},
        sys::create_udp_socket,
        utils::try_timeout,
    },
};

pub mod upstream;

fn should_forward_by_ptr_name(acl: &AccessControl, name: &Name) -> bool {
    let mut iter = name.iter().rev();
    let mut next = || match iter.next() {
        Some(label) => std::str::from_utf8(label).unwrap_or("*"),
        None => "0",    // zero fill the missing labels
    };
    if !"arpa".eq_ignore_ascii_case(next()) {
        return acl.is_default_in_proxy_list();
    }
    match &next().to_ascii_lowercase()[..] {
        "in-addr" => {
            let mut octets: [u8; 4] = [0; 4];
            for octet in octets.iter_mut() {
                match next().parse() {
                    Ok(result) => *octet = result,
                    Err(_) => return acl.is_default_in_proxy_list(),
                }
            }
            acl.check_ip_in_proxy_list(&IpAddr::V4(Ipv4Addr::new(octets[0], octets[1], octets[2], octets[3])))
        }
        "ip6" => {
            let mut segments: [u16; 8] = [0; 8];
            for segment in segments.iter_mut() {
                match u16::from_str_radix(&[next(), next(), next(), next()].concat(), 16) {
                    Ok(result) => *segment = result,
                    Err(_) => return acl.is_default_in_proxy_list(),
                }
            }
            acl.check_ip_in_proxy_list(&IpAddr::V6(Ipv6Addr::new(
                segments[0], segments[1], segments[2], segments[3], segments[4], segments[5], segments[6], segments[7]
            )))
        }
        _ => acl.is_default_in_proxy_list(),
    }
}

fn check_name_in_proxy_list(acl: &AccessControl, name: &Name) -> Option<bool> {
    if name.is_fqdn() {
        // remove the last dot from FQDN
        let mut name = name.to_ascii();
        name.pop();
        acl.check_host_in_proxy_list(&name)
    } else {
        // unconditionally use default for PQDNs
        Some(acl.is_default_in_proxy_list())
    }
}

/// given the query, determine whether remote/local query should be used, or inconclusive
fn should_forward_by_query(acl: &Option<AccessControl>, query: &Query) -> Option<bool> {
    if let Some(acl) = acl {
        if query.query_class() != DNSClass::IN {
            // unconditionally use default for all non-IN queries
            Some(acl.is_default_in_proxy_list())
        } else if query.query_type() == RecordType::PTR {
            Some(should_forward_by_ptr_name(acl, query.name()))
        } else {
            let result = check_name_in_proxy_list(acl, query.name());
            if result == None && acl.is_ip_empty() && acl.is_host_empty() {
                Some(acl.is_default_in_proxy_list())
            } else {
                result
            }
        }
    } else {
        Some(true)
    }
}

/// given the local response, determine whether remote response should be used instead
fn should_forward_by_response(
    acl: &Option<AccessControl>,
    local_response: &io::Result<Message>,
    query: &Query,
) -> bool {
    if let Some(acl) = acl {
        if let Ok(ref local_response) = local_response {
            let mut names = HashSet::new();
            names.insert(query.name());
            macro_rules! examine_name {
                ($name:expr, $is_answer:expr) => {{
                    names.insert($name);
                    if $is_answer {
                        if let Some(value) = check_name_in_proxy_list(acl, $name) {
                            value
                        } else {
                            acl.is_default_in_proxy_list()
                        }
                    } else {
                        acl.is_default_in_proxy_list()
                    }
                }}
            }
            macro_rules! examine_record {
                ($rec:ident, $is_answer:expr) => {
                    if let RData::CNAME(ref name) = $rec.rdata() {
                        if $is_answer {
                            if let Some(value) = check_name_in_proxy_list(acl, name) {
                                return value;
                            }
                        }
                        names.insert(name);
                        continue;
                    }
                    if $is_answer && !query.query_type().is_any() && $rec.record_type() != query.query_type() {
                        warn!("local DNS response has inconsistent answer type {} for query {}",
                              $rec.record_type(), query);
                        return true;
                    }
                    let forward = match $rec.rdata() {
                        RData::A(ref ip) => acl.check_ip_in_proxy_list(&IpAddr::V4(*ip)),
                        RData::AAAA(ref ip) => acl.check_ip_in_proxy_list(&IpAddr::V6(*ip)),
                        // MX records cause type A additional section processing for the host specified by EXCHANGE.
                        RData::MX(ref mx) => examine_name!(mx.exchange(), $is_answer),
                        // NS records cause both the usual additional section processing to locate a type A record...
                        RData::NS(ref name) => examine_name!(name, $is_answer),
                        RData::PTR(_) => unreachable!(),
                        _ => acl.is_default_in_proxy_list(),
                    };
                    if !forward {
                        return false;
                    }
                };
            }
            for rec in local_response.answers() {
                if !names.contains(rec.name()) {
                    warn!("local DNS response contains unexpected name {} for query {}", rec.name(), query);
                    return true;
                }
                examine_record!(rec, true);
            }
            for rec in local_response.additionals() {
                if names.contains(rec.name()) {
                    examine_record!(rec, false);
                }
            }
        }
        true
    } else {
        unreachable!()
    }
}

struct DnsRelay<Remote: upstream::Upstream> {
    context: SharedContext,
    remote_upstream: Remote,
}

impl<Remote: upstream::Upstream> DnsRelay<Remote> {
    async fn acl_lookup(&self, query: &Query) -> (io::Result<Message>, bool) {
        let acl = self.context.acl();
        let local = self.context.local_dns();
        let remote = &self.remote_upstream;
        // Start querying name servers
        debug!(
            "attempting lookup of {:?} {} with ns {:?} and {:?}",
            query.query_type(), query.name(), local, remote
        );

        let remote_response_fut = try_timeout(remote.lookup(query), Some(Duration::new(3, 0)));
        let local_response_fut = try_timeout(local.lookup(query), Some(Duration::new(3, 0)));

        match should_forward_by_query(acl, query) {
            Some(true) => {
                let remote_response = remote_response_fut.await;
                debug!("pick remote response (query): {:?}", remote_response);
                return (remote_response, true);
            }
            Some(false) => {
                let local_response = local_response_fut.await;
                debug!("pick local response (query): {:?}", local_response);
                return (local_response, false);
            }
            None => (),
        }

        let decider = async {
            let local_response = local_response_fut.await;
            if should_forward_by_response(acl, &local_response, query) {
                None
            } else {
                Some(local_response)
            }
        };
        tokio::pin!(remote_response_fut, decider);
        let mut use_remote = false;
        let mut remote_response = None;
        loop {
            tokio::select! {
                response = &mut remote_response_fut, if remote_response.is_none() => {
                    if use_remote {
                        debug!("pick remote response (response): {:?}", response);
                        return (response, true);
                    } else {
                        remote_response = Some(response);
                    }
                }
                decision = &mut decider, if !use_remote => {
                    if let Some(local_response) = decision {
                        debug!("pick local response (response): {:?}", local_response);
                        return (local_response, false);
                    } else if let Some(remote_response) = remote_response {
                        debug!("pick remote response (response): {:?}", remote_response);
                        return (remote_response, true);
                    } else {
                        use_remote = true;
                    }
                }
                else => unreachable!(),
            }
        }
    }

    async fn resolve(&self, request: Message) -> Message {
        let mut message = Message::new();
        message.set_id(request.id());
        message.set_recursion_desired(true);
        message.set_recursion_available(true);
        message.set_message_type(MessageType::Response);
        if !request.recursion_desired() {
            message.set_recursion_desired(false);
            message.set_response_code(ResponseCode::NotImp);
        } else if request.op_code() != OpCode::Query || request.message_type() != MessageType::Query {
            message.set_response_code(ResponseCode::NotImp);
        } else if request.query_count() > 0 {
            let (r, forward) = self.acl_lookup(&request.queries()[0]).await;
            if let Ok(result) = r {
                for rec in result.answers() {
                    debug!("dns answer: {:?}", rec);
                    match rec.rdata() {
                        RData::A(ref ip) => self.context.add_to_reverse_lookup_cache(&IpAddr::V4(*ip), forward),
                        RData::AAAA(ref ip) => self.context.add_to_reverse_lookup_cache(&IpAddr::V6(*ip), forward),
                        _ => (),
                    }
                }
                message = result;
                message.set_id(request.id());
            } else {
                message.set_response_code(ResponseCode::ServFail);
            }
        }
        message
    }
}

async fn run_tcp<Remote: upstream::Upstream + Send + Sync + 'static>(
    relay: Arc<DnsRelay<Remote>>,
    bind_addr: SocketAddr
) -> io::Result<()> {
    let mut listener = TcpListener::bind(&bind_addr).await?;

    let actual_local_addr = listener.local_addr()?;
    info!("shadowsocks DNS relay listening on tcp:{}", actual_local_addr);

    loop {
        let (mut stream, src) = listener.accept().await?;
        let relay = relay.clone();
        tokio::spawn(async move {
            match upstream::read_message(&mut stream).await {
                Ok(request) => {
                    debug!("received src: {}, query: {:?}", src, request);
                    let message = relay.resolve(request).await;
                    debug!("DNS src: {}, final response: {:?}", src, message);
                    if let Err(err) = upstream::write_message(&mut stream, &message).await {
                        error!("failed to write DNS response, error: {}", err);
                    }
                }
                Err(e) => error!("failed to parse TCP query message from {}, error: {:?}", src, e),
            }
        });
    }
}

async fn run_udp<Remote: upstream::Upstream + Send + Sync + 'static>(
    relay: Arc<DnsRelay<Remote>>,
    bind_addr: SocketAddr
) -> io::Result<()> {
    let socket = create_udp_socket(&bind_addr).await?;

    let actual_local_addr = socket.local_addr()?;
    info!("shadowsocks DNS relay listening on udp:{}", actual_local_addr);

    let (mut rx, tx) = socket.split();
    let tx = Arc::new(tokio::sync::Mutex::new(tx));

    loop {
        let mut req_buffer: [u8; 512] = [0; 512];
        let (_, src) = match rx.recv_from(&mut req_buffer).await {
            Ok(x) => x,
            Err(e) => {
                error!("DNS relay read from UDP socket error: {}", e);
                continue;
            }
        };

        let request = match Message::from_vec(&req_buffer) {
            Ok(x) => x,
            Err(e) => {
                error!("failed to parse UDP query message, error: {:?}", e);
                continue;
            }
        };

        debug!("received src: {}, query: {:?}", src, request);

        let relay = relay.clone();
        let tx = tx.clone();

        tokio::spawn(async move {
            let message = relay.resolve(request).await;
            debug!("DNS src: {}, final response: {:?}", src, message);

            match message.to_vec() {
                Err(err) => {
                    error!("failed to serialize message, error: {}", err);
                }
                Ok(res_buffer) => {
                    if let Err(err) = tx.lock().await.send_to(&res_buffer, &src).await {
                        error!("DNS send back UDP error: {}", err);
                    }
                }
            }
        });
    }
}

/// Start a DNS relay local server
pub async fn run(context: SharedContext) -> io::Result<()> {
    let local_addr = match context.config().config_type {
        ConfigType::DnsLocal => {
            // Standalone server
            context.config().local_addr.as_ref().expect("local config")
        }
        c if c.is_local() => {
            // Integrated mode
            context.config().dns_local_addr.as_ref().expect("dns relay addr")
        }
        _ => {
            panic!("ConfigType must be DnsLocal");
        }
    };
    let bind_addr = local_addr.bind_addr(&context).await?;

    let config = context.config();
    // FIXME: We use TCP to send remote queries by default, which should be configuable.
    let balancer = PlainPingBalancer::new(context.clone(), ServerType::Tcp).await;
    let relay = Arc::new(DnsRelay {
        context: context.clone(),
        remote_upstream: upstream::ProxyTcpUpstream {
            context: context.clone(),
            svr_cfg: move || balancer.pick_server().server_config().clone(),
            ns: config.remote_dns_addr.clone().expect("remote query DNS address"),
        }
    });

    future::select(
        tokio::spawn(run_tcp(relay.clone(), bind_addr)),
        tokio::spawn(run_udp(relay, bind_addr)),
    ).await;
    Ok(())
}
<|endoftext|>


<commit_before>use std::str::FromStr;
use std::num::SignedInt;
use board::{Edge, Hint};
use geom::{Point, Rotation, Move, Size,
           LEFT, UP, UCW90, UCW180, UCW270, H_FLIP, V_FLIP};
use util;

#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
enum Pattern {
    Hint(Hint, Point),
    Edge(Edge, Point, Point)
}

impl Pattern {
    fn normalized(self) -> Pattern {
        match self {
            Pattern::Edge(edge, p0, p1) if p1 < p0 => {
                Pattern::Edge(edge, p1, p0)
            }
            x => x
        }
    }

    fn rotate(self, rot: Rotation) -> Pattern {
        let o = Point(0, 0);
        match self {
            Pattern::Hint(h, p) => { Pattern::Hint(h, o + rot * (p - o)) }
            Pattern::Edge(e, p0, p1) => {
                Pattern::Edge(e, o + rot * (p0 - o), o + rot * (p1 - o))
            }
        }.normalized()
    }

    fn shift(self, d: Move) -> Pattern {
        match self {
            Pattern::Hint(h, p) => { Pattern::Hint(h, p + d) }
            Pattern::Edge(e, p0, p1) => {
                Pattern::Edge(e, p0 + d, p1 + d)
            }
        }.normalized()
    }
}

#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct Theorem {
    size: Size,
    matcher: Vec<Pattern>,
    result: Vec<Pattern>
}

impl Theorem {
    fn normalized(mut self) -> Theorem {
        self.matcher.sort();
        self.matcher.dedup();
        self.result.sort();
        self.result.dedup();
        self
    }

    fn rotate(self, rot: Rotation) -> Theorem {
        let Theorem { size, matcher, result } = self;
        let size = rot * Move(size.0, size.1);

        let mut d = Move(0, 0);
        if size.0 < 0 { d = d + Move(- size.0 - 1, 0); }
        if size.1 < 0 { d = d + Move(0, - size.1 - 1); }

        Theorem {
            size: Size(size.0.abs(), size.1.abs()),
            matcher: matcher.map_in_place(|x| x.rotate(rot).shift(d)),
            result: result.map_in_place(|x| x.rotate(rot).shift(d))
        }.normalized()
    }

    pub fn all_rotations(self) -> Vec<Theorem> {
        let Size(r, c) = self.size;
        let deg90  = self.clone().rotate(UCW90);
        let deg180 = self.clone().rotate(UCW180);
        let deg270 = self.clone().rotate(UCW270);
        let h_deg0   = self.clone().rotate(H_FLIP);
        let h_deg90  = h_deg0.clone().rotate(UCW90);
        let h_deg180 = h_deg0.clone().rotate(UCW180);
        let h_deg270 = h_deg0.clone().rotate(UCW270);
        let mut rots = vec![self.clone(), deg90, deg180, deg270,
                            h_deg0, h_deg90, h_deg180, h_deg270];
        rots.sort();
        rots.dedup();
        rots
    }
}

impl FromStr for Theorem {
    fn from_str(s: &str) -> Option<Theorem> {
        let mut matcher_lines = vec![];
        let mut result_lines = vec![];

        let mut lines = s.lines()
            .map(|l| l.trim_matches('\n'))
            .filter(|s| !s.is_empty());

        for line in lines {
            let mut it = line.splitn(2, '!');
            if let Some(l) = it.next() {
                matcher_lines.push(l.chars().collect());
            } else {
                return None
            }

            if let Some(l) = it.next() {
                result_lines.push(l.chars().collect());
            } else {
                return None
            }
        }

        let (m_size, m_pat) = match parse_lines(&matcher_lines[]) {
            Some(x) => x, None => return None
        };
        let (r_size, mut r_pat) = match parse_lines(&result_lines[]) {
            Some(x) => x, None => return None
        };

        if m_size != r_size { return None }

        let mut idx = 0;
        for &p in m_pat.iter() {
            match r_pat[idx ..].iter().position(|&x| x == p) {
                Some(i) => {
                    idx += i;
                    let _ = r_pat.remove(idx);
                }
                None => { return None }
            }
        }

        return Some(Theorem { size: m_size, matcher: m_pat, result: r_pat });

        fn parse_lines(lines: &[Vec<char>]) -> Option<(Size, Vec<Pattern>)> {
            use util::{VEdges, HEdges, Cells};

            let (rows, cols) = match util::find_lattice(lines) {
                Some(x) => x, None => return None
            };

            if rows.len() <= 1 { return None }
            if cols.len() <= 1 { return None }

            let size = Size((rows.len() - 1) as i32, (cols.len() - 1) as i32);

            let mut pat = vec![];

            for (p, s) in VEdges::new(lines, &rows[], &cols[]) {
                if s.is_empty() {
                    continue
                }
                if s.chars().all(|c| c == 'x') {
                    pat.push(Pattern::Edge(Edge::Cross, p + LEFT, p));
                    continue
                }
                if s.chars().all(|c| c == '|') {
                    pat.push(Pattern::Edge(Edge::Line, p + LEFT, p));
                    continue
                }
            }

            for (p, s) in HEdges::new(lines, &rows[], &cols[]) {
                if s.is_empty() {
                    continue
                }
                if s.chars().all(|c| c == 'x') {
                    pat.push(Pattern::Edge(Edge::Cross, p + UP, p));
                    continue
                }
                if s.chars().all(|c| c == '-') {
                    pat.push(Pattern::Edge(Edge::Line, p + UP, p));
                    continue
                }
            }

            let mut pairs: Vec<(char, Vec<Point>, Vec<Point>)> = vec![];

            for (p, s) in Cells::new(lines, &rows[], &cols[]) {
                for c in s.trim_matches(' ').chars() {
                    match c {
                        '0' => { pat.push(Pattern::Hint(Some(0), p)); }
                        '1' => { pat.push(Pattern::Hint(Some(1), p)); }
                        '2' => { pat.push(Pattern::Hint(Some(2), p)); }
                        '3' => { pat.push(Pattern::Hint(Some(3), p)); }
                        _ if c.is_alphabetic() => {
                            let key = c.to_lowercase();
                            match pairs.iter().position(|&(k, _, _)| k == key) {
                                Some(idx) => {
                                    if c.is_lowercase() {
                                        pairs[idx].1.push(p);
                                    } else {
                                        pairs[idx].2.push(p);
                                    }
                                }
                                None => {
                                    let (lower, upper) = if c.is_lowercase() {
                                        (vec![p], vec![])
                                    } else {
                                        (vec![], vec![p])
                                    };
                                    pairs.push((key, lower, upper));
                                }
                            }
                        }
                        _ => {}
                    }
                }
            }

            for &(_, ref ps0, ref ps1) in pairs.iter() {
                if !ps0.is_empty() && !ps1.is_empty() {
                    pat.push(Pattern::Edge(Edge::Line, ps0[0], ps1[0]));
                }

                if ps0.len() > 0 {
                    for &p in ps0[1 ..].iter() {
                        pat.push(Pattern::Edge(Edge::Cross, ps0[0], p));
                    }
                }
                if ps1.len() > 0 {
                    for &p in ps1[1 ..].iter() {
                        pat.push(Pattern::Edge(Edge::Cross, ps1[0], p));
                    }
                }
            }

            let mut pat = pat.map_in_place(|p| p.normalized());
            pat.sort();
            Some((size, pat))
        }
    }
}

#[cfg(test)]
mod tests {
    use geom::{Point, Size, Move, UCW0, UCW90, UCW180, UCW270, H_FLIP, V_FLIP};
    use board::Edge;
    use super::{Pattern, Theorem};

    #[test]
    fn parse() {
        fn check(size: Size, matcher: Vec<Pattern>, result: Vec<Pattern>,
                 input: &str)
        {
            let mut matcher = matcher.map_in_place(|p| p.normalized());
            let mut result = result.map_in_place(|p| p.normalized());
            matcher.sort();
            result.sort();
            assert_eq!(Some(Theorem { size: size, matcher: matcher, result: result }),
                       input.parse::<Theorem>())
        }

        check(Size(1, 1),
              vec![Pattern::Hint(Some(0), Point(0, 0))],
              vec![Pattern::Edge(Edge::Cross, Point(0, 0), Point(0, -1)),
                   Pattern::Edge(Edge::Cross, Point(0, 0), Point(0, 1)),
                   Pattern::Edge(Edge::Cross, Point(0, 0), Point(-1, 0)),
                   Pattern::Edge(Edge::Cross, Point(0, 0), Point(1, 0))],"
+ + ! +x+
 0  ! x0x
+ + ! +x+
");
        check(Size(3, 3),
              vec![Pattern::Hint(Some(0), Point(1, 0)),
                   Pattern::Hint(Some(3), Point(1, 1))],
              vec![Pattern::Edge(Edge::Cross, Point(1, 0), Point(1, -1)),
                   Pattern::Edge(Edge::Cross, Point(1, 0), Point(1, 1)),
                   Pattern::Edge(Edge::Cross, Point(1, 0), Point(0, 0)),
                   Pattern::Edge(Edge::Cross, Point(1, 0), Point(2, 0)),
                   Pattern::Edge(Edge::Cross, Point(0, 1), Point(0, 2)),
                   Pattern::Edge(Edge::Cross, Point(1, 2), Point(0, 2)),
                   Pattern::Edge(Edge::Cross, Point(1, 2), Point(2, 2)),
                   Pattern::Edge(Edge::Cross, Point(2, 1), Point(2, 2)),
                   Pattern::Edge(Edge::Line, Point(0, 0), Point(0, 1)),
                   Pattern::Edge(Edge::Line, Point(0, 1), Point(1, 1)),
                   Pattern::Edge(Edge::Line, Point(1, 1), Point(1, 2)),
                   Pattern::Edge(Edge::Line, Point(1, 1), Point(2, 1)),
                   Pattern::Edge(Edge::Line, Point(2, 0), Point(2, 1))], "
+ + + + ! + + + +
        !   | x
+ + + + ! +x+-+x+
 0 3    ! x0x3|
+ + + + ! +x+-+x+
        !   | x
+ + + + ! + + + +
");
        check(Size(2, 2),
              vec![Pattern::Hint(Some(1), Point(1, 1)),
                   Pattern::Edge(Edge::Line, Point(1, 0), Point(0, 1))],
              vec![Pattern::Edge(Edge::Cross, Point(1, 1), Point(1, 2)),
                   Pattern::Edge(Edge::Cross, Point(1, 1), Point(2, 1))], "
+ + + ! + + +
   a  !    a
+ + + ! + + +
 A 1  !  A 1x
+ + + ! + +x+
");
        check(Size(3, 3),
              vec![Pattern::Hint(Some(3), Point(1, 1)),
                   Pattern::Edge(Edge::Cross, Point(1, 0), Point(0, 1))],
              vec![Pattern::Edge(Edge::Cross, Point(0, 0), Point(0, 1)),
                   Pattern::Edge(Edge::Cross, Point(0, 0), Point(1, 0)),
                   Pattern::Edge(Edge::Line, Point(0, 1), Point(1, 1)),
                   Pattern::Edge(Edge::Line, Point(1, 0), Point(1, 1)),
                   Pattern::Edge(Edge::Line, Point(1, 2), Point(2, 1))], "
+ + + + ! + + + +
   a    !   xa
+ + + + ! +x+-+ +
 a 3    !  a|3 b
+ + + + ! + + + +
        !    B
+ + + + ! + + + +
");
    }

    #[test]
    fn rotate() {
        let deg0 = "
+ + + ! + + +
   a  !  bxa
+ + + ! +x+-+
 a 3  !  a|3
+ + + ! + + +
      !    B
+ + + ! + + +
".parse::<Theorem>().unwrap();

        let deg90 = "
+ + + + ! + + + +
 a 3    !  a|3 B
+ + + + ! +x+-+ +
   a    !  bxa
+ + + + ! + + + +
".parse::<Theorem>().unwrap();

        let deg180 = "
+ + + ! + + +
      !  B
+ + + ! + + +
 3 a  !  3|a
+ + + ! +-+x+
 a    !  axb
+ + + ! + + +
".parse::<Theorem>().unwrap();

        let deg270 = "
+ + + + ! + + + +
   a    !    axb
+ + + + ! + +-+x+
   3 a  !  B 3|a
+ + + + ! + + + +
".parse::<Theorem>().unwrap();

        let h_flip = "
+ + + ! + + +
 a    !  axb
+ + + ! +-+x+
 3 a  !  3|a
+ + + ! + + +
      !  B
+ + + ! + + +
".parse::<Theorem>().unwrap();

        let v_flip = "
+ + + ! + + +
      !    B
+ + + ! + + +
 a 3  !  a|3
+ + + ! +x+-+
   a  !  bxa
+ + + ! + + +
".parse::<Theorem>().unwrap();

        let Size(r, c) = deg0.size;
        assert_eq!(deg0.clone(), deg0.clone().rotate(UCW0));
        assert_eq!(deg90.clone(), deg0.clone().rotate(UCW90));
        assert_eq!(deg180.clone(), deg0.clone().rotate(UCW180));
        assert_eq!(deg270.clone(), deg0.clone().rotate(UCW270));
        assert_eq!(h_flip.clone(), deg0.clone().rotate(H_FLIP));
        assert_eq!(v_flip.clone(), deg0.clone().rotate(V_FLIP));
        assert_eq!(v_flip.clone(), h_flip.clone().rotate(UCW180));

        let mut rots = [deg0.clone(), deg90, deg180, deg270,
                        h_flip.clone(),
                        h_flip.clone().rotate(UCW90),
                        h_flip.clone().rotate(UCW180),
                        h_flip.clone().rotate(UCW270)];
        rots.sort();
        assert_eq!(rots, deg0.all_rotations());
    }

    #[test]
    fn all_rotations() {
        let theo = "
+ + ! +x+
 0  ! x0x
+ + ! +x+
".parse::<Theorem>().unwrap();
        let rots = theo.clone().all_rotations();
        assert_eq!([theo], rots);
    }
}
<commit_msg>[WIP] solver: refactor `Theorem`<commit_after>use std::str::FromStr;
use std::num::SignedInt;
use board::{Edge, Hint};
use geom::{Point, Rotation, Move, Size,
           LEFT, UP, UCW90, UCW180, UCW270, H_FLIP, V_FLIP};
use util;

#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
enum Pattern {
    Hint(Hint, Point),
    Edge(Edge, Point, Point)
}

impl Pattern {
    fn hint(h: Hint, p: Point) -> Pattern {
        Pattern::Hint(h, p).normalized()
    }
    fn cross(p0: Point, p1: Point) -> Pattern {
        Pattern::Edge(Edge::Cross, p0, p1).normalized()
    }
    fn line(p0: Point, p1: Point) -> Pattern {
        Pattern::Edge(Edge::Line, p0, p1).normalized()
    }

    fn normalized(self) -> Pattern {
        match self {
            Pattern::Edge(edge, p0, p1) if p1 < p0 => {
                Pattern::Edge(edge, p1, p0)
            }
            x => x
        }
    }

    fn rotate(self, rot: Rotation) -> Pattern {
        let o = Point(0, 0);
        match self {
            Pattern::Hint(h, p) => { Pattern::Hint(h, o + rot * (p - o)) }
            Pattern::Edge(e, p0, p1) => {
                Pattern::Edge(e, o + rot * (p0 - o), o + rot * (p1 - o))
            }
        }.normalized()
    }

    fn shift(self, d: Move) -> Pattern {
        match self {
            Pattern::Hint(h, p) => { Pattern::Hint(h, p + d) }
            Pattern::Edge(e, p0, p1) => {
                Pattern::Edge(e, p0 + d, p1 + d)
            }
        }.normalized()
    }
}

#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct Theorem {
    size: Size,
    matcher: Vec<Pattern>,
    result: Vec<Pattern>
}

impl Theorem {
    fn normalized(mut self) -> Theorem {
        self.matcher.sort();
        self.matcher.dedup();
        self.result.sort();
        self.result.dedup();
        self
    }

    fn rotate(self, rot: Rotation) -> Theorem {
        let Theorem { size, matcher, result } = self;
        let size = rot * Move(size.0, size.1);

        let mut d = Move(0, 0);
        if size.0 < 0 { d = d + Move(- size.0 - 1, 0); }
        if size.1 < 0 { d = d + Move(0, - size.1 - 1); }

        Theorem {
            size: Size(size.0.abs(), size.1.abs()),
            matcher: matcher.map_in_place(|x| x.rotate(rot).shift(d)),
            result: result.map_in_place(|x| x.rotate(rot).shift(d))
        }.normalized()
    }

    pub fn all_rotations(self) -> Vec<Theorem> {
        let Size(r, c) = self.size;
        let deg90  = self.clone().rotate(UCW90);
        let deg180 = self.clone().rotate(UCW180);
        let deg270 = self.clone().rotate(UCW270);
        let h_deg0   = self.clone().rotate(H_FLIP);
        let h_deg90  = h_deg0.clone().rotate(UCW90);
        let h_deg180 = h_deg0.clone().rotate(UCW180);
        let h_deg270 = h_deg0.clone().rotate(UCW270);
        let mut rots = vec![self.clone(), deg90, deg180, deg270,
                            h_deg0, h_deg90, h_deg180, h_deg270];
        rots.sort();
        // FIXME: Should reduce the elements that has different result but size
        //        and matcher are same.
        rots.dedup();

        rots
    }
}

impl FromStr for Theorem {
    fn from_str(s: &str) -> Option<Theorem> {
        let mut matcher_lines = vec![];
        let mut result_lines = vec![];

        let mut lines = s.lines()
            .map(|l| l.trim_matches('\n'))
            .filter(|s| !s.is_empty());

        for line in lines {
            let mut it = line.splitn(2, '!');
            if let Some(l) = it.next() {
                matcher_lines.push(l.chars().collect());
            } else {
                return None
            }

            if let Some(l) = it.next() {
                result_lines.push(l.chars().collect());
            } else {
                return None
            }
        }

        let (m_size, m_pat) = match parse_lines(&matcher_lines[]) {
            Some(x) => x, None => return None
        };
        let (r_size, mut r_pat) = match parse_lines(&result_lines[]) {
            Some(x) => x, None => return None
        };

        if m_size != r_size { return None }

        let mut idx = 0;
        for &p in m_pat.iter() {
            match r_pat[idx ..].iter().position(|&x| x == p) {
                Some(i) => {
                    idx += i;
                    let _ = r_pat.remove(idx);
                }
                None => { return None }
            }
        }

        return Some(Theorem { size: m_size, matcher: m_pat, result: r_pat });

        fn parse_lines(lines: &[Vec<char>]) -> Option<(Size, Vec<Pattern>)> {
            use util::{VEdges, HEdges, Cells};

            let (rows, cols) = match util::find_lattice(lines) {
                Some(x) => x, None => return None
            };

            if rows.len() <= 1 { return None }
            if cols.len() <= 1 { return None }

            let size = Size((rows.len() - 1) as i32, (cols.len() - 1) as i32);

            let mut pat = vec![];

            for (p, s) in VEdges::new(lines, &rows[], &cols[]) {
                if s.is_empty() {
                    continue
                }
                if s.chars().all(|c| c == 'x') {
                    pat.push(Pattern::cross(p + LEFT, p));
                    continue
                }
                if s.chars().all(|c| c == '|') {
                    pat.push(Pattern::line(p + LEFT, p));
                    continue
                }
            }

            for (p, s) in HEdges::new(lines, &rows[], &cols[]) {
                if s.is_empty() {
                    continue
                }
                if s.chars().all(|c| c == 'x') {
                    pat.push(Pattern::cross(p + UP, p));
                    continue
                }
                if s.chars().all(|c| c == '-') {
                    pat.push(Pattern::line(p + UP, p));
                    continue
                }
            }

            let mut pairs: Vec<(char, Vec<Point>, Vec<Point>)> = vec![];

            for (p, s) in Cells::new(lines, &rows[], &cols[]) {
                for c in s.trim_matches(' ').chars() {
                    match c {
                        '0' => { pat.push(Pattern::hint(Some(0), p)); }
                        '1' => { pat.push(Pattern::hint(Some(1), p)); }
                        '2' => { pat.push(Pattern::hint(Some(2), p)); }
                        '3' => { pat.push(Pattern::hint(Some(3), p)); }
                        _ if c.is_alphabetic() => {
                            let key = c.to_lowercase();
                            match pairs.iter().position(|&(k, _, _)| k == key) {
                                Some(idx) => {
                                    if c.is_lowercase() {
                                        pairs[idx].1.push(p);
                                    } else {
                                        pairs[idx].2.push(p);
                                    }
                                }
                                None => {
                                    let (lower, upper) = if c.is_lowercase() {
                                        (vec![p], vec![])
                                    } else {
                                        (vec![], vec![p])
                                    };
                                    pairs.push((key, lower, upper));
                                }
                            }
                        }
                        _ => {}
                    }
                }
            }

            for &(_, ref ps0, ref ps1) in pairs.iter() {
                if !ps0.is_empty() && !ps1.is_empty() {
                    pat.push(Pattern::line(ps0[0], ps1[0]));
                }

                if ps0.len() > 0 {
                    for &p in ps0[1 ..].iter() {
                        pat.push(Pattern::cross(ps0[0], p));
                    }
                }
                if ps1.len() > 0 {
                    for &p in ps1[1 ..].iter() {
                        pat.push(Pattern::cross(ps1[0], p));
                    }
                }
            }

            pat.sort();
            pat.dedup();
            Some((size, pat))
        }
    }
}

#[cfg(test)]
mod tests {
    use geom::{Point, Size, Move, UCW0, UCW90, UCW180, UCW270, H_FLIP, V_FLIP};
    use board::Edge;
    use super::{Pattern, Theorem};

    #[test]
    fn parse() {
        fn check(size: Size, matcher: Vec<Pattern>, result: Vec<Pattern>,
                 input: &str)
        {
            let mut matcher = matcher.map_in_place(|p| p.normalized());
            let mut result = result.map_in_place(|p| p.normalized());
            matcher.sort();
            matcher.dedup();
            result.sort();
            result.dedup();
            assert_eq!(Some(Theorem { size: size, matcher: matcher, result: result }),
                       input.parse::<Theorem>())
        }

        check(Size(1, 1),
              vec![Pattern::hint(Some(0), Point(0, 0))],
              vec![Pattern::cross(Point(0, 0), Point(0, -1)),
                   Pattern::cross(Point(0, 0), Point(0, 1)),
                   Pattern::cross(Point(0, 0), Point(-1, 0)),
                   Pattern::cross(Point(0, 0), Point(1, 0))],"
+ + ! +x+
 0  ! x0x
+ + ! +x+
");
        check(Size(3, 3),
              vec![Pattern::hint(Some(0), Point(1, 0)),
                   Pattern::hint(Some(3), Point(1, 1))],
              vec![Pattern::cross(Point(1, 0), Point(1, -1)),
                   Pattern::cross(Point(1, 0), Point(1, 1)),
                   Pattern::cross(Point(1, 0), Point(0, 0)),
                   Pattern::cross(Point(1, 0), Point(2, 0)),
                   Pattern::cross(Point(0, 1), Point(0, 2)),
                   Pattern::cross(Point(1, 2), Point(0, 2)),
                   Pattern::cross(Point(1, 2), Point(2, 2)),
                   Pattern::cross(Point(2, 1), Point(2, 2)),
                   Pattern::line(Point(0, 0), Point(0, 1)),
                   Pattern::line(Point(0, 1), Point(1, 1)),
                   Pattern::line(Point(1, 1), Point(1, 2)),
                   Pattern::line(Point(1, 1), Point(2, 1)),
                   Pattern::line(Point(2, 0), Point(2, 1))], "
+ + + + ! + + + +
        !   | x
+ + + + ! +x+-+x+
 0 3    ! x0x3|
+ + + + ! +x+-+x+
        !   | x
+ + + + ! + + + +
");
        check(Size(2, 2),
              vec![Pattern::hint(Some(1), Point(1, 1)),
                   Pattern::line(Point(1, 0), Point(0, 1))],
              vec![Pattern::cross(Point(1, 1), Point(1, 2)),
                   Pattern::cross(Point(1, 1), Point(2, 1))], "
+ + + ! + + +
   a  !    a
+ + + ! + + +
 A 1  !  A 1x
+ + + ! + +x+
");
        check(Size(3, 3),
              vec![Pattern::hint(Some(3), Point(1, 1)),
                   Pattern::cross(Point(1, 0), Point(0, 1))],
              vec![Pattern::cross(Point(0, 0), Point(0, 1)),
                   Pattern::cross(Point(0, 0), Point(1, 0)),
                   Pattern::line(Point(0, 1), Point(1, 1)),
                   Pattern::line(Point(1, 0), Point(1, 1)),
                   Pattern::line(Point(1, 2), Point(2, 1))], "
+ + + + ! + + + +
   a    !   xa
+ + + + ! +x+-+ +
 a 3    !  a|3 b
+ + + + ! + + + +
        !    B
+ + + + ! + + + +
");
    }

    #[test]
    fn rotate() {
        let deg0 = "
+ + + ! + + +
   a  !  bxa
+ + + ! +x+-+
 a 3  !  a|3
+ + + ! + + +
      !    B
+ + + ! + + +
".parse::<Theorem>().unwrap();

        let deg90 = "
+ + + + ! + + + +
 a 3    !  a|3 B
+ + + + ! +x+-+ +
   a    !  bxa
+ + + + ! + + + +
".parse::<Theorem>().unwrap();

        let deg180 = "
+ + + ! + + +
      !  B
+ + + ! + + +
 3 a  !  3|a
+ + + ! +-+x+
 a    !  axb
+ + + ! + + +
".parse::<Theorem>().unwrap();

        let deg270 = "
+ + + + ! + + + +
   a    !    axb
+ + + + ! + +-+x+
   3 a  !  B 3|a
+ + + + ! + + + +
".parse::<Theorem>().unwrap();

        let h_flip = "
+ + + ! + + +
 a    !  axb
+ + + ! +-+x+
 3 a  !  3|a
+ + + ! + + +
      !  B
+ + + ! + + +
".parse::<Theorem>().unwrap();

        let v_flip = "
+ + + ! + + +
      !    B
+ + + ! + + +
 a 3  !  a|3
+ + + ! +x+-+
   a  !  bxa
+ + + ! + + +
".parse::<Theorem>().unwrap();

        let Size(r, c) = deg0.size;
        assert_eq!(deg0.clone(), deg0.clone().rotate(UCW0));
        assert_eq!(deg90.clone(), deg0.clone().rotate(UCW90));
        assert_eq!(deg180.clone(), deg0.clone().rotate(UCW180));
        assert_eq!(deg270.clone(), deg0.clone().rotate(UCW270));
        assert_eq!(h_flip.clone(), deg0.clone().rotate(H_FLIP));
        assert_eq!(v_flip.clone(), deg0.clone().rotate(V_FLIP));
        assert_eq!(v_flip.clone(), h_flip.clone().rotate(UCW180));

        let mut rots = [deg0.clone(), deg90, deg180, deg270,
                        h_flip.clone(),
                        h_flip.clone().rotate(UCW90),
                        h_flip.clone().rotate(UCW180),
                        h_flip.clone().rotate(UCW270)];
        rots.sort();
        assert_eq!(rots, deg0.all_rotations());
    }

    #[test]
    fn all_rotations() {
        let theo = "
+ + ! +x+
 0  ! x0x
+ + ! +x+
".parse::<Theorem>().unwrap();
        let rots = theo.clone().all_rotations();
        assert_eq!([theo], rots);
    }
}
<|endoftext|>


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

use Mathf;

use std::ops::*;
use std::cmp::PartialEq;

/*================================================================================================*/
/*------STRUCTS-----------------------------------------------------------------------------------*/
/*================================================================================================*/

/// The Vec2f struct
///
/// It is used mainly for 2D releated mathematics (e.g. texture and UV coordinates).
/// 32-bit floats are used for the values.
pub struct Vec2f {

    x : f32,
    y : f32
}

/*================================================================================================*/
/*------PUBLIC FUNCTIONS--------------------------------------------------------------------------*/
/*================================================================================================*/

impl Vec2f {

    /// Formats the vector as a string.
    ///
    /// # Examples
    /// ```
    /// let vec = Vec2f {x : 10.0, y : 5.0};
    /// println! ("Vector = {}", vec.to_string ());
    /// ```
    /// ```c
    /// Output : Vector = 10.0, 5.0
    pub fn to_string (&self) -> String {

        format! ("{}, {}", self.x, self.y)
    }

/*================================================================================================*/
/*------PUBLIC STATIC FUNCTIONS-------------------------------------------------------------------*/
/*================================================================================================*/

    /// Creates a vector with a default value of zero.
    ///
    /// # Examples
    /// ```
    /// let vec = Vec2f::new ();
    pub fn new () -> Vec2f {

        Vec2f {x : 0.0,
               y : 0.0}
    }
}

/*================================================================================================*/
/*------OPERATOR OVERLOADS------------------------------------------------------------------------*/
/*================================================================================================*/

impl Add for Vec2f {

    type Output = Vec2f;

    // Addition operator (vector)
    fn add (self, rhs : Vec2f) -> Vec2f {

        Vec2f {x : self.x + rhs.x,
               y : self.y + rhs.y}
    }
}

/*================================================================================================*/

impl Add <f32> for Vec2f {

    type Output = Vec2f;

    // Addition operator (f32)
    fn add (self, rhs : f32) -> Vec2f {

        Vec2f {x : self.x + rhs,
               y : self.y + rhs}
    }
}

/*================================================================================================*/

impl AddAssign for Vec2f {

    // Addition assignment operator (vector)
    fn add_assign (&mut self, rhs : Vec2f) {

        self.x += rhs.x;
        self.y += rhs.y;
    }
}

/*================================================================================================*/

impl AddAssign <f32> for Vec2f {

    // Addition assignment operator (f32)
    fn add_assign (&mut self, rhs : f32) {

        self.x += rhs;
        self.y += rhs;
    }
}

/*================================================================================================*/

impl Sub for Vec2f {

    type Output = Vec2f;

    // Subtraction operator (vector)
    fn sub (self, rhs : Vec2f) -> Vec2f {

        Vec2f {x : self.x - rhs.x,
               y : self.y - rhs.y}
    }
}

/*================================================================================================*/

impl Sub <f32> for Vec2f {

    type Output = Vec2f;

    // Subtraction operator (f32)
    fn sub (self, rhs : f32) -> Vec2f {

        Vec2f {x : self.x - rhs,
               y : self.y - rhs}
    }
}

/*================================================================================================*/

impl SubAssign for Vec2f {

    // Subtraction assignment operator (vector)
    fn sub_assign (&mut self, rhs : Vec2f) {

        self.x -= rhs.x;
        self.y -= rhs.y;
    }
}

/*================================================================================================*/

impl SubAssign <f32> for Vec2f {

    // Subtraction assignment operator (f32)
    fn sub_assign (&mut self, rhs : f32) {

        self.x += rhs;
        self.y += rhs;
    }
}

/*================================================================================================*/

impl Neg for Vec2f {

    type Output = Vec2f;

    // Unary minus operator
    fn neg (self) -> Vec2f {

        Vec2f {x : -self.x,
               y : -self.y}
    }
}

/*================================================================================================*/

impl Mul for Vec2f {

    type Output = Vec2f;

    // Multiplication operator (vector)
    fn mul (self, rhs : Vec2f) -> Vec2f {

        Vec2f {x : self.x * rhs.x,
               y : self.y * rhs.y}
    }
}

/*================================================================================================*/

impl Mul <f32> for Vec2f {

    type Output = Vec2f;

    // Multiplication operator (f32)
    fn mul (self, rhs : f32) -> Vec2f {

        Vec2f {x : self.x * rhs,
               y : self.y * rhs}
    }
}

/*================================================================================================*/

impl MulAssign for Vec2f {

    // Multiplication assignment operator (vector)
    fn mul_assign (&mut self, rhs : Vec2f) {

        self.x *= rhs.x;
        self.y *= rhs.y;
    }
}

/*================================================================================================*/

impl MulAssign <f32> for Vec2f {

    // Multiplication assignment operator (f32)
    fn mul_assign (&mut self, rhs : f32) {

        self.x *= rhs;
        self.y *= rhs;
    }
}

/*================================================================================================*/

impl Div for Vec2f {

    type Output = Vec2f;

    // Division operator (vector)
    fn div (self, rhs : Vec2f) -> Vec2f {

        Vec2f {x : self.x / rhs.x,
               y : self.y / rhs.y}
    }
}

/*================================================================================================*/

impl Div <f32> for Vec2f {

    type Output = Vec2f;

    // Division operator (f32)
    fn div (self, rhs : f32) -> Vec2f {

        Vec2f {x : self.x / rhs,
               y : self.y / rhs}
    }
}

/*================================================================================================*/

impl DivAssign for Vec2f {

    // Division assignment operator (vector)
    fn div_assign (&mut self, rhs : Vec2f) {

        self.x /= rhs.x;
        self.y /= rhs.y;
    }
}

/*================================================================================================*/

impl DivAssign <f32> for Vec2f {

    // Division assignment operator (f32)
    fn div_assign (&mut self, rhs : f32) {

        self.x /= rhs;
        self.y /= rhs;
    }
}

/*================================================================================================*/

impl PartialEq for Vec2f {

    // Equal to operator
    fn eq (&self, rhs : &Vec2f) -> bool {

        self.x == rhs.x &&
        self.y == rhs.y
    }

/*================================================================================================*/

    // Not equal to operator
    fn ne (&self, rhs : &Vec2f) -> bool {

        self.x != rhs.x ||
        self.y != rhs.y
    }
}

/*================================================================================================*/

impl Index <u8> for Vec2f {

    type Output = f32;

    // Index operator (immutable)
    fn index (&self, index : u8) -> &f32 {

        match index {

            0 => &self.x,
            1 => &self.y,
            _ => unreachable! ("Index out of range for Vec2f")
        }
    }
}

/*================================================================================================*/

impl IndexMut <u8> for Vec2f {

    // Index operator (mutable)
    fn index_mut (&mut self, index : u8) -> &mut f32 {

        match index {

            0 => &mut self.x,
            1 => &mut self.y,
            _ => unreachable! ("Index out of range for Vec2f")
        }
    }
}
<commit_msg>Added Vec2f utility functions<commit_after>/*================================================================================================*/
// Copyright 2016 Kyle Finlay
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*================================================================================================*/

use Mathf;

use std::ops::*;
use std::cmp::PartialEq;

/*================================================================================================*/
/*------STRUCTS-----------------------------------------------------------------------------------*/
/*================================================================================================*/

/// The Vec2f struct
///
/// It is used mainly for 2D releated mathematics (e.g. texture and UV coordinates).
/// 32-bit floats are used for the values.
#[derive (Copy, Clone)]
pub struct Vec2f {

    x : f32,
    y : f32
}

/*================================================================================================*/
/*------PUBLIC FUNCTIONS--------------------------------------------------------------------------*/
/*================================================================================================*/

impl Vec2f {

    /// Formats the vector as a string.
    ///
    /// # Examples
    /// ```
    /// let vec = Vec2f {x : 10.0, y : 5.0};
    /// println! ("Vector = {}", vec.to_string ());
    /// ```
    /// ```c
    /// Output : Vector = 10.0, 5.0
    pub fn to_string (&self) -> String {

        format! ("{}, {}", self.x, self.y)
    }

/*================================================================================================*/
/*------PUBLIC STATIC FUNCTIONS-------------------------------------------------------------------*/
/*================================================================================================*/

    /// Creates a vector with a default value of zero.
    ///
    /// # Examples
    /// ```
    /// let vec = Vec2f::new ();
    pub fn new () -> Vec2f {

        Vec2f {x : 0.0,
               y : 0.0}
    }

/*================================================================================================*/

    /// Returns the dot product of two vectors.
    pub fn dot (lhs : &Vec2f, rhs : &Vec2f) -> f32 {

        (lhs.x * rhs.x) +
        (lhs.y * rhs.y)
    }

/*================================================================================================*/

    /// Returns the distance between two vectors
    pub fn distance (start : &Vec2f, end : &Vec2f) -> f32 {

        Vec2f::length (& (*start - *end))
    }

/*================================================================================================*/

    /// Returns the length of a vector
    pub fn length (vector : &Vec2f) -> f32 {

        (vector.x * vector.x +
         vector.y * vector.y).sqrt ()
    }

/*================================================================================================*/

    /// Linearly interpolates between two vectors.
    pub fn lerp (start : &Vec2f, end : &Vec2f, percentage : f32) -> Vec2f {

        Vec2f {x : Mathf::lerp (start.x, end.x, percentage),
               y : Mathf::lerp (start.y, end.y, percentage)}
    }

/*================================================================================================*/

    /// Linearly interpolates between two vectors without clamping
    pub fn lerp_unclamped (start : &Vec2f, end : &Vec2f, percentage : f32) -> Vec2f {

        Vec2f {x : Mathf::lerp_unclamped (start.x, end.x, percentage),
               y : Mathf::lerp_unclamped (start.y, end.y, percentage)}
    }

/*================================================================================================*/

    /// Get a normalized vector.
    pub fn normalize (vector : &Vec2f) -> Vec2f {

        let length = Vec2f::length (vector);

        if length != 0.0 {

            return Vec2f {x : vector.x / length,
                          y : vector.y / length}
        }

        Vec2f::new ()
    }
}

/*================================================================================================*/
/*------OPERATOR OVERLOADS------------------------------------------------------------------------*/
/*================================================================================================*/

impl Add for Vec2f {

    type Output = Vec2f;

    // Addition operator (vector)
    fn add (self, rhs : Vec2f) -> Vec2f {

        Vec2f {x : self.x + rhs.x,
               y : self.y + rhs.y}
    }
}

/*================================================================================================*/

impl Add <f32> for Vec2f {

    type Output = Vec2f;

    // Addition operator (f32)
    fn add (self, rhs : f32) -> Vec2f {

        Vec2f {x : self.x + rhs,
               y : self.y + rhs}
    }
}

/*================================================================================================*/

impl AddAssign for Vec2f {

    // Addition assignment operator (vector)
    fn add_assign (&mut self, rhs : Vec2f) {

        self.x += rhs.x;
        self.y += rhs.y;
    }
}

/*================================================================================================*/

impl AddAssign <f32> for Vec2f {

    // Addition assignment operator (f32)
    fn add_assign (&mut self, rhs : f32) {

        self.x += rhs;
        self.y += rhs;
    }
}

/*================================================================================================*/

impl Sub for Vec2f {

    type Output = Vec2f;

    // Subtraction operator (vector)
    fn sub (self, rhs : Vec2f) -> Vec2f {

        Vec2f {x : self.x - rhs.x,
               y : self.y - rhs.y}
    }
}

/*================================================================================================*/

impl Sub <f32> for Vec2f {

    type Output = Vec2f;

    // Subtraction operator (f32)
    fn sub (self, rhs : f32) -> Vec2f {

        Vec2f {x : self.x - rhs,
               y : self.y - rhs}
    }
}

/*================================================================================================*/

impl SubAssign for Vec2f {

    // Subtraction assignment operator (vector)
    fn sub_assign (&mut self, rhs : Vec2f) {

        self.x -= rhs.x;
        self.y -= rhs.y;
    }
}

/*================================================================================================*/

impl SubAssign <f32> for Vec2f {

    // Subtraction assignment operator (f32)
    fn sub_assign (&mut self, rhs : f32) {

        self.x += rhs;
        self.y += rhs;
    }
}

/*================================================================================================*/

impl Neg for Vec2f {

    type Output = Vec2f;

    // Unary minus operator
    fn neg (self) -> Vec2f {

        Vec2f {x : -self.x,
               y : -self.y}
    }
}

/*================================================================================================*/

impl Mul for Vec2f {

    type Output = Vec2f;

    // Multiplication operator (vector)
    fn mul (self, rhs : Vec2f) -> Vec2f {

        Vec2f {x : self.x * rhs.x,
               y : self.y * rhs.y}
    }
}

/*================================================================================================*/

impl Mul <f32> for Vec2f {

    type Output = Vec2f;

    // Multiplication operator (f32)
    fn mul (self, rhs : f32) -> Vec2f {

        Vec2f {x : self.x * rhs,
               y : self.y * rhs}
    }
}

/*================================================================================================*/

impl MulAssign for Vec2f {

    // Multiplication assignment operator (vector)
    fn mul_assign (&mut self, rhs : Vec2f) {

        self.x *= rhs.x;
        self.y *= rhs.y;
    }
}

/*================================================================================================*/

impl MulAssign <f32> for Vec2f {

    // Multiplication assignment operator (f32)
    fn mul_assign (&mut self, rhs : f32) {

        self.x *= rhs;
        self.y *= rhs;
    }
}

/*================================================================================================*/

impl Div for Vec2f {

    type Output = Vec2f;

    // Division operator (vector)
    fn div (self, rhs : Vec2f) -> Vec2f {

        Vec2f {x : self.x / rhs.x,
               y : self.y / rhs.y}
    }
}

/*================================================================================================*/

impl Div <f32> for Vec2f {

    type Output = Vec2f;

    // Division operator (f32)
    fn div (self, rhs : f32) -> Vec2f {

        Vec2f {x : self.x / rhs,
               y : self.y / rhs}
    }
}

/*================================================================================================*/

impl DivAssign for Vec2f {

    // Division assignment operator (vector)
    fn div_assign (&mut self, rhs : Vec2f) {

        self.x /= rhs.x;
        self.y /= rhs.y;
    }
}

/*================================================================================================*/

impl DivAssign <f32> for Vec2f {

    // Division assignment operator (f32)
    fn div_assign (&mut self, rhs : f32) {

        self.x /= rhs;
        self.y /= rhs;
    }
}

/*================================================================================================*/

impl PartialEq for Vec2f {

    // Equal to operator
    fn eq (&self, rhs : &Vec2f) -> bool {

        self.x == rhs.x &&
        self.y == rhs.y
    }

/*================================================================================================*/

    // Not equal to operator
    fn ne (&self, rhs : &Vec2f) -> bool {

        self.x != rhs.x ||
        self.y != rhs.y
    }
}

/*================================================================================================*/

impl Index <u8> for Vec2f {

    type Output = f32;

    // Index operator (immutable)
    fn index (&self, index : u8) -> &f32 {

        match index {

            0 => &self.x,
            1 => &self.y,
            _ => unreachable! ("Index out of range for Vec2f")
        }
    }
}

/*================================================================================================*/

impl IndexMut <u8> for Vec2f {

    // Index operator (mutable)
    fn index_mut (&mut self, index : u8) -> &mut f32 {

        match index {

            0 => &mut self.x,
            1 => &mut self.y,
            _ => unreachable! ("Index out of range for Vec2f")
        }
    }
}
<|endoftext|>


<commit_before>
use {
    AddColor,
    Borrowed,
    Field,
    Matrix2d,
    TweenPolygonsColorContext,
    Value,
};

/// An animation inbetweening context with color.
pub struct TweenPolygonsContext<'a> {
    /// Base/origin transform.
    pub base: Field<'a, Matrix2d>,
    /// Current transform.
    pub transform: Field<'a, Matrix2d>,
    /// Animation inbetweening factor.
    pub tween_factor: Field<'a, f64>,
    /// The animated polygons.
    pub polygons: Field<'a, &'a [&'a [f64]]>,
}

impl<'a> AddColor<'a, TweenPolygonsColorContext<'a>> for TweenPolygonsContext<'a> {
    /// Creates a RectangleColorContext.
    #[inline(always)]
    fn rgba(&'a self, r: f32, g: f32, b: f32, a: f32) -> TweenPolygonsColorContext<'a> {
        TweenPolygonsColorContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.transform.get()),
            color: Value([r, g, b, a]),
            tween_factor: Borrowed(self.tween_factor.get()),
            polygons: Borrowed(self.polygons.get()),
        }
    }
}

<commit_msg>Implemented `HasTransform` and `CanTransform` for `TweenPolygonsContext`<commit_after>
use {
    AddColor,
    Borrowed,
    Field,
    Matrix2d,
    TweenPolygonsColorContext,
    Value,
};
use internal::{
    CanTransform,
    HasTransform,
};

/// An animation inbetweening context with color.
pub struct TweenPolygonsContext<'a> {
    /// Base/origin transform.
    pub base: Field<'a, Matrix2d>,
    /// Current transform.
    pub transform: Field<'a, Matrix2d>,
    /// Animation inbetweening factor.
    pub tween_factor: Field<'a, f64>,
    /// The animated polygons.
    pub polygons: Field<'a, &'a [&'a [f64]]>,
}

impl<'a> AddColor<'a, TweenPolygonsColorContext<'a>> for TweenPolygonsContext<'a> {
    /// Creates a RectangleColorContext.
    #[inline(always)]
    fn rgba(&'a self, r: f32, g: f32, b: f32, a: f32) -> TweenPolygonsColorContext<'a> {
        TweenPolygonsColorContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.transform.get()),
            color: Value([r, g, b, a]),
            tween_factor: Borrowed(self.tween_factor.get()),
            polygons: Borrowed(self.polygons.get()),
        }
    }
}

impl<'a> HasTransform<'a, Matrix2d> for TweenPolygonsContext<'a> {
    #[inline(alwyas)]
    fn get_transform(&'a self) -> &'a Matrix2d {
        self.transform.get()
    }
}

impl<'a> CanTransform<'a, TweenPolygonsContext<'a>, Matrix2d> for TweenPolygonsContext<'a> {
    #[inline(always)]
    fn transform(&'a self, value: Matrix2d) -> TweenPolygonsContext<'a> {
        TweenPolygonsContext {
            base: Borrowed(self.base.get()),
            transform: Value(value),
            tween_factor: Borrowed(self.tween_factor.get()),
            polygons: Borrowed(self.polygons.get()),
        }
    }
}

<|endoftext|>


<commit_before>// Copyright 2017 The Grin 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.

//! Main interface for callers into cuckoo-miner. Provides functionality
//! to load a mining plugin, send it a Cuckoo Cycle POW problem, and
//! return any result solutions that may be found.
//!
//! #Example
//! ```
//!  let mut config = CuckooMinerConfig::new();
//!  config.plugin_full_path = caps[0].full_path.clone();
//!
//!  //set the number of threads for the miner to use
//!  config.num_threads=2;
//!
//!  //set the number of trimes, 0 lets the plugin decide
//!  config.num_trims=0;
//!
//!  //Build a new miner with this info, which will load
//!  //the associated plugin and
//!
//!  let mut miner = CuckooMiner::new(config).expect("");
//!
//!  //Keep a structure to hold the solution.. this will be
//!  //filled out by the plugin
//!  let mut solution = CuckooMinerSolution::new();
//!
//!  //Mine with given header and check for result
//!  let result = miner.mine(&test_header, &mut solution).unwrap();
//!
//!  if result == true {
//!      println!("Solution found: {}", solution);
//!  } else {
//!      println!("No Solution found");
//!  }
/// ```

use std::{fmt,cmp};
use std::collections::HashMap;

use cuckoo_sys::{call_cuckoo,
                 load_cuckoo_lib,
                 call_cuckoo_set_parameter};

use error::CuckooMinerError;

// Hardcoed assumption for now that the solution size will be 42 will be
// maintained, to avoid having to allocate memory within the called C functions

const CUCKOO_SOLUTION_SIZE:usize = 42;

/// A simple struct to hold a cuckoo miner solution. Currently,
/// it's assumed that a solution will be 42 bytes. The `solution_nonces`
/// member is statically allocated here, and will be filled in
/// by a plugin upon finding a solution.
///

pub struct CuckooMinerSolution {
    /// An array allocated in rust that will be filled
    /// by the called plugin upon successfully finding
    /// a solution

    pub solution_nonces:[u32; CUCKOO_SOLUTION_SIZE],
}

impl Default for CuckooMinerSolution {
	fn default() -> CuckooMinerSolution {
        CuckooMinerSolution {
		    solution_nonces: [0; CUCKOO_SOLUTION_SIZE],
        }
	}
}

impl CuckooMinerSolution{

    /// Creates a new cuckoo miner solution
    /// with nonces set to a u32 array of size
    /// 42 filled with zeroes.

    pub fn new()->CuckooMinerSolution{
        CuckooMinerSolution::default()
    }

    /// Sets the solution, mostly for testing
    pub fn set_solution(&mut self, nonces:[u32; CUCKOO_SOLUTION_SIZE]){
        self.solution_nonces = nonces;
    }
}

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

impl fmt::Debug for CuckooMinerSolution {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,"{:?}", &self.solution_nonces[..])
    }
}

impl cmp::PartialEq for CuckooMinerSolution {
    fn eq(&self, other: &CuckooMinerSolution) -> bool {
        for i in 0..CUCKOO_SOLUTION_SIZE {
            if self.solution_nonces[i]!=other.solution_nonces[i]{
                return false;
            }
        }
        return true;
    }
}

/// Structure containing the configuration values to pass into an
/// instance of a miner

pub struct CuckooMinerConfig {

    /// The full path to the plugin to load and use to find a solution
    /// to a POW problem. Defaults to empty string, so must be filled
    /// before use.
    pub plugin_full_path: String,

    /// A parameter list, which differs depending on which
    /// plugin is being called
    pub parameter_list: HashMap<String, u32>,

}

impl Default for CuckooMinerConfig {
	fn default() -> CuckooMinerConfig {
		CuckooMinerConfig{
            plugin_full_path: String::from(""),
            parameter_list: HashMap::new(),
		}
	}
}

impl CuckooMinerConfig{

    /// Returns a new instance of CuckooMinerConfig

    pub fn new()->CuckooMinerConfig{
        CuckooMinerConfig::default()
    }
}

/// An instance of a miner, which loads a cuckoo-miner plugin
/// and calls it's mine function according to the provided configuration
///

pub struct CuckooMiner{
    /// The internal Configuration object
    pub config: CuckooMinerConfig,
}

impl Default for CuckooMiner {
	fn default() -> CuckooMiner {
		CuckooMiner {
            config: CuckooMinerConfig::default(),
		}
	}
}

impl CuckooMiner {

    /// #Description
    ///
    /// Creates a new instance of a CuckooMiner with the given configuration.
    ///
    /// #Arguments
    ///
    /// * `config` an instance of [CuckooMinerConfig](struct.CuckooMinerConfig.html), that
    /// must be filled with the full path name of a valid mining plugin. It may also contain
    /// values in its `parameter_list` field, which will be automatically set in the plugin
    ///
    /// #Returns
    ///
    /// If successful, Ok() is returned and the specified plugin has been loaded internally.
    /// Otherwise a [CuckooMinerError](../../error/error/enum.CuckooMinerError.html)
    /// with specific detail is returned.
    ///

    pub fn new(config:CuckooMinerConfig)->Result<CuckooMiner, CuckooMinerError>{
        let mut return_val=CuckooMiner{
            config: config,
        };
        return_val.init()?;
        //set any parameters provided in the config
        for (name, value) in return_val.config.parameter_list.clone() {
           return_val.set_parameter(name.clone(), value.clone())?;
        }

        Ok(return_val)
    }

    /// Internal function to perform tha actual library loading

    fn init(&mut self) -> Result<(), CuckooMinerError> {
        load_cuckoo_lib(&self.config.plugin_full_path)
    }

    /// #Description
    ///
    /// Sets a parameter in the currently loaded plugin
    ///
    /// #Arguments
    ///
    /// * `name` The name of the parameter to set
    ///
    /// * `value` The value to set the parameter to
    ///
    /// #Returns
    ///
    /// If successful, Ok() is returned and the parameter has been set.
    /// Otherwise a [CuckooMinerError](../../error/error/enum.CuckooMinerError.html)
    /// with specific detail is returned.
    ///

    pub fn set_parameter(&mut self, name: String, value:u32) -> Result<(), CuckooMinerError>{
        let return_code = call_cuckoo_set_parameter(name.as_bytes(), value)?;
        if return_code != 0 {

            let reason = match return_code {
                1 => "Property doesn't exist for this plugin",
                2 => "Property outside allowed range",
                _ => "Unknown Error"
            };

            return Err(CuckooMinerError::ParameterError(String::from(
                format!("Error setting parameter: {} to {} - {}", name, value, reason)
                )));
        }
        Ok(())
    }

    /// #Description
    ///
    /// Call to the cuckoo_call function of the currently loaded plugin, which will perform
    /// a Cuckoo Cycle on the given seed, filling the first solution (a length 42 cycle)
    /// that is found in the provided [CuckooMinerSolution](struct.CuckooMinerSolution.html) structure.
    /// The implementation details are dependent on particular loaded plugin. Values provided
    /// to the loaded plugin are contained in the internal [CuckooMinerConfig](struct.CuckooMinerConfig.html)
    ///
    /// #Arguments
    ///
    /// * `header` (IN) A reference to a block of [u8] bytes to use for the seed to the
    ///    internal SIPHASH function which generates edge locations in the graph. In practice,
    ///    this is a SHA3 hash of a Grin blockheader, but from the plugin's perspective this
    ///    can be anything.
    ///
    /// * `solution` (OUT) An empty [CuckooMinerSolution](struct.CuckooMinerSolution.html).
    ///    If a solution is found, this structure will contain a list of solution nonces,
    ///    otherwise, it will remain untouched.
    ///
    /// #Returns
    ///
    /// * Ok(true) if a solution is found, with the 42 solution nonces contained within
    /// the provided [CuckooMinerSolution](struct.CuckooMinerSolution.html).
    /// * Ok(false) if no solution is found and `solution` remains untouched.
    /// * A [CuckooMinerError](../../error/error/enum.CuckooMinerError.html)
    /// if there is no plugin loaded, or if there is an error calling the function.
    ///

    pub fn mine(&self, header: &[u8], solution:&mut CuckooMinerSolution)
        -> Result<bool, CuckooMinerError> {
            match call_cuckoo(header,
                              &mut solution.solution_nonces) {
                Ok(result) => {
                    match result {
                        1 => {
                            debug!("Solution found.");
                            Ok(true)
                        }
                        0 => Ok(false),
                        _ => Err(CuckooMinerError::UnexpectedResultError(result))
                    }
                },
                Err(_) => Err(CuckooMinerError::PluginNotLoadedError(
                    String::from("Please call init to load a miner plug-in"))),
            }
    }
}<commit_msg>Making cuckoo miner configuration clonable<commit_after>// Copyright 2017 The Grin 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.

//! Main interface for callers into cuckoo-miner. Provides functionality
//! to load a mining plugin, send it a Cuckoo Cycle POW problem, and
//! return any result solutions that may be found.
//!
//! #Example
//! ```
//!  let mut config = CuckooMinerConfig::new();
//!  config.plugin_full_path = caps[0].full_path.clone();
//!
//!  //set the number of threads for the miner to use
//!  config.num_threads=2;
//!
//!  //set the number of trimes, 0 lets the plugin decide
//!  config.num_trims=0;
//!
//!  //Build a new miner with this info, which will load
//!  //the associated plugin and
//!
//!  let mut miner = CuckooMiner::new(config).expect("");
//!
//!  //Keep a structure to hold the solution.. this will be
//!  //filled out by the plugin
//!  let mut solution = CuckooMinerSolution::new();
//!
//!  //Mine with given header and check for result
//!  let result = miner.mine(&test_header, &mut solution).unwrap();
//!
//!  if result == true {
//!      println!("Solution found: {}", solution);
//!  } else {
//!      println!("No Solution found");
//!  }
/// ```

use std::{fmt,cmp};
use std::collections::HashMap;

use cuckoo_sys::{call_cuckoo,
                 load_cuckoo_lib,
                 call_cuckoo_set_parameter};

use error::CuckooMinerError;

// Hardcoed assumption for now that the solution size will be 42 will be
// maintained, to avoid having to allocate memory within the called C functions

const CUCKOO_SOLUTION_SIZE:usize = 42;

/// A simple struct to hold a cuckoo miner solution. Currently,
/// it's assumed that a solution will be 42 bytes. The `solution_nonces`
/// member is statically allocated here, and will be filled in
/// by a plugin upon finding a solution.
///

pub struct CuckooMinerSolution {
    /// An array allocated in rust that will be filled
    /// by the called plugin upon successfully finding
    /// a solution

    pub solution_nonces:[u32; CUCKOO_SOLUTION_SIZE],
}

impl Default for CuckooMinerSolution {
	fn default() -> CuckooMinerSolution {
        CuckooMinerSolution {
		    solution_nonces: [0; CUCKOO_SOLUTION_SIZE],
        }
	}
}

impl CuckooMinerSolution{

    /// Creates a new cuckoo miner solution
    /// with nonces set to a u32 array of size
    /// 42 filled with zeroes.

    pub fn new()->CuckooMinerSolution{
        CuckooMinerSolution::default()
    }

    /// Sets the solution, mostly for testing
    pub fn set_solution(&mut self, nonces:[u32; CUCKOO_SOLUTION_SIZE]){
        self.solution_nonces = nonces;
    }
}

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

impl fmt::Debug for CuckooMinerSolution {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,"{:?}", &self.solution_nonces[..])
    }
}

impl cmp::PartialEq for CuckooMinerSolution {
    fn eq(&self, other: &CuckooMinerSolution) -> bool {
        for i in 0..CUCKOO_SOLUTION_SIZE {
            if self.solution_nonces[i]!=other.solution_nonces[i]{
                return false;
            }
        }
        return true;
    }
}

/// Structure containing the configuration values to pass into an
/// instance of a miner
#[derive(Debug, Clone)]
pub struct CuckooMinerConfig {

    /// The full path to the plugin to load and use to find a solution
    /// to a POW problem. Defaults to empty string, so must be filled
    /// before use.
    pub plugin_full_path: String,

    /// A parameter list, which differs depending on which
    /// plugin is being called
    pub parameter_list: HashMap<String, u32>,

}

impl Default for CuckooMinerConfig {
	fn default() -> CuckooMinerConfig {
		CuckooMinerConfig{
            plugin_full_path: String::from(""),
            parameter_list: HashMap::new(),
		}
	}
}

impl CuckooMinerConfig{

    /// Returns a new instance of CuckooMinerConfig

    pub fn new()->CuckooMinerConfig{
        CuckooMinerConfig::default()
    }
}

/// An instance of a miner, which loads a cuckoo-miner plugin
/// and calls it's mine function according to the provided configuration
///

pub struct CuckooMiner{
    /// The internal Configuration object
    pub config: CuckooMinerConfig,
}

impl Default for CuckooMiner {
	fn default() -> CuckooMiner {
		CuckooMiner {
            config: CuckooMinerConfig::default(),
		}
	}
}

impl CuckooMiner {

    /// #Description
    ///
    /// Creates a new instance of a CuckooMiner with the given configuration.
    ///
    /// #Arguments
    ///
    /// * `config` an instance of [CuckooMinerConfig](struct.CuckooMinerConfig.html), that
    /// must be filled with the full path name of a valid mining plugin. It may also contain
    /// values in its `parameter_list` field, which will be automatically set in the plugin
    ///
    /// #Returns
    ///
    /// If successful, Ok() is returned and the specified plugin has been loaded internally.
    /// Otherwise a [CuckooMinerError](../../error/error/enum.CuckooMinerError.html)
    /// with specific detail is returned.
    ///

    pub fn new(config:CuckooMinerConfig)->Result<CuckooMiner, CuckooMinerError>{
        let mut return_val=CuckooMiner{
            config: config,
        };
        return_val.init()?;
        //set any parameters provided in the config
        for (name, value) in return_val.config.parameter_list.clone() {
           return_val.set_parameter(name.clone(), value.clone())?;
        }

        Ok(return_val)
    }

    /// Internal function to perform tha actual library loading

    fn init(&mut self) -> Result<(), CuckooMinerError> {
        load_cuckoo_lib(&self.config.plugin_full_path)
    }

    /// #Description
    ///
    /// Sets a parameter in the currently loaded plugin
    ///
    /// #Arguments
    ///
    /// * `name` The name of the parameter to set
    ///
    /// * `value` The value to set the parameter to
    ///
    /// #Returns
    ///
    /// If successful, Ok() is returned and the parameter has been set.
    /// Otherwise a [CuckooMinerError](../../error/error/enum.CuckooMinerError.html)
    /// with specific detail is returned.
    ///

    pub fn set_parameter(&mut self, name: String, value:u32) -> Result<(), CuckooMinerError>{
        let return_code = call_cuckoo_set_parameter(name.as_bytes(), value)?;
        if return_code != 0 {

            let reason = match return_code {
                1 => "Property doesn't exist for this plugin",
                2 => "Property outside allowed range",
                _ => "Unknown Error"
            };

            return Err(CuckooMinerError::ParameterError(String::from(
                format!("Error setting parameter: {} to {} - {}", name, value, reason)
                )));
        }
        Ok(())
    }

    /// #Description
    ///
    /// Call to the cuckoo_call function of the currently loaded plugin, which will perform
    /// a Cuckoo Cycle on the given seed, filling the first solution (a length 42 cycle)
    /// that is found in the provided [CuckooMinerSolution](struct.CuckooMinerSolution.html) structure.
    /// The implementation details are dependent on particular loaded plugin. Values provided
    /// to the loaded plugin are contained in the internal [CuckooMinerConfig](struct.CuckooMinerConfig.html)
    ///
    /// #Arguments
    ///
    /// * `header` (IN) A reference to a block of [u8] bytes to use for the seed to the
    ///    internal SIPHASH function which generates edge locations in the graph. In practice,
    ///    this is a SHA3 hash of a Grin blockheader, but from the plugin's perspective this
    ///    can be anything.
    ///
    /// * `solution` (OUT) An empty [CuckooMinerSolution](struct.CuckooMinerSolution.html).
    ///    If a solution is found, this structure will contain a list of solution nonces,
    ///    otherwise, it will remain untouched.
    ///
    /// #Returns
    ///
    /// * Ok(true) if a solution is found, with the 42 solution nonces contained within
    /// the provided [CuckooMinerSolution](struct.CuckooMinerSolution.html).
    /// * Ok(false) if no solution is found and `solution` remains untouched.
    /// * A [CuckooMinerError](../../error/error/enum.CuckooMinerError.html)
    /// if there is no plugin loaded, or if there is an error calling the function.
    ///

    pub fn mine(&self, header: &[u8], solution:&mut CuckooMinerSolution)
        -> Result<bool, CuckooMinerError> {
            match call_cuckoo(header,
                              &mut solution.solution_nonces) {
                Ok(result) => {
                    match result {
                        1 => {
                            debug!("Solution found.");
                            Ok(true)
                        }
                        0 => Ok(false),
                        _ => Err(CuckooMinerError::UnexpectedResultError(result))
                    }
                },
                Err(_) => Err(CuckooMinerError::PluginNotLoadedError(
                    String::from("Please call init to load a miner plug-in"))),
            }
    }
}<|endoftext|>