<commit_before>use select::SelectType;
use join::Join;
use order_by::OrderBy;
use where_cl::{ToSQL, WhereType};

pub struct Query<'a> {
    pub select: SelectType<'a>,
    pub from: &'a str,
    pub joins: &'a [Join<'a>],
    pub group_by: &'a [&'a str],
    pub order_by: &'a [OrderBy<'a>],
    pub where_cl: WhereType<'a>,
    pub having: WhereType<'a>
}

impl<'a> Query<'a> {
    fn to_sql(&self) -> String {
        let mut rv = String::new();
        rv.push_str("SELECT");
        rv.push(' ');
        rv.push_str(&self.select.to_sql());
        rv.push(' ');
        rv.push_str("FROM");
        rv.push(' ');
        rv.push_str(self.from);

        for join in self.joins {
            rv.push(' ');
            rv.push_str(&join.to_sql());
        }

        let where_string = " WHERE ";
        match self.where_cl {
            WhereType::Empty => {},
            WhereType::Simple(clause) => {
                rv.push_str(where_string);
                rv.push_str(&clause.to_sql());
            },
            WhereType::Extended(clause) => {
                rv.push_str(where_string);
                rv.push_str(&clause.to_sql());
            }
        }

        if !self.group_by.is_empty() {
            rv.push(' ');
            rv.push_str("GROUP BY");
            rv.push(' ');
            rv.push_str(&self.group_by.join(", "));
        }

        let having_string = " HAVING ";
        match self.having {
            WhereType::Empty => {},
            WhereType::Simple(clause) => {
                rv.push_str(having_string);
                rv.push_str(&clause.to_sql());
            },
            WhereType::Extended(clause) => {
                rv.push_str(having_string);
                rv.push_str(&clause.to_sql());
            }
        }

        if !self.order_by.is_empty() {
            rv.push(' ');
            rv.push_str("ORDER BY");
            rv.push(' ');
            rv.push_str(&self.order_by
                        .into_iter()
                        .map(|x| x.to_sql())
                        .collect::<Vec<String>>()
                        .join(", "));
        }

        rv.push(';');
        rv
    }
}

#[cfg(test)]
mod tests {
    extern crate test;
    use self::test::Bencher;

    use super::Query;
    use select::SelectType;
    use join::{JoinType, Join};
    use order_by::{Ordering, OrderBy};
    use where_cl::{Operator, ToSQL, WhereType, Where};

    #[test]
    fn select_all() {
        let query = Query {
            select: SelectType::All,
            from: "test_table",
            joins: &[],
            group_by: &[],
            order_by: &[],
            where_cl: WhereType::Empty,
            having: WhereType::Empty,
        };
        assert_eq!(query.to_sql(), "SELECT * FROM test_table;".to_string());
    }

    #[test]
    fn select_foo_and_bar() {
        let clauses = &["foo", "bar"];
        let query = Query {
            select: SelectType::Specific(clauses),
            from: "test_table",
            joins: &[],
            group_by: &[],
            order_by: &[],
            where_cl: WhereType::Empty,
            having: WhereType::Empty,
        };
        assert_eq!(query.to_sql(), "SELECT foo, bar FROM test_table;".to_string());
    }

    #[test]
    fn select_foo_and_bar_with_vec_params() {
        let clauses = vec!["foo", "bar"];
        let query = Query {
            select: SelectType::Specific(&clauses),
            from: "test_table",
            joins: &[],
            group_by: &[],
            order_by: &[],
            where_cl: WhereType::Empty,
            having: WhereType::Empty,
        };
        assert_eq!(query.to_sql(), "SELECT foo, bar FROM test_table;".to_string());
    }

    #[test]
    fn select_foo_and_bar_with_vec_params_and_strings() {
        let clauses = vec!["foo", "bar"];
        let query = Query {
            select: SelectType::Specific(&clauses),
            from: "test_table",
            joins: &[],
            group_by: &[],
            order_by: &[],
            where_cl: WhereType::Empty,
            having: WhereType::Empty,
        };
        assert_eq!(query.to_sql(), "SELECT foo, bar FROM test_table;".to_string());
    }

    #[test]
    fn select_foo_and_join_bar() {
        let join = Join {
            join_type: JoinType::Inner,
            target: "target_table",
            clause: "2 == 2"
        };

        let query = Query {
            select: SelectType::All,
            from: "test_table",
            joins: &[join],
            group_by: &[],
            order_by: &[],
            where_cl: WhereType::Empty,
            having: WhereType::Empty,
        };

        let test_sql_string = {
            "SELECT * \
            FROM test_table \
            INNER JOIN target_table ON 2 == 2;".to_string()
        };
        assert_eq!(query.to_sql(), test_sql_string);
    }

    #[test]
    fn select_foo_and_join_bar_and_bazz() {
        let bar_join = Join {
            join_type: JoinType::Inner,
            target: "bar_table",
            clause: "1 == 1"
        };

        let bazz_join = Join {
            join_type: JoinType::Left,
            target: "bazz_table",
            clause: "2 == 2"
        };

        let query = Query {
            select: SelectType::All,
            from: "test_table",
            joins: &[bar_join, bazz_join],
            group_by: &[],
            order_by: &[],
            where_cl: WhereType::Empty,
            having: WhereType::Empty,
        };

        let test_sql_string = {
            "SELECT * \
            FROM test_table \
            INNER JOIN bar_table ON 1 == 1 \
            LEFT JOIN bazz_table ON 2 == 2;".to_string()
        };
        assert_eq!(query.to_sql(), test_sql_string);
    }

    #[test]
    fn select_all_and_group_by_foo() {
        let query = Query {
            select: SelectType::All,
            from: "test_table",
            joins: &[],
            group_by: &["foo"],
            order_by: &[],
            where_cl: WhereType::Empty,
            having: WhereType::Empty,
        };

        let test_sql_string = {
            "SELECT * \
            FROM test_table \
            GROUP BY foo;".to_string()
        };
        assert_eq!(query.to_sql(), test_sql_string);
    }

    #[test]
    fn select_all_and_group_by_foo_and_bar() {
        let query = Query {
            select: SelectType::All,
            from: "test_table",
            joins: &[],
            group_by: &["foo", "bar"],
            order_by: &[],
            where_cl: WhereType::Empty,
            having: WhereType::Empty,
        };

        let test_sql_string = {
            "SELECT * \
            FROM test_table \
            GROUP BY foo, bar;".to_string()
        };
        assert_eq!(query.to_sql(), test_sql_string);
    }

    #[test]
    fn select_all_and_order_by() {
        let order_by_foo_asc = OrderBy {
            ordering: Ordering::Ascending,
            order_by: "foo"
        };

        let query = Query {
            select: SelectType::All,
            from: "test_table",
            joins: &[],
            group_by: &[],
            order_by: &[order_by_foo_asc],
            where_cl: WhereType::Empty,
            having: WhereType::Empty,
        };

        let test_sql_string = {
            "SELECT * \
            FROM test_table \
            ORDER BY foo ASC;".to_string()
        };
        assert_eq!(query.to_sql(), test_sql_string);
    }

    #[test]
    fn select_all_and_multi_order_by() {
        let order_by_foo_asc = OrderBy {
            ordering: Ordering::Ascending,
            order_by: "foo"
        };

        let order_by_bar_desc = OrderBy {
            ordering: Ordering::Descending,
            order_by: "bar"
        };

        let query = Query {
            select: SelectType::All,
            from: "test_table",
            joins: &[],
            group_by: &[],
            order_by: &[order_by_foo_asc, order_by_bar_desc],
            where_cl: WhereType::Empty,
            having: WhereType::Empty,
        };

        let test_sql_string = {
            "SELECT * \
            FROM test_table \
            ORDER BY foo ASC, bar DESC;".to_string()
        };
        assert_eq!(query.to_sql(), test_sql_string);
    }

    #[test]
    fn select_all_where_simple() {
        let query = Query {
            select: SelectType::All,
            from: "test_table",
            joins: &[],
            group_by: &[],
            order_by: &[],
            where_cl: WhereType::Simple("foo == bar"),
            having: WhereType::Empty,
        };

        let test_sql_string = {
            "SELECT * \
            FROM test_table \
            WHERE foo == bar;".to_string()
        };
        assert_eq!(query.to_sql(), test_sql_string);
    }

    #[test]
    fn select_all_where_extended() {
        let where_cl = Where {
            operator: Operator::And,
            clause: &["foo == bar", "lala == blah"]
        };

        let query = Query {
            select: SelectType::All,
            from: "test_table",
            joins: &[],
            group_by: &[],
            order_by: &[],
            where_cl: WhereType::Extended(&where_cl),
            having: WhereType::Empty,
        };

        let test_sql_string = {
            "SELECT * \
            FROM test_table \
            WHERE (foo == bar AND lala == blah);".to_string()
        };
        assert_eq!(query.to_sql(), test_sql_string);
    }

    #[test]
    fn select_all_with_having() {
        let where_cl = Where {
            operator: Operator::And,
            clause: &["foo == bar", "lala == blah"]
        };

        let query = Query {
            select: SelectType::All,
            from: "test_table",
            joins: &[],
            group_by: &[],
            order_by: &[],
            where_cl: WhereType::Empty,
            having: WhereType::Simple("foo == bar")
        };

        let test_sql_string = {
            "SELECT * \
            FROM test_table \
            HAVING foo == bar;".to_string()
        };
        assert_eq!(query.to_sql(), test_sql_string);
    }

    #[test]
    fn select_all_with_extended_having() {
        let where_cl = Where {
            operator: Operator::And,
            clause: &["foo == bar", "lala == blah"]
        };

        let query = Query {
            select: SelectType::All,
            from: "test_table",
            joins: &[],
            group_by: &[],
            order_by: &[],
            where_cl: WhereType::Empty,
            having: WhereType::Extended(&where_cl),
        };

        let test_sql_string = {
            "SELECT * \
            FROM test_table \
            HAVING (foo == bar AND lala == blah);".to_string()
        };
        assert_eq!(query.to_sql(), test_sql_string);
    }

