<commit_before>//! This exposes the command line interface that the binary uses
use std::fs;
use std::io;
use std::env;
use std::process;
use std::sync::Mutex;

use clap::{App, Arg, SubCommand, ArgMatches, AppSettings};
use chrono;
use log;
use openssl_probe::init_ssl_cert_env_vars;

use super::{Result, ResultExt, Error};
use super::sdk::{Sdk, SdkInfo, DumpOptions};
use super::config::Config;
use super::constants::VERSION;
use super::memdb::stash::{MemDbStash, SyncOptions};
use super::api::server::{ApiServer, BindOptions};

struct SimpleLogger<W: ?Sized> {
    f: Mutex<Box<W>>,
}

impl<W: io::Write + Send + ?Sized> log::Log for SimpleLogger<W> {

    fn enabled(&self, metadata: &log::LogMetadata) -> bool {
        metadata.level() <= log::LogLevel::Info
    }

    fn log(&self, record: &log::LogRecord) {
        let mut f = self.f.lock().unwrap();
        if self.enabled(record.metadata()) {
            writeln!(f, "[{}] {} | {}{}",
                     chrono::Local::now(),
                     record.target().split(':').next().unwrap(),
                     match record.level() {
                         log::LogLevel::Error => "ERROR: ",
                         log::LogLevel::Warn => "WARNING: ",
                         _ => "",
                     },
                     record.args()).ok();
        }
    }
}

fn setup_openssl() {
    init_ssl_cert_env_vars();
}

fn setup_logging(config: &Config) -> Result<()> {
    let filter = config.get_log_level_filter()?;
    if filter >= log::LogLevel::Debug {
        env::set_var("RUST_BACKTRACE", "1");
    }

    let f : Box<io::Write + Send> = match config.get_log_filename()? {
        Some(path) => Box::new(fs::File::open(path)?),
        None => Box::new(io::stdout()),
    };
    log::set_logger(|max_log_level| {
        max_log_level.set(filter);
        Box::new(SimpleLogger {
            f: Mutex::new(f),
        })
    }).unwrap();
    Ok(())
}

/// Main entry point that starts the CLI
pub fn main() {
    match execute() {
        Ok(()) => {},
        Err(err) => {
            use std::error::Error;
            println!("error: {}", err);
            let mut cause = err.cause();
            while let Some(the_cause) = cause {
                println!("  caused by: {}", the_cause);
                cause = the_cause.cause();
            }
            if let Some(backtrace) = err.backtrace() {
                debug!("  Traceback: {:?}", backtrace);
            }
            process::exit(1);
        }
    }
}

fn config_from_matches(matches: &ArgMatches) -> Result<Config> {
    let mut cfg = if let Some(config_path) = matches.value_of("config") {
        Config::load_file(config_path)?
    } else {
        Config::load_default()?
    };

    if let Some(value) = matches.value_of("log_level") {
        let filter = value.parse()
            .map_err(|_| Error::from("Invalid value for log level"))?;
        cfg.set_log_level_filter(filter);
    };

    if let Some(value) = matches.value_of("symbol_dir") {
        cfg.set_symbol_dir(value);
    }

    if let Some(value) = matches.value_of("aws_bucket_url") {
        cfg.set_aws_bucket_url(value);
    }

    if let Some(value) = matches.value_of("aws_region") {
        let region = value.parse()
            .map_err(|_| Error::from("Invalid AWS region"))?;
        cfg.set_aws_region(region);
    }

    Ok(cfg)
}

fn execute() -> Result<()> {
    setup_openssl();

    let app = App::new("sentry-symbolserver")
        .version(VERSION)
        .about("This tool implements an Apple SDK processor and server.")
        .setting(AppSettings::SubcommandRequiredElseHelp)
        .setting(AppSettings::ColorNever)
        .arg(Arg::with_name("config")
             .long("config")
             .value_name("FILE")
             .help("The path to the config file"))
        .arg(Arg::with_name("log_level")
             .short("l")
             .long("log-level")
             .value_name("LEVEL")
             .help("Overrides the log level from the config"))
        .arg(Arg::with_name("symbol_dir")
             .short("p")
             .long("symbol-dir")
             .value_name("PATH")
             .help("The path to the symbol directory"))
        .arg(Arg::with_name("aws_bucket_url")
             .long("aws-bucket-url")
             .value_name("URL")
             .help("The bucket URL the sync tool should pull from"))
        .arg(Arg::with_name("aws_region")
             .long("aws-region")
             .value_name("REGION")
             .help("Sets the AWS region the bucket is located in"))
        .subcommand(
            SubCommand::with_name("sync")
                .about("Updates symbols from S3"))
        .subcommand(
            SubCommand::with_name("run")
                .about("Runs the symbol server")
                .arg(Arg::with_name("disable_sync")
                     .long("disable-sync")
                     .help("Disables the background synching"))
                .arg(Arg::with_name("bind")
                     .long("bind")
                     .value_name("ADDR")
                     .help("Bind to a specific address (ip:port)"))
                .arg(Arg::with_name("bind_fd")
                     .long("bind-fd")
                     .value_name("FD")
                     .help("Bind to a specific file descriptor"))
                .arg(Arg::with_name("threads")
                     .long("threads")
                     .short("t")
                     .value_name("COUNT")
                     .help("Overrides the default listener thread count")))
        .subcommand(
            SubCommand::with_name("convert-sdk")
                .about("Converts an SDK into a memdb file")
                .arg(Arg::with_name("path")
                     .index(1)
                     .value_name("PATH")
                     .required(true)
                     .multiple(true)
                     .help("Path to the support folder"))
                .arg(Arg::with_name("compress")
                     .short("c")
                     .long("compress")
                     .help("Write compressed files instead."))
                .arg(Arg::with_name("output-path")
                     .short("o")
                     .long("output")
                     .help("Where the result should be stored")))
        .subcommand(
            SubCommand::with_name("dump-object")
                .about("Dumps an object from a memdb in the stash")
                .arg(Arg::with_name("sdk_id")
                     .index(1)
                     .value_name("SDK_ID")
                     .required(true)
                     .help("The SDK id to dump"))
                .arg(Arg::with_name("name_or_uuid")
                     .index(2)
                     .value_name("NAME_OR_UUID")
                     .required(true)
                     .help("The object to dump")));
    let matches = app.get_matches();

    let cfg = config_from_matches(&matches)?;
    setup_logging(&cfg)?;

    if let Some(matches) = matches.subcommand_matches("convert-sdk") {
        convert_sdk_action(matches.values_of("path").unwrap().collect(),
                           matches.value_of("output-path").unwrap_or("."),
                           matches.is_present("compress"))?;
    } else if let Some(matches) = matches.subcommand_matches("dump-object") {
        dump_object_action(&cfg, matches.value_of("sdk_id").unwrap(),
                           matches.value_of("name_or_uuid").unwrap())?;
    } else if let Some(matches) = matches.subcommand_matches("run") {
        run_action(&cfg, matches)?;
    } else if let Some(_matches) = matches.subcommand_matches("sync") {
        sync_action(&cfg)?;
    }

    Ok(())
}

fn convert_sdk_action(paths: Vec<&str>, output_path: &str, compress: bool)
    -> Result<()>
{
    let dst_base = env::current_dir().unwrap().join(output_path);

    for (idx, path) in paths.iter().enumerate() {
        if idx > 0 {
            println!("");
        }
        let sdk = Sdk::new(&path)?;
        let mut dst = dst_base.join(sdk.info().memdb_filename());
        if compress {
            dst.set_extension("memdbz");
        }

        println!("SDK {} ({} {}):", sdk.info().name(),
                 sdk.info().version(), sdk.info().build().unwrap_or("UNKNONW"));

        // make sure we close the file at the end, in case we want to
        // re-open it for compressing.
        let f = fs::File::create(&dst)?;
        let options = DumpOptions {
            show_progress_bar: true,
            compress: compress,
            ..Default::default()
        };
        sdk.dump_memdb(f, options)?;
    }

    Ok(())
}

fn dump_object_action(config: &Config, sdk_id: &str, name_or_uuid: &str) -> Result<()> {
    let stash = MemDbStash::new(config)?;
    let info = SdkInfo::from_filename(sdk_id).ok_or_else(||
        Error::from("Invalid SDK ID"))?;
    let memdb = stash.get_memdb(&info)?;
    let uuid = memdb.find_uuid_fuzzy(name_or_uuid)?.ok_or_else(||
        Error::from("Object not found in SDK"))?;

    for item_rv in memdb.iter_symbols(uuid)? {
        let item = item_rv?;
        println!("{:>014} {}", item.addr(), item.symbol());
    }
    Ok(())
}

fn sync_action(config: &Config) -> Result<()> {
    let stash = MemDbStash::new(config)?;
    stash.sync(SyncOptions {
        user_facing: true,
        ..Default::default()
    })?;
    Ok(())
}

fn run_action(config: &Config, matches: &ArgMatches) -> Result<()> {
    let api_server = ApiServer::new(config, !matches.is_present("disable_sync"))?;

    let threads: usize = if let Some(threads) = matches.value_of("threads") {
        threads.parse().chain_err(|| "invalid value for threads")?
    } else {
        config.get_server_threads()?
    };

    api_server.run(threads, if let Some(addr) = matches.value_of("bind") {
        BindOptions::BindToAddr(addr)
    } else if let Some(fd) = matches.value_of("bind_fd") {
        BindOptions::BindToFd(fd.parse().chain_err(|| "invalid value for file descriptor")?)
    } else {
        BindOptions::UseConfig
    })?;

    Ok(())
}
<commit_msg>Output hex instead of int in dump sdk<commit_after>//! This exposes the command line interface that the binary uses
use std::fs;
use std::io;
use std::env;
use std::process;
use std::sync::Mutex;

use clap::{App, Arg, SubCommand, ArgMatches, AppSettings};
use chrono;
use log;
use openssl_probe::init_ssl_cert_env_vars;

use super::{Result, ResultExt, Error};
use super::sdk::{Sdk, SdkInfo, DumpOptions};
use super::config::Config;
use super::constants::VERSION;
use super::memdb::stash::{MemDbStash, SyncOptions};
use super::api::server::{ApiServer, BindOptions};

struct SimpleLogger<W: ?Sized> {
    f: Mutex<Box<W>>,
}

impl<W: io::Write + Send + ?Sized> log::Log for SimpleLogger<W> {

    fn enabled(&self, metadata: &log::LogMetadata) -> bool {
        metadata.level() <= log::LogLevel::Info
    }

    fn log(&self, record: &log::LogRecord) {
        let mut f = self.f.lock().unwrap();
        if self.enabled(record.metadata()) {
            writeln!(f, "[{}] {} | {}{}",
                     chrono::Local::now(),
                     record.target().split(':').next().unwrap(),
                     match record.level() {
                         log::LogLevel::Error => "ERROR: ",
                         log::LogLevel::Warn => "WARNING: ",
                         _ => "",
                     },
                     record.args()).ok();
        }
    }
}

fn setup_openssl() {
    init_ssl_cert_env_vars();
}

fn setup_logging(config: &Config) -> Result<()> {
    let filter = config.get_log_level_filter()?;
    if filter >= log::LogLevel::Debug {
        env::set_var("RUST_BACKTRACE", "1");
    }

    let f : Box<io::Write + Send> = match config.get_log_filename()? {
        Some(path) => Box::new(fs::File::open(path)?),
        None => Box::new(io::stdout()),
    };
    log::set_logger(|max_log_level| {
        max_log_level.set(filter);
        Box::new(SimpleLogger {
            f: Mutex::new(f),
        })
    }).unwrap();
    Ok(())
}

/// Main entry point that starts the CLI
pub fn main() {
    match execute() {
        Ok(()) => {},
        Err(err) => {
            use std::error::Error;
            println!("error: {}", err);
            let mut cause = err.cause();
            while let Some(the_cause) = cause {
                println!("  caused by: {}", the_cause);
                cause = the_cause.cause();
            }
            if let Some(backtrace) = err.backtrace() {
                debug!("  Traceback: {:?}", backtrace);
            }
            process::exit(1);
        }
    }
}

