
#include "Math/gf2n.h"
#include "Math/gfp.h"
#include "Protocols/Share.h"
#include "Math/Setup.h"
#include "Protocols/Spdz2kShare.h"
#include "Protocols/BrainShare.h"
#include "Protocols/MaliciousRep3Share.h"
#include "Protocols/PostSacriRepRingShare.h"
#include "Protocols/PostSacriRepFieldShare.h"
#include "Protocols/SemiShare.h"
#include "Protocols/MaliciousShamirShare.h"
#include "Protocols/SpdzWiseRingShare.h"
#include "Protocols/SpdzWiseShare.h"
#include "Protocols/Rep4Share2k.h"
#include "Protocols/fake-stuff.h"
#include "Tools/Exceptions.h"
#include "GC/MaliciousRepSecret.h"
#include "GC/SemiSecret.h"
#include "GC/TinySecret.h"
#include "GC/TinierSecret.h"
#include "GC/MaliciousCcdSecret.h"
#include "GC/Rep4Secret.h"

#include "Math/Setup.h"
#include "Processor/Data_Files.h"
#include "Tools/mkpath.h"
#include "Tools/ezOptionParser.h"
#include "Tools/benchmarking.h"

#include "Protocols/fake-stuff.hpp"
#include "Protocols/Shamir.hpp"
#include "Processor/Data_Files.hpp"
#include "Math/Z2k.hpp"
#include "Math/gfp.hpp"
#include "GC/Secret.hpp"
#include "Machines/ShamirMachine.hpp"

#include <sstream>
#include <fstream>
using namespace std;


string prep_data_prefix;

class FakeParams
{
  int nplayers, default_num;
  bool zero;

public:
  ez::ezOptionParser opt;

  template<class T>
  int generate();

  template<class T>
  void generate_field(true_type);
  template<class T>
  void generate_field(false_type)
  {
  }

  template<class T>
  void make_with_mac_key(int nplayers, int default_num, bool zero);
  template<class T>
  void make_basic(const typename T::mac_type& key, int nplayers, int nitems, bool zero);

  template<class T>
  void make_edabits(const typename T::mac_type& key, int N, int ntrip, bool zero, false_type,
      const typename T::bit_type::mac_type& bit_key = {});
  template<class T>
  void make_edabits(const typename T::mac_type&, int, int, bool, true_type)
  {
  }
};

void make_bit_triples(const gf2n& key,int N,int ntrip,Dtype dtype,bool zero)
{
  PRNG G;
  G.ReSeed();

  ofstream* outf=new ofstream[N];
  gf2n a,b,c, one;
  one.assign_one();
  vector<Share<gf2n> > Sa(N),Sb(N),Sc(N);
  /* Generate Triples */
  for (int i=0; i<N; i++)
    { stringstream filename;
      filename << get_prep_sub_dir<Share<gf2n>>(prep_data_prefix, N)
          << DataPositions::dtype_names[dtype] << "-2-P" << i;
      cout << "Opening " << filename.str() << endl;
      outf[i].open(filename.str().c_str(),ios::out | ios::binary);
      if (outf[i].fail()) { throw file_error(filename.str().c_str()); }
    }
  for (int i=0; i<ntrip; i++)
    {
      if (!zero)
        a.randomize(G);
      a &= one;
      make_share(Sa,a,N,key,G);
      if (!zero)
        b.randomize(G);
      if (dtype == DATA_BITTRIPLE)
        b &= (one);
      make_share(Sb,b,N,key,G);
      c.mul(a,b);
      make_share(Sc,c,N,key,G);
      for (int j=0; j<N; j++)
        { Sa[j].output(outf[j],false);
          Sb[j].output(outf[j],false);
          Sc[j].output(outf[j],false);
        }
    }
  check_files(outf, N);
  for (int i=0; i<N; i++)
    { outf[i].close(); }
  delete[] outf;
}


/* N      = Number players
 * ntrip  = Number tuples needed
 * str    = "2" or "p"
 */
