#include <NTL/mat_lzz_p.h>
#include <flint/nmod_mat.h>
#include <cstdio>

NTL_CLIENT


struct Flint_mat_zz_p {
   nmod_mat_t value;

   explicit
   Flint_mat_zz_p(const mat_zz_p& a)
   {
      long n = a.NumRows();
      long m = a.NumCols();
      nmod_mat_init(value, n, m, zz_p::modulus());

      for (long i = 0; i < n; i++) {
         for (long j = 0; j < m; j++) {
            (*nmod_mat_entry_ptr(value, i, j)) = rep(a[i][j]);
         }
      }
   }

   ~Flint_mat_zz_p() { nmod_mat_clear(value); }
};




void FillRandom(Mat<zz_p>& A)
{
   long n = A.NumRows();
   long m = A.NumCols();
   for (long i = 0; i < n; i++)
      for (long j = 0; j < m; j++)
         random(A[i][j]);
}


int main()
{
   setbuf(stdout, NULL);

   for (long l = 5; l <= 60; l += 5) {
      // n = 512 768 1024 1536 2048 3072 4096 
      for (long idx = 0; idx < 9; idx ++) {
         long n  = 512*(1L << idx/2);
         if (idx & 1) n += n/2;

	 SetSeed((ZZ(l) << 64) + ZZ(n));

	 long p;

	 p = RandomPrime_long(l);
	 zz_p::init(p);

	 mat_zz_p A, C;

	 A.SetDims(n, n);
	 FillRandom(A);

         while (determinant(A) == 0) FillRandom(A);

	 C.SetDims(n, n);

	 Flint_mat_zz_p fA(A), fC(C);


	 double t;

	 long iter = 1;
	 do {
	    t = GetTime();
	    for (long i = 0; i < iter; i++) inv(C, A);
	    t = GetTime() - t;
	    iter *= 2;
	 } while (t < 3);
	 iter /= 2;

	 double NTLTime = t;


	 t = GetTime();
	 for (long i = 0; i < iter; i++) nmod_mat_inv(fC.value, fA.value);
	 t = GetTime()-t;
	 double FlintTime = t;

	 printf("%8.2f", FlintTime/NTLTime);
      }

      printf("\n");
   }
}


