/************************************************************************* This subroutine precomputes data for complex Rader's FFT and writes them to array PrecR[] at specified offset. It is responsibility of the caller to make sure that PrecR[] is large enough. INPUT PARAMETERS: N - original size of the transform (before reduction to N-1) RQ - primitive root modulo N RIQ - inverse of primitive root modulo N PrecR - preallocated array Offs - offset OUTPUT PARAMETERS: PrecR - data at Offs:Offs+2*(N-1)-1 store FFT of Rader's factors, other parts of PrecR are unchanged. NOTE: this function performs internal (N-1)-point FFT. It allocates temporary plan which is destroyed after leaving this function. -- ALGLIB -- Copyright 08.05.2013 by Bochkanov Sergey *************************************************************************/ private static void ftprecomputeradersfft(int n, int rq, int riq, double[] precr, int offs) { int q = 0; fasttransformplan plan = new fasttransformplan(); int kiq = 0; double v = 0; // // Fill PrecR with Rader factors, perform FFT // kiq = 1; for (q = 0; q <= n - 2; q++) { v = -(2 * Math.PI * kiq / n); precr[offs + 2 * q + 0] = Math.Cos(v); precr[offs + 2 * q + 1] = Math.Sin(v); kiq = kiq * riq % n; } ftcomplexfftplan(n - 1, 1, plan); ftapplysubplan(plan, 0, precr, offs, 0, plan.buffer, 1); }
/************************************************************************* This subroutine applies complex Rader's FFT to input/output array A. INPUT PARAMETERS: A - array, must be large enough for plan to work ABase - base offset in array A, this value points to start of subarray whose length is equal to length of the plan AOffset - offset with respect to ABase, 0<=AOffset<PlanLength. This is an offset within large PlanLength-subarray of the chunk to process. OperandsCnt - number of repeated operands (length N each) N - original data length (measured in complex numbers) SubPlan - position of the (N-1)-point FFT subplan which is used by transformation RQ - primitive root modulo N RIQ - inverse of primitive root modulo N PrecOffs - offset of the precomputed data for the plan Buf - temporary array OUTPUT PARAMETERS: A - transformed array -- ALGLIB -- Copyright 05.04.2013 by Bochkanov Sergey *************************************************************************/ private static void ftradersfft(fasttransformplan plan, double[] a, int abase, int aoffset, int operandscnt, int n, int subplan, int rq, int riq, int precoffs, double[] buf) { int opidx = 0; int i = 0; int q = 0; int kq = 0; int kiq = 0; double x0 = 0; double y0 = 0; int p0 = 0; int p1 = 0; double ax = 0; double ay = 0; double bx = 0; double by = 0; double rx = 0; double ry = 0; alglib.ap.assert(operandscnt >= 1, "FTApplyComplexRefFFT: OperandsCnt<1"); // // Process operands // for (opidx = 0; opidx <= operandscnt - 1; opidx++) { // // fill QA // kq = 1; p0 = abase + aoffset + opidx * n * 2; p1 = aoffset + opidx * n * 2; rx = a[p0 + 0]; ry = a[p0 + 1]; x0 = rx; y0 = ry; for (q = 0; q <= n - 2; q++) { ax = a[p0 + 2 * kq + 0]; ay = a[p0 + 2 * kq + 1]; buf[p1 + 0] = ax; buf[p1 + 1] = ay; rx = rx + ax; ry = ry + ay; kq = kq * rq % n; p1 = p1 + 2; } p0 = abase + aoffset + opidx * n * 2; p1 = aoffset + opidx * n * 2; for (q = 0; q <= n - 2; q++) { a[p0] = buf[p1]; a[p0 + 1] = buf[p1 + 1]; p0 = p0 + 2; p1 = p1 + 2; } // // Convolution // ftapplysubplan(plan, subplan, a, abase, aoffset + opidx * n * 2, buf, 1); p0 = abase + aoffset + opidx * n * 2; p1 = precoffs; for (i = 0; i <= n - 2; i++) { ax = a[p0 + 0]; ay = a[p0 + 1]; bx = plan.precr[p1 + 0]; by = plan.precr[p1 + 1]; a[p0 + 0] = ax * bx - ay * by; a[p0 + 1] = -(ax * by + ay * bx); p0 = p0 + 2; p1 = p1 + 2; } ftapplysubplan(plan, subplan, a, abase, aoffset + opidx * n * 2, buf, 1); p0 = abase + aoffset + opidx * n * 2; for (i = 0; i <= n - 2; i++) { a[p0 + 0] = a[p0 + 0] / (n - 1); a[p0 + 1] = -(a[p0 + 1] / (n - 1)); p0 = p0 + 2; } // // Result // buf[aoffset + opidx * n * 2 + 0] = rx; buf[aoffset + opidx * n * 2 + 1] = ry; kiq = 1; p0 = aoffset + opidx * n * 2; p1 = abase + aoffset + opidx * n * 2; for (q = 0; q <= n - 2; q++) { buf[p0 + 2 * kiq + 0] = x0 + a[p1 + 0]; buf[p0 + 2 * kiq + 1] = y0 + a[p1 + 1]; kiq = kiq * riq % n; p1 = p1 + 2; } p0 = abase + aoffset + opidx * n * 2; p1 = aoffset + opidx * n * 2; for (q = 0; q <= n - 1; q++) { a[p0] = buf[p1]; a[p0 + 1] = buf[p1 + 1]; p0 = p0 + 2; p1 = p1 + 2; } } }
/************************************************************************* This subroutine precomputes data for complex Bluestein's FFT and writes them to array PrecR[] at specified offset. It is responsibility of the caller to make sure that PrecR[] is large enough. INPUT PARAMETERS: N - original size of the transform M - size of the "padded" Bluestein's transform PrecR - preallocated array Offs - offset OUTPUT PARAMETERS: PrecR - data at Offs:Offs+4*M-1 are modified: * PrecR[Offs:Offs+2*M-1] stores Z[k]=exp(i*pi*k^2/N) * PrecR[Offs+2*M:Offs+4*M-1] stores FFT of the Z Other parts of PrecR are unchanged. NOTE: this function performs internal M-point FFT. It allocates temporary plan which is destroyed after leaving this function. -- ALGLIB -- Copyright 08.05.2013 by Bochkanov Sergey *************************************************************************/ private static void ftprecomputebluesteinsfft(int n, int m, double[] precr, int offs) { int i = 0; double bx = 0; double by = 0; fasttransformplan plan = new fasttransformplan(); // // Fill first half of PrecR with b[k] = exp(i*pi*k^2/N) // for (i = 0; i <= 2 * m - 1; i++) { precr[offs + i] = 0; } for (i = 0; i <= n - 1; i++) { bx = Math.Cos(Math.PI / n * i * i); by = Math.Sin(Math.PI / n * i * i); precr[offs + 2 * i + 0] = bx; precr[offs + 2 * i + 1] = by; precr[offs + 2 * ((m - i) % m) + 0] = bx; precr[offs + 2 * ((m - i) % m) + 1] = by; } // // Precomputed FFT // ftcomplexfftplan(m, 1, plan); for (i = 0; i <= 2 * m - 1; i++) { precr[offs + 2 * m + i] = precr[offs + i]; } ftapplysubplan(plan, 0, precr, offs + 2 * m, 0, plan.buffer, 1); }
/************************************************************************* This subroutine applies complex Bluestein's FFT to input/output array A. INPUT PARAMETERS: Plan - transformation plan A - array, must be large enough for plan to work ABase - base offset in array A, this value points to start of subarray whose length is equal to length of the plan AOffset - offset with respect to ABase, 0<=AOffset<PlanLength. This is an offset within large PlanLength-subarray of the chunk to process. OperandsCnt - number of repeated operands (length N each) N - original data length (measured in complex numbers) M - padded data length (measured in complex numbers) PrecOffs - offset of the precomputed data for the plan SubPlan - position of the length-M FFT subplan which is used by transformation BufA - temporary buffer, at least 2*M elements BufB - temporary buffer, at least 2*M elements BufC - temporary buffer, at least 2*M elements BufD - temporary buffer, at least 2*M elements OUTPUT PARAMETERS: A - transformed array -- ALGLIB -- Copyright 05.04.2013 by Bochkanov Sergey *************************************************************************/ private static void ftbluesteinsfft(fasttransformplan plan, double[] a, int abase, int aoffset, int operandscnt, int n, int m, int precoffs, int subplan, double[] bufa, double[] bufb, double[] bufc, double[] bufd) { int op = 0; int i = 0; double x = 0; double y = 0; double bx = 0; double by = 0; double ax = 0; double ay = 0; double rx = 0; double ry = 0; int p0 = 0; int p1 = 0; int p2 = 0; for (op = 0; op <= operandscnt - 1; op++) { // // Multiply A by conj(Z), store to buffer. // Pad A by zeros. // // NOTE: Z[k]=exp(i*pi*k^2/N) // p0 = abase + aoffset + op * 2 * n; p1 = precoffs; for (i = 0; i <= n - 1; i++) { x = a[p0 + 0]; y = a[p0 + 1]; bx = plan.precr[p1 + 0]; by = -plan.precr[p1 + 1]; bufa[2 * i + 0] = x * bx - y * by; bufa[2 * i + 1] = x * by + y * bx; p0 = p0 + 2; p1 = p1 + 2; } for (i = 2 * n; i <= 2 * m - 1; i++) { bufa[i] = 0; } // // Perform convolution of A and Z (using precomputed // FFT of Z stored in Plan structure). // ftapplysubplan(plan, subplan, bufa, 0, 0, bufc, 1); p0 = 0; p1 = precoffs + 2 * m; for (i = 0; i <= m - 1; i++) { ax = bufa[p0 + 0]; ay = bufa[p0 + 1]; bx = plan.precr[p1 + 0]; by = plan.precr[p1 + 1]; bufa[p0 + 0] = ax * bx - ay * by; bufa[p0 + 1] = -(ax * by + ay * bx); p0 = p0 + 2; p1 = p1 + 2; } ftapplysubplan(plan, subplan, bufa, 0, 0, bufc, 1); // // Post processing: // A:=conj(Z)*conj(A)/M // Here conj(A)/M corresponds to last stage of inverse DFT, // and conj(Z) comes from Bluestein's FFT algorithm. // p0 = precoffs; p1 = 0; p2 = abase + aoffset + op * 2 * n; for (i = 0; i <= n - 1; i++) { bx = plan.precr[p0 + 0]; by = plan.precr[p0 + 1]; rx = bufa[p1 + 0] / m; ry = -(bufa[p1 + 1] / m); a[p2 + 0] = rx * bx - ry * -by; a[p2 + 1] = rx * -by + ry * bx; p0 = p0 + 2; p1 = p1 + 2; p2 = p2 + 2; } } }
/************************************************************************* Same as FTPushEntry(), but sets Param0, Param1, Param2 and Param3. This function pushes one more entry to the plan. It resized Entries matrix if needed. INPUT PARAMETERS: Plan - plan (generated so far) RowPtr - index which points to past-the-last entry generated so far EType - entry type EOpCnt - operands count EOpSize - operand size EMcvSize - microvector size EParam0 - parameter 0 EParam1 - parameter 1 EParam2 - parameter 2 EParam3 - parameter 3 OUTPUT PARAMETERS: Plan - updated plan RowPtr - updated pointer -- ALGLIB -- Copyright 05.04.2013 by Bochkanov Sergey *************************************************************************/ private static void ftpushentry4(fasttransformplan plan, ref int rowptr, int etype, int eopcnt, int eopsize, int emcvsize, int eparam0, int eparam1, int eparam2, int eparam3) { if (rowptr >= alglib.ap.rows(plan.entries)) { apserv.imatrixresize(ref plan.entries, Math.Max(2 * alglib.ap.rows(plan.entries), 1), colscnt); } plan.entries[rowptr, coltype] = etype; plan.entries[rowptr, coloperandscnt] = eopcnt; plan.entries[rowptr, coloperandsize] = eopsize; plan.entries[rowptr, colmicrovectorsize] = emcvsize; plan.entries[rowptr, colparam0] = eparam0; plan.entries[rowptr, colparam1] = eparam1; plan.entries[rowptr, colparam2] = eparam2; plan.entries[rowptr, colparam3] = eparam3; rowptr = rowptr + 1; }
/************************************************************************* This subroutine applies subplan to input/output array A. INPUT PARAMETERS: Plan - transformation plan SubPlan - subplan index A - array, must be large enough for plan to work ABase - base offset in array A, this value points to start of subarray whose length is equal to length of the plan AOffset - offset with respect to ABase, 0<=AOffset<PlanLength. This is an offset within large PlanLength-subarray of the chunk to process. Buf - temporary buffer whose length is equal to plan length (without taking into account RepCnt) or larger. OffsBuf - offset in the buffer array RepCnt - repetition count (transformation is repeatedly applied to subsequent subarrays) OUTPUT PARAMETERS: Plan - plan (temporary buffers can be modified, plan itself is unchanged and can be reused) A - transformed array -- ALGLIB -- Copyright 05.04.2013 by Bochkanov Sergey *************************************************************************/ private static void ftapplysubplan(fasttransformplan plan, int subplan, double[] a, int abase, int aoffset, double[] buf, int repcnt) { int rowidx = 0; int i = 0; int n1 = 0; int n2 = 0; int operation = 0; int operandscnt = 0; int operandsize = 0; int microvectorsize = 0; int param0 = 0; int param1 = 0; int parentsize = 0; int childsize = 0; int chunksize = 0; int lastchunksize = 0; apserv.srealarray bufa = null; apserv.srealarray bufb = null; apserv.srealarray bufc = null; apserv.srealarray bufd = null; alglib.ap.assert(plan.entries[subplan, coltype] == opstart, "FTApplySubPlan: incorrect subplan header"); rowidx = subplan + 1; while (plan.entries[rowidx, coltype] != opend) { operation = plan.entries[rowidx, coltype]; operandscnt = repcnt * plan.entries[rowidx, coloperandscnt]; operandsize = plan.entries[rowidx, coloperandsize]; microvectorsize = plan.entries[rowidx, colmicrovectorsize]; param0 = plan.entries[rowidx, colparam0]; param1 = plan.entries[rowidx, colparam1]; apserv.touchint(ref param1); // // Process "jump" operation // if (operation == opjmp) { rowidx = rowidx + plan.entries[rowidx, colparam0]; continue; } // // Process "parallel call" operation: // * we perform initial check for consistency between parent and child plans // * we call FTSplitAndApplyParallelPlan(), which splits parallel plan into // several parallel tasks // if (operation == opparallelcall) { parentsize = operandsize * microvectorsize; childsize = plan.entries[rowidx + param0, coloperandscnt] * plan.entries[rowidx + param0, coloperandsize] * plan.entries[rowidx + param0, colmicrovectorsize]; alglib.ap.assert(plan.entries[rowidx + param0, coltype] == opstart, "FTApplySubPlan: incorrect child subplan header"); alglib.ap.assert(parentsize == childsize, "FTApplySubPlan: incorrect child subplan header"); chunksize = Math.Max(recursivethreshold / childsize, 1); lastchunksize = operandscnt % chunksize; if (lastchunksize == 0) { lastchunksize = chunksize; } i = 0; while (i < operandscnt) { chunksize = Math.Min(chunksize, operandscnt - i); ftapplysubplan(plan, rowidx + param0, a, abase, aoffset + i * childsize, buf, chunksize); i = i + chunksize; } rowidx = rowidx + 1; continue; } // // Process "reference complex FFT" operation // if (operation == opcomplexreffft) { ftapplycomplexreffft(a, abase + aoffset, operandscnt, operandsize, microvectorsize, buf); rowidx = rowidx + 1; continue; } // // Process "codelet FFT" operation // if (operation == opcomplexcodeletfft) { ftapplycomplexcodeletfft(a, abase + aoffset, operandscnt, operandsize, microvectorsize); rowidx = rowidx + 1; continue; } // // Process "integrated codelet FFT" operation // if (operation == opcomplexcodelettwfft) { ftapplycomplexcodelettwfft(a, abase + aoffset, operandscnt, operandsize, microvectorsize); rowidx = rowidx + 1; continue; } // // Process Bluestein's FFT operation // if (operation == opbluesteinsfft) { alglib.ap.assert(microvectorsize == 2, "FTApplySubPlan: microvectorsize!=2 for Bluesteins FFT"); alglib.smp.ae_shared_pool_retrieve(plan.bluesteinpool, ref bufa); alglib.smp.ae_shared_pool_retrieve(plan.bluesteinpool, ref bufb); alglib.smp.ae_shared_pool_retrieve(plan.bluesteinpool, ref bufc); alglib.smp.ae_shared_pool_retrieve(plan.bluesteinpool, ref bufd); ftbluesteinsfft(plan, a, abase, aoffset, operandscnt, operandsize, plan.entries[rowidx, colparam0], plan.entries[rowidx, colparam2], rowidx + plan.entries[rowidx, colparam1], bufa.val, bufb.val, bufc.val, bufd.val); alglib.smp.ae_shared_pool_recycle(plan.bluesteinpool, ref bufa); alglib.smp.ae_shared_pool_recycle(plan.bluesteinpool, ref bufb); alglib.smp.ae_shared_pool_recycle(plan.bluesteinpool, ref bufc); alglib.smp.ae_shared_pool_recycle(plan.bluesteinpool, ref bufd); rowidx = rowidx + 1; continue; } // // Process Rader's FFT // if (operation == opradersfft) { ftradersfft(plan, a, abase, aoffset, operandscnt, operandsize, rowidx + plan.entries[rowidx, colparam0], plan.entries[rowidx, colparam1], plan.entries[rowidx, colparam2], plan.entries[rowidx, colparam3], buf); rowidx = rowidx + 1; continue; } // // Process "complex twiddle factors" operation // if (operation == opcomplexfftfactors) { alglib.ap.assert(microvectorsize == 2, "FTApplySubPlan: MicrovectorSize<>1"); n1 = plan.entries[rowidx, colparam0]; n2 = operandsize / n1; for (i = 0; i <= operandscnt - 1; i++) { ffttwcalc(a, abase + aoffset + i * operandsize * 2, n1, n2); } rowidx = rowidx + 1; continue; } // // Process "complex transposition" operation // if (operation == opcomplextranspose) { alglib.ap.assert(microvectorsize == 2, "FTApplySubPlan: MicrovectorSize<>1"); n1 = plan.entries[rowidx, colparam0]; n2 = operandsize / n1; for (i = 0; i <= operandscnt - 1; i++) { internalcomplexlintranspose(a, n1, n2, abase + aoffset + i * operandsize * 2, buf); } rowidx = rowidx + 1; continue; } // // Error // alglib.ap.assert(false, "FTApplySubPlan: unexpected plan type"); } }
/************************************************************************* This function pushes one more entry to the plan. It resizes Entries matrix if needed. INPUT PARAMETERS: Plan - plan (generated so far) RowPtr - index which points to past-the-last entry generated so far EType - entry type EOpCnt - operands count EOpSize - operand size EMcvSize - microvector size EParam0 - parameter 0 OUTPUT PARAMETERS: Plan - updated plan RowPtr - updated pointer NOTE: Param1 is set to -1. -- ALGLIB -- Copyright 05.04.2013 by Bochkanov Sergey *************************************************************************/ private static void ftpushentry(fasttransformplan plan, ref int rowptr, int etype, int eopcnt, int eopsize, int emcvsize, int eparam0) { ftpushentry2(plan, ref rowptr, etype, eopcnt, eopsize, emcvsize, eparam0, -1); }
/************************************************************************* Recurrent function called by FTComplexFFTPlan() and other functions. It recursively builds transformation plan INPUT PARAMETERS: N - FFT length (in complex numbers), N>=1 K - number of repetitions, K>=1 ChildPlan - if True, plan generator inserts OpStart/opEnd in the plan header/footer. TopmostPlan - if True, plan generator assumes that it is topmost plan: * it may use global buffer for transpositions and there is no other plan which executes in parallel RowPtr - index which points to past-the-last entry generated so far BluesteinSize- amount of storage (in real numbers) required for Bluestein buffer PrecRPtr - pointer to unused part of precomputed real buffer (Plan.PrecR): * when this function stores some data to precomputed buffer, it advances pointer. * it is responsibility of the function to assert that Plan.PrecR has enough space to store data before actually writing to buffer. * it is responsibility of the caller to allocate enough space before calling this function PrecIPtr - pointer to unused part of precomputed integer buffer (Plan.PrecI): * when this function stores some data to precomputed buffer, it advances pointer. * it is responsibility of the function to assert that Plan.PrecR has enough space to store data before actually writing to buffer. * it is responsibility of the caller to allocate enough space before calling this function Plan - plan (generated so far) OUTPUT PARAMETERS: RowPtr - updated pointer (advanced by number of entries generated by function) BluesteinSize- updated amount (may be increased, but may never be decreased) NOTE: in case TopmostPlan is True, ChildPlan is also must be True. -- ALGLIB -- Copyright 05.04.2013 by Bochkanov Sergey *************************************************************************/ private static void ftcomplexfftplanrec(int n, int k, bool childplan, bool topmostplan, ref int rowptr, ref int bluesteinsize, ref int precrptr, ref int preciptr, fasttransformplan plan) { apserv.srealarray localbuf = new apserv.srealarray(); int m = 0; int n1 = 0; int n2 = 0; int gq = 0; int giq = 0; int row0 = 0; int row1 = 0; int row2 = 0; int row3 = 0; alglib.ap.assert(n > 0, "FTComplexFFTPlan: N<=0"); alglib.ap.assert(k > 0, "FTComplexFFTPlan: K<=0"); alglib.ap.assert(!topmostplan || childplan, "FTComplexFFTPlan: ChildPlan is inconsistent with TopmostPlan"); // // Try to generate "topmost" plan // if (topmostplan && n > recursivethreshold) { ftfactorize(n, false, ref n1, ref n2); if (n1 * n2 == 0) { // // Handle prime-factor FFT with Bluestein's FFT. // Determine size of Bluestein's buffer. // m = ftbasefindsmooth(2 * n - 1); bluesteinsize = Math.Max(2 * m, bluesteinsize); // // Generate plan // ftpushentry2(plan, ref rowptr, opstart, k, n, 2, -1, ftoptimisticestimate(n)); ftpushentry4(plan, ref rowptr, opbluesteinsfft, k, n, 2, m, 2, precrptr, 0); row0 = rowptr; ftpushentry(plan, ref rowptr, opjmp, 0, 0, 0, 0); ftcomplexfftplanrec(m, 1, true, true, ref rowptr, ref bluesteinsize, ref precrptr, ref preciptr, plan); row1 = rowptr; plan.entries[row0, colparam0] = row1 - row0; ftpushentry(plan, ref rowptr, opend, k, n, 2, 0); // // Fill precomputed buffer // ftprecomputebluesteinsfft(n, m, plan.precr, precrptr); // // Update pointer to the precomputed area // precrptr = precrptr + 4 * m; } else { // // Handle composite FFT with recursive Cooley-Tukey which // uses global buffer instead of local one. // ftpushentry2(plan, ref rowptr, opstart, k, n, 2, -1, ftoptimisticestimate(n)); ftpushentry(plan, ref rowptr, opcomplextranspose, k, n, 2, n1); row0 = rowptr; ftpushentry2(plan, ref rowptr, opparallelcall, k * n2, n1, 2, 0, ftoptimisticestimate(n)); ftpushentry(plan, ref rowptr, opcomplexfftfactors, k, n, 2, n1); ftpushentry(plan, ref rowptr, opcomplextranspose, k, n, 2, n2); row2 = rowptr; ftpushentry2(plan, ref rowptr, opparallelcall, k * n1, n2, 2, 0, ftoptimisticestimate(n)); ftpushentry(plan, ref rowptr, opcomplextranspose, k, n, 2, n1); ftpushentry(plan, ref rowptr, opend, k, n, 2, 0); row1 = rowptr; ftcomplexfftplanrec(n1, 1, true, false, ref rowptr, ref bluesteinsize, ref precrptr, ref preciptr, plan); plan.entries[row0, colparam0] = row1 - row0; row3 = rowptr; ftcomplexfftplanrec(n2, 1, true, false, ref rowptr, ref bluesteinsize, ref precrptr, ref preciptr, plan); plan.entries[row2, colparam0] = row3 - row2; } return; } // // Prepare "non-topmost" plan: // * calculate factorization // * use local (shared) buffer // * update buffer size - ANY plan will need at least // 2*N temporaries, additional requirements can be // applied later // ftfactorize(n, false, ref n1, ref n2); // // Handle FFT's with N1*N2=0: either small-N or prime-factor // if (n1 * n2 == 0) { if (n <= maxradix) { // // Small-N FFT // if (childplan) { ftpushentry2(plan, ref rowptr, opstart, k, n, 2, -1, ftoptimisticestimate(n)); } ftpushentry(plan, ref rowptr, opcomplexcodeletfft, k, n, 2, 0); if (childplan) { ftpushentry(plan, ref rowptr, opend, k, n, 2, 0); } return; } if (n <= raderthreshold) { // // Handle prime-factor FFT's with Rader's FFT // m = n - 1; if (childplan) { ftpushentry2(plan, ref rowptr, opstart, k, n, 2, -1, ftoptimisticestimate(n)); } ntheory.findprimitiverootandinverse(n, ref gq, ref giq); ftpushentry4(plan, ref rowptr, opradersfft, k, n, 2, 2, gq, giq, precrptr); ftprecomputeradersfft(n, gq, giq, plan.precr, precrptr); precrptr = precrptr + 2 * (n - 1); row0 = rowptr; ftpushentry(plan, ref rowptr, opjmp, 0, 0, 0, 0); ftcomplexfftplanrec(m, 1, true, false, ref rowptr, ref bluesteinsize, ref precrptr, ref preciptr, plan); row1 = rowptr; plan.entries[row0, colparam0] = row1 - row0; if (childplan) { ftpushentry(plan, ref rowptr, opend, k, n, 2, 0); } } else { // // Handle prime-factor FFT's with Bluestein's FFT // m = ftbasefindsmooth(2 * n - 1); bluesteinsize = Math.Max(2 * m, bluesteinsize); if (childplan) { ftpushentry2(plan, ref rowptr, opstart, k, n, 2, -1, ftoptimisticestimate(n)); } ftpushentry4(plan, ref rowptr, opbluesteinsfft, k, n, 2, m, 2, precrptr, 0); ftprecomputebluesteinsfft(n, m, plan.precr, precrptr); precrptr = precrptr + 4 * m; row0 = rowptr; ftpushentry(plan, ref rowptr, opjmp, 0, 0, 0, 0); ftcomplexfftplanrec(m, 1, true, false, ref rowptr, ref bluesteinsize, ref precrptr, ref preciptr, plan); row1 = rowptr; plan.entries[row0, colparam0] = row1 - row0; if (childplan) { ftpushentry(plan, ref rowptr, opend, k, n, 2, 0); } } return; } // // Handle Cooley-Tukey FFT with small N1 // if (n1 <= maxradix) { // // Specialized transformation for small N1: // * N2 short inplace FFT's, each N1-point, with integrated twiddle factors // * N1 long FFT's // * final transposition // if (childplan) { ftpushentry2(plan, ref rowptr, opstart, k, n, 2, -1, ftoptimisticestimate(n)); } ftpushentry(plan, ref rowptr, opcomplexcodelettwfft, k, n1, 2 * n2, 0); ftcomplexfftplanrec(n2, k * n1, false, false, ref rowptr, ref bluesteinsize, ref precrptr, ref preciptr, plan); ftpushentry(plan, ref rowptr, opcomplextranspose, k, n, 2, n1); if (childplan) { ftpushentry(plan, ref rowptr, opend, k, n, 2, 0); } return; } // // Handle general Cooley-Tukey FFT, either "flat" or "recursive" // if (n <= recursivethreshold) { // // General code for large N1/N2, "flat" version without explicit recurrence // (nested subplans are inserted directly into the body of the plan) // if (childplan) { ftpushentry2(plan, ref rowptr, opstart, k, n, 2, -1, ftoptimisticestimate(n)); } ftpushentry(plan, ref rowptr, opcomplextranspose, k, n, 2, n1); ftcomplexfftplanrec(n1, k * n2, false, false, ref rowptr, ref bluesteinsize, ref precrptr, ref preciptr, plan); ftpushentry(plan, ref rowptr, opcomplexfftfactors, k, n, 2, n1); ftpushentry(plan, ref rowptr, opcomplextranspose, k, n, 2, n2); ftcomplexfftplanrec(n2, k * n1, false, false, ref rowptr, ref bluesteinsize, ref precrptr, ref preciptr, plan); ftpushentry(plan, ref rowptr, opcomplextranspose, k, n, 2, n1); if (childplan) { ftpushentry(plan, ref rowptr, opend, k, n, 2, 0); } } else { // // General code for large N1/N2, "recursive" version - nested subplans // are separated from the plan body. // // Generate parent plan. // if (childplan) { ftpushentry2(plan, ref rowptr, opstart, k, n, 2, -1, ftoptimisticestimate(n)); } ftpushentry(plan, ref rowptr, opcomplextranspose, k, n, 2, n1); row0 = rowptr; ftpushentry2(plan, ref rowptr, opparallelcall, k * n2, n1, 2, 0, ftoptimisticestimate(n)); ftpushentry(plan, ref rowptr, opcomplexfftfactors, k, n, 2, n1); ftpushentry(plan, ref rowptr, opcomplextranspose, k, n, 2, n2); row2 = rowptr; ftpushentry2(plan, ref rowptr, opparallelcall, k * n1, n2, 2, 0, ftoptimisticestimate(n)); ftpushentry(plan, ref rowptr, opcomplextranspose, k, n, 2, n1); if (childplan) { ftpushentry(plan, ref rowptr, opend, k, n, 2, 0); } // // Generate child subplans, insert refence to parent plans // row1 = rowptr; ftcomplexfftplanrec(n1, 1, true, false, ref rowptr, ref bluesteinsize, ref precrptr, ref preciptr, plan); plan.entries[row0, colparam0] = row1 - row0; row3 = rowptr; ftcomplexfftplanrec(n2, 1, true, false, ref rowptr, ref bluesteinsize, ref precrptr, ref preciptr, plan); plan.entries[row2, colparam0] = row3 - row2; } }
/************************************************************************* This subroutine applies transformation plan to input/output array A. INPUT PARAMETERS: Plan - transformation plan A - array, must be large enough for plan to work OffsA - offset of the subarray to process RepCnt - repetition count (transformation is repeatedly applied to subsequent subarrays) OUTPUT PARAMETERS: Plan - plan (temporary buffers can be modified, plan itself is unchanged and can be reused) A - transformed array -- ALGLIB -- Copyright 05.04.2013 by Bochkanov Sergey *************************************************************************/ public static void ftapplyplan(fasttransformplan plan, double[] a, int offsa, int repcnt) { int plansize = 0; int i = 0; plansize = plan.entries[0, coloperandscnt] * plan.entries[0, coloperandsize] * plan.entries[0, colmicrovectorsize]; for (i = 0; i <= repcnt - 1; i++) { ftapplysubplan(plan, 0, a, offsa + plansize * i, 0, plan.buffer, 1); } }
/************************************************************************* This subroutine generates FFT plan for K complex FFT's with length N each. INPUT PARAMETERS: N - FFT length (in complex numbers), N>=1 K - number of repetitions, K>=1 OUTPUT PARAMETERS: Plan - plan -- ALGLIB -- Copyright 05.04.2013 by Bochkanov Sergey *************************************************************************/ public static void ftcomplexfftplan(int n, int k, fasttransformplan plan) { apserv.srealarray bluesteinbuf = new apserv.srealarray(); int rowptr = 0; int bluesteinsize = 0; int precrptr = 0; int preciptr = 0; int precrsize = 0; int precisize = 0; // // Initial check for parameters // alglib.ap.assert(n > 0, "FTComplexFFTPlan: N<=0"); alglib.ap.assert(k > 0, "FTComplexFFTPlan: K<=0"); // // Determine required sizes of precomputed real and integer // buffers. This stage of code is highly dependent on internals // of FTComplexFFTPlanRec() and must be kept synchronized with // possible changes in internals of plan generation function. // // Buffer size is determined as follows: // * N is factorized // * we factor out anything which is less or equal to MaxRadix // * prime factor F>RaderThreshold requires 4*FTBaseFindSmooth(2*F-1) // real entries to store precomputed Quantities for Bluestein's // transformation // * prime factor F<=RaderThreshold does NOT require // precomputed storage // precrsize = 0; precisize = 0; ftdeterminespacerequirements(n, ref precrsize, ref precisize); if (precrsize > 0) { plan.precr = new double[precrsize]; } if (precisize > 0) { plan.preci = new double[precisize]; } // // Generate plan // rowptr = 0; precrptr = 0; preciptr = 0; bluesteinsize = 1; plan.buffer = new double[2 * n * k]; ftcomplexfftplanrec(n, k, true, true, ref rowptr, ref bluesteinsize, ref precrptr, ref preciptr, plan); bluesteinbuf.val = new double[bluesteinsize]; alglib.smp.ae_shared_pool_set_seed(plan.bluesteinpool, bluesteinbuf); // // Check that actual amount of precomputed space used by transformation // plan is EXACTLY equal to amount of space allocated by us. // alglib.ap.assert(precrptr == precrsize, "FTComplexFFTPlan: internal error (PrecRPtr<>PrecRSize)"); alglib.ap.assert(preciptr == precisize, "FTComplexFFTPlan: internal error (PrecRPtr<>PrecRSize)"); }
public override alglib.apobject make_copy() { fasttransformplan _result = new fasttransformplan(); _result.entries = (int[,])entries.Clone(); _result.buffer = (double[])buffer.Clone(); _result.precr = (double[])precr.Clone(); _result.preci = (double[])preci.Clone(); _result.bluesteinpool = (alglib.smp.shared_pool)bluesteinpool.make_copy(); return _result; }