fn config_from_matches(matches: &ArgMatches) -> Result<Config> {
    let mut cfg = if let Some(config_path) = matches.value_of("config") {
        Config::load_file(config_path)?
    } else {
        Config::load_default()?
    };

    if let Some(value) = matches.value_of("log_level") {
        let filter = value.parse()
            .map_err(|_| Error::from("Invalid value for log level"))?;
        cfg.set_log_level_filter(filter);
    };

    if let Some(value) = matches.value_of("symbol_dir") {
        cfg.set_symbol_dir(value);
    }

    if let Some(value) = matches.value_of("aws_bucket_url") {
        cfg.set_aws_bucket_url(value);
    }

    if let Some(value) = matches.value_of("aws_region") {
        let region = value.parse()
            .map_err(|_| Error::from("Invalid AWS region"))?;
        cfg.set_aws_region(region);
    }

    Ok(cfg)
}

fn execute() -> Result<()> {
    setup_openssl();

    let app = App::new("sentry-symbolserver")
        .version(VERSION)
        .about("This tool implements an Apple SDK processor and server.")
        .setting(AppSettings::SubcommandRequiredElseHelp)
        .setting(AppSettings::ColorNever)
        .arg(Arg::with_name("config")
             .long("config")
             .value_name("FILE")
             .help("The path to the config file"))
        .arg(Arg::with_name("log_level")
             .short("l")
             .long("log-level")
             .value_name("LEVEL")
             .help("Overrides the log level from the config"))
        .arg(Arg::with_name("symbol_dir")
             .short("p")
             .long("symbol-dir")
             .value_name("PATH")
             .help("The path to the symbol directory"))
        .arg(Arg::with_name("aws_bucket_url")
             .long("aws-bucket-url")
             .value_name("URL")
             .help("The bucket URL the sync tool should pull from"))
        .arg(Arg::with_name("aws_region")
             .long("aws-region")
             .value_name("REGION")
             .help("Sets the AWS region the bucket is located in"))
        .subcommand(
            SubCommand::with_name("sync")
                .about("Updates symbols from S3"))
        .subcommand(
            SubCommand::with_name("run")
                .about("Runs the symbol server")
                .arg(Arg::with_name("disable_sync")
                     .long("disable-sync")
                     .help("Disables the background synching"))
                .arg(Arg::with_name("bind")
                     .long("bind")
                     .value_name("ADDR")
                     .help("Bind to a specific address (ip:port)"))
                .arg(Arg::with_name("bind_fd")
                     .long("bind-fd")
                     .value_name("FD")
                     .help("Bind to a specific file descriptor"))
                .arg(Arg::with_name("threads")
                     .long("threads")
                     .short("t")
                     .value_name("COUNT")
                     .help("Overrides the default listener thread count")))
        .subcommand(
            SubCommand::with_name("convert-sdk")
                .about("Converts an SDK into a memdb file")
                .arg(Arg::with_name("path")
                     .index(1)
                     .value_name("PATH")
                     .required(true)
                     .multiple(true)
                     .help("Path to the support folder"))
                .arg(Arg::with_name("compress")
                     .short("c")
                     .long("compress")
                     .help("Write compressed files instead."))
                .arg(Arg::with_name("output-path")
                     .short("o")
                     .long("output")
                     .help("Where the result should be stored")))
        .subcommand(
            SubCommand::with_name("dump-object")
                .about("Dumps an object from a memdb in the stash")
                .arg(Arg::with_name("sdk_id")
                     .index(1)
                     .value_name("SDK_ID")
                     .required(true)
                     .help("The SDK id to dump"))
                .arg(Arg::with_name("name_or_uuid")
                     .index(2)
                     .value_name("NAME_OR_UUID")
                     .required(true)
                     .help("The object to dump")));
    let matches = app.get_matches();

    let cfg = config_from_matches(&matches)?;
    setup_logging(&cfg)?;

    if let Some(matches) = matches.subcommand_matches("convert-sdk") {
        convert_sdk_action(matches.values_of("path").unwrap().collect(),
                           matches.value_of("output-path").unwrap_or("."),
                           matches.is_present("compress"))?;
    } else if let Some(matches) = matches.subcommand_matches("dump-object") {
        dump_object_action(&cfg, matches.value_of("sdk_id").unwrap(),
                           matches.value_of("name_or_uuid").unwrap())?;
    } else if let Some(matches) = matches.subcommand_matches("run") {
        run_action(&cfg, matches)?;
    } else if let Some(_matches) = matches.subcommand_matches("sync") {
        sync_action(&cfg)?;
    }

    Ok(())
}

fn convert_sdk_action(paths: Vec<&str>, output_path: &str, compress: bool)
    -> Result<()>
{
    let dst_base = env::current_dir().unwrap().join(output_path);

    for (idx, path) in paths.iter().enumerate() {
        if idx > 0 {
            println!("");
        }
        let sdk = Sdk::new(&path)?;
        let mut dst = dst_base.join(sdk.info().memdb_filename());
        if compress {
            dst.set_extension("memdbz");
        }

        println!("SDK {} ({} {}):", sdk.info().name(),
                 sdk.info().version(), sdk.info().build().unwrap_or("UNKNONW"));

        // make sure we close the file at the end, in case we want to
        // re-open it for compressing.
        let f = fs::File::create(&dst)?;
        let options = DumpOptions {
            show_progress_bar: true,
            compress: compress,
            ..Default::default()
        };
        sdk.dump_memdb(f, options)?;
    }

    Ok(())
}

fn dump_object_action(config: &Config, sdk_id: &str, name_or_uuid: &str) -> Result<()> {
    let stash = MemDbStash::new(config)?;
    let info = SdkInfo::from_filename(sdk_id).ok_or_else(||
        Error::from("Invalid SDK ID"))?;
    let memdb = stash.get_memdb(&info)?;
    let uuid = memdb.find_uuid_fuzzy(name_or_uuid)?.ok_or_else(||
        Error::from("Object not found in SDK"))?;

    for item_rv in memdb.iter_symbols(uuid)? {
        let item = item_rv?;
        println!("{:>014x} {}", item.addr(), item.symbol());
    }
    Ok(())
}

fn sync_action(config: &Config) -> Result<()> {
    let stash = MemDbStash::new(config)?;
    stash.sync(SyncOptions {
        user_facing: true,
        ..Default::default()
    })?;
    Ok(())
}

fn run_action(config: &Config, matches: &ArgMatches) -> Result<()> {
    let api_server = ApiServer::new(config, !matches.is_present("disable_sync"))?;

    let threads: usize = if let Some(threads) = matches.value_of("threads") {
        threads.parse().chain_err(|| "invalid value for threads")?
    } else {
        config.get_server_threads()?
    };

    api_server.run(threads, if let Some(addr) = matches.value_of("bind") {
        BindOptions::BindToAddr(addr)
    } else if let Some(fd) = matches.value_of("bind_fd") {
        BindOptions::BindToFd(fd.parse().chain_err(|| "invalid value for file descriptor")?)
    } else {
        BindOptions::UseConfig
    })?;

    Ok(())
}
<|endoftext|>


<commit_before>use std::iter;
use std::collections::vec_deque::VecDeque;
use player::Player;
use cell::Cell;

#[derive(Debug)]
pub struct DfaState {
  empty: i32,
  red: i32,
  black: i32,
  bad: i32,
  pattern: i32
}

impl DfaState {
  pub fn new(empty: i32, red: i32, black: i32, bad: i32, pattern: i32) -> DfaState {
    DfaState {
      empty: empty,
      red: red,
      black: black,
      bad: bad,
      pattern: pattern
    }
  }
}

pub struct Dfa {
  states: Vec<DfaState>
}

impl Dfa {
  pub fn new(states: Vec<DfaState>) -> Dfa {
    Dfa {
      states: states
    }
  }

  pub fn product(&self, other: &Dfa) -> Dfa {
    let other_len = other.states.len();
    let other_len_i32 = other_len as i32;
    let mut new_states = Vec::with_capacity(self.states.len() * other_len);
    for self_state in &self.states {
      let base_empty = (self_state.empty + 1) * other_len_i32;
      let base_red = (self_state.red + 1) * other_len_i32;
      let base_black = (self_state.black + 1) * other_len_i32;
      let base_bad = (self_state.bad + 1) * other_len_i32;
      for other_state in &other.states {
        let new_state = DfaState {
          empty: base_empty + other_state.empty,
          red: base_red + other_state.red,
          black: base_black + other_state.black,
          bad: base_bad + other_state.bad,
          pattern: if self_state.pattern != -1 { self_state.pattern } else { other_state.pattern }
        };
        new_states.push(new_state);
      }
    }
    Dfa {
      states: new_states
    }
  }

  pub fn run<T: Iterator<Item = Cell>>(&self, iter: &mut T) -> Option<u32> {
    let mut state_idx = 0i32;
    loop {
      let state = &self.states[state_idx as usize];
      if state.pattern != -1 {
        return Some(state.pattern as u32);
      }
      if let Some(cell) = iter.next() {
        if cell.is_bad() {
          state_idx = state.bad;
        } else if let Some(player) = cell.get_owner() {
          match player {
            Player::Red => state_idx = state.red,
            Player::Black => state_idx = state.black
          }
        } else {
          state_idx = state.empty;
        }
      } else {
        return None;
      }
      if state_idx == -1 {
        return None;
      }
    }
  }

  fn delete_states(&mut self, states_for_delete: Vec<u32>) {
    let mut shifts = Vec::with_capacity(self.states.len());
    for sd in &states_for_delete {
      let shift = shifts.last().map_or(0, |&d| d) + sd;
      shifts.push(shift);
    }
    let deletions = shifts.last().map_or(0, |&d| d);
    if deletions == 0 {
      return;
    }
    let mut new_states = Vec::with_capacity(self.states.len() - deletions as usize);
    for state in self.states.iter().zip(states_for_delete.into_iter()).filter_map(|(state, sd)| if sd == 0 { Some(state) } else { None }) {
      let new_state = DfaState {
        empty: if state.empty == -1 { -1 } else { state.empty - shifts[state.empty as usize] as i32 },
        red: if state.red == -1 { -1 } else { state.red - shifts[state.red as usize] as i32 },
        black: if state.black == -1 { -1 } else { state.black - shifts[state.black as usize] as i32 },
        bad: if state.bad == -1 { -1 } else { state.bad - shifts[state.bad as usize] as i32 },
        pattern: state.pattern
      };
      new_states.push(new_state);
    }
    self.states = new_states;
  }

  pub fn delete_non_reachable(&mut self) {
    let mut non_reachable = iter::repeat(1).take(self.states.len()).collect::<Vec<u32>>();
    let mut q = VecDeque::with_capacity(self.states.len());
    q.push_back(1);
    while let Some(idx) = q.pop_front() {
      let state = &self.states[idx as usize];
      if state.empty != -1 && non_reachable[state.empty as usize] == 1 {
        non_reachable[state.empty as usize] = 0;
        q.push_back(state.empty);
      }
      if state.red != -1 && non_reachable[state.red as usize] == 1 {
        non_reachable[state.red as usize] = 0;
        q.push_back(state.red);
      }
      if state.black != -1 && non_reachable[state.black as usize] == 1 {
        non_reachable[state.black as usize] = 0;
        q.push_back(state.black);
      }
      if state.bad != -1 && non_reachable[state.bad as usize] == 1 {
        non_reachable[state.bad as usize] = 0;
        q.push_back(state.bad);
      }
    }
    self.delete_states(non_reachable);
  }

  fn pyramid_idx_base(i: usize) -> usize {
    i * (i - 1) / 2
  }

  fn pyramid_idx(i: usize, j: usize) -> usize {
    Dfa::pyramid_idx_base(i) + j
  }

