// File: 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
movq %rsp, %rbp
subq $0x1e0, %rsp            # imm = 0x1E0
movq 0x18(%rbp), %rax
movq 0x10(%rbp), %rax
movq %rdi, -0x8(%rbp)
movl %esi, -0xc(%rbp)
movq %rdx, -0x18(%rbp)
movq %rcx, -0x20(%rbp)
movq %r8, -0x28(%rbp)
movq %r9, -0x30(%rbp)
movq 0x18(%rbp), %rsi
xorl %edi, %edi
callq 0x16f30
movl %eax, -0x34(%rbp)
movq -0x8(%rbp), %rax
cmpl $0x0, (%rax)
jle 0x1cbcd
movq -0x18(%rbp), %rdi
callq 0x1d920
testb $0x1, %al
jne 0x1ca70
movb $0x0, -0x35(%rbp)
leaq -0x48(%rbp), %rdi
callq 0x1d940
leaq 0x1ec1e(%rip), %rsi     # 0x3b368
leaq -0x50(%rbp), %rdi
callq 0x1d580
movq -0x18(%rbp), %rdi
callq 0x1d920
movb %al, -0x161(%rbp)
jmp 0x1c764
movb -0x161(%rbp), %al
testb $0x1, %al
jne 0x1c7bf
jmp 0x1c770
movq -0x18(%rbp), %rdi
callq 0x1d960
movq %rax, -0x170(%rbp)
jmp 0x1c782
movq -0x170(%rbp), %rsi
leaq -0x48(%rbp), %rdi
callq 0x1d980
jmp 0x1c794
movq -0x18(%rbp), %rdi
callq 0x1d9b0
jmp 0x1c79f
movb $0x1, -0x35(%rbp)
jmp 0x1c7bf
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x58(%rbp)
movl %eax, -0x5c(%rbp)
leaq -0x50(%rbp), %rdi
callq 0x1d850
jmp 0x1cbd6
leaq -0x50(%rbp), %rdi
callq 0x1d850
testb $0x1, -0x35(%rbp)
je 0x1ca6b
movq -0x48(%rbp), %rax
movq %rax, -0x68(%rbp)
movss -0x40(%rbp), %xmm0
movss %xmm0, -0x6c(%rbp)
movb $0x0, -0x6d(%rbp)
movq -0x28(%rbp), %rdi
leaq -0x68(%rbp), %rsi
movq %rsi, -0x188(%rbp)
callq 0x1d470
movq %rax, %rdi
callq 0x1d550
movq %rax, %rsi
leaq -0x78(%rbp), %rdi
callq 0x1d580
movq -0x188(%rbp), %rsi
movss -0x6c(%rbp), %xmm0
movss %xmm0, -0x17c(%rbp)
movq -0x20(%rbp), %rdi
callq 0x1d5b0
movq %rax, -0x178(%rbp)
jmp 0x1c836
movss -0x17c(%rbp), %xmm1
movq -0x178(%rbp), %rax
movss (%rax), %xmm0
ucomiss %xmm1, %xmm0
jbe 0x1c8a4
movss -0x6c(%rbp), %xmm0
movss %xmm0, -0x194(%rbp)
movq -0x20(%rbp), %rdi
leaq -0x68(%rbp), %rsi
callq 0x1d5b0
movq %rax, -0x190(%rbp)
jmp 0x1c871
movq -0x190(%rbp), %rax
movss -0x194(%rbp), %xmm0
movss %xmm0, (%rax)
movb $0x1, -0x6d(%rbp)
jmp 0x1c8a4
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x58(%rbp)
movl %eax, -0x5c(%rbp)
leaq -0x78(%rbp), %rdi
callq 0x1d850
jmp 0x1cbd6
leaq -0x78(%rbp), %rdi
callq 0x1d850
testb $0x1, -0x6d(%rbp)
je 0x1ca69
movq -0x68(%rbp), %rsi
movq 0x18(%rbp), %rdx
leaq -0x98(%rbp), %rdi
callq 0x13d50
leaq -0x98(%rbp), %rdi
callq 0x1da00
movq %rax, -0xa0(%rbp)
leaq -0x98(%rbp), %rdi
callq 0x1da20
movq %rax, -0xa8(%rbp)
movq -0xa0(%rbp), %rdi
movq -0xa8(%rbp), %rsi
callq 0x13e70
testb $0x1, %al
jne 0x1c90d
jmp 0x1ca67
movq -0xa0(%rbp), %rsi
leaq -0xc8(%rbp), %rdi
movq %rdi, -0x1d0(%rbp)
callq 0x13ed0
movq -0x1d0(%rbp), %rdi
movq 0x18(%rbp), %rsi
callq 0x13eb0
movq %rax, -0xb0(%rbp)
movq -0xa0(%rbp), %rsi
leaq -0xe8(%rbp), %rdi
movq %rdi, -0x1c8(%rbp)
callq 0x13ed0
movq -0x1c8(%rbp), %rsi
leaq -0x34(%rbp), %rdi
callq 0x15910
cvtsi2ssl (%rax), %xmm0
movss %xmm0, -0xcc(%rbp)
movss -0x6c(%rbp), %xmm0
movss -0xcc(%rbp), %xmm1
addss %xmm1, %xmm0
movss %xmm0, -0xec(%rbp)
movq -0xb0(%rbp), %rax
movq %rax, -0x1c0(%rbp)
movq -0x68(%rbp), %rax
movq %rax, -0x1b8(%rbp)
movss -0xec(%rbp), %xmm0
movss %xmm0, -0x1ac(%rbp)
movq -0x18(%rbp), %rax
movq %rax, -0x1a8(%rbp)
movq -0x28(%rbp), %rsi
leaq -0x120(%rbp), %rdi
movq %rdi, -0x1a0(%rbp)
callq 0x1da40
movq -0x1c0(%rbp), %rdi
movq -0x1b8(%rbp), %rsi
movss -0x1ac(%rbp), %xmm0
movq -0x1a8(%rbp), %rdx
movq -0x1a0(%rbp), %rcx
movq -0x20(%rbp), %r8
movq -0x30(%rbp), %r9
movq 0x10(%rbp), %r10
movl -0xc(%rbp), %r11d
movq %rsp, %rax
movl %r11d, 0x8(%rax)
movq %r10, (%rax)
callq 0x1c340
jmp 0x1ca1d
leaq -0x120(%rbp), %rdi
callq 0x1da70
movq -0xa0(%rbp), %rdi
xorl %esi, %esi
callq 0x1da90
movq %rax, -0x130(%rbp)
movq %rdx, -0x128(%rbp)
jmp 0x1c8f1
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x58(%rbp)
movl %eax, -0x5c(%rbp)
leaq -0x120(%rbp), %rdi
callq 0x1da70
jmp 0x1cbd6
jmp 0x1ca69
jmp 0x1ca6b
jmp 0x1cbc8
leaq -0x140(%rbp), %rdi
leaq 0x1e912(%rip), %rsi     # 0x3b390
callq 0x1dad0
movq -0x8(%rbp), %rax
movl (%rax), %ecx
addl $-0x1, %ecx
movl %ecx, (%rax)
movq -0x8(%rbp), %rax
cmpl $0x0, (%rax)
jne 0x1cad1
leaq -0x140(%rbp), %rdi
callq 0x1db10
jmp 0x1caa5
leaq 0x1e934(%rip), %rdi     # 0x3b3e0
callq 0x4540
movl $0x1, -0x144(%rbp)
jmp 0x1cb9e
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x58(%rbp)
movl %eax, -0x5c(%rbp)
jmp 0x1cbba
leaq -0x140(%rbp), %rdi
callq 0x1db10
jmp 0x1cadf
leaq 0x1e8d2(%rip), %rsi     # 0x3b3b8
leaq -0x158(%rbp), %rdi
callq 0x1dad0
jmp 0x1caf4
leaq 0x1e8e5(%rip), %rdi     # 0x3b3e0
leaq -0x158(%rbp), %rsi
callq 0x40f0
leaq 0x1e882(%rip), %rsi     # 0x3b390
leaq -0x160(%rbp), %rdi
callq 0x1d580
jmp 0x1cb1c
movq -0x8(%rbp), %rax
cmpl $0x0, (%rax)
jne 0x1cb4b
movl $0x1, -0x144(%rbp)
jmp 0x1cb60
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x58(%rbp)
movl %eax, -0x5c(%rbp)
leaq -0x158(%rbp), %rdi
callq 0x1db60
jmp 0x1cbba
movq -0x8(%rbp), %rax
movl (%rax), %ecx
addl $0x1, %ecx
movl %ecx, (%rax)
movl $0x0, -0x144(%rbp)
leaq -0x160(%rbp), %rdi
callq 0x1d850
leaq -0x158(%rbp), %rdi
callq 0x1db60
movl -0x144(%rbp), %eax
testl %eax, %eax
jne 0x1cb9e
jmp 0x1cb84
jmp 0x1cb86
leaq -0x140(%rbp), %rdi
callq 0x1db10
jmp 0x1cb94
movl $0x0, -0x144(%rbp)
leaq -0x140(%rbp), %rdi
callq 0x1db60
movl -0x144(%rbp), %eax
testl %eax, %eax
je 0x1cbb8
jmp 0x1cbb6
jmp 0x1cbcd
jmp 0x1cbc8
leaq -0x140(%rbp), %rdi
callq 0x1db60
jmp 0x1cbd6
jmp 0x1c718
addq $0x1e0, %rsp            # imm = 0x1E0
popq %rbp
retq
movq -0x58(%rbp), %rdi
callq 0x4580
nop