#include "Verifier.h"
#include "FHE/P2Data.h"
#include "Math/Z2k.hpp"
#include "Math/modp.hpp"

template <class FD>
Verifier<FD>::Verifier(Proof& proof, const FD& FieldD) :
    P(proof), FieldD(FieldD)
{
#ifdef LESS_ALLOC_MORE_MEM
  z.resize(proof.phim);
  z.allocate_slots(bigint(1) << proof.B_plain_length);
  t.resize(3, proof.phim);
  t.allocate_slots(bigint(1) << proof.B_rand_length);
#endif
}


template <class T, class FD, class S>
bool Check_Decoding(const Plaintext<T,FD,S>& AE,bool Diag)
{
//  // Now check decoding z[i]
//  if (!AE.to_type(0))
//    { cout << "Fail Check 4 " << endl;
//      return false;
//    }
  if (Diag && !AE.is_diagonal())
    { cout << "Fail Check 5 " << endl;
      return false;
    }
  return true;
}

template <>
bool Check_Decoding(const vector<Proof::bound_type>& AE, bool Diag, const FFT_Data&)
{
  if (Diag)
    {
      for (size_t i = 1; i < AE.size(); i++)
        if (AE[i] != 0)
          return false;
    }
  return true;
}

template <>
bool Check_Decoding(const vector<Proof::bound_type>& AE, bool Diag, const P2Data& p2d)
{
  if (Diag)
    {
      Plaintext_<P2Data> tmp(p2d);
      for (size_t i = 0; i < AE.size(); i++)
        tmp.set_coeff(i, AE[i].get_limb(0) % 2);
      return tmp.is_diagonal();
    }
  return true;
}



template <class FD>
void Verifier<FD>::Stage_2(
                          AddableVector<Ciphertext>& c,octetStream& ciphertexts,
                          octetStream& cleartexts,
                          const FHE_PK& pk,bool binary)
{
  unsigned int i, V;

  c.unpack(ciphertexts, pk);
  if (c.size() != P.U)
    throw length_error("number of received ciphertexts incorrect");

  // Now check the encryptions are correct
  Ciphertext d1(pk.get_params()), d2(pk.get_params());
  Random_Coins rc(pk.get_params());
  ciphertexts.get(V);
  if (V != P.V)
    throw length_error("number of received commitments incorrect");
  cleartexts.get(V);
  if (V != P.V)
    throw length_error("number of received cleartexts incorrect");
  for (i=0; i<V; i++)
    {
      z.unpack(cleartexts);
      t.unpack(cleartexts);
      if (!P.check_bounds(z, t, i))
        throw runtime_error("preimage out of bounds");
      d1.unpack(ciphertexts);
      P.apply_challenge(i, d1, c, pk);
      rc.assign(t[0], t[1], t[2]);
      pk.encrypt(d2,z,rc);
      if (!(d1 == d2))
        { cout << "Fail Check 6 " << i << endl; 
          throw runtime_error("ciphertexts don't match");
        }
      if (!Check_Decoding(z,P.get_diagonal(),FieldD))
         { cout << "\tCheck : " << i << endl; 
           throw runtime_error("cleartext isn't diagonal");
         }
      if (binary && !z.is_binary())
        {
          cout << "Not binary " << i << endl;
          throw runtime_error("cleartext isn't binary");
        }
    }
}

  

/* This is the non-interactive version using the ROM
*/
template <class FD>
void Verifier<FD>::NIZKPoK(AddableVector<Ciphertext>& c,
                          octetStream& ciphertexts, octetStream& cleartexts,
                          const FHE_PK& pk,
                          bool binary)
{
  P.set_challenge(ciphertexts);

  Stage_2(c,ciphertexts,cleartexts,pk,binary);

  if (P.top_gear)
    {
      assert(not P.get_diagonal());
      assert(not binary);
      c += c;
    }
}


template class Verifier<FFT_Data>;
template class Verifier<P2Data>;
