<commit_before>// Copyright 2014 Jeffery Olson
//
// Licensed under the 3-Clause BSD License, see LICENSE.txt
// at the top-level of this repository.
// This file may not be copied, modified, or distributed
// except according to those terms.

use num::bigint::BigInt;
use num::rational::BigRational;
use env::Env;
use expr::{Expr, Atom, Lambda, BuiltIn, Integer, Float, Symbol,
           Boolean, List, Number};
use result::SchemerResult;

pub fn add_builtins(mut env: Env) -> Env {
    env.define("+".to_string(), Atom(Lambda(BuiltIn("+".to_string(), builtin_add))));
    env.define("-".to_string(), Atom(Lambda(BuiltIn("-".to_string(), builtin_subtract))));
    env.define("*".to_string(), Atom(Lambda(BuiltIn("*".to_string(), builtin_multiply))));
    env.define("/".to_string(), Atom(Lambda(BuiltIn("/".to_string(), builtin_divide))));
    env.define("<".to_string(), Atom(Lambda(BuiltIn("<".to_string(), builtin_lt))));
    env.define(">".to_string(), Atom(Lambda(BuiltIn(">".to_string(), builtin_gt))));
    env.define("<=".to_string(), Atom(Lambda(BuiltIn("<=".to_string(), builtin_lte))));
    env.define(">=".to_string(), Atom(Lambda(BuiltIn(">=".to_string(), builtin_gte))));
    env.define("not".to_string(), Atom(Lambda(BuiltIn("not".to_string(), builtin_not))));
    env.define("=".to_string(), Atom(Lambda(BuiltIn("=".to_string(), builtin_eq))));
    env.define("eq?".to_string(), Atom(Lambda(BuiltIn("eq?".to_string(), builtin_eq))));
    env.define("equal?".to_string(), Atom(Lambda(BuiltIn("equal?".to_string(), builtin_eq))));
    env.define("length".to_string(), Atom(Lambda(BuiltIn("length".to_string(), builtin_length))));
    env.define("cons".to_string(), Atom(Lambda(BuiltIn("cons".to_string(), builtin_cons))));
    env.define("car".to_string(), Atom(Lambda(BuiltIn("car".to_string(), builtin_car))));
    env.define("cdr".to_string(), Atom(Lambda(BuiltIn("cdr".to_string(), builtin_cdr))));
    env.define("append".to_string(), Atom(Lambda(BuiltIn("append".to_string(), builtin_append))));
    env.define("list".to_string(), Atom(Lambda(BuiltIn("list".to_string(), builtin_list))));
    env.define("list?".to_string(), Atom(Lambda(BuiltIn("list?".to_string(), builtin_is_list))));
    env.define("null?".to_string(), Atom(Lambda(BuiltIn("null?".to_string(), builtin_is_null))));
    env.define("symbol?".to_string(),
               Atom(Lambda(BuiltIn("symbol?".to_string(), builtin_is_symbol))));
    env.define("display".to_string(),
               Atom(Lambda(BuiltIn("display".to_string(), builtin_display))));
    env
}

fn builtin_arithmetic(mut args: Vec<Expr>, env: Env,
                      name: &str,
                      transformer_bi: |BigInt, BigInt| -> BigInt,
                      transformer_br: |BigRational, BigRational| -> BigRational,
                      ) -> SchemerResult<(Option<Expr>, Env)> {
    let mut hasFloats = false;
    for atom in args.iter() {
        match atom {
            &Atom(Float(_)) => hasFloats = true,
            &Atom(Integer(_)) => {},
            val => return Err(format!("{}: invoking with non numeric input: '{}'",
                         name, val.print()))
        }
    }
    let out_val = if hasFloats {
        let mut out_val = match args.shift()
            {
                Some(v) => match v {
                    v @ Atom(Integer(_)) | v @ Atom(Float(_)) => match v.into_float() {
                        Ok(v) => v,
                        Err(e) => return Err(format!("{}: {}", name, e))
                    },
                    _ => return Err(format!("{}: cannot process non-numeric input", name))
                },
                None => return Err(format!("{}: head of add args should be Some()", name))
            };
        for atom in args.move_iter() {
            match atom {
                v @ Atom(Integer(_)) | v @ Atom(Float(_)) =>
                    out_val = match v.into_float() {
                        Ok(v) => transformer_br(out_val, v),
                        Err(e) => return Err(format!("{}: {}", name, e))
                    },
                _ => return Err(format!("{}: invoking with non numeric input", name))
            }
        }
        Atom(Float(out_val))
    } else {
        let mut out_val = match args.shift()
            .expect("head of add args should be Some()") {
                v @ Atom(Integer(_)) | v @ Atom(Float(_)) => match v.into_integer() {
                    Ok(v) => v,
                    Err(e) => return Err(format!("{}", e))
                },
                _ => return Err(format!("{}: cannot process non-numeric input", name))
            };
        for atom in args.move_iter() {
            match atom {
                Atom(Integer(val)) => out_val = transformer_bi(out_val, val),
                _ => return Err(format!("{}: invoking with non numeric input", name))
            }
        }
        Atom(Integer(out_val))
    };
    Ok((Some(out_val), env))
}

fn builtin_add(args: Vec<Expr>, env: Env) -> (Option<Expr>, Env) {
    let name = "+";
    let transformer_bi = |left:BigInt, right: BigInt| left + right;
    let transformer_br = |left:BigRational, right: BigRational| left + right;
    match builtin_arithmetic(args, env, name, transformer_bi, transformer_br) {
        Ok(v) => v,
        Err(e) => fail!("{}: {}", name, e)
    }
}

fn builtin_subtract(args: Vec<Expr>, env: Env) -> (Option<Expr>, Env) {
    let name = "-";
    let transformer_bi = |left:BigInt, right: BigInt| left - right;
    let transformer_br = |left:BigRational, right: BigRational| left - right;
    match builtin_arithmetic(args, env, name, transformer_bi, transformer_br) {
        Ok(v) => v,
        Err(e) => fail!("{}: {}", name, e)
    }
}

fn builtin_multiply(args: Vec<Expr>, env: Env) -> (Option<Expr>, Env) {
    let name = "*";
    let transformer_bi = |left:BigInt, right: BigInt| left * right;
    let transformer_br = |left:BigRational, right: BigRational| left * right;
    match builtin_arithmetic(args, env, name, transformer_bi, transformer_br) {
        Ok(v) => v,
        Err(e) => fail!("{}: {}", name, e)
    }
}

fn builtin_divide(args: Vec<Expr>, env: Env) -> (Option<Expr>, Env) {
    let name = "/";
    let transformer_bi = |left:BigInt, right: BigInt| left / right;
    let transformer_br = |left:BigRational, right: BigRational| left / right;
    match builtin_arithmetic(args, env, name, transformer_bi, transformer_br) {
        Ok(v) => v,
        Err(e) => fail!("{}: {}", name, e)
    }
}

fn builtin_predicate_compare(mut args: Vec<Expr>,
                   env: Env,
                   name: &str,
                   transformer_bi: |BigInt, BigInt| -> bool,
                   transformer_br: |BigRational, BigRational| -> bool,
                   ) -> SchemerResult<(Option<Expr>, Env)> {
    let mut hasFloats = false;
    if args.len() < 2 {
        return Err(format!("{}: too few parameters (must provide at least two)", name));
    }
    for atom in args.iter() {
        match atom {
            &Atom(Float(_)) => hasFloats = true,
            &Atom(Integer(_)) => {},
            val => return Err(format!(
                "{}: invoking with non numeric input: '{}'", name, val.print()))
        }
    }
    let out_val = if hasFloats {
        let mut state = false;
        let mut left = match args.shift() {
            None => return Err(format!("{}: head of less-than args should be Some()", name)),
            Some(v) => match v {
                    left @ Atom(Integer(_)) |
                    left @ Atom(Float(_)) => try!(left.into_float()),
                    _ => return Err(format!("{}: no match", name))
                }
            };
        for atom in args.move_iter() {
            match atom {
                right @ Atom(Integer(_)) | right @ Atom(Float(_)) => {
                    let right = try!(right.into_float());
                    state = transformer_br(left, right.clone());
                    left = right;
                },
                _ => return Err(format!("{}: invoking with non numeric input", name))
            }
        }
        Atom(Boolean(state))
    } else {
        let mut state = false;
        let mut left = match args.shift() {
            None => return Err(format!("{}: head of less-than args should be Some()", name)),
            Some(v) => match v {
                left @ Atom(Integer(_))
                    | left @ Atom(Float(_)) => try!(left.into_integer()),
                _ => return Err(format!("{}: cannot process non-numeric input", name))
            }
        };
        for atom in args.move_iter() {
            match atom {
                Atom(Integer(right)) => {
                    state = transformer_bi(left, right.clone());
                    left = right;
                }
                _ => return Err(format!("{}: invoking with non numeric input", name))
            }
        }
        Atom(Boolean(state))
    };
    Ok((Some(out_val), env))
}

fn builtin_lt(args: Vec<Expr>, env: Env) -> (Option<Expr>, Env) {
    let name = "<";
    let transformer_bi = |left:BigInt, right:BigInt| left < right;
    let transformer_br = |left:BigRational, right:BigRational| left < right;
    match builtin_predicate_compare(args, env, name, transformer_bi,
                          transformer_br) {
        Ok(r) => r,
        Err(e) => fail!("{}", e)
    }
}

fn builtin_gt(args: Vec<Expr>, env: Env) -> (Option<Expr>, Env) {
    let name = ">";
    let transformer_bi = |left:BigInt, right:BigInt| left > right;
    let transformer_br = |left:BigRational, right:BigRational| left > right;
    match builtin_predicate_compare(args, env, name, transformer_bi,
                          transformer_br) {
        Ok(r) => r,
        Err(e) => fail!("{}", e)
    }
}

fn builtin_lte(args: Vec<Expr>, env: Env) -> (Option<Expr>, Env) {
    let name = "<=";
    let transformer_bi = |left:BigInt, right:BigInt| left <= right;
    let transformer_br = |left:BigRational, right:BigRational| left <= right;
    match builtin_predicate_compare(args, env, name, transformer_bi,
                          transformer_br) {
        Ok(r) => r,
        Err(e) => fail!("{}", e)
    }
}

fn builtin_gte(args: Vec<Expr>, env: Env) -> (Option<Expr>, Env) {
    let name = ">=";
    let transformer_bi = |left:BigInt, right:BigInt| left >= right;
    let transformer_br = |left:BigRational, right:BigRational| left >= right;
    match builtin_predicate_compare(args, env, name, transformer_bi,
                          transformer_br) {
        Ok(r) => r,
        Err(e) => fail!("{}", e)
    }
}

