Skip to content

DFT

cmul function

template <typename T, size_t N1, size_t N2>
vec<T, const_max(N1, N2)> cmul(const vec<T, N1> &x,
                               const vec<T, N2> &y)

Complex Multiplication

Source code
template <typename T, size_t N1, size_t N2>
KFR_INTRINSIC vec<T, const_max(N1, N2)> cmul(const vec<T, N1>& x, const vec<T, N2>& y)
{
    return intrinsics::cmul_impl(x, y);
}

https://github.com/kfrlib/kfr/blob//include/kfr/dft/impl/ft.hpp#L78

dct_plan

dct_plan class

template <typename T> dct_plan

DCT type 2 (unscaled)

Source code
template <typename T>
struct dct_plan : dft_plan<T>
{
    dct_plan(size_t size) : dft_plan<T>(size) { this->temp_size += sizeof(complex<T>) * size * 2; }

    dct_plan(cpu_t cpu, size_t size) : dft_plan<T>(cpu, size)
    {
        this->temp_size += sizeof(complex<T>) * size * 2;
    }

    KFR_MEM_INTRINSIC void execute(T* out, const T* in, u8* temp, bool inverse = false) const
    {
        const size_t size                  = this->size;
        const size_t halfSize              = size / 2;
        univector_ref<complex<T>> mirrored = make_univector(
            ptr_cast<complex<T>>(temp + this->temp_size - sizeof(complex<T>) * size * 2), size);
        univector_ref<complex<T>> mirrored_dft =
            make_univector(ptr_cast<complex<T>>(temp + this->temp_size - sizeof(complex<T>) * size), size);
        auto t = counter() * c_pi<T> / (size * 2);
        if (!inverse)
        {
            for (size_t i = 0; i < halfSize; i++)
            {
                mirrored[i]            = in[i * 2];
                mirrored[size - 1 - i] = in[i * 2 + 1];
            }
            if (size % 2)
            {
                mirrored[halfSize] = in[size - 1];
            }
            dft_plan<T>::execute(mirrored_dft.data(), mirrored.data(), temp, cfalse);
            make_univector(out, size) = real(mirrored_dft) * cos(t) + imag(mirrored_dft) * sin(t);
        }
        else
        {
            mirrored    = make_complex(make_univector(in, size) * cos(t), make_univector(in, size) * -sin(t));
            mirrored[0] = mirrored[0] * T(0.5);
            dft_plan<T>::execute(mirrored_dft.data(), mirrored.data(), temp, cfalse);
            for (size_t i = 0; i < halfSize; i++)
            {
                out[i * 2 + 0] = mirrored_dft[i].real();
                out[i * 2 + 1] = mirrored_dft[size - 1 - i].real();
            }
            if (size % 2)
            {
                out[size - 1] = mirrored_dft[halfSize].real();
            }
        }
    }

    template <univector_tag Tag1, univector_tag Tag2, univector_tag Tag3>
    KFR_MEM_INTRINSIC void execute(univector<T, Tag1>& out, const univector<T, Tag2>& in,
                                   univector<u8, Tag3>& temp, bool inverse = false) const
    {
        execute(out.data(), in.data(), temp.data(), inverse);
    }
}

https://github.com/kfrlib/kfr/blob//include/kfr/dft/fft.hpp#L500

dft function

template <typename T, univector_tag Tag>
univector<complex<T>>
dft(const univector<complex<T>, Tag> &input)

Performs Direct DFT using cached plan

Source code
template <typename T, univector_tag Tag>
univector<complex<T>> dft(const univector<complex<T>, Tag>& input)
{
    dft_plan_ptr<T> dft = dft_cache::instance().get(ctype_t<T>(), input.size());
    univector<complex<T>> output(input.size(), std::numeric_limits<T>::quiet_NaN());
    univector<u8> temp(dft->temp_size);
    dft->execute(output, input, temp);
    return output;
}

https://github.com/kfrlib/kfr/blob//include/kfr/dft/cache.hpp#L130

