<commit_before>//! [β-reduction](https://en.wikipedia.org/wiki/Beta_normal_form) for lambda `Term`s

use term::*;
use term::Term::*;
use self::Order::*;

/// Set to `true` to see all the steps of β-reductions. The default is `false`.
pub const SHOW_REDUCTIONS: bool = false;

/// The [evaluation order](https://en.wikipedia.org/wiki/Lambda_calculus#Reduction_strategies) of
/// β-reductions. `Applicative` order will fail to fully reduce expressions containing functions
/// without a normal form, e.g. the Y combinator (they will expand forever). `CallByName`,
/// `HeadSpine` and `CallByValue` orders don't always normalize fully. The default is `Normal`.
#[derive(Debug, PartialEq)]
pub enum Order {
    /// leftmost outermost
    Normal,
    /// leftmost outermost, but not inside abstractions
    CallByName,
    /// leftmost outermost, but abstractions reduced only in head position
    HeadSpine,
    /// `HeadSpine` + `Normal`
    HybridNormal,
    /// leftmost innermost
    Applicative,
    /// leftmost innermost, but not inside abstractions
    CallByValue,
    /// `CallByValue` + `Applicative`
    HybridApplicative
}

/// Applies two `Term`s with substitution and variable update, consuming the first one in the
/// process. It produces an `Error` if the first `Term` is not an `Abs`traction.
///
/// # Example
/// ```
/// use lambda_calculus::reduction::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);

    lhs.unabs()
}

fn _apply(lhs: &mut Term, rhs: &Term, depth: usize) {
    match *lhs {
        Var(i) => if i == depth {
            *lhs = rhs.clone(); // substitute a top-level variable from lhs with rhs
            update_free_variables(lhs, depth - 1, 0); // update indices of free variables 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, depth);
            _apply(lhs.rhs_ref_mut().unwrap(), rhs, depth)
        }
    }
}

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

/// Performs β-reduction on a `Term` with the specified evaluation `Order` and an optional limit on
/// number of reductions.
///
/// # Example
///
/// ```
/// use lambda_calculus::arithmetic::pred;
/// use lambda_calculus::reduction::beta;
/// use lambda_calculus::reduction::Order::Normal;
///
/// let pred_one = pred().app(1.into());
///
/// assert_eq!(beta(pred_one, &Normal, None), 0.into());
/// ```
pub fn beta(mut term: Term, order: &Order, limit: Option<usize>) -> Term {
    term.beta(order, limit);
    term
}

impl Term {
    /// Applies `self` to another `Term` and performs substitution, consuming `self` in the process.
    /// It produces an `Error` if `self` is not an `Abs`traction.
    ///
    /// # Example
    /// ```
    /// 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!(lhs.apply(&rhs), Ok(result));
    /// ```
    pub fn apply(self, rhs: &Term) -> Result<Term, Error> {
        apply(self, rhs)
    }

    /// Reduces an `App`lication by substitution and variable update. It produces an `Error` if
    /// `self` is not an `App`lication.
    ///
    /// # 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()).eval(), Ok(zero()));
    /// ```
    pub fn eval(self) -> Result<Term, Error> {
        let (lhs, rhs) = try!(self.unapp());

        apply(lhs, &rhs)
    }

    fn eval_with_info(&mut self, depth: u32) {
        if SHOW_REDUCTIONS { print!("    {} reduces to ", show_precedence(self, 0, depth)) };
        let copy = self.clone();
        *self = copy.eval().unwrap();
        if SHOW_REDUCTIONS { println!("{}", show_precedence(self, 0, depth)) };
    }

    fn is_reducible(&self, limit: &mut Option<usize>) -> bool {
        self.lhs_ref().unwrap().unabs_ref().is_ok() &&
        (limit.is_none() || limit.is_some() && limit.unwrap() > 0)
    }

    /// Performs β-reduction on a `Term` with the specified evaluation `Order` and an optional
    /// limit on number of reductions.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::pred;
    /// use lambda_calculus::reduction::Order::Normal;
    ///
    /// let mut pred_one = pred().app(1.into());
    /// pred_one.beta(&Normal, None);
    ///
    /// assert_eq!(pred_one, 0.into());
    /// ```
    pub fn beta(&mut self, order: &Order, mut limit: Option<usize>) {
        match *order {
            CallByName        => self.beta_cbn(0, &mut limit),
            Normal            => self.beta_nor(0, &mut limit),
            CallByValue       => self.beta_cbv(0, &mut limit),
            Applicative       => self.beta_app(0, &mut limit),
            HeadSpine         => self.beta_hs(0, &mut limit),
            HybridNormal      => self.beta_hnor(0, &mut limit),
            HybridApplicative => self.beta_happ(0, &mut limit)
        }
    }

    fn beta_cbn(&mut self, depth: u32, limit: &mut Option<usize>) {
        if let Some(0) = *limit { return }

        match *self {
            App(_, _) => {
                self.lhs_ref_mut().unwrap().beta_cbn(depth, limit);

                if self.is_reducible(limit) {
                    self.eval_with_info(depth);
                    if let Some(l) = *limit { *limit = Some(l - 1) }
                    self.beta_cbn(depth, limit);
                }
            },
            _ => ()
        }
    }

    fn beta_nor(&mut self, depth: u32, limit: &mut Option<usize>) {
        if let Some(0) = *limit { return }

        match *self {
            Var(_) => (),
            Abs(_) => self.unabs_ref_mut().unwrap().beta_nor(depth + 1, limit),
            App(_, _) => {
                self.lhs_ref_mut().unwrap().beta_cbn(depth, limit);

                if self.is_reducible(limit) {
                    self.eval_with_info(depth);
                    if let Some(l) = *limit { *limit = Some(l - 1) }
                    self.beta_nor(depth, limit);
                } else {
                    self.lhs_ref_mut().unwrap().beta_nor(depth, limit);
                    self.rhs_ref_mut().unwrap().beta_nor(depth, limit);
                }
            }
        }
    }

    fn beta_cbv(&mut self, depth: u32, limit: &mut Option<usize>) {
        if let Some(0) = *limit { return }

        match *self {
            App(_, _) => {
                self.lhs_ref_mut().unwrap().beta_cbv(depth, limit);
                self.rhs_ref_mut().unwrap().beta_cbv(depth, limit);

                if self.is_reducible(limit) {
                    self.eval_with_info(depth);
                    if let Some(l) = *limit { *limit = Some(l - 1) }
                    self.beta_cbv(depth, limit);
                }
            },
            _ => ()
        }
    }

    fn beta_app(&mut self, depth: u32, limit: &mut Option<usize>) {
        if let Some(0) = *limit { return }

        match *self {
            Var(_) => (),
            Abs(_) => self.unabs_ref_mut().unwrap().beta_app(depth + 1, limit),
            App(_, _) => {
                self.lhs_ref_mut().unwrap().beta_app(depth, limit);
                self.rhs_ref_mut().unwrap().beta_app(depth, limit);

                if self.is_reducible(limit) {
                    self.eval_with_info(depth);
                    if let Some(l) = *limit { *limit = Some(l - 1) }
                    self.beta_app(depth, limit);
                }
            }
        }

        if let Some(0) = *limit { return }
    }

    fn beta_happ(&mut self, depth: u32, limit: &mut Option<usize>) {
        if let Some(0) = *limit { return }

        match *self {
            Var(_) => (),
            Abs(_) => self.unabs_ref_mut().unwrap().beta_happ(depth + 1, limit),
            App(_, _) => {
                self.lhs_ref_mut().unwrap().beta_cbv(depth, limit);
                self.rhs_ref_mut().unwrap().beta_happ(depth, limit);

                if self.is_reducible(limit) {
                    self.eval_with_info(depth);
                    if let Some(l) = *limit { *limit = Some(l - 1) }
                    self.beta_happ(depth, limit);
                } else {
                    self.lhs_ref_mut().unwrap().beta_happ(depth, limit);
                }
            }
        }
    }

    fn beta_hs(&mut self, depth: u32, limit: &mut Option<usize>) {
        if let Some(0) = *limit { return }

        match *self {
            Var(_) => (),
            Abs(_) => self.unabs_ref_mut().unwrap().beta_hs(depth + 1, limit),
            App(_, _) => {
                self.lhs_ref_mut().unwrap().beta_cbn(depth, limit);

                if self.is_reducible(limit) {
                    self.eval_with_info(depth);
                    if let Some(l) = *limit { *limit = Some(l - 1) }
                    self.beta_hs(depth, limit)
                }
            }
        }
    }

    fn beta_hnor(&mut self, depth: u32, limit: &mut Option<usize>) {
        if let Some(0) = *limit { return }

        match *self {
            Var(_) => (),
            Abs(_) => self.unabs_ref_mut().unwrap().beta_hnor(depth + 1, limit),
            App(_, _) => {
                self.lhs_ref_mut().unwrap().beta_hs(depth, limit);

                if self.is_reducible(limit) {
                    self.eval_with_info(depth);
                    if let Some(l) = *limit { *limit = Some(l - 1) }
                    self.beta_hnor(depth, limit)
                } else {
                    self.lhs_ref_mut().unwrap().beta_hnor(depth, limit);
                    self.rhs_ref_mut().unwrap().beta_hnor(depth, limit);
                }
            }
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use parser::parse;
    use combinators::i;

    #[test]
    fn normal_order() {
        let reduces_instantly = parse(&"(λλ1)((λλλ((32)1))(λλ2))").unwrap();
        assert_eq!(beta(reduces_instantly.clone(), &Normal, None   ),
                   beta(reduces_instantly,         &Normal, Some(1))
        );

        let should_reduce = parse(&"(λ2)((λ111)(λ111))").unwrap();
        assert_eq!(beta(should_reduce, &Normal, None), Var(1));
    }

    #[test]
    fn call_by_name_order() {
        let mut expr = app(abs(app(i(), Var(1))), app(i(), i()));
        expr.beta(&CallByName, Some(1));
        assert_eq!(expr, app(i(), app(i(), i())));
        expr.beta(&CallByName, Some(1));
        assert_eq!(expr, app(i(), i()));
        expr.beta(&CallByName, Some(1));
        assert_eq!(expr, i());
    }

    #[test]
    fn applicative_order() {
        let expr = parse(&"λ1(((λλλ1)1)((λλ21)1))").unwrap();
        assert_eq!(&format!("{}", beta(expr, &Applicative, Some(1))), "λ1((λλ1)((λλ21)1))");

        let expands = parse(&"(λ2)((λ111)(λ111))").unwrap();
        assert_eq!(&format!("{}", beta(expands, &Applicative, Some(1))), "(λ2)((λ111)(λ111)(λ111))");
    }

    #[test]
    fn call_by_value_order() {
        let mut expr = app(abs(app(i(), Var(1))), app(i(), i()));
        expr.beta(&CallByValue, Some(1));
        assert_eq!(expr, app(abs(app(i(), Var(1))), i()));
        expr.beta(&CallByValue, Some(1));
        assert_eq!(expr, app(i(), i()));
        expr.beta(&CallByValue, Some(1));
        assert_eq!(expr, i());
    }
}
<commit_msg>nicer display of reductions<commit_after>//! [β-reduction](https://en.wikipedia.org/wiki/Beta_normal_form) for lambda `Term`s

use term::*;
use term::Term::*;
use self::Order::*;

/// Set to `true` to see all the steps of β-reductions. The default is `false`.
pub const SHOW_REDUCTIONS: bool = false;

/// The [evaluation order](https://en.wikipedia.org/wiki/Lambda_calculus#Reduction_strategies) of
/// β-reductions. `Applicative` order will fail to fully reduce expressions containing functions
/// without a normal form, e.g. the Y combinator (they will expand forever). `CallByName`,
/// `HeadSpine` and `CallByValue` orders don't always normalize fully. The default is `Normal`.
#[derive(Debug, PartialEq)]
pub enum Order {
    /// leftmost outermost
    Normal,
    /// leftmost outermost, but not inside abstractions
    CallByName,
    /// leftmost outermost, but abstractions reduced only in head position
    HeadSpine,
    /// `HeadSpine` + `Normal`
    HybridNormal,
    /// leftmost innermost
    Applicative,
    /// leftmost innermost, but not inside abstractions
    CallByValue,
    /// `CallByValue` + `Applicative`
    HybridApplicative
}

/// Applies two `Term`s with substitution and variable update, consuming the first one in the
/// process. It produces an `Error` if the first `Term` is not an `Abs`traction.
///
/// # Example
/// ```
/// use lambda_calculus::reduction::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);

    lhs.unabs()
}

fn _apply(lhs: &mut Term, rhs: &Term, depth: usize) {
    match *lhs {
        Var(i) => if i == depth {
            *lhs = rhs.clone(); // substitute a top-level variable from lhs with rhs
            update_free_variables(lhs, depth - 1, 0); // update indices of free variables 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, depth);
            _apply(lhs.rhs_ref_mut().unwrap(), rhs, depth)
        }
    }
}

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

/// Performs β-reduction on a `Term` with the specified evaluation `Order` and an optional limit on
/// number of reductions.
///
/// # Example
///
/// ```
/// use lambda_calculus::arithmetic::pred;
/// use lambda_calculus::reduction::beta;
/// use lambda_calculus::reduction::Order::Normal;
///
/// let pred_one = pred().app(1.into());
///
/// assert_eq!(beta(pred_one, &Normal, None), 0.into());
/// ```
pub fn beta(mut term: Term, order: &Order, limit: Option<usize>) -> Term {
    term.beta(order, limit);
    term
}

impl Term {
    /// Applies `self` to another `Term` and performs substitution, consuming `self` in the process.
    /// It produces an `Error` if `self` is not an `Abs`traction.
    ///
    /// # Example
    /// ```
    /// 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!(lhs.apply(&rhs), Ok(result));
    /// ```
    pub fn apply(self, rhs: &Term) -> Result<Term, Error> {
        apply(self, rhs)
    }

