diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..edeb248 Binary files /dev/null and b/.DS_Store differ diff --git a/buffer.hpp b/buffer.hpp new file mode 100644 index 0000000..1696510 --- /dev/null +++ b/buffer.hpp @@ -0,0 +1,145 @@ +#pragma once +#include "record.hpp" +#include +#include +#include + +// Main buffer pool - owns all memory +class BufferPool { +private: + std::vector storage; + int buffer_capacity; + int num_buffers; + +public: + BufferPool(int num_buffers, int buffer_capacity) + : storage(num_buffers * buffer_capacity) + , buffer_capacity(buffer_capacity) + , num_buffers(num_buffers) + { + } + + Record* get_buffer_ptr(int index) + { + return storage.data() + (index * buffer_capacity); + } + + int get_buffer_capacity() const { return buffer_capacity; } + int get_num_buffers() const { return num_buffers; } + int total_capacity() const { return storage.size(); } + + // Sort entire pool (for stage 1 - creating runs) + void sort_all(int count) + { + std::sort(storage.begin(), storage.begin() + count); + } +}; + +// View into a section of the buffer pool +class BufferView { +private: + Record* data; + int capacity; + int length; + +public: + BufferView(Record* ptr, int cap) + : data(ptr) + , capacity(cap) + , length(0) + { + } + + // Read up to capacity records from stream + int read_from(std::istream& in) + { + length = 0; + for (int i = 0; i < capacity; i++) { + if (!(in >> data[i])) { + break; + } + length++; + } + return length; + } + + // Write all current records to stream + bool write_to(std::ostream& out) const + { + for (int i = 0; i < length; i++) { + out << data[i] << '\n'; + if (!out) + return false; + } + return true; + } + + // For stage 2: peek at front record without consuming + const Record& front() const { return data[0]; } + + // For stage 2: consume the front record + void pop_front() + { + if (length > 0) { + for (int i = 0; i < length - 1; i++) { + data[i] = data[i + 1]; + } + length--; + } + } + + // Refill from stream when buffer becomes empty during merge + bool refill_from(std::istream& in) + { + return read_from(in) > 0; + } + + int size() const { return length; } + int get_capacity() const { return capacity; } + bool empty() const { return length == 0; } + bool full() const { return length == capacity; } + + Record& operator[](int i) { return data[i]; } + const Record& operator[](int i) const { return data[i]; } +}; + +// For output buffer during merge +class OutputBuffer { +private: + Record* data; + int capacity; + int length; + +public: + OutputBuffer(Record* ptr, int cap) + : data(ptr) + , capacity(cap) + , length(0) + { + } + + // Add record to output buffer + bool add(const Record& rec) + { + if (length >= capacity) { + return false; // Buffer full + } + data[length++] = rec; + return true; + } + + // Flush buffer to stream and reset + bool flush_to(std::ostream& out) + { + for (int i = 0; i < length; i++) { + out << data[i] << '\n'; + if (!out) + return false; + } + length = 0; + return true; + } + + bool full() const { return length >= capacity; } + int size() const { return length; } +}; diff --git a/config.hpp b/config.hpp new file mode 100644 index 0000000..7bdc3c9 --- /dev/null +++ b/config.hpp @@ -0,0 +1,63 @@ +#pragma once +#include +#include +#include +#include + +class Configuration { +public: + std::string input_file = "1.in"; + std::string output_file = "1.in"; + std::string directory = "tmp"; + bool generate_data = false; + int N = 100000; // number of records in a file + int n = 101; // number of buffers + int b = 10; // blocking factor + + static Configuration parse_args(int argc, char** argv) + { + if (argc <= 1) { + std::cerr << "Please specify input arguments\n"; + std::cerr << "Usage: " << argv[0] << " [-g] [-N count] [-i input] [-o output] [-d directory] [-n buffers] [-b block_size]\n"; + throw std::invalid_argument("No input arguments"); + } + Configuration config; + bool input_set = false; + int opt; + + while ((opt = getopt(argc, argv, "i:o:d:gN:n:b:")) != -1) { + switch (opt) { + case 'i': + config.input_file = optarg; + input_set = true; + break; + case 'o': + config.output_file = optarg; + break; + case 'd': + config.directory = optarg; + break; + case 'g': + config.generate_data = true; + break; + case 'N': + config.N = std::stoi(optarg); + break; + case 'n': + config.n = std::stoi(optarg); + break; + case 'b': + config.b = std::stoi(optarg); + break; + case '?': + throw std::invalid_argument("Invalid command line argument"); + } + } + + if (config.generate_data && input_set) { + throw std::invalid_argument("Cannot specify both -g and -i options"); + } + + return config; + } +}; diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..1403137 --- /dev/null +++ b/main.cpp @@ -0,0 +1,46 @@ +#include "config.hpp" +#include "record.hpp" +#include +#include +#include +#include + +int generate_file(int N, const std::string& filename) +{ + std::ofstream out(filename); + if (!out) { + std::cerr << "Failed to open output file: " << filename << '\n'; + return 1; + } + + for (int i = 0; i < N; i++) { + Record rec = Record::random(); + out << rec << '\n'; + } + + return 0; +} + +int sort_file(const Configuration& opts) +{ + // TODO: Implement sorting with new buffer classes + std::cout << "Sorting not yet implemented\n"; + return 0; +} + +int main(int argc, char** argv) +{ + try { + Configuration opts = Configuration::parse_args(argc, argv); + + if (opts.generate_data) { + return generate_file(opts.N, opts.output_file); + } + + return sort_file(opts); + + } catch (const std::exception& e) { + std::cerr << "Error: " << e.what() << '\n'; + return 1; + } +} diff --git a/record.hpp b/record.hpp new file mode 100644 index 0000000..411663f --- /dev/null +++ b/record.hpp @@ -0,0 +1,53 @@ +#pragma once +#include +#include +#include + +class Record { +public: + std::array a; + int x; + + int evaluate() const + { + int out = 0; + int x_n = 1; + for (int i = 0; i < 5; i++) { + out += a[i] * x_n; + x_n *= x; + } + return out; + } + + bool operator<(const Record& other) const + { + return evaluate() < other.evaluate(); + } + + friend std::ostream& operator<<(std::ostream& os, const Record& r) + { + os << r.a[0] << ' ' << r.a[1] << ' ' << r.a[2] << ' ' + << r.a[3] << ' ' << r.a[4] << ' ' << r.x; + return os; + } + + friend std::istream& operator>>(std::istream& is, Record& r) + { + is >> r.a[0] >> r.a[1] >> r.a[2] >> r.a[3] >> r.a[4] >> r.x; + return is; + } + + static Record random() + { + static std::random_device rd; + static std::mt19937 gen(rd()); + static std::uniform_int_distribution dist; + + Record r; + for (int& val : r.a) { + val = dist(gen); + } + r.x = dist(gen); + return r; + } +}; \ No newline at end of file