template<class T>
void make_square_tuples(const typename T::mac_type& key,int N,int ntrip,const string& str,bool zero)
{
  (void) str;

  PRNG G;
  G.ReSeed();

  ofstream* outf=new ofstream[N];
  typename T::clear a,c;
  vector<T> Sa(N),Sc(N);
  /* Generate Squares */
  for (int i=0; i<N; i++)
    { stringstream filename;
      filename << get_prep_sub_dir<T>(prep_data_prefix, N) << "Squares-"
          << T::type_short() << "-P" << i;
      cout << "Opening " << filename.str() << endl;
      outf[i].open(filename.str().c_str(),ios::out | ios::binary);
      if (outf[i].fail()) { throw file_error(filename.str().c_str()); }
    }
  for (int i=0; i<ntrip; i++)
    {
      if (!zero)
        a.randomize(G);
      make_share(Sa,a,N,key,G);
      c = a * a;
      make_share(Sc,c,N,key,G);
      for (int j=0; j<N; j++)
        { Sa[j].output(outf[j],false);
          Sc[j].output(outf[j],false);
        }
    }
  check_files(outf, N);
  for (int i=0; i<N; i++)
    { outf[i].close(); }
  delete[] outf;
}

/* N      = Number players
 * ntrip  = Number bits needed
 */
template<class T>
void make_bits(const typename T::mac_type& key, int N, int ntrip, bool zero,
    int thread_num = -1)
{
  PRNG G;
  G.ReSeed();

  ofstream* outf=new ofstream[N];
  typename T::clear a;
  vector<T> Sa(N);
  /* Generate Bits */
  for (int i=0; i<N; i++)
    { stringstream filename;
      filename << get_prep_sub_dir<T>(prep_data_prefix, N) << "Bits-"
          << T::type_short() << "-P" << i
          << Sub_Data_Files<T>::get_suffix(thread_num);
      cout << "Opening " << filename.str() << endl;
      outf[i].open(filename.str().c_str(),ios::out | ios::binary);
      if (outf[i].fail()) { throw file_error(filename.str().c_str()); }
    }
  for (int i=0; i<ntrip; i++)
    { if ((G.get_uchar()&1)==0 || zero) { a.assign_zero(); }
      else                       { a.assign_one();  }
      make_share(Sa,a,N,key,G);
      for (int j=0; j<N; j++)
        { Sa[j].output(outf[j],false); }
    }
  check_files(outf, N);
  for (int i=0; i<N; i++)
    { outf[i].close(); }
  delete[] outf;
}

template<class T>
void make_dabits(const typename T::mac_type& key, int N, int ntrip, bool zero,
    const typename T::bit_type::mac_type& bit_key = { })
{
  Files<T> files(N, key,
      get_prep_sub_dir<T>(prep_data_prefix, N)
          + DataPositions::dtype_names[DATA_DABIT] + "-" + T::type_short());
  SeededPRNG G;
  for (int i = 0; i < ntrip; i++)
    {
      bool bit = not zero && G.get_bit();
      files.template output_shares<T>(bit);
      files.template output_shares<typename dabit<T>::bit_type>(bit, bit_key);
    }
}

template<class T>
void FakeParams::make_edabits(const typename T::mac_type& key, int N, int ntrip, bool zero, false_type,
    const typename T::bit_type::mac_type& bit_key)
{
  vector<int> lengths;
  opt.get("-e")->getInts(lengths);
  for (auto length : lengths)
    {
      Files<T> files(N, key,
          get_prep_sub_dir<T>(prep_data_prefix, N)
          + "edaBits-" + to_string(length));
      SeededPRNG G;
      bigint value;
      int max_size = edabitvec<T>::MAX_SIZE;
      for (int i = 0; i < ntrip / max_size; i++)
        {
          vector<typename T::clear> as(max_size);
          vector<typename T::bit_type::part_type::clear> bs(length);
          for (int j = 0; j < max_size; j++)
            {
              if (not zero)
                G.get_bigint(value, length, true);
              as[j] = value;
              for (int k = 0; k < length; k++)
                bs[k] ^= BitVec(bigint((value >> k) & 1).get_si()) << j;
            }
          for (auto& a : as)
            files.template output_shares<T>(a);
          for (auto& b : bs)
            files.template output_shares<typename T::bit_type::part_type>(b, bit_key);
        }
    }
}

