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

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

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

/// The [evaluation order](http://www.cs.cornell.edu/courses/cs6110/2014sp/Handouts/Sestoft.pdf) 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 (`0` means no limit).
///
/// # 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, 0), 0.into());
/// ```
pub fn beta(mut term: Term, order: &Order, limit: 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, count: &usize) {
        if SHOW_REDUCTIONS {
            print!("\n{}. {}\n=>", count + 1, show_precedence(self, 0, depth));
            let mut indent_len = ((*count + 1) as f32).log10().trunc() as usize + 3;
            if DISPLAY_CLASSIC { indent_len += 3 }
            for _ in 0..indent_len { print!(" ") };
        };
        let copy = self.clone();
        *self = copy.eval().unwrap();
        if SHOW_REDUCTIONS { println!("{}", show_precedence(self, 0, depth)) };
    }

    fn is_reducible(&self, limit: usize, count: &usize) -> bool {
        self.lhs_ref().unwrap().unabs_ref().is_ok() &&
        (limit == 0 || *count < limit )
    }

    /// Performs β-reduction on a `Term` with the specified evaluation `Order` and an optional
    /// limit on number of reductions (`0` means no limit).
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::pred;
    /// use lambda_calculus::reduction::Order::Normal;
    ///
    /// let mut pred_one = pred().app(1.into());
    /// pred_one.beta(&Normal, 0);
    ///
    /// assert_eq!(pred_one, 0.into());
    /// ```
    pub fn beta(&mut self, order: &Order, limit: usize) {
        if SHOW_REDUCTIONS {
            println!("reducing {} [{} order{}]:", self, order,
                if limit != 0 {
                    format!(", limit of {} reduction{}", limit,
                        if limit == 1 { "" } else { "s" }
                    )
                } else {
                    "".into()
                }
            );
        };

        let mut count = 0;

        match *order {
            CallByName        => self.beta_cbn(0, limit, &mut count),
            Normal            => self.beta_nor(0, limit, &mut count),
            CallByValue       => self.beta_cbv(0, limit, &mut count),
            Applicative       => self.beta_app(0, limit, &mut count),
            HeadSpine         => self.beta_hs(0, limit, &mut count),
            HybridNormal      => self.beta_hnor(0, limit, &mut count),
            HybridApplicative => self.beta_happ(0, limit, &mut count)
        }
        if SHOW_REDUCTIONS { println!("\nresult after {} reductions: {}\n", count, self) };
    }

    fn beta_cbn(&mut self, depth: u32, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

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

                if self.is_reducible(limit, count) {
                    self.eval_with_info(depth, count);
                    *count += 1;
                    self.beta_cbn(depth, limit, count);
                }
            },
            _ => ()
        }
    }

    fn beta_nor(&mut self, depth: u32, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

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

                if self.is_reducible(limit, count) {
                    self.eval_with_info(depth, count);
                    *count += 1;
                    self.beta_nor(depth, limit, count);
                } else {
                    self.lhs_ref_mut().unwrap().beta_nor(depth, limit, count);
                    self.rhs_ref_mut().unwrap().beta_nor(depth, limit, count);
                }
            }
        }
    }

    fn beta_cbv(&mut self, depth: u32, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

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

                if self.is_reducible(limit, count) {
                    self.eval_with_info(depth, count);
                    *count += 1;
                    self.beta_cbv(depth, limit, count);
                }
            },
            _ => ()
        }
    }

    fn beta_app(&mut self, depth: u32, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

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

                if self.is_reducible(limit, count) {
                    self.eval_with_info(depth, count);
                    *count += 1;
                    self.beta_app(depth, limit, count);
                }
            }
        }
    }

    fn beta_happ(&mut self, depth: u32, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

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

                if self.is_reducible(limit, count) {
                    self.eval_with_info(depth, count);
                    *count += 1;
                    self.beta_happ(depth, limit, count);
                } else {
                    self.lhs_ref_mut().unwrap().beta_happ(depth, limit, count);
                }
            }
        }
    }

    fn beta_hs(&mut self, depth: u32, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

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

                if self.is_reducible(limit, count) {
                    self.eval_with_info(depth, count);
                    *count += 1;
                    self.beta_hs(depth, limit, count)
                }
            }
        }
    }

    fn beta_hnor(&mut self, depth: u32, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

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

                if self.is_reducible(limit, count) {
                    self.eval_with_info(depth, count);
                    *count += 1;
                    self.beta_hnor(depth, limit, count)
                } else {
                    self.lhs_ref_mut().unwrap().beta_hnor(depth, limit, count);
                    self.rhs_ref_mut().unwrap().beta_hnor(depth, limit, count);
                }
            }
        }
    }
}

impl fmt::Display for Order {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", match *self {
            Normal => "normal",
            CallByName => "call-by-name",
            HeadSpine => "head spine",
            HybridNormal => "hybrid normal",
            Applicative => "applicative",
            CallByValue => "call-by-value",
            HybridApplicative => "hybrid applicative"
        })
    }
}

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

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

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

        let does_reduce = app(abs(Var(2)), omm());
        assert_eq!(beta(does_reduce, &Normal, 0), Var(1));
    }

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

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

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

        let mut wont_reduce = app(abs(Var(2)), omm());
        wont_reduce.beta(&Applicative, 3);
        assert_eq!(wont_reduce, app(abs(Var(2)), omm()));
    }

    #[test]
    fn call_by_value_order() {
        let mut expr = app(abs(app(i(), Var(1))), app(i(), i()));
        expr.beta(&CallByValue, 1);
        assert_eq!(expr, app(abs(app(i(), Var(1))), i()));
        expr.beta(&CallByValue, 1);
        assert_eq!(expr, app(i(), i()));
        expr.beta(&CallByValue, 1);
        assert_eq!(expr, i());
    }
}
<commit_msg>don't print 's' when reduction count is 1<commit_after>//! [β-reduction](https://en.wikipedia.org/wiki/Beta_normal_form) for lambda `Term`s

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

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

/// The [evaluation order](http://www.cs.cornell.edu/courses/cs6110/2014sp/Handouts/Sestoft.pdf) 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 (`0` means no limit).
///
/// # 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, 0), 0.into());
/// ```
pub fn beta(mut term: Term, order: &Order, limit: 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, count: &usize) {
        if SHOW_REDUCTIONS {
            print!("\n{}. {}\n=>", count + 1, show_precedence(self, 0, depth));
            let mut indent_len = ((*count + 1) as f32).log10().trunc() as usize + 3;
            if DISPLAY_CLASSIC { indent_len += 3 }
            for _ in 0..indent_len { print!(" ") };
        };
        let copy = self.clone();
        *self = copy.eval().unwrap();
        if SHOW_REDUCTIONS { println!("{}", show_precedence(self, 0, depth)) };
    }

    fn is_reducible(&self, limit: usize, count: &usize) -> bool {
        self.lhs_ref().unwrap().unabs_ref().is_ok() &&
        (limit == 0 || *count < limit )
    }

    /// Performs β-reduction on a `Term` with the specified evaluation `Order` and an optional
    /// limit on number of reductions (`0` means no limit).
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::pred;
    /// use lambda_calculus::reduction::Order::Normal;
    ///
    /// let mut pred_one = pred().app(1.into());
    /// pred_one.beta(&Normal, 0);
    ///
    /// assert_eq!(pred_one, 0.into());
    /// ```
    pub fn beta(&mut self, order: &Order, limit: usize) {
        if SHOW_REDUCTIONS {
            println!("reducing {} [{} order{}]:", self, order,
                if limit != 0 {
                    format!(", limit of {} reduction{}", limit,
                        if limit == 1 { "" } else { "s" }
                    )
                } else {
                    "".into()
                }
            );
        };

        let mut count = 0;

        match *order {
            CallByName        => self.beta_cbn(0, limit, &mut count),
            Normal            => self.beta_nor(0, limit, &mut count),
            CallByValue       => self.beta_cbv(0, limit, &mut count),
            Applicative       => self.beta_app(0, limit, &mut count),
            HeadSpine         => self.beta_hs(0, limit, &mut count),
            HybridNormal      => self.beta_hnor(0, limit, &mut count),
            HybridApplicative => self.beta_happ(0, limit, &mut count)
        }
        if SHOW_REDUCTIONS {
            println!("\nresult after {} reduction{}: {}\n", count,
                if limit == 1 { "" } else { "s" }, self);
        };
    }

    fn beta_cbn(&mut self, depth: u32, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

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

                if self.is_reducible(limit, count) {
                    self.eval_with_info(depth, count);
                    *count += 1;
                    self.beta_cbn(depth, limit, count);
                }
            },
            _ => ()
        }
    }

    fn beta_nor(&mut self, depth: u32, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

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

                if self.is_reducible(limit, count) {
                    self.eval_with_info(depth, count);
                    *count += 1;
                    self.beta_nor(depth, limit, count);
                } else {
                    self.lhs_ref_mut().unwrap().beta_nor(depth, limit, count);
                    self.rhs_ref_mut().unwrap().beta_nor(depth, limit, count);
                }
            }
        }
    }

    fn beta_cbv(&mut self, depth: u32, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

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

                if self.is_reducible(limit, count) {
                    self.eval_with_info(depth, count);
                    *count += 1;
                    self.beta_cbv(depth, limit, count);
                }
            },
            _ => ()
        }
    }

    fn beta_app(&mut self, depth: u32, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

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

                if self.is_reducible(limit, count) {
                    self.eval_with_info(depth, count);
                    *count += 1;
                    self.beta_app(depth, limit, count);
                }
            }
        }
    }

    fn beta_happ(&mut self, depth: u32, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

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

                if self.is_reducible(limit, count) {
                    self.eval_with_info(depth, count);
                    *count += 1;
                    self.beta_happ(depth, limit, count);
                } else {
                    self.lhs_ref_mut().unwrap().beta_happ(depth, limit, count);
                }
            }
        }
    }

    fn beta_hs(&mut self, depth: u32, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

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

                if self.is_reducible(limit, count) {
                    self.eval_with_info(depth, count);
                    *count += 1;
                    self.beta_hs(depth, limit, count)
                }
            }
        }
    }

    fn beta_hnor(&mut self, depth: u32, limit: usize, count: &mut usize) {
        if limit != 0 && *count == limit { return }

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

                if self.is_reducible(limit, count) {
                    self.eval_with_info(depth, count);
                    *count += 1;
                    self.beta_hnor(depth, limit, count)
                } else {
                    self.lhs_ref_mut().unwrap().beta_hnor(depth, limit, count);
                    self.rhs_ref_mut().unwrap().beta_hnor(depth, limit, count);
                }
            }
        }
    }
}

