Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env bash -vex
- # C++14 implementation of constant subexpression folding algorithm
- #
- # Copyright (c) 2013-2014, Anatoliy V. Tomilov
- # All rights reserved.
- #
- # Redistribution and use in source and binary forms, with or without
- # modification, are permitted provided that the following condition is met:
- # Redistributions of source code must retain the above copyright notice, this condition and the following disclaimer.
- #
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- # POSSIBILITY OF SUCH DAMAGE.
- #
- # cls ; bash self.bash 2>&1 | tee self.log | less
- WARN="-W -Wall -Wextra -Werror"
- INCLUDE="-I/c/libs/boost-trunk"
- g++ -x c++ - -std=gnu++1y $WARN $INCLUDE -Og -g -o a <<__EOF && ./a && echo -e "\e[1;32msucceeded\e[0m" || echo -e "\e[1;31mfailed\e[0m"
- #include <boost/variant.hpp>
- #include <boost/variant/recursive_variant.hpp>
- #include <boost/fusion/include/adapt_struct.hpp>
- #include <boost/mpl/distance.hpp>
- #include <boost/mpl/begin.hpp>
- #include <boost/mpl/find.hpp>
- #include <iostream>
- #include <string>
- #include <deque>
- #include <stack>
- #include <iterator>
- #include <stdexcept>
- #include <type_traits>
- #include <functional>
- #include <utility>
- #include <cstdlib>
- #include <cstddef>
- #include <cmath>
- using size_type = std::size_t;
- using symbol_type = std::string;
- template< typename V, typename T >
- using variant_index = boost::mpl::distance< typename boost::mpl::begin< typename V::types >::type, typename boost::mpl::find< typename V::types, T >::type >;
- enum class binary { add, sub, mul, div, mod, pow };
- inline
- size_type
- precedence(binary const _op)
- {
- switch (_op) {
- case binary::add :
- case binary::sub : {
- return 2;
- }
- case binary::mul :
- case binary::div :
- case binary::mod : {
- return 3;
- }
- case binary::pow : {
- return 4;
- }
- default : {
- break;
- }
- }
- throw std::logic_error("unsupported operator");
- }
- inline
- std::ostream & operator << (std::ostream & out, binary const _op)
- {
- switch (_op) {
- case binary::add : return out << " + ";
- case binary::sub : return out << " - ";
- case binary::mul : return out << " * ";
- case binary::div : return out << " / ";
- case binary::mod : return out << " % ";
- case binary::pow : return out << " ^ ";
- default : {
- break;
- }
- }
- throw std::logic_error("unsupported operator");
- }
- template< typename ...T >
- struct extended_variant
- {
- struct adapted_variant_tag;
- using base_type = extended_variant;
- using variant_type = boost::variant< T... >;
- using types = typename variant_type::types;
- template< typename X >
- using index = variant_index< variant_type, X >;
- extended_variant() = default;
- extended_variant(extended_variant const &) = default;
- extended_variant(extended_variant &) = default;
- extended_variant(extended_variant &&) = default;
- extended_variant & operator = (extended_variant const &) = default;
- extended_variant & operator = (extended_variant &) = default;
- extended_variant & operator = (extended_variant &&) = default;
- template< typename X >
- extended_variant(X && _value)
- : variant_(std::forward< X >(_value))
- { ; }
- template< typename X >
- extended_variant &
- operator = (X && _value)
- {
- variant_ = std::forward< X >(_value);
- return *this;
- }
- template< typename Visitor >
- typename Visitor::result_type
- apply_visitor(Visitor & _visitor)
- {
- return variant_.apply_visitor(_visitor);
- }
- template< typename Visitor >
- typename Visitor::result_type
- apply_visitor(Visitor & _visitor) const
- {
- return variant_.apply_visitor(_visitor);
- }
- variant_type &
- get()
- {
- return variant_;
- }
- variant_type const &
- get() const
- {
- return variant_;
- }
- private :
- variant_type variant_;
- };
- namespace boost
- {
- template< typename X, typename ...T >
- X const &
- get(extended_variant< T... > const & _extended_variant)
- {
- return boost::get< X >(_extended_variant.get());
- }
- template< typename X, typename ...T >
- X &
- get(extended_variant< T... > & _extended_variant)
- {
- return boost::get< X >(_extended_variant.get());
- }
- template< typename X, typename ...T >
- X const *
- get(extended_variant< T... > const * _extended_variant)
- {
- return boost::get< X >(std::addressof(_extended_variant->get()));
- }
- template< typename X, typename ...T >
- X *
- get(extended_variant< T... > * _extended_variant)
- {
- return boost::get< X >(std::addressof(_extended_variant->get()));
- }
- } // namespace boost
- namespace ast
- {
- using identifier = symbol_type;
- template< typename G >
- struct expression;
- template< typename G >
- struct binary_expression;
- template< typename G >
- struct operand;
- template< typename G >
- using operand_cref = std::reference_wrapper< operand< G > const >;
- template< typename G >
- struct operand
- : extended_variant<
- boost::blank,
- G,
- identifier,
- boost::recursive_wrapper< expression< G > >,
- boost::recursive_wrapper< binary_expression< G > >,
- operand_cref< G >
- >
- {
- operand() = default;
- operand(operand const &) = default;
- operand(operand &) = default;
- operand(operand &&) = default;
- using operand::base_type::base_type;
- operand & operator = (operand const &) = default;
- operand & operator = (operand &) = default;
- operand & operator = (operand &&) = default;
- template< typename X >
- operand &
- operator = (X && _value)
- {
- operand::base_type::get() = std::forward< X >(_value);
- return *this;
- }
- friend
- inline
- std::ostream & operator << (std::ostream & out, operand const & _operand)
- {
- switch (_operand.get().which()) {
- case operand::base_type::template index< binary_expression< G > >::value :
- case operand::base_type::template index< expression< G > >::value : {
- return out << '(' << _operand.get() << ')';
- }
- default : {
- break;
- }
- }
- return out << _operand.get();
- }
- };
- template< typename G >
- struct operation
- {
- binary operator_;
- operand< G > operand_;
- friend
- inline
- std::ostream & operator << (std::ostream & out, operation const & _operation)
- {
- return out << _operation.operator_
- << _operation.operand_;
- }
- };
- template< typename G >
- using operation_list = std::deque< operation< G > >;
- template< typename G >
- struct expression
- {
- operand< G > first_;
- operation_list< G > rest_;
- friend
- inline
- std::ostream & operator << (std::ostream & out, expression const & _expression)
- {
- out << _expression.first_;
- for (operation< G > const & operation_ : _expression.rest_) {
- out << operation_;
- }
- return out;
- }
- };
- template< typename G >
- struct binary_expression
- {
- operand< G > lhs_;
- binary operator_;
- operand< G > rhs_;
- friend
- inline
- std::ostream & operator << (std::ostream & out, binary_expression const & _ast)
- {
- return out << _ast.lhs_ << _ast.operator_ << _ast.rhs_;
- }
- };
- } // namespace ast
- namespace std
- {
- template< typename G >
- ast::operand_cref< G >
- cref(ast::operand< G > const & _operand)
- {
- if (_operand.get().which() == ast::operand< G >::template index< ast::operand_cref< G > >::value) {
- return boost::get< ast::operand_cref< G > >(_operand);
- }
- return _operand;
- }
- } // namespace std
- #if 0
- // unused
- BOOST_FUSION_ADAPT_TPL_STRUCT(
- (G),
- (ast::operation)(G),
- (binary, operator_)
- (ast::operand< G >, operand_)
- )
- BOOST_FUSION_ADAPT_TPL_STRUCT(
- (G),
- (ast::expression)(G),
- (ast::operand< G >, first_)
- (ast::operation_list< G >, rest_)
- )
- #endif
- template< typename G, typename D >
- struct shunting_yard_algorithm
- : boost::static_visitor< typename D::result_type >
- {
- static_assert(!std::is_reference< D >::value,
- "!");
- using typename boost::static_visitor< typename D::result_type >::result_type;
- shunting_yard_algorithm(D & _dispatcher)
- : dispatcher_(_dispatcher)
- { ; }
- result_type
- traverse(ast::expression< G > const & _expression);
- struct lhs_op_rhs
- {
- size_type const lhs_;
- binary const op_;
- size_type const rhs_;
- };
- using operand_cref_type = ast::operand_cref< G >;
- using node_type = boost::variant< lhs_op_rhs, operand_cref_type >;
- result_type
- operator () (node_type const & _node) const
- {
- return boost::apply_visitor(*this, _node);
- }
- result_type
- operator () (lhs_op_rhs const & _top) const
- { // dispatcher can perform a tree balancing here
- return dispatcher_(output_.at(_top.lhs_), _top.op_, output_.at(_top.rhs_));
- }
- result_type
- operator () (operand_cref_type const & _top) const
- {
- return dispatcher_(_top.get());
- }
- private :
- D & dispatcher_;
- std::deque< node_type > output_;
- struct lhs_op
- {
- size_type const lhs_;
- binary const op_;
- };
- };
- template< typename G, typename D >
- auto
- shunting_yard_algorithm< G, D >::traverse(ast::expression< G > const & _input)
- -> result_type
- {
- BOOST_ASSERT(output_.empty());
- if (_input.rest_.empty()) {
- return dispatcher_(_input.first_);
- }
- if (_input.rest_.size() == 1) {
- ast::operation< G > const & operation_ = _input.rest_.back();
- return dispatcher_(_input.first_, operation_.operator_, operation_.operand_);
- }
- //output_.reserve(_input.rest_.size() * 2 + 1); // total number of operators and operands, but we needs push_front()
- std::stack< lhs_op > lhs_op_;
- for (ast::operation< G > const & operation_ : _input.rest_) {
- if (!lhs_op_.empty()) {
- size_type const p_ = precedence(operation_.operator_);
- do {
- lhs_op const & top_ = lhs_op_.top();
- if (precedence(top_.op_) < p_) {
- break;
- }
- output_.emplace_back(lhs_op_rhs{top_.lhs_, top_.op_, output_.size()});
- lhs_op_.pop();
- } while (!lhs_op_.empty());
- }
- lhs_op_.emplace(lhs_op{output_.size(), operation_.operator_});
- output_.emplace_back(operation_.operand_);
- }
- while (!lhs_op_.empty()) {
- lhs_op const & top_ = lhs_op_.top();
- output_.emplace_back(lhs_op_rhs{top_.lhs_, top_.op_, output_.size()});
- lhs_op_.pop();
- }
- output_.emplace_front(_input.first_);
- return operator () (output_.back());
- }
- template< typename G >
- struct expression_evaluator
- : boost::static_visitor< ast::operand< G > >
- {
- using typename boost::static_visitor< ast::operand< G > >::result_type;
- template< typename ...T >
- result_type
- operator () (T &&... _ast) const
- {
- return evaluate(std::forward< T >(_ast)...);
- }
- private :
- using identifier_type = ast::identifier;
- using operation_type = ast::operation< G >;
- using expression_type = ast::expression< G >;
- using binary_expression_type = ast::binary_expression< G >;
- using operand_cref_type = ast::operand_cref< G >;
- using operand_type = ast::operand< G >;
- result_type
- evaluate(boost::blank const &) const
- {
- return {};
- }
- result_type
- evaluate(G const & _ast) const
- {
- return _ast;
- }
- result_type
- evaluate(identifier_type const & _ast) const
- {
- return _ast;
- }
- struct operation_commutator
- : boost::static_visitor< result_type >
- {
- constexpr
- operation_commutator(binary const _op)
- : op_(_op)
- { ; }
- result_type
- operator () (operand_type && _lhs, operand_type && _rhs) const
- {
- return boost::apply_visitor(*this, _lhs.get(), _rhs.get());
- }
- template< typename L, typename R >
- result_type
- operator () (L & _lhs, R & _rhs) const
- {
- return binary_expression_type{std::move(_lhs), op_, std::move(_rhs)};
- }
- template< typename L >
- result_type
- operator () (L & _lhs, G & _rhs) const
- {
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wfloat-equal"
- switch (op_) {
- case binary::add :
- case binary::sub : {
- if (_rhs == G(0.0L)) {
- return std::move(_lhs);
- }
- break;
- }
- case binary::mul : {
- if (_rhs == G(0.0L)) {
- return G(0.0L);
- } else if (_rhs == G(1.0L)) {
- return std::move(_lhs);
- }
- break;
- }
- case binary::div : {
- if (_rhs == G(1.0L)) {
- return std::move(_lhs);
- } else if (_rhs == G(0.0L)) {
- throw std::runtime_error("domain error: / division by zero");
- }
- break;
- }
- case binary::mod : {
- if (_rhs == G(0.0L)) {
- throw std::runtime_error("domain error: % division by zero");
- }
- break;
- }
- case binary::pow : {
- if (_rhs == G(0.0L)) {
- return G(1.0L);
- } else if (_rhs == G(1.0L)) {
- return std::move(_lhs);
- }
- break;
- }
- default : {
- throw std::logic_error("unsupported operator");
- }
- }
- #pragma GCC diagnostic pop
- return binary_expression_type{std::move(_lhs), op_, std::move(_rhs)};
- }
- template< typename R >
- result_type
- operator () (G & _lhs, R & _rhs) const
- {
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wfloat-equal"
- switch (op_) {
- case binary::add : {
- if (_lhs == G(0.0L)) {
- return std::move(_rhs);
- }
- break;
- }
- case binary::sub : {
- break;
- }
- case binary::mul : {
- if (_lhs == G(0.0L)) {
- return G(0.0L);
- } else if (_lhs == G(1.0L)) {
- return std::move(_rhs);
- }
- break;
- }
- case binary::div : {
- if (_lhs == G(0.0L)) {
- return G(0.0L);
- }
- break;
- }
- case binary::mod : {
- if (_lhs == G(0.0L)) {
- return G(0.0L);
- }
- break;
- }
- case binary::pow : {
- if (_lhs == G(1.0L)) {
- return G(1.0L);
- }
- break;
- }
- default : {
- throw std::logic_error("unsupported operator");
- }
- }
- #pragma GCC diagnostic pop
- return binary_expression_type{std::move(_lhs), op_, std::move(_rhs)};
- }
- result_type
- operator () (G & _lhs, G & _rhs) const;
- private :
- binary const op_;
- };
- operation_commutator const add_{binary::add};
- operation_commutator const sub_{binary::sub};
- operation_commutator const mul_{binary::mul};
- operation_commutator const div_{binary::div};
- operation_commutator const mod_{binary::mod};
- operation_commutator const pow_{binary::pow};
- result_type
- evaluate(operand_type && _lhs, binary const _op, operand_type && _rhs) const
- {
- switch (_op) {
- case binary::add : {
- return add_(std::move(_lhs), std::move(_rhs));
- }
- case binary::sub : {
- return sub_(std::move(_lhs), std::move(_rhs));
- }
- case binary::mul : {
- return mul_(std::move(_lhs), std::move(_rhs));
- }
- case binary::div : {
- return div_(std::move(_lhs), std::move(_rhs));
- }
- case binary::mod : {
- return mod_(std::move(_lhs), std::move(_rhs));
- }
- case binary::pow : {
- return pow_(std::move(_lhs), std::move(_rhs));
- }
- default : {
- break;
- }
- }
- throw std::logic_error("unsupported operator");
- }
- result_type
- evaluate(binary_expression_type const & _ast) const
- {
- return operator () (operator () (_ast.lhs_),
- _ast.operator_,
- operator () (_ast.rhs_));
- }
- struct tree_switch
- : boost::static_visitor< result_type >
- {
- using shunting_yard_algorithm_type = shunting_yard_algorithm< G, tree_switch const >;
- using node_type = typename shunting_yard_algorithm_type::node_type;
- tree_switch(expression_evaluator const & _evaluate_expression)
- : evaluate_expression_(_evaluate_expression)
- , _(*this)
- { ; }
- result_type
- traverse(expression_type const & _expression)
- {
- return _.traverse(_expression);
- }
- result_type
- operator () (operand_type const & _ast) const
- {
- return evaluate_expression_(_ast);
- }
- result_type
- operator () (node_type const & _lhs, binary const _op, node_type const & _rhs) const
- {
- return evaluate_expression_(_(_lhs).get(), _op, _(_rhs).get());
- }
- private :
- expression_evaluator const & evaluate_expression_;
- shunting_yard_algorithm_type _;
- };
- result_type
- evaluate(expression_type const & _expression) const
- {
- if (_expression.rest_.empty()) {
- return operator () (_expression.first_);
- } else if (_expression.rest_.size() == 1) {
- operation_type const & op_rhs_ = _expression.rest_.back();
- return operator () (operator () (_expression.first_),
- op_rhs_.operator_,
- operator () (op_rhs_.operand_));
- } else {
- tree_switch Dijkstra(*this);
- return Dijkstra.traverse(_expression);
- }
- }
- result_type
- evaluate(operand_type const & _ast) const
- {
- return boost::apply_visitor(*this, _ast.get());
- }
- result_type
- evaluate(operand_cref_type const & _ast) const
- {
- return operator () (_ast.get());
- }
- };
- template< typename G >
- inline G pow(G const & _x, G const & _y);
- template< typename G >
- inline G fmod(G const & _x, G const & _y);
- template< typename G >
- auto
- expression_evaluator< G >::operation_commutator::operator () (G & _lhs, G & _rhs) const
- -> result_type
- {
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wfloat-equal"
- switch (op_) {
- case binary::add : {
- return _lhs + _rhs;
- }
- case binary::sub : {
- return _lhs - _rhs;
- }
- case binary::mul : {
- return _lhs * _rhs;
- }
- case binary::div : {
- if (_rhs == G(0.0L)) {
- throw std::runtime_error("domain error: / division by zero");
- }
- return _lhs / _rhs;
- }
- case binary::mod : {
- if (_rhs == G(0.0L)) {
- throw std::runtime_error("domain error: % division by zero");
- }
- return fmod<>(_lhs, _rhs);
- }
- case binary::pow : {
- if (_lhs == G(0.0L)) {
- if (G(0.0L) < _rhs) {
- return G(0.0L);
- } else {
- throw std::runtime_error("domain error: ^ base is 0 and exp is less than or equal to 0");
- }
- } else if (_lhs < G(0.0L)) {
- if (!(fmod<>(_rhs, G(1.0L)) == G(0.0L))) {
- throw std::runtime_error("domain error: ^ base is negative and exp is not an integer value");
- }
- }
- if (_rhs == G(0.0L)) {
- return G(1.0L);
- } else if (_rhs == G(1.0L)) {
- return _lhs;
- }
- return pow<>(_lhs, _rhs);
- }
- default : {
- break;
- }
- }
- #pragma GCC diagnostic pop
- throw std::logic_error("unsupported operator");
- }
- template< typename G >
- struct test
- {
- using operand_type = ast::operand< G >;
- using expression_type = ast::expression< G >;
- using binary_expression_type = ast::binary_expression< G >;
- test(std::ostream & _out)
- : out(_out)
- , expression_evaluator_()
- { ; }
- void
- operator () () const
- {
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wmissing-field-initializers"
- operand_type const x{
- "x"
- };
- expression_type const e0{
- x
- };
- print(e0);
- expression_type const e1{
- e0,
- {
- {binary::add, G(1.0L)}
- }
- };
- print(e1);
- expression_type const e2{
- G(2.0L),
- {
- {binary::pow, G(3.0L)}
- }
- };
- print(e2);
- expression_type const e3{
- expression_type{
- expression_type{
- expression_type{
- expression_type{
- G(3.0L)
- }
- }
- }
- }
- };
- print(e3);
- expression_type const e4{
- e2,
- {
- {binary::add, G(3.0L)},
- {binary::div, G(1.5L)}
- }
- };
- print(e4);
- expression_type const e5{
- expression_type{
- e0,
- {
- {binary::sub, e2}
- }
- },
- {
- {binary::mul, e4}
- }
- };
- print(e5);
- expression_type const e6{
- "x",
- {
- {binary::mul, "y"},
- {binary::add, "z"},
- {binary::div, "t"},
- {binary::sub, "u"},
- {binary::mul, "v"},
- {binary::pow, "w"}
- }
- };
- print(e6);
- expression_type const e7{
- G(2.0L),
- {
- {binary::mul, G(3.0L)},
- {binary::add, G(10.0L)},
- {binary::div, G(2.0L)},
- {binary::sub, G(7.0L)},
- {binary::mul, G(2.0L)},
- {binary::pow, G(3.0L)}
- }
- };
- print(e7);
- expression_type const e8{
- G(2.0L),
- {
- {binary::mul, "a"},
- {binary::add, G(10.0L)},
- {binary::div, "b"},
- {binary::sub, G(7.0L)},
- {binary::mul, "c"},
- {binary::pow, G(3.0L)}
- }
- };
- print(e8);
- expression_type const e9{
- "first",
- {
- {binary::sub, G(3.0L)},
- {binary::add, G(10.0L)},
- {binary::sub, G(2.0L)},
- {binary::add, G(7.0L)},
- {binary::sub, G(2.0L)},
- {binary::add, G(3.0L)}
- }
- };
- print(e9);
- expression_type const e10{
- G(2.0L),
- {
- {binary::sub, G(3.0L)},
- {binary::add, G(10.0L)},
- {binary::sub, G(2.0L)},
- {binary::add, G(7.0L)},
- {binary::sub, G(2.0L)},
- {binary::add, "last"}
- }
- };
- print(e10);
- expression_type const e11{
- G(2.0L),
- {
- {binary::div, G(0.0L)}
- }
- };
- print(e11);
- expression_type const e12{
- G(-1.0L),
- {
- {binary::pow, G(1.1L)}
- }
- };
- print(e12);
- #pragma GCC diagnostic pop
- }
- private :
- std::ostream & out;
- expression_evaluator< G > const expression_evaluator_;
- void
- print(expression_type const & _expression) const
- {
- try {
- out << _expression << std::flush
- << " -> "
- << expression_evaluator_(_expression)
- << std::endl;
- } catch (std::runtime_error const & _re) {
- std::cerr << _re.what() << std::endl;
- }
- }
- };
- using G = double;
- template<>
- inline
- G
- pow<>(G const & _x, G const & _y)
- { return std::pow(_x, _y); }
- template<>
- inline
- G
- fmod<>(G const & _x, G const & _y)
- { return std::fmod(_x, _y); }
- int main()
- {
- test< G > const test_ = std::cout;
- test_();
- return EXIT_SUCCESS;
- }
- /*
- x -> x
- (x) + 1 -> (x + 1)
- 2 ^ 3 -> 8
- ((((3)))) -> 3
- (2 ^ 3) + 3 / 1.5 -> 10
- ((x) - (2 ^ 3)) * ((2 ^ 3) + 3 / 1.5) -> ((x - 8) * 10)
- x * y + z / t - u * v ^ w -> (((x * y) + (z / t)) - (u * (v ^ w)))
- 2 * 3 + 10 / 2 - 7 * 2 ^ 3 -> -45
- 2 * a + 10 / b - 7 * c ^ 3 -> (((2 * a) + (10 / b)) - (7 * (c ^ 3)))
- first - 3 + 10 - 2 + 7 - 2 + 3 -> ((((((first - 3) + 10) - 2) + 7) - 2) + 3)
- 2 - 3 + 10 - 2 + 7 - 2 + last -> (12 + last)
- domain error: / division by zero
- domain error: ^ base is negative and exp is not an integer value
- */
- __EOF
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement