<commit_before>//! [Lambda terms](https://en.wikipedia.org/wiki/Lambda_calculus#Lambda_terms)

use self::Term::*;
use self::Error::*;
use std::fmt;
use std::borrow::Cow;

/// A lambda term that is either a variable with a De Bruijn index, an abstraction over a term or
/// an applicaction of one term to another.
#[derive(Debug, PartialEq, Clone)]
pub enum Term {
    Var(usize),
    Abs(Box<Term>),
    App(Box<Term>, Box<Term>)
}

/// An error that can be returned when an inapplicable function is applied to a term.
#[derive(Debug, PartialEq, Clone)]
pub enum Error {
    NotAVar,
    NotAnAbs,
    NotAnApp,
    NotANum,
    NotAPair,
    NotAList
}

impl Term {
    /// Applies `self` to another term without substitution or reduction.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    /// use lambda_calculus::arithmetic::{zero, succ};
    ///
    /// assert_eq!(succ().app(zero()), App(Box::new(succ()), Box::new(zero())));
    /// ```
    pub fn app(self, argument: Term) -> Term { App(Box::new(self), Box::new(argument)) }

    /// Consumes a lambda variable and returns its De Bruijn index.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(1).unvar(), Ok(1));
    /// ```
    pub fn unvar(self) -> Result<usize, Error> {
        match self {
            Var(n) => Ok(n),
            _ => Err(NotAVar)
        }
    }

    /// Consumes an abstraction and returns its underlying term.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    /// use lambda_calculus::term::abs;
    ///
    /// assert_eq!(abs(Var(1)).unabs(), Ok(Var(1)));
    /// ```
    pub fn unabs(self) -> Result<Term, Error> {
        match self {
            Abs(x) => Ok(*x),
            _ => Err(NotAnAbs)
        }
    }

    /// Returns a reference to an abstraction's underlying term.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    /// use lambda_calculus::term::abs;
    ///
    /// assert_eq!(abs(Var(1)).unabs_ref(), Ok(&Var(1)));
    /// ```
    pub fn unabs_ref(&self) -> Result<&Term, Error> {
        match *self {
            Abs(ref x) => Ok(&*x),
            _ => Err(NotAnAbs)
        }
    }

    /// Returns a mutable reference to an abstraction's underlying term.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    /// use lambda_calculus::term::abs;
    ///
    /// assert_eq!(abs(Var(1)).unabs_ref_mut(), Ok(&mut Var(1)));
    /// ```
    pub fn unabs_ref_mut(&mut self) -> Result<&mut Term, Error> {
        match *self {
            Abs(ref mut x) => Ok(&mut *x),
            _ => Err(NotAnAbs)
        }
    }

    /// Consumes an application and returns a pair containing its underlying terms.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).unapp(), Ok((Var(0), Var(1))));
    /// ```
    pub fn unapp(self) -> Result<(Term, Term), Error> {
        match self {
            App(lhs, rhs) => Ok((*lhs, *rhs)),
            _ => Err(NotAnApp)
        }
    }

    /// Returns a pair containing references to an application's underlying terms.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).unapp_ref(), Ok((&Var(0), &Var(1))));
    /// ```
    pub fn unapp_ref(&self) -> Result<(&Term, &Term), Error> {
        match *self {
            App(ref lhs, ref rhs) => Ok((&*lhs, &*rhs)),
            _ => Err(NotAnApp)
        }
    }

    /// Returns a pair containing mutable references to an application's underlying terms.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).unapp_ref_mut(), Ok((&mut Var(0), &mut Var(1))));
    /// ```
    pub fn unapp_ref_mut(&mut self) -> Result<(&mut Term, &mut Term), Error> {
        match *self {
            App(ref mut lhs, ref mut rhs) => Ok((&mut *lhs, &mut *rhs)),
            _ => Err(NotAnApp)
        }
    }

    /// Returns the left-hand side term of an application. Consumes `self`.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).lhs(), Ok(Var(0)));
    /// ```
    pub fn lhs(self) -> Result<Term, Error> {
        if let Ok((lhs, _)) = self.unapp() { Ok(lhs) } else { Err(NotAnApp) }
    }

    /// Returns a reference to the left-hand side term of an application.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).lhs_ref(), Ok(&Var(0)));
    /// ```
    pub fn lhs_ref(&self) -> Result<&Term, Error> {
        if let Ok((lhs, _)) = self.unapp_ref() { Ok(lhs) } else { Err(NotAnApp) }
    }

    /// Returns a mutable reference to the left-hand side term of an application.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).lhs_ref_mut(), Ok(&mut Var(0)));
    /// ```
    pub fn lhs_ref_mut(&mut self) -> Result<&mut Term, Error> {
        if let Ok((lhs, _)) = self.unapp_ref_mut() { Ok(lhs) } else { Err(NotAnApp) }
    }

    /// Returns the right-hand side term of an application. Consumes `self`.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).rhs(), Ok(Var(1)));
    /// ```
    pub fn rhs(self) -> Result<Term, Error> {
        if let Ok((_, rhs)) = self.unapp() { Ok(rhs) } else { Err(NotAnApp) }
    }

    /// Returns a reference to the right-hand side term of an application.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).rhs_ref(), Ok(&Var(1)));
    /// ```
    pub fn rhs_ref(&self) -> Result<&Term, Error> {
        if let Ok((_, rhs)) = self.unapp_ref() { Ok(rhs) } else { Err(NotAnApp) }
    }

    /// Returns a mutable reference to the right-hand side term of an application.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).rhs_ref_mut(), Ok(&mut Var(1)));
    /// ```
    pub fn rhs_ref_mut(&mut self) -> Result<&mut Term, Error> {
        if let Ok((_, rhs)) = self.unapp_ref_mut() { Ok(rhs) } else { Err(NotAnApp) }
    }

    /// Performs an `App`lication by substitution and variable update.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::{app, abs};
    /// use lambda_calculus::term::Term::Var;
    /// use lambda_calculus::arithmetic::zero;
    /// use lambda_calculus::combinators::i;
    ///
    /// assert_eq!(app(i(), zero()).evaluate(), Ok(abs(abs(Var(1)))));
    /// ```
    pub fn evaluate(self) -> Result<Term, Error> {
        let (mut lhs, rhs) = try!(self.unapp());

        _apply(&mut lhs, rhs, 0);

        Ok(try!(lhs.unabs()))
    }
}

/// Wraps a term in an abstraction. Consumes its argument.
///
/// # Example
/// ```
/// use lambda_calculus::term::Term::*;
/// use lambda_calculus::term::abs;
///
/// assert_eq!(abs(Var(1)), Abs(Box::new(Var(1))));
/// ```
pub fn abs(term: Term) -> Term { Abs(Box::new(term)) }

/// Produces an application of its arguments without substitution or reduction, consuming them in
/// the process.
///
/// # Example
/// ```
/// use lambda_calculus::term::Term::*;
/// use lambda_calculus::term::app;
///
/// assert_eq!(app(Var(0), Var(1)), App(Box::new(Var(0)), Box::new(Var(1))));
/// ```
pub fn app(lhs: Term, rhs: Term) -> Term { App(Box::new(lhs), Box::new(rhs)) }

/// Applies two terms with substitution and variable update (but without reduction), consuming them
/// in the process.
///
/// # Example
/// ```
/// use lambda_calculus::term::apply;
/// use lambda_calculus::parser::parse;
///
/// let lhs    = parse(&"λλ42(λ13)").unwrap();
/// let rhs    = parse(&"λ51").unwrap();
/// let result = parse(&"λ3(λ61)(λ1(λ71))").unwrap();
///
/// assert_eq!(apply(lhs, rhs), Ok(result));
/// ```
pub fn apply(mut lhs: Term, rhs: Term) -> Result<Term, Error> {
    _apply(&mut lhs, rhs, 0);

    Ok(try!(lhs.unabs()))
}

fn _apply(lhs: &mut Term, rhs: Term, depth: usize) {
    match *lhs {
        Var(i) => if i == depth {
            *lhs = rhs; // substitute a top-level variable from lhs with rhs
            update_free_variables(lhs, depth - 1) // update free variables' indices from rhs
        } else if i > depth {
            *lhs = Var(i - 1) // decrement a free variable's index
        },
        Abs(_) => {
            _apply(lhs.unabs_ref_mut().unwrap(), rhs, depth + 1)
        },
        App(_, _) => {
            _apply(lhs.lhs_ref_mut().unwrap(), rhs.clone(), depth);
            _apply(lhs.rhs_ref_mut().unwrap(), rhs, depth);
        }
    }
}

fn update_free_variables(term: &mut Term, depth: usize) {
    match *term {
        Var(i) => if i > depth {
            *term = Var(i + depth)
        },
        Abs(_) => {
            update_free_variables(term.unabs_ref_mut().unwrap(), depth)
        },
        App(_, _) => {
            update_free_variables(term.lhs_ref_mut().unwrap(), depth);
            update_free_variables(term.rhs_ref_mut().unwrap(), depth);
        }
    }
}

/// Set to `true` for λ or `false` for \ when displaying lambda terms. The default is `true`.
pub const DISPLAY_PRETTY: bool = true;

impl fmt::Display for Term {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", show_precedence(0, self))
    }
}