dft_plan

dft_plan class

template <typename T> dft_plan

1D DFT/FFT

Source code
template <typename T>
struct dft_plan

https://github.com/kfrlib/kfr/blob//include/kfr/dft/fft.hpp#L116

dft_plan

template <typename T> dft_plan

1D DFT/FFT

Source code
template <typename T>
struct dft_plan
{
    size_t size;
    size_t temp_size;

    dft_plan()
        : size(0), temp_size(0), data_size(0), arblen(false), disposition_inplace{}, disposition_outofplace{}
    {
    }

    dft_plan(const dft_plan&)            = delete;
    dft_plan(dft_plan&&)                 = default;
    dft_plan& operator=(const dft_plan&) = delete;
    dft_plan& operator=(dft_plan&&)      = default;

    bool is_initialized() const { return size != 0; }

    explicit dft_plan(cpu_t cpu, size_t size, dft_order order = dft_order::normal)
        : size(size), temp_size(0), data_size(0), arblen(false)
    {
#ifdef KFR_DFT_MULTI
        if (cpu == cpu_t::runtime)
            cpu = get_cpu();
        switch (cpu)
        {
        case cpu_t::avx512:
            CMT_IF_ENABLED_AVX512(avx512::dft_initialize(*this); break;)
        case cpu_t::avx2:
            CMT_IF_ENABLED_AVX2(avx2::dft_initialize(*this); break;)
        case cpu_t::avx:
            CMT_IF_ENABLED_AVX(avx::dft_initialize(*this); break;)
        case cpu_t::sse42:
        case cpu_t::sse41:
            CMT_IF_ENABLED_SSE41(sse41::dft_initialize(*this); break;)
        case cpu_t::ssse3:
            CMT_IF_ENABLED_SSSE3(ssse3::dft_initialize(*this); break;)
        case cpu_t::sse3:
            CMT_IF_ENABLED_SSE3(sse3::dft_initialize(*this); break;)
        default:
            CMT_IF_ENABLED_SSE2(sse2::dft_initialize(*this); break;);
        }
#else
        (void)cpu;
        dft_initialize(*this);
#endif
    }
    explicit dft_plan(size_t size, dft_order order = dft_order::normal)
        : dft_plan(cpu_t::runtime, size, order)
    {
    }

    void dump() const
    {
        for (const std::unique_ptr<dft_stage<T>>& s : all_stages)
        {
            s->dump();
        }
    }

    KFR_MEM_INTRINSIC void execute(complex<T>* out, const complex<T>* in, u8* temp,
                                   bool inverse = false) const
    {
        if (inverse)
            execute_dft(ctrue, out, in, temp);
        else
            execute_dft(cfalse, out, in, temp);
    }
    ~dft_plan() {}
    template <bool inverse>
    KFR_MEM_INTRINSIC void execute(complex<T>* out, const complex<T>* in, u8* temp,
                                   cbool_t<inverse> inv) const
    {
        execute_dft(inv, out, in, temp);
    }

    template <univector_tag Tag1, univector_tag Tag2, univector_tag Tag3>
    KFR_MEM_INTRINSIC void execute(univector<complex<T>, Tag1>& out, const univector<complex<T>, Tag2>& in,
                                   univector<u8, Tag3>& temp, bool inverse = false) const
    {
        if (inverse)
            execute_dft(ctrue, out.data(), in.data(), temp.data());
        else
            execute_dft(cfalse, out.data(), in.data(), temp.data());
    }
    template <bool inverse, univector_tag Tag1, univector_tag Tag2, univector_tag Tag3>
    KFR_MEM_INTRINSIC void execute(univector<complex<T>, Tag1>& out, const univector<complex<T>, Tag2>& in,
                                   univector<u8, Tag3>& temp, cbool_t<inverse> inv) const
    {
        execute_dft(inv, out.data(), in.data(), temp.data());
    }

    autofree<u8> data;
    size_t data_size;