    #[test]
    fn test_complex() {
        let where_cl = Where {
            operator: Operator::And,
            clause: &["foo == bar", "lala == blah"]
        };

        let order_by_bar_desc = OrderBy {
            ordering: Ordering::Descending,
            order_by: "bar"
        };

        let order_by_foo_asc = OrderBy {
            ordering: Ordering::Ascending,
            order_by: "foo"
        };

        let bar_join = Join {
            join_type: JoinType::Inner,
            target: "bar_table",
            clause: "1 == 1"
        };

        let bazz_join = Join {
            join_type: JoinType::Left,
            target: "bazz_table",
            clause: "2 == 2"
        };

        let clauses = ["foo", "bar"];
        let query = Query {
            select: SelectType::Specific(&clauses),
            from: "test_table",
            joins: &[bar_join, bazz_join],
            group_by: &["foo", "bar"],
            order_by: &[order_by_bar_desc, order_by_foo_asc],
            where_cl: WhereType::Extended(&where_cl),
            having: WhereType::Extended(&where_cl)
        };

        let test_sql_string = {
            "SELECT foo, bar \
            FROM test_table \
            INNER JOIN bar_table ON 1 == 1 \
            LEFT JOIN bazz_table ON 2 == 2 \
            WHERE (foo == bar AND lala == blah) \
            GROUP BY foo, bar \
            HAVING (foo == bar AND lala == blah) \
            ORDER BY bar DESC, foo ASC;".to_string()
        };
        assert_eq!(query.to_sql(), test_sql_string);
    }

    #[bench]
    fn bench_query_with_extended_where(b: &mut Bencher) {
        let where_cl = Where {
            operator: Operator::And,
            clause: &["foo == bar", "lala == blah"]
        };

        let order_by_bar_desc = OrderBy {
            ordering: Ordering::Descending,
            order_by: "bar"
        };

        let order_by_foo_asc = OrderBy {
            ordering: Ordering::Ascending,
            order_by: "foo"
        };

        let bar_join = Join {
            join_type: JoinType::Inner,
            target: "bar_table",
            clause: "1 == 1"
        };

        let bazz_join = Join {
            join_type: JoinType::Left,
            target: "bazz_table",
            clause: "2 == 2"
        };

        let clauses = ["foo", "bar"];
        let query = Query {
            select: SelectType::Specific(&clauses),
            from: "test_table",
            joins: &[bar_join, bazz_join],
            group_by: &["foo", "bar"],
            order_by: &[order_by_bar_desc, order_by_foo_asc],
            where_cl: WhereType::Extended(&where_cl),
            having: WhereType::Empty,
        };

        b.iter(|| query.to_sql());
    }

    #[bench]
    fn bench_query_with_empty_where(b: &mut Bencher) {
        let order_by_bar_desc = OrderBy {
            ordering: Ordering::Descending,
            order_by: "bar"
        };

        let order_by_foo_asc = OrderBy {
            ordering: Ordering::Ascending,
            order_by: "foo"
        };

        let bar_join = Join {
            join_type: JoinType::Inner,
            target: "bar_table",
            clause: "1 == 1"
        };

        let bazz_join = Join {
            join_type: JoinType::Left,
            target: "bazz_table",
            clause: "2 == 2"
        };

        let clauses = ["foo", "bar"];
        let query = Query {
            select: SelectType::Specific(&clauses),
            from: "test_table",
            joins: &[bar_join, bazz_join],
            group_by: &["foo", "bar"],
            order_by: &[order_by_bar_desc, order_by_foo_asc],
            where_cl: WhereType::Empty,
            having: WhereType::Empty,
        };

        b.iter(|| query.to_sql());
    }
}
<commit_msg>Added Distinct support<commit_after>use select::SelectType;
use join::Join;
use order_by::OrderBy;
use where_cl::{ToSQL, WhereType};

pub struct Query<'a> {
    pub select: SelectType<'a>,
    pub distinct: bool,
    pub from: &'a str,
    pub joins: &'a [Join<'a>],
    pub group_by: &'a [&'a str],
    pub order_by: &'a [OrderBy<'a>],
    pub where_cl: WhereType<'a>,
    pub having: WhereType<'a>
}

impl<'a> Query<'a> {
    fn to_sql(&self) -> String {
        let mut rv = String::new();
        rv.push_str("SELECT");

        match self.distinct {
            true => {
                rv.push(' ');
                rv.push_str("DISTINCT");
            },
            false => {}
        }

        rv.push(' ');
        rv.push_str(&self.select.to_sql());
        rv.push(' ');
        rv.push_str("FROM");
        rv.push(' ');
        rv.push_str(self.from);

        for join in self.joins {
            rv.push(' ');
            rv.push_str(&join.to_sql());
        }

        let where_string = " WHERE ";
        match self.where_cl {
            WhereType::Empty => {},
            WhereType::Simple(clause) => {
                rv.push_str(where_string);
                rv.push_str(&clause.to_sql());
            },
            WhereType::Extended(clause) => {
                rv.push_str(where_string);
                rv.push_str(&clause.to_sql());
            }
        }

        if !self.group_by.is_empty() {
            rv.push(' ');
            rv.push_str("GROUP BY");
            rv.push(' ');
            rv.push_str(&self.group_by.join(", "));
        }

        let having_string = " HAVING ";
        match self.having {
            WhereType::Empty => {},
            WhereType::Simple(clause) => {
                rv.push_str(having_string);
                rv.push_str(&clause.to_sql());
            },
            WhereType::Extended(clause) => {
                rv.push_str(having_string);
                rv.push_str(&clause.to_sql());
            }
        }

        if !self.order_by.is_empty() {
            rv.push(' ');
            rv.push_str("ORDER BY");
            rv.push(' ');
            rv.push_str(&self.order_by
                        .into_iter()
                        .map(|x| x.to_sql())
                        .collect::<Vec<String>>()
                        .join(", "));
        }

        rv.push(';');
        rv
    }
}

#[cfg(test)]
mod tests {
    extern crate test;
    use self::test::Bencher;

    use super::Query;
    use select::SelectType;
    use join::{JoinType, Join};
    use order_by::{Ordering, OrderBy};
    use where_cl::{Operator, ToSQL, WhereType, Where};

    #[test]
    fn select_all() {
        let query = Query {
            select: SelectType::All,
            distinct: false,
            from: "test_table",
            joins: &[],
            group_by: &[],
            order_by: &[],
            where_cl: WhereType::Empty,
            having: WhereType::Empty,
        };
        assert_eq!(query.to_sql(), "SELECT * FROM test_table;".to_string());
    }

    #[test]
    fn select_foo_and_bar() {
        let clauses = &["foo", "bar"];
        let query = Query {
            select: SelectType::Specific(clauses),
            distinct: false,
            from: "test_table",
            joins: &[],
            group_by: &[],
            order_by: &[],
            where_cl: WhereType::Empty,
            having: WhereType::Empty,
        };
        assert_eq!(query.to_sql(), "SELECT foo, bar FROM test_table;".to_string());
    }

    #[test]
    fn select_foo_and_bar_with_vec_params() {
        let clauses = vec!["foo", "bar"];
        let query = Query {
            select: SelectType::Specific(&clauses),
            distinct: false,
            from: "test_table",
            joins: &[],
            group_by: &[],
            order_by: &[],
            where_cl: WhereType::Empty,
            having: WhereType::Empty,
        };
        assert_eq!(query.to_sql(), "SELECT foo, bar FROM test_table;".to_string());
    }

    #[test]
    fn select_foo_and_bar_with_vec_params_and_strings() {
        let clauses = vec!["foo", "bar"];
        let query = Query {
            select: SelectType::Specific(&clauses),
            distinct: false,
            from: "test_table",
            joins: &[],
            group_by: &[],
            order_by: &[],
            where_cl: WhereType::Empty,
            having: WhereType::Empty,
        };
        assert_eq!(query.to_sql(), "SELECT foo, bar FROM test_table;".to_string());
    }

    #[test]
    fn select_foo_and_join_bar() {
        let join = Join {
            join_type: JoinType::Inner,
            target: "target_table",
            clause: "2 == 2"
        };

        let query = Query {
            select: SelectType::All,
            distinct: false,
            from: "test_table",
            joins: &[join],
            group_by: &[],
            order_by: &[],
            where_cl: WhereType::Empty,
            having: WhereType::Empty,
        };

        let test_sql_string = {
            "SELECT * \
            FROM test_table \
            INNER JOIN target_table ON 2 == 2;".to_string()
        };
        assert_eq!(query.to_sql(), test_sql_string);
    }

    #[test]
    fn select_foo_and_join_bar_and_bazz() {
        let bar_join = Join {
            join_type: JoinType::Inner,
            target: "bar_table",
            clause: "1 == 1"
        };

        let bazz_join = Join {
            join_type: JoinType::Left,
            target: "bazz_table",
            clause: "2 == 2"
        };

        let query = Query {
            select: SelectType::All,
            distinct: false,
            from: "test_table",
            joins: &[bar_join, bazz_join],
            group_by: &[],
            order_by: &[],
            where_cl: WhereType::Empty,
            having: WhereType::Empty,
        };

        let test_sql_string = {
            "SELECT * \
            FROM test_table \
            INNER JOIN bar_table ON 1 == 1 \
            LEFT JOIN bazz_table ON 2 == 2;".to_string()
        };
        assert_eq!(query.to_sql(), test_sql_string);
    }

    #[test]
    fn select_all_and_group_by_foo() {
        let query = Query {
            select: SelectType::All,
            distinct: false,
            from: "test_table",
            joins: &[],
            group_by: &["foo"],
            order_by: &[],
            where_cl: WhereType::Empty,
            having: WhereType::Empty,
        };

        let test_sql_string = {
            "SELECT * \
            FROM test_table \
            GROUP BY foo;".to_string()
        };
        assert_eq!(query.to_sql(), test_sql_string);
    }

    #[test]
    fn select_all_and_group_by_foo_and_bar() {
        let query = Query {
            select: SelectType::All,
            distinct: false,
            from: "test_table",
            joins: &[],
            group_by: &["foo", "bar"],
            order_by: &[],
            where_cl: WhereType::Empty,
            having: WhereType::Empty,
        };

        let test_sql_string = {
            "SELECT * \
            FROM test_table \
            GROUP BY foo, bar;".to_string()
        };
        assert_eq!(query.to_sql(), test_sql_string);
    }

