cannam@167: /**************************************************************************/ cannam@167: /* NOTE to users: this is the FFTW self-test and benchmark program. cannam@167: It is probably NOT a good place to learn FFTW usage, since it has a cannam@167: lot of added complexity in order to exercise and test the full API, cannam@167: etcetera. We suggest reading the manual. cannam@167: cannam@167: (Some of the self-test code is split off into fftw-bench.c and cannam@167: hook.c.) */ cannam@167: /**************************************************************************/ cannam@167: cannam@167: #include cannam@167: #include cannam@167: #include cannam@167: #include "tests/fftw-bench.h" cannam@167: cannam@167: static const char *mkversion(void) { return FFTW(version); } cannam@167: static const char *mkcc(void) { return FFTW(cc); } cannam@167: static const char *mkcodelet_optim(void) { return FFTW(codelet_optim); } cannam@167: cannam@167: BEGIN_BENCH_DOC cannam@167: BENCH_DOC("name", "fftw3") cannam@167: BENCH_DOCF("version", mkversion) cannam@167: BENCH_DOCF("cc", mkcc) cannam@167: BENCH_DOCF("codelet-optim", mkcodelet_optim) cannam@167: END_BENCH_DOC cannam@167: cannam@167: static FFTW(iodim) *bench_tensor_to_fftw_iodim(bench_tensor *t) cannam@167: { cannam@167: FFTW(iodim) *d; cannam@167: int i; cannam@167: cannam@167: BENCH_ASSERT(t->rnk >= 0); cannam@167: if (t->rnk == 0) return 0; cannam@167: cannam@167: d = (FFTW(iodim) *)bench_malloc(sizeof(FFTW(iodim)) * t->rnk); cannam@167: for (i = 0; i < t->rnk; ++i) { cannam@167: d[i].n = t->dims[i].n; cannam@167: d[i].is = t->dims[i].is; cannam@167: d[i].os = t->dims[i].os; cannam@167: } cannam@167: cannam@167: return d; cannam@167: } cannam@167: cannam@167: static void extract_reim_split(int sign, int size, bench_real *p, cannam@167: bench_real **r, bench_real **i) cannam@167: { cannam@167: if (sign == FFTW_FORWARD) { cannam@167: *r = p + 0; cannam@167: *i = p + size; cannam@167: } else { cannam@167: *r = p + size; cannam@167: *i = p + 0; cannam@167: } cannam@167: } cannam@167: cannam@167: static int sizeof_problem(bench_problem *p) cannam@167: { cannam@167: return tensor_sz(p->sz) * tensor_sz(p->vecsz); cannam@167: } cannam@167: cannam@167: /* ouch */ cannam@167: static int expressible_as_api_many(bench_tensor *t) cannam@167: { cannam@167: int i; cannam@167: cannam@167: BENCH_ASSERT(BENCH_FINITE_RNK(t->rnk)); cannam@167: cannam@167: i = t->rnk - 1; cannam@167: while (--i >= 0) { cannam@167: bench_iodim *d = t->dims + i; cannam@167: if (d[0].is % d[1].is) return 0; cannam@167: if (d[0].os % d[1].os) return 0; cannam@167: } cannam@167: return 1; cannam@167: } cannam@167: cannam@167: static int *mkn(bench_tensor *t) cannam@167: { cannam@167: int *n = (int *) bench_malloc(sizeof(int *) * t->rnk); cannam@167: int i; cannam@167: for (i = 0; i < t->rnk; ++i) cannam@167: n[i] = t->dims[i].n; cannam@167: return n; cannam@167: } cannam@167: cannam@167: static void mknembed_many(bench_tensor *t, int **inembedp, int **onembedp) cannam@167: { cannam@167: int i; cannam@167: bench_iodim *d; cannam@167: int *inembed = (int *) bench_malloc(sizeof(int *) * t->rnk); cannam@167: int *onembed = (int *) bench_malloc(sizeof(int *) * t->rnk); cannam@167: cannam@167: BENCH_ASSERT(BENCH_FINITE_RNK(t->rnk)); cannam@167: *inembedp = inembed; *onembedp = onembed; cannam@167: cannam@167: i = t->rnk - 1; cannam@167: while (--i >= 0) { cannam@167: d = t->dims + i; cannam@167: inembed[i+1] = d[0].is / d[1].is; cannam@167: onembed[i+1] = d[0].os / d[1].os; cannam@167: } cannam@167: } cannam@167: cannam@167: /* try to use the most appropriate API function. Big mess. */ cannam@167: cannam@167: static int imax(int a, int b) { return (a > b ? a : b); } cannam@167: cannam@167: static int halfish_sizeof_problem(bench_problem *p) cannam@167: { cannam@167: int n2 = sizeof_problem(p); cannam@167: if (BENCH_FINITE_RNK(p->sz->rnk) && p->sz->rnk > 0) cannam@167: n2 = (n2 / imax(p->sz->dims[p->sz->rnk - 1].n, 1)) * cannam@167: (p->sz->dims[p->sz->rnk - 1].n / 2 + 1); cannam@167: return n2; cannam@167: } cannam@167: cannam@167: static FFTW(plan) mkplan_real_split(bench_problem *p, unsigned flags) cannam@167: { cannam@167: FFTW(plan) pln; cannam@167: bench_tensor *sz = p->sz, *vecsz = p->vecsz; cannam@167: FFTW(iodim) *dims, *howmany_dims; cannam@167: bench_real *ri, *ii, *ro, *io; cannam@167: int n2 = halfish_sizeof_problem(p); cannam@167: cannam@167: extract_reim_split(FFTW_FORWARD, n2, (bench_real *) p->in, &ri, &ii); cannam@167: extract_reim_split(FFTW_FORWARD, n2, (bench_real *) p->out, &ro, &io); cannam@167: cannam@167: dims = bench_tensor_to_fftw_iodim(sz); cannam@167: howmany_dims = bench_tensor_to_fftw_iodim(vecsz); cannam@167: if (p->sign < 0) { cannam@167: if (verbose > 2) printf("using plan_guru_split_dft_r2c\n"); cannam@167: pln = FFTW(plan_guru_split_dft_r2c)(sz->rnk, dims, cannam@167: vecsz->rnk, howmany_dims, cannam@167: ri, ro, io, flags); cannam@167: } cannam@167: else { cannam@167: if (verbose > 2) printf("using plan_guru_split_dft_c2r\n"); cannam@167: pln = FFTW(plan_guru_split_dft_c2r)(sz->rnk, dims, cannam@167: vecsz->rnk, howmany_dims, cannam@167: ri, ii, ro, flags); cannam@167: } cannam@167: bench_free(dims); cannam@167: bench_free(howmany_dims); cannam@167: return pln; cannam@167: } cannam@167: cannam@167: static FFTW(plan) mkplan_real_interleaved(bench_problem *p, unsigned flags) cannam@167: { cannam@167: FFTW(plan) pln; cannam@167: bench_tensor *sz = p->sz, *vecsz = p->vecsz; cannam@167: cannam@167: if (vecsz->rnk == 0 && tensor_unitstridep(sz) cannam@167: && tensor_real_rowmajorp(sz, p->sign, p->in_place)) cannam@167: goto api_simple; cannam@167: cannam@167: if (vecsz->rnk == 1 && expressible_as_api_many(sz)) cannam@167: goto api_many; cannam@167: cannam@167: goto api_guru; cannam@167: cannam@167: api_simple: cannam@167: switch (sz->rnk) { cannam@167: case 1: cannam@167: if (p->sign < 0) { cannam@167: if (verbose > 2) printf("using plan_dft_r2c_1d\n"); cannam@167: return FFTW(plan_dft_r2c_1d)(sz->dims[0].n, cannam@167: (bench_real *) p->in, cannam@167: (bench_complex *) p->out, cannam@167: flags); cannam@167: } cannam@167: else { cannam@167: if (verbose > 2) printf("using plan_dft_c2r_1d\n"); cannam@167: return FFTW(plan_dft_c2r_1d)(sz->dims[0].n, cannam@167: (bench_complex *) p->in, cannam@167: (bench_real *) p->out, cannam@167: flags); cannam@167: } cannam@167: break; cannam@167: case 2: cannam@167: if (p->sign < 0) { cannam@167: if (verbose > 2) printf("using plan_dft_r2c_2d\n"); cannam@167: return FFTW(plan_dft_r2c_2d)(sz->dims[0].n, sz->dims[1].n, cannam@167: (bench_real *) p->in, cannam@167: (bench_complex *) p->out, cannam@167: flags); cannam@167: } cannam@167: else { cannam@167: if (verbose > 2) printf("using plan_dft_c2r_2d\n"); cannam@167: return FFTW(plan_dft_c2r_2d)(sz->dims[0].n, sz->dims[1].n, cannam@167: (bench_complex *) p->in, cannam@167: (bench_real *) p->out, cannam@167: flags); cannam@167: } cannam@167: break; cannam@167: case 3: cannam@167: if (p->sign < 0) { cannam@167: if (verbose > 2) printf("using plan_dft_r2c_3d\n"); cannam@167: return FFTW(plan_dft_r2c_3d)( cannam@167: sz->dims[0].n, sz->dims[1].n, sz->dims[2].n, cannam@167: (bench_real *) p->in, (bench_complex *) p->out, cannam@167: flags); cannam@167: } cannam@167: else { cannam@167: if (verbose > 2) printf("using plan_dft_c2r_3d\n"); cannam@167: return FFTW(plan_dft_c2r_3d)( cannam@167: sz->dims[0].n, sz->dims[1].n, sz->dims[2].n, cannam@167: (bench_complex *) p->in, (bench_real *) p->out, cannam@167: flags); cannam@167: } cannam@167: break; cannam@167: default: { cannam@167: int *n = mkn(sz); cannam@167: if (p->sign < 0) { cannam@167: if (verbose > 2) printf("using plan_dft_r2c\n"); cannam@167: pln = FFTW(plan_dft_r2c)(sz->rnk, n, cannam@167: (bench_real *) p->in, cannam@167: (bench_complex *) p->out, cannam@167: flags); cannam@167: } cannam@167: else { cannam@167: if (verbose > 2) printf("using plan_dft_c2r\n"); cannam@167: pln = FFTW(plan_dft_c2r)(sz->rnk, n, cannam@167: (bench_complex *) p->in, cannam@167: (bench_real *) p->out, cannam@167: flags); cannam@167: } cannam@167: bench_free(n); cannam@167: return pln; cannam@167: } cannam@167: } cannam@167: cannam@167: api_many: cannam@167: { cannam@167: int *n, *inembed, *onembed; cannam@167: BENCH_ASSERT(vecsz->rnk == 1); cannam@167: n = mkn(sz); cannam@167: mknembed_many(sz, &inembed, &onembed); cannam@167: if (p->sign < 0) { cannam@167: if (verbose > 2) printf("using plan_many_dft_r2c\n"); cannam@167: pln = FFTW(plan_many_dft_r2c)( cannam@167: sz->rnk, n, vecsz->dims[0].n, cannam@167: (bench_real *) p->in, inembed, cannam@167: sz->dims[sz->rnk - 1].is, vecsz->dims[0].is, cannam@167: (bench_complex *) p->out, onembed, cannam@167: sz->dims[sz->rnk - 1].os, vecsz->dims[0].os, cannam@167: flags); cannam@167: } cannam@167: else { cannam@167: if (verbose > 2) printf("using plan_many_dft_c2r\n"); cannam@167: pln = FFTW(plan_many_dft_c2r)( cannam@167: sz->rnk, n, vecsz->dims[0].n, cannam@167: (bench_complex *) p->in, inembed, cannam@167: sz->dims[sz->rnk - 1].is, vecsz->dims[0].is, cannam@167: (bench_real *) p->out, onembed, cannam@167: sz->dims[sz->rnk - 1].os, vecsz->dims[0].os, cannam@167: flags); cannam@167: } cannam@167: bench_free(n); bench_free(inembed); bench_free(onembed); cannam@167: return pln; cannam@167: } cannam@167: cannam@167: api_guru: cannam@167: { cannam@167: FFTW(iodim) *dims, *howmany_dims; cannam@167: cannam@167: if (p->sign < 0) { cannam@167: dims = bench_tensor_to_fftw_iodim(sz); cannam@167: howmany_dims = bench_tensor_to_fftw_iodim(vecsz); cannam@167: if (verbose > 2) printf("using plan_guru_dft_r2c\n"); cannam@167: pln = FFTW(plan_guru_dft_r2c)(sz->rnk, dims, cannam@167: vecsz->rnk, howmany_dims, cannam@167: (bench_real *) p->in, cannam@167: (bench_complex *) p->out, cannam@167: flags); cannam@167: } cannam@167: else { cannam@167: dims = bench_tensor_to_fftw_iodim(sz); cannam@167: howmany_dims = bench_tensor_to_fftw_iodim(vecsz); cannam@167: if (verbose > 2) printf("using plan_guru_dft_c2r\n"); cannam@167: pln = FFTW(plan_guru_dft_c2r)(sz->rnk, dims, cannam@167: vecsz->rnk, howmany_dims, cannam@167: (bench_complex *) p->in, cannam@167: (bench_real *) p->out, cannam@167: flags); cannam@167: } cannam@167: bench_free(dims); cannam@167: bench_free(howmany_dims); cannam@167: return pln; cannam@167: } cannam@167: } cannam@167: cannam@167: static FFTW(plan) mkplan_real(bench_problem *p, unsigned flags) cannam@167: { cannam@167: if (p->split) cannam@167: return mkplan_real_split(p, flags); cannam@167: else cannam@167: return mkplan_real_interleaved(p, flags); cannam@167: } cannam@167: cannam@167: static FFTW(plan) mkplan_complex_split(bench_problem *p, unsigned flags) cannam@167: { cannam@167: FFTW(plan) pln; cannam@167: bench_tensor *sz = p->sz, *vecsz = p->vecsz; cannam@167: FFTW(iodim) *dims, *howmany_dims; cannam@167: bench_real *ri, *ii, *ro, *io; cannam@167: cannam@167: extract_reim_split(p->sign, p->iphyssz, (bench_real *) p->in, &ri, &ii); cannam@167: extract_reim_split(p->sign, p->ophyssz, (bench_real *) p->out, &ro, &io); cannam@167: cannam@167: dims = bench_tensor_to_fftw_iodim(sz); cannam@167: howmany_dims = bench_tensor_to_fftw_iodim(vecsz); cannam@167: if (verbose > 2) printf("using plan_guru_split_dft\n"); cannam@167: pln = FFTW(plan_guru_split_dft)(sz->rnk, dims, cannam@167: vecsz->rnk, howmany_dims, cannam@167: ri, ii, ro, io, flags); cannam@167: bench_free(dims); cannam@167: bench_free(howmany_dims); cannam@167: return pln; cannam@167: } cannam@167: cannam@167: static FFTW(plan) mkplan_complex_interleaved(bench_problem *p, unsigned flags) cannam@167: { cannam@167: FFTW(plan) pln; cannam@167: bench_tensor *sz = p->sz, *vecsz = p->vecsz; cannam@167: cannam@167: if (vecsz->rnk == 0 && tensor_unitstridep(sz) && tensor_rowmajorp(sz)) cannam@167: goto api_simple; cannam@167: cannam@167: if (vecsz->rnk == 1 && expressible_as_api_many(sz)) cannam@167: goto api_many; cannam@167: cannam@167: goto api_guru; cannam@167: cannam@167: api_simple: cannam@167: switch (sz->rnk) { cannam@167: case 1: cannam@167: if (verbose > 2) printf("using plan_dft_1d\n"); cannam@167: return FFTW(plan_dft_1d)(sz->dims[0].n, cannam@167: (bench_complex *) p->in, cannam@167: (bench_complex *) p->out, cannam@167: p->sign, flags); cannam@167: break; cannam@167: case 2: cannam@167: if (verbose > 2) printf("using plan_dft_2d\n"); cannam@167: return FFTW(plan_dft_2d)(sz->dims[0].n, sz->dims[1].n, cannam@167: (bench_complex *) p->in, cannam@167: (bench_complex *) p->out, cannam@167: p->sign, flags); cannam@167: break; cannam@167: case 3: cannam@167: if (verbose > 2) printf("using plan_dft_3d\n"); cannam@167: return FFTW(plan_dft_3d)( cannam@167: sz->dims[0].n, sz->dims[1].n, sz->dims[2].n, cannam@167: (bench_complex *) p->in, (bench_complex *) p->out, cannam@167: p->sign, flags); cannam@167: break; cannam@167: default: { cannam@167: int *n = mkn(sz); cannam@167: if (verbose > 2) printf("using plan_dft\n"); cannam@167: pln = FFTW(plan_dft)(sz->rnk, n, cannam@167: (bench_complex *) p->in, cannam@167: (bench_complex *) p->out, p->sign, flags); cannam@167: bench_free(n); cannam@167: return pln; cannam@167: } cannam@167: } cannam@167: cannam@167: api_many: cannam@167: { cannam@167: int *n, *inembed, *onembed; cannam@167: BENCH_ASSERT(vecsz->rnk == 1); cannam@167: n = mkn(sz); cannam@167: mknembed_many(sz, &inembed, &onembed); cannam@167: if (verbose > 2) printf("using plan_many_dft\n"); cannam@167: pln = FFTW(plan_many_dft)( cannam@167: sz->rnk, n, vecsz->dims[0].n, cannam@167: (bench_complex *) p->in, cannam@167: inembed, sz->dims[sz->rnk - 1].is, vecsz->dims[0].is, cannam@167: (bench_complex *) p->out, cannam@167: onembed, sz->dims[sz->rnk - 1].os, vecsz->dims[0].os, cannam@167: p->sign, flags); cannam@167: bench_free(n); bench_free(inembed); bench_free(onembed); cannam@167: return pln; cannam@167: } cannam@167: cannam@167: api_guru: cannam@167: { cannam@167: FFTW(iodim) *dims, *howmany_dims; cannam@167: cannam@167: dims = bench_tensor_to_fftw_iodim(sz); cannam@167: howmany_dims = bench_tensor_to_fftw_iodim(vecsz); cannam@167: if (verbose > 2) printf("using plan_guru_dft\n"); cannam@167: pln = FFTW(plan_guru_dft)(sz->rnk, dims, cannam@167: vecsz->rnk, howmany_dims, cannam@167: (bench_complex *) p->in, cannam@167: (bench_complex *) p->out, cannam@167: p->sign, flags); cannam@167: bench_free(dims); cannam@167: bench_free(howmany_dims); cannam@167: return pln; cannam@167: } cannam@167: } cannam@167: cannam@167: static FFTW(plan) mkplan_complex(bench_problem *p, unsigned flags) cannam@167: { cannam@167: if (p->split) cannam@167: return mkplan_complex_split(p, flags); cannam@167: else cannam@167: return mkplan_complex_interleaved(p, flags); cannam@167: } cannam@167: cannam@167: static FFTW(plan) mkplan_r2r(bench_problem *p, unsigned flags) cannam@167: { cannam@167: FFTW(plan) pln; cannam@167: bench_tensor *sz = p->sz, *vecsz = p->vecsz; cannam@167: FFTW(r2r_kind) *k; cannam@167: cannam@167: k = (FFTW(r2r_kind) *) bench_malloc(sizeof(FFTW(r2r_kind)) * sz->rnk); cannam@167: { cannam@167: int i; cannam@167: for (i = 0; i < sz->rnk; ++i) cannam@167: switch (p->k[i]) { cannam@167: case R2R_R2HC: k[i] = FFTW_R2HC; break; cannam@167: case R2R_HC2R: k[i] = FFTW_HC2R; break; cannam@167: case R2R_DHT: k[i] = FFTW_DHT; break; cannam@167: case R2R_REDFT00: k[i] = FFTW_REDFT00; break; cannam@167: case R2R_REDFT01: k[i] = FFTW_REDFT01; break; cannam@167: case R2R_REDFT10: k[i] = FFTW_REDFT10; break; cannam@167: case R2R_REDFT11: k[i] = FFTW_REDFT11; break; cannam@167: case R2R_RODFT00: k[i] = FFTW_RODFT00; break; cannam@167: case R2R_RODFT01: k[i] = FFTW_RODFT01; break; cannam@167: case R2R_RODFT10: k[i] = FFTW_RODFT10; break; cannam@167: case R2R_RODFT11: k[i] = FFTW_RODFT11; break; cannam@167: default: BENCH_ASSERT(0); cannam@167: } cannam@167: } cannam@167: cannam@167: if (vecsz->rnk == 0 && tensor_unitstridep(sz) && tensor_rowmajorp(sz)) cannam@167: goto api_simple; cannam@167: cannam@167: if (vecsz->rnk == 1 && expressible_as_api_many(sz)) cannam@167: goto api_many; cannam@167: cannam@167: goto api_guru; cannam@167: cannam@167: api_simple: cannam@167: switch (sz->rnk) { cannam@167: case 1: cannam@167: if (verbose > 2) printf("using plan_r2r_1d\n"); cannam@167: pln = FFTW(plan_r2r_1d)(sz->dims[0].n, cannam@167: (bench_real *) p->in, cannam@167: (bench_real *) p->out, cannam@167: k[0], flags); cannam@167: goto done; cannam@167: case 2: cannam@167: if (verbose > 2) printf("using plan_r2r_2d\n"); cannam@167: pln = FFTW(plan_r2r_2d)(sz->dims[0].n, sz->dims[1].n, cannam@167: (bench_real *) p->in, cannam@167: (bench_real *) p->out, cannam@167: k[0], k[1], flags); cannam@167: goto done; cannam@167: case 3: cannam@167: if (verbose > 2) printf("using plan_r2r_3d\n"); cannam@167: pln = FFTW(plan_r2r_3d)( cannam@167: sz->dims[0].n, sz->dims[1].n, sz->dims[2].n, cannam@167: (bench_real *) p->in, (bench_real *) p->out, cannam@167: k[0], k[1], k[2], flags); cannam@167: goto done; cannam@167: default: { cannam@167: int *n = mkn(sz); cannam@167: if (verbose > 2) printf("using plan_r2r\n"); cannam@167: pln = FFTW(plan_r2r)(sz->rnk, n, cannam@167: (bench_real *) p->in, (bench_real *) p->out, cannam@167: k, flags); cannam@167: bench_free(n); cannam@167: goto done; cannam@167: } cannam@167: } cannam@167: cannam@167: api_many: cannam@167: { cannam@167: int *n, *inembed, *onembed; cannam@167: BENCH_ASSERT(vecsz->rnk == 1); cannam@167: n = mkn(sz); cannam@167: mknembed_many(sz, &inembed, &onembed); cannam@167: if (verbose > 2) printf("using plan_many_r2r\n"); cannam@167: pln = FFTW(plan_many_r2r)( cannam@167: sz->rnk, n, vecsz->dims[0].n, cannam@167: (bench_real *) p->in, cannam@167: inembed, sz->dims[sz->rnk - 1].is, vecsz->dims[0].is, cannam@167: (bench_real *) p->out, cannam@167: onembed, sz->dims[sz->rnk - 1].os, vecsz->dims[0].os, cannam@167: k, flags); cannam@167: bench_free(n); bench_free(inembed); bench_free(onembed); cannam@167: goto done; cannam@167: } cannam@167: cannam@167: api_guru: cannam@167: { cannam@167: FFTW(iodim) *dims, *howmany_dims; cannam@167: cannam@167: dims = bench_tensor_to_fftw_iodim(sz); cannam@167: howmany_dims = bench_tensor_to_fftw_iodim(vecsz); cannam@167: if (verbose > 2) printf("using plan_guru_r2r\n"); cannam@167: pln = FFTW(plan_guru_r2r)(sz->rnk, dims, cannam@167: vecsz->rnk, howmany_dims, cannam@167: (bench_real *) p->in, cannam@167: (bench_real *) p->out, k, flags); cannam@167: bench_free(dims); cannam@167: bench_free(howmany_dims); cannam@167: goto done; cannam@167: } cannam@167: cannam@167: done: cannam@167: bench_free(k); cannam@167: return pln; cannam@167: } cannam@167: cannam@167: FFTW(plan) mkplan(bench_problem *p, unsigned flags) cannam@167: { cannam@167: switch (p->kind) { cannam@167: case PROBLEM_COMPLEX: return mkplan_complex(p, flags); cannam@167: case PROBLEM_REAL: return mkplan_real(p, flags); cannam@167: case PROBLEM_R2R: return mkplan_r2r(p, flags); cannam@167: default: BENCH_ASSERT(0); return 0; cannam@167: } cannam@167: } cannam@167: cannam@167: void main_init(int *argc, char ***argv) cannam@167: { cannam@167: UNUSED(argc); cannam@167: UNUSED(argv); cannam@167: } cannam@167: cannam@167: void initial_cleanup(void) cannam@167: { cannam@167: } cannam@167: cannam@167: void final_cleanup(void) cannam@167: { cannam@167: } cannam@167: cannam@167: int import_wisdom(FILE *f) cannam@167: { cannam@167: return FFTW(import_wisdom_from_file)(f); cannam@167: } cannam@167: cannam@167: void export_wisdom(FILE *f) cannam@167: { cannam@167: FFTW(export_wisdom_to_file)(f); cannam@167: }