<commit_before>mod buf;
mod format;
mod lexer;
mod nodes;
mod parser;
mod translator;
use md5;
#[cfg(test)]
use pretty_assertions::assert_eq;
use std::env;
use std::fs;
use std::path::Path;
use std::process::{exit, Command};
use std::string::String;

fn main() {
    let args: Vec<String> = env::args().collect();
    if args[1] == "build" {
        match build(&args[2..]) {
            Ok(_) => exit(0),
            Err(m) => {
                eprintln!("{}", m);
                exit(1);
            }
        }
    }
    if args[1] == "deptree" {
        exit(deptree(&args[2..]));
    }
    exit(1);
}

#[test]
fn snapshots() {
    for entry in fs::read_dir("test/snapshots").unwrap() {
        let tmp = entry.unwrap().path();
        let path = tmp.to_str().unwrap();
        if path.ends_with(".out.c") {
            continue;
        }
        println!("{}", path);

        // get the output
        let m = parser::get_module(&path.to_string()).unwrap();
        let mods = resolve_deps(&m);
        let c_mods: Vec<nodes::CompatModule> =
            mods.iter().map(|m| translator::translate(&m)).collect();
        let result = format::format_compat_module(&c_mods[0]);

        let snapshot_path = path.replace(".c", ".out.c");
        if env::var("UPDATE_SNAPSHOT").is_ok() {
            fs::write(snapshot_path, result).unwrap();
        } else {
            assert_eq!(result, fs::read_to_string(snapshot_path).unwrap());
        }
    }
}

fn build(argv: &[String]) -> Result<(), String> {
    if argv.len() < 1 || argv.len() > 2 {
        return Err(String::from("Usage: build <source-path> [<output-path>]"));
    }
    let path = &argv[0];
    let name = if argv.len() == 2 {
        argv[1].clone()
    } else {
        Path::new(&path)
            .with_extension("")
            .file_name()
            .unwrap()
            .to_str()
            .unwrap()
            .to_string()
    };

    // Get the module we're building.
    let m = parser::get_module(&path)?;

    // Get all modules that our module depends on. This includes our module as
    // well, so this is a complete list of modules required for the build.
    let mods = resolve_deps(&m);

    // Convert all modules to C modules.
    let c_mods: Vec<nodes::CompatModule> = mods.iter().map(|m| translator::translate(&m)).collect();

    // Save all C modules somewhere.
    if fs::metadata("tmp").is_err() {
        fs::create_dir("tmp").unwrap();
    }
    let mut paths: Vec<String> = Vec::new();
    for module in &c_mods {
        let src = format::format_compat_module(&module);
        let path = format!("tmp/{:x}.c", md5::compute(&module.id));
        fs::write(&path, &src).unwrap();
        paths.push(path);
    }

    let mut link: Vec<String> = vec!["m".to_string()];
    for module in &c_mods {
        for l in &module.link {
            if link.iter().position(|x| x == l).is_none() {
                link.push(l.clone());
            }
        }
    }

    let mut cmd = Command::new("c99");
    cmd.args(&[
        "-Wall",
        "-Wextra",
        "-Werror",
        "-pedantic",
        "-pedantic-errors",
        "-fmax-errors=3",
        "-Wno-parentheses",
        "-g",
    ]);
    for p in paths {
        cmd.arg(p);
    }
    cmd.args(&["-o", &name]);
    for l in link {
        cmd.args(&["-l", &l.trim()]);
    }
    let mut ch = cmd.spawn().unwrap();
    let r = ch.wait().unwrap();
    exit(r.code().unwrap());
}

fn resolve_deps(m: &nodes::Module) -> Vec<nodes::Module> {
    let mut deps: Vec<nodes::Module> = vec![m.clone()];
    let mut present = vec![m.id.clone()];
    for imp in module_imports(&m) {
        let sub = parser::get_module(&imp.path).unwrap();
        for dep in resolve_deps(&sub) {
            if present.contains(&&dep.id) {
                continue;
            }
            present.push(dep.id.clone());
            deps.push(dep);
        }
    }
    return deps;
}

fn module_imports(module: &nodes::Module) -> Vec<nodes::Import> {
    let mut list: Vec<nodes::Import> = vec![];
    for element in &module.elements {
        match element {
            nodes::ModuleObject::Import(x) => list.push(x.clone()),
            _ => {}
        }
    }
    return list;
}

fn deptree(argv: &[String]) -> i32 {
    if argv.len() != 1 {
        eprintln!("usage: deptree <source-path>");
        return 1;
    }
    let path = &argv[0];
    let m = parser::get_module(&path).unwrap();
    let tree = build_tree(&m);
    let r = render_tree(&tree);
    println!("{}", r.join("\n"));
    return 0;
}

struct DepNode {
    name: String,
    deps: Vec<DepNode>,
}

fn build_tree(module: &nodes::Module) -> DepNode {
    let imports = module_imports(module);
    let mut deps = vec![];
    for import in imports {
        let m = parser::get_module(&import.path).unwrap();
        deps.push(build_tree(&m));
    }
    return DepNode {
        name: module.id.clone(),
        deps,
    };
}

fn render_tree(tree: &DepNode) -> Vec<String> {
    let mut lines: Vec<String> = vec![tree.name.clone()];
    let n = tree.deps.len();
    for (i, dep) in tree.deps.iter().enumerate() {
        let r = render_tree(&dep);
        if i == n - 1 {
            lines.append(&mut indent_tree(r, " └", "  "));
        } else {
            lines.append(&mut indent_tree(r, " ├", " │"));
        }
    }
    return lines;
}

fn indent_tree(lines: Vec<String>, first: &str, cont: &str) -> Vec<String> {
    let mut result: Vec<String> = Vec::new();
    for (i, line) in lines.iter().enumerate() {
        if i == 0 {
            result.push(format!("{}{}", first, line))
        } else {
            result.push(format!("{}{}", cont, line));
        }
    }
    return result;
}
<commit_msg>Translate paths back to modules in c errors<commit_after>mod buf;
mod format;
mod lexer;
mod nodes;
mod parser;
mod translator;
use md5;
#[cfg(test)]
use pretty_assertions::assert_eq;
use std::env;
use std::fs;
use std::io::BufRead;
use std::path::Path;
use std::process::{exit, Command, Stdio};
use std::string::String;

fn main() {
    let args: Vec<String> = env::args().collect();
    if args[1] == "build" {
        match build(&args[2..]) {
            Ok(_) => exit(0),
            Err(m) => {
                eprintln!("{}", m);
                exit(1);
            }
        }
    }
    if args[1] == "deptree" {
        exit(deptree(&args[2..]));
    }
    exit(1);
}

#[test]
fn snapshots() {
    for entry in fs::read_dir("test/snapshots").unwrap() {
        let tmp = entry.unwrap().path();
        let path = tmp.to_str().unwrap();
        if path.ends_with(".out.c") {
            continue;
        }
        println!("{}", path);

        // get the output
        let m = parser::get_module(&path.to_string()).unwrap();
        let mods = resolve_deps(&m);
        let c_mods: Vec<nodes::CompatModule> =
            mods.iter().map(|m| translator::translate(&m)).collect();
        let result = format::format_compat_module(&c_mods[0]);

        let snapshot_path = path.replace(".c", ".out.c");
        if env::var("UPDATE_SNAPSHOT").is_ok() {
            fs::write(snapshot_path, result).unwrap();
        } else {
            assert_eq!(result, fs::read_to_string(snapshot_path).unwrap());
        }
    }
}

fn build(argv: &[String]) -> Result<(), String> {
    if argv.len() < 1 || argv.len() > 2 {
        return Err(String::from("Usage: build <source-path> [<output-path>]"));
    }
    let path = &argv[0];
    let name = if argv.len() == 2 {
        argv[1].clone()
    } else {
        Path::new(&path)
            .with_extension("")
            .file_name()
            .unwrap()
            .to_str()
            .unwrap()
            .to_string()
    };

    // Get the module we're building.
    let m = parser::get_module(&path)?;

    // Get all modules that our module depends on. This includes our module as
    // well, so this is a complete list of modules required for the build.
    let mods = resolve_deps(&m);

    // Convert all modules to C modules.
    let c_mods: Vec<nodes::CompatModule> = mods.iter().map(|m| translator::translate(&m)).collect();

    // Save all C modules somewhere.
    if fs::metadata("tmp").is_err() {
        fs::create_dir("tmp").unwrap();
    }
    struct PathId {
        path: String,
        id: String,
    }
    let mut paths: Vec<PathId> = Vec::new();
    for module in &c_mods {
        let src = format::format_compat_module(&module);
        let path = format!("tmp/{:x}.c", md5::compute(&module.id));
        paths.push(PathId {
            path: String::from(&path),
            id: String::from(&module.id),
        });
        fs::write(&path, &src).unwrap();
    }

    let mut link: Vec<String> = vec!["m".to_string()];
    for module in &c_mods {
        for l in &module.link {
            if link.iter().position(|x| x == l).is_none() {
                link.push(l.clone());
            }
        }
    }

    let mut cmd = Command::new("c99");
    cmd.args(&[
        "-Wall",
        "-Wextra",
        "-Werror",
        "-pedantic",
        "-pedantic-errors",
        "-fmax-errors=3",
        "-Wno-parentheses",
        "-g",
    ]);
    for p in &paths {
        cmd.arg(p.path.clone());
    }
    cmd.args(&["-o", &name]);
    for l in link {
        cmd.args(&["-l", &l.trim()]);
    }
    cmd.stderr(Stdio::piped());
    let mut proc = cmd.spawn().unwrap();
    let out = proc.stderr.as_mut().unwrap();
    let reader = std::io::BufReader::new(out);
    for liner in reader.lines() {
        let mut line = liner.unwrap();
        for p in &paths {
            line = line.replace(&p.path, &p.id);
        }
        println!("{}", &line);
    }
    let r = proc.wait().unwrap();
    exit(r.code().unwrap());
}

fn resolve_deps(m: &nodes::Module) -> Vec<nodes::Module> {
    let mut deps: Vec<nodes::Module> = vec![m.clone()];
    let mut present = vec![m.id.clone()];
    for imp in module_imports(&m) {
        let sub = parser::get_module(&imp.path).unwrap();
        for dep in resolve_deps(&sub) {
            if present.contains(&&dep.id) {
                continue;
            }
            present.push(dep.id.clone());
            deps.push(dep);
        }
    }
    return deps;
}

fn module_imports(module: &nodes::Module) -> Vec<nodes::Import> {
    let mut list: Vec<nodes::Import> = vec![];
    for element in &module.elements {
        match element {
            nodes::ModuleObject::Import(x) => list.push(x.clone()),
            _ => {}
        }
    }
    return list;
}

fn deptree(argv: &[String]) -> i32 {
    if argv.len() != 1 {
        eprintln!("usage: deptree <source-path>");
        return 1;
    }
    let path = &argv[0];
    let m = parser::get_module(&path).unwrap();
    let tree = build_tree(&m);
    let r = render_tree(&tree);
    println!("{}", r.join("\n"));
    return 0;
}

struct DepNode {
    name: String,
    deps: Vec<DepNode>,
}

fn build_tree(module: &nodes::Module) -> DepNode {
    let imports = module_imports(module);
    let mut deps = vec![];
    for import in imports {
        let m = parser::get_module(&import.path).unwrap();
        deps.push(build_tree(&m));
    }
    return DepNode {
        name: module.id.clone(),
        deps,
    };
}

fn render_tree(tree: &DepNode) -> Vec<String> {
    let mut lines: Vec<String> = vec![tree.name.clone()];
    let n = tree.deps.len();
    for (i, dep) in tree.deps.iter().enumerate() {
        let r = render_tree(&dep);
        if i == n - 1 {
            lines.append(&mut indent_tree(r, " └", "  "));
        } else {
            lines.append(&mut indent_tree(r, " ├", " │"));
        }
    }
    return lines;
}