/* N      = Number players
 * ntrip  = Number inputs needed
 * str    = "2" or "p"
 *
 */
template<class T>
void make_inputs(const typename T::mac_type& key,int N,int ntrip,const string& str,bool zero)
{
  (void) str;

  PRNG G;
  G.ReSeed();

  ofstream* outf=new ofstream[N];
  typename T::open_type a;
  vector<T> Sa(N);
  /* Generate Inputs */
  for (int player=0; player<N; player++)
    { for (int i=0; i<N; i++)
        { stringstream filename;
          filename << get_prep_sub_dir<T>(prep_data_prefix, N) << "Inputs-"
              << T::type_short() << "-P" << i << "-" << player;
          cout << "Opening " << filename.str() << endl;
          outf[i].open(filename.str().c_str(),ios::out | ios::binary);
          if (outf[i].fail()) { throw file_error(filename.str().c_str()); }
        }
      for (int i=0; i<ntrip; i++)
        {
          if (!zero)
            a.randomize(G);
          make_share(Sa,a,N,key,G);
          for (int j=0; j<N; j++)
            { Sa[j].output(outf[j],false); 
              if (j==player)
	        { a.output(outf[j],false);  }
            }
        }
      for (int i=0; i<N; i++)
        { outf[i].close(); }
    }
  check_files(outf, N);
  delete[] outf;
}


template<class T>
void make_PreMulC(const typename T::mac_type& key, int N, int ntrip, bool zero)
{
  stringstream ss;
  ss << get_prep_sub_dir<T>(prep_data_prefix, N) << "PreMulC-" << T::type_short();
  Files<T> files(N, key, ss.str());
  PRNG G;
  G.ReSeed();
  typename T::clear a, b, c;
  c = 1;
  for (int i=0; i<ntrip; i++)
    {
      // close the circle
      if (i == ntrip - 1 || zero)
        a.assign_one();
      else
        do
          a.randomize(G);
        while (a.is_zero());
      files.output_shares(a);
      b = a.invert();
      files.output_shares(b);
      files.output_shares(a * c);
      c = b;
    }
}
// Code for TTP AES
unsigned char sbox[256] =
{
   0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
   0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
   0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
   0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
   0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
   0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
   0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
   0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
   0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
   0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
   0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
   0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
   0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
   0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
   0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
   0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
};

template<class T>
void make_AES(const typename T::mac_type& key, int N, int ntrip, bool zero) {
  stringstream ss;
  ss << get_prep_sub_dir<T>(prep_data_prefix, N) << "Sbox-" << T::type_short();
  Files<T> files(N, key, ss.str());
  PRNG G;
  G.ReSeed();
  gf2n_short x;

  for (int i = 0; i < ntrip; i++)
    {
      int mask = 0;
      if (!zero)
        mask = G.get_uchar();
      expand_byte(x, mask);
      files.output_shares(x);

      for (int j = 0; j < 256; j++)
        {
          expand_byte(x, sbox[mask ^ j]);
          files.output_shares(x);
        }
    }
}

// Code for TTP DES
vector<vector<unsigned char>> des_sbox = {
    {14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0, 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13},
    {15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9},
    {10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12},
    {7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4, 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14},
    {2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14, 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3},
    {12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13},
    {4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12},
    {13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11}
};


template<class T>
void make_DES(const typename T::mac_type& key, int N, int ntrip, bool zero)
{
  stringstream ss;
  ss << get_prep_sub_dir<T>(prep_data_prefix, N) << "SboxDes-" << T::type_short();
  Files<T> files(N, key, ss.str());
  PRNG G;
  G.ReSeed();
  gf2n_short x;

  for (int i = 0; i < ntrip; i++)
    {
      for (int r = 0; r < 8; ++r) {
        int mask = 0;
        if (!zero)
          mask = G.get_uchar();
        mask &= 63; //take only first 6 bits
        expand_byte(x, mask);
        files.output_shares(x);
        for (int j = 0; j < 64; j++)
          {
            files.output_shares(des_sbox[r][mask ^ j]);
          }
      }
    }
}

template<class T>
void make_Sbox(const typename T::mac_type& key, int N, int ntrip, bool zero, T, gf2n_short)
{
  make_AES<T>(key, N, ntrip, zero);
  make_DES<T>(key, N, ntrip, zero);
}