    #[test]
    fn select_all_and_order_by() {
        let order_by_foo_asc = OrderBy {
            ordering: Ordering::Ascending,
            order_by: "foo"
        };

        let query = Query {
            select: SelectType::All,
            distinct: false,
            from: "test_table",
            joins: &[],
            group_by: &[],
            order_by: &[order_by_foo_asc],
            where_cl: WhereType::Empty,
            having: WhereType::Empty,
        };

        let test_sql_string = {
            "SELECT * \
            FROM test_table \
            ORDER BY foo ASC;".to_string()
        };
        assert_eq!(query.to_sql(), test_sql_string);
    }

    #[test]
    fn select_all_and_multi_order_by() {
        let order_by_foo_asc = OrderBy {
            ordering: Ordering::Ascending,
            order_by: "foo"
        };

        let order_by_bar_desc = OrderBy {
            ordering: Ordering::Descending,
            order_by: "bar"
        };

        let query = Query {
            select: SelectType::All,
            distinct: false,
            from: "test_table",
            joins: &[],
            group_by: &[],
            order_by: &[order_by_foo_asc, order_by_bar_desc],
            where_cl: WhereType::Empty,
            having: WhereType::Empty,
        };

        let test_sql_string = {
            "SELECT * \
            FROM test_table \
            ORDER BY foo ASC, bar DESC;".to_string()
        };
        assert_eq!(query.to_sql(), test_sql_string);
    }

    #[test]
    fn select_all_where_simple() {
        let query = Query {
            select: SelectType::All,
            distinct: false,
            from: "test_table",
            joins: &[],
            group_by: &[],
            order_by: &[],
            where_cl: WhereType::Simple("foo == bar"),
            having: WhereType::Empty,
        };

        let test_sql_string = {
            "SELECT * \
            FROM test_table \
            WHERE foo == bar;".to_string()
        };
        assert_eq!(query.to_sql(), test_sql_string);
    }

    #[test]
    fn select_all_where_extended() {
        let where_cl = Where {
            operator: Operator::And,
            clause: &["foo == bar", "lala == blah"]
        };

        let query = Query {
            select: SelectType::All,
            distinct: false,
            from: "test_table",
            joins: &[],
            group_by: &[],
            order_by: &[],
            where_cl: WhereType::Extended(&where_cl),
            having: WhereType::Empty,
        };

        let test_sql_string = {
            "SELECT * \
            FROM test_table \
            WHERE (foo == bar AND lala == blah);".to_string()
        };
        assert_eq!(query.to_sql(), test_sql_string);
    }

    #[test]
    fn select_all_with_having() {
        let where_cl = Where {
            operator: Operator::And,
            clause: &["foo == bar", "lala == blah"]
        };

        let query = Query {
            select: SelectType::All,
            distinct: false,
            from: "test_table",
            joins: &[],
            group_by: &[],
            order_by: &[],
            where_cl: WhereType::Empty,
            having: WhereType::Simple("foo == bar")
        };

        let test_sql_string = {
            "SELECT * \
            FROM test_table \
            HAVING foo == bar;".to_string()
        };
        assert_eq!(query.to_sql(), test_sql_string);
    }

    #[test]
    fn select_all_with_extended_having() {
        let where_cl = Where {
            operator: Operator::And,
            clause: &["foo == bar", "lala == blah"]
        };

        let query = Query {
            select: SelectType::All,
            distinct: false,
            from: "test_table",
            joins: &[],
            group_by: &[],
            order_by: &[],
            where_cl: WhereType::Empty,
            having: WhereType::Extended(&where_cl),
        };

        let test_sql_string = {
            "SELECT * \
            FROM test_table \
            HAVING (foo == bar AND lala == blah);".to_string()
        };
        assert_eq!(query.to_sql(), test_sql_string);
    }

    #[test]
    fn select_all_distinct() {
        let query = Query {
            select: SelectType::All,
            distinct: true,
            from: "test_table",
            joins: &[],
            group_by: &[],
            order_by: &[],
            where_cl: WhereType::Empty,
            having: WhereType::Empty
        };

        let test_sql_string = {
            "SELECT DISTINCT * \
            FROM test_table;".to_string()
        };
        assert_eq!(query.to_sql(), test_sql_string);

    }

    #[test]
    fn test_complex() {
        let where_cl = Where {
            operator: Operator::And,
            clause: &["foo == bar", "lala == blah"]
        };

        let order_by_bar_desc = OrderBy {
            ordering: Ordering::Descending,
            order_by: "bar"
        };

        let order_by_foo_asc = OrderBy {
            ordering: Ordering::Ascending,
            order_by: "foo"
        };

        let bar_join = Join {
            join_type: JoinType::Inner,
            target: "bar_table",
            clause: "1 == 1"
        };

        let bazz_join = Join {
            join_type: JoinType::Left,
            target: "bazz_table",
            clause: "2 == 2"
        };

        let clauses = ["foo", "bar"];
        let query = Query {
            select: SelectType::Specific(&clauses),
            distinct: true,
            from: "test_table",
            joins: &[bar_join, bazz_join],
            group_by: &["foo", "bar"],
            order_by: &[order_by_bar_desc, order_by_foo_asc],
            where_cl: WhereType::Extended(&where_cl),
            having: WhereType::Extended(&where_cl)
        };

        let test_sql_string = {
            "SELECT DISTINCT foo, bar \
            FROM test_table \
            INNER JOIN bar_table ON 1 == 1 \
            LEFT JOIN bazz_table ON 2 == 2 \
            WHERE (foo == bar AND lala == blah) \
            GROUP BY foo, bar \
            HAVING (foo == bar AND lala == blah) \
            ORDER BY bar DESC, foo ASC;".to_string()
        };
        assert_eq!(query.to_sql(), test_sql_string);
    }

    #[bench]
    fn bench_query_with_extended_where(b: &mut Bencher) {
        let where_cl = Where {
            operator: Operator::And,
            clause: &["foo == bar", "lala == blah"]
        };

        let order_by_bar_desc = OrderBy {
            ordering: Ordering::Descending,
            order_by: "bar"
        };

        let order_by_foo_asc = OrderBy {
            ordering: Ordering::Ascending,
            order_by: "foo"
        };

        let bar_join = Join {
            join_type: JoinType::Inner,
            target: "bar_table",
            clause: "1 == 1"
        };

        let bazz_join = Join {
            join_type: JoinType::Left,
            target: "bazz_table",
            clause: "2 == 2"
        };

        let clauses = ["foo", "bar"];
        let query = Query {
            select: SelectType::Specific(&clauses),
            distinct: false,
            from: "test_table",
            joins: &[bar_join, bazz_join],
            group_by: &["foo", "bar"],
            order_by: &[order_by_bar_desc, order_by_foo_asc],
            where_cl: WhereType::Extended(&where_cl),
            having: WhereType::Empty,
        };

        b.iter(|| query.to_sql());
    }

    #[bench]
    fn bench_query_with_empty_where(b: &mut Bencher) {
        let order_by_bar_desc = OrderBy {
            ordering: Ordering::Descending,
            order_by: "bar"
        };

        let order_by_foo_asc = OrderBy {
            ordering: Ordering::Ascending,
            order_by: "foo"
        };

        let bar_join = Join {
            join_type: JoinType::Inner,
            target: "bar_table",
            clause: "1 == 1"
        };

        let bazz_join = Join {
            join_type: JoinType::Left,
            target: "bazz_table",
            clause: "2 == 2"
        };

        let clauses = ["foo", "bar"];
        let query = Query {
            select: SelectType::Specific(&clauses),
            distinct: false,
            from: "test_table",
            joins: &[bar_join, bazz_join],
            group_by: &["foo", "bar"],
            order_by: &[order_by_bar_desc, order_by_foo_asc],
            where_cl: WhereType::Empty,
            having: WhereType::Empty,
        };

        b.iter(|| query.to_sql());
    }
}
<|endoftext|>


<commit_before>use errors::*;
use objective::*;
use fnv::FnvHashSet;
use rand::{Rng, thread_rng, sample};
use rand::distributions::{IndependentSample, Range};
use slog::{Drain, Logger};
use slog_stdlog;
use rayon::prelude::*;
use std::f64;
use std::iter::empty;

#[derive(Copy, Clone, PartialEq, Eq, Ord, PartialOrd)]
pub enum SampleElement<Element> {
    Independent(Element),
    Dependent(Element),
}

/// Compute the total primal curvature between two sets `S` and `T`. The input `ST` should be `S ∪ T`.
///
/// # Errors
/// If $f_{el}(S) = 0$ but $f_{el}(S \cup T) \neq 0$ then `Err(DivideByZero)` is returned.
#[allow(non_snake_case)]
pub fn gamma<O: Objective>(obj: &O,
                           el: O::Element,
                           ST: &FnvHashSet<O::Element>,
                           S: &FnvHashSet<O::Element>,
                           state: &O::State,
                           joint_state: &O::State)
                           -> Result<f64> {
    let lower = obj.delta(el, S, state)?;
    let upper = obj.delta(el, ST, joint_state)?;

    if lower == upper {
        Ok(1.0)
    } else if lower == 0.0 {
        Err(ErrorKind::DivideByZero(upper, lower).into())
    } else {
        Ok(upper / lower)
    }
}

/// Compute `Λ(T, S)`. The ordering of elements `σ` is given by the ordering of `T`.
///
/// # Errors
/// If `T` contains duplicate elements, `Err(DuplicateElements)` is returned.
/// If any `Γ` returns `Err(DivideByZero)`, that is propagated.
#[allow(non_snake_case)]
pub fn lambda<O: Objective>(obj: &O,
                            T: &Vec<SampleElement<O::Element>>,
                            S: &FnvHashSet<O::Element>,
                            state: &O::State)
                            -> Result<f64> {
    let mut Ti = S.clone();
    let mut tstate = state.clone();
    let mut sum = 0.0;
    for t in T {
        if let &SampleElement::Dependent(t) = t {
            if S.contains(&t) {
                continue;
            }
            obj.insert_mut(t, &mut tstate)?;
            sum += gamma(obj, t, &Ti, S, state, &tstate)?;
            Ti.insert(t);
        } else {
            sum += 1.0; // gamma is 1 for independent elements by definition
        }
    }
    Ok(sum)
}