    std::vector<dft_stage_ptr<T>> all_stages;
    std::array<std::vector<dft_stage<T>*>, 2> stages;
    bool arblen;
    using bitset = std::bitset<DFT_MAX_STAGES>;
    std::array<bitset, 2> disposition_inplace;
    std::array<bitset, 2> disposition_outofplace;

    void calc_disposition()
    {
        for (bool inverse : { false, true })
        {
            auto&& stages = this->stages[inverse];
            bitset can_inplace_per_stage;
            for (int i = 0; i < stages.size(); ++i)
            {
                can_inplace_per_stage[i] = stages[i]->can_inplace;
            }

            disposition_inplace[static_cast<int>(inverse)] =
                precompute_disposition(stages.size(), can_inplace_per_stage, true);
            disposition_outofplace[static_cast<int>(inverse)] =
                precompute_disposition(stages.size(), can_inplace_per_stage, false);
        }
    }

    static bitset precompute_disposition(int num_stages, bitset can_inplace_per_stage, bool inplace_requested)
    {
        static bitset even{ 0x5555555555555555ull };
        bitset mask = ~bitset() >> (DFT_MAX_STAGES - num_stages);
        bitset result;
        // disposition indicates where is input for corresponding stage
        // first bit : 0 - input,  1 - scratch
        // other bits: 0 - output, 1 - scratch

        // build disposition that works always
        if (num_stages % 2 == 0)
        { // even
            result = ~even & mask;
        }
        else
        { // odd
            result = even & mask;
        }

        int num_inplace = can_inplace_per_stage.count();

#ifdef KFR_DFT_ELIMINATE_MEMCPY
        if (num_inplace > 0 && inplace_requested)
        {
            if (result.test(0)) // input is in scratch
            {
                // num_inplace must be odd
                if (num_inplace % 2 == 0)
                    --num_inplace;
            }
            else
            {
                // num_inplace must be even
                if (num_inplace % 2 != 0)
                    --num_inplace;
            }
        }
#endif
        if (num_inplace > 0)
        {
            for (int i = num_stages - 1; i >= 0; --i)
            {
                if (can_inplace_per_stage.test(i))
                {
                    result ^= ~bitset() >> (DFT_MAX_STAGES - (i + 1));

                    if (--num_inplace == 0)
                        break;
                }
            }
        }

        if (!inplace_requested) // out-of-place first stage; IN->OUT
            result.reset(0);

        return result;
    }

protected:
    struct noinit
    {
    };
    explicit dft_plan(noinit, size_t size, dft_order order = dft_order::normal)
        : size(size), temp_size(0), data_size(0), arblen(false)
    {
    }
    const complex<T>* select_in(bitset disposition, size_t stage, const complex<T>* out, const complex<T>* in,
                                const complex<T>* scratch) const
    {
        return disposition.test(stage) ? scratch : stage == 0 ? in : out;
    }
    complex<T>* select_out(bitset disposition, size_t stage, size_t total_stages, complex<T>* out,
                           complex<T>* scratch) const
    {
        return stage == total_stages - 1 ? out : disposition.test(stage + 1) ? scratch : out;
    }