fn builtin_not(mut args: Vec<Expr>, env: Env) -> (Option<Expr>, Env) {
    if args.len() != 1 {
        fail!("not: expect a single parameter")
    }
    match args.pop().unwrap() {
        Atom(Boolean(false)) => (Some(Atom(Boolean(true))), env),
        _ => (Some(Atom(Boolean(false))), env),
    }
}
fn builtin_eq(mut args: Vec<Expr>, env: Env) -> (Option<Expr>, Env) {
    if args.len() < 2 {
        fail!("eq: expect at least two parameters")
    }
    let mut state = false;
    let mut left = args.shift().expect("head of eq args should be Some()");
    for right in args.move_iter() {
        state = left == right;
        left = right;
    }
    (Some(Atom(Boolean(state))), env)
}
fn builtin_length(mut args: Vec<Expr>, env: Env) -> (Option<Expr>, Env) {
    if args.len() != 1 {
        fail!("length: expects only a single list parameter")
    }
    match args.shift().expect("head of length args should be Some()") {
        List(items) => {
            println!("length: {}", items.len());
            (Some(Atom(Number::uint(items.len()))), env)
        }
        _ => fail!("length: expecting a list parameter")
    }
}
fn builtin_cons(mut args: Vec<Expr>, env: Env) -> (Option<Expr>, Env) {
    if args.len() != 2 {
        fail!("cons: expects two parameters")
    }
    let cons_result = match Expr::cons(args.shift().unwrap(), args.shift().unwrap()) {
        Ok(r) => r,
        Err(e) => fail!("builtin_cons: failure in Expr::cons(): {}", e)
    };
    (Some(cons_result), env)
}
fn builtin_car(mut args: Vec<Expr>, env: Env) -> (Option<Expr>, Env) {
    if args.len() != 1 {
        fail!("car: expect a single list parameter");
    }
    let (car, _) = match args.pop().unwrap() {
        v @ List(_) => match v.un_cons() {
            Ok(v) => v,
            Err(e) => fail!("car: {}", e)
        },
        _ => fail!("car: expected a list")
    };
    (Some(car), env)
}
fn builtin_cdr(mut args: Vec<Expr>, env: Env) -> (Option<Expr>, Env) {
    if args.len() != 1 {
        fail!("cdr: expect a single list parameter");
    }
    let (_, cdr) = match args.pop().unwrap() {
        v @ List(_) => match v.un_cons() {
            Ok(v) => v,
            Err(e) => fail!("cdr: {}", e)
        },
        _ => fail!("cdr: expected a list")
    };
    (Some(cdr), env)
}
fn builtin_append(mut args: Vec<Expr>, env: Env) -> (Option<Expr>, Env) {
    if args.len() != 2 {
        fail!("append: expect two arguments");
    }
    let out_items = match args.shift().unwrap() {
        List(mut items) => {
            items.push(box args.pop().unwrap());
            items
        },
        _ => fail!("append: expect first arg to be a list")
    };
    (Some(List(out_items)), env)
}
fn builtin_list(args: Vec<Expr>, env: Env) -> (Option<Expr>, Env) {
    (Some(List(args.move_iter().map(|x| box x).collect())), env)
}
fn builtin_is_list(mut args: Vec<Expr>, env: Env) -> (Option<Expr>, Env) {
    if args.len() != 1 {
        fail!("list?: expect a single argument");
    }
    let result = match args.shift().unwrap() {
        List(_) => {
            Atom(Boolean(true))
        },
        _ => Atom(Boolean(false))
    };
    (Some(result), env)
}

fn builtin_is_null(mut args: Vec<Expr>, env: Env) -> (Option<Expr>, Env) {
    if args.len() != 1 {
        fail!("null?: expect a single argument");
    }
    let result = match args.shift().unwrap() {
        List(ref v) if v.len() == 0 => {
            Atom(Boolean(true))
        },
        _ => Atom(Boolean(false))
    };
    (Some(result), env)
}

fn builtin_is_symbol(mut args: Vec<Expr>, env: Env) -> (Option<Expr>, Env) {
    if args.len() != 1 {
        fail!("symbol?: expect a single argument");
    }
    let result = match args.shift().unwrap() {
        Atom(Symbol(_)) => {
            Atom(Boolean(true))
        },
        _ => Atom(Boolean(false))
    };
    (Some(result), env)
}

fn builtin_display(mut args: Vec<Expr>, env: Env) -> (Option<Expr>, Env) {
    if args.len() != 1 {
        fail!("display: expect a single argument");
    }
    println!("{}", args.shift().unwrap().print());
    (None, env)
}
<commit_msg>fix trailing whitespace<commit_after>// Copyright 2014 Jeffery Olson
//
// Licensed under the 3-Clause BSD License, see LICENSE.txt
// at the top-level of this repository.
// This file may not be copied, modified, or distributed
// except according to those terms.

use num::bigint::BigInt;
use num::rational::BigRational;
use env::Env;
use expr::{Expr, Atom, Lambda, BuiltIn, Integer, Float, Symbol,
           Boolean, List, Number};
use result::SchemerResult;

pub fn add_builtins(mut env: Env) -> Env {
    env.define("+".to_string(), Atom(Lambda(BuiltIn("+".to_string(), builtin_add))));
    env.define("-".to_string(), Atom(Lambda(BuiltIn("-".to_string(), builtin_subtract))));
    env.define("*".to_string(), Atom(Lambda(BuiltIn("*".to_string(), builtin_multiply))));
    env.define("/".to_string(), Atom(Lambda(BuiltIn("/".to_string(), builtin_divide))));
    env.define("<".to_string(), Atom(Lambda(BuiltIn("<".to_string(), builtin_lt))));
    env.define(">".to_string(), Atom(Lambda(BuiltIn(">".to_string(), builtin_gt))));
    env.define("<=".to_string(), Atom(Lambda(BuiltIn("<=".to_string(), builtin_lte))));
    env.define(">=".to_string(), Atom(Lambda(BuiltIn(">=".to_string(), builtin_gte))));
    env.define("not".to_string(), Atom(Lambda(BuiltIn("not".to_string(), builtin_not))));
    env.define("=".to_string(), Atom(Lambda(BuiltIn("=".to_string(), builtin_eq))));
    env.define("eq?".to_string(), Atom(Lambda(BuiltIn("eq?".to_string(), builtin_eq))));
    env.define("equal?".to_string(), Atom(Lambda(BuiltIn("equal?".to_string(), builtin_eq))));
    env.define("length".to_string(), Atom(Lambda(BuiltIn("length".to_string(), builtin_length))));
    env.define("cons".to_string(), Atom(Lambda(BuiltIn("cons".to_string(), builtin_cons))));
    env.define("car".to_string(), Atom(Lambda(BuiltIn("car".to_string(), builtin_car))));
    env.define("cdr".to_string(), Atom(Lambda(BuiltIn("cdr".to_string(), builtin_cdr))));
    env.define("append".to_string(), Atom(Lambda(BuiltIn("append".to_string(), builtin_append))));
    env.define("list".to_string(), Atom(Lambda(BuiltIn("list".to_string(), builtin_list))));
    env.define("list?".to_string(), Atom(Lambda(BuiltIn("list?".to_string(), builtin_is_list))));
    env.define("null?".to_string(), Atom(Lambda(BuiltIn("null?".to_string(), builtin_is_null))));
    env.define("symbol?".to_string(),
               Atom(Lambda(BuiltIn("symbol?".to_string(), builtin_is_symbol))));
    env.define("display".to_string(),
               Atom(Lambda(BuiltIn("display".to_string(), builtin_display))));
    env
}

fn builtin_arithmetic(mut args: Vec<Expr>, env: Env,
                      name: &str,
                      transformer_bi: |BigInt, BigInt| -> BigInt,
                      transformer_br: |BigRational, BigRational| -> BigRational,
                      ) -> SchemerResult<(Option<Expr>, Env)> {
    let mut hasFloats = false;
    for atom in args.iter() {
        match atom {
            &Atom(Float(_)) => hasFloats = true,
            &Atom(Integer(_)) => {},
            val => return Err(format!("{}: invoking with non numeric input: '{}'",
                         name, val.print()))
        }
    }
    let out_val = if hasFloats {
        let mut out_val = match args.shift()
            {
                Some(v) => match v {
                    v @ Atom(Integer(_)) | v @ Atom(Float(_)) => match v.into_float() {
                        Ok(v) => v,
                        Err(e) => return Err(format!("{}: {}", name, e))
                    },
                    _ => return Err(format!("{}: cannot process non-numeric input", name))
                },
                None => return Err(format!("{}: head of add args should be Some()", name))
            };
        for atom in args.move_iter() {
            match atom {
                v @ Atom(Integer(_)) | v @ Atom(Float(_)) =>
                    out_val = match v.into_float() {
                        Ok(v) => transformer_br(out_val, v),
                        Err(e) => return Err(format!("{}: {}", name, e))
                    },
                _ => return Err(format!("{}: invoking with non numeric input", name))
            }
        }
        Atom(Float(out_val))
    } else {
        let mut out_val = match args.shift()
            .expect("head of add args should be Some()") {
                v @ Atom(Integer(_)) | v @ Atom(Float(_)) => match v.into_integer() {
                    Ok(v) => v,
                    Err(e) => return Err(format!("{}", e))
                },
                _ => return Err(format!("{}: cannot process non-numeric input", name))
            };
        for atom in args.move_iter() {
            match atom {
                Atom(Integer(val)) => out_val = transformer_bi(out_val, val),
                _ => return Err(format!("{}: invoking with non numeric input", name))
            }
        }
        Atom(Integer(out_val))
    };
    Ok((Some(out_val), env))
}

fn builtin_add(args: Vec<Expr>, env: Env) -> (Option<Expr>, Env) {
    let name = "+";
    let transformer_bi = |left:BigInt, right: BigInt| left + right;
    let transformer_br = |left:BigRational, right: BigRational| left + right;
    match builtin_arithmetic(args, env, name, transformer_bi, transformer_br) {
        Ok(v) => v,
        Err(e) => fail!("{}: {}", name, e)
    }
}

fn builtin_subtract(args: Vec<Expr>, env: Env) -> (Option<Expr>, Env) {
    let name = "-";
    let transformer_bi = |left:BigInt, right: BigInt| left - right;
    let transformer_br = |left:BigRational, right: BigRational| left - right;
    match builtin_arithmetic(args, env, name, transformer_bi, transformer_br) {
        Ok(v) => v,
        Err(e) => fail!("{}: {}", name, e)
    }
}

