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.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+