impl fmt::Display for Order {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", match *self {
            Normal => "normal",
            CallByName => "call-by-name",
            HeadSpine => "head spine",
            HybridNormal => "hybrid normal",
            Applicative => "applicative",
            CallByValue => "call-by-value",
            HybridApplicative => "hybrid applicative"
        })
    }
}

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

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

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

        let does_reduce = app(abs(Var(2)), omm());
        assert_eq!(beta(does_reduce, &Normal, 0), Var(1));
    }

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

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

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

        let mut wont_reduce = app(abs(Var(2)), omm());
        wont_reduce.beta(&Applicative, 3);
        assert_eq!(wont_reduce, app(abs(Var(2)), omm()));
    }

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


<commit_before>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::mem::size_of;

#[repr(i8)]
enum Ei8 {
    Ai8 = 0,
    Bi8 = 1
}

#[repr(u8)]
enum Eu8 {
    Au8 = 0,
    Bu8 = 1
}

#[repr(i16)]
enum Ei16 {
    Ai16 = 0,
    Bi16 = 1
}

#[repr(u16)]
enum Eu16 {
    Au16 = 0,
    Bu16 = 1
}

#[repr(i32)]
enum Ei32 {
    Ai32 = 0,
    Bi32 = 1
}

#[repr(u32)]
enum Eu32 {
    Au32 = 0,
    Bu32 = 1
}

#[repr(i64)]
enum Ei64 {
    Ai64 = 0,
    Bi64 = 1
}

#[repr(u64)]
enum Eu64 {
    Au64 = 0,
    Bu64 = 1
}

#[repr(isize)]
enum Eint {
    Aint = 0,
    Bint = 1
}

#[repr(usize)]
enum Euint {
    Auint = 0,
    Buint = 1
}

#[repr(i64)]
enum Ei64_non_c_like<T> {
    _None,
    _Some(T),
}

#[repr(u64)]
enum Eu64_non_c_like<T> {
    _None,
    _Some(T),
}

pub fn main() {
    assert_eq!(size_of::<Ei8>(), 1);
    assert_eq!(size_of::<Eu8>(), 1);
    assert_eq!(size_of::<Ei16>(), 2);
    assert_eq!(size_of::<Eu16>(), 2);
    assert_eq!(size_of::<Ei32>(), 4);
    assert_eq!(size_of::<Eu32>(), 4);
    assert_eq!(size_of::<Ei64>(), 8);
    assert_eq!(size_of::<Eu64>(), 8);
    assert_eq!(size_of::<Eint>(), size_of::<isize>());
    assert_eq!(size_of::<Euint>(), size_of::<usize>());
    assert_eq!(size_of::<Ei64_non_c_like<()>>(), 8);
    assert_eq!(size_of::<Eu64_non_c_like<()>>(), 8);
}
<commit_msg>Improve tests for #26114<commit_after>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::mem::{size_of, align_of};

#[repr(i8)]
enum Ei8 {
    Ai8 = 0,
    Bi8 = 1
}

#[repr(u8)]
enum Eu8 {
    Au8 = 0,
    Bu8 = 1
}

#[repr(i16)]
enum Ei16 {
    Ai16 = 0,
    Bi16 = 1
}

#[repr(u16)]
enum Eu16 {
    Au16 = 0,
    Bu16 = 1
}

#[repr(i32)]
enum Ei32 {
    Ai32 = 0,
    Bi32 = 1
}

#[repr(u32)]
enum Eu32 {
    Au32 = 0,
    Bu32 = 1
}

#[repr(i64)]
enum Ei64 {
    Ai64 = 0,
    Bi64 = 1
}

#[repr(u64)]
enum Eu64 {
    Au64 = 0,
    Bu64 = 1
}

#[repr(isize)]
enum Eint {
    Aint = 0,
    Bint = 1
}

#[repr(usize)]
enum Euint {
    Auint = 0,
    Buint = 1
}

#[repr(u8)]
enum Eu8NonCLike<T> {
    _None,
    _Some(T),
}

#[repr(i64)]
enum Ei64NonCLike<T> {
    _None,
    _Some(T),
}

#[repr(u64)]
enum Eu64NonCLike<T> {
    _None,
    _Some(T),
}

pub fn main() {
    assert_eq!(size_of::<Ei8>(), 1);
    assert_eq!(size_of::<Eu8>(), 1);
    assert_eq!(size_of::<Ei16>(), 2);
    assert_eq!(size_of::<Eu16>(), 2);
    assert_eq!(size_of::<Ei32>(), 4);
    assert_eq!(size_of::<Eu32>(), 4);
    assert_eq!(size_of::<Ei64>(), 8);
    assert_eq!(size_of::<Eu64>(), 8);
    assert_eq!(size_of::<Eint>(), size_of::<isize>());
    assert_eq!(size_of::<Euint>(), size_of::<usize>());
    assert_eq!(size_of::<Eu8NonCLike<()>>(), 1);
    assert_eq!(size_of::<Ei64NonCLike<()>>(), 8);
    assert_eq!(size_of::<Eu64NonCLike<()>>(), 8);
    let u8_expected_size = round_up(9, align_of::<Eu64NonCLike<u8>>());
    assert_eq!(size_of::<Eu64NonCLike<u8>>(), u8_expected_size);
    let array_expected_size = round_up(28, align_of::<Eu64NonCLike<[u32; 5]>>());
    assert_eq!(size_of::<Eu64NonCLike<[u32; 5]>>(), array_expected_size);
    assert_eq!(size_of::<Eu64NonCLike<[u32; 6]>>(), 32);
}

// Rounds x up to the next multiple of a
fn round_up(x: usize, a: usize) -> usize {
    ((x + (a - 1)) / a) * a
}
<|endoftext|>


<commit_before>use nom::{
    self,
    bytes::streaming::{tag, take},
    character::streaming::digit1,
    sequence::tuple,
    IResult,
};

use std::str;

// ----- number -----

// number          = 1*DIGIT
//                    ; Unsigned 32-bit integer
//                    ; (0 <= n < 4,294,967,296)
named!(pub number<u32>, flat_map!(digit1, parse_to!(u32)));

// same as `number` but 64-bit
named!(pub number_64<u64>, flat_map!(digit1, parse_to!(u64)));

// ----- string -----

// string = quoted / literal
named!(pub string<&[u8]>, alt!(quoted | literal));

// string bytes as utf8
named!(pub string_utf8<&str>, map_res!(string, str::from_utf8));

// quoted = DQUOTE *QUOTED-CHAR DQUOTE
named!(pub quoted<&[u8]>, delimited!(
    char!('"'),
    quoted_data,
    char!('"')
));

// quoted bytes as utf8
named!(pub quoted_utf8<&str>, map_res!(quoted, str::from_utf8));

// QUOTED-CHAR = <any TEXT-CHAR except quoted-specials> / "\" quoted-specials
pub fn quoted_data(i: &[u8]) -> IResult<&[u8], &[u8]> {
    // Ideally this should use nom's `escaped` macro, but it suffers from broken
    // type inference unless compiled with the verbose-errors feature enabled.
    let mut escape = false;
    let mut len = 0;
    for c in i {
        if *c == b'"' && !escape {
            break;
        }
        len += 1;
        if *c == b'\\' && !escape {
            escape = true
        } else if escape {
            escape = false;
        }
    }
    Ok((&i[len..], &i[..len]))
}

// quoted-specials = DQUOTE / "\"
pub fn is_quoted_specials(c: u8) -> bool {
    c == b'"' || c == b'\\'
}

/// literal = "{" number "}" CRLF *CHAR8
///            ; Number represents the number of CHAR8s
pub fn literal(input: &[u8]) -> IResult<&[u8], &[u8]> {
    let parser = tuple((tag(b"{"), number, tag(b"}"), tag("\r\n")));

    let (remaining, (_, count, _, _)) = parser(input)?;

    let (remaining, data) = take(count)(remaining)?;

    if !data.iter().all(|byte| is_char8(*byte)) {
        // FIXME: what ErrorKind should this have?
        return Err(nom::Err::Error((remaining, nom::error::ErrorKind::Verify)));
    }

    Ok((remaining, data))
}