fn builtin_multiply(args: Vec<Expr>, env: Env) -> (Option<Expr>, Env) {
    let name = "*";
    let transformer_bi = |left:BigInt, right: BigInt| left * right;
    let transformer_br = |left:BigRational, right: BigRational| left * right;
    match builtin_arithmetic(args, env, name, transformer_bi, transformer_br) {
        Ok(v) => v,
        Err(e) => fail!("{}: {}", name, e)
    }
}

fn builtin_divide(args: Vec<Expr>, env: Env) -> (Option<Expr>, Env) {
    let name = "/";
    let transformer_bi = |left:BigInt, right: BigInt| left / right;
    let transformer_br = |left:BigRational, right: BigRational| left / right;
    match builtin_arithmetic(args, env, name, transformer_bi, transformer_br) {
        Ok(v) => v,
        Err(e) => fail!("{}: {}", name, e)
    }
}

fn builtin_predicate_compare(mut args: Vec<Expr>,
                   env: Env,
                   name: &str,
                   transformer_bi: |BigInt, BigInt| -> bool,
                   transformer_br: |BigRational, BigRational| -> bool,
                   ) -> SchemerResult<(Option<Expr>, Env)> {
    let mut hasFloats = false;
    if args.len() < 2 {
        return Err(format!("{}: too few parameters (must provide at least two)", name));
    }
    for atom in args.iter() {
        match atom {
            &Atom(Float(_)) => hasFloats = true,
            &Atom(Integer(_)) => {},
            val => return Err(format!(
                "{}: invoking with non numeric input: '{}'", name, val.print()))
        }
    }
    let out_val = if hasFloats {
        let mut state = false;
        let mut left = match args.shift() {
            None => return Err(format!("{}: head of less-than args should be Some()", name)),
            Some(v) => match v {
                    left @ Atom(Integer(_)) |
                    left @ Atom(Float(_)) => try!(left.into_float()),
                    _ => return Err(format!("{}: no match", name))
                }
            };
        for atom in args.move_iter() {
            match atom {
                right @ Atom(Integer(_)) | right @ Atom(Float(_)) => {
                    let right = try!(right.into_float());
                    state = transformer_br(left, right.clone());
                    left = right;
                },
                _ => return Err(format!("{}: invoking with non numeric input", name))
            }
        }
        Atom(Boolean(state))
    } else {
        let mut state = false;
        let mut left = match args.shift() {
            None => return Err(format!("{}: head of less-than args should be Some()", name)),
            Some(v) => match v {
                left @ Atom(Integer(_))
                    | left @ Atom(Float(_)) => try!(left.into_integer()),
                _ => return Err(format!("{}: cannot process non-numeric input", name))
            }
        };
        for atom in args.move_iter() {
            match atom {
                Atom(Integer(right)) => {
                    state = transformer_bi(left, right.clone());
                    left = right;
                }
                _ => return Err(format!("{}: invoking with non numeric input", name))
            }
        }
        Atom(Boolean(state))
    };
    Ok((Some(out_val), env))
}

fn builtin_lt(args: Vec<Expr>, env: Env) -> (Option<Expr>, Env) {
    let name = "<";
    let transformer_bi = |left:BigInt, right:BigInt| left < right;
    let transformer_br = |left:BigRational, right:BigRational| left < right;
    match builtin_predicate_compare(args, env, name, transformer_bi,
                          transformer_br) {
        Ok(r) => r,
        Err(e) => fail!("{}", e)
    }
}

fn builtin_gt(args: Vec<Expr>, env: Env) -> (Option<Expr>, Env) {
    let name = ">";
    let transformer_bi = |left:BigInt, right:BigInt| left > right;
    let transformer_br = |left:BigRational, right:BigRational| left > right;
    match builtin_predicate_compare(args, env, name, transformer_bi,
                          transformer_br) {
        Ok(r) => r,
        Err(e) => fail!("{}", e)
    }
}

fn builtin_lte(args: Vec<Expr>, env: Env) -> (Option<Expr>, Env) {
    let name = "<=";
    let transformer_bi = |left:BigInt, right:BigInt| left <= right;
    let transformer_br = |left:BigRational, right:BigRational| left <= right;
    match builtin_predicate_compare(args, env, name, transformer_bi,
                          transformer_br) {
        Ok(r) => r,
        Err(e) => fail!("{}", e)
    }
}

fn builtin_gte(args: Vec<Expr>, env: Env) -> (Option<Expr>, Env) {
    let name = ">=";
    let transformer_bi = |left:BigInt, right:BigInt| left >= right;
    let transformer_br = |left:BigRational, right:BigRational| left >= right;
    match builtin_predicate_compare(args, env, name, transformer_bi,
                          transformer_br) {
        Ok(r) => r,
        Err(e) => fail!("{}", e)
    }
}

fn builtin_not(mut args: Vec<Expr>, env: Env) -> (Option<Expr>, Env) {
    if args.len() != 1 {
        fail!("not: expect a single parameter")
    }
    match args.pop().unwrap() {
        Atom(Boolean(false)) => (Some(Atom(Boolean(true))), env),
        _ => (Some(Atom(Boolean(false))), env),
    }
}
fn builtin_eq(mut args: Vec<Expr>, env: Env) -> (Option<Expr>, Env) {
    if args.len() < 2 {
        fail!("eq: expect at least two parameters")
    }
    let mut state = false;
    let mut left = args.shift().expect("head of eq args should be Some()");
    for right in args.move_iter() {
        state = left == right;
        left = right;
    }
    (Some(Atom(Boolean(state))), env)
}
fn builtin_length(mut args: Vec<Expr>, env: Env) -> (Option<Expr>, Env) {
    if args.len() != 1 {
        fail!("length: expects only a single list parameter")
    }
    match args.shift().expect("head of length args should be Some()") {
        List(items) => {
            println!("length: {}", items.len());
            (Some(Atom(Number::uint(items.len()))), env)
        }
        _ => fail!("length: expecting a list parameter")
    }
}
fn builtin_cons(mut args: Vec<Expr>, env: Env) -> (Option<Expr>, Env) {
    if args.len() != 2 {
        fail!("cons: expects two parameters")
    }
    let cons_result = match Expr::cons(args.shift().unwrap(), args.shift().unwrap()) {
        Ok(r) => r,
        Err(e) => fail!("builtin_cons: failure in Expr::cons(): {}", e)
    };
    (Some(cons_result), env)
}
fn builtin_car(mut args: Vec<Expr>, env: Env) -> (Option<Expr>, Env) {
    if args.len() != 1 {
        fail!("car: expect a single list parameter");
    }
    let (car, _) = match args.pop().unwrap() {
        v @ List(_) => match v.un_cons() {
            Ok(v) => v,
            Err(e) => fail!("car: {}", e)
        },
        _ => fail!("car: expected a list")
    };
    (Some(car), env)
}
fn builtin_cdr(mut args: Vec<Expr>, env: Env) -> (Option<Expr>, Env) {
    if args.len() != 1 {
        fail!("cdr: expect a single list parameter");
    }
    let (_, cdr) = match args.pop().unwrap() {
        v @ List(_) => match v.un_cons() {
            Ok(v) => v,
            Err(e) => fail!("cdr: {}", e)
        },
        _ => fail!("cdr: expected a list")
    };
    (Some(cdr), env)
}
fn builtin_append(mut args: Vec<Expr>, env: Env) -> (Option<Expr>, Env) {
    if args.len() != 2 {
        fail!("append: expect two arguments");
    }
    let out_items = match args.shift().unwrap() {
        List(mut items) => {
            items.push(box args.pop().unwrap());
            items
        },
        _ => fail!("append: expect first arg to be a list")
    };
    (Some(List(out_items)), env)
}
fn builtin_list(args: Vec<Expr>, env: Env) -> (Option<Expr>, Env) {
    (Some(List(args.move_iter().map(|x| box x).collect())), env)
}
fn builtin_is_list(mut args: Vec<Expr>, env: Env) -> (Option<Expr>, Env) {
    if args.len() != 1 {
        fail!("list?: expect a single argument");
    }
    let result = match args.shift().unwrap() {
        List(_) => {
            Atom(Boolean(true))
        },
        _ => Atom(Boolean(false))
    };
    (Some(result), env)
}

fn builtin_is_null(mut args: Vec<Expr>, env: Env) -> (Option<Expr>, Env) {
    if args.len() != 1 {
        fail!("null?: expect a single argument");
    }
    let result = match args.shift().unwrap() {
        List(ref v) if v.len() == 0 => {
            Atom(Boolean(true))
        },
        _ => Atom(Boolean(false))
    };
    (Some(result), env)
}

fn builtin_is_symbol(mut args: Vec<Expr>, env: Env) -> (Option<Expr>, Env) {
    if args.len() != 1 {
        fail!("symbol?: expect a single argument");
    }
    let result = match args.shift().unwrap() {
        Atom(Symbol(_)) => {
            Atom(Boolean(true))
        },
        _ => Atom(Boolean(false))
    };
    (Some(result), env)
}

fn builtin_display(mut args: Vec<Expr>, env: Env) -> (Option<Expr>, Env) {
    if args.len() != 1 {
        fail!("display: expect a single argument");
    }
    println!("{}", args.shift().unwrap().print());
    (None, env)
}
<|endoftext|>


<commit_before>use cookie::Cookie;
use headers::{self, HeaderMapExt};
use http::{Response, StatusCode};

use crate::service::dto;
use crate::service::test::{constants::*, ServiceType, TestService};
use crate::test_name;

fn validate_cookies<T>(response: &Response<T>) {
	let cookies: Vec<Cookie> = response
		.headers()
		.get_all(http::header::SET_COOKIE)
		.iter()
		.map(|c| Cookie::parse(c.to_str().unwrap()).unwrap())
		.collect();
	assert!(cookies.iter().any(|c| c.name() == dto::COOKIE_SESSION));
	assert!(cookies.iter().any(|c| c.name() == dto::COOKIE_USERNAME));
	assert!(cookies.iter().any(|c| c.name() == dto::COOKIE_ADMIN));
}

fn validate_no_cookies<T>(response: &Response<T>) {
	let cookies: Vec<Cookie> = response
		.headers()
		.get_all(http::header::SET_COOKIE)
		.iter()
		.map(|c| Cookie::parse(c.to_str().unwrap()).unwrap())
		.collect();
	assert!(!cookies.iter().any(|c| c.name() == dto::COOKIE_SESSION));
	assert!(!cookies.iter().any(|c| c.name() == dto::COOKIE_USERNAME));
	assert!(!cookies.iter().any(|c| c.name() == dto::COOKIE_ADMIN));
}