fn indent_tree(lines: Vec<String>, first: &str, cont: &str) -> Vec<String> {
    let mut result: Vec<String> = Vec::new();
    for (i, line) in lines.iter().enumerate() {
        if i == 0 {
            result.push(format!("{}{}", first, line))
        } else {
            result.push(format!("{}{}", cont, line));
        }
    }
    return result;
}
<|endoftext|>


<commit_before>extern crate discord;

use std::env;
use discord::{Discord, State};
use discord::model::Event;

pub fn main() {
	// Log in to Discord using a bot token from the environment
	let discord = Discord::from_bot_token(
		&env::var("DISCORD_TOKEN").expect("Expected token"),
	).expect("login failed");

	// establish websocket and voice connection
	let (mut connection, ready) = discord.connect().expect("connect failed");
	println!("[Ready] {} is serving {} servers", ready.user.username, ready.servers.len());
	let mut state = State::new(ready);
	connection.sync_calls(&state.all_private_channels());

	// receive events forever
	loop {
		let event = match connection.recv_event() {
			Ok(event) => event,
			Err(err) => {
				println!("[Warning] Receive error: {:?}", err);
				if let discord::Error::WebSocket(..) = err {
					// Handle the websocket connection being dropped
					let (new_connection, ready) = discord.connect().expect("connect failed");
					connection = new_connection;
					state = State::new(ready);
					println!("[Ready] Reconnected successfully.");
				}
				if let discord::Error::Closed(..) = err {
					break
				}
				continue
			},
		};
		state.update(&event);

		match event {
			Event::MessageCreate(message) => {
				use std::ascii::AsciiExt;
				// safeguard: stop if the message is from us
				if message.author.id == state.user().id {
					continue
				}

				// reply to a command if there was one
				let mut split = message.content.split(' ');
				let first_word = split.next().unwrap_or("");
				let argument = split.next().unwrap_or("");

				//Section of avaliable commands.
                if first_word.eq_ignore_ascii_case("!help") {
                    if argument.eq_ignore_ascii_case("commands") {
                        warn(discord.send_message(message.channel_id, "help, about, uber, distrowiki, and sourcecode are the avaliable commands. (!dj is buggy)", "", false))
                    }
		    		else if argument.eq_ignore_ascii_case("about") {
			 			warn(discord.send_message(message.channel_id, "Displays info about me. No arguments available.", "", false))
		    		}
		    		else if argument.eq_ignore_ascii_case("uber") {
						warn(discord.send_message(message.channel_id, "Uber an object (I'm picky).", "", false))
		    		}
		    		else if argument.eq_ignore_ascii_case("distrowiki") {
						warn(discord.send_message(message.channel_id, "Finds the wiki of the specified distro. Arguments include Arch, Ubuntu, Debian, Antergos, Mint, CentOS, OpenBSD, Opensuse, Sabayon, KaliLinux, Solus, and Gentoo.", "", false))
		    		}
		    		else if argument.eq_ignore_ascii_case("sourcecode") {
						warn(discord.send_message(message.channel_id, "Displays where I am on GitHub. No arguments available", "", false))
	            	}
		    		else if argument.eq_ignore_ascii_case("help") {
						warn(discord.send_message(message.channel_id, "Are you trying to break me?", "", false))
	            	}
		    		else if argument.eq_ignore_ascii_case("[args]") {
						warn(discord.send_message(message.channel_id, "You're helpless.", "", false))
	            	}
                    else {
                    warn(discord.send_message(message.channel_id, "Umm, you gotta use it like this. !help [args] Try using the `commands` argument.", "", false))
                    }
                }

                if first_word.eq_ignore_ascii_case("!about") {
                    warn(discord.send_message(message.channel_id, "I have no clue. All I know is that I was written by Arroz in a language called Rust, and my code is still being developed.", "", false))
                }

                if first_word.eq_ignore_ascii_case("!uber"){
					if argument.eq_ignore_ascii_case("ddog75") {
                        warn(discord.send_message(message.channel_id, "Alright, 10 minutes.", "", false))
                    }
					else if argument.eq_ignore_ascii_case("aspire") {
						warn(discord.send_message(message.channel_id, "Not the meatballs.", "", false))
					}
					else if argument.eq_ignore_ascii_case("nic") {
						warn(discord.send_message(message.channel_id, "NOT THE BEES!!!", "", false))
					}
					 else {
                        warn(discord.send_message(message.channel_id, "Fuck off!", "", false))
                    }
                }

                if first_word.eq_ignore_ascii_case("!sourcecode") {
                    warn(discord.send_message(message.channel_id, "Okay, here you go. https://github.com/aarroz/rice_bot2", "", false))
                }
				//Section that prints wikis for linux distributions.
				if first_word.eq_ignore_ascii_case("!distrowiki") {
					if argument.eq_ignore_ascii_case("arch") {
						warn(discord.send_message(message.channel_id, "Here's the Arch Wiki! https://wiki.archlinux.org/", "", false))
					}
					else if argument.eq_ignore_ascii_case("ubuntu") {
						warn(discord.send_message(message.channel_id, "Here's the Ubuntu Wiki! https://wiki.ubuntu.com/", "", false))
					}
					else if argument.eq_ignore_ascii_case("debian") {
						warn(discord.send_message(message.channel_id, "Here's the Debian Wiki! https://wiki.debian.org/", "", false))
					}
					else if argument.eq_ignore_ascii_case("antergos") {
						warn(discord.send_message(message.channel_id, "Here's the Antergos Wiki! https://antergos.com/wiki/", "", false))
					}
					else if argument.eq_ignore_ascii_case("centos") {
						warn(discord.send_message(message.channel_id, "Here's the CentOS Wiki! https://wiki.centos.org/", "", false))
					}
					//Sorry OpenBSD users.
					else if argument.eq_ignore_ascii_case("openbsd") {
						warn(discord.send_message(message.channel_id, "No, just no.", "", false))
					}
					//Not sorry.
					else if argument.eq_ignore_ascii_case("gentoo") {
						warn(discord.send_message(message.channel_id, "Here's the Gentoo Wiki! https://wiki.gentoo.org/wiki/Main_Page", "", false))
					}
					else if argument.eq_ignore_ascii_case("opensuse") {
						warn(discord.send_message(message.channel_id, "Here's the OpenSuse Wiki! https://en.opensuse.org/Portal:Wiki", "", false))
					}
					else if argument.eq_ignore_ascii_case("sabayon") {
						warn(discord.send_message(message.channel_id, "Here's the Sabayon Wiki! https://wiki.sabayon.org/", "", false))
					}
					else if argument.eq_ignore_ascii_case("kalilinux") {
						warn(discord.send_message(message.channel_id, "Heres's the KaliLinux Wiki! https://en.wikipedia.org/wiki/Kali_Linux", "", false))
					}
					else if argument.eq_ignore_ascii_case("mint") {
						warn(discord.send_message(message.channel_id, "Here's the Mint newbie page! https://forums.linuxmint.com/viewforum.php?f=90", "", false))
					}
					else if argument.eq_ignore_ascii_case("solus") {
						warn(discord.send_message(message.channel_id, "There is no wiki for solus, however there is the help page! https://solus-project.com/help-center/home/", "", false))
					}
					else if argument.eq_ignore_ascii_case("windows") {
						warn(discord.send_message(message.channel_id, "Thats not a distro.", "", false))
					}
					else if argument.eq_ignore_ascii_case("macos") {
						warn(discord.send_message(message.channel_id, "Sure its close to linux, but I'm too tired to have that argument.", "", false))
					}
					else {
						warn(discord.send_message(message.channel_id, "Please use a distro from !help distrowiki.", "", false))
					}
				}

                // Command for using dj.
				if first_word.eq_ignore_ascii_case("!dj") {
					let vchan = state.find_voice_user(message.author.id);
					if argument.eq_ignore_ascii_case("stop") {
						vchan.map(|(sid, _)| connection.voice(sid).stop());
					} else if argument.eq_ignore_ascii_case("quit") {
						vchan.map(|(sid, _)| connection.drop_voice(sid));
					} else {
						let output = if let Some((server_id, channel_id)) = vchan {
							match discord::voice::open_ytdl_stream(argument) {
								Ok(stream) => {
									let voice = connection.voice(server_id);
									voice.set_deaf(true);
									voice.connect(channel_id);
									voice.play(stream);
									String::new()
								},
								Err(error) => format!("Error: {}", error),
							}
						} else {
							"You must be in a voice channel to DJ".to_owned()
						};
						if !output.is_empty() {
							warn(discord.send_message(message.channel_id, &output, "", false));
						}
					}
				}
			}
			Event::VoiceStateUpdate(server_id, _) => {
				// If someone moves/hangs up, and we are in a voice channel,
				if let Some(cur_channel) = connection.voice(server_id).current_channel() {
					// and our current voice channel is empty, disconnect from voice
					match server_id {
						Some(server_id) => if let Some(srv) = state.servers().iter().find(|srv| srv.id == server_id) {
							if srv.voice_states.iter().filter(|vs| vs.channel_id == Some(cur_channel)).count() <= 1 {
								connection.voice(Some(server_id)).disconnect();
							}
						},
						None => if let Some(call) = state.calls().get(&cur_channel) {
							if call.voice_states.len() <= 1 {
								connection.voice(server_id).disconnect();
							}
						}
					}
				}
			}
			_ => {}, // discard other events
		}
	}
}

fn warn<T, E: ::std::fmt::Debug>(result: Result<T, E>) {
	match result {
		Ok(_) => {},
		Err(err) => println!("[Warning] {:?}", err)
	}
}
<commit_msg>Wheres Teal?<commit_after>extern crate discord;

use std::env;
use discord::{Discord, State};
use discord::model::Event;

