<commit_before>extern crate json;
extern crate rawr;
extern crate rlua;
extern crate sexuality_def_bot;

use rawr::auth::PasswordAuthenticator;
use rawr::client::RedditClient;
use rawr::structures::subreddit::Subreddit;

use rlua::Lua;

use std::fs::File;
use std::process;
use std::io::Read;
use std::io;

use json::JsonValue;

fn read_file(name: &str) -> Result<String, io::Error> {
    let mut file = File::open(name)?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;
    Ok(contents)
}

/// Produces the RedditClient from the authentication data
fn get_client(mut data: JsonValue) -> RedditClient {
    let authenticator = PasswordAuthenticator::new(
        data.remove("id").as_str().expect("No id specified in json"),
        data.remove("secret").as_str().expect("No secret specified in json"),
        data.remove("username").as_str().expect("No id specified in json"),
        data.remove("password").as_str().expect("No id specified in json")
    );
    RedditClient::new("linux:rawr:v0.0.1 (by /u/skeletonxf)", authenticator)
}

// Returns a list of Subreddits specified in the subreddits.json
fn get_subreddits(data: JsonValue, client: &RedditClient) -> Vec<Subreddit> {
    let mut list = Vec::new();
    for element in data.members() {
        let name = element.as_str().expect(
            "All elements in subreddits.json array should be strings");
        list.push(client.subreddit(name));
    }
    println!("{}", data.dump());
    list
}

fn main() {
    // need to work out how to pass and retrieve values: https://docs.rs/rlua/0.12.2/rlua/struct.Lua.html
    //let lua = Lua::new();
    //lua.eval::<()>(
    //   r#"
    //   print('hello world')
    //   "#,
    //   None,
    //);
    //fn respond_to(string : &str) will call lua code on contents of some Commentable

    let authentication_data = read_file("authentication.json").unwrap_or_else(|e| {
        println!("Problem reading authentication data: {}", e);
        process::exit(1);
    });
    let json_authentication_data = json::parse(&authentication_data).unwrap_or_else(|e| {
        println!("Problem parsing authentication data: {}", e);
        process::exit(1);
    });
    let client = get_client(json_authentication_data);

    let subreddits_data = read_file("subreddits.json").unwrap_or_else(|e| {
        println!("Problem reading subreddits data: {}", e);
        process::exit(1);
    });
    let json_subreddits_data = json::parse(&subreddits_data).unwrap_or_else(|e| {
        println!("Problem parsing subreddits data: {}", e);
        process::exit(1);
    });
    let subreddits = get_subreddits(json_subreddits_data, &client);

    sexuality_def_bot::run(subreddits);
}
<commit_msg>Use box to reduce repetition in json file parsing<commit_after>extern crate json;
extern crate rawr;
extern crate rlua;
extern crate sexuality_def_bot;

use rawr::auth::PasswordAuthenticator;
use rawr::client::RedditClient;
use rawr::structures::subreddit::Subreddit;

use rlua::Lua;

use std::error::Error;
use std::fs::File;
use std::io::Read;
use std::process;

use json::JsonValue;

fn read_json(name: &str) -> Result<JsonValue, Box<Error>> {
    let mut file = File::open(name)?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;
    let parsed = json::parse(&contents)?;
    Ok(parsed)
}

/// Produces the RedditClient from the authentication data
fn get_client(mut data: JsonValue) -> RedditClient {
    let authenticator = PasswordAuthenticator::new(
        data.remove("id").as_str().expect("No id specified in json"),
        data.remove("secret").as_str().expect("No secret specified in json"),
        data.remove("username").as_str().expect("No id specified in json"),
        data.remove("password").as_str().expect("No id specified in json")
    );
    RedditClient::new("linux:rawr:v0.0.1 (by /u/skeletonxf)", authenticator)
}

// Returns a list of Subreddits specified in the subreddits.json
fn get_subreddits(data: JsonValue, client: &RedditClient) -> Vec<Subreddit> {
    let mut list = Vec::new();
    for element in data.members() {
        let name = element.as_str().expect(
            "All elements in subreddits.json array should be strings");
        list.push(client.subreddit(name));
    }
    println!("{}", data.dump());
    list
}

fn main() {
    // need to work out how to pass and retrieve values: https://docs.rs/rlua/0.12.2/rlua/struct.Lua.html
    //let lua = Lua::new();
    //lua.eval::<()>(
    //   r#"
    //   print('hello world')
    //   "#,
    //   None,
    //);
    //fn respond_to(string : &str) will call lua code on contents of some Commentable

    let authentication_data = read_json("authentication.json").unwrap_or_else(|e| {
        println!("Problem with authentication data: {}", e);
        process::exit(1);
    });
    let client = get_client(authentication_data);

    let json_subreddits_data = read_json("subreddits.json").unwrap_or_else(|e| {
        println!("Problem with subreddits data: {}", e);
        process::exit(1);
    });
    let subreddits = get_subreddits(json_subreddits_data, &client);

    sexuality_def_bot::run(subreddits);
}
<|endoftext|>


<commit_before>#![feature(plugin)]
#![plugin(quickcheck_macros)]

#[cfg(test)]
extern crate quickcheck;

use quickcheck::{TestResult, quickcheck};

fn is_fizz(x: u32) -> bool {
    (x % 3) == 0
}

fn is_buzz(x: u32) -> bool {
    (x % 5) == 0
}

fn is_fizzbuzz(x: u32) -> bool {
    is_fizz(x) && is_buzz(x)
}

fn fb_string(x: u32) -> String {
    if is_fizzbuzz(x) {
        return "FizzBuzz!".to_string();
    }
    if is_fizz(x) {
        return "Fizz".to_string();
    }
    if is_buzz(x) {
        return "Buzz".to_string();
    }
    return x.to_string();
}

fn fizzy_print(x: u32) {
    println!("{}", fb_string(x));
}

fn main() {
    for i in 1..101 {
        fizzy_print(i);
    }
}

#[quickcheck]
fn prop_fizz(x: u32) -> TestResult {
    if x % 3 == 0 {
        TestResult::from_bool(is_fizz(x))
    } else {
        TestResult::from_bool(!is_fizz(x))
    }
}

#[test]
fn is_fizz_test() {
    let fizzies = [3, 6, 9, 12, 300, 60];

    for x in fizzies.iter() {
        assert!(is_fizz(*x));
    }
}

#[test]
fn is_buzz_test() {

    let buzzies = [5, 10, 15, 20];

    for x in buzzies.iter() {
        assert!(is_buzz(*x));
    }

    assert!(!is_buzz(4))
}

#[test]
fn is_fizzbuzz_test() {
    let fizzbuzzies = [15, 30, 45, 60];

    for x in fizzbuzzies.iter() {
        assert!(is_fizzbuzz(*x));
    }
}
<commit_msg>0.0.3 Refactored to vector<commit_after>
struct ModGuard {
    val: u32,
    display: &'static str
}

fn get_fizzbuzz_guards() -> Vec<ModGuard> {
    vec![
        ModGuard{val: 3, display: "Fizz"},
        ModGuard{val: 5, display: "Buzz"}]
}

fn generate_str(x: u32) -> String {
    let v = get_fizzbuzz_guards();
    let mut s = String::from("");

    for i in 0..v.len() {
        if x % &v[i].val == 0 {
            s.push_str(v[i].display);
        }
    }
    if s.len() == 0 {
        x.to_string()
    } else {
        s.to_string()
    }
}

fn doit()  {
    for i in 1..101 {
        println!("{}", generate_str(i));
    }
}

fn main() {
    doit() ;
}

#[test]
fn is_fizz_test() {
    let fizzies = [3, 6, 9, 12];

    for x in fizzies.iter() {
        assert_eq!(generate_str(*x), "Fizz");
    }
}

#[test]
fn is_buzz_test() {

    let buzzies = [5, 10, 20];

    for x in buzzies.iter() {
        assert_eq!(generate_str(*x), "Buzz");
    }

}

#[test]
fn is_fizzbuzz_test() {
    let fizzbuzzies = [15, 30, 45, 60];

    for x in fizzbuzzies.iter() {
        assert_eq!(generate_str(*x), "FizzBuzz");
    }
}
<|endoftext|>


<commit_before>extern crate piston_window;

use piston_window::*;

#[derive(Debug,Copy,Clone,PartialEq)]
struct Candidate {
    coordinates: [f64; 4],
    color: [f32; 4],
}

fn main() {
    let window: PistonWindow = WindowSettings::new(
            "colors",
            [600, 600]
        )
        .exit_on_esc(true)
        .build()
        .unwrap();

    let mut mouse_position = (0.0, 0.0);
    let mut candidates = vec![
        Candidate { coordinates: [300.0, 300.0, 100.0, 100.0], color: [1.0, 0.0, 0.0, 1.0] },
        Candidate { coordinates: [100.0, 200.0, 100.0, 100.0], color: [0.0, 1.0, 0.0, 1.0] },
        Candidate { coordinates: [300.0, 100.0, 100.0, 100.0], color: [0.0, 0.0, 1.0, 1.0] },
    ];

    for e in window {
        let target_color = match candidates.get(0) {
            Some(candidate) => candidate.color,
            None => break,
        };
        e.draw_2d(|c, g| {
           clear(target_color, g);

           let playing_surface = Rectangle::new([1.0, 1.0, 1.0, 1.0]);
           playing_surface.draw([50.0, 50.0, 500.0, 500.0], &c.draw_state, c.transform, g);

           for candidate in &candidates {
               let r = Rectangle::new(candidate.color);
               r.draw(candidate.coordinates, &c.draw_state, c.transform, g);
           }
        });
        e.mouse_cursor(|x, y| {
            mouse_position = (x, y);
        });
        if let Some(button) = e.press_args() {
            if button == Button::Mouse(MouseButton::Left) {
                if let Some(clicked_on) = candidates.iter().filter(|candidate| {
                    mouse_position.0 > candidate.coordinates[0] &&
                       mouse_position.0 < (candidate.coordinates[0] + candidate.coordinates[2]) &&
                       mouse_position.1 > candidate.coordinates[1] &&
                       mouse_position.1 < (candidate.coordinates[1] + candidate.coordinates[3])
                }).next().cloned() {
                    if clicked_on.color == target_color {
                        candidates.retain(|&r| r != clicked_on);
                        println!("WIN");
                    } else {
                        println!("LOSER");
                    }
                }
            }
        };

    }
}
<commit_msg>Exit if you get it wrong<commit_after>extern crate piston_window;