    template <bool inverse>
    void execute_dft(cbool_t<inverse>, complex<T>* out, const complex<T>* in, u8* temp) const
    {
        auto&& stages = this->stages[inverse];
        if (stages.size() == 1 && (stages[0]->can_inplace || in != out))
        {
            return stages[0]->execute(cbool<inverse>, out, in, temp);
        }
        size_t stack[DFT_MAX_STAGES] = { 0 };

        bitset disposition =
            in == out ? this->disposition_inplace[inverse] : this->disposition_outofplace[inverse];

        complex<T>* scratch = ptr_cast<complex<T>>(
            temp + this->temp_size -
            align_up(sizeof(complex<T>) * this->size, platform<>::native_cache_alignment));

        bool in_scratch = disposition.test(0);
        if (in_scratch)
        {
            builtin_memcpy(scratch, in, sizeof(complex<T>) * this->size);
        }

        const size_t count = stages.size();

        for (size_t depth = 0; depth < count;)
        {
            if (stages[depth]->recursion)
            {
                size_t offset   = 0;
                size_t rdepth   = depth;
                size_t maxdepth = depth;
                do
                {
                    if (stack[rdepth] == stages[rdepth]->repeats)
                    {
                        stack[rdepth] = 0;
                        rdepth--;
                    }
                    else
                    {
                        complex<T>* rout      = select_out(disposition, rdepth, stages.size(), out, scratch);
                        const complex<T>* rin = select_in(disposition, rdepth, out, in, scratch);
                        stages[rdepth]->execute(cbool<inverse>, rout + offset, rin + offset, temp);
                        offset += stages[rdepth]->out_offset;
                        stack[rdepth]++;
                        if (rdepth < count - 1 && stages[rdepth + 1]->recursion)
                            rdepth++;
                        else
                            maxdepth = rdepth;
                    }
                } while (rdepth != depth);
                depth = maxdepth + 1;
            }
            else
            {
                size_t offset = 0;
                while (offset < this->size)
                {
                    stages[depth]->execute(
                        cbool<inverse>, select_out(disposition, depth, stages.size(), out, scratch) + offset,
                        select_in(disposition, depth, out, in, scratch) + offset, temp);
                    offset += stages[depth]->stage_size;
                }
                depth++;
            }
        }
    }
}

https://github.com/kfrlib/kfr/blob//include/kfr/dft/fft.hpp#L132

dft_plan_real

dft_plan_real class

template <typename T> dft_plan_real

Real-to-complex and Complex-to-real 1D DFT

Source code
template <typename T>
struct dft_plan_real

https://github.com/kfrlib/kfr/blob//include/kfr/dft/fft.hpp#L119

dft_plan_real

template <typename T> dft_plan_real

Real-to-complex and Complex-to-real 1D DFT

Source code
template <typename T>
struct dft_plan_real : dft_plan<T>
{
    size_t size;
    dft_pack_format fmt;

    dft_plan_real() : size(0), fmt(dft_pack_format::CCs) {}

    dft_plan_real(const dft_plan_real&)            = delete;
    dft_plan_real(dft_plan_real&&)                 = default;
    dft_plan_real& operator=(const dft_plan_real&) = delete;
    dft_plan_real& operator=(dft_plan_real&&)      = default;

    bool is_initialized() const { return size != 0; }

    explicit dft_plan_real(cpu_t cpu, size_t size, dft_pack_format fmt = dft_pack_format::CCs)
        : dft_plan<T>(typename dft_plan<T>::noinit{}, size / 2), size(size), fmt(fmt)
    {
        KFR_LOGIC_CHECK(is_even(size), "dft_plan_real requires size to be even");
#ifdef KFR_DFT_MULTI
        if (cpu == cpu_t::runtime)
            cpu = get_cpu();
        switch (cpu)
        {
        case cpu_t::avx512:
            CMT_IF_ENABLED_AVX512(avx512::dft_real_initialize(*this); break;)
        case cpu_t::avx2:
            CMT_IF_ENABLED_AVX2(avx2::dft_real_initialize(*this); break;)
        case cpu_t::avx:
            CMT_IF_ENABLED_AVX(avx::dft_real_initialize(*this); break;)
        case cpu_t::sse42:
        case cpu_t::sse41:
            CMT_IF_ENABLED_SSE41(sse41::dft_real_initialize(*this); break;)
        case cpu_t::ssse3:
            CMT_IF_ENABLED_SSSE3(ssse3::dft_real_initialize(*this); break;)
        case cpu_t::sse3:
            CMT_IF_ENABLED_SSE3(sse3::dft_real_initialize(*this); break;)
        default:
            CMT_IF_ENABLED_SSE2(sse2::dft_real_initialize(*this); break;);
        }
#else
        (void)cpu;
        dft_real_initialize(*this);
#endif
    }