fn show_precedence(context_precedence: usize, term: &Term) -> String {
    match *term {
        Var(i) => format!("{:X}", i), // max. index = 15
        Abs(ref t) => {
            let ret = format!("{}{}", if DISPLAY_PRETTY { 'λ' } else { '\\' }, t);
            parenthesize_if(context_precedence > 1, &ret).into()
        },
        App(ref t1, ref t2) => {
            let ret = format!("{}{}", show_precedence(2, t1), show_precedence(3, t2));
            parenthesize_if(context_precedence == 3, &ret).into()
        }
    }
}

fn parenthesize_if(condition: bool, input: &str) -> Cow<str> {
    if condition {
        format!("({})", input).into()
    } else {
        input.into()
    }
}

// WIP
/*
fn beta_reduce(mut term: Term) -> Result<Term, Error> {
    match term {
        App(lhs, rhs) => term = try!(apply(*lhs, *rhs)),
        Abs(t) => try!(beta_reduce(*t)),
        _ => ()
    }

    Ok(term)
}
*/
fn _beta_reduce(term: &mut Term) {
    match *term {
        App(_, _) => {
            let (lhs, rhs) = term.clone().unapp().unwrap();
            println!("attempting to reduce {}...", *term);
            if let Ok(reduced) = apply(lhs, rhs) {
                println!("\tattempt successful: {}", reduced);
                *term = reduced;
            } else {
                println!("\tfailed; trying to reduce the inner terms");
                let (mut lhs, mut rhs) = term.clone().unapp().unwrap();
                _beta_reduce(&mut lhs);
                _beta_reduce(&mut rhs);
            }
        },
        Abs(ref mut t) => _beta_reduce(t),
        _ => ()
    }
}

#[cfg(test)]
mod test {
    use arithmetic::*;
    use term::{apply, _beta_reduce};

    #[test]
    fn applying() {
        let lhs    = parse(&"λλ42(λ13)").unwrap();
        let rhs    = parse(&"λ51").unwrap();
        let result = parse(&"λ3(λ61)(λ1(λ71))").unwrap();
        assert_eq!(apply(lhs, rhs), Ok(result));

        assert_eq!(apply(i(), zero()).unwrap(), abs(abs(Var(1))));
    }
        let mut unreduced = apply(succ(), zero()).unwrap();
        println!("before: {}", unreduced);
        let _ = _beta_reduce(&mut unreduced);
        println!("after: {}", unreduced);
    }

    #[test]
    fn displaying_terms() {
        assert_eq!(&format!("{}", zero()), "λλ1");
        assert_eq!(&format!("{}", succ()), "λλλ2(321)");
        assert_eq!(&format!("{}", pred()), "λλλ3(λλ1(24))(λ2)(λ1)");
    }
}
<commit_msg>Remove WIP code<commit_after>//! [Lambda terms](https://en.wikipedia.org/wiki/Lambda_calculus#Lambda_terms)

use self::Term::*;
use self::Error::*;
use std::fmt;
use std::borrow::Cow;

/// A lambda term that is either a variable with a De Bruijn index, an abstraction over a term or
/// an applicaction of one term to another.
#[derive(Debug, PartialEq, Clone)]
pub enum Term {
    Var(usize),
    Abs(Box<Term>),
    App(Box<Term>, Box<Term>)
}

/// An error that can be returned when an inapplicable function is applied to a term.
#[derive(Debug, PartialEq, Clone)]
pub enum Error {
    NotAVar,
    NotAnAbs,
    NotAnApp,
    NotANum,
    NotAPair,
    NotAList
}

impl Term {
    /// Applies `self` to another term without substitution or reduction.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    /// use lambda_calculus::arithmetic::{zero, succ};
    ///
    /// assert_eq!(succ().app(zero()), App(Box::new(succ()), Box::new(zero())));
    /// ```
    pub fn app(self, argument: Term) -> Term { App(Box::new(self), Box::new(argument)) }

    /// Consumes a lambda variable and returns its De Bruijn index.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(1).unvar(), Ok(1));
    /// ```
    pub fn unvar(self) -> Result<usize, Error> {
        match self {
            Var(n) => Ok(n),
            _ => Err(NotAVar)
        }
    }

    /// Consumes an abstraction and returns its underlying term.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    /// use lambda_calculus::term::abs;
    ///
    /// assert_eq!(abs(Var(1)).unabs(), Ok(Var(1)));
    /// ```
    pub fn unabs(self) -> Result<Term, Error> {
        match self {
            Abs(x) => Ok(*x),
            _ => Err(NotAnAbs)
        }
    }

    /// Returns a reference to an abstraction's underlying term.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    /// use lambda_calculus::term::abs;
    ///
    /// assert_eq!(abs(Var(1)).unabs_ref(), Ok(&Var(1)));
    /// ```
    pub fn unabs_ref(&self) -> Result<&Term, Error> {
        match *self {
            Abs(ref x) => Ok(&*x),
            _ => Err(NotAnAbs)
        }
    }

    /// Returns a mutable reference to an abstraction's underlying term.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    /// use lambda_calculus::term::abs;
    ///
    /// assert_eq!(abs(Var(1)).unabs_ref_mut(), Ok(&mut Var(1)));
    /// ```
    pub fn unabs_ref_mut(&mut self) -> Result<&mut Term, Error> {
        match *self {
            Abs(ref mut x) => Ok(&mut *x),
            _ => Err(NotAnAbs)
        }
    }

    /// Consumes an application and returns a pair containing its underlying terms.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).unapp(), Ok((Var(0), Var(1))));
    /// ```
    pub fn unapp(self) -> Result<(Term, Term), Error> {
        match self {
            App(lhs, rhs) => Ok((*lhs, *rhs)),
            _ => Err(NotAnApp)
        }
    }

    /// Returns a pair containing references to an application's underlying terms.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).unapp_ref(), Ok((&Var(0), &Var(1))));
    /// ```
    pub fn unapp_ref(&self) -> Result<(&Term, &Term), Error> {
        match *self {
            App(ref lhs, ref rhs) => Ok((&*lhs, &*rhs)),
            _ => Err(NotAnApp)
        }
    }

    /// Returns a pair containing mutable references to an application's underlying terms.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).unapp_ref_mut(), Ok((&mut Var(0), &mut Var(1))));
    /// ```
    pub fn unapp_ref_mut(&mut self) -> Result<(&mut Term, &mut Term), Error> {
        match *self {
            App(ref mut lhs, ref mut rhs) => Ok((&mut *lhs, &mut *rhs)),
            _ => Err(NotAnApp)
        }
    }

    /// Returns the left-hand side term of an application. Consumes `self`.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).lhs(), Ok(Var(0)));
    /// ```
    pub fn lhs(self) -> Result<Term, Error> {
        if let Ok((lhs, _)) = self.unapp() { Ok(lhs) } else { Err(NotAnApp) }
    }

    /// Returns a reference to the left-hand side term of an application.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).lhs_ref(), Ok(&Var(0)));
    /// ```
    pub fn lhs_ref(&self) -> Result<&Term, Error> {
        if let Ok((lhs, _)) = self.unapp_ref() { Ok(lhs) } else { Err(NotAnApp) }
    }

    /// Returns a mutable reference to the left-hand side term of an application.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).lhs_ref_mut(), Ok(&mut Var(0)));
    /// ```
    pub fn lhs_ref_mut(&mut self) -> Result<&mut Term, Error> {
        if let Ok((lhs, _)) = self.unapp_ref_mut() { Ok(lhs) } else { Err(NotAnApp) }
    }

    /// Returns the right-hand side term of an application. Consumes `self`.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).rhs(), Ok(Var(1)));
    /// ```
    pub fn rhs(self) -> Result<Term, Error> {
        if let Ok((_, rhs)) = self.unapp() { Ok(rhs) } else { Err(NotAnApp) }
    }

    /// Returns a reference to the right-hand side term of an application.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).rhs_ref(), Ok(&Var(1)));
    /// ```
    pub fn rhs_ref(&self) -> Result<&Term, Error> {
        if let Ok((_, rhs)) = self.unapp_ref() { Ok(rhs) } else { Err(NotAnApp) }
    }

    /// Returns a mutable reference to the right-hand side term of an application.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(0).app(Var(1)).rhs_ref_mut(), Ok(&mut Var(1)));
    /// ```
    pub fn rhs_ref_mut(&mut self) -> Result<&mut Term, Error> {
        if let Ok((_, rhs)) = self.unapp_ref_mut() { Ok(rhs) } else { Err(NotAnApp) }
    }

    /// Performs an `App`lication by substitution and variable update.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::{app, abs};
    /// use lambda_calculus::term::Term::Var;
    /// use lambda_calculus::arithmetic::zero;
    /// use lambda_calculus::combinators::i;
    ///
    /// assert_eq!(app(i(), zero()).evaluate(), Ok(abs(abs(Var(1)))));
    /// ```
    pub fn evaluate(self) -> Result<Term, Error> {
        let (mut lhs, rhs) = try!(self.unapp());

        _apply(&mut lhs, rhs, 0);

        Ok(try!(lhs.unabs()))
    }
}

/// Wraps a term in an abstraction. Consumes its argument.
///
/// # Example
/// ```
/// use lambda_calculus::term::Term::*;
/// use lambda_calculus::term::abs;
///
/// assert_eq!(abs(Var(1)), Abs(Box::new(Var(1))));
/// ```
pub fn abs(term: Term) -> Term { Abs(Box::new(term)) }

/// Produces an application of its arguments without substitution or reduction, consuming them in
/// the process.
///
/// # Example
/// ```
/// use lambda_calculus::term::Term::*;
/// use lambda_calculus::term::app;
///
/// assert_eq!(app(Var(0), Var(1)), App(Box::new(Var(0)), Box::new(Var(1))));
/// ```
pub fn app(lhs: Term, rhs: Term) -> Term { App(Box::new(lhs), Box::new(rhs)) }