pub fn main() {
	// Log in to Discord using a bot token from the environment
	let discord = Discord::from_bot_token(
		&env::var("DISCORD_TOKEN").expect("Expected token"),
	).expect("login failed");

	// establish websocket and voice connection
	let (mut connection, ready) = discord.connect().expect("connect failed");
	println!("[Ready] {} is serving {} servers", ready.user.username, ready.servers.len());
	let mut state = State::new(ready);
	connection.sync_calls(&state.all_private_channels());

	// receive events forever
	loop {
		let event = match connection.recv_event() {
			Ok(event) => event,
			Err(err) => {
				println!("[Warning] Receive error: {:?}", err);
				if let discord::Error::WebSocket(..) = err {
					// Handle the websocket connection being dropped
					let (new_connection, ready) = discord.connect().expect("connect failed");
					connection = new_connection;
					state = State::new(ready);
					println!("[Ready] Reconnected successfully.");
				}
				if let discord::Error::Closed(..) = err {
					break
				}
				continue
			},
		};
		state.update(&event);

		match event {
			Event::MessageCreate(message) => {
				use std::ascii::AsciiExt;
				// safeguard: stop if the message is from us
				if message.author.id == state.user().id {
					continue
				}

				// reply to a command if there was one
				let mut split = message.content.split(' ');
				let first_word = split.next().unwrap_or("");
				let argument = split.next().unwrap_or("");

				//Section of avaliable commands.
                if first_word.eq_ignore_ascii_case("!help") {
                    if argument.eq_ignore_ascii_case("commands") {
                        warn(discord.send_message(message.channel_id, "help, about, uber, distrowiki, and sourcecode are the avaliable commands. (!dj is buggy)", "", false))
                    }
		    		else if argument.eq_ignore_ascii_case("about") {
			 			warn(discord.send_message(message.channel_id, "Displays info about me. No arguments available.", "", false))
		    		}
		    		else if argument.eq_ignore_ascii_case("uber") {
						warn(discord.send_message(message.channel_id, "Uber an object (I'm picky).", "", false))
		    		}
		    		else if argument.eq_ignore_ascii_case("distrowiki") {
						warn(discord.send_message(message.channel_id, "Finds the wiki of the specified distro. Arguments include Arch, Ubuntu, Debian, Antergos, Mint, CentOS, OpenBSD, Opensuse, Sabayon, KaliLinux, Solus, and Gentoo.", "", false))
		    		}
		    		else if argument.eq_ignore_ascii_case("sourcecode") {
						warn(discord.send_message(message.channel_id, "Displays where I am on GitHub. No arguments available", "", false))
	            	}
		    		else if argument.eq_ignore_ascii_case("help") {
						warn(discord.send_message(message.channel_id, "Are you trying to break me?", "", false))
	            	}
		    		else if argument.eq_ignore_ascii_case("[args]") {
						warn(discord.send_message(message.channel_id, "You're helpless.", "", false))
	            	}
                    else {
                    warn(discord.send_message(message.channel_id, "Umm, you gotta use it like this. !help [args] Try using the `commands` argument.", "", false))
                    }
                }

                if first_word.eq_ignore_ascii_case("!about") {
                    warn(discord.send_message(message.channel_id, "I have no clue. All I know is that I was written by Arroz in a language called Rust, and my code is still being developed.", "", false))
                }
		if first_word.eq_ignore_ascii_case("!wheresteal") {
			warn(discord.send_message(message.channel_id, "Go ask him, I know nothing ;_;", "", false))
		}

                if first_word.eq_ignore_ascii_case("!uber"){
					if argument.eq_ignore_ascii_case("ddog75") {
                        warn(discord.send_message(message.channel_id, "Alright, 10 minutes.", "", false))
                    }
					else if argument.eq_ignore_ascii_case("aspire") {
						warn(discord.send_message(message.channel_id, "Not the meatballs.", "", false))
					}
					else if argument.eq_ignore_ascii_case("nic") {
						warn(discord.send_message(message.channel_id, "NOT THE BEES!!!", "", false))
					}
					 else {
                        warn(discord.send_message(message.channel_id, "Fuck off!", "", false))
                    }
                }

                if first_word.eq_ignore_ascii_case("!sourcecode") {
                    warn(discord.send_message(message.channel_id, "Okay, here you go. https://github.com/aarroz/rice_bot2", "", false))
                }
				//Section that prints wikis for linux distributions.
				if first_word.eq_ignore_ascii_case("!distrowiki") {
					if argument.eq_ignore_ascii_case("arch") {
						warn(discord.send_message(message.channel_id, "Here's the Arch Wiki! https://wiki.archlinux.org/", "", false))
					}
					else if argument.eq_ignore_ascii_case("ubuntu") {
						warn(discord.send_message(message.channel_id, "Here's the Ubuntu Wiki! https://wiki.ubuntu.com/", "", false))
					}
					else if argument.eq_ignore_ascii_case("debian") {
						warn(discord.send_message(message.channel_id, "Here's the Debian Wiki! https://wiki.debian.org/", "", false))
					}
					else if argument.eq_ignore_ascii_case("antergos") {
						warn(discord.send_message(message.channel_id, "Here's the Antergos Wiki! https://antergos.com/wiki/", "", false))
					}
					else if argument.eq_ignore_ascii_case("centos") {
						warn(discord.send_message(message.channel_id, "Here's the CentOS Wiki! https://wiki.centos.org/", "", false))
					}
					//Sorry OpenBSD users.
					else if argument.eq_ignore_ascii_case("openbsd") {
						warn(discord.send_message(message.channel_id, "No, just no.", "", false))
					}
					//Not sorry.
					else if argument.eq_ignore_ascii_case("gentoo") {
						warn(discord.send_message(message.channel_id, "Here's the Gentoo Wiki! https://wiki.gentoo.org/wiki/Main_Page", "", false))
					}
					else if argument.eq_ignore_ascii_case("opensuse") {
						warn(discord.send_message(message.channel_id, "Here's the OpenSuse Wiki! https://en.opensuse.org/Portal:Wiki", "", false))
					}
					else if argument.eq_ignore_ascii_case("sabayon") {
						warn(discord.send_message(message.channel_id, "Here's the Sabayon Wiki! https://wiki.sabayon.org/", "", false))
					}
					else if argument.eq_ignore_ascii_case("kalilinux") {
						warn(discord.send_message(message.channel_id, "Heres's the KaliLinux Wiki! https://en.wikipedia.org/wiki/Kali_Linux", "", false))
					}
					else if argument.eq_ignore_ascii_case("mint") {
						warn(discord.send_message(message.channel_id, "Here's the Mint newbie page! https://forums.linuxmint.com/viewforum.php?f=90", "", false))
					}
					else if argument.eq_ignore_ascii_case("solus") {
						warn(discord.send_message(message.channel_id, "There is no wiki for solus, however there is the help page! https://solus-project.com/help-center/home/", "", false))
					}
					else if argument.eq_ignore_ascii_case("windows") {
						warn(discord.send_message(message.channel_id, "Thats not a distro.", "", false))
					}
					else if argument.eq_ignore_ascii_case("macos") {
						warn(discord.send_message(message.channel_id, "Sure its close to linux, but I'm too tired to have that argument.", "", false))
					}
					else {
						warn(discord.send_message(message.channel_id, "Please use a distro from !help distrowiki.", "", false))
					}
				}

                // Command for using dj.
				if first_word.eq_ignore_ascii_case("!dj") {
					let vchan = state.find_voice_user(message.author.id);
					if argument.eq_ignore_ascii_case("stop") {
						vchan.map(|(sid, _)| connection.voice(sid).stop());
					} else if argument.eq_ignore_ascii_case("quit") {
						vchan.map(|(sid, _)| connection.drop_voice(sid));
					} else {
						let output = if let Some((server_id, channel_id)) = vchan {
							match discord::voice::open_ytdl_stream(argument) {
								Ok(stream) => {
									let voice = connection.voice(server_id);
									voice.set_deaf(true);
									voice.connect(channel_id);
									voice.play(stream);
									String::new()
								},
								Err(error) => format!("Error: {}", error),
							}
						} else {
							"You must be in a voice channel to DJ".to_owned()
						};
						if !output.is_empty() {
							warn(discord.send_message(message.channel_id, &output, "", false));
						}
					}
				}
			}
			Event::VoiceStateUpdate(server_id, _) => {
				// If someone moves/hangs up, and we are in a voice channel,
				if let Some(cur_channel) = connection.voice(server_id).current_channel() {
					// and our current voice channel is empty, disconnect from voice
					match server_id {
						Some(server_id) => if let Some(srv) = state.servers().iter().find(|srv| srv.id == server_id) {
							if srv.voice_states.iter().filter(|vs| vs.channel_id == Some(cur_channel)).count() <= 1 {
								connection.voice(Some(server_id)).disconnect();
							}
						},
						None => if let Some(call) = state.calls().get(&cur_channel) {
							if call.voice_states.len() <= 1 {
								connection.voice(server_id).disconnect();
							}
						}
					}
				}
			}
			_ => {}, // discard other events
		}
	}
}

fn warn<T, E: ::std::fmt::Debug>(result: Result<T, E>) {
	match result {
		Ok(_) => {},
		Err(err) => println!("[Warning] {:?}", err)
	}
}
<|endoftext|>


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

#[macro_use]
extern crate kafka;
extern crate log;
extern crate openssl;
extern crate toml;

mod flowgger;

const DEFAULT_CONFIG_FILE: &'static str = "flowgger.toml";

fn main() {
    let config_file = std::env::args().skip(1).next().unwrap_or(DEFAULT_CONFIG_FILE.to_owned());
    flowgger::start(&config_file);
}
<commit_msg>Print version on statup<commit_after>
#![feature(plugin)]
#![plugin(clippy)]

#[macro_use]
extern crate kafka;
extern crate log;
extern crate openssl;
extern crate toml;

mod flowgger;

const DEFAULT_CONFIG_FILE: &'static str = "flowgger.toml";

fn main() {
    println!("Starting flowgger 0.1.2");
    let config_file = std::env::args().skip(1).next().unwrap_or(DEFAULT_CONFIG_FILE.to_owned());
    flowgger::start(&config_file);
}
<|endoftext|>


<commit_before>#![cfg_attr(feature="clippy", plugin(clippy))]
#![feature(plugin)]

#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;
extern crate flate2;
extern crate rusoto;
extern crate crossbeam;
extern crate chrono;
extern crate syslog;
extern crate walkdir;
extern crate ifaces;

use std::io;
use std::io::prelude::*;
use std::fs::{File, remove_file, create_dir_all};
use std::path::Path;
use std::env;
use std::str::FromStr;
use rusoto::{AutoRefreshingProvider, default_tls_client, DefaultCredentialsProvider, Region};
use rusoto::sts::{StsClient, StsAssumeRoleSessionCredentialsProvider};
use rusoto::s3::{S3Client, PutObjectRequest, Metadata};
use flate2::Compression;
use flate2::write::GzEncoder;
use chrono::prelude::Local;
use chrono::{Datelike, Timelike, Duration};
use crossbeam::scope;
use syslog::{Facility, Severity};
use walkdir::WalkDir;

#[derive(Serialize, Deserialize)]
struct ConfigFile {
    cachedir: String,
    role_arn: String,
    region: String,
    bucket: String,
    prefix: String,
}

const MAX_LINES: usize = 50000;
const MAX_BYTES: usize = 10485760;
const MAX_TIMEOUT: i64 = 10;
const INTERFACE: &str = "eth0";

fn main() {

    let args: Vec<_> = env::args().collect();
    if args.len() < 1 {
        println!("s3post <config.json>");
    }

    let file_open = File::open(&args[1]).expect("could not open file");
    let config: ConfigFile = serde_json::from_reader(file_open).expect("config has invalid json");

    println!("MAX_LINES:\t{}\nMAX_BYTES:\t{}\nMAX_TIMEOUT:\t{}",
             MAX_LINES,
             MAX_BYTES,
             MAX_TIMEOUT);

    // attempt to resend any logs that we might have not successfully sent
    resend_logs(&config);

    let reader = io::stdin();
    let mut buffer = reader.lock();
    let mut data = vec![0];
    let mut time = Local::now();
    let mut timeout = time + Duration::seconds(MAX_TIMEOUT);

    loop {
        match buffer.fill_buf() {
            Ok(bytes) => {
                // add the current bytes to our data vector
                data.extend(bytes);
                // evaulate if data and time meet of processing conditions
                if data.lines().count() >= MAX_LINES || data.len() >= MAX_BYTES ||
                   timeout <= time && !data.is_empty() {
                    // send the data to the compress function
                    compress(data.as_slice(), time, &config);
                    // clear our data vector
                    data.clear();
                    // reset our timer
                    time = Local::now();
                    timeout = time + Duration::seconds(MAX_TIMEOUT);
                } else {
                    // update the time
                    time = Local::now();
                    // attempt to resend any logs that we might have not successfully sent
                    resend_logs(&config);
                }
            }
            Err(err) => panic!(err),
        }

        // consume the data from the buffer so we don't reprocess it
        buffer.consume(data.len());
    }
}