/// Construct a sample `T ~ D_{p,k}` where `p = bias` and `k` is given.
///
/// For worst case, bias should be 1.
#[allow(non_snake_case)]
pub fn biased_dependency_sample<O: Objective>(obj: &O,
                                              bias: f64,
                                              k: usize)
                                              -> Result<Vec<SampleElement<O::Element>>> {
    use ratio::SampleElement::*;
    let mut rng = thread_rng();
    let mut T = Vec::with_capacity(k);
    let uniform = Range::new(0.0, 1.0);

    fn choose<'a, T: 'a + Copy, I: IntoIterator<Item = &'a T>, R: Rng>(rng: &mut R,
                                                                       iter: I)
                                                                       -> Option<T> {
        let mut iter = iter.into_iter();
        if iter.size_hint().1.unwrap() == 0 {
            return None;
        }
        let range = Range::new(0, iter.size_hint().1.unwrap());
        iter.nth(range.ind_sample(rng)).cloned()
    }

    T.push(Dependent(sample(&mut rng, obj.elements(), 1)[0]));

    let deps = |t, supermodular| if let Dependent(t) = t {
        if supermodular {
            obj.supermodular_depends(t, &O::State::default())
        } else {
            obj.depends(t, &O::State::default())
        }
    } else {
        Ok(Box::new(empty::<O::Element>()) as ElementIterator<O>)
    };

    let element_count = obj.elements().count();
    let mut independent = obj.elements().collect::<FnvHashSet<_>>();
    for dep in deps(T[0], false)? {
        independent.remove(&dep);
    }
    let mut superdepends = deps(T[0], true)?.collect::<FnvHashSet<_>>();

    for i in 1..k {
        let x = if uniform.ind_sample(&mut rng) <= bias {
            // dependent
            match choose(&mut rng, &superdepends) {
                None => {
                    if superdepends.len() + T.len() == element_count {
                        // no more independent elements either
                        return Err(ErrorKind::InsufficientElements(k, i - 1).into());
                    } else {
                        Independent(choose(&mut rng, &independent).unwrap())
                    }
                }
                Some(x) => Dependent(x),
            }
        } else {
            // independent
            if independent.len() == 0 {
                // no more independent elements
                Dependent(choose(&mut rng, &superdepends).ok_or(Error::from(ErrorKind::InsufficientElements(k, i - 1)))?)
            } else {
                Independent(choose(&mut rng, &independent).unwrap())
            }
        };

        for dep in deps(x, false)? {
            independent.remove(&dep);
        }
        superdepends.extend(deps(x, true)?);

        if let Dependent(_) = x {
            assert!(!T.contains(&x));
        }
        T.push(x);

        for &t in &T {
            let t = match t {
                Dependent(t) => t,
                Independent(t) => t,
            };
            superdepends.remove(&t);
            independent.remove(&t);
        }
    }

    assert!(T.len() == k);
    Ok(T)
}

/// Find the value `x` such that `Pr[X ≤ x] ≥ δ` for random variable `X` with `n` i.i.d samples.
///
/// Uses a binary search on the `X_i` to locate a suitable value. The `gap` parameter controls how
/// precise the value found must be: once `max - min <= gap` the binary search will terminate.
///
/// TODO: this could be *much* more efficient than recomputing the cdf for every x tested.
fn empirical_cdf(samples: &Vec<f64>, delta: f64, gap: f64, log: Option<Logger>) -> Result<f64> {
    let log = log.unwrap_or_else(|| Logger::root(slog_stdlog::StdLog.fuse(), o!()));
    const ITERS_MAX: usize = 100;
    if delta > 1.0 - gap as f64 {
        warn!(log, "δ set to a higher precision than η, binary search may never converge"; "δ" => delta, "η" => gap);
    }
    let n = samples.len();
    let mut max =
        samples.iter().filter(|&f| !f.is_nan()).fold(f64::NEG_INFINITY,
                                                     |max, &cur| if cur > max { cur } else { max });
    let mut min = samples.iter()
        .filter(|&f| !f.is_nan())
        .fold(f64::INFINITY, |min, &cur| if cur < min { cur } else { min });
    assert!(max.is_finite());
    assert!(min.is_finite());

    let cdf = |x| samples.iter().filter(|&xi| *xi <= x).count() as f64 / n as f64;
    let mut iters = 0;

    loop {
        let x = (max + min) / 2.0;
        assert!(x.is_finite());
        debug!(log, "testing"; "x" => x, "gap" => max - min, "max" => max, "min" => min);

        let p = cdf(x);

        if p >= delta && max - min <= gap {
            info!(log, "found x with gap satisfied"; "x" => x, "gap" => max - min, "p" => p);
            return Ok(x);
        } else if max - min <= gap {
            // gotten small enough
            let p = cdf(max);
            assert!(p >= delta);
            info!(log, "gap satisfied, but x not found. using max"; "x" => max, "gap" => max - min, "p" => p);
            return Ok(max);
        }

        if p >= delta {
            debug!(log, "p >= δ"; "p" => p);
            // decrease max
            max = x;
        } else {
            debug!(log, "p < δ"; "p" => p);
            // increase min
            min = x;
        }
        iters += 1;
        if iters > ITERS_MAX {
            crit!(log, "binary search failed to converge (likely due to loss of precision from an abnormal number of ζ events)"; "min" => min, "max" => max, "gap" => max - min, "η" => gap);
            return Err(ErrorKind::NoConvergence(min, max, iters).into());
        }
    }
}

pub fn sample_lambda<O: Objective + Sync>(obj: &O,
                                          sol: &FnvHashSet<O::Element>,
                                          state: O::State,
                                          bias: f64,
                                          k: usize,
                                          num_samples: usize)
                                          -> Result<Vec<f64>>
    where O::Element: Send + Sync,
          O::State: Sync
{
    let mut sample_vec = Vec::with_capacity(num_samples);

    (0..num_samples)
        .into_par_iter()
        .map(|_| {
            let sample = biased_dependency_sample(obj, bias, k)?;
            lambda(obj, &sample, sol, &state)
        })
        .collect_into(&mut sample_vec);

    sample_vec.into_iter().collect::<Result<Vec<_>>>()
}

pub fn estimate_lambda<O: Objective + Sync>(obj: &O,
                                            sol: &FnvHashSet<O::Element>,
                                            state: O::State,
                                            bias: f64,
                                            k: usize,
                                            eps: f64,
                                            delta: f64,
                                            delta2: f64,
                                            eta: f64,
                                            log: Option<Logger>)
                                            -> Result<f64>
    where O::Element: Send + Sync,
          O::State: Sync
{
    let log = log.unwrap_or_else(|| Logger::root(slog_stdlog::StdLog.fuse(), o!()));

    let num_samples = (1.0 - delta2).ln() / (-2.0 * eps.powi(2));
    // assert!(eps >= (1.0 / (2.0 * num_samples) * 2f64.ln()).sqrt()); // sufficiency condition for the bound.
    assert!((-2.0 * eps.powi(2) * num_samples).exp() <= 0.5);

    let num_samples = num_samples.ceil() as usize;
    info!(log, "constructing samples"; "num_samples" => num_samples);

    let samples = sample_lambda(obj, sol, state, bias, k, num_samples)?;
    assert!(samples.len() == num_samples);
    let num_nans: usize = samples.iter().filter(|&f| f.is_nan() || f.is_infinite()).count();

    if num_nans > 0 {
        crit!(log, "samples contain NaN / infinite values"; "count" => num_nans);
        panic!("samples contain NaN / infinite values");
    }

    let mean = samples.iter().sum::<f64>() / samples.len() as f64;
    let variance = samples.iter().map(|xi| (xi - mean).powi(2)).sum::<f64>() /
                   (samples.len() as f64 - 1.0);
    let min = samples.iter().fold(f64::INFINITY, |min, xi| xi.min(min));
    let max = samples.iter().fold(0.0, |max, xi| xi.max(max));

    info!(log, "done sampling"; "mean" => mean, "variance" => variance, "min" => min, "max" => max);

    info!(log, "estimating cdf"; "η" => eta);
    match empirical_cdf(&samples, delta, eta, Some(log.new(o!("section" => "cdf")))) {
        Ok(f) => Ok(f),
        Err(Error(ErrorKind::NoConvergence(_, max, _), _)) => Ok(max),
        err => err,
    }
}

