LCOV - code coverage report
Current view: top level - lps/include/mcrl2/lps - lpsparunfoldlib.h (source / functions) Hit Total Coverage
Test: mcrl2_coverage.info.cleaned Lines: 60 238 25.2 %
Date: 2024-04-19 03:43:27 Functions: 13 36 36.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Author(s): Frank Stappers, Thomas Neele
       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 lpsparunfold/lpsparunfoldlib.h
      10             : /// \brief Unfold process parameters in mCRL2 process specifications.
      11             : 
      12             : #ifndef MCRL2_LPS_LPSPARUNFOLDLIB_H
      13             : //Fileinfo
      14             : #define MCRL2_LPS_LPSPARUNFOLDLIB_H
      15             : 
      16             : #include "mcrl2/data/consistency.h"
      17             : #include "mcrl2/data/representative_generator.h"
      18             : #include "mcrl2/data/unfold_pattern_matching.h"
      19             : #include "mcrl2/lps/detail/lps_algorithm.h"
      20             : #include "mcrl2/lps/replace_capture_avoiding.h"
      21             : 
      22             : namespace mcrl2::lps
      23             : {
      24             : 
      25             : /// \brief Element in the cache that keeps track of the information for a single
      26             : ///        unfolded sort, say s.
      27             : struct unfold_cache_element
      28             : {
      29             :   mcrl2::data::function_symbol_vector affected_constructors; // constructors for s
      30             :   mcrl2::data::basic_sort fresh_basic_sort; // new sort introduced to represent s
      31             :   mcrl2::data::function_symbol_vector new_constructors; // constructors for fresh_basic_sort
      32             :   mcrl2::core::identifier_string case_function_name; // name of the case function introduced to deal with sort s
      33             :   // case functions for sort s. Note this may be more due to, e.g., adding distribution rules.
      34             :   // invariant: case_functions.front() is the case function
      35             :   // C: fresh_basic_sort # s # ... # s -> s
      36             :   std::map<mcrl2::data::sort_expression, mcrl2::data::function_symbol> case_functions;
      37             :   mcrl2::data::function_symbol determine_function;  // Det function for s
      38             :   mcrl2::data::function_symbol_vector projection_functions; // pi functions for s
      39             : };
      40             : 
      41             : namespace detail
      42             : {
      43             :   /// \brief Fresh variable generator for the arguments of a function symbol.
      44             :   ///
      45             :   /// Intended for use to generate variables in the left hand side of a data equation.
      46             :   /// Therefore, reuse of fresh variables is allowed in multiple equations.
      47             :   /// This generator reuses such variable names.
      48             :   class data_equation_argument_generator
      49             :   {
      50             :     public:
      51           1 :       data_equation_argument_generator(
      52             :         data::set_identifier_generator& identifier_generator)
      53           1 :         : m_identifier_generator(identifier_generator)
      54           1 :       {}
      55             : 
      56             :       /// Generate argument variables for f.
      57          15 :       data::variable_vector arguments(const data::function_symbol& f)
      58             :       {
      59          15 :         data::variable_vector result;
      60          15 :         std::map<data::sort_expression, std::size_t> used_arguments;
      61          15 :         if (is_function_sort(f.sort()))
      62             :         {
      63          42 :           for(const data::sort_expression& arg_sort: atermpp::down_cast<data::function_sort>(f.sort()).domain())
      64             :           {
      65             :             // Check if we already used the sort for this function symbol.
      66          31 :             if(used_arguments.find(arg_sort) == used_arguments.end())
      67             :             {
      68          21 :               used_arguments[arg_sort] = 0;
      69             :             }
      70             : 
      71             :             // Check if we need to generate a new variable.
      72          31 :             if(m_variables[arg_sort].size() == used_arguments[arg_sort])
      73             :             {
      74           7 :               m_variables[arg_sort].emplace_back(m_identifier_generator("d"), arg_sort);
      75             :             }
      76             : 
      77             :             // Push new variable
      78          31 :             result.push_back(m_variables[arg_sort].at(used_arguments[arg_sort]));
      79          31 :             ++used_arguments[arg_sort];
      80             :           }
      81             :         }
      82          30 :         return result;
      83          15 :       }
      84             : 
      85             :     protected:
      86             :       data::set_identifier_generator& m_identifier_generator;
      87             :       std::map<data::sort_expression, data::variable_vector> m_variables;
      88             :   };
      89             : 
      90             :   class unfold_data_manager
      91             :   {
      92             :     private:
      93             :     /// \brief cache for previously unfolded sorts.
      94             :     /// facilitates reuse of previously introduced sorts and function symbols.
      95             :     std::map< mcrl2::data::sort_expression , unfold_cache_element >& m_cache;
      96             : 
      97             :     data::data_specification& m_dataspec;
      98             : 
      99             :     /// \brief set of identifiers to use during fresh variable generation
     100             :     mcrl2::data::set_identifier_generator m_identifier_generator;
     101             : 
     102             :     /// \brief generator for arguments in left hand side of data equations
     103             :     detail::data_equation_argument_generator m_data_equation_argument_generator;
     104             : 
     105             :     /// \brief a generator for default data expressions of a given sort;
     106             :     mcrl2::data::representative_generator m_representative_generator;
     107             : 
     108             :     /// \brief Boolean indicating whether rewrite rules may be added that could make
     109             :     ///        the data specification inconsistent.
     110             :     bool m_possibly_inconsistent;
     111             : 
     112             :     public:
     113           1 :     unfold_data_manager(std::map< mcrl2::data::sort_expression , unfold_cache_element >& cache, data::data_specification& dataspec, bool possibly_inconsistent)
     114           1 :     : m_cache(cache)
     115           1 :     , m_dataspec(dataspec)
     116           1 :     , m_identifier_generator()
     117           1 :     , m_data_equation_argument_generator(m_identifier_generator)
     118           1 :     , m_representative_generator(dataspec)
     119           1 :     , m_possibly_inconsistent(possibly_inconsistent)
     120           1 :     {}
     121             : 
     122           1 :     const data::data_specification& dataspec() const { return m_dataspec; }
     123             : 
     124          30 :     void add_used_identifier(const core::identifier_string& id)
     125             :     {
     126          30 :       m_identifier_generator.add_identifier(id);
     127          30 :     }
     128             : 
     129           2 :     void add_used_identifiers(const std::set<core::identifier_string>& ids)
     130             :     {
     131           2 :       m_identifier_generator.add_identifiers(ids);
     132           2 :     }
     133             : 
     134           0 :     data::set_identifier_generator& id_gen()
     135             :     {
     136           0 :       return m_identifier_generator;
     137             :     }
     138             : 
     139             :     unfold_cache_element& get_cache_element(const data::sort_expression& sort);
     140             : 
     141           0 :     bool is_cached(const data::sort_expression& sort) const
     142             :     {
     143           0 :       return m_cache.find(sort) != m_cache.end();
     144             :     }
     145             : 
     146           0 :     bool is_constructor(const data::function_symbol& f) const
     147             :     {
     148           0 :       return utilities::detail::contains(m_dataspec.constructors(), f);
     149             :     }
     150             : 
     151           0 :     const std::vector<data::function_symbol>& get_constructors(const data::sort_expression& sort)
     152             :     {
     153           0 :       return get_cache_element(sort).affected_constructors;
     154             :     }
     155             : 
     156           0 :     data::data_expression create_cases(const data::data_expression& target, const data::data_expression_vector& rhss)
     157             :     {
     158           0 :       unfold_cache_element& cache_elem = get_cache_element(target.sort());
     159           0 :       data::application first_arg(cache_elem.determine_function, target);
     160             : 
     161           0 :       data::data_expression_vector args;
     162           0 :       args.push_back(first_arg);
     163           0 :       for (const data::data_expression& rhs: rhss)
     164             :       {
     165           0 :         args.push_back(rhs);
     166             :       }
     167             : 
     168           0 :       return data::application(cache_elem.case_functions.at(rhss.front().sort()), args);
     169           0 :     }
     170             : 
     171           0 :     const data::function_symbol_vector& get_projection_funcs(const data::function_symbol& f)
     172             :     {
     173           0 :       return get_cache_element(f.sort().target_sort()).projection_functions;
     174             :     }
     175             : 
     176             :     /** \brief  Generates a fresh basic sort given a sort expression.
     177             :       * \param  sort This sort's name will be used to derive a name for the new sort
     178             :       * \return A fresh basic sort.
     179             :     **/
     180             :     mcrl2::data::basic_sort generate_fresh_basic_sort(const data::sort_expression& sort);
     181             : 
     182             :     /** \brief  Generates a fresh name for a constructor or mapping.
     183             :       * \param  str a string value. The value is used to generate a fresh
     184             :       *         name for a constructor or mapping.
     185             :       * \post   A fresh name for a constructor or mapping is generated.
     186             :       * \return A fresh name for a constructor or mapping.
     187             :     **/
     188             :     mcrl2::core::identifier_string generate_fresh_function_symbol_name(const std::string& str);
     189             : 
     190             :     /** \brief Generates variable of type sort based on a given string str.
     191             :       * \param str a string value. The value is used to generate a fresh
     192             :       *         variable name.
     193             :       * \param sort The sort of the variable to generate.
     194             :       * \post   A fresh variable is generated, which has an unique name.
     195             :       * \return A fresh variable.
     196             :     **/
     197             :     mcrl2::data::variable generate_fresh_variable(std::string str, const data::sort_expression& sort);
     198             : 
     199             : 
     200             :     /** \brief  Creates the determine function.
     201             :       * \param sort The sort on which this function operates
     202             :       * \return A function that maps constructors to the fresh basic sort
     203             :     **/
     204             :     void create_determine_function(const data::sort_expression& sort);
     205             : 
     206             :     /** \brief  Creates projection functions for the unfolded process parameter.
     207             :       * \param sort The sort on which these functions operate
     208             :       * \return A function that returns the projection functions for the
     209             :       *         constructors of the unfolded process parameter.
     210             :     **/
     211             :     void create_projection_functions(const data::sort_expression& sort);
     212             : 
     213             :     /** \brief  Determines the constructors that are affected with the unfold
     214             :       *         process parameter.
     215             :       * \param sort The sort for which to find constructors
     216             :       * \post The constructors that are affected with the unfold
     217             :       *         process parameter are stored in m_affected_constructors
     218             :     **/
     219             :     void determine_affected_constructors(const data::sort_expression& sort);
     220             : 
     221             :     /** \brief  Creates a set of constructors for the fresh basic sort
     222             :       * \param sort The sort for which to create analogous constructors
     223             :       * \return The constructors that are created for the fresh basic sort
     224             :     **/
     225             :     void create_new_constructors(const data::sort_expression& sort);
     226             : 
     227             :     /** \brief  Creates the case function with number of arguments determined by
     228             :      *          the number of affected constructors, the sort of the arguments and
     229             :      *          result are determined by sort..
     230             :      *  \param  det_sort The sort whose constructor is determined in the first argument
     231             :      *  \param  output_sort The sort of the arguments and return sort of the case function
     232             :       * \return A function that returns the corresponding constructor given the
     233             :       *         case selector and constructors.
     234             :     **/
     235             :     data::function_symbol create_case_function(const data::sort_expression& det_sort, const data::sort_expression& output_sort);
     236             : 
     237             :     /** \brief Create distribution rules for distribution_functions over case_functions
     238             :     **/
     239             :     void create_distribution_law_over_case(
     240             :       const data::sort_expression& sort,
     241             :       const data::function_symbol& function_for_distribution,
     242             :       const data::function_symbol case_function);
     243             : 
     244             : 
     245             :     /** \brief Create the data equations for case functions */
     246             :     void generate_case_function_equations(
     247             :       const data::sort_expression& sort,
     248             :       const data::function_symbol& case_function);
     249             : 
     250             :     /** \brief Create the data equations for the determine function */
     251             :     void generate_determine_function_equations(const data::sort_expression& sort);
     252             : 
     253             :     /** \brief Create the data equations for the projection functions */
     254             :     void generate_projection_function_equations(const data::sort_expression& sort);
     255             : 
     256          40 :     static bool char_filter(char c)
     257             :     {
     258             :       // Put unwanted characters here
     259          40 :       return c==' ' || c==':' || c==',' || c=='|'
     260          40 :              || c=='>' || c=='[' || c==']' || c=='@'
     261          38 :              || c=='.' || c=='{' || c=='}' || c=='#'
     262          38 :              || c=='%' || c=='&' || c=='*' || c=='!'
     263          80 :              || c=='(' || c==')'
     264             :              ;
     265             :     }
     266             : 
     267           6 :     std::string filter_illegal_characters(std::string in) const
     268             :     {
     269           6 :       in.resize(std::remove_if(in.begin(), in.end(), &char_filter) - in.begin());
     270           6 :       return in;
     271             :     }
     272             :   };
     273             : 
     274             :   /// @brief Class for unfolding expressions f(a1,...,an) based on the pattern-matching
     275             :   /// rewrite rules that define f.
     276             :   class pattern_match_unfolder
     277             :   {
     278             :     private:
     279             :     unfold_data_manager& m_datamgr;
     280             :     data::representative_generator m_repgen;
     281             :     std::map<data::function_symbol, bool> m_is_pattern_matching;
     282             :     std::map<data::function_symbol, data::data_equation> m_new_eqns;
     283             : 
     284             :     /// @brief Finds all rewriting equations for f
     285           0 :     const data::data_equation_vector find_equations(const data::function_symbol& f)
     286             :     {
     287           0 :       data::data_equation_vector result;
     288           0 :       for (const data::data_equation& eqn: m_datamgr.dataspec().equations())
     289             :       {
     290           0 :         if (data::detail::get_top_fs(eqn.lhs()) == f)
     291             :         {
     292           0 :           result.push_back(eqn);
     293             :         }
     294             :       }
     295           0 :       return result;
     296           0 :     }
     297             : 
     298             :     /// @brief Checks whether f is defined by pattern matching
     299           0 :     bool is_pattern_matching(const data::function_symbol& f)
     300             :     {
     301           0 :       auto find_result = m_is_pattern_matching.find(f);
     302           0 :       if (find_result != m_is_pattern_matching.end())
     303             :       {
     304           0 :         return find_result->second;
     305             :       }
     306           0 :       data::data_equation_vector eqns = find_equations(f);
     307           0 :       bool result = std::all_of(eqns.begin(), eqns.end(),
     308           0 :         [&](auto& eqn) { return data::is_pattern_matching_rule(m_datamgr, eqn); });
     309           0 :       m_is_pattern_matching.insert_or_assign(f, result);
     310           0 :       return result;
     311           0 :     }
     312             : 
     313           0 :     data::data_expression unfolded_expr(const data::function_symbol& f, const data::data_expression_vector& args)
     314             :     {
     315           0 :       data::data_equation new_eqn;
     316           0 :       auto find_result = m_new_eqns.find(f);
     317           0 :       if (find_result != m_new_eqns.end())
     318             :       {
     319           0 :         new_eqn = find_result->second;
     320             :       }
     321             :       else
     322             :       {
     323           0 :         new_eqn = data::unfold_pattern_matching(f, find_equations(f), m_datamgr, m_repgen, m_datamgr.id_gen());
     324           0 :         m_new_eqns.emplace(f, new_eqn);
     325             :       }
     326             : 
     327           0 :       std::map<data::variable, data::data_expression> sigma;
     328           0 :       auto it1 = atermpp::down_cast<data::application>(new_eqn.lhs()).begin();
     329           0 :       auto it2 = args.begin();
     330           0 :       while (it2 != args.end())
     331             :       {
     332           0 :         sigma[atermpp::down_cast<data::variable>(*it1++)] = *it2++;
     333             :       }
     334           0 :       data::map_substitution sigma1(sigma);
     335             : 
     336             : 
     337           0 :       return data::replace_all_variables(new_eqn.rhs(), sigma1);
     338           0 :     }
     339             : 
     340             :     public:
     341           1 :     pattern_match_unfolder(unfold_data_manager& datamgr)
     342           1 :     : m_datamgr(datamgr)
     343           1 :     , m_repgen(datamgr.dataspec())
     344           1 :     {}
     345             : 
     346           0 :     bool is_constructor(const data::function_symbol& f)
     347             :     {
     348           0 :       return m_datamgr.is_constructor(f);
     349             :     }
     350             : 
     351             :     /// @brief Checks whether expr is of the shape Det(h(a1,...,an)) or pi(h(a1,...,an)),
     352             :     /// where h is defined by patter matching, and, if so, unfolds h(a1,...,an).
     353           2 :     bool is_det_or_pi(const data::application& expr) const
     354             :     {
     355             :       using utilities::detail::contains;
     356             :       
     357           2 :       const data::function_symbol f = data::detail::get_top_fs(expr);
     358             :       // If f is not unary, then it is certainly unequal to Det or pi
     359           2 :       if (f == data::function_symbol() || expr.size() != 1)
     360             :       {
     361           2 :         return false;
     362             :       }
     363           0 :       const data::sort_expression& arg_sort = *atermpp::down_cast<data::function_sort>(f.sort()).domain().begin();
     364             :       // The argument of f must be of an unfolded sort
     365           0 :       if (!m_datamgr.is_cached(arg_sort))
     366             :       {
     367           0 :         return false;
     368             :       }
     369           0 :       const unfold_cache_element& cache_elem = m_datamgr.get_cache_element(arg_sort);
     370           0 :       if (f != cache_elem.determine_function && !contains(cache_elem.projection_functions, f))
     371             :       {
     372           0 :         return false;
     373             :       }
     374             : 
     375           0 :       return true;
     376           2 :     }
     377             : 
     378           0 :     bool can_unfold(const data::data_expression& x)
     379             :     {
     380           0 :       if (!data::is_application(x))
     381             :       {
     382           0 :         return false;
     383             :       }
     384             : 
     385           0 :       const data::function_symbol f = data::detail::get_top_fs(atermpp::down_cast<data::application>(x));
     386           0 :       if (f == data::function_symbol() || !is_pattern_matching(f))
     387             :       {
     388           0 :         return false;
     389             :       }
     390           0 :       auto udm = m_datamgr.dataspec().mappings();
     391           0 :       const data::data_specification& dataspec = m_datamgr.dataspec();
     392           0 :       if (std::find_if(udm.begin(), udm.end(), 
     393           0 :           [&](const auto& f2){ return f.name() == f2.name() && dataspec.equal_sorts(f.sort(), f2.sort()); }) == udm.end())
     394             :       {
     395             :         // f is not a mapping, but likely a constructor
     396           0 :         return false;
     397             :       }
     398           0 :       if (!matches_only_known_sorts(f))
     399             :       {
     400           0 :         return false;
     401             :       }
     402           0 :       return true;
     403           0 :     }
     404             : 
     405           0 :     std::vector<std::size_t> pattern_matching_args(const data::function_symbol& f)
     406             :     {
     407           0 :       std::vector<std::size_t> result;
     408           0 :       if (!data::is_function_sort(f.sort()))
     409             :       {
     410           0 :         return result;
     411             :       }
     412           0 :       data::data_equation_vector eqns = find_equations(f);
     413             : 
     414           0 :       std::size_t f_num_args = atermpp::down_cast<data::function_sort>(f.sort()).domain().size();
     415           0 :       for (std::size_t arg = 0; arg < f_num_args; arg++)
     416             :       {
     417           0 :         for (const data::data_equation& eq: eqns)
     418             :         {
     419           0 :           const data::application& lhs_appl = atermpp::down_cast<data::application>(eq.lhs());
     420           0 :           if (!data::is_variable(lhs_appl[arg]))
     421             :           {
     422           0 :             result.push_back(arg);
     423           0 :             break;
     424             :           }
     425             :         }
     426             :       }
     427           0 :       return result;
     428           0 :     }
     429             : 
     430             :     /// @brief Determines whether f pattern matches on argument arg
     431             :     /// @pre this->can_unfold(f)
     432           0 :     bool matches_only_known_sorts(const data::function_symbol& f)
     433             :     {
     434           0 :       if (!data::is_function_sort(f.sort()))
     435             :       {
     436           0 :         return true;
     437             :       }
     438             : 
     439           0 :       const data::sort_expression_list& domain = atermpp::down_cast<data::function_sort>(f.sort()).domain();
     440           0 :       const data::sort_expression_vector domain_vec{domain.begin(), domain.end()};
     441           0 :       for (const std::size_t i: pattern_matching_args(f))
     442             :       {
     443           0 :         if (!m_datamgr.is_cached(domain_vec[i]))
     444             :         {
     445           0 :           return false;
     446             :         }
     447           0 :       }
     448           0 :       return true;
     449           0 :     }
     450             : 
     451             :     /// @brief Unfolds expr if it is of the shape h(a1,...,an) and h is defined by pattern matching
     452             :     /// @pre this->can_unfold(x)
     453             :     template <class T>
     454           0 :     void operator()(T& result, const data::application& x)
     455             :     {
     456           0 :       const data::data_expression_vector args(x.begin(), x.end());
     457           0 :       result = unfolded_expr(data::detail::get_top_fs(x), args);
     458           0 :     }
     459             :   };
     460             : 
     461             : 
     462             :   template <template <class> class Builder>
     463             :   struct replace_pattern_match_builder: public Builder<replace_pattern_match_builder<Builder> >
     464             :   {
     465             :     typedef Builder<replace_pattern_match_builder<Builder> > super;
     466             :     using super::enter;
     467             :     using super::leave;
     468             :     using super::apply;
     469             :     using super::update;
     470             : 
     471             :     pattern_match_unfolder& m_unfolder;
     472             :     bool m_currently_recursing = false;
     473             :     std::size_t m_current_depth = 0;
     474             : 
     475           3 :     replace_pattern_match_builder(pattern_match_unfolder& unfolder)
     476           3 :       : m_unfolder(unfolder)
     477           3 :     {}
     478             : 
     479           0 :     bool is_applied_to_constructor(const data::application& x)
     480             :     {
     481             :       using utilities::detail::contains;
     482           0 :       const std::vector<std::size_t> pattern_matching_args = m_unfolder.pattern_matching_args(data::detail::get_top_fs(x));
     483           0 :       for(const std::size_t i: pattern_matching_args)
     484             :       {
     485           0 :         if (m_unfolder.is_constructor(data::detail::get_top_fs(x[i])))
     486             :         {
     487           0 :           return true;
     488             :         }
     489             :       }
     490           0 :       return false;
     491           0 :     }
     492             : 
     493             :     template <class T>
     494           2 :     void apply(T& result, const data::application& x)
     495             :     {
     496           2 :       if (m_currently_recursing)
     497             :       {
     498           0 :         if (m_current_depth >= 3 || m_unfolder.is_constructor(data::detail::get_top_fs(x)))
     499             :         {
     500             :           // Stop recursing after unfolding three times or when meeting a constructor
     501             :           // In the latter case, we have done enough to rewrite Det() and pi()
     502           0 :           result = x;
     503           0 :           return;
     504             :         }
     505           0 :         else if (data::is_if_application(x))
     506             :         {
     507             :           // In if-statements, do not traverse the condition
     508           0 :           data::data_expression branch1;
     509           0 :           super::apply(branch1, x[1]);
     510           0 :           data::data_expression branch2;
     511           0 :           super::apply(branch2, x[2]);
     512             :           
     513           0 :           data::make_application(result,
     514             :             data::if_(x.sort()),
     515             :             x[0],
     516             :             branch1,
     517             :             branch2
     518             :           );
     519           0 :         }
     520           0 :         else if (m_unfolder.can_unfold(x) && is_applied_to_constructor(x))
     521             :         {
     522           0 :           data::data_expression intermediate_result;
     523           0 :           m_unfolder(intermediate_result, x);
     524             :           // Recursively apply unfolding
     525           0 :           m_current_depth++;
     526           0 :           super::apply(result, intermediate_result);
     527           0 :           m_current_depth--;
     528           0 :         }
     529             :         else
     530             :         {
     531           0 :           super::apply(result, x);
     532             :         }
     533             :       }
     534             :       else
     535             :       {
     536             :         // Determine whether we see Det(f(..)) or pi(f(..))
     537             :         // If so, unfold and start recursing
     538           2 :         if (m_unfolder.is_det_or_pi(x) && m_unfolder.can_unfold(x[0]))
     539             :         {
     540           0 :           data::data_expression intermediate_result1, intermediate_result2;
     541           0 :           m_unfolder(intermediate_result1, atermpp::down_cast<data::application>(x[0]));
     542             : 
     543           0 :           m_currently_recursing = true;
     544           0 :           m_current_depth++;
     545           0 :           super::apply(intermediate_result2, intermediate_result1);
     546           0 :           m_current_depth--;
     547             : 
     548           0 :           mCRL2log(log::debug) << "Unfolded " << x[0] << " into " << intermediate_result2 << std::endl;
     549           0 :           data::make_application(result, x.head(), intermediate_result2);
     550           0 :           m_currently_recursing = false;
     551           0 :         }
     552             :         else
     553             :         {
     554           2 :           super::apply(result, x);
     555             :         }
     556             :       }
     557             :     }
     558             :   };
     559             : 
     560             :   inline
     561           3 :   data::data_expression unfold_pattern_matching(const data::data_expression& x,
     562             :                                pattern_match_unfolder& unfolder
     563             :                               )
     564             :   {
     565           3 :     data::data_expression result;
     566           3 :     replace_pattern_match_builder<data::data_expression_builder>(unfolder).apply(result, x);
     567           3 :     return result;
     568           0 :   }
     569             : } // namespace detail
     570             : 
     571             : class lpsparunfold: public detail::lps_algorithm<lps::stochastic_specification>
     572             : {
     573             :   typedef typename detail::lps_algorithm<lps::stochastic_specification> super;
     574             : 
     575             :   public:
     576             : 
     577             :     // old parameter, case function, determinizing parameter, replacement expressions
     578             :     typedef std::tuple<data::variable, std::map<data::sort_expression, data::function_symbol>, data::variable, data::data_expression_vector> case_func_replacement;
     579             : 
     580             :     /** \brief  Constructor for lpsparunfold algorithm.
     581             :       * \param[in] spec which is a valid mCRL2 process specification.
     582             :       * \param[in,out] cache Cache to store information for reuse.
     583             :       * \param[in] alt_case_placement If true, case functions are placed at a higher level.
     584             :       * \param[in] possibly_inconsistent If true, case functions over Booleans are replaced by a disjunction of conjunctions.
     585             :       *                 For this to be correct, the unfolded sort needs to satisfy some restrictions.
     586             :       * \post   The content of mCRL2 process specification analysed for useful information and class variables are set.
     587             :       **/
     588             :     lpsparunfold(lps::stochastic_specification& spec,
     589             :                  std::map< data::sort_expression , unfold_cache_element >& cache,
     590             :                  bool alt_case_placement = false,
     591             :                  bool possibly_inconsistent = false,
     592             :                  bool unfold_pattern_matching = true);
     593             : 
     594             :     /** \brief  Applies lpsparunfold algorithm on a process parameter of an mCRL2 process specification .
     595             :      *  \pre algorithm has not been called before.
     596             :      *  \param[in] parameter_at_index An integer value that represents the index value of an process parameter.
     597             :      *  \post   The process parameter at index parameter_at_index is unfolded in the mCRL2 process specification.
     598             :     **/
     599             :     void algorithm(const std::size_t parameter_at_index);
     600             : 
     601             :   private:
     602             :     /// \brief set to true when the algorithm has been run once; as the algorithm should
     603             :     /// run only once...
     604             :     bool m_run_before;
     605             : 
     606             :     /// @brief Bookkeeper for recogniser and projection functions.
     607             :     detail::unfold_data_manager m_datamgr;
     608             :     detail::pattern_match_unfolder m_pattern_unfolder;
     609             : 
     610             :     /// \brief The process parameter that needs to be unfold.
     611             :     mcrl2::data::variable m_unfold_parameter;
     612             : 
     613             :     /// \brief The process parameters that are inserted.
     614             :     mcrl2::data::variable_vector m_injected_parameters;
     615             : 
     616             :     /// \brief Boolean to indicate if alternative placement of case functions should be used.
     617             :     bool m_alt_case_placement;
     618             : 
     619             :     /// \brief Indicates whether functions defined by pattern matching that occur in the scope of
     620             :     /// a Det or pi in a state update should be unfolded.
     621             :     bool m_unfold_pattern_matching;
     622             : 
     623             : 
     624             :     //data::data_expression apply_case_function(const data::data_expression& expr, const case_func_replacement& case_funcs);
     625             :     case_func_replacement parameter_case_function();
     626             : 
     627             :     /** \brief  Get the process parameter at given index
     628             :       * \param  index The index of the parameter which must be obtained.
     629             :       * \return the process parameter at given index.
     630             :     **/
     631             :     mcrl2::data::variable process_parameter_at(const std::size_t index);
     632             : 
     633             :     /** \brief  substitute function for replacing process parameters with unfolded process parameters functions.
     634             :       * \return substitute function for replacing process parameters with unfolded process parameters functions.
     635             :     **/
     636             :     std::map<mcrl2::data::variable, mcrl2::data::data_expression> parameter_substitution();
     637             : 
     638             :     data::data_expression apply_function(const data::function_symbol& f, const data::data_expression& de) const;
     639             : 
     640             :     /** \brief unfolds a data expression into a vector of process parameters
     641             :       * \param  de the data expression
     642             :       * \return The following vector: < det(de), pi_0(de), ... ,pi_n(de) >
     643             :     **/
     644             :     mcrl2::data::data_expression_vector unfold_constructor(
     645             :       const mcrl2::data::data_expression& de);
     646             : 
     647             :     /** \brief substitute unfold process parameter in the linear process
     648             :       * \param  parameter_at_index the parameter index
     649             :       * \post the process parameter at given index is unfolded in the the linear process
     650             :     **/
     651             :     void update_linear_process(std::size_t parameter_at_index);
     652             : 
     653             :     /** \brief substitute unfold process parameter in the initialization of the linear process
     654             :       * \param  parameter_at_index the parameter index
     655             :       * \post the initialization for the linear process is updated by unfolding the parameter at given index is unfolded
     656             :     **/
     657             :    void update_linear_process_initialization(
     658             :       std::size_t parameter_at_index);
     659             : 
     660             :     // Applies 'process unfolding' to a sequence of summands.
     661             :     void unfold_summands(mcrl2::lps::stochastic_action_summand_vector& summands);
     662             : };
     663             : 
     664             : 
     665             : template <template <class> class Builder, template <template <class> class, class, class> class Binder>
     666             : struct parunfold_replacement: public
     667             : Binder<Builder, parunfold_replacement<Builder, Binder>, parunfold_replacement<Builder, Binder>>
     668             : {
     669             :   typedef Binder<Builder, parunfold_replacement<Builder, Binder>, parunfold_replacement<Builder, Binder>> super;
     670             :   using super::enter;
     671             :   using super::leave;
     672             :   using super::apply;
     673             :   using super::update;
     674             : 
     675             :   data::detail::capture_avoiding_substitution_updater<parunfold_replacement<Builder, Binder>> sigma1;
     676             :   lpsparunfold::case_func_replacement case_funcs;
     677             :   data::data_expression current_replacement;
     678             : 
     679           0 :   parunfold_replacement(const lpsparunfold::case_func_replacement& case_funcs,
     680             :                         data::set_identifier_generator& id_generator)
     681           0 :   : super(sigma1)
     682           0 :   , sigma1(*this, id_generator)
     683             :   {
     684           0 :     this->case_funcs = case_funcs;
     685           0 :   }
     686             : 
     687             :   template <class T>
     688           0 :   void apply(T& result, const data::application& x)
     689             :   {
     690           0 :     if (current_replacement != data::data_expression() || data::is_and(x) || data::is_or(x) || data::is_not(x) || data::is_imp(x) || data::is_if_application(x))
     691             :     {
     692             :       // if no placement of case functions is underway, or we are still traversing the regular boolean operators, we continue as usual
     693           0 :       super::apply(result, x);
     694             :     }
     695             :     else
     696             :     {
     697             :       // place the case functions here
     698           0 :       apply_case_function(result, x);
     699             :     }
     700           0 :   }
     701             : 
     702           0 :   void apply_case_function(data::data_expression& result, const data::application& expr)
     703             :   {
     704           0 :     auto& [par, case_f, det_f, replacements] = case_funcs;
     705             : 
     706           0 :     if (data::find_free_variables(expr).count(par) == 0)
     707             :     {
     708             :       // variable to be replaced does not occur here
     709             :       // make sure to still apply the substitutions necessary for the capture avoiding tricks
     710             :       // NB: stack overflow happens if type of second argument is 'data::data_expression'.
     711           0 :       super::apply(result, expr);
     712             :     }
     713             :     else
     714             :     {
     715           0 :       data::data_expression_vector args;
     716           0 :       args.push_back(det_f);
     717             : 
     718           0 :       for (const data::data_expression& r : replacements)
     719             :       {
     720           0 :         current_replacement = r;
     721           0 :         data::data_expression arg;
     722           0 :         super::apply(arg, expr);
     723           0 :         args.push_back(arg);
     724             :       }
     725           0 :       current_replacement = data::data_expression();
     726             : 
     727           0 :       if (case_f.find(expr.sort()) == case_f.end())
     728             :       {
     729           0 :         throw mcrl2::runtime_error("Case function with target sort " +
     730             :                                    data::pp(expr.sort()) + " not declared.");
     731             :       }
     732           0 :       result = data::application(case_f[expr.sort()], args);
     733           0 :     }
     734           0 :   }
     735             : 
     736             :   // Substitution application
     737           0 :   data::data_expression operator()(const data::variable& x)
     738             :   {
     739           0 :     if (current_replacement == data::data_expression())
     740             :     {
     741           0 :       return x;
     742             :     }
     743           0 :     if (std::get<0>(case_funcs) != x)
     744             :     {
     745           0 :       return x;
     746             :     }
     747           0 :     return current_replacement;
     748             :   }
     749             : };
     750             : 
     751             : template <template <class> class Builder, template <template <class> class, class, class> class Binder>
     752             : parunfold_replacement<Builder, Binder>
     753           0 : apply_parunfold_replacement_builder(const lpsparunfold::case_func_replacement& case_funcs,
     754             :                                     data::set_identifier_generator& id_generator)
     755             : {
     756           0 :   return parunfold_replacement<Builder, Binder>(case_funcs, id_generator);
     757             : }
     758             : 
     759             : template <typename T>
     760           0 : void insert_case_functions(T& x,
     761             :                            const lpsparunfold::case_func_replacement& cfv,
     762             :                            data::set_identifier_generator& id_generator,
     763             :                            typename std::enable_if<!std::is_base_of<atermpp::aterm, T>::value>::type* = nullptr
     764             : )
     765             : {
     766           0 :   apply_parunfold_replacement_builder<lps::data_expression_builder, lps::detail::add_capture_avoiding_replacement>(cfv, id_generator).update(x);
     767           0 : }
     768             : 
     769             : template <typename T>
     770             : void insert_case_functions(T& x,
     771             :                            const lpsparunfold::case_func_replacement& cfv,
     772             :                            typename std::enable_if<!std::is_base_of<atermpp::aterm, T>::value>::type* = nullptr
     773             : )
     774             : {
     775             :   data::set_identifier_generator id_generator;
     776             :   id_generator.add_identifiers(lps::find_identifiers(x));
     777             :   auto& [par, case_f, det_f, replacements] = cfv;
     778             : 
     779             :   // assumes all case functions have the same name and that par.sort() is in the map of case functions
     780             :   id_generator.add_identifier(case_f.find(par.sort())->second.name());
     781             :   id_generator.add_identifier(det_f.name());
     782             :   for (const data::data_expression& r: replacements)
     783             :   {
     784             :     id_generator.add_identifiers(data::find_identifiers(r));
     785             :   }
     786             : 
     787             :   insert_case_functions(x, cfv, id_generator);
     788             : }
     789             : 
     790             : } // namespace mcrl2::lps
     791             : 
     792             : 
     793             : #endif

Generated by: LCOV version 1.14