// Copyright (C) 2016-2018 T. Zachary Laine // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include #include template using term = boost::yap::terminal; template using ref = boost::yap::expression_ref; namespace yap = boost::yap; namespace bh = boost::hana; namespace user { struct number { double value; friend number operator+(number lhs, number rhs) { return number{lhs.value + rhs.value}; } friend number operator-(number lhs, number rhs) { return number{lhs.value - rhs.value}; } friend number operator*(number lhs, number rhs) { return number{lhs.value * rhs.value}; } friend number operator-(number n) { return number{-n.value}; } friend bool operator<(number lhs, double rhs) { return lhs.value < rhs; } friend bool operator<(double lhs, number rhs) { return lhs < rhs.value; } }; number naxpy(number a, number x, number y) { return number{a.value * x.value + y.value + 10.0}; } struct empty_xform {}; struct eval_xform_tag { decltype(auto) operator()( yap::expr_tag, user::number const & n) { return n; } }; struct eval_xform_expr { decltype(auto) operator()(term const & expr) { return ::boost::yap::value(expr); } }; struct eval_xform_both { decltype(auto) operator()( yap::expr_tag, user::number const & n) { return n; } decltype(auto) operator()(term const & expr) { throw std::logic_error("Oops! Picked the wrong overload!"); return ::boost::yap::value(expr); } }; struct plus_to_minus_xform_tag { decltype(auto) operator()( yap::expr_tag, user::number const & lhs, user::number const & rhs) { return yap::make_expression( term{lhs}, term{rhs}); } }; struct plus_to_minus_xform_expr { template decltype(auto) operator()(yap::expression< yap::expr_kind::plus, bh::tuple> const & expr) { return yap::make_expression( ::boost::yap::left(expr), ::boost::yap::right(expr)); } }; struct plus_to_minus_xform_both { decltype(auto) operator()( yap::expr_tag, user::number const & lhs, user::number const & rhs) { return yap::make_expression( term{lhs}, term{rhs}); } template decltype(auto) operator()(yap::expression< yap::expr_kind::plus, bh::tuple> const & expr) { throw std::logic_error("Oops! Picked the wrong overload!"); return yap::make_expression( ::boost::yap::left(expr), ::boost::yap::right(expr)); } }; struct term_nonterm_xform_tag { auto operator()( yap::expr_tag, user::number const & n) { return yap::make_terminal(n * user::number{2.0}); } auto operator()( yap::expr_tag, user::number const & lhs, user::number const & rhs) { return yap::make_expression( yap::transform(::boost::yap::make_terminal(lhs), *this), yap::transform(::boost::yap::make_terminal(rhs), *this)); } }; struct term_nonterm_xform_expr { decltype(auto) operator()(term const & expr) { return yap::make_terminal( ::boost::yap::value(expr) * user::number{2.0}); } template decltype(auto) operator()(yap::expression< yap::expr_kind::plus, bh::tuple> const & expr) { return yap::make_expression( yap::transform(::boost::yap::left(expr), *this), yap::transform(::boost::yap::right(expr), *this)); } }; struct term_nonterm_xform_both { decltype(auto) operator()( yap::expr_tag, user::number const & n) { return yap::make_terminal(n * user::number{2.0}); } decltype(auto) operator()(term const & expr) { return yap::make_terminal( ::boost::yap::value(expr) * user::number{2.0}); } decltype(auto) operator()( yap::expr_tag, user::number const & lhs, user::number const & rhs) { return yap::make_expression( yap::transform(::boost::yap::make_terminal(lhs), *this), yap::transform(::boost::yap::make_terminal(rhs), *this)); } template decltype(auto) operator()(yap::expression< yap::expr_kind::plus, bh::tuple> const & expr) { return yap::make_expression( yap::transform(::boost::yap::left(expr), *this), yap::transform(::boost::yap::right(expr), *this)); } }; struct eval_term_nonterm_xform_tag { decltype(auto) operator()( yap::expr_tag, user::number const & n) { return n * user::number{2.0}; } template decltype(auto) operator()( yap::expr_tag, Expr1 const & lhs, Expr2 const & rhs) { return boost::yap::transform(::boost::yap::as_expr(lhs), *this) - boost::yap::transform(::boost::yap::as_expr(rhs), *this); } }; struct eval_term_nonterm_xform_expr { decltype(auto) operator()(term const & expr) { return ::boost::yap::value(expr) * user::number{2.0}; } template decltype(auto) operator()(yap::expression< yap::expr_kind::plus, bh::tuple> const & expr) { return boost::yap::transform(::boost::yap::left(expr), *this) - boost::yap::transform(::boost::yap::right(expr), *this); } }; struct eval_term_nonterm_xform_both { decltype(auto) operator()( yap::expr_tag, user::number const & n) { return n * user::number{2.0}; } decltype(auto) operator()(term const & expr) { return ::boost::yap::value(expr) * user::number{2.0}; } template decltype(auto) operator()( yap::expr_tag, Expr1 const & lhs, Expr2 const & rhs) { return boost::yap::transform(::boost::yap::as_expr(lhs), *this) - boost::yap::transform(::boost::yap::as_expr(rhs), *this); } template decltype(auto) operator()(yap::expression< yap::expr_kind::plus, bh::tuple> const & expr) { return boost::yap::transform(::boost::yap::left(expr), *this) - boost::yap::transform(::boost::yap::right(expr), *this); } }; decltype(auto) naxpy_eager_nontemplate_xform(yap::expression< yap::expr_kind::plus, bh::tuple< yap::expression< yap::expr_kind::multiplies, bh::tuple< ref &>, ref &>>>, ref &>>> const & expr) { auto a = evaluate(expr.left().left()); auto x = evaluate(expr.left().right()); auto y = evaluate(expr.right()); return yap::make_terminal(naxpy(a, x, y)); } decltype(auto) naxpy_lazy_nontemplate_xform(yap::expression< yap::expr_kind::plus, bh::tuple< yap::expression< yap::expr_kind::multiplies, bh::tuple< ref &>, ref &>>>, ref &>>> const & expr) { decltype(auto) a = expr.left().left().value(); decltype(auto) x = expr.left().right().value(); decltype(auto) y = expr.right().value(); return yap::make_terminal(naxpy)(a, x, y); } struct naxpy_xform { template decltype(auto) operator()(yap::expression< yap::expr_kind::plus, bh::tuple< yap::expression< yap::expr_kind::multiplies, bh::tuple>, Expr3>> const & expr) { return yap::make_terminal(naxpy)( transform(expr.left().left(), naxpy_xform{}), transform(expr.left().right(), naxpy_xform{}), transform(expr.right(), naxpy_xform{})); } }; // unary transforms struct disable_negate_xform_tag { auto operator()(yap::expr_tag, user::number value) { return yap::make_terminal(std::move(value)); } template auto operator()(yap::expr_tag, Expr const & expr) { return expr; } }; struct disable_negate_xform_expr { template decltype(auto) operator()( yap::expression> const & expr) { return ::boost::yap::value(expr); } }; struct disable_negate_xform_both { decltype(auto) operator()(yap::expr_tag, user::number value) { return yap::make_terminal(std::move(value)); } template decltype(auto) operator()(yap::expr_tag, Expr const & expr) { return expr; } template decltype(auto) operator()( yap::expression> const & expr) { throw std::logic_error("Oops! Picked the wrong overload!"); return ::boost::yap::value(expr); } }; // ternary transforms //[ tag_xform struct ternary_to_else_xform_tag { template decltype(auto) operator()( boost::yap::expr_tag, Expr const & cond, user::number then, user::number else_) { return boost::yap::make_terminal(std::move(else_)); } }; //] //[ expr_xform struct ternary_to_else_xform_expr { template decltype(auto) operator()(boost::yap::expression< boost::yap::expr_kind::if_else, boost::hana::tuple> const & expr) { return ::boost::yap::else_(expr); } }; //] struct ternary_to_else_xform_both { template decltype(auto) operator()( yap::expr_tag, Expr const & cond, user::number then, user::number else_) { return yap::make_terminal(std::move(else_)); } template decltype(auto) operator()(yap::expression< yap::expr_kind::if_else, bh::tuple> const & expr) { throw std::logic_error("Oops! Picked the wrong overload!"); return ::boost::yap::else_(expr); } }; } auto double_to_float(term expr) { return term{(float)expr.value()}; } auto check_unique_ptrs_equal_7(term> && expr) { using namespace boost::hana::literals; BOOST_CHECK(*expr.elements[0_c] == 7); return std::move(expr); } int test_main(int, char * []) { { term a{{1.0}}; term x{{42.0}}; term y{{3.0}}; { auto expr = a; { user::number result = evaluate(expr); BOOST_CHECK(result.value == 1); } { auto transformed_expr = transform(expr, user::empty_xform{}); user::number result = evaluate(transformed_expr); BOOST_CHECK(result.value == 1); } { auto transformed_expr = transform(expr, user::eval_xform_tag{}); BOOST_CHECK(transformed_expr.value == 1); } { auto transformed_expr = transform(expr, user::eval_xform_expr{}); BOOST_CHECK(transformed_expr.value == 1); } { auto transformed_expr = transform(expr, user::eval_xform_both{}); BOOST_CHECK(transformed_expr.value == 1); } } { auto expr = x + y; { user::number result = evaluate(expr); BOOST_CHECK(result.value == 45); } { auto transformed_expr = transform(expr, user::plus_to_minus_xform_tag{}); user::number result = evaluate(transformed_expr); BOOST_CHECK(result.value == 39); } { auto transformed_expr = transform(expr, user::plus_to_minus_xform_expr{}); user::number result = evaluate(transformed_expr); BOOST_CHECK(result.value == 39); } { auto transformed_expr = transform(expr, user::plus_to_minus_xform_both{}); user::number result = evaluate(transformed_expr); BOOST_CHECK(result.value == 39); } } { auto expr = x + user::number{3.0}; { user::number result = evaluate(expr); BOOST_CHECK(result.value == 45); } { auto transformed_expr = transform(expr, user::term_nonterm_xform_tag{}); user::number result = evaluate(transformed_expr); BOOST_CHECK(result.value == 39 * 2); } { auto transformed_expr = transform(expr, user::term_nonterm_xform_expr{}); user::number result = evaluate(transformed_expr); BOOST_CHECK(result.value == 39 * 2); } { auto transformed_expr = transform(expr, user::term_nonterm_xform_both{}); user::number result = evaluate(transformed_expr); BOOST_CHECK(result.value == 39 * 2); } } { auto expr = x + y; { user::number result = evaluate(expr); BOOST_CHECK(result.value == 45); } { auto transformed_expr = transform(expr, user::term_nonterm_xform_tag{}); user::number result = evaluate(transformed_expr); BOOST_CHECK(result.value == 39 * 2); } { auto transformed_expr = transform(expr, user::term_nonterm_xform_expr{}); user::number result = evaluate(transformed_expr); BOOST_CHECK(result.value == 39 * 2); } { auto transformed_expr = transform(expr, user::term_nonterm_xform_both{}); user::number result = evaluate(transformed_expr); BOOST_CHECK(result.value == 39 * 2); } } { auto expr = (x + y) + user::number{1.0}; { user::number result = evaluate(expr); BOOST_CHECK(result.value == 46); } { // Differs from those below, because it matches terminals, not // expressions. auto transformed_expr = transform(expr, user::term_nonterm_xform_tag{}); user::number result = evaluate(transformed_expr); BOOST_CHECK(result.value == 40 * 2); } { auto transformed_expr = transform(expr, user::term_nonterm_xform_expr{}); user::number result = evaluate(transformed_expr); BOOST_CHECK(result.value == 38 * 2); } { auto transformed_expr = transform(expr, user::term_nonterm_xform_both{}); user::number result = evaluate(transformed_expr); BOOST_CHECK(result.value == 38 * 2); } } { auto expr = x + user::number{3.0}; { user::number result = evaluate(expr); BOOST_CHECK(result.value == 45); } { user::number result = transform(expr, user::eval_term_nonterm_xform_tag{}); BOOST_CHECK(result.value == 39 * 2); } { user::number result = transform(expr, user::eval_term_nonterm_xform_expr{}); BOOST_CHECK(result.value == 39 * 2); } { user::number result = transform(expr, user::eval_term_nonterm_xform_both{}); BOOST_CHECK(result.value == 39 * 2); } } { auto expr = x + y; { user::number result = evaluate(expr); BOOST_CHECK(result.value == 45); } { user::number result = transform(expr, user::eval_term_nonterm_xform_tag{}); BOOST_CHECK(result.value == 39 * 2); } { user::number result = transform(expr, user::eval_term_nonterm_xform_expr{}); BOOST_CHECK(result.value == 39 * 2); } { user::number result = transform(expr, user::eval_term_nonterm_xform_both{}); BOOST_CHECK(result.value == 39 * 2); } } { auto expr = (x + y) + user::number{1.0}; { user::number result = evaluate(expr); BOOST_CHECK(result.value == 46); } { user::number result = transform(expr, user::eval_term_nonterm_xform_tag{}); BOOST_CHECK(result.value == 38 * 2); } { user::number result = transform(expr, user::eval_term_nonterm_xform_expr{}); BOOST_CHECK(result.value == 38 * 2); } { user::number result = transform(expr, user::eval_term_nonterm_xform_both{}); BOOST_CHECK(result.value == 38 * 2); } } { auto expr = a * x + y; { user::number result = evaluate(expr); BOOST_CHECK(result.value == 45); } auto transformed_expr = transform(expr, user::naxpy_eager_nontemplate_xform); { user::number result = evaluate(transformed_expr); BOOST_CHECK(result.value == 55); } } { auto expr = a + (a * x + y); { user::number result = evaluate(expr); BOOST_CHECK(result.value == 46); } auto transformed_expr = transform(expr, user::naxpy_eager_nontemplate_xform); { user::number result = evaluate(transformed_expr); BOOST_CHECK(result.value == 56); } } { auto expr = a * x + y; { user::number result = evaluate(expr); BOOST_CHECK(result.value == 45); } auto transformed_expr = transform(expr, user::naxpy_lazy_nontemplate_xform); { user::number result = evaluate(transformed_expr); BOOST_CHECK(result.value == 55); } } { auto expr = a + (a * x + y); { user::number result = evaluate(expr); BOOST_CHECK(result.value == 46); } auto transformed_expr = transform(expr, user::naxpy_lazy_nontemplate_xform); { user::number result = evaluate(transformed_expr); BOOST_CHECK(result.value == 56); } } { auto expr = (a * x + y) * (a * x + y) + (a * x + y); { user::number result = evaluate(expr); BOOST_CHECK(result.value == 45 * 45 + 45); } auto transformed_expr = transform(expr, user::naxpy_xform{}); { user::number result = evaluate(transformed_expr); BOOST_CHECK(result.value == 55 * 55 + 55 + 10); } } } { term unity{1.0}; term> i{new int{7}}; yap::expression< yap::expr_kind::plus, bh::tuple &>, term>>> expr_1 = unity + std::move(i); yap::expression< yap::expr_kind::plus, bh::tuple< ref &>, yap::expression< yap::expr_kind::plus, bh::tuple< ref &>, term>>>>> expr_2 = unity + std::move(expr_1); auto transformed_expr = transform(std::move(expr_2), double_to_float); transform(std::move(transformed_expr), check_unique_ptrs_equal_7); } { term a{{1.0}}; term x{{42.0}}; term y{{3.0}}; { auto expr = -x; { user::number result = evaluate(expr); BOOST_CHECK(result.value == -42); } { auto transformed_expr = transform(expr, user::disable_negate_xform_tag{}); user::number result = evaluate(transformed_expr); BOOST_CHECK(result.value == 42); } { auto transformed_expr = transform(expr, user::disable_negate_xform_expr{}); user::number result = evaluate(transformed_expr); BOOST_CHECK(result.value == 42); } { auto transformed_expr = transform(expr, user::disable_negate_xform_both{}); user::number result = evaluate(transformed_expr); BOOST_CHECK(result.value == 42); } } { auto expr = a * -x + y; { user::number result = evaluate(expr); BOOST_CHECK(result.value == -39); } { auto transformed_expr = transform(expr, user::disable_negate_xform_tag{}); user::number result = evaluate(transformed_expr); BOOST_CHECK(result.value == 45); } { auto transformed_expr = transform(expr, user::disable_negate_xform_expr{}); user::number result = evaluate(transformed_expr); BOOST_CHECK(result.value == 45); } { auto transformed_expr = transform(expr, user::disable_negate_xform_both{}); user::number result = evaluate(transformed_expr); BOOST_CHECK(result.value == 45); } } { auto expr = -(x + y); { user::number result = evaluate(expr); BOOST_CHECK(result.value == -45); } { auto transformed_expr = transform(expr, user::disable_negate_xform_tag{}); user::number result = evaluate(transformed_expr); BOOST_CHECK(result.value == 45); } { auto transformed_expr = transform(expr, user::disable_negate_xform_expr{}); user::number result = evaluate(transformed_expr); BOOST_CHECK(result.value == 45); } { auto transformed_expr = transform(expr, user::disable_negate_xform_both{}); user::number result = evaluate(transformed_expr); BOOST_CHECK(result.value == 45); } } } { term a{{1.0}}; term x{{42.0}}; term y{{3.0}}; { auto expr = if_else(0 < a, x, y); { user::number result = evaluate(expr); BOOST_CHECK(result.value == 42); } { auto transformed_expr = transform(expr, user::ternary_to_else_xform_tag{}); user::number result = evaluate(transformed_expr); BOOST_CHECK(result.value == 3); } { auto transformed_expr = transform(expr, user::ternary_to_else_xform_expr{}); user::number result = evaluate(transformed_expr); BOOST_CHECK(result.value == 3); } { auto transformed_expr = transform(expr, user::ternary_to_else_xform_both{}); user::number result = evaluate(transformed_expr); BOOST_CHECK(result.value == 3); } } { auto expr = y * if_else(0 < a, x, y) + user::number{0.0}; { user::number result = evaluate(expr); BOOST_CHECK(result.value == 126); } { auto transformed_expr = transform(expr, user::ternary_to_else_xform_tag{}); user::number result = evaluate(transformed_expr); BOOST_CHECK(result.value == 9); } { auto transformed_expr = transform(expr, user::ternary_to_else_xform_expr{}); user::number result = evaluate(transformed_expr); BOOST_CHECK(result.value == 9); } { auto transformed_expr = transform(expr, user::ternary_to_else_xform_both{}); user::number result = evaluate(transformed_expr); BOOST_CHECK(result.value == 9); } } } return 0; }