lol-0.0.1.0: src/Crypto/Lol/Cyclotomic/Tensor/CTensor/crt.c
#include "tensorTypes.h"
#include <time.h>
#include <stdlib.h>
// there should be a special cases that do NOT require temp space to be allocated for all primes *smaller* than DFTP_GENERIC_SIZE
#define DFTP_GENERIC_SIZE 11
#ifdef STATS
int crtRqCtr = 0;
int crtInvRqCtr = 0;
int crtCCtr = 0;
int crtInvCCtr = 0;
struct timespec crttime1 = {0,0};
struct timespec crttime2 = {0,0};
struct timespec crttime3 = {0,0};
struct timespec crttime4 = {0,0};
struct timespec crtInvRqTime = {0,0};
struct timespec crtCTime = {0,0};
struct timespec crtInvCTime = {0,0};
#endif
hDim_t bitrev (PrimeExponent pe, hDim_t j) {
hShort_t e;
hDim_t p = pe.prime;
hDim_t tempj = j;
hDim_t acc = 0;
for(e = pe.exponent-1; e >= 0; e--) {
div_t qr = div(tempj,p);
acc += qr.rem * ipow(p,e);
tempj = qr.quot;
}
return acc;
}
void crtTwiddleRq (hInt_t* y, hDim_t lts, hDim_t rts, PrimeExponent pe, hInt_t* ru, hInt_t q)
{
hDim_t p = pe.prime;
hShort_t e = pe.exponent;
#ifdef DEBUG_MODE
ASSERT(e != 0);
#endif
pe.exponent -= 1; // used for an argument to bitrev
if(p == 2)
{
hDim_t mprime = 1<<(e-1);
hDim_t blockDim = rts*mprime; // size of block in block diagonal tensor matrix
for(hDim_t i0 = 1; i0 < mprime; i0++) // loops over i/(p-1) for i = 0..(m'-1), we can skip i0 = 0
{
hDim_t temp2 = i0*rts;
hInt_t twid = ru[bitrev(pe, i0)];
for(hDim_t blockIdx = 0; blockIdx < lts; blockIdx++)
{
hDim_t temp3 = blockIdx*blockDim + temp2;
for(hDim_t modOffset = 0; modOffset < rts; modOffset++)
{
hDim_t idx = temp3 + modOffset;
y[idx] = (y[idx]*twid) % q;
}
}
}
}
else // This loop is faster, probably due to the division in the loop above.
// cilk also slows it down
{
hDim_t mprime = ipow(p,e-1);
hDim_t blockDim = rts*(p-1)*mprime; // size of block in block diagonal tensor matrix
for(hDim_t i0 = 1; i0 < mprime; i0++) // loops over i/(p-1) for i = 0..(m'-1), we can skip i0 = 0
{
hDim_t temp1 = i0*(p-1);
for(hDim_t i1 = 0; i1 < (p-1); i1++) // loops over i%(p-1) for i = 0..(m'-1)
{
hDim_t temp2 = (temp1+i1)*rts;
hInt_t twid = ru[bitrev(pe, i0)*(i1+1)];
for(hDim_t blockIdx = 0; blockIdx < lts; blockIdx++)
{
hDim_t temp3 = blockIdx*blockDim + temp2;
for(hDim_t modOffset = 0; modOffset < rts; modOffset++)
{
hDim_t idx = temp3 + modOffset;
y[idx] = (y[idx]*twid) % q;
}
}
}
}
}
}
// dim is power of p
void dftptwidRq (hInt_t* y, hDim_t lts, hDim_t rts, PrimeExponent pe, hDim_t dim, hDim_t rustride, hInt_t* ru, hInt_t q)
{
hDim_t idx;
hDim_t p = pe.prime;
pe.exponent -= 1; // used for an argument to bitrev
if(p == 2) {
hDim_t mprime = dim>>1; // divides evenly
hDim_t temp1 = rts*dim; // for use in computing [modified] tensorOffset
for(hDim_t i0 = 1; i0 < mprime; i0++) // loops over i/p for i = 0..(dim-1), but we skip i0=0
{
hDim_t temp3 = rts*(i0*p+1);
hInt_t twid = ru[bitrev(pe,i0)*rustride];
for(hDim_t blockOffset = 0; blockOffset < lts; blockOffset++)
{
hDim_t temp2 = blockOffset*temp1 + temp3;
for(hDim_t modOffset = 0; modOffset < rts; modOffset++)
{
idx = temp2 + modOffset;
y[idx] = (y[idx]*twid) % q;
}
}
}
}
else
{
hDim_t mprime = dim/p; // divides evenly
hDim_t temp1 = rts*dim; // for use in computing [modified] tensorOffset
for(hDim_t i0 = 1; i0 < mprime; i0++) // loops over i/p for i = 0..(dim-1), but we skip i0=0
{
for(hDim_t i1 = 1; i1 < p; i1++) // loops over i%p for i = 0..(dim-1), but we skip i1=0
{
hDim_t temp3 = rts*(i0*p+i1);
hInt_t twid = ru[bitrev(pe,i0)*i1*rustride];
for(hDim_t blockOffset = 0; blockOffset < lts; blockOffset++)
{
hDim_t temp2 = blockOffset*temp1 + temp3;
for(hDim_t modOffset = 0; modOffset < rts; modOffset++)
{
idx = temp2 + modOffset;
y[idx] = (y[idx]*twid) % q;
}
}
}
}
}
}
//implied length of ru is rustride*p
//implied length of tempSpace is p, if p is not a special case
// temp is allowed to be NULL if p < DFTP_GENERIC_SIZE
void dftpRq (hInt_t* y, hDim_t lts, hDim_t rts, hDim_t p, hDim_t rustride, hInt_t* ru, hInt_t* tempSpace, hInt_t q)
{
hDim_t tensorOffset;
if(p == 2)
{
hDim_t temp1 = rts<<1;
for(hDim_t blockOffset = 0; blockOffset < lts; blockOffset++)
{
hDim_t temp2 = blockOffset*temp1;
for(hDim_t modOffset = 0; modOffset < rts; modOffset++)
{
tensorOffset = temp2 + modOffset;
hInt_t u = y[tensorOffset];
hInt_t t = y[tensorOffset+rts];
y[tensorOffset] = (u + t) % q;
y[tensorOffset+rts] = (u - t) % q;
}
}
}
else if(p == 3)
{
hInt_t ru1 = ru[rustride];
hInt_t ru2 = ru[rustride<<1];
hDim_t temp1 = rts*3;
for(hDim_t blockOffset = 0; blockOffset < lts; blockOffset++)
{
hDim_t temp2 = blockOffset*temp1;
for(hDim_t modOffset = 0; modOffset < rts; modOffset++)
{
tensorOffset = temp2 + modOffset;
hInt_t y1, y2, y3;
y1 = y[tensorOffset];
y2 = y[tensorOffset+rts];
y3 = y[tensorOffset+(rts<<1)];
//q is <32 bits, so we can do 3 additions without overflow
y[tensorOffset] = (y1 + y2 + y3) % q;
y[tensorOffset+rts] = (y1 + ((ru1*y2) % q) + ((ru2*y3) % q)) % q;
y[tensorOffset+(rts<<1)] = (y1 + ((ru2*y2) % q) + ((ru1*y3) % q)) % q;
}
}
}
else if(p == 5)
{
hDim_t temp1 = rts*5;
hInt_t ru1 = ru[rustride];
hInt_t ru2 = ru[rustride<<1];
hInt_t ru3 = ru[rustride*3];
hInt_t ru4 = ru[rustride<<2];
for(hDim_t blockOffset = 0; blockOffset < lts; blockOffset++)
{
hDim_t temp2 = blockOffset*temp1;
for(hDim_t modOffset = 0; modOffset < rts; modOffset++)
{
tensorOffset = temp2 + modOffset;
hInt_t y1, y2, y3, y4, y5;
y1 = y[tensorOffset];
y2 = y[tensorOffset+rts];
y3 = y[tensorOffset+(rts<<1)];
y4 = y[tensorOffset+3*rts];
y5 = y[tensorOffset+(rts<<2)];
y[tensorOffset] = (y1 + y2 + y3 + y4 + y5) % q;
y[tensorOffset+rts] = (y1 + ((ru1*y2) % q) + ((ru2*y3) % q) + ((ru3*y4) % q) + ((ru4*y5) % q)) % q;
y[tensorOffset+(rts<<1)] = (y1 + ((ru2*y2) % q) + ((ru4*y3) % q) + ((ru1*y4) % q) + ((ru3*y5) % q)) % q;
y[tensorOffset+rts*3] = (y1 + ((ru3*y2) % q) + ((ru1*y3) % q) + ((ru4*y4) % q) + ((ru2*y5) % q)) % q;
y[tensorOffset+(rts<<2)] = (y1 + ((ru4*y2) % q) + ((ru3*y3) % q) + ((ru2*y4) % q) + ((ru1*y5) % q)) % q;
}
}
}
else if(p == 7)
{
hDim_t temp1 = rts*7;
hInt_t ru1 = ru[rustride];
hInt_t ru2 = ru[rustride<<1];
hInt_t ru3 = ru[rustride*3];
hInt_t ru4 = ru[rustride<<2];
hInt_t ru5 = ru[rustride*5];
hInt_t ru6 = ru[rustride*6];
for(hDim_t blockOffset = 0; blockOffset < lts; blockOffset++)
{
hDim_t temp2 = blockOffset*temp1;
for(hDim_t modOffset = 0; modOffset < rts; modOffset++)
{
tensorOffset = temp2 + modOffset;
hInt_t y1, y2, y3, y4, y5, y6, y7;
y1 = y[tensorOffset];
y2 = y[tensorOffset+rts];
y3 = y[tensorOffset+(rts<<1)];
y4 = y[tensorOffset+3*rts];
y5 = y[tensorOffset+(rts<<2)];
y6 = y[tensorOffset+rts*5];
y7 = y[tensorOffset+rts*6];
y[tensorOffset] = (y1 + y2 + y3 + y4 + y5 + y6 + y7) % q;
y[tensorOffset+rts] = (y1 + ((ru1*y2) % q) + ((ru2*y3) % q) + ((ru3*y4) % q) + ((ru4*y5) % q) + ((ru5*y6) % q) + ((ru6*y7) % q)) % q;
y[tensorOffset+(rts<<1)] = (y1 + ((ru2*y2) % q) + ((ru4*y3) % q) + ((ru6*y4) % q) + ((ru1*y5) % q) + ((ru3*y6) % q) + ((ru5*y7) % q)) % q;
y[tensorOffset+rts*3] = (y1 + ((ru3*y2) % q) + ((ru6*y3) % q) + ((ru2*y4) % q) + ((ru5*y5) % q) + ((ru1*y6) % q) + ((ru4*y7) % q)) % q;
y[tensorOffset+(rts<<2)] = (y1 + ((ru4*y2) % q) + ((ru1*y3) % q) + ((ru5*y4) % q) + ((ru2*y5) % q) + ((ru6*y6) % q) + ((ru3*y7) % q)) % q;
y[tensorOffset+rts*5] = (y1 + ((ru5*y2) % q) + ((ru3*y3) % q) + ((ru1*y4) % q) + ((ru6*y5) % q) + ((ru4*y6) % q) + ((ru2*y7) % q)) % q;
y[tensorOffset+rts*6] = (y1 + ((ru6*y2) % q) + ((ru5*y3) % q) + ((ru4*y4) % q) + ((ru3*y5) % q) + ((ru2*y6) % q) + ((ru1*y7) % q)) % q;
}
}
}
else
{
hDim_t temp1 = rts*p;
for(hDim_t blockOffset = 0; blockOffset < lts; blockOffset++)
{
hDim_t temp2 = blockOffset*temp1;
for(hDim_t modOffset = 0; modOffset < rts; modOffset++)
{
tensorOffset = temp2 + modOffset;
for(hDim_t row = 0; row < p; row++)
{
hInt_t acc = 0;
//p is small (<< 30 bits), so we can do p additions of mod-q values without overflow
for(hDim_t col = 0; col < p; col++)
{
acc += ((y[tensorOffset+col*rts]*ru[((col*row) % p)*rustride])%q);
}
tempSpace[row] = acc % q;
}
for(hDim_t row = 0; row < p; row++)
{
y[tensorOffset+rts*row] = tempSpace[row];
}
}
}
}
}
void crtpRq (hInt_t* y, hDim_t lts, hDim_t rts, hDim_t p, hDim_t rustride, hInt_t* ru, hInt_t q)
{
hDim_t tensorOffset;
if(p == 2)
{
return;
}
else if(p == 3)
{
hDim_t temp1 = rts*2;
hInt_t ru1 = ru[rustride];
hInt_t ru2 = ru[rustride<<1];
for(hDim_t blockOffset = 0; blockOffset < lts; blockOffset++)
{
hDim_t temp2 = blockOffset*temp1;
for(hDim_t modOffset = 0; modOffset < rts; modOffset++)
{
tensorOffset = temp2 + modOffset;
hInt_t y1, y2;
y1 = y[tensorOffset];
y2 = y[tensorOffset+rts];
y[tensorOffset] = (y1 + ((ru1*y2)%q)) % q;
y[tensorOffset+rts] = (y1 + ((ru2*y2)%q)) % q;
}
}
}
else if(p == 5)
{
hDim_t temp1 = rts*4;
hInt_t ru1 = ru[rustride];
hInt_t ru2 = ru[rustride<<1];
hInt_t ru3 = ru[rustride*3];
hInt_t ru4 = ru[rustride<<2];
for(hDim_t blockOffset = 0; blockOffset < lts; blockOffset++)
{
hDim_t temp2 = blockOffset*temp1;
for(hDim_t modOffset = 0; modOffset < rts; modOffset++)
{
tensorOffset = temp2 + modOffset;
hInt_t y1, y2, y3, y4;
y1 = y[tensorOffset];
y2 = y[tensorOffset+rts];
y3 = y[tensorOffset+(rts<<1)];
y4 = y[tensorOffset+3*rts];
y[tensorOffset] = (y1 + ((ru1*y2) % q) + ((ru2*y3) % q) + ((ru3*y4) % q)) % q;
y[tensorOffset+rts] = (y1 + ((ru2*y2) % q) + ((ru4*y3) % q) + ((ru1*y4) % q)) % q;
y[tensorOffset+(rts<<1)] = (y1 + ((ru3*y2) % q) + ((ru1*y3) % q) + ((ru4*y4) % q)) % q;
y[tensorOffset+rts*3] = (y1 + ((ru4*y2) % q) + ((ru3*y3) % q) + ((ru2*y4) % q)) % q;
}
}
}
else if(p == 7)
{
hDim_t temp1 = rts*6;
hInt_t ru1 = ru[rustride];
hInt_t ru2 = ru[rustride<<1];
hInt_t ru3 = ru[rustride*3];
hInt_t ru4 = ru[rustride<<2];
hInt_t ru5 = ru[rustride*5];
hInt_t ru6 = ru[rustride*6];
for(hDim_t blockOffset = 0; blockOffset < lts; blockOffset++)
{
hDim_t temp2 = blockOffset*temp1;
for(hDim_t modOffset = 0; modOffset < rts; modOffset++)
{
tensorOffset = temp2 + modOffset;
hInt_t y1, y2, y3, y4, y5, y6;
y1 = y[tensorOffset];
y2 = y[tensorOffset+rts];
y3 = y[tensorOffset+(rts<<1)];
y4 = y[tensorOffset+3*rts];
y5 = y[tensorOffset+(rts<<2)];
y6 = y[tensorOffset+rts*5];
y[tensorOffset] = (y1 + ((ru1*y2) % q) + ((ru2*y3) % q) + ((ru3*y4) % q) + ((ru4*y5) % q) + ((ru5*y6) % q)) % q;
y[tensorOffset+rts] = (y1 + ((ru2*y2) % q) + ((ru4*y3) % q) + ((ru6*y4) % q) + ((ru1*y5) % q) + ((ru3*y6) % q)) % q;
y[tensorOffset+(rts<<1)] = (y1 + ((ru3*y2) % q) + ((ru6*y3) % q) + ((ru2*y4) % q) + ((ru5*y5) % q) + ((ru1*y6) % q)) % q;
y[tensorOffset+rts*3] = (y1 + ((ru4*y2) % q) + ((ru1*y3) % q) + ((ru5*y4) % q) + ((ru2*y5) % q) + ((ru6*y6) % q)) % q;
y[tensorOffset+(rts<<2)] = (y1 + ((ru5*y2) % q) + ((ru3*y3) % q) + ((ru1*y4) % q) + ((ru6*y5) % q) + ((ru4*y6) % q)) % q;
y[tensorOffset+rts*5] = (y1 + ((ru6*y2) % q) + ((ru5*y3) % q) + ((ru4*y4) % q) + ((ru3*y5) % q) + ((ru2*y6) % q)) % q;
}
}
}
else
{
hInt_t* tempSpace = (hInt_t*)malloc((p-1)*sizeof(hInt_t));
hDim_t temp1 = rts*(p-1);
for(hDim_t blockOffset = 0; blockOffset < lts; blockOffset++)
{
hDim_t temp2 = blockOffset*temp1;
for(hDim_t modOffset = 0; modOffset < rts; modOffset++)
{
tensorOffset = temp2 + modOffset;
for(hDim_t row = 1; row < p; row++)
{
hInt_t acc = 0;
for(hDim_t col = 0; col < p-1; col++)
{
acc += ((y[tensorOffset+col*rts]*ru[((col*row) % p)*rustride]) % q);
}
tempSpace[row-1] = acc % q;
}
for(hDim_t row = 0; row < p-1; row++)
{
y[tensorOffset+rts*row] = tempSpace[row];
}
}
}
free(tempSpace);
}
}
//takes inverse rus
void crtpinvRq (hInt_t* y, hDim_t lts, hDim_t rts, hDim_t p, hDim_t rustride, hInt_t* ruinv, hInt_t q)
{
if(p ==2)
{
// need this case so that we can divide overall by mhat^(-1)
return;
}
else
{
hDim_t tensorOffset,i;
hInt_t* tempSpace = (hInt_t*)malloc((p-1)*sizeof(hInt_t));
hDim_t temp1 = rts*(p-1);
for(hDim_t blockOffset = 0; blockOffset < lts; blockOffset++)
{
hDim_t temp2 = blockOffset*temp1;
for(hDim_t modOffset = 0; modOffset < rts; modOffset++)
{
tensorOffset = temp2 + modOffset;
for(i = 0; i < p-1; i++)
{
hInt_t sum = 0;
int j;
for(j = 0; j < p-1; j++)
{
int ruIdx = ((j+1)*i) % p;
sum += ((y[tensorOffset+j*rts] * ruinv[ruIdx*rustride]) % q);
}
tempSpace[i] = sum % q;
}
hInt_t shift = 0;
for(i = 0; i < p-1; i++)
{
// we were given the inverse rus, so we need to negate the indices
shift += ((y[tensorOffset+i*rts] * ruinv[rustride*(p-(i+1))]) % q);
}
for(i = 0; i < p-1; i++)
{
y[tensorOffset+i*rts] = (tempSpace[i] - shift) % q;
}
}
}
}
}
void ppDFTRq (hInt_t* y, hDim_t lts, hDim_t rts, PrimeExponent pe, hDim_t rustride, hInt_t* ru, hInt_t q)
{
hDim_t p = pe.prime;
hShort_t e = pe.exponent;
if(e == 0)
{
return;
}
hDim_t primeRuStride = rustride*ipow(p,e-1);
hInt_t* temp = 0;
if(p >= DFTP_GENERIC_SIZE)
{
temp = (hInt_t*)malloc(p*sizeof(hInt_t));
}
hShort_t i;
hDim_t ltsScale = ipow(p,e-1);
hDim_t rtsScale = 1;
hDim_t twidRuStride = rustride;
for(i = 0; i < e; i++)
{
hDim_t rtsDim = rts*rtsScale;
dftpRq (y, lts*ltsScale, rtsDim, p, primeRuStride, ru, temp, q);
dftptwidRq (y, lts, rtsDim, pe, ltsScale*p, twidRuStride, ru, q);
ltsScale /= p;
rtsScale *= p;
twidRuStride *= p;
pe.exponent -= 1;
}
if(p >= DFTP_GENERIC_SIZE)
{
free(temp);
}
}
void ppDFTInvRq (hInt_t* y, hDim_t lts, hDim_t rts, PrimeExponent pe, hDim_t rustride, hInt_t* ru, hInt_t q)
{
hDim_t p = pe.prime;
hShort_t e = pe.exponent;
if(e == 0)
{
return;
}
hDim_t primeRuStride = rustride*ipow(p,e-1);
hInt_t* temp = 0;
if(p >= DFTP_GENERIC_SIZE)
{
temp = (hInt_t*)malloc(p*sizeof(hInt_t));
}
hShort_t i;
hDim_t ltsScale = 1;
hDim_t rtsScale = ipow(p,e-1);
hDim_t twidRuStride = primeRuStride;
pe.exponent = 1;
for(i = 0; i < e; i++)
{
hDim_t rtsDim = rts*rtsScale;
hDim_t ltsScaleP = ltsScale*p;
dftptwidRq (y, lts, rtsDim, pe, ltsScaleP, twidRuStride, ru, q);
dftpRq (y, lts*ltsScale, rtsDim, p, primeRuStride, ru, temp, q);
ltsScale = ltsScaleP;
rtsScale /= p;
twidRuStride /= p;
pe.exponent += 1;
}
if(p >= DFTP_GENERIC_SIZE)
{
free(temp);
}
}
void ppcrtRq (void* y, hDim_t lts, hDim_t rts, PrimeExponent pe, void* ru, hInt_t q)
{
hDim_t p = pe.prime;
hDim_t e = pe.exponent;
#ifdef DEBUG_MODE
ASSERT(e != 0);
#endif
hDim_t mprime = ipow(p,e-1);
#ifdef DEBUG_MODE
printf("lts is %" PRId32 "\trts is %" PRId32 "\n", lts, rts);
printf("rus for p=%" PRId32 ", e=%" PRId16 "\t[", pe.prime, pe.exponent);
hDim_t i;
for(i = 0; i < ipow(p,e); i++) {
printf("%" PRId64 ",", ((hInt_t*)ru)[i]);
}
printf("]\n");
#endif
crtpRq ((hInt_t*)y, lts*mprime, rts, p, mprime, (hInt_t*)ru, q);
crtTwiddleRq ((hInt_t*)y, lts, rts, pe, (hInt_t*)ru, q);
pe.exponent -= 1;
ppDFTRq ((hInt_t*)y, lts, rts*(p-1), pe, p, (hInt_t*)ru, q);
}
void ppcrtinvRq (void* y, hDim_t lts, hDim_t rts, PrimeExponent pe, void* ru, hInt_t q)
{
hDim_t p = pe.prime;
hDim_t e = pe.exponent;
#ifdef DEBUG_MODE
ASSERT(e != 0);
#endif
hDim_t mprime = ipow(p,e-1);
#ifdef DEBUG_MODE
printf("lts is %" PRId32 "\trts is %" PRId32 "\n", lts, rts);
printf("rus for p=%" PRId32 ", e=%" PRId16 "\t[", pe.prime, pe.exponent);
hDim_t i;
for(i = 0; i < ipow(p,e); i++) {
printf("%" PRId64 ",", ((hInt_t*)ru)[i]);
}
printf("]\n");
#endif
pe.exponent -= 1;
ppDFTInvRq ((hInt_t*)y, lts, rts*(p-1), pe, p, (hInt_t*)ru, q);
pe.exponent += 1;
crtTwiddleRq ((hInt_t*)y, lts, rts, pe, (hInt_t*)ru, q);
crtpinvRq ((hInt_t*)y, lts*mprime, rts, p, mprime, (hInt_t*)ru, q);
}
// EAC: Somebody who knows C/C++ should find a better way to handle pointers-to-pointers in a generic way
void tensorCRTRq (hInt_t* y, hDim_t totm, PrimeExponent* peArr, hShort_t sizeOfPE, hInt_t** ru, hInt_t q)
{
hDim_t i;
#ifdef STATS
struct timespec s1,s2,s3,s4,t1,t2,t3,t4;
crtRqCtr++;
clock_gettime(CLOCK_REALTIME, &s1);
clock_gettime(CLOCK_MONOTONIC, &s2);
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &s3);
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &s4);
#endif
#ifdef DEBUG_MODE
printf("\n\nEntered tensorCRTRq\ttotm=%" PRId32 "\tnumFacts=%" PRId16 "\tq=%" PRId64 "\n[", totm, sizeOfPE, q);
for(i = 0; i < totm; i++) {
printf("%" PRId64 ",", y[i]);
}
printf("]\n[");
for(i = 0; i < sizeOfPE; i++) {
printf("(%" PRId32 ",%" PRId16 "),", peArr[i].prime, peArr[i].exponent);
}
printf("]\n");
#endif
void** rus = (void**)malloc(sizeOfPE*sizeof(void*));
for(i = 0; i < sizeOfPE; i++)
{
rus[i] = (void*) (ru[i]);
}
tensorFuserCRT (y, ppcrtRq, totm, peArr, sizeOfPE, rus, q);
for(hDim_t j = 0; j < totm; j++)
{
if(y[j]<0)
{
y[j]+=q;
}
#ifdef DEBUG_MODE
if(y[j]<0)
{
printf("TENSOR CRT^T INV\n");
}
#endif
}
free(rus);
#ifdef STATS
clock_gettime(CLOCK_REALTIME, &t1);
clock_gettime(CLOCK_MONOTONIC, &t2);
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &t3);
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &t4);
crttime1 = tsAdd(crttime1, tsSubtract(t1,s1));
crttime2 = tsAdd(crttime2, tsSubtract(t2,s2));
crttime3 = tsAdd(crttime3, tsSubtract(t3,s3));
crttime4 = tsAdd(crttime4, tsSubtract(t4,s4));
#endif
}
//takes inverse rus
void tensorCRTInvRq (hInt_t* y, hDim_t totm, PrimeExponent* peArr, hShort_t sizeOfPE, hInt_t** ruinv, hInt_t mhatInv, hInt_t q)
{
hDim_t i;
#ifdef STATS
struct timespec s1,t1;
crtInvRqCtr++;
clock_gettime(CLOCK_REALTIME, &s1);
#endif
#ifdef DEBUG_MODE
printf("\n\nEntered tensorCRTInvRq\ttotm=%" PRId32 "\tnumFacts=%" PRId16 "\tminv=%" PRId64 "\tq=%" PRId64 "\n[", totm, sizeOfPE, mhatInv, q);
for(i = 0; i < totm; i++) {
printf("%" PRId64 ",", y[i]);
}
printf("]\n[");
for(i = 0; i < sizeOfPE; i++) {
printf("(%" PRId32 ",%" PRId16 "),", peArr[i].prime, peArr[i].exponent);
}
printf("]\n");
#endif
void** rus = (void**)malloc(sizeOfPE*sizeof(void*));
for(i = 0; i < sizeOfPE; i++)
{
rus[i] = (void*) (ruinv[i]);
}
tensorFuserCRT (y, ppcrtinvRq, totm, peArr, sizeOfPE, rus, q);
for (hDim_t j = 0; j < totm; j++)
{
y[j] = (y[j]*mhatInv)%q;
if(y[j] < 0)
{
y[j] +=q;
}
#ifdef DEBUG_MODE
if(y[j]<0)
{
printf("TENSOR CRT INV\n");
}
#endif
}
free(rus);
#ifdef STATS
clock_gettime(CLOCK_REALTIME, &t1);
crtInvRqTime = tsAdd(crtInvRqTime, tsSubtract(t1,s1));
#endif
}
void crtTwiddleC (complex_t* y, hDim_t lts, hDim_t rts, PrimeExponent pe, complex_t* ru)
{
hDim_t idx;
hDim_t p = pe.prime;
hShort_t e = pe.exponent;
#ifdef DEBUG_MODE
ASSERT(e != 0);
#endif
pe.exponent -= 1; // used for an argument to bitrev
if(p == 2)
{
hDim_t mprime = 1<<(e-1);
hDim_t blockDim = rts*mprime; // size of block in block diagonal tensor matrix
for(hDim_t i0 = 1; i0 < mprime; i0++) // loops over i/(p-1) for i = 0..(m'-1), we can skip i0 = 0
{
hDim_t temp2 = i0*rts;
complex_t twid = ru[bitrev(pe,i0)];
for(hDim_t blockIdx = 0; blockIdx < lts; blockIdx++)
{
hDim_t temp3 = blockIdx*blockDim + temp2;
for(hDim_t modOffset = 0; modOffset < rts; modOffset++)
{
idx = temp3 + modOffset;
CMPLX_IMUL(y[idx],twid);
}
}
}
}
else
{
hDim_t mprime = ipow(p,e-1);
hDim_t blockDim = rts*(p-1)*mprime; // size of block in block diagonal tensor matrix
for(hDim_t i0 = 1; i0 < mprime; i0++) // loops over i/(p-1) for i = 0..(m'-1), we can skip i0 = 0
{
hDim_t temp1 = i0*(p-1);
for(hDim_t i1 = 0; i1 < (p-1); i1++) // loops over i%(p-1) for i = 0..(m'-1)
{
hDim_t temp2 = (temp1+i1)*rts;
complex_t twid = ru[bitrev(pe,i0)*(i1+1)];
for(hDim_t blockIdx = 0; blockIdx < lts; blockIdx++)
{
hDim_t temp3 = blockIdx*blockDim + temp2;
for(hDim_t modOffset = 0; modOffset < rts; modOffset++)
{
idx = temp3 + modOffset;
CMPLX_IMUL(y[idx],twid);
}
}
}
}
}
}
// dim is power of p
void dftptwidC (complex_t* y, hDim_t lts, hDim_t rts, PrimeExponent pe, hDim_t dim, hDim_t rustride, complex_t* ru)
{
hDim_t idx;
hDim_t p = pe.prime;
pe.exponent -= 1; // used for an argument to bitrev
if(p == 2)
{
hDim_t mprime = dim>>1; // divides evenly
hDim_t temp1 = rts*dim; // for use in computing [modified] tensorOffset
for(hDim_t i0 = 1; i0 < mprime; i0++) // loops over i/p for i = 0..(dim-1), but we skip i0=0
{
hDim_t temp3 = rts*(i0*p+1);
complex_t twid = ru[bitrev(pe,i0)*rustride];
for(hDim_t blockOffset = 0; blockOffset < lts; blockOffset++)
{
hDim_t temp2 = blockOffset*temp1 + temp3;
for(hDim_t modOffset = 0; modOffset < rts; modOffset++)
{
idx = temp2 + modOffset;
CMPLX_IMUL(y[idx],twid);
}
}
}
}
else
{
hDim_t mprime = dim/p; // divides evenly
hDim_t temp1 = rts*dim; // for use in computing [modified] tensorOffset
for(hDim_t i0 = 1; i0 < mprime; i0++) // loops over i/p for i = 0..(dim-1), but we skip i0=0
{
for(hDim_t i1 = 1; i1 < p; i1++) // loops over i%p for i = 0..(dim-1), but we skip i1=0
{
hDim_t temp3 = rts*(i0*p+i1);
complex_t twid = ru[bitrev(pe,i0)*i1*rustride];
for(hDim_t blockOffset = 0; blockOffset < lts; blockOffset++)
{
hDim_t temp2 = blockOffset*temp1 + temp3;
for(hDim_t modOffset = 0; modOffset < rts; modOffset++)
{
idx = temp2 + modOffset;
CMPLX_IMUL(y[idx],twid);
}
}
}
}
}
}
//implied length of ru is rustride*p
//implied length of tempSpace is p, if p is not a special case
void dftpC (complex_t* y, hDim_t lts, hDim_t rts, hDim_t p, hDim_t rustride, complex_t* ru, complex_t* tempSpace)
{
hDim_t blockOffset, modOffset, tensorOffset;
if(p == 2)
{
hDim_t temp1 = rts<<1;
for(blockOffset = 0; blockOffset < lts; blockOffset++)
{
hDim_t temp2 = blockOffset*temp1;
for(modOffset = 0; modOffset < rts; modOffset++)
{
tensorOffset = temp2 + modOffset;
complex_t u = y[tensorOffset];
complex_t t = y[tensorOffset+rts];
y[tensorOffset] = CMPLX_ADD(u,t);
y[tensorOffset+rts] = CMPLX_SUB(u,t);
}
}
}
else if(p == 3)
{
hDim_t temp1 = rts*3;
complex_t ru1 = ru[rustride];
complex_t ru2 = ru[rustride<<1];
for(blockOffset = 0; blockOffset < lts; blockOffset++)
{
hDim_t temp2 = blockOffset*temp1;
for(modOffset = 0; modOffset < rts; modOffset++)
{
tensorOffset = temp2 + modOffset;
complex_t y1, y2, y3;
y1 = y[tensorOffset];
y2 = y[tensorOffset+rts];
y3 = y[tensorOffset+(rts<<1)];
y[tensorOffset] = CMPLX_ADD3(y1, y2, y3);
y[tensorOffset+rts] = CMPLX_ADD3(y1, CMPLX_MUL(ru1,y2), CMPLX_MUL(ru2,y3));
y[tensorOffset+(rts<<1)] = CMPLX_ADD3(y1, CMPLX_MUL(ru2,y2), CMPLX_MUL(ru1,y3));
}
}
}
else if(p == 5)
{
hDim_t temp1 = rts*5;
complex_t ru1 = ru[rustride];
complex_t ru2 = ru[rustride<<1];
complex_t ru3 = ru[rustride*3];
complex_t ru4 = ru[rustride<<2];
for(blockOffset = 0; blockOffset < lts; blockOffset++)
{
hDim_t temp2 = blockOffset*temp1;
for(modOffset = 0; modOffset < rts; modOffset++)
{
tensorOffset = temp2 + modOffset;
complex_t y1, y2, y3, y4, y5;
y1 = y[tensorOffset];
y2 = y[tensorOffset+rts];
y3 = y[tensorOffset+(rts<<1)];
y4 = y[tensorOffset+3*rts];
y5 = y[tensorOffset+(rts<<2)];
y[tensorOffset] = CMPLX_ADD5(y1, y2, y3, y4, y5);
y[tensorOffset+rts] = CMPLX_ADD5(y1, CMPLX_MUL(ru1,y2), CMPLX_MUL(ru2,y3), CMPLX_MUL(ru3,y4), CMPLX_MUL(ru4,y5));
y[tensorOffset+(rts<<1)] = CMPLX_ADD5(y1, CMPLX_MUL(ru2,y2), CMPLX_MUL(ru4,y3), CMPLX_MUL(ru1,y4), CMPLX_MUL(ru3,y5));
y[tensorOffset+rts*3] = CMPLX_ADD5(y1, CMPLX_MUL(ru3,y2), CMPLX_MUL(ru1,y3), CMPLX_MUL(ru4,y4), CMPLX_MUL(ru2,y5));
y[tensorOffset+(rts<<2)] = CMPLX_ADD5(y1, CMPLX_MUL(ru4,y2), CMPLX_MUL(ru3,y3), CMPLX_MUL(ru2,y4), CMPLX_MUL(ru1,y5));
}
}
}
else if(p == 7)
{
hDim_t temp1 = rts*7;
complex_t ru1 = ru[rustride];
complex_t ru2 = ru[rustride<<1];
complex_t ru3 = ru[rustride*3];
complex_t ru4 = ru[rustride<<2];
complex_t ru5 = ru[rustride*5];
complex_t ru6 = ru[rustride*6];
for(blockOffset = 0; blockOffset < lts; blockOffset++)
{
hDim_t temp2 = blockOffset*temp1;
for(modOffset = 0; modOffset < rts; modOffset++)
{
tensorOffset = temp2 + modOffset;
complex_t y1, y2, y3, y4, y5, y6, y7;
y1 = y[tensorOffset];
y2 = y[tensorOffset+rts];
y3 = y[tensorOffset+(rts<<1)];
y4 = y[tensorOffset+3*rts];
y5 = y[tensorOffset+(rts<<2)];
y6 = y[tensorOffset+rts*5];
y7 = y[tensorOffset+rts*6];
y[tensorOffset] = CMPLX_ADD7(y1, y2, y3, y4, y5, y6, y7);
y[tensorOffset+rts] = CMPLX_ADD7(y1, CMPLX_MUL(ru1,y2), CMPLX_MUL(ru2,y3), CMPLX_MUL(ru3,y4), CMPLX_MUL(ru4,y5), CMPLX_MUL(ru5,y6), CMPLX_MUL(ru6,y7));
y[tensorOffset+(rts<<1)] = CMPLX_ADD7(y1, CMPLX_MUL(ru2,y2), CMPLX_MUL(ru4,y3), CMPLX_MUL(ru6,y4), CMPLX_MUL(ru1,y5), CMPLX_MUL(ru3,y6), CMPLX_MUL(ru5,y7));
y[tensorOffset+rts*3] = CMPLX_ADD7(y1, CMPLX_MUL(ru3,y2), CMPLX_MUL(ru6,y3), CMPLX_MUL(ru2,y4), CMPLX_MUL(ru5,y5), CMPLX_MUL(ru1,y6), CMPLX_MUL(ru4,y7));
y[tensorOffset+(rts<<2)] = CMPLX_ADD7(y1, CMPLX_MUL(ru4,y2), CMPLX_MUL(ru1,y3), CMPLX_MUL(ru5,y4), CMPLX_MUL(ru2,y5), CMPLX_MUL(ru6,y6), CMPLX_MUL(ru3,y7));
y[tensorOffset+rts*5] = CMPLX_ADD7(y1, CMPLX_MUL(ru5,y2), CMPLX_MUL(ru3,y3), CMPLX_MUL(ru1,y4), CMPLX_MUL(ru6,y5), CMPLX_MUL(ru4,y6), CMPLX_MUL(ru2,y7));
y[tensorOffset+rts*6] = CMPLX_ADD7(y1, CMPLX_MUL(ru6,y2), CMPLX_MUL(ru5,y3), CMPLX_MUL(ru4,y4), CMPLX_MUL(ru3,y5), CMPLX_MUL(ru2,y6), CMPLX_MUL(ru1,y7));
}
}
}
else
{
hDim_t temp1 = rts*p;
for(blockOffset = 0; blockOffset < lts; blockOffset++)
{
hDim_t temp2 = blockOffset*temp1;
for(modOffset = 0; modOffset < rts; modOffset++)
{
tensorOffset = temp2 + modOffset;
hDim_t row, col;
for(row = 0; row < p; row++)
{
complex_t acc = ((complex_t){0,0});
for(col = 0; col < p; col++)
{
CMPLX_IADD(acc, CMPLX_MUL(y[tensorOffset+col*rts], ru[((col*row) % p)*rustride]));
}
tempSpace[row] = acc;
}
for(row = 0; row < p; row++)
{
y[tensorOffset+rts*row] = tempSpace[row];
}
}
}
}
}
void crtpC (complex_t* y, hDim_t lts, hDim_t rts, hDim_t p, hDim_t rustride, complex_t* ru)
{
hDim_t blockOffset, modOffset, tensorOffset;
if(p == 2)
{
return;
}
else if(p == 3)
{
hDim_t temp1 = rts*2;
complex_t ru1 = ru[rustride];
complex_t ru2 = ru[rustride<<1];
for(blockOffset = 0; blockOffset < lts; blockOffset++)
{
hDim_t temp2 = blockOffset*temp1;
for(modOffset = 0; modOffset < rts; modOffset++)
{
tensorOffset = temp2 + modOffset;
complex_t y1, y2;
y1 = y[tensorOffset];
y2 = y[tensorOffset+rts];
y[tensorOffset] = CMPLX_ADD(y1, CMPLX_MUL(ru1,y2));
y[tensorOffset+rts] = CMPLX_ADD(y1, CMPLX_MUL(ru2,y2));
}
}
}
else if(p == 5)
{
hDim_t temp1 = rts*4;
complex_t ru1 = ru[rustride];
complex_t ru2 = ru[rustride<<1];
complex_t ru3 = ru[rustride*3];
complex_t ru4 = ru[rustride<<2];
for(blockOffset = 0; blockOffset < lts; blockOffset++)
{
hDim_t temp2 = blockOffset*temp1;
for(modOffset = 0; modOffset < rts; modOffset++)
{
tensorOffset = temp2 + modOffset;
complex_t y1, y2, y3, y4;
y1 = y[tensorOffset];
y2 = y[tensorOffset+rts];
y3 = y[tensorOffset+(rts<<1)];
y4 = y[tensorOffset+3*rts];
y[tensorOffset] = CMPLX_ADD4(y1, CMPLX_MUL(ru1,y2), CMPLX_MUL(ru2,y3), CMPLX_MUL(ru3,y4));
y[tensorOffset+rts] = CMPLX_ADD4(y1, CMPLX_MUL(ru2,y2), CMPLX_MUL(ru4,y3), CMPLX_MUL(ru1,y4));
y[tensorOffset+(rts<<1)] = CMPLX_ADD4(y1, CMPLX_MUL(ru3,y2), CMPLX_MUL(ru1,y3), CMPLX_MUL(ru4,y4));
y[tensorOffset+rts*3] = CMPLX_ADD4(y1, CMPLX_MUL(ru4,y2), CMPLX_MUL(ru3,y3), CMPLX_MUL(ru2,y4));
}
}
}
else if(p == 7)
{
hDim_t temp1 = rts*6;
complex_t ru1 = ru[rustride];
complex_t ru2 = ru[rustride<<1];
complex_t ru3 = ru[rustride*3];
complex_t ru4 = ru[rustride<<2];
complex_t ru5 = ru[rustride*5];
complex_t ru6 = ru[rustride*6];
for(blockOffset = 0; blockOffset < lts; blockOffset++)
{
hDim_t temp2 = blockOffset*temp1;
for(modOffset = 0; modOffset < rts; modOffset++)
{
tensorOffset = temp2 + modOffset;
complex_t y1, y2, y3, y4, y5, y6;
y1 = y[tensorOffset];
y2 = y[tensorOffset+rts];
y3 = y[tensorOffset+(rts<<1)];
y4 = y[tensorOffset+3*rts];
y5 = y[tensorOffset+(rts<<2)];
y6 = y[tensorOffset+rts*5];
y[tensorOffset] = CMPLX_ADD6(y1, CMPLX_MUL(ru1,y2), CMPLX_MUL(ru2,y3), CMPLX_MUL(ru3,y4), CMPLX_MUL(ru4,y5), CMPLX_MUL(ru5,y6));
y[tensorOffset+rts] = CMPLX_ADD6(y1, CMPLX_MUL(ru2,y2), CMPLX_MUL(ru4,y3), CMPLX_MUL(ru6,y4), CMPLX_MUL(ru1,y5), CMPLX_MUL(ru3,y6));
y[tensorOffset+(rts<<1)] = CMPLX_ADD6(y1, CMPLX_MUL(ru3,y2), CMPLX_MUL(ru6,y3), CMPLX_MUL(ru2,y4), CMPLX_MUL(ru5,y5), CMPLX_MUL(ru1,y6));
y[tensorOffset+rts*3] = CMPLX_ADD6(y1, CMPLX_MUL(ru4,y2), CMPLX_MUL(ru1,y3), CMPLX_MUL(ru5,y4), CMPLX_MUL(ru2,y5), CMPLX_MUL(ru6,y6));
y[tensorOffset+(rts<<2)] = CMPLX_ADD6(y1, CMPLX_MUL(ru5,y2), CMPLX_MUL(ru3,y3), CMPLX_MUL(ru1,y4), CMPLX_MUL(ru6,y5), CMPLX_MUL(ru4,y6));
y[tensorOffset+rts*5] = CMPLX_ADD6(y1, CMPLX_MUL(ru6,y2), CMPLX_MUL(ru5,y3), CMPLX_MUL(ru4,y4), CMPLX_MUL(ru3,y5), CMPLX_MUL(ru2,y6));
}
}
}
else
{
complex_t* tempSpace = (complex_t*)malloc((p-1)*sizeof(complex_t));
hDim_t temp1 = rts*(p-1);
for(blockOffset = 0; blockOffset < lts; blockOffset++)
{
hDim_t temp2 = blockOffset*temp1;
for(modOffset = 0; modOffset < rts; modOffset++)
{
tensorOffset = temp2 + modOffset;
hDim_t row, col;
for(row = 1; row < p; row++)
{
complex_t acc = ((complex_t){0,0});
for(col = 0; col < p-1; col++)
{
CMPLX_IADD(acc, CMPLX_MUL(y[tensorOffset+col*rts], ru[((col*row) % p)*rustride]));
}
tempSpace[row-1] = acc;
}
for(row = 0; row < p-1; row++)
{
y[tensorOffset+rts*row] = tempSpace[row];
}
}
}
free(tempSpace);
}
}
//takes inverse rus
void crtpinvC (complex_t* y, hDim_t lts, hDim_t rts, hDim_t p, hDim_t rustride, complex_t* ruinv)
{
if(p ==2)
{
// need this case so that we can divide overall by mhat^(-1)
return;
}
else
{
hDim_t tensorOffset,i;
complex_t* tempSpace = (complex_t*)malloc(p*sizeof(complex_t));
hDim_t temp1 = rts*(p-1);
for(hDim_t blockOffset = 0; blockOffset < lts; blockOffset++)
{
hDim_t temp2 = blockOffset*temp1;
for(hDim_t modOffset = 0; modOffset < rts; modOffset++)
{
tensorOffset = temp2 + modOffset;
for(i = 0; i < p-1; i++)
{
complex_t sum = ((complex_t){0,0});
int j;
for(j = 0; j < p-1; j++)
{
int ruIdx = (((j+1)*i) % p)*rustride;
CMPLX_IADD(sum, CMPLX_MUL(y[tensorOffset+j*rts],ruinv[ruIdx]));
}
tempSpace[i] = sum;
}
complex_t shift = ((complex_t){0,0});
for(i = 0; i < p-1; i++)
{
// we were given the inverse rus, so we need to negate the indices
int ruIdx = p-(i+1);
CMPLX_IADD(shift, CMPLX_MUL(y[tensorOffset+i*rts], ruinv[rustride*ruIdx]));
}
for(i = 0; i < p-1; i++)
{
y[tensorOffset+i*rts] = CMPLX_SUB(tempSpace[i], shift);
}
}
}
}
}
void ppDFTC (complex_t* y, hDim_t lts, hDim_t rts, PrimeExponent pe, hDim_t rustride, complex_t* ru)
{
hDim_t p = pe.prime;
hShort_t e = pe.exponent;
if(e == 0)
{
return;
}
hDim_t primeRuStride = rustride*ipow(p,e-1);
complex_t* temp = 0;
if(p >= DFTP_GENERIC_SIZE)
{
temp = (complex_t*)malloc(p*sizeof(complex_t));
}
hShort_t i;
hDim_t ltsScale = ipow(p,e-1);
hDim_t rtsScale = 1;
hDim_t twidRuStride = rustride;
for(i = 0; i < e; i++)
{
hDim_t rtsDim = rts*rtsScale;
dftpC (y, lts*ltsScale, rtsDim, p, primeRuStride, ru, temp);
dftptwidC (y, lts, rtsDim, pe, ltsScale*p, twidRuStride, ru);
ltsScale /= p;
rtsScale *= p;
twidRuStride *= p;
pe.exponent -= 1;
}
if(p >= DFTP_GENERIC_SIZE)
{
free(temp);
}
}
void ppDFTInvC (complex_t* y, hDim_t lts, hDim_t rts, PrimeExponent pe, hDim_t rustride, complex_t* ru)
{
hDim_t p = pe.prime;
hShort_t e = pe.exponent;
if(e == 0)
{
return;
}
hDim_t primeRuStride = rustride*ipow(p,e-1);
complex_t* temp = 0;
if(p >= DFTP_GENERIC_SIZE)
{
temp = (complex_t*)malloc(p*sizeof(complex_t));
}
hShort_t i;
hDim_t ltsScale = 1;
hDim_t rtsScale = ipow(p,e-1);
hDim_t twidRuStride = primeRuStride;
pe.exponent = 1;
for(i = 0; i < e; i++)
{
hDim_t rtsDim = rts*rtsScale;
hDim_t ltsScaleP = ltsScale*p;
dftptwidC (y, lts, rtsDim, pe, ltsScaleP, twidRuStride, ru);
dftpC (y, lts*ltsScale, rtsDim, p, primeRuStride, ru, temp);
ltsScale = ltsScaleP;
rtsScale /= p;
twidRuStride /= p;
pe.exponent += 1;
}
if(p >= DFTP_GENERIC_SIZE)
{
free(temp);
}
}
void ppcrtC (void* y, hDim_t lts, hDim_t rts, PrimeExponent pe, void* ru, hInt_t q)
{
hDim_t p = pe.prime;
hDim_t e = pe.exponent;
#ifdef DEBUG_MODE
ASSERT(e != 0);
#endif
hDim_t mprime = ipow(p,e-1);
#ifdef DEBUG_MODE
printf("rus for p=%" PRId32 ", e=%" PRId16 "\t[", pe.prime, pe.exponent);
hDim_t i;
for(i = 0; i < ipow(p,e); i++) {
printf("(%f,%f),", ((complex_t*)ru)[i].real, ((complex_t*)ru)[i].imag);
}
printf("]\n");
#endif
crtpC ((complex_t*)y, lts*mprime, rts, p, mprime, (complex_t*)ru);
crtTwiddleC ((complex_t*)y, lts, rts, pe, (complex_t*)ru);
pe.exponent -= 1;
ppDFTC ((complex_t*)y, lts, rts*(p-1), pe, p, (complex_t*)ru);
}
void ppcrtinvC (void* y, hDim_t lts, hDim_t rts, PrimeExponent pe, void* ru, hInt_t q)
{
hDim_t p = pe.prime;
hDim_t e = pe.exponent;
#ifdef DEBUG_MODE
ASSERT(e != 0);
#endif
hDim_t mprime = ipow(p,e-1);
pe.exponent -= 1;
ppDFTInvC ((complex_t*)y, lts, rts*(p-1), pe, p, (complex_t*)ru);
pe.exponent += 1;
crtTwiddleC ((complex_t*)y, lts, rts, pe, (complex_t*)ru);
crtpinvC ((complex_t*)y, lts*mprime, rts, p, mprime, (complex_t*)ru);
}
void tensorCRTC (complex_t* y, hDim_t totm, PrimeExponent* peArr, hShort_t sizeOfPE, complex_t** ru)
{
#ifdef STATS
struct timespec s1,t1;
crtCCtr++;
clock_gettime(CLOCK_REALTIME, &s1);
#endif
#ifdef DEBUG_MODE
printf("\n\nEntered tensorCRTC\ttotm=%" PRId32 "\tnumFacts=%" PRId16 "\n[", totm, sizeOfPE);
hDim_t j;
for(j = 0; j < totm; j++) {
printf("(%f,%f),", y[j].real, y[j].imag);
}
printf("]\n[");
for(j = 0; j < sizeOfPE; j++) {
printf("(%" PRId32 ",%" PRId16 "),", peArr[j].prime, peArr[j].exponent);
}
printf("]\n");
#endif
void** rus = (void**)malloc(sizeOfPE*sizeof(void*));
hShort_t i;
for(i = 0; i < sizeOfPE; i++)
{
rus[i] = (void*) (ru[i]);
}
tensorFuserCRT (y, ppcrtC, totm, peArr, sizeOfPE, rus, 0);
free(rus);
#ifdef STATS
clock_gettime(CLOCK_REALTIME, &t1);
crtCTime = tsAdd(crtCTime, tsSubtract(t1,s1));
#endif
}
//takes inverse rus
void tensorCRTInvC (complex_t* y, hDim_t totm, PrimeExponent* peArr, hShort_t sizeOfPE, complex_t** ruinv, double mhatInv)
{
#ifdef STATS
struct timespec s1,t1;
crtInvCCtr++;
clock_gettime(CLOCK_REALTIME, &s1);
#endif
hDim_t i;
void** rus = (void**)malloc(sizeOfPE*sizeof(void*));
for(i = 0; i < sizeOfPE; i++)
{
rus[i] = (void*) (ruinv[i]);
}
tensorFuserCRT (y, ppcrtinvC, totm, peArr, sizeOfPE, rus, 0);
complex_t minvcmplx = ((complex_t){mhatInv,0});
for (hDim_t j = 0; j < totm; j++)
{
CMPLX_IMUL(y[j], minvcmplx);
}
free(rus);
#ifdef STATS
clock_gettime(CLOCK_REALTIME, &t1);
crtInvCTime = tsAdd(crtInvCTime, tsSubtract(t1,s1));
#endif
}