
enum step
{
  write_out = 0,
  num_steps
};
size_t duoram_progress[step::num_steps] = { 0 };

 

 
// const size_t expo =  5;
// const size_t db_nitems			  = 1ULL << expo;
const size_t number_of_writes = 2;


// const size_t number_of_ind_reads  = 5;

typedef  int64_t DB_t;

struct DuORAM_Write
{
	size_t shift;
	DB_t CW;
};



size_t target_index = 2;
size_t bytes_written2 =0;

DB_t final_cw;

DB_t * DB; 
DB_t * updated_DB; 
DB_t * blinds;
DB_t * updated_blinds;
DB_t * blinded_DB;
DB_t * blinded_DB_recv;
DB_t * updated_blinded_DB_recv;

DB_t M;



DB_t * reading_temp; 
DB_t distinguised_value[number_of_writes];


DB_t * b;
DB_t * c;
DB_t * d;

DB_t one_shareA, one_shareB, one_shareC;
 

int8_t * reading_b;
int8_t * reading_c;
int8_t * reading_d; 

int8_t * writing_b;
int8_t * writing_c;
int8_t * writing_d; 

void generate_flags_and_standard_basis_vectors2(bool party, size_t db_nitems)
{
  for(size_t j = 0; j < db_nitems; ++j)
  { 
    reading_b[j] = 0;
    reading_c[j] = 0;
    reading_d[j] = 0;
    
    if(j > 5 && j < 42)
    {  
      reading_b[j] = 1;
      reading_c[j] = 1;
      reading_d[j] = 1;
    }    
    
    if(j > 75 && (j % 3) == 0)
    {
      reading_b[j] = 1;
      reading_c[j] = 1;
      reading_d[j] = 1;
    }  
  } 
    
    if(party)
    {
     reading_b[target_index] = 1;
     reading_c[target_index] = 1;
     reading_d[target_index] = 1;
   }

      if(!party)
      {
            for(size_t j = 0; j < db_nitems; ++j)
         {
          reading_b[j] = -reading_b[j];
          reading_c[j] = -reading_c[j];
           reading_d[j] = -reading_d[j];
         }
   } 





     for(size_t j = 0; j < db_nitems; ++j)
    {
      b[j] = j;
      c[j] = j;
      d[j] = j;

        writing_b[j] = 0;
      writing_c[j] = 0;
      writing_d[j] = 0;
 
       if(j > 5 && j < 42)
       {
         writing_b[j] = 1;
         writing_c[j] = 1;
         writing_d[j] = 1;
       }

         if(j > 75 && (j % 3) == 0)
       {
         writing_b[j] = 1;
         writing_c[j] = 1;
         writing_d[j] = 1;
       }
    }

 
     
      if(party)
      {
            writing_b[target_index] = 1;
         writing_c[target_index] = 1;
         writing_d[target_index] = 1;
       }

      
      if(party) 
    {
      // printf("PARTY 1\n");
      // for(size_t j = 0; j < db_nitems; ++j)
      // {
      //    if(writing_b[j]) pm0 += 1;
      //    if(writing_c[j]) pm1 += 1;
      //    if(writing_d[j]) pm2 += 1;
      // }
    }
    else
     {
      //    printf("PARTY 0\n");
      //    for(size_t j = 0; j < db_nitems; ++j)
      // {
      //       if(writing_b[j]) pm0 -= 1;
      //    if(writing_c[j]) pm1 -= 1;
      //    if(writing_d[j]) pm2 -= 1;
      // }

      for(size_t j = 0; j < db_nitems; ++j)
      {         
         writing_b[j] = -writing_b[j]; 
         writing_c[j] = -writing_c[j]; 
         writing_d[j] = -writing_d[j];

         b[j] = -b[j];
         c[j] = -c[j];
         d[j] = -d[j];      
      }
     }

     if(party)
     {
      b[target_index] = -47920;
      c[target_index] = -47920;
      d[target_index] = -47920;     
   }
   else
   {
      b[target_index] = 0;
      c[target_index] = 0;
      d[target_index] = 0; 
   }
}
 void setup(DB_t * DB, DB_t * updated_DB, DB_t * blinded_DB_recv, DB_t * blinds, DB_t * updated_blinds, DB_t * updated_blinded_DB_recv, size_t db_nitems, bool party)
 {
 	for(size_t j = 0; j < db_nitems; ++j)
 	{
      if(party)
      {
        DB[j] 				= 0;
        updated_DB[j] 		= 0;
          blinded_DB_recv[j]  = 0; 
      }
      else
      {
        DB[j] 				= 0;
        updated_DB[j] 		= 0;
        blinded_DB_recv[j]  = 0; 
      }
 	}

  for(size_t j = 0; j < db_nitems; ++j) 
	{
		blinds[j] = 0;
		updated_blinds[j] = blinds[j];
		updated_blinded_DB_recv[j] = blinded_DB_recv[j];
	} 
}

