#include "zmult.h"

#define reg register double /* XXX: long double */

/* (u,v) <- (u + v,2^(-96k)(u - v); 0 <= k < 16 */
void zmult_32fft_unbutterfly(double u[32],double v[32],int k,double tmp[32])
{
  int i;
  reg t0, t1, t2, t3, t4, t5, t6, t7;

  k *= 2;

  for (i = 0;i < k;++i) {
    t0 = u[i];
    t2 = v[i];
    t1 = t0;
    t1 = t2 - t1;
    tmp[i] = t1;
    t0 += t2;
    u[i] = t0;
  }
  for (i = k;i < 32;++i) {
    t0 = u[i];
    t2 = v[i];
    t1 = t0;
    t1 -= t2;
    v[i - k] = t1;
    t0 += t2;
    u[i] = t0;
  }
  v -= k;
  for (i = 0;i < k;++i)
    v[32 + i] = tmp[i];
}

static void zmult_32fft_un2(double u[64],int b,double tmp[32])
{
  zmult_32fft_unbutterfly(u,u + 32,b,tmp);
}

static void zmult_32fft_un4(double u[128],int b,double tmp[32])
{
  zmult_32fft_un2(u,b,tmp);
  zmult_32fft_un2(u + 64,b + 8,tmp);
  zmult_32fft_unbutterfly(u,u + 64,2 * b,tmp);
  zmult_32fft_unbutterfly(u + 32,u + 96,2 * b,tmp);
}

static void zmult_32fft_un8(double u[256],int b,double tmp[32])
{
  zmult_32fft_un4(u,b,tmp);
  zmult_32fft_un4(u + 128,b + 4,tmp);
  zmult_32fft_unbutterfly(u + 0,u + 128,4 * b,tmp);
  zmult_32fft_unbutterfly(u + 32,u + 160,4 * b,tmp);
  zmult_32fft_unbutterfly(u + 64,u + 192,4 * b,tmp);
  zmult_32fft_unbutterfly(u + 96,u + 224,4 * b,tmp);
}

static void zmult_32fft_un16(double u[512],int b,double tmp[32])
{
  int i;
  zmult_32fft_un8(u,b,tmp);
  zmult_32fft_un8(u + 256,b + 2,tmp);
  for (i = 0;i < 256;i += 32)
    zmult_32fft_unbutterfly(u + i,u + 256 + i,8 * b,tmp);
}

void zmult_32fft_un32_0(double u[1024],double tmp[32])
{
  int i;
  zmult_32fft_un16(u,0,tmp);
  zmult_32fft_un16(u + 512,1,tmp);
  for (i = 0;i < 512;i += 32)
    zmult_32fft_unbutterfly(u + i,u + 512 + i,0,tmp);
}

static void zmult_32fft_un32(double u[1024],int b,double tmp[32])
{
  int i;
  zmult_32fft_un32_0(u,tmp);
  b = -b;
  for (i = 0;i < 32;++i)
    zmult_32fft_twist(u + 32 * i,(i * b) & 1023,tmp);
}

static void zmult_32fft_un128(double u[4096],int b,double tmp[32])
{
  int i;
  zmult_32fft_un32(u,b,tmp);
  zmult_32fft_un32(u + 1024,b + 16,tmp);
  zmult_32fft_un32(u + 2048,b + 8,tmp);
  zmult_32fft_un32(u + 3072,b + 24,tmp);
  for (i = 0;i < 1024;i += 32) {
    zmult_32fft_unbutterfly(u + i,u + 1024 + i,b,tmp);
    zmult_32fft_unbutterfly(u + 2048 + i,u + 3072 + i,b + 8,tmp);
    zmult_32fft_unbutterfly(u + i,u + 2048 + i,2 * b,tmp);
    zmult_32fft_unbutterfly(u + 1024 + i,u + 3072 + i,2 * b,tmp);
  }
}

void zmult_32fft_un512(double u[16384],int b,double tmp[32])
{
  int i;
  zmult_32fft_un128(u,b,tmp);
  zmult_32fft_un128(u + 4096,b + 4,tmp);
  zmult_32fft_un128(u + 8192,b + 2,tmp);
  zmult_32fft_un128(u + 12288,b + 6,tmp);
  for (i = 0;i < 4096;i += 32) {
    zmult_32fft_unbutterfly(u + i,u + 4096 + i,4 * b,tmp);
    zmult_32fft_unbutterfly(u + 8192 + i,u + 12288 + i,8 + 4 * b,tmp);
    zmult_32fft_unbutterfly(u + i,u + 8192 + i,8 * b,tmp);
    zmult_32fft_unbutterfly(u + 4096 + i,u + 12288 + i,8 * b,tmp);
  }
}