#[test]
fn test_login_rejects_bad_username() {
	let mut service = ServiceType::new(&test_name!());
	service.complete_initial_setup();

	let request = service.request_builder().login("garbage", TEST_PASSWORD);
	let response = service.fetch(&request);
	assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
}

#[test]
fn test_login_rejects_bad_password() {
	let mut service = ServiceType::new(&test_name!());
	service.complete_initial_setup();

	let request = service.request_builder().login(TEST_USERNAME, "garbage");
	let response = service.fetch(&request);
	assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
}

#[test]
fn test_login_golden_path() {
	let mut service = ServiceType::new(&test_name!());
	service.complete_initial_setup();

	let request = service
		.request_builder()
		.login(TEST_USERNAME, TEST_PASSWORD);
	let response = service.fetch(&request);
	assert_eq!(response.status(), StatusCode::OK);

	validate_cookies(&response);
}

#[test]
fn test_requests_without_auth_header_do_not_set_cookies() {
	let mut service = ServiceType::new(&test_name!());
	service.complete_initial_setup();
	service.login();

	let request = service.request_builder().random();
	let response = service.fetch(&request);
	assert_eq!(response.status(), StatusCode::OK);

	validate_no_cookies(&response);
}

#[test]
fn test_authentication_via_http_header_rejects_bad_username() {
	let mut service = ServiceType::new(&test_name!());
	service.complete_initial_setup();

	let mut request = service.request_builder().random();
	let basic = headers::Authorization::basic("garbage", TEST_PASSWORD);
	request.headers_mut().typed_insert(basic);

	let response = service.fetch(&request);
	assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
}

#[test]
fn test_authentication_via_http_header_rejects_bad_password() {
	let mut service = ServiceType::new(&test_name!());
	service.complete_initial_setup();

	let mut request = service.request_builder().random();
	let basic = headers::Authorization::basic(TEST_PASSWORD, "garbage");
	request.headers_mut().typed_insert(basic);

	let response = service.fetch(&request);
	assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
}

#[test]
fn test_authentication_via_http_header_golden_path() {
	let mut service = ServiceType::new(&test_name!());
	service.complete_initial_setup();

	let mut request = service.request_builder().random();
	let basic = headers::Authorization::basic(TEST_USERNAME, TEST_PASSWORD);
	request.headers_mut().typed_insert(basic);

	let response = service.fetch(&request);
	assert_eq!(response.status(), StatusCode::OK);

	validate_cookies(&response);
}
<commit_msg>Validate that session cookie looks somewhat encrypted<commit_after>use cookie::Cookie;
use headers::{self, HeaderMapExt};
use http::{Response, StatusCode};

use crate::service::dto;
use crate::service::test::{constants::*, ServiceType, TestService};
use crate::test_name;

fn validate_cookies<T>(response: &Response<T>) {
	let cookies: Vec<Cookie> = response
		.headers()
		.get_all(http::header::SET_COOKIE)
		.iter()
		.map(|c| Cookie::parse(c.to_str().unwrap()).unwrap())
		.collect();
	let session = cookies
		.iter()
		.find_map(|c| {
			if c.name() == dto::COOKIE_SESSION {
				Some(c.value())
			} else {
				None
			}
		})
		.unwrap();
	assert_ne!(session, TEST_USERNAME);
	assert_ne!(session, TEST_USERNAME_ADMIN);
	assert!(cookies.iter().any(|c| c.name() == dto::COOKIE_USERNAME));
	assert!(cookies.iter().any(|c| c.name() == dto::COOKIE_ADMIN));
}

fn validate_no_cookies<T>(response: &Response<T>) {
	let cookies: Vec<Cookie> = response
		.headers()
		.get_all(http::header::SET_COOKIE)
		.iter()
		.map(|c| Cookie::parse(c.to_str().unwrap()).unwrap())
		.collect();
	assert!(!cookies.iter().any(|c| c.name() == dto::COOKIE_SESSION));
	assert!(!cookies.iter().any(|c| c.name() == dto::COOKIE_USERNAME));
	assert!(!cookies.iter().any(|c| c.name() == dto::COOKIE_ADMIN));
}

#[test]
fn test_login_rejects_bad_username() {
	let mut service = ServiceType::new(&test_name!());
	service.complete_initial_setup();

	let request = service.request_builder().login("garbage", TEST_PASSWORD);
	let response = service.fetch(&request);
	assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
}

#[test]
fn test_login_rejects_bad_password() {
	let mut service = ServiceType::new(&test_name!());
	service.complete_initial_setup();

	let request = service.request_builder().login(TEST_USERNAME, "garbage");
	let response = service.fetch(&request);
	assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
}

#[test]
fn test_login_golden_path() {
	let mut service = ServiceType::new(&test_name!());
	service.complete_initial_setup();

	let request = service
		.request_builder()
		.login(TEST_USERNAME, TEST_PASSWORD);
	let response = service.fetch(&request);
	assert_eq!(response.status(), StatusCode::OK);

	validate_cookies(&response);
}

#[test]
fn test_requests_without_auth_header_do_not_set_cookies() {
	let mut service = ServiceType::new(&test_name!());
	service.complete_initial_setup();
	service.login();

	let request = service.request_builder().random();
	let response = service.fetch(&request);
	assert_eq!(response.status(), StatusCode::OK);

	validate_no_cookies(&response);
}

#[test]
fn test_authentication_via_http_header_rejects_bad_username() {
	let mut service = ServiceType::new(&test_name!());
	service.complete_initial_setup();

	let mut request = service.request_builder().random();
	let basic = headers::Authorization::basic("garbage", TEST_PASSWORD);
	request.headers_mut().typed_insert(basic);

	let response = service.fetch(&request);
	assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
}

#[test]
fn test_authentication_via_http_header_rejects_bad_password() {
	let mut service = ServiceType::new(&test_name!());
	service.complete_initial_setup();

	let mut request = service.request_builder().random();
	let basic = headers::Authorization::basic(TEST_PASSWORD, "garbage");
	request.headers_mut().typed_insert(basic);

	let response = service.fetch(&request);
	assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
}

#[test]
fn test_authentication_via_http_header_golden_path() {
	let mut service = ServiceType::new(&test_name!());
	service.complete_initial_setup();

	let mut request = service.request_builder().random();
	let basic = headers::Authorization::basic(TEST_USERNAME, TEST_PASSWORD);
	request.headers_mut().typed_insert(basic);

	let response = service.fetch(&request);
	assert_eq!(response.status(), StatusCode::OK);

	validate_cookies(&response);
}
<|endoftext|>


<commit_before>use fixedbitset::FixedBitSet;

use crate::core::{Card, Hand};
pub enum Round {
    Starting,
    Preflop,
    Flop,
    Turn,
    River,
    Showdown,
}

pub struct RoundData {
    pub player_active: FixedBitSet,
    // The minimum allowed bet.
    pub min_bet: usize,
    // The value to be called.
    pub bet: usize,
    // How much each player has put in so far.
    pub player_bet: Vec<usize>,
    // The number of times the player has put in money.
    pub bet_count: Vec<usize>,
    // The index of the next player to act.
    pub to_act_idx: usize,
}

impl RoundData {
    pub fn advance(&mut self) {
        loop {
            self.to_act_idx = (self.to_act_idx + 1) % self.player_active.len();
            if self.player_active.count_ones(..) == 0
                || self.player_active.contains(self.to_act_idx)
            {
                break;
            }
        }
    }
}

pub struct GameState {
    pub num_players: usize,
    pub player_active: FixedBitSet,
    pub stacks: Vec<usize>,
    pub big_blind: usize,
    pub small_blind: usize,
    pub hands: Vec<Hand>,
    pub dealer_idx: usize,
    pub last_raise: usize,
    pub round: Round,
    pub round_data: Vec<RoundData>,
    pub board: Vec<Card>,
}

impl GameState {
    pub fn new(stacks: Vec<usize>, big_blind: usize, small_blind: usize) -> Self {
        let num_players = stacks.len();

        // Everyone's active right now
        let mut active_mask = FixedBitSet::with_capacity(num_players);
        active_mask.set_range(.., true);
        GameState {
            num_players,
            stacks,
            big_blind,
            small_blind,
            player_active: active_mask,
            dealer_idx: 0,
            last_raise: 0,
            hands: vec![Hand::default(); num_players],
            round: Round::Starting,
            board: vec![],
            round_data: vec![],
        }
    }

    pub fn advance_round(&mut self) {
        match self.round {
            Round::Starting => self.advance_preflop(),
            Round::Preflop => self.advance_flop(),
            _ => todo!(),
        }
    }

    fn advance_preflop(&mut self) {
        self.round = Round::Preflop;
        self.round_data.push(self.round_data());
        self.do_bet(self.small_blind, true);
        self.do_bet(self.big_blind, true);
    }

    fn advance_flop(&mut self) {
        self.round = Round::Flop;
        self.round_data.push(self.round_data())
    }

    fn round_data(&self) -> RoundData {
        RoundData {
            player_bet: vec![0; self.num_players],
            bet_count: vec![0; self.num_players],
            bet: 0,
            min_bet: self.big_blind,
            player_active: self.player_active.clone(),
            to_act_idx: self.left_of_dealer(),
        }
    }

    pub fn do_bet(&mut self, ammount: usize, is_forced: bool) -> Option<usize> {
        if let Some(round_idx) = self.round_index() {
            // Grab the current round data.
            let rd = &mut self.round_data[round_idx];

            // Which player is next to act
            let idx = rd.to_act_idx;
            // Can't bet what's not there
            let bet_ammount = ammount.min(self.stacks[idx]);
            // Take the money out.
            self.stacks[idx] -= bet_ammount;
            // If they put money into the pot then they are done this turn.
            rd.player_active.set(idx, is_forced);

            rd.player_bet[idx] += bet_ammount;
            rd.bet_count[idx] += 1;

            rd.bet = rd.bet.max(bet_ammount);

            // We're out and can't continue.
            if self.stacks[idx] == 0 {
                self.player_active.set(idx, false);
                rd.player_active.set(idx, false);
            }

            // Advance the next to act.
            rd.advance();

            Some(bet_ammount)
        } else {
            None
        }
    }

    pub fn left_of_dealer(&self) -> usize {
        (self.dealer_idx + 1) % self.num_players
    }

    fn round_index(&self) -> Option<usize> {
        match self.round {
            Round::Preflop => Some(0),
            Round::Flop => Some(1),
            Round::Turn => Some(2),
            Round::River => Some(3),
            _ => None,
        }
    }
}
<commit_msg>Total pot and bet/min_bet<commit_after>use fixedbitset::FixedBitSet;

use crate::core::{Card, Hand};
pub enum Round {
    Starting,
    Preflop,
    Flop,
    Turn,
    River,
    Showdown,
}

