From 3c835f3d0654205e462f9fdc353e7bc9b35706db Mon Sep 17 00:00:00 2001 From: rakhimov Date: Tue, 25 Apr 2017 15:55:15 -0700 Subject: [PATCH] Add switch expression Issue #67 --- share/input.rng | 17 +++++++++++++ src/expression/conditional.cc | 22 +++++++++++++++++ src/expression/conditional.h | 32 +++++++++++++++++++++++++ src/initializer.cc | 24 ++++++++++++++++++- tests/expression_tests.cc | 20 ++++++++++++++++ tests/input/fta/correct_expressions.xml | 19 +++++++++++++++ 6 files changed, 133 insertions(+), 1 deletion(-) diff --git a/share/input.rng b/share/input.rng index 787ca2596a..930e04a6bb 100644 --- a/share/input.rng +++ b/share/input.rng @@ -841,6 +841,7 @@ + @@ -852,6 +853,22 @@ + + + + + + + + + + + + + + + + diff --git a/src/expression/conditional.cc b/src/expression/conditional.cc index b86246ab43..f68e595757 100644 --- a/src/expression/conditional.cc +++ b/src/expression/conditional.cc @@ -34,5 +34,27 @@ Interval Ite::interval() noexcept { std::max(then_interval.upper(), else_interval.upper())); } +Switch::Switch(std::vector cases, Expression* default_value) + : ExpressionFormula({default_value}), + cases_(std::move(cases)), + default_value_(*default_value) { + for (auto& case_arm : cases_) { + Expression::AddArg(&case_arm.condition); + Expression::AddArg(&case_arm.value); + } +} + +Interval Switch::interval() noexcept { + Interval default_interval = default_value_.interval(); + double min_value = default_interval.lower(); + double max_value = default_interval.upper(); + for (auto& case_arm : cases_) { + Interval case_interval = case_arm.value.interval(); + min_value = std::min(min_value, case_interval.lower()); + max_value = std::max(max_value, case_interval.upper()); + } + return Interval::closed(min_value, max_value); +} + } // namespace mef } // namespace scram diff --git a/src/expression/conditional.h b/src/expression/conditional.h index 0b3f9eb487..355f7b1454 100644 --- a/src/expression/conditional.h +++ b/src/expression/conditional.h @@ -21,6 +21,8 @@ #ifndef SCRAM_SRC_EXPRESSION_CONDITIONAL_H_ #define SCRAM_SRC_EXPRESSION_CONDITIONAL_H_ +#include + #include "src/expression.h" namespace scram { @@ -45,6 +47,36 @@ class Ite : public ExpressionFormula { } }; +/// Switch-Case conditional operations. +class Switch : public ExpressionFormula { + public: + /// Individual cases in the switch-case operation. + struct Case { + Expression& condition; ///< The case condition. + Expression& value; ///< The value to evaluated if the condition is true. + }; + + /// @param[in] cases The collection of cases to evaluate. + /// @param[in] default_value The default value if all cases are false. + Switch(std::vector cases, Expression* default_value); + + Interval interval() noexcept override; + + /// Computes the switch-case expression with the given evaluator. + template + double Compute(F&& eval) noexcept { + for (Case& case_arm : cases_) { + if (eval(&case_arm.condition)) + return eval(&case_arm.value); + } + return eval(&default_value_); + } + + private: + std::vector cases_; ///< Ordered collection of cases. + Expression& default_value_; ///< The default case value. +}; + } // namespace mef } // namespace scram diff --git a/src/initializer.cc b/src/initializer.cc index 0c4010d32d..fa30119d09 100644 --- a/src/initializer.cc +++ b/src/initializer.cc @@ -790,6 +790,27 @@ std::unique_ptr Initializer::Extract( } } +/// Specialization for Switch-Case operation extraction. +template <> +std::unique_ptr Initializer::Extract( + const xmlpp::NodeSet& args, + const std::string& base_path, + Initializer* init) { + assert(!args.empty()); + Expression* default_value = + init->GetExpression(XmlElement(args.back()), base_path); + std::vector cases; + auto it_end = std::prev(args.end()); + for (auto it = args.begin(); it != it_end; ++it) { + xmlpp::NodeSet nodes = (*it)->find("./*"); + assert(nodes.size() == 2); + cases.push_back( + {*init->GetExpression(XmlElement(nodes.front()), base_path), + *init->GetExpression(XmlElement(nodes.back()), base_path)}); + } + return std::make_unique(std::move(cases), default_value); +} + const Initializer::ExtractorMap Initializer::kExpressionExtractors_ = { {"exponential", &Extract}, {"GLM", &Extract}, @@ -836,7 +857,8 @@ const Initializer::ExtractorMap Initializer::kExpressionExtractors_ = { {"gt", &Extract}, {"leq", &Extract}, {"geq", &Extract}, - {"ite", &Extract}}; + {"ite", &Extract}, + {"switch", &Extract}}; Expression* Initializer::GetExpression(const xmlpp::Element* expr_element, const std::string& base_path) { diff --git a/tests/expression_tests.cc b/tests/expression_tests.cc index ed4eda2d3d..b1ada02f52 100644 --- a/tests/expression_tests.cc +++ b/tests/expression_tests.cc @@ -1167,6 +1167,26 @@ TEST(ExpressionTest, Ite) { << dev->interval(); } +TEST(ExpressionTest, Switch) { + OpenExpression arg_one(1); + OpenExpression arg_two(42, 42, 32, 52); + OpenExpression arg_three(10, 10, 5, 15); + std::unique_ptr dev; + ASSERT_NO_THROW( + dev = std::make_unique( + std::vector{{arg_one, arg_two}}, &arg_three)); + EXPECT_DOUBLE_EQ(42, dev->value()); + arg_one.mean = 0; + EXPECT_DOUBLE_EQ(10, dev->value()); + arg_one.mean = 0.5; + EXPECT_DOUBLE_EQ(42, dev->value()); + + EXPECT_TRUE(Interval::closed(5, 52) == dev->interval()) + << dev->interval(); + + EXPECT_DOUBLE_EQ(10, Switch({}, &arg_three).value()); +} + } // namespace test } // namespace mef } // namespace scram diff --git a/tests/input/fta/correct_expressions.xml b/tests/input/fta/correct_expressions.xml index 4df168874e..a1d0a18ef7 100644 --- a/tests/input/fta/correct_expressions.xml +++ b/tests/input/fta/correct_expressions.xml @@ -360,5 +360,24 @@ The input tries to utilize all the functionality including optional cases. + + + + + + + + + + + + + + + + + + +