    explicit dft_plan_real(size_t size, dft_pack_format fmt = dft_pack_format::CCs)
        : dft_plan_real(cpu_t::runtime, size, fmt)
    {
    }

    void execute(complex<T>*, const complex<T>*, u8*, bool = false) const = delete;

    template <bool inverse>
    void execute(complex<T>*, const complex<T>*, u8*, cbool_t<inverse>) const = delete;

    template <univector_tag Tag1, univector_tag Tag2, univector_tag Tag3>
    void execute(univector<complex<T>, Tag1>&, const univector<complex<T>, Tag2>&, univector<u8, Tag3>&,
                 bool = false) const = delete;

    template <bool inverse, univector_tag Tag1, univector_tag Tag2, univector_tag Tag3>
    void execute(univector<complex<T>, Tag1>&, const univector<complex<T>, Tag2>&, univector<u8, Tag3>&,
                 cbool_t<inverse>) const = delete;

    KFR_MEM_INTRINSIC void execute(complex<T>* out, const T* in, u8* temp, cdirect_t = {}) const
    {
        this->execute_dft(cfalse, out, ptr_cast<complex<T>>(in), temp);
    }
    KFR_MEM_INTRINSIC void execute(T* out, const complex<T>* in, u8* temp, cinvert_t = {}) const
    {
        this->execute_dft(ctrue, ptr_cast<complex<T>>(out), in, temp);
    }

    template <univector_tag Tag1, univector_tag Tag2, univector_tag Tag3>
    KFR_MEM_INTRINSIC void execute(univector<complex<T>, Tag1>& out, const univector<T, Tag2>& in,
                                   univector<u8, Tag3>& temp, cdirect_t = {}) const
    {
        this->execute_dft(cfalse, out.data(), ptr_cast<complex<T>>(in.data()), temp.data());
    }
    template <univector_tag Tag1, univector_tag Tag2, univector_tag Tag3>
    KFR_MEM_INTRINSIC void execute(univector<T, Tag1>& out, const univector<complex<T>, Tag2>& in,
                                   univector<u8, Tag3>& temp, cinvert_t = {}) const
    {
        this->execute_dft(ctrue, ptr_cast<complex<T>>(out.data()), in.data(), temp.data());
    }

    // Deprecated. fmt must be passed to constructor instead
    void execute(complex<T>*, const T*, u8*, dft_pack_format) const = delete;
    void execute(T*, const complex<T>*, u8*, dft_pack_format) const = delete;

    // Deprecated. fmt must be passed to constructor instead
    template <univector_tag Tag1, univector_tag Tag2, univector_tag Tag3>
    void execute(univector<complex<T>, Tag1>&, const univector<T, Tag2>&, univector<u8, Tag3>&,
                 dft_pack_format) const = delete;
    template <univector_tag Tag1, univector_tag Tag2, univector_tag Tag3>
    void execute(univector<T, Tag1>&, const univector<complex<T>, Tag2>&, univector<u8, Tag3>&,
                 dft_pack_format) const = delete;
}

https://github.com/kfrlib/kfr/blob//include/kfr/dft/fft.hpp#L400

fft_inverse

fft_inverse class

template <typename E> fft_inverse

[0, N - 1, N - 2, N - 3, ..., 3, 2, 1]

Source code
template <typename E>
struct fft_inverse : expression_with_traits<E>
{
    using value_type = typename expression_with_traits<E>::value_type;

    KFR_MEM_INTRINSIC fft_inverse(E&& expr) CMT_NOEXCEPT : expression_with_traits<E>(std::forward<E>(expr)) {}

    friend KFR_INTRINSIC vec<value_type, 1> get_elements(const fft_inverse& self, shape<1> index,
                                                         axis_params<0, 1>)
    {
        const size_t size = get_shape(self).front();
        return get_elements(self.first(), index.front() == 0 ? 0 : size - index, axis_params<0, 1>());
    }