  pub fn minimize(&mut self) {
    let len = self.states.len();
    let mut not_equal = iter::repeat(0).take(len * (len - 1) / 2 + len - 1).collect::<Vec<u32>>();
    for (i, pattern_i) in self.states.iter().enumerate().skip(1) {
      let base = Dfa::pyramid_idx_base(i);
      for (j, pattern_j) in self.states[.. i - 1].iter().enumerate() {
        if pattern_i.pattern != pattern_j.pattern {
          not_equal[base + j] = 1;
        }
      }
    }
    'outer: loop {
      for (i, pattern_i) in self.states.iter().enumerate().skip(1) {
        let base = Dfa::pyramid_idx_base(i);
        for (j, pattern_j) in self.states[.. i - 1].iter().enumerate() {
          let idx = base + j;
          if not_equal[idx] == 0 {
            if pattern_i.empty != pattern_j.empty && (pattern_i.empty == -1 || pattern_j.empty == -1 || not_equal[Dfa::pyramid_idx(pattern_i.empty as usize, pattern_j.empty as usize)] == 1) ||
               pattern_i.red != pattern_j.red && (pattern_i.red == -1 || pattern_j.red == -1 || not_equal[Dfa::pyramid_idx(pattern_i.red as usize, pattern_j.red as usize)] == 1) ||
               pattern_i.black != pattern_j.black && (pattern_i.black == -1 || pattern_j.black == -1 || not_equal[Dfa::pyramid_idx(pattern_i.black as usize, pattern_j.black as usize)] == 1) ||
               pattern_i.bad != pattern_j.bad && (pattern_i.bad == -1 || pattern_j.bad == -1 || not_equal[Dfa::pyramid_idx(pattern_i.bad as usize, pattern_j.bad as usize)] == 1) {
              not_equal[idx] = 1;
              continue 'outer;
            }
          }
        }
      }
      break;
    }
    let mut deleted = iter::repeat(0).take(self.states.len()).collect::<Vec<u32>>();
    for i in 1 .. self.states.len() {
      let base = Dfa::pyramid_idx_base(i);
      let i_i32 = i as i32;
      for j in 0 .. i - 1 {
        let idx = base + j;
        if not_equal[idx] == 0 && deleted[j] == 0 {
          let j_i32 = j as i32;
          for state in &mut self.states {
            if state.empty == i_i32 {
              state.empty = j_i32;
            }
            if state.red == i_i32 {
              state.red = j_i32;
            }
            if state.black == i_i32 {
              state.black = j_i32;
            }
            if state.bad == i_i32 {
              state.bad = j_i32;
            }
          }
          deleted[i] = 1;
        }
      }
    }
    self.delete_states(deleted);
  }
}
<commit_msg>Add todo.<commit_after>use std::iter;
use std::collections::vec_deque::VecDeque;
use player::Player;
use cell::Cell;

#[derive(Debug)]
pub struct DfaState {
  empty: i32,
  red: i32,
  black: i32,
  bad: i32,
  pattern: i32
}

impl DfaState {
  pub fn new(empty: i32, red: i32, black: i32, bad: i32, pattern: i32) -> DfaState {
    DfaState {
      empty: empty,
      red: red,
      black: black,
      bad: bad,
      pattern: pattern
    }
  }
}

pub struct Dfa {
  states: Vec<DfaState>
}

impl Dfa {
  pub fn new(states: Vec<DfaState>) -> Dfa {
    Dfa {
      states: states
    }
  }

  pub fn product(&self, other: &Dfa) -> Dfa {
    let other_len = other.states.len();
    let other_len_i32 = other_len as i32;
    let mut new_states = Vec::with_capacity(self.states.len() * other_len);
    for self_state in &self.states {
      let base_empty = (self_state.empty + 1) * other_len_i32;
      let base_red = (self_state.red + 1) * other_len_i32;
      let base_black = (self_state.black + 1) * other_len_i32;
      let base_bad = (self_state.bad + 1) * other_len_i32;
      for other_state in &other.states {
        let new_state = DfaState {
          empty: base_empty + other_state.empty,
          red: base_red + other_state.red,
          black: base_black + other_state.black,
          bad: base_bad + other_state.bad,
          pattern: if self_state.pattern != -1 { self_state.pattern } else { other_state.pattern }
        };
        new_states.push(new_state);
      }
    }
    Dfa {
      states: new_states
    }
  }

  pub fn run<T: Iterator<Item = Cell>>(&self, iter: &mut T) -> Option<u32> {
    let mut state_idx = 0i32;
    loop {
      let state = &self.states[state_idx as usize];
      if state.pattern != -1 {
        return Some(state.pattern as u32);
      }
      if let Some(cell) = iter.next() {
        if cell.is_bad() {
          state_idx = state.bad;
        } else if let Some(player) = cell.get_owner() {
          match player {
            Player::Red => state_idx = state.red,
            Player::Black => state_idx = state.black
          }
        } else {
          state_idx = state.empty;
        }
      } else {
        return None;
      }
      if state_idx == -1 {
        return None;
      }
    }
  }

  fn delete_states(&mut self, states_for_delete: Vec<u32>) {
    let mut shifts = Vec::with_capacity(self.states.len());
    for sd in &states_for_delete {
      let shift = shifts.last().map_or(0, |&d| d) + sd;
      shifts.push(shift);
    }
    let deletions = shifts.last().map_or(0, |&d| d);
    if deletions == 0 {
      return;
    }
    let mut new_states = Vec::with_capacity(self.states.len() - deletions as usize);
    for state in self.states.iter().zip(states_for_delete.into_iter()).filter_map(|(state, sd)| if sd == 0 { Some(state) } else { None }) {
      let new_state = DfaState {
        empty: if state.empty == -1 { -1 } else { state.empty - shifts[state.empty as usize] as i32 },
        red: if state.red == -1 { -1 } else { state.red - shifts[state.red as usize] as i32 },
        black: if state.black == -1 { -1 } else { state.black - shifts[state.black as usize] as i32 },
        bad: if state.bad == -1 { -1 } else { state.bad - shifts[state.bad as usize] as i32 },
        pattern: state.pattern
      };
      new_states.push(new_state);
    }
    self.states = new_states;
  }

  pub fn delete_non_reachable(&mut self) {
    let mut non_reachable = iter::repeat(1).take(self.states.len()).collect::<Vec<u32>>();
    let mut q = VecDeque::with_capacity(self.states.len());
    q.push_back(1);
    while let Some(idx) = q.pop_front() {
      let state = &self.states[idx as usize];
      if state.empty != -1 && non_reachable[state.empty as usize] == 1 {
        non_reachable[state.empty as usize] = 0;
        q.push_back(state.empty);
      }
      if state.red != -1 && non_reachable[state.red as usize] == 1 {
        non_reachable[state.red as usize] = 0;
        q.push_back(state.red);
      }
      if state.black != -1 && non_reachable[state.black as usize] == 1 {
        non_reachable[state.black as usize] = 0;
        q.push_back(state.black);
      }
      if state.bad != -1 && non_reachable[state.bad as usize] == 1 {
        non_reachable[state.bad as usize] = 0;
        q.push_back(state.bad);
      }
    }
    self.delete_states(non_reachable);
  }

  fn pyramid_idx_base(i: usize) -> usize {
    i * (i - 1) / 2
  }

  fn pyramid_idx(i: usize, j: usize) -> usize {
    Dfa::pyramid_idx_base(i) + j
  }

  pub fn minimize(&mut self) { //TODO: delete unnecesarry states at the end.
    let len = self.states.len();
    let mut not_equal = iter::repeat(0).take(len * (len - 1) / 2 + len - 1).collect::<Vec<u32>>();
    for (i, pattern_i) in self.states.iter().enumerate().skip(1) {
      let base = Dfa::pyramid_idx_base(i);
      for (j, pattern_j) in self.states[.. i - 1].iter().enumerate() {
        if pattern_i.pattern != pattern_j.pattern {
          not_equal[base + j] = 1;
        }
      }
    }
    'outer: loop {
      for (i, pattern_i) in self.states.iter().enumerate().skip(1) {
        let base = Dfa::pyramid_idx_base(i);
        for (j, pattern_j) in self.states[.. i - 1].iter().enumerate() {
          let idx = base + j;
          if not_equal[idx] == 0 {
            if pattern_i.empty != pattern_j.empty && (pattern_i.empty == -1 || pattern_j.empty == -1 || not_equal[Dfa::pyramid_idx(pattern_i.empty as usize, pattern_j.empty as usize)] == 1) ||
               pattern_i.red != pattern_j.red && (pattern_i.red == -1 || pattern_j.red == -1 || not_equal[Dfa::pyramid_idx(pattern_i.red as usize, pattern_j.red as usize)] == 1) ||
               pattern_i.black != pattern_j.black && (pattern_i.black == -1 || pattern_j.black == -1 || not_equal[Dfa::pyramid_idx(pattern_i.black as usize, pattern_j.black as usize)] == 1) ||
               pattern_i.bad != pattern_j.bad && (pattern_i.bad == -1 || pattern_j.bad == -1 || not_equal[Dfa::pyramid_idx(pattern_i.bad as usize, pattern_j.bad as usize)] == 1) {
              not_equal[idx] = 1;
              continue 'outer;
            }
          }
        }
      }
      break;
    }
    let mut deleted = iter::repeat(0).take(self.states.len()).collect::<Vec<u32>>();
    for i in 1 .. self.states.len() {
      let base = Dfa::pyramid_idx_base(i);
      let i_i32 = i as i32;
      for j in 0 .. i - 1 {
        let idx = base + j;
        if not_equal[idx] == 0 && deleted[j] == 0 {
          let j_i32 = j as i32;
          for state in &mut self.states {
            if state.empty == i_i32 {
              state.empty = j_i32;
            }
            if state.red == i_i32 {
              state.red = j_i32;
            }
            if state.black == i_i32 {
              state.black = j_i32;
            }
            if state.bad == i_i32 {
              state.bad = j_i32;
            }
          }
          deleted[i] = 1;
        }
      }
    }
    self.delete_states(deleted);
  }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::convert::TryInto;
use std::env;

use crate::error::*;
use crate::source::Source;
use crate::value::{Value, ValueKind};

#[derive(Clone, Debug)]
pub struct Environment {
    /// Optional prefix that will limit access to the environment to only keys that
    /// begin with the defined prefix.
    ///
    /// A prefix with a separator of `_` is tested to be present on each key before its considered
    /// to be part of the source environment.
    ///
    /// For example, the key `CONFIG_DEBUG` would become `DEBUG` with a prefix of `config`.
    prefix: Option<String>,

    /// Optional character sequence that separates each key segment in an environment key pattern.
    /// Consider a nested configuration such as `redis.password`, a separator of `_` would allow
    /// an environment key of `REDIS_PASSWORD` to match.
    separator: Option<String>,

    /// Ignore empty env values (treat as unset).
    ignore_empty: bool,

    /// Parses booleans, integers and floats if they're detected (can be safely parsed).
    try_parsing: bool,
}

impl Environment {
    pub fn new() -> Self {
        Environment::default()
    }

    pub fn with_prefix(s: &str) -> Self {
        Environment {
            prefix: Some(s.into()),
            ..Environment::default()
        }
    }

    pub fn prefix(mut self, s: &str) -> Self {
        self.prefix = Some(s.into());
        self
    }

    pub fn separator(mut self, s: &str) -> Self {
        self.separator = Some(s.into());
        self
    }

    pub fn ignore_empty(mut self, ignore: bool) -> Self {
        self.ignore_empty = ignore;
        self
    }

    pub fn try_parsing(mut self, try_parsing: bool) -> Self {
        self.try_parsing = try_parsing;
        self
    }
}

impl Default for Environment {
    fn default() -> Environment {
        Environment {
            prefix: None,
            separator: None,
            ignore_empty: false,
            try_parsing: false,
        }
    }
}

impl Source for Environment {
    fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> {
        Box::new((*self).clone())
    }

