/************************************************************************* 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 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)"); }