/// Applies two terms with substitution and variable update (but without reduction), consuming them
/// in the process.
///
/// # Example
/// ```
/// use lambda_calculus::term::apply;
/// use lambda_calculus::parser::parse;
///
/// let lhs    = parse(&"λλ42(λ13)").unwrap();
/// let rhs    = parse(&"λ51").unwrap();
/// let result = parse(&"λ3(λ61)(λ1(λ71))").unwrap();
///
/// assert_eq!(apply(lhs, rhs), Ok(result));
/// ```
pub fn apply(mut lhs: Term, rhs: Term) -> Result<Term, Error> {
    _apply(&mut lhs, rhs, 0);

    Ok(try!(lhs.unabs()))
}

fn _apply(lhs: &mut Term, rhs: Term, depth: usize) {
    match *lhs {
        Var(i) => if i == depth {
            *lhs = rhs; // substitute a top-level variable from lhs with rhs
            update_free_variables(lhs, depth - 1) // update free variables' indices from rhs
        } else if i > depth {
            *lhs = Var(i - 1) // decrement a free variable's index
        },
        Abs(_) => {
            _apply(lhs.unabs_ref_mut().unwrap(), rhs, depth + 1)
        },
        App(_, _) => {
            _apply(lhs.lhs_ref_mut().unwrap(), rhs.clone(), depth);
            _apply(lhs.rhs_ref_mut().unwrap(), rhs, depth);
        }
    }
}

fn update_free_variables(term: &mut Term, depth: usize) {
    match *term {
        Var(i) => if i > depth {
            *term = Var(i + depth)
        },
        Abs(_) => {
            update_free_variables(term.unabs_ref_mut().unwrap(), depth)
        },
        App(_, _) => {
            update_free_variables(term.lhs_ref_mut().unwrap(), depth);
            update_free_variables(term.rhs_ref_mut().unwrap(), depth);
        }
    }
}

/// Set to `true` for λ or `false` for \ when displaying lambda terms. The default is `true`.
pub const DISPLAY_PRETTY: bool = true;

impl fmt::Display for Term {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", show_precedence(0, self))
    }
}

fn show_precedence(context_precedence: usize, term: &Term) -> String {
    match *term {
        Var(i) => format!("{:X}", i), // max. index = 15
        Abs(ref t) => {
            let ret = format!("{}{}", if DISPLAY_PRETTY { 'λ' } else { '\\' }, t);
            parenthesize_if(context_precedence > 1, &ret).into()
        },
        App(ref t1, ref t2) => {
            let ret = format!("{}{}", show_precedence(2, t1), show_precedence(3, t2));
            parenthesize_if(context_precedence == 3, &ret).into()
        }
    }
}

fn parenthesize_if(condition: bool, input: &str) -> Cow<str> {
    if condition {
        format!("({})", input).into()
    } else {
        input.into()
    }
}

#[cfg(test)]
mod test {
    use arithmetic::{zero, succ, pred};
    use combinators::i;
    use parser::parse;
    use term::{apply, abs};
    use term::Term::Var;

    #[test]
    fn applying() {
        let lhs    = parse(&"λλ42(λ13)").unwrap();
        let rhs    = parse(&"λ51").unwrap();
        let result = parse(&"λ3(λ61)(λ1(λ71))").unwrap();
        assert_eq!(apply(lhs, rhs), Ok(result));

        assert_eq!(apply(i(), zero()).unwrap(), abs(abs(Var(1))));
    }

    #[test]
    fn displaying_terms() {
        assert_eq!(&format!("{}", zero()), "λλ1");
        assert_eq!(&format!("{}", succ()), "λλλ2(321)");
        assert_eq!(&format!("{}", pred()), "λλλ3(λλ1(24))(λ2)(λ1)");
    }
}
<|endoftext|>


<commit_before>//! Functions for computing [integral images](https://en.wikipedia.org/wiki/Summed_area_table)
//! and running sums of rows and columns.

extern crate image;

use image::{
    Luma,
    GenericImage,
    ImageBuffer
};

use definitions::{
    VecBuffer
};

/// Computes the integral image of an 8bpp grayscale image.
/// I is the integral image of an image F if I(x, y) is the
/// sum of F(x', y') for x' <= x, y' <= y. i.e. each pixel
/// in the integral image contains the sum of the pixel intensities
/// of all input pixels that are above it and to its left.
/// The integral image has the helpful property that it lets us
/// compute the sum of pixel intensities from any rectangular region
/// in the input image in constant time.
/// Specifically, given a rectangle in F with clockwise corners
/// A, B, C, D, with A at the upper left, the total pixel intensity
/// of this rectangle is I(C) - I(B) - I(D) + I(A).
// TODO: Support more formats.
// TODO: This is extremely slow. Fix that!
pub fn integral_image<I>(image: &I) -> VecBuffer<Luma<u32>>
    where I: GenericImage<Pixel=Luma<u8>> {
    padded_integral_image(image, 0, 0)
}

/// Computes the integral image of the result of padding image
/// with its boundary pixels for x_padding columns on either
/// side and y_padding rows at its top and bottom.
/// Returned image has width image.width() + 2 * x_padding
/// and height image.height() + 2 * y_padding.
pub fn padded_integral_image<I>(image: &I, x_padding: u32, y_padding: u32)
        -> VecBuffer<Luma<u32>>
    where I: GenericImage<Pixel=Luma<u8>> {

    let (in_width, in_height) = image.dimensions();
    let out_width = in_width + 2 * x_padding;
    let out_height = in_height + 2 * y_padding;

    let mut out: ImageBuffer<Luma<u32>, Vec<u32>>
        = ImageBuffer::new(out_width, out_height);

    for y in 0..out_height {
        let y_in: u32;
        if y < y_padding {
            y_in = 0;
        }
        else if y >= in_height + y_padding {
            y_in = in_height - 1;
        }
        else {
            y_in = y - y_padding;
        }

        for x in 0..out_width {
            let x_in: u32;
            if x < x_padding {
                x_in = 0;
            }
            else if x >= in_width + x_padding {
                x_in = in_width - 1;
            }
            else {
                x_in = x - x_padding;
            }

            unsafe {
                let p = image.unsafe_get_pixel(x_in, y_in);
                out.unsafe_put_pixel(x, y, Luma([p[0] as u32]));
            }
        }
    }

    for x in 1..out_width {
        (*out.get_pixel_mut(x, 0))[0] += unsafe { out.unsafe_get_pixel(x - 1, 0)[0] };
    }

    for y in 1..out_height {
        (*out.get_pixel_mut(0, y))[0] += unsafe { out.unsafe_get_pixel(0, y - 1)[0] };

        for x in 1..out_width {
            (*out.get_pixel_mut(x, y))[0] += unsafe { out.unsafe_get_pixel(x, y - 1)[0] };
            (*out.get_pixel_mut(x, y))[0] += unsafe { out.unsafe_get_pixel(x - 1, y)[0] };
            (*out.get_pixel_mut(x, y))[0] -= unsafe { out.unsafe_get_pixel(x - 1, y - 1)[0] };
        }
    }

    out
}

/// Computes the running sum of one row of image, padded
/// at the beginning and end. The padding is by continuity.
/// Takes a reference to buffer so that this can be reused
/// for all rows in an image.
// TODO: faster, more formats
pub fn row_running_sum<I>(image: &I, row: u32, buffer: &mut [u32], padding: u32)
    where I: GenericImage<Pixel=Luma<u8>> {

    let width = image.width();
    assert!(buffer.len() >= (width + 2 * padding) as usize,
        format!("Buffer length {} is less than {} + 2 * {}", buffer.len(), width, padding));
    assert!(row < image.height(),
        format!("row out of bound: {} >= {}", row, image.height()));

    for x in 0..padding {
        buffer[x as usize] = unsafe { image.unsafe_get_pixel(0, row)[0] as u32 };
    }

    for x in 0..width {
        let idx = (x + padding) as usize;
        buffer[idx] = unsafe { image.unsafe_get_pixel(x, row)[0] as u32 };
    }

    for x in 0..padding {
        let idx = (x + width + padding) as usize;
        buffer[idx] = unsafe { image.unsafe_get_pixel(width - 1, row)[0] as u32 };
    }

    for x in 1..width + 2 * padding {
        buffer[x as usize] += buffer[(x - 1) as usize] as u32;
    }
}

/// Computes the running sum of one column of image, padded
/// at the top and bottom. The padding is by continuity.
/// Takes a reference to buffer so that this can be reused
/// for all columns in an image.
// TODO: faster, more formats
pub fn column_running_sum<I>(image: &I, column: u32, buffer: &mut [u32], padding: u32)
    where I: GenericImage<Pixel=Luma<u8>> {

    let height = image.height();
    assert!(buffer.len() >= (height + 2 * padding) as usize,
        format!("Buffer length {} is less than {} + 2 * {}", buffer.len(), height, padding));
    assert!(column < image.width(),
        format!("column out of bound: {} >= {}", column, image.width()));

    for y in 0..padding {
        buffer[y as usize] = unsafe { image.unsafe_get_pixel(column, 0)[0] as u32 };
    }

    for y in 0..height {
        let idx = (y + padding) as usize;
        buffer[idx] = unsafe { image.unsafe_get_pixel(column, y)[0] as u32 };
    }

    for y in 0..padding {
        let idx = (y + height + padding) as usize;
        buffer[idx] = unsafe { image.unsafe_get_pixel(column, height - 1)[0] as u32 };
    }

    for y in 1..height + 2 * padding {
        buffer[y as usize] += buffer[(y - 1) as usize] as u32;
    }
}

