// File: /workspace/github_cpp_makefile_star2_O2/flastest[P]HeterogeneousParallelDijkstra/dijkstra_cond_var.cc
// Function: parallel_dijkstra_thread(int&, int, std::priority_queue<std::pair<unsigned long, float>, std::vector<std::pair<unsigned long, float>, std::allocator<std::pair<unsigned long, float>>>, Offer_Comparator>&, std::map<unsigned long, float, std::less<unsigned long>, std::allocator<std::pair<unsigned long const, float>>>&, std::map<unsigned long, std::shared_ptr<std::mutex>, std::less<unsigned long>, std::allocator<std::pair<unsigned long const, std::shared_ptr<std::mutex>>>>&, std::map<unsigned long, std::shared_ptr<std::mutex>, std::less<unsigned long>, std::allocator<std::pair<unsigned long const, std::shared_ptr<std::mutex>>>>&, std::map<unsigned long, unsigned long, std::less<unsigned long>, std::allocator<std::pair<unsigned long const, unsigned long>>>&, boost::adjacency_list<boost::listS, boost::vecS, boost::directedS, boost::no_property, boost::property<boost::edge_weight_t, int, boost::no_property>, boost::no_property, boost::listS> const&)
void parallel_dijkstra_thread(int& not_done,
					int thread_id,
					offer_pqueue_t& offer_pq,
					distance_map_t& distances,
					mutex_map_t& distance_mutexes,
					mutex_map_t& predecessor_mutexes,
					predecessor_map_t& predecessors,
					const graph_t& graph)
{
	//std::cout<<"hi, I'm " << thread_id <<std::endl;

	//if (DEBUG_THREAD) print_thread_debug(thread_id, "starting thread", debug_file_stream);
	//if (DEBUG) std::cout<<"starting thread: "<<thread_id<<"!!!!!!" <<std::endl;
	const auto weight = get(edge_weight, graph);
	//if (DEBUG) std::cout<< "is thread done? it shouldn't be. This should be true " <<!done[thread_id]<<std::endl;

	while (not_done > 0){
		if(!offer_pq.empty())
		{
			//if (DEBUG_THREAD) 
			//{

			//	print_thread_debug(thread_id, "offer_pq isn't empty", debug_file_stream);
			//	print_thread_debug(thread_id, std::to_string(offer_pq.size())+" amount of offers in offer pqueue", debug_file_stream);
			//}
			//if(DEBUG) std::cout<<"the offer pq isn't empty."<<std::endl;

			bool offer_found = false;

			offer_t offer;
			{
			//	print_thread_debug(thread_id, "locking offer", debug_file_stream);
			//	if (DEBUG) std::cout<<"locking offer"<<std::endl;
				std::lock_guard<std::mutex> guard(pq_mutex);
				if(!offer_pq.empty())
				{
					offer = offer_pq.top();
					offer_pq.pop();
			//		if (DEBUG_THREAD) print_thread_debug(thread_id, std::to_string(offer_pq.size())+" amount of offers in offer pqueue", debug_file_stream);
			//		if (DEBUG_THREAD) print_thread_debug(thread_id, "offer is locked. its node is "+std::to_string(offer.first)+" and its dist is " +std::to_string(offer.second), debug_file_stream);
					offer_found = true;
				}
			//	if (DEBUG_THREAD) print_thread_debug(thread_id, "unlocking offer", debug_file_stream);
			}	


			if (offer_found)
			{
			//	if (DEBUG) std::cout<< "we've obtained offer"<<std::endl; 
				const auto vertex = offer.first;
				const auto offer_distance = offer.second;
				bool explore = false;

			//	if (DEBUG) std::cout<<"offer distance is "<<offer_distance<<std::endl;
				{
					std::lock_guard<std::mutex> guard(*distance_mutexes[vertex]);
			//		if (DEBUG_THREAD) print_thread_debug(thread_id, "offer distance is " +std::to_string(offer_distance) + " and the already done distance is " + std::to_string(distances[vertex]), debug_file_stream);
			//		if(DEBUG) std::cout<<"offer distance is "<<offer_distance <<" and distances[vertex] is "<<distances[vertex] <<std::endl;
					if(offer_distance < distances[vertex])
					{
			//			if (DEBUG) std::cout <<"updating distances of " <<vertex<< " to offer_distance" <<std::endl;
			//			if (DEBUG_THREAD) print_thread_debug(thread_id, "updating distance of " + std::to_string(vertex) + " to offer distance", debug_file_stream);
						distances[vertex] = offer_distance;
						explore = true;
					}
				}


				if (explore) 
				{

					auto [start, finish] = out_edges(vertex,graph);

					for(;start != finish; start++)
					{

						vertex_descriptor neighbor = target(*start, graph);

						float weight_of_edge = get(weight, *start);

						float vd = offer_distance + weight_of_edge;

						//now checking to see if we shoud relax the neighbors
						relax_vertex(neighbor, 
							vertex,
						   	vd,  
			   				offer_pq,
			   				distance_mutexes, 
			   				distances,
			   				predecessor_mutexes,
							predecessors,
			   				thread_id );
			//			if(DEBUG_THREAD) print_thread_debug(thread_id, 
			//				"returned from relaxing. how many things in offer_pq now? " + std::to_string(offer_pq.size()), 
			//				debug_file_stream);

					}
				}
			}
		}
			
		else // priority  queue is empty
		{	
			//if(DEBUG_THREAD) print_thread_debug(thread_id, "pqueue is empty! not done is "+std::to_string(not_done), debug_file_stream);

			
			{
				//act as if there's no work to do
				std::unique_lock<std::mutex> guard(done_mutex);
				not_done--;
				//check to see if everyone else agrees that there's no work to do
				if (not_done == 0)
				{
					guard.unlock();
			//		if(DEBUG) std::cout<<"thread "<<thread_id<<" says no more threads have work to do!"<<std::endl;
			//		if(DEBUG_THREAD) print_thread_debug(thread_id, "okay, all the threads are done. returning.", debug_file_stream);
					
					//notify all, tell all the threads to check again so that if they were waiting, they no longer need to.
					cond_var_offer_pq.notify_all();
					return;
				}

				// if some threads are still at work, but the pqueue is empty, there could be more work to do in the future
				//wait here to wait for other threads to finish
				//some threads might either be about to relax and add more to the pqueue, or finding out that they're over too.
				else 
				{
					guard.unlock();
					std::unique_lock<std::mutex> lk(mutex_cond_var);
   					//condition variable wait
			//		if(DEBUG_THREAD) print_thread_debug(thread_id, "okay, I'm about to wait because pq is empty but not all threads r done", debug_file_stream);

    				cond_var_offer_pq.wait(lk);

    				//this thread might be notified to wake up in the case where the last thread finishes, so we need to check for
    				// everyone being done again.
    				std::lock_guard<std::mutex> done_guard(done_mutex);
    				if(not_done == 0){
    					//cond_var_offer_pq.notify_all();	
    					return;
    				}

					//in the case where the thread is notified because there's more work to do, increment not_done and go back to work
					not_done++;
				}
				//just to be on the safe side....
				guard.unlock();
			}
			
			
			//block until someone else is making progress


			//now the thread will go to sleep until the cond var wakes it up
			///std::unique_lock<std::mutex> lck(mutex_cond_var);
			
			//!adding_to_pq might also mean everything is done
			//cond_var_offer_pq.wait(lck);
	//		{
	//			std::lock_guard<std::mutex> guard(done_mutex);
	//			not_done++;
	//		}
		}
	}
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x98, %rsp
movq %r9, 0x60(%rsp)
movq %r8, 0x40(%rsp)
movq %rcx, 0x20(%rsp)
movq %rdx, %r12
movq %rdi, %r14
leaq 0xbb67(%rip), %rbx      # 0x172e8
leaq 0x28(%rsp), %r15
leaq 0x48(%rsp), %r13
leaq 0xbb2e(%rip), %rbp      # 0x172c0
movq %rdi, 0x58(%rsp)
cmpl $0x0, (%r14)
jle 0xb9bb
movq (%r12), %rax
cmpq 0x8(%r12), %rax
je 0xb8d9
movq %rbp, %rdi
callq 0xc1ea
movq (%r12), %rax
cmpq 0x8(%r12), %rax
je 0xb94c
movq (%rax), %rbx
movss 0x8(%rax), %xmm0
movss %xmm0, 0x1c(%rsp)
movq %r12, %rdi
callq 0xbdf0
leaq 0xbadc(%rip), %rdi      # 0x172c0
callq 0x4210
movq %rbx, 0x28(%rsp)
movq 0x40(%rsp), %rdi
movq %r15, %rsi
callq 0xbcea
movq (%rax), %r13
movq %r13, %rdi
callq 0xc1ea
movq 0x20(%rsp), %rdi
movq %r15, %rsi
callq 0xbd3a
movss (%rax), %xmm0
ucomiss 0x1c(%rsp), %xmm0
jbe 0xb959
movq 0x20(%rsp), %rdi
movq %r15, %rsi
callq 0xbd3a
movss 0x1c(%rsp), %xmm0
movss %xmm0, (%rax)
movq %r13, %rdi
callq 0x4210
movq 0x28(%rsp), %r15
shlq $0x5, %r15
movq 0xd8(%rsp), %rax
addq 0x18(%rax), %r15
movq %r15, %r14
leaq 0x48(%rsp), %r13
movq (%r14), %r14
cmpq %r15, %r14
je 0xb966
movq 0x18(%r14), %rax
cvtsi2ssl (%rax), %xmm0
movq 0x10(%r14), %r13
addss 0x1c(%rsp), %xmm0
movss %xmm0, 0x3c(%rsp)
movq 0x28(%rsp), %rbx
movq %r12, %rbp
leaq 0x68(%rsp), %r12
movq %r12, %rdi
movq 0x40(%rsp), %rsi
callq 0xbf1c
movq 0xd0(%rsp), %rax
movq %rax, (%rsp)
movq %r13, %rdi
movq %rbx, %rsi
movq %r12, %rbx
movq %rbp, %r12
movss 0x3c(%rsp), %xmm0
movq %rbp, %rdx
movq %rbx, %rcx
movq 0x20(%rsp), %r8
movq 0x60(%rsp), %r9
callq 0xb4fc
movq %rbx, %rdi
callq 0xc1a2
jmp 0xb859
movq %r15, %rdi
movq %rbx, %rsi
callq 0xbe08
decl (%r14)
je 0xb983
movq %r15, %rdi
callq 0xbe1e
movq %r13, %rdi
leaq 0xba11(%rip), %rsi      # 0x17310
callq 0xbe08
leaq 0xba2d(%rip), %rdi      # 0x17338
movq %r13, %rsi
callq 0x40d0
movq %rbx, %rdi
callq 0xc1ea
movl (%r14), %eax
testl %eax, %eax
je 0xb99b
incl %eax
movl %eax, (%r14)
movq %rbx, %rdi
callq 0x4210
movq %r13, %rdi
callq 0xbe44
movq %r15, %rdi
callq 0xbe1e
movq %r15, %rdi
callq 0xbe44
jmp 0xb797
movq %rbp, %rdi
callq 0x4210
jmp 0xb797
movq %r13, %rdi
callq 0x4210
leaq 0x48(%rsp), %r13
movq 0x58(%rsp), %r14
leaq 0xb976(%rip), %rbx      # 0x172e8
leaq 0x28(%rsp), %r15
leaq 0xb942(%rip), %rbp      # 0x172c0
jmp 0xb797
leaq 0x28(%rsp), %rdi
callq 0xbe1e
leaq 0xb9a4(%rip), %rdi      # 0x17338
callq 0x4430
jmp 0xb9b1
leaq 0xb946(%rip), %rdi      # 0x172e8
callq 0x4210
leaq 0x48(%rsp), %rdi
callq 0xbe44
leaq 0x28(%rsp), %rdi
callq 0xbe44
addq $0x98, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
jmp 0xb9f9
jmp 0xb9f9
movq %rax, %rbx
leaq 0x48(%rsp), %rdi
callq 0xbe44
jmp 0xb9fc
movq %rax, %rbx
leaq 0xb8d6(%rip), %rdi      # 0x172c0
jmp 0xb9f2
movq %rax, %rbx
movq %r13, %rdi
callq 0x4210
jmp 0xba15
movq %rax, %rbx
leaq 0x28(%rsp), %rdi
callq 0xbe44
jmp 0xba15
movq %rax, %rbx
leaq 0x68(%rsp), %rdi
callq 0xc1a2
movq %rbx, %rdi
callq 0x4470