#include #include #include #include #include #include #include "BitVector.hpp" // ========================================== // Aufgabenteil a) Baseline-Implementierung // ========================================== class BaselineTree { public: struct Node { uint32_t parent = 0; // 0 = Root std::vector children; }; private: std::vector 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& 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 void bfs(Fn&& fn) const { std::queue 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 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(end - start).count(); // std::cout << "Zeit LOUDS: " << duration << " ms" << std::endl; // std::cout << "Checksum: " << checksum << std::endl; return 0; }