    template <size_t N>
    friend KFR_MEM_INTRINSIC vec<value_type, N> get_elements(const fft_inverse& self, shape<1> index,
                                                             axis_params<0, N>)
    {
        const size_t size = get_shape(self).front();
        if (index.front() == 0)
        {
            return concat(get_elements(self.first(), index, axis_params<0, 1>()),
                          reverse(get_elements(self.first(), size - (N - 1), axis_params<0, N - 1>())));
        }
        return reverse(get_elements(self.first(), size - index - (N - 1), axis_params<0, N>()));
    }
}

https://github.com/kfrlib/kfr/blob//include/kfr/dft/impl/dft-impl.hpp#L166

idft function

template <typename T, univector_tag Tag>
univector<complex<T>>
idft(const univector<complex<T>, Tag> &input)

Performs Inverse DFT using cached plan

Source code
template <typename T, univector_tag Tag>
univector<complex<T>> idft(const univector<complex<T>, Tag>& input)
{
    dft_plan_ptr<T> dft = dft_cache::instance().get(ctype_t<T>(), input.size());
    univector<complex<T>> output(input.size(), std::numeric_limits<T>::quiet_NaN());
    univector<u8> temp(dft->temp_size);
    dft->execute(output, input, temp, ctrue);
    return output;
}

https://github.com/kfrlib/kfr/blob//include/kfr/dft/cache.hpp#L141

irealdft function

template <typename T, univector_tag Tag>
univector<T>
irealdft(const univector<complex<T>, Tag> &input)

Permorms Real Inverse DFT using cached plan

Source code
template <typename T, univector_tag Tag>
univector<T> irealdft(const univector<complex<T>, Tag>& input)
{
    dft_plan_real_ptr<T> dft = dft_cache::instance().getreal(ctype_t<T>(), (input.size() - 1) * 2);
    univector<T> output((input.size() - 1) * 2, std::numeric_limits<T>::quiet_NaN());
    univector<u8> temp(dft->temp_size);
    dft->execute(output, input, temp);
    return output;
}

https://github.com/kfrlib/kfr/blob//include/kfr/dft/cache.hpp#L163

realdft function

template <typename T, univector_tag Tag>
univector<complex<T>>
realdft(const univector<T, Tag> &input)

Performs Real Direct DFT using cached plan

Source code
template <typename T, univector_tag Tag>
univector<complex<T>> realdft(const univector<T, Tag>& input)
{
    dft_plan_real_ptr<T> dft = dft_cache::instance().getreal(ctype_t<T>(), input.size());
    univector<complex<T>> output(input.size() / 2 + 1, std::numeric_limits<T>::quiet_NaN());
    univector<u8> temp(dft->temp_size);
    dft->execute(output, input, temp);
    return output;
}

https://github.com/kfrlib/kfr/blob//include/kfr/dft/cache.hpp#L152

reference_dft function

template <typename Tnumber = double, typename T>
void reference_dft(complex<T> *out, const complex<T> *in,
                   size_t size, bool inversion = false)

Performs Complex DFT using reference implementation (slow, used for testing)

Source code
template <typename Tnumber = double, typename T>
void reference_dft(complex<T>* out, const complex<T>* in, size_t size, bool inversion = false)
{
    using std::cos;
    using std::sin;
    if (is_poweroftwo(size))
    {
        return reference_fft<Tnumber>(out, in, size, inversion);
    }
    constexpr Tnumber pi2 = c_pi<Tnumber, 2>;
    if (size < 2)
        return;
    std::vector<complex<T>> datain;
    if (out == in)
    {
        datain.resize(size);
        std::copy_n(in, size, datain.begin());
        in = datain.data();
    }
    {
        Tnumber sumr = 0;
        Tnumber sumi = 0;
        for (size_t j = 0; j < size; j++)
        {
            sumr += static_cast<Tnumber>(in[j].real());
            sumi += static_cast<Tnumber>(in[j].imag());
        }
        out[0] = { static_cast<T>(sumr), static_cast<T>(sumi) };
    }
    for (size_t i = 1; i < size; i++)
    {
        Tnumber sumr = static_cast<Tnumber>(in[0].real());
        Tnumber sumi = static_cast<Tnumber>(in[0].imag());

        for (size_t j = 1; j < size; j++)
        {
            const Tnumber x = pi2 * ((i * j) % size) / size;
            Tnumber twr     = cos(x);
            Tnumber twi     = sin(x);
            if (inversion)
                twi = -twi;

            sumr += twr * static_cast<Tnumber>(in[j].real()) + twi * static_cast<Tnumber>(in[j].imag());
            sumi += twr * static_cast<Tnumber>(in[j].imag()) - twi * static_cast<Tnumber>(in[j].real());
            out[i] = { static_cast<T>(sumr), static_cast<T>(sumi) };
        }
    }
}