fn compress(bytes: &[u8], timestamp: chrono::DateTime<chrono::Local>, config: &ConfigFile) {

    // our compression routine will be sent to another thread
    scope(|scope| {
        scope.spawn(move || {

            // use our environment variable of hostname. will be essentially the CBID
            let hostname = match env::var("HOSTNAME") {
                Ok(hostname) => hostname,
                Err(err) => panic!("Unable to get hostname. {}", err),
            };

            // generate our local path
            let path = format!("{}/{}/{}/{}/{}/",
                               timestamp.year(),
                               timestamp.month(),
                               timestamp.day(),
                               timestamp.hour(),
                               timestamp.minute());

            // setting the format for how we write out the file.
            let file = format!("{}.{}-{}.raw.gz",
                               timestamp.second(),
                               timestamp.timestamp_subsec_millis(),
                               &hostname);

            // append the cachedir so we write to the correct place
            let fullpath = format!("{}/{}", &config.cachedir, &path);

            // create the directory structure
            let _ = create_dir_all(&fullpath);

            // create the file in the full path
            let mut output = File::create(format!("{}{}", &fullpath, &file)).unwrap();

            // create a gzip encoder
            let mut encoder = GzEncoder::new(Vec::new(), Compression::Default);

            // encode the retrieved bytes
            encoder.write_all(bytes).unwrap();
            let log = encoder.finish().unwrap();

            // write out the encoded data to our file
            output.write_all(&log).unwrap();

            // call write_s3 to send the gzip'd file to s3
            write_s3(&config, &file, &path, &log)
        });
    });
}

fn write_s3(config: &ConfigFile, file: &str, path: &str, log: &[u8]) {

    // move to new thread
    scope(|scope| {
        scope.spawn(move || {

            // need the ipaddr of our interface. will be part of the metadata
            let mut address = String::new();
            for iface in ifaces::Interface::get_all().unwrap() {
                if iface.name == INTERFACE && iface.kind == ifaces::Kind::Ipv4 {
                    address = format!("{}", iface.addr.unwrap());
                    address = address.replace(":0", "");
                } else if iface.name == "en0" && iface.kind == ifaces::Kind::Ipv4 {
                    address = format!("{}", iface.addr.unwrap());
                    address = address.replace(":0", "");
                } else {
                    panic!("Unable to find usable interface. Aborting!");
                }
            }

            // use our environment variable of hostname. will be essentially the CBID
            let hostname = match env::var("HOSTNAME") {
                Ok(hostname) => hostname,
                Err(err) => panic!("Unable to get hostname. {}", err),
            };

            let path = format!("{}/{}/{}", &config.prefix, &path, &file);

            // set up our credentials provider for aws
            let provider = match DefaultCredentialsProvider::new() {
                Ok(provider) => provider,
                Err(err) => panic!("Unable to load credentials. {}", err),
            };

            // initiate our sts client
            let sts_client = StsClient::new(default_tls_client().unwrap(),
                                            provider,
                                            Region::from_str(&config.region).unwrap());
            // generate a sts provider
            let sts_provider = StsAssumeRoleSessionCredentialsProvider::new(sts_client,
                                                                            config.role_arn
                                                                                .to_owned(),
                                                                            "s3post".to_owned(),
                                                                            None,
                                                                            None,
                                                                            None,
                                                                            None);
            // allow our STS to auto-refresh
            let auto_sts_provider = AutoRefreshingProvider::with_refcell(sts_provider);

            // create our s3 client initialization
            let s3 = S3Client::new(default_tls_client().unwrap(),
                                   auto_sts_provider.unwrap(),
                                   Region::from_str(&config.region).unwrap());

            // create a u8 vector
            let mut contents: Vec<u8> = Vec::new();
            // add our encoded log file to the vector
            contents.extend(log);

            // generate our metadata which we add to the s3 upload
            let mut metadata = Metadata::new();
            metadata.insert("cbid".to_string(), hostname.to_string());
            metadata.insert("ip".to_string(), address.to_string());
            metadata.insert("uncompressed_bytes".to_string(), contents.len().to_string());
            metadata.insert("lines".to_string(), contents.lines().count().to_string());

            // if we can read the contents to the buffer we will attempt to send the log to s3
            // need to build our request
            let req = PutObjectRequest {
                bucket: config.bucket.to_owned(),
                key: path.to_owned(),
                body: Some(contents),
                metadata: Some(metadata),
                ..Default::default()
            };

            match s3.put_object(&req) {
                // we were successful!
                Ok(_) => {
                    // Send some notifications to SYSLOG
                    match syslog::unix(Facility::LOG_SYSLOG) {
                        Err(e) => println!("impossible to connect to syslog: {:?}", e),
                        Ok(writer) => {
                            let success = format!("Successfully wrote {}/{}", &req.bucket, &path);
                            let _ = writer.send_3164(Severity::LOG_ALERT, &success).is_ok();
                        }
                    }
                    // only remove the file if we are successful
                    let localpath = format!("{}/{}", &config.cachedir, &path);
                    if remove_file(&localpath).is_ok() {
                        // Send some notifications to SYSLOG
                        match syslog::unix(Facility::LOG_SYSLOG) {
                            Err(e) => println!("impossible to connect to syslog: {:?}", e),
                            Ok(writer) => {
                                let success = format!("Removed file {}", &localpath);
                                let _ = writer.send_3164(Severity::LOG_ALERT, &success).is_ok();
                            }
                        }
                    }
                }
                Err(e) => println!("Could not write file {} {}", &file, e),
            }
        });
    });
}

fn resend_logs(config: &ConfigFile) {

    // create iterator over the directory
    for entry in WalkDir::new(&config.cachedir).into_iter().filter_map(|e| e.ok()) {

        // filter out gzip'd file suffixes
        if entry.file_name()
               .to_str()
               .map(|s| s.ends_with(".gz"))
               .unwrap() {

            // get just the file name
            let filename = entry.file_name().to_str().unwrap();

            // need to return only the parent and strip off the cachedir prefix
            // this then is the path write_s3 is expecting as it is the path within s3
            let path =
                Path::new(entry.path().parent().unwrap()).strip_prefix(&config.cachedir).unwrap();
            let path = Path::new(path).to_str().unwrap();

            // build out the complete path for reading
            let filepath = format!("{}/{}/{}", &config.cachedir, &path, &filename);
            // open the file for reading
            let mut file = File::open(&filepath).expect("Unable to read file");

            // create a u8 vector
            let mut contents: Vec<u8> = Vec::new();
            // add our encoded log file to the vector
            let _ = file.read_to_end(&mut contents);

            // Send some notifications to SYSLOG
            match syslog::unix(Facility::LOG_SYSLOG) {
                Err(e) => println!("impossible to connect to syslog: {:?}", e),
                Ok(writer) => {
                    let success = format!("Found unsent log {}/{}", &path, &filename);
                    let _ = writer.send_3164(Severity::LOG_ALERT, &success).is_ok();
                }
            }
            // pass the unset logs to s3
            write_s3(&config, filename, path, &contents)

        }
    }
}
<commit_msg>store system info in a struct<commit_after>#![cfg_attr(feature="clippy", plugin(clippy))]
#![feature(plugin)]

#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;
extern crate flate2;
extern crate rusoto;
extern crate crossbeam;
extern crate chrono;
extern crate syslog;
extern crate walkdir;
extern crate ifaces;

use std::io;
use std::io::prelude::*;
use std::fs::{File, remove_file, create_dir_all};
use std::path::Path;
use std::env;
use std::str::FromStr;
use rusoto::{AutoRefreshingProvider, default_tls_client, DefaultCredentialsProvider, Region};
use rusoto::sts::{StsClient, StsAssumeRoleSessionCredentialsProvider};
use rusoto::s3::{S3Client, PutObjectRequest, Metadata};
use flate2::Compression;
use flate2::write::GzEncoder;
use chrono::prelude::Local;
use chrono::{Datelike, Timelike, Duration};
use crossbeam::scope;
use syslog::{Facility, Severity};
use walkdir::WalkDir;

// struct for our config file
#[derive(Serialize, Deserialize)]
struct ConfigFile {
    cachedir: String,
    role_arn: String,
    region: String,
    bucket: String,
    prefix: String,
}

// struct for our system information
struct SystemInfo {
    hostname: String,
    ipaddr: String,
}

// few consts. might make these configurable later
const MAX_LINES: usize = 50000;
const MAX_BYTES: usize = 10485760;
const MAX_TIMEOUT: i64 = 10;
const INTERFACE: &str = "eth0";

fn main() {

    let args: Vec<_> = env::args().collect();
    if args.len() < 1 {
        println!("s3post <config.json>");
    }

    // open our config file
    let file_open = File::open(&args[1]).expect("could not open file");
    // attempt to deserialize the config to our struct
    let config: ConfigFile = serde_json::from_reader(file_open).expect("config has invalid json");

    // need the ipaddr of our interface. will be part of the metadata
    let mut address = String::new();
    for iface in ifaces::Interface::get_all().unwrap() {
        if iface.name == INTERFACE && iface.kind == ifaces::Kind::Ipv4 {
            address = format!("{}", iface.addr.unwrap());
            address = address.replace(":0", "");
        } else {
            panic!("Unable to find usable interface. Aborting!");
        }
    }

    // use our environment variable of hostname. will be essentially the CBID
    let hostname = match env::var("HOSTNAME") {
        Ok(hostname) => hostname,
        Err(err) => panic!("Unable to get hostname. {}", err),
    };

    // store hostname and ip address in our struct
    let system: SystemInfo = SystemInfo {
        hostname: hostname,
        ipaddr: address,
    };

    println!("MAX_LINES:\t{}\tMAX_BYTES:\t{}\tMAX_TIMEOUT:\t{}",
             MAX_LINES,
             MAX_BYTES,
             MAX_TIMEOUT);

    // attempt to resend any logs that we might have not successfully sent
    resend_logs(&config, &system);

    // create a reader from stdin
    let reader = io::stdin();
    // create a buffer from the stdin lock
    let mut buffer = reader.lock();
    // initialize an empty vector
    let mut data = vec![0];
    // grab the current time
    let mut time = Local::now();
    // create initial timeout
    let mut timeout = time + Duration::seconds(MAX_TIMEOUT);

    loop {
        match buffer.fill_buf() {
            Ok(bytes) => {
                // add the current bytes to our data vector
                data.extend(bytes);
                // evaulate if data and time meet of processing conditions
                if data.lines().count() >= MAX_LINES || data.len() >= MAX_BYTES ||
                   timeout <= time && !data.is_empty() {
                    // send the data to the compress function
                    compress(data.as_slice(), time, &config, &system);
                    // clear our data vector
                    data.clear();
                    // reset our timer
                    time = Local::now();
                    timeout = time + Duration::seconds(MAX_TIMEOUT);
                } else {
                    // update the time
                    time = Local::now();
                    // attempt to resend any logs that we might have not successfully sent
                    resend_logs(&config, &system);
                }
            }
            Err(err) => panic!(err),
        }
        // consume the data from the buffer so we don't reprocess it
        buffer.consume(data.len());
    }
}

fn compress(bytes: &[u8],
            timestamp: chrono::DateTime<chrono::Local>,
            config: &ConfigFile,
            system: &SystemInfo) {

    // our compression routine will be sent to another thread
    scope(|scope| {
        scope.spawn(move || {

            // generate our local path
            let path = format!("{}/{}/{}/{}/{}/",
                               timestamp.year(),
                               timestamp.month(),
                               timestamp.day(),
                               timestamp.hour(),
                               timestamp.minute());

            // setting the format for how we write out the file.
            let file = format!("{}.{}-{}.raw.gz",
                               timestamp.second(),
                               timestamp.timestamp_subsec_millis(),
                               &system.hostname);

            // append the cachedir so we write to the correct place
            let fullpath = format!("{}/{}", &config.cachedir, &path);

            // create the directory structure
            let _ = create_dir_all(&fullpath);

            // create the file in the full path
            let mut output = File::create(format!("{}{}", &fullpath, &file)).unwrap();

            // create a gzip encoder
            let mut encoder = GzEncoder::new(Vec::new(), Compression::Default);

            // encode the retrieved bytes
            encoder.write_all(bytes).unwrap();
            let log = encoder.finish().unwrap();

            // write out the encoded data to our file
            output.write_all(&log).unwrap();

            // call write_s3 to send the gzip'd file to s3
            write_s3(&config, &system, &file, &path, &log)
        });
    });
}