template<class T, class U>
void make_Sbox(const typename T::mac_type& key, int N, int ntrip, bool zero, T, U)
{
  (void)key, (void)N, (void)ntrip, (void)zero;
}

template<class T>
void make_Sbox(const typename T::mac_type& key, int N, int ntrip, bool zero)
{
  make_Sbox(key, N, ntrip, zero, T(), typename T::clear());
}

template<class T>
void make_minimal(const typename T::mac_type& key, int nplayers, int nitems, bool zero)
{
    make_mult_triples<T>(key, nplayers, nitems, zero, prep_data_prefix);
    make_bits<T>(key, nplayers, nitems, zero);
    make_inputs<T>(key, nplayers, nitems, T::type_short(), zero);
}

template<class T>
void FakeParams::make_basic(const typename T::mac_type& key, int nplayers, int nitems, bool zero)
{
    make_minimal<T>(key, nplayers, nitems, zero);
    make_square_tuples<T>(key, nplayers, nitems, T::type_short(), zero);
    make_dabits<T>(key, nplayers, nitems, zero);
    make_edabits<T>(key, nplayers, nitems, zero, T::clear::characteristic_two);
    if (T::clear::invertible)
    {
        make_inverse<T>(key, nplayers, nitems, zero, prep_data_prefix);
        if (opt.isSet("-s"))
        {
            make_PreMulC<T>(key, nplayers, nitems, zero);
            make_Sbox<T>(key, nplayers, nitems, zero);
        }
    }
}

template<class T>
void FakeParams::make_with_mac_key(int nplayers, int default_num, bool zero)
{
    typename T::mac_share_type::open_type key;
    generate_mac_keys<T>(key, nplayers, prep_data_prefix);
    make_basic<T>(key, nplayers, default_num, zero);
}

template<class T>
int generate(ez::ezOptionParser& opt);

