//Author: Vera and Yi, unless commented, parts are done by Vera

#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <NTL/ZZ.h>
#include <NTL/ZZ_p.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>



#define G 5
#define P 23

//Yi's head part for sending 
#define PORT 6996
#define BUFSIZE 5

static char buf[BUFSIZE];
static int mybuf[BUFSIZE];

ZZ VALUE =  to_ZZ(5460); //a value for changing the 0,1s in the OT array to different numbers

//end Yi's head part for sending

//void bit_and(ZZ& x, const ZZ& a, const ZZ& b); // x = |a| AND |b|
//void bit_or(ZZ& x, const ZZ& a, const ZZ& b); // x = |a| OR |b|
//void bit_xor(ZZ& x, const ZZ& a, const ZZ& b); // x = |a| XOR |b|
//void mul(ZZ& x,const ZZ& a,const ZZ& b); // x = a * b
//void power(ZZ_p& x, const ZZ_p& a, const ZZ& e); // x = a^e (e may be negative)
//void div(ZZ_p& x, const ZZ_p& a, const ZZ_p& b); // x = a/b


ZZ multy(ZZ a,ZZ b)
{
  ZZ c;
  bit_and(c,a,b);
  return c;
}

void OT_input(ZZ a,ZZ b,ZZ c1,ZZ* array)
{
  ZZ c,a1,b1;
  bit_and(c,a,b);
  bit_xor(array[0],c,c1);
 // cout<<"array 0 is "<<array[0]<<endl;
  bit_xor(b1,b,1);
  bit_and(c,a,b1);
  bit_xor(array[1],c,c1);
 // cout<<"array 1 is "<<array[1]<<endl;
  bit_xor(a1,a,1);
  bit_and(c,a1,b);
  bit_xor(array[2],c,c1);
 // cout<<"array 2 is "<<array[2]<<endl;
  bit_and(c,a1,b1);
  bit_xor(array[3],c,c1);
 //  cout<<"array 3 is "<<array[3]<<endl;
}
ZZ pick(int k)
{
  ZZ c;
  ZZ r;
  time_t t;
  sleep(k);
  t=time(NULL);
  srandom(t);
  r = random();
  
  if ((r%to_ZZ(2)) == 0)
    { 
      c= 0;
    }else {c=1;}
  cout<<"the c1 is "<<c<<endl;
  return c;
}
void ChoosePair(ZZ a,ZZ* A)
{
  time_t t;
  int r;
  ZZ a1,a2;
  //sleep(2);
  t=time(NULL);
  srandom(t*t);
  r = random();
  cout<<"the r is "<< r <<endl;
  if (a==1){
    if (r%2==0){
      cout << "the choosen pair is (0,1).."<< endl;
      a1 = 0;
      a2 = 1;
    }else{
      cout<< "the choosen pair is (1,0)\n  "<< endl;
      a1 = 1;
      a2 = 0;
    }
    
  }else{ 
    if (r%2 == 0){
      cout << "the choosen pair is (0,0).."<< endl;
      a1 = 0;
      a2 = 0;
    }else{
      cout<< "the choosen pair is (1,1)  "<< endl;
      a1 = 1;
      a2 = 1;
    }
    
  }
  A[0]=a1;
  A[1]=a2;
}
//ZZ RandomBits_ZZ(long l);


// the hash function
ZZ hash(ZZ a, ZZ b, ZZ c)
{
   ZZ k;
   b = b % to_ZZ(173); 
   k = a * b + c;
   //k = k + c;
   return k;
}

// generating function
/*ZZ generate(ZZ p)
{
        //p=23, g = 5
        int k,i,s;
        ZZ c,G,q;
        ZZ Zp[23];
	int done;
	G=2;
	for (i=0;i<p;i++) {
             Zp[i]=PowerMod(G,i,p);
             s = 0;
             for(k=0;k<i;k++) {
                 if (Zp[i]==Zp[k])  s = 1;
             }
             if (s==1) {
                if (i==(p-1)) break; 
		else{
   		    G++;
                    i=-1;
		 }
			      
             }
        }
        cout<<"the Zp set is "<<endl;
        for (i=0;i<p;i++) cout<<"Zp["<<i<<"]= "<< Zp[i]<<endl;
        return G;
} */                                                                                                                                                                 

//Yi's function part
ZZ_p RandomNumberP(int rt){
	time_t t;
	ZZ_p r;
	sleep(rt);
	t=time(NULL);
	srandom(t);
	r = random();
	return(r);
}

ZZ RandomNumber(int rt){
        time_t t;
        ZZ r;
        sleep(rt);
        t=time(NULL);
        srandom(t);
        r = random();
        return(r);
}