#[cfg(test)]
mod test {

    use super::{
        column_running_sum,
        integral_image,
        padded_integral_image,
        row_running_sum
    };
    use utils::{
        gray_bench_image
    };
    use image::{
        GrayImage,
        ImageBuffer,
        Luma
    };
    use test;

    #[test]
    fn test_integral_image() {
        let image: GrayImage = ImageBuffer::from_raw(3, 2, vec![
            1, 2, 3,
            4, 5, 6]).unwrap();

        let expected: ImageBuffer<Luma<u32>, Vec<u32>>
            = ImageBuffer::from_raw(3, 2, vec![
            1,  3,  6,
            5, 12, 21]).unwrap();

        assert_pixels_eq!(integral_image(&image), expected);
    }

    #[bench]
    fn bench_integral_image(b: &mut test::Bencher) {
        let image = gray_bench_image(500, 500);
        b.iter(|| {
            let integral = integral_image(&image);
            test::black_box(integral);
            });
    }

    #[test]
    fn test_padded_integral_image() {
        let image: GrayImage = ImageBuffer::from_raw(3, 2, vec![
            1, 2, 3,
            4, 5, 6]).unwrap();

        let expected: ImageBuffer<Luma<u32>, Vec<u32>>
            = ImageBuffer::from_raw(5, 4, vec![
              1,  2,   4,   7,  10,
              2,  4,   8,  14,  20,
              6, 12,  21,  33,  45,
             10, 20,  34,  52,  70]).unwrap();

        assert_pixels_eq!(padded_integral_image(&image, 1, 1), expected);
    }

    #[test]
    fn test_row_running_sum() {
        let image: GrayImage = ImageBuffer::from_raw(3, 2, vec![
            1, 2, 3,
            4, 5, 6]).unwrap();

        let expected = [1, 2, 4, 7, 10];

        let mut buffer = [0; 5];
        row_running_sum(&image, 0, &mut buffer, 1);

        assert_eq!(buffer, expected);
    }

    #[bench]
    fn bench_row_running_sum(b: &mut test::Bencher) {
        let image = gray_bench_image(1000, 1);
        let mut buffer = [0; 1010];
        b.iter(|| {
            row_running_sum(&image, 0, &mut buffer, 5);
            });
    }

    #[test]
    fn test_column_running_sum() {
        let image: GrayImage = ImageBuffer::from_raw(2, 3, vec![
            1, 4,
            2, 5,
            3, 6]).unwrap();

        let expected = [1, 2, 4, 7, 10];

        let mut buffer = [0; 5];
        column_running_sum(&image, 0, &mut buffer, 1);

        assert_eq!(buffer, expected);
    }

    #[bench]
    fn bench_column_running_sum(b: &mut test::Bencher) {
        let image = gray_bench_image(100, 1000);
        let mut buffer = [0; 1010];
        b.iter(|| {
            column_running_sum(&image, 0, &mut buffer, 5);
            });
    }
}
<commit_msg>Formatting<commit_after>//! Functions for computing [integral images](https://en.wikipedia.org/wiki/Summed_area_table)
//! and running sums of rows and columns.

extern crate image;

use image::{
    Luma,
    GenericImage,
    ImageBuffer
};

use definitions::{
    VecBuffer
};

/// Computes the integral image of an 8bpp grayscale image.
/// I is the integral image of an image F if I(x, y) is the
/// sum of F(x', y') for x' <= x, y' <= y. i.e. each pixel
/// in the integral image contains the sum of the pixel intensities
/// of all input pixels that are above it and to its left.
/// The integral image has the helpful property that it lets us
/// compute the sum of pixel intensities from any rectangular region
/// in the input image in constant time.
/// Specifically, given a rectangle in F with clockwise corners
/// A, B, C, D, with A at the upper left, the total pixel intensity
/// of this rectangle is I(C) - I(B) - I(D) + I(A).
// TODO: Support more formats.
// TODO: This is extremely slow. Fix that!
pub fn integral_image<I>(image: &I) -> VecBuffer<Luma<u32>>
    where I: GenericImage<Pixel=Luma<u8>> {
    padded_integral_image(image, 0, 0)
}

/// Computes the integral image of the result of padding image
/// with its boundary pixels for x_padding columns on either
/// side and y_padding rows at its top and bottom.
/// Returned image has width image.width() + 2 * x_padding
/// and height image.height() + 2 * y_padding.
pub fn padded_integral_image<I>(image: &I, x_padding: u32, y_padding: u32) -> VecBuffer<Luma<u32>>
    where I: GenericImage<Pixel=Luma<u8>>
{
    let (in_width, in_height) = image.dimensions();
    let out_width = in_width + 2 * x_padding;
    let out_height = in_height + 2 * y_padding;

    let mut out: ImageBuffer<Luma<u32>, Vec<u32>> = ImageBuffer::new(out_width, out_height);

    for y in 0..out_height {
        let y_in: u32;
        if y < y_padding {
            y_in = 0;
        }
        else if y >= in_height + y_padding {
            y_in = in_height - 1;
        }
        else {
            y_in = y - y_padding;
        }

        for x in 0..out_width {
            let x_in: u32;
            if x < x_padding {
                x_in = 0;
            }
            else if x >= in_width + x_padding {
                x_in = in_width - 1;
            }
            else {
                x_in = x - x_padding;
            }

            unsafe {
                let p = image.unsafe_get_pixel(x_in, y_in);
                out.unsafe_put_pixel(x, y, Luma([p[0] as u32]));
            }
        }
    }

    for x in 1..out_width {
        (*out.get_pixel_mut(x, 0))[0] += unsafe { out.unsafe_get_pixel(x - 1, 0)[0] };
    }

    for y in 1..out_height {
        (*out.get_pixel_mut(0, y))[0] += unsafe { out.unsafe_get_pixel(0, y - 1)[0] };

        for x in 1..out_width {
            (*out.get_pixel_mut(x, y))[0] += unsafe { out.unsafe_get_pixel(x, y - 1)[0] };
            (*out.get_pixel_mut(x, y))[0] += unsafe { out.unsafe_get_pixel(x - 1, y)[0] };
            (*out.get_pixel_mut(x, y))[0] -= unsafe { out.unsafe_get_pixel(x - 1, y - 1)[0] };
        }
    }

    out
}

/// Computes the running sum of one row of image, padded
/// at the beginning and end. The padding is by continuity.
/// Takes a reference to buffer so that this can be reused
/// for all rows in an image.
// TODO: faster, more formats
pub fn row_running_sum<I>(image: &I, row: u32, buffer: &mut [u32], padding: u32)
    where I: GenericImage<Pixel=Luma<u8>> {

    let width = image.width();
    assert!(buffer.len() >= (width + 2 * padding) as usize,
        format!("Buffer length {} is less than {} + 2 * {}", buffer.len(), width, padding));
    assert!(row < image.height(),
        format!("row out of bound: {} >= {}", row, image.height()));

    for x in 0..padding {
        buffer[x as usize] = unsafe { image.unsafe_get_pixel(0, row)[0] as u32 };
    }

    for x in 0..width {
        let idx = (x + padding) as usize;
        buffer[idx] = unsafe { image.unsafe_get_pixel(x, row)[0] as u32 };
    }

    for x in 0..padding {
        let idx = (x + width + padding) as usize;
        buffer[idx] = unsafe { image.unsafe_get_pixel(width - 1, row)[0] as u32 };
    }

    for x in 1..width + 2 * padding {
        buffer[x as usize] += buffer[(x - 1) as usize] as u32;
    }
}

/// Computes the running sum of one column of image, padded
/// at the top and bottom. The padding is by continuity.
/// Takes a reference to buffer so that this can be reused
/// for all columns in an image.
// TODO: faster, more formats
pub fn column_running_sum<I>(image: &I, column: u32, buffer: &mut [u32], padding: u32)
    where I: GenericImage<Pixel=Luma<u8>> {

    let height = image.height();
    assert!(buffer.len() >= (height + 2 * padding) as usize,
        format!("Buffer length {} is less than {} + 2 * {}", buffer.len(), height, padding));
    assert!(column < image.width(),
        format!("column out of bound: {} >= {}", column, image.width()));

    for y in 0..padding {
        buffer[y as usize] = unsafe { image.unsafe_get_pixel(column, 0)[0] as u32 };
    }

    for y in 0..height {
        let idx = (y + padding) as usize;
        buffer[idx] = unsafe { image.unsafe_get_pixel(column, y)[0] as u32 };
    }

    for y in 0..padding {
        let idx = (y + height + padding) as usize;
        buffer[idx] = unsafe { image.unsafe_get_pixel(column, height - 1)[0] as u32 };
    }

    for y in 1..height + 2 * padding {
        buffer[y as usize] += buffer[(y - 1) as usize] as u32;
    }
}

#[cfg(test)]
mod test {

    use super::{
        column_running_sum,
        integral_image,
        padded_integral_image,
        row_running_sum
    };
    use utils::{
        gray_bench_image
    };
    use image::{
        GrayImage,
        ImageBuffer,
        Luma
    };
    use test;

