summaryrefslogtreecommitdiff
path: root/src/support/topological_orders.cpp
blob: 145ba30043d3f73c5fe8bed4830bd4a3935ca99c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/*
 * Copyright 2024 WebAssembly Community Group participants
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <cassert>

#include "topological_orders.h"

namespace wasm {

TopologicalOrders::Selector
TopologicalOrders::Selector::select(TopologicalOrders& ctx) {
  assert(count >= 1);
  assert(start + count <= ctx.buf.size());
  auto selection = ctx.buf[start];
  // The next selector will select the next index and will not be able to choose
  // the vertex we just selected.
  Selector next = {start + 1, count - 1, 0};
  // Append any child that this selection makes available to the choices for the
  // next selector.
  for (auto child : ctx.graph[selection]) {
    assert(ctx.indegrees[child] > 0);
    if (--ctx.indegrees[child] == 0) {
      ctx.buf[next.start + next.count++] = child;
    }
  }
  return next;
}

std::optional<TopologicalOrders::Selector>
TopologicalOrders::Selector::advance(TopologicalOrders& ctx) {
  assert(count >= 1);
  // Undo the current selection. Backtrack by incrementing the in-degree for
  // each child of the vertex we are unselecting. No need to remove the newly
  // unavailable children from the buffer; they will be overwritten with valid
  // selections.
  auto unselected = ctx.buf[start];
  for (auto child : ctx.graph[unselected]) {
    ++ctx.indegrees[child];
  }
  if (index == count - 1) {
    // We are wrapping back to the original configuration. The current selection
    // element needs to go back on the end and everything else needs to be
    // shifted back to its original location. This ensures that we leave
    // everything how we found it so the previous selector can make its next
    // selection without observing anything having changed in the meantime.
    for (size_t i = 1; i < count; ++i) {
      ctx.buf[start + i - 1] = ctx.buf[start + i];
    }
    ctx.buf[start + count - 1] = unselected;
    return std::nullopt;
  }
  // Otherwise, just swap the next selection into the first position and
  // finalize the selection.
  std::swap(ctx.buf[start], ctx.buf[start + ++index]);
  return select(ctx);
}

TopologicalOrders::TopologicalOrders(
  const std::vector<std::vector<size_t>>& graph)
  : graph(graph), indegrees(graph.size()), buf(graph.size()) {
  if (graph.size() == 0) {
    return;
  }
  // Find the in-degree of each vertex.
  for (const auto& vertex : graph) {
    for (auto child : vertex) {
      ++indegrees[child];
    }
  }
  // Set up the first selector with its possible selections.
  selectors.reserve(graph.size());
  selectors.push_back({0, 0, 0});
  auto& first = selectors.back();
  for (size_t i = 0; i < graph.size(); ++i) {
    if (indegrees[i] == 0) {
      buf[first.count++] = i;
    }
  }
  // Initialize the full stack of selectors.
  while (selectors.size() < graph.size()) {
    selectors.push_back(selectors.back().select(*this));
  }
}

TopologicalOrders& TopologicalOrders::operator++() {
  // Find the last selector that can be advanced, popping any that cannot.
  std::optional<Selector> next;
  while (!selectors.empty() && !(next = selectors.back().advance(*this))) {
    selectors.pop_back();
  }
  if (!next) {
    // No selector could be advanced, so we've seen every possible ordering.
    assert(selectors.empty());
    return *this;
  }
  // We've advanced the last selector on the stack, so initialize the
  // subsequent selectors.
  assert(selectors.size() < graph.size());
  selectors.push_back(*next);
  while (selectors.size() < graph.size()) {
    selectors.push_back(selectors.back().select(*this));
  }

  return *this;
}

} // namespace wasm