/// CHAR8 = %x01-ff ; any OCTET except NUL, %x00
pub fn is_char8(i: u8) -> bool {
    i != 0
}

// ----- astring ----- atom (roughly) or string

// astring = 1*ASTRING-CHAR / string
named!(pub astring<&[u8]>, alt!(
    take_while1!(is_astring_char) |
    string
));

// astring bytes as utf8
named!(pub astring_utf8<&str>, map_res!(astring, str::from_utf8));

// ASTRING-CHAR = ATOM-CHAR / resp-specials
pub fn is_astring_char(c: u8) -> bool {
    is_atom_char(c) || is_resp_specials(c)
}

// ATOM-CHAR = <any CHAR except atom-specials>
pub fn is_atom_char(c: u8) -> bool {
    is_char(c) && !is_atom_specials(c)
}

// atom-specials = "(" / ")" / "{" / SP / CTL / list-wildcards / quoted-specials / resp-specials
pub fn is_atom_specials(c: u8) -> bool {
    c == b'('
        || c == b')'
        || c == b'{'
        || c == b' '
        || c < 32
        || is_list_wildcards(c)
        || is_quoted_specials(c)
        || is_resp_specials(c)
}

// resp-specials = "]"
pub fn is_resp_specials(c: u8) -> bool {
    c == b']'
}

// atom = 1*ATOM-CHAR
named!(pub atom<&str>, map_res!(take_while1!(is_atom_char),
    str::from_utf8
));

// ----- nstring ----- nil or string

// nstring = string / nil
named!(pub nstring<Option<&[u8]>>, alt!(
    map!(nil, |_| None) |
    map!(string, |s| Some(s))
));

// nstring bytes as utf8
named!(pub nstring_utf8<Option<&str>>, alt!(
    map!(nil, |_| None) |
    map!(string_utf8, |s| Some(s))
));

// nil = "NIL"
named!(pub nil, tag_no_case!("NIL"));

// ----- text -----

// text = 1*TEXT-CHAR
named!(pub text<&str>, map_res!(take_while!(is_text_char),
    str::from_utf8
));

// TEXT-CHAR = <any CHAR except CR and LF>
pub fn is_text_char(c: u8) -> bool {
    is_char(c) && c != b'\r' && c != b'\n'
}

// CHAR = %x01-7F
//          ; any 7-bit US-ASCII character,
//          ;  excluding NUL
// From RFC5234
pub fn is_char(c: u8) -> bool {
    match c {
        0x01..=0x7F => true,
        _ => false,
    }
}

// ----- others -----

// list-wildcards = "%" / "*"
pub fn is_list_wildcards(c: u8) -> bool {
    c == b'%' || c == b'*'
}

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

    #[test]
    fn test_string_literal() {
        match string(b"{3}\r\nXYZ") {
            Ok((_, value)) => {
                assert_eq!(value, b"XYZ");
            }
            rsp => panic!("unexpected response {:?}", rsp),
        }
    }

    #[test]
    fn test_astring() {
        match astring(b"text ") {
            Ok((_, value)) => {
                assert_eq!(value, b"text");
            }
            rsp => panic!("unexpected response {:?}", rsp),
        }
    }
}
<commit_msg>Convert core parsers to function style<commit_after>use nom::{
    self,
    branch::alt,
    bytes::streaming::{tag, tag_no_case, take, take_while, take_while1},
    character::streaming::{char, digit1},
    combinator::{map, map_res},
    sequence::{delimited, tuple},
    IResult,
};

use std::str::{self, FromStr};

// ----- number -----

// number          = 1*DIGIT
//                    ; Unsigned 32-bit integer
//                    ; (0 <= n < 4,294,967,296)
pub fn number(i: &[u8]) -> IResult<&[u8], u32> {
    let (i, bytes) = digit1(i)?;
    match str::from_utf8(bytes)
        .ok()
        .and_then(|s| u32::from_str(s).ok())
    {
        Some(v) => Ok((i, v)),
        None => Err(nom::Err::Error(nom::error::make_error(
            i,
            nom::error::ErrorKind::ParseTo,
        ))),
    }
}

// same as `number` but 64-bit
pub fn number_64(i: &[u8]) -> IResult<&[u8], u64> {
    let (i, bytes) = digit1(i)?;
    match str::from_utf8(bytes)
        .ok()
        .and_then(|s| u64::from_str(s).ok())
    {
        Some(v) => Ok((i, v)),
        None => Err(nom::Err::Error(nom::error::make_error(
            i,
            nom::error::ErrorKind::ParseTo,
        ))),
    }
}

// ----- string -----

// string = quoted / literal
pub fn string(i: &[u8]) -> IResult<&[u8], &[u8]> {
    alt((quoted, literal))(i)
}

// string bytes as utf8
pub fn string_utf8(i: &[u8]) -> IResult<&[u8], &str> {
    map_res(string, str::from_utf8)(i)
}

// quoted = DQUOTE *QUOTED-CHAR DQUOTE
pub fn quoted(i: &[u8]) -> IResult<&[u8], &[u8]> {
    delimited(char('"'), quoted_data, char('"'))(i)
}

// quoted bytes as utf8
pub fn quoted_utf8(i: &[u8]) -> IResult<&[u8], &str> {
    map_res(quoted, str::from_utf8)(i)
}

// QUOTED-CHAR = <any TEXT-CHAR except quoted-specials> / "\" quoted-specials
pub fn quoted_data(i: &[u8]) -> IResult<&[u8], &[u8]> {
    // Ideally this should use nom's `escaped` macro, but it suffers from broken
    // type inference unless compiled with the verbose-errors feature enabled.
    let mut escape = false;
    let mut len = 0;
    for c in i {
        if *c == b'"' && !escape {
            break;
        }
        len += 1;
        if *c == b'\\' && !escape {
            escape = true
        } else if escape {
            escape = false;
        }
    }
    Ok((&i[len..], &i[..len]))
}

// quoted-specials = DQUOTE / "\"
pub fn is_quoted_specials(c: u8) -> bool {
    c == b'"' || c == b'\\'
}

/// literal = "{" number "}" CRLF *CHAR8
///            ; Number represents the number of CHAR8s
pub fn literal(input: &[u8]) -> IResult<&[u8], &[u8]> {
    let parser = tuple((tag(b"{"), number, tag(b"}"), tag("\r\n")));

    let (remaining, (_, count, _, _)) = parser(input)?;

    let (remaining, data) = take(count)(remaining)?;

    if !data.iter().all(|byte| is_char8(*byte)) {
        // FIXME: what ErrorKind should this have?
        return Err(nom::Err::Error((remaining, nom::error::ErrorKind::Verify)));
    }

    Ok((remaining, data))
}

/// CHAR8 = %x01-ff ; any OCTET except NUL, %x00
pub fn is_char8(i: u8) -> bool {
    i != 0
}

// ----- astring ----- atom (roughly) or string

// astring = 1*ASTRING-CHAR / string
pub fn astring(i: &[u8]) -> IResult<&[u8], &[u8]> {
    alt((take_while1(is_astring_char), string))(i)
}

// astring bytes as utf8
pub fn astring_utf8(i: &[u8]) -> IResult<&[u8], &str> {
    map_res(astring, str::from_utf8)(i)
}

// ASTRING-CHAR = ATOM-CHAR / resp-specials
pub fn is_astring_char(c: u8) -> bool {
    is_atom_char(c) || is_resp_specials(c)
}

// ATOM-CHAR = <any CHAR except atom-specials>
pub fn is_atom_char(c: u8) -> bool {
    is_char(c) && !is_atom_specials(c)
}

// atom-specials = "(" / ")" / "{" / SP / CTL / list-wildcards / quoted-specials / resp-specials
pub fn is_atom_specials(c: u8) -> bool {
    c == b'('
        || c == b')'
        || c == b'{'
        || c == b' '
        || c < 32
        || is_list_wildcards(c)
        || is_quoted_specials(c)
        || is_resp_specials(c)
}

// resp-specials = "]"
pub fn is_resp_specials(c: u8) -> bool {
    c == b']'
}

// atom = 1*ATOM-CHAR
pub fn atom(i: &[u8]) -> IResult<&[u8], &str> {
    map_res(take_while1(is_atom_char), str::from_utf8)(i)
}

// ----- nstring ----- nil or string

// nstring = string / nil
pub fn nstring(i: &[u8]) -> IResult<&[u8], Option<&[u8]>> {
    alt((map(nil, |_| None), map(string, Some)))(i)
}

// nstring bytes as utf8
pub fn nstring_utf8(i: &[u8]) -> IResult<&[u8], Option<&str>> {
    alt((map(nil, |_| None), map(string_utf8, Some)))(i)
}

// nil = "NIL"
pub fn nil(i: &[u8]) -> IResult<&[u8], &[u8]> {
    tag_no_case("NIL")(i)
}

// ----- text -----

// text = 1*TEXT-CHAR
pub fn text(i: &[u8]) -> IResult<&[u8], &str> {
    map_res(take_while(is_text_char), str::from_utf8)(i)
}

// TEXT-CHAR = <any CHAR except CR and LF>
pub fn is_text_char(c: u8) -> bool {
    is_char(c) && c != b'\r' && c != b'\n'
}

// CHAR = %x01-7F
//          ; any 7-bit US-ASCII character,
//          ;  excluding NUL
// From RFC5234
pub fn is_char(c: u8) -> bool {
    match c {
        0x01..=0x7F => true,
        _ => false,
    }
}

// ----- others -----

