423 lines
12 KiB
C++
423 lines
12 KiB
C++
#include <thread>
|
|
#include <chrono>
|
|
#include <ctime>
|
|
#include <cstdlib>
|
|
#include <iostream>
|
|
#include <mutex>
|
|
#include <vector>
|
|
#include <queue>
|
|
#include <atomic>
|
|
#include <condition_variable>
|
|
#include <cmath>
|
|
#include <map>
|
|
#include <random>
|
|
#include "libpoly.h"
|
|
|
|
|
|
//############################################################ GLOBALS
|
|
|
|
std::condition_variable can_pop;
|
|
std::condition_variable can_push;
|
|
|
|
std::atomic_uint32_t active_producents;
|
|
std::atomic_uint32_t active_consuments;
|
|
std::ostream& os = std::cout;
|
|
std::mutex os_l;
|
|
|
|
//############################# MODIFY HERE
|
|
|
|
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){}
|
|
};
|
|
|
|
using Temp_Task_p = std::shared_ptr< Temp_Task >;
|
|
|
|
class Task {
|
|
private:
|
|
Temp_Task_p task;
|
|
|
|
double triangulate_basic(LP_Polygon* p);
|
|
double triangulate();
|
|
double triangulate_subpolys(std::vector<bool> poly_key, std::map<int, double>* m);
|
|
std::vector<bool> create_key(std::vector<int>* indices);
|
|
|
|
public:
|
|
Task(): task(nullptr){};
|
|
Task(const Task&) = delete;
|
|
Task(Task&& t): task(t.task){
|
|
t.task.reset();
|
|
}
|
|
Task(Temp_Task_p&& tp): task(tp){}
|
|
|
|
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);
|
|
}
|
|
};
|
|
|
|
class Queue{
|
|
std::queue<Task> q;
|
|
std::mutex q_l;
|
|
const size_t MAX_LENGHT = 100'000;
|
|
|
|
public:
|
|
|
|
void push(Task&& t){
|
|
std::unique_lock<std::mutex> pushlock(q_l);
|
|
can_push.wait(pushlock, [this](){ return (q.size() < MAX_LENGHT); });
|
|
|
|
q.push(std::move(t));
|
|
pushlock.unlock();
|
|
can_pop.notify_one();
|
|
}
|
|
|
|
Task pop(){
|
|
std::unique_lock<std::mutex> poplock(q_l);
|
|
can_pop.wait(poplock, [this](){ return !is_empty(); });
|
|
|
|
Task t(std::move(q.front()));
|
|
q.pop();
|
|
poplock.unlock();
|
|
can_push.notify_one();
|
|
return t;
|
|
}
|
|
|
|
bool is_empty( ){
|
|
return q.empty();
|
|
}
|
|
};
|
|
|
|
using task_queue_ptr = std::shared_ptr<Queue>;
|
|
|
|
//############################################################ PRODUCE THREAD
|
|
/*
|
|
class Producent{
|
|
private:
|
|
task_queue_ptr task_queue;
|
|
public:
|
|
Producent(task_queue_ptr tq): task_queue(tq){ active_producents++; }
|
|
Producent(const Producent&) = delete;
|
|
~Producent(){ active_producents--; }
|
|
|
|
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<unsigned int>(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<Temp_Task>(std::move( p ));
|
|
|
|
return Task(std::move(tp));
|
|
}
|
|
|
|
void fill_queue(){
|
|
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();
|
|
}
|
|
};
|
|
|
|
void produce_thread(task_queue_ptr task_queue){
|
|
Producent p(task_queue);
|
|
p.fill_queue();
|
|
}
|
|
|
|
//############################################################ WORKER THREAD
|
|
|
|
class Consument {
|
|
private:
|
|
task_queue_ptr task_queue;
|
|
public:
|
|
Consument(task_queue_ptr tq): task_queue(tq){ active_consuments++; }
|
|
Consument(const Consument&) = delete;
|
|
~Consument() { active_consuments--; }
|
|
|
|
Task get_task(){ // might be redundant
|
|
os_l.lock();
|
|
os << "processing" << std::endl;
|
|
os_l.unlock();
|
|
return task_queue->pop();
|
|
}
|
|
|
|
void work(){
|
|
while(active_producents || ! task_queue->is_empty()){
|
|
Task t = get_task();
|
|
t.solve_stupid();
|
|
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();
|
|
}
|
|
};
|
|
|
|
void consume_thread(task_queue_ptr task_queue){
|
|
Consument c(task_queue);
|
|
c.work();
|
|
}
|
|
*/
|
|
//############################################################ TRIANGULATION
|
|
|
|
struct Line{
|
|
LP_Point a;
|
|
LP_Point b;
|
|
double length;
|
|
|
|
Line() = delete;
|
|
Line(LP_Point _a, LP_Point _b): a(_a), b(_b){
|
|
LP_Point vec_ba;
|
|
vec_ba.x = b.x - a.x;
|
|
vec_ba.y = b.y - a.y;
|
|
length = sqrt(vec_ba.x*vec_ba.x + vec_ba.y*vec_ba.y);
|
|
}
|
|
Line(Line& l): a(l.a), b(l.b), length(l.length){}
|
|
};
|
|
|
|
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 = INFINITY;
|
|
double min_triangulation = INFINITY;
|
|
|
|
for (size_t point_idx = 0; point_idx < poly_size; point_idx++){
|
|
Line cut(p->points.at( (point_idx - 1) % poly_size ),
|
|
p->points.at( (point_idx + 1) % poly_size ));
|
|
|
|
std::vector<LP_Point> smaller_poly;
|
|
for(size_t i = 0; i < poly_size; i++){
|
|
if(i != point_idx) smaller_poly.push_back(p->points.at(i));
|
|
}
|
|
|
|
LP_Polygon s_p(std::move(smaller_poly));
|
|
triangulation = triangulate_basic(&s_p) + cut.length;
|
|
if(triangulation < min_triangulation) min_triangulation = triangulation;
|
|
}
|
|
|
|
return min_triangulation;
|
|
|
|
}
|
|
|
|
//########################### 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
|
|
|
|
double Task::triangulate(){
|
|
std::map<int, double> m;
|
|
std::vector<bool> poly_key(task->p.points.size(), true);
|
|
return triangulate_subpolys(poly_key, &m);
|
|
//
|
|
// return triangulate_subpolys_but_better();
|
|
}
|
|
|
|
int vec_to_int(std::vector<bool>* vec){
|
|
int res = 0;
|
|
for (size_t i = 0; i < vec->size(); i++){
|
|
res += vec->at(i) * 2^i;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
std::vector<bool> Task::create_key(std::vector<int>* indices){
|
|
|
|
size_t key_length = task->p.points.size();
|
|
std::vector<bool> 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<bool> poly_key, std::map<int, double>* m){
|
|
// convert p to int
|
|
int key = vec_to_int(&poly_key);
|
|
|
|
// try to find in map
|
|
// success -> return mapped value
|
|
if (m->count(key)) return m->at(key); // ! contains() is c++20
|
|
|
|
// failure -> create poly from key
|
|
std::vector<LP_Point> curr_points;
|
|
std::vector<int> 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<int> 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
|
|
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<Temp_Task>(std::move(p1));
|
|
Temp_Task_p tp2 = std::make_shared<Temp_Task>(std::move(p2));
|
|
Temp_Task_p tp3 = std::make_shared<Temp_Task>(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(){
|
|
|
|
//############################### MULTI THREAD
|
|
|
|
// active_producents = active_consuments = 0;
|
|
|
|
// task_queue_ptr queue_ptr = std::make_shared<Queue>();
|
|
// std::vector<std::thread> threads;
|
|
|
|
// 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();
|
|
} |