/* test-znpoly.c
 * Daniel S. Roche, May 2009
 * http://www.cs.uwaterloo.ca/~droche/
 *
 * Test code for classical/Karatsuba polynomial multiplication.
 * Two filenames should be given as arguments, for the input and output.
 * The input has six integers on the first line:
 * t: the number of test cases to follow
 * m: greater than the degree of the first polynomial in each pair
 * n: greater than the degree of the second polynomial in each pair
 * p: the modulus to use
 * k, w: Ignored integers
 * What follows are 2t lines each containing a polynomial 
 * (alternating sizes m and n),
 * formatted as [a0 a1 a2 ... am] where a0..am are the coefficients.
 * The output consists of t polynomials (the products), in the same format,
 * one per line.
 * Upon completion, the number of seconds used to 
 * perform the computation is written to standard out.
 *
 * See LICENSE.txt for copyright and permissions.
 */

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <time.h>
#include "lsmul.h"
#include <zn_poly/zn_poly.h>

inline void to_zn (ulong *out, const lsmul_ele *in, long len) {
   long i;
   for (i=0; i<len; ++i) out[i] = (ulong) in[i];
}

inline void from_zn (lsmul_ele *out, ulong *in, long len) {
   long i;
   for (i=0; i<len; ++i) out[i] = (lsmul_ele) in[i];
}

int main(int argc, char** argv) {
   long t,m,n,i,mi,ni;
   size_t *sizes;
   ulong p,w;
   int k;
   lsmul_ele *temp;
   zn_mod_t mod;
   ulong *polys, *currF;
   clock_t cstart, cend;
   FILE *in, *out;

   assert(argc == 3);
   in = fopen(argv[1],"r");
   assert(in);

   i = fscanf(in, " %ld %ld %ld %lu %d %lu", 
      &t, &m, &n, &p, &k, &w);
   assert (i == 6);

   zn_mod_init (mod, p);

   temp = malloc ((m+n-1) * t * sizeof(lsmul_ele));
   assert (temp);
   polys = malloc ((2*m + 2*n - 1) * t * sizeof(ulong));
   assert (polys);
   sizes = malloc (2*t*sizeof(size_t));
   assert (sizes);

   // Read in the test data
   currF = polys;
   for (i=0; i<2*t; i += 2) {
      sizes[i] = read_poly_prealloc (in, temp);
      to_zn (currF, temp, sizes[i]);
      sizes[i+1] = read_poly_prealloc (in, temp);
      to_zn (currF+sizes[i], temp, sizes[i+1]);
      currF += 2*sizes[i] + 2*sizes[i+1] - 1;
   }

   fclose(in);

   // Perform the multiplications
   cstart = clock();
   currF = polys;
   for(i=0; i<2*t; i += 2) {
      mi = sizes[i];
      ni = sizes[i+1];

      if (mi >= ni)
         zn_array_mul (currF+mi+ni, currF, mi, currF+mi, ni, mod);
      else
         zn_array_mul (currF+mi+ni, currF+mi, ni, currF, mi, mod);

      currF += 2*mi + 2*ni - 1;
   }
   cend = clock();

   // Write the results
   out = fopen(argv[2],"w");
   assert(out);
   currF = polys;
   for (i=0; i<2*t; i += 2) {
      mi = sizes[i];
      ni = sizes[i+1];
      from_zn (temp, currF+mi+ni, mi+ni-1);
      write_poly (out, temp, mi+ni-1);
      putc( '\n', out );
      currF += 2*mi + 2*ni - 1;
   }
   fclose(out);

   printf("========================================================================\n");
   printf("Time for zn_poly: %lf\n", ((double)(cend-cstart))/CLOCKS_PER_SEC);
   printf("========================================================================\n");

   free(temp);
   free(sizes);
   free(polys);

   return 0;
}