// list-wildcards = "%" / "*"
pub fn is_list_wildcards(c: u8) -> bool {
    c == b'%' || c == b'*'
}

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

    #[test]
    fn test_string_literal() {
        match string(b"{3}\r\nXYZ") {
            Ok((_, value)) => {
                assert_eq!(value, b"XYZ");
            }
            rsp => panic!("unexpected response {:?}", rsp),
        }
    }

    #[test]
    fn test_astring() {
        match astring(b"text ") {
            Ok((_, value)) => {
                assert_eq!(value, b"text");
            }
            rsp => panic!("unexpected response {:?}", rsp),
        }
    }
}
<|endoftext|>


<commit_before>use graphics::modular_index::{offset, previous, next};

// Local crate.
use Vector2d = vector2d::Vector2d;

static EPSILON: f64 = 0.0000000001;

pub fn area(contour: &[Vector2d]) -> f64 {
    let n = contour.len();
    let mut sum = 0.0_f64;

    for i in range(0, n) {
        let q = i;
        let p = previous(n, i);
        sum += contour.get(p).unwrap().get_x() * contour.get(q).unwrap().get_y()
            - contour.get(q).unwrap().get_x() * contour.get(p).unwrap().get_y();
    }

    sum * 0.5_f64
}

/// InsideTriangle decides if a point P is Inside of the triangle
/// defined by A, B, C.
pub fn inside_triangle(
    a_x: f64, a_y: f64,
    b_x: f64, b_y: f64,
    c_x: f64, c_y: f64,
    p_x: f64, p_y: f64
) -> bool {

    let (ax, ay) = (c_x - b_x, c_y - b_y);
    let (bx, by) = (a_x - c_x, a_y - c_y);
    let (cx, cy) = (b_x - a_x, b_y - a_y);
    let (apx, apy) = (p_x - a_x, p_y - a_y);
    let (bpx, bpy) = (p_x - b_x, p_y - b_y);
    let (cpx, cpy) = (p_x - c_x, p_y - c_y);

    let aCROSSbp = ax*bpy - ay*bpx;
    let cCROSSap = cx*apy - cy*apx;
    let bCROSScp = bx*cpy - by*cpx;

    (
        (aCROSSbp >= 0.0_f64)
        && (bCROSScp >= 0.0_f64)
        && (cCROSSap >= 0.0_f64)
    )
}

pub fn snip(
    contour: &[Vector2d],
    triangle: [uint, ..3],
    n: uint,
    vertex_indices: &Vec<uint>
) -> bool {
    let (u, v, w) = (triangle[0], triangle[1], triangle[2]);

    let a_pos = contour.get(*vertex_indices.get(u)).unwrap();
    let b_pos = contour.get(*vertex_indices.get(v)).unwrap();
    let c_pos = contour.get(*vertex_indices.get(w)).unwrap();

    let (ax, ay) = (a_pos.get_x(), a_pos.get_y());
    let (bx, by) = (b_pos.get_x(), b_pos.get_y());
    let (cx, cy) = (c_pos.get_x(), c_pos.get_y());

    if EPSILON > (((bx-ax)*(cy-ay)) - ((by-ay)*(cx-ax))) {
        return false;
    }

    for p in range(0, n) {
        // Do not check for any of the vertices in the triangle.
        if (p == u) || (p == v) || (p == w) { continue; }

        let p_pos = contour.get(*vertex_indices.get(p)).unwrap();
        if inside_triangle(ax,ay,bx,by,cx,cy,p_pos.get_x(),p_pos.get_y()) { return false; }
    }

    true
}

pub fn process(
    contour: &[Vector2d]
) -> Option<Vec<Vector2d>> {
    /* allocate and initialize list of Vertices in polygon */

    let n = contour.len();
    if n < 3 { return None; }

    let mut result: Vec<Vector2d> = Vec::new();
    let mut vertex_indices: Vec<uint> = Vec::with_capacity(n);

    /* we want a counter-clockwise polygon in V */

    // Computes the area which has opposite signs
    // whether the polygon is clock-wise or counter-clockwise.
    if 0.0_f64 < area(contour) {
        for v in range(0, n) { vertex_indices.push(v); }
    } else {
        // Add indices in reverse order.
        for v in range(0, n) { vertex_indices.push((n-1)-v); }
    }

    // Stores the number of vertices.
    // Used instead of `vertex_indices.len()` to emulate removing of vertices.
    let mut number_of_vertices = n;

    /*  remove nv-2 Vertices, creating 1 triangle every time */

    // Used for error detection.
    let mut count = 2 * number_of_vertices;

    let mut v = number_of_vertices - 1;
    while number_of_vertices > 2 {
        /* if we loop, it is probably a non-simple polygon */
        if count <= 0 {
            //** Triangulate: ERROR - probable bad polygon!
            return None;
        }

        count -= 1;

        /* three consecutive vertices in current polygon, <u,v,w> */
        let triangle = [
            v,
            next(number_of_vertices, v),
            offset(number_of_vertices, v, 2)
        ];
        v = triangle[1];

        if snip(contour, triangle, number_of_vertices, &mut vertex_indices) {
            for &i in triangle.iter() {
                result.push(*contour.get(*vertex_indices.get(i)).unwrap());
            }

            /* remove v from remaining polygon */
            for i in range(0, number_of_vertices - 3) {
                let s = v + i;
                let t = v + i + 1;

                *vertex_indices.get_mut(s) = *vertex_indices.get(t);
            }

            // "Removed" a vertex.
            number_of_vertices -= 1;

            /* resest error detection counter */
            count = 2 * number_of_vertices;
        }
    }

    Some(result)
}

<commit_msg>Simplified code<commit_after>use graphics::modular_index::{offset, previous, next};

// Local crate.
use Vector2d = vector2d::Vector2d;

static EPSILON: f64 = 0.0000000001;

pub fn area(contour: &[Vector2d]) -> f64 {
    let n = contour.len();
    let mut sum = 0.0_f64;

    for i in range(0, n) {
        let q = i;
        let p = previous(n, i);
        sum += contour.get(p).unwrap().get_x() * contour.get(q).unwrap().get_y()
            - contour.get(q).unwrap().get_x() * contour.get(p).unwrap().get_y();
    }

    sum * 0.5_f64
}

/// InsideTriangle decides if a point P is Inside of the triangle
/// defined by A, B, C.
pub fn inside_triangle(
    a_x: f64, a_y: f64,
    b_x: f64, b_y: f64,
    c_x: f64, c_y: f64,
    p_x: f64, p_y: f64
) -> bool {

    let (bcx, bcy) = (c_x - b_x, c_y - b_y);
    let (cax, cay) = (a_x - c_x, a_y - c_y);
    let (abx, aby) = (b_x - a_x, b_y - a_y);
    let (apx, apy) = (p_x - a_x, p_y - a_y);
    let (bpx, bpy) = (p_x - b_x, p_y - b_y);
    let (cpx, cpy) = (p_x - c_x, p_y - c_y);

    let aCROSSbp = bcx*bpy - bcy*bpx;
    let cCROSSap = abx*apy - aby*apx;
    let bCROSScp = cax*cpy - cay*cpx;

    (
        (aCROSSbp >= 0.0_f64)
        && (bCROSScp >= 0.0_f64)
        && (cCROSSap >= 0.0_f64)
    )
}

pub fn snip(
    contour: &[Vector2d],
    triangle: [uint, ..3],
    n: uint,
    vertex_indices: &Vec<uint>
) -> bool {
    let (u, v, w) = (triangle[0], triangle[1], triangle[2]);

    let a_pos = contour.get(*vertex_indices.get(u)).unwrap();
    let b_pos = contour.get(*vertex_indices.get(v)).unwrap();
    let c_pos = contour.get(*vertex_indices.get(w)).unwrap();

    let (ax, ay) = (a_pos.get_x(), a_pos.get_y());
    let (bx, by) = (b_pos.get_x(), b_pos.get_y());
    let (cx, cy) = (c_pos.get_x(), c_pos.get_y());

    if EPSILON > (((bx-ax)*(cy-ay)) - ((by-ay)*(cx-ax))) {
        return false;
    }

    for p in range(0, n) {
        // Do not check for any of the vertices in the triangle.
        if (p == u) || (p == v) || (p == w) { continue; }

        let p_pos = contour.get(*vertex_indices.get(p)).unwrap();
        if inside_triangle(ax,ay,bx,by,cx,cy,p_pos.get_x(),p_pos.get_y()) { return false; }
    }

    true
}