int main(int argc, const char** argv)
{
  insecure("preprocessing");
  bigint::init_thread();

  FakeParams params;
  auto& opt = params.opt;

  opt.syntax = "./Fake-Offline.x <nplayers> [OPTIONS]\n\nOptions with 2 arguments take the form '-X <#gf2n tuples>,<#modp tuples>'";
  opt.example = "./Fake-Offline.x 2 -lgp 128 -lg2 128 --default 10000\n./Fake-Offline.x 3 -trip 50000,10000 -btrip 100000\n";

  opt.add(
        "128", // Default.
        0, // Required?
        1, // Number of args expected.
        0, // Delimiter if expecting multiple args.
        "Bit length of GF(p) field (default: 128)", // Help description.
        "-lgp", // Flag token.
        "--lgp" // Flag token.
  );
  opt.add(
          "", // Default.
          0, // Required?
          1, // Number of args expected.
          0, // Delimiter if expecting multiple args.
          "Prime for GF(p) field (default: generated from -lgp argument)", // Help description.
          "-P", // Flag token.
          "--prime" // Flag token.
  );
  opt.add(
        to_string(gf2n::default_degree()).c_str(), // Default.
        0, // Required?
        1, // Number of args expected.
        0, // Delimiter if expecting multiple args.
        ("Bit length of GF(2^n) field (default: " + to_string(gf2n::default_degree()) + ")").c_str(), // Help description.
        "-lg2", // Flag token.
        "--lg2" // Flag token.
  );
  opt.add(
        "1000", // Default.
        0, // Required?
        1, // Number of args expected.
        0, // Delimiter if expecting multiple args.
        "Default number of tuples to generate for ALL data types (default: 1000)", // Help description.
        "-d", // Flag token.
        "--default" // Flag token.
  );
  opt.add(
        "", // Default.
        0, // Required?
        2, // Number of args expected.
        ',', // Delimiter if expecting multiple args.
        "Number of triples, for gf2n / modp types", // Help description.
        "-trip", // Flag token.
        "--ntriples" // Flag token.
  );
  opt.add(
        "", // Default.
        0, // Required?
        2, // Number of args expected.
        ',', // Delimiter if expecting multiple args.
        "Number of random bits, for gf2n / modp types", // Help description.
        "-bit", // Flag token.
        "--nbits" // Flag token.
  );
  opt.add(
        "", // Default.
        0, // Required?
        2, // Number of args expected.
        ',', // Delimiter if expecting multiple args.
        "Number of input tuples, for gf2n / modp types", // Help description.
        "-inp", // Flag token.
        "--ninputs" // Flag token.
  );
  opt.add(
        "", // Default.
        0, // Required?
        2, // Number of args expected.
        ',', // Delimiter if expecting multiple args.
        "Number of square tuples, for gf2n / modp types", // Help description.
        "-sq", // Flag token.
        "--nsquares" // Flag token.
  );
  opt.add(
        "", // Default.
        0, // Required?
        1, // Number of args expected.
        0, // Delimiter if expecting multiple args.
        "Number of inverse tuples (modp only)", // Help description.
        "-inv", // Flag token.
        "--ninverses" // Flag token.
  );
  opt.add(
        "", // Default.
        0, // Required?
        1, // Number of args expected.
        0, // Delimiter if expecting multiple args.
        "Number of GF(2) triples", // Help description.
        "-btrip", // Flag token.
        "--nbittriples" // Flag token.
  );
  opt.add(
        "", // Default.
        0, // Required?
        1, // Number of args expected.
        0, // Delimiter if expecting multiple args.
        "Number of GF(2) x GF(2^n) triples", // Help description.
        "-mixed", // Flag token.
        "--nbitgf2ntriples" // Flag token.
  );
  opt.add(
        "", // Default.
        0, // Required?
        0, // Number of args expected.
        0, // Delimiter if expecting multiple args.
        "Set all values to zero, but not the shares", // Help description.
        "-z", // Flag token.
        "--zero" // Flag token.
  );
  opt.add(
        "", // Default.
        0, // Required?
        1, // Number of args expected.
        0, // Delimiter if expecting multiple args.
        "Generate for SPDZ2k with parameter k (bit length)", // Help description.
        "-Z", // Flag token.
        "--spdz2k" // Flag token.
  );
  opt.add(
        "", // Default.
        0, // Required?
        1, // Number of args expected.
        0, // Delimiter if expecting multiple args.
        "SPDZ2k security parameter (default: k)", // Help description.
        "-S", // Flag token.
        "--security" // Flag token.
  );
  opt.add(
        "", // Default.
        0, // Required?
        -1, // Number of args expected.
        ',', // Delimiter if expecting multiple args.
        "edaBit lengths (separate by comma)", // Help description.
        "-e", // Flag token.
        "--edabits" // Flag token.
  );
  opt.add(
        "", // Default.
        0, // Required?
        0, // Number of args expected.
        ',', // Delimiter if expecting multiple args.
        "Special preprocessing", // Help description.
        "-s", // Flag token.
        "--special" // Flag token.
  );
  opt.add(
          "", // Default.
          0, // Required?
          1, // Number of args expected.
          0, // Delimiter if expecting multiple args.
          "Number of corrupted parties for Shamir secret sharing "
          "(default: just below half)", // Help description.
          "-T", // Flag token.
          "--threshold" // Flag token.
  );
  opt.parse(argc, argv);

  int lgp;
  opt.get("--lgp")->getInt(lgp);

  if (opt.isSet("-Z"))
    {
      int k, s;
      opt.get("-Z")->getInt(k);
      s = k;
      if (opt.isSet("-S"))
        opt.get("-S")->getInt(s);
      if (k == 32 and s == 32)
        return params.generate<Spdz2kShare<32, 32>>();
      else if (k == 64 and s == 64)
        return params.generate<Spdz2kShare<64, 64>>();
      else if (k == 64 and s == 48)
        return params.generate<Spdz2kShare<64, 48>>();
      else
        throw runtime_error("not compiled for k=" + to_string(k) + " and s=" + to_string(s));
    }
  else
      params.generate<Share<gfpvar>>();
}