fn write_s3(config: &ConfigFile, system: &SystemInfo, file: &str, path: &str, log: &[u8]) {

    // move to new thread
    scope(|scope| {
        scope.spawn(move || {

            let path = format!("{}/{}/{}", &config.prefix, &path, &file);

            // set up our credentials provider for aws
            let provider = match DefaultCredentialsProvider::new() {
                Ok(provider) => provider,
                Err(err) => panic!("Unable to load credentials. {}", err),
            };

            // initiate our sts client
            let sts_client = StsClient::new(default_tls_client().unwrap(),
                                            provider,
                                            Region::from_str(&config.region).unwrap());
            // generate a sts provider
            let sts_provider = StsAssumeRoleSessionCredentialsProvider::new(sts_client,
                                                                            config.role_arn
                                                                                .to_owned(),
                                                                            "s3post".to_owned(),
                                                                            None,
                                                                            None,
                                                                            None,
                                                                            None);
            // allow our STS to auto-refresh
            let auto_sts_provider = AutoRefreshingProvider::with_refcell(sts_provider);

            // create our s3 client initialization
            let s3 = S3Client::new(default_tls_client().unwrap(),
                                   auto_sts_provider.unwrap(),
                                   Region::from_str(&config.region).unwrap());

            // create a u8 vector
            let mut contents: Vec<u8> = Vec::new();
            // add our encoded log file to the vector
            contents.extend(log);

            // generate our metadata which we add to the s3 upload
            let mut metadata = Metadata::new();
            metadata.insert("cbid".to_string(), system.hostname.to_string());
            metadata.insert("ip".to_string(), system.ipaddr.to_string());
            metadata.insert("uncompressed_bytes".to_string(), contents.len().to_string());
            metadata.insert("lines".to_string(), contents.lines().count().to_string());

            // if we can read the contents to the buffer we will attempt to send the log to s3
            // need to build our request
            let req = PutObjectRequest {
                bucket: config.bucket.to_owned(),
                key: path.to_owned(),
                body: Some(contents),
                metadata: Some(metadata),
                ..Default::default()
            };

            match s3.put_object(&req) {
                // we were successful!
                Ok(_) => {
                    // Send some notifications to SYSLOG
                    match syslog::unix(Facility::LOG_SYSLOG) {
                        Err(e) => println!("impossible to connect to syslog: {:?}", e),
                        Ok(writer) => {
                            let success = format!("Successfully wrote {}/{}", &req.bucket, &path);
                            let _ = writer.send_3164(Severity::LOG_ALERT, &success).is_ok();
                        }
                    }
                    // only remove the file if we are successful
                    let localpath = format!("{}/{}", &config.cachedir, &path);
                    if remove_file(&localpath).is_ok() {
                        // Send some notifications to SYSLOG
                        match syslog::unix(Facility::LOG_SYSLOG) {
                            Err(e) => println!("impossible to connect to syslog: {:?}", e),
                            Ok(writer) => {
                                let success = format!("Removed file {}", &localpath);
                                let _ = writer.send_3164(Severity::LOG_ALERT, &success).is_ok();
                            }
                        }
                    }
                }
                Err(e) => println!("Could not write file {} {}", &file, e),
            }
        });
    });
}

fn resend_logs(config: &ConfigFile, system: &SystemInfo) {

    // create iterator over the directory
    for entry in WalkDir::new(&config.cachedir).into_iter().filter_map(|e| e.ok()) {

        // filter out gzip'd file suffixes
        if entry.file_name()
               .to_str()
               .map(|s| s.ends_with(".gz"))
               .unwrap() {

            // get just the file name
            let filename = entry.file_name().to_str().unwrap();

            // need to return only the parent and strip off the cachedir prefix
            // this then is the path write_s3 is expecting as it is the path within s3
            let path =
                Path::new(entry.path().parent().unwrap()).strip_prefix(&config.cachedir).unwrap();
            let path = Path::new(path).to_str().unwrap();

            // build out the complete path for reading
            let filepath = format!("{}/{}/{}", &config.cachedir, &path, &filename);
            // open the file for reading
            let mut file = File::open(&filepath).expect("Unable to read file");

            // create a u8 vector
            let mut contents: Vec<u8> = Vec::new();
            // add our encoded log file to the vector
            let _ = file.read_to_end(&mut contents);

            // Send some notifications to SYSLOG
            match syslog::unix(Facility::LOG_SYSLOG) {
                Err(e) => println!("impossible to connect to syslog: {:?}", e),
                Ok(writer) => {
                    let success = format!("Found unsent log {}/{}", &path, &filename);
                    let _ = writer.send_3164(Severity::LOG_ALERT, &success).is_ok();
                }
            }
            // pass the unset logs to s3
            write_s3(&config, &system, filename, path, &contents)

        }
    }
}
<|endoftext|>


<commit_before>const EPSILON: f64 = 0.001;

use std::ops::Add;

#[derive(Debug)]
pub struct Vector3 {
    pub x: f64,
    pub y: f64,
    pub z: f64,
}

impl Vector3 {
    pub fn dot(&self, other: &Self) -> f64 {
        return self.x * other.x + self.y * other.y + self.z * other.z;
    }

    pub fn cross(&self, other: &Self) -> Self {
        let x0 = self.y * other.z - self.z * other.y;
        let y0 = self.z * other.x - self.x * other.z;
        let z0 = self.x * other.y - self.y * other.x;

        Vector3 {
            x: x0,
            y: y0,
            z: z0,
        }
    }

    pub fn length(&self) -> f64 {
        self.dot(&self).sqrt()
    }

    pub fn normalize(&self) -> Self {
        let l = self.length();

        if l == 0.0 {
            return Vector3 {
                       x: 0.0,
                       y: 0.0,
                       z: 0.0,
                   };
        }

        Vector3 {
            x: self.x / l,
            y: self.y / l,
            z: self.z / l,
        }
    }
}

// Operators

// This declares two lifetime references. One for the right hand side(a) and
// another for self(b). Since Add is implemented for &'a Vector this means
// self is actually a Vector3 reference. I think that's how it works anyway...
impl<'a, 'b> Add<&'b Vector3> for &'a Vector3 {
    type Output = Vector3;

    fn add(self, other: &'b Vector3) -> Vector3 {
        Vector3 {
            x: self.x + other.x,
            y: self.y + other.y,
            z: self.z + other.z,
        }
    }
}

impl Add for Vector3 {
    type Output = Vector3;

    fn add(self, other: Vector3) -> Vector3 {
        &self + &other
    }
}

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

    macro_rules! assert_eq_within_bound {
        ($x:expr, $y: expr, $bound: expr) => (
            assert!($x >= $y - $bound && $x <= $y + $bound, "{} is not equal to {} within bound {}", $x, $y, $bound);
        );
    }

    macro_rules! assert_eq_vector3 {
        ($x:expr, $y: expr, $bound: expr) => (
            assert_eq_within_bound!($x.x, $y.x, $bound);
            assert_eq_within_bound!($x.y, $y.y, $bound);
            assert_eq_within_bound!($x.z, $y.z, $bound);
        );
    }

    #[test]
    fn test_constructor() {
        let vec = Vector3 {
            x: 2.0,
            y: 1.0,
            z: 0.0,
        };

        assert_eq_within_bound!(vec.x, 2.0, EPSILON);
        assert_eq_within_bound!(vec.y, 1.0, EPSILON);
        assert_eq_within_bound!(vec.z, 0.0, EPSILON);
    }

    #[test]
    fn test_dot() {
        let vec1 = Vector3 {
            x: 3.52,
            y: 8.23,
            z: 29.0,
        };

        let vec2 = Vector3 {
            x: 0.0,
            y: 1.3,
            z: -3.23,
        };

        assert_eq_within_bound!(vec1.dot(&vec2), -82.971, EPSILON);
        assert_eq_within_bound!(vec2.dot(&vec1), -82.971, EPSILON);
    }

    #[test]
    fn test_cross() {
        let vec1 = Vector3 {
            x: 2.4,
            y: 9.3,
            z: -1.3,
        };

        let vec2 = Vector3 {
            x: -2.3,
            y: 2.5,
            z: -3.5,
        };

        let result1 = vec1.cross(&vec2);
        let result2 = vec2.cross(&vec1);

        assert_eq_vector3!(result1,
                           Vector3 {
                               x: -29.3,
                               y: 11.39,
                               z: 27.39,
                           },
                           EPSILON);
        assert_eq_vector3!(result2,
                           Vector3 {
                               x: 29.3,
                               y: -11.39,
                               z: -27.39,
                           },
                           EPSILON);
    }

    #[test]
    fn test_length() {
        let vectors = vec![(Vector3 {
                                x: 0.0,
                                y: 0.0,
                                z: 0.0,
                            },
                            0.0),
                           (Vector3 {
                                x: 2.3,
                                y: -2.1,
                                z: 2.1,
                            },
                            3.756327994),
                           (Vector3 {
                                x: 1.0,
                                y: 0.0,
                                z: 0.0,
                            },
                            1.0),
                           (Vector3 {
                                x: 0.802,
                                y: 0.267,
                                z: 0.534,
                            },
                            1.0)];

        for (vec, length) in vectors {
            assert_eq_within_bound!(vec.length(), length, EPSILON);
        }
    }

    #[test]
    fn test_normalize_zero_length() {
        let vec = Vector3 {
            x: 0.0,
            y: 0.0,
            z: 0.0,
        };

        assert_eq_vector3!(vec.normalize(), vec, EPSILON);
    }

    #[test]
    fn test_normalize() {
        let vec = Vector3 {
            x: 4.0,
            y: 63.0,
            z: 0.5,
        };

        let result = vec.normalize();
        let expected = Vector3 {
            x: 0.063362486,
            y: 0.99795915,
            z: 0.007920311,
        };

        assert_eq_vector3!(result, expected, EPSILON);
        assert_eq_within_bound!(result.length(), 1.0, EPSILON);
    }
}
<commit_msg>Implement more math<commit_after>const EPSILON: f64 = 0.001;

use std::ops::{Add, Sub, Mul};

#[derive(Debug, Copy, Clone)]
pub struct Vector3 {
    pub x: f64,
    pub y: f64,
    pub z: f64,
}

impl Vector3 {
    pub fn dot(&self, other: &Self) -> f64 {
        return self.x * other.x + self.y * other.y + self.z * other.z;
    }

    pub fn cross(&self, other: &Self) -> Self {
        let x0 = self.y * other.z - self.z * other.y;
        let y0 = self.z * other.x - self.x * other.z;
        let z0 = self.x * other.y - self.y * other.x;

        Vector3 {
            x: x0,
            y: y0,
            z: z0,
        }
    }

    pub fn length(&self) -> f64 {
        self.dot(&self).sqrt()
    }

    pub fn normalize(&self) -> Self {
        let l = self.length();

        if l == 0.0 {
            return Vector3 {
                       x: 0.0,
                       y: 0.0,
                       z: 0.0,
                   };
        }

        Vector3 {
            x: self.x / l,
            y: self.y / l,
            z: self.z / l,
        }
    }
}

// Operators

macro_rules! define_scalar_add {
    ($T: ty) => (
        impl Add<$T> for Vector3 {
            type Output = Vector3;

            fn add(self, other: $T) -> Vector3 {
                Vector3 {
                    x: self.x + (other as f64),
                    y: self.y + (other as f64),
                    z: self.z + (other as f64),
                }
            }
        }
    )
}

impl Add for Vector3 {
    type Output = Vector3;

    fn add(self, other: Vector3) -> Vector3 {
        Vector3 {
            x: self.x + other.x,
            y: self.y + other.y,
            z: self.z + other.z,
        }
    }
}