pub fn process(
    contour: &[Vector2d]
) -> Option<Vec<Vector2d>> {
    /* allocate and initialize list of Vertices in polygon */

    let n = contour.len();
    if n < 3 { return None; }

    let mut result: Vec<Vector2d> = Vec::new();
    let mut vertex_indices: Vec<uint> = Vec::with_capacity(n);

    /* we want a counter-clockwise polygon in V */

    // Computes the area which has opposite signs
    // whether the polygon is clock-wise or counter-clockwise.
    if 0.0_f64 < area(contour) {
        for v in range(0, n) { vertex_indices.push(v); }
    } else {
        // Add indices in reverse order.
        for v in range(0, n) { vertex_indices.push((n-1)-v); }
    }

    // Stores the number of vertices.
    // Used instead of `vertex_indices.len()` to emulate removing of vertices.
    let mut number_of_vertices = n;

    /*  remove nv-2 Vertices, creating 1 triangle every time */

    // Used for error detection.
    let mut count = 2 * number_of_vertices;

    let mut v = number_of_vertices - 1;
    while number_of_vertices > 2 {
        /* if we loop, it is probably a non-simple polygon */
        if count <= 0 {
            //** Triangulate: ERROR - probable bad polygon!
            return None;
        }

        count -= 1;

        /* three consecutive vertices in current polygon, <u,v,w> */
        let triangle = [
            v,
            next(number_of_vertices, v),
            offset(number_of_vertices, v, 2)
        ];
        v = triangle[1];

        if snip(contour, triangle, number_of_vertices, &mut vertex_indices) {
            for &i in triangle.iter() {
                result.push(*contour.get(*vertex_indices.get(i)).unwrap());
            }

            /* remove v from remaining polygon */
            for i in range(0, number_of_vertices - 3) {
                let s = v + i;
                let t = v + i + 1;

                *vertex_indices.get_mut(s) = *vertex_indices.get(t);
            }

            // "Removed" a vertex.
            number_of_vertices -= 1;

            /* resest error detection counter */
            count = 2 * number_of_vertices;
        }
    }

    Some(result)
}

<|endoftext|>


<commit_before>/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use clap::{ArgEnum, Parser};
use common::ConsoleLogger;
use log::{error, info};
use relay_compiler::{
    build_project::artifact_writer::ArtifactValidationWriter,
    compiler::Compiler,
    config::{Config, SingleProjectConfigFile},
    FileSourceKind, LocalPersister, OperationPersister, PersistConfig, RemotePersister,
};
use relay_typegen::TypegenLanguage;
use simplelog::{ColorChoice, ConfigBuilder, LevelFilter, TermLogger, TerminalMode};
use std::{
    env::{self, current_dir},
    path::PathBuf,
    process::Command,
    sync::Arc,
};

#[derive(Parser)]
#[clap(
    name = "Relay Compiler",
    version = option_env!("CARGO_PKG_VERSION").unwrap_or("unknown"),
    about = "Compiles Relay files and writes generated files.",
    rename_all = "camel_case"
)]
struct Opt {
    /// Compile and watch for changes
    #[clap(long, short)]
    watch: bool,

    /// Compile using this config file. If not provided, searches for a config in
    /// package.json under the `relay` key or `relay.config.json` files among other up
    /// from the current working directory.
    config: Option<PathBuf>,

    #[clap(flatten)]
    cli_config: CliConfig,

    /// Run the persister even if the query has not changed.
    #[clap(long)]
    repersist: bool,

    /// Verbosity level
    #[clap(long, arg_enum, default_value = "verbose")]
    output: OutputKind,

    /// Looks for pending changes and exits with non-zero code instead of
    /// writing to disk
    #[structopt(long)]
    validate: bool,
}

#[derive(ArgEnum, Clone, Copy)]
enum OutputKind {
    Debug,
    Quiet,
    QuietWithErrors,
    Verbose,
}

#[derive(Parser)]
#[clap(rename_all = "camel_case")]
pub struct CliConfig {
    /// Path for the directory where to search for source code
    #[clap(long)]
    pub src: Option<PathBuf>,
    /// Path to schema file
    #[clap(long)]
    pub schema: Option<PathBuf>,
    /// Path to a directory, where the compiler should write artifacts
    #[clap(long)]
    pub artifact_directory: Option<PathBuf>,
}

impl CliConfig {
    pub fn is_defined(&self) -> bool {
        self.src.is_some() || self.schema.is_some() || self.artifact_directory.is_some()
    }
}

impl From<CliConfig> for SingleProjectConfigFile {
    fn from(cli_config: CliConfig) -> Self {
        SingleProjectConfigFile {
            schema: cli_config.schema.expect("schema is required."),
            artifact_directory: cli_config.artifact_directory,
            src: cli_config.src.unwrap_or_else(|| PathBuf::from("./")),
            language: Some(TypegenLanguage::TypeScript),
            ..Default::default()
        }
    }
}

#[tokio::main]
async fn main() {
    let opt = Opt::parse();

    let log_level = match opt.output {
        OutputKind::Debug => LevelFilter::Debug,
        OutputKind::Quiet => LevelFilter::Off,
        OutputKind::QuietWithErrors => LevelFilter::Error,
        OutputKind::Verbose => LevelFilter::Info,
    };

    let config = ConfigBuilder::new()
        .set_time_level(LevelFilter::Off)
        .set_target_level(LevelFilter::Off)
        .set_location_level(LevelFilter::Off)
        .set_thread_level(LevelFilter::Off)
        .build();

    TermLogger::init(log_level, config, TerminalMode::Mixed, ColorChoice::Auto).unwrap();

    let config_result = if let Some(config_path) = opt.config {
        Config::load(config_path)
    } else if opt.cli_config.is_defined() {
        Ok(Config::from(SingleProjectConfigFile::from(opt.cli_config)))
    } else {
        Config::search(&current_dir().expect("Unable to get current working directory."))
    };

    let mut config = config_result.unwrap_or_else(|err| {
        error!("{}", err);
        std::process::exit(1);
    });

    if opt.validate {
        config.artifact_writer = Box::new(ArtifactValidationWriter::default());
    }

    config.create_operation_persister = Some(Box::new(|project_config| {
        project_config.persist.as_ref().map(
            |persist_config| -> Box<dyn OperationPersister + Send + Sync> {
                match persist_config {
                    PersistConfig::Remote(remote_config) => {
                        Box::new(RemotePersister::new(remote_config.clone()))
                    }
                    PersistConfig::Local(local_config) => {
                        Box::new(LocalPersister::new(local_config.clone()))
                    }
                }
            },
        )
    }));

    config.file_source_config = if should_use_watchman() {
        FileSourceKind::Watchman
    } else {
        FileSourceKind::WalkDir
    };
    config.repersist_operations = opt.repersist;

    if opt.watch && !matches!(&config.file_source_config, FileSourceKind::Watchman) {
        panic!(
            "Cannot run relay in watch mode if `watchman` is not available (or explicitly disabled)."
        );
    }

    let compiler = Compiler::new(Arc::new(config), Arc::new(ConsoleLogger));

    if opt.watch {
        if let Err(err) = compiler.watch().await {
            error!("Watchman error: {}", err);
            std::process::exit(1);
        }
    } else {
        match compiler.compile().await {
            Ok(_compiler_state) => {
                info!("Done");
            }
            Err(_err) => {
                std::process::exit(1);
            }
        }
    }
}

/// Check if `watchman` is available.
/// Additionally, this method is checking for an existence of `FORCE_NO_WATCHMAN`
/// environment variable. If this `FORCE_NO_WATCHMAN` is set, this method will return `false`
/// and compiler will use non-watchman file finder.
fn should_use_watchman() -> bool {
    let check_watchman = Command::new("watchman")
        .args(["list-capabilities"])
        .output();

    check_watchman.is_ok() && env::var("FORCE_NO_WATCHMAN").is_err()
}
<commit_msg>Switch relay to clap v3<commit_after>/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use clap::{ArgEnum, Parser};
use common::ConsoleLogger;
use log::{error, info};
use relay_compiler::{
    build_project::artifact_writer::ArtifactValidationWriter,
    compiler::Compiler,
    config::{Config, SingleProjectConfigFile},
    FileSourceKind, LocalPersister, OperationPersister, PersistConfig, RemotePersister,
};
use relay_typegen::TypegenLanguage;
use simplelog::{ColorChoice, ConfigBuilder, LevelFilter, TermLogger, TerminalMode};
use std::{
    env::{self, current_dir},
    path::PathBuf,
    process::Command,
    sync::Arc,
};

#[derive(Parser)]
#[clap(
    name = "Relay Compiler",
    version = option_env!("CARGO_PKG_VERSION").unwrap_or("unknown"),
    about = "Compiles Relay files and writes generated files.",
    rename_all = "camel_case"
)]
struct Opt {
    /// Compile and watch for changes
    #[clap(long, short)]
    watch: bool,

    /// Compile using this config file. If not provided, searches for a config in
    /// package.json under the `relay` key or `relay.config.json` files among other up
    /// from the current working directory.
    config: Option<PathBuf>,

    #[clap(flatten)]
    cli_config: CliConfig,

    /// Run the persister even if the query has not changed.
    #[clap(long)]
    repersist: bool,

    /// Verbosity level
    #[clap(long, arg_enum, default_value = "verbose")]
    output: OutputKind,

    /// Looks for pending changes and exits with non-zero code instead of
    /// writing to disk
    #[clap(long)]
    validate: bool,
}

#[derive(ArgEnum, Clone, Copy)]
enum OutputKind {
    Debug,
    Quiet,
    QuietWithErrors,
    Verbose,
}

#[derive(Parser)]
#[clap(rename_all = "camel_case")]
pub struct CliConfig {
    /// Path for the directory where to search for source code
    #[clap(long)]
    pub src: Option<PathBuf>,
    /// Path to schema file
    #[clap(long)]
    pub schema: Option<PathBuf>,
    /// Path to a directory, where the compiler should write artifacts
    #[clap(long)]
    pub artifact_directory: Option<PathBuf>,
}

impl CliConfig {
    pub fn is_defined(&self) -> bool {
        self.src.is_some() || self.schema.is_some() || self.artifact_directory.is_some()
    }
}

