// Copyright (C) 2026 Kiyotsugu Arai // SPDX-License-Identifier: LGPL-3.0-or-later // // example_doc_fft_samples.cpp // Verification harness for samples in api/FFT.html (ja+en). #define _USE_MATH_DEFINES #include #include #include #include #include #include #include #include #include #include int main() { std::cout << std::setprecision(15); using C = sangi::Complex; // NOTE: sangi::FFT requires sangi::Complex // (NOT std::complex — see doc note) // ---- basic 8-point FFT round-trip ---------------------------- { std::cout << "[basic FFT 8-pt]\n"; std::vector signal = {C(1), C(2), C(3), C(4), C(4), C(3), C(2), C(1)}; std::vector orig = signal; sangi::FFT::fft(signal); std::cout << " X[0].re=" << signal[0].re << " (sum of samples = 20)\n"; std::cout << " X[1].re=" << signal[1].re << " .im=" << signal[1].im << "\n"; std::cout << " X[4].re=" << signal[4].re << " (Nyquist bin)\n"; sangi::FFT::ifft(signal); double err = 0; for (std::size_t i = 0; i < signal.size(); ++i) { err += std::abs(signal[i].re - orig[i].re) + std::abs(signal[i].im - orig[i].im); } std::cout << " round-trip L1 error = " << err << "\n"; } // ---- spectrum peak (440 Hz @ 44.1k, N=1024) ------------------ { std::cout << "[spectrum analysis 440Hz]\n"; constexpr int N = 1024; constexpr double fs = 44100.0; constexpr double f0 = 440.0; std::vector signal(N); for (int i = 0; i < N; ++i) signal[i] = std::sin(2.0 * M_PI * f0 * i / fs); auto spectrum = sangi::RealFFT::fft(signal); auto amp = sangi::amplitude_spectrum(spectrum); int peak = 0; for (int k = 1; k < (int)amp.size(); ++k) if (amp[k] > amp[peak]) peak = k; double peakFreq = peak * fs / N; std::cout << " peak bin = " << peak << " peakFreq = " << peakFreq << " Hz\n"; std::cout << " (expected ~ 440 Hz; bin resolution = " << (fs / N) << " Hz so ~ 430.66 Hz is the closest)\n"; } // ---- convolution (polynomial multiplication) ---------------- { std::cout << "[convolve]\n"; std::vector p = {C(1), C(2), C(3)}; std::vector q = {C(4), C(5)}; auto r = sangi::FFT::convolve(p, q); std::cout << " conv = ["; for (std::size_t i = 0; i < r.size(); ++i) { if (i) std::cout << ", "; std::cout << r[i].re; } std::cout << "] (expected 4, 13, 22, 15)\n"; } // ---- 2D FFT round-trip --------------------------------------- { std::cout << "[2D FFT 4x4]\n"; using SC = sangi::Complex; sangi::Matrix img(4, 4); for (int r = 0; r < 4; ++r) for (int c = 0; c < 4; ++c) img(r, c) = SC(double(r + c), 0.0); auto freq = sangi::fft2d(img); auto rec = sangi::ifft2d(freq); std::cout << " Recovered (0,0).re = " << rec(0, 0).re << " (expected 0)\n"; std::cout << " Recovered (3,3).re = " << rec(3, 3).re << " (expected 6)\n"; } // ---- DCT round-trip ------------------------------------------ { std::cout << "[DCT 8-pt]\n"; std::vector data = {1, 2, 3, 4, 5, 6, 7, 8}; auto coeffs = sangi::dct(data); auto rec = sangi::idct(coeffs); std::cout << " coeffs[0..2] = " << coeffs[0] << ", " << coeffs[1] << ", " << coeffs[2] << "\n"; double err = 0; for (std::size_t i = 0; i < data.size(); ++i) err += std::abs(rec[i] - data[i]); std::cout << " round-trip L1 error = " << err << "\n"; } // ---- FFT with multi-precision Float (sangi::Float) ---------- { std::cout << "[FFT, Float> 8-pt]\n"; using sangi::Float; Float::setDefaultPrecision(170); // ≈ 50 decimal digits using FC = sangi::Complex; std::vector signal = {FC(Float(1)), FC(Float(2)), FC(Float(3)), FC(Float(4)), FC(Float(4)), FC(Float(3)), FC(Float(2)), FC(Float(1))}; std::vector orig = signal; sangi::FFT::fft(signal); std::cout << " X[0].re = " << signal[0].re.toDecimalString(15) << " (sum=20)\n"; std::cout << " X[1].re = " << signal[1].re.toDecimalString(15) << "\n"; std::cout << " X[1].im = " << signal[1].im.toDecimalString(15) << "\n"; std::cout << " X[4].re = " << signal[4].re.toDecimalString(15) << " (Nyquist)\n"; sangi::FFT::ifft(signal); Float err(Float(0)); for (std::size_t i = 0; i < signal.size(); ++i) { Float dr = signal[i].re - orig[i].re; Float di = signal[i].im - orig[i].im; if (dr.isNegative()) dr = -dr; if (di.isNegative()) di = -di; err = err + dr + di; } std::cout << " round-trip L1 err = " << err.toDecimalString(8) << " (multi-precision rounding)\n"; } // ---- Hilbert / instantaneous amplitude ---------------------- { std::cout << "[Hilbert envelope]\n"; constexpr int N = 256; std::vector signal(N); for (int i = 0; i < N; ++i) { double t = double(i) / N; signal[i] = (1.0 + 0.5 * std::cos(2 * M_PI * 5 * t)) * std::cos(2 * M_PI * 50 * t); } auto env = sangi::instantaneousAmplitude(signal); std::cout << " env[0] = " << env[0] << " (modulator at t=0: 1.5)\n"; std::cout << " env[N/2] = " << env[N / 2] << "\n"; } return 0; }