diff --git a/share/input.rng b/share/input.rng index 3edd84bd5e..787ca2596a 100644 --- a/share/input.rng +++ b/share/input.rng @@ -632,6 +632,7 @@ + @@ -837,6 +838,19 @@ + + + + + + + + + + + + + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ab313c8d2b..639ef973cc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -32,6 +32,7 @@ set(SCRAM_CORE_SRC "${CMAKE_CURRENT_SOURCE_DIR}/element.cc" "${CMAKE_CURRENT_SOURCE_DIR}/expression.cc" "${CMAKE_CURRENT_SOURCE_DIR}/parameter.cc" + "${CMAKE_CURRENT_SOURCE_DIR}/expression/conditional.cc" "${CMAKE_CURRENT_SOURCE_DIR}/expression/constant.cc" "${CMAKE_CURRENT_SOURCE_DIR}/expression/numerical.cc" "${CMAKE_CURRENT_SOURCE_DIR}/expression/exponential.cc" diff --git a/src/expression/conditional.cc b/src/expression/conditional.cc new file mode 100644 index 0000000000..b86246ab43 --- /dev/null +++ b/src/expression/conditional.cc @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2017 Olzhas Rakhimov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/// @file conditional.cc +/// Implementation of conditional expressions. + +#include "conditional.h" + +#include + +namespace scram { +namespace mef { + +Interval Ite::interval() noexcept { + assert(args().size() == 3); + Interval then_interval = args()[1]->interval(); + Interval else_interval = args()[2]->interval(); + return Interval::closed( + std::min(then_interval.lower(), else_interval.lower()), + std::max(then_interval.upper(), else_interval.upper())); +} + +} // namespace mef +} // namespace scram diff --git a/src/expression/conditional.h b/src/expression/conditional.h new file mode 100644 index 0000000000..0b3f9eb487 --- /dev/null +++ b/src/expression/conditional.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2017 Olzhas Rakhimov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/// @file conditional.h +/// Conditional (if-then-else, switch-case) expressions. + +#ifndef SCRAM_SRC_EXPRESSION_CONDITIONAL_H_ +#define SCRAM_SRC_EXPRESSION_CONDITIONAL_H_ + +#include "src/expression.h" + +namespace scram { +namespace mef { + +/// If-Then-Else ternary expression. +class Ite : public ExpressionFormula { + public: + /// @param[in] condition The Boolean expression to be tested. + /// @param[in] then_arm The expression if the Boolean is true. + /// @param[in] else_arm The expression if the Boolean is false. + Ite(Expression* condition, Expression* then_arm, Expression* else_arm) + : ExpressionFormula({condition, then_arm, else_arm}) {} + + Interval interval() noexcept override; + + /// Computes the if-then-else expression with the given evaluator. + template + double Compute(F&& eval) noexcept { + assert(args().size() == 3); + return eval(args()[0]) ? eval(args()[1]) : eval(args()[2]); + } +}; + +} // namespace mef +} // namespace scram + +#endif // SCRAM_SRC_EXPRESSION_CONDITIONAL_H_ diff --git a/src/initializer.cc b/src/initializer.cc index 4692e97ca7..0c4010d32d 100644 --- a/src/initializer.cc +++ b/src/initializer.cc @@ -30,6 +30,7 @@ #include "env.h" #include "error.h" #include "expression/boolean.h" +#include "expression/conditional.h" #include "expression/exponential.h" #include "expression/numerical.h" #include "expression/random_deviate.h" @@ -834,7 +835,8 @@ const Initializer::ExtractorMap Initializer::kExpressionExtractors_ = { {"lt", &Extract}, {"gt", &Extract}, {"leq", &Extract}, - {"geq", &Extract}}; + {"geq", &Extract}, + {"ite", &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 32dcb2ef39..ed4eda2d3d 100644 --- a/tests/expression_tests.cc +++ b/tests/expression_tests.cc @@ -17,6 +17,7 @@ #include "expression.h" #include "expression/boolean.h" +#include "expression/conditional.h" #include "expression/exponential.h" #include "expression/constant.h" #include "expression/numerical.h" @@ -1150,6 +1151,22 @@ TEST(ExpressionTest, Geq) { EXPECT_DOUBLE_EQ(0, dev->value()); } +TEST(ExpressionTest, Ite) { + 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(&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(); +} + } // namespace test } // namespace mef } // namespace scram diff --git a/tests/input/fta/correct_expressions.xml b/tests/input/fta/correct_expressions.xml index d32df4bd38..4df168874e 100644 --- a/tests/input/fta/correct_expressions.xml +++ b/tests/input/fta/correct_expressions.xml @@ -353,5 +353,12 @@ The input tries to utilize all the functionality including optional cases. + + + + + + +