207 lines
5.4 KiB
C++
207 lines
5.4 KiB
C++
#include <vector>
|
|
#include <queue>
|
|
#include <cstdint>
|
|
#include <random>
|
|
#include <stdexcept>
|
|
#include <iostream>
|
|
|
|
#include "BitVector.hpp"
|
|
|
|
// ==========================================
|
|
// Aufgabenteil a) Baseline-Implementierung
|
|
// ==========================================
|
|
|
|
class BaselineTree {
|
|
public:
|
|
struct Node {
|
|
uint32_t parent = 0; // 0 = Root
|
|
std::vector<uint32_t> children;
|
|
};
|
|
|
|
private:
|
|
std::vector<Node> nodes;
|
|
|
|
public:
|
|
BaselineTree() {
|
|
nodes.emplace_back();
|
|
nodes.emplace_back();
|
|
}
|
|
|
|
static int32_t root() {
|
|
return 1;
|
|
}
|
|
|
|
[[nodiscard]] uint64_t nodeCount() const {
|
|
return nodes.size() - 1;
|
|
}
|
|
|
|
[[nodiscard]] const std::vector<uint32_t>& children(const uint32_t id) const {
|
|
return nodes[id].children;
|
|
}
|
|
|
|
[[nodiscard]] uint32_t parent(const uint32_t id) const {
|
|
return nodes[id].parent;
|
|
}
|
|
|
|
[[nodiscard]] bool exists(const uint32_t id) const {
|
|
return id > 0 && id < nodes.size();
|
|
}
|
|
|
|
uint32_t add(const uint32_t parentId) {
|
|
if (!exists(parentId)) {
|
|
throw std::invalid_argument("parentId does not exist");
|
|
}
|
|
|
|
nodes.emplace_back();
|
|
const uint32_t id = nodes.size() - 1;
|
|
|
|
nodes[id].parent = parentId;
|
|
nodes[parentId].children.push_back(id);
|
|
|
|
return id;
|
|
}
|
|
|
|
// BFS traversing with lambda for increased reusability
|
|
template<typename Fn>
|
|
void bfs(Fn&& fn) const {
|
|
std::queue<uint32_t> q;
|
|
q.push(root());
|
|
|
|
while (!q.empty()) {
|
|
uint32_t u = q.front();
|
|
q.pop();
|
|
|
|
fn(u, nodes[u]);
|
|
|
|
for (uint32_t v : nodes[u].children) {
|
|
q.push(v);
|
|
}
|
|
}
|
|
}
|
|
|
|
[[nodiscard]] uint64_t sizeInBytes() const {
|
|
uint64_t total = 0;
|
|
|
|
// Size of data in the tree instance
|
|
total += sizeof(BaselineTree);
|
|
total += nodes.capacity() * sizeof(Node);
|
|
|
|
// Size of data in node instances
|
|
bfs([&](uint32_t, const Node& n) {
|
|
total += n.children.capacity() * sizeof(uint32_t);
|
|
});
|
|
|
|
return total;
|
|
}
|
|
|
|
static BaselineTree randomTree(const uint64_t N, const uint32_t seed = 03062001) {
|
|
BaselineTree t;
|
|
t.nodes.reserve(N + 1);
|
|
|
|
std::mt19937 gen(seed);
|
|
for (uint32_t i = 2; i <= N; ++i) {
|
|
std::uniform_int_distribution<uint32_t> dist(1, i - 1);
|
|
t.add(dist(gen));
|
|
}
|
|
|
|
return t;
|
|
}
|
|
};
|
|
|
|
|
|
|
|
// ==========================================
|
|
// Aufgabenteil b) LOUDS-Implementierung
|
|
// ==========================================
|
|
|
|
class LOUDSTree {
|
|
// Der BitVector ist der einzige Datenspeicher! Keine Knoten-Objekte.
|
|
BitVector bv;
|
|
|
|
public:
|
|
// TODO: Passen Sie den Konstruktor an Ihre BaselineTree-Klasse an.
|
|
// Der Konstruktor soll den übergebenen Baum in Level-Order traversieren
|
|
// und die LOUDS-Bits in 'bv' setzen.
|
|
explicit LOUDSTree(const BaselineTree &tree) : bv(1 /* TODO: Richtige Größe berechnen! */) {
|
|
// TODO: Implementierung der LOUDS-Erstellung (BFS Traversierung)
|
|
// TODO: bfs can be used here! Comment by JNLOOS
|
|
|
|
// WICHTIG: Am Ende muss der Index gebaut werden:
|
|
bv.buildIndex();
|
|
}
|
|
|
|
uint64_t sizeInBytes() const {
|
|
return bv.sizeInBytes();
|
|
}
|
|
|
|
// --- LOUDS-Operationen (Ausschließlich via rank/select implementieren!) ---
|
|
|
|
uint64_t parent(uint64_t i) {
|
|
// TODO
|
|
return 0;
|
|
}
|
|
|
|
bool isRoot(uint64_t i) {
|
|
// TODO
|
|
return false;
|
|
}
|
|
|
|
bool isLeaf(uint64_t i) {
|
|
// TODO
|
|
return false;
|
|
}
|
|
|
|
uint64_t outDegree(uint64_t i) {
|
|
// TODO
|
|
return 0;
|
|
}
|
|
|
|
uint64_t childNum(uint64_t i, uint64_t j) {
|
|
// TODO: Geben Sie das j-te Kind von nodeId zurück (j ist 1-basiert)
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
|
|
int main() {
|
|
// Parameter für den Benchmark, 1 Million Knoten
|
|
const uint64_t N = 1000000;
|
|
|
|
std::cout << "--- Start der Uebung: LOUDS (N=" << N << ") ---" << std::endl;
|
|
|
|
// 1. Baseline Tree erstellen
|
|
std::cout << "[Init] Erstelle Baseline Tree..." << std::endl;
|
|
// Nutzen Sie std::mt19937 für reproduzierbare Zufallszahlen.
|
|
const BaselineTree baseline = BaselineTree::randomTree(N);
|
|
|
|
// 2. LOUDS Tree erstellen
|
|
// std::cout << "[Init] Konvertiere zu LOUDS..." << std::endl;
|
|
// const LOUDSTree louds(baseline);
|
|
|
|
// 3. Speichermessung
|
|
const uint64_t bytesBaseline = baseline.sizeInBytes();
|
|
// uint64_t bytesLouds = louds.sizeInBytes();
|
|
|
|
std::cout << "Speicher Baseline: " << bytesBaseline / (1024.0 * 1024.0) << " MB" << std::endl;
|
|
// std::cout << "Speicher LOUDS: " << bytesLouds / (1024.0 * 1024.0) << " MB" << std::endl;
|
|
// if (bytesLouds > 0) {
|
|
// std::cout << "Faktor: " << (double) bytesBaseline / bytesLouds << "x" << std::endl;
|
|
// }
|
|
|
|
// 4. Laufzeitmessung (Parent Operation)
|
|
// std::cout << "[Benchmark] Starte 1.000.000 Parent-Abfragen..." << std::endl;
|
|
// uint64_t checksum = 0;
|
|
|
|
// auto start = std::chrono::high_resolution_clock::now();
|
|
// for (uint64_t i = 2; i <= N; ++i) {
|
|
// checksum += louds.parent(i);
|
|
// }
|
|
// auto end = std::chrono::high_resolution_clock::now();
|
|
|
|
// auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
|
|
// std::cout << "Zeit LOUDS: " << duration << " ms" << std::endl;
|
|
// std::cout << "Checksum: " << checksum << std::endl;
|
|
|
|
return 0;
|
|
}
|