    fn collect(&self) -> Result<HashMap<String, Value>> {
        let mut m = HashMap::new();
        let uri: String = "the environment".into();

        let separator = match self.separator {
            Some(ref separator) => separator,
            _ => "",
        };

        let group_separator = match self.separator {
            Some(ref separator) => separator,
            _ => "_",
        };

        // Define a prefix pattern to test and exclude from keys
        let prefix_pattern = self
            .prefix
            .as_ref()
            .map(|prefix| format!("{}{}", prefix.clone(), group_separator));

        for (key, value) in env::vars() {
            // Treat empty environment variables as unset
            if self.ignore_empty && value.is_empty() {
                continue;
            }

            let mut key = key.to_string();

            // Check for prefix
            if let Some(ref prefix_pattern) = prefix_pattern {
                if key
                    .to_lowercase()
                    .starts_with(&prefix_pattern.to_lowercase())
                {
                    // Remove this prefix from the key
                    key = key[prefix_pattern.len()..].to_string();
                } else {
                    // Skip this key
                    continue;
                }
            }

            // If separator is given replace with `.`
            if !separator.is_empty() {
                key = key.replace(separator, ".");
            }

            let value = if self.try_parsing {
                let string_value = Value::new(Some(&uri), ValueKind::String(value.clone()));

                // if the value is "true" or "false" it should be parsed as a bool
                if let (true, Ok(parsed)) = (
                    value.to_lowercase() == "true" || value.to_lowercase() == "false",
                    string_value.clone().into_bool(),
                ) {
                    ValueKind::Boolean(parsed)
                } else if let Ok(parsed) = string_value.clone().into_int() {
                    ValueKind::Integer(parsed)
                } else if let Ok(parsed) = string_value.clone().into_float() {
                    ValueKind::Float(parsed)
                } else if let Ok(parsed) = string_value.clone().into_bool() {
                    ValueKind::Boolean(parsed)
                } else {
                    ValueKind::String(value)
                }
            } else {
                ValueKind::String(value)
            };

            m.insert(key.to_lowercase(), Value::new(Some(&uri), value));
        }

        Ok(m)
    }
}
<commit_msg>clearer env bool parse if<commit_after>use std::collections::HashMap;
use std::convert::TryInto;
use std::env;

use crate::error::*;
use crate::source::Source;
use crate::value::{Value, ValueKind};

#[derive(Clone, Debug)]
pub struct Environment {
    /// Optional prefix that will limit access to the environment to only keys that
    /// begin with the defined prefix.
    ///
    /// A prefix with a separator of `_` is tested to be present on each key before its considered
    /// to be part of the source environment.
    ///
    /// For example, the key `CONFIG_DEBUG` would become `DEBUG` with a prefix of `config`.
    prefix: Option<String>,

    /// Optional character sequence that separates each key segment in an environment key pattern.
    /// Consider a nested configuration such as `redis.password`, a separator of `_` would allow
    /// an environment key of `REDIS_PASSWORD` to match.
    separator: Option<String>,

    /// Ignore empty env values (treat as unset).
    ignore_empty: bool,

    /// Parses booleans, integers and floats if they're detected (can be safely parsed).
    try_parsing: bool,
}

impl Environment {
    pub fn new() -> Self {
        Environment::default()
    }

    pub fn with_prefix(s: &str) -> Self {
        Environment {
            prefix: Some(s.into()),
            ..Environment::default()
        }
    }

    pub fn prefix(mut self, s: &str) -> Self {
        self.prefix = Some(s.into());
        self
    }

    pub fn separator(mut self, s: &str) -> Self {
        self.separator = Some(s.into());
        self
    }

    pub fn ignore_empty(mut self, ignore: bool) -> Self {
        self.ignore_empty = ignore;
        self
    }

    pub fn try_parsing(mut self, try_parsing: bool) -> Self {
        self.try_parsing = try_parsing;
        self
    }
}

impl Default for Environment {
    fn default() -> Environment {
        Environment {
            prefix: None,
            separator: None,
            ignore_empty: false,
            try_parsing: false,
        }
    }
}

impl Source for Environment {
    fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> {
        Box::new((*self).clone())
    }

    fn collect(&self) -> Result<HashMap<String, Value>> {
        let mut m = HashMap::new();
        let uri: String = "the environment".into();

        let separator = match self.separator {
            Some(ref separator) => separator,
            _ => "",
        };

        let group_separator = match self.separator {
            Some(ref separator) => separator,
            _ => "_",
        };

        // Define a prefix pattern to test and exclude from keys
        let prefix_pattern = self
            .prefix
            .as_ref()
            .map(|prefix| format!("{}{}", prefix.clone(), group_separator));

        for (key, value) in env::vars() {
            // Treat empty environment variables as unset
            if self.ignore_empty && value.is_empty() {
                continue;
            }

            let mut key = key.to_string();

            // Check for prefix
            if let Some(ref prefix_pattern) = prefix_pattern {
                if key
                    .to_lowercase()
                    .starts_with(&prefix_pattern.to_lowercase())
                {
                    // Remove this prefix from the key
                    key = key[prefix_pattern.len()..].to_string();
                } else {
                    // Skip this key
                    continue;
                }
            }

            // If separator is given replace with `.`
            if !separator.is_empty() {
                key = key.replace(separator, ".");
            }

            let value = if self.try_parsing {
                let string_value = Value::new(Some(&uri), ValueKind::String(value.clone()));
                let is_true_false =
                    value.to_lowercase() == "true" || value.to_lowercase() == "false";

                // if the value is "true" or "false" it should be parsed as a bool
                if let (true, Ok(parsed)) = (is_true_false, string_value.clone().into_bool()) {
                    ValueKind::Boolean(parsed)
                } else if let Ok(parsed) = string_value.clone().into_int() {
                    ValueKind::Integer(parsed)
                } else if let Ok(parsed) = string_value.clone().into_float() {
                    ValueKind::Float(parsed)
                } else if let Ok(parsed) = string_value.clone().into_bool() {
                    ValueKind::Boolean(parsed)
                } else {
                    ValueKind::String(value)
                }
            } else {
                ValueKind::String(value)
            };

            m.insert(key.to_lowercase(), Value::new(Some(&uri), value));
        }

        Ok(m)
    }
}
<|endoftext|>


<commit_before>use crate::error::{Error, Result};
use std::ffi::OsStr;
use std::fmt::Debug;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::str;

pub struct Git<'a> {
    command: &'a str,
    git_dir: &'a Path,
}

impl<'a> Git<'a> {
    pub fn command<S: AsRef<OsStr> + Debug>(&self, args: &[S]) -> Result<String> {
        let out = Command::new(&self.command)
            .arg("--git-dir")
            .arg(self.git_dir)
            .args(args)
            .output()?;
        if out.status.success() {
            Ok(String::from_utf8_lossy(&out.stdout).trim().to_string())
        } else {
            Err(Error::GitCommandError {
                stderr: String::from_utf8_lossy(&out.stderr)
                    .trim()
                    .replace('\n', " "),
                args: args.iter().map(|a| a.as_ref().to_owned()).collect(),
            })
        }
    }

    pub fn hash<S: AsRef<str>>(&self, commit: S) -> Result<String> {
        self.command(&["rev-parse", commit.as_ref()])
            .map_err(|err| Error::GitObjectNotFound {
                kind: "commit",
                object: commit.as_ref().to_string(),
                msg: format!("{}", err),
            })
    }

    pub fn tag_hash<S: AsRef<str>>(&self, tagname: S) -> Result<String> {
        let tagname = tagname.as_ref();
        let stdout = self
            .command(&["show-ref", "--tags", tagname])
            .map_err(|err| Error::GitObjectNotFound {
                kind: "tag name",
                object: tagname.to_string(),
                msg: format!("{}", err),
            })?;
        // Output must be in format '{rev} {ref name}'
        Ok(stdout.splitn(2, ' ').next().unwrap().to_string())
    }

    pub fn remote_url<S: AsRef<str>>(&self, name: S) -> Result<String> {
        // XXX:
        // `git remote get-url {name}` is not available because it's added recently (at 2.6.1).
        // Note that git installed in Ubuntu 14.04 is 1.9.1.
        let name = name.as_ref();
        self.command(&["config", "--get", &format!("remote.{}.url", name)])
            .map_err(|err| Error::GitObjectNotFound {
                kind: "remote",
                object: name.to_string(),
                msg: format!("{}", err),
            })
    }

    pub fn tracking_remote_url<S>(&self, branch: &Option<S>) -> Result<(String, String)>
    where
        S: AsRef<str>,
    {
        let rev = match branch {
            Some(b) => format!("{}@{{u}}", b.as_ref()),
            None => "@{u}".to_string(),
        };

        let out = match self.command(&["rev-parse", "--abbrev-ref", "--symbolic", rev.as_str()]) {
            Ok(stdout) => stdout,
            Err(Error::GitCommandError { ref stderr, .. })
                if stderr.contains("does not point to a branch") =>
            {
                return Ok((self.remote_url("origin")?, "origin".to_string()));
            }
            Err(err) => return Err(err),
        };

        // out is formatted as '{remote-name}/{branch-name}'
        match out.splitn(2, '/').next() {
            Some(ref u) => Ok((self.remote_url(u)?, u.to_string())),
            None => Err(Error::UnexpectedRemoteName(out.clone())),
        }
    }

    pub fn root_dir(&self) -> Result<PathBuf> {
        // XXX:
        // `git rev-parse` can't be used with --git-dir arguments.
        // `git --git-dir ../.git rev-parse --show-toplevel` always returns
        // current working directory.
        // So here root directory is calculated from git-dir.
        let p = self
            .git_dir
            .parent()
            .ok_or_else(|| Error::GitRootDirNotFound {
                git_dir: self.git_dir.to_owned(),
            })?;
        Ok(p.to_owned())
    }

    pub fn current_branch(&self) -> Result<String> {
        self.command(&["rev-parse", "--abbrev-ref", "--symbolic", "HEAD"])
    }

    pub fn remote_contains<S, T>(&self, spec: S, remote_branch: T) -> Result<bool>
    where
        S: AsRef<str>,
        T: AsRef<str>,
    {
        self.command(&[
            "branch",
            "--remote",
            remote_branch.as_ref(),
            "--contains",
            spec.as_ref(),
        ])
        .map(|o| !o.is_empty())
    }

    // Returns {remote}/{branch}
    pub fn remote_branch<S: AsRef<str>, T: AsRef<str>>(
        &self,
        remote_name: &Option<S>,
        local_branch: &Option<T>,
    ) -> Result<String> {
        let remote = match remote_name {
            Some(r) => r.as_ref(),
            None => "{u}",
        };
        let branch = match local_branch {
            Some(b) => b.as_ref(),
            None => "",
        };
        let rev = format!("{}@{}", branch, remote);
        self.command(&["rev-parse", "--abbrev-ref", "--symbolic", rev.as_str()])
    }
}

impl<'a> Git<'a> {
    pub fn new(dir: &'a Path, command: &'a str) -> Git<'a> {
        Git {
            command,
            git_dir: dir,
        }
    }
}

pub fn git_dir<P: AsRef<Path>>(dir: Option<P>, git_cmd: &str) -> Result<PathBuf> {
    let mut cmd = Command::new(if git_cmd != "" { git_cmd } else { "git" });
    cmd.arg("rev-parse").arg("--absolute-git-dir");
    if let Some(d) = dir {
        cmd.current_dir(fs::canonicalize(&d)?);
    }

    let out = cmd.output()?;
    if !out.status.success() {
        return Err(Error::GitCommandError {
            stderr: String::from_utf8_lossy(&out.stderr)
                .trim()
                .replace('\n', " "),
            args: vec![
                OsStr::new(git_cmd).to_os_string(),
                OsStr::new("rev-parse").to_os_string(),
                OsStr::new("--absolute-git-dir").to_os_string(),
            ],
        });
    }

    let stdout = str::from_utf8(&out.stdout)
        .expect("Invalid UTF-8 sequence in stdout of git command")
        .trim();

    Ok(Path::new(stdout).canonicalize()?)
}
<commit_msg>fix clippy error for double reference<commit_after>use crate::error::{Error, Result};
use std::ffi::OsStr;
use std::fmt::Debug;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::str;

pub struct Git<'a> {
    command: &'a str,
    git_dir: &'a Path,
}

impl<'a> Git<'a> {
    pub fn command<S: AsRef<OsStr> + Debug>(&self, args: &[S]) -> Result<String> {
        let out = Command::new(&self.command)
            .arg("--git-dir")
            .arg(self.git_dir)
            .args(args)
            .output()?;
        if out.status.success() {
            Ok(String::from_utf8_lossy(&out.stdout).trim().to_string())
        } else {
            Err(Error::GitCommandError {
                stderr: String::from_utf8_lossy(&out.stderr)
                    .trim()
                    .replace('\n', " "),
                args: args.iter().map(|a| a.as_ref().to_owned()).collect(),
            })
        }
    }