pub struct RoundData {
    pub player_active: FixedBitSet,
    // The minimum allowed bet.
    pub min_bet: usize,
    // The value to be called.
    pub bet: usize,
    // How much each player has put in so far.
    pub player_bet: Vec<usize>,
    // The number of times the player has put in money.
    pub bet_count: Vec<usize>,
    // The index of the next player to act.
    pub to_act_idx: usize,
}

impl RoundData {
    pub fn advance(&mut self) {
        loop {
            self.to_act_idx = (self.to_act_idx + 1) % self.player_active.len();
            if self.player_active.count_ones(..) == 0
                || self.player_active.contains(self.to_act_idx)
            {
                break;
            }
        }
    }
}

pub struct GameState {
    pub num_players: usize,
    pub player_active: FixedBitSet,
    total_pot: usize,
    pub stacks: Vec<usize>,
    pub big_blind: usize,
    pub small_blind: usize,
    pub hands: Vec<Hand>,
    pub dealer_idx: usize,
    pub last_raise: usize,
    pub round: Round,
    pub round_data: Vec<RoundData>,
    pub board: Vec<Card>,
}

impl GameState {
    pub fn new(stacks: Vec<usize>, big_blind: usize, small_blind: usize) -> Self {
        let num_players = stacks.len();

        // Everyone's active right now
        let mut active_mask = FixedBitSet::with_capacity(num_players);
        active_mask.set_range(.., true);
        GameState {
            num_players,
            stacks,
            big_blind,
            small_blind,
            player_active: active_mask,
            dealer_idx: 0,
            last_raise: 0,
            total_pot: 0,
            hands: vec![Hand::default(); num_players],
            round: Round::Starting,
            board: vec![],
            round_data: vec![],
        }
    }

    pub fn advance_round(&mut self) {
        match self.round {
            Round::Starting => self.advance_preflop(),
            Round::Preflop => self.advance_flop(),
            _ => todo!(),
        }
    }

    fn advance_preflop(&mut self) {
        self.round = Round::Preflop;
        self.round_data.push(self.round_data());
        self.do_bet(self.small_blind, true);
        self.do_bet(self.big_blind, true);
    }

    fn advance_flop(&mut self) {
        self.round = Round::Flop;
        self.round_data.push(self.round_data())
    }

    fn round_data(&self) -> RoundData {
        RoundData {
            player_bet: vec![0; self.num_players],
            bet_count: vec![0; self.num_players],
            bet: 0,
            min_bet: self.big_blind,
            player_active: self.player_active.clone(),
            to_act_idx: self.left_of_dealer(),
        }
    }

    pub fn do_bet(&mut self, ammount: usize, is_forced: bool) -> Option<usize> {
        if let Some(round_idx) = self.round_index() {
            // Grab the current round data.
            let rd = &mut self.round_data[round_idx];

            // Which player is next to act
            let idx = rd.to_act_idx;
            // Can't bet what's not there
            let bet_ammount = ammount.min(self.stacks[idx]);
            // Take the money out.
            self.stacks[idx] -= bet_ammount;
            // If they put money into the pot then they are done this turn.
            rd.player_active.set(idx, is_forced);

            rd.player_bet[idx] += bet_ammount;
            rd.bet_count[idx] += 1;

            // The amount to be called is
            // the maximum anyone has wagered.
            rd.bet = rd.bet.max(rd.player_bet[idx]);

            // Keep the maximum bet ammount. Anything
            // smaller should be due to going all in.
            rd.min_bet = rd.min_bet.max(bet_ammount);

            self.total_pot += bet_ammount;

            // We're out and can't continue.
            if self.stacks[idx] == 0 {
                self.player_active.set(idx, false);
                rd.player_active.set(idx, false);
            }

            // Advance the next to act.
            rd.advance();

            Some(bet_ammount)
        } else {
            None
        }
    }

    pub fn left_of_dealer(&self) -> usize {
        (self.dealer_idx + 1) % self.num_players
    }

    fn round_index(&self) -> Option<usize> {
        match self.round {
            Round::Preflop => Some(0),
            Round::Flop => Some(1),
            Round::Turn => Some(2),
            Round::River => Some(3),
            _ => None,
        }
    }
}
<|endoftext|>


<commit_before>use bvh::*;
use bounding_hierarchy::BHShape;
use std::collections::HashSet;

// TODO Consider: Instead of getting the scene's shapes passed, let leaf nodes store an AABB
// that is updated from the outside, perhaps by passing not only the indices of the changed
// shapes, but also their new AABBs into optimize().

impl BVH {
    /// Optimizes the `BVH` by batch-reorganizing updated nodes.
    /// Based on https://github.com/jeske/SimpleScene/blob/master/SimpleScene/Util/ssBVH/ssBVH.cs
    ///
    /// Needs all the scene's shapes, plus the indices of the shapes that were updated.
    ///
    pub fn optimize<Shape: BHShape>(&mut self,
                                    refit_shape_indices: &HashSet<usize>,
                                    shapes: &[Shape]) {
        let mut refit_node_indices: HashSet<usize> = refit_shape_indices.iter()
            .map(|x| shapes[*x].bh_node_index())
            .collect();

        // As long as we have refit nodes left, take the list of refit nodes
        // with the highest depth (sweep nodes) and try to rotate them all
        while refit_node_indices.len() > 0 {
            let mut max_depth = 0;
            let mut sweep_node_indices: Vec<usize> = Vec::new();

            // Find max_depth and sweep_node_indices in one iteration
            for refit_node_index in refit_node_indices.iter() {
                let depth = match self.nodes[*refit_node_index] {
                    BVHNode::Node { depth, .. } => depth,
                    BVHNode::Leaf { depth, .. } => depth,
                    BVHNode::Dummy => panic!("Dummy node found during BVH optimization!"),
                };

                if depth > max_depth {
                    max_depth = depth;
                    sweep_node_indices.clear();
                    sweep_node_indices.push(*refit_node_index);
                } else if depth == max_depth {
                    sweep_node_indices.push(*refit_node_index);
                }
            }

            // Try to find a useful tree rotation with all nodes previously found
            for sweep_node_index in sweep_node_indices.iter() {
                // This node does not need to be checked again
                refit_node_indices.remove(&sweep_node_index);

                let new_refit_node_index = self.try_rotate(*sweep_node_index, shapes);

                // Instead of finding a useful tree rotation, we found another node
                // that we should check, so we add its index to the refit_node_indices.
                if let Some(index) = new_refit_node_index {
                    refit_node_indices.insert(index);
                }
            }
        }
    }

    /// Checks if there is a way to rotate a child and a grandchild (or two grandchildren) of
    /// the given node (specified by `node_index`) that would improve the `BVH`.
    /// If there is, the best rotation found is performed.
    /// Relies on the children and transitive children of the given node having correct AABBs.
    ///
    /// Returns Some(index_of_node) if a new node was found that should be used for optimization.
    ///
    fn try_rotate<Shape: BHShape>(&mut self, node_index: usize, shapes: &[Shape]) -> Option<usize> {
        let mut nodes = &mut self.nodes;

        let mut node_clone = nodes[node_index].clone();

        // Contains the surface area that would result from applying the currently favored rotation.
        // The rotation with the smallest SA will be applied in the end.
        // The value is calculated by child_l_aabb.surface_area() + child_r_aabb.surface_area()
        let mut best_SA = 0f32;

        // TODO Re-implement without mutability
        let mut parent_index: usize = 0;

        // If this node is not a grandparent, update the AABB,
        // queue the parent for refitting, and bail out.
        // If it is a grandparent, calculate the current best_SA.
        let (child_l, child_l_aabb, child_r, child_r_aabb) = match node_clone {
            BVHNode::Node { parent, child_l, child_r, child_l_aabb, child_r_aabb, .. } => {
                if let BVHNode::Leaf { shape, .. } = nodes[child_l] {
                    let shape_l_index = shape;
                    if let BVHNode::Leaf { shape, .. } = nodes[child_r] {
                        let shape_r_index = shape;

                        // Update the AABBs saved for the children
                        // since at least one of them changed
                        let mut node = &mut nodes[node_index];
                        match node {
                            &mut BVHNode::Node { ref mut child_l_aabb,
                                                 ref mut child_r_aabb,
                                                 .. } => {
                                *child_l_aabb = shapes[shape_l_index].aabb();
                                *child_r_aabb = shapes[shape_r_index].aabb();
                            }
                            _ => unreachable!(),
                        }

                        return Some(parent);
                    }
                }

                parent_index = parent;
                best_SA = child_l_aabb.surface_area() + child_r_aabb.surface_area();
                (child_l, child_l_aabb, child_r, child_r_aabb)
            }
            BVHNode::Leaf { parent, .. } => {
                return Some(parent);
            }
            BVHNode::Dummy => panic!("Dummy node found during BVH optimization!"),
        };

        // Stores the Rotation that would result in the surface area best_SA,
        // thus being the favored rotation that will be executed after considering all rotations.
        let mut best_rotation: Option<(usize, usize)> = None;

        // TODO Get indices and AABBs of all grandchildren

        macro_rules! consider_rotation {
            ($a:expr, $b:expr) => {
                // TODO Calculate surface area that would result from rotating the given nodes.
                let surface_area = 100f32;

                if surface_area < best_SA {
                    best_SA = surface_area;
                    best_rotation = Some(($a, $b));
                }
                unimplemented!();
            };
        }

        // Child to grandchild rotations
        consider_rotation!(child_l, child_rl);
        consider_rotation!(child_l, child_rr);
        consider_rotation!(child_r, child_ll);
        consider_rotation!(child_r, child_lr);
        // Grandchild to grandchild rotations
        consider_rotation!(child_ll, child_rl);
        consider_rotation!(child_ll, child_rr);

        let new_refit_node_index = if parent_index > 0 {
            Some(parent_index)
        } else {
            None
        };

        if best_rotation == None {
            // TODO Recalculate this node's AABBs (child_l_aabb, child_r_aabb)
            // according to the children nodes' AABBs.

            // Even with no rotation being useful for this node, a parent node's rotation
            // could be beneficial, so queue the parent sometimes.
            // TODO Return None most of the time, randomly
            // (see https://github.com/jeske/SimpleScene/blob/master/SimpleScene/Util/ssBVH/ssBVH_Node.cs#L307)
            new_refit_node_index
        } else {
            // TODO Perform rotation using rotate()

            // TODO Update all changed node AABBs

            // Return parent node's index for upcoming refitting,
            // since this node just changed its AABB
            new_refit_node_index
        }
    }