    #[test]
    fn test_integral_image() {
        let image: GrayImage = ImageBuffer::from_raw(3, 2, vec![
            1, 2, 3,
            4, 5, 6]).unwrap();

        let expected: ImageBuffer<Luma<u32>, Vec<u32>>
            = ImageBuffer::from_raw(3, 2, vec![
            1,  3,  6,
            5, 12, 21]).unwrap();

        assert_pixels_eq!(integral_image(&image), expected);
    }

    #[bench]
    fn bench_integral_image(b: &mut test::Bencher) {
        let image = gray_bench_image(500, 500);
        b.iter(|| {
            let integral = integral_image(&image);
            test::black_box(integral);
            });
    }

    #[test]
    fn test_padded_integral_image() {
        let image: GrayImage = ImageBuffer::from_raw(3, 2, vec![
            1, 2, 3,
            4, 5, 6]).unwrap();

        let expected: ImageBuffer<Luma<u32>, Vec<u32>>
            = ImageBuffer::from_raw(5, 4, vec![
              1,  2,   4,   7,  10,
              2,  4,   8,  14,  20,
              6, 12,  21,  33,  45,
             10, 20,  34,  52,  70]).unwrap();

        assert_pixels_eq!(padded_integral_image(&image, 1, 1), expected);
    }

    #[test]
    fn test_row_running_sum() {
        let image: GrayImage = ImageBuffer::from_raw(3, 2, vec![
            1, 2, 3,
            4, 5, 6]).unwrap();

        let expected = [1, 2, 4, 7, 10];

        let mut buffer = [0; 5];
        row_running_sum(&image, 0, &mut buffer, 1);

        assert_eq!(buffer, expected);
    }

    #[bench]
    fn bench_row_running_sum(b: &mut test::Bencher) {
        let image = gray_bench_image(1000, 1);
        let mut buffer = [0; 1010];
        b.iter(|| {
            row_running_sum(&image, 0, &mut buffer, 5);
            });
    }

    #[test]
    fn test_column_running_sum() {
        let image: GrayImage = ImageBuffer::from_raw(2, 3, vec![
            1, 4,
            2, 5,
            3, 6]).unwrap();

        let expected = [1, 2, 4, 7, 10];

        let mut buffer = [0; 5];
        column_running_sum(&image, 0, &mut buffer, 1);

        assert_eq!(buffer, expected);
    }

    #[bench]
    fn bench_column_running_sum(b: &mut test::Bencher) {
        let image = gray_bench_image(100, 1000);
        let mut buffer = [0; 1010];
        b.iter(|| {
            column_running_sum(&image, 0, &mut buffer, 5);
            });
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use super::table::Table;

pub struct Database {
    tables: HashMap<String, Table>
}

impl Database {
    pub fn new() -> Database {
        Database{
            tables: HashMap::new()
        }
    }

    fn create_table(&mut self, name: String)  {
        let tmp = Table::new();
        self.tables.insert(name, tmp);
    }
}

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

}
<commit_msg>can create a table without a schema<commit_after>use std::collections::HashMap;
use super::table::Table;

pub struct Database {
    tables: HashMap<String, Table>
}

impl Database {
    pub fn new() -> Database {
        Database{
            tables: HashMap::new()
        }
    }

    fn create_table(&mut self, name: &str)  {
        let tmp = Table::new();
        self.tables.insert(name.to_string(), tmp);
    }
}

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

    #[test]
    fn create_table() {
        let mut db = Database::new();
        db.create_table("Jon");
    }
}
<|endoftext|>


<commit_before>//! The memory module.
//! This module declares a single struct, the Memory Management Unit (MMU).
//! The MMU acts as the system bus, allowing components to communicate with each other, reaches
//! RAM, ROM, I/O registers and more.
use std::fmt;
use std::cell::Cell;

use jeebie::video::gpu::GPU;
use jeebie::cart::Cartridge;
use jeebie::bootrom::DMG_BOOTROM;

/// The Memory Management Unit.
/// Provides access to all mapped memory in the system, including I/O and graphics.
pub struct MMU {
    // TODO: MMU should own RAM/High RAM (8k + 256 bytes), maybe some registers.
    data: [u8; 65536],
    loading_bios: Cell<bool>,

    pub gpu: GPU,
}

impl fmt::Debug for MMU {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "MMU 64k")
    }
}

impl MMU {

    /// Creates a new memory controller with no program loaded, except for the bootrom.
    pub fn new() -> Self {
        MMU {
            loading_bios: Cell::new(true),
            data: [0; 65536],
            gpu: GPU::new(),
        }
    }

    /// Creates a memory controller with the specified cartridge loaded.
    pub fn new_with_rom(cart: &Cartridge) -> Self {
        let mut mmu = MMU::new();
        mmu.load_rom(cart);
        mmu
    }

    fn load_rom(&mut self, cart: &Cartridge) {
        for i in 0..cart.size {
            self.data[i] = cart.data[i];
        }
    }

    /// reads a byte at the memory address specified
    pub fn read_b(&self, addr: u16) -> u8 {
        // when PC first reaches 0x100, the BIOS data is not addressable anymore.
        // using Cell sucks, but this is one of the few cases mentioned by official docs where
        // it is an option. Read is logically an immutable operation, but we need to make the
        // bios unaddressable after its first read.
        if self.loading_bios.get() && addr == 0x0100 {
            self.loading_bios.set(false);
        }

        match addr {
            // bios area, 256B long for regular gameboy.
            0...0x00FF if self.loading_bios.get() => DMG_BOOTROM[(addr & 0xFF) as usize],
            // ROM0 area accessible only when bootrom is disabled (first 256 bytes).
            0x0000...0x00FF if !self.loading_bios.get() => self.data[addr as usize],
            // ROM0 area, this is banked memory, it will swap according to selected bank
            0x0100...0x3FFF => self.data[addr as usize],
            // ROM1 area, 16kB unbanked data
            0x4000...0x7FFF => self.data[addr as usize],
            // Graphics, 8kB VRAM
            0x8000...0x9FFF => self.gpu.read_vram((addr & 0x1FFF) as usize),
            // Switchable RAM bank, 8kB
            0xA000...0xBFFF => {
                // TODO: handle RAM banks
                self.data[addr as usize]
            }
            // Internal RAM, 8kB
            0xC000...0xDFFF => self.data[addr as usize],
            // Echo of internal RAM, this is less than 8k, up to 0xFDFF,
            0xE000...0xFDFF => self.data[(addr - 0x2000) as usize],
            // Sprite attribute memory, 160B
            0xFE00...0xFE9F => self.gpu.read_oam((addr - 0xFE00) as usize),
            // empty
            0xFEA0...0xFEFF | 0xFF4C...0xFF7F => 0,
            // I/O ports
            0xFF00...0xFF4B => {
                match addr & 0xFF {
                    0x40...0x47 => self.gpu.read_register(addr as usize),
                    _ => unimplemented!(),
                }
            },
            // High RAM (zero page), used with LDH instructions
            0xFF80...0xFFFE => self.data[addr as usize],
            // Interrupt Enable register
            0xFFFF => unimplemented!(),

            _ => panic!("tried to read at unknown address: {:04x}", addr),
        }
    }

    pub fn write_b(&mut self, addr: u16, data: u8) {
        match addr {
            // bios area, 256B long for regular gameboy.
            0...0x00FF if self.loading_bios.get() => panic!("Writing to bootrom ${:04x} <- {:02x}", addr, data),
            // ROM0 area, this is banked memory, it will swap according to selected bank
            0x0000...0x3FFF => panic!("Writing to ROM0 ${:04x} <- {:02x}", addr, data),
            // ROM1 area, 16kB unbanked data
            0x4000...0x7FFF => panic!("Writing to ROM1 ${:04x} <- {:02x}", addr, data),
            // Graphics, 8kB VRAM
            0x8000...0x9FFF => self.gpu.write_vram((addr & 0x1FFF) as usize, data),
            // Switchable RAM bank, 8kB
            0xA000...0xBFFF => {
                // TODO: handle RAM banks
                self.data[addr as usize] = data;
            }
            // Internal RAM, 8kB
            0xC000...0xDFFF => {
                self.data[addr as usize] = data;
            },
            // Echo of internal RAM, this is less than 8k, up to 0xFDFF,
            0xE000...0xFDFF => {
                self.data[(addr - 0x2000) as usize] = data;
            },
            // Sprite attribute memory, 160B
            0xFE00...0xFE9F => {
                self.gpu.write_oam((addr - 0xFE00) as usize, data);
            }
            // empty
            0xFEA0...0xFEFF | 0xFF4C...0xFF7F => {},
            // I/O ports
            0xFF00...0xFF4B => {
                match addr & 0xFF {
                    0x40...0x47 => self.gpu.write_register(addr as usize, data),
                    _ => {},
                }
            },
            // High RAM (zero page), used with LDH instructions
            0xFF80...0xFFFE => { self.data[addr as usize] = data; },
            // Interrupt Enable register
            0xFFFF => unimplemented!(),

            _ => panic!("tried to write at unknown address: {:4x}", addr),
        }
    }
}
<commit_msg>Replaced array with vec in MMU<commit_after>//! The memory module.
//! This module declares a single struct, the Memory Management Unit (MMU).
//! The MMU acts as the system bus, allowing components to communicate with each other, reaches
//! RAM, ROM, I/O registers and more.
use std::fmt;
use std::cell::Cell;

use jeebie::video::gpu::GPU;
use jeebie::cart::Cartridge;
use jeebie::bootrom::DMG_BOOTROM;