    pub fn hash<S: AsRef<str>>(&self, commit: S) -> Result<String> {
        self.command(&["rev-parse", commit.as_ref()])
            .map_err(|err| Error::GitObjectNotFound {
                kind: "commit",
                object: commit.as_ref().to_string(),
                msg: format!("{}", err),
            })
    }

    pub fn tag_hash<S: AsRef<str>>(&self, tagname: S) -> Result<String> {
        let tagname = tagname.as_ref();
        let stdout = self
            .command(&["show-ref", "--tags", tagname])
            .map_err(|err| Error::GitObjectNotFound {
                kind: "tag name",
                object: tagname.to_string(),
                msg: format!("{}", err),
            })?;
        // Output must be in format '{rev} {ref name}'
        Ok(stdout.splitn(2, ' ').next().unwrap().to_string())
    }

    pub fn remote_url<S: AsRef<str>>(&self, name: S) -> Result<String> {
        // XXX:
        // `git remote get-url {name}` is not available because it's added recently (at 2.6.1).
        // Note that git installed in Ubuntu 14.04 is 1.9.1.
        let name = name.as_ref();
        self.command(&["config", "--get", &format!("remote.{}.url", name)])
            .map_err(|err| Error::GitObjectNotFound {
                kind: "remote",
                object: name.to_string(),
                msg: format!("{}", err),
            })
    }

    pub fn tracking_remote_url<S>(&self, branch: &Option<S>) -> Result<(String, String)>
    where
        S: AsRef<str>,
    {
        let rev = match branch {
            Some(b) => format!("{}@{{u}}", b.as_ref()),
            None => "@{u}".to_string(),
        };

        let out = match self.command(&["rev-parse", "--abbrev-ref", "--symbolic", rev.as_str()]) {
            Ok(stdout) => stdout,
            Err(Error::GitCommandError { ref stderr, .. })
                if stderr.contains("does not point to a branch") =>
            {
                return Ok((self.remote_url("origin")?, "origin".to_string()));
            }
            Err(err) => return Err(err),
        };

        // out is formatted as '{remote-name}/{branch-name}'
        match out.splitn(2, '/').next() {
            Some(u) => Ok((self.remote_url(u)?, u.to_string())),
            None => Err(Error::UnexpectedRemoteName(out.clone())),
        }
    }

    pub fn root_dir(&self) -> Result<PathBuf> {
        // XXX:
        // `git rev-parse` can't be used with --git-dir arguments.
        // `git --git-dir ../.git rev-parse --show-toplevel` always returns
        // current working directory.
        // So here root directory is calculated from git-dir.
        let p = self
            .git_dir
            .parent()
            .ok_or_else(|| Error::GitRootDirNotFound {
                git_dir: self.git_dir.to_owned(),
            })?;
        Ok(p.to_owned())
    }

    pub fn current_branch(&self) -> Result<String> {
        self.command(&["rev-parse", "--abbrev-ref", "--symbolic", "HEAD"])
    }

    pub fn remote_contains<S, T>(&self, spec: S, remote_branch: T) -> Result<bool>
    where
        S: AsRef<str>,
        T: AsRef<str>,
    {
        self.command(&[
            "branch",
            "--remote",
            remote_branch.as_ref(),
            "--contains",
            spec.as_ref(),
        ])
        .map(|o| !o.is_empty())
    }

    // Returns {remote}/{branch}
    pub fn remote_branch<S: AsRef<str>, T: AsRef<str>>(
        &self,
        remote_name: &Option<S>,
        local_branch: &Option<T>,
    ) -> Result<String> {
        let remote = match remote_name {
            Some(r) => r.as_ref(),
            None => "{u}",
        };
        let branch = match local_branch {
            Some(b) => b.as_ref(),
            None => "",
        };
        let rev = format!("{}@{}", branch, remote);
        self.command(&["rev-parse", "--abbrev-ref", "--symbolic", rev.as_str()])
    }
}

impl<'a> Git<'a> {
    pub fn new(dir: &'a Path, command: &'a str) -> Git<'a> {
        Git {
            command,
            git_dir: dir,
        }
    }
}

pub fn git_dir<P: AsRef<Path>>(dir: Option<P>, git_cmd: &str) -> Result<PathBuf> {
    let mut cmd = Command::new(if git_cmd != "" { git_cmd } else { "git" });
    cmd.arg("rev-parse").arg("--absolute-git-dir");
    if let Some(d) = dir {
        cmd.current_dir(fs::canonicalize(&d)?);
    }

    let out = cmd.output()?;
    if !out.status.success() {
        return Err(Error::GitCommandError {
            stderr: String::from_utf8_lossy(&out.stderr)
                .trim()
                .replace('\n', " "),
            args: vec![
                OsStr::new(git_cmd).to_os_string(),
                OsStr::new("rev-parse").to_os_string(),
                OsStr::new("--absolute-git-dir").to_os_string(),
            ],
        });
    }

    let stdout = str::from_utf8(&out.stdout)
        .expect("Invalid UTF-8 sequence in stdout of git command")
        .trim();

    Ok(Path::new(stdout).canonicalize()?)
}
<|endoftext|>


<commit_before>
static VRAM_SIZE: uint = 0x2000;
static VOAM_SIZE: uint = 0xA0;
static SCREEN_W: uint = 160;
static SCREEN_H: uint = 144;

pub struct GPU {
	priv mode: u8,
	priv modeclock: uint,
	priv line: u8,
	priv lyc: u8,
	priv lcd_on: bool,
	priv win_tilemapbase: u16,
	priv win_on: bool,
	priv tilebase: u16,
	priv bg_tilemap: u16,
	priv sprite_size: uint,
	priv sprite_on: bool,
	priv bg_on: bool,
	priv lyc_inte: bool,
	priv m0_inte: bool,
	priv m1_inte: bool,
	priv m2_inte: bool,
	priv scy: u8,
	priv scx: u8,
	priv winy: u8,
	priv winx: u8,
	priv palbr: u8,
	priv pal0r: u8,
	priv pal1r: u8,
	priv palb: [u8,.. 4],
	priv pal0: [u8,.. 4],
	priv pal1: [u8,.. 4],
	priv vram: ~[u8,.. VRAM_SIZE],
	priv voam: ~[u8,.. VOAM_SIZE],
	data: ~[u8,.. SCREEN_W * SCREEN_H * 3],
	updated: bool,
	interrupt: u8,
}

impl GPU {
	pub fn new() -> GPU {
		GPU {
			mode: 0,
			modeclock: 0,
			line: 0,
			lyc: 0,
			lcd_on: false,
			win_tilemapbase: 0x9C00,
			win_on: false,
			tilebase: 0x8000,
			bg_tilemap: 0x9C00,
			sprite_size: 8,
			sprite_on: false,
			bg_on: false,
			lyc_inte: false,
			m2_inte: false,
			m1_inte: false,
			m0_inte: false,
			scy: 0,
			scx: 0,
			winy: 0,
			winx: 0,
			palbr: 0,
			pal0r: 0,
			pal1r: 1,
			palb: [0,.. 4],
			pal0: [0,.. 4],
			pal1: [0,.. 4],
			vram: ~([0,.. VRAM_SIZE]),
			voam: ~([0,.. VOAM_SIZE]),
			data: ~([0,.. SCREEN_W * SCREEN_H * 3]),
			updated: false,
			interrupt: 0,
		}
	}

	pub fn cycle(&mut self, ticks: uint) {
		if !self.lcd_on { return }

		self.modeclock += ticks;
		match self.mode {
			2 => if self.modeclock >= 20 {
				self.modeclock -= 20;
				self.mode = 3;
			},
			3 => if self.modeclock >= 43 {
				self.renderscan();
				self.mode = 0;
				self.modeclock -= 43;
			},
			0 => if self.modeclock >= 51 {
				self.modeclock -= 51;
				self.line += 1;
				if self.line == 144 {
					self.mode = 1;
					self.updated = true;
				} else {
					self.mode = 2;
				}
			},
			1 => if self.modeclock >= 114 {
				self.modeclock -= 114;
				if self.line == 153 {
					self.mode = 2;
					self.line = 0;
				}
				if self.mode == 1 {
					self.line += 1;
				}
			},
			_ => {},
		}

		self.set_interrupts();
	}

	fn set_interrupts(&mut self) {
		if self.updated { self.interrupt |= 0x01; }
		if (self.lyc_inte && self.line == self.lyc)
			|| (self.m2_inte && self.mode == 2)
			|| (self.m1_inte && self.mode == 1)
			|| (self.m0_inte && self.mode == 0) {
			self.interrupt |= 0x02;
		}
	}

	pub fn rb(&self, a: u16) -> u8 {
		match a {
			0x8000 .. 0x9FFF => self.vram[a & 0x1FFF],
			0xFE00 .. 0xFE9F => self.voam[a & 0xA0],
			0xFF40 => {
				(if self.lcd_on { 0x80 } else { 0 }) |
				(if self.win_tilemapbase == 0x9C00 { 0x40 } else { 0 }) |
				(if self.win_on { 0x20 } else { 0 }) |
				(if self.tilebase == 0x8000 { 0x10 } else { 0 }) |
				(if self.bg_tilemap == 0x9C00 { 0x08 } else { 0 }) |
				(if self.sprite_size == 16 { 0x04 } else { 0 }) |
				(if self.sprite_on { 0x02 } else { 0 }) |
				(if self.bg_on { 0x01 } else { 0 })
			},
			0xFF41 => {
				(if self.lyc_inte { 0x40 } else { 0 }) |
				(if self.m2_inte { 0x20 } else { 0 }) |
				(if self.m1_inte { 0x10 } else { 0 }) |
				(if self.m0_inte { 0x08 } else { 0 }) |
				(if self.line == self.lyc { 0x04 } else { 0 }) |
				self.mode
			},
			0xFF42 => self.scy,
			0xFF43 => self.scx,
			0xFF44 => self.line,
			0xFF45 => self.lyc,
			0xFF46 => 0, // Write only
			0xFF47 => self.palbr,
			0xFF48 => self.pal0r,
			0xFF49 => self.pal1r,
			0xFF4A => self.winy,
			0xFF4B => self.winx,
			_ => fail!("GPU does not handle read {:04X}", a),
		}
	}

	pub fn wb(&mut self, a: u16, v: u8) {
		match a {
			0x8000 .. 0x9FFF => self.vram[a & 0x1FFF] = v,
			0xFE00 .. 0xFE9F => self.voam[a & 0xA0] = v,
			0xFF40 => {
				self.lcd_on = v & 0x80 == 0x80;
				self.win_tilemapbase = if v & 0x40 == 0x40 { 0x9C00 } else { 0x9800 };
				self.win_on = v & 0x20 == 0x20;
				self.tilebase = if v & 0x10 == 0x10 { 0x8000 } else { 0x9000 };
				self.bg_tilemap = if v & 0x08 == 0x08 { 0x9C00 } else { 0x9800 };
				self.sprite_size = if v & 0x04 == 0x04 { 16 } else { 8 };
				self.sprite_on = v & 0x02 == 0x02;
				self.bg_on = v & 0x01 == 0x01;
			},
			0xFF41 => {
				self.lyc_inte = v & 0x40 == 0x40;
				self.m2_inte = v & 0x20 == 0x20;
				self.m1_inte = v & 0x10 == 0x10;
				self.m0_inte = v & 0x08 == 0x08;
			},
			0xFF42 => self.scy = v,
			0xFF43 => self.scx = v,
			0xFF44 => {}, // Read-only
			0xFF45 => self.lyc = v,
			0xFF46 => fail!("0xFF46 should be handled by MMU"),
			0xFF47 => { self.palbr = v; self.update_pal(); },
			0xFF48 => { self.pal0r = v; self.update_pal(); },
			0xFF49 => { self.pal1r = v; self.update_pal(); },
			0xFF4A => self.winy = v,
			0xFF4B => self.winx = v,
			_ => fail!("GPU does not handle write {:04X}", a),
		}
	}

	fn update_pal(&mut self) {
		for i in range(0, 4) {
			self.palb[i] = match (self.palbr >> 2*i) & 3 {
				0 => 255,
				1 => 192,
				2 => 96,
				_ => 0,
			};
			self.pal0[i] = match (self.pal0r >> 2*i) & 3 {
				0 => 255,
				1 => 192,
				2 => 96,
				_ => 0,
			};
			self.pal1[i] = match (self.pal1r >> 2*i) & 3 {
				0 => 255,
				1 => 192,
				2 => 96,
				_ => 0,
			};
		}
	}