void debug_(tcp::socket& in2, tcp::socket& sb, size_t db_nitems)
{
	for(size_t j = 0; j < db_nitems; ++j)
  {
    	DB_t debug_blinds2;
      boost::asio::read(in2, boost::asio::buffer(&debug_blinds2, sizeof(debug_blinds2)));
      assert(blinds[j] == debug_blinds2);
  }

	for(size_t jj = 0; jj < db_nitems; ++jj)
	{
		DB_t debug_refresh;
	  boost::asio::write(sb, boost::asio::buffer(&updated_blinded_DB_recv[jj], sizeof(updated_blinded_DB_recv[jj])));
	  boost::asio::read(sb, boost::asio::buffer(&debug_refresh, sizeof(debug_refresh)));	
		assert(debug_refresh == DB[jj] + blinds[jj]);
	}
	
		DB_t DB_out;
	 	boost::asio::write(sb, boost::asio::buffer(&DB[target_index], sizeof(DB[target_index])));
		boost::asio::read(sb, boost::asio::buffer(&DB_out, sizeof(DB_out)));


	 	DB_out = DB_out + DB[target_index];
	  
	  std::cout << "DB_out = " << DB_out << std::endl;
}


DB_t print_reconstruction(tcp::socket& sb, DB_t output)
{
		  		DB_t out_reconstruction;
		    	boost::asio::write(sb, boost::asio::buffer(&output, sizeof(output)));
		    	boost::asio::read(sb, boost::asio::buffer(&out_reconstruction, sizeof(out_reconstruction)));
		    	out_reconstruction = out_reconstruction + output;

          return out_reconstruction;
}


template<typename T> 
void rotate_by(T * orginal_vector, T * rotated_vector, size_t rotate_by, size_t db_nitems)
{

 			 for(size_t j = 0; j < db_nitems; ++j)
			 {
				
			 	 rotated_vector[(j + rotate_by) % db_nitems] = orginal_vector[j];
			 	 
			 }
}

template<typename T1, typename T2> 
void rotate_all( T1 * orginal_vector1, T1 * rotated_vector1,
								 T2 * orginal_vector2, T2 * rotated_vector2,
								 T1 * orginal_vector3, T1 * rotated_vector3,
							   T2 * orginal_vector4, T2 * rotated_vector4,
								 T1 * orginal_vector5, T1 * rotated_vector5, 
								 T2 * orginal_vector6, T2 * rotated_vector6,
								size_t rotate_by, size_t db_nitems)
{

 			 for(size_t j = 0; j < db_nitems; ++j)
			 {
				
			 	 rotated_vector1[(j + rotate_by) % db_nitems] = orginal_vector1[j];
			 	 rotated_vector2[(j + rotate_by) % db_nitems] = orginal_vector2[j];
			 	 rotated_vector3[(j + rotate_by) % db_nitems] = orginal_vector3[j];
			 	 rotated_vector4[(j + rotate_by) % db_nitems] = orginal_vector4[j];
			 	 rotated_vector5[(j + rotate_by) % db_nitems] = orginal_vector5[j];
			 	 rotated_vector6[(j + rotate_by) % db_nitems] = orginal_vector6[j];

			 	 
			 }
}

template<typename T1> 
void rotate_all( T1 * orginal_vector1, T1 * rotated_vector1,
								 T1 * orginal_vector3, T1 * rotated_vector3,
							   T1 * orginal_vector5, T1 * rotated_vector5, 
								size_t rotate_by, size_t db_nitems)
{

 			 for(size_t j = 0; j < db_nitems; ++j)
			 {
				
			 	 rotated_vector1[(j + rotate_by) % db_nitems] = orginal_vector1[j];
			 	 rotated_vector3[(j + rotate_by) % db_nitems] = orginal_vector3[j];
			 	 rotated_vector5[(j + rotate_by) % db_nitems] = orginal_vector5[j];			 	 
			 }
}
void read_final_cws(tcp::socket& in2)
{

	  read(in2, boost::asio::buffer(&one_shareA, sizeof(one_shareA)));
	  read(in2, boost::asio::buffer(&one_shareB, sizeof(one_shareB)));
	  read(in2, boost::asio::buffer(&one_shareC, sizeof(one_shareC)));
	// in2 >> one_shareA;

 // 	in2 >> one_shareB;
 	
 // 	in2 >> one_shareC;
}