impl From<CliConfig> for SingleProjectConfigFile {
    fn from(cli_config: CliConfig) -> Self {
        SingleProjectConfigFile {
            schema: cli_config.schema.expect("schema is required."),
            artifact_directory: cli_config.artifact_directory,
            src: cli_config.src.unwrap_or_else(|| PathBuf::from("./")),
            language: Some(TypegenLanguage::TypeScript),
            ..Default::default()
        }
    }
}

#[tokio::main]
async fn main() {
    let opt = Opt::parse();

    let log_level = match opt.output {
        OutputKind::Debug => LevelFilter::Debug,
        OutputKind::Quiet => LevelFilter::Off,
        OutputKind::QuietWithErrors => LevelFilter::Error,
        OutputKind::Verbose => LevelFilter::Info,
    };

    let config = ConfigBuilder::new()
        .set_time_level(LevelFilter::Off)
        .set_target_level(LevelFilter::Off)
        .set_location_level(LevelFilter::Off)
        .set_thread_level(LevelFilter::Off)
        .build();

    TermLogger::init(log_level, config, TerminalMode::Mixed, ColorChoice::Auto).unwrap();

    let config_result = if let Some(config_path) = opt.config {
        Config::load(config_path)
    } else if opt.cli_config.is_defined() {
        Ok(Config::from(SingleProjectConfigFile::from(opt.cli_config)))
    } else {
        Config::search(&current_dir().expect("Unable to get current working directory."))
    };

    let mut config = config_result.unwrap_or_else(|err| {
        error!("{}", err);
        std::process::exit(1);
    });

    if opt.validate {
        config.artifact_writer = Box::new(ArtifactValidationWriter::default());
    }

    config.create_operation_persister = Some(Box::new(|project_config| {
        project_config.persist.as_ref().map(
            |persist_config| -> Box<dyn OperationPersister + Send + Sync> {
                match persist_config {
                    PersistConfig::Remote(remote_config) => {
                        Box::new(RemotePersister::new(remote_config.clone()))
                    }
                    PersistConfig::Local(local_config) => {
                        Box::new(LocalPersister::new(local_config.clone()))
                    }
                }
            },
        )
    }));

    config.file_source_config = if should_use_watchman() {
        FileSourceKind::Watchman
    } else {
        FileSourceKind::WalkDir
    };
    config.repersist_operations = opt.repersist;

    if opt.watch && !matches!(&config.file_source_config, FileSourceKind::Watchman) {
        panic!(
            "Cannot run relay in watch mode if `watchman` is not available (or explicitly disabled)."
        );
    }

    let compiler = Compiler::new(Arc::new(config), Arc::new(ConsoleLogger));

    if opt.watch {
        if let Err(err) = compiler.watch().await {
            error!("Watchman error: {}", err);
            std::process::exit(1);
        }
    } else {
        match compiler.compile().await {
            Ok(_compiler_state) => {
                info!("Done");
            }
            Err(_err) => {
                std::process::exit(1);
            }
        }
    }
}

