kfr

Fast, modern C++ DSP framework, FFT, Sample Rate Conversion, FIR/IIR/Biquad Filters (SSE, AVX, AVX-512, ARM NEON)
Log | Files | Refs | README

commit 55e7bb014cd21b1fb72d6a9e171023cef1656f0e
parent 445452b3275043ae99b5bb767cc1e6828e19aaf8
Author: d.levin256@gmail.com <d.levin256@gmail.com>
Date:   Sun,  3 Dec 2023 23:49:13 +0000

Move some tests to internal/

Diffstat:
MCMakeLists.txt | 45++++++++++++++++++++++-----------------------
Minclude/kfr/base.hpp | 2++
Minclude/kfr/dft.hpp | 1+
Minclude/kfr/dsp.hpp | 1-
Minclude/kfr/dsp/delay.hpp | 15+++++++++++++++
Dinclude/kfr/dsp/fracdelay.hpp | 45---------------------------------------------
Minclude/kfr/math/impl/atan.hpp | 1+
Msources.cmake | 13++++++++++---
Mtests/CMakeLists.txt | 9++-------
Mtests/all_tests.cpp | 18------------------
Dtests/all_tests_merged.cpp | 25-------------------------
Dtests/base_test.cpp | 122-------------------------------------------------------------------------------
Dtests/complex_test.cpp | 233-------------------------------------------------------------------------------
Dtests/dsp_test.cpp | 137-------------------------------------------------------------------------------
Dtests/expression_test.cpp | 53-----------------------------------------------------
Rtests/asm_test.cpp -> tests/internal/asm_test.cpp | 0
Rtests/generate_data.cpp -> tests/internal/generate_data.cpp | 0
Dtests/io_test.cpp | 85-------------------------------------------------------------------------------
Mtests/unit/base/generators.cpp | 16++++++++++++++++
Atests/unit/base/math_expressions.cpp | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atests/unit/base/simd_expressions.cpp | 33+++++++++++++++++++++++++++++++++
Atests/unit/base/univector.cpp | 44++++++++++++++++++++++++++++++++++++++++++++
Atests/unit/cometa.cpp | 19+++++++++++++++++++
Atests/unit/dsp/delay.cpp | 47+++++++++++++++++++++++++++++++++++++++++++++++
Atests/unit/dsp/goertzel.cpp | 38++++++++++++++++++++++++++++++++++++++
Atests/unit/dsp/mixdown.cpp | 36++++++++++++++++++++++++++++++++++++
Atests/unit/dsp/oscillators.cpp | 31+++++++++++++++++++++++++++++++
Atests/unit/io/audiofile.cpp | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtests/unit/math/asin_acos.cpp | 2+-
Mtests/unit/math/atan.cpp | 2+-
Atests/unit/math/complex_math.cpp | 84+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtests/unit/math/hyperbolic.cpp | 2+-
Mtests/unit/math/log_exp.cpp | 2+-
Mtests/unit/math/sin_cos.cpp | 2+-
Mtests/unit/math/tan.cpp | 2+-
Rtests/numeric_tests.hpp -> tests/unit/numeric_tests.hpp | 0
Mtests/unit/simd/complex.cpp | 98+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtests/unit/simd/shuffle.cpp | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
38 files changed, 716 insertions(+), 758 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt @@ -107,23 +107,6 @@ if (KFR_ENABLE_CAPI_BUILD AND NOT KFR_ENABLE_DFT_NP) "KFR_ENABLE_CAPI_BUILD requires KFR_ENABLE_DFT_NP to be enabled") endif () -function (add_arch_library NAME ARCH SRCS DEFS) - add_library(${NAME}_${ARCH} ${SRCS}) - target_link_libraries(${NAME}_${ARCH} kfr) - target_set_arch(${NAME}_${ARCH} PRIVATE ${ARCH}) - target_compile_options(${NAME}_${ARCH} PRIVATE ${DEFS}) - target_link_libraries(${NAME}_all INTERFACE ${NAME}_${ARCH}) -endfunction () - -add_subdirectory(src/dsp) -add_subdirectory(src/io) -if (KFR_ENABLE_DFT) - add_subdirectory(src/dft) -endif () -if (KFR_ENABLE_CAPI_BUILD) - add_subdirectory(src/capi) -endif () - if (NOT KFR_ARCH) set(KFR_ARCH target) endif () @@ -179,7 +162,7 @@ if (KFR_ARCH IN_LIST DETECT_NAMES) else () message( STATUS - "Default CPU architecture for KFR is ${KFR_ARCH} (set by cmake variable)" + "Default CPU architecture for KFR is ${KFR_ARCH} (set by KFR_ARCH)" ) endif () @@ -195,7 +178,19 @@ if (NOT KFR_ARCHS) endif () endif () -string (REPLACE ";" ", " KFR_ARCHS_COMMA "${KFR_ARCHS}") +if (KFR_ENABLE_MULTIARCH) + message( + STATUS + "Runtime dispatch is enabled for architectures: ${KFR_ARCHS} (set KFR_ARCHS to change, set KFR_ENABLE_MULTIARCH=OFF to disable)" + ) +else () + message( + STATUS + "Runtime dispatch is disabled. Set KFR_ENABLE_MULTIARCH=ON to enable" + ) +endif () + +string(REPLACE ";" ", " KFR_ARCHS_COMMA "${KFR_ARCHS}") if (KFR_ENABLE_MULTIARCH) add_compile_definitions(KFR_ENABLED_ARCHS="${KFR_ARCHS_COMMA}") @@ -260,10 +255,14 @@ if (KFR_EXTENDED_TESTS) target_compile_definitions(kfr INTERFACE -DKFR_EXTENDED_TESTS) endif () -# if (X86) add_executable(detect_cpu -# ${CMAKE_CURRENT_SOURCE_DIR}/cmake/detect_cpu.cpp) -# target_link_libraries(detect_cpu PRIVATE kfr) target_set_arch(detect_cpu -# PRIVATE generic) endif () +add_subdirectory(src/dsp) +add_subdirectory(src/io) +if (KFR_ENABLE_DFT) + add_subdirectory(src/dft) +endif () +if (KFR_ENABLE_CAPI_BUILD) + add_subdirectory(src/capi) +endif () if (ENABLE_EXAMPLES) add_subdirectory(examples) diff --git a/include/kfr/base.hpp b/include/kfr/base.hpp @@ -26,6 +26,7 @@ #include "base/basic_expressions.hpp" #include "base/conversion.hpp" +#include "base/endianness.hpp" #include "base/expression.hpp" #include "base/filter.hpp" #include "base/fraction.hpp" @@ -41,4 +42,5 @@ #include "base/small_buffer.hpp" #include "base/state_holder.hpp" #include "base/tensor.hpp" +#include "base/transpose.hpp" #include "base/univector.hpp" diff --git a/include/kfr/dft.hpp b/include/kfr/dft.hpp @@ -24,6 +24,7 @@ #include "base.hpp" +#include "dft/cache.hpp" #include "dft/convolution.hpp" #include "dft/fft.hpp" #include "dft/reference_dft.hpp" diff --git a/include/kfr/dsp.hpp b/include/kfr/dsp.hpp @@ -31,7 +31,6 @@ #include "dsp/ebu.hpp" #include "dsp/fir.hpp" #include "dsp/fir_design.hpp" -#include "dsp/fracdelay.hpp" #include "dsp/goertzel.hpp" #include "dsp/iir_design.hpp" #include "dsp/mixdown.hpp" diff --git a/include/kfr/dsp/delay.hpp b/include/kfr/dsp/delay.hpp @@ -29,6 +29,7 @@ #include "../base/expression.hpp" #include "../base/state_holder.hpp" #include "../base/univector.hpp" +#include "fir.hpp" namespace kfr { @@ -180,5 +181,19 @@ KFR_INTRINSIC expression_delay<samples, E1, true, STag> delay(delay_state<T, sam static_assert(STag == tag_dynamic_vector || (samples >= 1 && samples < 1024), ""); return expression_delay<samples, E1, true, STag>(std::forward<E1>(e1), state); } + +/** + * @brief Returns template expression that applies a fractional delay to the input + * @param e1 an input expression + * @param e1 a fractional delay in range 0..1 + */ +template <typename T, typename E1> +KFR_INTRINSIC expression_short_fir<2, T, expression_value_type<E1>, E1> fracdelay(E1&& e1, T delay) +{ + if (CMT_UNLIKELY(delay < 0)) + delay = 0; + univector<T, 2> taps({ 1 - delay, delay }); + return expression_short_fir<2, T, expression_value_type<E1>, E1>(std::forward<E1>(e1), taps); +} } // namespace CMT_ARCH_NAME } // namespace kfr diff --git a/include/kfr/dsp/fracdelay.hpp b/include/kfr/dsp/fracdelay.hpp @@ -1,45 +0,0 @@ -/** @addtogroup fir - * @{ - */ -/* - Copyright (C) 2016-2023 Dan Cazarin (https://www.kfrlib.com) - This file is part of KFR - - KFR is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 2 of the License, or - (at your option) any later version. - - KFR is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with KFR. - - If GPL is not suitable for your project, you must purchase a commercial license to use KFR. - Buying a commercial license is mandatory as soon as you develop commercial activities without - disclosing the source code of your own applications. - See https://www.kfrlib.com for details. - */ -#pragma once - -#include "fir.hpp" - -namespace kfr -{ - -inline namespace CMT_ARCH_NAME -{ - -template <typename T, typename E1> -KFR_INTRINSIC expression_short_fir<2, T, expression_value_type<E1>, E1> fracdelay(E1&& e1, T delay) -{ - if (CMT_UNLIKELY(delay < 0)) - delay = 0; - univector<T, 2> taps({ 1 - delay, delay }); - return expression_short_fir<2, T, expression_value_type<E1>, E1>(std::forward<E1>(e1), taps); -} -} // namespace CMT_ARCH_NAME -} // namespace kfr diff --git a/include/kfr/math/impl/atan.hpp b/include/kfr/math/impl/atan.hpp @@ -25,6 +25,7 @@ #include "../../simd/constants.hpp" #include "../../simd/impl/function.hpp" #include "../../simd/operators.hpp" +#include "../../simd/comparison.hpp" #include "../../simd/select.hpp" #include "../sin_cos.hpp" diff --git a/sources.cmake b/sources.cmake @@ -64,7 +64,6 @@ set( ${PROJECT_SOURCE_DIR}/include/kfr/dsp/ebu.hpp ${PROJECT_SOURCE_DIR}/include/kfr/dsp/fir.hpp ${PROJECT_SOURCE_DIR}/include/kfr/dsp/fir_design.hpp - ${PROJECT_SOURCE_DIR}/include/kfr/dsp/fracdelay.hpp ${PROJECT_SOURCE_DIR}/include/kfr/dsp/goertzel.hpp ${PROJECT_SOURCE_DIR}/include/kfr/dsp/iir_design.hpp ${PROJECT_SOURCE_DIR}/include/kfr/dsp/mixdown.hpp @@ -262,7 +261,6 @@ set( ${PROJECT_SOURCE_DIR}/include/kfr/dsp/ebu.hpp ${PROJECT_SOURCE_DIR}/include/kfr/dsp/fir.hpp ${PROJECT_SOURCE_DIR}/include/kfr/dsp/fir_design.hpp - ${PROJECT_SOURCE_DIR}/include/kfr/dsp/fracdelay.hpp ${PROJECT_SOURCE_DIR}/include/kfr/dsp/goertzel.hpp ${PROJECT_SOURCE_DIR}/include/kfr/dsp/iir_design.hpp ${PROJECT_SOURCE_DIR}/include/kfr/dsp/mixdown.hpp @@ -363,7 +361,6 @@ set( ${PROJECT_SOURCE_DIR}/include/kfr/dsp/ebu.hpp ${PROJECT_SOURCE_DIR}/include/kfr/dsp/fir.hpp ${PROJECT_SOURCE_DIR}/include/kfr/dsp/fir_design.hpp - ${PROJECT_SOURCE_DIR}/include/kfr/dsp/fracdelay.hpp ${PROJECT_SOURCE_DIR}/include/kfr/dsp/goertzel.hpp ${PROJECT_SOURCE_DIR}/include/kfr/dsp/iir_design.hpp ${PROJECT_SOURCE_DIR}/include/kfr/dsp/mixdown.hpp @@ -481,30 +478,40 @@ set( set( KFR_UNITTEST_SRC + ${PROJECT_SOURCE_DIR}/tests/unit/cometa.cpp ${PROJECT_SOURCE_DIR}/tests/unit/base/base.cpp ${PROJECT_SOURCE_DIR}/tests/unit/base/basic_expressions.cpp ${PROJECT_SOURCE_DIR}/tests/unit/base/conversion.cpp ${PROJECT_SOURCE_DIR}/tests/unit/base/fraction.cpp ${PROJECT_SOURCE_DIR}/tests/unit/base/generators.cpp ${PROJECT_SOURCE_DIR}/tests/unit/base/handle.cpp + ${PROJECT_SOURCE_DIR}/tests/unit/base/math_expressions.cpp ${PROJECT_SOURCE_DIR}/tests/unit/base/random.cpp ${PROJECT_SOURCE_DIR}/tests/unit/base/reduce.cpp ${PROJECT_SOURCE_DIR}/tests/unit/base/shape.cpp + ${PROJECT_SOURCE_DIR}/tests/unit/base/simd_expressions.cpp ${PROJECT_SOURCE_DIR}/tests/unit/base/std_ambiguities.cpp ${PROJECT_SOURCE_DIR}/tests/unit/base/tensor.cpp + ${PROJECT_SOURCE_DIR}/tests/unit/base/univector.cpp ${PROJECT_SOURCE_DIR}/tests/unit/dsp/biquad.cpp ${PROJECT_SOURCE_DIR}/tests/unit/dsp/biquad_design.cpp + ${PROJECT_SOURCE_DIR}/tests/unit/dsp/delay.cpp ${PROJECT_SOURCE_DIR}/tests/unit/dsp/dsp.cpp ${PROJECT_SOURCE_DIR}/tests/unit/dsp/ebu.cpp ${PROJECT_SOURCE_DIR}/tests/unit/dsp/fir.cpp + ${PROJECT_SOURCE_DIR}/tests/unit/dsp/goertzel.cpp + ${PROJECT_SOURCE_DIR}/tests/unit/dsp/mixdown.cpp + ${PROJECT_SOURCE_DIR}/tests/unit/dsp/oscillators.cpp ${PROJECT_SOURCE_DIR}/tests/unit/dsp/sample_rate_conversion.cpp ${PROJECT_SOURCE_DIR}/tests/unit/dsp/units.cpp ${PROJECT_SOURCE_DIR}/tests/unit/dsp/window.cpp ${PROJECT_SOURCE_DIR}/tests/unit/graphics/color.cpp ${PROJECT_SOURCE_DIR}/tests/unit/graphics/geometry.cpp ${PROJECT_SOURCE_DIR}/tests/unit/graphics/graphics.cpp + ${PROJECT_SOURCE_DIR}/tests/unit/io/audiofile.cpp ${PROJECT_SOURCE_DIR}/tests/unit/math/asin_acos.cpp ${PROJECT_SOURCE_DIR}/tests/unit/math/atan.cpp + ${PROJECT_SOURCE_DIR}/tests/unit/math/complex_math.cpp ${PROJECT_SOURCE_DIR}/tests/unit/math/hyperbolic.cpp ${PROJECT_SOURCE_DIR}/tests/unit/math/log_exp.cpp ${PROJECT_SOURCE_DIR}/tests/unit/math/math.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt @@ -60,7 +60,7 @@ if (KFR_ENABLE_CAPI_BUILD) endif () if (KFR_ENABLE_ASMTEST) - add_executable(asm_test asm_test.cpp) + add_executable(asm_test internal/asm_test.cpp) target_link_libraries(asm_test kfr) target_set_arch(asm_test PRIVATE avx2) target_compile_definitions(asm_test PRIVATE KFR_SHOW_NOT_OPTIMIZED) @@ -80,11 +80,6 @@ if (KFR_ENABLE_ASMTEST) endif () set(ALL_TESTS_CPP - base_test.cpp - complex_test.cpp - dsp_test.cpp - expression_test.cpp - io_test.cpp ${KFR_UNITTEST_SRC}) if (KFR_ENABLE_DFT) @@ -98,7 +93,7 @@ if (KFR_REGENERATE_TESTS) find_package(GMP) if (MPFR_FOUND AND GMP_FOUND) - add_executable(generate_data generate_data.cpp) + add_executable(generate_data internal/generate_data.cpp) target_link_libraries(generate_data kfr) target_include_directories(generate_data PRIVATE ${MPFR_INCLUDE_DIR} ${GMP_INCLUDE_DIR}) diff --git a/tests/all_tests.cpp b/tests/all_tests.cpp @@ -13,24 +13,6 @@ using namespace kfr; -#ifdef KFR_MULTI_ARCH - -#define FORCE_LINK(arch) \ - namespace arch \ - { \ - extern void force_link(); \ - void (*p)() = &force_link; \ - } - -FORCE_LINK(sse2) -FORCE_LINK(sse3) -FORCE_LINK(ssse3) -FORCE_LINK(sse41) -FORCE_LINK(avx) -FORCE_LINK(avx2) -// FORCE_LINK(avx512) -#endif - int main() { println(library_version(), " running on ", cpu_runtime()); diff --git a/tests/all_tests_merged.cpp b/tests/all_tests_merged.cpp @@ -1,25 +0,0 @@ -#include <kfr/cident.h> - -CMT_PRAGMA_GNU(GCC diagnostic push) -CMT_PRAGMA_GNU(GCC diagnostic ignored "-Wparentheses") - -#include "auto_test.cpp" - -#include "base_test.cpp" -#include "complex_test.cpp" -#include "dsp_test.cpp" -#include "expression_test.cpp" -#include "intrinsic_test.cpp" -#include "io_test.cpp" -#include "resampler_test.cpp" - -#ifndef KFR_NO_DFT -#include "dft_test.cpp" -#endif - -namespace CMT_ARCH_NAME -{ -void force_link() {} -} // namespace CMT_ARCH_NAME - -CMT_PRAGMA_GNU(GCC diagnostic pop) diff --git a/tests/base_test.cpp b/tests/base_test.cpp @@ -1,122 +0,0 @@ -/** - * KFR (https://www.kfrlib.com) - * Copyright (C) 2016-2023 Dan Cazarin - * See LICENSE.txt for details - */ - -#include <kfr/testo/testo.hpp> - -// #include <kfr/io.hpp> -#include <kfr/base.hpp> - -using namespace kfr; - -namespace CMT_ARCH_NAME -{ - -TEST(test_basic) -{ - // How to make a vector: - - // * Use constructor - const vec<double, 4> first{ 1, 2.5, -infinity, 3.1415926 }; - CHECK(first == vec<double, 4>{ 1, 2.5, -infinity, 3.1415926 }); - - // * Use make_vector function - const auto second = make_vector(-1, +1); - CHECK(second == vec<int, 2>{ -1, 1 }); - - // * Convert from vector of other type: - const vec<int, 4> int_vector{ 10, 20, 30, 40 }; - const vec<double, 4> double_vector = cast<double>(int_vector); - CHECK(double_vector == vec<double, 4>{ 10, 20, 30, 40 }); - - // * Concat two vectors: - const vec<int, 1> left_part{ 1 }; - const vec<int, 1> right_part{ 2 }; - const vec<int, 2> pair{ left_part, right_part }; - CHECK(pair == vec<int, 2>{ 1, 2 }); - - // * Same, but using make_vector and concat: - const vec<int, 2> pair2 = concat(make_vector(10), make_vector(20)); - CHECK(pair2 == vec<int, 2>{ 10, 20 }); - - // * Repeat vector multiple times: - const vec<short, 8> repeated = repeat<4>(make_vector<short>(0, -1)); - CHECK(repeated == vec<short, 8>{ 0, -1, 0, -1, 0, -1, 0, -1 }); - - // * Use enumerate to generate sequence of numbers: - const vec<int, 8> eight = enumerate<int, 8>(); - CHECK(eight == vec<int, 8>{ 0, 1, 2, 3, 4, 5, 6, 7 }); - - // * Vectors can be of any length... - const vec<int, 1> one{ 42 }; - const vec<int, 2> two = concat(one, make_vector(42)); - CHECK(two == vec<int, 2>{ 42, 42 }); - - const vec<u8, 256> very_long_vector = repeat<64>(make_vector<u8>(1, 2, 4, 8)); - CHECK(slice<0, 17>(very_long_vector) == - vec<unsigned char, 17>{ 1, 2, 4, 8, 1, 2, 4, 8, 1, 2, 4, 8, 1, 2, 4, 8, 1 }); - - // * ...really any: - using big_vector = vec<i16, 107>; - big_vector v107 = enumerate<i16, 107>(); - CHECK(hadd(v107) == static_cast<short>(5671)); - - using color = vec<u8, 3>; - const color green = cast<u8>(make_vector(0.0, 1.0, 0.0) * 255); - CHECK(green == vec<unsigned char, 3>{ 0, 255, 0 }); - - // Vectors support all standard operators: - const auto op1 = make_vector(0, 1, 10, 100); - const auto op2 = make_vector(20, 2, -2, 200); - const auto result = op1 * op2 - 4; - CHECK(result == vec<int, 4>{ -4, -2, -24, 19996 }); - - // * Transform vector: - const vec<int, 8> numbers1 = enumerate<int, 8>(); - const vec<int, 8> numbers2 = enumerate<int, 8>() + 100; - CHECK(odd(numbers1) == vec<int, 4>{ 1, 3, 5, 7 }); - CHECK(even(numbers2) == vec<int, 4>{ 100, 102, 104, 106 }); - - CHECK(subadd(pack(0, 1, 2, 3, 4, 5, 6, 7), pack(10, 10, 10, 10, 10, 10, 10, 10)) == - pack(-10, 11, -8, 13, -6, 15, -4, 17)); - CHECK(addsub(pack(0, 1, 2, 3, 4, 5, 6, 7), pack(10, 10, 10, 10, 10, 10, 10, 10)) == - pack(10, -9, 12, -7, 14, -5, 16, -3)); - - CHECK(digitreverse4(pack(0.f, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)) == - pack(0.f, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15)); - - CHECK(inrange(pack(1, 2, 3), 1, 3) == make_mask<int>(true, true, true)); - CHECK(inrange(pack(1, 2, 3), 1, 2) == make_mask<int>(true, true, false)); - CHECK(inrange(pack(1, 2, 3), 1, 1) == make_mask<int>(true, false, false)); -} - -TEST(ctti) { CHECK(cometa::type_name<float>() == std::string("float")); } - -#ifdef KFR_USE_STD_ALLOCATION -TEST(std_allocation) -{ - univector<float> u; - std::vector<float>& v = u; - - std::vector<float> v2{ 1, 2, 3, 4 }; - - // Technically an UB but ok with all sane compilers - reinterpret_cast<univector<float>&>(v2) += 100.f; - CHECK(v2[0] == 101); - CHECK(v2[1] == 102); - CHECK(v2[2] == 103); - CHECK(v2[3] == 104); -} -#endif - -} // namespace CMT_ARCH_NAME - -#ifndef KFR_NO_MAIN -int main() -{ - println(library_version()); - return testo::run_all("", true); -} -#endif diff --git a/tests/complex_test.cpp b/tests/complex_test.cpp @@ -1,233 +0,0 @@ -/** - * KFR (https://www.kfrlib.com) - * Copyright (C) 2016-2023 Dan Cazarin - * See LICENSE.txt for details - */ - -#include <kfr/testo/testo.hpp> - -#include <complex> -#include <kfr/base.hpp> -#include <kfr/io.hpp> - -using namespace kfr; - -namespace CMT_ARCH_NAME -{ - -TEST(complex_vector) -{ - const vec<c32, 1> c32x1{ c32{ 0, 1 } }; - CHECK(c32x1.flatten()[0] == 0.0f); - CHECK(c32x1.flatten()[1] == 1.0f); - - const vec<c32, 2> c32x2{ c32{ 0, 1 }, c32{ 2, 3 } }; - CHECK(c32x2.flatten()[0] == 0.0f); - CHECK(c32x2.flatten()[1] == 1.0f); - CHECK(c32x2.flatten()[2] == 2.0f); - CHECK(c32x2.flatten()[3] == 3.0f); - - const vec<c32, 3> c32x3{ c32{ 0, 1 }, c32{ 2, 3 }, c32{ 4, 5 } }; - CHECK(c32x3.flatten()[0] == 0.0f); - CHECK(c32x3.flatten()[1] == 1.0f); - CHECK(c32x3.flatten()[2] == 2.0f); - CHECK(c32x3.flatten()[3] == 3.0f); - CHECK(c32x3.flatten()[4] == 4.0f); - CHECK(c32x3.flatten()[5] == 5.0f); - - const vec<c32, 1> c32s = 2; - CHECK(c32s.flatten()[0] == 2.f); - CHECK(c32s.flatten()[1] == 0.f); -} - -TEST(complex_cast) -{ - const vec<f32, 4> v1 = bitcast<f32>(make_vector(c32{ 0, 1 }, c32{ 2, 3 })); - CHECK(v1.flatten()[0] == 0.f); - CHECK(v1.flatten()[1] == 1.f); - CHECK(v1.flatten()[2] == 2.f); - CHECK(v1.flatten()[3] == 3.f); - - const vec<c32, 1> v2 = bitcast<c32>(make_vector(1.f, 2.f)); - CHECK(v2.flatten()[0] == 1.f); - CHECK(v2.flatten()[1] == 2.f); - - const vec<c32, 2> v3 = make_vector(1.f, 2.f); - CHECK(v3.flatten()[0] == 1.f); - CHECK(v3.flatten()[1] == 0.f); - CHECK(v3.flatten()[2] == 2.f); - CHECK(v3.flatten()[3] == 0.f); - - const vec<c32, 2> v4 = make_vector(1, 2); - CHECK(v4.flatten()[0] == 1.f); - CHECK(v4.flatten()[1] == 0.f); - CHECK(v4.flatten()[2] == 2.f); - CHECK(v4.flatten()[3] == 0.f); - - CHECK(zerovector<c32, 4>() == make_vector(c32{ 0, 0 }, c32{ 0, 0 }, c32{ 0, 0 }, c32{ 0, 0 })); - CHECK(enumerate<c32, 4>() == make_vector(c32{ 0, 0 }, c32{ 1, 0 }, c32{ 2, 0 }, c32{ 3, 0 })); -} - -TEST(complex_math) -{ - const vec<c32, 1> a{ c32{ 1, 2 } }; - const vec<c32, 1> b{ c32{ 3, 4 } }; - CHECK(c32(vec<c32, 1>(2)[0]) == c32{ 2, 0 }); - CHECK(a + b == make_vector(c32{ 4, 6 })); - CHECK(a - b == make_vector(c32{ -2, -2 })); - CHECK(a * b == make_vector(c32{ -5, 10 })); - CHECK(a * vec<c32, 1>(2) == make_vector(c32{ 2, 4 })); - CHECK(a * 2 == make_vector(c32{ 2, 4 })); - CHECK(a / b == make_vector(c32{ 0.44f, 0.08f })); - CHECK(-a == make_vector(c32{ -1, -2 })); - - CHECK(real(a) == make_vector(1.f)); - CHECK(imag(a) == make_vector(2.f)); - - CHECK(make_complex(5.f, 7) == c32{ 5.f, 7.f }); - CHECK(make_complex(make_vector(5.f, 8.f), make_vector(7.f, 9.f)) == - make_vector(c32{ 5.f, 7.f }, c32{ 8.f, 9.f })); - - CHECK(cabs(c32{ 3.f, 4.f }) == 5.f); - CHECK(cabs(make_vector(c32{ 3.f, 4.f })) == make_vector(5.f)); - - CHECK(cabs(-3.f) == 3.f); - CHECK(cabs(make_vector(-3.f)) == make_vector(3.f)); - - CHECK(carg(c32{ +1.f, 0.f }) == 0.f); - CHECK(carg(c32{ 0.f, +1.f }) == c_pi<float> / 2); - CHECK(carg(c32{ 0.f, -1.f }) == -c_pi<float> / 2); - CHECK(carg(c32{ -1.f, 0.f }) == c_pi<float>); - - testo::epsilon_scope<void> eps(5); - - CHECK(csin(c32{ 1.f, 1.f }) == c32{ 1.2984575814159773f, 0.634963914784736f }); - CHECK(ccos(c32{ 1.f, 1.f }) == c32{ 0.8337300251311489f, -0.9888977057628651f }); - CHECK(csinh(c32{ 1.f, 1.f }) == c32{ 0.634963914784736f, 1.2984575814159773f }); - CHECK(ccosh(c32{ 1.f, 1.f }) == c32{ 0.8337300251311489f, 0.9888977057628651f }); - - CHECK(clog(c32{ 1.f, 1.f }) == c32{ 0.34657359027997264f, 0.7853981633974483f }); - CHECK(clog2(c32{ 1.f, 1.f }) == c32{ 0.5f, 1.1330900354567983f }); - CHECK(clog10(c32{ 1.f, 1.f }) == c32{ 0.15051499783199057f, 0.3410940884604603f }); - - CHECK(cexp(c32{ 1.f, 1.f }) == c32{ 1.4686939399158849f, 2.2873552871788423f }); - CHECK(cexp2(c32{ 1.f, 1.f }) == c32{ 1.5384778027279442f, 1.2779225526272695f }); - CHECK(cexp10(c32{ 1.f, 1.f }) == c32{ -6.682015101903131f, 7.439803369574931f }); - -#ifdef CMT_NATIVE_F64 - CHECK(csin(c64{ 1.0, 1.0 }) == c64{ 1.2984575814159773, 0.634963914784736 }); - CHECK(ccos(c64{ 1.0, 1.0 }) == c64{ 0.8337300251311489, -0.9888977057628651 }); - CHECK(csinh(c64{ 1.0, 1.0 }) == c64{ 0.634963914784736, 1.2984575814159773 }); - CHECK(ccosh(c64{ 1.0, 1.0 }) == c64{ 0.8337300251311489, 0.9888977057628651 }); - CHECK(clog(c64{ 1.0, 1.0 }) == c64{ 0.34657359027997264, 0.7853981633974483 }); - CHECK(cexp(c64{ 1.0, 1.0 }) == c64{ 1.4686939399158849, 2.2873552871788423 }); -#endif -} - -TEST(complex_read_write) -{ - c32 buffer[8] = { c32{ 1, 2 }, c32{ 3, 4 }, c32{ 5, 6 }, c32{ 7, 8 }, - c32{ 9, 10 }, c32{ 11, 12 }, c32{ 13, 14 }, c32{ 15, 16 } }; - - CHECK(read<4>(buffer) == make_vector(c32{ 1, 2 }, c32{ 3, 4 }, c32{ 5, 6 }, c32{ 7, 8 })); - CHECK(read<3>(buffer + 1) == make_vector(c32{ 3, 4 }, c32{ 5, 6 }, c32{ 7, 8 })); - write(buffer + 2, make_vector(c32{ 10, 11 }, c32{ 12, 13 })); - CHECK(read<4>(buffer) == make_vector(c32{ 1, 2 }, c32{ 3, 4 }, c32{ 10, 11 }, c32{ 12, 13 })); -} - -TEST(complex_shuffle) -{ - const vec<c32, 2> a{ c32{ 0, 1 }, c32{ 2, 3 } }; - CHECK(reverse(a) == make_vector(c32{ 2, 3 }, c32{ 0, 1 })); -} - -TEST(complex_basic_expressions) -{ - const univector<c32, 15> uv1 = zeros(); - CHECK(uv1[0] == c32{ 0, 0 }); - CHECK(uv1[1] == c32{ 0, 0 }); - CHECK(uv1[2] == c32{ 0, 0 }); - CHECK(uv1[14] == c32{ 0, 0 }); - const univector<c32, 15> uv2 = ones(); - CHECK(uv2[0] == c32{ 1, 0 }); - CHECK(uv2[1] == c32{ 1, 0 }); - CHECK(uv2[2] == c32{ 1, 0 }); - CHECK(uv2[14] == c32{ 1, 0 }); - const univector<c32, 15> uv3 = counter(); - CHECK(uv3[0] == c32{ 0, 0 }); - CHECK(uv3[1] == c32{ 1, 0 }); - CHECK(uv3[2] == c32{ 2, 0 }); - CHECK(uv3[14] == c32{ 14, 0 }); -} - -TEST(complex_functions) -{ - CHECK(csqr(complex<f32>(4.f, 0.f)) == c32{ 16.f, 0.f }); - CHECK(csqrt(complex<f32>(16.f, 0.f)) == c32{ 4.f, 0.f }); - - CHECK(csqr(complex<f32>(1.f, 4.f)) == c32{ -15.f, 8.f }); - - CHECK(csqrt(complex<f32>(15.f, 8.f)) == c32{ 4.f, 1.f }); - CHECK(csqrt(complex<f32>(-15.f, 8.f)) == c32{ 1.f, 4.f }); - CHECK(csqrt(complex<f32>(15.f, -8.f)) == c32{ 4.f, -1.f }); - CHECK(csqrt(complex<f32>(-15.f, -8.f)) == c32{ 1.f, -4.f }); -} - -TEST(complex_function_expressions) -{ - const univector<c32, 4> uv1 = sqr(counter()); - CHECK(uv1[0] == c32{ 0, 0 }); - CHECK(uv1[1] == c32{ 1, 0 }); - CHECK(uv1[2] == c32{ 4, 0 }); - CHECK(uv1[3] == c32{ 9, 0 }); - - const univector<c32, 4> uv2 = uv1 * 2.f; - CHECK(uv2[0] == c32{ 0, 0 }); - CHECK(uv2[1] == c32{ 2, 0 }); - CHECK(uv2[2] == c32{ 8, 0 }); - CHECK(uv2[3] == c32{ 18, 0 }); - - const univector<f32, 4> uv3 = real(uv2); - CHECK(uv3[0] == 0.f); - CHECK(uv3[1] == 2.f); - CHECK(uv3[2] == 8.f); - CHECK(uv3[3] == 18.f); - testo::assert_is_same<c32, expression_value_type<decltype(uv2)>>(); - testo::assert_is_same<f32, expression_value_type<decltype(uv3)>>(); - testo::assert_is_same<f32, expression_value_type<decltype(real(uv2))>>(); -} - -TEST(static_tests) -{ - static_assert(is_numeric<vec<complex<float>, 4>>, ""); - static_assert(is_numeric_args<vec<complex<float>, 4>>, ""); - - static_assert(sizeof(vec<c32, 4>) == sizeof(vec<f32, 8>), ""); - static_assert(vec<f32, 4>::size() == 4, ""); - static_assert(vec<c32, 4>::size() == 4, ""); - static_assert(vec<f32, 4>::scalar_size() == 4, ""); - static_assert(vec<c32, 4>::scalar_size() == 8, ""); - testo::assert_is_same<subtype<complex<i32>>, i32>(); - testo::assert_is_same<vec<c32, 4>::value_type, c32>(); - testo::assert_is_same<vec<c32, 4>::scalar_type, f32>(); - testo::assert_is_same<vec<f32, 4>::value_type, f32>(); - testo::assert_is_same<vec<f32, 4>::scalar_type, f32>(); - testo::assert_is_same<vec<c32, 1>, decltype(make_vector(c32{ 0, 0 }))>(); - testo::assert_is_same<vec<c32, 2>, decltype(make_vector(c32{ 0, 0 }, 4))>(); - testo::assert_is_same<ftype<complex<i32>>, complex<f32>>(); - testo::assert_is_same<ftype<complex<i64>>, complex<f64>>(); - testo::assert_is_same<ftype<vec<complex<i32>, 4>>, vec<complex<f32>, 4>>(); - testo::assert_is_same<ftype<vec<complex<i64>, 8>>, vec<complex<f64>, 8>>(); - - testo::assert_is_same<std::common_type_t<complex<int>, double>, complex<double>>(); -} -} // namespace CMT_ARCH_NAME - -#ifndef KFR_NO_MAIN -int main() -{ - println(library_version()); - - return testo::run_all("", true); -} -#endif diff --git a/tests/dsp_test.cpp b/tests/dsp_test.cpp @@ -1,137 +0,0 @@ -/** - * KFR (https://www.kfrlib.com) - * Copyright (C) 2016-2023 Dan Cazarin - * See LICENSE.txt for details - */ - -#include <kfr/testo/testo.hpp> - -#include <kfr/base.hpp> -#include <kfr/dsp.hpp> -#include <kfr/io.hpp> - -#include <complex> -#include <numeric> - -using namespace kfr; - -namespace CMT_ARCH_NAME -{ - -TEST(goertzel) -{ - testo::epsilon_scope<float> e(100); - univector<float, 16> a; - a = sinenorm(phasor(0.125f)); - - float omega = c_pi<float, 2> * 0.125f; - - complex<float> c; - process(goertzel(c, omega), a); - CHECK(cabs(c) == 8.f); - - complex<float> cs[3]; - float omegas[3] = { omega, omega, omega }; - process(goertzel(cs, omegas), a); - println(cs[0]); - CHECK(cabs(cs[0]) == 8.f); - CHECK(cabs(cs[1]) == 8.f); - CHECK(cabs(cs[2]) == 8.f); -} - -TEST(delay) -{ - const univector<float, 33> v1 = counter() + 100; - CHECK_EXPRESSION(delay(v1), 33, [](size_t i) { return i < 1 ? 0.f : (i - 1) + 100.f; }); - - CHECK_EXPRESSION(delay<3>(v1), 33, [](size_t i) { return i < 3 ? 0.f : (i - 3) + 100.f; }); - - delay_state<float, 3> state1; - CHECK_EXPRESSION(delay(state1, v1), 33, [](size_t i) { return i < 3 ? 0.f : (i - 3) + 100.f; }); - - delay_state<float, 3, tag_dynamic_vector> state2; - CHECK_EXPRESSION(delay(state2, v1), 33, [](size_t i) { return i < 3 ? 0.f : (i - 3) + 100.f; }); -} - -TEST(fracdelay) -{ - univector<double, 5> a({ 1, 2, 3, 4, 5 }); - univector<double, 5> b = fracdelay(a, 0.5); - CHECK(rms(b - univector<double>({ 0.5, 1.5, 2.5, 3.5, 4.5 })) < constants<double>::epsilon * 5); - - b = fracdelay(a, 0.1); - CHECK(rms(b - univector<double>({ 0.9, 1.9, 2.9, 3.9, 4.9 })) < constants<double>::epsilon * 5); - - b = fracdelay(a, 0.0); - CHECK(rms(b - univector<double>({ 1, 2, 3, 4, 5 })) < constants<double>::epsilon * 5); - - b = fracdelay(a, 1.0); - CHECK(rms(b - univector<double>({ 0, 1, 2, 3, 4 })) < constants<double>::epsilon * 5); -} - -TEST(mixdown) -{ - CHECK_EXPRESSION(mixdown(counter(), counter() * 2 + 100), infinite_size, - [](size_t i) { return i + i * 2 + 100; }); -} - -TEST(mixdown_stereo) -{ - const univector<double, 21> left = counter(); - const univector<double, 21> right = counter() * 2 + 100; - univector<double, 21> mid; - univector<double, 21> side; - unpack(mid, side) = mixdown_stereo(left, right, matrix_sum_diff()); - - CHECK_EXPRESSION(mid, 21, [](size_t i) { return i + i * 2.0 + 100.0; }); - CHECK_EXPRESSION(side, 21, [](size_t i) { return i - (i * 2.0 + 100.0); }); -} - -TEST(sine_type) -{ - double ph = 0.0; - using T = decltype(sine(ph)); - static_assert(std::is_same_v<T, double>); -} - -TEST(phasor) -{ - constexpr fbase sr = 44100.0; - univector<fbase, 100> v1 = sinenorm(phasor(15000, sr)); - univector<fbase, 100> v2 = sin(constants<fbase>::pi_s(2) * counter(0, 15000 / sr)); - CHECK(rms(v1 - v2) < 1.e-5); -} - -template <typename E, typename T, size_t size> -void test_ir(E&& e, const univector<T, size>& test_vector) -{ - substitute(e, to_handle(unitimpulse<T>())); - const univector<T, size> ir = e; - println(absmaxof(ir - test_vector)); -} - -TEST(gen_sin) -{ - kfr::univector<kfr::fbase> x; - constexpr size_t size = 132; - kfr::fbase step = kfr::c_pi<kfr::fbase> / (size + 1); - kfr::univector<kfr::fbase> up; - up = kfr::truncate(kfr::gen_sin<kfr::fbase>(kfr::c_pi<kfr::fbase> / 2, step), size); - - kfr::univector<kfr::fbase> up2(size); - for (int i = 0; i < size; ++i) - { - up2[i] = std::sin(kfr::c_pi<kfr::fbase> / 2 + i * step); - } - CHECK(rms(up - up2) < 0.00001); -} - -} // namespace CMT_ARCH_NAME - -#ifndef KFR_NO_MAIN -int main(int argc, char* argv[]) -{ - println(library_version()); - return testo::run_all(argc > 1 ? argv[1] : "", true); -} -#endif diff --git a/tests/expression_test.cpp b/tests/expression_test.cpp @@ -1,53 +0,0 @@ -/** - * KFR (https://www.kfrlib.com) - * Copyright (C) 2016-2023 Dan Cazarin - * See LICENSE.txt for details - */ - -#include <kfr/testo/testo.hpp> - -#include <kfr/base.hpp> -#include <kfr/cometa/function.hpp> -#include <kfr/dsp.hpp> -#include <kfr/io.hpp> - -using namespace kfr; - -namespace CMT_ARCH_NAME -{ - -TEST(univector_assignment) -{ - univector<int> x = truncate(counter(), 10); - CHECK(x.size() == 10u); - - univector<int> y; - y = truncate(counter(), 10); - CHECK(y.size() == 10u); -} - -TEST(mix) -{ - CHECK_EXPRESSION(mix(sequence(0, 0.5f, 1, 0.5f), counter(), counter() * 10), infinite_size, - [](size_t i) { - return mix(std::array<float, 4>{ 0, 0.5f, 1, 0.5f }[i % 4], i, i * 10); - }); -} - -TEST(expression_mask) -{ - univector<float> x(100); - univector<float> y(100); - x = select(x > y, 0.5f, 0.1f) * (y - x) + x; -} - -} // namespace CMT_ARCH_NAME - -#ifndef KFR_NO_MAIN -int main() -{ - println(library_version()); - - return testo::run_all("", true); -} -#endif diff --git a/tests/asm_test.cpp b/tests/internal/asm_test.cpp diff --git a/tests/generate_data.cpp b/tests/internal/generate_data.cpp diff --git a/tests/io_test.cpp b/tests/io_test.cpp @@ -1,85 +0,0 @@ -/** - * KFR (https://www.kfrlib.com) - * Copyright (C) 2016-2023 Dan Cazarin - * See LICENSE.txt for details - */ - -#include <kfr/testo/testo.hpp> - -#include <kfr/base.hpp> -#include <kfr/cometa/function.hpp> -#include <kfr/io.hpp> - -using namespace kfr; - -namespace CMT_ARCH_NAME -{ - -#ifndef KFR_DISABLE_WAV -TEST(write_wav_file) -{ - audio_writer_wav<float> writer(open_file_for_writing(KFR_FILEPATH("temp_audio_file.wav")), - audio_format{}); - univector<float> data(44100 * 2); - data = sin(counter() * 0.01f); - size_t wr = writer.write(data.data(), data.size()); - CHECK(wr == data.size()); - CHECK(umax(writer.format().length) == data.size() / 2); -} - -TEST(read_wav_file) -{ - audio_reader_wav<float> reader(open_file_for_reading(KFR_FILEPATH("temp_audio_file.wav"))); - CHECK(reader.format().channels == 2u); - CHECK(reader.format().type == audio_sample_type::i16); - CHECK(reader.format().samplerate == 44100); - univector<float> data(44100 * 2); - CHECK(umax(reader.format().length) == data.size() / 2); - size_t rd = reader.read(data.data(), data.size()); - CHECK(rd == data.size()); - CHECK(absmaxof(data - render(sin(counter() * 0.01f), data.size())) < 0.0001f); -} -#endif - -#ifndef KFR_DISABLE_FLAC -TEST(read_flac_file) -{ - audio_reader_flac<float> reader( - open_file_for_reading(KFR_FILEPATH(KFR_SRC_DIR "/tests/test-audio/sine.flac"))); - CHECK(reader.format().channels == 2u); - CHECK(reader.format().type == audio_sample_type::i32); - CHECK(reader.format().samplerate == 44100); - univector<float> data(44100 * 2); - CHECK(reader.format().length == data.size() / 2); - size_t rd = reader.read(data.data(), data.size()); - CHECK(rd == data.size()); - CHECK(absmaxof(data - render(sin(counter() * 0.01f), data.size())) < 0.0001f); -} -#endif - -#ifndef KFR_DISABLE_MP3 -TEST(read_mp3_file) -{ - audio_reader_mp3<float> reader( - open_file_for_reading(KFR_FILEPATH(KFR_SRC_DIR "/tests/test-audio/sine.mp3"))); - CHECK(reader.format().channels == 2u); - CHECK(reader.format().type == audio_sample_type::i16); - CHECK(reader.format().samplerate == 44100); - univector<float> data(44100 * 2); - CHECK(reader.format().length >= data.size() / 2); - size_t rd = reader.read(data.data(), data.size()); - CHECK(rd == data.size()); - data = data.slice(2402, 2 * 44100); // MP3 format delay - CHECK(absmaxof(data - render(sin(counter() * 0.01f), data.size())) < 0.005f); -} -#endif -} // namespace CMT_ARCH_NAME - -#ifndef KFR_NO_MAIN -int main() -{ - println(library_version()); - - return testo::run_all("", true); -} -#endif diff --git a/tests/unit/base/generators.cpp b/tests/unit/base/generators.cpp @@ -30,4 +30,20 @@ TEST(test_gen_expj) // In most cases error is much lower (less than 0.00001) } +TEST(gen_sin) +{ + kfr::univector<kfr::fbase> x; + constexpr size_t size = 132; + kfr::fbase step = kfr::c_pi<kfr::fbase> / (size + 1); + kfr::univector<kfr::fbase> up; + up = kfr::truncate(kfr::gen_sin<kfr::fbase>(kfr::c_pi<kfr::fbase> / 2, step), size); + + kfr::univector<kfr::fbase> up2(size); + for (int i = 0; i < size; ++i) + { + up2[i] = std::sin(kfr::c_pi<kfr::fbase> / 2 + i * step); + } + CHECK(rms(up - up2) < 0.00001); +} + } // namespace CMT_ARCH_NAME diff --git a/tests/unit/base/math_expressions.cpp b/tests/unit/base/math_expressions.cpp @@ -0,0 +1,60 @@ +/** + * KFR (https://www.kfrlib.com) + * Copyright (C) 2016-2023 Dan Cazarin + * See LICENSE.txt for details + */ + +#include <kfr/testo/testo.hpp> + +#include <kfr/base/simd_expressions.hpp> +#include <kfr/base/math_expressions.hpp> +#include <kfr/base/univector.hpp> + +using namespace kfr; + +namespace CMT_ARCH_NAME +{ + +TEST(complex_basic_expressions) +{ + const univector<c32, 15> uv1 = zeros(); + CHECK(uv1[0] == c32{ 0, 0 }); + CHECK(uv1[1] == c32{ 0, 0 }); + CHECK(uv1[2] == c32{ 0, 0 }); + CHECK(uv1[14] == c32{ 0, 0 }); + const univector<c32, 15> uv2 = ones(); + CHECK(uv2[0] == c32{ 1, 0 }); + CHECK(uv2[1] == c32{ 1, 0 }); + CHECK(uv2[2] == c32{ 1, 0 }); + CHECK(uv2[14] == c32{ 1, 0 }); + const univector<c32, 15> uv3 = counter(); + CHECK(uv3[0] == c32{ 0, 0 }); + CHECK(uv3[1] == c32{ 1, 0 }); + CHECK(uv3[2] == c32{ 2, 0 }); + CHECK(uv3[14] == c32{ 14, 0 }); +} + +TEST(complex_function_expressions) +{ + const univector<c32, 4> uv1 = sqr(counter()); + CHECK(uv1[0] == c32{ 0, 0 }); + CHECK(uv1[1] == c32{ 1, 0 }); + CHECK(uv1[2] == c32{ 4, 0 }); + CHECK(uv1[3] == c32{ 9, 0 }); + + const univector<c32, 4> uv2 = uv1 * 2.f; + CHECK(uv2[0] == c32{ 0, 0 }); + CHECK(uv2[1] == c32{ 2, 0 }); + CHECK(uv2[2] == c32{ 8, 0 }); + CHECK(uv2[3] == c32{ 18, 0 }); + + const univector<f32, 4> uv3 = real(uv2); + CHECK(uv3[0] == 0.f); + CHECK(uv3[1] == 2.f); + CHECK(uv3[2] == 8.f); + CHECK(uv3[3] == 18.f); + testo::assert_is_same<c32, expression_value_type<decltype(uv2)>>(); + testo::assert_is_same<f32, expression_value_type<decltype(uv3)>>(); + testo::assert_is_same<f32, expression_value_type<decltype(real(uv2))>>(); +} +} // namespace CMT_ARCH_NAME diff --git a/tests/unit/base/simd_expressions.cpp b/tests/unit/base/simd_expressions.cpp @@ -0,0 +1,33 @@ +/** + * KFR (https://www.kfrlib.com) + * Copyright (C) 2016-2023 Dan Cazarin + * See LICENSE.txt for details + */ + +#include <kfr/base/basic_expressions.hpp> +#include <kfr/base/simd_expressions.hpp> +#include <kfr/base/univector.hpp> +#include <kfr/io/tostring.hpp> + +namespace kfr +{ +inline namespace CMT_ARCH_NAME +{ + +TEST(expression_mask) +{ + univector<float> x(100); + univector<float> y(100); + x = select(x > y, 0.5f, 0.1f) * (y - x) + x; +} + +TEST(mix) +{ + CHECK_EXPRESSION(mix(sequence(0, 0.5f, 1, 0.5f), counter(), counter() * 10), infinite_size, + [](size_t i) { + return mix(std::array<float, 4>{ 0, 0.5f, 1, 0.5f }[i % 4], i, i * 10); + }); +} + +} // namespace CMT_ARCH_NAME +} // namespace kfr diff --git a/tests/unit/base/univector.cpp b/tests/unit/base/univector.cpp @@ -0,0 +1,44 @@ +/** + * KFR (https://www.kfrlib.com) + * Copyright (C) 2016-2023 Dan Cazarin + * See LICENSE.txt for details + */ + +#include <kfr/base/basic_expressions.hpp> +#include <kfr/base/univector.hpp> +#include <kfr/io/tostring.hpp> + +namespace kfr +{ +inline namespace CMT_ARCH_NAME +{ + +TEST(univector_assignment) +{ + univector<int> x = truncate(counter(), 10); + CHECK(x.size() == 10u); + + univector<int> y; + y = truncate(counter(), 10); + CHECK(y.size() == 10u); +} + +#ifdef KFR_USE_STD_ALLOCATION +TEST(std_allocation) +{ + univector<float> u; + std::vector<float>& v = u; + + std::vector<float> v2{ 1, 2, 3, 4 }; + + // Technically an UB but ok with all sane compilers + reinterpret_cast<univector<float>&>(v2) += 100.f; + CHECK(v2[0] == 101); + CHECK(v2[1] == 102); + CHECK(v2[2] == 103); + CHECK(v2[3] == 104); +} +#endif + +} // namespace CMT_ARCH_NAME +} // namespace kfr diff --git a/tests/unit/cometa.cpp b/tests/unit/cometa.cpp @@ -0,0 +1,19 @@ +/** + * KFR (https://www.kfrlib.com) + * Copyright (C) 2016-2023 Dan Cazarin + * See LICENSE.txt for details + */ + +#include <kfr/cometa.hpp> +#include <kfr/cometa/ctti.hpp> +#include <kfr/testo/testo.hpp> + +namespace kfr +{ + +TEST(ctti) +{ + CHECK(cometa::type_name<float>() == std::string("float")); + CHECK(cometa::type_name<int>() == std::string("int")); +} +} // namespace kfr diff --git a/tests/unit/dsp/delay.cpp b/tests/unit/dsp/delay.cpp @@ -0,0 +1,47 @@ +/** + * KFR (https://www.kfrlib.com) + * Copyright (C) 2016-2023 Dan Cazarin + * See LICENSE.txt for details + */ + +#include <kfr/testo/testo.hpp> + +#include <kfr/base.hpp> +#include <kfr/dsp/delay.hpp> + +using namespace kfr; + +namespace CMT_ARCH_NAME +{ + +TEST(delay) +{ + const univector<float, 33> v1 = counter() + 100; + CHECK_EXPRESSION(delay(v1), 33, [](size_t i) { return i < 1 ? 0.f : (i - 1) + 100.f; }); + + CHECK_EXPRESSION(delay<3>(v1), 33, [](size_t i) { return i < 3 ? 0.f : (i - 3) + 100.f; }); + + delay_state<float, 3> state1; + CHECK_EXPRESSION(delay(state1, v1), 33, [](size_t i) { return i < 3 ? 0.f : (i - 3) + 100.f; }); + + delay_state<float, 3, tag_dynamic_vector> state2; + CHECK_EXPRESSION(delay(state2, v1), 33, [](size_t i) { return i < 3 ? 0.f : (i - 3) + 100.f; }); +} + +TEST(fracdelay) +{ + univector<double, 5> a({ 1, 2, 3, 4, 5 }); + univector<double, 5> b = fracdelay(a, 0.5); + CHECK(rms(b - univector<double>({ 0.5, 1.5, 2.5, 3.5, 4.5 })) < constants<double>::epsilon * 5); + + b = fracdelay(a, 0.1); + CHECK(rms(b - univector<double>({ 0.9, 1.9, 2.9, 3.9, 4.9 })) < constants<double>::epsilon * 5); + + b = fracdelay(a, 0.0); + CHECK(rms(b - univector<double>({ 1, 2, 3, 4, 5 })) < constants<double>::epsilon * 5); + + b = fracdelay(a, 1.0); + CHECK(rms(b - univector<double>({ 0, 1, 2, 3, 4 })) < constants<double>::epsilon * 5); +} + +} // namespace CMT_ARCH_NAME diff --git a/tests/unit/dsp/goertzel.cpp b/tests/unit/dsp/goertzel.cpp @@ -0,0 +1,38 @@ +/** + * KFR (https://www.kfrlib.com) + * Copyright (C) 2016-2023 Dan Cazarin + * See LICENSE.txt for details + */ + +#include <kfr/testo/testo.hpp> + +#include <kfr/base.hpp> +#include <kfr/dsp.hpp> + +using namespace kfr; + +namespace CMT_ARCH_NAME +{ + +TEST(goertzel) +{ + testo::epsilon_scope<float> e(100); + univector<float, 16> a; + a = sinenorm(phasor(0.125f)); + + float omega = c_pi<float, 2> * 0.125f; + + complex<float> c; + process(goertzel(c, omega), a); + CHECK(cabs(c) == 8.f); + + complex<float> cs[3]; + float omegas[3] = { omega, omega, omega }; + process(goertzel(cs, omegas), a); + println(cs[0]); + CHECK(cabs(cs[0]) == 8.f); + CHECK(cabs(cs[1]) == 8.f); + CHECK(cabs(cs[2]) == 8.f); +} + +} // namespace CMT_ARCH_NAME diff --git a/tests/unit/dsp/mixdown.cpp b/tests/unit/dsp/mixdown.cpp @@ -0,0 +1,36 @@ +/** + * KFR (https://www.kfrlib.com) + * Copyright (C) 2016-2023 Dan Cazarin + * See LICENSE.txt for details + */ + +#include <kfr/base/reduce.hpp> +#include <kfr/base/simd_expressions.hpp> +#include <kfr/base/univector.hpp> +#include <kfr/dsp/mixdown.hpp> + +namespace kfr +{ +inline namespace CMT_ARCH_NAME +{ + +TEST(mixdown) +{ + CHECK_EXPRESSION(mixdown(counter(), counter() * 2 + 100), infinite_size, + [](size_t i) { return i + i * 2 + 100; }); +} + +TEST(mixdown_stereo) +{ + const univector<double, 21> left = counter(); + const univector<double, 21> right = counter() * 2 + 100; + univector<double, 21> mid; + univector<double, 21> side; + unpack(mid, side) = mixdown_stereo(left, right, matrix_sum_diff()); + + CHECK_EXPRESSION(mid, 21, [](size_t i) { return i + i * 2.0 + 100.0; }); + CHECK_EXPRESSION(side, 21, [](size_t i) { return i - (i * 2.0 + 100.0); }); +} + +} // namespace CMT_ARCH_NAME +} // namespace kfr diff --git a/tests/unit/dsp/oscillators.cpp b/tests/unit/dsp/oscillators.cpp @@ -0,0 +1,31 @@ +/** + * KFR (https://www.kfrlib.com) + * Copyright (C) 2016-2023 Dan Cazarin + * See LICENSE.txt for details + */ + +#include <kfr/testo/testo.hpp> + +#include <kfr/base.hpp> +#include <kfr/dsp/oscillators.hpp> + +using namespace kfr; + +namespace CMT_ARCH_NAME +{ + +TEST(sine_type) +{ + double ph = 0.0; + using T = decltype(sine(ph)); + static_assert(std::is_same_v<T, double>); +} + +TEST(phasor) +{ + constexpr fbase sr = 44100.0; + univector<fbase, 100> v1 = sinenorm(phasor(15000, sr)); + univector<fbase, 100> v2 = sin(constants<fbase>::pi_s(2) * counter(0, 15000 / sr)); + CHECK(rms(v1 - v2) < 1.e-5); +} +} // namespace CMT_ARCH_NAME diff --git a/tests/unit/io/audiofile.cpp b/tests/unit/io/audiofile.cpp @@ -0,0 +1,72 @@ +/** + * KFR (https://www.kfrlib.com) + * Copyright (C) 2016-2023 Dan Cazarin + * See LICENSE.txt for details + */ + +#include <kfr/io/audiofile.hpp> +#include <kfr/testo/testo.hpp> +#include <kfr/base.hpp> + +namespace kfr +{ + +#ifndef KFR_DISABLE_WAV +TEST(write_wav_file) +{ + audio_writer_wav<float> writer(open_file_for_writing(KFR_FILEPATH("temp_audio_file.wav")), + audio_format{}); + univector<float> data(44100 * 2); + data = sin(counter() * 0.01f); + size_t wr = writer.write(data.data(), data.size()); + CHECK(wr == data.size()); + CHECK(umax(writer.format().length) == data.size() / 2); +} + +TEST(read_wav_file) +{ + audio_reader_wav<float> reader(open_file_for_reading(KFR_FILEPATH("temp_audio_file.wav"))); + CHECK(reader.format().channels == 2u); + CHECK(reader.format().type == audio_sample_type::i16); + CHECK(reader.format().samplerate == 44100); + univector<float> data(44100 * 2); + CHECK(umax(reader.format().length) == data.size() / 2); + size_t rd = reader.read(data.data(), data.size()); + CHECK(rd == data.size()); + CHECK(absmaxof(data - render(sin(counter() * 0.01f), data.size())) < 0.0001f); +} +#endif + +#ifndef KFR_DISABLE_FLAC +TEST(read_flac_file) +{ + audio_reader_flac<float> reader( + open_file_for_reading(KFR_FILEPATH(KFR_SRC_DIR "/tests/test-audio/sine.flac"))); + CHECK(reader.format().channels == 2u); + CHECK(reader.format().type == audio_sample_type::i32); + CHECK(reader.format().samplerate == 44100); + univector<float> data(44100 * 2); + CHECK(reader.format().length == data.size() / 2); + size_t rd = reader.read(data.data(), data.size()); + CHECK(rd == data.size()); + CHECK(absmaxof(data - render(sin(counter() * 0.01f), data.size())) < 0.0001f); +} +#endif + +#ifndef KFR_DISABLE_MP3 +TEST(read_mp3_file) +{ + audio_reader_mp3<float> reader( + open_file_for_reading(KFR_FILEPATH(KFR_SRC_DIR "/tests/test-audio/sine.mp3"))); + CHECK(reader.format().channels == 2u); + CHECK(reader.format().type == audio_sample_type::i16); + CHECK(reader.format().samplerate == 44100); + univector<float> data(44100 * 2); + CHECK(reader.format().length >= data.size() / 2); + size_t rd = reader.read(data.data(), data.size()); + CHECK(rd == data.size()); + data = data.slice(2402, 2 * 44100); // MP3 format delay + CHECK(absmaxof(data - render(sin(counter() * 0.01f), data.size())) < 0.005f); +} +#endif +} // namespace kfr diff --git a/tests/unit/math/asin_acos.cpp b/tests/unit/math/asin_acos.cpp @@ -3,7 +3,7 @@ * Copyright (C) 2016-2023 Dan Cazarin * See LICENSE.txt for details */ -#include "../../numeric_tests.hpp" +#include "../numeric_tests.hpp" #include <kfr/math/asin_acos.hpp> diff --git a/tests/unit/math/atan.cpp b/tests/unit/math/atan.cpp @@ -4,7 +4,7 @@ * See LICENSE.txt for details */ -#include "../../numeric_tests.hpp" +#include "../numeric_tests.hpp" #include <kfr/math/atan.hpp> diff --git a/tests/unit/math/complex_math.cpp b/tests/unit/math/complex_math.cpp @@ -0,0 +1,84 @@ +/** + * KFR (https://www.kfrlib.com) + * Copyright (C) 2016-2023 Dan Cazarin + * See LICENSE.txt for details + */ + +#include <kfr/math/complex_math.hpp> + +namespace kfr +{ +inline namespace CMT_ARCH_NAME +{ + +TEST(complex_math) +{ + const vec<c32, 1> a{ c32{ 1, 2 } }; + const vec<c32, 1> b{ c32{ 3, 4 } }; + CHECK(c32(vec<c32, 1>(2)[0]) == c32{ 2, 0 }); + CHECK(a + b == make_vector(c32{ 4, 6 })); + CHECK(a - b == make_vector(c32{ -2, -2 })); + CHECK(a * b == make_vector(c32{ -5, 10 })); + CHECK(a * vec<c32, 1>(2) == make_vector(c32{ 2, 4 })); + CHECK(a * 2 == make_vector(c32{ 2, 4 })); + CHECK(a / b == make_vector(c32{ 0.44f, 0.08f })); + CHECK(-a == make_vector(c32{ -1, -2 })); + + CHECK(real(a) == make_vector(1.f)); + CHECK(imag(a) == make_vector(2.f)); + + CHECK(make_complex(5.f, 7) == c32{ 5.f, 7.f }); + CHECK(make_complex(make_vector(5.f, 8.f), make_vector(7.f, 9.f)) == + make_vector(c32{ 5.f, 7.f }, c32{ 8.f, 9.f })); + + CHECK(cabs(c32{ 3.f, 4.f }) == 5.f); + CHECK(cabs(make_vector(c32{ 3.f, 4.f })) == make_vector(5.f)); + + CHECK(cabs(-3.f) == 3.f); + CHECK(cabs(make_vector(-3.f)) == make_vector(3.f)); + + CHECK(carg(c32{ +1.f, 0.f }) == 0.f); + CHECK(carg(c32{ 0.f, +1.f }) == c_pi<float> / 2); + CHECK(carg(c32{ 0.f, -1.f }) == -c_pi<float> / 2); + CHECK(carg(c32{ -1.f, 0.f }) == c_pi<float>); + + testo::epsilon_scope<void> eps(5); + + CHECK(csin(c32{ 1.f, 1.f }) == c32{ 1.2984575814159773f, 0.634963914784736f }); + CHECK(ccos(c32{ 1.f, 1.f }) == c32{ 0.8337300251311489f, -0.9888977057628651f }); + CHECK(csinh(c32{ 1.f, 1.f }) == c32{ 0.634963914784736f, 1.2984575814159773f }); + CHECK(ccosh(c32{ 1.f, 1.f }) == c32{ 0.8337300251311489f, 0.9888977057628651f }); + + CHECK(clog(c32{ 1.f, 1.f }) == c32{ 0.34657359027997264f, 0.7853981633974483f }); + CHECK(clog2(c32{ 1.f, 1.f }) == c32{ 0.5f, 1.1330900354567983f }); + CHECK(clog10(c32{ 1.f, 1.f }) == c32{ 0.15051499783199057f, 0.3410940884604603f }); + + CHECK(cexp(c32{ 1.f, 1.f }) == c32{ 1.4686939399158849f, 2.2873552871788423f }); + CHECK(cexp2(c32{ 1.f, 1.f }) == c32{ 1.5384778027279442f, 1.2779225526272695f }); + CHECK(cexp10(c32{ 1.f, 1.f }) == c32{ -6.682015101903131f, 7.439803369574931f }); + +#ifdef CMT_NATIVE_F64 + CHECK(csin(c64{ 1.0, 1.0 }) == c64{ 1.2984575814159773, 0.634963914784736 }); + CHECK(ccos(c64{ 1.0, 1.0 }) == c64{ 0.8337300251311489, -0.9888977057628651 }); + CHECK(csinh(c64{ 1.0, 1.0 }) == c64{ 0.634963914784736, 1.2984575814159773 }); + CHECK(ccosh(c64{ 1.0, 1.0 }) == c64{ 0.8337300251311489, 0.9888977057628651 }); + CHECK(clog(c64{ 1.0, 1.0 }) == c64{ 0.34657359027997264, 0.7853981633974483 }); + CHECK(cexp(c64{ 1.0, 1.0 }) == c64{ 1.4686939399158849, 2.2873552871788423 }); +#endif +} + +TEST(complex_functions) +{ + CHECK(csqr(complex<f32>(4.f, 0.f)) == c32{ 16.f, 0.f }); + CHECK(csqrt(complex<f32>(16.f, 0.f)) == c32{ 4.f, 0.f }); + + CHECK(csqr(complex<f32>(1.f, 4.f)) == c32{ -15.f, 8.f }); + + CHECK(csqrt(complex<f32>(15.f, 8.f)) == c32{ 4.f, 1.f }); + CHECK(csqrt(complex<f32>(-15.f, 8.f)) == c32{ 1.f, 4.f }); + CHECK(csqrt(complex<f32>(15.f, -8.f)) == c32{ 4.f, -1.f }); + CHECK(csqrt(complex<f32>(-15.f, -8.f)) == c32{ 1.f, -4.f }); +} + +} // namespace CMT_ARCH_NAME +} // namespace kfr diff --git a/tests/unit/math/hyperbolic.cpp b/tests/unit/math/hyperbolic.cpp @@ -4,7 +4,7 @@ * See LICENSE.txt for details */ -#include "../../numeric_tests.hpp" +#include "../numeric_tests.hpp" #include <kfr/math/hyperbolic.hpp> diff --git a/tests/unit/math/log_exp.cpp b/tests/unit/math/log_exp.cpp @@ -3,7 +3,7 @@ * Copyright (C) 2016-2023 Dan Cazarin * See LICENSE.txt for details */ -#include "../../numeric_tests.hpp" +#include "../numeric_tests.hpp" #include <kfr/math/log_exp.hpp> diff --git a/tests/unit/math/sin_cos.cpp b/tests/unit/math/sin_cos.cpp @@ -3,7 +3,7 @@ * Copyright (C) 2016-2023 Dan Cazarin * See LICENSE.txt for details */ -#include "../../numeric_tests.hpp" +#include "../numeric_tests.hpp" #include <kfr/math/sin_cos.hpp> diff --git a/tests/unit/math/tan.cpp b/tests/unit/math/tan.cpp @@ -3,7 +3,7 @@ * Copyright (C) 2016-2023 Dan Cazarin * See LICENSE.txt for details */ -#include "../../numeric_tests.hpp" +#include "../numeric_tests.hpp" #include <kfr/math/tan.hpp> diff --git a/tests/numeric_tests.hpp b/tests/unit/numeric_tests.hpp diff --git a/tests/unit/simd/complex.cpp b/tests/unit/simd/complex.cpp @@ -5,11 +5,13 @@ */ #include <kfr/simd/complex.hpp> +#include <kfr/simd/read_write.hpp> namespace kfr { inline namespace CMT_ARCH_NAME { + TEST(complex_convertible) { static_assert(std::is_convertible<float, complex<float>>::value, ""); @@ -35,5 +37,101 @@ TEST(complex_convertible) CHECK(static_cast<vec<complex<double>, 2>>(vec<complex<float>, 2>{ c32{ 1.f, 2.f }, c32{ 1.f, 2.f } }) == vec<complex<double>, 2>{ c64{ 1., 2. }, c64{ 1., 2. } }); } + +TEST(complex_static_tests) +{ + static_assert(is_numeric<vec<complex<float>, 4>>, ""); + static_assert(is_numeric_args<vec<complex<float>, 4>>, ""); + + static_assert(sizeof(vec<c32, 4>) == sizeof(vec<f32, 8>), ""); + static_assert(vec<f32, 4>::size() == 4, ""); + static_assert(vec<c32, 4>::size() == 4, ""); + static_assert(vec<f32, 4>::scalar_size() == 4, ""); + static_assert(vec<c32, 4>::scalar_size() == 8, ""); + testo::assert_is_same<subtype<complex<i32>>, i32>(); + testo::assert_is_same<vec<c32, 4>::value_type, c32>(); + testo::assert_is_same<vec<c32, 4>::scalar_type, f32>(); + testo::assert_is_same<vec<f32, 4>::value_type, f32>(); + testo::assert_is_same<vec<f32, 4>::scalar_type, f32>(); + testo::assert_is_same<vec<c32, 1>, decltype(make_vector(c32{ 0, 0 }))>(); + testo::assert_is_same<vec<c32, 2>, decltype(make_vector(c32{ 0, 0 }, 4))>(); + testo::assert_is_same<ftype<complex<i32>>, complex<f32>>(); + testo::assert_is_same<ftype<complex<i64>>, complex<f64>>(); + testo::assert_is_same<ftype<vec<complex<i32>, 4>>, vec<complex<f32>, 4>>(); + testo::assert_is_same<ftype<vec<complex<i64>, 8>>, vec<complex<f64>, 8>>(); + + testo::assert_is_same<std::common_type_t<complex<int>, double>, complex<double>>(); +} + +TEST(complex_shuffle) +{ + const vec<c32, 2> a{ c32{ 0, 1 }, c32{ 2, 3 } }; + CHECK(reverse(a) == make_vector(c32{ 2, 3 }, c32{ 0, 1 })); +} + +TEST(complex_read_write) +{ + c32 buffer[8] = { c32{ 1, 2 }, c32{ 3, 4 }, c32{ 5, 6 }, c32{ 7, 8 }, + c32{ 9, 10 }, c32{ 11, 12 }, c32{ 13, 14 }, c32{ 15, 16 } }; + + CHECK(read<4>(buffer) == make_vector(c32{ 1, 2 }, c32{ 3, 4 }, c32{ 5, 6 }, c32{ 7, 8 })); + CHECK(read<3>(buffer + 1) == make_vector(c32{ 3, 4 }, c32{ 5, 6 }, c32{ 7, 8 })); + write(buffer + 2, make_vector(c32{ 10, 11 }, c32{ 12, 13 })); + CHECK(read<4>(buffer) == make_vector(c32{ 1, 2 }, c32{ 3, 4 }, c32{ 10, 11 }, c32{ 12, 13 })); +} + +TEST(complex_cast) +{ + const vec<f32, 4> v1 = bitcast<f32>(make_vector(c32{ 0, 1 }, c32{ 2, 3 })); + CHECK(v1.flatten()[0] == 0.f); + CHECK(v1.flatten()[1] == 1.f); + CHECK(v1.flatten()[2] == 2.f); + CHECK(v1.flatten()[3] == 3.f); + + const vec<c32, 1> v2 = bitcast<c32>(make_vector(1.f, 2.f)); + CHECK(v2.flatten()[0] == 1.f); + CHECK(v2.flatten()[1] == 2.f); + + const vec<c32, 2> v3 = make_vector(1.f, 2.f); + CHECK(v3.flatten()[0] == 1.f); + CHECK(v3.flatten()[1] == 0.f); + CHECK(v3.flatten()[2] == 2.f); + CHECK(v3.flatten()[3] == 0.f); + + const vec<c32, 2> v4 = make_vector(1, 2); + CHECK(v4.flatten()[0] == 1.f); + CHECK(v4.flatten()[1] == 0.f); + CHECK(v4.flatten()[2] == 2.f); + CHECK(v4.flatten()[3] == 0.f); + + CHECK(zerovector<c32, 4>() == make_vector(c32{ 0, 0 }, c32{ 0, 0 }, c32{ 0, 0 }, c32{ 0, 0 })); + CHECK(enumerate<c32, 4>() == make_vector(c32{ 0, 0 }, c32{ 1, 0 }, c32{ 2, 0 }, c32{ 3, 0 })); +} + +TEST(complex_vector) +{ + const vec<c32, 1> c32x1{ c32{ 0, 1 } }; + CHECK(c32x1.flatten()[0] == 0.0f); + CHECK(c32x1.flatten()[1] == 1.0f); + + const vec<c32, 2> c32x2{ c32{ 0, 1 }, c32{ 2, 3 } }; + CHECK(c32x2.flatten()[0] == 0.0f); + CHECK(c32x2.flatten()[1] == 1.0f); + CHECK(c32x2.flatten()[2] == 2.0f); + CHECK(c32x2.flatten()[3] == 3.0f); + + const vec<c32, 3> c32x3{ c32{ 0, 1 }, c32{ 2, 3 }, c32{ 4, 5 } }; + CHECK(c32x3.flatten()[0] == 0.0f); + CHECK(c32x3.flatten()[1] == 1.0f); + CHECK(c32x3.flatten()[2] == 2.0f); + CHECK(c32x3.flatten()[3] == 3.0f); + CHECK(c32x3.flatten()[4] == 4.0f); + CHECK(c32x3.flatten()[5] == 5.0f); + + const vec<c32, 1> c32s = 2; + CHECK(c32s.flatten()[0] == 2.f); + CHECK(c32s.flatten()[1] == 0.f); +} + } // namespace CMT_ARCH_NAME } // namespace kfr diff --git a/tests/unit/simd/shuffle.cpp b/tests/unit/simd/shuffle.cpp @@ -186,5 +186,84 @@ TEST(enumerate) CHECK(enumerate(vec_shape<int, 8>{}, 3) == vec{ 0, 3, 6, 9, 12, 15, 18, 21 }); CHECK(enumerate(vec_shape<int, 7>{}, 3) == vec{ 0, 3, 6, 9, 12, 15, 18 }); } + + +TEST(test_basic) +{ + // How to make a vector: + + // * Use constructor + const vec<double, 4> first{ 1, 2.5, -infinity, 3.1415926 }; + CHECK(first == vec<double, 4>{ 1, 2.5, -infinity, 3.1415926 }); + + // * Use make_vector function + const auto second = make_vector(-1, +1); + CHECK(second == vec<int, 2>{ -1, 1 }); + + // * Convert from vector of other type: + const vec<int, 4> int_vector{ 10, 20, 30, 40 }; + const vec<double, 4> double_vector = cast<double>(int_vector); + CHECK(double_vector == vec<double, 4>{ 10, 20, 30, 40 }); + + // * Concat two vectors: + const vec<int, 1> left_part{ 1 }; + const vec<int, 1> right_part{ 2 }; + const vec<int, 2> pair{ left_part, right_part }; + CHECK(pair == vec<int, 2>{ 1, 2 }); + + // * Same, but using make_vector and concat: + const vec<int, 2> pair2 = concat(make_vector(10), make_vector(20)); + CHECK(pair2 == vec<int, 2>{ 10, 20 }); + + // * Repeat vector multiple times: + const vec<short, 8> repeated = repeat<4>(make_vector<short>(0, -1)); + CHECK(repeated == vec<short, 8>{ 0, -1, 0, -1, 0, -1, 0, -1 }); + + // * Use enumerate to generate sequence of numbers: + const vec<int, 8> eight = enumerate<int, 8>(); + CHECK(eight == vec<int, 8>{ 0, 1, 2, 3, 4, 5, 6, 7 }); + + // * Vectors can be of any length... + const vec<int, 1> one{ 42 }; + const vec<int, 2> two = concat(one, make_vector(42)); + CHECK(two == vec<int, 2>{ 42, 42 }); + + const vec<u8, 256> very_long_vector = repeat<64>(make_vector<u8>(1, 2, 4, 8)); + CHECK(slice<0, 17>(very_long_vector) == + vec<unsigned char, 17>{ 1, 2, 4, 8, 1, 2, 4, 8, 1, 2, 4, 8, 1, 2, 4, 8, 1 }); + + // * ...really any: + using big_vector = vec<i16, 107>; + big_vector v107 = enumerate<i16, 107>(); + CHECK(hadd(v107) == static_cast<short>(5671)); + + using color = vec<u8, 3>; + const color green = cast<u8>(make_vector(0.0, 1.0, 0.0) * 255); + CHECK(green == vec<unsigned char, 3>{ 0, 255, 0 }); + + // Vectors support all standard operators: + const auto op1 = make_vector(0, 1, 10, 100); + const auto op2 = make_vector(20, 2, -2, 200); + const auto result = op1 * op2 - 4; + CHECK(result == vec<int, 4>{ -4, -2, -24, 19996 }); + + // * Transform vector: + const vec<int, 8> numbers1 = enumerate<int, 8>(); + const vec<int, 8> numbers2 = enumerate<int, 8>() + 100; + CHECK(odd(numbers1) == vec<int, 4>{ 1, 3, 5, 7 }); + CHECK(even(numbers2) == vec<int, 4>{ 100, 102, 104, 106 }); + + CHECK(subadd(pack(0, 1, 2, 3, 4, 5, 6, 7), pack(10, 10, 10, 10, 10, 10, 10, 10)) == + pack(-10, 11, -8, 13, -6, 15, -4, 17)); + CHECK(addsub(pack(0, 1, 2, 3, 4, 5, 6, 7), pack(10, 10, 10, 10, 10, 10, 10, 10)) == + pack(10, -9, 12, -7, 14, -5, 16, -3)); + + CHECK(digitreverse4(pack(0.f, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)) == + pack(0.f, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15)); + + CHECK(inrange(pack(1, 2, 3), 1, 3) == make_mask<int>(true, true, true)); + CHECK(inrange(pack(1, 2, 3), 1, 2) == make_mask<int>(true, true, false)); + CHECK(inrange(pack(1, 2, 3), 1, 1) == make_mask<int>(true, false, false)); +} } // namespace CMT_ARCH_NAME } // namespace kfr