pub fn estimate_lambda_chebyshev<O: Objective + Sync>(obj: &O,
                                                      sol: &FnvHashSet<O::Element>,
                                                      state: O::State,
                                                      bias: f64,
                                                      k: usize,
                                                      delta: f64,
                                                      num_samples: usize,
                                                      log: Option<Logger>)
                                                      -> Result<f64>
    where O::Element: Send + Sync,
          O::State: Sync
{
    let log = log.unwrap_or_else(|| Logger::root(slog_stdlog::StdLog.fuse(), o!()));

    info!(log, "constructing samples"; "num_samples" => num_samples);

    let samples = sample_lambda(obj, sol, state, bias, k, num_samples)?;
    assert!(samples.len() == num_samples);
    info!(log, #"no_term", "sampled Λ"; "samples" => format!("{:?}", samples));
    let num_nans: usize = samples.iter().filter(|&f| f.is_nan() || f.is_infinite()).count();

    if num_nans > 0 {
        crit!(log, "samples contain NaN / infinite values"; "count" => num_nans);
        panic!("samples contain NaN / infinite values");
    }

    let mean = samples.iter().sum::<f64>() / samples.len() as f64;
    let variance = samples.iter().map(|xi| (xi - mean).powi(2)).sum::<f64>() /
                   (samples.len() as f64 - 1.0);
    let stddev = variance.sqrt();
    let min = samples.iter().fold(f64::INFINITY, |min, xi| xi.min(min));
    let max = samples.iter().fold(0.0, |max, xi| xi.max(max));

    info!(log, "done sampling"; "mean" => mean, "variance" => variance, "std. dev." => stddev, "min" => min, "max" => max);
    let factor = (1.0 / (1.0 - delta)).sqrt(); // d = 1 - 1/k^2 -> 1/k^2 = 1 - d -> k^2 = 1/(1 - d) -> sqrt(1/(1 - d))
    Ok(mean + factor * stddev)
}

pub fn estimate_lambda_saw<O: Objective + Sync>(obj: &O,
                                                sol: &FnvHashSet<O::Element>,
                                                state: O::State,
                                                bias: f64,
                                                k: usize,
                                                delta: f64,
                                                eta: f64,
                                                num_samples: usize,
                                                log: Option<Logger>)
                                                -> Result<f64>
    where O::Element: Send + Sync,
          O::State: Sync
{
    let log = log.unwrap_or_else(|| Logger::root(slog_stdlog::StdLog.fuse(), o!()));

    info!(log, "constructing samples"; "num_samples" => num_samples);

    let samples = sample_lambda(obj, sol, state, bias, k, num_samples)?;
    assert!(samples.len() == num_samples);
    info!(log, #"no_term", "sampled Λ"; "samples" => format!("{:?}", samples));
    let num_nans: usize = samples.iter().filter(|&f| f.is_nan() || f.is_infinite()).count();

    if num_nans > 0 {
        crit!(log, "samples contain NaN / infinite values"; "count" => num_nans);
        panic!("samples contain NaN / infinite values");
    }

    let mean = samples.iter().sum::<f64>() / samples.len() as f64;
    let variance = samples.iter().map(|xi| (xi - mean).powi(2)).sum::<f64>() /
                   (samples.len() as f64 - 1.0);
    let stddev = variance.sqrt();
    let min = samples.iter().fold(f64::INFINITY, |min, xi| xi.min(min));
    let max = samples.iter().fold(0.0, |max, xi| xi.max(max));

    info!(log, "done sampling"; "mean" => mean, "variance" => variance, "std. dev." => stddev, "min" => min, "max" => max);
    let r = num_samples as f64;
    let factor = binary_search(|lambda| {
                                   1.0 -
                                   1.0 / (r + 1.0) *
                                   ((r + 1.0) / r * ((r - 1.0) / lambda.powi(2) + 1.0)).floor()
                               },
                               (1.0, 100.0),
                               delta,
                               eta,
                               Some(log));
    let factor = match factor {
        Ok(f) => Ok(f),
        Err(Error(ErrorKind::NoConvergence(_, max, _), _)) => Ok(max),
        err => err,
    }?;
    Ok(mean + factor * ((num_samples as f64 + 1.0) / num_samples as f64 * variance).sqrt())
}

/// Does binary search to find the smallest `x` such that f(x) >= goal.
///
/// Terminates when the bounds of the binary search (initially the `(min, max)` given) satisfy `max
/// - min ≤ η`.
fn binary_search<F>(f: F,
                    (mut min, mut max): (f64, f64),
                    goal: f64,
                    gap: f64,
                    log: Option<Logger>)
                    -> Result<f64>
    where F: Fn(f64) -> f64
{
    let log = log.unwrap_or_else(|| Logger::root(slog_stdlog::StdLog.fuse(), o!()));
    const ITERS_MAX: usize = 100;
    assert!(max.is_finite());
    assert!(min.is_finite());

    let mut iters = 0;
    loop {
        let x = (max + min) / 2.0;
        assert!(x.is_finite());
        debug!(log, "testing"; "x" => x, "gap" => max - min, "max" => max, "min" => min);

        let p = f(x);

        if p >= goal && max - min <= gap {
            info!(log, "found x with gap satisfied"; "x" => x, "gap" => max - min, "p" => p);
            return Ok(x);
        } else if max - min <= gap {
            // gotten small enough
            let p = f(max);
            if p < goal {
                crit!(log, "max does not satisfy f(max) >= goal"; "max" => max, "f(max)" => p, "goal" => goal);
                assert!(p >= goal);
            }
            info!(log, "gap satisfied, but x not found. using max"; "x" => max, "gap" => max - min, "p" => p);
            return Ok(max);
        }

        if p >= goal {
            debug!(log, "p >= δ"; "p" => p);
            // decrease max
            max = x;
        } else {
            debug!(log, "p < δ"; "p" => p);
            // increase min
            min = x;
        }
        iters += 1;
        if iters > ITERS_MAX {
            crit!(log, "binary search failed to converge (likely due to loss of precision from an abnormal number of ζ events)"; "min" => min, "max" => max, "gap" => max - min, "η" => gap);
            return Err(ErrorKind::NoConvergence(min, max, iters).into());
        }
    }
}
<commit_msg>Removed ratio code -- no guarantees on this so don't trust it.<commit_after><|endoftext|>


<commit_before>#![crate_id = "rustyredis#0.1"]
#![desc = "A Rust client library for Redis based off hiredis"]
#![crate_type = "lib"]

use std::io::net::tcp::TcpStream;
use std::io::BufReader;
use std::int::parse_bytes;

enum Global {
  RedisReaderMaxBuf = 1024 * 16
}

enum RedisReplyType {
  RedisReplyString,
  RedisReplyInteger,
  RedisReplyArray,
  RedisReplyNil,
  RedisReplyStatus,
  RedisReplyError
}

#[deriving(Clone)]
pub enum RedisObject {
  RedisString(String),
  RedisInteger(int),
  RedisArray(Vec<RedisObject>),
  RedisNil,
  RedisStatus(String),
  RedisError(String)
}

enum RedisError {
  RedisNoError,
  RedisIoError,
  RedisProcessError
}

struct RedisReadTask {
  kind: RedisReplyType,
  elm: int,
  idx: int,
  obj: RedisObject
}

struct RedisReader {
  err: RedisError,
  errstr: String,
  buf: String,
  reply: RedisObject,
  pos: uint,
  len: uint,
  rstack: [RedisReadTask, ..9],
  ridx: int
}

pub struct RedisContext {
  err: RedisError,
  errstr: String,
  tcpstream: TcpStream,
  flags: int,
  obuf: String,
  reader: RedisReader
}

impl RedisReadTask {
  fn new() -> RedisReadTask {
    RedisReadTask {
      kind: RedisReplyNil,
      elm: -1,
      idx: -1,
      obj: RedisNil,
    }
  }
}

impl RedisReader {
  fn create_integer(&self, s: &str) -> Result<RedisObject, &str> {
    match parse_bytes(s.as_bytes(), 10) {
      Some(n) => Ok(RedisInteger(n)),
      None => Err("Invalid integer for create_integer")
    }
  }

  fn process_line_item(&mut self) -> Result<(), &str> {
    let mut reader = BufReader::new(self.buf.as_bytes());

    match reader.read_line() {
      Ok(s) => {
        let mut ns = s.clone();
        ns.shift_char(); // this is dirty and quick
        let nns = ns.as_slice().trim();

        match self.rstack[self.ridx as uint].kind {
          RedisReplyStatus => self.reply = RedisStatus(nns.to_str()),
          RedisReplyError => self.reply = RedisError(nns.to_str()),
          RedisReplyInteger => match self.create_integer(nns) {
            Ok(n) => self.reply = n,
            Err(e) => return Err(e)
          },
          _ => return Err("Invalid type for process_line_item")
        };
        Ok(())
      },
      Err(e) => Err(e.desc)
    }
  }

  fn process_bulk_item(&mut self) -> Result<(), &str> {
    unimplemented!();
  }

  fn process_multi_bulk_item(&mut self) -> Result<(), &str> {
    unimplemented!();
  }

  fn process_item(&mut self) -> Result<(), &str> {
    match self.rstack[self.ridx as uint].kind {
      RedisReplyError => self.process_line_item(),
      RedisReplyStatus => self.process_line_item(),
      RedisReplyInteger => self.process_line_item(),
      RedisReplyString => self.process_bulk_item(),
      RedisReplyArray => self.process_multi_bulk_item(),
      _ => Err("This should never happen")
    }
  }

  fn check_reply_type(&mut self) -> Result<(), &str> {
    let mut kind: RedisReplyType;
    let slice = self.buf.as_slice();
    match slice.char_at(0) {
      '-' => kind = RedisReplyError,
      '+' => kind = RedisReplyStatus,
      ':' => kind = RedisReplyInteger,
      '$' => kind = RedisReplyString,
      '*' => kind = RedisReplyArray,
      _ =>  return Err("Failed reading control byte")
    }

    self.rstack[self.ridx as uint].kind = kind;
    Ok(())
  }

  fn process_reply(&mut self) -> Result<Option<RedisObject>, &str> {
    if self.len == 0 {
      return Ok(None)
    }

    if self.ridx == -1 {
      self.ridx = 0;
    }

    while self.ridx >= 0 {
      match self.check_reply_type(){
        Err(s) => { self.set_error(RedisProcessError, s); return Err(s) },
        _ => match self.process_item() {
               Err(s) => { self.set_error(RedisProcessError, s); return Err(s) },
               _ => self.ridx = self.ridx - 1
             }
      }
    }

    Ok(Some(self.reply.clone()))
  }

  fn feed(&mut self, buf: &mut [u8], len: uint){
    let mut i = 0;
    while i < len {
      let c = buf[i].to_ascii();
      self.buf.push_char(c.to_char());
      i = i + 1;
    }
    self.len = len;
  }

  fn set_error(&mut self, err: RedisError, errstr: &str){
    self.err = err;
    self.errstr = errstr.to_str();
  }

  fn new() -> RedisReader {
    RedisReader {
      err: RedisNoError,
      errstr: String::new(),
      buf: String::new(),
      reply: RedisNil,
      pos: 0,
      len: 0,
      rstack: [RedisReadTask::new(),
               RedisReadTask::new(),
               RedisReadTask::new(),
               RedisReadTask::new(),
               RedisReadTask::new(),
               RedisReadTask::new(),
               RedisReadTask::new(),
               RedisReadTask::new(),
               RedisReadTask::new()],
      ridx: -1
    }
  }
}

impl RedisContext {
  fn format_command(&self, format: &str) -> String {
    let mut argc: uint = 0;
    let mut c = String::new();
    let mut finalc = String::new();

    for arg in format.split(' '){
      let arglen = arg.len();
      c = c.append("$");
      c = c.append(arglen.to_str().as_slice());
      c = c.append("\r\n");
      c = c.append(arg);
      c = c.append("\r\n");
      argc = argc + 1;
    }
    finalc = finalc.append("*");
    finalc = finalc.append(argc.to_str().as_slice());
    finalc = finalc.append("\r\n");
    finalc = finalc.append(c.as_slice());
    finalc
  }

  fn set_error(&mut self, err: RedisError, errstr: &str){
    self.err = err;
    self.errstr = errstr.to_str();
  }

  fn get_reply_reader(&mut self) -> Result<Option<RedisObject>, &str> {
    self.reader.process_reply()
  }

  fn buffer_write(&mut self) -> int {
    match self.tcpstream.write_str(self.obuf.as_slice()){
      Err(e) => { self.set_error(RedisIoError, e.desc ); 1 }
      _ => 0
    }
  }

  fn buffer_read(&mut self) -> int {
    let mut buf = [0, ..RedisReaderMaxBuf as uint];
    let res = self.tcpstream.read(buf);
    match res {
      Ok(s) => { self.reader.feed(buf, s); 0 },
      Err(e) => { self.set_error(RedisIoError, e.desc); 1 }
    }

  }

  fn block_for_reply(&mut self) -> Result<Option<RedisObject>, &str> {
    let o = try!(self.get_reply_reader());
    match o {
      Some(s) => Ok(Some(s)),
      None => {
        self.buffer_write();
        self.buffer_read();
        let o2 = try!(self.get_reply_reader());
        match o2{
          Some(s) => Ok(Some(s)),
          None => Err("No reply was found")
        }
      }
    }
  }

  pub fn connect(ip: &str, port: u16) -> RedisContext {
    RedisContext {
      err: RedisNoError,
      errstr: String::new(),
      tcpstream: match TcpStream::connect(ip, port) {
        Ok(s) => s,
        Err(e) => fail!(e.desc)
      },
      flags: 0,
      obuf: String::new(),
      reader: RedisReader::new()
    }
  }

  pub fn command(&mut self, command: &str) -> Result<Option<RedisObject>, &str> {
    self.obuf = self.format_command(command);
    self.block_for_reply()
  }
}

<commit_msg>Supports multiple commands, some tests added<commit_after>#![crate_id = "rustyredis#0.1"]
#![desc = "A Rust client library for Redis based off hiredis"]
#![crate_type = "lib"]

use std::io::net::tcp::TcpStream;
use std::io::BufReader;
use std::int::parse_bytes;

enum Global {
  RedisReaderMaxBuf = 1024 * 16
}

enum RedisReplyType {
  RedisReplyString,
  RedisReplyInteger,
  RedisReplyArray,
  RedisReplyNil,
  RedisReplyStatus,
  RedisReplyError
}

#[deriving(Clone)]
pub enum RedisObject {
  RedisString(String),
  RedisInteger(int),
  RedisArray(Vec<RedisObject>),
  RedisNil,
  RedisStatus(String),
  RedisError(String)
}

enum RedisError {
  RedisNoError,
  RedisIoError,
  RedisProcessError
}

struct RedisReadTask {
  kind: RedisReplyType,
  elm: int,
  idx: int,
  obj: RedisObject
}

struct RedisReader {
  err: RedisError,
  errstr: String,
  buf: String, //this probably needs to be changed somehow
  reply: RedisObject,
  pos: uint, //might be needed in future, with dealing with multiple replies
  len: uint,
  rstack: [RedisReadTask, ..9],
  ridx: int
}

pub struct RedisContext {
  err: RedisError,
  errstr: String,
  tcpstream: TcpStream,
  flags: int, // async flag?
  obuf: String,
  reader: RedisReader
}

impl RedisReadTask {
  fn new() -> RedisReadTask {
    RedisReadTask {
      kind: RedisReplyNil,
      elm: -1,
      idx: -1,
      obj: RedisNil,
    }
  }
}

impl RedisReader {
  fn create_integer(&self, s: &str) -> Result<RedisObject, &str> {
    match parse_bytes(s.as_bytes(), 10) {
      Some(n) => Ok(RedisInteger(n)),
      None => Err("Invalid integer for create_integer")
    }
  }

  fn process_line_item(&mut self) -> Result<(), &str> {
    let mut reader = BufReader::new(self.buf.as_bytes());

    match reader.read_line() {
      Ok(s) => {
        let mut ns = s.clone();
        ns.shift_char(); // this is dirty and quick...well slow O(n)
        let nns = ns.as_slice().trim();

        match self.rstack[self.ridx as uint].kind {
          RedisReplyStatus => self.reply = RedisStatus(nns.to_str()),
          RedisReplyError => self.reply = RedisError(nns.to_str()),
          RedisReplyInteger => match self.create_integer(nns) {
            Ok(n) => self.reply = n,
            Err(e) => return Err(e)
          },
          _ => return Err("Invalid type for process_line_item")
        };
        Ok(())
      },
      Err(e) => Err(e.desc)
    }
  }

  fn process_bulk_item(&mut self) -> Result<(), &str> {
    unimplemented!();
  }

  fn process_multi_bulk_item(&mut self) -> Result<(), &str> {
    unimplemented!();
  }

  fn process_item(&mut self) -> Result<(), &str> {
    match self.rstack[self.ridx as uint].kind {
      RedisReplyError => self.process_line_item(),
      RedisReplyStatus => self.process_line_item(),
      RedisReplyInteger => self.process_line_item(),
      RedisReplyString => self.process_bulk_item(),
      RedisReplyArray => self.process_multi_bulk_item(),
      _ => Err("This should never happen")
    }
  }

  fn check_reply_type(&mut self) -> Result<(), &str> {
    let mut kind: RedisReplyType;
    let slice = self.buf.as_slice();
    match slice.char_at(0) {
      '-' => kind = RedisReplyError,
      '+' => kind = RedisReplyStatus,
      ':' => kind = RedisReplyInteger,
      '$' => kind = RedisReplyString,
      '*' => kind = RedisReplyArray,
      _ =>  return Err("Failed reading control byte")
    }

    self.rstack[self.ridx as uint].kind = kind;
    Ok(())
  }

  fn process_reply(&mut self) -> Result<Option<RedisObject>, &str> {
    if self.len == 0 {
      return Ok(None)
    }

    if self.ridx == -1 {
      self.ridx = 0;
    }

    while self.ridx >= 0 {
      match self.check_reply_type(){
        Err(s) => { self.set_error(RedisProcessError, s); return Err(s) },
        _ => match self.process_item() {
               Err(s) => { self.set_error(RedisProcessError, s); return Err(s) },
               _ => self.ridx = self.ridx - 1
             }
      }
    }
    self.buf.truncate(0);
    self.len = 0;
    Ok(Some(self.reply.clone()))
  }

  fn feed(&mut self, buf: &mut [u8], len: uint){
    let mut i = 0;
    while i < len {
      let c = buf[i].to_ascii();
      self.buf.push_char(c.to_char());
      i = i + 1;
    }
    self.len = len;
  }

  fn set_error(&mut self, err: RedisError, errstr: &str){
    self.err = err;
    self.errstr = errstr.to_str();
  }

  fn new() -> RedisReader {
    RedisReader {
      err: RedisNoError,
      errstr: String::new(),
      buf: String::new(),
      reply: RedisNil,
      pos: 0,
      len: 0,
      rstack: [RedisReadTask::new(),
               RedisReadTask::new(),
               RedisReadTask::new(),
               RedisReadTask::new(),
               RedisReadTask::new(),
               RedisReadTask::new(),
               RedisReadTask::new(),
               RedisReadTask::new(),
               RedisReadTask::new()],
      ridx: -1
    }
  }
}

impl RedisContext {
  fn format_command(&self, format: &str) -> String {
    let mut argc: uint = 0;
    let mut c = String::new();
    let mut finalc = String::new();

    for arg in format.split(' '){
      let arglen = arg.len();
      c = c.append("$");
      c = c.append(arglen.to_str().as_slice());
      c = c.append("\r\n");
      c = c.append(arg);
      c = c.append("\r\n");
      argc = argc + 1;
    }
    finalc = finalc.append("*");
    finalc = finalc.append(argc.to_str().as_slice());
    finalc = finalc.append("\r\n");
    finalc = finalc.append(c.as_slice());
    finalc
  }

  fn set_error(&mut self, err: RedisError, errstr: &str){
    self.err = err;
    self.errstr = errstr.to_str();
  }

  fn check_error(&self) -> bool {
    match self.err {
      RedisNoError => true,
      _ => true
    }
  }

  fn get_reply_reader(&mut self) -> Result<Option<RedisObject>, &str> {
    self.reader.process_reply()
  }

  fn buffer_write(&mut self) {
    match self.tcpstream.write_str(self.obuf.as_slice()){
      Err(e) => { self.set_error(RedisIoError, e.desc ); }
      _ => ()
    }
  }

  fn buffer_read(&mut self) {
    let mut buf = [0, ..RedisReaderMaxBuf as uint];
    let res = self.tcpstream.read(buf);
    match res {
      Ok(s) => { self.reader.feed(buf, s); },
      Err(e) => { self.set_error(RedisIoError, e.desc); }
    }

  }

  fn block_for_reply(&mut self) -> Result<Option<RedisObject>, &str> {
    let o = try!(self.get_reply_reader());
    match o {
      Some(s) => Ok(Some(s)),
      None => {
        self.buffer_write();
        self.buffer_read();

        if self.check_error() {
          return Err("Error. Check error members.");
        }
        let o2 = try!(self.get_reply_reader());
        match o2{
          Some(s) => Ok(Some(s)),
          None => Err("No reply was found. Check error members.")
        }
      }
    }
  }

  pub fn connect(ip: &str, port: u16) -> RedisContext {
    RedisContext {
      err: RedisNoError,
      errstr: String::new(),
      tcpstream: match TcpStream::connect(ip, port) {
        Ok(s) => s,
        Err(e) => fail!(e.desc)
      },
      flags: 0,
      obuf: String::new(),
      reader: RedisReader::new()
    }
  }

  pub fn command(&mut self, command: &str) -> Result<Option<RedisObject>, &str> {
    self.obuf = self.format_command(command);
    self.block_for_reply()
  }
}

#[cfg(test)]
mod test {
  #[test]
  fn test_connect(){
    let c = ::RedisContext::connect("127.0.0.1", 6379);
  }

  #[test]
  fn test_command(){
    let mut c = ::RedisContext::connect("127.0.0.1", 6379);
    let r = c.command("PING");

    match r {
      Ok(s) => match s {
        Some(s) => match s {
          ::RedisStatus(s) => assert!(s.eq(&"PONG".to_str())),
          _ => fail!("Didn't return correct reply type.")
        },
        None => fail!("Didn't return anything.")
      },
      Err(e) => fail!("{}", e)
    }
  }
}

<|endoftext|>


<commit_before>use crate::FileTime;
use std::fs::{self, File};
use std::io;
use std::os::unix::prelude::*;
use std::path::Path;

pub fn set_file_times(p: &Path, atime: FileTime, mtime: FileTime) -> io::Result<()> {
    let fd = syscall::open(p.as_os_str().as_bytes(), 0)
        .map_err(|err| io::Error::from_raw_os_error(err.errno))?;
    set_file_times_redox(fd, atime, mtime)
}

pub fn set_file_mtime(_p: &Path, _mtime: FileTime) -> io::Result<()> {
    unimplemented!()
}

pub fn set_file_atime(_p: &Path, _atime: FileTime) -> io::Result<()> {
    unimplemented!()
}

pub fn set_file_handle_times(
    _f: &File,
    _atime: Option<FileTime>,
    _mtime: Option<FileTime>,
) -> io::Result<()> {
    unimplemented!()
}

pub fn set_symlink_file_times(p: &Path, atime: FileTime, mtime: FileTime) -> io::Result<()> {
    let fd = syscall::open(p.as_os_str().as_bytes(), syscall::O_NOFOLLOW)
        .map_err(|err| io::Error::from_raw_os_error(err.errno))?;
    set_file_times_redox(fd, atime, mtime)
}

fn set_file_times_redox(fd: usize, atime: FileTime, mtime: FileTime) -> io::Result<()> {
    use syscall::TimeSpec;

    fn to_timespec(ft: &FileTime) -> TimeSpec {
        TimeSpec {
            tv_sec: ft.seconds(),
            tv_nsec: ft.nanoseconds() as i32,
        }
    }

    let times = [to_timespec(&atime), to_timespec(&mtime)];
    let res = syscall::futimens(fd, &times);
    let _ = syscall::close(fd);
    match res {
        Ok(_) => Ok(()),
        Err(err) => Err(io::Error::from_raw_os_error(err.errno)),
    }
}

pub fn from_last_modification_time(meta: &fs::Metadata) -> FileTime {
    FileTime {
        seconds: meta.mtime(),
        nanos: meta.mtime_nsec() as u32,
    }
}

pub fn from_last_access_time(meta: &fs::Metadata) -> FileTime {
    FileTime {
        seconds: meta.atime(),
        nanos: meta.atime_nsec() as u32,
    }
}

pub fn from_creation_time(_meta: &fs::Metadata) -> Option<FileTime> {
    None
}
<commit_msg>Complete Redox implementation (#55)<commit_after>use crate::FileTime;
use std::fs::{self, File};
use std::io;
use std::os::unix::prelude::*;
use std::path::Path;

pub fn set_file_times(p: &Path, atime: FileTime, mtime: FileTime) -> io::Result<()> {
    let fd = syscall::open(p.as_os_str().as_bytes(), 0)
        .map_err(|err| io::Error::from_raw_os_error(err.errno))?;
    let res = set_file_times_redox(fd, atime, mtime);
    let _ = syscall::close(fd);
    res
}

pub fn set_file_mtime(p: &Path, mtime: FileTime) -> io::Result<()> {
    let fd = syscall::open(p.as_os_str().as_bytes(), 0)
        .map_err(|err| io::Error::from_raw_os_error(err.errno))?;
    let mut st = syscall::Stat::default();
    let res = match syscall::fstat(fd, &mut st) {
        Err(err) => Err(io::Error::from_raw_os_error(err.errno)),
        Ok(_) => set_file_times_redox(
            fd,
            FileTime {
                seconds: st.st_atime as i64,
                nanos: st.st_atime_nsec as u32,
            },
            mtime,
        ),
    };
    let _ = syscall::close(fd);
    res
}

pub fn set_file_atime(p: &Path, atime: FileTime) -> io::Result<()> {
    let fd = syscall::open(p.as_os_str().as_bytes(), 0)
        .map_err(|err| io::Error::from_raw_os_error(err.errno))?;
    let mut st = syscall::Stat::default();
    let res = match syscall::fstat(fd, &mut st) {
        Err(err) => Err(io::Error::from_raw_os_error(err.errno)),
        Ok(_) => set_file_times_redox(
            fd,
            atime,
            FileTime {
                seconds: st.st_mtime as i64,
                nanos: st.st_mtime_nsec as u32,
            },
        ),
    };
    let _ = syscall::close(fd);
    res
}

pub fn set_symlink_file_times(p: &Path, atime: FileTime, mtime: FileTime) -> io::Result<()> {
    let fd = syscall::open(p.as_os_str().as_bytes(), syscall::O_NOFOLLOW)
        .map_err(|err| io::Error::from_raw_os_error(err.errno))?;
    let res = set_file_times_redox(fd, atime, mtime);
    let _ = syscall::close(fd);
    res
}

pub fn set_file_handle_times(
    f: &File,
    atime: Option<FileTime>,
    mtime: Option<FileTime>,
) -> io::Result<()> {
    let (atime1, mtime1) = match (atime, mtime) {
        (Some(a), Some(b)) => (a, b),
        (None, None) => return Ok(()),
        (Some(a), None) => {
            let meta = f.metadata()?;
            (a, FileTime::from_last_modification_time(&meta))
        }
        (None, Some(b)) => {
            let meta = f.metadata()?;
            (FileTime::from_last_access_time(&meta), b)
        }
    };
    set_file_times_redox(f.as_raw_fd() as usize, atime1, mtime1)
}

fn set_file_times_redox(fd: usize, atime: FileTime, mtime: FileTime) -> io::Result<()> {
    use syscall::TimeSpec;

    fn to_timespec(ft: &FileTime) -> TimeSpec {
        TimeSpec {
            tv_sec: ft.seconds(),
            tv_nsec: ft.nanoseconds() as i32,
        }
    }

    let times = [to_timespec(&atime), to_timespec(&mtime)];
    match syscall::futimens(fd, &times) {
        Ok(_) => Ok(()),
        Err(err) => Err(io::Error::from_raw_os_error(err.errno)),
    }
}

pub fn from_last_modification_time(meta: &fs::Metadata) -> FileTime {
    FileTime {
        seconds: meta.mtime(),
        nanos: meta.mtime_nsec() as u32,
    }
}

pub fn from_last_access_time(meta: &fs::Metadata) -> FileTime {
    FileTime {
        seconds: meta.atime(),
        nanos: meta.atime_nsec() as u32,
    }
}

pub fn from_creation_time(_meta: &fs::Metadata) -> Option<FileTime> {
    None
}
<|endoftext|>


<commit_before>use ordered_float::OrderedFloat;

use cgmath::prelude::*;
use cgmath::dot;

use color::Color;
use intersection::Intersection;
use light::{Light, LightType};
use math::*;
use primitive::Primitive;
use ray::Ray;

pub struct Scene {
    primitives: Vec<Primitive>,
    lights: Vec<Light>,

    pub ambient_color: Color,
}

impl Scene {
    pub fn new(ambient_color: Color) -> Scene {
        Scene {
            primitives: Vec::new(),
            lights: Vec::new(),
            ambient_color: ambient_color,
        }
    }

    pub fn add_primitive(&mut self, primitive: Primitive) {
        self.primitives.push(primitive);
    }

    pub fn add_light(&mut self, light: Light) {
        self.lights.push(light);
    }

    pub fn intersect(&self, ray: &Ray) -> Option<(Intersection, &Primitive)> {
        self.primitives
        .iter()
        .filter_map(|p| p.intersect(ray).map(|i| (i, p)))
        .min_by_key(|&(i, _)| OrderedFloat(i.distance))
    }

    /// Calculate the irradiance, i.e. the reflected amount of light, at a point
    /// in the scene.
    pub fn irradiance_at(&self, intersection: &Intersection) -> Color {
        let mut radiance = self.ambient_color;

        for light in self.lights.iter() {
            let direction = match light.light_type() {
                LightType::Point { position } => position - intersection.position,
                LightType::Directional { direction } => direction,
            };

            if dot(intersection.normal, direction) < 0.0 {
                continue; // Ignore lights that are on the other side of the surface.
            }

            let distance = direction.magnitude();
            let direction = direction / distance; // Normalize the direction.

            // We offset the ray origin a tiny amount towards the light, to prevent 'shadow acne.'
            let shadow_ray = Ray::new(intersection.position + EPSILON * direction, direction);

            if !self.is_occluded(&shadow_ray, distance) {
                radiance += light.color / distance.powi(2) * dot(intersection.normal, direction);
            }
        }

        radiance
    }

    /// Check whether a ray passes through any primitives, within a maximum
    /// distance.
    pub fn is_occluded(&self, ray: &Ray, max_distance: f32) -> bool {
        self.primitives
            .iter()
            .filter_map(|p| p.intersect(ray))
            .any(|i| i.distance < max_distance)
    }
}
<commit_msg>Fix indentation in `Scene::intersect()`<commit_after>use ordered_float::OrderedFloat;

use cgmath::prelude::*;
use cgmath::dot;

use color::Color;
use intersection::Intersection;
use light::{Light, LightType};
use math::*;
use primitive::Primitive;
use ray::Ray;

pub struct Scene {
    primitives: Vec<Primitive>,
    lights: Vec<Light>,

    pub ambient_color: Color,
}

impl Scene {
    pub fn new(ambient_color: Color) -> Scene {
        Scene {
            primitives: Vec::new(),
            lights: Vec::new(),
            ambient_color: ambient_color,
        }
    }

    pub fn add_primitive(&mut self, primitive: Primitive) {
        self.primitives.push(primitive);
    }

    pub fn add_light(&mut self, light: Light) {
        self.lights.push(light);
    }

    pub fn intersect(&self, ray: &Ray) -> Option<(Intersection, &Primitive)> {
        self.primitives
            .iter()
            .filter_map(|p| p.intersect(ray).map(|i| (i, p)))
            .min_by_key(|&(i, _)| OrderedFloat(i.distance))
    }

    /// Calculate the irradiance, i.e. the reflected amount of light, at a point
    /// in the scene.
    pub fn irradiance_at(&self, intersection: &Intersection) -> Color {
        let mut radiance = self.ambient_color;

        for light in self.lights.iter() {
            let direction = match light.light_type() {
                LightType::Point { position } => position - intersection.position,
                LightType::Directional { direction } => direction,
            };

            if dot(intersection.normal, direction) < 0.0 {
                continue; // Ignore lights that are on the other side of the surface.
            }

            let distance = direction.magnitude();
            let direction = direction / distance; // Normalize the direction.

            // We offset the ray origin a tiny amount towards the light, to prevent 'shadow acne.'
            let shadow_ray = Ray::new(intersection.position + EPSILON * direction, direction);

            if !self.is_occluded(&shadow_ray, distance) {
                radiance += light.color / distance.powi(2) * dot(intersection.normal, direction);
            }
        }

        radiance
    }

    /// Check whether a ray passes through any primitives, within a maximum
    /// distance.
    pub fn is_occluded(&self, ray: &Ray, max_distance: f32) -> bool {
        self.primitives
            .iter()
            .filter_map(|p| p.intersect(ray))
            .any(|i| i.distance < max_distance)
    }
}
<|endoftext|>


<commit_before>use main::{WINDOW_WIDTH, WINDOW_HEIGHT, RGB};
use opencl;
use opencl::mem::CLBuffer;

pub struct Scene {
  pub obj1_center: [f32; 3],
  pub obj1_radius: f32,
  pub obj2_center: [f32; 3],
  pub obj2_radius: f32,
  pub camera: [f32; 3],
}

impl Scene {
  pub fn render(&self) -> Vec<RGB> {
    let (device, ctx, queue) = opencl::util::create_compute_context().unwrap();

    let len = WINDOW_WIDTH as usize * WINDOW_HEIGHT as usize;

    let output_buffer: CLBuffer<RGB> = ctx.create_buffer(len, opencl::cl::CL_MEM_WRITE_ONLY);

    let program = {
      let ker = format!("
        // Doesn't return 0 so that rays can bounce without bumping.
        float toi(
          const float3 eye,
          const float3 look,

          const float3 sphere,
          const float radius)
        {{
          // quadratic coefficients
          float a = dot(look, look);
          float b = 2 * dot(eye - sphere, look);
          float c = dot(sphere, sphere) + dot(eye, eye) - dot(eye, sphere) - radius * radius;

          // discriminant
          float d = b*b - 4*a*c;

          if (d < 0) {{
            return HUGE_VALF;
          }}

          float s1 = (sqrt(d) - b) / (2 * a);
          if (s1 <= 0) {{
            s1 = HUGE_VALF;
          }}

          float s2 = (-sqrt(d) - b) / (2 * a);
          if (s1 < s2 || s2 <= 0) {{
            return s1;
          }}

          return s2;
        }}

        float3 rotate_vec(
          const float3 vec,
          const float3 axis)
        {{
          float3 r1 = {{2*axis[0]*axis[0] - 1, 2*axis[0]*axis[1], 2*axis[0]*axis[2]}};
          float3 r2 = {{2*axis[0]*axis[1], 2*axis[1]*axis[1] - 1, 2*axis[1]*axis[2]}};
          float3 r3 = {{2*axis[0]*axis[2], 2*axis[1]*axis[2], 2*axis[2]*axis[2] - 1}};

          float3 r = {{dot(r1, vec), dot(r2, vec), dot(r3, vec)}};
          return r;
        }}

        __kernel void render(
          const float3 obj1_center,
          const float obj1_radius,
          const float3 obj2_center,
          const float obj2_radius,

          float3 eye,

          __global float * output)
        {{
          int W = {};
          int H = {};

          float fov_x = 3.14 / 2;
          float fov_y = 3.14 / 2;

          int i = get_global_id(0);

          float x_pix = i % W;
          float y_pix = i / W;

          float t_x = fov_x * (x_pix / W - 0.5);
          float t_y = fov_y * (y_pix / H - 0.5);

          float c = cos(t_y);
          float3 look = {{c*sin(t_x), sin(t_y), -c*cos(t_x)}};

          i = i * 3;
          output[i+0] = 1;
          output[i+1] = 1;
          output[i+2] = 1;

          int max_bounces = 1;
          // The number of casts is the number of bounces - 1.
          for (int cast = 0; cast <= max_bounces; ++cast) {{
            float toi1 = toi(eye, look, obj1_center, obj1_radius);
            float toi2 = toi(eye, look, obj2_center, obj2_radius);

            if (toi1 == HUGE_VALF && toi2 == HUGE_VALF) {{
              output[i+0] = 0;
              output[i+1] = 0;
              output[i+2] = 0;
              return;
            }}

            if (toi1 < toi2) {{
              // obj1 color factor
              output[i+0] *= 1;
              output[i+1] *= 0;
              output[i+2] *= 0;

              float3 intersection = eye + toi1 * look;
              float3 normal = normalize(intersection - obj1_center);
              float directness = -dot(normal, look) / length(look);
              if (directness < 0) {{
                directness = 0;
              }}
              output[i+0] *= directness;
              output[i+1] *= directness;
              output[i+2] *= directness;

              eye = intersection;
              look = rotate_vec(-look, normal);
            }} else {{
              // We hit the light.
              return;
            }}
          }}

          output[i+0] = 1;
          output[i+1] = 0;
          output[i+2] = 1;
        }}",
        WINDOW_WIDTH,
        WINDOW_HEIGHT,
      );
      ctx.create_program_from_source(ker.as_slice())
    };
    program.build(&device).unwrap();

    let kernel = program.create_kernel("render");
    kernel.set_arg(0, &self.obj1_center);
    kernel.set_arg(1, &self.obj1_radius);
    kernel.set_arg(2, &self.obj2_center);
    kernel.set_arg(3, &self.obj2_radius);
    kernel.set_arg(4, &self.camera);

    // This is sketchy; we "implicitly cast" output_buffer from a CLBuffer<RGB> to a CLBuffer<f32>.
    kernel.set_arg(5, &output_buffer);

    let event = queue.enqueue_async_kernel(&kernel, len, None, ());

    queue.get(&output_buffer, &event)
  }
}
<commit_msg>Kernel output* is properly write-only.<commit_after>use main::{WINDOW_WIDTH, WINDOW_HEIGHT, RGB};
use opencl;
use opencl::mem::CLBuffer;

pub struct Scene {
  pub obj1_center: [f32; 3],
  pub obj1_radius: f32,
  pub obj2_center: [f32; 3],
  pub obj2_radius: f32,
  pub camera: [f32; 3],
}

impl Scene {
  pub fn render(&self) -> Vec<RGB> {
    let (device, ctx, queue) = opencl::util::create_compute_context().unwrap();

    let len = WINDOW_WIDTH as usize * WINDOW_HEIGHT as usize;

    let output_buffer: CLBuffer<RGB> = ctx.create_buffer(len, opencl::cl::CL_MEM_WRITE_ONLY);

    let program = {
      let ker = format!("
        // Doesn't return 0 so that rays can bounce without bumping.
        float toi(
          const float3 eye,
          const float3 look,

          const float3 sphere,
          const float radius)
        {{
          // quadratic coefficients
          float a = dot(look, look);
          float b = 2 * dot(eye - sphere, look);
          float c = dot(sphere, sphere) + dot(eye, eye) - dot(eye, sphere) - radius * radius;

          // discriminant
          float d = b*b - 4*a*c;

          if (d < 0) {{
            return HUGE_VALF;
          }}

          float s1 = (sqrt(d) - b) / (2 * a);
          if (s1 <= 0) {{
            s1 = HUGE_VALF;
          }}

          float s2 = (-sqrt(d) - b) / (2 * a);
          if (s1 < s2 || s2 <= 0) {{
            return s1;
          }}

          return s2;
        }}

        float3 rotate_vec(
          const float3 vec,
          const float3 axis)
        {{
          float3 r1 = {{2*axis[0]*axis[0] - 1, 2*axis[0]*axis[1], 2*axis[0]*axis[2]}};
          float3 r2 = {{2*axis[0]*axis[1], 2*axis[1]*axis[1] - 1, 2*axis[1]*axis[2]}};
          float3 r3 = {{2*axis[0]*axis[2], 2*axis[1]*axis[2], 2*axis[2]*axis[2] - 1}};

          float3 r = {{dot(r1, vec), dot(r2, vec), dot(r3, vec)}};
          return r;
        }}

        __kernel void render(
          const float3 obj1_center,
          const float obj1_radius,
          const float3 obj2_center,
          const float obj2_radius,

          float3 eye,

          __global float * output)
        {{
          int W = {};
          int H = {};

          float fov_x = 3.14 / 2;
          float fov_y = 3.14 / 2;

          int i = get_global_id(0);

          float x_pix = i % W;
          float y_pix = i / W;

          float t_x = fov_x * (x_pix / W - 0.5);
          float t_y = fov_y * (y_pix / H - 0.5);

          float c = cos(t_y);
          float3 look = {{c*sin(t_x), sin(t_y), -c*cos(t_x)}};

          i = i * 3;
          float3 color = {{1, 1, 1}};

          int max_bounces = 1;
          // The number of casts is the number of bounces - 1.
          for (int cast = 0; cast <= max_bounces; ++cast) {{
            float toi1 = toi(eye, look, obj1_center, obj1_radius);
            float toi2 = toi(eye, look, obj2_center, obj2_radius);

            if (toi1 == HUGE_VALF && toi2 == HUGE_VALF) {{
              output[i+0] = 0;
              output[i+1] = 0;
              output[i+2] = 0;
              return;
            }}

            if (toi1 < toi2) {{
              float3 obj_color = {{1, 0, 0}};
              color *= obj_color;

              float3 intersection = eye + toi1 * look;
              float3 normal = normalize(intersection - obj1_center);
              float directness = -dot(normal, look) / length(look);

              if (directness < 0) {{
                directness = 0;
              }}

              color *= directness;

              eye = intersection;
              look = rotate_vec(-look, normal);
            }} else {{
              // We hit the light.

              output[i+0] = color[0];
              output[i+1] = color[1];
              output[i+2] = color[2];
              return;
            }}
          }}

          output[i+0] = 1;
          output[i+1] = 0;
          output[i+2] = 1;
        }}",
        WINDOW_WIDTH,
        WINDOW_HEIGHT,
      );
      ctx.create_program_from_source(ker.as_slice())
    };
    program.build(&device).unwrap();

    let kernel = program.create_kernel("render");
    kernel.set_arg(0, &self.obj1_center);
    kernel.set_arg(1, &self.obj1_radius);
    kernel.set_arg(2, &self.obj2_center);
    kernel.set_arg(3, &self.obj2_radius);
    kernel.set_arg(4, &self.camera);

    // This is sketchy; we "implicitly cast" output_buffer from a CLBuffer<RGB> to a CLBuffer<f32>.
    kernel.set_arg(5, &output_buffer);

    let event = queue.enqueue_async_kernel(&kernel, len, None, ());

    queue.get(&output_buffer, &event)
  }
}
<|endoftext|>