define_scalar_add!(f64);
define_scalar_add!(f32);
define_scalar_add!(i8);
define_scalar_add!(i16);
define_scalar_add!(i32);
define_scalar_add!(i64);
define_scalar_add!(u8);
define_scalar_add!(u16);
define_scalar_add!(u32);
define_scalar_add!(u64);

macro_rules! define_scalar_sub {
    ($T: ty) => (
        impl Sub<$T> for Vector3 {
            type Output = Vector3;

            fn sub(self, other: $T) -> Vector3 {
                Vector3 {
                    x: self.x - (other as f64),
                    y: self.y - (other as f64),
                    z: self.z - (other as f64),
                }
            }
        }
    )
}

impl Sub for Vector3 {
    type Output = Vector3;

    fn sub(self, other: Vector3) -> Vector3 {
        Vector3 {
            x: self.x - other.x,
            y: self.y - other.y,
            z: self.z - other.z,
        }
    }
}

define_scalar_sub!(f64);
define_scalar_sub!(f32);
define_scalar_sub!(i8);
define_scalar_sub!(i16);
define_scalar_sub!(i32);
define_scalar_sub!(i64);
define_scalar_sub!(u8);
define_scalar_sub!(u16);
define_scalar_sub!(u32);
define_scalar_sub!(u64);

macro_rules! define_scalar_mul {
    ($T: ty) => (
        impl Mul<$T> for Vector3 {
            type Output = Vector3;

            fn mul(self, other: $T) -> Vector3 {
                Vector3 {
                    x: self.x * (other as f64),
                    y: self.y * (other as f64),
                    z: self.z * (other as f64),
                }
            }
        }
    )
}

impl Mul for Vector3 {
    type Output = Vector3;

    fn mul(self, other: Vector3) -> Vector3 {
        Vector3 {
            x: self.x * other.x,
            y: self.y * other.y,
            z: self.z * other.z,
        }
    }
}

