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/