/// The Memory Management Unit.
/// Provides access to all mapped memory in the system, including I/O and graphics.
pub struct MMU {
    // TODO: MMU should own RAM/High RAM (8k + 256 bytes), maybe some registers.
    data: Vec<u8>,
    loading_bios: Cell<bool>,

    pub gpu: GPU,
}

impl fmt::Debug for MMU {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "MMU 64k")
    }
}

impl MMU {

    /// Creates a new memory controller with no program loaded, except for the bootrom.
    pub fn new() -> Self {
        MMU {
            loading_bios: Cell::new(true),
            data: vec![0; 65536],
            gpu: GPU::new(),
        }
    }

    /// Creates a memory controller with the specified cartridge loaded.
    pub fn new_with_rom(cart: &Cartridge) -> Self {
        let mut mmu = MMU::new();
        mmu.load_rom(cart);
        mmu
    }

    fn load_rom(&mut self, cart: &Cartridge) {
        for i in 0..cart.size {
            self.data[i] = cart.data[i];
        }
    }

    /// reads a byte at the memory address specified
    pub fn read_b(&self, addr: u16) -> u8 {
        // when PC first reaches 0x100, the BIOS data is not addressable anymore.
        // using Cell sucks, but this is one of the few cases mentioned by official docs where
        // it is an option. Read is logically an immutable operation, but we need to make the
        // bios unaddressable after its first read.
        if self.loading_bios.get() && addr == 0x0100 {
            self.loading_bios.set(false);
        }

        match addr {
            // bios area, 256B long for regular gameboy.
            0...0x00FF if self.loading_bios.get() => DMG_BOOTROM[(addr & 0xFF) as usize],
            // ROM0 area accessible only when bootrom is disabled (first 256 bytes).
            0x0000...0x00FF if !self.loading_bios.get() => self.data[addr as usize],
            // ROM0 area, this is banked memory, it will swap according to selected bank
            0x0100...0x3FFF => self.data[addr as usize],
            // ROM1 area, 16kB unbanked data
            0x4000...0x7FFF => self.data[addr as usize],
            // Graphics, 8kB VRAM
            0x8000...0x9FFF => self.gpu.read_vram((addr & 0x1FFF) as usize),
            // Switchable RAM bank, 8kB
            0xA000...0xBFFF => {
                // TODO: handle RAM banks
                self.data[addr as usize]
            }
            // Internal RAM, 8kB
            0xC000...0xDFFF => self.data[addr as usize],
            // Echo of internal RAM, this is less than 8k, up to 0xFDFF,
            0xE000...0xFDFF => self.data[(addr - 0x2000) as usize],
            // Sprite attribute memory, 160B
            0xFE00...0xFE9F => self.gpu.read_oam((addr - 0xFE00) as usize),
            // empty
            0xFEA0...0xFEFF | 0xFF4C...0xFF7F => 0,
            // I/O ports
            0xFF00...0xFF4B => {
                match addr & 0xFF {
                    0x40...0x47 => self.gpu.read_register(addr as usize),
                    _ => unimplemented!(),
                }
            },
            // High RAM (zero page), used with LDH instructions
            0xFF80...0xFFFE => self.data[addr as usize],
            // Interrupt Enable register
            0xFFFF => unimplemented!(),

            _ => panic!("tried to read at unknown address: {:04x}", addr),
        }
    }

    pub fn write_b(&mut self, addr: u16, data: u8) {
        match addr {
            // bios area, 256B long for regular gameboy.
            0...0x00FF if self.loading_bios.get() => panic!("Writing to bootrom ${:04x} <- {:02x}", addr, data),
            // ROM0 area, this is banked memory, it will swap according to selected bank
            0x0000...0x3FFF => panic!("Writing to ROM0 ${:04x} <- {:02x}", addr, data),
            // ROM1 area, 16kB unbanked data
            0x4000...0x7FFF => panic!("Writing to ROM1 ${:04x} <- {:02x}", addr, data),
            // Graphics, 8kB VRAM
            0x8000...0x9FFF => self.gpu.write_vram((addr & 0x1FFF) as usize, data),
            // Switchable RAM bank, 8kB
            0xA000...0xBFFF => {
                // TODO: handle RAM banks
                self.data[addr as usize] = data;
            }
            // Internal RAM, 8kB
            0xC000...0xDFFF => {
                self.data[addr as usize] = data;
            },
            // Echo of internal RAM, this is less than 8k, up to 0xFDFF,
            0xE000...0xFDFF => {
                self.data[(addr - 0x2000) as usize] = data;
            },
            // Sprite attribute memory, 160B
            0xFE00...0xFE9F => {
                self.gpu.write_oam((addr - 0xFE00) as usize, data);
            }
            // empty
            0xFEA0...0xFEFF | 0xFF4C...0xFF7F => {},
            // I/O ports
            0xFF00...0xFF4B => {
                match addr & 0xFF {
                    0x40...0x47 => self.gpu.write_register(addr as usize, data),
                    _ => {},
                }
            },
            // High RAM (zero page), used with LDH instructions
            0xFF80...0xFFFE => { self.data[addr as usize] = data; },
            // Interrupt Enable register
            0xFFFF => unimplemented!(),

            _ => panic!("tried to write at unknown address: {:4x}", addr),
        }
    }
}
<|endoftext|>


<commit_before>use crate::{generate::*, inner::*, norm::Norm, types::*};
use ndarray::*;

/// Iterative orthogonalizer using modified Gram-Schmit procedure
#[derive(Debug, Clone)]
pub struct MGS {
    /// Dimension of base space
    dimension: usize,
    /// Basis of spanned space
    q: Vec<Array1>,
}

/// Q-matrix (unitary)
pub type Q = Array2;
/// R-matrix (upper triangle)
pub type R = Array2;

impl MGS {
    /// Create empty linear space
    ///
    /// ```rust
    /// # use ndarray_linalg::*;
    /// const N: usize = 5;
    /// let mgs = arnoldi::MGS::<f32>::new(N);
    /// assert_eq!(mgs.dim(), N);
    /// assert_eq!(mgs.len(), 0);
    /// ```
    pub fn new(dimension: usize) -> Self {
        Self {
            dimension,
            q: Vec::new(),
        }
    }

    pub fn dim(&self) -> usize {
        self.dimension
    }

    pub fn len(&self) -> usize {
        self.q.len()
    }

    /// Orthogonalize given vector using current basis
    ///
    /// Panic
    /// -------
    /// - if the size of the input array mismaches to the dimension
    pub fn orthogonalize<S>(&self, a: &mut ArrayBase<S, Ix1>) -> Array1
    where
        A: Lapack,
        S: DataMut<Elem = A>,
    {
        assert_eq!(a.len(), self.dim());
        let mut coef = Array1::zeros(self.len() + 1);
        for i in 0..self.len() {
            let q = &self.q[i];
            let c = q.inner(&a);
            azip!(mut a (&mut *a), q (q) in { *a = *a - c * q } );
            coef[i] = c;
        }
        let nrm = a.norm_l2();
        coef[self.len()] = A::from_real(nrm);
        coef
    }

    /// Add new vector if the residual is larger than relative tolerance
    ///
    /// Panic
    /// -------
    /// - if the size of the input array mismaches to the dimension
    ///
    /// ```rust
    /// # use ndarray::*;
    /// # use ndarray_linalg::*;
    /// let mut mgs = arnoldi::MGS::new(3);
    /// let coef = mgs.append(array![1.0, 0.0, 0.0], 1e-9).unwrap();
    /// close_l2(&coef, &array![1.0], 1e-9).unwrap();
    ///
    /// let coef = mgs.append(array![1.0, 1.0, 0.0], 1e-9).unwrap();
    /// close_l2(&coef, &array![1.0, 1.0], 1e-9).unwrap();
    ///
    /// assert!(mgs.append(array![1.0, 2.0, 0.0], 1e-9).is_none());  // Cannot append dependent vector
    /// ```
    pub fn append<S>(&mut self, a: ArrayBase<S, Ix1>, rtol: A::Real) -> Option<Array1>
    where
        A: Lapack,
        S: Data<Elem = A>,
    {
        let mut a = a.into_owned();
        let coef = self.orthogonalize(&mut a);
        let nrm = coef[coef.len() - 1].re();
        if nrm < rtol {
            // Linearly dependent
            return None;
        }
        azip!(mut a in { *a = *a / A::from_real(nrm) });
        self.q.push(a);
        Some(coef)
    }

    /// Get orthogonal basis as Q matrix
    pub fn get_q(&self) -> Q {
        hstack(&self.q).unwrap()
    }
}

pub fn mgs(iter: impl Iterator<Item = ArrayBase<S, Ix1>>, dim: usize, rtol: A::Real) -> (Q, R)
where
    A: Scalar + Lapack,
    S: Data<Elem = A>,
{
    let mut ortho = MGS::new(dim);
    let mut coefs = Vec::new();
    for a in iter {
        match ortho.append(a, rtol) {
            Some(coef) => coefs.push(coef),
            None => break,
        }
    }
    let m = coefs.len();
    let mut r = Array2::zeros((m, m));
    for i in 0..m {
        for j in 0..=i {
            r[(j, i)] = coefs[i][j];
        }
    }
    (ortho.get_q(), r)
}
<commit_msg>Strategy for linearly dependent vectors<commit_after>use crate::{generate::*, inner::*, norm::Norm, types::*};
use ndarray::*;