    /// Switch two nodes by rewiring the involved indices (not by moving them in the nodes slice).
    fn rotate(&mut self, node_a_index: usize, node_b_index: usize) {
        let mut nodes = &mut self.nodes;

        macro_rules! should_not_happen {
            () => ( panic!("While rotating BVH nodes, something unexpected happened."); );
            ($s:expr) => ( panic!("While rotating BVH nodes, something unexpected happened: {}", $s); );
        }

        #[allow(dead_code)] // The compiler falsely detects dead code here
        fn get_parent_index(nodes: &Vec<BVHNode>, node_index: usize) -> usize {
            let node = &nodes[node_index];

            match *node {
                BVHNode::Node { parent, .. } |
                BVHNode::Leaf { parent, .. } => parent,
                _ => should_not_happen!(),
            }
        }

        // Get parent indices
        let node_a_parent_index = get_parent_index(nodes, node_a_index);
        let node_b_parent_index = get_parent_index(nodes, node_b_index);

        #[allow(dead_code)] // The compiler falsely detects dead code here
        fn get_is_left_child(nodes: &Vec<BVHNode>,
                             node_index: usize,
                             node_parent_index: usize)
                             -> bool {
            let node_parent = &nodes[node_parent_index];

            match *node_parent {
                BVHNode::Node { child_l, .. } => child_l == node_index,
                _ => should_not_happen!(),
            }
        }

        // Get info about the nodes being a left or right child
        let node_a_is_left_child = get_is_left_child(nodes, node_a_index, node_a_parent_index);
        let node_b_is_left_child = get_is_left_child(nodes, node_b_index, node_b_parent_index);

        #[allow(dead_code)] // The compiler falsely detects dead code here
        fn connect_nodes(nodes: &mut Vec<BVHNode>,
                         child_index: usize,
                         parent_index: usize,
                         left_child: bool) {
            // Set parent's child and get its depth
            let parent_depth = {
                let mut parent = &mut nodes[parent_index];
                match parent {
                    &mut BVHNode::Node { ref mut child_l, ref mut child_r, depth, .. } => {
                        if left_child {
                            *child_l = child_index;
                        } else {
                            *child_r = child_index;
                        }
                        depth
                    }
                    _ => should_not_happen!(),
                }
            };

            // Set child's parent and depth
            {
                let mut child = &mut nodes[child_index];
                match child {
                    &mut BVHNode::Node { ref mut parent, ref mut depth, .. } |
                    &mut BVHNode::Leaf { ref mut parent, ref mut depth, .. } => {
                        *parent = parent_index;
                        *depth = parent_depth + 1;
                    }
                    _ => should_not_happen!(),
                };
            }
        }

        // Perform the switch
        connect_nodes(nodes,
                      node_a_index,
                      node_b_parent_index,
                      node_b_is_left_child);
        connect_nodes(nodes,
                      node_b_index,
                      node_a_parent_index,
                      node_a_is_left_child);
    }
}

#[cfg(test)]
pub mod tests {
    use bvh::BVH;

    #[test]
    /// Tests if the optimize function tries to change a fresh BVH even though it shouldn't
    fn test_optimizing_new_bvh() {
        let (mut shapes, mut bvh) = build_some_bh::<BVH>();

        let refit_shape_indices: HashSet<usize> = (0..shapes.len()).collect();
        bvh.optimize(&refit_shape_indices, &mut shapes);
    }

    // TODO Add tests for:
    // * correct parent
    // * correct depth
    // * correct BVH after optimizing
    // * correct parent and depth after optimizing
}
<commit_msg>Add NodeData struct<commit_after>use bvh::*;
use bounding_hierarchy::BHShape;
use std::collections::HashSet;

// TODO Consider: Instead of getting the scene's shapes passed, let leaf nodes store an AABB
// that is updated from the outside, perhaps by passing not only the indices of the changed
// shapes, but also their new AABBs into optimize().

impl BVH {
    /// Optimizes the `BVH` by batch-reorganizing updated nodes.
    /// Based on https://github.com/jeske/SimpleScene/blob/master/SimpleScene/Util/ssBVH/ssBVH.cs
    ///
    /// Needs all the scene's shapes, plus the indices of the shapes that were updated.
    ///
    pub fn optimize<Shape: BHShape>(&mut self,
                                    refit_shape_indices: &HashSet<usize>,
                                    shapes: &[Shape]) {
        let mut refit_node_indices: HashSet<usize> = refit_shape_indices.iter()
            .map(|x| shapes[*x].bh_node_index())
            .collect();

        // As long as we have refit nodes left, take the list of refit nodes
        // with the highest depth (sweep nodes) and try to rotate them all
        while refit_node_indices.len() > 0 {
            let mut max_depth = 0;
            let mut sweep_node_indices: Vec<usize> = Vec::new();

            // Find max_depth and sweep_node_indices in one iteration
            for refit_node_index in refit_node_indices.iter() {
                let depth = match self.nodes[*refit_node_index] {
                    BVHNode::Node { depth, .. } => depth,
                    BVHNode::Leaf { depth, .. } => depth,
                    BVHNode::Dummy => panic!("Dummy node found during BVH optimization!"),
                };

                if depth > max_depth {
                    max_depth = depth;
                    sweep_node_indices.clear();
                    sweep_node_indices.push(*refit_node_index);
                } else if depth == max_depth {
                    sweep_node_indices.push(*refit_node_index);
                }
            }

            // Try to find a useful tree rotation with all nodes previously found
            for sweep_node_index in sweep_node_indices.iter() {
                // This node does not need to be checked again
                refit_node_indices.remove(&sweep_node_index);

                let new_refit_node_index = self.try_rotate(*sweep_node_index, shapes);

                // Instead of finding a useful tree rotation, we found another node
                // that we should check, so we add its index to the refit_node_indices.
                if let Some(index) = new_refit_node_index {
                    refit_node_indices.insert(index);
                }
            }
        }
    }

    /// Checks if there is a way to rotate a child and a grandchild (or two grandchildren) of
    /// the given node (specified by `node_index`) that would improve the `BVH`.
    /// If there is, the best rotation found is performed.
    /// Relies on the children and transitive children of the given node having correct AABBs.
    ///
    /// Returns Some(index_of_node) if a new node was found that should be used for optimization.
    ///
    fn try_rotate<Shape: BHShape>(&mut self, node_index: usize, shapes: &[Shape]) -> Option<usize> {
        let mut nodes = &mut self.nodes;

        let mut node_clone = nodes[node_index].clone();

        // Contains the surface area that would result from applying the currently favored rotation.
        // The rotation with the smallest SA will be applied in the end.
        // The value is calculated by child_l_aabb.surface_area() + child_r_aabb.surface_area()
        let mut best_SA = 0f32;

        // TODO Re-implement without mutability
        let mut parent_index: usize = 0;

        struct NodeData {
            index: usize,
            aabb: AABB,
        }

        // If this node is not a grandparent, update the AABB,
        // queue the parent for refitting, and bail out.
        // If it is a grandparent, calculate the current best_SA.
        let (child_l, child_r) = match node_clone {
            BVHNode::Node { parent, child_l, child_r, child_l_aabb, child_r_aabb, .. } => {
                if let BVHNode::Leaf { shape, .. } = nodes[child_l] {
                    let shape_l_index = shape;
                    if let BVHNode::Leaf { shape, .. } = nodes[child_r] {
                        let shape_r_index = shape;

                        // Update the AABBs saved for the children
                        // since at least one of them changed
                        let mut node = &mut nodes[node_index];
                        match node {
                            &mut BVHNode::Node { ref mut child_l_aabb,
                                                 ref mut child_r_aabb,
                                                 .. } => {
                                *child_l_aabb = shapes[shape_l_index].aabb();
                                *child_r_aabb = shapes[shape_r_index].aabb();
                            }
                            _ => unreachable!(),
                        }

                        return Some(parent);
                    }
                }

                parent_index = parent;
                best_SA = child_l_aabb.surface_area() + child_r_aabb.surface_area();
                (NodeData{index: child_l, aabb: child_l_aabb},
                 NodeData{index:child_r, aabb:child_r_aabb})
            }
            BVHNode::Leaf { parent, .. } => {
                return Some(parent);
            }
            BVHNode::Dummy => panic!("Dummy node found during BVH optimization!"),
        };

        // Stores the Rotation that would result in the surface area best_SA,
        // thus being the favored rotation that will be executed after considering all rotations.
        let mut best_rotation: Option<(usize, usize)> = None;

        // TODO Get indices and AABBs of all grandchildren

        macro_rules! consider_rotation {
            ($a:expr, $b:expr) => {
                // TODO Calculate surface area that would result from rotating the given nodes.
                let surface_area = 100f32;

                if surface_area < best_SA {
                    best_SA = surface_area;
                    best_rotation = Some(($a.index, $b.index));
                }
                unimplemented!();
            };
        }

        // Child to grandchild rotations
        consider_rotation!(child_l, child_rl);
        consider_rotation!(child_l, child_rr);
        consider_rotation!(child_r, child_ll);
        consider_rotation!(child_r, child_lr);
        // Grandchild to grandchild rotations
        consider_rotation!(child_ll, child_rl);
        consider_rotation!(child_ll, child_rr);

        let new_refit_node_index = if parent_index > 0 {
            Some(parent_index)
        } else {
            None
        };

        if best_rotation == None {
            // TODO Recalculate this node's AABBs (child_l_aabb, child_r_aabb)
            // according to the children nodes' AABBs.

            // Even with no rotation being useful for this node, a parent node's rotation
            // could be beneficial, so queue the parent sometimes.
            // TODO Return None most of the time, randomly
            // (see https://github.com/jeske/SimpleScene/blob/master/SimpleScene/Util/ssBVH/ssBVH_Node.cs#L307)
            new_refit_node_index
        } else {
            // TODO Perform rotation using rotate()

            // TODO Update all changed node AABBs

            // Return parent node's index for upcoming refitting,
            // since this node just changed its AABB
            new_refit_node_index
        }
    }

