LCOV - code coverage report
Current view: top level - buildbot/coverage/build/src/preprocessing/passes - normalize.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 289 439 65.8 %
Date: 2026-07-02 10:34:36 Functions: 16 18 88.9 %
Branches: 143 262 54.6 %

           Branch data     Line data    Source code
       1                 :            : /******************************************************************************
       2                 :            :  * This file is part of the cvc5 project.
       3                 :            :  *
       4                 :            :  * Copyright (c) 2009-2026 by the authors listed in the file AUTHORS
       5                 :            :  * in the top-level source directory and their institutional affiliations.
       6                 :            :  * All rights reserved.  See the file COPYING in the top-level source
       7                 :            :  * directory for licensing information.
       8                 :            :  * ****************************************************************************
       9                 :            :  *
      10                 :            :  * This pre-processing pass implementes the normalizer in the FMCAD 2025 paper.
      11                 :            :  */
      12                 :            : 
      13                 :            : #include "preprocessing/passes/normalize.h"
      14                 :            : 
      15                 :            : #include <unordered_map>
      16                 :            : 
      17                 :            : #include "expr/cardinality_constraint.h"
      18                 :            : #include "expr/normalize_sort_converter.h"
      19                 :            : #include "expr/skolem_manager.h"
      20                 :            : #include "preprocessing/assertion_pipeline.h"
      21                 :            : #include "preprocessing/preprocessing_pass_context.h"
      22                 :            : #include "util/statistics_registry.h"
      23                 :            : 
      24                 :            : using namespace cvc5::internal::kind;
      25                 :            : 
      26                 :            : namespace cvc5::internal {
      27                 :            : namespace preprocessing {
      28                 :            : namespace passes {
      29                 :            : 
      30                 :            : /**
      31                 :            :  * Compute (and cache) the superpattern for a given symbol (used in Step 5).
      32                 :            :  *
      33                 :            :  * A superpattern has one segment per equivalence class. Each segment begins
      34                 :            :  * with the count of -1 values (the number of members of the class where the
      35                 :            :  * symbol does not occur) followed by the sorted list of non-negative role
      36                 :            :  * indices where the symbol does occur.
      37                 :            :  *
      38                 :            :  * @param symbol The symbol whose superpattern is requested.
      39                 :            :  * @param eqClasses The equivalence classes of assertions (NodeInfo*).
      40                 :            :  * @param symbolOccurrences Map from symbol to the assertions in which it
      41                 :            :  *        occurs.
      42                 :            :  * @param patternCache Cache mapping symbols to their computed superpatterns.
      43                 :            :  * @return A const reference to the cached superpattern for the symbol.
      44                 :            :  */
      45                 :          4 : static const std::vector<std::vector<int32_t>>& getOrComputeSuperpattern(
      46                 :            :     const std::string& symbol,
      47                 :            :     const std::vector<std::vector<NodeInfo*>>& eqClasses,
      48                 :            :     const std::unordered_map<std::string,
      49                 :            :                              std::vector<std::shared_ptr<NodeInfo>>>&
      50                 :            :         symbolOccurrences,
      51                 :            :     std::unordered_map<std::string, std::vector<std::vector<int32_t>>>&
      52                 :            :         patternCache)
      53                 :            : {
      54                 :          4 :   auto it = patternCache.find(symbol);
      55         [ -  + ]:          4 :   if (it != patternCache.end())
      56                 :            :   {
      57                 :          0 :     return it->second;
      58                 :            :   }
      59                 :            : 
      60                 :            :   // Initialize the superpattern with empty vectors for each segment
      61                 :          4 :   std::vector<std::vector<int32_t>> superpattern(eqClasses.size());
      62                 :          4 :   auto symbolIt = symbolOccurrences.find(symbol);
      63 [ -  + ][ -  + ]:          4 :   Assert(symbolIt != symbolOccurrences.end());
                 [ -  - ]
      64                 :            : 
      65                 :            :   // Iterate over all occurrences of the symbol
      66         [ +  + ]:         10 :   for (const auto& nodeInfo : symbolIt->second)
      67                 :            :   {
      68                 :          6 :     auto roleIt = nodeInfo->role.find(symbol);
      69 [ -  + ][ -  + ]:          6 :     Assert(roleIt != nodeInfo->role.end());
                 [ -  - ]
      70                 :            : 
      71                 :            :     // Get the equivalence class ID for this node
      72                 :          6 :     size_t eqClassId = nodeInfo->id;
      73                 :            : 
      74                 :            :     // Push the non-negative value to the corresponding segment in the
      75                 :            :     // superpattern
      76                 :          6 :     superpattern[eqClassId].push_back(roleIt->second);
      77                 :            :   }
      78                 :            : 
      79                 :            :   // Sort each segment in the superpattern and compute the -1 count
      80         [ +  + ]:          8 :   for (size_t i = 0; i < superpattern.size(); ++i)
      81                 :            :   {
      82                 :          4 :     std::sort(superpattern[i].begin(), superpattern[i].end());
      83                 :            :     // The count of -1s is the size of the equivalence class minus
      84                 :            :     // the number of non-negative values
      85                 :          8 :     superpattern[i].insert(superpattern[i].begin(),
      86                 :          4 :                            eqClasses[i].size() - superpattern[i].size());
      87                 :            :   }
      88                 :            : 
      89                 :          4 :   auto& ret = patternCache[symbol];
      90                 :          4 :   ret = std::move(superpattern);
      91                 :          4 :   return ret;
      92                 :          4 : }
      93                 :            : 
      94                 :            : /**
      95                 :            :  * Comparator for NodeInfo* based on their superpatterns (used in Step 5).
      96                 :            :  *
      97                 :            :  * Comparison proceeds segment-by-segment:
      98                 :            :  *  - Compare the leading -1 count for the segment.
      99                 :            :  *  - Then compare the occurrence positions lexicographically.
     100                 :            :  *  - If one segment has more positions, it is considered greater.
     101                 :            :  *
     102                 :            :  * Superpatterns are computed on demand and cached in patternCache.
     103                 :            :  *
     104                 :            :  * @param a First NodeInfo.
     105                 :            :  * @param b Second NodeInfo.
     106                 :            :  * @param eqClasses Equivalence classes (for segment sizing).
     107                 :            :  * @param patternCache Cache of computed superpatterns.
     108                 :            :  * @param symbolOccurrences Occurrence lists per symbol.
     109                 :            :  * @return True if a should come before b.
     110                 :            :  */
     111                 :          2 : static bool compareBySuperpattern(
     112                 :            :     NodeInfo* a,
     113                 :            :     NodeInfo* b,
     114                 :            :     const std::vector<std::vector<NodeInfo*>>& eqClasses,
     115                 :            :     std::unordered_map<std::string, std::vector<std::vector<int32_t>>>&
     116                 :            :         patternCache,
     117                 :            :     const std::unordered_map<std::string,
     118                 :            :                              std::vector<std::shared_ptr<NodeInfo>>>&
     119                 :            :         symbolOccurrences)
     120                 :            : {
     121                 :          2 :   auto itA = a->varNames.begin();
     122                 :          2 :   auto itB = b->varNames.begin();
     123                 :            : 
     124 [ +  - ][ +  - ]:          2 :   while (itA != a->varNames.end() && itB != b->varNames.end())
                 [ +  - ]
     125                 :            :   {
     126                 :          2 :     const std::string& symbolA = itA->first;
     127                 :          2 :     const std::string& symbolB = itB->first;
     128                 :            : 
     129         [ -  + ]:          2 :     if (symbolA == symbolB)
     130                 :            :     {
     131                 :          0 :       ++itA;
     132                 :          0 :       ++itB;
     133                 :          0 :       continue;
     134                 :            :     }
     135                 :            : 
     136                 :          2 :     const auto& superpatternA = getOrComputeSuperpattern(
     137                 :            :         symbolA, eqClasses, symbolOccurrences, patternCache);
     138                 :          2 :     const auto& superpatternB = getOrComputeSuperpattern(
     139                 :            :         symbolB, eqClasses, symbolOccurrences, patternCache);
     140                 :            : 
     141                 :            :     // Compare superpatterns segment by segment
     142         [ +  - ]:          2 :     for (size_t i = 0; i < superpatternA.size(); ++i)
     143                 :            :     {
     144                 :          2 :       const auto& patA = superpatternA[i];
     145                 :          2 :       const auto& patB = superpatternB[i];
     146                 :            : 
     147                 :            :       // Compare the count of -1s in the segment
     148         [ +  - ]:          2 :       if (patA[0] != patB[0])
     149                 :            :       {
     150                 :          2 :         return patA[0] < patB[0];
     151                 :            :       }
     152                 :            : 
     153                 :            :       // Compare the non-negative values in the segment
     154 [ -  - ][ -  - ]:          0 :       for (size_t j = 1; j < patA.size() && j < patB.size(); ++j)
                 [ -  - ]
     155                 :            :       {
     156         [ -  - ]:          0 :         if (patA[j] != patB[j])
     157                 :            :         {
     158                 :          0 :           return patA[j] < patB[j];
     159                 :            :         }
     160                 :            :       }
     161                 :            : 
     162                 :            :       // If one segment has more non-negative values, it is considered greater
     163         [ -  - ]:          0 :       if (patA.size() != patB.size())
     164                 :            :       {
     165                 :          0 :         return patA.size() < patB.size();
     166                 :            :       }
     167                 :            :     }
     168                 :            : 
     169                 :          0 :     ++itA;
     170                 :          0 :     ++itB;
     171                 :            :   }
     172                 :            : 
     173                 :            :   // Handle cases where one iterator reaches the end before the other
     174         [ -  - ]:          0 :   if (itA != a->varNames.end()) return true;
     175         [ -  - ]:          0 :   if (itB != b->varNames.end()) return false;
     176                 :            : 
     177                 :          0 :   return false;
     178                 :            : }
     179                 :            : 
     180                 :            : /**
     181                 :            :  * Comparator for NodeInfo* based on normalized variable names (used in Step 8).
     182                 :            :  *
     183                 :            :  * @param a First NodeInfo.
     184                 :            :  * @param b Second NodeInfo.
     185                 :            :  * @param normalizedName Map from original symbol to normalized name.
     186                 :            :  * @return True if a should come before b.
     187                 :            :  */
     188                 :          4 : static bool compareByNormalizedNames(
     189                 :            :     NodeInfo* a,
     190                 :            :     NodeInfo* b,
     191                 :            :     std::unordered_map<std::string, std::string>& normalizedName)
     192                 :            : {
     193                 :          4 :   size_t sz = std::min(a->varNames.size(), b->varNames.size());
     194         [ +  - ]:          4 :   for (size_t i = 0; i < sz; ++i)
     195                 :            :   {
     196                 :          4 :     const std::string& symbolA = a->varNames[i].first;
     197                 :          4 :     const std::string& symbolB = b->varNames[i].first;
     198                 :          4 :     const std::string& normNameA = normalizedName[symbolA];
     199                 :          4 :     const std::string& normNameB = normalizedName[symbolB];
     200         [ +  - ]:          4 :     if (normNameA != normNameB)
     201                 :            :     {
     202                 :          4 :       return normNameA < normNameB;
     203                 :            :     }
     204                 :            :   }
     205                 :          0 :   return false;
     206                 :            : }
     207                 :            : 
     208                 :            : /**
     209                 :            :  * Constructor for the Normalize preprocessing pass.
     210                 :            :  *
     211                 :            :  * @param preprocContext The preprocessing pass context.
     212                 :            :  */
     213                 :      52018 : Normalize::Normalize(PreprocessingPassContext* preprocContext)
     214                 :            :     : PreprocessingPass(preprocContext, "normalize"),
     215                 :      52018 :       d_statistics(statisticsRegistry()) {};
     216                 :            : 
     217                 :            : /**
     218                 :            :  * Generates a compressed encoding (also known as a pattern) of the given node's
     219                 :            :  * DAG structure. Additionally, calculates the role map, which is used later for
     220                 :            :  * super-pattern comparison.
     221                 :            :  *
     222                 :            :  * @param root The node itself
     223                 :            :  * @param encoding The resulting compressed encoding string.
     224                 :            :  * @param role A map tracking the first occurrence index of each symbol (used in
     225                 :            :  * super-patterns).
     226                 :            :  */
     227                 :          4 : void generateEncoding(const Node& root,
     228                 :            :                       std::string& encoding,
     229                 :            :                       std::unordered_map<std::string, int32_t>& role)
     230                 :            : {
     231                 :          4 :   std::vector<std::pair<Node, Node>> stack;  // Pair of node, parent
     232                 :          4 :   std::unordered_map<Node, bool> visited;
     233                 :          4 :   std::unordered_map<Node, size_t> subtreeIdMap;
     234                 :          4 :   std::unordered_map<std::string, size_t> symbolMap;
     235                 :          4 :   size_t varIdCounter = 1;
     236                 :          4 :   size_t subtreeIdCounter = 1;
     237                 :          4 :   int32_t cnt = 0;
     238                 :            : 
     239                 :          4 :   std::vector<std::string> nodeEncodings;
     240                 :            : 
     241                 :          4 :   stack.push_back({root, root});
     242                 :            : 
     243         [ +  + ]:         44 :   while (!stack.empty())
     244                 :            :   {
     245                 :            :     // Get current node and its parent
     246                 :         40 :     auto [n, parent] = stack.back();
     247                 :            : 
     248                 :         40 :     auto [it, inserted] = visited.emplace(n, false);
     249         [ +  + ]:         40 :     if (inserted)
     250                 :            :     {
     251                 :            :       // First time seeing this node
     252 [ +  + ][ +  + ]:         28 :       if (!n.isVar() && !n.isConst())
                 [ +  + ]
     253                 :            :       {
     254                 :            :         // Operator node
     255         [ +  + ]:         36 :         for (const Node& child : n)
     256                 :            :         {
     257         [ +  - ]:         24 :           if (visited.find(child) == visited.end())
     258                 :            :           {
     259                 :            :             // Push child with current node n as its parent
     260                 :         24 :             stack.push_back({child, n});
     261                 :            :           }
     262                 :         24 :         }
     263                 :            :       }
     264                 :            :       else
     265                 :            :       {
     266                 :            :         // For variables and constants, update role map and mark as processed
     267                 :            :         // immediately
     268         [ +  + ]:         16 :         if (n.isVar())
     269                 :            :         {
     270                 :          8 :           std::string symbol = std::to_string(n.getId());
     271                 :            : 
     272                 :          8 :           if (role.find(symbol) == role.end()
     273 [ +  - ][ +  - ]:          8 :               && parent.getKind()
                 [ +  - ]
     274                 :            :                      != cvc5::internal::Kind::
     275                 :            :                          INST_ATTRIBUTE)  // Ignore variables in INST_ATTRIBUTE
     276                 :            :                                           // (qid)
     277                 :            :           {
     278                 :          8 :             role[symbol] = cnt;
     279                 :            :           }
     280                 :          8 :           cnt++;  // Increment cnt whether variable is new or not
     281                 :          8 :         }
     282                 :            : 
     283                 :         16 :         it->second = true;
     284                 :         16 :         stack.pop_back();
     285                 :            :       }
     286                 :            :     }
     287         [ +  - ]:         12 :     else if (!it->second)
     288                 :            :     {
     289                 :            :       // Second time seeing this node, process it
     290                 :            : 
     291         [ -  + ]:         12 :       if (n.isVar())
     292                 :            :       {
     293                 :            :         // Variables are processed when included in operator nodes
     294                 :          0 :         stack.pop_back();
     295                 :            :       }
     296         [ -  + ]:         12 :       else if (n.isConst())
     297                 :            :       {
     298                 :            :         // Constants are processed when included in operator nodes
     299                 :          0 :         stack.pop_back();
     300                 :            :       }
     301                 :            :       else
     302                 :            :       {
     303         [ +  - ]:         12 :         if (n.hasOperator())
     304                 :            :         {
     305                 :         12 :           Node opNode = n.getOperator();
     306         [ -  + ]:         12 :           if (opNode.isVar())
     307                 :            :           {
     308                 :          0 :             std::string symbol = std::to_string(opNode.getId());
     309                 :            : 
     310                 :          0 :             if (role.find(symbol) == role.end()
     311                 :          0 :                 && n.getKind() != cvc5::internal::Kind::INST_ATTRIBUTE)
     312                 :            :             {
     313                 :          0 :               role[symbol] = cnt;
     314                 :            :             }
     315                 :          0 :             cnt++;  // Increment cnt whether variable is new or not
     316                 :          0 :           }
     317                 :         12 :         }
     318                 :            : 
     319                 :            :         // Assign an ID to this node
     320                 :         12 :         size_t id = subtreeIdCounter++;
     321                 :         12 :         subtreeIdMap[n] = id;
     322                 :            : 
     323                 :            :         // Build the encoding string
     324                 :         12 :         std::string nodeEncoding = std::to_string(id) + ":";
     325                 :            : 
     326                 :            :         // Include operator kind
     327                 :         12 :         cvc5::internal::Kind k = static_cast<cvc5::internal::Kind>(n.getKind());
     328                 :         12 :         std::string opKind = cvc5::internal::kind::toString(k);
     329                 :         12 :         nodeEncoding += opKind + ",";
     330                 :            : 
     331                 :            :         // For each child, include appropriate encoding
     332         [ +  + ]:         36 :         for (size_t i = n.getNumChildren(); i-- > 0;)
     333                 :            :         {
     334                 :         24 :           Node child = n[i];
     335         [ +  + ]:         24 :           if (child.isConst())
     336                 :            :           {
     337                 :            :             // Include '^' followed by the constant value and '^'
     338                 :          8 :             std::string value = std::to_string(child.getId());
     339                 :          8 :             nodeEncoding += "^" + value + "^,";
     340                 :          8 :           }
     341         [ +  + ]:         16 :           else if (child.isVar())
     342                 :            :           {
     343                 :            :             // Update role map
     344                 :          8 :             std::string symbol = std::to_string(child.getId());
     345                 :            : 
     346                 :          8 :             if (role.find(symbol) == role.end()
     347 [ -  + ][ -  - ]:          8 :                 && n.getKind() != cvc5::internal::Kind::INST_ATTRIBUTE)
                 [ -  + ]
     348                 :            :             {
     349                 :          0 :               role[symbol] = cnt;
     350                 :            :             }
     351                 :          8 :             cnt++;  // Increment cnt whether variable is new or not
     352                 :            : 
     353                 :            :             // Assign ID to variable if not already assigned
     354         [ +  - ]:          8 :             if (symbolMap.find(symbol) == symbolMap.end())
     355                 :            :             {
     356                 :          8 :               symbolMap[symbol] = varIdCounter++;
     357                 :            :             }
     358                 :            : 
     359                 :            :             // Include variable ID
     360                 :          8 :             size_t varId = symbolMap[symbol];
     361                 :          8 :             nodeEncoding += std::to_string(varId) + ",";
     362                 :          8 :           }
     363                 :            :           else
     364                 :            :           {
     365                 :            :             // Subtree: Include '|' followed by its ID and '|'
     366                 :          8 :             size_t childId = subtreeIdMap[child];
     367                 :          8 :             nodeEncoding += "|" + std::to_string(childId) + "|,";
     368                 :            :           }
     369                 :         24 :         }
     370                 :            : 
     371                 :            :         // Remove the last comma
     372         [ +  - ]:         12 :         if (n.getNumChildren() > 0)
     373                 :            :         {
     374                 :         12 :           nodeEncoding.pop_back();
     375                 :            :         }
     376                 :            : 
     377                 :         12 :         nodeEncoding += ";";
     378                 :            : 
     379                 :            :         // Collect the encoding instead of concatenating
     380                 :         12 :         nodeEncodings.push_back(nodeEncoding);
     381                 :            : 
     382                 :            :         // Mark as processed and pop
     383                 :         12 :         it->second = true;
     384                 :         12 :         stack.pop_back();
     385                 :         12 :       }
     386                 :            :     }
     387                 :            :     else
     388                 :            :     {
     389                 :            :       // Node has already been processed
     390                 :          0 :       stack.pop_back();
     391                 :            :     }
     392                 :         40 :   }
     393                 :            : 
     394                 :            :   // Concatenate the node encodings in reverse order
     395         [ +  + ]:         16 :   for (auto it = nodeEncodings.rbegin(); it != nodeEncodings.rend(); ++it)
     396                 :            :   {
     397                 :         12 :     encoding += *it;
     398                 :            :   }
     399                 :          4 : }
     400                 :            : 
     401                 :            : /**
     402                 :            :  * Retrieves information about a node, including its encoding and role map.
     403                 :            :  *
     404                 :            :  * @param node The node to analyze.
     405                 :            :  * @return A unique pointer to the NodeInfo structure containing the node's
     406                 :            :  * details.
     407                 :            :  */
     408                 :          4 : std::unique_ptr<NodeInfo> Normalize::getNodeInfo(const Node& node)
     409                 :            : {
     410                 :          4 :   std::string encoding;
     411                 :          4 :   std::unordered_map<std::string, int32_t> role;
     412                 :          4 :   generateEncoding(node, encoding, role);
     413                 :            : 
     414                 :            :   std::vector<std::pair<std::string, int32_t>> varNames(role.begin(),
     415                 :          4 :                                                         role.end());
     416                 :          4 :   std::sort(varNames.begin(),
     417                 :            :             varNames.end(),
     418                 :          8 :             [](const std::pair<std::string, int32_t>& A,
     419                 :            :                const std::pair<std::string, int32_t>& B) {
     420                 :          8 :               return A.second > B.second;
     421                 :            :             });
     422                 :            : 
     423                 :          8 :   return std::make_unique<NodeInfo>(node, encoding, 0, role, varNames);
     424                 :          4 : }
     425                 :            : 
     426                 :            : /**
     427                 :            :  * Calculates the number of digits in a given non-negative integer.
     428                 :            :  *
     429                 :            :  * @param n The non-negative integer to analyze.
     430                 :            :  * @return The number of digits in the non-negative integer.
     431                 :            :  */
     432                 :          6 : size_t numDigits(size_t n)
     433                 :            : {
     434         [ +  + ]:          6 :   if (n == 0)
     435                 :            :   {
     436                 :          2 :     return 1;
     437                 :            :   }
     438                 :          4 :   int count = 0;
     439         [ +  + ]:          8 :   while (n > 0)
     440                 :            :   {
     441                 :          4 :     n = n / 10;
     442                 :          4 :     count++;
     443                 :            :   }
     444                 :          4 :   return count;
     445                 :            : }
     446                 :            : 
     447                 :            : /**
     448                 :            :  * Renames variables in a node to ensure consistent naming.
     449                 :            :  *
     450                 :            :  * @param n The node to rename.
     451                 :            :  * @param freeVar2node Map for free variable substitutions.
     452                 :            :  * @param boundVar2node Map for bound variable substitutions.
     453                 :            :  * @param normalizedName Map for storing normalized variable names.
     454                 :            :  * @param nodeManager The node manager for creating new nodes.
     455                 :            :  * @param d_preprocContext The preprocessing pass context.
     456                 :            :  * @param sortNormalizer The sort normalizer for type conversion.
     457                 :            :  * @param hasQID Flag indicating if quantifier IDs are present.
     458                 :            :  * @return The renamed node.
     459                 :            :  */
     460                 :          4 : Node rename(const Node& n,
     461                 :            :             std::unordered_map<Node, Node>& freeVar2node,
     462                 :            :             std::unordered_map<Node, Node>& boundVar2node,
     463                 :            :             std::unordered_map<std::string, std::string>& normalizedName,
     464                 :            :             NodeManager* nodeManager,
     465                 :            :             PreprocessingPassContext* d_preprocContext,
     466                 :            :             NormalizeSortNodeConverter& sortNormalizer,
     467                 :            :             bool& hasQID)
     468                 :            : {
     469                 :            :   // Map to cache normalized nodes
     470                 :          4 :   std::unordered_map<Node, Node> normalized;
     471                 :            : 
     472                 :            :   // Stack for iterative traversal
     473                 :          4 :   std::vector<Node> stack;
     474                 :            : 
     475                 :            :   // Map to keep track of visited nodes
     476                 :          4 :   std::unordered_map<Node, bool> visited;
     477                 :            : 
     478                 :            :   // Initialize a global variable counter for variable renaming
     479                 :            :   static size_t globalVarCounter = 0;
     480                 :            : 
     481                 :            :   // Initialize a variable counter for bound variables
     482                 :          4 :   size_t boundVarCounter = 0;
     483                 :          4 :   boundVar2node.clear();
     484                 :            : 
     485                 :            :   // Push the root node onto the stack
     486                 :          4 :   stack.push_back(n);
     487                 :            : 
     488         [ +  + ]:         44 :   while (!stack.empty())
     489                 :            :   {
     490                 :         40 :     Node current = stack.back();
     491                 :            : 
     492                 :         40 :     auto [it, inserted] = visited.emplace(current, false);
     493         [ +  + ]:         40 :     if (inserted)
     494                 :            :     {
     495                 :            :       // First time seeing this node
     496                 :            : 
     497 [ +  + ][ +  + ]:         28 :       if (current.isConst() || current.isVar())
                 [ +  + ]
     498                 :            :       {
     499                 :            :         // For constants and variables, process immediately
     500         [ +  + ]:         16 :         if (current.isVar())
     501                 :            :         {
     502         [ -  + ]:          8 :           if (current.getKind() == cvc5::internal::Kind::BOUND_VARIABLE)
     503                 :            :           {
     504                 :          0 :             auto it_bv = boundVar2node.find(current);
     505         [ -  - ]:          0 :             if (it_bv != boundVar2node.end())
     506                 :            :             {
     507                 :          0 :               normalized[current] = it_bv->second;
     508                 :            :             }
     509                 :            :             else
     510                 :            :             {
     511                 :          0 :               size_t id = boundVarCounter++;
     512                 :            :               std::string new_var_name = "u"
     513                 :          0 :                                          + std::string(8 - numDigits(id), '0')
     514                 :          0 :                                          + std::to_string(id);
     515                 :            :               Node ret = nodeManager->mkBoundVar(
     516                 :          0 :                   new_var_name, sortNormalizer.convertType(current.getType()));
     517                 :            : 
     518                 :          0 :               boundVar2node[current] = ret;
     519                 :          0 :               normalized[current] = ret;
     520                 :          0 :               normalizedName[std::to_string(current.getId())] = new_var_name;
     521                 :          0 :             }
     522                 :            :           }
     523                 :            :           else
     524                 :            :           {
     525                 :          8 :             auto it_fv = freeVar2node.find(current);
     526         [ +  + ]:          8 :             if (it_fv != freeVar2node.end())
     527                 :            :             {
     528                 :          2 :               normalized[current] = it_fv->second;
     529                 :            :             }
     530                 :            :             else
     531                 :            :             {
     532                 :          6 :               std::vector<Node> cnodes;
     533                 :          6 :               size_t id = globalVarCounter++;
     534                 :            :               std::string new_var_name = "v"
     535                 :         12 :                                          + std::string(8 - numDigits(id), '0')
     536                 :         12 :                                          + std::to_string(id);
     537                 :            :               // Create substitution variable with original type for
     538                 :            :               // substitution
     539                 :            :               Node substVar = nodeManager->getSkolemManager()->mkDummySkolem(
     540                 :            :                   new_var_name,
     541                 :          6 :                   current.getType(),
     542                 :         12 :                   SkolemFlags::SKOLEM_EXACT_NAME);
     543                 :            : 
     544                 :            :               // Create normalized variable with normalized type for the final
     545                 :            :               // result
     546                 :            :               Node ret = nodeManager->getSkolemManager()->mkDummySkolem(
     547                 :            :                   new_var_name,
     548                 :         12 :                   sortNormalizer.convertType(current.getType()),
     549                 :         12 :                   SkolemFlags::SKOLEM_EXACT_NAME);
     550                 :            : 
     551                 :          6 :               freeVar2node[current] = ret;
     552                 :          6 :               normalized[current] = ret;
     553                 :          6 :               d_preprocContext->addSubstitution(current, substVar);
     554                 :          6 :               normalizedName[std::to_string(current.getId())] = new_var_name;
     555                 :          6 :             }
     556                 :            :           }
     557                 :            :         }
     558                 :            :         else
     559                 :            :         {
     560                 :            :           // Constants are unchanged
     561                 :          8 :           normalized[current] = current;
     562                 :            :         }
     563                 :            : 
     564                 :            :         // Mark as processed and pop from the stack
     565                 :         16 :         it->second = true;
     566                 :         16 :         stack.pop_back();
     567                 :            :       }
     568                 :            :       else
     569                 :            :       {
     570                 :            :         // Non-const, non-var node
     571                 :            : 
     572                 :         24 :         if (!hasQID
     573 [ +  - ][ -  + ]:         12 :             && current.getKind() == cvc5::internal::Kind::INST_ATTRIBUTE)
                 [ -  + ]
     574                 :            :         {
     575         [ -  - ]:          0 :           for (const Node& child : current)
     576                 :            :           {
     577         [ -  - ]:          0 :             if (child.isVar())
     578                 :            :             {
     579                 :          0 :               hasQID = true;
     580                 :          0 :               break;  // Found at least one qid, no need to check further
     581                 :            :             }
     582         [ -  - ]:          0 :           }
     583                 :            :         }
     584                 :            : 
     585                 :            :         // For quantifiers, process bound variables immediately
     586                 :         12 :         if (current.getKind() == cvc5::internal::Kind::FORALL
     587 [ +  - ][ -  + ]:         12 :             || current.getKind() == cvc5::internal::Kind::EXISTS)
                 [ -  + ]
     588                 :            :         {
     589                 :          0 :           Node bound_vars = current[0];
     590                 :            : 
     591                 :            :           // Normalize bound variables and update boundVar2node
     592                 :          0 :           std::vector<Node> normalizedBoundVars;
     593         [ -  - ]:          0 :           for (const Node& bv : bound_vars)
     594                 :            :           {
     595                 :          0 :             auto it_bv = boundVar2node.find(bv);
     596         [ -  - ]:          0 :             if (it_bv != boundVar2node.end())
     597                 :            :             {
     598                 :          0 :               normalized[bv] = it_bv->second;
     599                 :            :             }
     600                 :            :             else
     601                 :            :             {
     602                 :          0 :               size_t id = boundVarCounter++;
     603                 :            :               std::string new_var_name = "u"
     604                 :          0 :                                          + std::string(8 - numDigits(id), '0')
     605                 :          0 :                                          + std::to_string(id);
     606                 :            : 
     607                 :            :               Node ret = nodeManager->mkBoundVar(
     608                 :          0 :                   new_var_name, sortNormalizer.convertType(bv.getType()));
     609                 :          0 :               boundVar2node[bv] = ret;
     610                 :          0 :               normalized[bv] = ret;
     611                 :          0 :             }
     612                 :          0 :             normalizedBoundVars.push_back(normalized[bv]);
     613                 :          0 :           }
     614                 :            : 
     615                 :            :           Node normalizedBoundVarList = nodeManager->mkNode(
     616                 :          0 :               cvc5::internal::Kind::BOUND_VAR_LIST, normalizedBoundVars);
     617                 :          0 :           normalized[bound_vars] = normalizedBoundVarList;
     618                 :          0 :         }
     619                 :            : 
     620                 :            :         // Push unvisited children onto the stack
     621         [ +  + ]:         36 :         for (int i = current.getNumChildren() - 1; i >= 0; --i)
     622                 :            :         {
     623                 :         24 :           const Node& child = current[i];
     624         [ +  - ]:         24 :           if (visited.find(child) == visited.end())
     625                 :            :           {
     626                 :         24 :             stack.push_back(child);
     627                 :            :           }
     628                 :         24 :         }
     629                 :            : 
     630                 :            :         // For APPLY_UF nodes, push the operator
     631         [ -  + ]:         12 :         if (current.getKind() == cvc5::internal::Kind::APPLY_UF)
     632                 :            :         {
     633                 :          0 :           Node op = current.getOperator();
     634         [ -  - ]:          0 :           if (visited.find(op) == visited.end())
     635                 :            :           {
     636                 :          0 :             stack.push_back(op);
     637                 :            :           }
     638                 :          0 :         }
     639                 :            : 
     640                 :            :         // Leave 'it->second' as false; we'll process this node later
     641                 :            :       }
     642                 :            :     }
     643         [ +  - ]:         12 :     else if (!it->second)
     644                 :            :     {
     645                 :            :       // Second time seeing this node, process it after its children
     646                 :            : 
     647                 :         12 :       std::vector<Node> children;
     648                 :            : 
     649         [ -  + ]:         12 :       if (current.getKind() == cvc5::internal::Kind::APPLY_UF)
     650                 :            :       {
     651                 :            :         // Handle operator for APPLY_UF nodes
     652                 :          0 :         auto opIt = normalized.find(current.getOperator());
     653                 :          0 :         Assert(opIt != normalized.end());
     654                 :          0 :         children.push_back(opIt->second);
     655                 :            :       }
     656         [ -  + ]:         12 :       else if (current.getMetaKind() == metakind::PARAMETERIZED)
     657                 :            :       {
     658                 :            :         // For parameterized nodes, include the operator
     659                 :          0 :         children.push_back(current.getOperator());
     660                 :            :       }
     661                 :            : 
     662         [ +  + ]:         36 :       for (const Node& child : current)
     663                 :            :       {
     664                 :         24 :         auto childIt = normalized.find(child);
     665 [ -  + ][ -  + ]:         24 :         Assert(childIt != normalized.end());
                 [ -  - ]
     666                 :         24 :         children.push_back(childIt->second);
     667                 :         24 :       }
     668                 :            : 
     669                 :         12 :       Node ret = nodeManager->mkNode(current.getKind(), children);
     670                 :         12 :       normalized[current] = ret;
     671                 :            : 
     672                 :            :       // Mark as processed and pop from the stack
     673                 :         12 :       it->second = true;
     674                 :         12 :       stack.pop_back();
     675                 :         12 :     }
     676                 :            :     else
     677                 :            :     {
     678                 :            :       // Node has already been processed
     679                 :          0 :       stack.pop_back();
     680                 :            :     }
     681                 :         40 :   }
     682                 :            : 
     683                 :          8 :   return normalized[n];
     684                 :          4 : }
     685                 :            : 
     686                 :            : /**
     687                 :            :  * Checks if a node represents a trivial equality (e.g., x = x).
     688                 :            :  *
     689                 :            :  * @param n The node to check.
     690                 :            :  * @return True if the node is a trivial equality, false otherwise.
     691                 :            :  */
     692                 :          6 : bool isTrivialEquality(const Node& n)
     693                 :            : {
     694         [ -  + ]:          6 :   if (n.getKind() == cvc5::internal::Kind::EQUAL)
     695                 :            :   {
     696                 :          0 :     const auto &lhs = n[0], rhs = n[1];
     697         [ -  - ]:          0 :     if (lhs == rhs)
     698                 :            :     {
     699                 :          0 :       return true;
     700                 :            :     }
     701 [ -  - ][ -  - ]:          0 :   }
     702                 :          6 :   return false;
     703                 :            : }
     704                 :            : 
     705                 :            : /**
     706                 :            :  * Checks if a node represents the constant true.
     707                 :            :  *
     708                 :            :  * @param n The node to check.
     709                 :            :  * @return True if the node is the constant true, false otherwise.
     710                 :            :  */
     711                 :          6 : bool isTrue(const Node& n)
     712                 :            : {
     713 [ +  + ][ +  - ]:          6 :   if (n.isConst() && n.getType().isBoolean())
         [ +  + ][ +  + ]
                 [ -  - ]
     714                 :            :   {
     715                 :          2 :     return n.getConst<bool>();
     716                 :            :   }
     717                 :          4 :   return false;
     718                 :            : }
     719                 :            : 
     720                 :            : /**
     721                 :            :  * Collects all unique types present in a node's subtree.
     722                 :            :  *
     723                 :            :  * @param n The root node of the subtree.
     724                 :            :  * @param types Vector to store the collected types.
     725                 :            :  * @param visited Set to track visited nodes.
     726                 :            :  * @param mark Set to track collected types.
     727                 :            :  */
     728                 :          4 : void collectTypes(TNode n,
     729                 :            :                   std::vector<TypeNode>& types,
     730                 :            :                   std::unordered_set<TNode>& visited,
     731                 :            :                   std::unordered_set<TypeNode>& mark)
     732                 :            : {
     733                 :          4 :   std::unordered_set<TNode>::iterator it;
     734                 :          4 :   std::vector<TNode> visit;
     735                 :          4 :   TNode cur;
     736                 :          4 :   visit.push_back(n);
     737                 :            :   do
     738                 :            :   {
     739                 :         28 :     cur = visit.back();
     740                 :         28 :     visit.pop_back();
     741                 :         28 :     it = visited.find(cur);
     742         [ +  + ]:         28 :     if (it == visited.end())
     743                 :            :     {
     744                 :         22 :       visited.insert(cur);
     745                 :            : 
     746         [ +  + ]:         22 :       if (mark.find(cur.getType()) == mark.end())
     747                 :            :       {
     748                 :          4 :         mark.insert(cur.getType());
     749                 :          4 :         types.push_back(cur.getType());
     750                 :            :       }
     751                 :            : 
     752                 :            :       // special cases where the type is not part of the AST
     753         [ -  + ]:         22 :       if (cur.getKind() == Kind::CARDINALITY_CONSTRAINT)
     754                 :            :       {
     755                 :          0 :         if (mark.find(
     756                 :          0 :                 cur.getOperator().getConst<CardinalityConstraint>().getType())
     757         [ -  - ]:          0 :             == mark.end())
     758                 :            :         {
     759                 :          0 :           mark.insert(
     760                 :          0 :               cur.getOperator().getConst<CardinalityConstraint>().getType());
     761                 :          0 :           types.push_back(
     762                 :          0 :               cur.getOperator().getConst<CardinalityConstraint>().getType());
     763                 :            :         }
     764                 :            :       }
     765                 :         22 :       visit.insert(visit.end(), cur.begin(), cur.end());
     766                 :            :     }
     767         [ +  + ]:         28 :   } while (!visit.empty());
     768                 :          4 : }
     769                 :            : 
     770                 :            : /**
     771                 :            :  * Renames quantifier IDs (QIDs) in a node to ensure consistent naming.
     772                 :            :  *
     773                 :            :  * @param n The node to rename.
     774                 :            :  * @param qidRenamed Map for storing renamed QIDs.
     775                 :            :  * @param normalizedName Map for storing normalized QID names.
     776                 :            :  * @param nodeManager The node manager for creating new nodes.
     777                 :            :  * @return The renamed node.
     778                 :            :  */
     779                 :          0 : Node renameQid(const Node& n,
     780                 :            :                std::unordered_map<Node, Node>& qidRenamed,
     781                 :            :                std::unordered_map<std::string, std::string>& normalizedName,
     782                 :            :                NodeManager* nodeManager)
     783                 :            : {
     784                 :            :   // Map to cache normalized nodes.
     785                 :          0 :   std::unordered_map<Node, Node> normalized;
     786                 :            :   // Map to track visited nodes (false: first visit; true: processed).
     787                 :          0 :   std::unordered_map<Node, bool> visited;
     788                 :            :   // Stack for iterative traversal.
     789                 :            :   // Each element is a pair: (current node, its parent).
     790                 :          0 :   std::vector<std::pair<Node, Node>> stack;
     791                 :            :   // Global counter for qid renaming (starting at 1).
     792                 :            :   static size_t qidCounter = 1;
     793                 :            : 
     794                 :            :   // Push the root node with a null parent.
     795                 :          0 :   stack.push_back({n, Node()});
     796                 :            : 
     797         [ -  - ]:          0 :   while (!stack.empty())
     798                 :            :   {
     799                 :            :     // Get current node and its parent.
     800                 :          0 :     auto [current, parent] = stack.back();
     801                 :            : 
     802                 :            :     // Try inserting current node into visited.
     803                 :          0 :     auto [it, inserted] = visited.emplace(current, false);
     804         [ -  - ]:          0 :     if (inserted)
     805                 :            :     {
     806                 :            :       // First time seeing this node.
     807                 :          0 :       if (current.isConst() || current.isVar())
     808                 :            :       {
     809                 :            :         // Check if current is a variable and qualifies as a qid.
     810                 :            :         // A qid is defined as a variable whose parent has kind INST_ATTRIBUTE.
     811                 :          0 :         if (current.isVar()
     812                 :          0 :             && (!parent.isNull()
     813         [ -  - ]:          0 :                 && parent.getKind() == cvc5::internal::Kind::INST_ATTRIBUTE))
     814                 :            :         {
     815                 :            :           // If we already renamed this variable, use that renaming.
     816                 :          0 :           auto it_qid = qidRenamed.find(current);
     817         [ -  - ]:          0 :           if (it_qid != qidRenamed.end())
     818                 :            :           {
     819                 :          0 :             normalized[current] = it_qid->second;
     820                 :            :           }
     821                 :            :           else
     822                 :            :           {
     823                 :            :             // Otherwise, generate a new name "q00000001", "q00000002", etc.
     824                 :            :             std::string new_var_name =
     825                 :          0 :                 "q" + std::string(8 - numDigits(qidCounter), '0')
     826                 :          0 :                 + std::to_string(qidCounter);
     827                 :          0 :             qidCounter++;
     828                 :            : 
     829                 :            :             // Create a new dummy skolem (or qid variable) with the new name.
     830                 :            :             Node ret = nodeManager->getSkolemManager()->mkDummySkolem(
     831                 :            :                 new_var_name,
     832                 :          0 :                 current.getType(),
     833                 :          0 :                 SkolemFlags::SKOLEM_EXACT_NAME);
     834                 :          0 :             qidRenamed[current] = ret;
     835                 :          0 :             normalized[current] = ret;
     836                 :          0 :             normalizedName[std::to_string(current.getId())] = new_var_name;
     837                 :          0 :           }
     838                 :            :         }
     839                 :            :         else
     840                 :            :         {
     841                 :            :           // Otherwise, leave the node unchanged.
     842                 :          0 :           normalized[current] = current;
     843                 :            :         }
     844                 :            :         // Mark this node as processed.
     845                 :          0 :         visited[current] = true;
     846                 :          0 :         stack.pop_back();
     847                 :            :       }
     848                 :            :       else
     849                 :            :       {
     850                 :            :         // For non-const and non-var nodes, push their children (with current as
     851                 :            :         // parent) in reverse order to preserve left-to-right traversal.
     852         [ -  - ]:          0 :         for (int i = current.getNumChildren() - 1; i >= 0; --i)
     853                 :            :         {
     854                 :          0 :           Node child = current[i];
     855                 :          0 :           stack.push_back({child, current});
     856                 :          0 :         }
     857                 :            :         // For APPLY_UF nodes, push the operator.
     858         [ -  - ]:          0 :         if (current.getKind() == cvc5::internal::Kind::APPLY_UF)
     859                 :            :         {
     860                 :          0 :           Node op = current.getOperator();
     861                 :          0 :           stack.push_back({op, current});
     862                 :          0 :         }
     863                 :            :         // Leave the node for a second visit.
     864                 :            :       }
     865                 :            :     }
     866         [ -  - ]:          0 :     else if (!it->second)
     867                 :            :     {
     868                 :            :       // Second time visiting the node: all its children have been processed.
     869                 :          0 :       std::vector<Node> children;
     870         [ -  - ]:          0 :       if (current.getKind() == cvc5::internal::Kind::APPLY_UF)
     871                 :            :       {
     872                 :            :         // Add the normalized operator.
     873                 :          0 :         auto opIt = normalized.find(current.getOperator());
     874                 :          0 :         Assert(opIt != normalized.end());
     875                 :          0 :         children.push_back(opIt->second);
     876                 :            :       }
     877         [ -  - ]:          0 :       else if (current.getMetaKind() == metakind::PARAMETERIZED)
     878                 :            :       {
     879                 :            :         // For parameterized nodes, include the operator.
     880                 :          0 :         children.push_back(current.getOperator());
     881                 :            :       }
     882                 :            :       // Add normalized children.
     883         [ -  - ]:          0 :       for (const Node& child : current)
     884                 :            :       {
     885                 :          0 :         auto childIt = normalized.find(child);
     886                 :          0 :         Assert(childIt != normalized.end());
     887                 :          0 :         children.push_back(childIt->second);
     888                 :          0 :       }
     889                 :            :       // Reconstruct the node with its normalized children.
     890                 :          0 :       Node ret = nodeManager->mkNode(current.getKind(), children);
     891                 :          0 :       normalized[current] = ret;
     892                 :          0 :       visited[current] = true;
     893                 :          0 :       stack.pop_back();
     894                 :          0 :     }
     895                 :            :     else
     896                 :            :     {
     897                 :            :       // Node already processed.
     898                 :          0 :       stack.pop_back();
     899                 :            :     }
     900                 :          0 :   }
     901                 :            : 
     902                 :          0 :   return normalized[n];
     903                 :          0 : }
     904                 :            : 
     905                 :          2 : PreprocessingPassResult Normalize::applyInternal(
     906                 :            :     AssertionPipeline* assertionsToPreprocess)
     907                 :            : {
     908                 :          2 :   TimerStat::CodeTimer codeTimer(d_statistics.d_passTime);
     909                 :            : 
     910                 :            :   // ----------------------------------------
     911                 :            :   // Step 1: Collect NodeInfo for all assertions
     912                 :            :   // ----------------------------------------
     913                 :          2 :   std::vector<std::shared_ptr<NodeInfo>> nodeInfos;
     914         [ +  + ]:          8 :   for (const Node& assertion : assertionsToPreprocess->ref())
     915                 :            :   {
     916 [ +  - ][ +  + ]:          6 :     if (isTrivialEquality(assertion) || isTrue(assertion))
                 [ +  + ]
     917                 :            :     {
     918                 :          2 :       continue;
     919                 :            :     }
     920                 :          4 :     auto ni = getNodeInfo(assertion);
     921                 :          4 :     nodeInfos.push_back(std::move(ni));
     922                 :          4 :   }
     923                 :            : 
     924                 :            :   // ----------------------------------------
     925                 :            :   // Step 2: Store symbol occurrences for super-pattern computation
     926                 :            :   // ----------------------------------------
     927                 :            :   std::unordered_map<std::string, std::vector<std::shared_ptr<NodeInfo>>>
     928                 :          2 :       symbolOccurrences;
     929         [ +  + ]:          6 :   for (const auto& nodeInfo : nodeInfos)
     930                 :            :   {
     931         [ +  + ]:         12 :     for (const auto& varName : nodeInfo->varNames)
     932                 :            :     {
     933                 :          8 :       symbolOccurrences[varName.first].push_back(nodeInfo);
     934                 :            :     }
     935                 :            :   }
     936                 :            : 
     937                 :            :   // ----------------------------------------
     938                 :            :   // Step 3: Classify assertions into equivalence classes
     939                 :            :   // ----------------------------------------
     940                 :          2 :   std::vector<std::vector<NodeInfo*>> eqClasses;
     941                 :          2 :   std::unordered_map<std::string, size_t> seenEncodings;
     942         [ +  + ]:          6 :   for (auto& niPtr : nodeInfos)
     943                 :            :   {
     944                 :          4 :     NodeInfo* current = niPtr.get();
     945                 :          4 :     auto it = seenEncodings.find(current->encoding);
     946         [ +  + ]:          4 :     if (it != seenEncodings.end())
     947                 :            :     {
     948                 :          2 :       eqClasses[it->second].push_back(current);
     949                 :            :     }
     950                 :            :     else
     951                 :            :     {
     952                 :          2 :       seenEncodings[current->encoding] = eqClasses.size();
     953                 :          2 :       std::vector<NodeInfo*> newClass;
     954                 :          2 :       newClass.push_back(current);
     955                 :          2 :       eqClasses.push_back(std::move(newClass));
     956                 :          2 :     }
     957                 :            :   }
     958                 :            : 
     959                 :            :   // ----------------------------------------
     960                 :            :   // Step 4: Sort equivalence classes based on encodings
     961                 :            :   // ----------------------------------------
     962                 :          2 :   std::sort(
     963                 :            :       eqClasses.begin(),
     964                 :            :       eqClasses.end(),
     965                 :          0 :       [](const std::vector<NodeInfo*>& a, const std::vector<NodeInfo*>& b) {
     966                 :            :         // Silence false positive:
     967                 :            :         // https://github.com/llvm/llvm-project/issues/78132
     968                 :            :         // NOLINTNEXTLINE(clang-analyzer-cplusplus.Move)
     969                 :          0 :         return a[0]->encoding > b[0]->encoding;
     970                 :            :       });
     971                 :            : 
     972                 :            :   // Assign IDs to equivalence classes for super-pattern computation
     973                 :          2 :   size_t idCnt = 0;
     974         [ +  + ]:          4 :   for (const auto& eqClass : eqClasses)
     975                 :            :   {
     976         [ +  + ]:          6 :     for (const auto& ni : eqClass)
     977                 :            :     {
     978                 :          4 :       ni->setId(idCnt);
     979                 :            :     }
     980                 :          2 :     idCnt++;
     981                 :            :   }
     982                 :            :   // ----------------------------------------
     983                 :            :   // Step 5: Sort within equivalence classes
     984                 :            :   // ----------------------------------------
     985                 :            :   std::unordered_map<std::string, std::vector<std::vector<int32_t>>>
     986                 :          2 :       patternCache;
     987         [ +  + ]:          4 :   for (auto& eqClass : eqClasses)
     988                 :            :   {
     989                 :          2 :     std::sort(eqClass.begin(), eqClass.end(), [&](NodeInfo* a, NodeInfo* b) {
     990                 :          4 :       return compareBySuperpattern(
     991                 :          2 :           a, b, eqClasses, patternCache, symbolOccurrences);
     992                 :            :     });
     993                 :            :   }
     994                 :            : 
     995                 :            :   // ----------------------------------------
     996                 :            :   // Step 6: Collect and normalize types
     997                 :            :   // ----------------------------------------
     998                 :          2 :   std::vector<TypeNode> types;
     999                 :          2 :   std::unordered_set<TNode> visited;
    1000                 :          2 :   std::unordered_set<TypeNode> mark;
    1001         [ +  + ]:          4 :   for (const std::vector<NodeInfo*>& eqClass : eqClasses)
    1002                 :            :   {
    1003         [ +  + ]:          6 :     for (NodeInfo* nodeInfo : eqClass)
    1004                 :            :     {
    1005                 :          4 :       collectTypes(nodeInfo->node, types, visited, mark);
    1006                 :            :     }
    1007                 :            :   }
    1008                 :            : 
    1009                 :          2 :   std::unordered_map<TypeNode, TypeNode> normalizedSorts;
    1010                 :          2 :   int sortCounter = 0;
    1011         [ +  + ]:          6 :   for (const TypeNode& ctn : types)
    1012                 :            :   {
    1013 [ -  + ][ -  - ]:          4 :     if (ctn.isUninterpretedSort() && ctn.getNumChildren() == 0)
                 [ -  + ]
    1014                 :            :     {
    1015                 :            :       std::string new_sort_name = "S"
    1016                 :          0 :                                   + std::string(8 - numDigits(sortCounter), '0')
    1017                 :          0 :                                   + std::to_string(sortCounter);
    1018                 :          0 :       sortCounter++;
    1019                 :          0 :       TypeNode new_sort = nodeManager()->mkSort(new_sort_name);
    1020                 :          0 :       normalizedSorts[ctn] = new_sort;
    1021                 :          0 :     }
    1022                 :            :   }
    1023                 :            : 
    1024                 :          2 :   NormalizeSortNodeConverter sortNormalizer(normalizedSorts, nodeManager());
    1025                 :            : 
    1026                 :            :   // ----------------------------------------
    1027                 :            :   // Step 7: Normalize nodes based on sorted order
    1028                 :            :   // ----------------------------------------
    1029                 :          2 :   std::unordered_map<Node, Node> freeVar2node;
    1030                 :          2 :   std::unordered_map<Node, Node> boundVar2node;
    1031                 :          2 :   std::unordered_map<std::string, std::string> normalizedName;
    1032                 :            : 
    1033                 :          2 :   bool hasQID = false;
    1034         [ +  + ]:          4 :   for (const auto& eqClass : eqClasses)
    1035                 :            :   {
    1036         [ +  + ]:          6 :     for (const auto& ni : eqClass)
    1037                 :            :     {
    1038                 :          4 :       Node renamed = rename(ni->node,
    1039                 :            :                             freeVar2node,
    1040                 :            :                             boundVar2node,
    1041                 :            :                             normalizedName,
    1042                 :            :                             nodeManager(),
    1043                 :            :                             d_preprocContext,
    1044                 :            :                             sortNormalizer,
    1045                 :          4 :                             hasQID);
    1046                 :          4 :       ni->node = renamed;
    1047                 :          4 :     }
    1048                 :            :   }
    1049                 :            : 
    1050                 :            :   // ----------------------------------------
    1051                 :            :   // Step 8: Sort nodes within equivalence classes based on normalized names
    1052                 :            :   // ----------------------------------------
    1053         [ +  + ]:          4 :   for (auto& eqClass : eqClasses)
    1054                 :            :   {
    1055                 :          2 :     std::sort(eqClass.begin(), eqClass.end(), [&](NodeInfo* a, NodeInfo* b) {
    1056                 :          4 :       return compareByNormalizedNames(a, b, normalizedName);
    1057                 :            :     });
    1058                 :            :   }
    1059                 :            : 
    1060                 :            :   // ----------------------------------------
    1061                 :            :   // Step 9: Reassign qid top to bottom
    1062                 :            :   // ----------------------------------------
    1063                 :          2 :   std::unordered_map<Node, Node> qidRenamed;
    1064         [ -  + ]:          2 :   if (hasQID)
    1065                 :            :   {
    1066         [ -  - ]:          0 :     for (const auto& eqClass : eqClasses)
    1067                 :            :     {
    1068         [ -  - ]:          0 :       for (const auto& ni : eqClass)
    1069                 :            :       {
    1070                 :            :         Node renamed =
    1071                 :          0 :             renameQid(ni->node, qidRenamed, normalizedName, nodeManager());
    1072                 :          0 :         ni->node = renamed;
    1073                 :          0 :       }
    1074                 :            :     }
    1075                 :            :   }
    1076                 :            : 
    1077                 :            :   // ----------------------------------------
    1078                 :            :   // Step 10: Replace assertions with normalized versions
    1079                 :            :   // ----------------------------------------
    1080                 :          2 :   size_t idx = 0;
    1081         [ +  + ]:          4 :   for (const auto& eqClass : eqClasses)
    1082                 :            :   {
    1083         [ +  + ]:          6 :     for (const auto& ni : eqClass)
    1084                 :            :     {
    1085                 :          4 :       assertionsToPreprocess->replace(idx++, ni->node);
    1086                 :            :     }
    1087                 :            :   }
    1088                 :          2 :   assertionsToPreprocess->resize(idx);
    1089                 :            : 
    1090                 :          2 :   return PreprocessingPassResult::NO_CONFLICT;
    1091                 :          2 : }
    1092                 :            : 
    1093                 :      52018 : Normalize::Statistics::Statistics(StatisticsRegistry& reg)
    1094                 :      52018 :     : d_passTime(reg.registerTimer("Normalize::pass_time"))
    1095                 :            : {
    1096                 :      52018 : }
    1097                 :            : 
    1098                 :            : }  // namespace passes
    1099                 :            : }  // namespace preprocessing
    1100                 :            : }  // namespace cvc5::internal

Generated by: LCOV version 1.14