	fn renderscan(&mut self) {
		self.draw_bg();
		self.draw_win();
		self.draw_sprites();
	}

	fn setcolor(&mut self, x: uint, color: u8) {
		self.data[self.line as uint * SCREEN_W * 3 + x * 3 + 0] = color;
		self.data[self.line as uint * SCREEN_W * 3 + x * 3 + 1] = color;
		self.data[self.line as uint * SCREEN_W * 3 + x * 3 + 2] = color;
	}

	fn draw_bg(&mut self) {
		if !self.bg_on { return }

		let bgy = self.scy + self.line;
		let tiley = (bgy as u16 >> 3) & 31;
		for x in range(0, SCREEN_W) {
			let bgx = self.scx as uint + x;
			let tilex = (bgx as u16 >> 3) & 31;

			let tilenr: u8 = self.rb(self.bg_tilemap + tiley * 32 + tilex);
			let tileaddress = (self.tilebase as int
			+ (if self.tilebase == 0x8000 {
				tilenr as u16 as int
			} else {
				tilenr as i8 as int
			}) * 16) as u16;

			let b1 = self.rb(tileaddress + ((bgy as u16 & 0x07) * 2));
			let b2 = self.rb(tileaddress + ((bgy as u16 & 0x07) * 2) + 1);

			let xbit = bgx & 0x07;
			let colnr = if b1 & (1 << (7 - xbit)) != 0 { 1 } else { 0 }
				| if b2 & (1 << (7 - xbit)) != 0 { 2 } else { 0 };
			self.setcolor(x, self.palb[colnr]);
		}
	}

	fn draw_win(&mut self) {
	}

	fn draw_sprites(&mut self) {
	}
}
<commit_msg>Added sprites<commit_after>
static VRAM_SIZE: uint = 0x2000;
static VOAM_SIZE: uint = 0xA0;
static SCREEN_W: uint = 160;
static SCREEN_H: uint = 144;

pub struct GPU {
	priv mode: u8,
	priv modeclock: uint,
	priv line: u8,
	priv lyc: u8,
	priv lcd_on: bool,
	priv win_tilemapbase: u16,
	priv win_on: bool,
	priv tilebase: u16,
	priv bg_tilemap: u16,
	priv sprite_size: uint,
	priv sprite_on: bool,
	priv bg_on: bool,
	priv lyc_inte: bool,
	priv m0_inte: bool,
	priv m1_inte: bool,
	priv m2_inte: bool,
	priv scy: u8,
	priv scx: u8,
	priv winy: u8,
	priv winx: u8,
	priv palbr: u8,
	priv pal0r: u8,
	priv pal1r: u8,
	priv palb: [u8,.. 4],
	priv pal0: [u8,.. 4],
	priv pal1: [u8,.. 4],
	priv vram: ~[u8,.. VRAM_SIZE],
	priv voam: ~[u8,.. VOAM_SIZE],
	data: ~[u8,.. SCREEN_W * SCREEN_H * 3],
	updated: bool,
	interrupt: u8,
}

impl GPU {
	pub fn new() -> GPU {
		GPU {
			mode: 0,
			modeclock: 0,
			line: 0,
			lyc: 0,
			lcd_on: false,
			win_tilemapbase: 0x9C00,
			win_on: false,
			tilebase: 0x8000,
			bg_tilemap: 0x9C00,
			sprite_size: 8,
			sprite_on: false,
			bg_on: false,
			lyc_inte: false,
			m2_inte: false,
			m1_inte: false,
			m0_inte: false,
			scy: 0,
			scx: 0,
			winy: 0,
			winx: 0,
			palbr: 0,
			pal0r: 0,
			pal1r: 1,
			palb: [0,.. 4],
			pal0: [0,.. 4],
			pal1: [0,.. 4],
			vram: ~([0,.. VRAM_SIZE]),
			voam: ~([0,.. VOAM_SIZE]),
			data: ~([0,.. SCREEN_W * SCREEN_H * 3]),
			updated: false,
			interrupt: 0,
		}
	}

	pub fn cycle(&mut self, ticks: uint) {
		if !self.lcd_on { return }

		self.modeclock += ticks;
		match self.mode {
			2 => if self.modeclock >= 20 {
				self.modeclock -= 20;
				self.mode = 3;
			},
			3 => if self.modeclock >= 43 {
				self.renderscan();
				self.mode = 0;
				self.modeclock -= 43;
			},
			0 => if self.modeclock >= 51 {
				self.modeclock -= 51;
				self.line += 1;
				if self.line == 144 {
					self.mode = 1;
					self.updated = true;
				} else {
					self.mode = 2;
				}
			},
			1 => if self.modeclock >= 114 {
				self.modeclock -= 114;
				if self.line == 153 {
					self.mode = 2;
					self.line = 0;
				}
				if self.mode == 1 {
					self.line += 1;
				}
			},
			_ => {},
		}

		self.set_interrupts();
	}

	fn set_interrupts(&mut self) {
		if self.updated { self.interrupt |= 0x01; }
		if (self.lyc_inte && self.line == self.lyc)
			|| (self.m2_inte && self.mode == 2)
			|| (self.m1_inte && self.mode == 1)
			|| (self.m0_inte && self.mode == 0) {
			self.interrupt |= 0x02;
		}
	}

	pub fn rb(&self, a: u16) -> u8 {
		match a {
			0x8000 .. 0x9FFF => self.vram[a & 0x1FFF],
			0xFE00 .. 0xFE9F => self.voam[a - 0xFE00],
			0xFF40 => {
				(if self.lcd_on { 0x80 } else { 0 }) |
				(if self.win_tilemapbase == 0x9C00 { 0x40 } else { 0 }) |
				(if self.win_on { 0x20 } else { 0 }) |
				(if self.tilebase == 0x8000 { 0x10 } else { 0 }) |
				(if self.bg_tilemap == 0x9C00 { 0x08 } else { 0 }) |
				(if self.sprite_size == 16 { 0x04 } else { 0 }) |
				(if self.sprite_on { 0x02 } else { 0 }) |
				(if self.bg_on { 0x01 } else { 0 })
			},
			0xFF41 => {
				(if self.lyc_inte { 0x40 } else { 0 }) |
				(if self.m2_inte { 0x20 } else { 0 }) |
				(if self.m1_inte { 0x10 } else { 0 }) |
				(if self.m0_inte { 0x08 } else { 0 }) |
				(if self.line == self.lyc { 0x04 } else { 0 }) |
				self.mode
			},
			0xFF42 => self.scy,
			0xFF43 => self.scx,
			0xFF44 => self.line,
			0xFF45 => self.lyc,
			0xFF46 => 0, // Write only
			0xFF47 => self.palbr,
			0xFF48 => self.pal0r,
			0xFF49 => self.pal1r,
			0xFF4A => self.winy,
			0xFF4B => self.winx,
			_ => fail!("GPU does not handle read {:04X}", a),
		}
	}

	pub fn wb(&mut self, a: u16, v: u8) {
		match a {
			0x8000 .. 0x9FFF => self.vram[a & 0x1FFF] = v,
			0xFE00 .. 0xFE9F => self.voam[a - 0xFE00] = v,
			0xFF40 => {
				self.lcd_on = v & 0x80 == 0x80;
				self.win_tilemapbase = if v & 0x40 == 0x40 { 0x9C00 } else { 0x9800 };
				self.win_on = v & 0x20 == 0x20;
				self.tilebase = if v & 0x10 == 0x10 { 0x8000 } else { 0x9000 };
				self.bg_tilemap = if v & 0x08 == 0x08 { 0x9C00 } else { 0x9800 };
				self.sprite_size = if v & 0x04 == 0x04 { 16 } else { 8 };
				self.sprite_on = v & 0x02 == 0x02;
				self.bg_on = v & 0x01 == 0x01;
			},
			0xFF41 => {
				self.lyc_inte = v & 0x40 == 0x40;
				self.m2_inte = v & 0x20 == 0x20;
				self.m1_inte = v & 0x10 == 0x10;
				self.m0_inte = v & 0x08 == 0x08;
			},
			0xFF42 => self.scy = v,
			0xFF43 => self.scx = v,
			0xFF44 => {}, // Read-only
			0xFF45 => self.lyc = v,
			0xFF46 => fail!("0xFF46 should be handled by MMU"),
			0xFF47 => { self.palbr = v; self.update_pal(); },
			0xFF48 => { self.pal0r = v; self.update_pal(); },
			0xFF49 => { self.pal1r = v; self.update_pal(); },
			0xFF4A => self.winy = v,
			0xFF4B => self.winx = v,
			_ => fail!("GPU does not handle write {:04X}", a),
		}
	}

	fn update_pal(&mut self) {
		for i in range(0, 4) {
			self.palb[i] = match (self.palbr >> 2*i) & 3 {
				0 => 255,
				1 => 192,
				2 => 96,
				_ => 0,
			};
			self.pal0[i] = match (self.pal0r >> 2*i) & 3 {
				0 => 255,
				1 => 192,
				2 => 96,
				_ => 0,
			};
			self.pal1[i] = match (self.pal1r >> 2*i) & 3 {
				0 => 255,
				1 => 192,
				2 => 96,
				_ => 0,
			};
		}
	}

	fn renderscan(&mut self) {
		self.draw_bg();
		self.draw_win();
		self.draw_sprites();
	}

	fn setcolor(&mut self, x: uint, color: u8) {
		self.data[self.line as uint * SCREEN_W * 3 + x * 3 + 0] = color;
		self.data[self.line as uint * SCREEN_W * 3 + x * 3 + 1] = color;
		self.data[self.line as uint * SCREEN_W * 3 + x * 3 + 2] = color;
	}

	fn draw_bg(&mut self) {
		if !self.bg_on { return }

		let bgy = self.scy + self.line;
		let tiley = (bgy as u16 >> 3) & 31;
		for x in range(0, SCREEN_W) {
			let bgx = self.scx as uint + x;
			let tilex = (bgx as u16 >> 3) & 31;

			let tilenr: u8 = self.rb(self.bg_tilemap + tiley * 32 + tilex);
			let tileaddress = (self.tilebase as int
			+ (if self.tilebase == 0x8000 {
				tilenr as u16 as int
			} else {
				tilenr as i8 as int
			}) * 16) as u16;

			let b1 = self.rb(tileaddress + ((bgy as u16 & 0x07) * 2));
			let b2 = self.rb(tileaddress + ((bgy as u16 & 0x07) * 2) + 1);

			let xbit = bgx & 0x07;
			let colnr = if b1 & (1 << (7 - xbit)) != 0 { 1 } else { 0 }
				| if b2 & (1 << (7 - xbit)) != 0 { 2 } else { 0 };
			self.setcolor(x, self.palb[colnr]);
		}
	}

	fn draw_win(&mut self) {
	}

	fn draw_sprites(&mut self) {
		if !self.sprite_on { return }

		for i in range(0u16, 40) {
			let spriteaddr: u16 = 0xFE00 + i * 4;
			let spritey: int = self.rb(spriteaddr + 0) as u16 as int - 16;
			let spritex: int = self.rb(spriteaddr + 1) as u16 as int - 8;
			let tilenum: u16 = (self.rb(spriteaddr + 2) & (if self.sprite_size == 16 { 0xFE } else { 0xFF })) as u16;
			let flags: u8 = self.rb(spriteaddr + 3);
			let usepal1: bool = flags & (1 << 4) != 0;
			let xflip: bool = flags & (1 << 5) != 0;
			let yflip: bool = flags & (1 << 6) != 0;
			let belowbg: bool = flags & (1 << 7) != 0;

			let line = self.line as int;
			let sprite_size = self.sprite_size as int;

			if line < spritey || line >= spritey + sprite_size { continue }
			if spritex < -7 || spritex >= (SCREEN_W as int) { continue }

			let tiley: u16 = if yflip {
				(sprite_size - 1 - (line - spritey)) as u16
			} else {
				(line - spritey) as u16
			};

			let tileaddress = 0x8000u16 + tilenum * 16 + tiley * 2;
			let b1 = self.rb(tileaddress);
			let b2 = self.rb(tileaddress + 1);

			for x in range(0, 8) {
				if spritex + x < 0 || spritex + x >= (SCREEN_W as int) { continue }

				let xbit: u8 = 1 << (if xflip { x } else { 7 - x });
				let colnr: u8 = (if b1 & xbit != 0 { 1 } else { 0 }) |
					(if b2 & xbit != 0 { 2 } else { 0 });
				if colnr == 0 { continue }
				//TODO: draw belowbg if bg == 0 and flag is set
				let color = if usepal1 { self.pal1[colnr] } else { self.pal0[colnr] };

				//fail!("x: {:t}, y: {:t}, t: {:t}, f: {:t}", self.rb(spriteaddr), self.rb(spriteaddr+1), self.rb(spriteaddr+2), self.rb(spriteaddr+3));

				self.setcolor((spritex + x) as uint, color);
			}
		}
	}
}
<|endoftext|>