    /// Switch two nodes by rewiring the involved indices (not by moving them in the nodes slice).
    fn rotate(&mut self, node_a_index: usize, node_b_index: usize) {
        let mut nodes = &mut self.nodes;

        macro_rules! should_not_happen {
            () => ( panic!("While rotating BVH nodes, something unexpected happened."); );
            ($s:expr) => ( panic!("While rotating BVH nodes, something unexpected happened: {}", $s); );
        }

        #[allow(dead_code)] // The compiler falsely detects dead code here
        fn get_parent_index(nodes: &Vec<BVHNode>, node_index: usize) -> usize {
            let node = &nodes[node_index];

            match *node {
                BVHNode::Node { parent, .. } |
                BVHNode::Leaf { parent, .. } => parent,
                _ => should_not_happen!(),
            }
        }

        // Get parent indices
        let node_a_parent_index = get_parent_index(nodes, node_a_index);
        let node_b_parent_index = get_parent_index(nodes, node_b_index);

        #[allow(dead_code)] // The compiler falsely detects dead code here
        fn get_is_left_child(nodes: &Vec<BVHNode>,
                             node_index: usize,
                             node_parent_index: usize)
                             -> bool {
            let node_parent = &nodes[node_parent_index];

            match *node_parent {
                BVHNode::Node { child_l, .. } => child_l == node_index,
                _ => should_not_happen!(),
            }
        }

        // Get info about the nodes being a left or right child
        let node_a_is_left_child = get_is_left_child(nodes, node_a_index, node_a_parent_index);
        let node_b_is_left_child = get_is_left_child(nodes, node_b_index, node_b_parent_index);

        #[allow(dead_code)] // The compiler falsely detects dead code here
        fn connect_nodes(nodes: &mut Vec<BVHNode>,
                         child_index: usize,
                         parent_index: usize,
                         left_child: bool) {
            // Set parent's child and get its depth
            let parent_depth = {
                let mut parent = &mut nodes[parent_index];
                match parent {
                    &mut BVHNode::Node { ref mut child_l, ref mut child_r, depth, .. } => {
                        if left_child {
                            *child_l = child_index;
                        } else {
                            *child_r = child_index;
                        }
                        depth
                    }
                    _ => should_not_happen!(),
                }
            };

            // Set child's parent and depth
            {
                let mut child = &mut nodes[child_index];
                match child {
                    &mut BVHNode::Node { ref mut parent, ref mut depth, .. } |
                    &mut BVHNode::Leaf { ref mut parent, ref mut depth, .. } => {
                        *parent = parent_index;
                        *depth = parent_depth + 1;
                    }
                    _ => should_not_happen!(),
                };
            }
        }

        // Perform the switch
        connect_nodes(nodes,
                      node_a_index,
                      node_b_parent_index,
                      node_b_is_left_child);
        connect_nodes(nodes,
                      node_b_index,
                      node_a_parent_index,
                      node_a_is_left_child);
    }
}

#[cfg(test)]
pub mod tests {
    use bvh::BVH;

    #[test]
    /// Tests if the optimize function tries to change a fresh BVH even though it shouldn't
    fn test_optimizing_new_bvh() {
        let (mut shapes, mut bvh) = build_some_bh::<BVH>();

        let refit_shape_indices: HashSet<usize> = (0..shapes.len()).collect();
        bvh.optimize(&refit_shape_indices, &mut shapes);
    }

    // TODO Add tests for:
    // * correct parent
    // * correct depth
    // * correct BVH after optimizing
    // * correct parent and depth after optimizing
}
<|endoftext|>


<commit_before>// * This file is part of the uutils coreutils package.
// *
// * For the full copyright and license information, please view the LICENSE file
// * that was distributed with this source code.

// spell-checker:ignore parenb parodd cmspar hupcl cstopb cread clocal crtscts CSIZE
// spell-checker:ignore ignbrk brkint ignpar parmrk inpck istrip inlcr igncr icrnl ixoff ixon iuclc ixany imaxbel iutf
// spell-checker:ignore opost olcuc ocrnl onlcr onocr onlret ofill ofdel nldly crdly tabdly bsdly vtdly ffdly
// spell-checker:ignore isig icanon iexten echoe crterase echok echonl noflsh xcase tostop echoprt prterase echoctl ctlecho echoke crtkill flusho extproc

use crate::Flag;
use nix::sys::termios::{ControlFlags as C, InputFlags as I, LocalFlags as L, OutputFlags as O};

pub const CONTROL_FLAGS: [Flag<C>; 12] = [
    Flag::new("parenb", C::PARENB),
    Flag::new("parodd", C::PARODD),
    Flag::new("cmspar", C::CMSPAR),
    Flag::new("cs5", C::CS5).group(C::CSIZE),
    Flag::new("cs6", C::CS6).group(C::CSIZE),
    Flag::new("cs7", C::CS7).group(C::CSIZE),
    Flag::new("cs8", C::CS8).group(C::CSIZE).sane(),
    Flag::new("hupcl", C::HUPCL).sane(),
    Flag::new("cstopb", C::CSTOPB),
    Flag::new("cread", C::CREAD).sane(),
    Flag::new("clocal", C::CLOCAL),
    Flag::new("crtscts", C::CRTSCTS),
];

pub const INPUT_FLAGS: [Flag<I>; 15] = [
    Flag::new("ignbrk", I::IGNBRK),
    Flag::new("brkint", I::BRKINT).sane(),
    Flag::new("ignpar", I::IGNPAR),
    Flag::new("parmrk", I::PARMRK),
    Flag::new("inpck", I::INPCK),
    Flag::new("istrip", I::ISTRIP),
    Flag::new("inlcr", I::INLCR),
    Flag::new("igncr", I::IGNCR),
    Flag::new("icrnl", I::ICRNL).sane(),
    Flag::new("ixoff", I::IXOFF),
    Flag::new("tandem", I::IXOFF),
    Flag::new("ixon", I::IXON),
    // not supported by nix
    // Flag::new("iuclc", I::IUCLC),
    Flag::new("ixany", I::IXANY).sane(),
    Flag::new("imaxbel", I::IMAXBEL).sane(),
    Flag::new("iutf8", I::IUTF8).sane(),
];

pub const OUTPUT_FLAGS: [Flag<O>; 24] = [
    Flag::new("opost", O::OPOST).sane(),
    Flag::new("olcuc", O::OLCUC),
    Flag::new("ocrnl", O::OCRNL),
    Flag::new("onlcr", O::ONLCR).sane(),
    Flag::new("onocr", O::ONOCR),
    Flag::new("onlret", O::ONLRET),
    Flag::new("ofill", O::OFILL),
    Flag::new("ofdel", O::OFDEL),
    Flag::new("nl0", O::NL0).group(O::NLDLY).sane(),
    Flag::new("nl1", O::NL1).group(O::NLDLY),
    Flag::new("cr0", O::CR0).group(O::CRDLY).sane(),
    Flag::new("cr1", O::CR1).group(O::CRDLY),
    Flag::new("cr2", O::CR2).group(O::CRDLY),
    Flag::new("cr3", O::CR3).group(O::CRDLY),
    Flag::new("tab0", O::TAB0).group(O::TABDLY).sane(),
    Flag::new("tab1", O::TAB1).group(O::TABDLY),
    Flag::new("tab2", O::TAB2).group(O::TABDLY),
    Flag::new("tab3", O::TAB3).group(O::TABDLY),
    Flag::new("bs0", O::BS0).group(O::BSDLY).sane(),
    Flag::new("bs1", O::BS1).group(O::BSDLY),
    Flag::new("vt0", O::VT0).group(O::VTDLY).sane(),
    Flag::new("vt1", O::VT1).group(O::VTDLY),
    Flag::new("ff0", O::FF0).group(O::FFDLY).sane(),
    Flag::new("ff1", O::FF1).group(O::FFDLY),
];

pub const LOCAL_FLAGS: [Flag<L>; 18] = [
    Flag::new("isig", L::ISIG).sane(),
    Flag::new("icanon", L::ICANON).sane(),
    Flag::new("iexten", L::IEXTEN).sane(),
    Flag::new("echo", L::ECHO).sane(),
    Flag::new("echoe", L::ECHOE).sane(),
    Flag::new("crterase", L::ECHOE).hidden().sane(),
    Flag::new("echok", L::ECHOK).sane(),
    Flag::new("echonl", L::ECHONL),
    Flag::new("noflsh", L::NOFLSH),
    // Not supported by nix
    // Flag::new("xcase", L::XCASE),
    Flag::new("tostop", L::TOSTOP),
    Flag::new("echoprt", L::ECHOPRT),
    Flag::new("prterase", L::ECHOPRT).hidden(),
    Flag::new("echoctl", L::ECHOCTL).sane(),
    Flag::new("ctlecho", L::ECHOCTL).sane().hidden(),
    Flag::new("echoke", L::ECHOKE).sane(),
    Flag::new("crtkill", L::ECHOKE).sane().hidden(),
    Flag::new("flusho", L::FLUSHO),
    Flag::new("extproc", L::EXTPROC),
];
<commit_msg>stty: fix ixany and iutf8 not sane<commit_after>// * This file is part of the uutils coreutils package.
// *
// * For the full copyright and license information, please view the LICENSE file
// * that was distributed with this source code.

// spell-checker:ignore parenb parodd cmspar hupcl cstopb cread clocal crtscts CSIZE
// spell-checker:ignore ignbrk brkint ignpar parmrk inpck istrip inlcr igncr icrnl ixoff ixon iuclc ixany imaxbel iutf
// spell-checker:ignore opost olcuc ocrnl onlcr onocr onlret ofill ofdel nldly crdly tabdly bsdly vtdly ffdly
// spell-checker:ignore isig icanon iexten echoe crterase echok echonl noflsh xcase tostop echoprt prterase echoctl ctlecho echoke crtkill flusho extproc

use crate::Flag;
use nix::sys::termios::{ControlFlags as C, InputFlags as I, LocalFlags as L, OutputFlags as O};

pub const CONTROL_FLAGS: [Flag<C>; 12] = [
    Flag::new("parenb", C::PARENB),
    Flag::new("parodd", C::PARODD),
    Flag::new("cmspar", C::CMSPAR),
    Flag::new("cs5", C::CS5).group(C::CSIZE),
    Flag::new("cs6", C::CS6).group(C::CSIZE),
    Flag::new("cs7", C::CS7).group(C::CSIZE),
    Flag::new("cs8", C::CS8).group(C::CSIZE).sane(),
    Flag::new("hupcl", C::HUPCL).sane(),
    Flag::new("cstopb", C::CSTOPB),
    Flag::new("cread", C::CREAD).sane(),
    Flag::new("clocal", C::CLOCAL),
    Flag::new("crtscts", C::CRTSCTS),
];

pub const INPUT_FLAGS: [Flag<I>; 15] = [
    Flag::new("ignbrk", I::IGNBRK),
    Flag::new("brkint", I::BRKINT).sane(),
    Flag::new("ignpar", I::IGNPAR),
    Flag::new("parmrk", I::PARMRK),
    Flag::new("inpck", I::INPCK),
    Flag::new("istrip", I::ISTRIP),
    Flag::new("inlcr", I::INLCR),
    Flag::new("igncr", I::IGNCR),
    Flag::new("icrnl", I::ICRNL).sane(),
    Flag::new("ixoff", I::IXOFF),
    Flag::new("tandem", I::IXOFF),
    Flag::new("ixon", I::IXON),
    // not supported by nix
    // Flag::new("iuclc", I::IUCLC),
    Flag::new("ixany", I::IXANY),
    Flag::new("imaxbel", I::IMAXBEL).sane(),
    Flag::new("iutf8", I::IUTF8),
];

