diff fft/nayukic/fft.c @ 32:ebc87a62321d

Add Nayuki fft.c compiled to JS
author Chris Cannam
date Mon, 09 Nov 2015 11:46:47 +0000
parents
children bbf5d4e825eb
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/nayukic/fft.c	Mon Nov 09 11:46:47 2015 +0000
@@ -0,0 +1,374 @@
+/* 
+ * Free FFT and convolution (C)
+ * 
+ * Copyright (c) 2014 Project Nayuki
+ * http://www.nayuki.io/page/free-small-fft-in-multiple-languages
+ * 
+ * (MIT License)
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ * - The above copyright notice and this permission notice shall be included in
+ *   all copies or substantial portions of the Software.
+ * - The Software is provided "as is", without warranty of any kind, express or
+ *   implied, including but not limited to the warranties of merchantability,
+ *   fitness for a particular purpose and noninfringement. In no event shall the
+ *   authors or copyright holders be liable for any claim, damages or other
+ *   liability, whether in an action of contract, tort or otherwise, arising from,
+ *   out of or in connection with the Software or the use or other dealings in the
+ *   Software.
+ */
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "fft.h"
+
+
+// Private function prototypes
+static size_t reverse_bits(size_t x, unsigned int n);
+static void *memdup(const void *src, size_t n);
+
+#define SIZE_MAX ((size_t)-1)
+
+
+int transform(double real[], double imag[], size_t n) {
+	if (n == 0)
+		return 1;
+	else if ((n & (n - 1)) == 0)  // Is power of 2
+		return transform_radix2(real, imag, n);
+	else  // More complicated algorithm for arbitrary sizes
+		return transform_bluestein(real, imag, n);
+}
+
+
+int inverse_transform(double real[], double imag[], size_t n) {
+	return transform(imag, real, n);
+}
+
+tables *precalc(size_t n) {
+    unsigned int levels;
+    // Compute levels = floor(log2(n))
+    {
+	size_t temp = n;
+	levels = 0;
+	while (temp > 1) {
+	    levels++;
+	    temp >>= 1;
+	}
+	if (1u << levels != n)
+	    return 0;  // n is not a power of 2
+    }
+    if (SIZE_MAX / sizeof(double) < n / 2) return 0;
+    tables *tables = malloc(sizeof(tables));
+    if (!tables) return tables;
+    tables->levels = levels;
+    size_t size = (n / 2) * sizeof(double);
+    tables->cos = malloc(size);
+    if (!tables->cos) {
+	free(tables);
+	return 0;
+    }
+    tables->sin = malloc(size);
+    if (!tables->sin) {
+	free(tables->cos);
+	free(tables);
+	return 0;
+    }
+    int i;
+    for (i = 0; i < n / 2; i++) {
+	tables->cos[i] = cos(2 * M_PI * i / n);
+	tables->sin[i] = sin(2 * M_PI * i / n);
+    }
+    return tables;
+}
+
+void dispose(tables *tables) {
+    if (!tables) return;
+    free(tables->cos);
+    free(tables->sin);
+    free(tables);
+}
+
+void transform_radix2_precalc(double real[], double imag[], int n, tables *tables) {
+	double *cos_table, *sin_table;
+	int size;
+	int i;
+	
+	// Trignometric tables
+	cos_table = tables->cos;
+	sin_table = tables->sin;
+
+	// Bit-reversed addressing permutation
+	for (i = 0; i < n; i++) {
+		int j = reverse_bits(i, tables->levels);
+		if (j > i) {
+			double temp = real[i];
+			real[i] = real[j];
+			real[j] = temp;
+			temp = imag[i];
+			imag[i] = imag[j];
+			imag[j] = temp;
+		}
+	}
+	
+	// Cooley-Tukey decimation-in-time radix-2 FFT
+	for (size = 2; size <= n; size *= 2) {
+		int halfsize = size / 2;
+		int tablestep = n / size;
+		for (i = 0; i < n; i += size) {
+			int j;
+			int k;
+			for (j = i, k = 0; j < i + halfsize; j++, k += tablestep) {
+				double tpre =  real[j+halfsize] * cos_table[k] + imag[j+halfsize] * sin_table[k];
+				double tpim = -real[j+halfsize] * sin_table[k] + imag[j+halfsize] * cos_table[k];
+				real[j + halfsize] = real[j] - tpre;
+				imag[j + halfsize] = imag[j] - tpim;
+				real[j] += tpre;
+				imag[j] += tpim;
+			}
+		}
+		if (size == n)  // Prevent overflow in 'size *= 2'
+			break;
+	}
+}
+
+int transform_radix2(double real[], double imag[], size_t n) {
+	// Variables
+	int status = 0;
+	unsigned int levels;
+	double *cos_table, *sin_table;
+	size_t size;
+	size_t i;
+	
+	// Compute levels = floor(log2(n))
+	{
+		size_t temp = n;
+		levels = 0;
+		while (temp > 1) {
+			levels++;
+			temp >>= 1;
+		}
+		if (1u << levels != n)
+			return 0;  // n is not a power of 2
+	}
+	
+	// Trignometric tables
+	if (SIZE_MAX / sizeof(double) < n / 2)
+		return 0;
+	size = (n / 2) * sizeof(double);
+	cos_table = malloc(size);
+	sin_table = malloc(size);
+	if (cos_table == NULL || sin_table == NULL)
+		goto cleanup;
+	for (i = 0; i < n / 2; i++) {
+		cos_table[i] = cos(2 * M_PI * i / n);
+		sin_table[i] = sin(2 * M_PI * i / n);
+	}
+	
+	// Bit-reversed addressing permutation
+	for (i = 0; i < n; i++) {
+		size_t j = reverse_bits(i, levels);
+		if (j > i) {
+			double temp = real[i];
+			real[i] = real[j];
+			real[j] = temp;
+			temp = imag[i];
+			imag[i] = imag[j];
+			imag[j] = temp;
+		}
+	}
+	
+	// Cooley-Tukey decimation-in-time radix-2 FFT
+	for (size = 2; size <= n; size *= 2) {
+		size_t halfsize = size / 2;
+		size_t tablestep = n / size;
+		for (i = 0; i < n; i += size) {
+			size_t j;
+			size_t k;
+			for (j = i, k = 0; j < i + halfsize; j++, k += tablestep) {
+				double tpre =  real[j+halfsize] * cos_table[k] + imag[j+halfsize] * sin_table[k];
+				double tpim = -real[j+halfsize] * sin_table[k] + imag[j+halfsize] * cos_table[k];
+				real[j + halfsize] = real[j] - tpre;
+				imag[j + halfsize] = imag[j] - tpim;
+				real[j] += tpre;
+				imag[j] += tpim;
+			}
+		}
+		if (size == n)  // Prevent overflow in 'size *= 2'
+			break;
+	}
+	status = 1;
+	
+cleanup:
+	free(sin_table);
+	free(cos_table);
+	return status;
+}
+
+
+int transform_bluestein(double real[], double imag[], size_t n) {
+	// Variables
+	int status = 0;
+	double *cos_table, *sin_table;
+	double *areal, *aimag;
+	double *breal, *bimag;
+	double *creal, *cimag;
+	size_t m;
+	size_t size_n, size_m;
+	size_t i;
+	
+	// Find a power-of-2 convolution length m such that m >= n * 2 + 1
+	{
+		size_t target;
+		if (n > (SIZE_MAX - 1) / 2)
+			return 0;
+		target = n * 2 + 1;
+		for (m = 1; m < target; m *= 2) {
+			if (SIZE_MAX / 2 < m)
+				return 0;
+		}
+	}
+	
+	// Allocate memory
+	if (SIZE_MAX / sizeof(double) < n || SIZE_MAX / sizeof(double) < m)
+		return 0;
+	size_n = n * sizeof(double);
+	size_m = m * sizeof(double);
+	cos_table = malloc(size_n);
+	sin_table = malloc(size_n);
+	areal = calloc(m, sizeof(double));
+	aimag = calloc(m, sizeof(double));
+	breal = calloc(m, sizeof(double));
+	bimag = calloc(m, sizeof(double));
+	creal = malloc(size_m);
+	cimag = malloc(size_m);
+	if (cos_table == NULL || sin_table == NULL
+			|| areal == NULL || aimag == NULL
+			|| breal == NULL || bimag == NULL
+			|| creal == NULL || cimag == NULL)
+		goto cleanup;
+	
+	// Trignometric tables
+	for (i = 0; i < n; i++) {
+		double temp = M_PI * (size_t)((unsigned long long)i * i % ((unsigned long long)n * 2)) / n;
+		// Less accurate version if long long is unavailable: double temp = M_PI * i * i / n;
+		cos_table[i] = cos(temp);
+		sin_table[i] = sin(temp);
+	}
+	
+	// Temporary vectors and preprocessing
+	for (i = 0; i < n; i++) {
+		areal[i] =  real[i] * cos_table[i] + imag[i] * sin_table[i];
+		aimag[i] = -real[i] * sin_table[i] + imag[i] * cos_table[i];
+	}
+	breal[0] = cos_table[0];
+	bimag[0] = sin_table[0];
+	for (i = 1; i < n; i++) {
+		breal[i] = breal[m - i] = cos_table[i];
+		bimag[i] = bimag[m - i] = sin_table[i];
+	}
+	
+	// Convolution
+	if (!convolve_complex(areal, aimag, breal, bimag, creal, cimag, m))
+		goto cleanup;
+	
+	// Postprocessing
+	for (i = 0; i < n; i++) {
+		real[i] =  creal[i] * cos_table[i] + cimag[i] * sin_table[i];
+		imag[i] = -creal[i] * sin_table[i] + cimag[i] * cos_table[i];
+	}
+	status = 1;
+	
+	// Deallocation
+cleanup:
+	free(cimag);
+	free(creal);
+	free(bimag);
+	free(breal);
+	free(aimag);
+	free(areal);
+	free(sin_table);
+	free(cos_table);
+	return status;
+}
+
+
+int convolve_real(const double x[], const double y[], double out[], size_t n) {
+	double *ximag, *yimag, *zimag;
+	int status = 0;
+	ximag = calloc(n, sizeof(double));
+	yimag = calloc(n, sizeof(double));
+	zimag = calloc(n, sizeof(double));
+	if (ximag == NULL || yimag == NULL || zimag == NULL)
+		goto cleanup;
+	
+	status = convolve_complex(x, ximag, y, yimag, out, zimag, n);
+cleanup:
+	free(zimag);
+	free(yimag);
+	free(ximag);
+	return status;
+}
+
+
+int convolve_complex(const double xreal[], const double ximag[], const double yreal[], const double yimag[], double outreal[], double outimag[], size_t n) {
+	int status = 0;
+	size_t size;
+	size_t i;
+	double *xr, *xi, *yr, *yi;
+	if (SIZE_MAX / sizeof(double) < n)
+		return 0;
+	size = n * sizeof(double);
+	xr = memdup(xreal, size);
+	xi = memdup(ximag, size);
+	yr = memdup(yreal, size);
+	yi = memdup(yimag, size);
+	if (xr == NULL || xi == NULL || yr == NULL || yi == NULL)
+		goto cleanup;
+	
+	if (!transform(xr, xi, n))
+		goto cleanup;
+	if (!transform(yr, yi, n))
+		goto cleanup;
+	for (i = 0; i < n; i++) {
+		double temp = xr[i] * yr[i] - xi[i] * yi[i];
+		xi[i] = xi[i] * yr[i] + xr[i] * yi[i];
+		xr[i] = temp;
+	}
+	if (!inverse_transform(xr, xi, n))
+		goto cleanup;
+	for (i = 0; i < n; i++) {  // Scaling (because this FFT implementation omits it)
+		outreal[i] = xr[i] / n;
+		outimag[i] = xi[i] / n;
+	}
+	status = 1;
+	
+cleanup:
+	free(yi);
+	free(yr);
+	free(xi);
+	free(xr);
+	return status;
+}
+
+
+static size_t reverse_bits(size_t x, unsigned int n) {
+	size_t result = 0;
+	unsigned int i;
+	for (i = 0; i < n; i++, x >>= 1)
+		result = (result << 1) | (x & 1);
+	return result;
+}
+
+
+static void *memdup(const void *src, size_t n) {
+	void *dest = malloc(n);
+	if (dest != NULL)
+		memcpy(dest, src, n);
+	return dest;
+}