<commit_before>use std::slice;
use std::ops::{Add, Sub};
use std::f32::consts::PI;
use std::cmp::max;
use num::{Integer, Float};
use geom::V2;

/// Hex grid geometry for vectors.
pub trait HexGeom {
    /// Hex distance represented by a vector.
    fn hex_dist(&self) -> i32;
}

impl HexGeom for V2<i32> {
    fn hex_dist(&self) -> i32 {
        let xd = self.0;
        let yd = self.1;
        if xd.signum() == yd.signum() {
            max(xd.abs(), yd.abs())
        } else {
            xd.abs() + yd.abs()
        }
    }
}

/// Hex grid directions.
#[derive_Rand]
#[derive(Copy, Eq, PartialEq, Clone, Debug, RustcEncodable, RustcDecodable)]
pub enum Dir6 {
    North = 0,
    NorthEast,
    SouthEast,
    South,
    SouthWest,
    NorthWest,
}

impl Dir6 {
    /// Convert a vector into the closest hex direction.
    ///
    /// ```notrust
    ///        *0*       *1*
    ///           \ 14 15 | 00 01
    ///           13\     |      02
    ///               \   |
    ///         12      \ |        03
    ///     *5* ----------O-X------- *2*
    ///         11        Y \      04
    ///                   |   \
    ///           10      |     \05
    ///             09 08 | 07 06 \
    ///                  *4*       *3*
    ///
    /// The hexadecants (00 to 15) and the hex
    /// directions (*0* to *5*) around the origin.
    /// ```
    ///
    /// Vectors that are in a space between two hex direction vectors are
    /// rounded to a hexadecant, then assigned the hex direction whose vector
    /// is nearest to that hexadecant.
    pub fn from_v2(v: V2<i32>) -> Dir6 {
        let hexadecant = {
            let width = PI / 8.0;
            let mut radian = (v.0 as f32).atan2(-v.1 as f32);
            if radian < 0.0 { radian += 2.0 * PI }
            (radian / width).floor() as i32
        };

        Dir6::from_int(match hexadecant {
            13 | 14 => 0,
            15 | 0 | 1 => 1,
            2 | 3 | 4 => 2,
            5 | 6 => 3,
            7 | 8 | 9 => 4,
            10 | 11 | 12 => 5,
            _ => panic!("Bad hexadecant")
        })
    }

    /// Convert an integer to a hex dir using modular arithmetic.
    pub fn from_int(i: i32) -> Dir6 { DIRS[i.mod_floor(&6) as usize] }

    /// Convert a hex dir into the corresponding unit vector.
    pub fn to_v2(&self) -> V2<i32> {
        [V2(-1, -1),
         V2( 0, -1),
         V2( 1,  0),
         V2( 1,  1),
         V2( 0,  1),
         V2(-1,  0)][*self as usize]
    }

    /// Iterate through the six hex dirs in the standard order.
    pub fn iter() -> slice::Iter<'static, Dir6> { DIRS.iter() }
}

impl Add<i32> for Dir6 {
    type Output = Dir6;
    fn add(self, other: i32) -> Dir6 { Dir6::from_int(self as i32 + other) }
}

impl Sub<i32> for Dir6 {
    type Output = Dir6;
    fn sub(self, other: i32) -> Dir6 { Dir6::from_int(self as i32 - other) }
}

static DIRS: [Dir6; 6] = [
    Dir6::North,
    Dir6::NorthEast,
    Dir6::SouthEast,
    Dir6::South,
    Dir6::SouthWest,
    Dir6::NorthWest];

/// Field of view iterator for a hexagonal map.
///
/// Takes a function that
/// indicates opaque cells and yields the visible locations from around the
/// origin up to the given radius.
pub struct HexFov<F> {
    /// Predicate for whether a given point will block the field of view.
    is_opaque: F,
    range: u32,
    stack: Vec<Sector>,
    fake_isometric_hack: bool,
    /// Extra values generated by special cases.
    side_channel: Vec<V2<i32>>,
}

impl<F: Fn<(V2<i32>,), Output=bool>> HexFov<F> {
    pub fn new(is_opaque: F, range: u32) -> HexFov<F> {
        // The origin position V2(0, 0) is a special case for the traversal
        // algorithm, but it's also always present, so instead of adding ugly
        // branches to the actual iterator, we just chain it in right here.
        let init_group = is_opaque(Dir6::from_int(0).to_v2());
        HexFov {
            is_opaque: is_opaque,
            range: range,
            stack: vec![Sector {
                begin: PolarPoint::new(0.0, 1),
                pt: PolarPoint::new(0.0, 1),
                end: PolarPoint::new(6.0, 1),
                group_opaque: init_group,
            }],
            fake_isometric_hack: false,
            // The FOV algorithm will not generate the origin point, so we use
            // the side channel to explicitly add it in the beginning.
            side_channel: vec![V2(0, 0)],
        }
    }

    /// Make wall tiles in acute corners visible when running the algorithm so
    /// that the complete wall rectangle of fake-isometric rooms will appear
    /// in the FOV.
    pub fn fake_isometric(mut self) -> HexFov<F> {
        self.fake_isometric_hack = true;
        self
    }
}

impl<F: Fn<(V2<i32>,), Output=bool>> Iterator for HexFov<F> {
    type Item = V2<i32>;
    fn next(&mut self) -> Option<V2<i32>> {
        if let Some(ret) = self.side_channel.pop() {
            return Some(ret);
        }

        if let Some(mut current) = self.stack.pop() {
            if current.pt.is_below(current.end) {
                let pos = current.pt.to_v2();
                let current_opaque = (self.is_opaque)(pos);

                // Terrain opacity changed, branch out.
                if current_opaque != current.group_opaque {
                    // Add the rest of this sector with the new opacity.
                    self.stack.push(Sector {
                        begin: current.pt,
                        pt: current.pt,
                        end: current.end,
                        group_opaque: current_opaque,
                    });

                    // If this was a visible sector and we're below range, branch
                    // out further.
                    if !current.group_opaque && current.begin.radius < self.range {
                        self.stack.push(Sector {
                            begin: current.begin.further(),
                            pt: current.begin.further(),
                            end: current.pt.further(),
                            group_opaque: (self.is_opaque)(current.begin.further().to_v2()),
                        });
                    }
                    return self.next();
                }

                // Hack for making acute corner tiles of fake-isometric rooms
                // visible.

                // XXX: Side cells should only be visible with wallform tiles,
                // but the FOV API can't currently distinguish between
                // wallform and blockform FOV blockers.
                if self.fake_isometric_hack {
                    if let Some(side_pt) = current.pt.side_point() {
                        // Only do this if both the front tiles and the target
                        // tile are opaque.
                        let next = current.pt.next();
                        if next.is_below(current.end)
                            && current.group_opaque
                            && (self.is_opaque)(next.to_v2())
                            && (self.is_opaque)(side_pt)
                            && current.begin.radius < self.range {
                                self.side_channel.push(side_pt);
                        }
                    }
                }

                // Proceed along the current sector.
                current.pt = current.pt.next();
                self.stack.push(current);
                return Some(pos);
            } else {
                // Hit the end of the sector.

                // If this was a visible sector and we're below range, branch
                // out further.
                if !current.group_opaque && current.begin.radius < self.range {
                    self.stack.push(Sector {
                        begin: current.begin.further(),
                        pt: current.begin.further(),
                        end: current.end.further(),
                        group_opaque: (self.is_opaque)(current.begin.further().to_v2()),
                    });
                }

                self.next()
            }
        } else {
            None
        }
    }
}

struct Sector {
    /// Start point of current sector.
    begin: PolarPoint,
    /// Point currently being processed.
    pt: PolarPoint,
    /// End point of current sector.
    end: PolarPoint,
    /// Currently iterating through a sequence of opaque cells.
    group_opaque: bool,
}

/// Points on a hex circle expressed in polar coordinates.
#[derive(Copy, Clone, PartialEq)]
struct PolarPoint {
    pos: f32,
    radius: u32
}

impl PolarPoint {
    pub fn new(pos: f32, radius: u32) -> PolarPoint { PolarPoint { pos: pos, radius: radius } }
    /// Index of the discrete hex cell along the circle that corresponds to this point.
    fn winding_index(self) -> i32 { (self.pos + 0.5).floor() as i32 }

    pub fn is_below(self, other: PolarPoint) -> bool { self.winding_index() < other.end_index() }
    fn end_index(self) -> i32 { (self.pos + 0.5).ceil() as i32 }

    pub fn to_v2(self) -> V2<i32> {
        if self.radius == 0 { return V2(0, 0); }
        let index = self.winding_index();
        let sector = index.mod_floor(&(self.radius as i32 * 6)) / self.radius as i32;
        let offset = index.mod_floor(&(self.radius as i32));
        let rod = Dir6::from_int(sector).to_v2() * (self.radius as i32);
        let tangent = Dir6::from_int((sector + 2) % 6).to_v2() * offset;
        rod + tangent
    }

    /// If this point and the next point are adjacent vertically (along the xy
    /// axis), return a tuple of the point outside of the circle between the
    /// two points.
    ///
    /// This is a helper function for the FOV special case where acute corners
    /// of fake isometric rooms are marked visible even though strict hex FOV
    /// logic would keep them unseen.
    pub fn side_point(self) -> Option<V2<i32>> {
        let next = self.next();
        let V2(x1, y1) = self.to_v2();
        let V2(x2, y2) = next.to_v2();

        if x2 == x1 + 1 && y2 == y1 + 1 {
            // Going down the right rim.
            Some(V2(x1 + 1, y1))
        } else if x2 == x1 - 1 && y2 == y1 - 1 {
            // Going up the left rim.
            Some(V2(x1 - 1, y1))
        } else {
            None
        }
    }

    /// The point corresponding to this one on the hex circle with radius +1.
    pub fn further(self) -> PolarPoint {
        PolarPoint::new(
            self.pos * (self.radius + 1) as f32 / self.radius as f32,
            self.radius + 1)
    }

    /// The point next to this one along the hex circle.
    pub fn next(self) -> PolarPoint {
        PolarPoint::new((self.pos + 0.5).floor() + 0.5, self.radius)
    }
}

#[cfg(test)]
mod test {
    use geom::V2;
    // XXX: Why doesn't super::* work here?
    use super::{Dir6};
    use super::Dir6::*;

    #[test]
    fn test_dir6() {
        assert_eq!(North, Dir6::from_int(0));
        assert_eq!(NorthWest, Dir6::from_int(-1));
        assert_eq!(NorthWest, Dir6::from_int(5));
        assert_eq!(NorthEast, Dir6::from_int(1));

        assert_eq!(NorthEast, Dir6::from_v2(V2(20, -21)));
        assert_eq!(SouthEast, Dir6::from_v2(V2(20, -10)));
        assert_eq!(North, Dir6::from_v2(V2(-10, -10)));
        assert_eq!(South, Dir6::from_v2(V2(1, 1)));

        for i in 0..6 {
            let d = Dir6::from_int(i);
            let v = d.to_v2();
            let v1 = Dir6::from_int(i - 1).to_v2();
            let v2 = Dir6::from_int(i + 1).to_v2();

            // Test static iter
            assert_eq!(Some(d), Dir6::iter().nth(i as usize).map(|&x| x));

            // Test vector mapping.
            assert_eq!(d, Dir6::from_v2(v));

            // Test opposite dir vector mapping.
            assert_eq!(Dir6::from_int(i + 3), Dir6::from_v2(-v));

            // Test approximation of longer vectors.
            assert_eq!(d, Dir6::from_v2(v * 3));
            assert_eq!(d, Dir6::from_v2(v * 3 + v1));
            assert_eq!(d, Dir6::from_v2(v * 3 + v2));
        }
    }
}
<commit_msg>Better Fn declaration<commit_after>use std::slice;
use std::ops::{Add, Sub};
use std::f32::consts::PI;
use std::cmp::max;
use num::{Integer, Float};
use geom::V2;

