1 /*
2  *  Copyright (c) 2003-2004, Mark Borgerding. All rights reserved.
3  *  This file is part of KISS FFT - https://github.com/mborgerding/kissfft
4  *
5  *  SPDX-License-Identifier: BSD-3-Clause
6  *  See COPYING file for more information.
7  */
8 
9 #include "kiss_fftnd.h"
10 #include "_kiss_fft_guts.h"
11 
12 struct kiss_fftnd_state{
13     int dimprod; /* dimsum would be mighty tasty right now */
14     int ndims;
15     int *dims;
16     kiss_fft_cfg *states; /* cfg states for each dimension */
17     kiss_fft_cpx * tmpbuf; /*buffer capable of hold the entire input */
18 };
19 
kiss_fftnd_alloc(const int * dims,int ndims,int inverse_fft,void * mem,size_t * lenmem)20 kiss_fftnd_cfg kiss_fftnd_alloc(const int *dims,int ndims,int inverse_fft,void*mem,size_t*lenmem)
21 {
22     kiss_fftnd_cfg st = NULL;
23     int i;
24     int dimprod=1;
25     size_t memneeded = sizeof(struct kiss_fftnd_state);
26     char * ptr;
27 
28     for (i=0;i<ndims;++i) {
29         size_t sublen=0;
30         kiss_fft_alloc (dims[i], inverse_fft, NULL, &sublen);
31         memneeded += sublen;   /* st->states[i] */
32         dimprod *= dims[i];
33     }
34     memneeded += sizeof(int) * ndims;/*  st->dims */
35     memneeded += sizeof(void*) * ndims;/* st->states  */
36     memneeded += sizeof(kiss_fft_cpx) * dimprod; /* st->tmpbuf */
37 
38     if (lenmem == NULL) {/* allocate for the caller*/
39         st = (kiss_fftnd_cfg) malloc (memneeded);
40     } else { /* initialize supplied buffer if big enough */
41         if (*lenmem >= memneeded)
42             st = (kiss_fftnd_cfg) mem;
43         *lenmem = memneeded; /*tell caller how big struct is (or would be) */
44     }
45     if (!st)
46         return NULL; /*malloc failed or buffer too small */
47 
48     st->dimprod = dimprod;
49     st->ndims = ndims;
50     ptr=(char*)(st+1);
51 
52     st->states = (kiss_fft_cfg *)ptr;
53     ptr += sizeof(void*) * ndims;
54 
55     st->dims = (int*)ptr;
56     ptr += sizeof(int) * ndims;
57 
58     st->tmpbuf = (kiss_fft_cpx*)ptr;
59     ptr += sizeof(kiss_fft_cpx) * dimprod;
60 
61     for (i=0;i<ndims;++i) {
62         size_t len;
63         st->dims[i] = dims[i];
64         kiss_fft_alloc (st->dims[i], inverse_fft, NULL, &len);
65         st->states[i] = kiss_fft_alloc (st->dims[i], inverse_fft, ptr,&len);
66         ptr += len;
67     }
68     /*
69 Hi there!
70 
71 If you're looking at this particular code, it probably means you've got a brain-dead bounds checker
72 that thinks the above code overwrites the end of the array.
73 
74 It doesn't.
75 
76 -- Mark
77 
78 P.S.
79 The below code might give you some warm fuzzies and help convince you.
80        */
81     if ( ptr - (char*)st != (int)memneeded ) {
82         fprintf(stderr,
83                 "################################################################################\n"
84                 "Internal error! Memory allocation miscalculation\n"
85                 "################################################################################\n"
86                );
87     }
88     return st;
89 }
90 
91 /*
92  This works by tackling one dimension at a time.
93 
94  In effect,
95  Each stage starts out by reshaping the matrix into a DixSi 2d matrix.
96  A Di-sized fft is taken of each column, transposing the matrix as it goes.
97 
98 Here's a 3-d example:
99 Take a 2x3x4 matrix, laid out in memory as a contiguous buffer
100  [ [ [ a b c d ] [ e f g h ] [ i j k l ] ]
101    [ [ m n o p ] [ q r s t ] [ u v w x ] ] ]
102 
103 Stage 0 ( D=2): treat the buffer as a 2x12 matrix
104    [ [a b ... k l]
105      [m n ... w x] ]
106 
107    FFT each column with size 2.
108    Transpose the matrix at the same time using kiss_fft_stride.
109 
110    [ [ a+m a-m ]
111      [ b+n b-n]
112      ...
113      [ k+w k-w ]
114      [ l+x l-x ] ]
115 
116    Note fft([x y]) == [x+y x-y]
117 
118 Stage 1 ( D=3) treats the buffer (the output of stage D=2) as an 3x8 matrix,
119    [ [ a+m a-m b+n b-n c+o c-o d+p d-p ]
120      [ e+q e-q f+r f-r g+s g-s h+t h-t ]
121      [ i+u i-u j+v j-v k+w k-w l+x l-x ] ]
122 
123    And perform FFTs (size=3) on each of the columns as above, transposing
124    the matrix as it goes.  The output of stage 1 is
125        (Legend: ap = [ a+m e+q i+u ]
126                 am = [ a-m e-q i-u ] )
127 
128    [ [ sum(ap) fft(ap)[0] fft(ap)[1] ]
129      [ sum(am) fft(am)[0] fft(am)[1] ]
130      [ sum(bp) fft(bp)[0] fft(bp)[1] ]
131      [ sum(bm) fft(bm)[0] fft(bm)[1] ]
132      [ sum(cp) fft(cp)[0] fft(cp)[1] ]
133      [ sum(cm) fft(cm)[0] fft(cm)[1] ]
134      [ sum(dp) fft(dp)[0] fft(dp)[1] ]
135      [ sum(dm) fft(dm)[0] fft(dm)[1] ]  ]
136 
137 Stage 2 ( D=4) treats this buffer as a 4*6 matrix,
138    [ [ sum(ap) fft(ap)[0] fft(ap)[1] sum(am) fft(am)[0] fft(am)[1] ]
139      [ sum(bp) fft(bp)[0] fft(bp)[1] sum(bm) fft(bm)[0] fft(bm)[1] ]
140      [ sum(cp) fft(cp)[0] fft(cp)[1] sum(cm) fft(cm)[0] fft(cm)[1] ]
141      [ sum(dp) fft(dp)[0] fft(dp)[1] sum(dm) fft(dm)[0] fft(dm)[1] ]  ]
142 
143    Then FFTs each column, transposing as it goes.
144 
145    The resulting matrix is the 3d FFT of the 2x3x4 input matrix.
146 
147    Note as a sanity check that the first element of the final
148    stage's output (DC term) is
149    sum( [ sum(ap) sum(bp) sum(cp) sum(dp) ] )
150    , i.e. the summation of all 24 input elements.
151 
152 */
kiss_fftnd(kiss_fftnd_cfg st,const kiss_fft_cpx * fin,kiss_fft_cpx * fout)153 void kiss_fftnd(kiss_fftnd_cfg st,const kiss_fft_cpx *fin,kiss_fft_cpx *fout)
154 {
155     int i,k;
156     const kiss_fft_cpx * bufin=fin;
157     kiss_fft_cpx * bufout;
158 
159     /*arrange it so the last bufout == fout*/
160     if ( st->ndims & 1 ) {
161         bufout = fout;
162         if (fin==fout) {
163             memcpy( st->tmpbuf, fin, sizeof(kiss_fft_cpx) * st->dimprod );
164             bufin = st->tmpbuf;
165         }
166     }else
167         bufout = st->tmpbuf;
168 
169     for ( k=0; k < st->ndims; ++k) {
170         int curdim = st->dims[k];
171         int stride = st->dimprod / curdim;
172 
173         for ( i=0 ; i<stride ; ++i )
174             kiss_fft_stride( st->states[k], bufin+i , bufout+i*curdim, stride );
175 
176         /*toggle back and forth between the two buffers*/
177         if (bufout == st->tmpbuf){
178             bufout = fout;
179             bufin = st->tmpbuf;
180         }else{
181             bufout = st->tmpbuf;
182             bufin = fout;
183         }
184     }
185 }
186