int read_database_shares(bool party, size_t db_nitems)
{
	if(party)
	{
		int const in { open( "DB1", O_RDONLY ) };
		size_t r = read(in, DB,  db_nitems * sizeof(DB_t));
		  
	  if(r < 0) {
  			perror("Read error");
  			close(in);
  			return 1;
			}

	}
	
	if(!party)
	{
		int const in { open( "DB0", O_RDONLY ) };
		size_t r = read(in, DB,  db_nitems * sizeof(DB_t));
		
		if(r < 0) {
    		 perror("Read error");
    		 close(in);
  		    return 1;
 		 }
	}

	return 0;
}

int read_flags(bool party, size_t db_nitems)
{
		if(!party)
	{
		int const in0 { open( "party0_read_flags_b", O_RDONLY ) };
	 	size_t r = read(in0, reading_b,  db_nitems * sizeof(reading_b[0]));	
 		 if(r < 0) {
     perror("Read error");
     close(in0);
     return 1;
 		 }
  
     int const in1 { open( "party0_read_flags_c", O_RDONLY ) };
		 r = read(in1, reading_c,  db_nitems * sizeof(reading_c[0]));
		 if(r < 0) {
     perror("Read error");
     close(in1);
     return 1;
 		 }

	   int const in2 { open( "party0_read_flags_d", O_RDONLY ) };
		 r = read(in2, reading_d,  db_nitems * sizeof(reading_d[0]));	
		 if(r < 0) {
     perror("Read error");
     close(in2);
     return 1;
 		 }



		 int const in0_w { open( "party0_write_flags_b", O_RDONLY ) };
	 	 r = read(in0_w, writing_b,  db_nitems * sizeof(writing_b[0]));	
	 	 if(r < 0) {
     perror("Read error");
     close(in0_w);
     return 1;
 		 }
 	   int const in1_w { open( "party0_write_flags_c", O_RDONLY ) };
		 r = read(in1_w, writing_c,  db_nitems * sizeof(writing_c[0]));
		 if(r < 0) {
     perror("Read error");
     close(in1_w);
     return 1;
 		 }
		 int const in2_w { open( "party0_write_flags_d", O_RDONLY ) };
		 r = read(in2_w, writing_d,  db_nitems * sizeof(writing_d[0]));	
		 if(r < 0) {
     perror("Read error");
     close(in2_w);
     return 1;
 		 }



		 int const in0_w_ { open( "party0_write_b", O_RDONLY ) };
	 	 r = read(in0_w_, b,  db_nitems * sizeof(b[0]));	
	 	 if(r < 0) {
     perror("Read error");
     close(in0_w_);
     return 1;
 		 }
 	   int const in1_w_ { open( "party0_write_c", O_RDONLY ) };
		 r = read(in1_w_, c,  db_nitems * sizeof(c[0]));
		 if(r < 0) {
     perror("Read error");
     close(in1_w_);
     return 1;
 		 }
		 int const in2_w_ { open( "party0_write_d", O_RDONLY ) };
		 r = read(in2_w_, d,  db_nitems * sizeof(d[0]));	
		 if(r < 0) {
     perror("Read error");
     close(in2_w_);
     return 1;
 		 }
	}

	if(party)
	{
		 int const in0 { open( "party1_read_flags_b", O_RDONLY ) };
	 	 size_t r = read(in0, reading_b,  db_nitems * sizeof(reading_b[0]));	
	 	 if(r < 0) {
     perror("Read error");
     close(in0);
     return 1;
 		 }
 	   int const in1 { open( "party1_read_flags_c", O_RDONLY ) };
		 r = read(in1, reading_c,  db_nitems * sizeof(reading_c[0]));
		 if(r < 0) {
     perror("Read error");
     close(in1);
     return 1;
 		 }
		 int const in2 { open( "party1_read_flags_d", O_RDONLY ) };
		 r = read(in2, reading_d,  db_nitems * sizeof(reading_d[0]));	
		 if(r < 0) {
     perror("Read error");
     close(in2);
     return 1;
 		 }


 		 int const in0_w { open( "party1_write_flags_b", O_RDONLY ) };
	 	 r = read(in0_w, writing_b,  db_nitems * sizeof(writing_b[0]));	
	 	 if(r < 0) {
     perror("Read error");
     close(in0_w);
     return 1;
 		 }
 	   int const in1_w { open( "party1_write_flags_c", O_RDONLY ) };
		 r = read(in1_w, writing_c,  db_nitems * sizeof(writing_c[0]));
		 if(r < 0) {
     perror("Read error");
     close(in1_w);
     return 1;
 		 }
		 int const in2_w { open( "party1_write_flags_d", O_RDONLY ) };
		 r = read(in2_w, writing_d,  db_nitems * sizeof(writing_d[0]));	
		 if(r < 0) {
     perror("Read error");
     close(in2_w);
     return 1;
 		 }


 		 int const in0_w_ { open( "party1_write_b", O_RDONLY ) };
	 	 r = read(in0_w_, b,  db_nitems * sizeof(b[0]));	
	 	 if(r < 0) {
     perror("Read error");
     close(in0_w);
     return 1;
 		 }
 	   int const in1_w_ { open( "party1_write_c", O_RDONLY ) };
		 r = read(in1_w_, c,  db_nitems * sizeof(c[0]));
		 if(r < 0) {
     perror("Read error");
     close(in1_w);
     return 1;
 		 }
		 int const in2_w_ { open( "party1_write_d", O_RDONLY ) };
		 r = read(in2_w_, d,  db_nitems * sizeof(d[0]));	
		 if(r < 0) {
     perror("Read error");
     close(in2_w);
     return 1;
 		 }
	}

	// if(party) 
 //  {
 //    	printf("PARTY 1\n");
 //    	for(size_t j = 0; j < db_nitems; ++j)
 //    	{
 //    		if(writing_b[j]) pm0 += 1;
 //    		if(writing_c[j]) pm1 += 1;
 //    		if(writing_d[j]) pm2 += 1;
 //    	}
 //   }
 //   else
 // 	 {
 // 	 	 	printf("PARTY 0\n");
 //   		for(size_t j = 0; j < db_nitems; ++j)
 //    	{
	//  	 	 	if(writing_b[j]) pm0 -= 1;
 //    		if(writing_c[j]) pm1 -= 1;
 //    		if(writing_d[j]) pm2 -= 1;
 //    	}
 //    }
	return 0;	
}

