LCOV - code coverage report
Current view: top level - src - cluster_linearize.h (source / functions) Coverage Total Hit
Test: fuzz_coverage.info Lines: 98.6 % 722 712
Test Date: 2026-02-19 05:12:30 Functions: 96.9 % 128 124
Branches: 71.1 % 1120 796

             Branch data     Line data    Source code
       1                 :             : // Copyright (c) The Bitcoin Core developers
       2                 :             : // Distributed under the MIT software license, see the accompanying
       3                 :             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       4                 :             : 
       5                 :             : #ifndef BITCOIN_CLUSTER_LINEARIZE_H
       6                 :             : #define BITCOIN_CLUSTER_LINEARIZE_H
       7                 :             : 
       8                 :             : #include <algorithm>
       9                 :             : #include <cstdint>
      10                 :             : #include <numeric>
      11                 :             : #include <optional>
      12                 :             : #include <utility>
      13                 :             : #include <vector>
      14                 :             : 
      15                 :             : #include <attributes.h>
      16                 :             : #include <memusage.h>
      17                 :             : #include <random.h>
      18                 :             : #include <span.h>
      19                 :             : #include <util/feefrac.h>
      20                 :             : #include <util/vecdeque.h>
      21                 :             : 
      22                 :             : namespace cluster_linearize {
      23                 :             : 
      24                 :             : /** Data type to represent transaction indices in DepGraphs and the clusters they represent. */
      25                 :             : using DepGraphIndex = uint32_t;
      26                 :             : 
      27                 :             : /** Data structure that holds a transaction graph's preprocessed data (fee, size, ancestors,
      28                 :             :  *  descendants). */
      29                 :             : template<typename SetType>
      30 [ +  - ][ +  +  :      365225 : class DepGraph
             +  -  +  - ]
      31                 :             : {
      32                 :             :     /** Information about a single transaction. */
      33                 :             :     struct Entry
      34                 :             :     {
      35                 :             :         /** Fee and size of transaction itself. */
      36                 :       32486 :         FeeFrac feerate;
      37                 :             :         /** All ancestors of the transaction (including itself). */
      38                 :       32486 :         SetType ancestors;
      39                 :             :         /** All descendants of the transaction (including itself). */
      40                 :       32486 :         SetType descendants;
      41                 :             : 
      42                 :             :         /** Equality operator (primarily for testing purposes). */
      43   [ +  -  +  -  :       64972 :         friend bool operator==(const Entry&, const Entry&) noexcept = default;
                   -  + ]
      44                 :             : 
      45                 :             :         /** Construct an empty entry. */
      46                 :      169690 :         Entry() noexcept = default;
      47                 :             :         /** Construct an entry with a given feerate, ancestor set, descendant set. */
      48                 :     2620142 :         Entry(const FeeFrac& f, const SetType& a, const SetType& d) noexcept : feerate(f), ancestors(a), descendants(d) {}
      49                 :             :     };
      50                 :             : 
      51                 :             :     /** Data for each transaction. */
      52                 :             :     std::vector<Entry> entries;
      53                 :             : 
      54                 :             :     /** Which positions are used. */
      55                 :             :     SetType m_used;
      56                 :             : 
      57                 :             : public:
      58                 :             :     /** Equality operator (primarily for testing purposes). */
      59                 :        2008 :     friend bool operator==(const DepGraph& a, const DepGraph& b) noexcept
      60                 :             :     {
      61         [ +  - ]:        2008 :         if (a.m_used != b.m_used) return false;
      62                 :             :         // Only compare the used positions within the entries vector.
      63   [ +  +  +  + ]:       36400 :         for (auto idx : a.m_used) {
      64         [ +  - ]:       32486 :             if (a.entries[idx] != b.entries[idx]) return false;
      65                 :             :         }
      66                 :             :         return true;
      67                 :             :     }
      68                 :             : 
      69                 :             :     // Default constructors.
      70                 :      751749 :     DepGraph() noexcept = default;
      71                 :       11817 :     DepGraph(const DepGraph&) noexcept = default;
      72                 :           0 :     DepGraph(DepGraph&&) noexcept = default;
      73                 :      308959 :     DepGraph& operator=(const DepGraph&) noexcept = default;
      74                 :      364097 :     DepGraph& operator=(DepGraph&&) noexcept = default;
      75                 :             : 
      76                 :             :     /** Construct a DepGraph object given another DepGraph and a mapping from old to new.
      77                 :             :      *
      78                 :             :      * @param depgraph   The original DepGraph that is being remapped.
      79                 :             :      *
      80                 :             :      * @param mapping    A span such that mapping[i] gives the position in the new DepGraph
      81                 :             :      *                   for position i in the old depgraph. Its size must be equal to
      82                 :             :      *                   depgraph.PositionRange(). The value of mapping[i] is ignored if
      83                 :             :      *                   position i is a hole in depgraph (i.e., if !depgraph.Positions()[i]).
      84                 :             :      *
      85                 :             :      * @param pos_range  The PositionRange() for the new DepGraph. It must equal the largest
      86                 :             :      *                   value in mapping for any used position in depgraph plus 1, or 0 if
      87                 :             :      *                   depgraph.TxCount() == 0.
      88                 :             :      *
      89                 :             :      * Complexity: O(N^2) where N=depgraph.TxCount().
      90                 :             :      */
      91         [ -  + ]:        6295 :     DepGraph(const DepGraph<SetType>& depgraph, std::span<const DepGraphIndex> mapping, DepGraphIndex pos_range) noexcept : entries(pos_range)
      92                 :             :     {
      93         [ -  + ]:        6295 :         Assume(mapping.size() == depgraph.PositionRange());
      94                 :       12590 :         Assume((pos_range == 0) == (depgraph.TxCount() == 0));
      95         [ +  + ]:      123440 :         for (DepGraphIndex i : depgraph.Positions()) {
      96         [ -  + ]:      117145 :             auto new_idx = mapping[i];
      97         [ -  + ]:      117145 :             Assume(new_idx < pos_range);
      98                 :             :             // Add transaction.
      99                 :      117145 :             entries[new_idx].ancestors = SetType::Singleton(new_idx);
     100                 :      117145 :             entries[new_idx].descendants = SetType::Singleton(new_idx);
     101                 :      117145 :             m_used.Set(new_idx);
     102                 :             :             // Fill in fee and size.
     103                 :      117145 :             entries[new_idx].feerate = depgraph.entries[i].feerate;
     104                 :             :         }
     105         [ +  + ]:      123440 :         for (DepGraphIndex i : depgraph.Positions()) {
     106                 :             :             // Fill in dependencies by mapping direct parents.
     107                 :      117145 :             SetType parents;
     108   [ +  +  +  + ]:      263998 :             for (auto j : depgraph.GetReducedParents(i)) parents.Set(mapping[j]);
     109                 :      117145 :             AddDependencies(parents, mapping[i]);
     110                 :             :         }
     111                 :             :         // Verify that the provided pos_range was correct (no unused positions at the end).
     112   [ +  +  -  + ]:        6295 :         Assume(m_used.None() ? (pos_range == 0) : (pos_range == m_used.Last() + 1));
     113                 :        6295 :     }
     114                 :             : 
     115                 :             :     /** Get the set of transactions positions in use. Complexity: O(1). */
     116   [ +  +  +  +  :     9559238 :     const SetType& Positions() const noexcept { return m_used; }
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
             -  +  +  +  
                      + ]
     117                 :             :     /** Get the range of positions in this DepGraph. All entries in Positions() are in [0, PositionRange() - 1]. */
     118   [ -  +  -  +  :      758744 :     DepGraphIndex PositionRange() const noexcept { return entries.size(); }
          -  +  +  +  -  
          +  -  +  -  +  
          +  -  -  +  +  
             -  -  +  +  
           + ][ -  +  +  
          -  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
           # ][ -  +  -  
          +  -  +  -  +  
                   -  + ]
     119                 :             :     /** Get the number of transactions in the graph. Complexity: O(1). */
     120   [ -  +  +  -  :      511642 :     auto TxCount() const noexcept { return m_used.Count(); }
          +  +  -  +  -  
          +  +  +  +  +  
          +  -  +  +  +  
             +  +  -  -  
           + ][ -  +  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
                      # ]
     121                 :             :     /** Get the feerate of a given transaction i. Complexity: O(1). */
     122   [ +  -  +  + ]:     6496046 :     const FeeFrac& FeeRate(DepGraphIndex i) const noexcept { return entries[i].feerate; }
     123                 :             :     /** Get the mutable feerate of a given transaction i. Complexity: O(1). */
     124   [ -  +  -  +  :    12271855 :     FeeFrac& FeeRate(DepGraphIndex i) noexcept { return entries[i].feerate; }
          +  +  +  -  #  
           # ][ +  -  -  
          +  -  +  +  -  
                   +  - ]
           [ +  +  +  - ]
     125                 :             :     /** Get the ancestors of a given transaction i. Complexity: O(1). */
     126   [ +  +  +  +  :   177515907 :     const SetType& Ancestors(DepGraphIndex i) const noexcept { return entries[i].ancestors; }
          +  +  +  -  -  
          +  +  +  +  +  
          +  +  +  -  -  
          +  +  -  +  +  
          +  +  -  +  -  
             +  +  +  -  
           + ][ +  -  +  
                +  -  + ]
     127                 :             :     /** Get the descendants of a given transaction i. Complexity: O(1). */
     128   [ +  +  -  +  :    51651476 :     const SetType& Descendants(DepGraphIndex i) const noexcept { return entries[i].descendants; }
             +  -  -  + ]
                 [ +  - ]
     129                 :             : 
     130                 :             :     /** Add a new unconnected transaction to this transaction graph (in the first available
     131                 :             :      *  position), and return its DepGraphIndex.
     132                 :             :      *
     133                 :             :      * Complexity: O(1) (amortized, due to resizing of backing vector).
     134                 :             :      */
     135                 :     2666724 :     DepGraphIndex AddTransaction(const FeeFrac& feefrac) noexcept
     136                 :             :     {
     137                 :             :         static constexpr auto ALL_POSITIONS = SetType::Fill(SetType::Size());
     138         [ -  + ]:     2666724 :         auto available = ALL_POSITIONS - m_used;
     139         [ -  + ]:     2950867 :         Assume(available.Any());
     140                 :     2666724 :         DepGraphIndex new_idx = available.First();
     141   [ -  +  +  + ]:     2666724 :         if (new_idx == entries.size()) {
     142                 :     2620142 :             entries.emplace_back(feefrac, SetType::Singleton(new_idx), SetType::Singleton(new_idx));
     143                 :             :         } else {
     144                 :       46582 :             entries[new_idx] = Entry(feefrac, SetType::Singleton(new_idx), SetType::Singleton(new_idx));
     145                 :             :         }
     146                 :     2666724 :         m_used.Set(new_idx);
     147                 :     2666724 :         return new_idx;
     148                 :             :     }
     149                 :             : 
     150                 :             :     /** Remove the specified positions from this DepGraph.
     151                 :             :      *
     152                 :             :      * The specified positions will no longer be part of Positions(), and dependencies with them are
     153                 :             :      * removed. Note that due to DepGraph only tracking ancestors/descendants (and not direct
     154                 :             :      * dependencies), if a parent is removed while a grandparent remains, the grandparent will
     155                 :             :      * remain an ancestor.
     156                 :             :      *
     157                 :             :      * Complexity: O(N) where N=TxCount().
     158                 :             :      */
     159                 :      445703 :     void RemoveTransactions(const SetType& del) noexcept
     160                 :             :     {
     161                 :      445703 :         m_used -= del;
     162                 :             :         // Remove now-unused trailing entries.
     163   [ +  +  -  +  :      988345 :         while (!entries.empty() && !m_used[entries.size() - 1]) {
                   +  + ]
     164                 :      542642 :             entries.pop_back();
     165                 :             :         }
     166                 :             :         // Remove the deleted transactions from ancestors/descendants of other transactions. Note
     167                 :             :         // that the deleted positions will retain old feerate and dependency information. This does
     168                 :             :         // not matter as they will be overwritten by AddTransaction if they get used again.
     169         [ +  + ]:    10801236 :         for (auto& entry : entries) {
     170         [ +  + ]:    27958271 :             entry.ancestors &= m_used;
     171         [ +  + ]:    27958271 :             entry.descendants &= m_used;
     172                 :             :         }
     173                 :      445703 :     }
     174                 :             : 
     175                 :             :     /** Modify this transaction graph, adding multiple parents to a specified child.
     176                 :             :      *
     177                 :             :      * Complexity: O(N) where N=TxCount().
     178                 :             :      */
     179                 :     3621219 :     void AddDependencies(const SetType& parents, DepGraphIndex child) noexcept
     180                 :             :     {
     181         [ -  + ]:     3621219 :         Assume(m_used[child]);
     182         [ -  + ]:     4065054 :         Assume(parents.IsSubsetOf(m_used));
     183                 :             :         // Compute the ancestors of parents that are not already ancestors of child.
     184         [ +  + ]:     3621219 :         SetType par_anc;
     185   [ +  +  +  + ]:     7898062 :         for (auto par : parents - Ancestors(child)) {
           [ +  +  #  # ]
     186                 :     3004451 :             par_anc |= Ancestors(par);
     187                 :             :         }
     188         [ +  + ]:     3621219 :         par_anc -= Ancestors(child);
     189                 :             :         // Bail out if there are no such ancestors.
     190         [ +  + ]:     3621219 :         if (par_anc.None()) return;
     191                 :             :         // To each such ancestor, add as descendants the descendants of the child.
     192                 :     1855980 :         const auto& chl_des = entries[child].descendants;
     193         [ +  + ]:     8376526 :         for (auto anc_of_par : par_anc) {
     194                 :     7038812 :             entries[anc_of_par].descendants |= chl_des;
     195                 :             :         }
     196                 :             :         // To each descendant of the child, add those ancestors.
     197   [ +  -  +  + ]:     5721091 :         for (auto dec_of_chl : Descendants(child)) {
           [ +  +  #  # ]
     198                 :     2853694 :             entries[dec_of_chl].ancestors |= par_anc;
     199                 :             :         }
     200                 :             :     }
     201                 :             : 
     202                 :             :     /** Compute the (reduced) set of parents of node i in this graph.
     203                 :             :      *
     204                 :             :      * This returns the minimal subset of the parents of i whose ancestors together equal all of
     205                 :             :      * i's ancestors (unless i is part of a cycle of dependencies). Note that DepGraph does not
     206                 :             :      * store the set of parents; this information is inferred from the ancestor sets.
     207                 :             :      *
     208                 :             :      * Complexity: O(N) where N=Ancestors(i).Count() (which is bounded by TxCount()).
     209                 :             :      */
     210                 :     8717711 :     SetType GetReducedParents(DepGraphIndex i) const noexcept
     211                 :             :     {
     212                 :     8717711 :         SetType parents = Ancestors(i);
     213                 :     8717711 :         parents.Reset(i);
     214   [ +  +  +  + ]:    47974226 :         for (auto parent : parents) {
           [ +  +  #  # ]
     215         [ +  + ]:    33282687 :             if (parents[parent]) {
     216                 :    31739286 :                 parents -= Ancestors(parent);
     217                 :    31739286 :                 parents.Set(parent);
     218                 :             :             }
     219                 :             :         }
     220                 :     8717711 :         return parents;
     221                 :             :     }
     222                 :             : 
     223                 :             :     /** Compute the (reduced) set of children of node i in this graph.
     224                 :             :      *
     225                 :             :      * This returns the minimal subset of the children of i whose descendants together equal all of
     226                 :             :      * i's descendants (unless i is part of a cycle of dependencies). Note that DepGraph does not
     227                 :             :      * store the set of children; this information is inferred from the descendant sets.
     228                 :             :      *
     229                 :             :      * Complexity: O(N) where N=Descendants(i).Count() (which is bounded by TxCount()).
     230                 :             :      */
     231                 :      236087 :     SetType GetReducedChildren(DepGraphIndex i) const noexcept
     232                 :             :     {
     233                 :      236087 :         SetType children = Descendants(i);
     234                 :      236087 :         children.Reset(i);
     235   [ +  +  +  + ]:     1609741 :         for (auto child : children) {
     236         [ +  + ]:     1213836 :             if (children[child]) {
     237                 :      393253 :                 children -= Descendants(child);
     238                 :      393253 :                 children.Set(child);
     239                 :             :             }
     240                 :             :         }
     241                 :      236087 :         return children;
     242                 :             :     }
     243                 :             : 
     244                 :             :     /** Compute the aggregate feerate of a set of nodes in this graph.
     245                 :             :      *
     246                 :             :      * Complexity: O(N) where N=elems.Count().
     247                 :             :      **/
     248                 :    34540845 :     FeeFrac FeeRate(const SetType& elems) const noexcept
     249                 :             :     {
     250                 :    34540845 :         FeeFrac ret;
     251   [ +  -  +  + ]:   671910787 :         for (auto pos : elems) ret += entries[pos].feerate;
           [ +  +  #  # ]
     252                 :    34540845 :         return ret;
     253                 :             :     }
     254                 :             : 
     255                 :             :     /** Get the connected component within the subset "todo" that contains tx (which must be in
     256                 :             :      *  todo).
     257                 :             :      *
     258                 :             :      * Two transactions are considered connected if they are both in `todo`, and one is an ancestor
     259                 :             :      * of the other in the entire graph (so not just within `todo`), or transitively there is a
     260                 :             :      * path of transactions connecting them. This does mean that if `todo` contains a transaction
     261                 :             :      * and a grandparent, but misses the parent, they will still be part of the same component.
     262                 :             :      *
     263                 :             :      * Complexity: O(ret.Count()).
     264                 :             :      */
     265                 :     6726338 :     SetType GetConnectedComponent(const SetType& todo, DepGraphIndex tx) const noexcept
     266                 :             :     {
     267         [ -  + ]:     6726338 :         Assume(todo[tx]);
     268         [ -  + ]:    13003416 :         Assume(todo.IsSubsetOf(m_used));
     269                 :     6726338 :         auto to_add = SetType::Singleton(tx);
     270                 :     6726338 :         SetType ret;
     271                 :             :         do {
     272                 :    14548784 :             SetType old = ret;
     273   [ +  -  +  + ]:    41297774 :             for (auto add : to_add) {
           [ +  +  #  # ]
     274                 :    48628193 :                 ret |= Descendants(add);
     275                 :    48628193 :                 ret |= Ancestors(add);
     276                 :             :             }
     277         [ +  + ]:    14548784 :             ret &= todo;
     278                 :    14548784 :             to_add = ret - old;
     279         [ +  + ]:    28009783 :         } while (to_add.Any());
     280                 :     6726338 :         return ret;
     281                 :             :     }
     282                 :             : 
     283                 :             :     /** Find some connected component within the subset "todo" of this graph.
     284                 :             :      *
     285                 :             :      * Specifically, this finds the connected component which contains the first transaction of
     286                 :             :      * todo (if any).
     287                 :             :      *
     288                 :             :      * Complexity: O(ret.Count()).
     289                 :             :      */
     290         [ +  + ]:     5563001 :     SetType FindConnectedComponent(const SetType& todo) const noexcept
     291                 :             :     {
     292         [ +  + ]:     5563001 :         if (todo.None()) return todo;
     293                 :     5555492 :         return GetConnectedComponent(todo, todo.First());
     294                 :             :     }
     295                 :             : 
     296                 :             :     /** Determine if a subset is connected.
     297                 :             :      *
     298                 :             :      * Complexity: O(subset.Count()).
     299                 :             :      */
     300                 :     2100899 :     bool IsConnected(const SetType& subset) const noexcept
     301                 :             :     {
     302         [ +  + ]:     2100899 :         return FindConnectedComponent(subset) == subset;
     303                 :             :     }
     304                 :             : 
     305                 :             :     /** Determine if this entire graph is connected.
     306                 :             :      *
     307                 :             :      * Complexity: O(TxCount()).
     308                 :             :      */
     309                 :         432 :     bool IsConnected() const noexcept { return IsConnected(m_used); }
     310                 :             : 
     311                 :             :     /** Append the entries of select to list in a topologically valid order.
     312                 :             :      *
     313                 :             :      * Complexity: O(select.Count() * log(select.Count())).
     314                 :             :      */
     315         [ -  + ]:       19847 :     void AppendTopo(std::vector<DepGraphIndex>& list, const SetType& select) const noexcept
     316                 :             :     {
     317                 :       19847 :         DepGraphIndex old_len = list.size();
     318   [ +  -  +  + ]:       80084 :         for (auto i : select) list.push_back(i);
     319                 :       19847 :         std::sort(list.begin() + old_len, list.end(), [&](DepGraphIndex a, DepGraphIndex b) noexcept {
     320         [ +  + ]:       99030 :             const auto a_anc_count = entries[a].ancestors.Count();
     321                 :       99030 :             const auto b_anc_count = entries[b].ancestors.Count();
     322         [ +  + ]:       99030 :             if (a_anc_count != b_anc_count) return a_anc_count < b_anc_count;
     323                 :       29873 :             return a < b;
     324                 :             :         });
     325                 :       19847 :     }
     326                 :             : 
     327                 :             :     /** Check if this graph is acyclic. */
     328                 :       47661 :     bool IsAcyclic() const noexcept
     329                 :             :     {
     330   [ +  +  +  + ]:      419890 :         for (auto i : Positions()) {
     331         [ +  + ]:      324917 :             if ((Ancestors(i) & Descendants(i)) != SetType::Singleton(i)) {
     332                 :             :                 return false;
     333                 :             :             }
     334                 :             :         }
     335                 :             :         return true;
     336                 :             :     }
     337                 :             : 
     338                 :             :     unsigned CountDependencies() const noexcept
     339                 :             :     {
     340                 :             :         unsigned ret = 0;
     341                 :             :         for (auto i : Positions()) {
     342                 :             :             ret += GetReducedParents(i).Count();
     343                 :             :         }
     344                 :             :         return ret;
     345                 :             :     }
     346                 :             : 
     347                 :             :     /** Reduce memory usage if possible. No observable effect. */
     348                 :      972760 :     void Compact() noexcept
     349                 :             :     {
     350         [ -  + ]:      972760 :         entries.shrink_to_fit();
     351                 :             :     }
     352                 :             : 
     353                 :     2277189 :     size_t DynamicMemoryUsage() const noexcept
     354                 :             :     {
     355   [ -  +  -  +  :     2315479 :         return memusage::DynamicUsage(entries);
           -  + ][ -  + ]
     356                 :             :     }
     357                 :             : };
     358                 :             : 
     359                 :             : /** A set of transactions together with their aggregate feerate. */
     360                 :             : template<typename SetType>
     361                 :             : struct SetInfo
     362                 :             : {
     363                 :             :     /** The transactions in the set. */
     364                 :         609 :     SetType transactions;
     365                 :             :     /** Their combined fee and size. */
     366                 :         609 :     FeeFrac feerate;
     367                 :             : 
     368                 :             :     /** Construct a SetInfo for the empty set. */
     369                 :     7262818 :     SetInfo() noexcept = default;
     370                 :             : 
     371                 :             :     /** Construct a SetInfo for a specified set and feerate. */
     372                 :        1472 :     SetInfo(const SetType& txn, const FeeFrac& fr) noexcept : transactions(txn), feerate(fr) {}
     373                 :             : 
     374                 :             :     /** Construct a SetInfo for a given transaction in a depgraph. */
     375                 :    17548661 :     explicit SetInfo(const DepGraph<SetType>& depgraph, DepGraphIndex pos) noexcept :
     376                 :    17548661 :         transactions(SetType::Singleton(pos)), feerate(depgraph.FeeRate(pos)) {}
     377                 :             : 
     378                 :             :     /** Construct a SetInfo for a set of transactions in a depgraph. */
     379                 :    34436419 :     explicit SetInfo(const DepGraph<SetType>& depgraph, const SetType& txn) noexcept :
     380                 :    34436419 :         transactions(txn), feerate(depgraph.FeeRate(txn)) {}
     381                 :             : 
     382                 :             :     /** Add a transaction to this SetInfo (which must not yet be in it). */
     383                 :        6737 :     void Set(const DepGraph<SetType>& depgraph, DepGraphIndex pos) noexcept
     384                 :             :     {
     385         [ -  + ]:        6737 :         Assume(!transactions[pos]);
     386                 :        6737 :         transactions.Set(pos);
     387                 :        6737 :         feerate += depgraph.FeeRate(pos);
     388                 :        6737 :     }
     389                 :             : 
     390                 :             :     /** Add the transactions of other to this SetInfo (no overlap allowed). */
     391                 :    14499255 :     SetInfo& operator|=(const SetInfo& other) noexcept
     392                 :             :     {
     393         [ -  + ]:    16122541 :         Assume(!transactions.Overlaps(other.transactions));
     394                 :    14499255 :         transactions |= other.transactions;
     395                 :    14499255 :         feerate += other.feerate;
     396                 :    14499255 :         return *this;
     397                 :             :     }
     398                 :             : 
     399                 :             :     /** Remove the transactions of other from this SetInfo (which must be a subset). */
     400                 :     1764578 :     SetInfo& operator-=(const SetInfo& other) noexcept
     401                 :             :     {
     402         [ -  + ]:     1770425 :         Assume(other.transactions.IsSubsetOf(transactions));
     403                 :     1764578 :         transactions -= other.transactions;
     404                 :     1764578 :         feerate -= other.feerate;
     405                 :     1764578 :         return *this;
     406                 :             :     }
     407                 :             : 
     408                 :             :     /** Compute the difference between this and other SetInfo (which must be a subset). */
     409                 :             :     SetInfo operator-(const SetInfo& other) const noexcept
     410                 :             :     {
     411                 :             :         Assume(other.transactions.IsSubsetOf(transactions));
     412                 :             :         return {transactions - other.transactions, feerate - other.feerate};
     413                 :             :     }
     414                 :             : 
     415                 :             :     /** Swap two SetInfo objects. */
     416                 :             :     friend void swap(SetInfo& a, SetInfo& b) noexcept
     417                 :             :     {
     418                 :             :         swap(a.transactions, b.transactions);
     419                 :             :         swap(a.feerate, b.feerate);
     420                 :             :     }
     421                 :             : 
     422                 :             :     /** Permit equality testing. */
     423   [ +  -  +  - ]:        1218 :     friend bool operator==(const SetInfo&, const SetInfo&) noexcept = default;
     424                 :             : };
     425                 :             : 
     426                 :             : /** Compute the chunks of linearization as SetInfos. */
     427                 :             : template<typename SetType>
     428                 :     1207399 : std::vector<SetInfo<SetType>> ChunkLinearizationInfo(const DepGraph<SetType>& depgraph, std::span<const DepGraphIndex> linearization) noexcept
     429                 :             : {
     430                 :     1207399 :     std::vector<SetInfo<SetType>> ret;
     431         [ +  + ]:    11493242 :     for (DepGraphIndex i : linearization) {
     432                 :             :         /** The new chunk to be added, initially a singleton. */
     433                 :    10285843 :         SetInfo<SetType> new_chunk(depgraph, i);
     434                 :             :         // As long as the new chunk has a higher feerate than the last chunk so far, absorb it.
     435   [ +  +  +  + ]:    14828386 :         while (!ret.empty() && new_chunk.feerate >> ret.back().feerate) {
     436                 :     4542543 :             new_chunk |= ret.back();
     437                 :     4542543 :             ret.pop_back();
     438                 :             :         }
     439                 :             :         // Actually move that new chunk into the chunking.
     440                 :    10285843 :         ret.emplace_back(std::move(new_chunk));
     441                 :             :     }
     442                 :     1207399 :     return ret;
     443                 :             : }
     444                 :             : 
     445                 :             : /** Compute the feerates of the chunks of linearization. Identical to ChunkLinearizationInfo, but
     446                 :             :  *  only returns the chunk feerates, not the corresponding transaction sets. */
     447                 :             : template<typename SetType>
     448                 :     1431081 : std::vector<FeeFrac> ChunkLinearization(const DepGraph<SetType>& depgraph, std::span<const DepGraphIndex> linearization) noexcept
     449                 :             : {
     450                 :     1431081 :     std::vector<FeeFrac> ret;
     451         [ +  + ]:    10939765 :     for (DepGraphIndex i : linearization) {
     452                 :             :         /** The new chunk to be added, initially a singleton. */
     453                 :     9508684 :         auto new_chunk = depgraph.FeeRate(i);
     454                 :             :         // As long as the new chunk has a higher feerate than the last chunk so far, absorb it.
     455   [ +  +  +  + ]:    14242965 :         while (!ret.empty() && new_chunk >> ret.back()) {
     456                 :     4734281 :             new_chunk += ret.back();
     457                 :     4734281 :             ret.pop_back();
     458                 :             :         }
     459                 :             :         // Actually move that new chunk into the chunking.
     460                 :     9508684 :         ret.push_back(std::move(new_chunk));
     461                 :             :     }
     462                 :     1431081 :     return ret;
     463                 :             : }
     464                 :             : 
     465                 :             : /** Concept for function objects that return std::strong_ordering when invoked with two Args. */
     466                 :             : template<typename F, typename Arg>
     467                 :             : concept StrongComparator =
     468                 :             :     std::regular_invocable<F, Arg, Arg> &&
     469                 :             :     std::is_same_v<std::invoke_result_t<F, Arg, Arg>, std::strong_ordering>;
     470                 :             : 
     471                 :             : /** Simple default transaction ordering function for SpanningForestState::GetLinearization() and
     472                 :             :  *  Linearize(), which just sorts by DepGraphIndex. */
     473                 :             : using IndexTxOrder = std::compare_three_way;
     474                 :             : 
     475                 :             : /** Class to represent the internal state of the spanning-forest linearization (SFL) algorithm.
     476                 :             :  *
     477                 :             :  * At all times, each dependency is marked as either "active" or "inactive". The subset of active
     478                 :             :  * dependencies is the state of the SFL algorithm. The implementation maintains several other
     479                 :             :  * values to speed up operations, but everything is ultimately a function of what that subset of
     480                 :             :  * active dependencies is.
     481                 :             :  *
     482                 :             :  * Given such a subset, define a chunk as the set of transactions that are connected through active
     483                 :             :  * dependencies (ignoring their parent/child direction). Thus, every state implies a particular
     484                 :             :  * partitioning of the graph into chunks (including potential singletons). In the extreme, each
     485                 :             :  * transaction may be in its own chunk, or in the other extreme all transactions may form a single
     486                 :             :  * chunk. A chunk's feerate is its total fee divided by its total size.
     487                 :             :  *
     488                 :             :  * The algorithm consists of switching dependencies between active and inactive. The final
     489                 :             :  * linearization that is produced at the end consists of these chunks, sorted from high to low
     490                 :             :  * feerate, each individually sorted in an arbitrary but topological (= no child before parent)
     491                 :             :  * way.
     492                 :             :  *
     493                 :             :  * We define four quality properties the state can have:
     494                 :             :  *
     495                 :             :  * - acyclic: The state is acyclic whenever no cycle of active dependencies exists within the
     496                 :             :  *            graph, ignoring the parent/child direction. This is equivalent to saying that within
     497                 :             :  *            each chunk the set of active dependencies form a tree, and thus the overall set of
     498                 :             :  *            active dependencies in the graph form a spanning forest, giving the algorithm its
     499                 :             :  *            name. Being acyclic is also equivalent to every chunk of N transactions having
     500                 :             :  *            exactly N-1 active dependencies.
     501                 :             :  *
     502                 :             :  *            For example in a diamond graph, D->{B,C}->A, the 4 dependencies cannot be
     503                 :             :  *            simultaneously active. If at least one is inactive, the state is acyclic.
     504                 :             :  *
     505                 :             :  *            The algorithm maintains an acyclic state at *all* times as an invariant. This implies
     506                 :             :  *            that activating a dependency always corresponds to merging two chunks, and that
     507                 :             :  *            deactivating one always corresponds to splitting two chunks.
     508                 :             :  *
     509                 :             :  * - topological: We say the state is topological whenever it is acyclic and no inactive dependency
     510                 :             :  *                exists between two distinct chunks such that the child chunk has higher or equal
     511                 :             :  *                feerate than the parent chunk.
     512                 :             :  *
     513                 :             :  *                The relevance is that whenever the state is topological, the produced output
     514                 :             :  *                linearization will be topological too (i.e., not have children before parents).
     515                 :             :  *                Note that the "or equal" part of the definition matters: if not, one can end up
     516                 :             :  *                in a situation with mutually-dependent equal-feerate chunks that cannot be
     517                 :             :  *                linearized. For example C->{A,B} and D->{A,B}, with C->A and D->B active. The AC
     518                 :             :  *                chunk depends on DB through C->B, and the BD chunk depends on AC through D->A.
     519                 :             :  *                Merging them into a single ABCD chunk fixes this.
     520                 :             :  *
     521                 :             :  *                The algorithm attempts to keep the state topological as much as possible, so it
     522                 :             :  *                can be interrupted to produce an output whenever, but will sometimes need to
     523                 :             :  *                temporarily deviate from it when improving the state.
     524                 :             :  *
     525                 :             :  * - optimal: For every active dependency, define its top and bottom set as the set of transactions
     526                 :             :  *            in the chunks that would result if the dependency were deactivated; the top being the
     527                 :             :  *            one with the dependency's parent, and the bottom being the one with the child. Note
     528                 :             :  *            that due to acyclicity, every deactivation splits a chunk exactly in two.
     529                 :             :  *
     530                 :             :  *            We say the state is optimal whenever it is topological and it has no active
     531                 :             :  *            dependency whose top feerate is strictly higher than its bottom feerate. The
     532                 :             :  *            relevance is that it can be proven that whenever the state is optimal, the produced
     533                 :             :  *            linearization will also be optimal (in the convexified feerate diagram sense). It can
     534                 :             :  *            also be proven that for every graph at least one optimal state exists.
     535                 :             :  *
     536                 :             :  *            Note that it is possible for the SFL state to not be optimal, but the produced
     537                 :             :  *            linearization to still be optimal. This happens when the chunks of a state are
     538                 :             :  *            identical to those of an optimal state, but the exact set of active dependencies
     539                 :             :  *            within a chunk differ in such a way that the state optimality condition is not
     540                 :             :  *            satisfied. Thus, the state being optimal is more a "the eventual output is *known*
     541                 :             :  *            to be optimal".
     542                 :             :  *
     543                 :             :  * - minimal: We say the state is minimal when it is:
     544                 :             :  *            - acyclic
     545                 :             :  *            - topological, except that inactive dependencies between equal-feerate chunks are
     546                 :             :  *              allowed as long as they do not form a loop.
     547                 :             :  *            - like optimal, no active dependencies whose top feerate is strictly higher than
     548                 :             :  *              the bottom feerate are allowed.
     549                 :             :  *            - no chunk contains a proper non-empty subset which includes all its own in-chunk
     550                 :             :  *              dependencies of the same feerate as the chunk itself.
     551                 :             :  *
     552                 :             :  *            A minimal state effectively corresponds to an optimal state, where every chunk has
     553                 :             :  *            been split into its minimal equal-feerate components.
     554                 :             :  *
     555                 :             :  *            The algorithm terminates whenever a minimal state is reached.
     556                 :             :  *
     557                 :             :  *
     558                 :             :  * This leads to the following high-level algorithm:
     559                 :             :  * - Start with all dependencies inactive, and thus all transactions in their own chunk. This is
     560                 :             :  *   definitely acyclic.
     561                 :             :  * - Activate dependencies (merging chunks) until the state is topological.
     562                 :             :  * - Loop until optimal (no dependencies with higher-feerate top than bottom), or time runs out:
     563                 :             :  *   - Deactivate a violating dependency, potentially making the state non-topological.
     564                 :             :  *   - Activate other dependencies to make the state topological again.
     565                 :             :  * - If there is time left and the state is optimal:
     566                 :             :  *   - Attempt to split chunks into equal-feerate parts without mutual dependencies between them.
     567                 :             :  *     When this succeeds, recurse into them.
     568                 :             :  *   - If no such chunks can be found, the state is minimal.
     569                 :             :  * - Output the chunks from high to low feerate, each internally sorted topologically.
     570                 :             :  *
     571                 :             :  * When merging, we always either:
     572                 :             :  * - Merge upwards: merge a chunk with the lowest-feerate other chunk it depends on, among those
     573                 :             :  *                  with lower or equal feerate than itself.
     574                 :             :  * - Merge downwards: merge a chunk with the highest-feerate other chunk that depends on it, among
     575                 :             :  *                    those with higher or equal feerate than itself.
     576                 :             :  *
     577                 :             :  * Using these strategies in the improvement loop above guarantees that the output linearization
     578                 :             :  * after a deactivate + merge step is never worse or incomparable (in the convexified feerate
     579                 :             :  * diagram sense) than the output linearization that would be produced before the step. With that,
     580                 :             :  * we can refine the high-level algorithm to:
     581                 :             :  * - Start with all dependencies inactive.
     582                 :             :  * - Perform merges as described until none are possible anymore, making the state topological.
     583                 :             :  * - Loop until optimal or time runs out:
     584                 :             :  *   - Pick a dependency D to deactivate among those with higher feerate top than bottom.
     585                 :             :  *   - Deactivate D, causing the chunk it is in to split into top T and bottom B.
     586                 :             :  *   - Do an upwards merge of T, if possible. If so, repeat the same with the merged result.
     587                 :             :  *   - Do a downwards merge of B, if possible. If so, repeat the same with the merged result.
     588                 :             :  * - Split chunks further to obtain a minimal state, see below.
     589                 :             :  * - Output the chunks from high to low feerate, each internally sorted topologically.
     590                 :             :  *
     591                 :             :  * Instead of performing merges arbitrarily to make the initial state topological, it is possible
     592                 :             :  * to do so guided by an existing linearization. This has the advantage that the state's would-be
     593                 :             :  * output linearization is immediately as good as the existing linearization it was based on:
     594                 :             :  * - Start with all dependencies inactive.
     595                 :             :  * - For each transaction t in the existing linearization:
     596                 :             :  *   - Find the chunk C that transaction is in (which will be singleton).
     597                 :             :  *   - Do an upwards merge of C, if possible. If so, repeat the same with the merged result.
     598                 :             :  * No downwards merges are needed in this case.
     599                 :             :  *
     600                 :             :  * After reaching an optimal state, it can be transformed into a minimal state by attempting to
     601                 :             :  * split chunks further into equal-feerate parts. To do so, pick a specific transaction in each
     602                 :             :  * chunk (the pivot), and rerun the above split-then-merge procedure again:
     603                 :             :  * - first, while pretending the pivot transaction has an infinitesimally higher (or lower) fee
     604                 :             :  *   than it really has. If a split exists with the pivot in the top part (or bottom part), this
     605                 :             :  *   will find it.
     606                 :             :  * - if that fails to split, repeat while pretending the pivot transaction has an infinitesimally
     607                 :             :  *   lower (or higher) fee. If a split exists with the pivot in the bottom part (or top part), this
     608                 :             :  *   will find it.
     609                 :             :  * - if either succeeds, repeat the procedure for the newly found chunks to split them further.
     610                 :             :  *   If not, the chunk is already minimal.
     611                 :             :  * If the chunk can be split into equal-feerate parts, then the pivot must exist in either the top
     612                 :             :  * or bottom part of that potential split. By trying both with the same pivot, if a split exists,
     613                 :             :  * it will be found.
     614                 :             :  *
     615                 :             :  * What remains to be specified are a number of heuristics:
     616                 :             :  *
     617                 :             :  * - How to decide which chunks to merge:
     618                 :             :  *   - The merge upwards and downward rules specify that the lowest-feerate respectively
     619                 :             :  *     highest-feerate candidate chunk is merged with, but if there are multiple equal-feerate
     620                 :             :  *     candidates, a uniformly random one among them is picked.
     621                 :             :  *
     622                 :             :  * - How to decide what dependency to activate (when merging chunks):
     623                 :             :  *   - After picking two chunks to be merged (see above), a uniformly random dependency between the
     624                 :             :  *     two chunks is activated.
     625                 :             :  *
     626                 :             :  * - How to decide which chunk to find a dependency to split in:
     627                 :             :  *   - A round-robin queue of chunks to improve is maintained. The initial ordering of this queue
     628                 :             :  *     is uniformly randomly permuted.
     629                 :             :  *
     630                 :             :  * - How to decide what dependency to deactivate (when splitting chunks):
     631                 :             :  *   - Inside the selected chunk (see above), among the dependencies whose top feerate is strictly
     632                 :             :  *     higher than its bottom feerate in the selected chunk, if any, a uniformly random dependency
     633                 :             :  *     is deactivated.
     634                 :             :  *   - After every split, it is possible that the top and the bottom chunk merge with each other
     635                 :             :  *     again in the merge sequence (through a top->bottom dependency, not through the deactivated
     636                 :             :  *     one, which was bottom->top). Call this a self-merge. If a self-merge does not occur after
     637                 :             :  *     a split, the resulting linearization is strictly improved (the area under the convexified
     638                 :             :  *     feerate diagram increases by at least gain/2), while self-merges do not change it.
     639                 :             :  *
     640                 :             :  * - How to decide the exact output linearization:
     641                 :             :  *   - When there are multiple equal-feerate chunks with no dependencies between them, output a
     642                 :             :  *     uniformly random one among the ones with no missing dependent chunks first.
     643                 :             :  *   - Within chunks, repeatedly pick a uniformly random transaction among those with no missing
     644                 :             :  *     dependencies.
     645                 :             :  */
     646                 :             : template<typename SetType>
     647                 :             : class SpanningForestState
     648                 :             : {
     649                 :             : private:
     650                 :             :     /** Internal RNG. */
     651                 :             :     InsecureRandomContext m_rng;
     652                 :             : 
     653                 :             :     /** Data type to represent indexing into m_tx_data. */
     654                 :             :     using TxIdx = DepGraphIndex;
     655                 :             :     /** Data type to represent indexing into m_set_info. Use the smallest type possible to improve
     656                 :             :      *  cache locality. */
     657                 :             :     using SetIdx = std::conditional_t<(SetType::Size() <= 0xff),
     658                 :             :                                       uint8_t,
     659                 :             :                                       std::conditional_t<(SetType::Size() <= 0xffff),
     660                 :             :                                                          uint16_t,
     661                 :             :                                                          uint32_t>>;
     662                 :             :     /** An invalid SetIdx. */
     663                 :             :     static constexpr SetIdx INVALID_SET_IDX = SetIdx(-1);
     664                 :             : 
     665                 :             :     /** Structure with information about a single transaction. */
     666                 :     7285377 :     struct TxData {
     667                 :             :         /** The top set for every active child dependency this transaction has, indexed by child
     668                 :             :          *  TxIdx. Only defined for indexes in active_children. */
     669                 :             :         std::array<SetIdx, SetType::Size()> dep_top_idx;
     670                 :             :         /** The set of parent transactions of this transaction. Immutable after construction. */
     671                 :             :         SetType parents;
     672                 :             :         /** The set of child transactions of this transaction. Immutable after construction. */
     673                 :             :         SetType children;
     674                 :             :         /** The set of child transactions reachable through an active dependency. */
     675                 :             :         SetType active_children;
     676                 :             :         /** Which chunk this transaction belongs to. */
     677                 :             :         SetIdx chunk_idx;
     678                 :             :     };
     679                 :             : 
     680                 :             :     /** The set of all TxIdx's of transactions in the cluster indexing into m_tx_data. */
     681                 :             :     SetType m_transaction_idxs;
     682                 :             :     /** The set of all chunk SetIdx's. This excludes the SetIdxs that refer to active
     683                 :             :      *  dependencies' tops. */
     684                 :             :     SetType m_chunk_idxs;
     685                 :             :     /** The set of all SetIdx's that appear in m_suboptimal_chunks. Note that they do not need to
     686                 :             :      *  be chunks: some of these sets may have been converted to a dependency's top set since being
     687                 :             :      *  added to m_suboptimal_chunks. */
     688                 :             :     SetType m_suboptimal_idxs;
     689                 :             :     /** Information about each transaction (and chunks). Keeps the "holes" from DepGraph during
     690                 :             :      *  construction. Indexed by TxIdx. */
     691                 :             :     std::vector<TxData> m_tx_data;
     692                 :             :     /** Information about each set (chunk, or active dependency top set). Indexed by SetIdx. */
     693                 :             :     std::vector<SetInfo<SetType>> m_set_info;
     694                 :             :     /** For each chunk, indexed by SetIdx, the set of out-of-chunk reachable transactions, in the
     695                 :             :      *  upwards (.first) and downwards (.second) direction. */
     696                 :             :     std::vector<std::pair<SetType, SetType>> m_reachable;
     697                 :             :     /** A FIFO of chunk SetIdxs for chunks that may be improved still. */
     698                 :             :     VecDeque<SetIdx> m_suboptimal_chunks;
     699                 :             :     /** A FIFO of chunk indexes with a pivot transaction in them, and a flag to indicate their
     700                 :             :      *  status:
     701                 :             :      *  - bit 1: currently attempting to move the pivot down, rather than up.
     702                 :             :      *  - bit 2: this is the second stage, so we have already tried moving the pivot in the other
     703                 :             :      *           direction.
     704                 :             :      */
     705                 :             :     VecDeque<std::tuple<SetIdx, TxIdx, unsigned>> m_nonminimal_chunks;
     706                 :             : 
     707                 :             :     /** The number of updated transactions in activations/deactivations. */
     708                 :             :     uint64_t m_cost{0};
     709                 :             : 
     710                 :             :     /** The DepGraph we are trying to linearize. */
     711                 :             :     const DepGraph<SetType>& m_depgraph;
     712                 :             : 
     713                 :             :     /** Pick a random transaction within a set (which must be non-empty). */
     714                 :     3997445 :     TxIdx PickRandomTx(const SetType& tx_idxs) noexcept
     715                 :             :     {
     716         [ -  + ]:     4021090 :         Assume(tx_idxs.Any());
     717                 :     3997445 :         unsigned pos = m_rng.randrange<unsigned>(tx_idxs.Count());
     718   [ +  -  +  - ]:     9731019 :         for (auto tx_idx : tx_idxs) {
           [ +  -  #  # ]
     719         [ +  + ]:     5757219 :             if (pos == 0) return tx_idx;
     720                 :     1759774 :             --pos;
     721                 :             :         }
     722                 :           0 :         Assume(false);
     723                 :           0 :         return TxIdx(-1);
     724                 :             :     }
     725                 :             : 
     726                 :             :     /** Find the set of out-of-chunk transactions reachable from tx_idxs, both in upwards and
     727                 :             :      *  downwards direction. Only used by SanityCheck to verify the precomputed reachable sets in
     728                 :             :      *  m_reachable that are maintained by Activate/Deactivate. */
     729                 :       46513 :     std::pair<SetType, SetType> GetReachable(const SetType& tx_idxs) const noexcept
     730                 :             :     {
     731                 :       46513 :         SetType parents, children;
     732   [ +  -  +  + ]:      189552 :         for (auto tx_idx : tx_idxs) {
     733                 :       96526 :             const auto& tx_data = m_tx_data[tx_idx];
     734                 :       96526 :             parents |= tx_data.parents;
     735                 :       96526 :             children |= tx_data.children;
     736                 :             :         }
     737                 :       46513 :         return {parents - tx_idxs, children - tx_idxs};
     738                 :             :     }
     739                 :             : 
     740                 :             :     /** Make the inactive dependency from child to parent, which must not be in the same chunk
     741                 :             :      *  already, active. Returns the merged chunk idx. */
     742                 :     3624626 :     SetIdx Activate(TxIdx parent_idx, TxIdx child_idx) noexcept
     743                 :             :     {
     744                 :             :         // Gather and check information about the parent and child transactions.
     745                 :     3624626 :         auto& parent_data = m_tx_data[parent_idx];
     746                 :     3624626 :         auto& child_data = m_tx_data[child_idx];
     747         [ -  + ]:     3624626 :         Assume(parent_data.children[child_idx]);
     748         [ -  + ]:     3624626 :         Assume(!parent_data.active_children[child_idx]);
     749                 :             :         // Get the set index of the chunks the parent and child are currently in. The parent chunk
     750                 :             :         // will become the top set of the newly activated dependency, while the child chunk will be
     751                 :             :         // grown to become the merged chunk.
     752                 :     3624626 :         auto parent_chunk_idx = parent_data.chunk_idx;
     753                 :     3624626 :         auto child_chunk_idx = child_data.chunk_idx;
     754         [ -  + ]:     3624626 :         Assume(parent_chunk_idx != child_chunk_idx);
     755         [ -  + ]:     3624626 :         Assume(m_chunk_idxs[parent_chunk_idx]);
     756         [ -  + ]:     3624626 :         Assume(m_chunk_idxs[child_chunk_idx]);
     757         [ +  - ]:     3624626 :         auto& top_info = m_set_info[parent_chunk_idx];
     758                 :     3624626 :         auto& bottom_info = m_set_info[child_chunk_idx];
     759                 :             : 
     760                 :             :         // Consider the following example:
     761                 :             :         //
     762                 :             :         //    A           A     There are two chunks, ABC and DEF, and the inactive E->C dependency
     763                 :             :         //   / \         / \    is activated, resulting in a single chunk ABCDEF.
     764                 :             :         //  B   C       B   C
     765                 :             :         //      :  ==>      |   Dependency | top set before | top set after | change
     766                 :             :         //  D   E       D   E   B->A       | AC             | ACDEF         | +DEF
     767                 :             :         //   \ /         \ /    C->A       | AB             | AB            |
     768                 :             :         //    F           F     F->D       | D              | D             |
     769                 :             :         //                      F->E       | E              | ABCE          | +ABC
     770                 :             :         //
     771                 :             :         // The common pattern here is that any dependency which has the parent or child of the
     772                 :             :         // dependency being activated (E->C here) in its top set, will have the opposite part added
     773                 :             :         // to it. This is true for B->A and F->E, but not for C->A and F->D.
     774                 :             :         //
     775                 :             :         // Traverse the old parent chunk top_info (ABC in example), and add bottom_info (DEF) to
     776                 :             :         // every dependency's top set which has the parent (C) in it. At the same time, change the
     777                 :             :         // chunk_idx for each to be child_chunk_idx, which becomes the set for the merged chunk.
     778   [ +  -  +  + ]:    23057442 :         for (auto tx_idx : top_info.transactions) {
           [ +  +  #  # ]
     779         [ +  + ]:    15818486 :             auto& tx_data = m_tx_data[tx_idx];
     780                 :    15818486 :             tx_data.chunk_idx = child_chunk_idx;
     781   [ +  +  +  + ]:    37568009 :             for (auto dep_child_idx : tx_data.active_children) {
           [ +  +  #  # ]
     782                 :    12193860 :                 auto& dep_top_info = m_set_info[tx_data.dep_top_idx[dep_child_idx]];
     783         [ +  + ]:    12193860 :                 if (dep_top_info.transactions[parent_idx]) dep_top_info |= bottom_info;
     784                 :             :             }
     785                 :             :         }
     786                 :             :         // Traverse the old child chunk bottom_info (DEF in example), and add top_info (ABC) to
     787                 :             :         // every dependency's top set which has the child (E) in it.
     788   [ +  -  +  + ]:    13963169 :         for (auto tx_idx : bottom_info.transactions) {
           [ +  +  #  # ]
     789         [ +  + ]:     6724213 :             auto& tx_data = m_tx_data[tx_idx];
     790   [ +  +  +  + ]:    12509238 :             for (auto dep_child_idx : tx_data.active_children) {
           [ +  +  #  # ]
     791                 :     3099587 :                 auto& dep_top_info = m_set_info[tx_data.dep_top_idx[dep_child_idx]];
     792         [ +  + ]:     3099587 :                 if (dep_top_info.transactions[child_idx]) dep_top_info |= top_info;
     793                 :             :             }
     794                 :             :         }
     795                 :             :         // Merge top_info into bottom_info, which becomes the merged chunk.
     796                 :     3624626 :         bottom_info |= top_info;
     797                 :     3624626 :         m_cost += bottom_info.transactions.Count();
     798                 :             :         // Compute merged sets of reachable transactions from the new chunk, based on the input
     799                 :             :         // chunks' reachable sets.
     800                 :     3624626 :         m_reachable[child_chunk_idx].first |= m_reachable[parent_chunk_idx].first;
     801                 :     3624626 :         m_reachable[child_chunk_idx].second |= m_reachable[parent_chunk_idx].second;
     802                 :     3624626 :         m_reachable[child_chunk_idx].first -= bottom_info.transactions;
     803                 :     3624626 :         m_reachable[child_chunk_idx].second -= bottom_info.transactions;
     804                 :             :         // Make parent chunk the set for the new active dependency.
     805                 :     3624626 :         parent_data.dep_top_idx[child_idx] = parent_chunk_idx;
     806                 :     3624626 :         parent_data.active_children.Set(child_idx);
     807                 :     3624626 :         m_chunk_idxs.Reset(parent_chunk_idx);
     808                 :             :         // Return the newly merged chunk.
     809                 :     3624626 :         return child_chunk_idx;
     810                 :             :     }
     811                 :             : 
     812                 :             :     /** Make a specified active dependency inactive. Returns the created parent and child chunk
     813                 :             :      *  indexes. */
     814                 :      420082 :     std::pair<SetIdx, SetIdx> Deactivate(TxIdx parent_idx, TxIdx child_idx) noexcept
     815                 :             :     {
     816                 :             :         // Gather and check information about the parent transactions.
     817                 :      420082 :         auto& parent_data = m_tx_data[parent_idx];
     818         [ -  + ]:      420082 :         Assume(parent_data.children[child_idx]);
     819         [ -  + ]:      420082 :         Assume(parent_data.active_children[child_idx]);
     820                 :             :         // Get the top set of the active dependency (which will become the parent chunk) and the
     821                 :             :         // chunk set the transactions are currently in (which will become the bottom chunk).
     822         [ -  + ]:      420082 :         auto parent_chunk_idx = parent_data.dep_top_idx[child_idx];
     823                 :      420082 :         auto child_chunk_idx = parent_data.chunk_idx;
     824         [ -  + ]:      420082 :         Assume(parent_chunk_idx != child_chunk_idx);
     825         [ -  + ]:      420082 :         Assume(m_chunk_idxs[child_chunk_idx]);
     826         [ -  + ]:      420082 :         Assume(!m_chunk_idxs[parent_chunk_idx]); // top set, not a chunk
     827                 :      420082 :         auto& top_info = m_set_info[parent_chunk_idx];
     828                 :      420082 :         auto& bottom_info = m_set_info[child_chunk_idx];
     829                 :             : 
     830                 :             :         // Remove the active dependency.
     831                 :      420082 :         parent_data.active_children.Reset(child_idx);
     832                 :      420082 :         m_chunk_idxs.Set(parent_chunk_idx);
     833                 :      420082 :         m_cost += bottom_info.transactions.Count();
     834                 :             :         // Subtract the top_info from the bottom_info, as it will become the child chunk.
     835                 :      420082 :         bottom_info -= top_info;
     836                 :             :         // See the comment above in Activate(). We perform the opposite operations here, removing
     837                 :             :         // instead of adding. Simultaneously, aggregate the top/bottom's union of parents/children.
     838                 :      420082 :         SetType top_parents, top_children;
     839   [ +  -  +  + ]:     2833735 :         for (auto tx_idx : top_info.transactions) {
           [ +  +  #  # ]
     840         [ +  + ]:     1996195 :             auto& tx_data = m_tx_data[tx_idx];
     841                 :     1996195 :             tx_data.chunk_idx = parent_chunk_idx;
     842         [ +  + ]:     2016073 :             top_parents |= tx_data.parents;
     843                 :     1996195 :             top_children |= tx_data.children;
     844   [ +  +  +  + ]:     4771614 :             for (auto dep_child_idx : tx_data.active_children) {
           [ +  +  #  # ]
     845                 :     1576113 :                 auto& dep_top_info = m_set_info[tx_data.dep_top_idx[dep_child_idx]];
     846         [ +  + ]:     1576113 :                 if (dep_top_info.transactions[parent_idx]) dep_top_info -= bottom_info;
     847                 :             :             }
     848                 :             :         }
     849                 :      420082 :         SetType bottom_parents, bottom_children;
     850   [ +  -  +  + ]:     1915671 :         for (auto tx_idx : bottom_info.transactions) {
           [ +  +  #  # ]
     851         [ +  + ]:     1078131 :             auto& tx_data = m_tx_data[tx_idx];
     852         [ +  + ]:     1093488 :             bottom_parents |= tx_data.parents;
     853                 :     1078131 :             bottom_children |= tx_data.children;
     854   [ +  +  +  + ]:     2225935 :             for (auto dep_child_idx : tx_data.active_children) {
           [ +  +  #  # ]
     855                 :      658049 :                 auto& dep_top_info = m_set_info[tx_data.dep_top_idx[dep_child_idx]];
     856         [ +  + ]:      658049 :                 if (dep_top_info.transactions[child_idx]) dep_top_info -= top_info;
     857                 :             :             }
     858                 :             :         }
     859                 :             :         // Compute the new sets of reachable transactions for each new chunk, based on the
     860                 :             :         // top/bottom parents and children computed above.
     861                 :      420082 :         m_reachable[parent_chunk_idx].first = top_parents - top_info.transactions;
     862                 :      420082 :         m_reachable[parent_chunk_idx].second = top_children - top_info.transactions;
     863                 :      420082 :         m_reachable[child_chunk_idx].first = bottom_parents - bottom_info.transactions;
     864                 :      420082 :         m_reachable[child_chunk_idx].second = bottom_children - bottom_info.transactions;
     865                 :             :         // Return the two new set idxs.
     866                 :      420082 :         return {parent_chunk_idx, child_chunk_idx};
     867                 :             :     }
     868                 :             : 
     869                 :             :     /** Activate a dependency from the bottom set to the top set, which must exist. Return the
     870                 :             :      *  index of the merged chunk. */
     871                 :     3624626 :     SetIdx MergeChunks(SetIdx top_idx, SetIdx bottom_idx) noexcept
     872                 :             :     {
     873         [ -  + ]:     3624626 :         Assume(m_chunk_idxs[top_idx]);
     874         [ -  + ]:     3624626 :         Assume(m_chunk_idxs[bottom_idx]);
     875         [ +  - ]:     3624626 :         auto& top_chunk_info = m_set_info[top_idx];
     876                 :     3624626 :         auto& bottom_chunk_info = m_set_info[bottom_idx];
     877                 :             :         // Count the number of dependencies between bottom_chunk and top_chunk.
     878                 :     3624626 :         unsigned num_deps{0};
     879   [ +  -  +  + ]:    23057442 :         for (auto tx_idx : top_chunk_info.transactions) {
           [ +  +  #  # ]
     880                 :    15818486 :             auto& tx_data = m_tx_data[tx_idx];
     881                 :    15818486 :             num_deps += (tx_data.children & bottom_chunk_info.transactions).Count();
     882                 :             :         }
     883         [ -  + ]:     3624626 :         Assume(num_deps > 0);
     884                 :             :         // Uniformly randomly pick one of them and activate it.
     885                 :     3624626 :         unsigned pick = m_rng.randrange(num_deps);
     886   [ +  -  +  - ]:    16334350 :         for (auto tx_idx : top_chunk_info.transactions) {
           [ +  -  #  # ]
     887         [ +  + ]:    12720020 :             auto& tx_data = m_tx_data[tx_idx];
     888         [ +  + ]:    12720020 :             auto intersect = tx_data.children & bottom_chunk_info.transactions;
     889                 :    12720020 :             auto count = intersect.Count();
     890         [ +  + ]:    12720020 :             if (pick < count) {
     891   [ +  -  +  - ]:     7249115 :                 for (auto child_idx : intersect) {
           [ +  -  #  # ]
     892         [ +  + ]:     3634785 :                     if (pick == 0) return Activate(tx_idx, child_idx);
     893                 :       10159 :                     --pick;
     894                 :             :                 }
     895                 :           0 :                 Assume(false);
     896                 :             :                 break;
     897                 :             :             }
     898                 :     9095394 :             pick -= count;
     899                 :             :         }
     900                 :           0 :         Assume(false);
     901                 :           0 :         return INVALID_SET_IDX;
     902                 :             :     }
     903                 :             : 
     904                 :             :     /** Activate a dependency from chunk_idx to merge_chunk_idx (if !DownWard), or a dependency
     905                 :             :      *  from merge_chunk_idx to chunk_idx (if DownWard). Return the index of the merged chunk. */
     906                 :             :     template<bool DownWard>
     907                 :     3577053 :     SetIdx MergeChunksDirected(SetIdx chunk_idx, SetIdx merge_chunk_idx) noexcept
     908                 :             :     {
     909                 :             :         if constexpr (DownWard) {
     910                 :       16327 :             return MergeChunks(chunk_idx, merge_chunk_idx);
     911                 :             :         } else {
     912                 :     3560726 :             return MergeChunks(merge_chunk_idx, chunk_idx);
     913                 :             :         }
     914                 :             :     }
     915                 :             : 
     916                 :             :     /** Determine which chunk to merge chunk_idx with, or INVALID_SET_IDX if none. */
     917                 :             :     template<bool DownWard>
     918                 :    13168759 :     SetIdx PickMergeCandidate(SetIdx chunk_idx) noexcept
     919                 :             :     {
     920                 :             :         /** Information about the chunk. */
     921         [ -  + ]:    13168759 :         Assume(m_chunk_idxs[chunk_idx]);
     922                 :    13168759 :         auto& chunk_info = m_set_info[chunk_idx];
     923                 :             :         // Iterate over all chunks reachable from this one. For those depended-on chunks,
     924                 :             :         // remember the highest-feerate (if DownWard) or lowest-feerate (if !DownWard) one.
     925                 :             :         // If multiple equal-feerate candidate chunks to merge with exist, pick a random one
     926                 :             :         // among them.
     927                 :             : 
     928                 :             :         /** The minimum feerate (if downward) or maximum feerate (if upward) to consider when
     929                 :             :          *  looking for candidate chunks to merge with. Initially, this is the original chunk's
     930                 :             :          *  feerate, but is updated to be the current best candidate whenever one is found. */
     931                 :    13168759 :         FeeFrac best_other_chunk_feerate = chunk_info.feerate;
     932                 :             :         /** The chunk index for the best candidate chunk to merge with. INVALID_SET_IDX if none. */
     933                 :    13168759 :         SetIdx best_other_chunk_idx = INVALID_SET_IDX;
     934                 :             :         /** We generate random tiebreak values to pick between equal-feerate candidate chunks.
     935                 :             :          *  This variable stores the tiebreak of the current best candidate. */
     936                 :    13168759 :         uint64_t best_other_chunk_tiebreak{0};
     937                 :             : 
     938                 :             :         /** Which parent/child transactions we still need to process the chunks for. */
     939                 :    13168759 :         auto todo = DownWard ? m_reachable[chunk_idx].second : m_reachable[chunk_idx].first;
     940                 :    13168759 :         unsigned steps = 0;
     941         [ +  + ]:    27226318 :         while (todo.Any()) {
     942                 :    13977660 :             ++steps;
     943                 :             :             // Find a chunk for a transaction in todo, and remove all its transactions from todo.
     944         [ +  + ]:    13977660 :             auto reached_chunk_idx = m_tx_data[todo.First()].chunk_idx;
     945                 :    13977660 :             auto& reached_chunk_info = m_set_info[reached_chunk_idx];
     946         [ +  + ]:    13977660 :             todo -= reached_chunk_info.transactions;
     947                 :             :             // See if it has an acceptable feerate.
     948         [ +  + ]:      941044 :             auto cmp = DownWard ? FeeRateCompare(best_other_chunk_feerate, reached_chunk_info.feerate)
     949         [ +  + ]:    13036616 :                                 : FeeRateCompare(reached_chunk_info.feerate, best_other_chunk_feerate);
     950         [ +  + ]:    13977660 :             if (cmp > 0) continue;
     951         [ +  + ]:     4374253 :             uint64_t tiebreak = m_rng.rand64();
     952   [ +  +  +  + ]:     4374253 :             if (cmp < 0 || tiebreak >= best_other_chunk_tiebreak) {
     953                 :     4178173 :                 best_other_chunk_feerate = reached_chunk_info.feerate;
     954                 :     4178173 :                 best_other_chunk_idx = reached_chunk_idx;
     955                 :     4178173 :                 best_other_chunk_tiebreak = tiebreak;
     956                 :             :             }
     957                 :             :         }
     958   [ -  +  -  + ]:    13168759 :         Assume(steps <= m_set_info.size());
     959                 :             : 
     960                 :    13168759 :         return best_other_chunk_idx;
     961                 :             :     }
     962                 :             : 
     963                 :             :     /** Perform an upward or downward merge step, on the specified chunk. Returns the merged chunk,
     964                 :             :      *  or INVALID_SET_IDX if no merge took place. */
     965                 :             :     template<bool DownWard>
     966                 :    13168759 :     SetIdx MergeStep(SetIdx chunk_idx) noexcept
     967                 :             :     {
     968                 :    13168759 :         auto merge_chunk_idx = PickMergeCandidate<DownWard>(chunk_idx);
     969         [ +  + ]:    13168759 :         if (merge_chunk_idx == INVALID_SET_IDX) return INVALID_SET_IDX;
     970                 :     3577053 :         chunk_idx = MergeChunksDirected<DownWard>(chunk_idx, merge_chunk_idx);
     971         [ -  + ]:     3577053 :         Assume(chunk_idx != INVALID_SET_IDX);
     972                 :             :         return chunk_idx;
     973                 :             :     }
     974                 :             : 
     975                 :             :     /** Perform an upward or downward merge sequence on the specified chunk. */
     976                 :             :     template<bool DownWard>
     977                 :       66328 :     void MergeSequence(SetIdx chunk_idx) noexcept
     978                 :             :     {
     979         [ -  + ]:       66328 :         Assume(m_chunk_idxs[chunk_idx]);
     980                 :       11617 :         while (true) {
     981                 :       77945 :             auto merged_chunk_idx = MergeStep<DownWard>(chunk_idx);
     982         [ +  + ]:       77945 :             if (merged_chunk_idx == INVALID_SET_IDX) break;
     983                 :       11617 :             chunk_idx = merged_chunk_idx;
     984                 :             :         }
     985                 :             :         // Add the chunk to the queue of improvable chunks, if it wasn't already there.
     986         [ +  + ]:       66328 :         if (!m_suboptimal_idxs[chunk_idx]) {
     987                 :       62999 :             m_suboptimal_idxs.Set(chunk_idx);
     988                 :       62999 :             m_suboptimal_chunks.push_back(chunk_idx);
     989                 :             :         }
     990                 :       66328 :     }
     991                 :             : 
     992                 :             :     /** Split a chunk, and then merge the resulting two chunks to make the graph topological
     993                 :             :      *  again. */
     994                 :       69452 :     void Improve(TxIdx parent_idx, TxIdx child_idx) noexcept
     995                 :             :     {
     996                 :             :         // Deactivate the specified dependency, splitting it into two new chunks: a top containing
     997                 :             :         // the parent, and a bottom containing the child. The top should have a higher feerate.
     998         [ +  + ]:       69452 :         auto [parent_chunk_idx, child_chunk_idx] = Deactivate(parent_idx, child_idx);
     999                 :             : 
    1000                 :             :         // At this point we have exactly two chunks which may violate topology constraints (the
    1001                 :             :         // parent chunk and child chunk that were produced by deactivation). We can fix
    1002                 :             :         // these using just merge sequences, one upwards and one downwards, avoiding the need for a
    1003                 :             :         // full MakeTopological.
    1004         [ +  + ]:       69452 :         const auto& parent_reachable = m_reachable[parent_chunk_idx].first;
    1005         [ +  + ]:       69452 :         const auto& child_chunk_txn = m_set_info[child_chunk_idx].transactions;
    1006         [ +  + ]:       69452 :         if (parent_reachable.Overlaps(child_chunk_txn)) {
    1007                 :             :             // The parent chunk has a dependency on a transaction in the child chunk. In this case,
    1008                 :             :             // the parent needs to merge back with the child chunk (a self-merge), and no other
    1009                 :             :             // merges are needed. Special-case this, so the overhead of PickMergeCandidate and
    1010                 :             :             // MergeSequence can be avoided.
    1011                 :             : 
    1012                 :             :             // In the self-merge, the roles reverse: the parent chunk (from the split) depends
    1013                 :             :             // on the child chunk, so child_chunk_idx is the "top" and parent_chunk_idx is the
    1014                 :             :             // "bottom" for MergeChunks.
    1015                 :       36288 :             auto merged_chunk_idx = MergeChunks(child_chunk_idx, parent_chunk_idx);
    1016         [ +  - ]:       36288 :             if (!m_suboptimal_idxs[merged_chunk_idx]) {
    1017                 :       36288 :                 m_suboptimal_idxs.Set(merged_chunk_idx);
    1018                 :       36288 :                 m_suboptimal_chunks.push_back(merged_chunk_idx);
    1019                 :             :             }
    1020                 :             :         } else {
    1021                 :             :             // Merge the top chunk with lower-feerate chunks it depends on.
    1022                 :       33164 :             MergeSequence<false>(parent_chunk_idx);
    1023                 :             :             // Merge the bottom chunk with higher-feerate chunks that depend on it.
    1024                 :       33164 :             MergeSequence<true>(child_chunk_idx);
    1025                 :             :         }
    1026                 :       69452 :     }
    1027                 :             : 
    1028                 :             :     /** Determine the next chunk to optimize, or INVALID_SET_IDX if none. */
    1029                 :     3736718 :     SetIdx PickChunkToOptimize() noexcept
    1030                 :             :     {
    1031         [ +  + ]:     3738104 :         while (!m_suboptimal_chunks.empty()) {
    1032                 :             :             // Pop an entry from the potentially-suboptimal chunk queue.
    1033                 :     3738019 :             SetIdx chunk_idx = m_suboptimal_chunks.front();
    1034         [ -  + ]:     3738019 :             Assume(m_suboptimal_idxs[chunk_idx]);
    1035                 :     3738019 :             m_suboptimal_idxs.Reset(chunk_idx);
    1036                 :     3738019 :             m_suboptimal_chunks.pop_front();
    1037         [ +  + ]:     3738019 :             if (m_chunk_idxs[chunk_idx]) return chunk_idx;
    1038                 :             :             // If what was popped is not currently a chunk, continue. This may
    1039                 :             :             // happen when a split chunk merges in Improve() with one or more existing chunks that
    1040                 :             :             // are themselves on the suboptimal queue already.
    1041                 :             :         }
    1042                 :             :         return INVALID_SET_IDX;
    1043                 :             :     }
    1044                 :             : 
    1045                 :             :     /** Find a (parent, child) dependency to deactivate in chunk_idx, or (-1, -1) if none. */
    1046                 :     3736633 :     std::pair<TxIdx, TxIdx> PickDependencyToSplit(SetIdx chunk_idx) noexcept
    1047                 :             :     {
    1048         [ -  + ]:     3736633 :         Assume(m_chunk_idxs[chunk_idx]);
    1049         [ +  - ]:     3736633 :         auto& chunk_info = m_set_info[chunk_idx];
    1050                 :             : 
    1051                 :             :         // Remember the best dependency {par, chl} seen so far.
    1052                 :     3736633 :         std::pair<TxIdx, TxIdx> candidate_dep = {TxIdx(-1), TxIdx(-1)};
    1053                 :     3736633 :         uint64_t candidate_tiebreak = 0;
    1054                 :             :         // Iterate over all transactions.
    1055   [ +  -  +  + ]:    15914819 :         for (auto tx_idx : chunk_info.transactions) {
           [ +  +  #  # ]
    1056         [ +  + ]:     8462850 :             const auto& tx_data = m_tx_data[tx_idx];
    1057                 :             :             // Iterate over all active child dependencies of the transaction.
    1058   [ +  +  +  + ]:    17113478 :             for (auto child_idx : tx_data.active_children) {
           [ +  +  #  # ]
    1059         [ +  + ]:     4726217 :                 auto& dep_top_info = m_set_info[tx_data.dep_top_idx[child_idx]];
    1060                 :             :                 // Skip if this dependency is ineligible (the top chunk that would be created
    1061                 :             :                 // does not have higher feerate than the chunk it is currently part of).
    1062         [ +  + ]:     4726217 :                 auto cmp = FeeRateCompare(dep_top_info.feerate, chunk_info.feerate);
    1063         [ +  + ]:     4726217 :                 if (cmp <= 0) continue;
    1064                 :             :                 // Generate a random tiebreak for this dependency, and reject it if its tiebreak
    1065                 :             :                 // is worse than the best so far. This means that among all eligible
    1066                 :             :                 // dependencies, a uniformly random one will be chosen.
    1067                 :      215737 :                 uint64_t tiebreak = m_rng.rand64();
    1068         [ +  + ]:      215737 :                 if (tiebreak < candidate_tiebreak) continue;
    1069                 :             :                 // Remember this as our (new) candidate dependency.
    1070                 :      110128 :                 candidate_dep = {tx_idx, child_idx};
    1071                 :      110128 :                 candidate_tiebreak = tiebreak;
    1072                 :             :             }
    1073                 :             :         }
    1074                 :     3736633 :         return candidate_dep;
    1075                 :             :     }
    1076                 :             : 
    1077                 :             : public:
    1078                 :             :     /** Construct a spanning forest for the given DepGraph, with every transaction in its own chunk
    1079                 :             :      *  (not topological). */
    1080                 :      959333 :     explicit SpanningForestState(const DepGraph<SetType>& depgraph LIFETIMEBOUND, uint64_t rng_seed) noexcept :
    1081         [ -  + ]:      959333 :         m_rng(rng_seed), m_depgraph(depgraph)
    1082                 :             :     {
    1083                 :      959333 :         m_transaction_idxs = depgraph.Positions();
    1084         [ -  + ]:      959333 :         auto num_transactions = m_transaction_idxs.Count();
    1085         [ -  + ]:      959333 :         m_tx_data.resize(depgraph.PositionRange());
    1086                 :      959333 :         m_set_info.resize(num_transactions);
    1087                 :      959333 :         m_reachable.resize(num_transactions);
    1088                 :      959333 :         size_t num_chunks = 0;
    1089   [ +  +  +  + ]:     9180792 :         for (auto tx_idx : m_transaction_idxs) {
           [ +  +  #  # ]
    1090                 :             :             // Fill in transaction data.
    1091                 :     7262818 :             auto& tx_data = m_tx_data[tx_idx];
    1092                 :     7262818 :             tx_data.parents = depgraph.GetReducedParents(tx_idx);
    1093   [ +  +  +  + ]:    19096946 :             for (auto parent_idx : tx_data.parents) {
           [ +  +  #  # ]
    1094                 :     6736364 :                 m_tx_data[parent_idx].children.Set(tx_idx);
    1095                 :             :             }
    1096                 :             :             // Create a singleton chunk for it.
    1097                 :     7262818 :             tx_data.chunk_idx = num_chunks;
    1098                 :     7262818 :             m_set_info[num_chunks++] = SetInfo(depgraph, tx_idx);
    1099                 :             :         }
    1100                 :             :         // Set the reachable transactions for each chunk to the transactions' parents and children.
    1101         [ +  + ]:     8222151 :         for (SetIdx chunk_idx = 0; chunk_idx < num_transactions; ++chunk_idx) {
    1102                 :     7262818 :             auto& tx_data = m_tx_data[m_set_info[chunk_idx].transactions.First()];
    1103                 :     7262818 :             m_reachable[chunk_idx].first = tx_data.parents;
    1104                 :     7262818 :             m_reachable[chunk_idx].second = tx_data.children;
    1105                 :             :         }
    1106         [ -  + ]:      959333 :         Assume(num_chunks == num_transactions);
    1107                 :             :         // Mark all chunk sets as chunks.
    1108                 :      959333 :         m_chunk_idxs = SetType::Fill(num_chunks);
    1109                 :      959333 :     }
    1110                 :             : 
    1111                 :             :     /** Load an existing linearization. Must be called immediately after constructor. The result is
    1112                 :             :      *  topological if the linearization is valid. Otherwise, MakeTopological still needs to be
    1113                 :             :      *  called. */
    1114                 :      958066 :     void LoadLinearization(std::span<const DepGraphIndex> old_linearization) noexcept
    1115                 :             :     {
    1116                 :             :         // Add transactions one by one, in order of existing linearization.
    1117         [ +  + ]:     8192295 :         for (DepGraphIndex tx_idx : old_linearization) {
    1118                 :     7234229 :             auto chunk_idx = m_tx_data[tx_idx].chunk_idx;
    1119                 :             :             // Merge the chunk upwards, as long as merging succeeds.
    1120                 :             :             while (true) {
    1121                 :    10779706 :                 chunk_idx = MergeStep<false>(chunk_idx);
    1122         [ +  + ]:    10779706 :                 if (chunk_idx == INVALID_SET_IDX) break;
    1123                 :             :             }
    1124                 :             :         }
    1125                 :      958066 :     }
    1126                 :             : 
    1127                 :             :     /** Make state topological. Can be called after constructing, or after LoadLinearization. */
    1128                 :      569800 :     void MakeTopological() noexcept
    1129                 :             :     {
    1130         [ -  + ]:      569800 :         Assume(m_suboptimal_chunks.empty());
    1131                 :             :         /** What direction to initially merge chunks in; one of the two directions is enough. This
    1132                 :             :          *  is sufficient because if a non-topological inactive dependency exists between two
    1133                 :             :          *  chunks, at least one of the two chunks will eventually be processed in a direction that
    1134                 :             :          *  discovers it - either the lower chunk tries upward, or the upper chunk tries downward.
    1135                 :             :          *  Chunks that are the result of the merging are always tried in both directions. */
    1136                 :      569800 :         unsigned init_dir = m_rng.randbool();
    1137                 :             :         /** Which chunks are the result of merging, and thus need merge attempts in both
    1138                 :             :          *  directions. */
    1139                 :      569800 :         SetType merged_chunks;
    1140                 :             :         // Mark chunks as suboptimal.
    1141                 :      569800 :         m_suboptimal_idxs = m_chunk_idxs;
    1142   [ +  +  +  + ]:     3437644 :         for (auto chunk_idx : m_chunk_idxs) {
           [ -  +  #  # ]
    1143                 :     2298135 :             m_suboptimal_chunks.emplace_back(chunk_idx);
    1144                 :             :             // Randomize the initial order of suboptimal chunks in the queue.
    1145                 :     2298135 :             SetIdx j = m_rng.randrange<SetIdx>(m_suboptimal_chunks.size());
    1146         [ +  + ]:     2298135 :             if (j != m_suboptimal_chunks.size() - 1) {
    1147                 :     1405266 :                 std::swap(m_suboptimal_chunks.back(), m_suboptimal_chunks[j]);
    1148                 :             :             }
    1149                 :             :         }
    1150         [ +  + ]:     2882062 :         while (!m_suboptimal_chunks.empty()) {
    1151                 :             :             // Pop an entry from the potentially-suboptimal chunk queue.
    1152                 :     2312262 :             SetIdx chunk_idx = m_suboptimal_chunks.front();
    1153                 :     2312262 :             m_suboptimal_chunks.pop_front();
    1154         [ -  + ]:     2312262 :             Assume(m_suboptimal_idxs[chunk_idx]);
    1155                 :     2312262 :             m_suboptimal_idxs.Reset(chunk_idx);
    1156                 :             :             // If what was popped is not currently a chunk, continue. This may
    1157                 :             :             // happen when it was merged with something else since being added.
    1158         [ +  + ]:     2312262 :             if (!m_chunk_idxs[chunk_idx]) continue;
    1159                 :             :             /** What direction(s) to attempt merging in. 1=up, 2=down, 3=both. */
    1160         [ +  + ]:     2303898 :             unsigned direction = merged_chunks[chunk_idx] ? 3 : init_dir + 1;
    1161                 :     2303898 :             int flip = m_rng.randbool();
    1162         [ +  + ]:     6880779 :             for (int i = 0; i < 2; ++i) {
    1163         [ +  + ]:     4596840 :                 if (i ^ flip) {
    1164         [ +  + ]:     2299626 :                     if (!(direction & 1)) continue;
    1165                 :             :                     // Attempt to merge the chunk upwards.
    1166                 :     1169628 :                     auto result_up = MergeStep<false>(chunk_idx);
    1167         [ +  + ]:     1169628 :                     if (result_up != INVALID_SET_IDX) {
    1168         [ +  - ]:       12078 :                         if (!m_suboptimal_idxs[result_up]) {
    1169                 :       12078 :                             m_suboptimal_idxs.Set(result_up);
    1170                 :       12078 :                             m_suboptimal_chunks.push_back(result_up);
    1171                 :             :                         }
    1172                 :       12078 :                         merged_chunks.Set(result_up);
    1173                 :       12078 :                         break;
    1174                 :             :                     }
    1175                 :             :                 } else {
    1176         [ +  + ]:     2297214 :                     if (!(direction & 2)) continue;
    1177                 :             :                     // Attempt to merge the chunk downwards.
    1178                 :     1141480 :                     auto result_down = MergeStep<true>(chunk_idx);
    1179         [ +  + ]:     1141480 :                     if (result_down != INVALID_SET_IDX) {
    1180         [ +  + ]:        7881 :                         if (!m_suboptimal_idxs[result_down]) {
    1181                 :        2049 :                             m_suboptimal_idxs.Set(result_down);
    1182                 :        2049 :                             m_suboptimal_chunks.push_back(result_down);
    1183                 :             :                         }
    1184                 :        7881 :                         merged_chunks.Set(result_down);
    1185                 :        7881 :                         break;
    1186                 :             :                     }
    1187                 :             :                 }
    1188                 :             :             }
    1189                 :             :         }
    1190                 :      569800 :     }
    1191                 :             : 
    1192                 :             :     /** Initialize the data structure for optimization. It must be topological already. */
    1193                 :      945959 :     void StartOptimizing() noexcept
    1194                 :             :     {
    1195         [ -  + ]:      945959 :         Assume(m_suboptimal_chunks.empty());
    1196                 :             :         // Mark chunks suboptimal.
    1197                 :      945959 :         m_suboptimal_idxs = m_chunk_idxs;
    1198   [ +  +  +  + ]:     5531105 :         for (auto chunk_idx : m_chunk_idxs) {
           [ +  +  #  # ]
    1199                 :     3639873 :             m_suboptimal_chunks.push_back(chunk_idx);
    1200                 :             :             // Randomize the initial order of suboptimal chunks in the queue.
    1201                 :     3639873 :             SetIdx j = m_rng.randrange<SetIdx>(m_suboptimal_chunks.size());
    1202         [ +  + ]:     3639873 :             if (j != m_suboptimal_chunks.size() - 1) {
    1203                 :     2148728 :                 std::swap(m_suboptimal_chunks.back(), m_suboptimal_chunks[j]);
    1204                 :             :             }
    1205                 :             :         }
    1206                 :      945959 :     }
    1207                 :             : 
    1208                 :             :     /** Try to improve the forest. Returns false if it is optimal, true otherwise. */
    1209                 :     3736718 :     bool OptimizeStep() noexcept
    1210                 :             :     {
    1211                 :     3736718 :         auto chunk_idx = PickChunkToOptimize();
    1212         [ +  + ]:     3736718 :         if (chunk_idx == INVALID_SET_IDX) {
    1213                 :             :             // No improvable chunk was found, we are done.
    1214                 :             :             return false;
    1215                 :             :         }
    1216         [ +  + ]:     3736633 :         auto [parent_idx, child_idx] = PickDependencyToSplit(chunk_idx);
    1217         [ +  + ]:     3736633 :         if (parent_idx == TxIdx(-1)) {
    1218                 :             :             // Nothing to improve in chunk_idx. Need to continue with other chunks, if any.
    1219                 :     3667181 :             return !m_suboptimal_chunks.empty();
    1220                 :             :         }
    1221                 :             :         // Deactivate the found dependency and then make the state topological again with a
    1222                 :             :         // sequence of merges.
    1223                 :       69452 :         Improve(parent_idx, child_idx);
    1224                 :       69452 :         return true;
    1225                 :             :     }
    1226                 :             : 
    1227                 :             :     /** Initialize data structure for minimizing the chunks. Can only be called if state is known
    1228                 :             :      *  to be optimal. OptimizeStep() cannot be called anymore afterwards. */
    1229                 :      945428 :     void StartMinimizing() noexcept
    1230                 :             :     {
    1231                 :      945428 :         m_nonminimal_chunks.clear();
    1232         [ +  + ]:      945428 :         m_nonminimal_chunks.reserve(m_transaction_idxs.Count());
    1233                 :             :         // Gather all chunks, and for each, add it with a random pivot in it, and a random initial
    1234                 :             :         // direction, to m_nonminimal_chunks.
    1235   [ +  +  +  + ]:     5548270 :         for (auto chunk_idx : m_chunk_idxs) {
           [ +  +  #  # ]
    1236                 :     3658100 :             TxIdx pivot_idx = PickRandomTx(m_set_info[chunk_idx].transactions);
    1237                 :     3658100 :             m_nonminimal_chunks.emplace_back(chunk_idx, pivot_idx, m_rng.randbits<1>());
    1238                 :             :             // Randomize the initial order of nonminimal chunks in the queue.
    1239                 :     3658100 :             SetIdx j = m_rng.randrange<SetIdx>(m_nonminimal_chunks.size());
    1240         [ +  + ]:     3658100 :             if (j != m_nonminimal_chunks.size() - 1) {
    1241                 :     2168039 :                 std::swap(m_nonminimal_chunks.back(), m_nonminimal_chunks[j]);
    1242                 :             :             }
    1243                 :             :         }
    1244                 :      945428 :     }
    1245                 :             : 
    1246                 :             :     /** Try to reduce a chunk's size. Returns false if all chunks are minimal, true otherwise. */
    1247                 :     5421466 :     bool MinimizeStep() noexcept
    1248                 :             :     {
    1249                 :             :         // If the queue of potentially-non-minimal chunks is empty, we are done.
    1250         [ +  + ]:     5421466 :         if (m_nonminimal_chunks.empty()) return false;
    1251                 :             :         // Pop an entry from the potentially-non-minimal chunk queue.
    1252                 :     4476582 :         auto [chunk_idx, pivot_idx, flags] = m_nonminimal_chunks.front();
    1253                 :     4476582 :         m_nonminimal_chunks.pop_front();
    1254         [ +  - ]:     4476582 :         auto& chunk_info = m_set_info[chunk_idx];
    1255                 :             :         /** Whether to move the pivot down rather than up. */
    1256                 :     4476582 :         bool move_pivot_down = flags & 1;
    1257                 :             :         /** Whether this is already the second stage. */
    1258                 :     4476582 :         bool second_stage = flags & 2;
    1259                 :             : 
    1260                 :             :         // Find a random dependency whose top and bottom set feerates are equal, and which has
    1261                 :             :         // pivot in bottom set (if move_pivot_down) or in top set (if !move_pivot_down).
    1262                 :     4476582 :         std::pair<TxIdx, TxIdx> candidate_dep;
    1263                 :     4476582 :         uint64_t candidate_tiebreak{0};
    1264                 :     4476582 :         bool have_any = false;
    1265                 :             :         // Iterate over all transactions.
    1266   [ +  -  +  + ]:    17909007 :         for (auto tx_idx : chunk_info.transactions) {
           [ +  +  #  # ]
    1267         [ +  + ]:     8982780 :             const auto& tx_data = m_tx_data[tx_idx];
    1268                 :             :             // Iterate over all active child dependencies of the transaction.
    1269   [ +  +  +  + ]:    17242655 :             for (auto child_idx : tx_data.active_children) {
           [ +  +  #  # ]
    1270         [ +  + ]:     4506198 :                 const auto& dep_top_info = m_set_info[tx_data.dep_top_idx[child_idx]];
    1271                 :             :                 // Skip if this dependency does not have equal top and bottom set feerates. Note
    1272                 :             :                 // that the top cannot have higher feerate than the bottom, or OptimizeSteps would
    1273                 :             :                 // have dealt with it.
    1274         [ +  + ]:     4506198 :                 if (dep_top_info.feerate << chunk_info.feerate) continue;
    1275                 :     1440958 :                 have_any = true;
    1276                 :             :                 // Skip if this dependency does not have pivot in the right place.
    1277         [ +  + ]:     1440958 :                 if (move_pivot_down == dep_top_info.transactions[pivot_idx]) continue;
    1278                 :             :                 // Remember this as our chosen dependency if it has a better tiebreak.
    1279                 :      811418 :                 uint64_t tiebreak = m_rng.rand64() | 1;
    1280         [ +  + ]:      811418 :                 if (tiebreak > candidate_tiebreak) {
    1281                 :      460411 :                     candidate_tiebreak = tiebreak;
    1282                 :      460411 :                     candidate_dep = {tx_idx, child_idx};
    1283                 :             :                 }
    1284                 :             :             }
    1285                 :             :         }
    1286                 :             :         // If no dependencies have equal top and bottom set feerate, this chunk is minimal.
    1287         [ +  + ]:     4476582 :         if (!have_any) return true;
    1288                 :             :         // If all found dependencies have the pivot in the wrong place, try moving it in the other
    1289                 :             :         // direction. If this was the second stage already, we are done.
    1290         [ +  + ]:      482438 :         if (candidate_tiebreak == 0) {
    1291                 :             :             // Switch to other direction, and to second phase.
    1292                 :      131808 :             flags ^= 3;
    1293         [ +  + ]:      131808 :             if (!second_stage) m_nonminimal_chunks.emplace_back(chunk_idx, pivot_idx, flags);
    1294                 :      131808 :             return true;
    1295                 :             :         }
    1296                 :             : 
    1297                 :             :         // Otherwise, deactivate the dependency that was found.
    1298         [ +  + ]:      350630 :         auto [parent_chunk_idx, child_chunk_idx] = Deactivate(candidate_dep.first, candidate_dep.second);
    1299                 :             :         // Determine if there is a dependency from the new bottom to the new top (opposite from the
    1300                 :             :         // dependency that was just deactivated).
    1301         [ +  + ]:      350630 :         auto& parent_reachable = m_reachable[parent_chunk_idx].first;
    1302         [ +  + ]:      350630 :         auto& child_chunk_txn = m_set_info[child_chunk_idx].transactions;
    1303         [ +  + ]:      350630 :         if (parent_reachable.Overlaps(child_chunk_txn)) {
    1304                 :             :             // A self-merge is needed. Note that the child_chunk_idx is the top, and
    1305                 :             :             // parent_chunk_idx is the bottom, because we activate a dependency in the reverse
    1306                 :             :             // direction compared to the deactivation above.
    1307                 :       11285 :             auto merged_chunk_idx = MergeChunks(child_chunk_idx, parent_chunk_idx);
    1308                 :             :             // Re-insert the chunk into the queue, in the same direction. Note that the chunk_idx
    1309                 :             :             // will have changed.
    1310                 :       11285 :             m_nonminimal_chunks.emplace_back(merged_chunk_idx, pivot_idx, flags);
    1311                 :             :         } else {
    1312                 :             :             // No self-merge happens, and thus we have found a way to split the chunk. Create two
    1313                 :             :             // smaller chunks, and add them to the queue. The one that contains the current pivot
    1314                 :             :             // gets to continue with it in the same direction, to minimize the number of times we
    1315                 :             :             // alternate direction. If we were in the second phase already, the newly created chunk
    1316                 :             :             // inherits that too, because we know no split with the pivot on the other side is
    1317                 :             :             // possible already. The new chunk without the current pivot gets a new randomly-chosen
    1318                 :             :             // one.
    1319         [ +  + ]:      339345 :             if (move_pivot_down) {
    1320                 :      164141 :                 auto parent_pivot_idx = PickRandomTx(m_set_info[parent_chunk_idx].transactions);
    1321                 :      164141 :                 m_nonminimal_chunks.emplace_back(parent_chunk_idx, parent_pivot_idx, m_rng.randbits<1>());
    1322                 :      164141 :                 m_nonminimal_chunks.emplace_back(child_chunk_idx, pivot_idx, flags);
    1323                 :             :             } else {
    1324                 :      175204 :                 auto child_pivot_idx = PickRandomTx(m_set_info[child_chunk_idx].transactions);
    1325                 :      175204 :                 m_nonminimal_chunks.emplace_back(parent_chunk_idx, pivot_idx, flags);
    1326                 :      175204 :                 m_nonminimal_chunks.emplace_back(child_chunk_idx, child_pivot_idx, m_rng.randbits<1>());
    1327                 :             :             }
    1328         [ +  + ]:      339345 :             if (m_rng.randbool()) {
    1329                 :      169091 :                 std::swap(m_nonminimal_chunks.back(), m_nonminimal_chunks[m_nonminimal_chunks.size() - 2]);
    1330                 :             :             }
    1331                 :             :         }
    1332                 :             :         return true;
    1333                 :             :     }
    1334                 :             : 
    1335                 :             :     /** Construct a topologically-valid linearization from the current forest state. Must be
    1336                 :             :      *  topological. fallback_order is a comparator that defines a strong order for DepGraphIndexes
    1337                 :             :      *  in this cluster, used to order equal-feerate transactions and chunks.
    1338                 :             :      *
    1339                 :             :      * Specifically, the resulting order consists of:
    1340                 :             :      * - The chunks of the current SFL state, sorted by (in decreasing order of priority):
    1341                 :             :      *   - topology (parents before children)
    1342                 :             :      *   - highest chunk feerate first
    1343                 :             :      *   - smallest chunk size first
    1344                 :             :      *   - the chunk with the lowest maximum transaction, by fallback_order, first
    1345                 :             :      * - The transactions within a chunk, sorted by (in decreasing order of priority):
    1346                 :             :      *   - topology (parents before children)
    1347                 :             :      *   - highest tx feerate first
    1348                 :             :      *   - smallest tx size first
    1349                 :             :      *   - the lowest transaction, by fallback_order, first
    1350                 :             :      */
    1351                 :      961472 :     std::vector<DepGraphIndex> GetLinearization(const StrongComparator<DepGraphIndex> auto& fallback_order) const noexcept
    1352                 :             :     {
    1353                 :             :         /** The output linearization. */
    1354                 :      961472 :         std::vector<DepGraphIndex> ret;
    1355         [ -  + ]:      961472 :         ret.reserve(m_set_info.size());
    1356                 :             :         /** A heap with all chunks (by set index) that can currently be included, sorted by
    1357                 :             :          *  chunk feerate (high to low), chunk size (small to large), and by least maximum element
    1358                 :             :          *  according to the fallback order (which is the second pair element). */
    1359                 :      961472 :         std::vector<std::pair<SetIdx, TxIdx>> ready_chunks;
    1360                 :             :         /** For every chunk, indexed by SetIdx, the number of unmet dependencies the chunk has on
    1361                 :             :          *  other chunks (not including dependencies within the chunk itself). */
    1362   [ -  +  -  + ]:      961472 :         std::vector<TxIdx> chunk_deps(m_set_info.size(), 0);
    1363                 :             :         /** For every transaction, indexed by TxIdx, the number of unmet dependencies the
    1364                 :             :          *  transaction has. */
    1365   [ -  +  +  + ]:      961472 :         std::vector<TxIdx> tx_deps(m_tx_data.size(), 0);
           [ -  +  #  # ]
    1366                 :             :         /** A heap with all transactions within the current chunk that can be included, sorted by
    1367                 :             :          *  tx feerate (high to low), tx size (small to large), and fallback order. */
    1368                 :      961472 :         std::vector<TxIdx> ready_tx;
    1369                 :             :         // Populate chunk_deps and tx_deps.
    1370   [ +  +  +  + ]:     9248049 :         for (TxIdx chl_idx : m_transaction_idxs) {
           [ +  +  #  # ]
    1371                 :     7325797 :             const auto& chl_data = m_tx_data[chl_idx];
    1372                 :     7325797 :             tx_deps[chl_idx] = chl_data.parents.Count();
    1373                 :     7325797 :             auto chl_chunk_idx = chl_data.chunk_idx;
    1374                 :     7325797 :             auto& chl_chunk_info = m_set_info[chl_chunk_idx];
    1375                 :     7325797 :             chunk_deps[chl_chunk_idx] += (chl_data.parents - chl_chunk_info.transactions).Count();
    1376                 :             :         }
    1377                 :             :         /** Function to compute the highest element of a chunk, by fallback_order. */
    1378                 :     5053117 :         auto max_fallback_fn = [&](SetIdx chunk_idx) noexcept {
    1379         [ +  - ]:     4091645 :             auto& chunk = m_set_info[chunk_idx].transactions;
    1380                 :     4091645 :             auto it = chunk.begin();
    1381                 :     4091645 :             DepGraphIndex ret = *it;
    1382                 :     4091645 :             ++it;
    1383         [ +  + ]:     7325797 :             while (it != chunk.end()) {
    1384   [ +  -  +  - ]:     3298777 :                 if (fallback_order(*it, ret) > 0) ret = *it;
           [ +  +  #  # ]
    1385                 :     3234152 :                 ++it;
    1386                 :             :             }
    1387                 :     4091645 :             return ret;
    1388                 :             :         };
    1389                 :             :         /** Comparison function for the transaction heap. Note that it is a max-heap, so
    1390                 :             :          *  tx_cmp_fn(a, b) == true means "a appears after b in the linearization". */
    1391                 :     3218202 :         auto tx_cmp_fn = [&](const auto& a, const auto& b) noexcept {
    1392                 :             :             // Bail out for identical transactions.
    1393         [ +  - ]:     2256730 :             if (a == b) return false;
    1394                 :             :             // First sort by increasing transaction feerate.
    1395         [ +  + ]:     2256730 :             auto& a_feerate = m_depgraph.FeeRate(a);
    1396                 :     2256730 :             auto& b_feerate = m_depgraph.FeeRate(b);
    1397         [ +  + ]:     2256730 :             auto feerate_cmp = FeeRateCompare(a_feerate, b_feerate);
    1398         [ +  + ]:     2256730 :             if (feerate_cmp != 0) return feerate_cmp < 0;
    1399                 :             :             // Then by decreasing transaction size.
    1400         [ +  + ]:      833559 :             if (a_feerate.size != b_feerate.size) {
    1401                 :       62521 :                 return a_feerate.size > b_feerate.size;
    1402                 :             :             }
    1403                 :             :             // Tie-break by decreasing fallback_order.
    1404   [ +  +  +  - ]:      810335 :             auto fallback_cmp = fallback_order(a, b);
    1405         [ +  - ]:      771038 :             if (fallback_cmp != 0) return fallback_cmp > 0;
    1406                 :             :             // This should not be hit, because fallback_order defines a strong ordering.
    1407                 :           0 :             Assume(false);
    1408                 :           0 :             return a < b;
    1409                 :             :         };
    1410                 :             :         // Construct a heap with all chunks that have no out-of-chunk dependencies.
    1411                 :             :         /** Comparison function for the chunk heap. Note that it is a max-heap, so
    1412                 :             :          *  chunk_cmp_fn(a, b) == true means "a appears after b in the linearization". */
    1413                 :    10406867 :         auto chunk_cmp_fn = [&](const auto& a, const auto& b) noexcept {
    1414                 :             :             // Bail out for identical chunks.
    1415         [ +  - ]:     9445395 :             if (a.first == b.first) return false;
    1416                 :             :             // First sort by increasing chunk feerate.
    1417         [ +  + ]:     9445395 :             auto& chunk_feerate_a = m_set_info[a.first].feerate;
    1418                 :     9445395 :             auto& chunk_feerate_b = m_set_info[b.first].feerate;
    1419         [ +  + ]:     9445395 :             auto feerate_cmp = FeeRateCompare(chunk_feerate_a, chunk_feerate_b);
    1420         [ +  + ]:     9445395 :             if (feerate_cmp != 0) return feerate_cmp < 0;
    1421                 :             :             // Then by decreasing chunk size.
    1422         [ +  + ]:     3126062 :             if (chunk_feerate_a.size != chunk_feerate_b.size) {
    1423                 :      182557 :                 return chunk_feerate_a.size > chunk_feerate_b.size;
    1424                 :             :             }
    1425                 :             :             // Tie-break by decreasing fallback_order.
    1426   [ +  -  +  - ]:     3030716 :             auto fallback_cmp = fallback_order(a.second, b.second);
    1427         [ +  - ]:     2943505 :             if (fallback_cmp != 0) return fallback_cmp > 0;
    1428                 :             :             // This should not be hit, because fallback_order defines a strong ordering.
    1429                 :           0 :             Assume(false);
    1430                 :           0 :             return a.second < b.second;
    1431                 :             :         };
    1432                 :             :         // Construct a heap with all chunks that have no out-of-chunk dependencies.
    1433   [ +  +  +  + ]:     6013897 :         for (SetIdx chunk_idx : m_chunk_idxs) {
           [ +  +  #  # ]
    1434         [ +  + ]:     4091645 :             if (chunk_deps[chunk_idx] == 0) {
    1435                 :     1684776 :                 ready_chunks.emplace_back(chunk_idx, max_fallback_fn(chunk_idx));
    1436                 :             :             }
    1437                 :             :         }
    1438                 :      961472 :         std::make_heap(ready_chunks.begin(), ready_chunks.end(), chunk_cmp_fn);
    1439                 :             :         // Pop chunks off the heap.
    1440         [ +  + ]:     5053117 :         while (!ready_chunks.empty()) {
    1441                 :     4091645 :             auto [chunk_idx, _rnd] = ready_chunks.front();
    1442         [ -  + ]:     4091645 :             std::pop_heap(ready_chunks.begin(), ready_chunks.end(), chunk_cmp_fn);
    1443                 :     4091645 :             ready_chunks.pop_back();
    1444         [ -  + ]:     4091645 :             Assume(chunk_deps[chunk_idx] == 0);
    1445         [ -  + ]:     4091645 :             const auto& chunk_txn = m_set_info[chunk_idx].transactions;
    1446                 :             :             // Build heap of all includable transactions in chunk.
    1447         [ -  + ]:     4091645 :             Assume(ready_tx.empty());
    1448   [ +  -  +  + ]:    15485442 :             for (TxIdx tx_idx : chunk_txn) {
           [ +  +  #  # ]
    1449         [ +  + ]:     7325797 :                 if (tx_deps[tx_idx] == 0) ready_tx.push_back(tx_idx);
    1450                 :             :             }
    1451         [ -  + ]:     4091645 :             Assume(!ready_tx.empty());
    1452                 :     4091645 :             std::make_heap(ready_tx.begin(), ready_tx.end(), tx_cmp_fn);
    1453                 :             :             // Pick transactions from the ready heap, append them to linearization, and decrement
    1454                 :             :             // dependency counts.
    1455         [ +  + ]:    11417442 :             while (!ready_tx.empty()) {
    1456                 :             :                 // Pop an element from the tx_ready heap.
    1457                 :     7325797 :                 auto tx_idx = ready_tx.front();
    1458                 :     7325797 :                 std::pop_heap(ready_tx.begin(), ready_tx.end(), tx_cmp_fn);
    1459                 :     7325797 :                 ready_tx.pop_back();
    1460                 :             :                 // Append to linearization.
    1461                 :     7325797 :                 ret.push_back(tx_idx);
    1462                 :             :                 // Decrement dependency counts.
    1463         [ +  + ]:     7325797 :                 auto& tx_data = m_tx_data[tx_idx];
    1464   [ +  +  +  + ]:    18915811 :                 for (TxIdx chl_idx : tx_data.children) {
           [ +  +  #  # ]
    1465         [ -  + ]:     6811442 :                     auto& chl_data = m_tx_data[chl_idx];
    1466                 :             :                     // Decrement tx dependency count.
    1467         [ -  + ]:     6811442 :                     Assume(tx_deps[chl_idx] > 0);
    1468   [ +  +  +  + ]:     6811442 :                     if (--tx_deps[chl_idx] == 0 && chunk_txn[chl_idx]) {
    1469                 :             :                         // Child tx has no dependencies left, and is in this chunk. Add it to the tx heap.
    1470                 :     2873901 :                         ready_tx.push_back(chl_idx);
    1471                 :     2873901 :                         std::push_heap(ready_tx.begin(), ready_tx.end(), tx_cmp_fn);
    1472                 :             :                     }
    1473                 :             :                     // Decrement chunk dependency count if this is out-of-chunk dependency.
    1474         [ +  + ]:     6811442 :                     if (chl_data.chunk_idx != chunk_idx) {
    1475         [ -  + ]:     3399802 :                         Assume(chunk_deps[chl_data.chunk_idx] > 0);
    1476         [ +  + ]:     3399802 :                         if (--chunk_deps[chl_data.chunk_idx] == 0) {
    1477                 :             :                             // Child chunk has no dependencies left. Add it to the chunk heap.
    1478                 :     2406869 :                             ready_chunks.emplace_back(chl_data.chunk_idx, max_fallback_fn(chl_data.chunk_idx));
    1479                 :     2406869 :                             std::push_heap(ready_chunks.begin(), ready_chunks.end(), chunk_cmp_fn);
    1480                 :             :                         }
    1481                 :             :                     }
    1482                 :             :                 }
    1483                 :             :             }
    1484                 :             :         }
    1485   [ -  +  -  +  :      961472 :         Assume(ret.size() == m_set_info.size());
                   -  + ]
    1486                 :      961472 :         return ret;
    1487                 :      961472 :     }
    1488                 :             : 
    1489                 :             :     /** Get the diagram for the current state, which must be topological. Test-only.
    1490                 :             :      *
    1491                 :             :      * The linearization produced by GetLinearization() is always at least as good (in the
    1492                 :             :      * CompareChunks() sense) as this diagram, but may be better.
    1493                 :             :      *
    1494                 :             :      * After an OptimizeStep(), the diagram will always be at least as good as before. Once
    1495                 :             :      * OptimizeStep() returns false, the diagram will be equivalent to that produced by
    1496                 :             :      * GetLinearization(), and optimal.
    1497                 :             :      *
    1498                 :             :      * After a MinimizeStep(), the diagram cannot change anymore (in the CompareChunks() sense),
    1499                 :             :      * but its number of segments can increase still. Once MinimizeStep() returns false, the number
    1500                 :             :      * of chunks of the produced linearization will match the number of segments in the diagram.
    1501                 :             :      */
    1502                 :       32939 :     std::vector<FeeFrac> GetDiagram() const noexcept
    1503                 :             :     {
    1504                 :       32939 :         std::vector<FeeFrac> ret;
    1505   [ +  -  +  + ]:      576857 :         for (auto chunk_idx : m_chunk_idxs) {
    1506                 :      510979 :             ret.push_back(m_set_info[chunk_idx].feerate);
    1507                 :             :         }
    1508                 :       32939 :         std::sort(ret.begin(), ret.end(), std::greater{});
    1509                 :       32939 :         return ret;
    1510                 :             :     }
    1511                 :             : 
    1512                 :             :     /** Determine how much work was performed so far. */
    1513                 :    10067885 :     uint64_t GetCost() const noexcept { return m_cost; }
    1514                 :             : 
    1515                 :             :     /** Verify internal consistency of the data structure. */
    1516                 :        3455 :     void SanityCheck() const
    1517                 :             :     {
    1518                 :             :         //
    1519                 :             :         // Verify dependency parent/child information, and build list of (active) dependencies.
    1520                 :             :         //
    1521                 :        3455 :         std::vector<std::pair<TxIdx, TxIdx>> expected_dependencies;
    1522                 :        3455 :         std::vector<std::pair<TxIdx, TxIdx>> all_dependencies;
    1523                 :        3455 :         std::vector<std::pair<TxIdx, TxIdx>> active_dependencies;
    1524   [ +  -  +  + ]:      103436 :         for (auto parent_idx : m_depgraph.Positions()) {
    1525   [ +  +  +  + ]:      285419 :             for (auto child_idx : m_depgraph.GetReducedChildren(parent_idx)) {
    1526         [ +  - ]:      122661 :                 expected_dependencies.emplace_back(parent_idx, child_idx);
    1527                 :             :             }
    1528                 :             :         }
    1529   [ +  -  +  + ]:      103436 :         for (auto tx_idx : m_transaction_idxs) {
    1530   [ +  +  +  + ]:      285419 :             for (auto child_idx : m_tx_data[tx_idx].children) {
    1531         [ +  - ]:      122661 :                 all_dependencies.emplace_back(tx_idx, child_idx);
    1532         [ +  + ]:      122661 :                 if (m_tx_data[tx_idx].active_children[child_idx]) {
    1533         [ +  - ]:       50013 :                     active_dependencies.emplace_back(tx_idx, child_idx);
    1534                 :             :                 }
    1535                 :             :             }
    1536                 :             :         }
    1537                 :        3455 :         std::sort(expected_dependencies.begin(), expected_dependencies.end());
    1538                 :        3455 :         std::sort(all_dependencies.begin(), all_dependencies.end());
    1539         [ -  + ]:        3455 :         assert(expected_dependencies == all_dependencies);
    1540                 :             : 
    1541                 :             :         //
    1542                 :             :         // Verify the chunks against the list of active dependencies
    1543                 :             :         //
    1544                 :        3455 :         SetType chunk_cover;
    1545   [ +  -  +  + ]:       53423 :         for (auto chunk_idx : m_chunk_idxs) {
    1546         [ +  - ]:       46513 :             const auto& chunk_info = m_set_info[chunk_idx];
    1547                 :             :             // Verify that transactions in the chunk point back to it. This guarantees
    1548                 :             :             // that chunks are non-overlapping.
    1549   [ +  -  +  + ]:      189552 :             for (auto tx_idx : chunk_info.transactions) {
    1550         [ -  + ]:       96526 :                 assert(m_tx_data[tx_idx].chunk_idx == chunk_idx);
    1551                 :             :             }
    1552         [ -  + ]:       46513 :             assert(!chunk_cover.Overlaps(chunk_info.transactions));
    1553         [ -  + ]:       46513 :             chunk_cover |= chunk_info.transactions;
    1554                 :             :             // Verify the chunk's transaction set: start from an arbitrary chunk transaction,
    1555                 :             :             // and for every active dependency, if it contains the parent or child, add the
    1556                 :             :             // other. It must have exactly N-1 active dependencies in it, guaranteeing it is
    1557                 :             :             // acyclic.
    1558         [ -  + ]:       46513 :             assert(chunk_info.transactions.Any());
    1559                 :       46513 :             SetType expected_chunk = SetType::Singleton(chunk_info.transactions.First());
    1560                 :             :             while (true) {
    1561                 :       64012 :                 auto old = expected_chunk;
    1562                 :       64012 :                 size_t active_dep_count{0};
    1563         [ +  + ]:      844610 :                 for (const auto& [par, chl] : active_dependencies) {
    1564   [ +  +  +  + ]:      780598 :                     if (expected_chunk[par] || expected_chunk[chl]) {
    1565                 :      164327 :                         expected_chunk.Set(par);
    1566                 :      164327 :                         expected_chunk.Set(chl);
    1567                 :      164327 :                         ++active_dep_count;
    1568                 :             :                     }
    1569                 :             :                 }
    1570         [ +  + ]:       64012 :                 if (old == expected_chunk) {
    1571         [ -  + ]:       46513 :                     assert(expected_chunk.Count() == active_dep_count + 1);
    1572                 :             :                     break;
    1573                 :             :                 }
    1574                 :             :             }
    1575         [ -  + ]:       46513 :             assert(chunk_info.transactions == expected_chunk);
    1576                 :             :             // Verify the chunk's feerate.
    1577         [ +  - ]:       46513 :             assert(chunk_info.feerate == m_depgraph.FeeRate(chunk_info.transactions));
    1578                 :             :             // Verify the chunk's reachable transactions.
    1579         [ +  - ]:       46513 :             assert(m_reachable[chunk_idx] == GetReachable(expected_chunk));
    1580                 :             :             // Verify that the chunk's reachable transactions don't include its own transactions.
    1581         [ -  + ]:       46513 :             assert(!m_reachable[chunk_idx].first.Overlaps(chunk_info.transactions));
    1582         [ -  + ]:       46513 :             assert(!m_reachable[chunk_idx].second.Overlaps(chunk_info.transactions));
    1583                 :             :         }
    1584                 :             :         // Verify that together, the chunks cover all transactions.
    1585         [ -  + ]:        3455 :         assert(chunk_cover == m_depgraph.Positions());
    1586                 :             : 
    1587                 :             :         //
    1588                 :             :         // Verify transaction data.
    1589                 :             :         //
    1590         [ -  + ]:        3455 :         assert(m_transaction_idxs == m_depgraph.Positions());
    1591   [ +  -  +  + ]:      103436 :         for (auto tx_idx : m_transaction_idxs) {
    1592                 :       96526 :             const auto& tx_data = m_tx_data[tx_idx];
    1593                 :             :             // Verify it has a valid chunk index, and that chunk includes this transaction.
    1594         [ -  + ]:       96526 :             assert(m_chunk_idxs[tx_data.chunk_idx]);
    1595         [ -  + ]:       96526 :             assert(m_set_info[tx_data.chunk_idx].transactions[tx_idx]);
    1596                 :             :             // Verify parents/children.
    1597         [ -  + ]:       96526 :             assert(tx_data.parents == m_depgraph.GetReducedParents(tx_idx));
    1598         [ -  + ]:       96526 :             assert(tx_data.children == m_depgraph.GetReducedChildren(tx_idx));
    1599                 :             :             // Verify active_children is a subset of children.
    1600         [ -  + ]:       96526 :             assert(tx_data.active_children.IsSubsetOf(tx_data.children));
    1601                 :             :             // Verify each active child's dep_top_idx points to a valid non-chunk set.
    1602   [ +  +  +  + ]:      187099 :             for (auto child_idx : tx_data.active_children) {
    1603   [ -  +  -  + ]:       50013 :                 assert(tx_data.dep_top_idx[child_idx] < m_set_info.size());
    1604         [ -  + ]:       50013 :                 assert(!m_chunk_idxs[tx_data.dep_top_idx[child_idx]]);
    1605                 :             :             }
    1606                 :             :         }
    1607                 :             : 
    1608                 :             :         //
    1609                 :             :         // Verify active dependencies' top sets.
    1610                 :             :         //
    1611         [ +  + ]:       53468 :         for (const auto& [par_idx, chl_idx] : active_dependencies) {
    1612                 :             :             // Verify the top set's transactions: it must contain the parent, and for every
    1613                 :             :             // active dependency, except the chl_idx->par_idx dependency itself, if it contains the
    1614                 :             :             // parent or child, it must contain both. It must have exactly N-1 active dependencies
    1615                 :             :             // in it, guaranteeing it is acyclic.
    1616                 :       50013 :             SetType expected_top = SetType::Singleton(par_idx);
    1617                 :             :             while (true) {
    1618                 :      153082 :                 auto old = expected_top;
    1619                 :      153082 :                 size_t active_dep_count{0};
    1620   [ +  +  +  + ]:     3713636 :                 for (const auto& [par2_idx, chl2_idx] : active_dependencies) {
    1621   [ +  +  +  + ]:     3560554 :                     if (par_idx == par2_idx && chl_idx == chl2_idx) continue;
    1622   [ +  +  +  + ]:     3407472 :                     if (expected_top[par2_idx] || expected_top[chl2_idx]) {
    1623                 :     1173769 :                         expected_top.Set(par2_idx);
    1624                 :     1173769 :                         expected_top.Set(chl2_idx);
    1625                 :     1173769 :                         ++active_dep_count;
    1626                 :             :                     }
    1627                 :             :                 }
    1628         [ +  + ]:      153082 :                 if (old == expected_top) {
    1629         [ -  + ]:       50013 :                     assert(expected_top.Count() == active_dep_count + 1);
    1630                 :             :                     break;
    1631                 :             :                 }
    1632                 :             :             }
    1633         [ -  + ]:       50013 :             assert(!expected_top[chl_idx]);
    1634         [ -  + ]:       50013 :             auto& dep_top_info = m_set_info[m_tx_data[par_idx].dep_top_idx[chl_idx]];
    1635         [ -  + ]:       50013 :             assert(dep_top_info.transactions == expected_top);
    1636                 :             :             // Verify the top set's feerate.
    1637         [ +  - ]:      100026 :             assert(dep_top_info.feerate == m_depgraph.FeeRate(dep_top_info.transactions));
    1638                 :             :         }
    1639                 :             : 
    1640                 :             :         //
    1641                 :             :         // Verify m_suboptimal_chunks.
    1642                 :             :         //
    1643                 :        3455 :         SetType suboptimal_idxs;
    1644         [ +  + ]:       13449 :         for (size_t i = 0; i < m_suboptimal_chunks.size(); ++i) {
    1645                 :        9994 :             auto chunk_idx = m_suboptimal_chunks[i];
    1646         [ -  + ]:        9994 :             assert(!suboptimal_idxs[chunk_idx]);
    1647                 :        9994 :             suboptimal_idxs.Set(chunk_idx);
    1648                 :             :         }
    1649         [ -  + ]:        3455 :         assert(m_suboptimal_idxs == suboptimal_idxs);
    1650                 :             : 
    1651                 :             :         //
    1652                 :             :         // Verify m_nonminimal_chunks.
    1653                 :             :         //
    1654                 :        3455 :         SetType nonminimal_idxs;
    1655         [ +  + ]:       12968 :         for (size_t i = 0; i < m_nonminimal_chunks.size(); ++i) {
    1656         [ -  + ]:        9513 :             auto [chunk_idx, pivot, flags] = m_nonminimal_chunks[i];
    1657         [ -  + ]:        9513 :             assert(m_tx_data[pivot].chunk_idx == chunk_idx);
    1658         [ -  + ]:        9513 :             assert(!nonminimal_idxs[chunk_idx]);
    1659                 :        9513 :             nonminimal_idxs.Set(chunk_idx);
    1660                 :             :         }
    1661         [ -  + ]:        3455 :         assert(nonminimal_idxs.IsSubsetOf(m_chunk_idxs));
    1662                 :        3455 :     }
    1663                 :             : };
    1664                 :             : 
    1665                 :             : /** Find or improve a linearization for a cluster.
    1666                 :             :  *
    1667                 :             :  * @param[in] depgraph            Dependency graph of the cluster to be linearized.
    1668                 :             :  * @param[in] max_iterations      Upper bound on the amount of work that will be done.
    1669                 :             :  * @param[in] rng_seed            A random number seed to control search order. This prevents peers
    1670                 :             :  *                                from predicting exactly which clusters would be hard for us to
    1671                 :             :  *                                linearize.
    1672                 :             :  * @param[in] fallback_order      A comparator to order transactions, used to sort equal-feerate
    1673                 :             :  *                                chunks and transactions. See SpanningForestState::GetLinearization
    1674                 :             :  *                                for details.
    1675                 :             :  * @param[in] old_linearization   An existing linearization for the cluster, or empty.
    1676                 :             :  * @param[in] is_topological      (Only relevant if old_linearization is not empty) Whether
    1677                 :             :  *                                old_linearization is topologically valid.
    1678                 :             :  * @return                        A tuple of:
    1679                 :             :  *                                - The resulting linearization. It is guaranteed to be at least as
    1680                 :             :  *                                  good (in the feerate diagram sense) as old_linearization.
    1681                 :             :  *                                - A boolean indicating whether the result is guaranteed to be
    1682                 :             :  *                                  optimal with minimal chunks.
    1683                 :             :  *                                - How many optimization steps were actually performed.
    1684                 :             :  */
    1685                 :             : template<typename SetType>
    1686                 :      958469 : std::tuple<std::vector<DepGraphIndex>, bool, uint64_t> Linearize(
    1687                 :             :     const DepGraph<SetType>& depgraph,
    1688                 :             :     uint64_t max_iterations,
    1689                 :             :     uint64_t rng_seed,
    1690                 :             :     const StrongComparator<DepGraphIndex> auto& fallback_order,
    1691                 :             :     std::span<const DepGraphIndex> old_linearization = {},
    1692                 :             :     bool is_topological = true) noexcept
    1693                 :             : {
    1694                 :             :     /** Initialize a spanning forest data structure for this cluster. */
    1695         [ +  + ]:      958469 :     SpanningForestState forest(depgraph, rng_seed);
    1696         [ +  + ]:      958469 :     if (!old_linearization.empty()) {
    1697                 :      957744 :         forest.LoadLinearization(old_linearization);
    1698         [ +  + ]:      957744 :         if (!is_topological) forest.MakeTopological();
    1699                 :             :     } else {
    1700                 :         725 :         forest.MakeTopological();
    1701                 :             :     }
    1702                 :             :     // Make improvement steps to it until we hit the max_iterations limit, or an optimal result
    1703                 :             :     // is found.
    1704         [ +  + ]:      958469 :     if (forest.GetCost() < max_iterations) {
    1705                 :      945095 :         forest.StartOptimizing();
    1706                 :             :         do {
    1707         [ +  + ]:     3722260 :             if (!forest.OptimizeStep()) break;
    1708         [ +  + ]:     2777696 :         } while (forest.GetCost() < max_iterations);
    1709                 :             :     }
    1710                 :             :     // Make chunk minimization steps until we hit the max_iterations limit, or all chunks are
    1711                 :             :     // minimal.
    1712                 :      958469 :     bool optimal = false;
    1713         [ +  + ]:      958469 :     if (forest.GetCost() < max_iterations) {
    1714                 :      944564 :         forest.StartMinimizing();
    1715                 :             :         do {
    1716         [ +  + ]:     5405577 :             if (!forest.MinimizeStep()) {
    1717                 :             :                 optimal = true;
    1718                 :             :                 break;
    1719                 :             :             }
    1720         [ +  + ]:     4461557 :         } while (forest.GetCost() < max_iterations);
    1721                 :             :     }
    1722                 :      958469 :     return {forest.GetLinearization(fallback_order), optimal, forest.GetCost()};
    1723                 :      958469 : }
    1724                 :             : 
    1725                 :             : /** Improve a given linearization.
    1726                 :             :  *
    1727                 :             :  * @param[in]     depgraph       Dependency graph of the cluster being linearized.
    1728                 :             :  * @param[in,out] linearization  On input, an existing linearization for depgraph. On output, a
    1729                 :             :  *                               potentially better linearization for the same graph.
    1730                 :             :  *
    1731                 :             :  * Postlinearization guarantees:
    1732                 :             :  * - The resulting chunks are connected.
    1733                 :             :  * - If the input has a tree shape (either all transactions have at most one child, or all
    1734                 :             :  *   transactions have at most one parent), the result is optimal.
    1735                 :             :  * - Given a linearization L1 and a leaf transaction T in it. Let L2 be L1 with T moved to the end,
    1736                 :             :  *   optionally with its fee increased. Let L3 be the postlinearization of L2. L3 will be at least
    1737                 :             :  *   as good as L1. This means that replacing transactions with same-size higher-fee transactions
    1738                 :             :  *   will not worsen linearizations through a "drop conflicts, append new transactions,
    1739                 :             :  *   postlinearize" process.
    1740                 :             :  */
    1741                 :             : template<typename SetType>
    1742         [ -  + ]:      958191 : void PostLinearize(const DepGraph<SetType>& depgraph, std::span<DepGraphIndex> linearization)
    1743                 :             : {
    1744                 :             :     // This algorithm performs a number of passes (currently 2); the even ones operate from back to
    1745                 :             :     // front, the odd ones from front to back. Each results in an equal-or-better linearization
    1746                 :             :     // than the one started from.
    1747                 :             :     // - One pass in either direction guarantees that the resulting chunks are connected.
    1748                 :             :     // - Each direction corresponds to one shape of tree being linearized optimally (forward passes
    1749                 :             :     //   guarantee this for graphs where each transaction has at most one child; backward passes
    1750                 :             :     //   guarantee this for graphs where each transaction has at most one parent).
    1751                 :             :     // - Starting with a backward pass guarantees the moved-tree property.
    1752                 :             :     //
    1753                 :             :     // During an odd (forward) pass, the high-level operation is:
    1754                 :             :     // - Start with an empty list of groups L=[].
    1755                 :             :     // - For every transaction i in the old linearization, from front to back:
    1756                 :             :     //   - Append a new group C=[i], containing just i, to the back of L.
    1757                 :             :     //   - While L has at least one group before C, and the group immediately before C has feerate
    1758                 :             :     //     lower than C:
    1759                 :             :     //     - If C depends on P:
    1760                 :             :     //       - Merge P into C, making C the concatenation of P+C, continuing with the combined C.
    1761                 :             :     //     - Otherwise:
    1762                 :             :     //       - Swap P with C, continuing with the now-moved C.
    1763                 :             :     // - The output linearization is the concatenation of the groups in L.
    1764                 :             :     //
    1765                 :             :     // During even (backward) passes, i iterates from the back to the front of the existing
    1766                 :             :     // linearization, and new groups are prepended instead of appended to the list L. To enable
    1767                 :             :     // more code reuse, both passes append groups, but during even passes the meanings of
    1768                 :             :     // parent/child, and of high/low feerate are reversed, and the final concatenation is reversed
    1769                 :             :     // on output.
    1770                 :             :     //
    1771                 :             :     // In the implementation below, the groups are represented by singly-linked lists (pointing
    1772                 :             :     // from the back to the front), which are themselves organized in a singly-linked circular
    1773                 :             :     // list (each group pointing to its predecessor, with a special sentinel group at the front
    1774                 :             :     // that points back to the last group).
    1775                 :             :     //
    1776                 :             :     // Information about transaction t is stored in entries[t + 1], while the sentinel is in
    1777                 :             :     // entries[0].
    1778                 :             : 
    1779                 :             :     /** Index of the sentinel in the entries array below. */
    1780                 :             :     static constexpr DepGraphIndex SENTINEL{0};
    1781                 :             :     /** Indicator that a group has no previous transaction. */
    1782                 :             :     static constexpr DepGraphIndex NO_PREV_TX{0};
    1783                 :             : 
    1784                 :             : 
    1785                 :             :     /** Data structure per transaction entry. */
    1786                 :     8206304 :     struct TxEntry
    1787                 :             :     {
    1788                 :             :         /** The index of the previous transaction in this group; NO_PREV_TX if this is the first
    1789                 :             :          *  entry of a group. */
    1790                 :             :         DepGraphIndex prev_tx;
    1791                 :             : 
    1792                 :             :         // The fields below are only used for transactions that are the last one in a group
    1793                 :             :         // (referred to as tail transactions below).
    1794                 :             : 
    1795                 :             :         /** Index of the first transaction in this group, possibly itself. */
    1796                 :             :         DepGraphIndex first_tx;
    1797                 :             :         /** Index of the last transaction in the previous group. The first group (the sentinel)
    1798                 :             :          *  points back to the last group here, making it a singly-linked circular list. */
    1799                 :             :         DepGraphIndex prev_group;
    1800                 :             :         /** All transactions in the group. Empty for the sentinel. */
    1801                 :             :         SetType group;
    1802                 :             :         /** All dependencies of the group (descendants in even passes; ancestors in odd ones). */
    1803                 :             :         SetType deps;
    1804                 :             :         /** The combined fee/size of transactions in the group. Fee is negated in even passes. */
    1805                 :             :         FeeFrac feerate;
    1806                 :             :     };
    1807                 :             : 
    1808                 :             :     // As an example, consider the state corresponding to the linearization [1,0,3,2], with
    1809                 :             :     // groups [1,0,3] and [2], in an odd pass. The linked lists would be:
    1810                 :             :     //
    1811                 :             :     //                                        +-----+
    1812                 :             :     //                                 0<-P-- | 0 S | ---\     Legend:
    1813                 :             :     //                                        +-----+    |
    1814                 :             :     //                                           ^       |     - digit in box: entries index
    1815                 :             :     //             /--------------F---------+    G       |       (note: one more than tx value)
    1816                 :             :     //             v                         \   |       |     - S: sentinel group
    1817                 :             :     //          +-----+        +-----+        +-----+    |          (empty feerate)
    1818                 :             :     //   0<-P-- | 2   | <--P-- | 1   | <--P-- | 4 T |    |     - T: tail transaction, contains
    1819                 :             :     //          +-----+        +-----+        +-----+    |          fields beyond prev_tv.
    1820                 :             :     //                                           ^       |     - P: prev_tx reference
    1821                 :             :     //                                           G       G     - F: first_tx reference
    1822                 :             :     //                                           |       |     - G: prev_group reference
    1823                 :             :     //                                        +-----+    |
    1824                 :             :     //                                 0<-P-- | 3 T | <--/
    1825                 :             :     //                                        +-----+
    1826                 :             :     //                                         ^   |
    1827                 :             :     //                                         \-F-/
    1828                 :             :     //
    1829                 :             :     // During an even pass, the diagram above would correspond to linearization [2,3,0,1], with
    1830                 :             :     // groups [2] and [3,0,1].
    1831                 :             : 
    1832                 :      958191 :     std::vector<TxEntry> entries(depgraph.PositionRange() + 1);
    1833                 :             : 
    1834                 :             :     // Perform two passes over the linearization.
    1835         [ +  + ]:     2874573 :     for (int pass = 0; pass < 2; ++pass) {
    1836         [ -  + ]:     1916382 :         int rev = !(pass & 1);
    1837                 :             :         // Construct a sentinel group, identifying the start of the list.
    1838                 :     1916382 :         entries[SENTINEL].prev_group = SENTINEL;
    1839         [ -  + ]:     1916382 :         Assume(entries[SENTINEL].feerate.IsEmpty());
    1840                 :             : 
    1841                 :             :         // Iterate over all elements in the existing linearization.
    1842         [ +  + ]:    16375558 :         for (DepGraphIndex i = 0; i < linearization.size(); ++i) {
    1843                 :             :             // Even passes are from back to front; odd passes from front to back.
    1844         [ +  + ]:    14459176 :             DepGraphIndex idx = linearization[rev ? linearization.size() - 1 - i : i];
    1845                 :             :             // Construct a new group containing just idx. In even passes, the meaning of
    1846                 :             :             // parent/child and high/low feerate are swapped.
    1847                 :    14459176 :             DepGraphIndex cur_group = idx + 1;
    1848         [ +  + ]:    14459176 :             entries[cur_group].group = SetType::Singleton(idx);
    1849   [ +  +  +  + ]:    14459176 :             entries[cur_group].deps = rev ? depgraph.Descendants(idx): depgraph.Ancestors(idx);
    1850                 :    14459176 :             entries[cur_group].feerate = depgraph.FeeRate(idx);
    1851         [ +  + ]:    14459176 :             if (rev) entries[cur_group].feerate.fee = -entries[cur_group].feerate.fee;
    1852                 :    14459176 :             entries[cur_group].prev_tx = NO_PREV_TX; // No previous transaction in group.
    1853                 :    14459176 :             entries[cur_group].first_tx = cur_group; // Transaction itself is first of group.
    1854                 :             :             // Insert the new group at the back of the groups linked list.
    1855                 :    14459176 :             entries[cur_group].prev_group = entries[SENTINEL].prev_group;
    1856                 :    14459176 :             entries[SENTINEL].prev_group = cur_group;
    1857                 :             : 
    1858                 :             :             // Start merge/swap cycle.
    1859                 :    14459176 :             DepGraphIndex next_group = SENTINEL; // We inserted at the end, so next group is sentinel.
    1860                 :    14459176 :             DepGraphIndex prev_group = entries[cur_group].prev_group;
    1861                 :             :             // Continue as long as the current group has higher feerate than the previous one.
    1862         [ +  + ]:    21018059 :             while (entries[cur_group].feerate >> entries[prev_group].feerate) {
    1863                 :             :                 // prev_group/cur_group/next_group refer to (the last transactions of) 3
    1864                 :             :                 // consecutive entries in groups list.
    1865         [ -  + ]:     6558883 :                 Assume(cur_group == entries[next_group].prev_group);
    1866         [ -  + ]:     6558883 :                 Assume(prev_group == entries[cur_group].prev_group);
    1867                 :             :                 // The sentinel has empty feerate, which is neither higher or lower than other
    1868                 :             :                 // feerates. Thus, the while loop we are in here guarantees that cur_group and
    1869                 :             :                 // prev_group are not the sentinel.
    1870         [ -  + ]:     6558883 :                 Assume(cur_group != SENTINEL);
    1871         [ -  + ]:     6558883 :                 Assume(prev_group != SENTINEL);
    1872         [ +  + ]:     6575578 :                 if (entries[cur_group].deps.Overlaps(entries[prev_group].group)) {
    1873                 :             :                     // There is a dependency between cur_group and prev_group; merge prev_group
    1874                 :             :                     // into cur_group. The group/deps/feerate fields of prev_group remain unchanged
    1875                 :             :                     // but become unused.
    1876         [ +  + ]:     6240998 :                     entries[cur_group].group |= entries[prev_group].group;
    1877         [ +  + ]:     6240998 :                     entries[cur_group].deps |= entries[prev_group].deps;
    1878                 :     6210310 :                     entries[cur_group].feerate += entries[prev_group].feerate;
    1879                 :             :                     // Make the first of the current group point to the tail of the previous group.
    1880                 :     6210310 :                     entries[entries[cur_group].first_tx].prev_tx = prev_group;
    1881                 :             :                     // The first of the previous group becomes the first of the newly-merged group.
    1882                 :     6210310 :                     entries[cur_group].first_tx = entries[prev_group].first_tx;
    1883                 :             :                     // The previous group becomes whatever group was before the former one.
    1884                 :     6210310 :                     prev_group = entries[prev_group].prev_group;
    1885                 :     6210310 :                     entries[cur_group].prev_group = prev_group;
    1886                 :             :                 } else {
    1887                 :             :                     // There is no dependency between cur_group and prev_group; swap them.
    1888                 :      348573 :                     DepGraphIndex preprev_group = entries[prev_group].prev_group;
    1889                 :             :                     // If PP, P, C, N were the old preprev, prev, cur, next groups, then the new
    1890                 :             :                     // layout becomes [PP, C, P, N]. Update prev_groups to reflect that order.
    1891                 :      348573 :                     entries[next_group].prev_group = prev_group;
    1892                 :      348573 :                     entries[prev_group].prev_group = cur_group;
    1893                 :      348573 :                     entries[cur_group].prev_group = preprev_group;
    1894                 :             :                     // The current group remains the same, but the groups before/after it have
    1895                 :             :                     // changed.
    1896                 :      348573 :                     next_group = prev_group;
    1897                 :      348573 :                     prev_group = preprev_group;
    1898                 :             :                 }
    1899                 :             :             }
    1900                 :             :         }
    1901                 :             : 
    1902                 :             :         // Convert the entries back to linearization (overwriting the existing one).
    1903                 :     1916382 :         DepGraphIndex cur_group = entries[0].prev_group;
    1904                 :     1916382 :         DepGraphIndex done = 0;
    1905         [ +  + ]:    10165248 :         while (cur_group != SENTINEL) {
    1906                 :     8248866 :             DepGraphIndex cur_tx = cur_group;
    1907                 :             :             // Traverse the transactions of cur_group (from back to front), and write them in the
    1908                 :             :             // same order during odd passes, and reversed (front to back) in even passes.
    1909         [ +  + ]:     8248866 :             if (rev) {
    1910                 :             :                 do {
    1911         [ +  + ]:     7229588 :                     *(linearization.begin() + (done++)) = cur_tx - 1;
    1912         [ +  + ]:     7229588 :                     cur_tx = entries[cur_tx].prev_tx;
    1913         [ +  + ]:     7229588 :                 } while (cur_tx != NO_PREV_TX);
    1914                 :             :             } else {
    1915                 :             :                 do {
    1916         [ +  + ]:     7229588 :                     *(linearization.end() - (++done)) = cur_tx - 1;
    1917         [ +  + ]:     7229588 :                     cur_tx = entries[cur_tx].prev_tx;
    1918         [ +  + ]:     7229588 :                 } while (cur_tx != NO_PREV_TX);
    1919                 :             :             }
    1920                 :     8248866 :             cur_group = entries[cur_group].prev_group;
    1921                 :             :         }
    1922         [ -  + ]:     1916382 :         Assume(done == linearization.size());
    1923                 :             :     }
    1924                 :      958191 : }
    1925                 :             : 
    1926                 :             : } // namespace cluster_linearize
    1927                 :             : 
    1928                 :             : #endif // BITCOIN_CLUSTER_LINEARIZE_H
        

Generated by: LCOV version 2.0-1