int convert(ZZ_p x){
	ZZ_p i;
	int j;
	for(i = 0, j = 0; i != x; i++, j++);
	return(j);
}	

ZZ_p convert_back(int x){
	ZZ_p j;
	int i;
	for(i = 0, j = 0; i < x; i++,j++);
	return(j);
}

ZZ convertp(ZZ_p x){
	ZZ j;
	ZZ_p i;
	for(i = 0, j = 0; i != x; i++,j++);
	return(j);
}

ZZ Produce(ZZ p, ZZ_p g, ZZ_p *Ciandgr, ZZ_p *Cir){
	int rnum = 1;
	ZZ r;
	
	Ciandgr[0] = RandomNumberP(rnum);
	cout<< "C1 is "<< Ciandgr[0] <<endl;
	
	Ciandgr[1] = RandomNumberP(rnum);
	cout<< "C2 is "<< Ciandgr[1] <<endl;
	
	Ciandgr[2] = RandomNumberP(rnum);
	cout<< "C3 is "<< Ciandgr[2] <<endl;

	r = RandomNumber(rnum);
	r = r % (p - 1);
        cout<< "r is "<< r <<endl;
	

	power(Ciandgr[3], g, r); // gr = g^r (e may be negative)
	cout<< "g^r is "<< Ciandgr[3] <<endl;

	power(Cir[0], Ciandgr[0], r); // C1r = C1^r (e may be negative)
        cout<< "C1^r is "<< Cir[0] <<endl;

	power(Cir[1], Ciandgr[1], r); // C2r = C2^r (e may be negative)
	cout<< "C2^r is "<< Cir[1] <<endl;

	power(Cir[2], Ciandgr[2], r); // C3r = C3^r (e may be negative)
	cout<< "C3^r is "<< Cir[2] <<endl;
	
	return(r);	

}	

void ComputePKi(ZZ_p PK0, ZZ r, ZZ_p *Cir, ZZ_p *PKir){
	 ZZ_p PK0r;
	 int i;
	 
	 power(PK0r, PK0, r); // PK0r = PK0^r (e may be negative)
         PKir[0] =  PK0r;
	 
	 cout<< "PK0 is "<< PK0 <<endl;
	 //cout<< "r is "<< r <<endl;
	 //cout<< "PK0^r is "<< PKir[0] <<endl;
	  
	 for(i = 1; i < 4; i++){
		div(PKir[i], Cir[i-1], PK0r); // PKi^r = Ci^r/Pk0^r
         }
	 
	 //cout<< "PK1^r is "<< PKir[1] <<endl;
	 //cout<< "PK2^r is "<< PKir[2] <<endl;
	 //cout<< "PK3^r is "<< PKir[3] <<endl;
}
	
ZZ changeMi(ZZ Pi,int index) { 
	ZZ Mi;
	Mi = Pi + to_ZZ(index + 1) * VALUE;
	return(Mi);
}

ZZ change_backMi(ZZ Mi,int index){
	ZZ Pi;
	Pi = Mi - to_ZZ(index + 1) * VALUE;
	return(Pi);
}



//end Yi's function part