pub const OUTPUT_FLAGS: [Flag<O>; 24] = [
    Flag::new("opost", O::OPOST).sane(),
    Flag::new("olcuc", O::OLCUC),
    Flag::new("ocrnl", O::OCRNL),
    Flag::new("onlcr", O::ONLCR).sane(),
    Flag::new("onocr", O::ONOCR),
    Flag::new("onlret", O::ONLRET),
    Flag::new("ofill", O::OFILL),
    Flag::new("ofdel", O::OFDEL),
    Flag::new("nl0", O::NL0).group(O::NLDLY).sane(),
    Flag::new("nl1", O::NL1).group(O::NLDLY),
    Flag::new("cr0", O::CR0).group(O::CRDLY).sane(),
    Flag::new("cr1", O::CR1).group(O::CRDLY),
    Flag::new("cr2", O::CR2).group(O::CRDLY),
    Flag::new("cr3", O::CR3).group(O::CRDLY),
    Flag::new("tab0", O::TAB0).group(O::TABDLY).sane(),
    Flag::new("tab1", O::TAB1).group(O::TABDLY),
    Flag::new("tab2", O::TAB2).group(O::TABDLY),
    Flag::new("tab3", O::TAB3).group(O::TABDLY),
    Flag::new("bs0", O::BS0).group(O::BSDLY).sane(),
    Flag::new("bs1", O::BS1).group(O::BSDLY),
    Flag::new("vt0", O::VT0).group(O::VTDLY).sane(),
    Flag::new("vt1", O::VT1).group(O::VTDLY),
    Flag::new("ff0", O::FF0).group(O::FFDLY).sane(),
    Flag::new("ff1", O::FF1).group(O::FFDLY),
];

pub const LOCAL_FLAGS: [Flag<L>; 18] = [
    Flag::new("isig", L::ISIG).sane(),
    Flag::new("icanon", L::ICANON).sane(),
    Flag::new("iexten", L::IEXTEN).sane(),
    Flag::new("echo", L::ECHO).sane(),
    Flag::new("echoe", L::ECHOE).sane(),
    Flag::new("crterase", L::ECHOE).hidden().sane(),
    Flag::new("echok", L::ECHOK).sane(),
    Flag::new("echonl", L::ECHONL),
    Flag::new("noflsh", L::NOFLSH),
    // Not supported by nix
    // Flag::new("xcase", L::XCASE),
    Flag::new("tostop", L::TOSTOP),
    Flag::new("echoprt", L::ECHOPRT),
    Flag::new("prterase", L::ECHOPRT).hidden(),
    Flag::new("echoctl", L::ECHOCTL).sane(),
    Flag::new("ctlecho", L::ECHOCTL).sane().hidden(),
    Flag::new("echoke", L::ECHOKE).sane(),
    Flag::new("crtkill", L::ECHOKE).sane().hidden(),
    Flag::new("flusho", L::FLUSHO),
    Flag::new("extproc", L::EXTPROC),
];
<|endoftext|>


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

extern crate magick_rust;

use magick_rust::{MagickWand, magick_wand_genesis};
use magick_rust::filters::{FilterType};

use std::error::Error;
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::sync::{Once, ONCE_INIT};

// Used to make sure MagickWand is initialized exactly once. Note that we
// do not bother shutting down, we simply exit when the tests are done.
static START: Once = ONCE_INIT;

#[test]
fn test_new_drop() {
    START.call_once(|| {
        magick_wand_genesis();
    });
    MagickWand::new();
}

#[test]
fn test_resize_image() {
    START.call_once(|| {
        magick_wand_genesis();
    });
    let wand = MagickWand::new();
    assert!(wand.read_image("tests/data/IMG_5745.JPG").is_ok());
    assert_eq!(512, wand.get_image_width());
    assert_eq!(384, wand.get_image_height());
    let halfwidth = match wand.get_image_width() {
        1 => 1,
        width => width / 2
    };
    let halfheight = match wand.get_image_height() {
        1 => 1,
        height => height / 2
    };
    wand.resize_image(halfwidth, halfheight, FilterType::LanczosFilter, 1.0);
    assert_eq!(256, wand.get_image_width());
    assert_eq!(192, wand.get_image_height());
}

#[test]
fn test_read_from_blob() {
    START.call_once(|| {
        magick_wand_genesis();
    });
    let wand = MagickWand::new();

    let path = Path::new("tests/data/IMG_5745.JPG");
    let mut file = match File::open(&path) {
        Err(why) => panic!("couldn't open file: {}", Error::description(&why)),
        Ok(file) => file
    };
    let mut data: Vec<u8> = Vec::new();
    match file.read_to_end(&mut data) {
        Err(why) => panic!("couldn't read file: {}", Error::description(&why)),
        Ok(_) => ()
    };
    assert!(wand.read_image_blob(data).is_ok());
    assert_eq!(512, wand.get_image_width());
    assert_eq!(384, wand.get_image_height());
}

#[test]
fn test_write_to_blob() {
    START.call_once(|| {
        magick_wand_genesis();
    });
    let wand = MagickWand::new();
    assert!(wand.read_image("tests/data/IMG_5745.JPG").is_ok());
    assert_eq!(512, wand.get_image_width());
    assert_eq!(384, wand.get_image_height());
    let blob = wand.write_image_blob("jpeg").unwrap();
    assert_eq!(104061, blob.len());
    // should be able to read it back again
    assert!(wand.read_image_blob(blob).is_ok());
    assert_eq!(512, wand.get_image_width());
    assert_eq!(384, wand.get_image_height());
}

#[test]
fn test_fit() {
    START.call_once(|| {
        magick_wand_genesis();
    });
    let wand = MagickWand::new();
    assert!(wand.read_image("tests/data/IMG_5745.JPG").is_ok());
    assert_eq!(512, wand.get_image_width());
    assert_eq!(384, wand.get_image_height());
    wand.fit(240, 240);
    assert_eq!(240, wand.get_image_width());
    assert_eq!(180, wand.get_image_height());
}

#[test]
fn test_get_image_property() {
    START.call_once(|| {
        magick_wand_genesis();
    });
    let wand = MagickWand::new();
    assert!(wand.read_image("tests/data/IMG_5745.JPG").is_ok());
    // retrieve a property we know exists
    let found_value = wand.get_image_property("exif:DateTime");
    assert!(found_value.is_ok());
    assert_eq!("2014:04:23 13:33:08", found_value.unwrap());
    // retrieve a property that does not exist
    let missing_value = wand.get_image_property("exif:Foobar");
    assert!(missing_value.is_err());
    assert_eq!("missing property", missing_value.unwrap_err());
}
<commit_msg>Use canonical tag name for original date<commit_after>/*
 * Copyright 2015 Nathan Fiedler
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

extern crate magick_rust;

use magick_rust::{MagickWand, magick_wand_genesis};
use magick_rust::filters::{FilterType};

use std::error::Error;
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::sync::{Once, ONCE_INIT};

// Used to make sure MagickWand is initialized exactly once. Note that we
// do not bother shutting down, we simply exit when the tests are done.
static START: Once = ONCE_INIT;

#[test]
fn test_new_drop() {
    START.call_once(|| {
        magick_wand_genesis();
    });
    MagickWand::new();
}

#[test]
fn test_resize_image() {
    START.call_once(|| {
        magick_wand_genesis();
    });
    let wand = MagickWand::new();
    assert!(wand.read_image("tests/data/IMG_5745.JPG").is_ok());
    assert_eq!(512, wand.get_image_width());
    assert_eq!(384, wand.get_image_height());
    let halfwidth = match wand.get_image_width() {
        1 => 1,
        width => width / 2
    };
    let halfheight = match wand.get_image_height() {
        1 => 1,
        height => height / 2
    };
    wand.resize_image(halfwidth, halfheight, FilterType::LanczosFilter, 1.0);
    assert_eq!(256, wand.get_image_width());
    assert_eq!(192, wand.get_image_height());
}

#[test]
fn test_read_from_blob() {
    START.call_once(|| {
        magick_wand_genesis();
    });
    let wand = MagickWand::new();

    let path = Path::new("tests/data/IMG_5745.JPG");
    let mut file = match File::open(&path) {
        Err(why) => panic!("couldn't open file: {}", Error::description(&why)),
        Ok(file) => file
    };
    let mut data: Vec<u8> = Vec::new();
    match file.read_to_end(&mut data) {
        Err(why) => panic!("couldn't read file: {}", Error::description(&why)),
        Ok(_) => ()
    };
    assert!(wand.read_image_blob(data).is_ok());
    assert_eq!(512, wand.get_image_width());
    assert_eq!(384, wand.get_image_height());
}

#[test]
fn test_write_to_blob() {
    START.call_once(|| {
        magick_wand_genesis();
    });
    let wand = MagickWand::new();
    assert!(wand.read_image("tests/data/IMG_5745.JPG").is_ok());
    assert_eq!(512, wand.get_image_width());
    assert_eq!(384, wand.get_image_height());
    let blob = wand.write_image_blob("jpeg").unwrap();
    assert_eq!(104061, blob.len());
    // should be able to read it back again
    assert!(wand.read_image_blob(blob).is_ok());
    assert_eq!(512, wand.get_image_width());
    assert_eq!(384, wand.get_image_height());
}

#[test]
fn test_fit() {
    START.call_once(|| {
        magick_wand_genesis();
    });
    let wand = MagickWand::new();
    assert!(wand.read_image("tests/data/IMG_5745.JPG").is_ok());
    assert_eq!(512, wand.get_image_width());
    assert_eq!(384, wand.get_image_height());
    wand.fit(240, 240);
    assert_eq!(240, wand.get_image_width());
    assert_eq!(180, wand.get_image_height());
}

#[test]
fn test_get_image_property() {
    START.call_once(|| {
        magick_wand_genesis();
    });
    let wand = MagickWand::new();
    assert!(wand.read_image("tests/data/IMG_5745.JPG").is_ok());
    // retrieve a property we know exists
    let found_value = wand.get_image_property("exif:DateTimeOriginal");
    assert!(found_value.is_ok());
    assert_eq!("2014:04:23 13:33:08", found_value.unwrap());
    // retrieve a property that does not exist
    let missing_value = wand.get_image_property("exif:Foobar");
    assert!(missing_value.is_err());
    assert_eq!("missing property", missing_value.unwrap_err());
}
<|endoftext|>