text: #include <emp-tool/emp-tool.h>
  #include "emp-tool/utils/hash.h"
  #include "emp-sh2pc/emp-sh2pc.h"
  
  #include <openssl/hmac.h>
  #include <openssl/evp.h>
  #include <stdio.h>
  #include <ctype.h>
  
  // #include "hmac.h"
  #include "sha-256.h"
  
  using namespace emp;
  using namespace std; 
  
  enum {
    SN_LENGTH = 12, CID_LENGTH = 4, DATA_LENGTH = 16, // data = sn + cid
    KEY_LENGTH = 32, RANDOM_LENGTH = 96, RPRIME_LENGTH = 32, TOKEN_LENGTH = 1
  };
  // static int SN_LENGTH = 12; 
  // static int CID_LENGTH = 4;
  // static int DATA_LENGTH = SN_LENGTH + CID_LENGTH;
  // static int KEY_LENGTH = 32; 
  // static int RANDOM_LENGTH = 96; 
  // static int RPRIME_LENGTH = 32;
  // static int TOKEN_LENGTH = 1;
  
  
  /* * * * * * * * * * * * 
   *  D E B U G G I N G  *
   * * * * * * * * * * * */
  void printInteger(Integer intToPrint, int bitSize) {
    for (int i = bitSize -1; i >= 0; i--) {
      cout << intToPrint[i].reveal();
    }
    return;
  }
  
  void printIntegerArray(Integer* intToPrint, int arraySize, int bitSize) {
    for(int i = 0; i < arraySize; i++) {
      printInteger(intToPrint[i], bitSize);
      cout << ", ";
    }
    cout << endl;
    return;
  }
  
  void printarray(char* array, int ARRAY_LENGTH) {
      for (int i = 0; i <ARRAY_LENGTH; i ++) {
        for (int j = 0; j < 8; j++) {
          printf("%d", !!((array[i] << j) & 0x80));
        }
        printf(", ");
      }
    cout << endl;
  }
  
  static int ALL = 0;
  static int Msg_Block = 1;
  static int Msg_Block_Index = 2;
  static int Msg_Intermediate_Hash = 4;
  
  void printContext(EMP_SHA256_CONTEXT *context, int flag, string debugMsg) {
    cout << debugMsg << endl;
    if (flag == ALL || flag == Msg_Intermediate_Hash) {
      cout << "Interemdiate Hash " << endl;
      printIntegerArray(context->Intermediate_Hash, INTERMEDIATE_HASH_LEN, 32);
    }
    if (flag == ALL) {
      cout << "Length high " << endl;
      printInteger(context->Length_High, LENGTH_BITS);
      cout << endl;
    }
    if (flag == ALL) {
      cout << "Length low " << endl;
      printInteger(context->Length_Low, LENGTH_BITS);
      cout << endl;
    }  
    if (flag == ALL || flag == Msg_Block_Index) { 
      cout << "Message block index " << endl;
      printInteger(context->Message_Block_Index, MESSAGE_BLOCK_INDEX_BITS);
      cout << endl;
    }
    if (flag == ALL || flag == Msg_Block) {
      cout << "Message block contents " << endl;
      printIntegerArray(context->Message_Block, SHA256_Message_Block_Size, MESSAGE_BLOCK_BITS);
    }
  }
  
  /* * * * * * * * * *
   *  T E S T I N G  *
   * * * * * * * * * */
  void printHash(Integer* Message_Digest) {
    cout << "Printing output hash: " << endl;
    for (int i =0; i < SHA256HashSize; i++) {
      for (int j =7; j >= 0; j--) {
        cout << Message_Digest[i][j].reveal();
      }
    }
    cout << endl;
  }
  
  void print_uint8_t(uint8_t n) {
    bitset<8> x(n);
    cout << x;
  }
  
  void printSSLHash(uint8_t* sslHash, int arraySize) {
    for(int i = 0; i < arraySize; i++) {
      print_uint8_t(sslHash[i]);
      cout << ", ";
    }
    cout << endl;
    return;
  }
  void xor_reconstruct(char* int1, char* int2, int output_length, Integer* output) {
    Integer intMsg1[output_length];
    for (int i = 0; i < output_length; i++) {
      intMsg1[i] = Integer(8, int1[i], ALICE);
    }
    Integer intMsg2[output_length];
    for (int i = 0; i < output_length; i++) {
      intMsg2[i] = Integer(8, int2[i], BOB);
    }
  
    for (int i = 0; i < output_length; i++) {
      output[i] = intMsg1[i] ^ intMsg2[i]; 
    }
  
    return;
  
  }
  
  Integer* convertStringtoIntegerArray(char* s, int S_LENGTH) {
    Integer output[S_LENGTH];
    for (int i =0; i < S_LENGTH; i++) {
      output[i] = Integer(8,s[i],PUBLIC);
    }
    //static Integer temp = output;
    static Integer* output_ptr = output; 
    return output_ptr;
  }
  
  bool compareUtk(char* expected, Integer* actual) {
    for (int i = 0; i < 96; i++) {
      for (int j = 0; j < 8; j++) {
        if ((int)!!((expected[i] << (7-j)) & 0x80) != (int)actual[i][j].reveal(PUBLIC)) {
          return false;
        }
      }
    }
    return true; 
  }
  
  void convertHexToChar(char* hex, char* output, int output_length) {
    for (int i = 0; i < output_length; i++) {
      char c[2]; 
      c[0] = hex[2*i];
      c[1] = hex[2*i+1];
      int number = (int) strtol(c,NULL,16);
      output[i] = (char)number;
    }
  }
  
  bool compareHash(uint8_t* sslHash, Integer* empHash) {
    for (int i =0; i < 32; i++) {
      bitset<8> sslBitset(sslHash[i]);
      for (int j = 7; j >= 0; j--) {
        if(empHash[i][j].reveal() != sslBitset[j]) {
          return false;
        }
      }
      cout <<  sslBitset << ", ";
    }
    cout << endl << "TRUE?" << endl;
    return true;
  }
  
  void runSHA(char* str, int length) {
       /* 2 PC */
    EMP_SHA256_CONTEXT sha;
    Integer err = SHA256_Reset(&sha);
  
    Integer input[length];
    for (int i = 0; i < length; i++) {
      input[i] = Integer(8, str[i], ALICE);
    }
    err = SHA256_Input(&sha, input, length);
    Integer Message_Digest_Buf[SHA256HashSize];
    Integer *Message_Digest = Message_Digest_Buf;
  
    err = SHA256_Result(&sha, Message_Digest);
  
    /* OpenSSL */
    uint8_t digest[32];
    Hash::hash_once(&digest, str, length);
    bool success = compareHash(digest, Message_Digest);
    if(!success) {
      cout << "Failed test with str: " << str << endl;
    }
  }
  
  int main(int argc, char** argv) {
  
    int port, party;
    parse_party_and_port(argv, &party, &port);
  
    NetIO * io = new NetIO(party==ALICE ? nullptr : "127.0.0.1", port);
  
    setup_semi_honest(io, party);
  
    runSHA((char*) "abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh", 144);
   
  
    delete io;
    return 0;
  }
  