use piston_window::*;

#[derive(Debug,Copy,Clone,PartialEq)]
struct Candidate {
    coordinates: [f64; 4],
    color: [f32; 4],
}

fn main() {
    let window: PistonWindow = WindowSettings::new(
            "colors",
            [600, 600]
        )
        .exit_on_esc(true)
        .build()
        .unwrap();

    let mut mouse_position = (0.0, 0.0);
    let mut candidates = vec![
        Candidate { coordinates: [300.0, 300.0, 100.0, 100.0], color: [1.0, 0.0, 0.0, 1.0] },
        Candidate { coordinates: [100.0, 200.0, 100.0, 100.0], color: [0.0, 1.0, 0.0, 1.0] },
        Candidate { coordinates: [300.0, 100.0, 100.0, 100.0], color: [0.0, 0.0, 1.0, 1.0] },
    ];

    for e in window {
        let target_color = match candidates.get(0) {
            Some(candidate) => candidate.color,
            None => break,
        };
        e.draw_2d(|c, g| {
           clear(target_color, g);

           let playing_surface = Rectangle::new([1.0, 1.0, 1.0, 1.0]);
           playing_surface.draw([50.0, 50.0, 500.0, 500.0], &c.draw_state, c.transform, g);

           for candidate in &candidates {
               let r = Rectangle::new(candidate.color);
               r.draw(candidate.coordinates, &c.draw_state, c.transform, g);
           }
        });
        e.mouse_cursor(|x, y| {
            mouse_position = (x, y);
        });
        if let Some(button) = e.press_args() {
            if button == Button::Mouse(MouseButton::Left) {
                if let Some(clicked_on) = candidates.iter().filter(|candidate| {
                    mouse_position.0 > candidate.coordinates[0] &&
                       mouse_position.0 < (candidate.coordinates[0] + candidate.coordinates[2]) &&
                       mouse_position.1 > candidate.coordinates[1] &&
                       mouse_position.1 < (candidate.coordinates[1] + candidate.coordinates[3])
                }).next().cloned() {
                    if clicked_on.color == target_color {
                        candidates.retain(|&r| r != clicked_on);
                    } else {
                        break;
                    }
                }
            }
        };

    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017  Sebastian Wiesner <swiesner@lunaryorn.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#![deny(warnings)]

#[macro_use]
extern crate clap;
extern crate rand;

use clap::{App, Arg};

static EFF_WORDLIST: &'static str = include_str!(concat!(env!("OUT_DIR"), "/eff_wordlist.txt"));

fn parse_diceware_list(input: &str) -> Vec<&str> {
    input.lines().map(|l| l.split_whitespace().last().unwrap()).collect()
}

fn main() {
    let matches = App::new("xkpwgen")
        .version(crate_version!())
        .about("Generate XKCD 936 passwords")
        .after_help("Copyright (C) 2017 Sebastian Wiesner <swiesner@lunaryorn.com>
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.")
        .version_message("Print version information")
        .help_message("Print this message")
        .arg(Arg::with_name("number")
                 .short("n")
                 .long("number")
                 .default_value("5")
                 .help("The number of passwords to generate at once"))
        .arg(Arg::with_name("length")
                 .short("l")
                 .long("length")
                 .default_value("4")
                 .help("The number of words in each password"))
        .get_matches();

    let words = parse_diceware_list(EFF_WORDLIST);
    let password_length = value_t_or_exit!(matches.value_of("length"), usize);
    let number_of_passwords = value_t_or_exit!(matches.value_of("number"), usize);
    for _ in 0..number_of_passwords {
        println!("{}",
                 rand::sample(&mut rand::thread_rng(),
                              words.iter().map(|s| *s),
                              password_length)
                         .join(" "));
    }
}
<commit_msg>Improve clap --help message<commit_after>/*
 * Copyright (C) 2017  Sebastian Wiesner <swiesner@lunaryorn.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#![deny(warnings)]

#[macro_use]
extern crate clap;
extern crate rand;

use clap::{App, AppSettings, Arg};

static EFF_WORDLIST: &'static str = include_str!(concat!(env!("OUT_DIR"), "/eff_wordlist.txt"));

fn parse_diceware_list(input: &str) -> Vec<&str> {
    input.lines().map(|l| l.split_whitespace().last().unwrap()).collect()
}

fn main() {
    let matches = App::new("xkpwgen")
        .version(crate_version!())
        .about("Generate XKCD 936 passwords")
        .after_help("Copyright (C) 2017 Sebastian Wiesner <swiesner@lunaryorn.com>
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.")
        .version_message("Print version information")
        .help_message("Print this message")
        .arg(Arg::with_name("number")
                 .short("n")
                 .long("number")
                 .default_value("5")
                 .help("The number of passwords to generate at once"))
        .arg(Arg::with_name("length")
                 .short("l")
                 .long("length")
                 .default_value("4")
                 .help("The number of words in each password"))
        .settings(&[AppSettings::ColoredHelp,
                    AppSettings::DontCollapseArgsInUsage,
                    // Don't put flags and options in separate --help groups
                    AppSettings::UnifiedHelpMessage])
        .get_matches();

    let words = parse_diceware_list(EFF_WORDLIST);
    let password_length = value_t_or_exit!(matches.value_of("length"), usize);
    let number_of_passwords = value_t_or_exit!(matches.value_of("number"), usize);
    for _ in 0..number_of_passwords {
        println!("{}",
                 rand::sample(&mut rand::thread_rng(),
                              words.iter().map(|s| *s),
                              password_length)
                         .join(" "));
    }
}
<|endoftext|>


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

// extern crate base64;
// extern crate blake2_rfc;
#[macro_use]
extern crate clap;
extern crate humansize;
extern crate linear_map;
extern crate rusqlite;
extern crate twox_hash;
extern crate walkdir;
extern crate indicatif;

// use blake2_rfc::blake2b::Blake2b;
use humansize::{FileSize, file_size_opts as options};
use clap::{App, Arg};
use linear_map::LinearMap;
use std::io;
use std::io::Read;
use std::fs::File;
use std::hash::Hasher;
use std::collections::BTreeMap;
use std::path::PathBuf;
use walkdir::WalkDir;

fn copy<R: io::Read, W: Hasher>(reader: &mut R, writer: &mut W) -> io::Result<u64> {
    let mut buf = [0; 64 * 1024];
    let mut written = 0;
    loop {
        let len = match reader.read(&mut buf) {
            Ok(0) => return Ok(written),
            Ok(len) => len,
            Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue,
            Err(e) => return Err(e),
        };
        writer.write(&buf[..len]);
        written += len as u64;
    }
}

fn to_hash(path: &PathBuf) -> u64 {
    let mut file = File::open(path).unwrap();
    let mut hasher = twox_hash::XxHash::default();
    copy(&mut file, &mut hasher).unwrap();
    hasher.finish()
}

fn read_file(path: &PathBuf) -> Vec<u8> {
    let mut file = File::open(path).unwrap();
    let mut vec = Vec::new();
    file.read_to_end(&mut vec).unwrap();
    vec
}

fn partition_by<I, F, R>(paths: I, f: F) -> LinearMap<R, Vec<PathBuf>>
    where I: Iterator<Item=PathBuf>, F: Fn(&PathBuf) -> R, R: Eq
{
    let mut map = LinearMap::with_capacity(paths.size_hint().0);
    for path in paths {
        let key = f(&path);
        map.entry(key).or_insert_with(Vec::new).push(path);
    }
    map
}

const DB_TABLE_SCHEMA: &'static str = r#"
CREATE TABLE files (
    hash BIGINT NOT NULL,
    path TEXT NOT NULL,
    size BIGINT NOT NULL
);
"#;

fn main() {
    let matches = App::new(crate_name!())
        .version(crate_version!())
        .author(crate_authors!("\n"))
        .arg(Arg::with_name("media_directory")
            .help("The location of the media store")
            .index(1)
            .required(true))
        .get_matches();

    let path = matches.value_of("media_directory").unwrap();

    println!("Creating DB...");

    let db = rusqlite::Connection::open_in_memory().expect("failed to open sqlite db");
    db.execute_batch(DB_TABLE_SCHEMA).expect("failed to create db schema");

    let mut paths_by_size = BTreeMap::new();
    let mut total_files = 0;
    let mut total_size = 0;

    println!("Walking fs...");

    let pb = indicatif::ProgressBar::new_spinner();

    for entry in WalkDir::new(path) {
        let entry = entry.unwrap();
        if !entry.file_type().is_file() {
            continue
        }

        let file_size = entry.metadata().unwrap().len() as usize;
        paths_by_size.entry(file_size).or_insert_with(Vec::new).push(entry.path().to_owned());

        total_files += 1;
        total_size += file_size;

        pb.inc(1);
        if total_files % 100 == 0 {
            // println!("Handled {} files", total_files);
            // pb.set_message(&format!("Handled {} files", total_files));
        }
        pb.set_message(&format!("Handled {} files", total_files));
    }

    pb.finish_and_clear();

    println!("Handled {} files", total_files);
    println!();

     let pb = indicatif::ProgressBar::new(total_size as u64);

    let mut total_wasted_size = 0;

    for (file_size, paths) in paths_by_size {
        if paths.len() == 1 {
            pb.inc(file_size as u64);
            continue
        }

        let by_hash = partition_by(paths.into_iter(), to_hash);

        for (hash, paths) in by_hash {
            if paths.len() == 1 {
                pb.inc(file_size as u64);
                continue
            }

            let by_contents = partition_by(paths.into_iter(), read_file);

            for (_, paths) in by_contents {
                if paths.len() == 1 {
                    pb.inc(file_size as u64);
                    continue
                }

                // print!("Duplicate {} paths (hash: {})\n", paths.len(), hash);
                for path in &paths {
                    db.execute("INSERT INTO files (hash, path, size) VALUES (?, ?, ?)", &[&(hash as i64), &path.to_str().unwrap(), &(file_size as i64)]).expect("failed to write to db");
                    // print!("  {}\n", path.display());
                }

                let wasted = file_size * (paths.len() - 1);
                // print!(
                //     " Size: {}. Wasting {}.\n",
                //     file_size.file_size(options::CONVENTIONAL).unwrap(),
                //     wasted.file_size(options::CONVENTIONAL).unwrap(),
                // );

                total_wasted_size += wasted;

                pb.inc((file_size * paths.len()) as u64);
            }
        }
    }

    pb.finish();

    println!();
    println!(
        "Total wasted size: {} out of {}. Percentage: {:.2}%",
        total_wasted_size.file_size(options::CONVENTIONAL).unwrap(),
        total_size.file_size(options::CONVENTIONAL).unwrap(),
        (total_wasted_size * 100) as f64 / total_size as f64,
    );

    let mut disk_db = rusqlite::Connection::open("media_store_sizes.db").expect("failed to open sqlite db");
    let backup = rusqlite::backup::Backup::new(&db, &mut disk_db).expect("failed to create backup");
    backup.run_to_completion(5, std::time::Duration::from_millis(0), None).expect("failed to write to disk");
}
<commit_msg>Better progress indicators<commit_after>//    Copyright 2017 Vector Creations Limited
//
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//
//        http://www.apache.org/licenses/LICENSE-2.0
//
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.

// extern crate base64;
// extern crate blake2_rfc;
#[macro_use]
extern crate clap;
extern crate humansize;
extern crate linear_map;
extern crate rusqlite;
extern crate twox_hash;
extern crate walkdir;
extern crate indicatif;

// use blake2_rfc::blake2b::Blake2b;
use humansize::{FileSize, file_size_opts as options};
use clap::{App, Arg};
use linear_map::LinearMap;
use std::io;
use std::io::Read;
use std::fs::File;
use std::hash::Hasher;
use std::collections::BTreeMap;
use std::path::PathBuf;
use walkdir::WalkDir;

fn copy<R: io::Read, W: Hasher>(reader: &mut R, writer: &mut W) -> io::Result<u64> {
    let mut buf = [0; 64 * 1024];
    let mut written = 0;
    loop {
        let len = match reader.read(&mut buf) {
            Ok(0) => return Ok(written),
            Ok(len) => len,
            Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue,
            Err(e) => return Err(e),
        };
        writer.write(&buf[..len]);
        written += len as u64;
    }
}

fn to_hash(path: &PathBuf) -> u64 {
    let mut file = File::open(path).unwrap();
    let mut hasher = twox_hash::XxHash::default();
    copy(&mut file, &mut hasher).unwrap();
    hasher.finish()
}

fn read_file(path: &PathBuf) -> Vec<u8> {
    let mut file = File::open(path).unwrap();
    let mut vec = Vec::new();
    file.read_to_end(&mut vec).unwrap();
    vec
}

fn partition_by<I, F, R>(paths: I, f: F) -> LinearMap<R, Vec<PathBuf>>
    where I: Iterator<Item=PathBuf>, F: Fn(&PathBuf) -> R, R: Eq
{
    let mut map = LinearMap::with_capacity(paths.size_hint().0);
    for path in paths {
        let key = f(&path);
        map.entry(key).or_insert_with(Vec::new).push(path);
    }
    map
}

const DB_TABLE_SCHEMA: &'static str = r#"
CREATE TABLE files (
    hash BIGINT NOT NULL,
    path TEXT NOT NULL,
    size BIGINT NOT NULL
);
"#;

fn main() {
    let matches = App::new(crate_name!())
        .version(crate_version!())
        .author(crate_authors!("\n"))
        .arg(Arg::with_name("media_directory")
            .help("The location of the media store")
            .index(1)
            .multiple(true)
            .required(true))
        .get_matches();

    let paths_to_search = matches.values_of("media_directory").unwrap();

    let db = rusqlite::Connection::open_in_memory().expect("failed to open sqlite db");
    db.execute_batch(DB_TABLE_SCHEMA).expect("failed to create db schema");

    let mut paths_by_size = BTreeMap::new();
    let mut total_files = 0;
    let mut total_size = 0;

    let pb = indicatif::ProgressBar::new_spinner();
    pb.set_style(
        indicatif::ProgressStyle::default_spinner()
        .template("{spinner} Collected metadata for {pos} files...")
    );

    for path in paths_to_search {
        for entry in WalkDir::new(path) {
            let entry = entry.unwrap();
            if !entry.file_type().is_file() {
                continue
            }

            let file_size = entry.metadata().unwrap().len() as usize;
            paths_by_size.entry(file_size).or_insert_with(Vec::new).push(entry.path().to_owned());

            total_files += 1;
            total_size += file_size;

            pb.inc(1);
        }
    }

    pb.finish_and_clear();

    println!("  Collected metadata for {} files", total_files);

    let pb = indicatif::ProgressBar::new(total_files);
    pb.set_style(
        indicatif::ProgressStyle::default_bar()
        .template("  Searching for possible duplicates  {bar:40} {pos:>8}/{len}")
    );

    let mut number_possible_duplicates = 0;
    let mut possible_total_size = 0;
    for (file_size, paths) in &paths_by_size {
        if paths.len() > 1 {
            number_possible_duplicates += paths.len();
            possible_total_size += *file_size * paths.len();
        }
        pb.inc(1);
    }

    pb.finish();

    let pb = indicatif::ProgressBar::new(possible_total_size as u64);
    pb.set_style(
        indicatif::ProgressStyle::default_bar()
        .template("  Comparing hashes                   {bar:40} {bytes:>8}/{total_bytes}")
    );

    let mut total_wasted_size = 0;

    for (file_size, paths) in paths_by_size {
        if paths.len() == 1 {
            continue
        }

        let by_hash = partition_by(paths.into_iter(), to_hash);

        for (hash, paths) in by_hash {
            if paths.len() == 1 {
                pb.inc(file_size as u64);
                continue
            }

            let by_contents = partition_by(paths.into_iter(), read_file);

            for (_, paths) in by_contents {
                if paths.len() == 1 {
                    pb.inc(file_size as u64);
                    continue
                }

                for path in &paths {
                    db.execute("INSERT INTO files (hash, path, size) VALUES (?, ?, ?)", &[&(hash as i64), &path.to_str().unwrap(), &(file_size as i64)]).expect("failed to write to db");
                }

                let wasted = file_size * (paths.len() - 1);

                total_wasted_size += wasted;

                pb.inc((file_size * paths.len()) as u64);
            }
        }
    }

    pb.finish();

    println!();
    println!(
        "Total wasted size: {} out of {}. Percentage: {:.2}%",
        total_wasted_size.file_size(options::CONVENTIONAL).unwrap(),
        total_size.file_size(options::CONVENTIONAL).unwrap(),
        (total_wasted_size * 100) as f64 / total_size as f64,
    );

    let mut disk_db = rusqlite::Connection::open("media_store_sizes.db").expect("failed to open sqlite db");
    let backup = rusqlite::backup::Backup::new(&db, &mut disk_db).expect("failed to create backup");
    backup.run_to_completion(5, std::time::Duration::from_millis(0), None).expect("failed to write to disk");
}
<|endoftext|>


<commit_before>extern crate rustc_serialize;
extern crate sdl2;
extern crate sdl2_image;
extern crate sdl2_ttf;
extern crate toml;

use std::collections::BTreeMap;
use std::fs::{self, File};
use std::io::Read;
use std::path::Path;
use std::process::Command;
use std::time::Duration;

use sdl2::controller::{Axis, Button};
use sdl2::event::Event;
use sdl2::keyboard::Scancode;
use sdl2::mouse::Mouse;
use sdl2::pixels::Color;
use sdl2::rect::Rect;
use sdl2::render::{Renderer, Texture};

use sdl2_image::LoadTexture;

struct Cursor {
    pub x: f32,
    pub y: f32,
    texture: Texture,
}

impl Cursor {
    pub fn new(renderer: &Renderer, image: &str) -> Cursor {
        Cursor {
            x: 0.0,
            y: 0.0,
            texture: renderer.load_texture(&Path::new(image)).unwrap(),
        }
    }

    pub fn set(&mut self, renderer: &Renderer, x: f32, y: f32) {
        self.x = x.max(0.0).min(renderer.output_size().unwrap().0 as f32);
        self.y = y.max(0.0).min(renderer.output_size().unwrap().1 as f32);
    }

    pub fn offset(&mut self, renderer: &Renderer, dx: f32, dy: f32) {
        let x = self.x + dx;
        let y = self.y + dy;
        self.set(renderer, x, y);
    }

    pub fn draw(&self, renderer: &mut Renderer) {
        renderer.copy(&self.texture, None, Rect::new((self.x - 26.0) as i32, (self.y - 4.0) as i32, 64, 64).unwrap());
    }
}

struct Font {
    font: sdl2_ttf::Font
}

impl Font {
    pub fn new(font: &str, size: i32) -> Font {
        Font {
            font: sdl2_ttf::Font::from_file(&Path::new(font), size).unwrap()
        }
    }

    pub fn render(&self, renderer: &Renderer, text: &str, color: Color) -> Texture {
        let surface = self.font.render(text, sdl2_ttf::blended(color)).unwrap();
        return renderer.create_texture_from_surface(&surface).unwrap();
    }
}

struct RomConfig {
    pub name: String,
    pub file: String,
    pub image: String,
}

struct Rom {
    name: Texture,
    image: Option<Texture>,
    config: RomConfig,
}

impl Rom {
    pub fn new(renderer: &Renderer, font: &Font, config: RomConfig) -> Rom {
        Rom {
            name: font.render(&renderer, &config.name, Color::RGBA(0, 0, 0, 255)),
            image: renderer.load_texture(&Path::new(&config.image)).ok(),
            config: config
        }
    }

    pub fn draw(&self, renderer: &mut Renderer, x: i32, y: i32, w: u32, h: u32) {
        if let Some(ref image) = self.image {
            let query = image.query();
            let aspect = query.width as f32 / query.height as f32;
    		let w2 = (aspect * h as f32).min(w as f32) as u32;
    		let h2 = (w as f32 / aspect).min(h as f32) as u32;
            let x2 = x + (w - w2) as i32/2;
            let y2 = y + (h - h2) as i32/2;
            renderer.copy(&image, None, Rect::new(x2, y2, w2, h2).unwrap());
        }

        {
            let query = self.name.query();
            let w2 = query.width;
            let h2 = query.height;
            let x2 = x + (w - w2) as i32/2;
            let y2 = y + h as i32;
            renderer.copy(&self.name, None, Rect::new(x2, y2, w2, h2).unwrap());
        }
    }
}

#[derive(RustcDecodable)]
struct EmulatorConfig {
    pub name: String,
    pub image: String,
    pub roms: String,
    pub program: String,
    pub args: Vec<String>,
}

struct Emulator {
    name: Texture,
    image: Texture,
    roms: Vec<Rom>,
    config: EmulatorConfig
}

impl Emulator {
    pub fn new(renderer: &Renderer, font: &Font, config: EmulatorConfig) -> Emulator {
        let mut roms = Vec::new();
        if let Ok(read_dir) = fs::read_dir(&config.roms) {
            for entry_result in read_dir {
                if let Ok(entry) = entry_result {
                    if let Some(path) = entry.path().to_str() {
                        roms.push(Rom::new(renderer, font, RomConfig {
                            name: path.replace(&config.roms, "").trim_matches('/').to_string(),
                            file: path.to_string() + "/rom.bin",
                            image: path.to_string() + "/image.jpg"
                        }))
                    }
                }
            }
        }

        roms.sort_by(|a, b| a.config.name.cmp(&b.config.name));

        Emulator {
            name: font.render(&renderer, &config.name, Color::RGBA(0, 0, 0, 255)),
            image: renderer.load_texture(&Path::new(&config.image)).unwrap(),
            roms: roms,
            config: config
        }
    }

    pub fn draw(&self, renderer: &mut Renderer, x: i32, y: i32, w: u32, h: u32) {
        {
            let query = self.image.query();
            let aspect = query.width as f32 / query.height as f32;
    		let w2 = (aspect * h as f32).min(w as f32) as u32;
    		let h2 = (w as f32 / aspect).min(h as f32) as u32;
            let x2 = x + (w - w2) as i32/2;
            let y2 = y + (h - h2) as i32/2;
            renderer.copy(&self.image, None, Rect::new(x2, y2, w2, h2).unwrap());
        }

        {
            let query = self.name.query();
            let w2 = query.width;
            let h2 = query.height;
            let x2 = x + (w - w2) as i32/2;
            let y2 = y + h as i32;
            renderer.copy(&self.name, None, Rect::new(x2, y2, w2, h2).unwrap());
        }
    }

    pub fn run(&self, rom: &Rom){
        let mut command = Command::new(&self.config.program);
        for arg in self.config.args.iter() {
            if arg == "%r" {
                command.arg(&rom.config.file);
            }else{
                command.arg(arg);
            }
        }

        println!("status: {}", command.status().unwrap());
    }
}

#[derive(Clone, PartialEq)]
enum View {
    Rom(String, usize),
    Emulator(String),
    Overview
}

fn main(){
    let sdl_context = sdl2::init().unwrap();
    let video_subsystem = sdl_context.video().unwrap();
    let controller_subsystem = sdl_context.game_controller().unwrap();
    let _ttf_context = sdl2_ttf::init();

    let window = video_subsystem.window("emulition", 1024, 768)
        .position_centered()
        .opengl()
        .build()
        .unwrap();

    let mut renderer = window.renderer().build().unwrap();

    let mut controllers = Vec::new();
    for id in 0 .. controller_subsystem.num_joysticks().unwrap() {
        if controller_subsystem.is_game_controller(id) {
            controllers.push(controller_subsystem.open(id).unwrap());
        }
    }

    let mut cursor = Cursor::new(&renderer, "res/cursor.png");

    let font = Font::new("res/DroidSans.ttf", 24);

    let mut emulators = BTreeMap::new();

    if let Ok(mut file) = File::open("res/config.toml") {
        let mut toml = String::new();
        if let Ok(_) = file.read_to_string(&mut toml) {
            if let Some(parsed) = toml::Parser::new(&toml).parse() {
                for (key, value) in parsed {
                    if let Some(config) = toml::decode::<EmulatorConfig>(value) {
                        emulators.insert(key, Emulator::new(&renderer, &font, config));
                    }
                }
            }
        }
    }

    let mut event_pump = sdl_context.event_pump().unwrap();

    let mut view = View::Overview;
    'running: loop {
        let mut forward = false;
        let mut backward = false;

        for event in event_pump.poll_iter() {
            match event {
                Event::Quit {..} | Event::KeyDown { scancode: Some(Scancode::Escape), .. } => break 'running,
                Event::KeyDown { scancode: Some(Scancode::Return), .. } => forward = true,
                Event::KeyDown { scancode: Some(Scancode::Backspace), .. } => backward = true,
                Event::ControllerButtonDown { button: Button::A, .. } => forward = true,
                Event::ControllerButtonDown { button: Button::B, .. } => backward = true,
                Event::MouseButtonDown { mouse_btn: Mouse::Left, .. } => forward = true,
                Event::MouseButtonDown { mouse_btn: Mouse::Right, .. } => backward = true,
                Event::MouseMotion { x, y, .. } => cursor.set(&renderer, x as f32, y as f32),
                _ => {}
            }
        }

        if event_pump.keyboard_state().is_scancode_pressed(Scancode::Left) {
            cursor.offset(&renderer, -8.0, 0.0);
        }
        if event_pump.keyboard_state().is_scancode_pressed(Scancode::Right) {
            cursor.offset(&renderer, 8.0, 0.0);
        }
        if event_pump.keyboard_state().is_scancode_pressed(Scancode::Up) {
            cursor.offset(&renderer, 0.0, -8.0);
        }
        if event_pump.keyboard_state().is_scancode_pressed(Scancode::Down) {
            cursor.offset(&renderer, 0.0, 8.0);
        }

        for controller in controllers.iter() {
            let dx = controller.axis(Axis::LeftX) as f32 / 32768.0;
            let dy = controller.axis(Axis::LeftY) as f32 / 32768.0;
            if (dx.powi(2) + dy.powi(2)).sqrt() > 0.2 {
                cursor.offset(&renderer, dx * 8.0, dy * 8.0);
            }

            if controller.button(Button::DPadLeft) {
                cursor.offset(&renderer, -8.0, 0.0);
            }
            if controller.button(Button::DPadRight) {
                cursor.offset(&renderer, 8.0, 0.0);
            }
            if controller.button(Button::DPadUp) {
                cursor.offset(&renderer, 0.0, -8.0);
            }
            if controller.button(Button::DPadDown) {
                cursor.offset(&renderer, 0.0, 8.0);
            }
        }

        renderer.set_draw_color(Color::RGB(255, 255, 255));
        renderer.clear();

        renderer.set_draw_color(Color::RGB(192, 192, 192));

        let mut x = 0;
        let mut y = 0;
        let mut s = renderer.output_size().unwrap().0 / 4;

        let mut new_view = view.clone();
        match view {
            View::Rom(ref key, index) => {
                if let Some(emulator) = emulators.get(key) {
                    emulator.draw(&mut renderer, x + 8, y + 8, s - 16, s - 64);

                    x = s as i32;
                    y = 0;
                    s = s * 3;
                    if let Some(rom) = emulator.roms.get(index) {
                        if cursor.x >= x as f32 && cursor.x < (x + s as i32) as f32 && cursor.y >= y as f32 && cursor.y < (y + s as i32) as f32 {
                            renderer.fill_rect(Rect::new(x, y, s, s).unwrap().unwrap());

                            if forward {
                                emulator.run(rom);
                            }
                        }

                        rom.draw(&mut renderer, x + 8, y + 8, s - 16, s - 64);

                        x = s as i32;
                        y = 0;

                        if backward {
                            new_view = View::Emulator(key.clone());
                        }
                    } else {
                        new_view = View::Emulator(key.clone());
                    }
                } else {
                    new_view = View::Overview
                }
            },
            View::Emulator(ref key) => {
                if let Some(emulator) = emulators.get(key) {
                    emulator.draw(&mut renderer, x + 8, y + 8, s - 16, s - 64);

                    x = s as i32;
                    y = 0;
                    for index in 0 .. emulator.roms.len() {
                        if let Some(rom) = emulator.roms.get(index) {
                            if cursor.x >= x as f32 && cursor.x < (x + s as i32) as f32 && cursor.y >= y as f32 && cursor.y < (y + s as i32) as f32 {
                                renderer.fill_rect(Rect::new(x, y, s, s).unwrap().unwrap());

                                if forward {
                                    new_view = View::Rom(key.clone(), index);
                                    //emulator.run(rom);
                                }
                            }

                            rom.draw(&mut renderer, x + 8, y + 8, s - 16, s - 64);

                            x += s as i32;
                            if x + s as i32 > renderer.output_size().unwrap().0 as i32 {
                                x = s as i32;
                                y += s as i32;
                            }
                        }
                    }

                    if backward {
                        new_view = View::Overview
                    }
                } else {
                    new_view = View::Overview
                }
            },
            View::Overview => {
                for (key, emulator) in emulators.iter() {
                    if cursor.x >= x as f32 && cursor.x < (x + s as i32) as f32 && cursor.y >= y as f32 && cursor.y < (y + s as i32) as f32 {
                        renderer.fill_rect(Rect::new(x, y, s, s).unwrap().unwrap());

                        if forward {
                            new_view = View::Emulator(key.clone());
                        }
                    }

                    emulator.draw(&mut renderer, x + 8, y + 8, s - 16, s - 64);

                    x += s as i32;
                    if x + s as i32 > renderer.output_size().unwrap().0 as i32 {
                        x = 0;
                        y += s as i32;
                    }
                }
            }
        };

        cursor.draw(&mut renderer);

        renderer.present();

        if new_view != view {
            view = new_view;
        } else {
            std::thread::sleep(Duration::from_millis(1000/60));
        }
    }
}
<commit_msg>Allow resizing<commit_after>extern crate rustc_serialize;
extern crate sdl2;
extern crate sdl2_image;
extern crate sdl2_ttf;
extern crate toml;

use std::cmp::min;
use std::collections::BTreeMap;
use std::fs::{self, File};
use std::io::Read;
use std::path::Path;
use std::process::Command;
use std::time::Duration;

use sdl2::controller::{Axis, Button};
use sdl2::event::Event;
use sdl2::keyboard::Scancode;
use sdl2::mouse::Mouse;
use sdl2::pixels::Color;
use sdl2::rect::Rect;
use sdl2::render::{Renderer, Texture};

use sdl2_image::LoadTexture;

struct NormalTexture {
    texture: Texture
}

impl NormalTexture {
    pub fn new(texture: Texture) -> NormalTexture {
        NormalTexture {
            texture: texture
        }
    }

    pub fn draw(&self, renderer: &mut Renderer, x: i32, y: i32) {
        let query = self.texture.query();
        renderer.copy(&self.texture, None, Rect::new(x, y, query.width, query.height).unwrap());
    }
}

struct CenteredTexture {
    texture: Texture
}

impl CenteredTexture {
    pub fn new(texture: Texture) -> CenteredTexture {
        CenteredTexture {
            texture: texture
        }
    }

    pub fn draw(&self, renderer: &mut Renderer, x: i32, y: i32, w: i32, h: i32) {
        let query = self.texture.query();
        let w2 = query.width as i32;
        let h2 = query.height as i32;
        let x2 = x + (w - w2)/2;
        let y2 = y + (h - h2)/2;
        if h2 > 0 && w2 > 0 {
            renderer.copy(&self.texture, None, Rect::new(x2, y2, w2 as u32, h2 as u32).unwrap());
        }
    }
}

struct ScaledTexture {
    texture: Texture
}

impl ScaledTexture {
    pub fn new(texture: Texture) -> ScaledTexture {
        ScaledTexture {
            texture: texture
        }
    }

    pub fn draw(&self, renderer: &mut Renderer, x: i32, y: i32, w: i32, h: i32) {
        let query = self.texture.query();
        let aspect = query.width as f32 / query.height as f32;
        let w2 = (aspect * h as f32).min(w as f32) as i32;
        let h2 = (w as f32 / aspect).min(h as f32) as i32;
        let x2 = x + (w - w2)/2;
        let y2 = y + (h - h2)/2;
        if h2 > 0 && w2 > 0 {
            renderer.copy(&self.texture, None, Rect::new(x2, y2, w2 as u32, h2 as u32).unwrap());
        }
    }
}

struct Cursor {
    pub x: f32,
    pub y: f32,
    texture: NormalTexture,
}

impl Cursor {
    pub fn new(renderer: &Renderer, image: &str) -> Cursor {
        Cursor {
            x: 0.0,
            y: 0.0,
            texture: NormalTexture::new(renderer.load_texture(&Path::new(image)).unwrap()),
        }
    }

    pub fn set(&mut self, renderer: &Renderer, x: f32, y: f32) {
        self.x = x.max(0.0).min(renderer.output_size().unwrap().0 as f32);
        self.y = y.max(0.0).min(renderer.output_size().unwrap().1 as f32);
    }

    pub fn offset(&mut self, renderer: &Renderer, dx: f32, dy: f32) {
        let x = self.x + dx;
        let y = self.y + dy;
        self.set(renderer, x, y);
    }

    pub fn draw(&self, renderer: &mut Renderer) {
        self.texture.draw(renderer, (self.x - 26.0) as i32, (self.y - 4.0) as i32);
    }
}

struct Font {
    font: sdl2_ttf::Font
}

impl Font {
    pub fn new(font: &str, size: i32) -> Font {
        Font {
            font: sdl2_ttf::Font::from_file(&Path::new(font), size).unwrap()
        }
    }

    pub fn render(&self, renderer: &Renderer, text: &str, color: Color) -> Texture {
        let surface = self.font.render(text, sdl2_ttf::blended(color)).unwrap();
        return renderer.create_texture_from_surface(&surface).unwrap();
    }
}

struct RomConfig {
    pub name: String,
    pub file: String,
    pub image: String,
}

struct Rom {
    name: CenteredTexture,
    image: Option<ScaledTexture>,
    config: RomConfig,
}

impl Rom {
    pub fn new(renderer: &Renderer, font: &Font, config: RomConfig) -> Rom {
        Rom {
            name: CenteredTexture::new(font.render(&renderer, &config.name, Color::RGB(0, 0, 0))),
            image: if let Some(texture) = renderer.load_texture(&Path::new(&config.image)).ok() {
                Some(ScaledTexture::new(texture))
            } else {
                None
            },
            config: config
        }
    }

    pub fn draw(&self, renderer: &mut Renderer, x: i32, y: i32, w: i32, h: i32) {
        if let Some(ref image) = self.image {
            image.draw(renderer, x + 8, y + 8, w - 16, h - 64);
        }
        self.name.draw(renderer, x, y + h - 64, w, 64);
    }
}

#[derive(RustcDecodable)]
struct EmulatorConfig {
    pub name: String,
    pub image: String,
    pub roms: String,
    pub program: String,
    pub args: Vec<String>,
}

struct Emulator {
    name: CenteredTexture,
    image: ScaledTexture,
    roms: Vec<Rom>,
    config: EmulatorConfig
}

impl Emulator {
    pub fn new(renderer: &Renderer, font: &Font, config: EmulatorConfig) -> Emulator {
        let mut roms = Vec::new();
        if let Ok(read_dir) = fs::read_dir(&config.roms) {
            for entry_result in read_dir {
                if let Ok(entry) = entry_result {
                    if let Some(path) = entry.path().to_str() {
                        roms.push(Rom::new(renderer, font, RomConfig {
                            name: path.replace(&config.roms, "").trim_matches('/').to_string(),
                            file: path.to_string() + "/rom.bin",
                            image: path.to_string() + "/image.jpg"
                        }))
                    }
                }
            }
        }

        roms.sort_by(|a, b| a.config.name.cmp(&b.config.name));

        Emulator {
            name: CenteredTexture::new(font.render(&renderer, &config.name, Color::RGB(0, 0, 0))),
            image: ScaledTexture::new(renderer.load_texture(&Path::new(&config.image)).unwrap()),
            roms: roms,
            config: config
        }
    }

    pub fn draw(&self, renderer: &mut Renderer, x: i32, y: i32, w: i32, h: i32) {
        self.image.draw(renderer, x + 8, y + 8, w - 16, h - 64);
        self.name.draw(renderer, x, y + h as i32 - 64, w, 64);
    }

    pub fn run(&self, rom: &Rom){
        let mut command = Command::new(&self.config.program);
        for arg in self.config.args.iter() {
            if arg == "%r" {
                command.arg(&rom.config.file);
            }else{
                command.arg(arg);
            }
        }

        println!("status: {}", command.status().unwrap());
    }
}

#[derive(Clone, PartialEq)]
enum View {
    Rom(String, usize),
    Emulator(String),
    Overview
}

fn main(){
    let sdl_context = sdl2::init().unwrap();
    let video_subsystem = sdl_context.video().unwrap();
    let controller_subsystem = sdl_context.game_controller().unwrap();
    let _ttf_context = sdl2_ttf::init();

    let window = video_subsystem.window("emulition", 1024, 768)
        .position_centered()
        .resizable()
        .opengl()
        .build()
        .unwrap();

    let mut renderer = window.renderer().build().unwrap();

    let mut controllers = Vec::new();
    for id in 0 .. controller_subsystem.num_joysticks().unwrap() {
        if controller_subsystem.is_game_controller(id) {
            controllers.push(controller_subsystem.open(id).unwrap());
        }
    }

    let mut cursor = Cursor::new(&renderer, "res/cursor.png");

    let font = Font::new("res/DroidSans.ttf", 24);

    let back = font.render(&renderer, "< Back", Color::RGB(0, 0, 0));
    let download = font.render(&renderer, "Download ROMs", Color::RGB(0, 0, 0));

    let mut emulators = BTreeMap::new();

    if let Ok(mut file) = File::open("res/config.toml") {
        let mut toml = String::new();
        if let Ok(_) = file.read_to_string(&mut toml) {
            if let Some(parsed) = toml::Parser::new(&toml).parse() {
                for (key, value) in parsed {
                    if let Some(config) = toml::decode::<EmulatorConfig>(value) {
                        emulators.insert(key, Emulator::new(&renderer, &font, config));
                    }
                }
            }
        }
    }

    let mut event_pump = sdl_context.event_pump().unwrap();

    let mut view = View::Overview;
    'running: loop {
        let mut forward = false;
        let mut backward = false;

        for event in event_pump.poll_iter() {
            match event {
                Event::Quit {..} | Event::KeyDown { scancode: Some(Scancode::Escape), .. } => break 'running,
                Event::KeyDown { scancode: Some(Scancode::Return), .. } => forward = true,
                Event::KeyDown { scancode: Some(Scancode::Backspace), .. } => backward = true,
                Event::ControllerButtonDown { button: Button::A, .. } => forward = true,
                Event::ControllerButtonDown { button: Button::B, .. } => backward = true,
                Event::MouseButtonDown { mouse_btn: Mouse::Left, .. } => forward = true,
                Event::MouseButtonDown { mouse_btn: Mouse::Right, .. } => backward = true,
                Event::MouseMotion { x, y, .. } => cursor.set(&renderer, x as f32, y as f32),
                _ => {}
            }
        }

        if event_pump.keyboard_state().is_scancode_pressed(Scancode::Left) {
            cursor.offset(&renderer, -8.0, 0.0);
        }
        if event_pump.keyboard_state().is_scancode_pressed(Scancode::Right) {
            cursor.offset(&renderer, 8.0, 0.0);
        }
        if event_pump.keyboard_state().is_scancode_pressed(Scancode::Up) {
            cursor.offset(&renderer, 0.0, -8.0);
        }
        if event_pump.keyboard_state().is_scancode_pressed(Scancode::Down) {
            cursor.offset(&renderer, 0.0, 8.0);
        }

        for controller in controllers.iter() {
            let dx = controller.axis(Axis::LeftX) as f32 / 32768.0;
            let dy = controller.axis(Axis::LeftY) as f32 / 32768.0;
            if (dx.powi(2) + dy.powi(2)).sqrt() > 0.2 {
                cursor.offset(&renderer, dx * 8.0, dy * 8.0);
            }

            if controller.button(Button::DPadLeft) {
                cursor.offset(&renderer, -8.0, 0.0);
            }
            if controller.button(Button::DPadRight) {
                cursor.offset(&renderer, 8.0, 0.0);
            }
            if controller.button(Button::DPadUp) {
                cursor.offset(&renderer, 0.0, -8.0);
            }
            if controller.button(Button::DPadDown) {
                cursor.offset(&renderer, 0.0, 8.0);
            }
        }

        renderer.set_draw_color(Color::RGB(255, 255, 255));
        renderer.clear();

        renderer.set_draw_color(Color::RGB(192, 192, 192));

        let mut x = 0;
        let mut y = 0;
        let mut s = min(renderer.output_size().unwrap().0 as i32 / 4, renderer.output_size().unwrap().1 as i32 / 3);

        let mut new_view = view.clone();
        match view {
            View::Rom(ref key, index) => {
                if let Some(emulator) = emulators.get(key) {
                    emulator.draw(&mut renderer, x, y, s, s);

                    x = s;
                    y = 0;
                    s = s * 3;
                    if let Some(rom) = emulator.roms.get(index) {
                        if cursor.x >= x as f32 && cursor.x < (x + s) as f32 && cursor.y >= y as f32 && cursor.y < (y + s) as f32 {
                            renderer.fill_rect(Rect::new(x, y, s as u32, s as u32).unwrap().unwrap());

                            if forward {
                                emulator.run(rom);
                            }
                        }

                        rom.draw(&mut renderer, x, y, s, s);

                        x = s;
                        y = 0;

                        if backward {
                            new_view = View::Emulator(key.clone());
                        }
                    } else {
                        new_view = View::Emulator(key.clone());
                    }
                } else {
                    new_view = View::Overview
                }
            },
            View::Emulator(ref key) => {
                if let Some(emulator) = emulators.get(key) {
                    emulator.draw(&mut renderer, x, y, s, s);
                    y += s;

                    x = s;
                    y = 0;
                    for index in 0 .. emulator.roms.len() {
                        if let Some(rom) = emulator.roms.get(index) {
                            if cursor.x >= x as f32 && cursor.x < (x + s) as f32 && cursor.y >= y as f32 && cursor.y < (y + s) as f32 {
                                renderer.fill_rect(Rect::new(x, y, s as u32, s as u32).unwrap().unwrap());

                                if forward {
                                    new_view = View::Rom(key.clone(), index);
                                }
                            }

                            rom.draw(&mut renderer, x, y, s, s);

                            x += s;
                            if x + s > renderer.output_size().unwrap().0 as i32 {
                                x = s;
                                y += s;
                            }
                        }
                    }

                    if backward {
                        new_view = View::Overview
                    }
                } else {
                    new_view = View::Overview
                }
            },
            View::Overview => {
                for (key, emulator) in emulators.iter() {
                    if cursor.x >= x as f32 && cursor.x < (x + s) as f32 && cursor.y >= y as f32 && cursor.y < (y + s) as f32 {
                        renderer.fill_rect(Rect::new(x, y, s as u32, s as u32).unwrap().unwrap());

                        if forward {
                            new_view = View::Emulator(key.clone());
                        }
                    }

                    emulator.draw(&mut renderer, x, y, s, s);

                    x += s;
                    if x + s > renderer.output_size().unwrap().0 as i32 {
                        x = 0;
                        y += s;
                    }
                }
            }
        };

        cursor.draw(&mut renderer);

        renderer.present();

        if new_view != view {
            view = new_view;
        } else {
            std::thread::sleep(Duration::from_millis(1000/60));
        }
    }
}
<|endoftext|>


<commit_before>//#![feature(no_std, lang_items, asm, core, core_str_ext)]
//#![no_std]
#![feature(asm, libc)]
#![feature(convert)]
#![no_main]

#![allow(dead_code)] // yells about consts otherwise
#![allow(unused_variables)]

//mod llvm_symbols;
mod auxv;
mod kernel_block;
mod utils;
mod image;
mod binary;
mod relocate;
mod link_map;
mod linker;

use kernel_block::KernelBlock;
use utils::*;

extern crate libc;

// below is gcc attrs for this function...
//extern "C"
//void __attribute__((noinline)) __attribute__((visibility("default")))
// unused; someone figure out how to get gdb working when running as a dyld
extern "C" {
    fn rtld_db_dlactivity();
}

extern {
    /// elf abi requires `_start`; this must be in assembly because we need
    /// the raw stack pointer as the argument to `_dryad_init`;
    /// i.e., kernel calls symbol `_start` on dynamic linker with the kernel argument block, etc.,
    /// which in our case then calls _back_ into `dryad_init`
    fn _start();
    fn printf();
}

#[no_mangle]
pub extern fn _dryad_init(raw_args: *const u64) -> u64 {

    let block = KernelBlock::new(raw_args);
    unsafe { block.unsafe_print(); }

    let linker_base = block.getauxval(auxv::AT_BASE).unwrap();
    let entry  = block.getauxval(auxv::AT_ENTRY).unwrap();

    let start_addr = _start as *const u64 as u64;

    // without this,
    // following comparison fails for some inexplicable reason... yay for side-effectful printing again
    unsafe {
        write(&"start: 0x");
        write_u64(start_addr, true);
        write(&" entry: 0x");
        write_u64(entry, true);
        write(&"\n");
    }

    if start_addr == entry {
        // because it's _tradition_
        // (https://fossies.org/dox/glibc-2.22/rtld_8c_source.html)
        // line 786:
        // > Ho ho.  We are not the program interpreter!  We are the program itself!
        unsafe { write(&"-=|dryad====-\n"); }
        _exit(0);
        return 0xd47ad // to make compiler happy
    }

    match linker::Linker::new(linker_base, &block) {
        Ok (dryad) => {
            println!("Dryad:\n  {:#?}", &dryad);
            println!("BEGIN EXE LINKING");
            let name = utils::as_str(block.argv[0]);
            let phdr_addr = block.getauxval(auxv::AT_PHDR).unwrap();
            let phnum  = block.getauxval(auxv::AT_PHNUM).unwrap();
            let main_image = image::elf::ElfExec::new(name, phdr_addr, phnum as usize);
            println!("Main Image:\n  {:#?}", &main_image);

            println!("<dryad> Final result: {:?}", dryad.link(main_image));

            // commenting _exit will successfully
            // tranfer control (in my single test case ;))
            // to the program entry in test/test,
            // but segfaults when printf is called (obviously)
            // since we've done no dynamic linking
            _exit(0);
            entry
        },
        Err (msg) => {
            // relocating self failed somehow; we write the error message and exit
            unsafe { write(&msg); }
            _exit(1);
            0xd47ad
        }
    }
}
<commit_msg>remove unused printf extern import<commit_after>//#![feature(no_std, lang_items, asm, core, core_str_ext)]
//#![no_std]
#![feature(asm, libc)]
#![feature(convert)]
#![no_main]

#![allow(dead_code)] // yells about consts otherwise
#![allow(unused_variables)]

//mod llvm_symbols;
mod auxv;
mod kernel_block;
mod utils;
mod image;
mod binary;
mod relocate;
mod link_map;
mod linker;

use kernel_block::KernelBlock;
use utils::*;

extern crate libc;

// below is gcc attrs for this function...
//extern "C"
//void __attribute__((noinline)) __attribute__((visibility("default")))
// unused; someone figure out how to get gdb working when running as a dyld
extern "C" {
    fn rtld_db_dlactivity();
}

extern {
    /// elf abi requires `_start`; this must be in assembly because we need
    /// the raw stack pointer as the argument to `_dryad_init`;
    /// i.e., kernel calls symbol `_start` on dynamic linker with the kernel argument block, etc.,
    /// which in our case then calls _back_ into `dryad_init`
    fn _start();
}

#[no_mangle]
pub extern fn _dryad_init(raw_args: *const u64) -> u64 {

    let block = KernelBlock::new(raw_args);
    unsafe { block.unsafe_print(); }

    let linker_base = block.getauxval(auxv::AT_BASE).unwrap();
    let entry  = block.getauxval(auxv::AT_ENTRY).unwrap();

    let start_addr = _start as *const u64 as u64;

    // without this,
    // following comparison fails for some inexplicable reason... yay for side-effectful printing again
    unsafe {
        write(&"start: 0x");
        write_u64(start_addr, true);
        write(&" entry: 0x");
        write_u64(entry, true);
        write(&"\n");
    }

    if start_addr == entry {
        // because it's _tradition_
        // (https://fossies.org/dox/glibc-2.22/rtld_8c_source.html)
        // line 786:
        // > Ho ho.  We are not the program interpreter!  We are the program itself!
        unsafe { write(&"-=|dryad====-\n"); }
        _exit(0);
        return 0xd47ad // to make compiler happy
    }

    match linker::Linker::new(linker_base, &block) {
        Ok (dryad) => {
            println!("Dryad:\n  {:#?}", &dryad);
            println!("BEGIN EXE LINKING");
            let name = utils::as_str(block.argv[0]);
            let phdr_addr = block.getauxval(auxv::AT_PHDR).unwrap();
            let phnum  = block.getauxval(auxv::AT_PHNUM).unwrap();
            let main_image = image::elf::ElfExec::new(name, phdr_addr, phnum as usize);
            println!("Main Image:\n  {:#?}", &main_image);

            println!("<dryad> Final result: {:?}", dryad.link(main_image));

            // commenting _exit will successfully
            // tranfer control (in my single test case ;))
            // to the program entry in test/test,
            // but segfaults when printf is called (obviously)
            // since we've done no dynamic linking
            _exit(0);
            entry
        },
        Err (msg) => {
            // relocating self failed somehow; we write the error message and exit
            unsafe { write(&msg); }
            _exit(1);
            0xd47ad
        }
    }
}
<|endoftext|>


<commit_before>extern crate mmap;
extern crate time;
extern crate timely;
extern crate columnar;
extern crate docopt;

use docopt::Docopt;

use std::thread;

use timely::progress::timestamp::RootTimestamp;
use timely::progress::scope::Scope;
use timely::progress::nested::Summary::Local;
use timely::example_shared::*;
use timely::example_shared::operators::*;
use timely::communication::*;
use timely::communication::pact::Exchange;

use timely::networking::initialize_networking;
use timely::networking::initialize_networking_from_file;

use timely::drain::DrainExt;

use columnar::Columnar;

mod typedrw;
mod graphmap;
use graphmap::GraphMMap;

mod sorting;
use sorting::{SegmentList, radix_sort_32};

static USAGE: &'static str = "
Usage: pagerank <source> [options] [<arguments>...]

Options:
    -w <arg>, --workers <arg>    number of workers per process [default: 1]
    -p <arg>, --processid <arg>  identity of this process      [default: 0]
    -n <arg>, --processes <arg>  number of processes involved  [default: 1]
    -h <arg>, --hosts <arg>      file containing list of host:port for workers
";

#[derive(Copy, Clone, Debug)]
struct NodeRank { node: u32, rank: f32, }
impl Columnar for NodeRank { type Stack = Vec<NodeRank>; }

fn main () {
    let args = Docopt::new(USAGE).and_then(|dopt| dopt.parse()).unwrap_or_else(|e| e.exit());

    let source = args.get_str("<source>").to_owned();

    let workers: u64 = if let Ok(threads) = args.get_str("-w").parse() { threads }
                       else { panic!("invalid setting for --workers: {}", args.get_str("-t")) };
    let process_id: u64 = if let Ok(proc_id) = args.get_str("-p").parse() { proc_id }
                          else { panic!("invalid setting for --processid: {}", args.get_str("-p")) };
    let processes: u64 = if let Ok(processes) = args.get_str("-n").parse() { processes }
                         else { panic!("invalid setting for --processes: {}", args.get_str("-n")) };

    println!("Starting pagerank dataflow with");
    println!("\tworkers:\t{}", workers);
    println!("\tprocesses:\t{}", processes);
    println!("\tprocessid:\t{}", process_id);

    // vector holding communicators to use; one per local worker.
    if processes > 1 {
        println!("Initializing BinaryCommunicator");

        let hosts = args.get_str("-h");
        let communicators = if hosts != "" {
            initialize_networking_from_file(hosts, process_id, workers).ok().expect("error initializing networking")
        }
        else {
            let addresses = (0..processes).map(|index| format!("localhost:{}", 2101 + index).to_string()).collect();
            initialize_networking(addresses, process_id, workers).ok().expect("error initializing networking")
        };

        pagerank_spawn(communicators, source);
    }
    else if workers > 1 { pagerank_spawn(ProcessCommunicator::new_vector(workers), source); }
    else { pagerank_spawn(vec![ThreadCommunicator], source); };
}

fn pagerank_spawn<C>(communicators: Vec<C>, filename: String)
where C: Communicator+Send {
    let mut guards = Vec::new();
    let workers = communicators.len();
    for communicator in communicators.into_iter() {
        let filename = filename.clone();
        guards.push(thread::Builder::new().name(format!("timely worker {}", communicator.index()))
                                          .spawn(move || pagerank_thread(communicator, filename, workers))
                                          .unwrap());
    }

    for guard in guards { guard.join().unwrap(); }
}

// returns [src/peers] degrees, (dst, deg) pairs, and a list of [src/peers] endpoints
fn transpose(mut edges: Vec<Vec<(u32, u32)>>, peers: usize, nodes: usize) -> (Vec<u32>, Vec<(u32, u32)>, Vec<u32>)  {

    let mut deg = vec![0; (nodes as usize / peers) + 1];
    for list in &edges {
        for &(s, _) in list {
            deg[s as usize / peers] += 1;
        }
    }

    // let start = time::precise_time_s();
    radix_sort_32(&mut edges, &mut Vec::new(), &|&(_,d)| d);
    // println!("sorted in {}s", time::precise_time_s() - start);

    let mut rev = Vec::<(u32,u32)>::with_capacity(deg.len());
    let mut trn = Vec::with_capacity(edges.len() * 1024);
    for list in edges {
        for (s,d) in list {
            let len = rev.len();
            if (len == 0) || (rev[len-1].0 < d) {
                rev.push((d, 0u32));
            }

            let len = rev.len();
            rev[len-1].1 += 1;
            trn.push(s / peers as u32);
        }
    }

    return (deg, rev, trn);
}

// pagerank dataflow graph has a set of edges as input, and a binary vertex that for each epoch of
// received edges initiates an iterative subcomputation to compute the pagerank.

fn pagerank_thread<C>(communicator: C, filename: String, _workers: usize)
where C: Communicator {
    let index = communicator.index() as usize;
    let peers = communicator.peers() as usize;

    let mut root = GraphRoot::new(communicator);

    let start = time::precise_time_s();

    let nodes = GraphMMap::new(&filename).nodes();

    let mut segments = SegmentList::new(1024); // list of edge segments

    let mut src = vec![];   // holds ranks
    let mut deg = vec![];   // holds source degrees
    let mut rev = vec![];   // holds (dst, deg) pairs
    let mut trn = vec![];   // holds transposed sources

    let mut input = root.subcomputation(|builder| {

        let (input, edges) = builder.new_input::<(u32, u32)>();
        let (cycle, ranks) = builder.loop_variable::<NodeRank>(RootTimestamp::new(20), Local(1));

        edges.binary_notify(&ranks,
                            Exchange::new(|x: &(u32,u32)| x.0 as u64),
                            Exchange::new(|x: &NodeRank| x.node as u64),
                            format!("pagerank"),
                            vec![RootTimestamp::new(0)],
                            move |input1, input2, output, notificator| {

            // receive incoming edges
            while let Some((_index, data)) = input1.pull() {
                segments.push(data.drain_temp());
            }

            while let Some((iter, _)) = notificator.next() {

                // if the very first iteration, prepare some stuff
                if iter.inner == 0 {
                    let (a, b, c) = transpose(segments.finalize(), peers, nodes);
                    deg = a; rev = b; trn = c;
                    src = vec![0.0f32; deg.len()];
                }

                println!("{}\tworker {}: notify[{}] begin", time::precise_time_s() - start, index, iter.inner);

                for s in 0..src.len() { src[s] = 0.15 + 0.85 * src[s] / deg[s] as f32; }

                let mut trn_slice = &trn[..];
                let mut rev_slice = &rev[..];
                while rev_slice.len() > 0 {
                    // TODO: session should just flush...
                    let mut session = output.session(&iter);
                    let next = std::cmp::min(100_000, rev_slice.len());
                    for &(dst, deg) in &rev_slice[..next] {
                        let mut accum = 0.0;
                        for &s in &trn_slice[..deg as usize] {
                            accum += src[s as usize];
                        }
                        trn_slice = &trn_slice[deg as usize..];
                        session.give(NodeRank { node: dst, rank: accum });
                    }
                    rev_slice = &rev_slice[next..];
                }

                for s in &mut src { *s = 0.0; }

                println!("{}\tworker {}: notify[{}] ended", time::precise_time_s() - start, index, iter.inner);
            }

            while let Some((iter, data)) = input2.pull() {
                notificator.notify_at(&iter);
                for x in data.drain_temp() {
                    src[x.node as usize / peers] += x.rank;
                }
            }
        })
        .connect_loop(cycle);

        input
    });

    {
        let graph = GraphMMap::new(&filename);

        let mut edges = Vec::new();
        for node in 0..graph.nodes() {
            if node % peers == index {
                for dst in graph.edges(node) {
                    edges.push((node as u32, *dst as u32));
                }
                if edges.len() > 1024 {
                    input.send_at(0, edges.drain_temp());
                    root.step();
                }
            }
        }

        input.send_at(0, edges.drain_temp());
    }
    input.close();
    while root.step() { }

    if index == 0 { println!("elapsed: {}", time::precise_time_s() - start); }
}

    // .unary_notify(
    //     Exchange::new(move |x: &(u32, f32)| (workers * (index / workers)) as u64 + (x.0 as u64 % workers as u64)),
    //     format!("Aggregation"),
    //     vec![],
    //     move |input, output, iterator| {
    //         while let Some((iter, data)) = input.pull() {
    //             iterator.notify_at(&iter);
    //             for (node, rank) in data.drain_temp() {
    //                 acc[node as usize / workers] += rank;
    //             }
    //         }
    //
    //         while let Some((item, _)) = iterator.next() {
    //
    //             output.give_at(&item, acc.drain_temp().enumerate().filter(|x| x.1 != 0.0)
    //                                      .map(|(u,f)| (((u * workers + local_index) as u32), f)));
    //
    //             for _ in 0..(1 + (nodes/workers)) { acc.push(0.0); }
    //             assert!(acc.len() == (1 + (nodes/workers)));
    //         }
    //     }
    // )
<commit_msg>beautified reporting<commit_after>extern crate mmap;
extern crate time;
extern crate timely;
extern crate columnar;
extern crate docopt;

use docopt::Docopt;

use std::thread;

use timely::progress::timestamp::RootTimestamp;
use timely::progress::scope::Scope;
use timely::progress::nested::Summary::Local;
use timely::example_shared::*;
use timely::example_shared::operators::*;
use timely::communication::*;
use timely::communication::pact::Exchange;

use timely::networking::initialize_networking;
use timely::networking::initialize_networking_from_file;

use timely::drain::DrainExt;

use columnar::Columnar;

mod typedrw;
mod graphmap;
use graphmap::GraphMMap;

mod sorting;
use sorting::{SegmentList, radix_sort_32};

static USAGE: &'static str = "
Usage: pagerank <source> [options] [<arguments>...]

Options:
    -w <arg>, --workers <arg>    number of workers per process [default: 1]
    -p <arg>, --processid <arg>  identity of this process      [default: 0]
    -n <arg>, --processes <arg>  number of processes involved  [default: 1]
    -h <arg>, --hosts <arg>      file containing list of host:port for workers
";

#[derive(Copy, Clone, Debug)]
struct NodeRank { node: u32, rank: f32, }
impl Columnar for NodeRank { type Stack = Vec<NodeRank>; }

fn main () {
    let args = Docopt::new(USAGE).and_then(|dopt| dopt.parse()).unwrap_or_else(|e| e.exit());

    let source = args.get_str("<source>").to_owned();

    let workers: u64 = if let Ok(threads) = args.get_str("-w").parse() { threads }
                       else { panic!("invalid setting for --workers: {}", args.get_str("-t")) };
    let process_id: u64 = if let Ok(proc_id) = args.get_str("-p").parse() { proc_id }
                          else { panic!("invalid setting for --processid: {}", args.get_str("-p")) };
    let processes: u64 = if let Ok(processes) = args.get_str("-n").parse() { processes }
                         else { panic!("invalid setting for --processes: {}", args.get_str("-n")) };

    println!("Starting pagerank dataflow with");
    println!("\tworkers:\t{}", workers);
    println!("\tprocesses:\t{}", processes);
    println!("\tprocessid:\t{}", process_id);

    // vector holding communicators to use; one per local worker.
    if processes > 1 {
        println!("Initializing BinaryCommunicator");

        let hosts = args.get_str("-h");
        let communicators = if hosts != "" {
            initialize_networking_from_file(hosts, process_id, workers).ok().expect("error initializing networking")
        }
        else {
            let addresses = (0..processes).map(|index| format!("localhost:{}", 2101 + index).to_string()).collect();
            initialize_networking(addresses, process_id, workers).ok().expect("error initializing networking")
        };

        pagerank_spawn(communicators, source);
    }
    else if workers > 1 { pagerank_spawn(ProcessCommunicator::new_vector(workers), source); }
    else { pagerank_spawn(vec![ThreadCommunicator], source); };
}

fn pagerank_spawn<C>(communicators: Vec<C>, filename: String)
where C: Communicator+Send {
    let mut guards = Vec::new();
    let workers = communicators.len();
    for communicator in communicators.into_iter() {
        let filename = filename.clone();
        guards.push(thread::Builder::new().name(format!("timely worker {}", communicator.index()))
                                          .spawn(move || pagerank_thread(communicator, filename, workers))
                                          .unwrap());
    }

    for guard in guards { guard.join().unwrap(); }
}

// returns [src/peers] degrees, (dst, deg) pairs, and a list of [src/peers] endpoints
fn transpose(mut edges: Vec<Vec<(u32, u32)>>, peers: usize, nodes: usize) -> (Vec<u32>, Vec<(u32, u32)>, Vec<u32>)  {

    let mut deg = vec![0; (nodes as usize / peers) + 1];
    for list in &edges {
        for &(s, _) in list {
            deg[s as usize / peers] += 1;
        }
    }

    // let start = time::precise_time_s();
    radix_sort_32(&mut edges, &mut Vec::new(), &|&(_,d)| d);
    // println!("sorted in {}s", time::precise_time_s() - start);

    let mut rev = Vec::<(u32,u32)>::with_capacity(deg.len());
    let mut trn = Vec::with_capacity(edges.len() * 1024);
    for list in edges {
        for (s,d) in list {
            let len = rev.len();
            if (len == 0) || (rev[len-1].0 < d) {
                rev.push((d, 0u32));
            }

            let len = rev.len();
            rev[len-1].1 += 1;
            trn.push(s / peers as u32);
        }
    }

    return (deg, rev, trn);
}

// pagerank dataflow graph has a set of edges as input, and a binary vertex that for each epoch of
// received edges initiates an iterative subcomputation to compute the pagerank.

fn pagerank_thread<C>(communicator: C, filename: String, _workers: usize)
where C: Communicator {
    let index = communicator.index() as usize;
    let peers = communicator.peers() as usize;

    let mut root = GraphRoot::new(communicator);

    let start = time::precise_time_s();

    let nodes = GraphMMap::new(&filename).nodes();

    let mut segments = SegmentList::new(1024); // list of edge segments

    let mut src = vec![];   // holds ranks
    let mut deg = vec![];   // holds source degrees
    let mut rev = vec![];   // holds (dst, deg) pairs
    let mut trn = vec![];   // holds transposed sources

    let mut input = root.subcomputation(|builder| {

        let (input, edges) = builder.new_input::<(u32, u32)>();
        let (cycle, ranks) = builder.loop_variable::<NodeRank>(RootTimestamp::new(20), Local(1));

        edges.binary_notify(&ranks,
                            Exchange::new(|x: &(u32,u32)| x.0 as u64),
                            Exchange::new(|x: &NodeRank| x.node as u64),
                            format!("pagerank"),
                            vec![RootTimestamp::new(0)],
                            move |input1, input2, output, notificator| {

            // receive incoming edges
            while let Some((_index, data)) = input1.pull() {
                segments.push(data.drain_temp());
            }

            while let Some((iter, _)) = notificator.next() {

                // if the very first iteration, prepare some stuff
                if iter.inner == 0 {
                    let (a, b, c) = transpose(segments.finalize(), peers, nodes);
                    deg = a; rev = b; trn = c;
                    src = vec![0.0f32; deg.len()];
                }

                println!("{:.3}\tworker {}: notify[{}] begin", time::precise_time_s() - start, index, iter.inner);

                for s in 0..src.len() { src[s] = 0.15 + 0.85 * src[s] / deg[s] as f32; }

                let mut trn_slice = &trn[..];
                let mut rev_slice = &rev[..];
                while rev_slice.len() > 0 {
                    // TODO: session should just flush...
                    let mut session = output.session(&iter);
                    let next = std::cmp::min(100_000, rev_slice.len());
                    for &(dst, deg) in &rev_slice[..next] {
                        let mut accum = 0.0;
                        for &s in &trn_slice[..deg as usize] {
                            accum += src[s as usize];
                        }
                        trn_slice = &trn_slice[deg as usize..];
                        session.give(NodeRank { node: dst, rank: accum });
                    }
                    rev_slice = &rev_slice[next..];
                }

                for s in &mut src { *s = 0.0; }

                println!("{:.3}\tworker {}: notify[{}] ended", time::precise_time_s() - start, index, iter.inner);
            }

            while let Some((iter, data)) = input2.pull() {
                notificator.notify_at(&iter);
                for x in data.drain_temp() {
                    src[x.node as usize / peers] += x.rank;
                }
            }
        })
        .connect_loop(cycle);

        input
    });

    {
        let graph = GraphMMap::new(&filename);

        let mut edges = Vec::new();
        for node in 0..graph.nodes() {
            if node % peers == index {
                for dst in graph.edges(node) {
                    edges.push((node as u32, *dst as u32));
                }
                if edges.len() > 1024 {
                    input.send_at(0, edges.drain_temp());
                    root.step();
                }
            }
        }

        input.send_at(0, edges.drain_temp());
    }
    input.close();
    while root.step() { }

    if index == 0 { println!("elapsed: {}", time::precise_time_s() - start); }
}

    // .unary_notify(
    //     Exchange::new(move |x: &(u32, f32)| (workers * (index / workers)) as u64 + (x.0 as u64 % workers as u64)),
    //     format!("Aggregation"),
    //     vec![],
    //     move |input, output, iterator| {
    //         while let Some((iter, data)) = input.pull() {
    //             iterator.notify_at(&iter);
    //             for (node, rank) in data.drain_temp() {
    //                 acc[node as usize / workers] += rank;
    //             }
    //         }
    //
    //         while let Some((item, _)) = iterator.next() {
    //
    //             output.give_at(&item, acc.drain_temp().enumerate().filter(|x| x.1 != 0.0)
    //                                      .map(|(u,f)| (((u * workers + local_index) as u32), f)));
    //
    //             for _ in 0..(1 + (nodes/workers)) { acc.push(0.0); }
    //             assert!(acc.len() == (1 + (nodes/workers)));
    //         }
    //     }
    // )
<|endoftext|>