#define DRAKE_COMMON_SYMBOLIC_EXPRESSION_DETAIL_HEADER
#include "drake/common/symbolic/expression/expression_cell.h"
#undef DRAKE_COMMON_SYMBOLIC_EXPRESSION_DETAIL_HEADER

#include <gtest/gtest.h>

#include "drake/common/test_utilities/symbolic_test_util.h"

namespace drake {
namespace symbolic {
namespace {

using test::ExprEqual;
using test::FormulaEqual;

// Provides common variables and expressions that are used by the following
// tests.
class SymbolicExpressionCellTest : public ::testing::Test {
 protected:
  const Variable var_x_{"x"};
  const Variable var_y_{"y"};
  const Variable var_z_{"z"};

  const Expression x_{var_x_};
  const Expression y_{var_y_};

  const Expression e_constant_{1.0};
  const Expression e_var_{var_x_};
  const Expression e_add_{x_ + y_};
  const Expression e_mul_{x_ * y_};
  const Expression e_div_{x_ / y_};
  const Expression e_log_{log(x_)};
  const Expression e_abs_{abs(x_)};
  const Expression e_exp_{exp(x_)};
  const Expression e_sqrt_{sqrt(x_)};
  const Expression e_pow_{pow(x_, y_)};
  const Expression e_sin_{sin(x_)};
  const Expression e_cos_{cos(x_)};
  const Expression e_tan_{tan(x_)};
  const Expression e_asin_{asin(x_)};
  const Expression e_acos_{acos(x_)};
  const Expression e_atan_{atan(x_)};
  const Expression e_atan2_{atan2(x_, y_)};
  const Expression e_sinh_{sinh(x_)};
  const Expression e_cosh_{cosh(x_)};
  const Expression e_tanh_{tanh(x_)};
  const Expression e_min_{min(x_, y_)};
  const Expression e_max_{max(x_, y_)};
  const Expression e_ceil_{ceil(x_)};
  const Expression e_floor_{floor(x_)};
  const Expression e_ite_{if_then_else(x_ < y_, x_, y_)};
  const Expression e_uf_{uninterpreted_function("uf", {var_x_, var_y_})};
};

TEST_F(SymbolicExpressionCellTest, CastFunctionsConst) {
  EXPECT_EQ(to_variable(e_var_).get_variable(), get_variable(e_var_));

  EXPECT_EQ(to_addition(e_add_).get_constant(),
            get_constant_in_addition(e_add_));
  EXPECT_EQ(to_addition(e_add_).get_expr_to_coeff_map(),
            get_expr_to_coeff_map_in_addition(e_add_));

  EXPECT_EQ(to_multiplication(e_mul_).get_constant(),
            get_constant_in_multiplication(e_mul_));
  EXPECT_EQ(to_multiplication(e_mul_).get_base_to_exponent_map().size(),
            get_base_to_exponent_map_in_multiplication(e_mul_).size());
  EXPECT_EQ(to_multiplication(e_mul_).get_base_to_exponent_map().size(), 2);
  EXPECT_PRED2(ExprEqual,
               to_multiplication(e_mul_).get_base_to_exponent_map().at(var_x_),
               get_base_to_exponent_map_in_multiplication(e_mul_).at(var_x_));
  EXPECT_PRED2(ExprEqual,
               to_multiplication(e_mul_).get_base_to_exponent_map().at(var_y_),
               get_base_to_exponent_map_in_multiplication(e_mul_).at(var_y_));

  EXPECT_EQ(to_division(e_div_).get_second_argument(),
            get_second_argument(e_div_));
  EXPECT_EQ(to_division(e_div_).get_second_argument(),
            get_second_argument(e_div_));

  EXPECT_EQ(to_log(e_log_).get_argument(), get_argument(e_log_));
  EXPECT_EQ(to_abs(e_abs_).get_argument(), get_argument(e_abs_));
  EXPECT_EQ(to_exp(e_exp_).get_argument(), get_argument(e_exp_));
  EXPECT_EQ(to_sqrt(e_sqrt_).get_argument(), get_argument(e_sqrt_));
  EXPECT_EQ(to_pow(e_pow_).get_first_argument(), get_first_argument(e_pow_));
  EXPECT_EQ(to_pow(e_pow_).get_second_argument(), get_second_argument(e_pow_));
  EXPECT_EQ(to_sin(e_sin_).get_argument(), get_argument(e_sin_));
  EXPECT_EQ(to_cos(e_cos_).get_argument(), get_argument(e_cos_));
  EXPECT_EQ(to_tan(e_tan_).get_argument(), get_argument(e_tan_));
  EXPECT_EQ(to_asin(e_asin_).get_argument(), get_argument(e_asin_));
  EXPECT_EQ(to_acos(e_acos_).get_argument(), get_argument(e_acos_));
  EXPECT_EQ(to_atan(e_atan_).get_argument(), get_argument(e_atan_));
  EXPECT_EQ(to_atan2(e_atan2_).get_first_argument(),
            get_first_argument(e_atan2_));
  EXPECT_EQ(to_atan2(e_atan2_).get_second_argument(),
            get_second_argument(e_atan2_));
  EXPECT_EQ(to_sinh(e_sinh_).get_argument(), get_argument(e_sinh_));
  EXPECT_EQ(to_cosh(e_cosh_).get_argument(), get_argument(e_cosh_));
  EXPECT_EQ(to_tanh(e_tanh_).get_argument(), get_argument(e_tanh_));
  EXPECT_EQ(to_min(e_min_).get_first_argument(), get_first_argument(e_min_));
  EXPECT_EQ(to_min(e_min_).get_second_argument(), get_second_argument(e_min_));
  EXPECT_EQ(to_max(e_max_).get_first_argument(), get_first_argument(e_max_));
  EXPECT_EQ(to_max(e_max_).get_second_argument(), get_second_argument(e_max_));
  EXPECT_EQ(to_ceil(e_ceil_).get_argument(), get_argument(e_ceil_));
  EXPECT_EQ(to_floor(e_floor_).get_argument(), get_argument(e_floor_));
  EXPECT_PRED2(FormulaEqual, to_if_then_else(e_ite_).get_conditional_formula(),
               get_conditional_formula(e_ite_));
  EXPECT_PRED2(ExprEqual, to_if_then_else(e_ite_).get_then_expression(),
               get_then_expression(e_ite_));
  EXPECT_PRED2(ExprEqual, to_if_then_else(e_ite_).get_else_expression(),
               get_else_expression(e_ite_));
  EXPECT_EQ(to_uninterpreted_function(e_uf_).get_name(),
            get_uninterpreted_function_name(e_uf_));
  EXPECT_EQ(to_uninterpreted_function(e_uf_).get_arguments().size(), 2);
  EXPECT_EQ(to_uninterpreted_function(e_uf_).get_arguments().size(),
            get_uninterpreted_function_arguments(e_uf_).size());
  EXPECT_EQ(to_uninterpreted_function(e_uf_).get_arguments()[0],
            get_uninterpreted_function_arguments(e_uf_)[0]);
  EXPECT_EQ(to_uninterpreted_function(e_uf_).get_arguments()[1],
            get_uninterpreted_function_arguments(e_uf_)[1]);
}

TEST_F(SymbolicExpressionCellTest, CastFunctionsNonConst) {
  EXPECT_EQ(to_variable(Expression{e_var_}).get_variable(),
            get_variable(e_var_));

  EXPECT_EQ(to_addition(Expression{e_add_}).get_constant(),
            get_constant_in_addition(e_add_));
  EXPECT_EQ(to_addition(Expression{e_add_}).get_expr_to_coeff_map(),
            get_expr_to_coeff_map_in_addition(e_add_));

  EXPECT_EQ(to_multiplication(Expression{e_mul_}).get_constant(),
            get_constant_in_multiplication(e_mul_));
  EXPECT_EQ(
      to_multiplication(Expression{e_mul_}).get_base_to_exponent_map().size(),
      get_base_to_exponent_map_in_multiplication(e_mul_).size());
  EXPECT_EQ(
      to_multiplication(Expression{e_mul_}).get_base_to_exponent_map().size(),
      2);
  EXPECT_PRED2(ExprEqual,
               to_multiplication(Expression{e_mul_})
                   .get_base_to_exponent_map()
                   .at(var_x_),
               get_base_to_exponent_map_in_multiplication(e_mul_).at(var_x_));
  EXPECT_PRED2(ExprEqual,
               to_multiplication(Expression{e_mul_})
                   .get_base_to_exponent_map()
                   .at(var_y_),
               get_base_to_exponent_map_in_multiplication(e_mul_).at(var_y_));

  EXPECT_EQ(to_division(Expression{e_div_}).get_second_argument(),
            get_second_argument(e_div_));
  EXPECT_EQ(to_division(Expression{e_div_}).get_second_argument(),
            get_second_argument(e_div_));

  EXPECT_EQ(to_log(Expression{e_log_}).get_argument(), get_argument(e_log_));
  EXPECT_EQ(to_abs(Expression{e_abs_}).get_argument(), get_argument(e_abs_));
  EXPECT_EQ(to_exp(Expression{e_exp_}).get_argument(), get_argument(e_exp_));
  EXPECT_EQ(to_sqrt(Expression{e_sqrt_}).get_argument(), get_argument(e_sqrt_));
  EXPECT_EQ(to_pow(Expression{e_pow_}).get_first_argument(),
            get_first_argument(e_pow_));
  EXPECT_EQ(to_pow(Expression{e_pow_}).get_second_argument(),
            get_second_argument(e_pow_));
  EXPECT_EQ(to_sin(Expression{e_sin_}).get_argument(), get_argument(e_sin_));
  EXPECT_EQ(to_cos(Expression{e_cos_}).get_argument(), get_argument(e_cos_));
  EXPECT_EQ(to_tan(Expression{e_tan_}).get_argument(), get_argument(e_tan_));
  EXPECT_EQ(to_asin(Expression{e_asin_}).get_argument(), get_argument(e_asin_));
  EXPECT_EQ(to_acos(Expression{e_acos_}).get_argument(), get_argument(e_acos_));
  EXPECT_EQ(to_atan(Expression{e_atan_}).get_argument(), get_argument(e_atan_));
  EXPECT_EQ(to_atan2(Expression{e_atan2_}).get_first_argument(),
            get_first_argument(e_atan2_));
  EXPECT_EQ(to_atan2(Expression{e_atan2_}).get_second_argument(),
            get_second_argument(e_atan2_));
  EXPECT_EQ(to_sinh(Expression{e_sinh_}).get_argument(), get_argument(e_sinh_));
  EXPECT_EQ(to_cosh(Expression{e_cosh_}).get_argument(), get_argument(e_cosh_));
  EXPECT_EQ(to_tanh(Expression{e_tanh_}).get_argument(), get_argument(e_tanh_));
  EXPECT_EQ(to_min(Expression{e_min_}).get_first_argument(),
            get_first_argument(e_min_));
  EXPECT_EQ(to_min(Expression{e_min_}).get_second_argument(),
            get_second_argument(e_min_));
  EXPECT_EQ(to_max(Expression{e_max_}).get_first_argument(),
            get_first_argument(e_max_));
  EXPECT_EQ(to_max(Expression{e_max_}).get_second_argument(),
            get_second_argument(e_max_));
  EXPECT_EQ(to_ceil(Expression{e_ceil_}).get_argument(), get_argument(e_ceil_));
  EXPECT_EQ(to_floor(Expression{e_floor_}).get_argument(),
            get_argument(e_floor_));
  EXPECT_PRED2(FormulaEqual,
               to_if_then_else(Expression{e_ite_}).get_conditional_formula(),
               get_conditional_formula(e_ite_));
  EXPECT_PRED2(ExprEqual,
               to_if_then_else(Expression{e_ite_}).get_then_expression(),
               get_then_expression(e_ite_));
  EXPECT_PRED2(ExprEqual,
               to_if_then_else(Expression{e_ite_}).get_else_expression(),
               get_else_expression(e_ite_));
  EXPECT_EQ(to_uninterpreted_function(Expression{e_uf_}).get_name(),
            get_uninterpreted_function_name(e_uf_));
  EXPECT_EQ(to_uninterpreted_function(Expression{e_uf_}).get_arguments().size(),
            2);
  EXPECT_EQ(to_uninterpreted_function(Expression{e_uf_}).get_arguments().size(),
            get_uninterpreted_function_arguments(e_uf_).size());
  EXPECT_EQ(to_uninterpreted_function(Expression{e_uf_}).get_arguments()[0],
            get_uninterpreted_function_arguments(e_uf_)[0]);
  EXPECT_EQ(to_uninterpreted_function(Expression{e_uf_}).get_arguments()[1],
            get_uninterpreted_function_arguments(e_uf_)[1]);
}

}  // namespace
}  // namespace symbolic
}  // namespace drake