https://github.com/kfrlib/kfr/blob//include/kfr/dft/reference_dft.hpp#L107

template <typename T>
void reference_dft(complex<T> *out, const T *in,
                   size_t size)

Performs Direct Real DFT using reference implementation (slow, used for testing)

Source code
template <typename T>
void reference_dft(complex<T>* out, const T* in, size_t size)
{
    if (size < 1)
        return;
    std::vector<complex<T>> datain(size);
    std::copy(in, in + size, datain.begin());
    reference_dft(out, datain.data(), size, false);
}

https://github.com/kfrlib/kfr/blob//include/kfr/dft/reference_dft.hpp#L157

template <typename T>
void reference_dft(T *out, const complex<T> *in,
                   size_t size)

Performs Inverse Real DFT using reference implementation (slow, used for testing)

Source code
template <typename T>
void reference_dft(T* out, const complex<T>* in, size_t size)
{
    if (size < 1)
        return;
    std::vector<complex<T>> dataout(size);
    reference_dft(dataout.data(), in, size, true);
    for (size_t i = 0; i < size; i++)
        out[i] = dataout[i].real();
}

https://github.com/kfrlib/kfr/blob//include/kfr/dft/reference_dft.hpp#L168

template <typename Tnumber = double, typename T>
inline univector<complex<T>>
reference_dft(const univector<complex<T>> &in,
              bool inversion = false)

Performs DFT using reference implementation (slow, used for testing)

Source code
template <typename Tnumber = double, typename T>
inline univector<complex<T>> reference_dft(const univector<complex<T>>& in, bool inversion = false)
{
    univector<complex<T>> out(in.size());
    reference_dft(&out[0], &in[0], in.size(), inversion);
    return out;
}

https://github.com/kfrlib/kfr/blob//include/kfr/dft/reference_dft.hpp#L180

reference_fft function

template <typename Tnumber = double, typename T>
void reference_fft(complex<T> *out, const complex<T> *in,
                   size_t size, bool inversion = false)

Performs Complex FFT using reference implementation (slow, used for testing)

Source code
template <typename Tnumber = double, typename T>
void reference_fft(complex<T>* out, const complex<T>* in, size_t size, bool inversion = false)
{
    using Tcmplx = Tnumber(*)[2];
    if (size < 1)
        return;
    if (size == 1)
    {
        out[0] = in[0];
        return;
    }
    std::vector<complex<Tnumber>> datain(size);
    std::vector<complex<Tnumber>> dataout(size);
    std::vector<complex<Tnumber>> temp(size);
    std::copy(in, in + size, datain.begin());
    const Tnumber pi2 = c_pi<Tnumber, 2, 1>;
    reference_fft_pass<Tnumber>(pi2, size, 0, 1, inversion ? -1 : +1, Tcmplx(datain.data()),
                                Tcmplx(dataout.data()), Tcmplx(temp.data()));
    std::copy(dataout.begin(), dataout.end(), out);
}

https://github.com/kfrlib/kfr/blob//include/kfr/dft/reference_dft.hpp#L85


Auto-generated from sources, Revision , https://github.com/kfrlib/kfr/blob//include/kfr/