#include <stdint.h>
#include "params.h"
#include "polx.h"
#include "poly.h"
#include "polz.h"
#include "pdata.h"
#include "gaussian.h"

void polx_gaussian64(polx *r) {
  polz f;
  polz_gaussian64(&f);
  polz_cmodq(&f);
  polz_topolx(r,&f);
}

void polx_gaussian70(polx *r) {
  polz f;
  polz_gaussian70(&f);
  polz_cmodq(&f);
  polz_topolx(r,&f);
}

void polx_frompoly(polx *r,const poly *a) {
  r->vec[0] = *a;
  r->vec[1] = *a;
  r->vec[2] = *a;
  r->vec[3] = *a;
  r->vec[4] = *a;
  r->vec[5] = *a;
}

void polx_reduce(polx *r) {
  poly_reduce(&r->vec[0],PDATA0);
  poly_reduce(&r->vec[1],PDATA1);
  poly_reduce(&r->vec[2],PDATA2);
  poly_reduce(&r->vec[3],PDATA3);
  poly_reduce(&r->vec[4],PDATA4);
  poly_reduce(&r->vec[5],PDATA5);
}

void polx_add(polx *r,const polx *a,const polx *b) {
  poly_add(&r->vec[0],&a->vec[0],&b->vec[0]);
  poly_add(&r->vec[1],&a->vec[1],&b->vec[1]);
  poly_add(&r->vec[2],&a->vec[2],&b->vec[2]);
  poly_add(&r->vec[3],&a->vec[3],&b->vec[3]);
  poly_add(&r->vec[4],&a->vec[4],&b->vec[4]);
  poly_add(&r->vec[5],&a->vec[5],&b->vec[5]);
}

void polx_sub(polx *r,const polx *a,const polx *b) {
  poly_sub(&r->vec[0],&a->vec[0],&b->vec[0]);
  poly_sub(&r->vec[1],&a->vec[1],&b->vec[1]);
  poly_sub(&r->vec[2],&a->vec[2],&b->vec[2]);
  poly_sub(&r->vec[3],&a->vec[3],&b->vec[3]);
  poly_sub(&r->vec[4],&a->vec[4],&b->vec[4]);
  poly_sub(&r->vec[5],&a->vec[5],&b->vec[5]);
}

void polx_ntt(polx *r) {
  poly_ntt(&r->vec[0],PDATA0);
  poly_ntt(&r->vec[1],PDATA1);
  poly_ntt(&r->vec[2],PDATA2);
  poly_ntt(&r->vec[3],PDATA3);
  poly_ntt(&r->vec[4],PDATA4);
  poly_ntt(&r->vec[5],PDATA5);
}

void polx_invntt(polx *r) {
  poly_invntt(&r->vec[0],PDATA0);
  poly_invntt(&r->vec[1],PDATA1);
  poly_invntt(&r->vec[2],PDATA2);
  poly_invntt(&r->vec[3],PDATA3);
  poly_invntt(&r->vec[4],PDATA4);
  poly_invntt(&r->vec[5],PDATA5);
}

void polx_pointwise(polx *r,const polx *a,const polx *b) {
  poly_pointwise(&r->vec[0],&a->vec[0],&b->vec[0],PDATA0);
  poly_pointwise(&r->vec[1],&a->vec[1],&b->vec[1],PDATA1);
  poly_pointwise(&r->vec[2],&a->vec[2],&b->vec[2],PDATA2);
  poly_pointwise(&r->vec[3],&a->vec[3],&b->vec[3],PDATA3);
  poly_pointwise(&r->vec[4],&a->vec[4],&b->vec[4],PDATA4);
  poly_pointwise(&r->vec[5],&a->vec[5],&b->vec[5],PDATA5);
}

void polx_pointwise_add(polx *r,const polx *a,const polx *b,const polx *c) {
  poly f;

  poly_pointwise(&f,&a->vec[0],&b->vec[0],PDATA0);
  poly_add(&r->vec[0],&c->vec[0],&f);
  poly_pointwise(&f,&a->vec[1],&b->vec[1],PDATA1);
  poly_add(&r->vec[1],&c->vec[1],&f);
  poly_pointwise(&f,&a->vec[2],&b->vec[2],PDATA2);
  poly_add(&r->vec[2],&c->vec[2],&f);
  poly_pointwise(&f,&a->vec[3],&b->vec[3],PDATA3);
  poly_add(&r->vec[3],&c->vec[3],&f);
  poly_pointwise(&f,&a->vec[4],&b->vec[4],PDATA4);
  poly_add(&r->vec[4],&c->vec[4],&f);
  poly_pointwise(&f,&a->vec[5],&b->vec[5],PDATA5);
  poly_add(&r->vec[5],&c->vec[5],&f);
}

void polx_add_poly(polx *r,const polx *a,const poly *b) {
  poly_add(&r->vec[0],&a->vec[0],b);
  poly_add(&r->vec[1],&a->vec[1],b);
  poly_add(&r->vec[2],&a->vec[2],b);
  poly_add(&r->vec[3],&a->vec[3],b);
  poly_add(&r->vec[4],&a->vec[4],b);
  poly_add(&r->vec[5],&a->vec[5],b);
}

void polx_scale(polx *r,const polx *a,const int32_t s[3]) {
  int32_t sx[6];

  sx[0] = ((((((int64_t)s[2] << 31) + s[1]) % P0) << 31) + s[0]) % P0;
  sx[1] = ((((((int64_t)s[2] << 31) + s[1]) % P1) << 31) + s[0]) % P1;
  sx[2] = ((((((int64_t)s[2] << 31) + s[1]) % P2) << 31) + s[0]) % P2;
  sx[3] = ((((((int64_t)s[2] << 31) + s[1]) % P3) << 31) + s[0]) % P3;
  sx[4] = ((((((int64_t)s[2] << 31) + s[1]) % P4) << 31) + s[0]) % P4;
  sx[5] = ((((((int64_t)s[2] << 31) + s[1]) % P5) << 31) + s[0]) % P5;

  poly_scale(&r->vec[0],&a->vec[0],PDATA0,sx[0]);
  poly_scale(&r->vec[1],&a->vec[1],PDATA1,sx[1]);
  poly_scale(&r->vec[2],&a->vec[2],PDATA2,sx[2]);
  poly_scale(&r->vec[3],&a->vec[3],PDATA3,sx[3]);
  poly_scale(&r->vec[4],&a->vec[4],PDATA4,sx[4]);
  poly_scale(&r->vec[5],&a->vec[5],PDATA5,sx[5]);
}

void polx_sigmam1(polx *r,const polx *a) {
  int i;
  poly f;

  for(i=0;i<6;i++) {
    f = a->vec[i];
    poly_sigmam1(&r->vec[i],&f);
  }
}

void polx_sigma5(polx *r,const polx *a) {
  int i;
  poly f;

  for(i=0;i<6;i++) {
    f = a->vec[i];
    poly_sigma5(&r->vec[i],&f);
  }
}

void polx_sigma5inv(polx *r,const polx *a) {
  int i;
  poly f;

  for(i=0;i<6;i++) {
    f = a->vec[i];
    poly_sigma5inv(&r->vec[i],&f);
  }
}