main (int argc, char *argv[]){
  ZZ A[2];
  ZZ B[2];
  ZZ D[4];
  ZZ r,c,part1,part2,result,ResultFromB;
  ZZ a,a1,a2,c1;
  ZZ array[4];
  time_t t;
  
  int orig_sock,soket[2],len;
  struct sockaddr_in serv_adr;
  struct hostent *host;

 //Yi's variable
  int ciandgr[4], i, pk0,rnum;
  ZZ R, Rstring, temp, temp1, M[4], BiggerValue;
  ZZ Zp;
  Zp = P;
  ZZ_p::init(Zp);
  ZZ_p g, Ciandgr[4], Cir[3], PK0, PKir[4]; 
  ZZ pkir[4];
  g = G;
 //end Yi's variable 
  
  if (argc != 2) {
    fprintf(stderr,"usage: %s server\n", argv[0]);
    exit(1);
  }
  host = gethostbyname(argv[1]);
  if (host == (struct hostent *) NULL){
    perror ("gethostbyname");
    exit(2);
  }
  memset(&serv_adr, 0, sizeof(serv_adr));
  serv_adr.sin_family = AF_INET;
  memcpy(&serv_adr.sin_addr, host->h_addr, host->h_length);
  serv_adr.sin_port = htons(PORT);
  
  if ((orig_sock = socket(AF_INET, SOCK_STREAM, 0))<0){
    perror("generate error");
    exit(3);
  }

  if (connect(orig_sock,(struct sockaddr *)&serv_adr, sizeof(serv_adr))<0){
    perror("connect error");
    exit(4);
  }
  
  t=time(NULL);
  srandom(t);
  r = random();
  //cout<<"the r is "<< r <<endl;
  if ((r%2) == 0)
    { 
      a = 1;
    }else {a=0;}
  cout<<"your randon choosen value a is "<<a<<endl;
  ChoosePair(a,A);
  
  cout<<"a1 is "<<A[0]<<endl;
  
  cout<<"a2 is "<<A[1]<<endl;
  
  if (A[1]==1)
    buf[0]='1';
  else
    buf[0]='0';
  
  cout<<"sending to B is a2 "<<buf[0]<<endl;
  //cout<<"the buf is "<< buf<<endl;
  
 
  write (orig_sock,buf,sizeof(buf[0]));
  read(orig_sock,buf,sizeof(buf));
  B[0]=atoi(buf);
  cout<<"the B- b1 that we have is "<< B[0]<<endl;
  bit_xor(part2, A[0], B[0]); //produce the xor value of a1, b1
  
  c1 = pick(1);
  OT_input(A[0],B[0],c1,D);

//Yi's sender part
  cout<<"array 0 is "<<D[0]<<endl;
  cout<<"array 1 is "<<D[1]<<endl;
  cout<<"array 2 is "<<D[2]<<endl;
  cout<<"array 3 is "<<D[3]<<endl;


  R = Produce(Zp, g, Ciandgr, Cir);

  ciandgr[3] = convert(Ciandgr[3]);
  ciandgr[2] = convert(Ciandgr[2]);
  ciandgr[1] = convert(Ciandgr[1]);
  ciandgr[0] = convert(Ciandgr[0]);
						
  //cout<<"C1 is "<<ciandgr[0]<<endl;
  //cout<<"C2 is "<<ciandgr[1]<<endl;
  //cout<<"C3 is "<<ciandgr[2]<<endl;
  //cout<<"g^r is "<<ciandgr[3]<<endl;
  //cout<<"R is "<< R <<endl; 

  for(i = 0; i < 4; i++){
  	   mybuf[i] = ciandgr[i]; 
  } 

  printf("the buf I sent is: %d %d %d %d\n", mybuf[0], mybuf[1], mybuf[2], mybuf[3]);
  
  write (orig_sock, mybuf, 4 * sizeof(mybuf[0])); //send C1, C2, C3 and g^r
  
  read(orig_sock,mybuf,sizeof(mybuf[0]));  //recieve Pk0
  
  pk0 = mybuf[0];
  
  PK0 = convert_back(pk0);
  
  ComputePKi(PK0, R, Cir, PKir);

  rnum = 3;
  Rstring = RandomNumber(rnum);
  cout<<"The R string is "<< Rstring <<endl;
  
  for(i = 0; i < 4; i++){
            cout<< "PK"<<i<<"^r is "<< PKir[i] <<endl;
	    pkir[i] = convertp(PKir[i]);  
	    //cout<<"PK"<<i<<"^r converted to ZZ is "<< pkir[i] <<endl;
	    
	    M[i] = changeMi(D[i], i);
	    cout<<"M" << i << " is "<< M[i] <<endl;
	  
	    temp1 = hash(pkir[i], Rstring, to_ZZ(i)),
	    cout<<"Hash" << i <<" is "<<temp1 <<endl; 
	    
	    bit_xor(temp, temp1, M[i]);
	    mybuf[i] = to_int(temp);
	    
  }
  mybuf[i] = to_int(Rstring);
  write (orig_sock, mybuf, 5 * sizeof(mybuf[0])); //send Hash1, Hash2, Hash3 and R

   printf("the buf I sent is: %d %d %d %d %d\n", mybuf[0], mybuf[1], mybuf[2], mybuf[3], mybuf[4]);

   part1 = c1;
   bit_xor(result, part1, part2);
   cout<<"The result on side A is: "<< result <<endl;
   
   read(orig_sock,mybuf, sizeof(mybuf[0]));
   ResultFromB = to_ZZ(mybuf[0]);
   cout<<"Partial result recieved from B is "<< ResultFromB <<endl;
    
   mybuf[0] = to_int(result);
   write(orig_sock,mybuf, sizeof(mybuf[0]));
   cout<<"Sending my final partial result to B" <<endl;
   
   bit_xor(BiggerValue, ResultFromB, result);
   cout<<"Therefore the bigger value of our numbers is "<<BiggerValue<<endl;
						
   
//end Yi's sender part

  close(orig_sock);
  exit(0);
}



