LCOV - code coverage report
Current view: top level - data/include/mcrl2/data - enumerator.h (source / functions) Hit Total Coverage
Test: mcrl2_coverage.info.cleaned Lines: 302 347 87.0 %
Date: 2024-04-21 03:44:01 Functions: 241 532 45.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Author(s): Wieger Wesselink, Jan Friso Groote
       2             : // Copyright: see the accompanying file COPYING or copy at
       3             : // https://github.com/mCRL2org/mCRL2/blob/master/COPYING
       4             : //
       5             : // Distributed under the Boost Software License, Version 1.0.
       6             : // (See accompanying file LICENSE_1_0.txt or copy at
       7             : // http://www.boost.org/LICENSE_1_0.txt)
       8             : //
       9             : /// \file mcrl2/data/enumerator.h
      10             : /// \brief The class enumerator.
      11             : 
      12             : #ifndef MCRL2_DATA_ENUMERATOR_H
      13             : #define MCRL2_DATA_ENUMERATOR_H
      14             : 
      15             : #include <boost/iterator/iterator_facade.hpp>
      16             : #include "mcrl2/atermpp/standard_containers/deque.h"
      17             : #include "mcrl2/core/detail/print_utility.h"
      18             : #include "mcrl2/data/detail/enumerator_iteration_limit.h"
      19             : #include "mcrl2/data/rewriter.h"
      20             : #include "mcrl2/data/substitutions/enumerator_substitution.h"
      21             : #include "mcrl2/utilities/math.h"
      22             : 
      23             : namespace mcrl2
      24             : {
      25             : 
      26             : namespace data
      27             : {
      28             : 
      29             : namespace detail
      30             : {
      31             : 
      32             : inline
      33         292 : data_expression make_set_(std::size_t function_index, const sort_expression& element_sort, const data_expression_vector& set_elements)
      34             : {
      35         292 :   data_expression result = sort_fset::empty(element_sort);
      36        1260 :   for (const auto & set_element : set_elements)
      37             :   {
      38         968 :     if (function_index % 2 == 1)
      39             :     {
      40         484 :       result=sort_fset::insert(element_sort, set_element, result);
      41             :     }
      42         968 :     function_index = function_index / 2;
      43             :   }
      44         292 :   return result;
      45           0 : }
      46             : 
      47             : inline
      48       23280 : data_expression make_if_expression_(std::size_t& function_index,
      49             :                                    const std::size_t argument_index,
      50             :                                    const std::vector<data_expression_vector>& data_domain_expressions,
      51             :                                    const data_expression_vector& codomain_expressions,
      52             :                                    const variable_vector& parameters)
      53             : {
      54       23280 :   if (argument_index == data_domain_expressions.size())
      55             :   {
      56       12448 :     std::size_t result_expression_index = function_index % codomain_expressions.size();
      57       12448 :     function_index = function_index / codomain_expressions.size();
      58       12448 :     return codomain_expressions[result_expression_index];
      59             :   }
      60             : 
      61       10832 :   data_expression result;
      62       10832 :   const data_expression_vector& current_enumerated_elements = data_domain_expressions[argument_index];
      63       32496 :   for (auto i = current_enumerated_elements.rbegin(); i != current_enumerated_elements.rend(); ++i)
      64             :   {
      65       21664 :     if (i == current_enumerated_elements.rbegin())
      66             :     {
      67       10832 :       result = make_if_expression_(function_index, argument_index + 1, data_domain_expressions, codomain_expressions, parameters);
      68             :     }
      69             :     else
      70             :     {
      71       10832 :       const data_expression lhs = make_if_expression_(function_index, argument_index + 1, data_domain_expressions, codomain_expressions, parameters);
      72       10832 :       if (lhs != result) // Optimize: if the lhs and rhs are equal, return the rhs.
      73             :       {
      74        6856 :         result = if_(equal_to(parameters[argument_index], *i), lhs, result);
      75             :       }
      76       10832 :     }
      77             :   }
      78       10832 :   return result;
      79       10832 : }
      80             : 
      81             : /// \brief Computes the elements of a finite set sort, and puts them in result. If there are too many elements, false is returned.
      82             : template <class Rewriter, class MutableSubstitution>
      83          37 : bool compute_finite_set_elements(const container_sort& sort,
      84             :                                  const data_specification& dataspec,
      85             :                                  Rewriter datar, MutableSubstitution& sigma,
      86             :                                  data_expression_vector& result,
      87             :                                  enumerator_identifier_generator& id_generator)
      88             : {
      89             :   // TODO: This routine would really benefit from caching the calculated set of elements, as the same set is often generated
      90             :   // over and over again.
      91          37 :   data_expression_vector all_element_expressions = enumerate_expressions(sort.element_sort(), dataspec, datar, id_generator);
      92          37 :   if (all_element_expressions.size() >= 32)  // If there are at least 2^32 functions, then enumerating them makes little sense.
      93             :   {
      94           0 :     return false;
      95             :   }
      96          37 :   if (all_element_expressions.size() > 16)  // If there are more than 2^16 functions, provide a warning.
      97             :   {
      98           0 :     mCRL2log(log::warning) << "Generate 2^" << all_element_expressions.size() << " sets to enumerate sort " << sort << "\n";
      99             :   }
     100          37 :   const std::size_t number_of_sets = utilities::power_size_t(2, all_element_expressions.size());
     101         329 :   for (std::size_t i = 0; i < number_of_sets; ++i)
     102             :   {
     103         292 :     result.push_back(datar(make_set_(i, sort.element_sort(), all_element_expressions), sigma));
     104             :   }
     105          37 :   return true;
     106          37 : }
     107             : 
     108             : /// \brief Computes the elements of a finite function sort, and puts them in result. If there are too many elements, false is returned.
     109             : template <class Rewriter>
     110          26 : bool compute_finite_function_sorts(const function_sort& sort,
     111             :                                    enumerator_identifier_generator& id_generator,
     112             :                                    const data::data_specification& dataspec,
     113             :                                    Rewriter datar,
     114             :                                    data_expression_vector& result,
     115             :                                    variable_list& function_parameter_list
     116             :                                   )
     117             : {
     118          26 :   data_expression_vector codomain_expressions = enumerate_expressions(sort.codomain(), dataspec, datar, id_generator);
     119          26 :   std::vector<data_expression_vector> domain_expressions;
     120          26 :   std::size_t total_domain_size = 1;
     121          26 :   variable_vector function_parameters;
     122             : 
     123          90 :   for (const sort_expression& s: sort.domain())
     124             :   {
     125          38 :     domain_expressions.push_back(enumerate_expressions(s, dataspec, datar, id_generator));
     126          38 :     total_domain_size = total_domain_size * domain_expressions.back().size();
     127          38 :     function_parameters.emplace_back(id_generator(), s);
     128             :   }
     129             : 
     130          26 :   if (total_domain_size * utilities::ceil_log2(codomain_expressions.size()) >= 32)  // If there are at least 2^32 functions, then enumerating them makes little sense.
     131             :   {
     132           0 :     return false;
     133             :   }
     134             : 
     135          26 :   if (total_domain_size * utilities::ceil_log2(codomain_expressions.size()) > 16)  // If there are more than 2^16 functions, provide a warning.
     136             :   {
     137           0 :     mCRL2log(log::warning) << "Generate " << codomain_expressions.size() << "^" << total_domain_size << " functions to enumerate sort " << sort << "\n";
     138             :   }
     139             : 
     140          26 :   function_parameter_list = variable_list(function_parameters.begin(), function_parameters.end());
     141             : 
     142          26 :   const std::size_t number_of_functions = utilities::power_size_t(codomain_expressions.size(), total_domain_size);
     143             : 
     144          26 :   if (number_of_functions == 1)
     145             :   {
     146           0 :     result.push_back(abstraction(lambda_binder(), function_parameter_list, codomain_expressions.front()));
     147             :   }
     148             :   else
     149             :   {
     150        1642 :     for (std::size_t i = 0; i < number_of_functions; ++i)
     151             :     {
     152        1616 :       std::size_t function_index = i; // function_index is changed in make_if_expression. A copy is therefore required.
     153        1616 :       result.push_back(abstraction(lambda_binder(), function_parameter_list, make_if_expression_(function_index, 0, domain_expressions, codomain_expressions, function_parameters)));
     154             :     }
     155             :   }
     156          26 :   return true;
     157          26 : }
     158             : 
     159             : template <typename Rewriter>
     160        2750 : bool is_enumerable(const data_specification& dataspec, 
     161             :                    const Rewriter& rewr, 
     162             :                    const sort_expression& sort, 
     163             :                    std::list<sort_expression>& parents)
     164             : {
     165        2750 :   assert(sort == normalize_sorts(sort,dataspec));
     166        2750 :   if (sort_bag::is_bag(sort) || sort_fbag::is_fbag(sort))
     167             :   {
     168           0 :     return false;
     169             :   }
     170        2750 :   else if (is_function_sort(sort))
     171             :   {
     172           1 :     const function_sort& func = atermpp::down_cast<function_sort>(sort);
     173           2 :     enumerator_identifier_generator id_gen;
     174           1 :     data_expression_vector expr_vec;
     175           1 :     variable_list var_list;
     176           2 :     return dataspec.is_certainly_finite(func) &&
     177           1 :       detail::compute_finite_function_sorts(func, id_gen, dataspec, rewr, expr_vec, var_list);
     178           1 :   }
     179        2749 :   else if (sort_set::is_set(sort) || sort_fset::is_fset(sort))
     180             :   {
     181           0 :     enumerator_identifier_generator id_gen;
     182           0 :     data_expression_vector expr_vec;
     183           0 :     mutable_indexed_substitution<> mut_sub;
     184           0 :     return dataspec.is_certainly_finite(atermpp::down_cast<container_sort>(sort).element_sort()) &&
     185           0 :       (!sort_fset::is_fset(sort) || detail::compute_finite_set_elements(atermpp::down_cast<container_sort>(sort), dataspec, rewr, mut_sub, expr_vec, id_gen));
     186           0 :   }
     187             :   else
     188             :   {
     189        2749 :     const function_symbol_vector& constructors = dataspec.constructors(sort, true);
     190        2749 :     if (constructors.empty())
     191             :     {
     192           4 :       return false;
     193             :     }
     194             :     else
     195             :     {
     196        2745 :       if (std::find(parents.begin(), parents.end(), sort) != parents.end())
     197             :       {
     198         369 :         return true;
     199             :       }
     200        2376 :       parents.push_back(sort);
     201        2376 :       bool result = std::all_of(constructors.begin(), constructors.end(), [&](const function_symbol& constructor)
     202             :         {
     203        5471 :           return !is_function_sort(constructor.sort()) ||
     204         724 :             std::all_of(atermpp::down_cast<function_sort>(constructor.sort()).domain().begin(), atermpp::down_cast<function_sort>(constructor.sort()).domain().end(),
     205        5840 :               [&](const sort_expression& arg_sort){ return is_enumerable(dataspec, rewr, arg_sort, parents); });
     206             :         });
     207        2376 :       parents.pop_back();
     208        2376 :       return result;
     209             :     }
     210             :   }
     211             : }
     212             : 
     213             : } // namespace detail
     214             : 
     215             : /// \brief Enumerator exception
     216             : struct enumerator_error: public mcrl2::runtime_error
     217             : {
     218           0 :   explicit enumerator_error(const std::string& message): mcrl2::runtime_error(message)
     219           0 :   {}
     220             : };
     221             : 
     222             : template <typename Rewriter>
     223        1657 : static bool is_enumerable(const data_specification& dataspec, const Rewriter& rewr, const sort_expression& sort)
     224             : {
     225        1657 :   std::list<sort_expression> parentstack;
     226        3314 :   return detail::is_enumerable(dataspec, rewr, sort, parentstack);
     227        1657 : }
     228             : 
     229             : /// \brief The default element for the todo list of the enumerator
     230             : template <typename Expression = data::data_expression>
     231             : class enumerator_list_element
     232             : {
     233             :   protected:
     234             :     data::variable_list v;
     235             :     Expression phi;
     236             : 
     237             :   public:
     238             :     typedef Expression expression_type;
     239             : 
     240             :     /// \brief Default constructor
     241        2804 :     enumerator_list_element() = default;
     242             : 
     243             :     /// \brief Constructs the element (v, phi)
     244        4623 :     enumerator_list_element(data::variable_list v_, const Expression& phi_)
     245        4623 :       : v(std::move(v_)), phi(phi_)
     246        4623 :     {}
     247             : 
     248             :     /// \brief Constructs the element (v, phi)
     249             :     enumerator_list_element(data::variable_list v_,
     250             :                             const Expression& phi_,
     251             :                             const enumerator_list_element&
     252             :                            )
     253             :       : v(std::move(v_)), phi(phi_)
     254             :     {}
     255             : 
     256             :     /// \brief Constructs the element (v, phi)
     257       26774 :     enumerator_list_element(data::variable_list v_,
     258             :                             const Expression& phi_,
     259             :                             const enumerator_list_element&,
     260             :                             const data::variable&,
     261             :                             const data::data_expression&
     262             :                            )
     263       26774 :       : v(std::move(v_)), phi(phi_)
     264       26774 :     {}
     265             : 
     266       20234 :     const data::variable_list& variables() const
     267             :     {
     268       20234 :       return v;
     269             :     }
     270             : 
     271        8465 :     const Expression& expression() const
     272             :     {
     273        8465 :       return phi;
     274             :     }
     275             : 
     276       19652 :     Expression& expression()
     277             :     {
     278       19652 :       return phi;
     279             :     }
     280             : 
     281       19928 :     bool is_solution() const
     282             :     {
     283       19928 :       return v.empty();
     284             :     }
     285             : 
     286             :     /// \brief Invalidates the element, by giving phi an undefined value
     287           1 :     void invalidate()
     288             :     {
     289           1 :       phi = data::undefined_data_expression();
     290           1 :     }
     291             : 
     292             :     /// \brief Returns true if the element is valid. If it becomes false, this is used to signal that
     293             :     /// the enumeration has been aborted.
     294         123 :     bool is_valid() const
     295             :     {
     296         123 :       return phi != data::undefined_data_expression();
     297             :     }
     298             :  
     299             :     /// \brief The following function is needed to mark the aterms in this class,
     300             :     ///        when elements of this class are used in an atermpp standard container.
     301             :     ///        When garbage collection of aterms is taking place this function is
     302             :     ///        called for all elements of this class in the atermpp container. 
     303         218 :     void mark(atermpp::term_mark_stack& todo) const
     304             :     {
     305         218 :       mark_term(v,todo);
     306         218 :       mark_term(phi,todo);
     307         218 :     }
     308             : 
     309             :     /// \brief Set the variables and the condition explicitly.
     310             :     /// \param v_ The variable list.
     311             :     /// \param phi_ The condition.
     312        2738 :     void set(const data::variable_list& v_, const Expression& phi_)
     313             :     {
     314        2738 :       v=v_;
     315        2738 :       phi=phi_;
     316        2738 :     }
     317             : 
     318             :     /// \brief Set the variables and the condition explicitly.
     319             :     /// \param v_ The variable list.
     320             :     /// \param phi_ The condition.
     321             :     /// \details Other arguments than the first two are ignored. 
     322           0 :     void set(const data::variable_list& v_, const Expression& phi_,const enumerator_list_element&)
     323             :     {
     324           0 :       v=v_;
     325           0 :       phi=phi_;
     326           0 :     }
     327             : 
     328             :     /// \brief Set the variable ands and the expression explicitly
     329             :     ///        as the element (v, phi, e.sigma[v := x]).
     330             :     /// \param v_ The variable list.
     331             :     /// \param phi_ The condition.
     332             :     /// \details Other arguments than the first two are ignored. 
     333        1989 :     void set(const data::variable_list& v_,
     334             :              const Expression& phi_,
     335             :              const enumerator_list_element&,
     336             :              const data::variable&,
     337             :              const data::data_expression&)
     338             :     {
     339        1989 :       v=v_;
     340        1989 :       phi=phi_;
     341        1989 :     }
     342             : 
     343             : };
     344             : 
     345             : /// \brief An element for the todo list of the enumerator that collects the substitution
     346             : /// corresponding to the expression phi
     347             : template <typename Expression = data::data_expression>
     348             : class enumerator_list_element_with_substitution: public enumerator_list_element<Expression>
     349             : {
     350             :   protected:
     351             :     data::variable_list m_variables;
     352             :     data::data_expression_list m_expressions;
     353             : 
     354             :   public:
     355             :     typedef Expression expression_type;
     356             : 
     357             :     /// \brief Default constructor
     358        1147 :     enumerator_list_element_with_substitution() = default;
     359             : 
     360             :     /// \brief Constructs the element (v, phi, [])
     361        1147 :     enumerator_list_element_with_substitution(const data::variable_list& v, const Expression& phi)
     362        1147 :       : enumerator_list_element<Expression>(v, phi)
     363        1147 :     {}
     364             : 
     365             :     /// \brief Constructs the element (v, phi, e.sigma[v := x])
     366             :     enumerator_list_element_with_substitution(const data::variable_list& v,
     367             :                             const Expression& phi,
     368             :                             const enumerator_list_element_with_substitution<Expression>& elem
     369             :                            )
     370             :       : enumerator_list_element<Expression>(v, phi),
     371             :         m_variables(elem.m_variables),
     372             :         m_expressions(elem.m_expressions)
     373             :     {
     374             :     }
     375             : 
     376             :     /// \brief Constructs the element (v, phi, e.sigma[v := x])
     377        1828 :     enumerator_list_element_with_substitution(
     378             :                             const data::variable_list& v,
     379             :                             const Expression& phi,
     380             :                             const enumerator_list_element_with_substitution<Expression>& elem,
     381             :                             const data::variable& d,
     382             :                             const data::data_expression& e
     383             :                            )
     384             :       : enumerator_list_element<Expression>(v, phi),
     385        1828 :         m_variables(elem.m_variables),
     386        1828 :         m_expressions(elem.m_expressions)
     387             :     {
     388        1828 :       m_variables.push_front(d);
     389        1828 :       m_expressions.push_front(e);
     390        1828 :     }
     391             : 
     392             :     /// \brief Adds the assignments that correspond with this element to the substitution result.
     393             :     template <typename VariableList, typename MutableSubstitution, typename Rewriter>
     394        2967 :     void add_assignments(const VariableList& v, MutableSubstitution& result, const Rewriter& rewriter) const
     395             :     {
     396        5934 :       data::enumerator_substitution sigma(m_variables, m_expressions);
     397        2967 :       sigma.revert();
     398        8922 :       for (const data::variable& v_i: v)
     399             :       {
     400        2988 :         result[v_i] = rewriter(sigma(v_i));
     401             :       }
     402        2967 :     }
     403             : 
     404             :     /// \brief Removes the assignments corresponding with this element from the substitution result.
     405             :     template <typename VariableList, typename MutableSubstitution>
     406             :     void remove_assignments(const VariableList& v, MutableSubstitution& result) const
     407             :     {
     408             :       for (const data::variable& v_i: v)
     409             :       {
     410             :         result[v_i] = v_i;
     411             :       }
     412             :     }
     413             : 
     414             :     /// \brief Returns the right hand sides corresponding to the variables v
     415             :     /// \pre variables in v must be contained in variables()
     416             :     template <typename VariableList, typename Rewriter>
     417           0 :     data::data_expression_list assign_expressions(const VariableList& v, const Rewriter& rewriter) const
     418             :     {
     419           0 :       data::enumerator_substitution sigma(m_variables, m_expressions);
     420           0 :       sigma.revert();
     421           0 :       return data_expression_list(v.begin(), v.end(), [&](const data::variable& v_i) { return rewriter(sigma(v_i)); });
     422           0 :     }
     423             : 
     424             :     data::enumerator_substitution sigma() const
     425             :     {
     426             :       return data::enumerator_substitution(m_variables, m_expressions);
     427             :     }
     428             :  
     429             :     /// \brief The following function is needed to mark the aterms in this class,
     430             :     ///        when elements of this class are used in an atermpp standard container.
     431             :     ///        When garbage collection of aterms is taking place this function is
     432             :     ///        called for all elements of this class in the atermpp container. 
     433           0 :     void mark(std::stack<std::reference_wrapper<atermpp::detail::_aterm>>& todo) const
     434             :     {
     435           0 :       mark_term(*atermpp::detail::address(m_variables), todo);
     436           0 :       mark_term(*atermpp::detail::address(m_expressions), todo);
     437           0 :       static_cast<enumerator_list_element<Expression>>(*this).mark(todo);
     438           0 :     }
     439             :     
     440             :     /// \brief Set the variable ands and the expression explicitly
     441             :     ///        as the element (v, phi, e.sigma[v := x]).
     442        2719 :     void set(const data::variable_list& v,
     443             :              const Expression& phi,
     444             :              const enumerator_list_element_with_substitution<Expression>& elem,
     445             :              const data::variable& d,
     446             :              const data::data_expression& e)
     447             :     {
     448        2719 :       enumerator_list_element<Expression>::set(v, phi);
     449        2719 :       m_variables=elem.m_variables;
     450        2719 :       m_expressions=elem.m_expressions;
     451        2719 :       m_variables.push_front(d);
     452        2719 :       m_expressions.push_front(e);
     453        2719 :     }
     454             : 
     455             :     /// \brief Set the variable ands and the expression explicitly
     456             :     ///        as the element (v, phi, e.sigma[v := x]).
     457          19 :     void set(const data::variable_list& v,
     458             :              const Expression& phi,
     459             :              const enumerator_list_element_with_substitution<Expression>& elem)
     460             :     {
     461          19 :       enumerator_list_element<Expression>::set(v, phi);
     462          19 :       m_variables=elem.m_variables;
     463          19 :       m_expressions=elem.m_expressions;
     464          19 :     }
     465             : };
     466             : 
     467             : template <typename Expression>
     468             : std::ostream& operator<<(std::ostream& out, const enumerator_list_element<Expression>& p)
     469             : {
     470             :   out << "{ [";
     471             :   const auto& variables = p.variables();
     472             :   for (auto i = variables.begin(); i != variables.end(); ++i)
     473             :   {
     474             :     if (i != variables.begin())
     475             :     {
     476             :       out << ", ";
     477             :     }
     478             :     out << *i << ": " << i->sort();
     479             :   }
     480             :   return out << "], " << p.expression() << " }";
     481             : }
     482             : 
     483             : template <typename Expression>
     484             : std::ostream& operator<<(std::ostream& out, const enumerator_list_element_with_substitution<Expression>& p)
     485             : {
     486             :   out << "{ [";
     487             :   const auto& variables = p.variables();
     488             :   for (auto i = variables.begin(); i != variables.end(); ++i)
     489             :   {
     490             :     if (i != variables.begin())
     491             :     {
     492             :       out << ", ";
     493             :     }
     494             :     out << *i << ": " << i->sort();
     495             :   }
     496             :   return out << "], " << p.expression() << ", " << p.sigma() << " }";
     497             : }
     498             : 
     499             : /// \brief Contains the enumerator queue.
     500             : template <typename EnumeratorListElement>
     501             : class enumerator_queue
     502             : {
     503             :   protected:
     504             :     atermpp::deque<EnumeratorListElement> P;
     505             :     EnumeratorListElement m_enumerator_element_cache;  // This element is used to temporarily store
     506             :                                                        // information, which is more efficient than
     507             :                                                        // declaring such a variable on the stack
     508             :                                                        // as its protection using a protection set
     509             :                                                        // is expensive. 
     510             : 
     511             :   public:
     512             :     // The following variables are used for local store, to prevent the
     513             :     // variables to be inserted and removed continuously in a protection set. 
     514             :     typename EnumeratorListElement::expression_type scratch_expression;
     515             :     data_expression scratch_data_expression;
     516             :     variable_list scratch_variable_list;
     517             : 
     518             :     typedef EnumeratorListElement value_type;
     519             :     typedef typename atermpp::deque<EnumeratorListElement>::size_type size_type;
     520             : 
     521             :     /// \brief Default constructor
     522        1031 :     enumerator_queue() = default;
     523             : 
     524             :     /// \brief Initializes the enumerator queue with the given value
     525        1773 :     explicit enumerator_queue(const EnumeratorListElement& value)
     526        3546 :             : P({value})
     527        1773 :     { }
     528             : 
     529       28386 :     void push_back(const EnumeratorListElement& x)
     530             :     {
     531             : #ifdef MCRL2_LOG_ENUMERATOR
     532             :       std::cout << "push_back " << x << std::endl;
     533             : #endif
     534       28386 :       P.push_back(x);
     535       28386 :     }
     536             : 
     537             :     template <class... Args>
     538        1238 :     void emplace_back(Args&&... args)
     539             :     {
     540             : #ifdef MCRL2_LOG_ENUMERATOR
     541             :       std::cout << "emplace_back " << EnumeratorListElement(args...) << std::endl;
     542             : #endif
     543        1238 :       P.emplace_back(std::forward<Args>(args)...);
     544        1238 :     }
     545             : 
     546       48646 :     bool empty() const
     547             :     {
     548       48646 :       return P.empty();
     549             :     }
     550             : 
     551           2 :     void clear()
     552             :     {
     553           2 :       P.clear();
     554           2 :     }
     555             : 
     556        4970 :     typename atermpp::deque<EnumeratorListElement>::size_type size() const
     557             :     {
     558        4970 :       return P.size();
     559             :     }
     560             : 
     561             :     const EnumeratorListElement& front() const
     562             :     {
     563             :       return P.front();
     564             :     }
     565             : 
     566       42869 :     EnumeratorListElement& front()
     567             :     {
     568       42869 :       return P.front();
     569             :     }
     570             : 
     571             :     const EnumeratorListElement& back() const
     572             :     {
     573             :       return P.back();
     574             :     }
     575             : 
     576             :     EnumeratorListElement& back()
     577             :     {
     578             :       return P.back();
     579             :     }
     580             : 
     581       21820 :     void pop_front()
     582             :     {
     583       21820 :       P.pop_front();
     584       21820 :     }
     585             : 
     586             :     void pop_back()
     587             :     {
     588             :       P.pop_back();
     589             :     }
     590             : 
     591             :     template <class... Args>
     592        4727 :     const EnumeratorListElement& enumerator_element_cache(const Args&... args)
     593             :     {
     594        4727 :       m_enumerator_element_cache.set(args...);
     595        4727 :       return m_enumerator_element_cache;
     596             :     }
     597             : };
     598             : 
     599             : /// \brief An enumerator algorithm that generates solutions of a condition.
     600             : template <typename Rewriter = data::rewriter, typename DataRewriter = data::rewriter>
     601             : class enumerator_algorithm
     602             : {
     603             :   protected:
     604             :     // A rewriter
     605             :     const Rewriter& R;
     606             : 
     607             :     /// \brief A data specification.
     608             :     const data::data_specification& dataspec;
     609             : 
     610             :     // Needed for enumerate_expressions
     611             :     const DataRewriter& r;
     612             : 
     613             :     // A name generator
     614             :     enumerator_identifier_generator& id_generator;
     615             : 
     616             :     /// \brief max_count The enumeration is aborted after max_count iterations
     617             :     std::size_t m_max_count;
     618             : 
     619             :     /// \brief If true, solutions with a non-empty list of variables may be reported.
     620             :     bool m_accept_solutions_with_variables;
     621             : 
     622             : #ifdef MCRL2_ENUMERATOR_COUNT_REWRITE_CALLS
     623             :     mutable std::size_t rewrite_calls = 0;
     624             : #endif
     625             : 
     626             :     std::string print(const data::variable& x) const
     627             :     {
     628             :       std::ostringstream out;
     629             :       out << x << ": " << x.sort();
     630             :       return out.str();
     631             :     }
     632             : 
     633             :     template <typename Expression, typename MutableSubstitution>
     634             :     inline
     635       36340 :     Expression rewrite(const Expression& phi, MutableSubstitution& sigma) const
     636             :     {
     637             : #ifdef MCRL2_ENUMERATOR_COUNT_REWRITE_CALLS
     638             :       rewrite_calls++;
     639             : #endif
     640       36340 :       return const_cast<Rewriter&>(R)(phi, sigma);
     641             :     }
     642             : 
     643             :     template <typename Expression, typename MutableSubstitution>
     644             :     inline
     645        6501 :     void rewrite(Expression& result, const Expression& phi, MutableSubstitution& sigma) const
     646             :     {
     647             : #ifdef MCRL2_ENUMERATOR_COUNT_REWRITE_CALLS
     648             :       rewrite_calls++;
     649             : #endif
     650        6501 :       const_cast<Rewriter&>(R)(result, phi, sigma);
     651        6501 :     }
     652             : 
     653             :   public:
     654        7111 :     enumerator_algorithm(const Rewriter& R_,
     655             :                          const data::data_specification& dataspec_,
     656             :                          const DataRewriter& datar_,
     657             :                          enumerator_identifier_generator& id_generator_,
     658             :                          bool accept_solutions_with_variables,
     659             :                          std::size_t max_count = (std::numeric_limits<std::size_t>::max)()
     660             :     )
     661        7111 :       : R(R_),
     662        7111 :         dataspec(dataspec_),
     663        7111 :         r(datar_),
     664        7111 :         id_generator(id_generator_),
     665        7111 :         m_max_count(max_count),
     666        7111 :         m_accept_solutions_with_variables(accept_solutions_with_variables)
     667        7111 :     {}
     668             : 
     669             :     enumerator_algorithm(const enumerator_algorithm<Rewriter, DataRewriter>&) = delete;
     670             : 
     671        7111 :     ~enumerator_algorithm()
     672             :     {
     673             : #ifdef MCRL2_ENUMERATOR_COUNT_REWRITE_CALLS
     674             :       std::cout << "number of enumerator rewrite calls: " << rewrite_calls << std::endl;
     675             : #endif
     676        7111 :     }
     677             : 
     678             :     void reset_id_generator()
     679             :     {
     680             :       id_generator.clear();
     681             :     }
     682             :  
     683             :     template <typename T>
     684             :     struct always_false
     685             :     {
     686        3460 :       bool operator()(const T&) { return false; }
     687             :     };
     688             : 
     689             :     /// \brief Enumerates the front element of the todo list P.
     690             :     /// The enumeration is interrupted when report_solution returns true for the reported solution.
     691             :     /// \param P The todo list of the algorithm.
     692             :     /// \param sigma A mutable substitution that is applied by the rewriter.
     693             :     /// \param reject Elements p for which reject(p) is true are discarded.
     694             :     /// \param accept Elements p for which accept(p) is true are reported as a solution, even if the list of variables of the enumerator element is non-empty.
     695             :     /// \param report_solution A callback function that is called whenever a solution is found.
     696             :     /// It takes an enumerator element as argument.
     697             :     /// If report_solution returns true, the enumeration is interrupted.
     698             :     /// N.B. If the enumeration is resumed after an interruption, the element p that
     699             :     /// was interrupted will be enumerated again.
     700             :     /// \pre !P.empty()
     701             :     /// \return If the return value is true, enumeration will be interrupted
     702             :     template <typename EnumeratorListElement,
     703             :               typename MutableSubstitution,
     704             :               typename ReportSolution,
     705             :               typename Reject = always_false<typename EnumeratorListElement::expression_type>,
     706             :               typename Accept = always_false<typename EnumeratorListElement::expression_type>
     707             :              >
     708        2406 :     bool enumerate_front(enumerator_queue<EnumeratorListElement>& P,
     709             :                          MutableSubstitution& sigma,
     710             :                          ReportSolution report_solution,
     711             :                          Reject reject = Reject(),
     712             :                          Accept accept = Accept()
     713             :                         ) const
     714             :     {
     715        2406 :       assert(!P.empty());
     716        2406 :       const EnumeratorListElement& p = P.front();
     717             : 
     718       24751 :       auto add_element = [&](const data::variable_list& variables,
     719             :                              const typename EnumeratorListElement::expression_type& phi,
     720             :                              const data::variable& v,
     721             :                              const data::data_expression& e
     722             :                             ) -> bool
     723             :                             {
     724        6304 :                               rewrite(P.scratch_expression, phi, sigma);
     725        6304 :                               if (reject(P.scratch_expression))
     726             :                               {
     727        1490 :                                 return false;
     728             :                               }
     729        4814 :                               if ((accept(P.scratch_expression) && m_accept_solutions_with_variables) || variables.empty())
     730             :                               {
     731             :                                 // EnumeratorListElement q(variables, phi1, p, v, e);
     732             :                                 // return report_solution(q);
     733        4704 :                                 return report_solution(P.enumerator_element_cache(variables, P.scratch_expression, p, v, e));
     734             :                               }
     735         110 :                               P.emplace_back(variables, P.scratch_expression, p, v, e);
     736         110 :                               return false;
     737             :                             };
     738             : 
     739        3112 :       auto add_element_with_variables = [&](const data::variable_list& variables,
     740             :                                             const data::variable_list& added_variables,
     741             :                                             const typename EnumeratorListElement::expression_type& phi,
     742             :                                             const data::variable& v,
     743             :                                             const data::data_expression& e
     744             :                                            ) -> bool
     745             :                                            {
     746         178 :                                              rewrite(P.scratch_expression, phi, sigma);
     747         178 :                                              if (reject(P.scratch_expression))
     748             :                                              {
     749          64 :                                                return false;
     750             :                                              }
     751         114 :                                              bool added_variables_empty = added_variables.empty() || (P.scratch_expression == phi && m_accept_solutions_with_variables);
     752         114 :                                              if ((accept(P.scratch_expression) && m_accept_solutions_with_variables) || (variables.empty() && added_variables_empty))
     753             :                                              {
     754             :                                                // EnumeratorListElement q(variables + added_variables, phi1, p, v, e);
     755           4 :                                                return report_solution(P.enumerator_element_cache(variables + added_variables, P.scratch_expression, p, v, e));
     756             :                                              }
     757         110 :                                              if (added_variables_empty)
     758             :                                              {
     759           0 :                                                P.emplace_back(variables, P.scratch_expression, p, v, e);
     760             :                                              }
     761             :                                              else
     762             :                                              {
     763         110 :                                                P.emplace_back(variables + added_variables, P.scratch_expression, p, v, e);
     764             :                                              }
     765         110 :                                              return false;
     766             :                                            };
     767             : 
     768        2406 :       const data::variable_list& v = p.variables();
     769        2406 :       const auto& phi = p.expression();
     770             : 
     771        2406 :       if (v.empty())
     772             :       {
     773          19 :         rewrite(P.scratch_expression, phi, sigma);
     774          19 :         if (reject(P.scratch_expression))
     775             :         {
     776           0 :           return false;
     777             :         }
     778             :         // EnumeratorListElement q(v, phi1, p);
     779          19 :         return report_solution(P.enumerator_element_cache(v, P.scratch_expression, p));
     780             :       }
     781             : 
     782        2387 :       const auto& v1 = v.front();
     783        2387 :       const auto& v_tail = v.tail();
     784        2387 :       const auto& v1_sort = v1.sort();
     785             : 
     786        2387 :       if (reject(phi))
     787             :       {
     788             :         // skip
     789             :       }
     790        2387 :       else if (data::is_function_sort(v1_sort))
     791             :       {
     792          24 :         const function_sort& function = atermpp::down_cast<function_sort>(v1_sort);
     793          24 :         if (dataspec.is_certainly_finite(function))
     794             :         {
     795          24 :           data_expression_vector function_sorts;
     796          24 :           variable_list function_parameter_list;
     797          24 :           bool result = detail::compute_finite_function_sorts(function, id_generator, dataspec, r, function_sorts, function_parameter_list);
     798          24 :           if (!result)
     799             :           {
     800           0 :             throw mcrl2::runtime_error("Sort " + data::pp(v1_sort) + " has too many elements to enumerate.");
     801             :           }
     802             : 
     803        1632 :           for (const data_expression& f: function_sorts)
     804             :           {
     805        1608 :             sigma[v1] = f;
     806        1608 :             if (add_element(v_tail, phi, v1, f))
     807             :             {
     808           0 :               sigma[v1] = v1;
     809           0 :               return true;
     810             :             }
     811             :           }
     812          24 :         }
     813             :         else
     814             :         {
     815           0 :           throw mcrl2::runtime_error("Cannot enumerate elements of function sort " + data::pp(v1_sort) + ".");
     816             :         }
     817             :       }
     818        2363 :       else if (sort_set::is_set(v1_sort))
     819             :       {
     820          12 :         const sort_expression& element_sort = atermpp::down_cast<container_sort>(v1_sort).element_sort();
     821          12 :         if (dataspec.is_certainly_finite(element_sort))
     822             :         {
     823          48 :           const data_expression lambda_term = abstraction(lambda_binder(), { variable(id_generator(), element_sort) }, sort_bool::false_());
     824          24 :           const variable fset_variable(id_generator(), sort_fset::fset(element_sort));
     825          12 :           data_expression e = sort_set::constructor(element_sort, lambda_term, fset_variable);
     826          12 :           sigma[v1] = e;
     827          24 :           if (add_element_with_variables(v_tail, { fset_variable }, phi, v1, e))
     828             :           {
     829           0 :             sigma[v1] = v1;
     830           0 :             return true;
     831             :           }
     832          12 :         }
     833             :         else
     834             :         {
     835           0 :           throw mcrl2::runtime_error("Cannot enumerate elements of set sort " + data::pp(v1_sort) + ".");
     836             :         }
     837             :       }
     838        2351 :       else if (sort_fset::is_fset(v1_sort))
     839             :       {
     840          36 :         const auto& fset = atermpp::down_cast<container_sort>(v1_sort);
     841          36 :         if (dataspec.is_certainly_finite(fset.element_sort()))
     842             :         {
     843          36 :           data_expression_vector set_elements;
     844          36 :           bool result = detail::compute_finite_set_elements(fset, dataspec, r, sigma, set_elements, id_generator);
     845          36 :           if (!result)
     846             :           {
     847           0 :             throw mcrl2::runtime_error("Finite set sort " + data::pp(v1_sort) + " has too many elements to enumerate.");
     848             :           }
     849             : 
     850         324 :           for (const data_expression& e: set_elements)
     851             :           {
     852         288 :             sigma[v1] = e;
     853         288 :             if (add_element(v_tail, phi, v1, e))
     854             :             {
     855           0 :               sigma[v1] = v1;
     856           0 :               return true;
     857             :             }
     858             :           }
     859          36 :         }
     860             :         else
     861             :         {
     862           0 :           throw mcrl2::runtime_error("Cannot enumerate elements of finite set sort " + data::pp(v1_sort) + ".");
     863             :         }
     864             :       }
     865        2315 :       else if (sort_bag::is_bag(v1_sort))
     866             :       {
     867           0 :         throw mcrl2::runtime_error("Cannot enumerate elements of bag sort " + data::pp(v1_sort) + ".");
     868             :       }
     869        2315 :       else if (sort_fbag::is_fbag(v1_sort))
     870             :       {
     871           0 :         throw mcrl2::runtime_error("Cannot enumerate elements of finite bag sort " + data::pp(v1_sort) + ".");
     872             :       }
     873             :       else
     874             :       {
     875        2315 :         const function_symbol_vector& C = dataspec.constructors(v1_sort);
     876        2315 :         if (!C.empty())
     877             :         {
     878        6775 :           for (const function_symbol& c: C)
     879             :           {
     880        4574 :             if (data::is_function_sort(c.sort()))
     881             :             {
     882         166 :               const sort_expression_list& domain = atermpp::down_cast<data::function_sort>(c.sort()).domain();
     883         166 :               atermpp::make_term_list(P.scratch_variable_list, 
     884             :                                        domain.begin(), 
     885             :                                        domain.end(), 
     886         300 :                                        [&](const data::sort_expression& s) { return data::variable(id_generator(), s); });
     887             :               // data_expression cy = r(application(c, y.begin(), y.end()));
     888             :               // below sigma is not used, but it prevents generating a dummy sigma. 
     889         166 :               r(P.scratch_data_expression, application(c, P.scratch_variable_list.begin(), P.scratch_variable_list.end()),sigma); 
     890         166 :               sigma[v1] = P.scratch_data_expression;
     891         166 :               if (add_element_with_variables(v_tail, P.scratch_variable_list, phi, v1, P.scratch_data_expression))
     892             :               {
     893           4 :                 sigma[v1] = v1;
     894         113 :                 return true;
     895             :               }
     896             :             }
     897             :             else
     898             :             {
     899             :               // const data_expression e1 = r(c);
     900        4408 :               r(P.scratch_data_expression,c,sigma); // sigma is not used, but in this way no dummy sigma needs to be created. 
     901        4408 :               sigma[v1] = P.scratch_data_expression;
     902        4408 :               if (add_element(v_tail, phi, v1, P.scratch_data_expression))
     903             :               {
     904         109 :                 sigma[v1] = v1;
     905         109 :                 return true;
     906             :               }
     907             :             }
     908             :           }
     909             :         }
     910             :         else
     911             :         {
     912           1 :           throw mcrl2::runtime_error("Cannot enumerate elements of sort " + data::pp(v1_sort) + " without constructors.");
     913             :         }
     914             :       }
     915        2273 :       sigma[v1] = v1;
     916        2273 :       return false;
     917             :     }
     918             : 
     919             :     /// \brief Enumerates until P is empty. Solutions are reported using the callback function report_solution.
     920             :     /// The enumeration is interrupted when report_solution returns true for the reported solution.
     921             :     /// \param P The todo list of the algorithm.
     922             :     /// \param sigma A substitution.
     923             :     /// \param reject Elements p for which reject(p) is true are discarded.
     924             :     /// \param accept Elements p for which accept(p) is true are reported as a solution, even if the list of variables of the enumerator element is non-empty.
     925             :     /// \param report_solution A callback function that is called whenever a solution is found.
     926             :     /// It takes an enumerator element as argument.
     927             :     /// If report_solution returns true, the enumeration is interrupted.
     928             :     /// N.B. If the enumeration is resumed after an interruption, the element p that
     929             :     /// was interrupted will be enumerated again.
     930             :     /// \return The number of elements that have been processed
     931             :     template <typename EnumeratorListElement,
     932             :               typename MutableSubstitution,
     933             :               typename ReportSolution,
     934             :               typename Reject = always_false<typename EnumeratorListElement::expression_type>,
     935             :               typename Accept = always_false<typename EnumeratorListElement::expression_type>
     936             :              >
     937        2187 :     std::size_t enumerate_all(enumerator_queue<EnumeratorListElement>& P,
     938             :                               MutableSubstitution& sigma,
     939             :                               ReportSolution report_solution,
     940             :                               Reject reject = Reject(),
     941             :                               Accept accept = Accept()
     942             :     ) const
     943             :     {
     944        2187 :       std::size_t count = 0;
     945        4479 :       while (!P.empty())
     946             :       {
     947        2406 :         if (count++ >= m_max_count)
     948             :         {
     949           0 :           break;
     950             :         }
     951        2406 :         if (enumerate_front(P, sigma, report_solution, reject, accept))
     952             :         {
     953         113 :           break;
     954             :         }
     955        2292 :         P.pop_front();
     956             :       }
     957        2186 :       return count;
     958             :     }
     959             : 
     960             :     /// \brief Enumerates the element p. Solutions are reported using the callback function report_solution.
     961             :     /// The enumeration is interrupted when report_solution returns true for the reported solution.
     962             :     /// \param p An enumerator element, i.e. an expression with a list of variables.
     963             :     /// \param sigma A substitution.
     964             :     /// \param reject Elements p for which reject(p) is true are discarded.
     965             :     /// \param accept Elements p for which accept(p) is true are reported as a solution, even if the list of variables of the enumerator element is non-empty.
     966             :     /// \param report_solution A callback function that is called whenever a solution is found.
     967             :     /// It takes an enumerator element as argument.
     968             :     /// If report_solution returns true, the enumeration is interrupted.
     969             :     /// N.B. If the enumeration is resumed after an interruption, the element p that
     970             :     /// was interrupted will be enumerated again.
     971             :     /// \return The number of elements that have been processed
     972             :     template <typename EnumeratorListElement,
     973             :               typename MutableSubstitution,
     974             :               typename ReportSolution,
     975             :               typename Reject = always_false<typename EnumeratorListElement::expression_type>,
     976             :               typename Accept = always_false<typename EnumeratorListElement::expression_type>
     977             :     >
     978        1169 :     std::size_t enumerate(const EnumeratorListElement& p,
     979             :                           MutableSubstitution& sigma,
     980             :                           ReportSolution report_solution,
     981             :                           Reject reject = Reject(),
     982             :                           Accept accept = Accept()
     983             :     ) const
     984             :     {
     985        1169 :       enumerator_queue<EnumeratorListElement> P(p);
     986        2337 :       return enumerate_all(P, sigma, report_solution, reject, accept);
     987        1169 :     }
     988             : 
     989             :     /// \brief Enumerates the variables v for condition c. Solutions are reported using the callback function report_solution.
     990             :     /// The enumeration is interrupted when report_solution returns true for the reported solution.
     991             :     /// \param p An enumerator element, i.e. an expression with a list of variables.
     992             :     /// \param sigma A substitution.
     993             :     /// \param reject Elements p for which reject(p) is true are discarded.
     994             :     /// \param accept Elements p for which accept(p) is true are reported as a solution, even if the list of variables of the enumerator element is non-empty.
     995             :     /// \param report_solution A callback function that is called whenever a solution is found.
     996             :     /// It takes an enumerator element as argument.
     997             :     /// If report_solution returns true, the enumeration is interrupted.
     998             :     /// N.B. If the enumeration is resumed after an interruption, the element p that
     999             :     /// was interrupted will be enumerated again.
    1000             :     /// \return The number of elements that have been processed
    1001             :     template <typename EnumeratorListElement,
    1002             :               typename MutableSubstitution,
    1003             :               typename ReportSolution,
    1004             :               typename Reject = always_false<typename EnumeratorListElement::expression_type>,
    1005             :               typename Accept = always_false<typename EnumeratorListElement::expression_type>
    1006             :     >
    1007        1018 :     std::size_t enumerate(const variable_list& vars,
    1008             :                           const typename EnumeratorListElement::expression_type& cond,
    1009             :                           MutableSubstitution& sigma,
    1010             :                           ReportSolution report_solution,
    1011             :                           Reject reject = Reject(),
    1012             :                           Accept accept = Accept()
    1013             :     ) const
    1014             :     {
    1015        1018 :       enumerator_queue<EnumeratorListElement> P;
    1016        1018 :       P.emplace_back(vars, cond);
    1017        2036 :       return enumerate_all(P, sigma, report_solution, reject, accept);
    1018        1018 :     }
    1019             : 
    1020        1849 :     std::size_t max_count() const
    1021             :     {
    1022        1849 :       return m_max_count;
    1023             :     }
    1024             : };
    1025             : 
    1026             : /// \brief Returns a vector with all expressions of sort s.
    1027             : /// \param s A sort expression.
    1028             : /// \param dataspec The data specification defining the terms of sort \a s.
    1029             : /// \param rewr A rewriter to be used to simplify terms and conditions.
    1030             : /// \param id_generator An identifier generator used to generate new names for variables.
    1031             : /// \details It is assumed that the sort s has only a finite number of elements.
    1032             : template <class Rewriter>
    1033         103 : data_expression_vector enumerate_expressions(const sort_expression& s,
    1034             :                                              const data_specification& dataspec,
    1035             :                                              const Rewriter& rewr,
    1036             :                                              enumerator_identifier_generator& id_generator)
    1037             : {
    1038             :   typedef typename Rewriter::term_type term_type;
    1039             :   typedef enumerator_list_element_with_substitution<term_type> enumerator_element;
    1040         103 :   assert(dataspec.is_certainly_finite(s));
    1041             : 
    1042         103 :   bool accept_solutions_with_variables = false;
    1043         103 :   enumerator_algorithm<Rewriter, Rewriter> E(rewr, dataspec, rewr, id_generator, accept_solutions_with_variables);
    1044         103 :   data_expression_vector result;
    1045         103 :   mutable_indexed_substitution<> sigma;
    1046         206 :   const variable v("@var@", s);
    1047         206 :   const variable_list v_list{ v };
    1048         206 :   E.template enumerate<enumerator_element>(
    1049             :               v_list, 
    1050         103 :               sort_bool::true_(),
    1051             :               sigma,
    1052         464 :               [&](const enumerator_element& p)
    1053             :               {
    1054         232 :                 p.add_assignments(v_list, sigma, rewr);
    1055         232 :                 result.push_back(sigma(v));
    1056         232 :                 return false;
    1057             :               }
    1058             :   );
    1059         206 :   return result;
    1060         103 : }
    1061             : 
    1062             : /// \brief Returns a vector with all expressions of sort s.
    1063             : /// \param s A sort expression.
    1064             : /// \param dataspec The data specification defining the terms of sort \a s.
    1065             : /// \param rewr A rewriter to be used to simplify terms and conditions.
    1066             : /// \details It is assumed that the sort s has only a finite number of elements.
    1067             : template <class Rewriter>
    1068           2 : data_expression_vector enumerate_expressions(const sort_expression& s,
    1069             :                                              const data_specification& dataspec,
    1070             :                                              const Rewriter& rewr)
    1071             : {
    1072           4 :   enumerator_identifier_generator id_generator;
    1073           4 :   return enumerate_expressions(s, dataspec, rewr, id_generator);
    1074           2 : }
    1075             : 
    1076             : } // namespace data
    1077             : 
    1078             : } // namespace mcrl2
    1079             : 
    1080             : #endif // MCRL2_DATA_ENUMERATOR_H

Generated by: LCOV version 1.14