    /// Reduces an `App`lication by substitution and variable update. It produces an `Error` if
    /// `self` is not an `App`lication.
    ///
    /// # 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()).eval(), Ok(zero()));
    /// ```
    pub fn eval(self) -> Result<Term, Error> {
        let (lhs, rhs) = try!(self.unapp());

        apply(lhs, &rhs)
    }

    fn eval_with_info(&mut self, depth: u32) {
        if SHOW_REDUCTIONS { print!("\n{} reduces to ", show_precedence(self, 0, depth)) };
        let copy = self.clone();
        *self = copy.eval().unwrap();
        if SHOW_REDUCTIONS { println!("{}", show_precedence(self, 0, depth)) };
    }

    fn is_reducible(&self, limit: &mut Option<usize>) -> bool {
        self.lhs_ref().unwrap().unabs_ref().is_ok() &&
        (limit.is_none() || limit.is_some() && limit.unwrap() > 0)
    }

    /// Performs β-reduction on a `Term` with the specified evaluation `Order` and an optional
    /// limit on number of reductions.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::pred;
    /// use lambda_calculus::reduction::Order::Normal;
    ///
    /// let mut pred_one = pred().app(1.into());
    /// pred_one.beta(&Normal, None);
    ///
    /// assert_eq!(pred_one, 0.into());
    /// ```
    pub fn beta(&mut self, order: &Order, mut limit: Option<usize>) {
        if SHOW_REDUCTIONS { println!("reducing {}:", self) };
        match *order {
            CallByName        => self.beta_cbn(0, &mut limit),
            Normal            => self.beta_nor(0, &mut limit),
            CallByValue       => self.beta_cbv(0, &mut limit),
            Applicative       => self.beta_app(0, &mut limit),
            HeadSpine         => self.beta_hs(0, &mut limit),
            HybridNormal      => self.beta_hnor(0, &mut limit),
            HybridApplicative => self.beta_happ(0, &mut limit)
        }
        if SHOW_REDUCTIONS { println!("\n{}", self) };
    }

    fn beta_cbn(&mut self, depth: u32, limit: &mut Option<usize>) {
        if let Some(0) = *limit { return }

        match *self {
            App(_, _) => {
                self.lhs_ref_mut().unwrap().beta_cbn(depth, limit);

                if self.is_reducible(limit) {
                    self.eval_with_info(depth);
                    if let Some(l) = *limit { *limit = Some(l - 1) }
                    self.beta_cbn(depth, limit);
                }
            },
            _ => ()
        }
    }

    fn beta_nor(&mut self, depth: u32, limit: &mut Option<usize>) {
        if let Some(0) = *limit { return }

        match *self {
            Var(_) => (),
            Abs(_) => self.unabs_ref_mut().unwrap().beta_nor(depth + 1, limit),
            App(_, _) => {
                self.lhs_ref_mut().unwrap().beta_cbn(depth, limit);

                if self.is_reducible(limit) {
                    self.eval_with_info(depth);
                    if let Some(l) = *limit { *limit = Some(l - 1) }
                    self.beta_nor(depth, limit);
                } else {
                    self.lhs_ref_mut().unwrap().beta_nor(depth, limit);
                    self.rhs_ref_mut().unwrap().beta_nor(depth, limit);
                }
            }
        }
    }

    fn beta_cbv(&mut self, depth: u32, limit: &mut Option<usize>) {
        if let Some(0) = *limit { return }

        match *self {
            App(_, _) => {
                self.lhs_ref_mut().unwrap().beta_cbv(depth, limit);
                self.rhs_ref_mut().unwrap().beta_cbv(depth, limit);

                if self.is_reducible(limit) {
                    self.eval_with_info(depth);
                    if let Some(l) = *limit { *limit = Some(l - 1) }
                    self.beta_cbv(depth, limit);
                }
            },
            _ => ()
        }
    }

    fn beta_app(&mut self, depth: u32, limit: &mut Option<usize>) {
        if let Some(0) = *limit { return }

        match *self {
            Var(_) => (),
            Abs(_) => self.unabs_ref_mut().unwrap().beta_app(depth + 1, limit),
            App(_, _) => {
                self.lhs_ref_mut().unwrap().beta_app(depth, limit);
                self.rhs_ref_mut().unwrap().beta_app(depth, limit);

                if self.is_reducible(limit) {
                    self.eval_with_info(depth);
                    if let Some(l) = *limit { *limit = Some(l - 1) }
                    self.beta_app(depth, limit);
                }
            }
        }

        if let Some(0) = *limit { return }
    }

    fn beta_happ(&mut self, depth: u32, limit: &mut Option<usize>) {
        if let Some(0) = *limit { return }

        match *self {
            Var(_) => (),
            Abs(_) => self.unabs_ref_mut().unwrap().beta_happ(depth + 1, limit),
            App(_, _) => {
                self.lhs_ref_mut().unwrap().beta_cbv(depth, limit);
                self.rhs_ref_mut().unwrap().beta_happ(depth, limit);

                if self.is_reducible(limit) {
                    self.eval_with_info(depth);
                    if let Some(l) = *limit { *limit = Some(l - 1) }
                    self.beta_happ(depth, limit);
                } else {
                    self.lhs_ref_mut().unwrap().beta_happ(depth, limit);
                }
            }
        }
    }

    fn beta_hs(&mut self, depth: u32, limit: &mut Option<usize>) {
        if let Some(0) = *limit { return }

        match *self {
            Var(_) => (),
            Abs(_) => self.unabs_ref_mut().unwrap().beta_hs(depth + 1, limit),
            App(_, _) => {
                self.lhs_ref_mut().unwrap().beta_cbn(depth, limit);

                if self.is_reducible(limit) {
                    self.eval_with_info(depth);
                    if let Some(l) = *limit { *limit = Some(l - 1) }
                    self.beta_hs(depth, limit)
                }
            }
        }
    }

    fn beta_hnor(&mut self, depth: u32, limit: &mut Option<usize>) {
        if let Some(0) = *limit { return }

        match *self {
            Var(_) => (),
            Abs(_) => self.unabs_ref_mut().unwrap().beta_hnor(depth + 1, limit),
            App(_, _) => {
                self.lhs_ref_mut().unwrap().beta_hs(depth, limit);

                if self.is_reducible(limit) {
                    self.eval_with_info(depth);
                    if let Some(l) = *limit { *limit = Some(l - 1) }
                    self.beta_hnor(depth, limit)
                } else {
                    self.lhs_ref_mut().unwrap().beta_hnor(depth, limit);
                    self.rhs_ref_mut().unwrap().beta_hnor(depth, limit);
                }
            }
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use parser::parse;
    use combinators::i;

    #[test]
    fn normal_order() {
        let reduces_instantly = parse(&"(λλ1)((λλλ((32)1))(λλ2))").unwrap();
        assert_eq!(beta(reduces_instantly.clone(), &Normal, None   ),
                   beta(reduces_instantly,         &Normal, Some(1))
        );

        let should_reduce = parse(&"(λ2)((λ111)(λ111))").unwrap();
        assert_eq!(beta(should_reduce, &Normal, None), Var(1));
    }

    #[test]
    fn call_by_name_order() {
        let mut expr = app(abs(app(i(), Var(1))), app(i(), i()));
        expr.beta(&CallByName, Some(1));
        assert_eq!(expr, app(i(), app(i(), i())));
        expr.beta(&CallByName, Some(1));
        assert_eq!(expr, app(i(), i()));
        expr.beta(&CallByName, Some(1));
        assert_eq!(expr, i());
    }

    #[test]
    fn applicative_order() {
        let expr = parse(&"λ1(((λλλ1)1)((λλ21)1))").unwrap();
        assert_eq!(&format!("{}", beta(expr, &Applicative, Some(1))), "λ1((λλ1)((λλ21)1))");

        let expands = parse(&"(λ2)((λ111)(λ111))").unwrap();
        assert_eq!(&format!("{}", beta(expands, &Applicative, Some(1))), "(λ2)((λ111)(λ111)(λ111))");
    }

    #[test]
    fn call_by_value_order() {
        let mut expr = app(abs(app(i(), Var(1))), app(i(), i()));
        expr.beta(&CallByValue, Some(1));
        assert_eq!(expr, app(abs(app(i(), Var(1))), i()));
        expr.beta(&CallByValue, Some(1));
        assert_eq!(expr, app(i(), i()));
        expr.beta(&CallByValue, Some(1));
        assert_eq!(expr, i());
    }
}
<|endoftext|>


<commit_before>#![feature(test)]
#![cfg_attr(all(feature="i128_support", feature="nightly"), allow(stable_features))] // stable since 2018-03-27
#![cfg_attr(all(feature="i128_support", feature="nightly"), feature(i128_type, i128))]

extern crate test;
extern crate rand;
extern crate rand_xorshift;

const RAND_BENCH_N: u64 = 1000;

use std::mem::size_of;
use test::Bencher;

use rand::{Rng, FromEntropy};
use rand_xorshift::XorShiftRng;
use rand::distributions::*;

macro_rules! distr_int {
    ($fnn:ident, $ty:ty, $distr:expr) => {
        #[bench]
        fn $fnn(b: &mut Bencher) {
            let mut rng = XorShiftRng::from_entropy();
            let distr = $distr;

            b.iter(|| {
                let mut accum = 0 as $ty;
                for _ in 0..::RAND_BENCH_N {
                    let x: $ty = distr.sample(&mut rng);
                    accum = accum.wrapping_add(x);
                }
                accum
            });
            b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N;
        }
    }
}

macro_rules! distr_float {
    ($fnn:ident, $ty:ty, $distr:expr) => {
        #[bench]
        fn $fnn(b: &mut Bencher) {
            let mut rng = XorShiftRng::from_entropy();
            let distr = $distr;

            b.iter(|| {
                let mut accum = 0.0;
                for _ in 0..::RAND_BENCH_N {
                    let x: $ty = distr.sample(&mut rng);
                    accum += x;
                }
                accum
            });
            b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N;
        }
    }
}

macro_rules! distr {
    ($fnn:ident, $ty:ty, $distr:expr) => {
        #[bench]
        fn $fnn(b: &mut Bencher) {
            let mut rng = XorShiftRng::from_entropy();
            let distr = $distr;

            b.iter(|| {
                let mut accum = 0u32;
                for _ in 0..::RAND_BENCH_N {
                    let x: $ty = distr.sample(&mut rng);
                    accum = accum.wrapping_add(x as u32);
                }
                accum
            });
            b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N;
        }
    }
}

// uniform
distr_int!(distr_uniform_i8, i8, Uniform::new(20i8, 100));
distr_int!(distr_uniform_i16, i16, Uniform::new(-500i16, 2000));
distr_int!(distr_uniform_i32, i32, Uniform::new(-200_000_000i32, 800_000_000));
distr_int!(distr_uniform_i64, i64, Uniform::new(3i64, 123_456_789_123));
#[cfg(feature = "i128_support")]
distr_int!(distr_uniform_i128, i128, Uniform::new(-123_456_789_123i128, 123_456_789_123_456_789));

distr_float!(distr_uniform_f32, f32, Uniform::new(2.26f32, 2.319));
distr_float!(distr_uniform_f64, f64, Uniform::new(2.26f64, 2.319));

// standard
distr_int!(distr_standard_i8, i8, Standard);
distr_int!(distr_standard_i16, i16, Standard);
distr_int!(distr_standard_i32, i32, Standard);
distr_int!(distr_standard_i64, i64, Standard);
#[cfg(feature = "i128_support")]
distr_int!(distr_standard_i128, i128, Standard);

distr!(distr_standard_bool, bool, Standard);
distr!(distr_standard_alphanumeric, char, Alphanumeric);
distr!(distr_standard_codepoint, char, Standard);

distr_float!(distr_standard_f32, f32, Standard);
distr_float!(distr_standard_f64, f64, Standard);
distr_float!(distr_open01_f32, f32, Open01);
distr_float!(distr_open01_f64, f64, Open01);
distr_float!(distr_openclosed01_f32, f32, OpenClosed01);
distr_float!(distr_openclosed01_f64, f64, OpenClosed01);

// distributions
distr_float!(distr_exp, f64, Exp::new(1.23 * 4.56));
distr_float!(distr_normal, f64, Normal::new(-1.23, 4.56));
distr_float!(distr_log_normal, f64, LogNormal::new(-1.23, 4.56));
distr_float!(distr_gamma_large_shape, f64, Gamma::new(10., 1.0));
distr_float!(distr_gamma_small_shape, f64, Gamma::new(0.1, 1.0));
distr_float!(distr_cauchy, f64, Cauchy::new(4.2, 6.9));
distr_int!(distr_binomial, u64, Binomial::new(20, 0.7));
distr_int!(distr_poisson, u64, Poisson::new(4.0));
distr!(distr_bernoulli, bool, Bernoulli::new(0.18));

// Weighted
distr_int!(distr_weighted_i8, usize, WeightedIndex::new(&[1i8, 2, 3, 4, 12, 0, 2, 1]).unwrap());
distr_int!(distr_weighted_u32, usize, WeightedIndex::new(&[1u32, 2, 3, 4, 12, 0, 2, 1]).unwrap());
distr_int!(distr_weighted_f64, usize, WeightedIndex::new(&[1.0f64, 0.001, 1.0/3.0, 4.01, 0.0, 3.3, 22.0, 0.001]).unwrap());
distr_int!(distr_weighted_large_set, usize, WeightedIndex::new((0..10000).rev().chain(1..10001)).unwrap());

// construct and sample from a range
macro_rules! gen_range_int {
    ($fnn:ident, $ty:ident, $low:expr, $high:expr) => {
        #[bench]
        fn $fnn(b: &mut Bencher) {
            let mut rng = XorShiftRng::from_entropy();

            b.iter(|| {
                let mut high = $high;
                let mut accum: $ty = 0;
                for _ in 0..::RAND_BENCH_N {
                    accum = accum.wrapping_add(rng.gen_range($low, high));
                    // force recalculation of range each time
                    high = high.wrapping_add(1) & std::$ty::MAX;
                }
                accum
            });
            b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N;
        }
    }
}

gen_range_int!(gen_range_i8, i8, -20i8, 100);
gen_range_int!(gen_range_i16, i16, -500i16, 2000);
gen_range_int!(gen_range_i32, i32, -200_000_000i32, 800_000_000);
gen_range_int!(gen_range_i64, i64, 3i64, 123_456_789_123);
#[cfg(feature = "i128_support")]
gen_range_int!(gen_range_i128, i128, -12345678901234i128, 123_456_789_123_456_789);

// construct and sample from a floating-point range
macro_rules! gen_range_float {
    ($fnn:ident, $ty:ident, $low:expr, $high:expr) => {
        #[bench]
        fn $fnn(b: &mut Bencher) {
            let mut rng = XorShiftRng::from_entropy();

            b.iter(|| {
                let mut high = $high;
                let mut low = $low;
                let mut accum: $ty = 0.0;
                for _ in 0..::RAND_BENCH_N {
                    accum += rng.gen_range(low, high);
                    // force recalculation of range each time
                    low += 0.9;
                    high += 1.1;
                }
                accum
            });
            b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N;
        }
    }
}

gen_range_float!(gen_range_f32, f32, -20000.0f32, 100000.0);
gen_range_float!(gen_range_f64, f64, 123.456f64, 7890.12);

#[bench]
fn dist_iter(b: &mut Bencher) {
    let mut rng = XorShiftRng::from_entropy();
    let distr = Normal::new(-2.71828, 3.14159);
    let mut iter = distr.sample_iter(&mut rng);

    b.iter(|| {
        let mut accum = 0.0;
        for _ in 0..::RAND_BENCH_N {
            accum += iter.next().unwrap();
        }
        accum
    });
    b.bytes = size_of::<f64>() as u64 * ::RAND_BENCH_N;
}
<commit_msg>Prefer `SmallRng` in benchmarks<commit_after>#![feature(test)]
#![cfg_attr(all(feature="i128_support", feature="nightly"), allow(stable_features))] // stable since 2018-03-27
#![cfg_attr(all(feature="i128_support", feature="nightly"), feature(i128_type, i128))]

extern crate test;
extern crate rand;

const RAND_BENCH_N: u64 = 1000;

use std::mem::size_of;
use test::Bencher;

use rand::{Rng, FromEntropy};
use rand::rngs::SmallRng;
use rand::distributions::*;

macro_rules! distr_int {
    ($fnn:ident, $ty:ty, $distr:expr) => {
        #[bench]
        fn $fnn(b: &mut Bencher) {
            let mut rng = SmallRng::from_entropy();
            let distr = $distr;

            b.iter(|| {
                let mut accum = 0 as $ty;
                for _ in 0..::RAND_BENCH_N {
                    let x: $ty = distr.sample(&mut rng);
                    accum = accum.wrapping_add(x);
                }
                accum
            });
            b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N;
        }
    }
}

macro_rules! distr_float {
    ($fnn:ident, $ty:ty, $distr:expr) => {
        #[bench]
        fn $fnn(b: &mut Bencher) {
            let mut rng = SmallRng::from_entropy();
            let distr = $distr;

            b.iter(|| {
                let mut accum = 0.0;
                for _ in 0..::RAND_BENCH_N {
                    let x: $ty = distr.sample(&mut rng);
                    accum += x;
                }
                accum
            });
            b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N;
        }
    }
}

macro_rules! distr {
    ($fnn:ident, $ty:ty, $distr:expr) => {
        #[bench]
        fn $fnn(b: &mut Bencher) {
            let mut rng = SmallRng::from_entropy();
            let distr = $distr;

            b.iter(|| {
                let mut accum = 0u32;
                for _ in 0..::RAND_BENCH_N {
                    let x: $ty = distr.sample(&mut rng);
                    accum = accum.wrapping_add(x as u32);
                }
                accum
            });
            b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N;
        }
    }
}

// uniform
distr_int!(distr_uniform_i8, i8, Uniform::new(20i8, 100));
distr_int!(distr_uniform_i16, i16, Uniform::new(-500i16, 2000));
distr_int!(distr_uniform_i32, i32, Uniform::new(-200_000_000i32, 800_000_000));
distr_int!(distr_uniform_i64, i64, Uniform::new(3i64, 123_456_789_123));
#[cfg(feature = "i128_support")]
distr_int!(distr_uniform_i128, i128, Uniform::new(-123_456_789_123i128, 123_456_789_123_456_789));

distr_float!(distr_uniform_f32, f32, Uniform::new(2.26f32, 2.319));
distr_float!(distr_uniform_f64, f64, Uniform::new(2.26f64, 2.319));

// standard
distr_int!(distr_standard_i8, i8, Standard);
distr_int!(distr_standard_i16, i16, Standard);
distr_int!(distr_standard_i32, i32, Standard);
distr_int!(distr_standard_i64, i64, Standard);
#[cfg(feature = "i128_support")]
distr_int!(distr_standard_i128, i128, Standard);

distr!(distr_standard_bool, bool, Standard);
distr!(distr_standard_alphanumeric, char, Alphanumeric);
distr!(distr_standard_codepoint, char, Standard);

distr_float!(distr_standard_f32, f32, Standard);
distr_float!(distr_standard_f64, f64, Standard);
distr_float!(distr_open01_f32, f32, Open01);
distr_float!(distr_open01_f64, f64, Open01);
distr_float!(distr_openclosed01_f32, f32, OpenClosed01);
distr_float!(distr_openclosed01_f64, f64, OpenClosed01);

// distributions
distr_float!(distr_exp, f64, Exp::new(1.23 * 4.56));
distr_float!(distr_normal, f64, Normal::new(-1.23, 4.56));
distr_float!(distr_log_normal, f64, LogNormal::new(-1.23, 4.56));
distr_float!(distr_gamma_large_shape, f64, Gamma::new(10., 1.0));
distr_float!(distr_gamma_small_shape, f64, Gamma::new(0.1, 1.0));
distr_float!(distr_cauchy, f64, Cauchy::new(4.2, 6.9));
distr_int!(distr_binomial, u64, Binomial::new(20, 0.7));
distr_int!(distr_poisson, u64, Poisson::new(4.0));
distr!(distr_bernoulli, bool, Bernoulli::new(0.18));

// Weighted
distr_int!(distr_weighted_i8, usize, WeightedIndex::new(&[1i8, 2, 3, 4, 12, 0, 2, 1]).unwrap());
distr_int!(distr_weighted_u32, usize, WeightedIndex::new(&[1u32, 2, 3, 4, 12, 0, 2, 1]).unwrap());
distr_int!(distr_weighted_f64, usize, WeightedIndex::new(&[1.0f64, 0.001, 1.0/3.0, 4.01, 0.0, 3.3, 22.0, 0.001]).unwrap());
distr_int!(distr_weighted_large_set, usize, WeightedIndex::new((0..10000).rev().chain(1..10001)).unwrap());

// construct and sample from a range
macro_rules! gen_range_int {
    ($fnn:ident, $ty:ident, $low:expr, $high:expr) => {
        #[bench]
        fn $fnn(b: &mut Bencher) {
            let mut rng = SmallRng::from_entropy();

            b.iter(|| {
                let mut high = $high;
                let mut accum: $ty = 0;
                for _ in 0..::RAND_BENCH_N {
                    accum = accum.wrapping_add(rng.gen_range($low, high));
                    // force recalculation of range each time
                    high = high.wrapping_add(1) & std::$ty::MAX;
                }
                accum
            });
            b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N;
        }
    }
}

gen_range_int!(gen_range_i8, i8, -20i8, 100);
gen_range_int!(gen_range_i16, i16, -500i16, 2000);
gen_range_int!(gen_range_i32, i32, -200_000_000i32, 800_000_000);
gen_range_int!(gen_range_i64, i64, 3i64, 123_456_789_123);
#[cfg(feature = "i128_support")]
gen_range_int!(gen_range_i128, i128, -12345678901234i128, 123_456_789_123_456_789);

// construct and sample from a floating-point range
macro_rules! gen_range_float {
    ($fnn:ident, $ty:ident, $low:expr, $high:expr) => {
        #[bench]
        fn $fnn(b: &mut Bencher) {
            let mut rng = SmallRng::from_entropy();

            b.iter(|| {
                let mut high = $high;
                let mut low = $low;
                let mut accum: $ty = 0.0;
                for _ in 0..::RAND_BENCH_N {
                    accum += rng.gen_range(low, high);
                    // force recalculation of range each time
                    low += 0.9;
                    high += 1.1;
                }
                accum
            });
            b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N;
        }
    }
}

gen_range_float!(gen_range_f32, f32, -20000.0f32, 100000.0);
gen_range_float!(gen_range_f64, f64, 123.456f64, 7890.12);

#[bench]
fn dist_iter(b: &mut Bencher) {
    let mut rng = SmallRng::from_entropy();
    let distr = Normal::new(-2.71828, 3.14159);
    let mut iter = distr.sample_iter(&mut rng);

    b.iter(|| {
        let mut accum = 0.0;
        for _ in 0..::RAND_BENCH_N {
            accum += iter.next().unwrap();
        }
        accum
    });
    b.bytes = size_of::<f64>() as u64 * ::RAND_BENCH_N;
}
<|endoftext|>


<commit_before>#![feature(test)]

extern crate test;
extern crate bit_vec;
extern crate bio;

use test::Bencher;

use bio::data_structures::interval_tree::*;
use std::cmp::{min, max};
use std::ops::Range;

#[bench]
fn bench_interval_few_large_queries(b: &mut Bencher) {
    // insert 100_000 intervals of size 10
    // do 1000 queries, each resulting in 1000 matches
    b.iter(|| test_insert_query(10, (100_000..200_000), 1000, (105_000..106_000)));
}

#[bench]
fn bench_interval_many_small_queries(b: &mut Bencher) {
    // insert 100_000 intervals of size 10
    // do 100_000 queries, each resulting in at most 10 matches
    b.iter(|| test_insert_query(10, (100_000..200_000), 10, (99_995..199_995)));
}

fn test_insert_query(insert_size: i64,
                     insert_bounds: Range<i64>,
                     query_size: i64,
                     query_bounds: Range<i64>) {
    let mut tree: IntervalTree<i64, Range<i64>> = IntervalTree::new();

    for i in insert_bounds.clone() {
        tree.insert((i..i + insert_size), (i..i + insert_size));
    }
    for i in query_bounds {
        let lower_bound = i;
        let upper_bound = i + query_size;
        let smallest_start = max(lower_bound - insert_size + 1, insert_bounds.start.clone());
        let largest_start = min(upper_bound, insert_bounds.end.clone());
        let mut expected_intersections = vec![];
        for j in smallest_start..largest_start {
            expected_intersections.push(j..j + insert_size);
        }
        assert_intersections(&tree, (lower_bound..upper_bound), expected_intersections);
    }
}

fn assert_intersections(tree: &IntervalTree<i64, Range<i64>>,
                        target: Range<i64>,
                        expected_results: Vec<Range<i64>>) {
    let mut actual_entries: Vec<Entry<i64, Range<i64>>> = tree.find(&target).collect();
    actual_entries.sort_by(|x1, x2| x1.data().start.cmp(&x2.data().start));
    let mut expected_entries: Vec<(Range<i64>, Range<i64>)> =
        expected_results.iter().map(|x| (x.clone(), x.clone())).collect();
    expected_entries.sort_by(|x1, x2| x1.1.start.cmp(&x2.1.start));
    assert_eq!(actual_entries.len(), expected_entries.len());
    for (actual, expected) in actual_entries.iter().zip(expected_entries.iter()) {
        assert_eq!(actual.interval(), &expected.0);
        assert_eq!(actual.data(), &expected.1);
    }
}
<commit_msg>Fix bench for updated IntervalTree<commit_after>#![feature(test)]

extern crate test;
extern crate bit_vec;
extern crate bio;

use test::Bencher;

use bio::data_structures::interval_tree::*;
use std::cmp::{min, max};
use std::ops::Range;

#[bench]
fn bench_interval_few_large_queries(b: &mut Bencher) {
    // insert 100_000 intervals of size 10
    // do 1000 queries, each resulting in 1000 matches
    b.iter(|| test_insert_query(10, (100_000..200_000), 1000, (105_000..106_000)));
}

#[bench]
fn bench_interval_many_small_queries(b: &mut Bencher) {
    // insert 100_000 intervals of size 10
    // do 100_000 queries, each resulting in at most 10 matches
    b.iter(|| test_insert_query(10, (100_000..200_000), 10, (99_995..199_995)));
}

fn test_insert_query(insert_size: i64,
                     insert_bounds: Range<i64>,
                     query_size: i64,
                     query_bounds: Range<i64>) {
    let mut tree: IntervalTree<i64, Range<i64>> = IntervalTree::new();

    for i in insert_bounds.clone() {
        tree.insert((i..i + insert_size), (i..i + insert_size))
            .expect("Error inserting interval");
    }
    for i in query_bounds {
        let lower_bound = i;
        let upper_bound = i + query_size;
        let smallest_start = max(lower_bound - insert_size + 1, insert_bounds.start.clone());
        let largest_start = min(upper_bound, insert_bounds.end.clone());
        let mut expected_intersections = vec![];
        for j in smallest_start..largest_start {
            expected_intersections.push(j..j + insert_size);
        }
        assert_intersections(&tree, (lower_bound..upper_bound), expected_intersections);
    }
}

fn assert_intersections(tree: &IntervalTree<i64, Range<i64>>,
                        target: Range<i64>,
                        expected_results: Vec<Range<i64>>) {
    let mut actual_entries: Vec<Entry<i64, Range<i64>>> = tree.find(&target).unwrap().collect();
    actual_entries.sort_by(|x1, x2| x1.data().start.cmp(&x2.data().start));
    let mut expected_entries: Vec<(Range<i64>, Range<i64>)> =
        expected_results.iter().map(|x| (x.clone(), x.clone())).collect();
    expected_entries.sort_by(|x1, x2| x1.1.start.cmp(&x2.1.start));
    assert_eq!(actual_entries.len(), expected_entries.len());
    for (actual, expected) in actual_entries.iter().zip(expected_entries.iter()) {
        assert_eq!(actual.interval(), &expected.0);
        assert_eq!(actual.data(), &expected.1);
    }
}
<|endoftext|>


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

use std::env;
use std::path::PathBuf;
use std::process::Command;

pub fn run_java(class: &str, arguments: &str, expected_stdout: &[&str], expected_stderr: &[&str]) -> bool {
    let output = Command::new(&java())
        .arg(format!("-agentpath:{}{}", jvmkill().to_str().unwrap(), arguments))
        .arg("-cp").arg(jvmkill_test().to_str().unwrap())
        .arg("-Xmx50m")
        .arg("-XX:ReservedCodeCacheSize=10m")
        .arg(class)
        .output().expect("failed to run Java process");

    assert_contents(&output.stdout, expected_stdout);
    assert_contents(&output.stderr, expected_stderr);

    output.status.success()
}

fn assert_contents(stream: &Vec<u8>, expected :&[&str]) {
    let s = String::from_utf8_lossy(stream);
    println!("OUTPUT:\n{}\n:OUTPUT", s);
    let mut success = true;
    for o in expected {
        if !s.contains(o) {
            println!("{}", o);
            success = false;
        }
    }
    if !success {
        println!("the above were not found in:\n{}", s);
    }
    assert!(success);
}

fn java() -> PathBuf {
    PathBuf::from(env::var("JAVA_HOME").unwrap()).join("bin").join("java")
}

fn jvmkill_test() -> PathBuf {
    env::current_dir().unwrap().parent().unwrap().join("resource-exhaustion-generator").join("target").join("resource-exhaustion-generator-0.0.0.jar")
}

fn jvmkill() -> PathBuf {
    let lib_name = if cfg!(target_os = "macos") {
        "libjvmkill.dylib"
    } else {
        "libjvmkill.so"
    };

    env::var("LD_LIBRARY_PATH").or(env::var("DYLD_LIBRARY_PATH")).unwrap()
        .split(":")
        .map(|root| PathBuf::from(root).join(lib_name))
        .find(|path| path.exists()).unwrap()
}
<commit_msg>Reduce native memory allocation errors<commit_after>/*
 * Copyright (c) 2015-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use std::env;
use std::path::PathBuf;
use std::process::Command;

pub fn run_java(class: &str, arguments: &str, expected_stdout: &[&str], expected_stderr: &[&str]) -> bool {
    let output = Command::new(&java())
        .arg(format!("-agentpath:{}{}", jvmkill().to_str().unwrap(), arguments))
        .arg("-cp").arg(jvmkill_test().to_str().unwrap())
        .arg("-Xmx50m")
        .arg("-XX:ReservedCodeCacheSize=10m")
        .arg("-XX:-UseCompressedOops")
        .arg(class)
        .output().expect("failed to run Java process");

    assert_contents(&output.stdout, expected_stdout);
    assert_contents(&output.stderr, expected_stderr);

    output.status.success()
}

fn assert_contents(stream: &Vec<u8>, expected :&[&str]) {
    let s = String::from_utf8_lossy(stream);
    println!("OUTPUT:\n{}\n:OUTPUT", s);
    let mut success = true;
    for o in expected {
        if !s.contains(o) {
            println!("{}", o);
            success = false;
        }
    }
    if !success {
        println!("the above were not found in:\n{}", s);
    }
    assert!(success);
}

fn java() -> PathBuf {
    PathBuf::from(env::var("JAVA_HOME").unwrap()).join("bin").join("java")
}

fn jvmkill_test() -> PathBuf {
    env::current_dir().unwrap().parent().unwrap().join("resource-exhaustion-generator").join("target").join("resource-exhaustion-generator-0.0.0.jar")
}

fn jvmkill() -> PathBuf {
    let lib_name = if cfg!(target_os = "macos") {
        "libjvmkill.dylib"
    } else {
        "libjvmkill.so"
    };

    env::var("LD_LIBRARY_PATH").or(env::var("DYLD_LIBRARY_PATH")).unwrap()
        .split(":")
        .map(|root| PathBuf::from(root).join(lib_name))
        .find(|path| path.exists()).unwrap()
}
<|endoftext|>


<commit_before>//! Data types for addresses to nodes in DICOM networks.

use std::{
    net::{AddrParseError, SocketAddr, ToSocketAddrs},
    str::FromStr,
};

use snafu::{ResultExt, Snafu};

/// A specification for a full address to the target SCP:
/// an application entity title, plus a network socket address.
///
/// These addresses can be serialized and parsed
/// with the syntax `{ae_title}@{socket_address}`.
///
/// For the version of the struct without a mandatory AE title,
/// see [`AeAddr`].
///
/// # Example
///
/// ```
/// # use dicom_ul::FullAeAddr;
/// # use std::net::SocketAddr;
/// #
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let addr: FullAeAddr = "SCP-STORAGE@127.0.0.1:104".parse()?;
/// assert_eq!(addr.ae_title(), "SCP-STORAGE");
/// assert_eq!(addr.socket_addr(), SocketAddr::from(([127, 0, 0, 1], 104)));
/// assert_eq!(&addr.to_string(), "SCP-STORAGE@127.0.0.1:104");
/// # Ok(())
/// # }
/// ```
#[derive(Debug, Clone, PartialEq)]
pub struct FullAeAddr {
    ae_title: String,
    socket_addr: std::net::SocketAddr,
}

impl FullAeAddr {
    /// Create an AE address from its bare constituent parts.
    pub fn new(ae_title: impl Into<String>, socket_addr: SocketAddr) -> Self {
        FullAeAddr {
            ae_title: ae_title.into(),
            socket_addr,
        }
    }

    /// Retrieve the application entity title portion.
    pub fn ae_title(&self) -> &str {
        &self.ae_title
    }

    /// Retrieve the socket address portion.
    pub fn socket_addr(&self) -> std::net::SocketAddr {
        self.socket_addr
    }
}

impl From<(String, SocketAddr)> for FullAeAddr {
    fn from((ae_title, socket_addr): (String, SocketAddr)) -> Self {
        Self::new(ae_title, socket_addr)
    }
}

#[derive(Debug, Clone, Eq, PartialEq, Snafu)]
pub enum ParseAeAddressError {
    /// Missing `@` in full AE address
    MissingPart,

    /// Could not parse socket address
    ParseSocketAddress { source: AddrParseError },
}

impl FromStr for FullAeAddr {
    type Err = ParseAeAddressError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if let Some((ae_title, addr)) = s.split_once('@') {
            Ok(FullAeAddr {
                ae_title: ae_title.to_string(),
                socket_addr: addr.parse().context(ParseSocketAddressSnafu)?,
            })
        } else {
            Err(ParseAeAddressError::MissingPart)
        }
    }
}

impl ToSocketAddrs for FullAeAddr {
    type Iter = std::option::IntoIter<SocketAddr>;

    fn to_socket_addrs(&self) -> std::io::Result<Self::Iter> {
        self.socket_addr.to_socket_addrs()
    }
}

impl std::fmt::Display for FullAeAddr {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(&self.ae_title)?;
        f.write_str("@")?;
        std::fmt::Display::fmt(&self.socket_addr, f)
    }
}

/// A specification for an address to the target SCP:
/// a network socket address
/// which may also include an application entity title.
///
/// These addresses can be serialized and parsed
/// with the syntax `{ae_title}@{socket_address}`,
/// where the
///
/// For the version of the struct in which the AE title part is mandatory,
/// see [`FullAeAddr`].
///
/// # Example
///
/// ```
/// # use dicom_ul::{AeAddr, FullAeAddr};
/// # use std::net::SocketAddr;
/// #
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let addr: AeAddr = "SCP-STORAGE@127.0.0.1:104".parse()?;
/// assert_eq!(addr.ae_title(), Some("SCP-STORAGE"));
/// assert_eq!(addr.socket_addr(), SocketAddr::from(([127, 0, 0, 1], 104)));
/// assert_eq!(&addr.to_string(), "127.0.0.1:104");
///
/// // AE title can be missing
/// let addr: AeAddr = "192.168.1.99:1045".parse()?;
/// assert_eq!(addr.ae_title(), None);
/// // but can be provided later
/// let full_addr: FullAeAddr = addr.with_ae_title("SCP-QUERY");
/// assert_eq!(full_addr.ae_title(), "SCP-QUERY");
/// # Ok(())
/// # }
/// ```
#[derive(Debug, Clone, PartialEq)]
pub struct AeAddr {
    ae_title: Option<String>,
    socket_addr: std::net::SocketAddr,
}

impl AeAddr {
    /// Create an AE address from its bare constituent parts.
    pub fn new(ae_title: impl Into<String>, socket_addr: SocketAddr) -> Self {
        AeAddr {
            ae_title: Some(ae_title.into()),
            socket_addr,
        }
    }

    /// Create an AE address containing only a socket address.
    pub fn new_socket_addr(socket_addr: SocketAddr) -> Self {
        AeAddr {
            ae_title: None,
            socket_addr,
        }
    }

    /// Retrieve the application entity title portion, if present.
    pub fn ae_title(&self) -> Option<&str> {
        self.ae_title.as_deref()
    }

    /// Retrieve the socket address portion.
    pub fn socket_addr(&self) -> std::net::SocketAddr {
        self.socket_addr
    }

    /// Create a new address with the full application entity target,
    /// discarding any potentially existing AE title.
    pub fn with_ae_title(self, ae_title: impl Into<String>) -> FullAeAddr {
        FullAeAddr {
            ae_title: ae_title.into(),
            socket_addr: self.socket_addr,
        }
    }

    /// Create a new address with the full application entity target,
    /// using the given AE title if it is missing.
    pub fn with_default_ae_title(self, ae_title: impl Into<String>) -> FullAeAddr {
        FullAeAddr {
            ae_title: self.ae_title.unwrap_or_else(|| ae_title.into()),
            socket_addr: self.socket_addr,
        }
    }
}

/// This conversion provides an address without an AE title.
impl From<SocketAddr> for AeAddr {
    fn from(socket_addr: SocketAddr) -> Self {
        AeAddr {
            ae_title: None,
            socket_addr,
        }
    }
}

impl From<FullAeAddr> for AeAddr {
    fn from(full: FullAeAddr) -> Self {
        AeAddr {
            ae_title: Some(full.ae_title),
            socket_addr: full.socket_addr,
        }
    }
}

impl FromStr for AeAddr {
    type Err = std::net::AddrParseError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if let Some((ae_title, address)) = s.split_once('@') {
            Ok(AeAddr {
                ae_title: Some(ae_title.to_string()),
                socket_addr: address.parse()?,
            })
        } else {
            Ok(AeAddr {
                ae_title: None,
                socket_addr: s.parse()?,
            })
        }
    }
}

impl ToSocketAddrs for AeAddr {
    type Iter = std::option::IntoIter<SocketAddr>;

    fn to_socket_addrs(&self) -> std::io::Result<Self::Iter> {
        self.socket_addr.to_socket_addrs()
    }
}

impl std::fmt::Display for AeAddr {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        if let Some(ae_title) = &self.ae_title {
            f.write_str(ae_title)?;
            f.write_str("@")?;
        }

        std::fmt::Display::fmt(&self.socket_addr, f)
    }
}
<commit_msg>[ul] Rethink AE address<commit_after>//! Data types for addresses to nodes in DICOM networks.

use std::{
    net::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs},
    str::FromStr,
};

use snafu::{ensure, AsErrorSource, ResultExt, Snafu};

/// A specification for a full address to the target SCP:
/// an application entity title, plus a generic  address,
/// typically a socket address.
///
/// These addresses can be serialized and parsed
/// with the syntax `{ae_title}@{address}`.
///
/// For the version of the struct without a mandatory AE title,
/// see [`AeAddr`].
///
/// # Example
///
/// ```
/// # use dicom_ul::FullAeAddr;
/// # use std::net::SocketAddr;
/// #
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// # // socket address can be a string
/// let addr: FullAeAddr<String> = "SCP-STORAGE@127.0.0.1:104".parse()?;
/// assert_eq!(addr.ae_title(), "SCP-STORAGE");
/// assert_eq!(addr.socket_addr(), "127.0.0.1:104");
/// # // or anything else which can be parsed into a socket address
/// let addr: FullAeAddr<SocketAddr> = "SCP-STORAGE@127.0.0.1:104".parse()?;
/// assert_eq!(addr.ae_title(), "SCP-STORAGE");
/// assert_eq!(addr.socket_addr(), &SocketAddr::from(([127, 0, 0, 1], 104)));
/// assert_eq!(&addr.to_string(), "SCP-STORAGE@127.0.0.1:104");
/// # Ok(())
/// # }
/// ```
#[derive(Debug, Clone, PartialEq)]
pub struct FullAeAddr<T> {
    ae_title: String,
    socket_addr: T,
}

impl<T> FullAeAddr<T> {
    /// Create an AE address from its bare constituent parts.
    pub fn new(ae_title: impl Into<String>, socket_addr: T) -> Self {
        FullAeAddr {
            ae_title: ae_title.into(),
            socket_addr,
        }
    }

    /// Retrieve the application entity title portion.
    pub fn ae_title(&self) -> &str {
        &self.ae_title
    }

    /// Retrieve the network address portion.
    pub fn socket_addr(&self) -> &T {
        &self.socket_addr
    }

    /// Convert the full address into its constituent parts.
    pub fn into_parts(self) -> (String, T) {
        (self.ae_title, self.socket_addr)
    }
}

impl<T> From<(String, T)> for FullAeAddr<T> {
    fn from((ae_title, socket_addr): (String, T)) -> Self {
        Self::new(ae_title, socket_addr)
    }
}

/// A error which occurred when parsing an AE address.
#[derive(Debug, Clone, Eq, PartialEq, Snafu)]
pub enum ParseAeAddressError<E>
where
    E: std::fmt::Debug + AsErrorSource,
{
    /// Missing `@` in full AE address
    MissingPart,

    /// Could not parse network socket address
    ParseSocketAddress { source: E },
}

impl<T> FromStr for FullAeAddr<T>
where
    T: FromStr,
    T::Err: std::fmt::Debug + AsErrorSource,
{
    type Err = ParseAeAddressError<<T as FromStr>::Err>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        // !!! there should be a way to escape the `@`
        if let Some((ae_title, addr)) = s.split_once('@') {
            ensure!(!ae_title.is_empty(), MissingPartSnafu);
            Ok(FullAeAddr {
                ae_title: ae_title.to_string(),
                socket_addr: addr.parse().context(ParseSocketAddressSnafu)?,
            })
        } else {
            Err(ParseAeAddressError::MissingPart)
        }
    }
}

impl<T> ToSocketAddrs for FullAeAddr<T>
where
    T: ToSocketAddrs,
{
    type Iter = T::Iter;

    fn to_socket_addrs(&self) -> std::io::Result<Self::Iter> {
        self.socket_addr.to_socket_addrs()
    }
}

impl<T> std::fmt::Display for FullAeAddr<T>
where
    T: std::fmt::Display,
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(&self.ae_title.replace("@", "\\@"))?;
        f.write_str("@")?;
        std::fmt::Display::fmt(&self.socket_addr, f)
    }
}

/// A specification for an address to the target SCP:
/// a generic network socket address
/// which may also include an application entity title.
///
/// These addresses can be serialized and parsed
/// with the syntax `{ae_title}@{socket_address}`,
/// where the
///
/// For the version of the struct in which the AE title part is mandatory,
/// see [`FullAeAddr`].
///
/// # Example
///
/// ```
/// # use dicom_ul::{AeAddr, FullAeAddr};
/// # use std::net::{SocketAddr, SocketAddrV4};
/// #
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let addr: AeAddr<SocketAddrV4> = "SCP-STORAGE@127.0.0.1:104".parse()?;
/// assert_eq!(addr.ae_title(), Some("SCP-STORAGE"));
/// assert_eq!(addr.socket_addr(), &SocketAddrV4::new([127, 0, 0, 1].into(), 104));
/// assert_eq!(&addr.to_string(), "SCP-STORAGE@127.0.0.1:104");
///
/// // AE title can be missing
/// let addr: AeAddr<String> = "192.168.1.99:1045".parse()?;
/// assert_eq!(addr.ae_title(), None);
/// // but can be provided later
/// let full_addr: FullAeAddr<_> = addr.with_ae_title("SCP-QUERY");
/// assert_eq!(full_addr.ae_title(), "SCP-QUERY");
/// assert_eq!(&full_addr.to_string(), "SCP-QUERY@192.168.1.99:1045");
/// # Ok(())
/// # }
/// ```
#[derive(Debug, Clone, PartialEq)]
pub struct AeAddr<T> {
    ae_title: Option<String>,
    socket_addr: T,
}

impl<T> AeAddr<T> {
    /// Create an AE address from its bare constituent parts.
    pub fn new(ae_title: impl Into<String>, socket_addr: T) -> Self {
        AeAddr {
            ae_title: Some(ae_title.into()),
            socket_addr,
        }
    }

    /// Retrieve the application entity title portion, if present.
    pub fn ae_title(&self) -> Option<&str> {
        self.ae_title.as_deref()
    }

    /// Retrieve the socket address portion.
    pub fn socket_addr(&self) -> &T {
        &self.socket_addr
    }

    /// Create a new address with the full application entity target,
    /// discarding any potentially existing AE title.
    pub fn with_ae_title(self, ae_title: impl Into<String>) -> FullAeAddr<T> {
        FullAeAddr {
            ae_title: ae_title.into(),
            socket_addr: self.socket_addr,
        }
    }

    /// Create a new address with the full application entity target,
    /// using the given AE title if it is missing.
    pub fn with_default_ae_title(self, ae_title: impl Into<String>) -> FullAeAddr<T> {
        FullAeAddr {
            ae_title: self.ae_title.unwrap_or_else(|| ae_title.into()),
            socket_addr: self.socket_addr,
        }
    }

    /// Convert the address into its constituent parts.
    pub fn into_parts(self) -> (Option<String>, T) {
        (self.ae_title, self.socket_addr)
    }
}

/// This conversion provides a socket address without an AE title.
impl From<SocketAddr> for AeAddr<SocketAddr> {
    fn from(socket_addr: SocketAddr) -> Self {
        AeAddr {
            ae_title: None,
            socket_addr,
        }
    }
}

/// This conversion provides an IPv4 socket address without an AE title.
impl From<SocketAddrV4> for AeAddr<SocketAddrV4> {
    fn from(socket_addr: SocketAddrV4) -> Self {
        AeAddr {
            ae_title: None,
            socket_addr,
        }
    }
}

/// This conversion provides an IPv6 socket address without an AE title.
impl From<SocketAddrV6> for AeAddr<SocketAddrV6> {
    fn from(socket_addr: SocketAddrV6) -> Self {
        AeAddr {
            ae_title: None,
            socket_addr,
        }
    }
}

impl<T> From<FullAeAddr<T>> for AeAddr<T> {
    fn from(full: FullAeAddr<T>) -> Self {
        AeAddr {
            ae_title: Some(full.ae_title),
            socket_addr: full.socket_addr,
        }
    }
}

impl<T> FromStr for AeAddr<T>
where
    T: FromStr,
{
    type Err = <T as FromStr>::Err;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        // !!! there should be a way to escape the `@`
        if let Some((ae_title, address)) = s.split_once('@') {
            Ok(AeAddr {
                ae_title: Some(ae_title)
                    .filter(|s| !s.is_empty())
                    .map(|s| s.to_string()),
                socket_addr: address.parse()?,
            })
        } else {
            Ok(AeAddr {
                ae_title: None,
                socket_addr: s.parse()?,
            })
        }
    }
}

impl<T> ToSocketAddrs for AeAddr<T>
where
    T: ToSocketAddrs,
{
    type Iter = T::Iter;

    fn to_socket_addrs(&self) -> std::io::Result<Self::Iter> {
        self.socket_addr.to_socket_addrs()
    }
}

impl<T> std::fmt::Display for AeAddr<T>
where
    T: std::fmt::Display,
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let socket_addr = self.socket_addr.to_string();
        if let Some(ae_title) = &self.ae_title {
            f.write_str(&ae_title.replace("@", "\\@"))?;
            f.write_str("@")?;
        } else if socket_addr.contains("@") {
            // if formatted socket address contains a `@`,
            // we need to start the output with `@`
            // so that the start of the socket address
            // is not interpreted as an AE title
            f.write_str("@")?;
        }

        std::fmt::Display::fmt(&socket_addr, f)
    }
}

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

    #[test]
    fn ae_addr_parse() {
        // socket address can be a string
        let addr: FullAeAddr<String> = "SCP-STORAGE@127.0.0.1:104".parse().unwrap();
        assert_eq!(addr.ae_title(), "SCP-STORAGE");
        assert_eq!(addr.socket_addr(), "127.0.0.1:104");

        // or anything else which can be parsed into a socket address
        let addr: FullAeAddr<SocketAddr> = "SCP_STORAGE@127.0.0.1:104".parse().unwrap();
        assert_eq!(addr.ae_title(), "SCP_STORAGE");
        assert_eq!(addr.socket_addr(), &SocketAddr::from(([127, 0, 0, 1], 104)));
        assert_eq!(&addr.to_string(), "SCP_STORAGE@127.0.0.1:104");

        // IPv4 socket address
        let addr: FullAeAddr<SocketAddrV4> = "MAMMOSTORE@10.0.0.11:104".parse().unwrap();
        assert_eq!(addr.ae_title(), "MAMMOSTORE");
        assert_eq!(
            addr.socket_addr(),
            &SocketAddrV4::new([10, 0, 0, 11].into(), 104)
        );
        assert_eq!(&addr.to_string(), "MAMMOSTORE@10.0.0.11:104");
    }

    /// test addresses without an AE title
    #[test]
    fn ae_addr_parse_no_ae() {
        // should fail
        let res = FullAeAddr::<String>::from_str("pacs.hospital.example.com:104");
        assert!(matches!(res, Err(ParseAeAddressError::MissingPart)));
        // should also fail (AE title can't be empty)
        let res = FullAeAddr::<String>::from_str("@pacs.hospital.example.com:104");
        assert!(matches!(res, Err(ParseAeAddressError::MissingPart)));

        // should return an ae addr with no AE title
        let addr: AeAddr<String> = "pacs.hospital.example.com:104".parse().unwrap();
        assert_eq!(addr.ae_title(), None);
        assert_eq!(addr.socket_addr(), "pacs.hospital.example.com:104");
        // should also return an ae addr with no AE title
        let addr: AeAddr<String> = "@pacs.hospital.example.com:104".parse().unwrap();
        assert_eq!(addr.ae_title(), None);
        assert_eq!(addr.socket_addr(), "pacs.hospital.example.com:104");
    }

    #[test]
    fn ae_addr_parse_weird_scenarios() {
        // can parse addresses with multiple @'s
        let addr: FullAeAddr<String> = "ABC@DICOM@pacs.archive.example.com:104".parse().unwrap();
        assert_eq!(addr.ae_title(), "ABC");
        assert_eq!(addr.socket_addr(), "DICOM@pacs.archive.example.com:104");
        assert_eq!(&addr.to_string(), "ABC@DICOM@pacs.archive.example.com:104");
    }
}
<|endoftext|>


<commit_before>extern crate serde;
extern crate serde_json;
extern crate hyper;

#[macro_use]
extern crate serde_derive;

use std::process::{Command};
use std::io::Read;
use hyper::header::ContentType;
use hyper::client::Client;
use serde_json::Value;

#[derive(Serialize, Deserialize)]
struct RPCRequest {
    jsonrpc: String,
    method: String,
    params: Vec<String>,
    id: usize,
}

#[derive(Serialize, Deserialize)]
struct RPCObjectRequest<T> {
    jsonrpc: String,
    method: String,
    params: T,
    id: usize,
}

#[derive(Serialize, Deserialize)]
struct RPCObjectResponse<T> {
    jsonrpc: String,
    result: T,
    id: usize,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct RPCTransaction {
    pub hash: String,
    pub nonce: String,
    pub blockHash: String,
    pub blockNumber: String,
    pub transactionIndex: String,
    pub from: String,
    pub to: String,
    pub value: String,
    pub gas: String,
    pub gasPrice: String,
    pub input: String,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct RPCCall {
    pub from: String,
    pub to: String,
    pub gas: String,
    pub gasPrice: String,
    pub value: String,
    pub data: String,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct RPCBlock {
    pub number: String,
    pub hash: String,
    pub parentHash: String,
    pub nonce: String,
    pub sha3Uncles: String,
    pub logsBloom: String,
    pub transactionsRoot: String,
    pub stateRoot: String,
    pub miner: String,
    pub difficulty: String,
    pub totalDifficulty: String,
    pub extraData: String,
    pub size: String,
    pub gasLimit: String,
    pub gasUsed: String,
    pub timestamp: String,
    pub transactions: Vec<String>,
    pub uncles: Vec<String>
}

#[derive(Serialize, Deserialize, Debug)]
#[serde(untagged)]
pub enum RPCSyncStatus {
    NotSync(bool),
    Sync {
        startingBlock: String,
        currentBlock: String,
        highestBlock: String,
    }
}

#[derive(Serialize, Deserialize, Debug)]
pub struct RPCTransactionReceipt {
    pub transactionHash: String,
    pub transactionIndex: String,
    pub blockHash: String,
    pub blockNumber: String,
    pub cumulativeGasUsed: String,
    pub gasUsed: String,
    pub contractAddress: String,
    pub logs: Vec<String>,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct RPCFilter {
    pub fromBlock: String,
    pub toBlock: String,
    pub address: String,
    pub topics: Vec<String>,
}

pub struct GethRPCClient {
    endpoint: String,
    free_id: usize,
}

impl GethRPCClient {
    pub fn new(endpoint: &str) -> Self {
        GethRPCClient {
            endpoint: endpoint.to_string(),
            free_id: 1,
        }
    }

    fn rpc_request<T: serde::Deserialize>(&mut self, method: &str, params: Vec<String>) -> T {
        self.rpc_object_request::<Vec<String>, T>(method, params)
    }

    fn rpc_object_request<Req: serde::Serialize, Res: serde::Deserialize>(&mut self, method: &str, params: Req) -> Res {
        let request = RPCObjectRequest {
            jsonrpc: "2.0".to_string(),
            method: method.to_string(),
            params: params,
            id: self.free_id,
        };
        self.free_id = self.free_id + 1;

        let client = Client::new();
        let mut response_raw = client.post(&self.endpoint)
            .header(ContentType::json())
            .body(&serde_json::to_string(&request).unwrap())
            .send().unwrap();
        let mut buffer = String::new();
        response_raw.read_to_string(&mut buffer).unwrap();

        let response: RPCObjectResponse<Res> = serde_json::from_str(&buffer).unwrap();
        response.result
    }

    pub fn client_version(&mut self) -> String {
        self.rpc_request::<String>("web3_clientVersion", vec![])
    }

    pub fn net_version(&mut self) -> String {
        self.rpc_request::<String>("net_version", vec![])
    }

    pub fn net_listening(&mut self) -> bool {
        self.rpc_request::<bool>("net_listening", vec![])
    }

    pub fn net_peer_count(&mut self) -> String {
        self.rpc_request::<String>("net_peerCount", vec![])
    }

    pub fn sha3(&mut self, data: &str) -> String {
        self.rpc_request::<String>("web3_sha3", vec![])
    }

    pub fn protocol_version(&mut self) -> String {
        self.rpc_request::<String>("eth_protocolVersion", vec![])
    }

    pub fn syncing(&mut self) -> RPCSyncStatus {
        self.rpc_request::<RPCSyncStatus>("eth_syncing", vec![])
    }

    pub fn coinbase(&mut self) -> String {
        self.rpc_request::<String>("eth_coinbase", vec![])
    }

    pub fn mining(&mut self) -> bool {
        self.rpc_request::<bool>("eth_mining", vec![])
    }

    pub fn hashrate(&mut self) -> String {
        self.rpc_request::<String>("eth_hashrate", vec![])
    }

    pub fn gas_price(&mut self) -> String {
        self.rpc_request::<String>("eth_gasPrice", vec![])
    }

    pub fn accounts(&mut self) -> Vec<String> {
        self.rpc_request::<Vec<String>>("eth_accounts", vec![])
    }

    pub fn block_number(&mut self) -> String {
        self.rpc_request::<String>("eth_blockNumber", vec![])
    }

    pub fn get_balance(&mut self, address: &str, number: &str) -> String {
        self.rpc_request::<String>("eth_getBalance",
                                   vec![address.to_string(), number.to_string()])
    }

    pub fn get_storage_at(&mut self, address: &str, index: &str, number: &str) -> String {
        self.rpc_request::<String>("eth_getStorageAt",
                                   vec![address.to_string(), index.to_string(), number.to_string()])
    }

    pub fn get_transaction_count(&mut self, address: &str, number: &str) -> String {
        self.rpc_request::<String>("eth_getTransactionCount",
                                   vec![address.to_string(), number.to_string()])
    }

    pub fn get_block_transaction_count_by_hash(&mut self, hash: &str) -> String {
        self.rpc_request::<String>("eth_getBlockTransactionCountByHash",
                                   vec![hash.to_string()])
    }

    pub fn get_block_transaction_count_by_number(&mut self, number: &str) -> String {
        self.rpc_request::<String>("eth_getBlockTransactionCountByNumber",
                                   vec![number.to_string()])
    }

    pub fn get_uncle_count_by_block_hash(&mut self, hash: &str) -> String {
        self.rpc_request::<String>("eth_getUncleCountByBlockHash",
                                   vec![hash.to_string()])
    }

    pub fn get_uncle_count_by_block_number(&mut self, number: &str) -> String {
        self.rpc_request::<String>("eth_getUncleCountByBlockNumber",
                                   vec![number.to_string()])
    }

    pub fn get_code(&mut self, address: &str, number: &str) -> String {
        self.rpc_request::<String>("eth_getCode", vec![address.to_string(), number.to_string()])
    }

    pub fn sign(&mut self, address: &str, message: &str) -> String {
        self.rpc_request::<String>("eth_sign", vec![address.to_string(), message.to_string()])
    }

    pub fn send_transaction(&mut self, transaction: RPCTransaction) -> String {
        unimplemented!()
    }

    pub fn send_raw_transaction(&mut self, data: &str) -> String {
        self.rpc_request::<String>("eth_sendRawTransaction", vec![data.to_string()])
    }

    pub fn call(&mut self, transaction: RPCCall, number: &str) -> String {
        self.rpc_object_request::<(RPCCall, String), String>("eth_call", (transaction, number.to_string()))
    }

    pub fn estimate_gas(&mut self, transaction: RPCTransaction) -> String {
        unimplemented!()
    }

    pub fn get_block_by_hash(&mut self, hash: &str) -> RPCBlock {
        self.rpc_request::<RPCBlock>("eth_getBlockByHash",
                                     vec![hash.to_string(), "false".to_string()])
    }

    pub fn get_block_by_number(&mut self, number: &str) -> RPCBlock {
        self.rpc_request::<RPCBlock>("eth_getBlockByNumber",
                                     vec![number.to_string(), "false".to_string()])
    }

    pub fn get_transaction_by_hash(&mut self, hash: &str) -> RPCTransaction {
        self.rpc_request::<RPCTransaction>("eth_getTransactionByHash",
                                           vec![hash.to_string()])
    }

    pub fn get_transaction_by_block_hash_and_index(&mut self, hash: &str, index: &str) -> RPCTransaction {
        self.rpc_request::<RPCTransaction>("eth_getTransactionByBlockHashAndIndex",
                                           vec![hash.to_string(), index.to_string()])
    }

    pub fn get_transaction_by_block_number_and_index(&mut self, number: &str, index: &str)
                                                 -> RPCTransaction {
        self.rpc_request::<RPCTransaction>("eth_getTransactionByBlockNumberAndIndex",
                                           vec![number.to_string(), index.to_string()])
    }

    pub fn get_transaction_receipt(&mut self, hash: &str) -> RPCTransactionReceipt {
        self.rpc_request::<RPCTransactionReceipt>("eth_getTransactionReceipt",
                                                 vec![hash.to_string()])
    }

    pub fn get_uncle_by_block_hash_and_index(&mut self, hash: &str, index: &str) -> RPCBlock {
        self.rpc_request::<RPCBlock>("eth_getUncleByBlockHashAndIndex",
                                     vec![hash.to_string(), index.to_string()])
    }

    pub fn get_uncle_by_block_number_and_index(&mut self, number: &str, index: &str) -> RPCBlock {
        self.rpc_request::<RPCBlock>("eth_getUncleByBlockNumberAndIndex",
                                     vec![number.to_string(), index.to_string()])
    }

    pub fn get_compilers(&mut self) -> Vec<String> {
        self.rpc_request::<Vec<String>>("eth_getCompilers", vec![])
    }

    pub fn compile_solidity(&mut self, source: &str) -> String {
        unimplemented!()
    }

    pub fn compile_lll(&mut self, source: &str) -> String {
        self.rpc_request::<String>("eth_compileLLL", vec![source.to_string()])
    }

    pub fn compile_serpent(&mut self, source: &str) -> String {
        self.rpc_request::<String>("eth_compileSerpent", vec![source.to_string()])
    }

    pub fn new_filter(&mut self, filter: RPCFilter) -> String {
        unimplemented!()
    }

    pub fn new_block_filter(&mut self) -> String {
        self.rpc_request::<String>("eth_newBlockFilter", vec![])
    }

    pub fn new_pending_transaction_filter(&mut self) -> String {
        self.rpc_request::<String>("eth_newPendingTransactionFilter", vec![])
    }

    pub fn uninstall_filter(&mut self, id: &str) -> bool {
        self.rpc_request::<bool>("eth_uninstallFilter", vec![id.to_string()])
    }

    pub fn get_filter_changes(&mut self, id: &str) -> String {
        unimplemented!()
    }

    pub fn get_filter_logs(&mut self, id: &str) -> String {
        unimplemented!()
    }

    pub fn get_logs(&mut self, id: &str) -> String {
        unimplemented!()
    }

    pub fn get_work(&mut self) -> Vec<String> {
        self.rpc_request::<Vec<String>>("eth_getWork", vec![])
    }

    pub fn submit_work(&mut self, nonce: &str, pow: &str, mix: &str) -> bool {
        self.rpc_request::<bool>("eth_submitWork",
                                 vec![nonce.to_string(), pow.to_string(), mix.to_string()])
    }

    pub fn submit_hashrate(&mut self, hashrate: &str, id: &str) -> bool {
        self.rpc_request::<bool>("eth_submitHashrate",
                                 vec![hashrate.to_string(), id.to_string()])
    }

    pub fn put_string(&mut self, db: &str, key: &str, value: &str) -> bool {
        self.rpc_request::<bool>("db_putString",
                                 vec![db.to_string(), key.to_string(), value.to_string()])
    }

    pub fn get_string(&mut self, db: &str, key: &str) -> String {
        self.rpc_request::<String>("db_getString",
                                   vec![db.to_string(), key.to_string()])
    }

    pub fn put_hex(&mut self, db: &str, key: &str, value: &str) -> bool {
        self.rpc_request::<bool>("db_putHex",
                                 vec![db.to_string(), key.to_string(), value.to_string()])
    }

    pub fn get_hex(&mut self, db: &str, key: &str) -> String {
        self.rpc_request::<String>("db_getHex",
                                   vec![db.to_string(), key.to_string()])
    }
}

pub fn regression(address: &str) -> bool {
    let mut client = GethRPCClient::new("http://localhost:8545");
    let height = usize::from_str_radix(&client.block_number(), 16).unwrap();
    println!("height: {}", height);

    for blk_no in 49439..height {
        let block = client.get_block_by_number(format!("0x{:x}", blk_no).as_str());
        println!("{:?}", block);
    }
    false
}
<commit_msg>Problem: geth distingish "false" and false in JSON<commit_after>extern crate serde;
extern crate serde_json;
extern crate hyper;

#[macro_use]
extern crate serde_derive;

use std::process::{Command};
use std::io::Read;
use hyper::header::ContentType;
use hyper::client::Client;
use serde_json::Value;

#[derive(Serialize, Deserialize)]
struct RPCRequest {
    jsonrpc: String,
    method: String,
    params: Vec<String>,
    id: usize,
}

#[derive(Serialize, Deserialize)]
struct RPCObjectRequest<T> {
    jsonrpc: String,
    method: String,
    params: T,
    id: usize,
}

#[derive(Serialize, Deserialize)]
struct RPCObjectResponse<T> {
    jsonrpc: String,
    result: T,
    id: usize,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct RPCTransaction {
    pub hash: String,
    pub nonce: String,
    pub blockHash: String,
    pub blockNumber: String,
    pub transactionIndex: String,
    pub from: String,
    pub to: String,
    pub value: String,
    pub gas: String,
    pub gasPrice: String,
    pub input: String,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct RPCCall {
    pub from: String,
    pub to: String,
    pub gas: String,
    pub gasPrice: String,
    pub value: String,
    pub data: String,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct RPCBlock {
    pub number: String,
    pub hash: String,
    pub parentHash: String,
    pub nonce: String,
    pub sha3Uncles: String,
    pub logsBloom: String,
    pub transactionsRoot: String,
    pub stateRoot: String,
    pub miner: String,
    pub difficulty: String,
    pub totalDifficulty: String,
    pub extraData: String,
    pub size: String,
    pub gasLimit: String,
    pub gasUsed: String,
    pub timestamp: String,
    pub transactions: Vec<String>,
    pub uncles: Vec<String>
}

#[derive(Serialize, Deserialize, Debug)]
#[serde(untagged)]
pub enum RPCSyncStatus {
    NotSync(bool),
    Sync {
        startingBlock: String,
        currentBlock: String,
        highestBlock: String,
    }
}

#[derive(Serialize, Deserialize, Debug)]
pub struct RPCTransactionReceipt {
    pub transactionHash: String,
    pub transactionIndex: String,
    pub blockHash: String,
    pub blockNumber: String,
    pub cumulativeGasUsed: String,
    pub gasUsed: String,
    pub contractAddress: String,
    pub logs: Vec<String>,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct RPCFilter {
    pub fromBlock: String,
    pub toBlock: String,
    pub address: String,
    pub topics: Vec<String>,
}

pub struct GethRPCClient {
    endpoint: String,
    free_id: usize,
}

impl GethRPCClient {
    pub fn new(endpoint: &str) -> Self {
        GethRPCClient {
            endpoint: endpoint.to_string(),
            free_id: 1,
        }
    }

    fn rpc_request<T: serde::Deserialize>(&mut self, method: &str, params: Vec<String>) -> T {
        self.rpc_object_request::<Vec<String>, T>(method, params)
    }

    fn rpc_object_request<Req: serde::Serialize, Res: serde::Deserialize>(&mut self, method: &str, params: Req) -> Res {
        let request = RPCObjectRequest {
            jsonrpc: "2.0".to_string(),
            method: method.to_string(),
            params: params,
            id: self.free_id,
        };
        self.free_id = self.free_id + 1;

        let client = Client::new();
        let mut response_raw = client.post(&self.endpoint)
            .header(ContentType::json())
            .body(&serde_json::to_string(&request).unwrap())
            .send().unwrap();
        let mut buffer = String::new();
        response_raw.read_to_string(&mut buffer).unwrap();

        let response: RPCObjectResponse<Res> = serde_json::from_str(&buffer).unwrap();
        response.result
    }

    pub fn client_version(&mut self) -> String {
        self.rpc_request::<String>("web3_clientVersion", vec![])
    }

    pub fn net_version(&mut self) -> String {
        self.rpc_request::<String>("net_version", vec![])
    }

    pub fn net_listening(&mut self) -> bool {
        self.rpc_request::<bool>("net_listening", vec![])
    }

    pub fn net_peer_count(&mut self) -> String {
        self.rpc_request::<String>("net_peerCount", vec![])
    }

    pub fn sha3(&mut self, data: &str) -> String {
        self.rpc_request::<String>("web3_sha3", vec![])
    }

    pub fn protocol_version(&mut self) -> String {
        self.rpc_request::<String>("eth_protocolVersion", vec![])
    }

    pub fn syncing(&mut self) -> RPCSyncStatus {
        self.rpc_request::<RPCSyncStatus>("eth_syncing", vec![])
    }

    pub fn coinbase(&mut self) -> String {
        self.rpc_request::<String>("eth_coinbase", vec![])
    }

    pub fn mining(&mut self) -> bool {
        self.rpc_request::<bool>("eth_mining", vec![])
    }

    pub fn hashrate(&mut self) -> String {
        self.rpc_request::<String>("eth_hashrate", vec![])
    }

    pub fn gas_price(&mut self) -> String {
        self.rpc_request::<String>("eth_gasPrice", vec![])
    }

    pub fn accounts(&mut self) -> Vec<String> {
        self.rpc_request::<Vec<String>>("eth_accounts", vec![])
    }

    pub fn block_number(&mut self) -> String {
        self.rpc_request::<String>("eth_blockNumber", vec![])
    }

    pub fn get_balance(&mut self, address: &str, number: &str) -> String {
        self.rpc_request::<String>("eth_getBalance",
                                   vec![address.to_string(), number.to_string()])
    }

    pub fn get_storage_at(&mut self, address: &str, index: &str, number: &str) -> String {
        self.rpc_request::<String>("eth_getStorageAt",
                                   vec![address.to_string(), index.to_string(), number.to_string()])
    }

    pub fn get_transaction_count(&mut self, address: &str, number: &str) -> String {
        self.rpc_request::<String>("eth_getTransactionCount",
                                   vec![address.to_string(), number.to_string()])
    }

    pub fn get_block_transaction_count_by_hash(&mut self, hash: &str) -> String {
        self.rpc_request::<String>("eth_getBlockTransactionCountByHash",
                                   vec![hash.to_string()])
    }

    pub fn get_block_transaction_count_by_number(&mut self, number: &str) -> String {
        self.rpc_request::<String>("eth_getBlockTransactionCountByNumber",
                                   vec![number.to_string()])
    }

    pub fn get_uncle_count_by_block_hash(&mut self, hash: &str) -> String {
        self.rpc_request::<String>("eth_getUncleCountByBlockHash",
                                   vec![hash.to_string()])
    }

    pub fn get_uncle_count_by_block_number(&mut self, number: &str) -> String {
        self.rpc_request::<String>("eth_getUncleCountByBlockNumber",
                                   vec![number.to_string()])
    }

    pub fn get_code(&mut self, address: &str, number: &str) -> String {
        self.rpc_request::<String>("eth_getCode", vec![address.to_string(), number.to_string()])
    }

    pub fn sign(&mut self, address: &str, message: &str) -> String {
        self.rpc_request::<String>("eth_sign", vec![address.to_string(), message.to_string()])
    }

    pub fn send_transaction(&mut self, transaction: RPCTransaction) -> String {
        unimplemented!()
    }

    pub fn send_raw_transaction(&mut self, data: &str) -> String {
        self.rpc_request::<String>("eth_sendRawTransaction", vec![data.to_string()])
    }

    pub fn call(&mut self, transaction: RPCCall, number: &str) -> String {
        self.rpc_object_request::<(RPCCall, String), String>("eth_call", (transaction, number.to_string()))
    }

    pub fn estimate_gas(&mut self, transaction: RPCTransaction) -> String {
        unimplemented!()
    }

    pub fn get_block_by_hash(&mut self, hash: &str) -> RPCBlock {
        self.rpc_object_request::<(String, bool), RPCBlock>("eth_getBlockByHash",
                                                            (hash.to_string(), false))
    }

    pub fn get_block_by_number(&mut self, number: &str) -> RPCBlock {
        self.rpc_object_request::<(String, bool), RPCBlock>("eth_getBlockByNumber",
                                                            (number.to_string(), false))
    }

    pub fn get_transaction_by_hash(&mut self, hash: &str) -> RPCTransaction {
        self.rpc_request::<RPCTransaction>("eth_getTransactionByHash",
                                           vec![hash.to_string()])
    }

    pub fn get_transaction_by_block_hash_and_index(&mut self, hash: &str, index: &str) -> RPCTransaction {
        self.rpc_request::<RPCTransaction>("eth_getTransactionByBlockHashAndIndex",
                                           vec![hash.to_string(), index.to_string()])
    }

    pub fn get_transaction_by_block_number_and_index(&mut self, number: &str, index: &str)
                                                 -> RPCTransaction {
        self.rpc_request::<RPCTransaction>("eth_getTransactionByBlockNumberAndIndex",
                                           vec![number.to_string(), index.to_string()])
    }

    pub fn get_transaction_receipt(&mut self, hash: &str) -> RPCTransactionReceipt {
        self.rpc_request::<RPCTransactionReceipt>("eth_getTransactionReceipt",
                                                 vec![hash.to_string()])
    }

    pub fn get_uncle_by_block_hash_and_index(&mut self, hash: &str, index: &str) -> RPCBlock {
        self.rpc_request::<RPCBlock>("eth_getUncleByBlockHashAndIndex",
                                     vec![hash.to_string(), index.to_string()])
    }

    pub fn get_uncle_by_block_number_and_index(&mut self, number: &str, index: &str) -> RPCBlock {
        self.rpc_request::<RPCBlock>("eth_getUncleByBlockNumberAndIndex",
                                     vec![number.to_string(), index.to_string()])
    }

    pub fn get_compilers(&mut self) -> Vec<String> {
        self.rpc_request::<Vec<String>>("eth_getCompilers", vec![])
    }

    pub fn compile_solidity(&mut self, source: &str) -> String {
        unimplemented!()
    }

    pub fn compile_lll(&mut self, source: &str) -> String {
        self.rpc_request::<String>("eth_compileLLL", vec![source.to_string()])
    }

    pub fn compile_serpent(&mut self, source: &str) -> String {
        self.rpc_request::<String>("eth_compileSerpent", vec![source.to_string()])
    }

    pub fn new_filter(&mut self, filter: RPCFilter) -> String {
        unimplemented!()
    }

    pub fn new_block_filter(&mut self) -> String {
        self.rpc_request::<String>("eth_newBlockFilter", vec![])
    }

    pub fn new_pending_transaction_filter(&mut self) -> String {
        self.rpc_request::<String>("eth_newPendingTransactionFilter", vec![])
    }

    pub fn uninstall_filter(&mut self, id: &str) -> bool {
        self.rpc_request::<bool>("eth_uninstallFilter", vec![id.to_string()])
    }

    pub fn get_filter_changes(&mut self, id: &str) -> String {
        unimplemented!()
    }

    pub fn get_filter_logs(&mut self, id: &str) -> String {
        unimplemented!()
    }

    pub fn get_logs(&mut self, id: &str) -> String {
        unimplemented!()
    }

    pub fn get_work(&mut self) -> Vec<String> {
        self.rpc_request::<Vec<String>>("eth_getWork", vec![])
    }

    pub fn submit_work(&mut self, nonce: &str, pow: &str, mix: &str) -> bool {
        self.rpc_request::<bool>("eth_submitWork",
                                 vec![nonce.to_string(), pow.to_string(), mix.to_string()])
    }

    pub fn submit_hashrate(&mut self, hashrate: &str, id: &str) -> bool {
        self.rpc_request::<bool>("eth_submitHashrate",
                                 vec![hashrate.to_string(), id.to_string()])
    }

    pub fn put_string(&mut self, db: &str, key: &str, value: &str) -> bool {
        self.rpc_request::<bool>("db_putString",
                                 vec![db.to_string(), key.to_string(), value.to_string()])
    }

    pub fn get_string(&mut self, db: &str, key: &str) -> String {
        self.rpc_request::<String>("db_getString",
                                   vec![db.to_string(), key.to_string()])
    }

    pub fn put_hex(&mut self, db: &str, key: &str, value: &str) -> bool {
        self.rpc_request::<bool>("db_putHex",
                                 vec![db.to_string(), key.to_string(), value.to_string()])
    }

    pub fn get_hex(&mut self, db: &str, key: &str) -> String {
        self.rpc_request::<String>("db_getHex",
                                   vec![db.to_string(), key.to_string()])
    }
}

pub fn regression(address: &str) -> bool {
    let mut client = GethRPCClient::new("http://localhost:8545");
    let height = usize::from_str_radix(&client.block_number(), 16).unwrap();
    println!("height: {}", height);

    for blk_no in 49439..height {
        let block = client.get_block_by_number(format!("0x{:x}", blk_no).as_str());
        println!("{:?}", block);
    }
    false
}
<|endoftext|>