Chris@32
|
1 /*
|
Chris@32
|
2 * FFT and convolution test (C)
|
Chris@32
|
3 *
|
Chris@32
|
4 * Copyright (c) 2014 Project Nayuki
|
Chris@32
|
5 * http://www.nayuki.io/page/free-small-fft-in-multiple-languages
|
Chris@32
|
6 *
|
Chris@32
|
7 * (MIT License)
|
Chris@32
|
8 * Permission is hereby granted, free of charge, to any person obtaining a copy of
|
Chris@32
|
9 * this software and associated documentation files (the "Software"), to deal in
|
Chris@32
|
10 * the Software without restriction, including without limitation the rights to
|
Chris@32
|
11 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
Chris@32
|
12 * the Software, and to permit persons to whom the Software is furnished to do so,
|
Chris@32
|
13 * subject to the following conditions:
|
Chris@32
|
14 * - The above copyright notice and this permission notice shall be included in
|
Chris@32
|
15 * all copies or substantial portions of the Software.
|
Chris@32
|
16 * - The Software is provided "as is", without warranty of any kind, express or
|
Chris@32
|
17 * implied, including but not limited to the warranties of merchantability,
|
Chris@32
|
18 * fitness for a particular purpose and noninfringement. In no event shall the
|
Chris@32
|
19 * authors or copyright holders be liable for any claim, damages or other
|
Chris@32
|
20 * liability, whether in an action of contract, tort or otherwise, arising from,
|
Chris@32
|
21 * out of or in connection with the Software or the use or other dealings in the
|
Chris@32
|
22 * Software.
|
Chris@32
|
23 */
|
Chris@32
|
24
|
Chris@32
|
25 #include <math.h>
|
Chris@32
|
26 #include <stdio.h>
|
Chris@32
|
27 #include <stdlib.h>
|
Chris@32
|
28 #include <string.h>
|
Chris@32
|
29 #include <time.h>
|
Chris@32
|
30 #include "fft.h"
|
Chris@32
|
31
|
Chris@32
|
32
|
Chris@32
|
33 // Private function prototypes
|
Chris@32
|
34 static void test_fft(int n);
|
Chris@32
|
35 static void test_convolution(int n);
|
Chris@32
|
36 static void naive_dft(const double *inreal, const double *inimag, double *outreal, double *outimag, int inverse, int n);
|
Chris@32
|
37 static void naive_convolve(const double *xreal, const double *ximag, const double *yreal, const double *yimag, double *outreal, double *outimag, int n);
|
Chris@32
|
38 static double log10_rms_err(const double *xreal, const double *ximag, const double *yreal, const double *yimag, int n);
|
Chris@32
|
39 static double *random_reals(int n);
|
Chris@32
|
40 static void *memdup(const void *src, size_t n);
|
Chris@32
|
41
|
Chris@32
|
42 static double max_log_error = -INFINITY;
|
Chris@32
|
43
|
Chris@32
|
44
|
Chris@32
|
45 /* Main and test functions */
|
Chris@32
|
46
|
Chris@32
|
47 int main(int argc, char **argv) {
|
Chris@32
|
48 int i;
|
Chris@32
|
49 int prev;
|
Chris@32
|
50 srand(time(NULL));
|
Chris@32
|
51
|
Chris@32
|
52 // Test power-of-2 size FFTs
|
Chris@32
|
53 for (i = 0; i <= 12; i++)
|
Chris@32
|
54 test_fft(1 << i);
|
Chris@32
|
55
|
Chris@32
|
56 // Test small size FFTs
|
Chris@32
|
57 for (i = 0; i < 30; i++)
|
Chris@32
|
58 test_fft(i);
|
Chris@32
|
59
|
Chris@32
|
60 // Test diverse size FFTs
|
Chris@32
|
61 prev = 0;
|
Chris@32
|
62 for (i = 0; i <= 100; i++) {
|
Chris@32
|
63 int n = (int)lround(pow(1500, i / 100.0));
|
Chris@32
|
64 if (n > prev) {
|
Chris@32
|
65 test_fft(n);
|
Chris@32
|
66 prev = n;
|
Chris@32
|
67 }
|
Chris@32
|
68 }
|
Chris@32
|
69
|
Chris@32
|
70 // Test power-of-2 size convolutions
|
Chris@32
|
71 for (i = 0; i <= 12; i++)
|
Chris@32
|
72 test_convolution(1 << i);
|
Chris@32
|
73
|
Chris@32
|
74 // Test diverse size convolutions
|
Chris@32
|
75 prev = 0;
|
Chris@32
|
76 for (i = 0; i <= 100; i++) {
|
Chris@32
|
77 int n = (int)lround(pow(1500, i / 100.0));
|
Chris@32
|
78 if (n > prev) {
|
Chris@32
|
79 test_convolution(n);
|
Chris@32
|
80 prev = n;
|
Chris@32
|
81 }
|
Chris@32
|
82 }
|
Chris@32
|
83
|
Chris@32
|
84 printf("\n");
|
Chris@32
|
85 printf("Max log err = %.1f\n", max_log_error);
|
Chris@32
|
86 printf("Test %s\n", max_log_error < -10 ? "passed" : "failed");
|
Chris@32
|
87 return 0;
|
Chris@32
|
88 }
|
Chris@32
|
89
|
Chris@32
|
90
|
Chris@32
|
91 static void test_fft(int n) {
|
Chris@32
|
92 double *inputreal, *inputimag;
|
Chris@32
|
93 double *refoutreal, *refoutimag;
|
Chris@32
|
94 double *actualoutreal, *actualoutimag;
|
Chris@32
|
95
|
Chris@32
|
96 inputreal = random_reals(n);
|
Chris@32
|
97 inputimag = random_reals(n);
|
Chris@32
|
98
|
Chris@32
|
99 refoutreal = malloc(n * sizeof(double));
|
Chris@32
|
100 refoutimag = malloc(n * sizeof(double));
|
Chris@32
|
101 naive_dft(inputreal, inputimag, refoutreal, refoutimag, 0, n);
|
Chris@32
|
102
|
Chris@32
|
103 actualoutreal = memdup(inputreal, n * sizeof(double));
|
Chris@32
|
104 actualoutimag = memdup(inputimag, n * sizeof(double));
|
Chris@32
|
105 transform(actualoutreal, actualoutimag, n);
|
Chris@32
|
106
|
Chris@32
|
107 printf("fftsize=%4d logerr=%5.1f\n", n, log10_rms_err(refoutreal, refoutimag, actualoutreal, actualoutimag, n));
|
Chris@32
|
108
|
Chris@32
|
109 free(inputreal);
|
Chris@32
|
110 free(inputimag);
|
Chris@32
|
111 free(refoutreal);
|
Chris@32
|
112 free(refoutimag);
|
Chris@32
|
113 free(actualoutreal);
|
Chris@32
|
114 free(actualoutimag);
|
Chris@32
|
115 }
|
Chris@32
|
116
|
Chris@32
|
117
|
Chris@32
|
118 static void test_convolution(int n) {
|
Chris@32
|
119 double *input0real, *input0imag;
|
Chris@32
|
120 double *input1real, *input1imag;
|
Chris@32
|
121 double *refoutreal, *refoutimag;
|
Chris@32
|
122 double *actualoutreal, *actualoutimag;
|
Chris@32
|
123
|
Chris@32
|
124 input0real = random_reals(n);
|
Chris@32
|
125 input0imag = random_reals(n);
|
Chris@32
|
126 input1real = random_reals(n);
|
Chris@32
|
127 input1imag = random_reals(n);
|
Chris@32
|
128
|
Chris@32
|
129 refoutreal = malloc(n * sizeof(double));
|
Chris@32
|
130 refoutimag = malloc(n * sizeof(double));
|
Chris@32
|
131 naive_convolve(input0real, input0imag, input1real, input1imag, refoutreal, refoutimag, n);
|
Chris@32
|
132
|
Chris@32
|
133 actualoutreal = malloc(n * sizeof(double));
|
Chris@32
|
134 actualoutimag = malloc(n * sizeof(double));
|
Chris@32
|
135 convolve_complex(input0real, input0imag, input1real, input1imag, actualoutreal, actualoutimag, n);
|
Chris@32
|
136
|
Chris@32
|
137 printf("convsize=%4d logerr=%5.1f\n", n, log10_rms_err(refoutreal, refoutimag, actualoutreal, actualoutimag, n));
|
Chris@32
|
138
|
Chris@32
|
139 free(input0real);
|
Chris@32
|
140 free(input0imag);
|
Chris@32
|
141 free(input1real);
|
Chris@32
|
142 free(input1imag);
|
Chris@32
|
143 free(refoutreal);
|
Chris@32
|
144 free(refoutimag);
|
Chris@32
|
145 free(actualoutreal);
|
Chris@32
|
146 free(actualoutimag);
|
Chris@32
|
147 }
|
Chris@32
|
148
|
Chris@32
|
149
|
Chris@32
|
150 /* Naive reference computation functions */
|
Chris@32
|
151
|
Chris@32
|
152 static void naive_dft(const double *inreal, const double *inimag, double *outreal, double *outimag, int inverse, int n) {
|
Chris@32
|
153 double coef = (inverse ? 2 : -2) * M_PI;
|
Chris@32
|
154 int k;
|
Chris@32
|
155 for (k = 0; k < n; k++) { // For each output element
|
Chris@32
|
156 double sumreal = 0;
|
Chris@32
|
157 double sumimag = 0;
|
Chris@32
|
158 int t;
|
Chris@32
|
159 for (t = 0; t < n; t++) { // For each input element
|
Chris@32
|
160 double angle = coef * ((long long)t * k % n) / n;
|
Chris@32
|
161 sumreal += inreal[t]*cos(angle) - inimag[t]*sin(angle);
|
Chris@32
|
162 sumimag += inreal[t]*sin(angle) + inimag[t]*cos(angle);
|
Chris@32
|
163 }
|
Chris@32
|
164 outreal[k] = sumreal;
|
Chris@32
|
165 outimag[k] = sumimag;
|
Chris@32
|
166 }
|
Chris@32
|
167 }
|
Chris@32
|
168
|
Chris@32
|
169
|
Chris@32
|
170 static void naive_convolve(const double *xreal, const double *ximag, const double *yreal, const double *yimag, double *outreal, double *outimag, int n) {
|
Chris@32
|
171 int i;
|
Chris@32
|
172 for (i = 0; i < n; i++) {
|
Chris@32
|
173 double sumreal = 0;
|
Chris@32
|
174 double sumimag = 0;
|
Chris@32
|
175 int j;
|
Chris@32
|
176 for (j = 0; j < n; j++) {
|
Chris@32
|
177 int k = (i - j + n) % n;
|
Chris@32
|
178 sumreal += xreal[k] * yreal[j] - ximag[k] * yimag[j];
|
Chris@32
|
179 sumimag += xreal[k] * yimag[j] + ximag[k] * yreal[j];
|
Chris@32
|
180 }
|
Chris@32
|
181 outreal[i] = sumreal;
|
Chris@32
|
182 outimag[i] = sumimag;
|
Chris@32
|
183 }
|
Chris@32
|
184 }
|
Chris@32
|
185
|
Chris@32
|
186
|
Chris@32
|
187 /* Utility functions */
|
Chris@32
|
188
|
Chris@32
|
189 static double log10_rms_err(const double *xreal, const double *ximag, const double *yreal, const double *yimag, int n) {
|
Chris@32
|
190 double err = 0;
|
Chris@32
|
191 int i;
|
Chris@32
|
192 for (i = 0; i < n; i++)
|
Chris@32
|
193 err += (xreal[i] - yreal[i]) * (xreal[i] - yreal[i]) + (ximag[i] - yimag[i]) * (ximag[i] - yimag[i]);
|
Chris@32
|
194
|
Chris@32
|
195 err /= n > 0 ? n : 1;
|
Chris@32
|
196 err = sqrt(err); // Now this is a root mean square (RMS) error
|
Chris@32
|
197 err = err > 0 ? log10(err) : -99.0;
|
Chris@32
|
198 if (err > max_log_error)
|
Chris@32
|
199 max_log_error = err;
|
Chris@32
|
200 return err;
|
Chris@32
|
201 }
|
Chris@32
|
202
|
Chris@32
|
203
|
Chris@32
|
204 static double *random_reals(int n) {
|
Chris@32
|
205 double *result = malloc(n * sizeof(double));
|
Chris@32
|
206 int i;
|
Chris@32
|
207 for (i = 0; i < n; i++)
|
Chris@32
|
208 result[i] = (rand() / (RAND_MAX + 1.0)) * 2 - 1;
|
Chris@32
|
209 return result;
|
Chris@32
|
210 }
|
Chris@32
|
211
|
Chris@32
|
212
|
Chris@32
|
213 static void *memdup(const void *src, size_t n) {
|
Chris@32
|
214 void *dest = malloc(n);
|
Chris@32
|
215 if (dest != NULL)
|
Chris@32
|
216 memcpy(dest, src, n);
|
Chris@32
|
217 return dest;
|
Chris@32
|
218 }
|