 #include <type_traits>  // std::is_same<>
#include <limits>       // std::numeric_limits<>
#include <climits>      // CHAR_BIT
#include <cmath>        // std::log2, std::ceil, std::floor
#include <stdexcept>    // std::runtime_error
#include <array>        // std::array<>
#include <iostream>     // std::istream and std::ostream
#include <vector>       // std::vector<>
#include <memory>       // std::shared_ptr<>
#include <utility>      // std::move
#include <algorithm>    // std::copy
#include <cstring>      // std::memcpy
#include <bsd/stdlib.h> // arc4random_buf
#include <x86intrin.h>  // SSE and AVX intrinsics
#include "prg.h"
#include "prg_aes_impl.h"

#include <iostream>
 
 
#include <cstdlib>
 
 

#include "block.h"
#include <chrono>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <fstream>
#include <boost/asio.hpp>

using boost::asio::ip::tcp;

#include "duoram-utils.h" 
#include <mutex>
 

 

int main(int argc, char * argv[])
{   
 
    const size_t expo =  5;
    const size_t db_nitems            = 1ULL << expo;
    DB = (DB_t *) std::aligned_alloc(sizeof(__m256i), db_nitems * sizeof(DB_t));
    reading_b = (int8_t *) malloc(db_nitems * sizeof(int8_t));
    reading_c = (int8_t *) malloc(db_nitems * sizeof(int8_t));
    reading_d = (int8_t *) malloc(db_nitems * sizeof(int8_t));
    writing_b = (int8_t *) malloc(db_nitems * sizeof(int8_t));
    writing_c = (int8_t *) malloc(db_nitems * sizeof(int8_t));
    writing_d = (int8_t *) malloc(db_nitems * sizeof(int8_t));
    b = (DB_t *) std::aligned_alloc(sizeof(__m256i), db_nitems * sizeof(DB_t));
    c = (DB_t *) std::aligned_alloc(sizeof(__m256i), db_nitems * sizeof(DB_t));
    d = (DB_t *) std::aligned_alloc(sizeof(__m256i), db_nitems * sizeof(DB_t));
    
    DB_t DB0[db_nitems];
    DB_t DB1[db_nitems];

    for(size_t j = 0; j < db_nitems; ++j)
    {
      DB[j] = j;
      arc4random_buf(&DB0[j], sizeof(DB_t));
      DB1[j] = DB[j] - DB0[j];
    }
      char const * filename0;
      filename0 = "DB0";

      char const * filename1;
      filename1 = "DB1";      
      
      int w0 = open( filename0, O_WRONLY | O_CREAT, S_IWRITE | S_IREAD);
      size_t written = write(w0, DB0, db_nitems * sizeof(DB0[0]));
      if(written<0) {
             perror("Write error");
       }
      close(w0);

      int w1 = open( filename1, O_WRONLY | O_CREAT, S_IWRITE | S_IREAD);
      written = write(w1, DB1, db_nitems * sizeof(DB1[0]));
      if(written<0) {
             perror("Write error");
       }
      close(w1);
   

   generate_flags_and_standard_basis_vectors2(false, db_nitems);

   char const * p0_filename0;
   p0_filename0 = "party0_read_flags_b";
   w0 = open( p0_filename0, O_WRONLY | O_CREAT, S_IWRITE | S_IREAD);
   written = write(w0, reading_b, db_nitems * sizeof(reading_b[0]));
      if(written<0) {
             perror("Write error");
       }
      close(w0);
  
   p0_filename0 = "party0_write_flags_b";
   w0 = open( p0_filename0, O_WRONLY | O_CREAT, S_IWRITE | S_IREAD);
   written = write(w0, writing_b, db_nitems * sizeof(writing_b[0]));
      if(written<0) {
             perror("Write error");
       }
      close(w0);

   p0_filename0 = "party0_write_b";
   w0 = open( p0_filename0, O_WRONLY | O_CREAT, S_IWRITE | S_IREAD);
   written = write(w0, b, db_nitems * sizeof(b[0]));
      if(written<0) {
             perror("Write error");
       }
      close(w0);





   char const * p0_filename1;
   p0_filename1 = "party0_read_flags_c";
   w0 = open( p0_filename1, O_WRONLY | O_CREAT, S_IWRITE | S_IREAD);
       written = write(w0, reading_c, db_nitems * sizeof(reading_c[0]));
      if(written<0) {
             perror("Write error");
       }
      close(w0);

   p0_filename1 = "party0_write_flags_c";
   w0 = open( p0_filename1, O_WRONLY | O_CREAT, S_IWRITE | S_IREAD);
   written = write(w0, writing_c, db_nitems * sizeof(writing_c[0]));
      if(written<0) {
             perror("Write error");
       }
      close(w0);

   p0_filename1 = "party0_write_c";
   w0 = open( p0_filename1, O_WRONLY | O_CREAT, S_IWRITE | S_IREAD);
   written = write(w0, c, db_nitems * sizeof(c[0]));
      if(written<0) {
             perror("Write error");
       }
      close(w0);






   char const * p0_filename2;
   p0_filename2 = "party0_read_flags_d";
   w0 = open( p0_filename2, O_WRONLY | O_CREAT, S_IWRITE | S_IREAD);
       written = write(w0, reading_d, db_nitems * sizeof(reading_d[0]));
      if(written<0) {
             perror("Write error");
       }
      close(w0);

   p0_filename2 = "party0_write_flags_d";
   w0 = open( p0_filename2, O_WRONLY | O_CREAT, S_IWRITE | S_IREAD);
   written = write(w0, writing_d, db_nitems * sizeof(writing_d[0]));
      if(written<0) {
             perror("Write error");
       }
      close(w0);

   p0_filename2 = "party0_write_d";
   w0 = open( p0_filename2, O_WRONLY | O_CREAT, S_IWRITE | S_IREAD);
   written = write(w0, d, db_nitems * sizeof(d[0]));
      if(written<0) {
             perror("Write error");
       }
      close(w0);






  generate_flags_and_standard_basis_vectors2(true, db_nitems);



   char const * p1_filename0;
   p1_filename0 = "party1_read_flags_b";
   w0 = open( p1_filename0, O_WRONLY | O_CREAT, S_IWRITE | S_IREAD);
       written = write(w0, reading_b, db_nitems * sizeof(reading_b[0]));
      if(written<0) {
             perror("Write error");
       }
      close(w0);

   p1_filename0 = "party1_write_flags_b";
   w0 = open( p1_filename0, O_WRONLY | O_CREAT, S_IWRITE | S_IREAD);
       written = write(w0, writing_b, db_nitems * sizeof(writing_b[0]));
      if(written<0) {
             perror("Write error");
       }
      close(w0);

   p1_filename0 = "party1_write_b";
   w0 = open( p1_filename0, O_WRONLY | O_CREAT, S_IWRITE | S_IREAD);
       written = write(w0, b, db_nitems * sizeof(b[0]));
      if(written<0) {
             perror("Write error");
       }
      close(w0);








   char const * p1_filename1;
   p1_filename1 = "party1_read_flags_c";
   w0 = open( p1_filename1, O_WRONLY | O_CREAT, S_IWRITE | S_IREAD);
       written = write(w0, reading_c, db_nitems * sizeof(reading_c[0]));
      if(written<0) {
             perror("Write error");
       }
      close(w0);

   p1_filename1 = "party1_write_flags_c";
   w0 = open( p1_filename1, O_WRONLY | O_CREAT, S_IWRITE | S_IREAD);
       written = write(w0, writing_c, db_nitems * sizeof(writing_c[0]));
      if(written<0) {
             perror("Write error");
       }
      close(w0);

   p1_filename1 = "party1_write_c";
   w0 = open( p1_filename1, O_WRONLY | O_CREAT, S_IWRITE | S_IREAD);
   written = write(w0, c, db_nitems * sizeof(c[0]));
      if(written<0) {
             perror("Write error");
       }
      close(w0);







   char const * p1_filename2;
   p1_filename2 = "party1_read_flags_d";
   w0 = open( p1_filename2, O_WRONLY | O_CREAT, S_IWRITE | S_IREAD);
       written = write(w0, reading_d, db_nitems * sizeof(reading_d[0]));
      if(written<0) {
             perror("Write error");
       }
      close(w0);

   p1_filename2 = "party1_write_flags_d";
   w0 = open( p1_filename2, O_WRONLY | O_CREAT, S_IWRITE | S_IREAD);
       written = write(w0, writing_d, db_nitems * sizeof(writing_d[0]));
      if(written<0) {
             perror("Write error");
       }
      close(w0);

   p1_filename2 = "party1_write_d";
   w0 = open( p1_filename2, O_WRONLY | O_CREAT, S_IWRITE | S_IREAD);
   written = write(w0, d, db_nitems * sizeof(d[0]));
      if(written<0) {
             perror("Write error");
       }
      close(w0);


 return 0;
}