/// Check if `watchman` is available.
/// Additionally, this method is checking for an existence of `FORCE_NO_WATCHMAN`
/// environment variable. If this `FORCE_NO_WATCHMAN` is set, this method will return `false`
/// and compiler will use non-watchman file finder.
fn should_use_watchman() -> bool {
    let check_watchman = Command::new("watchman")
        .args(["list-capabilities"])
        .output();

    check_watchman.is_ok() && env::var("FORCE_NO_WATCHMAN").is_err()
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;
extern crate sputnikvm;
extern crate bigint;
extern crate serde_json;
extern crate gethrpc;

use serde_json::{Value};
use std::process;
use std::fs::File;
use std::path::Path;
use std::io::{BufReader};
use std::str::FromStr;
use std::collections::{HashMap, HashSet};

use sputnikvm::{Gas, Address};
use bigint::{U256, M256, read_hex};
use sputnikvm::vm::{BlockHeader, Context, SeqTransactionVM, Transaction, VM, Log, Patch, AccountCommitment, Account, FRONTIER_PATCH};
use sputnikvm::vm::errors::RequireError;
use gethrpc::{regression, GethRPCClient, RPCCall, RPCBlock, RPCTransaction, RPCLog};

fn from_rpc_block(block: &RPCBlock) -> BlockHeader {
    BlockHeader {
        coinbase: Address::from_str(&block.miner).unwrap(),
        timestamp: M256::from_str(&block.timestamp).unwrap(),
        number: M256::from_str(&block.number).unwrap(),
        difficulty: M256::from_str(&block.difficulty).unwrap(),
        gas_limit: Gas::from_str(&block.gasLimit).unwrap(),
    }
}

fn from_rpc_transaction(transaction: &RPCTransaction) -> Transaction {
    if transaction.to.is_empty() {
        Transaction::ContractCreation {
            caller: Address::from_str(&transaction.from).unwrap(),
            value: U256::from_str(&transaction.value).unwrap(),
            gas_limit: Gas::from_str(&transaction.gas).unwrap(),
            gas_price: Gas::from_str(&transaction.gasPrice).unwrap(),
            init: read_hex(&transaction.input).unwrap(),
        }
    } else {
        Transaction::MessageCall {
            caller: Address::from_str(&transaction.from).unwrap(),
            address: Address::from_str(&transaction.to).unwrap(),
            value: U256::from_str(&transaction.value).unwrap(),
            gas_limit: Gas::from_str(&transaction.gas).unwrap(),
            gas_price: Gas::from_str(&transaction.gasPrice).unwrap(),
            data: read_hex(&transaction.input).unwrap(),
        }
    }
}

fn from_rpc_log(log: &RPCLog) -> Log {
    let mut topics: Vec<M256> = Vec::new();
    for topic in &log.topics {
        topics.push(M256::from_str(&topic).unwrap());
    }
    Log {
        address: Address::from_str(&log.address).unwrap(),
        data: read_hex(&log.data).unwrap(),
        topics: topics,
    }
}

/// Accounts in this list should always be considered exists.
fn irregular_addresses(number: usize) -> HashSet<Address> {
    let config: HashMap<Address, usize> = {
        let mut config = HashMap::new();
        config.insert(Address::from_str("0x0000000000000000000000000000000000000000").unwrap(), 0);
        config.insert(Address::from_str("0x0000000000000000000000000000000000000001").unwrap(), 0);
        config.insert(Address::from_str("0x0000000000000000000000000000000000000002").unwrap(), 505137);
        config.insert(Address::from_str("0x0000000000000000000000000000000000000003").unwrap(), 0);
        config.insert(Address::from_str("0x0000000000000000000000000000000000000004").unwrap(), 0);

        config.insert(Address::from_str("0xbfe465e7eb5a2928b5bf22bef93ad06089dc6179").unwrap(), 229894);
        config.insert(Address::from_str("0xc7585b4ea1829ef37f2fcc6ceaa6063700000000").unwrap(), 188235);
        config.insert(Address::from_str("0x4fea5cb278edfabcc24f7c1425951a0d2faa609b").unwrap(), 370489);
        config.insert(Address::from_str("0x000000000000000000000000000007329b54129c").unwrap(), 254277);
        config.insert(Address::from_str("0x0000000000000000000000000000000000000194").unwrap(), 257654);

        config
    };

    let mut result: HashSet<Address> = HashSet::new();
    for (key, value) in config {
        if number > value {
            result.insert(key);
        }
    }
    result
}

fn handle_fire(client: &mut GethRPCClient, vm: &mut SeqTransactionVM, last_block_number: &str,
               irregular_addresses: &HashSet<Address>) {
    loop {
        match vm.fire() {
            Ok(()) => {
                println!("VM exited with {:?}.", vm.status());
                break;
            },
            Err(RequireError::Account(address)) => {
                println!("Feeding VM account at 0x{:x} ...", address);
                let nonce = M256::from_str(&client.get_transaction_count(&format!("0x{:x}", address),
                                                                         &last_block_number)).unwrap();
                let balance = U256::from_str(&client.get_balance(&format!("0x{:x}", address),
                                                                 &last_block_number)).unwrap();
                let code = read_hex(&client.get_code(&format!("0x{:x}", address),
                                                     &last_block_number)).unwrap();
                if nonce == M256::zero() && balance == U256::zero() && code.len() == 0 &&
                    !irregular_addresses.contains(&address)
                {
                    vm.commit_account(AccountCommitment::Nonexist(address)).unwrap();
                } else {
                    vm.commit_account(AccountCommitment::Full {
                        nonce: nonce,
                        address: address,
                        balance: balance,
                        code: code,
                    }).unwrap();
                }
            },
            Err(RequireError::AccountStorage(address, index)) => {
                println!("Feeding VM account storage at 0x{:x} with index 0x{:x} ...", address, index);
                let value = M256::from_str(&client.get_storage_at(&format!("0x{:x}", address),
                                                                  &format!("0x{:x}", index),
                                                                  &last_block_number)).unwrap();
                vm.commit_account(AccountCommitment::Storage {
                    address: address,
                    index: index,
                    value: value,
                }).unwrap();
            },
            Err(RequireError::AccountCode(address)) => {
                println!("Feeding VM account code at 0x{:x} ...", address);
                let code = read_hex(&client.get_code(&format!("0x{:x}", address),
                                                     &last_block_number)).unwrap();
                vm.commit_account(AccountCommitment::Code {
                    address: address,
                    code: code,
                }).unwrap();
            },
            Err(RequireError::Blockhash(number)) => {
                println!("Feeding blockhash with number 0x{:x} ...", number);
                let hash = M256::from_str(&client.get_block_by_number(&format!("0x{:x}", number))
                                          .hash).unwrap();
                vm.commit_blockhash(number, hash);
            },
        }
    }
}

fn is_miner_or_uncle(address: Address, block: &RPCBlock, client: &mut GethRPCClient) -> bool {
    // Give up balance testing if the address is a miner or an uncle.

    let miner = Address::from_str(&block.miner).unwrap();
    if miner == address {
        return true;
    }
    if block.uncles.len() > 0 {
        for i in 0..block.uncles.len() {
            let uncle = client.get_uncle_by_block_number_and_index(&block.number,
                                                                   &format!("0x{:x}", i));
            let uncle_miner = Address::from_str(&uncle.miner).unwrap();
            if uncle_miner == address {
                return true;
            }
        }
    }

    return false;
}

fn test_block(client: &mut GethRPCClient, number: usize) {
    let block = client.get_block_by_number(format!("0x{:x}", number).as_str());
    println!("block {}, transaction count: {}", block.number, block.transactions.len());
    let last_number = format!("0x{:x}", number - 1);
    let cur_number = block.number.to_string();
    let block_header = from_rpc_block(&block);
    let irregular_addresses = irregular_addresses(number);

    let mut last_vm: Option<SeqTransactionVM> = None;
    for transaction_hash in &block.transactions {
        println!("\nworking on transaction {}", transaction_hash);
        let transaction = from_rpc_transaction(&client.get_transaction_by_hash(&transaction_hash));
        let receipt = client.get_transaction_receipt(&transaction_hash);

        let mut vm = if last_vm.is_none() {
            SeqTransactionVM::new(transaction, block_header.clone(), &FRONTIER_PATCH)
        } else {
            SeqTransactionVM::with_previous(transaction, block_header.clone(), &FRONTIER_PATCH, last_vm.as_ref().unwrap())
        };

        handle_fire(client, &mut vm, &last_number, &irregular_addresses);

        assert!(Gas::from_str(&receipt.gasUsed).unwrap() == vm.real_used_gas());
        assert!(receipt.logs.len() == vm.logs().len());
        for i in 0..receipt.logs.len() {
            assert!(from_rpc_log(&receipt.logs[i]) == vm.logs()[i]);
        }

        last_vm = Some(vm);
    }

    if last_vm.is_some() {
        for account in last_vm.as_ref().unwrap().accounts() {
            match account {
                &Account::Full {
                    address,
                    balance,
                    ref changing_storage,
                    ..
                } => {
                    if !is_miner_or_uncle(address, &block, client) {
                        let expected_balance = client.get_balance(&format!("0x{:x}", address),
                                                                  &cur_number);
                        assert!(U256::from_str(&expected_balance).unwrap() == balance);
                    }
                    let changing_storage: HashMap<M256, M256> = changing_storage.clone().into();
                    for (key, value) in changing_storage {
                        let expected_value = client.get_storage_at(&format!("0x{:x}", address),
                                                                   &format!("0x{:x}", key),
                                                                   &cur_number);
                        assert!(M256::from_str(&expected_value).unwrap() == value);
                    }
                },
                &Account::Create {
                    address,
                    balance,
                    ref storage,
                    ..
                } => {
                    if !is_miner_or_uncle(address, &block, client) {
                        let expected_balance = client.get_balance(&format!("0x{:x}", address),
                                                                  &cur_number);
                        assert!(U256::from_str(&expected_balance).unwrap() == balance);
                    }
                    let storage: HashMap<M256, M256> = storage.clone().into();
                    for (key, value) in storage {
                        let expected_value = client.get_storage_at(&format!("0x{:x}", address),
                                                                   &format!("0x{:x}", key),
                                                                   &cur_number);
                        assert!(M256::from_str(&expected_value).unwrap() == value);
                    }
                },
                &Account::IncreaseBalance(address, balance) => {
                    if !is_miner_or_uncle(address, &block, client) {
                        let last_balance = client.get_balance(&format!("0x{:x}", address),
                                                              &last_number);
                        let cur_balance = client.get_balance(&format!("0x{:x}", address),
                                                             &cur_number);

                        assert!(U256::from_str(&last_balance).unwrap() + balance ==
                                U256::from_str(&cur_balance).unwrap());
                    }
                },
                &Account::DecreaseBalance(address, balance) => {
                    if !is_miner_or_uncle(address, &block, client) {
                        let last_balance = client.get_balance(&format!("0x{:x}", address),
                                                              &last_number);
                        let cur_balance = client.get_balance(&format!("0x{:x}", address),
                                                             &cur_number);

                        assert!(U256::from_str(&last_balance).unwrap() - balance ==
                                U256::from_str(&cur_balance).unwrap());
                    }
                },
            }
        }
    }
}

fn main() {
    let matches = clap_app!(regtests =>
        (version: "0.1")
        (author: "Ethereum Classic Contributors")
        (about: "Performs an regression test on the entire Ethereum Classic blockchain.\n\nSteps to reproduce:\n* Install Ethereum Classic Geth: `$ go install github.com/ethereumproject/go-ethereum/cmd/geth`.\n* Run Geth with this command: `$ ~/go/bin/geth --rpc --rpcaddr 127.0.0.1 --rpcport 8545`.\n* Wait for the chain to sync.\n* Change directory into the `regtests` directory `$ cd regtests`\n* Run this command: `$ RUST_BACKTRACE=1 cargo run --bin regtests -- -r http://127.0.0.1:8545")
        (@arg RPC: -r --rpc +takes_value +required "Domain of Ethereum Classic Geth's RPC endpoint. e.g. `-r http://127.0.0.1:8545`.")
        (@arg NUMBER: -n --number +takes_value + required "Block number to run this test. Radix is 10. e.g. `-n 49439`.")
    ).get_matches();

    let address = matches.value_of("RPC").unwrap();
    let number = matches.value_of("NUMBER").unwrap();
    let mut client = GethRPCClient::new(address);

    if number.contains("..") {
        let number: Vec<&str> = number.split("..").collect();
        let from = usize::from_str_radix(&number[0], 10).unwrap();
        let to = usize::from_str_radix(&number[1], 10).unwrap();
        for n in from..to {
            test_block(&mut client, n);
        }
    } else {
        let number = usize::from_str_radix(&number, 10).unwrap();
        test_block(&mut client, number);
    }
}
<commit_msg>Irregular account: add<commit_after>#[macro_use]
extern crate clap;
extern crate sputnikvm;
extern crate bigint;
extern crate serde_json;
extern crate gethrpc;

use serde_json::{Value};
use std::process;
use std::fs::File;
use std::path::Path;
use std::io::{BufReader};
use std::str::FromStr;
use std::collections::{HashMap, HashSet};

use sputnikvm::{Gas, Address};
use bigint::{U256, M256, read_hex};
use sputnikvm::vm::{BlockHeader, Context, SeqTransactionVM, Transaction, VM, Log, Patch, AccountCommitment, Account, FRONTIER_PATCH};
use sputnikvm::vm::errors::RequireError;
use gethrpc::{regression, GethRPCClient, RPCCall, RPCBlock, RPCTransaction, RPCLog};

fn from_rpc_block(block: &RPCBlock) -> BlockHeader {
    BlockHeader {
        coinbase: Address::from_str(&block.miner).unwrap(),
        timestamp: M256::from_str(&block.timestamp).unwrap(),
        number: M256::from_str(&block.number).unwrap(),
        difficulty: M256::from_str(&block.difficulty).unwrap(),
        gas_limit: Gas::from_str(&block.gasLimit).unwrap(),
    }
}

fn from_rpc_transaction(transaction: &RPCTransaction) -> Transaction {
    if transaction.to.is_empty() {
        Transaction::ContractCreation {
            caller: Address::from_str(&transaction.from).unwrap(),
            value: U256::from_str(&transaction.value).unwrap(),
            gas_limit: Gas::from_str(&transaction.gas).unwrap(),
            gas_price: Gas::from_str(&transaction.gasPrice).unwrap(),
            init: read_hex(&transaction.input).unwrap(),
        }
    } else {
        Transaction::MessageCall {
            caller: Address::from_str(&transaction.from).unwrap(),
            address: Address::from_str(&transaction.to).unwrap(),
            value: U256::from_str(&transaction.value).unwrap(),
            gas_limit: Gas::from_str(&transaction.gas).unwrap(),
            gas_price: Gas::from_str(&transaction.gasPrice).unwrap(),
            data: read_hex(&transaction.input).unwrap(),
        }
    }
}

fn from_rpc_log(log: &RPCLog) -> Log {
    let mut topics: Vec<M256> = Vec::new();
    for topic in &log.topics {
        topics.push(M256::from_str(&topic).unwrap());
    }
    Log {
        address: Address::from_str(&log.address).unwrap(),
        data: read_hex(&log.data).unwrap(),
        topics: topics,
    }
}

/// Accounts in this list should always be considered exists.
fn irregular_addresses(number: usize) -> HashSet<Address> {
    let config: HashMap<Address, usize> = {
        let mut config = HashMap::new();
        config.insert(Address::from_str("0x0000000000000000000000000000000000000000").unwrap(), 0);
        config.insert(Address::from_str("0x0000000000000000000000000000000000000001").unwrap(), 0);
        config.insert(Address::from_str("0x0000000000000000000000000000000000000002").unwrap(), 505137);
        config.insert(Address::from_str("0x0000000000000000000000000000000000000003").unwrap(), 0);
        config.insert(Address::from_str("0x0000000000000000000000000000000000000004").unwrap(), 0);

        config.insert(Address::from_str("0xbfe465e7eb5a2928b5bf22bef93ad06089dc6179").unwrap(), 229894);
        config.insert(Address::from_str("0xc7585b4ea1829ef37f2fcc6ceaa6063700000000").unwrap(), 188235);
        config.insert(Address::from_str("0x4fea5cb278edfabcc24f7c1425951a0d2faa609b").unwrap(), 370489);
        config.insert(Address::from_str("0x000000000000000000000000000007329b54129c").unwrap(), 254277);
        config.insert(Address::from_str("0x0000000000000000000000000000000000000194").unwrap(), 257654);
        config.insert(Address::from_str("0xf5ed057f297c6389d80245c8631b040f05804578").unwrap(), 637752);

        config
    };

    let mut result: HashSet<Address> = HashSet::new();
    for (key, value) in config {
        if number > value {
            result.insert(key);
        }
    }
    result
}

fn handle_fire(client: &mut GethRPCClient, vm: &mut SeqTransactionVM, last_block_number: &str,
               irregular_addresses: &HashSet<Address>) {
    loop {
        match vm.fire() {
            Ok(()) => {
                println!("VM exited with {:?}.", vm.status());
                break;
            },
            Err(RequireError::Account(address)) => {
                println!("Feeding VM account at 0x{:x} ...", address);
                let nonce = M256::from_str(&client.get_transaction_count(&format!("0x{:x}", address),
                                                                         &last_block_number)).unwrap();
                let balance = U256::from_str(&client.get_balance(&format!("0x{:x}", address),
                                                                 &last_block_number)).unwrap();
                let code = read_hex(&client.get_code(&format!("0x{:x}", address),
                                                     &last_block_number)).unwrap();
                if nonce == M256::zero() && balance == U256::zero() && code.len() == 0 &&
                    !irregular_addresses.contains(&address)
                {
                    vm.commit_account(AccountCommitment::Nonexist(address)).unwrap();
                } else {
                    vm.commit_account(AccountCommitment::Full {
                        nonce: nonce,
                        address: address,
                        balance: balance,
                        code: code,
                    }).unwrap();
                }
            },
            Err(RequireError::AccountStorage(address, index)) => {
                println!("Feeding VM account storage at 0x{:x} with index 0x{:x} ...", address, index);
                let value = M256::from_str(&client.get_storage_at(&format!("0x{:x}", address),
                                                                  &format!("0x{:x}", index),
                                                                  &last_block_number)).unwrap();
                vm.commit_account(AccountCommitment::Storage {
                    address: address,
                    index: index,
                    value: value,
                }).unwrap();
            },
            Err(RequireError::AccountCode(address)) => {
                println!("Feeding VM account code at 0x{:x} ...", address);
                let code = read_hex(&client.get_code(&format!("0x{:x}", address),
                                                     &last_block_number)).unwrap();
                vm.commit_account(AccountCommitment::Code {
                    address: address,
                    code: code,
                }).unwrap();
            },
            Err(RequireError::Blockhash(number)) => {
                println!("Feeding blockhash with number 0x{:x} ...", number);
                let hash = M256::from_str(&client.get_block_by_number(&format!("0x{:x}", number))
                                          .hash).unwrap();
                vm.commit_blockhash(number, hash);
            },
        }
    }
}

fn is_miner_or_uncle(address: Address, block: &RPCBlock, client: &mut GethRPCClient) -> bool {
    // Give up balance testing if the address is a miner or an uncle.

    let miner = Address::from_str(&block.miner).unwrap();
    if miner == address {
        return true;
    }
    if block.uncles.len() > 0 {
        for i in 0..block.uncles.len() {
            let uncle = client.get_uncle_by_block_number_and_index(&block.number,
                                                                   &format!("0x{:x}", i));
            let uncle_miner = Address::from_str(&uncle.miner).unwrap();
            if uncle_miner == address {
                return true;
            }
        }
    }

    return false;
}

fn test_block(client: &mut GethRPCClient, number: usize) {
    let block = client.get_block_by_number(format!("0x{:x}", number).as_str());
    println!("block {}, transaction count: {}", block.number, block.transactions.len());
    let last_number = format!("0x{:x}", number - 1);
    let cur_number = block.number.to_string();
    let block_header = from_rpc_block(&block);
    let irregular_addresses = irregular_addresses(number);

    let mut last_vm: Option<SeqTransactionVM> = None;
    for transaction_hash in &block.transactions {
        println!("\nworking on transaction {}", transaction_hash);
        let transaction = from_rpc_transaction(&client.get_transaction_by_hash(&transaction_hash));
        let receipt = client.get_transaction_receipt(&transaction_hash);

        let mut vm = if last_vm.is_none() {
            SeqTransactionVM::new(transaction, block_header.clone(), &FRONTIER_PATCH)
        } else {
            SeqTransactionVM::with_previous(transaction, block_header.clone(), &FRONTIER_PATCH, last_vm.as_ref().unwrap())
        };

        handle_fire(client, &mut vm, &last_number, &irregular_addresses);

        assert!(Gas::from_str(&receipt.gasUsed).unwrap() == vm.real_used_gas());
        assert!(receipt.logs.len() == vm.logs().len());
        for i in 0..receipt.logs.len() {
            assert!(from_rpc_log(&receipt.logs[i]) == vm.logs()[i]);
        }

        last_vm = Some(vm);
    }

    if last_vm.is_some() {
        for account in last_vm.as_ref().unwrap().accounts() {
            match account {
                &Account::Full {
                    address,
                    balance,
                    ref changing_storage,
                    ..
                } => {
                    if !is_miner_or_uncle(address, &block, client) {
                        let expected_balance = client.get_balance(&format!("0x{:x}", address),
                                                                  &cur_number);
                        assert!(U256::from_str(&expected_balance).unwrap() == balance);
                    }
                    let changing_storage: HashMap<M256, M256> = changing_storage.clone().into();
                    for (key, value) in changing_storage {
                        let expected_value = client.get_storage_at(&format!("0x{:x}", address),
                                                                   &format!("0x{:x}", key),
                                                                   &cur_number);
                        assert!(M256::from_str(&expected_value).unwrap() == value);
                    }
                },
                &Account::Create {
                    address,
                    balance,
                    ref storage,
                    ..
                } => {
                    if !is_miner_or_uncle(address, &block, client) {
                        let expected_balance = client.get_balance(&format!("0x{:x}", address),
                                                                  &cur_number);
                        assert!(U256::from_str(&expected_balance).unwrap() == balance);
                    }
                    let storage: HashMap<M256, M256> = storage.clone().into();
                    for (key, value) in storage {
                        let expected_value = client.get_storage_at(&format!("0x{:x}", address),
                                                                   &format!("0x{:x}", key),
                                                                   &cur_number);
                        assert!(M256::from_str(&expected_value).unwrap() == value);
                    }
                },
                &Account::IncreaseBalance(address, balance) => {
                    if !is_miner_or_uncle(address, &block, client) {
                        let last_balance = client.get_balance(&format!("0x{:x}", address),
                                                              &last_number);
                        let cur_balance = client.get_balance(&format!("0x{:x}", address),
                                                             &cur_number);

                        assert!(U256::from_str(&last_balance).unwrap() + balance ==
                                U256::from_str(&cur_balance).unwrap());
                    }
                },
                &Account::DecreaseBalance(address, balance) => {
                    if !is_miner_or_uncle(address, &block, client) {
                        let last_balance = client.get_balance(&format!("0x{:x}", address),
                                                              &last_number);
                        let cur_balance = client.get_balance(&format!("0x{:x}", address),
                                                             &cur_number);

                        assert!(U256::from_str(&last_balance).unwrap() - balance ==
                                U256::from_str(&cur_balance).unwrap());
                    }
                },
            }
        }
    }
}

fn main() {
    let matches = clap_app!(regtests =>
        (version: "0.1")
        (author: "Ethereum Classic Contributors")
        (about: "Performs an regression test on the entire Ethereum Classic blockchain.\n\nSteps to reproduce:\n* Install Ethereum Classic Geth: `$ go install github.com/ethereumproject/go-ethereum/cmd/geth`.\n* Run Geth with this command: `$ ~/go/bin/geth --rpc --rpcaddr 127.0.0.1 --rpcport 8545`.\n* Wait for the chain to sync.\n* Change directory into the `regtests` directory `$ cd regtests`\n* Run this command: `$ RUST_BACKTRACE=1 cargo run --bin regtests -- -r http://127.0.0.1:8545")
        (@arg RPC: -r --rpc +takes_value +required "Domain of Ethereum Classic Geth's RPC endpoint. e.g. `-r http://127.0.0.1:8545`.")
        (@arg NUMBER: -n --number +takes_value + required "Block number to run this test. Radix is 10. e.g. `-n 49439`.")
    ).get_matches();

    let address = matches.value_of("RPC").unwrap();
    let number = matches.value_of("NUMBER").unwrap();
    let mut client = GethRPCClient::new(address);

    if number.contains("..") {
        let number: Vec<&str> = number.split("..").collect();
        let from = usize::from_str_radix(&number[0], 10).unwrap();
        let to = usize::from_str_radix(&number[1], 10).unwrap();
        for n in from..to {
            test_block(&mut client, n);
        }
    } else {
        let number = usize::from_str_radix(&number, 10).unwrap();
        test_block(&mut client, number);
    }
}
<|endoftext|>