/// Iterative orthogonalizer using modified Gram-Schmit procedure
#[derive(Debug, Clone)]
pub struct MGS {
    /// Dimension of base space
    dimension: usize,
    /// Basis of spanned space
    q: Vec<Array1>,
}

/// Q-matrix (unitary)
pub type Q = Array2;
/// R-matrix (upper triangle)
pub type R = Array2;

impl MGS {
    /// Create empty linear space
    ///
    /// ```rust
    /// # use ndarray_linalg::*;
    /// const N: usize = 5;
    /// let mgs = arnoldi::MGS::<f32>::new(N);
    /// assert_eq!(mgs.dim(), N);
    /// assert_eq!(mgs.len(), 0);
    /// ```
    pub fn new(dimension: usize) -> Self {
        Self {
            dimension,
            q: Vec::new(),
        }
    }

    pub fn dim(&self) -> usize {
        self.dimension
    }

    pub fn len(&self) -> usize {
        self.q.len()
    }

    /// Orthogonalize given vector using current basis
    ///
    /// Panic
    /// -------
    /// - if the size of the input array mismaches to the dimension
    pub fn orthogonalize<S>(&self, a: &mut ArrayBase<S, Ix1>) -> Array1
    where
        A: Lapack,
        S: DataMut<Elem = A>,
    {
        assert_eq!(a.len(), self.dim());
        let mut coef = Array1::zeros(self.len() + 1);
        for i in 0..self.len() {
            let q = &self.q[i];
            let c = q.inner(&a);
            azip!(mut a (&mut *a), q (q) in { *a = *a - c * q } );
            coef[i] = c;
        }
        let nrm = a.norm_l2();
        coef[self.len()] = A::from_real(nrm);
        coef
    }

    /// Add new vector if the residual is larger than relative tolerance
    ///
    /// Panic
    /// -------
    /// - if the size of the input array mismaches to the dimension
    ///
    /// ```rust
    /// # use ndarray::*;
    /// # use ndarray_linalg::*;
    /// let mut mgs = arnoldi::MGS::new(3);
    /// let coef = mgs.append(array![0.0, 1.0, 0.0], 1e-9).unwrap();
    /// close_l2(&coef, &array![1.0], 1e-9).unwrap();
    ///
    /// let coef = mgs.append(array![1.0, 1.0, 0.0], 1e-9).unwrap();
    /// close_l2(&coef, &array![1.0, 1.0], 1e-9).unwrap();
    ///
    /// assert!(mgs.append(array![1.0, 2.0, 0.0], 1e-9).is_err());  // Fail if the vector is linearly dependend
    ///
    /// if let Err(coef) = mgs.append(array![1.0, 2.0, 0.0], 1e-9) {
    ///     close_l2(&coef, &array![2.0, 1.0, 0.0], 1e-9).unwrap(); // You can get coefficients of dependent vector
    /// }
    /// ```
    pub fn append<S>(&mut self, a: ArrayBase<S, Ix1>, rtol: A::Real) -> Result<Array1, Array1>
    where
        A: Lapack,
        S: Data<Elem = A>,
    {
        let mut a = a.into_owned();
        let coef = self.orthogonalize(&mut a);
        let nrm = coef[coef.len() - 1].re();
        if nrm < rtol {
            // Linearly dependent
            return Err(coef);
        }
        azip!(mut a in { *a = *a / A::from_real(nrm) });
        self.q.push(a);
        Ok(coef)
    }

    /// Get orthogonal basis as Q matrix
    pub fn get_q(&self) -> Q {
        hstack(&self.q).unwrap()
    }
}

/// Strategy for linearly dependent vectors appearing in iterative QR decomposition
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Strategy {
    /// Terminate iteration if dependent vector comes
    Terminate,

    /// Skip dependent vector
    Skip,

    /// Orghotonalize dependent vector without adding to Q,
    /// thus R must be non-regular like following:
    ///
    /// ```ignore
    /// x x x x x
    /// 0 x x x x
    /// 0 0 0 x x
    /// 0 0 0 0 x
    /// 0 0 0 0 0   // 0-filled to be square matrix
    /// ```
    Full,
}

pub fn mgs(
    iter: impl Iterator<Item = ArrayBase<S, Ix1>>,
    dim: usize,
    rtol: A::Real,
    strategy: Strategy,
) -> (Q, R)
where
    A: Scalar + Lapack,
    S: Data<Elem = A>,
{
    let mut ortho = MGS::new(dim);
    let mut coefs = Vec::new();
    for a in iter {
        match ortho.append(a, rtol) {
            Ok(coef) => coefs.push(coef),
            Err(coef) => match strategy {
                Strategy::Terminate => break,
                Strategy::Skip => continue,
                Strategy::Full => coefs.push(coef),
            },
        }
    }
    let m = coefs.len();
    let mut r = Array2::zeros((m, m));
    for i in 0..m {
        for j in 0..=i {
            r[(j, i)] = coefs[i][j];
        }
    }
    (ortho.get_q(), r)
}
<|endoftext|>


<commit_before>//! A TOML-parsing library
//!
//! This library is an implementation in Rust of a parser for TOML configuration
//! files [1]. It is focused around high quality errors including specific spans
//! and detailed error messages when things go wrong.
//!
//! This implementation currently passes the language agnostic [test suite][2].
//!
//! # Example
//!
//! ```
//! let toml = r#"
//!     [test]
//!     foo = "bar"
//! "#;
//!
//! let value = toml::Parser::new(toml).parse().unwrap();
//! println!("{}", value);
//! ```
//!
//! # Conversions
//!
//! This library also supports using the standard `Encodable` and `Decodable`
//! traits with TOML values. This library provides the following conversion
//! capabilities:
//!
//! * `String` => `toml::Value` - via `Parser`
//! * `toml::Value` => `String` - via `Show`
//! * `toml::Value` => rust object - via `Decoder`
//! * rust object => `toml::Value` - via `Encoder`
//!
//! Convenience functions for performing multiple conversions at a time are also
//! provided.
//!
//! [1]: https://github.com/mojombo/toml
//! [2]: https://github.com/BurntSushi/toml-test
//!
//! # Lookups
//!
//! Sometimes it might be useful to decode/retrieve only internal
//! nodes. You can use `lookup` to get corresponding value. Note, that
//! it tries its best to traverse both tables and arrays.  In the
//! latter case it expects a zero-based index as a path component
//!
//! ```
//! use std::from_str::FromStr;
//!
//! let toml = r#"
//!      [test]
//!      foo = "bar"
//!
//!      [[values]]
//!      foo = "baz"
//!
//!      [[values]]
//!      foo = "qux"
//! "#;
//! let value: toml::Value = FromStr::from_str(toml).unwrap();
//! let test_foo = value.lookup("test.foo").unwrap();
//! println!("test_foo is {}", test_foo);
//! assert_eq!(test_foo.as_str().unwrap(), "bar");
//! let foo1 = value.lookup("values.1.foo").unwrap();
//! println!("foo1 is {}", foo1);
//! assert_eq!(foo1.as_str().unwrap(), "qux");
//! ```

#![crate_type = "lib"]
#![feature(macro_rules)]
#![deny(warnings, missing_doc)]
#![allow(visible_private_types)]

extern crate serialize;

use std::collections::HashMap;
use std::from_str::FromStr;

pub use parser::{Parser, Error};
pub use serialization::{Encoder, encode, encode_str};
pub use serialization::{Decoder, decode, decode_str};
pub use serialization::{Error, NeedsKey, NoValue};
pub use serialization::{InvalidMapKeyLocation, InvalidMapKeyType};

mod parser;
mod show;
mod serialization;
#[cfg(test)] mod test;

/// Representation of a TOML value.
#[deriving(PartialEq, Clone)]
#[allow(missing_doc)]
pub enum Value {
    String(String),
    Integer(i64),
    Float(f64),
    Boolean(bool),
    Datetime(String),
    Array(Array),
    Table(Table),
}

pub type Array = Vec<Value>;
pub type Table = HashMap<String, Value>;

impl Value {
    /// Tests whether this and another value have the same type.
    pub fn same_type(&self, other: &Value) -> bool {
        match (self, other) {
            (&String(..), &String(..)) |
            (&Integer(..), &Integer(..)) |
            (&Float(..), &Float(..)) |
            (&Boolean(..), &Boolean(..)) |
            (&Datetime(..), &Datetime(..)) |
            (&Array(..), &Array(..)) |
            (&Table(..), &Table(..)) => true,

            _ => false,
        }
    }