void generate_random_distinguished_points(bool party)
{
	if(party)
  {
  	for(size_t j = 0; j < number_of_writes; ++j)
  	{
  		distinguised_value[j] = j + 2;
  	}
  }
  if(!party)
  {
  	for(size_t j = 0; j < number_of_writes; ++j)
  	{
  		distinguised_value[j] = j + 2;
  	}
  }
}



// template<typename T1> 
// void rotate_all( T1 * orginal_vector1, T1 * rotated_vector1,
// 								 T1 * orginal_vector3, T1 * rotated_vector3,
// 							   T1 * orginal_vector5, T1 * rotated_vector5, 
// 								size_t rotate_by, size_t db_nitems)
// {

//  			 for(size_t j = 0; j < db_nitems; ++j)
// 			 {
				
// 			 	 rotated_vector1[(j + rotate_by) % db_nitems] = orginal_vector1[j];
// 			 	 rotated_vector3[(j + rotate_by) % db_nitems] = orginal_vector3[j];
// 			 	 rotated_vector5[(j + rotate_by) % db_nitems] = orginal_vector5[j];			 	 
// 			 }
// }

DB_t dot_product_with_bool(DB_t D[], int8_t flags[], size_t db_nitems, size_t rotate_by = 0)
{
  DB_t result = 0;
  for(size_t j = 0; j < db_nitems; ++j)
  {
   	result = result + (D[(j + rotate_by) % db_nitems] * flags[j]); 
  }

  return result;
}

// template<typename T1, typename T2>
// T1 dot_product(T1 X[], T2 Y[])
// {
// 		T1 dot = 0;

 
// 		for(size_t j = 0; j < db_nitems; ++j)
// 		{
// 				dot ^= (X[j] & Y[j]);

     
// 		}

// 			return dot;
// }

// void generate_cws(DB_t M0, DB_t final_cw_w, DB_t final_cw_u)
// { 


// 	final_cw_writing  = M0 ^ final_cw_w;	

// 	final_cw_updating = M0 ^ final_cw_u;
 
// }


  



 