From b66adf7ab527644b5f961a47a419fa483bb2e9f8 Mon Sep 17 00:00:00 2001 From: TUNQRT Date: Sat, 22 Mar 2025 13:11:20 +0100 Subject: [PATCH] infrastructure --- .vscode/launch.json | 46 ++++++++ .vscode/settings.json | 55 +++++++++ .vscode/tasks.json | 32 +++++ Dockerfile | 22 ++++ Makefile | 2 + build.sh | 6 + kill.sh | 3 + libpoly.h | 44 +++++++ libpoly.so | Bin 0 -> 56056 bytes run.sh | 3 + triangulation.cpp | 267 ++++++++++++++++++++++++++++++++---------- 11 files changed, 417 insertions(+), 63 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 .vscode/tasks.json create mode 100644 Dockerfile create mode 100644 Makefile create mode 100755 build.sh create mode 100755 kill.sh create mode 100644 libpoly.h create mode 100644 libpoly.so create mode 100755 run.sh diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..c23a76e --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,46 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Docker C++ Debug", + "type": "cppdbg", + "request": "launch", + "program": "/app/a.out", + "args": [], + "stopAtEntry": true, + "cwd": "/app", + "environment": [], + "externalConsole": false, + "pipeTransport": { + "debuggerPath": "/usr/bin/gdb", + "pipeProgram": "ssh", + "pipeArgs": [ + "root@localhost", + "-p", "2222", + "-i", "${workspaceFolder}/key", + "-o", "StrictHostKeychecking=no", + "/usr/bin/gdb --interpreter=mi" + ] + }, + "sourceFileMap": { + "/app": "${workspaceFolder}" + }, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "skip libpoly", + "text": "skip -gfi libpoly.so", + "ignoreFailures": false + } + ] + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..a9e34ab --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,55 @@ +{ + "files.associations": { + "condition_variable": "cpp", + "thread": "cpp", + "array": "cpp", + "atomic": "cpp", + "bit": "cpp", + "*.tcc": "cpp", + "cctype": "cpp", + "chrono": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "deque": "cpp", + "unordered_map": "cpp", + "vector": "cpp", + "exception": "cpp", + "algorithm": "cpp", + "functional": "cpp", + "iterator": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "numeric": "cpp", + "optional": "cpp", + "random": "cpp", + "ratio": "cpp", + "string": "cpp", + "string_view": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "fstream": "cpp", + "initializer_list": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "limits": "cpp", + "mutex": "cpp", + "new": "cpp", + "ostream": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "typeinfo": "cpp", + "map": "cpp" + } +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..4744023 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,32 @@ +{ + "tasks": [ + { + "type": "cppbuild", + "label": "C/C++: g++ build active file", + "command": "/usr/bin/g++-14", + "args": [ + "-fdiagnostics-color=always", + "-g", + "${file}", + "-o", + "${fileDirname}/${fileBasenameNoExtension}", + "-Wall", + "-pedantic", + "-pthread", + "-std=c++20" + ], + "options": { + "cwd": "${fileDirname}" + }, + "problemMatcher": [ + "$gcc" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "detail": "Task generated by Debugger." + } + ], + "version": "2.0.0" +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..a91a01e --- /dev/null +++ b/Dockerfile @@ -0,0 +1,22 @@ +FROM gcc:latest + +COPY ./ /app + +WORKDIR /app + +RUN apt update && apt install -y gdb gdbserver openssh-server && \ + apt clean + +RUN make a.out && echo 'yeey' + +RUN mv key.pub /root/.ssh/authorized_keys && \ + mkdir /var/run/sshd + +EXPOSE 22 + +CMD ["/usr/sbin/sshd", "-D"] + + +# CMD ["sleep", "infinity"] + +# CMD ["/app/a.out"] \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..583566c --- /dev/null +++ b/Makefile @@ -0,0 +1,2 @@ +a.out: triangulation.cpp + g++ -Wl,-rpath=. triangulation.cpp -g -Wall -pedantic -pthread -lpoly -L./ -std=c++20 \ No newline at end of file diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..4625a5d --- /dev/null +++ b/build.sh @@ -0,0 +1,6 @@ +#!/bin/bash +if [ ! -f key ] || [ ! -f key.pub ]; then + rm -f key key.pub && \ + ssh-keygen -t ed25519 -N "" -f key +fi +docker build -t cpp20 . \ No newline at end of file diff --git a/kill.sh b/kill.sh new file mode 100755 index 0000000..8300ff8 --- /dev/null +++ b/kill.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +docker ps --filter name=triangulation --format "{{.ID}}"| xargs -L1 docker kill \ No newline at end of file diff --git a/libpoly.h b/libpoly.h new file mode 100644 index 0000000..220cd7f --- /dev/null +++ b/libpoly.h @@ -0,0 +1,44 @@ +#ifndef libpoly092986127876186578684563243 +#define libpoly092986127876186578684563243 + +#include +#include +#include + +void test(); // prints to stdout to test linking + +struct LP_Point { + double x, y; + auto operator<=>(const LP_Point&) const = default; +}; + +struct LP_Polygon { + std::vector< LP_Point > points; + LP_Polygon(std::vector< LP_Point >&& p) : points(p) {} + LP_Polygon() {} +}; + +class LP_Task : public std::enable_shared_from_this< LP_Task > { +struct Private{ explicit Private() = default; }; + +public: + LP_Task(Private) {} + static std::shared_ptr create() { + return std::make_shared(Private()); + } + + std::shared_ptr getptr() { + return shared_from_this(); + } + LP_Task(LP_Polygon&& _p): p(_p), minimal_triangulation(MAXFLOAT){}; + + LP_Polygon p; + double minimal_triangulation; +}; + +using LP_Task_p = std::shared_ptr< LP_Task >; + +LP_Task_p pickup_task(); +bool submit_task(LP_Task_p&&); + +#endif // libpoly092986127876186578684563243 \ No newline at end of file diff --git a/libpoly.so b/libpoly.so new file mode 100644 index 0000000000000000000000000000000000000000..610d8fcbe2463fbb76013f113cef59ae66e4e02b GIT binary patch literal 56056 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 PJ)Vbu{>A8GajEJLH2it@ literal 0 HcmV?d00001 diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..7af3e8e --- /dev/null +++ b/run.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +docker run --rm -d --name triangulation -p 2222:22 -p 7777:7777 cpp20 diff --git a/triangulation.cpp b/triangulation.cpp index ce62c73..0513059 100644 --- a/triangulation.cpp +++ b/triangulation.cpp @@ -21,21 +21,19 @@ std::condition_variable can_push; std::atomic_uint32_t active_producents; std::atomic_uint32_t active_consuments; -std::atomic_bool tasks_incoming; std::ostream& os = std::cout; std::mutex os_l; //############################# MODIFY HERE -constexpr unsigned NUM_PRODUCENTS = 1; -constexpr unsigned NUM_WORKERS = 3; +constexpr unsigned NUM_PRODUCENTS = 2; +constexpr unsigned NUM_WORKERS = 5; //############################################################ COMMON STRUCTURES struct Temp_Task: public std::enable_shared_from_this< Temp_Task > { LP_Polygon p; double minimal_triangulation; - Temp_Task(LP_Polygon&& _p): p(_p){} }; @@ -45,9 +43,10 @@ class Task { private: Temp_Task_p task; - double triangulate(LP_Polygon* p); + double triangulate_basic(LP_Polygon* p); double triangulate(); double triangulate_subpolys(std::vector poly_key, std::map* m); + std::vector create_key(std::vector* indices); public: Task(): task(nullptr){}; @@ -56,13 +55,20 @@ public: t.task.reset(); } Task(Temp_Task_p&& tp): task(tp){} - void solve_stupid(){ - task->minimal_triangulation = triangulate(&(task->p)); + + double solution() const { return task->minimal_triangulation; } + bool is_empty() const { + return task.get() == nullptr; } + + void solve_stupid(){ + task->minimal_triangulation = triangulate_basic(&(task->p)); + } + void solve_less_stupid(){ task->minimal_triangulation = triangulate(); } + Temp_Task_p unwrap(){ // TODO: vymyslet jinak, zaručit po tomto kroku selfdestruct return std::move(task); } - double const solution(){ return task->minimal_triangulation; } }; class Queue{ @@ -100,7 +106,7 @@ public: using task_queue_ptr = std::shared_ptr; //############################################################ PRODUCE THREAD - +/* class Producent{ private: task_queue_ptr task_queue; @@ -111,30 +117,41 @@ public: Task get_task(){ //LP_Task_p tp = pickup_task(); + os_l.lock(); + os << "picked up" << std::endl; + os_l.unlock(); //#########################3TEMP_PART: - std::vector< LP_Point > points; - srand(static_cast(std::chrono::system_clock::now().time_since_epoch().count())); - int num_of_points = (rand()%10) + 1; - for(int i = 0; i < num_of_points; i++){ - LP_Point p; + // std::vector< LP_Point > points; + // srand(static_cast(std::chrono::system_clock::now().time_since_epoch().count())); + // int num_of_points = (rand()%10) + 1; + // for(int i = 0; i < num_of_points; i++){ + // LP_Point p; - p.x = rand()%199 +1; - p.y = rand()%199 +1; - points.push_back(p); - } - LP_Polygon p(std::move(points)); - Temp_Task_p tp = std::make_shared(std::move( p )); + // p.x = rand()%199 +1; + // p.y = rand()%199 +1; + // points.push_back(p); + // } + // LP_Polygon p(std::move(points)); + // Temp_Task_p tp = std::make_shared(std::move( p )); return Task(std::move(tp)); } void fill_queue(){ - while(tasks_incoming){ - //Task t = get_task(); - - task_queue->push(get_task()); + while(1){ + Task t = get_task(); + if(t.is_empty()) break; + else{ + task_queue->push(std::move(t)); + os_l.lock(); + os << "queued" << std::endl; + os_l.unlock(); + } } + os_l.lock(); + os << "p ready to join" << std::endl; + os_l.unlock(); } }; @@ -154,6 +171,9 @@ public: ~Consument() { active_consuments--; } Task get_task(){ // might be redundant + os_l.lock(); + os << "processing" << std::endl; + os_l.unlock(); return task_queue->pop(); } @@ -161,11 +181,14 @@ public: while(active_producents || ! task_queue->is_empty()){ Task t = get_task(); t.solve_stupid(); - //submit_task(std::move(t.unwrap())); + submit_task(std::move(t.unwrap())); os_l.lock(); os << t.solution() << std::endl; os_l.unlock(); } + os_l.lock(); + os << "c ready to join" << std::endl; + os_l.unlock(); } }; @@ -173,7 +196,7 @@ void consume_thread(task_queue_ptr task_queue){ Consument c(task_queue); c.work(); } - +*/ //############################################################ TRIANGULATION struct Line{ @@ -191,12 +214,19 @@ struct Line{ Line(Line& l): a(l.a), b(l.b), length(l.length){} }; -double Task::triangulate(LP_Polygon* p){ +double Task::triangulate_basic(LP_Polygon* p){ size_t poly_size = p->points.size(); if(poly_size == 3) return 0.; + if(poly_size == 4){ + Line cut1(p->points.at( 0 ), + p->points.at( 2 )); + Line cut2(p->points.at( 1 ), + p->points.at( 3 )); + return std::min(cut1.length, cut2.length); + } - double triangulation; + double triangulation = INFINITY; double min_triangulation = INFINITY; for (size_t point_idx = 0; point_idx < poly_size; point_idx++){ @@ -209,24 +239,31 @@ double Task::triangulate(LP_Polygon* p){ } LP_Polygon s_p(std::move(smaller_poly)); - triangulation = triangulate(&s_p) + cut.length; + triangulation = triangulate_basic(&s_p) + cut.length; if(triangulation < min_triangulation) min_triangulation = triangulation; } return min_triangulation; } -//############################### dynamic something + +//########################### attempt of thought + +// build a list (array?) of all cut lengths ... n +// build a list of "quatrogones"-> pentagones -> hexagones -> ....; each of them carries its min_triangulation +// end up with our polygone already w min triang. +// not really better i guess... + +//########################### dynamic something //use std::map to store triangulations of polygons -//KEY CREATION: - // take relative positions of points in polygon, add them as vectors and do something with the amount of them - // maybe num * vx + num^2 * vy double Task::triangulate(){ std::map m; - std::vector poly_key; - return triangulate_subpolys(poly_key, &m); + std::vector poly_key(task->p.points.size(), true); + return triangulate_subpolys(poly_key, &m); + // + // return triangulate_subpolys_but_better(); } int vec_to_int(std::vector* vec){ @@ -237,46 +274,150 @@ int vec_to_int(std::vector* vec){ return res; } +std::vector Task::create_key(std::vector* indices){ + + size_t key_length = task->p.points.size(); + std::vector key(key_length, false); + + for(size_t i = 0; i < indices->size(); i++){ + key[indices->at(i)] = true; + } + return key; +} + double Task::triangulate_subpolys(std::vector poly_key, std::map* m){ // convert p to int + int key = vec_to_int(&poly_key); + // try to find in map // success -> return mapped value - // failure -> compute triangulation - // save value in map + if (m->count(key)) return m->at(key); // ! contains() is c++20 + + // failure -> create poly from key + std::vector curr_points; + std::vector curr_indices; + for(size_t i = 0; i < task->p.points.size(); i++){ + if(poly_key.at(i)){ + curr_points.push_back(task->p.points.at(i)); + curr_indices.push_back(i); + } + } + LP_Polygon poly(std::move(curr_points)); + + // use stupid triangulation but create poly_key instead of small_poly + // save value in map before any return + + size_t poly_size = poly.points.size(); + if(poly_size == 3){ + (*m)[key] = 0.; + return 0.; + } + + double triangulation; + double min_triangulation = INFINITY; + + for (size_t point_idx = 0; point_idx < poly_size; point_idx++){ + Line cut(poly.points.at( (point_idx - 1) % poly_size ), + poly.points.at( (point_idx + 1) % poly_size )); + + std::vector sub_indices = curr_indices; + sub_indices.erase(sub_indices.begin() + point_idx); + + triangulation = triangulate_subpolys( create_key( &sub_indices ), m ) + + cut.length; + + if(triangulation < min_triangulation) min_triangulation = triangulation; + } + + (*m)[key] = min_triangulation; + // return found value - // COMPUTE TRIANGULATION - // create polygon poly from p - // use stupid triangulation but create poly_key instead of small_poly - - return 0.; - - + return min_triangulation; } +//########################### dynamic something but better + + +//############################################################ TRIANGULATION TEST + +void task_test(){ + std::vector< LP_Point > v1 = {{0,0}, {1,1}, {3,1}, {2,0}}; + std::vector< LP_Point > v2 = {{0,0}, {0,1}, {1,2}, {2,1}, {2,0}}; + std::vector< LP_Point > v3 = {{0,0}, {1,1}, {2,2}, {3,1}, {2,0}, {1,-1}}; + + LP_Polygon p1(std::move(v1)); + LP_Polygon p2(std::move(v2)); + LP_Polygon p3(std::move(v3)); + + Temp_Task_p tp1 = std::make_shared(std::move(p1)); + Temp_Task_p tp2 = std::make_shared(std::move(p2)); + Temp_Task_p tp3 = std::make_shared(std::move(p3)); + + Task t1(std::move(tp1)); + Task t2(std::move(tp2)); + Task t3(std::move(tp3)); + + t1.solve_stupid(); + t2.solve_stupid(); + t3.solve_stupid(); + + std::cout << "4 vertices:\ncorrect: calculated:\n" << "1.414 " << t1.solution() << std::endl; + std::cout << "5 vertices:\ncorrect: calculated:\n" << "4.236 " << t2.solution() << std::endl; + std::cout << "6 vertices:\ncorrect: calculated:\n" << "5.414 " << t1.solution() << std::endl << std::endl; + + t1.solve_less_stupid(); + t2.solve_less_stupid(); + t3.solve_less_stupid(); + + std::cout << "SECOND VERSION:\n\n"; + + std::cout << "4 vertices:\ncorrect: calculated:\n" << "1.414 " << t1.solution() << std::endl; + std::cout << "5 vertices:\ncorrect: calculated:\n" << "4.236 " << t2.solution() << std::endl; + std::cout << "6 vertices:\ncorrect: calculated:\n" << "5.414 " << t1.solution() << std::endl << std::endl; +} + + + //############################################################ MAIN int main(){ - active_producents = active_consuments = 0; - tasks_incoming = true; - task_queue_ptr queue_ptr = std::make_shared(); - std::vector threads; + //############################### MULTI THREAD - for(unsigned int i = 0; i < NUM_PRODUCENTS; i++){ - threads.emplace_back(produce_thread, queue_ptr); - } + // active_producents = active_consuments = 0; - for(unsigned int i = 0; i < NUM_WORKERS; i++){ - threads.emplace_back(consume_thread, queue_ptr); - } + // task_queue_ptr queue_ptr = std::make_shared(); + // std::vector threads; - std::this_thread::sleep_for(std::chrono::milliseconds(5)); - tasks_incoming = false; - for(auto& t : threads){ - t.join(); - os_l.lock(); - os << "joined" << std::endl; - os_l.unlock(); - } - //test(); + // for(unsigned int i = 0; i < NUM_PRODUCENTS; i++){ + // threads.emplace_back(produce_thread, queue_ptr); + // } + + // for(unsigned int i = 0; i < NUM_WORKERS; i++){ + // threads.emplace_back(consume_thread, queue_ptr); + // } + + // for(auto& t : threads){ + // t.join(); + // os_l.lock(); + // os << "joined" << std::endl; + // os_l.unlock(); + // } + + //############################### SINGLE THREAD + + // while(1){ + // LP_Task_p tp = pickup_task(); + // if(!tp) break; + // Task t(std::move(tp)); + // t.solve_stupid(); + // submit_task(std::move(t.unwrap())); + // } + + //############################### CONNECTION TEST + + // test(); + + //############################### TRIANGULATION TEST + task_test(); } \ No newline at end of file