    /// Returns a human-readable representation of the type of this value.
    pub fn type_str(&self) -> &'static str {
        match *self {
            String(..) => "string",
            Integer(..) => "integer",
            Float(..) => "float",
            Boolean(..) => "boolean",
            Datetime(..) => "datetime",
            Array(..) => "array",
            Table(..) => "table",
        }
    }

    /// Extracts the string of this value if it is a string.
    pub fn as_str<'a>(&'a self) -> Option<&'a str> {
        match *self { String(ref s) => Some(s.as_slice()), _ => None }
    }

    /// Extracts the integer value if it is an integer.
    pub fn as_integer(&self) -> Option<i64> {
        match *self { Integer(i) => Some(i), _ => None }
    }

    /// Extracts the float value if it is a float.
    pub fn as_float(&self) -> Option<f64> {
        match *self { Float(f) => Some(f), _ => None }
    }

    /// Extracts the boolean value if it is a boolean.
    pub fn as_bool(&self) -> Option<bool> {
        match *self { Boolean(b) => Some(b), _ => None }
    }

    /// Extracts the datetime value if it is a datetime.
    ///
    /// Note that a parsed TOML value will only contain ISO 8601 dates. An
    /// example date is:
    ///
    /// ```notrust
    /// 1979-05-27T07:32:00Z
    /// ```
    pub fn as_datetime<'a>(&'a self) -> Option<&'a str> {
        match *self { Datetime(ref s) => Some(s.as_slice()), _ => None }
    }

    /// Extracts the array value if it is an array.
    pub fn as_slice<'a>(&'a self) -> Option<&'a [Value]> {
        match *self { Array(ref s) => Some(s.as_slice()), _ => None }
    }

    /// Extracts the table value if it is a table.
    pub fn as_table<'a>(&'a self) -> Option<&'a Table> {
        match *self { Table(ref s) => Some(s), _ => None }
    }

    /// Lookups for value at specified path.
    ///
    /// Uses '.' as a path separator.
    ///
    /// Note: arrays have zero-based indexes.
    pub fn lookup<'a>(&'a self, path: &'a str) -> Option<&'a Value> {
        Value::lookup_path(self, path.split('.'))
    }

    // Performs actual traverse starting with value
    //
    // For arrays tries to convert key to uint and retrieve
    // corresponding element
    fn lookup_path<'a, I:Iterator<&'a str>>(value: &'a Value,
                                            components: I) -> Option<&'a Value>{
        let mut cur_value: &'a Value = value;
        let mut iter = components;
        for key in iter {
            match cur_value {
                &Table(ref hm) => {
                    match hm.find_equiv::<'a>(&key) {
                        Some(v) => cur_value = v,
                        _ => return None
                    }
                },
                &Array(ref v) => {
                    let idx: Option<uint> = FromStr::from_str(key);
                    match idx {
                        Some(idx) => cur_value = v.get::<'a>(idx),
                        _ => return None
                    }
                },
                _ => return None
            }
        };

        Some(cur_value)
    }
}

impl FromStr for Value {
    fn from_str(s: &str) -> Option<Value> {
        Parser::new(s).parse().map(Table)
    }
}
<commit_msg>Added separate tests, moved comments<commit_after>//! A TOML-parsing library
//!
//! This library is an implementation in Rust of a parser for TOML configuration
//! files [1]. It is focused around high quality errors including specific spans
//! and detailed error messages when things go wrong.
//!
//! This implementation currently passes the language agnostic [test suite][2].
//!
//! # Example
//!
//! ```
//! let toml = r#"
//!     [test]
//!     foo = "bar"
//! "#;
//!
//! let value = toml::Parser::new(toml).parse().unwrap();
//! println!("{}", value);
//! ```
//!
//! # Conversions
//!
//! This library also supports using the standard `Encodable` and `Decodable`
//! traits with TOML values. This library provides the following conversion
//! capabilities:
//!
//! * `String` => `toml::Value` - via `Parser`
//! * `toml::Value` => `String` - via `Show`
//! * `toml::Value` => rust object - via `Decoder`
//! * rust object => `toml::Value` - via `Encoder`
//!
//! Convenience functions for performing multiple conversions at a time are also
//! provided.
//!
//! [1]: https://github.com/mojombo/toml
//! [2]: https://github.com/BurntSushi/toml-test
//!

#![crate_type = "lib"]
#![feature(macro_rules)]
#![deny(warnings, missing_doc)]
#![allow(visible_private_types)]

extern crate serialize;

use std::collections::HashMap;
use std::from_str::FromStr;

pub use parser::{Parser, Error};
pub use serialization::{Encoder, encode, encode_str};
pub use serialization::{Decoder, decode, decode_str};
pub use serialization::{Error, NeedsKey, NoValue};
pub use serialization::{InvalidMapKeyLocation, InvalidMapKeyType};

mod parser;
mod show;
mod serialization;

/// Representation of a TOML value.
#[deriving(PartialEq, Clone)]
#[allow(missing_doc)]
pub enum Value {
    String(String),
    Integer(i64),
    Float(f64),
    Boolean(bool),
    Datetime(String),
    Array(Array),
    Table(Table),
}

pub type Array = Vec<Value>;
pub type Table = HashMap<String, Value>;

impl Value {
    /// Tests whether this and another value have the same type.
    pub fn same_type(&self, other: &Value) -> bool {
        match (self, other) {
            (&String(..), &String(..)) |
            (&Integer(..), &Integer(..)) |
            (&Float(..), &Float(..)) |
            (&Boolean(..), &Boolean(..)) |
            (&Datetime(..), &Datetime(..)) |
            (&Array(..), &Array(..)) |
            (&Table(..), &Table(..)) => true,

            _ => false,
        }
    }

    /// Returns a human-readable representation of the type of this value.
    pub fn type_str(&self) -> &'static str {
        match *self {
            String(..) => "string",
            Integer(..) => "integer",
            Float(..) => "float",
            Boolean(..) => "boolean",
            Datetime(..) => "datetime",
            Array(..) => "array",
            Table(..) => "table",
        }
    }

    /// Extracts the string of this value if it is a string.
    pub fn as_str<'a>(&'a self) -> Option<&'a str> {
        match *self { String(ref s) => Some(s.as_slice()), _ => None }
    }

    /// Extracts the integer value if it is an integer.
    pub fn as_integer(&self) -> Option<i64> {
        match *self { Integer(i) => Some(i), _ => None }
    }

    /// Extracts the float value if it is a float.
    pub fn as_float(&self) -> Option<f64> {
        match *self { Float(f) => Some(f), _ => None }
    }

    /// Extracts the boolean value if it is a boolean.
    pub fn as_bool(&self) -> Option<bool> {
        match *self { Boolean(b) => Some(b), _ => None }
    }

    /// Extracts the datetime value if it is a datetime.
    ///
    /// Note that a parsed TOML value will only contain ISO 8601 dates. An
    /// example date is:
    ///
    /// ```notrust
    /// 1979-05-27T07:32:00Z
    /// ```
    pub fn as_datetime<'a>(&'a self) -> Option<&'a str> {
        match *self { Datetime(ref s) => Some(s.as_slice()), _ => None }
    }

    /// Extracts the array value if it is an array.
    pub fn as_slice<'a>(&'a self) -> Option<&'a [Value]> {
        match *self { Array(ref s) => Some(s.as_slice()), _ => None }
    }

    /// Extracts the table value if it is a table.
    pub fn as_table<'a>(&'a self) -> Option<&'a Table> {
        match *self { Table(ref s) => Some(s), _ => None }
    }

    /// Lookups for value at specified path.
    ///
    /// Uses '.' as a path separator.
    ///
    /// Note: arrays have zero-based indexes.
    ///
    /// ```
    /// let toml = r#"
    ///      [test]
    ///      foo = "bar"
    ///
    ///      [[values]]
    ///      foo = "baz"
    ///
    ///      [[values]]
    ///      foo = "qux"
    /// "#;
    /// let value: toml::Value = from_str(toml).unwrap();
    ///
    /// let foo = value.lookup("test.foo").unwrap();
    /// assert_eq!(foo.as_str().unwrap(), "bar");
    ///
    /// let foo = value.lookup("values.1.foo").unwrap();
    /// assert_eq!(foo.as_str().unwrap(), "qux");
    ///
    /// let no_bar = value.lookup("test.bar");
    /// assert_eq!(no_bar.is_none(), true);
    /// ```
    pub fn lookup<'a>(&'a self, path: &'a str) -> Option<&'a Value> {
        Value::lookup_path(self, path.split('.'))
    }

    // Performs actual traverse starting with value
    //
    // For arrays tries to convert key to uint and retrieve
    // corresponding element
    fn lookup_path<'a, I:Iterator<&'a str>>(value: &'a Value,
                                            components: I) -> Option<&'a Value> {
        let mut cur_value: &'a Value = value;
        let mut iter = components;
        for key in iter {
            match cur_value {
                &Table(ref hm) => {
                    match hm.find_equiv(&key) {
                        Some(v) => cur_value = v,
                        _ => return None
                    }
                },
                &Array(ref v) => {
                    let idx: Option<uint> = FromStr::from_str(key);
                    match idx {
                        Some(idx) if idx < v.len() => cur_value = v.get(idx),
                        _ => return None
                    }
                },
                _ => return None
            }
        };

        Some(cur_value)
    }
}

impl FromStr for Value {
    fn from_str(s: &str) -> Option<Value> {
        Parser::new(s).parse().map(Table)
    }
}

#[cfg(test)]
mod test {
    use super::Value;

    #[test]
    fn lookup_valid() {
        let toml = r#"
              [test]
              foo = "bar"

              [[values]]
              foo = "baz"

              [[values]]
              foo = "qux"
        "#;

        let value: Value = from_str(toml).unwrap();

        let test_foo = value.lookup("test.foo").unwrap();
        assert_eq!(test_foo.as_str().unwrap(), "bar");

        let foo1 = value.lookup("values.1.foo").unwrap();
        assert_eq!(foo1.as_str().unwrap(), "qux");

        let no_bar = value.lookup("test.bar");
        assert!(no_bar.is_none());
    }

    #[test]
    fn lookup_invalid_index() {
        let toml = r#"
            [[values]]
            foo = "baz"
        "#;

        let value: Value = from_str(toml).unwrap();

        let foo = value.lookup("test.foo");
        assert!(foo.is_none());

        let foo = value.lookup("values.100.foo");
        assert!(foo.is_none());

        let foo = value.lookup("values.str.foo");
        assert!(foo.is_none());
    }
}
<|endoftext|>