Files
revng-revng/tests/unit/DepthFirstVisit.h
2025-02-13 13:10:51 +02:00

268 lines
6.6 KiB
C++

#pragma once
//
// This file is distributed under the MIT License. See LICENSE.md for details.
//
#include <deque>
#include <iterator>
#include <queue>
#include <set>
#include "llvm/ADT/iterator_range.h"
#include "revng/ADT/RecursiveCoroutine.h"
#include "revng/Support/Assert.h"
class Node {
private:
size_t Index;
const char *Name;
std::vector<Node *> Children;
std::vector<Node *> Parents;
public:
Node(size_t Index, const char *Name) : Index(Index), Name(Name) {}
size_t index() const { return Index; }
const char *name() const { return Name; }
const std::vector<Node *> &children() const { return Children; }
void addChild(Node *NewChild) {
Children.push_back(NewChild);
NewChild->Parents.push_back(this);
}
Node *getRandomParent() const {
return Parents[static_cast<unsigned long int>(random()) % Parents.size()];
}
};
class Graph {
private:
std::deque<Node> Nodes;
Node *Root = nullptr;
public:
Graph() : Nodes(), Root(newNode()) {}
size_t size() const { return Nodes.size(); }
Node *root() { return Root; }
Node *newNode(const char *Name = nullptr) {
Nodes.emplace_back(Nodes.size(), Name);
return &Nodes.back();
}
Node *getRandom() {
return &Nodes[static_cast<unsigned long>(random()) % Nodes.size()];
}
};
inline Graph createSimpleGraph() {
Graph SimpleGraph;
Node *Root = SimpleGraph.root();
auto *Left = SimpleGraph.newNode("Left");
auto *Right = SimpleGraph.newNode("Right");
auto *Post = SimpleGraph.newNode("Post");
auto *PostPost = SimpleGraph.newNode("PostPost");
Root->addChild(Left);
Root->addChild(Right);
Right->addChild(Post);
Left->addChild(Post);
Post->addChild(PostPost);
return SimpleGraph;
}
inline Graph createRandomGraph() {
Graph Result;
srandom(1);
enum Action {
NewChildren,
LinkToRandom,
LinkToUncle,
Stop
};
auto GetRandomAction = []() {
const long int NewChildrenLikelyhood = 20;
const long int LinkToRandomLikelyhood = 5;
const long int LinkToUncleLikelyhood = 5;
const long int StopLikelyhood = 5;
const long int Total = NewChildrenLikelyhood + LinkToRandomLikelyhood
+ StopLikelyhood + LinkToUncleLikelyhood;
const long int Random = random() % Total;
if (Random < NewChildrenLikelyhood)
return NewChildren;
else if (Random < NewChildrenLikelyhood + LinkToRandomLikelyhood)
return LinkToRandom;
else if (Random < NewChildrenLikelyhood + LinkToRandomLikelyhood
+ LinkToUncleLikelyhood)
return LinkToRandom;
else
return Stop;
};
std::queue<Node *> ToProcess;
ToProcess.push(Result.root());
while (!ToProcess.empty() and Result.size() < 300) {
Node *Current = ToProcess.front();
ToProcess.pop();
Action CurrentAction = GetRandomAction();
while (CurrentAction != Stop) {
switch (CurrentAction) {
case NewChildren: {
Node *NewNode = Result.newNode();
ToProcess.push(NewNode);
Current->addChild(NewNode);
break;
}
case LinkToRandom:
Current->addChild(Result.getRandom());
break;
case LinkToUncle:
Current->getRandomParent()->addChild(Current);
break;
case Stop:
revng_abort();
}
CurrentAction = GetRandomAction();
}
}
return Result;
}
extern size_t MaxDepth;
extern size_t Iterations;
struct Entry {
using iterator = std::vector<Node *>::const_iterator;
Node *CurNode = nullptr;
bool FirstVisit = true;
iterator ChildNext;
iterator ChildEnd;
};
inline void iterativeFindMaxDepth(std::vector<Entry> &Stack) {
while (not Stack.empty()) {
revng_assert(nullptr != Stack.back().CurNode);
// The first time we visit a node, we increment the Iterations, we detect if
// we're closing a loop with something else already on the stack, and we
// increment MaxDepth.
Entry &CurrentEntry = Stack.back();
if (CurrentEntry.FirstVisit) {
CurrentEntry.FirstVisit = false;
++Iterations;
bool Skip = false;
for (const Entry &E : llvm::make_range(Stack.begin(), --Stack.end())) {
if (E.CurNode == CurrentEntry.CurNode) {
Skip = true;
break;
}
}
if (Skip) {
Stack.pop_back();
continue;
}
MaxDepth = std::max(MaxDepth, Stack.size());
continue;
}
if (CurrentEntry.ChildNext != CurrentEntry.ChildEnd) {
// Visit the next children
Node *Child = *CurrentEntry.ChildNext;
auto &Children = Child->children();
++CurrentEntry.ChildNext;
Stack.push_back(Entry{ Child, true, Children.begin(), Children.end() });
} else {
// We're done visiting all the children of the node, pop it
Stack.pop_back();
}
}
}
inline RecursiveCoroutine<> findMaxDepth(std::vector<Node *> &RCS) {
revng_check(not RCS.empty() and nullptr != RCS.back());
Node *Current = RCS.back();
++Iterations;
revng_assert(RCS.size() != 0);
for (const Node *N : llvm::make_range(RCS.begin(), --RCS.end())) {
if (N == Current) {
RCS.pop_back();
rc_return;
}
}
MaxDepth = std::max(MaxDepth, RCS.size());
for (Node *Child : Current->children()) {
RCS.emplace_back(Child);
rc_recur findMaxDepth(RCS);
}
RCS.pop_back();
rc_return;
}
inline RecursiveCoroutine<size_t> findMaxDepthRet(Node *Current,
std::set<Node *> &Stack) {
size_t MaxChildDepth = 0ULL;
for (Node *Child : Current->children()) {
auto &&[_, New] = Stack.insert(Child);
if (New)
MaxChildDepth = std::max(MaxChildDepth,
rc_recur findMaxDepthRet(Child, Stack));
}
size_t CurrDepth = MaxChildDepth + 1;
Stack.erase(Current);
rc_return CurrDepth;
}
inline size_t iterativeFindMaxRet(Node *Root, std::set<Node *> &Stack) {
using iterator = std::vector<Node *>::const_iterator;
std::vector<std::tuple<Node *, iterator, size_t>> IterativeStack;
size_t Max = 0ULL;
IterativeStack.push_back({ Root, Root->children().begin(), 0ULL });
while (not IterativeStack.empty()) {
auto &[Current, ChildIt, MaxChildDepth] = IterativeStack.back();
if (ChildIt != Current->children().end()) {
Node *Child = *ChildIt;
++ChildIt;
if (Stack.insert(Child).second)
IterativeStack.push_back({ Child, Child->children().begin(), 0ULL });
} else {
size_t CurDepth = MaxChildDepth + 1;
Stack.erase(Current);
Max = std::max(Max, CurDepth);
IterativeStack.pop_back();
if (not IterativeStack.empty()) {
auto &OldMax = std::get<2>(IterativeStack.back());
OldMax = std::max(OldMax, CurDepth);
}
}
}
return Max;
}