template<class T>
int FakeParams::generate()
{
  vector<string> badOptions;
  string usage;
  unsigned int i;
  if(!opt.gotRequired(badOptions))
  {
    for (i=0; i < badOptions.size(); ++i)
      cerr << "ERROR: Missing required option " << badOptions[i] << ".";
    opt.getUsage(usage);
    cout << usage;
    return 1;
  }

  if(!opt.gotExpected(badOptions))
  {
    for(i=0; i < badOptions.size(); ++i)
      cerr << "ERROR: Got unexpected number of arguments for option " << badOptions[i] << ".";
    opt.getUsage(usage);
    cout << usage;
    return 1;
  }

  if (opt.firstArgs.size() == 2)
  {
    nplayers = atoi(opt.firstArgs[1]->c_str());
  }
  else if (opt.lastArgs.size() == 1)
  {
    nplayers = atoi(opt.lastArgs[0]->c_str());
  }
  else
  {
    cerr << "ERROR: invalid number of arguments\n";
    opt.getUsage(usage);
    cout << usage;
    return 1;
  }

  if (nplayers > 2)
  {
    ShamirOptions::singleton.nparties = nplayers;
    ShamirOptions::singleton.set_threshold(opt);
  }

  int ntrip2=0, ntripp=0, nbits2=0,nbitsp=0,nsqr2=0,nsqrp=0,ninp2=0,ninpp=0,ninv=0, nbittrip=0, nbitgf2ntrip=0;
  vector<int> list_options;
  int lg2, lgp;

  opt.get("--lgp")->getInt(lgp);
  opt.get("--lg2")->getInt(lg2);

  opt.get("--default")->getInt(default_num);
  ntrip2 = ntripp = nbits2 = nbitsp = nsqr2 = nsqrp = ninp2 = ninpp = ninv =
  nbittrip = nbitgf2ntrip = default_num;
  
  if (opt.isSet("--ntriples"))
  {
    opt.get("--ntriples")->getInts(list_options);
    ntrip2 = list_options[0];
    ntripp = list_options[1];
  }
  if (opt.isSet("--nbits"))
  {
    opt.get("--nbits")->getInts(list_options);
    nbits2 = list_options[0];
    nbitsp = list_options[1];
  }
  if (opt.isSet("--ninputs"))
  {
    opt.get("--ninputs")->getInts(list_options);
    ninp2 = list_options[0];
    ninpp = list_options[1];
  }
  if (opt.isSet("--nsquares"))
  {
    opt.get("--nsquares")->getInts(list_options);
    nsqr2 = list_options[0];
    nsqrp = list_options[1];
  }
  if (opt.isSet("--ninverses"))
    opt.get("--ninverses")->getInt(ninv);
  if (opt.isSet("--nbittriples"))
    opt.get("--nbittriples")->getInt(nbittrip);
  if (opt.isSet("--nbitgf2ntriples"))
    opt.get("--nbitgf2ntriples")->getInt(nbitgf2ntrip);

  zero = opt.isSet("--zero");
  if (zero)
      cout << "Set all values to zero" << endl;

  // check compatibility
  gf2n::init_field(lg2);

  PRNG G;
  G.ReSeed();
  prep_data_prefix = PREP_DIR;
  // Set up the fields
  if (opt.isSet("--prime"))
  {
    string p;
    opt.get("--prime")->getString(p);
    T::clear::init_field(p);
    T::clear::template write_setup<T>(nplayers);
  }
  else
  {
    T::clear::template generate_setup<T>(prep_data_prefix, nplayers, lgp);
    T::clear::init_default(lgp);
  }

  /* Find number players and MAC keys etc*/
  typename T::mac_type::Scalar keyp;
  gf2n key2;

  // create PREP_DIR if not there
  if (mkdir_p(PREP_DIR) == -1)
  {
    cerr << "mkdir_p(" PREP_DIR ") failed\n";
    throw file_error(PREP_DIR);
  }

  typedef Share<gf2n> sgf2n;

  generate_mac_keys<T>(keyp, nplayers, prep_data_prefix);
  generate_mac_keys<sgf2n>(key2, nplayers, prep_data_prefix);

  make_mult_triples<sgf2n>(key2,nplayers,ntrip2,zero,prep_data_prefix);
  make_mult_triples<T>(keyp,nplayers,ntripp,zero,prep_data_prefix);
  make_bits<Share<gf2n>>(key2,nplayers,nbits2,zero);
  make_bits<T>(keyp,nplayers,nbitsp,zero);
  make_square_tuples<sgf2n>(key2,nplayers,nsqr2,"2",zero);
  make_square_tuples<T>(keyp,nplayers,nsqrp,"p",zero);
  make_inputs<sgf2n>(key2,nplayers,ninp2,"2",zero);
  make_inputs<T>(keyp,nplayers,ninpp,"p",zero);
  make_inverse<sgf2n>(key2,nplayers,ninv,zero,prep_data_prefix);
  if (T::clear::invertible)
    make_inverse<T>(keyp,nplayers,ninv,zero,prep_data_prefix);
  make_bit_triples(key2,nplayers,nbittrip,DATA_BITTRIPLE,zero);
  make_bit_triples(key2,nplayers,nbitgf2ntrip,DATA_BITGF2NTRIPLE,zero);

  if (opt.isSet("-s"))
  {
    make_PreMulC<sgf2n>(key2,nplayers,ninv,zero);
    if (T::clear::invertible)
      make_PreMulC<T>(keyp,nplayers,ninv,zero);
    make_Sbox<sgf2n>(key2,nplayers,ninv,zero);
  }

  // replicated secret sharing only for three parties
  if (nplayers == 3)
  {
    make_bits<Rep3Share<Integer>>({}, nplayers, nbitsp, zero);
    make_basic<BrainShare<64, 40>>({}, nplayers, default_num, zero);
    make_basic<PostSacriRepRingShare<64, 40>>({}, nplayers, default_num, zero);
    make_with_mac_key<SpdzWiseRingShare<64, 40>>(nplayers, default_num, zero);

    make_mult_triples<GC::MaliciousRepSecret>({}, nplayers, ntrip2, zero, prep_data_prefix);
    make_bits<GC::MaliciousRepSecret>({}, nplayers, nbits2, zero);
  }
  else if (nplayers == 4)
    make_basic<Rep4Share2<64>>({}, nplayers, default_num, zero);

  make_basic<SemiShare<Z2<64>>>({}, nplayers, default_num, zero);

  make_mult_triples<GC::SemiSecret>({}, nplayers, default_num, zero, prep_data_prefix);
  make_bits<GC::SemiSecret>({}, nplayers, default_num, zero);

  gf2n_short::reset();
  gf2n_short::init_field(40);

  Z2<41> keyt;
  generate_mac_keys<GC::TinySecret<40>>(keyt, nplayers, prep_data_prefix);

  make_minimal<GC::TinySecret<40>>(keyt, nplayers, default_num / 64, zero);

  gf2n_short keytt;
  generate_mac_keys<GC::TinierSecret<gf2n_short>>(keytt, nplayers, prep_data_prefix);
  make_minimal<GC::TinierSecret<gf2n_short>>(keytt, nplayers, default_num / 64, zero);

  make_dabits<T>(keyp, nplayers, default_num, zero, keytt);
  make_edabits<T>(keyp, nplayers, default_num, zero, false_type(), keytt);

  if (nplayers > 2)
    {
      make_mult_triples<GC::MaliciousCcdShare<gf2n_short>>({}, nplayers,
          default_num, zero, prep_data_prefix);
    }

  generate_field<typename T::clear>(T::clear::prime_field);
  generate_field<gf2n>(true_type());

  return 0;
}

template<class U>
void FakeParams::generate_field(true_type)
{
  if (nplayers == 3)
    {
      make_basic<Rep3Share<U>>({}, nplayers, default_num, zero);
      make_basic<MaliciousRep3Share<U>>({}, nplayers, default_num, zero);
      make_basic<PostSacriRepFieldShare<U>>({}, nplayers, default_num, zero);
      make_with_mac_key<SpdzWiseShare<MaliciousRep3Share<U>>>(nplayers, default_num, zero);
    }

  make_basic<SemiShare<U>>({}, nplayers, default_num, zero);

  if (nplayers > 2)
    {
      make_basic<ShamirShare<U>>({}, nplayers, default_num, zero);
      make_basic<MaliciousShamirShare<U>>({}, nplayers, default_num, zero);
      make_with_mac_key<SpdzWiseShare<MaliciousShamirShare<U>>>(nplayers,
          default_num, zero);
    }
}