define_scalar_mul!(f64);
define_scalar_mul!(f32);
define_scalar_mul!(i8);
define_scalar_mul!(i16);
define_scalar_mul!(i32);
define_scalar_mul!(i64);
define_scalar_mul!(u8);
define_scalar_mul!(u16);
define_scalar_mul!(u32);
define_scalar_mul!(u64);

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

    macro_rules! assert_eq_within_bound {
        ($x:expr, $y: expr, $bound: expr) => (
            assert!($x >= $y - $bound && $x <= $y + $bound, "{} is not equal to {} within bound {}", $x, $y, $bound);
        );
    }

    macro_rules! assert_eq_vector3 {
        ($x:expr, $y: expr, $bound: expr) => (
            assert_eq_within_bound!($x.x, $y.x, $bound);
            assert_eq_within_bound!($x.y, $y.y, $bound);
            assert_eq_within_bound!($x.z, $y.z, $bound);
        );
    }

    #[test]
    fn test_constructor() {
        let vec = Vector3 {
            x: 2.0,
            y: 1.0,
            z: 0.0,
        };

        assert_eq_within_bound!(vec.x, 2.0, EPSILON);
        assert_eq_within_bound!(vec.y, 1.0, EPSILON);
        assert_eq_within_bound!(vec.z, 0.0, EPSILON);
    }

    #[test]
    fn test_dot() {
        let vec1 = Vector3 {
            x: 3.52,
            y: 8.23,
            z: 29.0,
        };

        let vec2 = Vector3 {
            x: 0.0,
            y: 1.3,
            z: -3.23,
        };

        assert_eq_within_bound!(vec1.dot(&vec2), -82.971, EPSILON);
        assert_eq_within_bound!(vec2.dot(&vec1), -82.971, EPSILON);
    }

    #[test]
    fn test_cross() {
        let vec1 = Vector3 {
            x: 2.4,
            y: 9.3,
            z: -1.3,
        };

        let vec2 = Vector3 {
            x: -2.3,
            y: 2.5,
            z: -3.5,
        };

        let result1 = vec1.cross(&vec2);
        let result2 = vec2.cross(&vec1);

        assert_eq_vector3!(result1,
                           Vector3 {
                               x: -29.3,
                               y: 11.39,
                               z: 27.39,
                           },
                           EPSILON);
        assert_eq_vector3!(result2,
                           Vector3 {
                               x: 29.3,
                               y: -11.39,
                               z: -27.39,
                           },
                           EPSILON);
    }

    #[test]
    fn test_length() {
        let vectors = vec![(Vector3 {
                                x: 0.0,
                                y: 0.0,
                                z: 0.0,
                            },
                            0.0),
                           (Vector3 {
                                x: 2.3,
                                y: -2.1,
                                z: 2.1,
                            },
                            3.756327994),
                           (Vector3 {
                                x: 1.0,
                                y: 0.0,
                                z: 0.0,
                            },
                            1.0),
                           (Vector3 {
                                x: 0.802,
                                y: 0.267,
                                z: 0.534,
                            },
                            1.0)];

        for (vec, length) in vectors {
            assert_eq_within_bound!(vec.length(), length, EPSILON);
        }
    }

    #[test]
    fn test_normalize_zero_length() {
        let vec = Vector3 {
            x: 0.0,
            y: 0.0,
            z: 0.0,
        };

        assert_eq_vector3!(vec.normalize(), vec, EPSILON);
    }

    #[test]
    fn test_normalize() {
        let vec = Vector3 {
            x: 4.0,
            y: 63.0,
            z: 0.5,
        };

        let result = vec.normalize();
        let expected = Vector3 {
            x: 0.063362486,
            y: 0.99795915,
            z: 0.007920311,
        };

        assert_eq_vector3!(result, expected, EPSILON);
        assert_eq_within_bound!(result.length(), 1.0, EPSILON);
    }

    #[test]
    fn test_addition() {
        let vec1 = Vector3 {
            x: 1.0,
            y: 5.0,
            z: 3.0,
        };

        let vec2 = Vector3 {
            x: 3.2,
            y: 3.1,
            z: 2.1,
        };

        let expected1 = Vector3 {
            x: 4.2,
            y: 8.1,
            z: 5.1,
        };

        let expected2 = Vector3 {
            x: 11.0,
            y: 15.0,
            z: 13.0,
        };

        assert_eq_vector3!(vec1 + vec2, expected1, EPSILON);

        let result: Vector3 = vec1 + 10;
        assert_eq_vector3!(result, expected2, EPSILON);
    }

    #[test]
    fn test_subtraction() {
        let vec1 = Vector3 {
            x: 1.0,
            y: 5.0,
            z: 3.0,
        };

        let vec2 = Vector3 {
            x: 3.2,
            y: 3.1,
            z: 2.1,
        };

        let expected1 = Vector3 {
            x: -2.2,
            y: 1.9,
            z: 0.9,
        };

        let expected2 = Vector3 {
            x: -19.0,
            y: -15.0,
            z: -17.0,
        };

        assert_eq_vector3!(vec1 - vec2, expected1, EPSILON);

        let result: Vector3 = vec1 - 20.0;
        assert_eq_vector3!(result, expected2, EPSILON);
    }

    #[test]
    fn test_multiplication() {
        let vec1 = Vector3 {
            x: 1.0,
            y: 5.0,
            z: 3.0,
        };

        let vec2 = Vector3 {
            x: 3.2,
            y: 3.1,
            z: 2.1,
        };

        let expected1 = Vector3 {
            x: 3.2,
            y: 15.5,
            z: 6.3,
        };
        let expected2 = Vector3 {
            x: 20.0,
            y: 100.0,
            z: 60.0,
        };

        assert_eq_vector3!(vec1 * vec2, expected1, EPSILON);

        let result: Vector3 = vec1 * 20.0;
        assert_eq_vector3!(result, expected2, EPSILON);
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::rc::Rc;

use common::FFTnum;

use FFT;
use algorithm::*;
use algorithm::butterflies::*;

use math_utils;

const MIN_RADIX4_BITS: u32 = 8; // minimum size to consider radix 4 an option is 2^8 = 256
const BUTTERFLIES: [usize; 8] = [2, 3, 4, 5, 6, 7, 8, 16];
const COMPOSITE_BUTTERFLIES: [usize; 4] = [4, 6, 8, 16];

pub struct FFTplanner<T> {
    inverse: bool,
    algorithm_cache: HashMap<usize, Rc<FFT<T>>>,
    butterfly_cache: HashMap<usize, Rc<FFTButterfly<T>>>,
}

impl<T: FFTnum> FFTplanner<T> {
    pub fn new(inverse: bool) -> Self {
        FFTplanner {
            inverse: inverse,
            algorithm_cache: HashMap::new(),
            butterfly_cache: HashMap::new(),
        }
    }

    pub fn plan_fft(&mut self, len: usize) -> Rc<FFT<T>> {
        if len < 2 {
            Rc::new(DFT::new(len, self.inverse)) as Rc<FFT<T>>
        } else {
            let factors = math_utils::prime_factors(len);
            self.plan_fft_with_factors(len, &factors)
        }
    }

    fn plan_butterfly(&mut self, len: usize) -> Rc<FFTButterfly<T>> {
        let inverse = self.inverse;
        self.butterfly_cache.entry(len).or_insert_with(||
            match len {
                2 => Rc::new(Butterfly2::new(inverse)),
                3 => Rc::new(Butterfly3::new(inverse)),
                4 => Rc::new(Butterfly4::new(inverse)),
                5 => Rc::new(Butterfly5::new(inverse)),
                6 => Rc::new(Butterfly6::new(inverse)),
                7 => Rc::new(Butterfly7::new(inverse)),
                8 => Rc::new(Butterfly8::new(inverse)),
                16 => Rc::new(Butterfly16::new(inverse)),
                _ => panic!("Invalid butterfly size: {}", len),
            }
        ).clone()
    }

    fn plan_fft_with_factors(&mut self, len: usize, factors: &[usize]) -> Rc<FFT<T>> {
        if self.algorithm_cache.contains_key(&len) {
            self.algorithm_cache.get(&len).unwrap().clone()
        } else {
            let result = if factors.len() == 1 || COMPOSITE_BUTTERFLIES.contains(&len) {
                self.plan_fft_single_factor(len)

            } else if len.trailing_zeros() >= MIN_RADIX4_BITS {
                //the number of trailing zeroes in len is the number of `2` factors
                //ie if len = 2048 * n, len.trailing_zeros() will equal 11 because 2^11 == 2048

                if len.is_power_of_two() {
                    Rc::new(Radix4::new(len, self.inverse))
                } else {
                    let left_len = 1 << len.trailing_zeros();
                    let right_len = len / left_len;

                    let (left_factors, right_factors) = factors.split_at(len.trailing_zeros() as usize);

                    self.plan_mixed_radix(left_len, left_factors, right_len, right_factors)
                }

            } else {
                let sqrt = (len as f32).sqrt() as usize;
                if sqrt * sqrt == len {
                    // since len is a perfect square, each of its prime factors is duplicated.
                    // since we know they're sorted, we can loop through them in chunks of 2 and keep one out of each chunk
                    // if the stride iterator ever becomes stabilized, it'll be cleaner to use that instead of chunks
                    let mut sqrt_factors = Vec::with_capacity(factors.len() / 2);
                    for chunk in factors.chunks(2) {
                        sqrt_factors.push(chunk[0]);
                    }

                    self.plan_mixed_radix(sqrt, &sqrt_factors, sqrt, &sqrt_factors)
                } else {
                    //len isn't a perfect square. greedily take factors from the list until both sides are as close as possible to sqrt(len)
                    //TODO: We can probably make this more optimal by using a more sophisticated non-greedy algorithm
                    let mut product = 1;
                    let mut second_half_index = 1;
                    for (i, factor) in factors.iter().enumerate() {
                        if product * *factor > sqrt {
                            second_half_index = i;
                            break;
                        } else {
                            product = product * *factor;
                        }
                    }

                    //we now know that product is the largest it can be without being greater than len / product
                    //there's one more thing we can try to make them closer together -- if product * factors[index] < len / product,
                    if product * factors[second_half_index] < len / product {
                        product = product * factors[second_half_index];
                        second_half_index = second_half_index + 1;
                    }

                    //we now have our two FFT sizes: product and product / len
                    let (left_factors, right_factors) = factors.split_at(second_half_index);
                    self.plan_mixed_radix(product, left_factors, len / product, right_factors)
                }
            };
            self.algorithm_cache.insert(len, result.clone());
            result
        }
    }

    fn plan_mixed_radix(&mut self,
                        left_len: usize,
                        left_factors: &[usize],
                        right_len: usize,
                        right_factors: &[usize])
                        -> Rc<FFT<T>> {

        let left_is_butterfly = BUTTERFLIES.contains(&left_len);
        let right_is_butterfly = BUTTERFLIES.contains(&right_len);

        //if both left_len and right_len are butterflies, use a mixed radix implementation specialized for butterfly sub-FFTs
        if left_is_butterfly && right_is_butterfly {
            let left_fft = self.plan_butterfly(left_len);
            let right_fft = self.plan_butterfly(right_len);

            Rc::new(MixedRadixDoubleButterfly::new(left_fft, right_fft)) as
            Rc<FFT<T>>
        } else {
            //neither size is a butterfly, so go with the normal algorithm
            let left_fft = self.plan_fft_with_factors(left_len, left_factors);
            let right_fft = self.plan_fft_with_factors(right_len, right_factors);

            Rc::new(MixedRadix::new(left_fft, right_fft)) as Rc<FFT<T>>
        }
    }

    fn plan_fft_single_factor(&mut self, len: usize) -> Rc<FFT<T>> {
        match len {
            0...1 => Rc::new(DFT::new(len, self.inverse)) as Rc<FFT<T>>,
            2 => Rc::new(butterflies::Butterfly2::new(self.inverse)) as Rc<FFT<T>>,
            3 => Rc::new(butterflies::Butterfly3::new(self.inverse)) as Rc<FFT<T>>,
            4 => Rc::new(butterflies::Butterfly4::new(self.inverse)) as Rc<FFT<T>>,
            5 => Rc::new(butterflies::Butterfly5::new(self.inverse)) as Rc<FFT<T>>,
            6 => Rc::new(butterflies::Butterfly6::new(self.inverse)) as Rc<FFT<T>>,
            7 => Rc::new(butterflies::Butterfly7::new(self.inverse)) as Rc<FFT<T>>,
            8 => Rc::new(butterflies::Butterfly8::new(self.inverse)) as Rc<FFT<T>>,
            16 => Rc::new(butterflies::Butterfly16::new(self.inverse)) as Rc<FFT<T>>,
            _ => self.plan_prime(len),
        }
    }

    fn plan_prime(&mut self, len: usize) -> Rc<FFT<T>> {
        let inner_fft_len = len - 1;
        let factors = math_utils::prime_factors(inner_fft_len);

        let inner_fft = self.plan_fft_with_factors(inner_fft_len, &factors);

        Rc::new(RadersAlgorithm::new(len, inner_fft)) as Rc<FFT<T>>
    }
}
<commit_msg>Added documentation and example for FFTplanner<commit_after>use std::collections::HashMap;
use std::rc::Rc;

use common::FFTnum;

use FFT;
use algorithm::*;
use algorithm::butterflies::*;

use math_utils;

const MIN_RADIX4_BITS: u32 = 8; // minimum size to consider radix 4 an option is 2^8 = 256
const BUTTERFLIES: [usize; 8] = [2, 3, 4, 5, 6, 7, 8, 16];
const COMPOSITE_BUTTERFLIES: [usize; 4] = [4, 6, 8, 16];

/// The FFT planner is used to make new FFT algorithm instances.
///
/// RustFFT has several FFT algorithms available; For a
/// given FFT size, the FFTplanner decides which of the available FFT algorithms to use and then initializes them.
///
/// ~~~
/// // Perform a forward FFT of size 1234
/// # use rustfft::FFTplanner;
/// # use rustfft::num_complex::Complex;
/// # use rustfft::num_traits::Zero;
///
/// let mut input:  Vec<Complex<f32>> = vec![Zero::zero(); 1234];
/// let mut output: Vec<Complex<f32>> = vec![Zero::zero(); 1234];
///
/// let mut planner = FFTplanner::new(false);
/// let fft = planner.plan_fft(1234);
/// fft.process(&mut input, &mut output);
/// ~~~
///
/// If you plan on creating multiple FFT instances, it is recommnded to reuse the same planner for all of them. This
/// is because the planner re-uses internal data wherever possible, saving memory and reducing setup time. FFT
/// instances created with one planner will never re-use data and buffers with FFT instances created by a different
/// planner.
///
/// Each FFT instance owns `Rc`s to its internal data, rather than borrowing it from the planner, so it's perfectly
/// safe to drop the planner after creating FFT instances.
pub struct FFTplanner<T> {
    inverse: bool,
    algorithm_cache: HashMap<usize, Rc<FFT<T>>>,
    butterfly_cache: HashMap<usize, Rc<FFTButterfly<T>>>,
}

impl<T: FFTnum> FFTplanner<T> {
    /// Creates a new FFT planner, which will plan forwarrd FFTs if `inverse` is false,
    /// or inverse FFTs if `inverse` is true
    pub fn new(inverse: bool) -> Self {
        FFTplanner {
            inverse: inverse,
            algorithm_cache: HashMap::new(),
            butterfly_cache: HashMap::new(),
        }
    }

    /// Returns an instance of an FFT algorithm capable of processing signals of size `len`
    /// Attempts to save time and memory by re-using data across multiple calls where possible.
    pub fn plan_fft(&mut self, len: usize) -> Rc<FFT<T>> {
        if len < 2 {
            Rc::new(DFT::new(len, self.inverse)) as Rc<FFT<T>>
        } else {
            let factors = math_utils::prime_factors(len);
            self.plan_fft_with_factors(len, &factors)
        }
    }

    fn plan_butterfly(&mut self, len: usize) -> Rc<FFTButterfly<T>> {
        let inverse = self.inverse;
        self.butterfly_cache.entry(len).or_insert_with(||
            match len {
                2 => Rc::new(Butterfly2::new(inverse)),
                3 => Rc::new(Butterfly3::new(inverse)),
                4 => Rc::new(Butterfly4::new(inverse)),
                5 => Rc::new(Butterfly5::new(inverse)),
                6 => Rc::new(Butterfly6::new(inverse)),
                7 => Rc::new(Butterfly7::new(inverse)),
                8 => Rc::new(Butterfly8::new(inverse)),
                16 => Rc::new(Butterfly16::new(inverse)),
                _ => panic!("Invalid butterfly size: {}", len),
            }
        ).clone()
    }

    fn plan_fft_with_factors(&mut self, len: usize, factors: &[usize]) -> Rc<FFT<T>> {
        if self.algorithm_cache.contains_key(&len) {
            self.algorithm_cache.get(&len).unwrap().clone()
        } else {
            let result = if factors.len() == 1 || COMPOSITE_BUTTERFLIES.contains(&len) {
                self.plan_fft_single_factor(len)

            } else if len.trailing_zeros() >= MIN_RADIX4_BITS {
                //the number of trailing zeroes in len is the number of `2` factors
                //ie if len = 2048 * n, len.trailing_zeros() will equal 11 because 2^11 == 2048

                if len.is_power_of_two() {
                    Rc::new(Radix4::new(len, self.inverse))
                } else {
                    let left_len = 1 << len.trailing_zeros();
                    let right_len = len / left_len;

                    let (left_factors, right_factors) = factors.split_at(len.trailing_zeros() as usize);

                    self.plan_mixed_radix(left_len, left_factors, right_len, right_factors)
                }

            } else {
                let sqrt = (len as f32).sqrt() as usize;
                if sqrt * sqrt == len {
                    // since len is a perfect square, each of its prime factors is duplicated.
                    // since we know they're sorted, we can loop through them in chunks of 2 and keep one out of each chunk
                    // if the stride iterator ever becomes stabilized, it'll be cleaner to use that instead of chunks
                    let mut sqrt_factors = Vec::with_capacity(factors.len() / 2);
                    for chunk in factors.chunks(2) {
                        sqrt_factors.push(chunk[0]);
                    }

                    self.plan_mixed_radix(sqrt, &sqrt_factors, sqrt, &sqrt_factors)
                } else {
                    //len isn't a perfect square. greedily take factors from the list until both sides are as close as possible to sqrt(len)
                    //TODO: We can probably make this more optimal by using a more sophisticated non-greedy algorithm
                    let mut product = 1;
                    let mut second_half_index = 1;
                    for (i, factor) in factors.iter().enumerate() {
                        if product * *factor > sqrt {
                            second_half_index = i;
                            break;
                        } else {
                            product = product * *factor;
                        }
                    }

                    //we now know that product is the largest it can be without being greater than len / product
                    //there's one more thing we can try to make them closer together -- if product * factors[index] < len / product,
                    if product * factors[second_half_index] < len / product {
                        product = product * factors[second_half_index];
                        second_half_index = second_half_index + 1;
                    }

                    //we now have our two FFT sizes: product and product / len
                    let (left_factors, right_factors) = factors.split_at(second_half_index);
                    self.plan_mixed_radix(product, left_factors, len / product, right_factors)
                }
            };
            self.algorithm_cache.insert(len, result.clone());
            result
        }
    }

    fn plan_mixed_radix(&mut self,
                        left_len: usize,
                        left_factors: &[usize],
                        right_len: usize,
                        right_factors: &[usize])
                        -> Rc<FFT<T>> {

        let left_is_butterfly = BUTTERFLIES.contains(&left_len);
        let right_is_butterfly = BUTTERFLIES.contains(&right_len);

        //if both left_len and right_len are butterflies, use a mixed radix implementation specialized for butterfly sub-FFTs
        if left_is_butterfly && right_is_butterfly {
            let left_fft = self.plan_butterfly(left_len);
            let right_fft = self.plan_butterfly(right_len);

            Rc::new(MixedRadixDoubleButterfly::new(left_fft, right_fft)) as
            Rc<FFT<T>>
        } else {
            //neither size is a butterfly, so go with the normal algorithm
            let left_fft = self.plan_fft_with_factors(left_len, left_factors);
            let right_fft = self.plan_fft_with_factors(right_len, right_factors);

            Rc::new(MixedRadix::new(left_fft, right_fft)) as Rc<FFT<T>>
        }
    }

    fn plan_fft_single_factor(&mut self, len: usize) -> Rc<FFT<T>> {
        match len {
            0...1 => Rc::new(DFT::new(len, self.inverse)) as Rc<FFT<T>>,
            2 => Rc::new(butterflies::Butterfly2::new(self.inverse)) as Rc<FFT<T>>,
            3 => Rc::new(butterflies::Butterfly3::new(self.inverse)) as Rc<FFT<T>>,
            4 => Rc::new(butterflies::Butterfly4::new(self.inverse)) as Rc<FFT<T>>,
            5 => Rc::new(butterflies::Butterfly5::new(self.inverse)) as Rc<FFT<T>>,
            6 => Rc::new(butterflies::Butterfly6::new(self.inverse)) as Rc<FFT<T>>,
            7 => Rc::new(butterflies::Butterfly7::new(self.inverse)) as Rc<FFT<T>>,
            8 => Rc::new(butterflies::Butterfly8::new(self.inverse)) as Rc<FFT<T>>,
            16 => Rc::new(butterflies::Butterfly16::new(self.inverse)) as Rc<FFT<T>>,
            _ => self.plan_prime(len),
        }
    }

    fn plan_prime(&mut self, len: usize) -> Rc<FFT<T>> {
        let inner_fft_len = len - 1;
        let factors = math_utils::prime_factors(inner_fft_len);

        let inner_fft = self.plan_fft_with_factors(inner_fft_len, &factors);

        Rc::new(RadersAlgorithm::new(len, inner_fft)) as Rc<FFT<T>>
    }
}
<|endoftext|>


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

use vector::{Line2f, Vec2f};

/// 2D Quadrilateral
///
/// ```text
///          top
///     p3________p2
///      |        |
///      |        |
/// left |        | right
///      |        |
///      |________|
///     p0        p1
///        bottom
/// ```
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Quad2f {
    pub points: (Vec2f, Vec2f, Vec2f, Vec2f)
}

/// Return type for Quad2f::iblerp
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum IBLerpResult {
    NoSolution,
    /// One solutions (as parametric coordinate)
    OneSolution(Vec2f),
    /// Two solutions (as parametric coordinates)
    TwoSolutions(Vec2f, Vec2f),
    ManySolutions
}

/// Return type for Quad2f::inv_bilerp_u
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum InvBilerpResult {
    NoSolution,
    /// One solutions (as parametric coordinate)
    OneSolution(f32),
    /// Two solutions (as parametric coordinates)
    TwoSolutions(f32, f32),
    ManySolutions
}

impl Quad2f {
    pub fn new(a: Vec2f, b: Vec2f, c: Vec2f, d: Vec2f) -> Quad2f {
        Quad2f {
            points: (a, b, c, d)
        }
    }

    // TODO(nicholasbishop): code dedup

    /// Inverse bilinear interpolation
    ///
    /// Adapted from stackoverflow.com/questions/808441
    pub fn iblerp(self, point: Vec2f) -> IBLerpResult {
        let p0mp = self.points.0 - point;
        let p1mp = self.points.1 - point;
        let p0mp3 = self.points.0 - self.points.3;
        let p1mp2 = self.points.1 - self.points.2;

        let a = p0mp.cross(p0mp3);
        let b0 = p0mp.cross(p1mp2);
        let b1 = p1mp.cross(p0mp3);
        let b = (b0 + b1) / 2.0;
        let c = p1mp.cross(p1mp2);

        let calc_st = |s| {
            let den = (1.0 - s) * p0mp3.x + s * p1mp2.x;
            let t = if den == 0.0 {
                // TODO(nicholasbishop): perhaps there's a more
                // efficient way to solve this, the SO post doesn't
                // seem to cover this case
                let rb = self.points.0.lerp(self.points.1, s);
                let rt = self.points.3.lerp(self.points.2, s);
                Line2f::new(rb, rt).closest_parametric_point(point)
            }
            else {
                ((1.0 - s) * (p0mp.x) + s * p1mp.x) / den
            };
            Vec2f::new(s, t)
        };

        let den = a - (2.0 * b) + c;
        if den == 0.0 {
            let m = a - c;
            if m == 0.0 {
                if a == 0.0 {
                    IBLerpResult::ManySolutions
                }
                else {
                    IBLerpResult::NoSolution
                }
            }
            else {
                IBLerpResult::OneSolution(calc_st(a / m))
            }
        }
        else {
            let left = a - b;
            let right = (b.powi(2) - a*c).sqrt();
            let s0 = (left + right) / den;
            let s1 = (left - right) / den;
            IBLerpResult::TwoSolutions(calc_st(s0), calc_st(s1))
        }
    }

    /// Calculate horizontal parametric coordinate from cartesian point.
    pub fn inv_bilerp_u(self, point: Vec2f) -> InvBilerpResult {
        let p0mp = self.points.0 - point;
        let p1mp = self.points.1 - point;
        let p0mp3 = self.points.0 - self.points.3;
        let p1mp2 = self.points.1 - self.points.2;

        let a = p0mp.cross(p0mp3);
        let b0 = p0mp.cross(p1mp2);
        let b1 = p1mp.cross(p0mp3);
        let b = (b0 + b1) / 2.0;
        let c = p1mp.cross(p1mp2);

        let den = a - (2.0 * b) + c;
        if den == 0.0 {
            let m = a - c;
            if m == 0.0 {
                if a == 0.0 {
                    InvBilerpResult::ManySolutions
                }
                else {
                    InvBilerpResult::NoSolution
                }
            }
            else {
                InvBilerpResult::OneSolution(a / m)
            }
        }
        else {
            let left = a - b;
            let right = (b.powi(2) - a*c).sqrt();
            let s0 = (left + right) / den;
            let s1 = (left - right) / den;
            InvBilerpResult::TwoSolutions(s0, s1)
        }
    }

    pub fn blerp(self, uv: Vec2f) -> Vec2f {
        let rb = self.points.0.lerp(self.points.1, uv.x);
        let rt = self.points.3.lerp(self.points.2, uv.x);
        rb.lerp(rt, uv.y)
    }
}

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

    #[test]
    fn test_inv_bilerp_u() {
        let q = Quad2f::new(vec2f(0, 0),
                            vec2f(4, 0),
                            vec2f(4, 4),
                            vec2f(0, 4));
        assert_eq!(q.inv_bilerp_u(vec2f(2, 2)),
                   InvBilerpResult::OneSolution(0.5));
        assert_eq!(q.inv_bilerp_u(vec2f(1, 2)),
                   InvBilerpResult::OneSolution(0.25));
        assert_eq!(q.inv_bilerp_u(vec2f(1, 3)),
                   InvBilerpResult::OneSolution(0.25));
    }

    #[test]
    fn test_iblerp() {
        let q = Quad2f::new(vec2f(0, 0),
                            vec2f(4, 0),
                            vec2f(4, 4),
                            vec2f(0, 4));
        assert_eq!(q.iblerp(vec2f(2, 2)),
                   IBLerpResult::OneSolution(vec2f(0.5, 0.5)));
    }
}
<commit_msg>Add more quad lerp methods<commit_after>// Copyright 2015 Nicholas Bishop
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use vector::{Line2f, Vec2f};