/// Hex grid geometry for vectors.
pub trait HexGeom {
    /// Hex distance represented by a vector.
    fn hex_dist(&self) -> i32;
}

impl HexGeom for V2<i32> {
    fn hex_dist(&self) -> i32 {
        let xd = self.0;
        let yd = self.1;
        if xd.signum() == yd.signum() {
            max(xd.abs(), yd.abs())
        } else {
            xd.abs() + yd.abs()
        }
    }
}

/// Hex grid directions.
#[derive_Rand]
#[derive(Copy, Eq, PartialEq, Clone, Debug, RustcEncodable, RustcDecodable)]
pub enum Dir6 {
    North = 0,
    NorthEast,
    SouthEast,
    South,
    SouthWest,
    NorthWest,
}

impl Dir6 {
    /// Convert a vector into the closest hex direction.
    ///
    /// ```notrust
    ///        *0*       *1*
    ///           \ 14 15 | 00 01
    ///           13\     |      02
    ///               \   |
    ///         12      \ |        03
    ///     *5* ----------O-X------- *2*
    ///         11        Y \      04
    ///                   |   \
    ///           10      |     \05
    ///             09 08 | 07 06 \
    ///                  *4*       *3*
    ///
    /// The hexadecants (00 to 15) and the hex
    /// directions (*0* to *5*) around the origin.
    /// ```
    ///
    /// Vectors that are in a space between two hex direction vectors are
    /// rounded to a hexadecant, then assigned the hex direction whose vector
    /// is nearest to that hexadecant.
    pub fn from_v2(v: V2<i32>) -> Dir6 {
        let hexadecant = {
            let width = PI / 8.0;
            let mut radian = (v.0 as f32).atan2(-v.1 as f32);
            if radian < 0.0 { radian += 2.0 * PI }
            (radian / width).floor() as i32
        };

        Dir6::from_int(match hexadecant {
            13 | 14 => 0,
            15 | 0 | 1 => 1,
            2 | 3 | 4 => 2,
            5 | 6 => 3,
            7 | 8 | 9 => 4,
            10 | 11 | 12 => 5,
            _ => panic!("Bad hexadecant")
        })
    }

    /// Convert an integer to a hex dir using modular arithmetic.
    pub fn from_int(i: i32) -> Dir6 { DIRS[i.mod_floor(&6) as usize] }

    /// Convert a hex dir into the corresponding unit vector.
    pub fn to_v2(&self) -> V2<i32> {
        [V2(-1, -1),
         V2( 0, -1),
         V2( 1,  0),
         V2( 1,  1),
         V2( 0,  1),
         V2(-1,  0)][*self as usize]
    }

    /// Iterate through the six hex dirs in the standard order.
    pub fn iter() -> slice::Iter<'static, Dir6> { DIRS.iter() }
}

impl Add<i32> for Dir6 {
    type Output = Dir6;
    fn add(self, other: i32) -> Dir6 { Dir6::from_int(self as i32 + other) }
}

impl Sub<i32> for Dir6 {
    type Output = Dir6;
    fn sub(self, other: i32) -> Dir6 { Dir6::from_int(self as i32 - other) }
}

static DIRS: [Dir6; 6] = [
    Dir6::North,
    Dir6::NorthEast,
    Dir6::SouthEast,
    Dir6::South,
    Dir6::SouthWest,
    Dir6::NorthWest];

/// Field of view iterator for a hexagonal map.
///
/// Takes a function that
/// indicates opaque cells and yields the visible locations from around the
/// origin up to the given radius.
pub struct HexFov<F> {
    /// Predicate for whether a given point will block the field of view.
    is_opaque: F,
    range: u32,
    stack: Vec<Sector>,
    fake_isometric_hack: bool,
    /// Extra values generated by special cases.
    side_channel: Vec<V2<i32>>,
}

impl<F> HexFov<F>
    where F: Fn(V2<i32>) -> bool
{
    pub fn new(is_opaque: F, range: u32) -> HexFov<F> {
        // The origin position V2(0, 0) is a special case for the traversal
        // algorithm, but it's also always present, so instead of adding ugly
        // branches to the actual iterator, we just chain it in right here.
        let init_group = is_opaque(Dir6::from_int(0).to_v2());
        HexFov {
            is_opaque: is_opaque,
            range: range,
            stack: vec![Sector {
                begin: PolarPoint::new(0.0, 1),
                pt: PolarPoint::new(0.0, 1),
                end: PolarPoint::new(6.0, 1),
                group_opaque: init_group,
            }],
            fake_isometric_hack: false,
            // The FOV algorithm will not generate the origin point, so we use
            // the side channel to explicitly add it in the beginning.
            side_channel: vec![V2(0, 0)],
        }
    }

    /// Make wall tiles in acute corners visible when running the algorithm so
    /// that the complete wall rectangle of fake-isometric rooms will appear
    /// in the FOV.
    pub fn fake_isometric(mut self) -> HexFov<F> {
        self.fake_isometric_hack = true;
        self
    }
}

impl<F> Iterator for HexFov<F>
    where F: Fn(V2<i32>) -> bool
{
    type Item = V2<i32>;
    fn next(&mut self) -> Option<V2<i32>> {
        if let Some(ret) = self.side_channel.pop() {
            return Some(ret);
        }

        if let Some(mut current) = self.stack.pop() {
            if current.pt.is_below(current.end) {
                let pos = current.pt.to_v2();
                let current_opaque = (self.is_opaque)(pos);

                // Terrain opacity changed, branch out.
                if current_opaque != current.group_opaque {
                    // Add the rest of this sector with the new opacity.
                    self.stack.push(Sector {
                        begin: current.pt,
                        pt: current.pt,
                        end: current.end,
                        group_opaque: current_opaque,
                    });

                    // If this was a visible sector and we're below range, branch
                    // out further.
                    if !current.group_opaque && current.begin.radius < self.range {
                        self.stack.push(Sector {
                            begin: current.begin.further(),
                            pt: current.begin.further(),
                            end: current.pt.further(),
                            group_opaque: (self.is_opaque)(current.begin.further().to_v2()),
                        });
                    }
                    return self.next();
                }

                // Hack for making acute corner tiles of fake-isometric rooms
                // visible.

                // XXX: Side cells should only be visible with wallform tiles,
                // but the FOV API can't currently distinguish between
                // wallform and blockform FOV blockers.
                if self.fake_isometric_hack {
                    if let Some(side_pt) = current.pt.side_point() {
                        // Only do this if both the front tiles and the target
                        // tile are opaque.
                        let next = current.pt.next();
                        if next.is_below(current.end)
                            && current.group_opaque
                            && (self.is_opaque)(next.to_v2())
                            && (self.is_opaque)(side_pt)
                            && current.begin.radius < self.range {
                                self.side_channel.push(side_pt);
                        }
                    }
                }

                // Proceed along the current sector.
                current.pt = current.pt.next();
                self.stack.push(current);
                return Some(pos);
            } else {
                // Hit the end of the sector.

                // If this was a visible sector and we're below range, branch
                // out further.
                if !current.group_opaque && current.begin.radius < self.range {
                    self.stack.push(Sector {
                        begin: current.begin.further(),
                        pt: current.begin.further(),
                        end: current.end.further(),
                        group_opaque: (self.is_opaque)(current.begin.further().to_v2()),
                    });
                }

                self.next()
            }
        } else {
            None
        }
    }
}

struct Sector {
    /// Start point of current sector.
    begin: PolarPoint,
    /// Point currently being processed.
    pt: PolarPoint,
    /// End point of current sector.
    end: PolarPoint,
    /// Currently iterating through a sequence of opaque cells.
    group_opaque: bool,
}

/// Points on a hex circle expressed in polar coordinates.
#[derive(Copy, Clone, PartialEq)]
struct PolarPoint {
    pos: f32,
    radius: u32
}

impl PolarPoint {
    pub fn new(pos: f32, radius: u32) -> PolarPoint { PolarPoint { pos: pos, radius: radius } }
    /// Index of the discrete hex cell along the circle that corresponds to this point.
    fn winding_index(self) -> i32 { (self.pos + 0.5).floor() as i32 }

    pub fn is_below(self, other: PolarPoint) -> bool { self.winding_index() < other.end_index() }
    fn end_index(self) -> i32 { (self.pos + 0.5).ceil() as i32 }

    pub fn to_v2(self) -> V2<i32> {
        if self.radius == 0 { return V2(0, 0); }
        let index = self.winding_index();
        let sector = index.mod_floor(&(self.radius as i32 * 6)) / self.radius as i32;
        let offset = index.mod_floor(&(self.radius as i32));
        let rod = Dir6::from_int(sector).to_v2() * (self.radius as i32);
        let tangent = Dir6::from_int((sector + 2) % 6).to_v2() * offset;
        rod + tangent
    }

    /// If this point and the next point are adjacent vertically (along the xy
    /// axis), return a tuple of the point outside of the circle between the
    /// two points.
    ///
    /// This is a helper function for the FOV special case where acute corners
    /// of fake isometric rooms are marked visible even though strict hex FOV
    /// logic would keep them unseen.
    pub fn side_point(self) -> Option<V2<i32>> {
        let next = self.next();
        let V2(x1, y1) = self.to_v2();
        let V2(x2, y2) = next.to_v2();

        if x2 == x1 + 1 && y2 == y1 + 1 {
            // Going down the right rim.
            Some(V2(x1 + 1, y1))
        } else if x2 == x1 - 1 && y2 == y1 - 1 {
            // Going up the left rim.
            Some(V2(x1 - 1, y1))
        } else {
            None
        }
    }

    /// The point corresponding to this one on the hex circle with radius +1.
    pub fn further(self) -> PolarPoint {
        PolarPoint::new(
            self.pos * (self.radius + 1) as f32 / self.radius as f32,
            self.radius + 1)
    }

    /// The point next to this one along the hex circle.
    pub fn next(self) -> PolarPoint {
        PolarPoint::new((self.pos + 0.5).floor() + 0.5, self.radius)
    }
}

#[cfg(test)]
mod test {
    use geom::V2;
    // XXX: Why doesn't super::* work here?
    use super::{Dir6};
    use super::Dir6::*;

    #[test]
    fn test_dir6() {
        assert_eq!(North, Dir6::from_int(0));
        assert_eq!(NorthWest, Dir6::from_int(-1));
        assert_eq!(NorthWest, Dir6::from_int(5));
        assert_eq!(NorthEast, Dir6::from_int(1));

        assert_eq!(NorthEast, Dir6::from_v2(V2(20, -21)));
        assert_eq!(SouthEast, Dir6::from_v2(V2(20, -10)));
        assert_eq!(North, Dir6::from_v2(V2(-10, -10)));
        assert_eq!(South, Dir6::from_v2(V2(1, 1)));

        for i in 0..6 {
            let d = Dir6::from_int(i);
            let v = d.to_v2();
            let v1 = Dir6::from_int(i - 1).to_v2();
            let v2 = Dir6::from_int(i + 1).to_v2();

            // Test static iter
            assert_eq!(Some(d), Dir6::iter().nth(i as usize).map(|&x| x));

            // Test vector mapping.
            assert_eq!(d, Dir6::from_v2(v));

            // Test opposite dir vector mapping.
            assert_eq!(Dir6::from_int(i + 3), Dir6::from_v2(-v));

            // Test approximation of longer vectors.
            assert_eq!(d, Dir6::from_v2(v * 3));
            assert_eq!(d, Dir6::from_v2(v * 3 + v1));
            assert_eq!(d, Dir6::from_v2(v * 3 + v2));
        }
    }
}
<|endoftext|>