/// 2D Quadrilateral
///
/// ```text
///          top
///     p3________p2
///      |        |
///      |        |
/// left |        | right
///      |        |
///      |________|
///     p0        p1
///        bottom
/// ```
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Quad2f {
    pub points: (Vec2f, Vec2f, Vec2f, Vec2f)
}

/// Return type for Quad2f::iblerp
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum IBLerpResult {
    NoSolution,
    /// One solutions (as parametric coordinate)
    OneSolution(Vec2f),
    /// Two solutions (as parametric coordinates)
    TwoSolutions(Vec2f, Vec2f),
    ManySolutions
}

/// Return type for Quad2f::inv_bilerp_u
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum InvBilerpResult {
    NoSolution,
    /// One solutions (as parametric coordinate)
    OneSolution(f32),
    /// Two solutions (as parametric coordinates)
    TwoSolutions(f32, f32),
    ManySolutions
}

impl Quad2f {
    pub fn new(a: Vec2f, b: Vec2f, c: Vec2f, d: Vec2f) -> Quad2f {
        Quad2f {
            points: (a, b, c, d)
        }
    }

    // TODO(nicholasbishop): code dedup

    /// Inverse bilinear interpolation
    ///
    /// Adapted from stackoverflow.com/questions/808441
    pub fn iblerp(self, point: Vec2f) -> IBLerpResult {
        let p0mp = self.points.0 - point;
        let p1mp = self.points.1 - point;
        let p0mp3 = self.points.0 - self.points.3;
        let p1mp2 = self.points.1 - self.points.2;

        let a = p0mp.cross(p0mp3);
        let b0 = p0mp.cross(p1mp2);
        let b1 = p1mp.cross(p0mp3);
        let b = (b0 + b1) / 2.0;
        let c = p1mp.cross(p1mp2);

        let calc_st = |s| {
            let den = (1.0 - s) * p0mp3.x + s * p1mp2.x;
            let t = if den == 0.0 {
                // TODO(nicholasbishop): perhaps there's a more
                // efficient way to solve this, the SO post doesn't
                // seem to cover this case
                let rb = self.points.0.lerp(self.points.1, s);
                let rt = self.points.3.lerp(self.points.2, s);
                Line2f::new(rb, rt).closest_parametric_point(point)
            }
            else {
                ((1.0 - s) * (p0mp.x) + s * p1mp.x) / den
            };
            Vec2f::new(s, t)
        };

        let den = a - (2.0 * b) + c;
        if den == 0.0 {
            let m = a - c;
            if m == 0.0 {
                if a == 0.0 {
                    IBLerpResult::ManySolutions
                }
                else {
                    IBLerpResult::NoSolution
                }
            }
            else {
                IBLerpResult::OneSolution(calc_st(a / m))
            }
        }
        else {
            let left = a - b;
            let right = (b.powi(2) - a*c).sqrt();
            let s0 = (left + right) / den;
            let s1 = (left - right) / den;
            IBLerpResult::TwoSolutions(calc_st(s0), calc_st(s1))
        }
    }

    /// Calculate horizontal parametric coordinate from cartesian point.
    pub fn inv_bilerp_u(self, point: Vec2f) -> InvBilerpResult {
        let p0mp = self.points.0 - point;
        let p1mp = self.points.1 - point;
        let p0mp3 = self.points.0 - self.points.3;
        let p1mp2 = self.points.1 - self.points.2;

        let a = p0mp.cross(p0mp3);
        let b0 = p0mp.cross(p1mp2);
        let b1 = p1mp.cross(p0mp3);
        let b = (b0 + b1) / 2.0;
        let c = p1mp.cross(p1mp2);

        let den = a - (2.0 * b) + c;
        if den == 0.0 {
            let m = a - c;
            if m == 0.0 {
                if a == 0.0 {
                    InvBilerpResult::ManySolutions
                }
                else {
                    InvBilerpResult::NoSolution
                }
            }
            else {
                InvBilerpResult::OneSolution(a / m)
            }
        }
        else {
            let left = a - b;
            let right = (b.powi(2) - a*c).sqrt();
            let s0 = (left + right) / den;
            let s1 = (left - right) / den;
            InvBilerpResult::TwoSolutions(s0, s1)
        }
    }

    pub fn lerp_bottom(self, u: f32) -> Vec2f {
        self.points.0.lerp(self.points.1, u)
    }

    pub fn lerp_top(self, u: f32) -> Vec2f {
        self.points.3.lerp(self.points.2, u)
    }

    pub fn lerp_left(self, v: f32) -> Vec2f {
        self.points.0.lerp(self.points.3, v)
    }

    pub fn lerp_right(self, v: f32) -> Vec2f {
        self.points.1.lerp(self.points.2, v)
    }

    pub fn blerp(self, uv: Vec2f) -> Vec2f {
        let rb = self.points.0.lerp(self.points.1, uv.x);
        let rt = self.points.3.lerp(self.points.2, uv.x);
        rb.lerp(rt, uv.y)
    }
}

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

    #[test]
    fn test_inv_bilerp_u() {
        let q = Quad2f::new(vec2f(0, 0),
                            vec2f(4, 0),
                            vec2f(4, 4),
                            vec2f(0, 4));
        assert_eq!(q.inv_bilerp_u(vec2f(2, 2)),
                   InvBilerpResult::OneSolution(0.5));
        assert_eq!(q.inv_bilerp_u(vec2f(1, 2)),
                   InvBilerpResult::OneSolution(0.25));
        assert_eq!(q.inv_bilerp_u(vec2f(1, 3)),
                   InvBilerpResult::OneSolution(0.25));
    }

    #[test]
    fn test_iblerp() {
        let q = Quad2f::new(vec2f(0, 0),
                            vec2f(4, 0),
                            vec2f(4, 4),
                            vec2f(0, 4));
        assert_eq!(q.iblerp(vec2f(2, 2)),
                   IBLerpResult::OneSolution(vec2f(0.5, 0.5)));
    }
}
<|endoftext|>